-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #50 from SundaeSwap-finance/pi/oracles
Add "Oracles"
- Loading branch information
Showing
8 changed files
with
572 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use aiken/transaction.{Output} | ||
use aiken/transaction/value.{ada_policy_id, ada_asset_name, PolicyId, AssetName} | ||
use types/order.{Destination, Fixed, Self} | ||
|
||
pub fn check_record( | ||
input: Output, | ||
destination: Destination, | ||
actual_protocol_fee: Int, | ||
output: Output, | ||
asset_id: (PolicyId, AssetName), | ||
) -> Bool { | ||
// Make sure all of the funds from the input make it into the oracle output | ||
// Note that the accuracy of the oracle is handled by the mint policy, since it has easier | ||
// access to the final state of the order | ||
|
||
// Theoretically, this could be satisfied if someone specified an asset_id / policy_id that | ||
// came from another input, rather than one that had to be minted here; but that would be on them | ||
// and maybe there's a good reason for someone to want that behavior. | ||
// We know that the `oracle` token we provide is secure against this, because it must be minted into | ||
// its own spending policy, and it must be burned when spent. | ||
let remainder = input.value | ||
|> value.add(ada_policy_id, ada_asset_name, -actual_protocol_fee) | ||
|> value.add(asset_id.1st, asset_id.2nd, 1) | ||
|
||
and { | ||
output.value == remainder, | ||
when destination is { | ||
// The datum of the destination can be used by the oracle minting policy | ||
// but we don't check it here | ||
// E.g. for a standard sundae oracle, the datum encodes the owner, and | ||
// then the oracle minting policy checks that the scooper minted an oracle | ||
// with the correct owner | ||
Fixed(address, _) -> output.address == address | ||
// It doesn't make sense for an oracle to chain into an oracle | ||
Self -> False | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -267,3 +267,7 @@ test count_orders_test() { | |
|
||
count_orders(inputs) == 10 | ||
} | ||
|
||
pub fn oracle_sft_name() { | ||
"oracle" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
use aiken/interval | ||
use aiken/transaction.{NoDatum, InlineDatum, Output} | ||
use aiken/transaction/credential.{Address, VerificationKeyCredential, ScriptCredential} | ||
use aiken/transaction/value | ||
use calculation/record.{check_record} | ||
use shared | ||
use sundae/multisig | ||
use types/oracle.{OracleDatum} as types_oracle | ||
use types/order.{Fixed} | ||
|
||
test record() { | ||
let oracle_policy_id = #"00000000000000000000000000000000000000000000000000000000" | ||
let oracle_name = shared.oracle_sft_name() | ||
let addr = | ||
Address( | ||
ScriptCredential( | ||
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", | ||
), | ||
None, | ||
) | ||
let input_value = | ||
value.from_lovelace(4_500_000) | ||
let destination = Fixed { | ||
address: addr, | ||
datum: NoDatum, | ||
} | ||
let input = Output { | ||
address: addr, | ||
value: input_value, | ||
datum: InlineDatum(Void), | ||
reference_script: None, | ||
} | ||
let output = Output { | ||
address: addr, | ||
value: value.from_lovelace(2_000_000) | ||
|> value.add(oracle_policy_id, oracle_name, 1), | ||
datum: InlineDatum(OracleDatum { | ||
owner: multisig.AnyOf([]), | ||
valid_range: interval.between(0, 1), | ||
pool_ident: #"00", | ||
reserve_a: (#"00", #"00", 1_000_000), | ||
reserve_b: (#"00", #"00", 1_000_000), | ||
circulating_lp: (#"00", #"00", 1_000_000), | ||
}), | ||
reference_script: None, | ||
} | ||
check_record(input, destination, 2_500_000, output, (oracle_policy_id, oracle_name)) | ||
} | ||
|
||
test record_must_pay_to_destination() fail { | ||
let oracle_policy_id = #"00000000000000000000000000000000000000000000000000000000" | ||
let oracle_name = shared.oracle_sft_name() | ||
let addr = | ||
Address( | ||
ScriptCredential( | ||
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", | ||
), | ||
None, | ||
) | ||
let input_value = | ||
value.from_lovelace(4_500_000) | ||
let destination = Fixed { | ||
address: addr, | ||
datum: NoDatum, | ||
} | ||
let input = Output { | ||
address: addr, | ||
value: input_value, | ||
datum: InlineDatum(Void), | ||
reference_script: None, | ||
} | ||
let output = Output { | ||
address: Address(VerificationKeyCredential(#"00"), None), | ||
value: value.from_lovelace(2_000_000) | ||
|> value.add(oracle_policy_id, oracle_name, 1), | ||
datum: InlineDatum(OracleDatum { | ||
owner: multisig.AnyOf([]), | ||
valid_range: interval.between(0, 1), | ||
pool_ident: #"00", | ||
reserve_a: (#"00", #"00", 1_000_000), | ||
reserve_b: (#"00", #"00", 1_000_000), | ||
circulating_lp: (#"00", #"00", 1_000_000), | ||
}), | ||
reference_script: None, | ||
} | ||
check_record(input, destination, 2_500_000, output, (oracle_policy_id, oracle_name)) | ||
} | ||
|
||
test record_must_have_token() fail { | ||
let oracle_policy_id = #"00000000000000000000000000000000000000000000000000000000" | ||
let oracle_name = shared.oracle_sft_name() | ||
let addr = | ||
Address( | ||
ScriptCredential( | ||
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", | ||
), | ||
None, | ||
) | ||
let input_value = | ||
value.from_lovelace(4_500_000) | ||
let destination = Fixed { | ||
address: addr, | ||
datum: NoDatum, | ||
} | ||
let input = Output { | ||
address: addr, | ||
value: input_value, | ||
datum: InlineDatum(Void), | ||
reference_script: None, | ||
} | ||
let output = Output { | ||
address: addr, | ||
value: value.from_lovelace(2_000_000), | ||
datum: InlineDatum(OracleDatum { | ||
owner: multisig.AnyOf([]), | ||
valid_range: interval.between(0, 1), | ||
pool_ident: #"00", | ||
reserve_a: (#"00", #"00", 1_000_000), | ||
reserve_b: (#"00", #"00", 1_000_000), | ||
circulating_lp: (#"00", #"00", 1_000_000), | ||
}), | ||
reference_script: None, | ||
} | ||
check_record(input, destination, 2_500_000, output, (oracle_policy_id, oracle_name)) | ||
} | ||
|
||
test record_bad_datum() fail { | ||
let oracle_policy_id = #"00000000000000000000000000000000000000000000000000000000" | ||
let oracle_name = shared.oracle_sft_name() | ||
let addr = | ||
Address( | ||
ScriptCredential( | ||
#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", | ||
), | ||
None, | ||
) | ||
let input_value = | ||
value.from_lovelace(4_500_000) | ||
|> value.add(oracle_policy_id, oracle_name, 1) | ||
let destination = Fixed { | ||
address: addr, | ||
datum: NoDatum, | ||
} | ||
let input = Output { | ||
address: addr, | ||
value: input_value, | ||
datum: InlineDatum(Void), | ||
reference_script: None, | ||
} | ||
let output = Output { | ||
address: addr, | ||
value: value.from_lovelace(2_000_000) | ||
|> value.add(oracle_policy_id, oracle_name, 1), | ||
datum: InlineDatum(OracleDatum { | ||
owner: multisig.AnyOf([]), | ||
valid_range: interval.between(0, 1), | ||
pool_ident: #"00", | ||
reserve_a: (#"00", #"00", 1_000_000), | ||
reserve_b: (#"00", #"00", 1_000_000), | ||
circulating_lp: (#"00", #"00", 1_000_000), | ||
}), | ||
reference_script: None, | ||
} | ||
check_record(input, destination, 2_500_000, output, (oracle_policy_id, oracle_name)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
use sundae/multisig | ||
use aiken/transaction.{ValidityRange} | ||
use shared.{SingletonValue, Ident} | ||
|
||
pub type OracleDatum { | ||
// The owner who is allowed to reclaim this datum at the end | ||
owner: multisig.MultisigScript, | ||
// The valid range for the scoop transaction that produced this Datum, which gives a confidence interval for when this price was valid | ||
valid_range: ValidityRange, | ||
// The pool identifier that this datum was produced for | ||
pool_ident: Ident, | ||
// The reserve *after* the scoop in question | ||
reserve_a: SingletonValue, | ||
reserve_b: SingletonValue, | ||
circulating_lp: SingletonValue, | ||
} | ||
|
||
pub type OracleRedeemer { | ||
Mint(Ident, List<Int>) | ||
Burn | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.