Skip to content

Commit

Permalink
Merge pull request #74 from SundaeSwap-finance/rrruko/ssw-102-support…
Browse files Browse the repository at this point in the history
…-multiple-withdrawals

Resolve SSW-102 (support multiple withdrawals on order scoop)
  • Loading branch information
Quantumplation authored Apr 9, 2024
2 parents 5d5afcd + f4e42c7 commit b6fbf3d
Show file tree
Hide file tree
Showing 6 changed files with 272 additions and 45 deletions.
16 changes: 15 additions & 1 deletion lib/tests/examples/ex_shared.ak
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use aiken/bytearray
use aiken/cbor
use aiken/transaction.{OutputReference, TransactionId}
use aiken/transaction/credential.{Address, ScriptCredential, VerificationKeyCredential}
use aiken/transaction/credential.{Address, Inline, StakeCredential, ScriptCredential, VerificationKeyCredential}

pub const examples_enabled: Int = 1
pub fn print_example(d: Data) -> Bool {
Expand Down Expand Up @@ -34,3 +34,17 @@ pub fn script_address(hash: ByteArray) -> Address {
pub fn wallet_address(hash: ByteArray) -> Address {
Address(VerificationKeyCredential(hash), None)
}

pub fn compare_stake(left: StakeCredential, right: StakeCredential) -> Ordering {
let left = when left is {
Inline(ScriptCredential(x)) -> x
Inline(VerificationKeyCredential(x)) -> x
_ -> fail
}
let right = when right is {
Inline(ScriptCredential(x)) -> x
Inline(VerificationKeyCredential(x)) -> x
_ -> fail
}
bytearray.compare(left, right)
}
170 changes: 157 additions & 13 deletions plutus.json

Large diffs are not rendered by default.

24 changes: 10 additions & 14 deletions validators/order.ak
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use types/order.{Cancel, OrderDatum, OrderRedeemer, Scoop}
/// That stake_script then checks that a pool NFT is present on the UTXO, where most of the subtle and important logic is implemented.
validator(stake_script_hash: Hash<Blake2b_224, Script>) {
// For the purposes of spending the order, we don't care what the datum is, so avoid deserializing it
fn spend(datum: Data, redeemer: OrderRedeemer, ctx: ScriptContext) -> Bool {
pub fn spend(datum: Data, redeemer: OrderRedeemer, ctx: ScriptContext) -> Bool {
when redeemer is {
Cancel -> {
// We only expect the datum here, to avoid any related costs in the other branch, which doesn't need it
Expand All @@ -41,19 +41,15 @@ validator(stake_script_hash: Hash<Blake2b_224, Script>) {
)
}
Scoop -> {
// Assume that the stake_script_hash is the first withdrawal in the list
// Since the ledger doesn't reorder withdrawals (TODO: confirm this!!)
// If we ever have other withdrawals (for example, additional constraints enforced by a different order)
// then that script can look for the appropriate one, and we can just ensure this one is first in the list.
expect [head] = dict.to_list(ctx.transaction.withdrawals)
when head is {
// We match against the script hash like this to ignore the withdrawal amount
// TODO: we could make this more efficient by CBOR encoding the `Inline(ScriptCredential())` into the `stake_script_hash` parameter
// or perhaps even the whole withdrawal list to compare it all at once!
// and just doing a direct equaltiy comparison. Since this runs for each order, this could represent significant savings!
(Inline(ScriptCredential(script)), _) -> script == stake_script_hash
_ -> False
}
dict.foldl(ctx.transaction.withdrawals, False, fn(withdrawal, _amt, acc) {
when withdrawal is {
// TODO: we could make this more efficient by CBOR encoding the `Inline(ScriptCredential())` into the `stake_script_hash` parameter
// or perhaps even the whole withdrawal list to compare it all at once!
// and just doing a direct equaltiy comparison. Since this runs for each order, this could represent significant savings!
Inline(ScriptCredential(script)) -> acc || script == stake_script_hash
_ -> acc
}
})
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion validators/tests/constants.ak
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Script hashes
pub const settings_policy_id = #"00000000000000000000000000000000000000000000000000000000"
pub const pool_script_hash = #"00000000000000000000000000000000000000000000000000000001"
pub const order_script_hash = #"00000000000000000000000000000000000000000000000000000002"
pub const order_script_hash = #"00000000000000000000000000000000000000000000000000000002"
pub const stake_script_hash = #"00000000000000000000000000000000000000000000000000000003"

pub const random_hash = #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"
pub const other_hash = #"01010101010101010101010101010101010101010101010101010101"
Expand Down
86 changes: 86 additions & 0 deletions validators/tests/order.ak
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use aiken/dict.{Dict}
use aiken/interval
use aiken/transaction.{
InlineDatum, Input, Output,
ScriptContext, Spend, Transaction,
}
use aiken/transaction/credential.{
Inline, StakeCredential, ScriptCredential,
}
use aiken/transaction/value
use tests/examples/ex_shared.{
mk_output_reference, mk_tx_hash, script_address, compare_stake,
}
use order as order_validator
use tests/constants
use types/order.{Scoop}

test scoop_order_test() {
scoop_order(
dict.from_list(
[(Inline(ScriptCredential(constants.stake_script_hash)), 0)],
compare_stake,
),
)
}

test scoop_order_extra_withdrawals() {
scoop_order(
dict.from_list(
[
(Inline(ScriptCredential(constants.random_hash)), 100),
(Inline(ScriptCredential(constants.other_hash)), 500),
(Inline(ScriptCredential(constants.stake_script_hash)), 0),
],
compare_stake,
),
)
}

test scoop_order_missing_stake_script_withdrawal() fail {
scoop_order(
dict.from_list(
[
(Inline(ScriptCredential(constants.random_hash)), 100),
(Inline(ScriptCredential(constants.other_hash)), 500),
],
compare_stake,
),
)
}

fn scoop_order(withdrawals: Dict<StakeCredential, Int>) {
let order_address = script_address(constants.order_script_hash)
let order_datum = Void // Not needed by scoop
let order_redeemer = Scoop
let order_input =
Input {
output_reference: mk_output_reference(1),
output: Output {
address: order_address,
value: value.from_lovelace(2_000_000),
datum: InlineDatum(order_datum),
reference_script: None,
},
}
let ctx =
ScriptContext {
transaction: Transaction {
inputs: [order_input],
reference_inputs: [],
outputs: [],
fee: value.from_lovelace(1_000_000),
mint: value.to_minted_value(value.from_lovelace(0)),
certificates: [],
withdrawals: withdrawals,
validity_range: interval.between(1, 2),
extra_signatories: [],
redeemers: dict.new(),
datums: dict.new(),
id: mk_tx_hash(1),
},
purpose: Spend(order_input.output_reference),
}
let result = order_validator.spend(constants.stake_script_hash, order_datum, order_redeemer, ctx)
result
}
18 changes: 2 additions & 16 deletions validators/tests/pool.ak
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use shared.{
use sundae/multisig
use tests/examples/ex_settings.{mk_valid_settings_input, mk_valid_settings_datum, example_treasury_admin, example_metadata_admin, example_treasury_address, example_settings_admin}
use tests/examples/ex_shared.{
mk_output_reference, mk_tx_hash, script_address, wallet_address,
mk_output_reference, mk_tx_hash, script_address, wallet_address, compare_stake,
}
use types/order.{Deposit, Destination, Fixed, Self, OrderDatum, Swap}
use types/pool.{
Expand Down Expand Up @@ -767,20 +767,6 @@ test cannot_update_pool_fees_transaction_test() fail {
update_pool_fees_transaction(settings)
}

pub fn compare_stake(left: StakeCredential, right: StakeCredential) -> Ordering {
let left = when left is {
Inline(ScriptCredential(x)) -> x
Inline(VerificationKeyCredential(x)) -> x
_ -> fail
}
let right = when right is {
Inline(ScriptCredential(x)) -> x
Inline(VerificationKeyCredential(x)) -> x
_ -> fail
}
bytearray.compare(left, right)
}

test update_pool_fees_transaction_with_script_test() {
let settings = ScoopTestOptions {
..default_scoop_test_options(),
Expand Down Expand Up @@ -1149,4 +1135,4 @@ test attempt_evaporate_pool_test() fail {
|> builder.add_signatory(example_treasury_admin)
|> builder.spend(pool_input.output_reference)
pool_validator.spend(constants.settings_policy_id, pool_datum, withdraw_fees_redeemer, ctx)
}
}

0 comments on commit b6fbf3d

Please sign in to comment.