From fda72d5229fa2f4cd85cb72696e275e98f380470 Mon Sep 17 00:00:00 2001 From: card Date: Fri, 19 Jan 2024 05:07:19 -0500 Subject: [PATCH 01/13] SETTINGS CHANGES ABSORBED DO NOT MERGE WIP amend before push --- aiken.lock | 11 + aiken.toml | 5 + flake.lock | 200 +++++++++++++++ validators/pool.ak | 557 ++++++++++++++++++++++++++++++++++++----- validators/settings.ak | 107 +++++++- 5 files changed, 817 insertions(+), 63 deletions(-) create mode 100644 flake.lock diff --git a/aiken.lock b/aiken.lock index c86c914..1482efe 100644 --- a/aiken.lock +++ b/aiken.lock @@ -11,6 +11,11 @@ name = "SundaeSwap-finance/aicone" version = "ae0852d40cc6332437492102451cf331a3c10b0d" source = "github" +[[requirements]] +name = "aiken-extra/tx_util" +version = "1.170.202312" +source = "github" + [[packages]] name = "aiken-lang/stdlib" version = "97cd61345bcc8925c521b30d0f354859eb0148cd" @@ -23,4 +28,10 @@ version = "ae0852d40cc6332437492102451cf331a3c10b0d" requirements = [] source = "github" +[[packages]] +name = "aiken-extra/tx_util" +version = "1.170.202312" +requirements = [] +source = "github" + [etags] diff --git a/aiken.toml b/aiken.toml index 8a78860..bb019cb 100644 --- a/aiken.toml +++ b/aiken.toml @@ -17,3 +17,8 @@ source = "github" name = "SundaeSwap-finance/aicone" version = "ae0852d40cc6332437492102451cf331a3c10b0d" source = "github" + +[[dependencies]] +name = "aiken-extra/tx_util" +version = "1.170.202312" +source = "github" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..1820a55 --- /dev/null +++ b/flake.lock @@ -0,0 +1,200 @@ +{ + "nodes": { + "aiken": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1705531678, + "narHash": "sha256-jxUnn5oq6AWVCHj5qnIeFaO2w3L3+iuPv5088QmYSSA=", + "ref": "refs/heads/nix-macos-build-libs", + "rev": "328514182ed9777581aa222aca0a5431e41ef4a7", + "revCount": 2147, + "type": "git", + "url": "file:///Users/card/Dev/job/sundaeswap/aiken" + }, + "original": { + "owner": "aiken-lang", + "ref": "v1.0.20-alpha", + "repo": "aiken", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1681202837, + "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_3": { + "inputs": { + "systems": "systems_3" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1690463849, + "narHash": "sha256-n/ej009hs6+q/wxPR+OlYrNMXXv9qVK+FRyl7IyDuG0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "3173ae5d63ebd44ce99b70965557906800a1fb02", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1681358109, + "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1705883077, + "narHash": "sha256-ByzHHX3KxpU1+V0erFy8jpujTufimh6KaS/Iv3AciHk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "5f5210aa20e343b7e35f40c033000db0ef80d7b9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "aiken": "aiken", + "flake-utils": "flake-utils_3", + "nixpkgs": "nixpkgs_3" + } + }, + "rust-overlay": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1690424156, + "narHash": "sha256-Bpml+L280tHTQpwpC5/BJbU4HSvEzMvW8IZ4gAXimhE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "f335a0213504c7e6481c359dc1009be9cf34432c", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_3": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/validators/pool.ak b/validators/pool.ak index 0a1eaff..d75899d 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -11,20 +11,28 @@ use aiken/transaction.{ ScriptContext, Spend, Transaction, TransactionId, } use aiken/transaction/credential.{ - Address, ScriptCredential, VerificationKeyCredential, Inline + Address, Inline, ScriptCredential, VerificationKeyCredential, } use aiken/transaction/value.{MintedValue, PolicyId, Value, ada_policy_id} use calculation/process.{pool_input_to_state, process_orders} use calculation/shared.{PoolState} as calc_shared -use shared.{AssetClass, Ident, spent_output, pool_nft_name, pool_lp_name, has_exact_token_count, count_orders} +use shared.{ + AssetClass, Ident, count_orders, has_exact_token_count, pool_lp_name, + pool_nft_name, spent_output, +} use sundae/multisig use tests/examples/ex_settings.{mk_valid_settings_input} -use tests/examples/ex_shared.{wallet_address, script_address, mk_output_reference, mk_tx_hash} -use types/order.{Destination, OrderDatum, Swap, Deposit} +use tests/examples/ex_shared.{ + mk_output_reference, mk_tx_hash, script_address, wallet_address, +} +use types/order.{Deposit, Destination, OrderDatum, Swap} use types/pool.{ - CreatePool, MintLP, PoolDatum, PoolMintRedeemer, PoolRedeemer, PoolScoop, WithdrawFees + CreatePool, MintLP, PoolDatum, PoolMintRedeemer, PoolRedeemer, PoolScoop, + WithdrawFees, } use types/settings.{SettingsDatum, find_settings_datum} +use tx_util/builder.{add_asset_to_tx_output, add_tx_input, add_tx_output, add_tx_ref_input, build_txn_context, mint_assets, new_tx_input, new_tx_output, with_asset_of_tx_input} +use aiken/cbor /// The core / base "pooled AMM" script for the SundaeSwap v3 protocol /// @@ -215,7 +223,6 @@ validator(settings_policy_id: PolicyId) { outcome, actual_protocol_fees, ) - // Now, we check various things about the output datum to ensure they're each correct. // Check that the datum correctly records the final circulating LP, accounting for any deposits and withdrawals // In particular, this is important because that circulating supply is exaclty what determines the users ownership of assets in the pool @@ -230,16 +237,12 @@ validator(settings_policy_id: PolicyId) { datum.assets == output_datum.assets, datum.fees_per_10_thousand == output_datum.fees_per_10_thousand, datum.market_open == output_datum.market_open, - // Finally, make sure we don't change the stake credential; this can only be done when withdrawing fees, by the treasury administrator pool_input.address.stake_credential == pool_output.address.stake_credential, } } WithdrawFees { amount, treasury_output } -> { - let PoolDatum { - protocol_fees: initial_protocol_fees, - .. - } = datum + let PoolDatum { protocol_fees: initial_protocol_fees, .. } = datum // Make sure we withdraw *only* up to what we've earned // We allow less than, so that you can leave some behind for the minUTXO cost, or continuing to earn staking rewards, etc. expect amount <= initial_protocol_fees @@ -405,24 +408,32 @@ validator(settings_policy_id: PolicyId) { // Confirm that the correct funds (at least one asset A, at least one asset B, exactly 3 tokens, including the pool NFT) is correct here // Note: should we instead just compare the expected pool output? let funds_spent_to_pool = and { - coin_a_amt_sans_protocol_fees >= 1, - coin_b_amt >= 1, - list.length(value.flatten(pool_output.value)) <= 3, - value.quantity_of( - pool_output.value, - own_policy_id, - new_pool_nft_token, - ) == 1 - } - + coin_a_amt_sans_protocol_fees >= 1, + coin_b_amt >= 1, + // BUG SSW-201 + // pool with pair without ADA will have an additional token + // so we should test for exotic pairs without ada + list.length(value.flatten(pool_output.value)) <= 3, + value.quantity_of( + pool_output.value, + own_policy_id, + new_pool_nft_token, + ) == 1, + } + // BUG SSW-202: pool_output.address unchecked + // for testing, we can run the whole of has_expected_pool_value // Make sure we send the pool metadata token to the metadata admin // We use an index from the redeemer to skip to the right output, in case there are multiple outputs to the metadata admin // This is safe to do for the usual reasons: if they point at a UTXO without the ref token, the transaction will fail. expect Some(metadata_output) = list.at(ctx.transaction.outputs, metadata_output_ix) expect metadata_output.address == settings_datum.metadata_admin - expect value.quantity_of(metadata_output.value, own_policy_id, new_pool_ref_token) == 1 - + expect + value.quantity_of( + metadata_output.value, + own_policy_id, + new_pool_ref_token, + ) == 1 // And check that the datum is initialized correctly; This is part of why we have a minting policy handling this, // as it allows us to authenticate the providence of the datum. @@ -522,7 +533,6 @@ fn has_expected_pool_value( has_exact_token_count(output_value, 3), value.lovelace_of(output_value) == final_protocol_fees + quantity_a_amt, value.quantity_of(output_value, quantity_b_policy_id, quantity_b_name) == quantity_b_amt, - value.quantity_of( output_value, pool_script_hash, @@ -1056,7 +1066,15 @@ fn scoop_swap_deposit(options: ScoopTestOptions) { result } -test mint_test() { +test mint_test_two_nfts() { + False +} + +fn mint_test_modify( + modify_pool_output: fn(Value) -> Value, + modify_lp_output: fn(Value) -> Value, + modify_ref_output: fn(Value) -> Value, + modify_datum: fn(Datum) -> Datum) { let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" let pool_address = script_address(hash_of_pool_script) @@ -1065,19 +1083,434 @@ test mint_test() { let user_address = wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") let settings_input = mk_valid_settings_input([], 1) - let funds_input = Input { - output_reference: OutputReference { - transaction_id: mk_tx_hash(0), - output_index: 0 - }, - output: Output { - address: user_address, - value: value.from_lovelace(10_000_000_000) - |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000), - datum: NoDatum, - reference_script: None, - }, + let funds_input_val = + value.from_lovelace(10_000_000_000) + |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) + let funds_input = + new_tx_input( + mk_tx_hash(0).hash, + user_address, + 0, + NoDatum, + ) |> with_asset_of_tx_input(funds_input_val) + + let pool_id = + funds_input.output_reference.transaction_id.hash + |> bytearray.concat(#"23") // '#' character + |> + bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) + |> hash.blake2b_256 + |> bytearray.drop(4) + let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) + let inline_pool_datum = InlineDatum( + PoolDatum { + identifier: pool_id, + assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + circulating_lp: 1_000_000_000, + fees_per_10_thousand: (5, 5), + market_open: 0, + fee_finalized: 0, + protocol_fees: 2_000_000, + } + ) + let pool_output_val = + value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) + |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + |> value.merge(value.from_lovelace(1_002_000_000)) + |> modify_lp_output + let pool_output = new_tx_output(user_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees + |> add_asset_to_tx_output(pool_output_val) + + let lp_output_val = + value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) + |> value.merge(value.from_lovelace(2_000_000)) + |> modify_lp_output + let lp_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(lp_output_val) + + let ref_output_val = + value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) + |> value.merge(value.from_lovelace(2_000_000)) + let ref_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(ref_output_val) + + let poolMintRedeemer = CreatePool { + assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + pool_output: 0, + metadata_output: 2, } + + let ctx = interval.between(1,2) + |> build_txn_context() + |> mint_assets(hash_of_pool_script, value.to_minted_value( + value.from_lovelace(0) + |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) + |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + |> value.add(hash_of_pool_script, new_pool_ref_token, 1) + )) + |> add_tx_input(funds_input) + |> add_tx_ref_input(settings_input) + + // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] + |> add_tx_output(ref_output) + |> add_tx_output(lp_output) + |> add_tx_output(pool_output) + + // trace "Old CTX: " + // trace cbor.diagnostic(ctxOld) + // trace "New CTX: " + // trace cbor.diagnostic(ctx) + + // trace "Purpose is identical: " + // trace cbor.diagnostic(ctxOld.purpose == ctx.purpose) + // trace "Transaction is identical: " + // trace cbor.diagnostic(ctxOld.transaction == ctx.transaction) + + // trace "Transaction inputs are identical: " + // trace cbor.diagnostic(ctxOld.transaction.inputs == ctx.transaction.inputs) + // trace "Transaction reference inputs are identical: " + // trace cbor.diagnostic(ctxOld.transaction.reference_inputs == ctx.transaction.reference_inputs) + // trace "Transaction outputs are identical: " + // trace cbor.diagnostic(ctxOld.transaction.outputs == ctx.transaction.outputs) + // trace "Transaction fee is identical: " + // trace cbor.diagnostic(ctxOld.transaction.fee == ctx.transaction.fee) + // trace "Transaction mint is identical: " + // trace cbor.diagnostic(ctxOld.transaction.mint == ctx.transaction.mint) + // trace "Transaction certificates are identical: " + // trace cbor.diagnostic(ctxOld.transaction.certificates == ctx.transaction.certificates) + // trace "Transaction withdrawals are identical: " + // trace cbor.diagnostic(ctxOld.transaction.withdrawals == ctx.transaction.withdrawals) + // trace "Transaction validity range is identical: " + // trace cbor.diagnostic(ctxOld.transaction.validity_range == ctx.transaction.validity_range) + // trace "Transaction extra signatories are identical: " + // trace cbor.diagnostic(ctxOld.transaction.extra_signatories == ctx.transaction.extra_signatories) + // trace "Transaction redeemers are identical: " + // trace cbor.diagnostic(ctxOld.transaction.redeemers == ctx.transaction.redeemers) + // trace "Transaction datums are identical: " + // trace cbor.diagnostic(ctxOld.transaction.datums == ctx.transaction.datums) + // trace "Transaction id is identical: " + // trace cbor.diagnostic(ctxOld.transaction.id == ctx.transaction.id) + + // trace "Old ctx outputs: " + // trace cbor.diagnostic(ctxOld.transaction.outputs) + // trace "New ctx outputs: " + // trace cbor.diagnostic(ctx.transaction.outputs) + + // // trace "Old ctx output 0: " + // // trace cbor.diagnostic(ctxOld.transaction.outputs[0]) + // // list.indexed_map does this + // expect _logItOld = when ctxOld.transaction.outputs is { + // [output1, output2, output3] -> { + // trace "Old ctx output 0 address: " + // trace cbor.diagnostic(output1.address) + // trace "Old ctx output 0 value: " + // trace cbor.diagnostic(output1.value) + // trace "Old ctx output 0 datum: " + // trace cbor.diagnostic(output1.datum) + // trace "Old ctx output 0 reference script: " + // trace cbor.diagnostic(output1.reference_script) + // Void + // } + // _ -> { + // trace "ctxOld doesnt have exactly 3 outputs" + // Void + // } + // } + // expect _logIt = when ctx.transaction.outputs is { + // [output1, output2, output3] -> { + // trace "New ctx output 0 address: " + // trace cbor.diagnostic(output1.address) + // trace "New ctx output 0 value: " + // trace cbor.diagnostic(output1.value) + // trace "New ctx output 0 datum: " + // trace cbor.diagnostic(output1.datum) + // trace "New ctx output 0 reference script: " + // trace cbor.diagnostic(output1.reference_script) + + // trace "New ctx output 1 address: " + // trace cbor.diagnostic(output2.address) + // trace "New ctx output 1 value: " + // trace cbor.diagnostic(output2.value) + // trace "New ctx output 1 datum: " + // trace cbor.diagnostic(output2.datum) + // trace "New ctx output 1 reference script: " + // trace cbor.diagnostic(output2.reference_script) + + // trace "New ctx output 2 address: " + // trace cbor.diagnostic(output3.address) + // trace "New ctx output 2 value: " + // trace cbor.diagnostic(output3.value) + // trace "New ctx output 2 datum: " + // trace cbor.diagnostic(output3.datum) + // trace "New ctx output 2 reference script: " + // trace cbor.diagnostic(output3.reference_script) + + + // Void + // } + // _ -> { + // trace "ctx doesnt have exactly 3 outputs" + // Void + // } + // } + + + let result = mint(settings_policy_id, poolMintRedeemer, ctx) + result +} + +test mint_test() { + mint_test_modify(identity, identity, identity, identity) + // let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" + // let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" + // let pool_address = script_address(hash_of_pool_script) + // let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + // let rberry_token_name = #"524245525259" + // let user_address = + // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + // let settings_input = mk_valid_settings_input([], 1) + // let funds_input = Input { + // output_reference: OutputReference { + // transaction_id: mk_tx_hash(0), + // output_index: 0 + // }, + // output: Output { + // address: user_address, + // value: value.from_lovelace(10_000_000_000) + // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000), + // datum: NoDatum, + // reference_script: None, + // }, + // } + // let pool_id = + // funds_input.output_reference.transaction_id.hash + // |> bytearray.concat(#"23") // '#' character + // |> + // bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) + // |> hash.blake2b_256 + // |> bytearray.drop(4) + // let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) + // let pool_output = Output { + // address: pool_address, + // value: value.from_lovelace(1_002_000_000) + // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) + // |> value.add(hash_of_pool_script, new_pool_nft_token, 1), + // datum: InlineDatum(PoolDatum { + // identifier: pool_id, + // assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + // circulating_lp: 1_000_000_000, + // fees_per_10_thousand: (5, 5), + // market_open: 0, + // fee_finalized: 0, + // protocol_fees: 2_000_000, + // }), + // reference_script: None, + // } + // let lp_output = Output { + // address: user_address, + // value: value.from_lovelace(2_000_000) + // |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000), + // datum: NoDatum, + // reference_script: None, + // } + // let ref_output = Output { + // address: user_address, + // value: value.from_lovelace(2_000_000) + // |> value.add(hash_of_pool_script, new_pool_ref_token, 1), + // datum: NoDatum, + // reference_script: None, + // } + // let poolMintRedeemer = CreatePool { + // assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + // pool_output: 0, + // metadata_output: 2, + // } + // // let ctxOld = ScriptContext { + // // transaction: Transaction { + // // inputs: [funds_input], + // // reference_inputs: [settings_input], + // // outputs: [pool_output, lp_output, ref_output], + // // fee: value.from_lovelace(1_000_000), + // // mint: value.to_minted_value( + // // value.from_lovelace(0) + // // |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) + // // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + // // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) + // // ), + // // certificates: [], + // // withdrawals: dict.new(), + // // validity_range: interval.between(1, 2), + // // extra_signatories: [], + // // redeemers: dict.new(), + // // datums: dict.new(), + // // id: mk_tx_hash(1), + // // }, + // // purpose: Mint(hash_of_pool_script), + // // } + // let ctx = interval.between(1,2) + // |> build_txn_context() + // |> mint_assets(hash_of_pool_script, value.to_minted_value( + // value.from_lovelace(0) + // |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) + // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) + // )) + // |> add_tx_input(funds_input) + // |> add_tx_ref_input(settings_input) + + // // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] + // |> add_tx_output(ref_output) + // |> add_tx_output(lp_output) + // |> add_tx_output(pool_output) + + // // trace "Old CTX: " + // // trace cbor.diagnostic(ctxOld) + // // trace "New CTX: " + // // trace cbor.diagnostic(ctx) + + // // trace "Purpose is identical: " + // // trace cbor.diagnostic(ctxOld.purpose == ctx.purpose) + // // trace "Transaction is identical: " + // // trace cbor.diagnostic(ctxOld.transaction == ctx.transaction) + + // // trace "Transaction inputs are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.inputs == ctx.transaction.inputs) + // // trace "Transaction reference inputs are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.reference_inputs == ctx.transaction.reference_inputs) + // // trace "Transaction outputs are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.outputs == ctx.transaction.outputs) + // // trace "Transaction fee is identical: " + // // trace cbor.diagnostic(ctxOld.transaction.fee == ctx.transaction.fee) + // // trace "Transaction mint is identical: " + // // trace cbor.diagnostic(ctxOld.transaction.mint == ctx.transaction.mint) + // // trace "Transaction certificates are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.certificates == ctx.transaction.certificates) + // // trace "Transaction withdrawals are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.withdrawals == ctx.transaction.withdrawals) + // // trace "Transaction validity range is identical: " + // // trace cbor.diagnostic(ctxOld.transaction.validity_range == ctx.transaction.validity_range) + // // trace "Transaction extra signatories are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.extra_signatories == ctx.transaction.extra_signatories) + // // trace "Transaction redeemers are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.redeemers == ctx.transaction.redeemers) + // // trace "Transaction datums are identical: " + // // trace cbor.diagnostic(ctxOld.transaction.datums == ctx.transaction.datums) + // // trace "Transaction id is identical: " + // // trace cbor.diagnostic(ctxOld.transaction.id == ctx.transaction.id) + + // // trace "Old ctx outputs: " + // // trace cbor.diagnostic(ctxOld.transaction.outputs) + // // trace "New ctx outputs: " + // // trace cbor.diagnostic(ctx.transaction.outputs) + + // // // trace "Old ctx output 0: " + // // // trace cbor.diagnostic(ctxOld.transaction.outputs[0]) + // // // list.indexed_map does this + // // expect _logItOld = when ctxOld.transaction.outputs is { + // // [output1, output2, output3] -> { + // // trace "Old ctx output 0 address: " + // // trace cbor.diagnostic(output1.address) + // // trace "Old ctx output 0 value: " + // // trace cbor.diagnostic(output1.value) + // // trace "Old ctx output 0 datum: " + // // trace cbor.diagnostic(output1.datum) + // // trace "Old ctx output 0 reference script: " + // // trace cbor.diagnostic(output1.reference_script) + // // Void + // // } + // // _ -> { + // // trace "ctxOld doesnt have exactly 3 outputs" + // // Void + // // } + // // } + // // expect _logIt = when ctx.transaction.outputs is { + // // [output1, output2, output3] -> { + // // trace "New ctx output 0 address: " + // // trace cbor.diagnostic(output1.address) + // // trace "New ctx output 0 value: " + // // trace cbor.diagnostic(output1.value) + // // trace "New ctx output 0 datum: " + // // trace cbor.diagnostic(output1.datum) + // // trace "New ctx output 0 reference script: " + // // trace cbor.diagnostic(output1.reference_script) + + // // trace "New ctx output 1 address: " + // // trace cbor.diagnostic(output2.address) + // // trace "New ctx output 1 value: " + // // trace cbor.diagnostic(output2.value) + // // trace "New ctx output 1 datum: " + // // trace cbor.diagnostic(output2.datum) + // // trace "New ctx output 1 reference script: " + // // trace cbor.diagnostic(output2.reference_script) + + // // trace "New ctx output 2 address: " + // // trace cbor.diagnostic(output3.address) + // // trace "New ctx output 2 value: " + // // trace cbor.diagnostic(output3.value) + // // trace "New ctx output 2 datum: " + // // trace cbor.diagnostic(output3.datum) + // // trace "New ctx output 2 reference script: " + // // trace cbor.diagnostic(output3.reference_script) + + + // // Void + // // } + // // _ -> { + // // trace "ctx doesnt have exactly 3 outputs" + // // Void + // // } + // // } + + + // let result = mint(settings_policy_id, poolMintRedeemer, ctx) + // result +} + +// BUG SSW-202: pool_output.address unchecked +// for testing, we can run the whole of has_expected_pool_value + +// test create_pool_mint_address() { +// False +// } + +// BUG SSW-201 +// pool with pair without ADA will have an additional token +// so we should test for exotic pairs without ada +// Confirm that the correct funds (at least one asset A, at least one asset B, exactly 3 tokens, including the pool NFT) is correct here +// this test will fail until SSW-201 is fixed +//TODO(elaine): seems the good place to parametrize this over is +test mint_test_non_ada() { + let settings_policy_id = + #"00000000000000000000000000000000000000000000000000000000" + let hash_of_pool_script = + #"00000000000000000000000000000000000000000000000000000000" + let pool_address = script_address(hash_of_pool_script) + let rberry_policy_id = + #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let rberry_token_name = #"524245525259" + let sberry_policy_id = + #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let sberry_token_name = #"aaa245525259" + let user_address = + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + let settings_input = mk_valid_settings_input([], 1) + let funds_input = + Input { + output_reference: OutputReference { + transaction_id: mk_tx_hash(0), + output_index: 0, + }, + output: Output { + address: user_address, + value: value.from_lovelace(10_000_000_000) + |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) + |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000), + datum: NoDatum, + reference_script: None, + }, + } let pool_id = funds_input.output_reference.transaction_id.hash |> bytearray.concat(#"23") // '#' character @@ -1088,12 +1521,13 @@ test mint_test() { let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) let pool_output = Output { address: pool_address, - value: value.from_lovelace(1_002_000_000) + value: value.from_lovelace(2_000_000) |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) + |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) |> value.add(hash_of_pool_script, new_pool_nft_token, 1), datum: InlineDatum(PoolDatum { identifier: pool_id, - assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), //TODO(Elaine) refactor here circulating_lp: 1_000_000_000, fees_per_10_thousand: (5, 5), market_open: 0, @@ -1116,33 +1550,34 @@ test mint_test() { datum: NoDatum, reference_script: None, } - let poolMintRedeemer = CreatePool { - assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + let pool_mint_redeemer = CreatePool { + assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), pool_output: 0, metadata_output: 2, } - let ctx = ScriptContext { - transaction: Transaction { - inputs: [funds_input], - reference_inputs: [settings_input], - outputs: [pool_output, lp_output, ref_output], - fee: value.from_lovelace(1_000_000), - mint: value.to_minted_value( + + let ctx = interval.between(1,2) + |> build_txn_context() + |> mint_assets(hash_of_pool_script, value.to_minted_value( value.from_lovelace(0) |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) |> value.add(hash_of_pool_script, new_pool_nft_token, 1) |> value.add(hash_of_pool_script, new_pool_ref_token, 1) - ), - certificates: [], - withdrawals: dict.new(), - validity_range: interval.between(1, 2), - extra_signatories: [], - redeemers: dict.new(), - datums: dict.new(), - id: mk_tx_hash(1), - }, - purpose: Mint(hash_of_pool_script), - } - let result = mint(settings_policy_id, poolMintRedeemer, ctx) + )) + |> add_tx_input(funds_input) + |> add_tx_input(settings_input) + + |> add_tx_output(pool_output) + |> add_tx_output(lp_output) + |> add_tx_output(ref_output) + + // not necessary *as long as* we have manually constructed Outputs + // but it would be nice to not manually construct anymore + // |> add_asset_to_tx_output(hash_of_pool_script) + // |> add_asset_to_tx_output(rberry_policy_id) + // |> add_asset_to_tx_output(sberry_policy_id) + + + let result = mint(settings_policy_id, pool_mint_redeemer, ctx) result -} +} \ No newline at end of file diff --git a/validators/settings.ak b/validators/settings.ak index 80a5f2e..790d928 100644 --- a/validators/settings.ak +++ b/validators/settings.ak @@ -1,8 +1,20 @@ use aiken/list -use aiken/transaction.{InlineDatum, Mint, ScriptContext, OutputReference} +use aiken/interval +use aiken/bytearray +use aiken/transaction.{InlineDatum, NoDatum, Spend, Mint, ScriptContext, OutputReference, Transaction, TransactionId} +use aiken/transaction/credential.{VerificationKeyCredential, Address, from_script} use aiken/transaction/value use sundae/multisig use types/settings.{SettingsDatum, SettingsRedeemer, SettingsAdminUpdate, TreasuryAdminUpdate, settings_nft_name} +use tx_util/builder.{ + build_txn_context, + mint_assets, + add_tx_input, + add_tx_output, + new_tx_output, + new_tx_input, + with_asset_of_tx_input, +} use shared.{spent_output} /// The settings validator lets the settings and treasury admins update global settings for the protocol @@ -121,10 +133,101 @@ validator(protocol_boot_utxo: OutputReference) { list.any(ctx.transaction.inputs, fn(input) { input.output_reference == protocol_boot_utxo }) - and { mints_exactly_one_settings_nft, spends_protocol_boot_utxo, } } } + + +fn test_mint_settings(settings_nfts_count: Int) { + let settings_nft_name = "settings" + let settings_nft_policy = #"00" + + let settings_nft = value.to_minted_value(value.from_asset(settings_nft_policy, settings_nft_name, settings_nfts_count)) + + let settings_datum = mk_valid_settings_datum([]) // Some([]) for authorized_scoopers means no one can scoop + + let settings_output = new_tx_output( + from_script(settings_nft_policy), + 2_000_000, + InlineDatum(settings_datum) + ) + + let protocol_boot_utxo = OutputReference { transaction_id: TransactionId { hash: #"00"}, output_index: 0 } + let protocol_boot_utxo_policy = #"00" + + let protocol_boot_utxo_input = new_tx_input( + protocol_boot_utxo.transaction_id.hash, + from_script(protocol_boot_utxo_policy), + 2_000_000, + NoDatum, + ) |> with_asset_of_tx_input(value.from_asset(protocol_boot_utxo_policy, "boot utxo name", 1)) + let ctx = + interval.between(1, 2) + |> build_txn_context() + |> mint_assets(settings_nft_policy, settings_nft) + |> add_tx_input(protocol_boot_utxo_input) + |> add_tx_output(settings_output) + + + let minted = mint(protocol_boot_utxo, Void, ctx) + minted +} + +test mint_invalid_settings_multiple_nft() { + !test_mint_settings(2) +} + +test mint_valid_settings() { + test_mint_settings(1) +} + +//TODO: import? +fn mk_tx_hash(n: Int) -> TransactionId { + expect n < 256 + let h = + bytearray.push( + #"00000000000000000000000000000000000000000000000000000000000000", + n, + ) + TransactionId { hash: h } +} + +fn mk_valid_settings_datum( + scoopers: List, +) -> SettingsDatum { + SettingsDatum { + settings_admin: multisig.Signature( + #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad", + ), + metadata_admin: Address( + VerificationKeyCredential( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ), + None, + ), + treasury_admin: multisig.Signature( + #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad", + ), + treasury_address: Address( + VerificationKeyCredential( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ), + None, + ), + treasury_allowance: (1, 10), + authorized_scoopers: Some(scoopers), + authorized_staking_keys: [ + VerificationKeyCredential( + #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad" + ), + ], + base_fee: 0, + simple_fee: 2_500_000, + strategy_fee: 5_000_000, + pool_creation_fee: 0, + extensions: Void, + } +} \ No newline at end of file From 6db7093d6335ce7b103866d7e614fcb796acca1b Mon Sep 17 00:00:00 2001 From: card Date: Thu, 1 Feb 2024 15:16:28 -0500 Subject: [PATCH 02/13] mint two pool nft, aiken compiler broken --- validators/pool.ak | 390 ++++++++------------------------------------- 1 file changed, 63 insertions(+), 327 deletions(-) diff --git a/validators/pool.ak b/validators/pool.ak index d75899d..b636cd8 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -1066,43 +1066,80 @@ fn scoop_swap_deposit(options: ScoopTestOptions) { result } +// this is for test +// FIXME(Elaine) figure out how to organize test globals etc +// this is slightly annoying because aiken is picky about variable scope with tests +const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" +fn pool_test_tx_input() -> Input { + let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let rberry_token_name = #"524245525259" + + let sberry_policy_id = + #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let sberry_token_name = #"aaa245525259" + + let user_address = + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + + let funds_input_val = + value.from_lovelace(10_000_000_000) + |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) + |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) + let funds_input = + new_tx_input( + mk_tx_hash(0).hash, + user_address, + 0, + NoDatum, + ) |> with_asset_of_tx_input(funds_input_val) + funds_input +} + test mint_test_two_nfts() { - False + let pool_id = pool_ident_from_input(pool_test_tx_input()) + let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) + // if we add on another pool NFT token to the pool output, it should fail + !mint_test_modify( + value.add(_, hash_of_pool_script, new_pool_nft_token, 1), + identity, + identity, + identity + ) +} + +fn pool_ident_from_input (tx_input: Input) { + tx_input.output_reference.transaction_id.hash + |> bytearray.concat(#"23") // '#' character + |> + bytearray.concat(int_to_ident(tx_input.output_reference.output_index)) + |> hash.blake2b_256 + |> bytearray.drop(4) } fn mint_test_modify( modify_pool_output: fn(Value) -> Value, modify_lp_output: fn(Value) -> Value, modify_ref_output: fn(Value) -> Value, - modify_datum: fn(Datum) -> Datum) { + modify_datum: fn(Datum) -> Datum) -> Bool { let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" - let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" let pool_address = script_address(hash_of_pool_script) let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" let rberry_token_name = #"524245525259" let user_address = wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") let settings_input = mk_valid_settings_input([], 1) - let funds_input_val = - value.from_lovelace(10_000_000_000) - |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) - let funds_input = - new_tx_input( - mk_tx_hash(0).hash, - user_address, - 0, - NoDatum, - ) |> with_asset_of_tx_input(funds_input_val) - let pool_id = - funds_input.output_reference.transaction_id.hash - |> bytearray.concat(#"23") // '#' character - |> - bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) - |> hash.blake2b_256 - |> bytearray.drop(4) + let funds_input = pool_test_tx_input() + let pool_id = pool_ident_from_input(funds_input) + let funds_input = pool_test_tx_input() + // funds_input.output_reference.transaction_id.hash + // |> bytearray.concat(#"23") // '#' character + // |> + // bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) + // |> hash.blake2b_256 + // |> bytearray.drop(4) let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - let inline_pool_datum = InlineDatum( + let inline_pool_datum = modify_datum(InlineDatum( PoolDatum { identifier: pool_id, assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), @@ -1112,13 +1149,13 @@ fn mint_test_modify( fee_finalized: 0, protocol_fees: 2_000_000, } - ) + )) let pool_output_val = value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) |> value.add(hash_of_pool_script, new_pool_nft_token, 1) |> value.merge(value.from_lovelace(1_002_000_000)) - |> modify_lp_output - let pool_output = new_tx_output(user_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees + |> modify_pool_output + let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees |> add_asset_to_tx_output(pool_output_val) let lp_output_val = @@ -1132,6 +1169,7 @@ fn mint_test_modify( let ref_output_val = value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) |> value.merge(value.from_lovelace(2_000_000)) + |> modify_ref_output let ref_output = new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added |> add_asset_to_tx_output(ref_output_val) @@ -1158,314 +1196,12 @@ fn mint_test_modify( |> add_tx_output(lp_output) |> add_tx_output(pool_output) - // trace "Old CTX: " - // trace cbor.diagnostic(ctxOld) - // trace "New CTX: " - // trace cbor.diagnostic(ctx) - - // trace "Purpose is identical: " - // trace cbor.diagnostic(ctxOld.purpose == ctx.purpose) - // trace "Transaction is identical: " - // trace cbor.diagnostic(ctxOld.transaction == ctx.transaction) - - // trace "Transaction inputs are identical: " - // trace cbor.diagnostic(ctxOld.transaction.inputs == ctx.transaction.inputs) - // trace "Transaction reference inputs are identical: " - // trace cbor.diagnostic(ctxOld.transaction.reference_inputs == ctx.transaction.reference_inputs) - // trace "Transaction outputs are identical: " - // trace cbor.diagnostic(ctxOld.transaction.outputs == ctx.transaction.outputs) - // trace "Transaction fee is identical: " - // trace cbor.diagnostic(ctxOld.transaction.fee == ctx.transaction.fee) - // trace "Transaction mint is identical: " - // trace cbor.diagnostic(ctxOld.transaction.mint == ctx.transaction.mint) - // trace "Transaction certificates are identical: " - // trace cbor.diagnostic(ctxOld.transaction.certificates == ctx.transaction.certificates) - // trace "Transaction withdrawals are identical: " - // trace cbor.diagnostic(ctxOld.transaction.withdrawals == ctx.transaction.withdrawals) - // trace "Transaction validity range is identical: " - // trace cbor.diagnostic(ctxOld.transaction.validity_range == ctx.transaction.validity_range) - // trace "Transaction extra signatories are identical: " - // trace cbor.diagnostic(ctxOld.transaction.extra_signatories == ctx.transaction.extra_signatories) - // trace "Transaction redeemers are identical: " - // trace cbor.diagnostic(ctxOld.transaction.redeemers == ctx.transaction.redeemers) - // trace "Transaction datums are identical: " - // trace cbor.diagnostic(ctxOld.transaction.datums == ctx.transaction.datums) - // trace "Transaction id is identical: " - // trace cbor.diagnostic(ctxOld.transaction.id == ctx.transaction.id) - - // trace "Old ctx outputs: " - // trace cbor.diagnostic(ctxOld.transaction.outputs) - // trace "New ctx outputs: " - // trace cbor.diagnostic(ctx.transaction.outputs) - - // // trace "Old ctx output 0: " - // // trace cbor.diagnostic(ctxOld.transaction.outputs[0]) - // // list.indexed_map does this - // expect _logItOld = when ctxOld.transaction.outputs is { - // [output1, output2, output3] -> { - // trace "Old ctx output 0 address: " - // trace cbor.diagnostic(output1.address) - // trace "Old ctx output 0 value: " - // trace cbor.diagnostic(output1.value) - // trace "Old ctx output 0 datum: " - // trace cbor.diagnostic(output1.datum) - // trace "Old ctx output 0 reference script: " - // trace cbor.diagnostic(output1.reference_script) - // Void - // } - // _ -> { - // trace "ctxOld doesnt have exactly 3 outputs" - // Void - // } - // } - // expect _logIt = when ctx.transaction.outputs is { - // [output1, output2, output3] -> { - // trace "New ctx output 0 address: " - // trace cbor.diagnostic(output1.address) - // trace "New ctx output 0 value: " - // trace cbor.diagnostic(output1.value) - // trace "New ctx output 0 datum: " - // trace cbor.diagnostic(output1.datum) - // trace "New ctx output 0 reference script: " - // trace cbor.diagnostic(output1.reference_script) - - // trace "New ctx output 1 address: " - // trace cbor.diagnostic(output2.address) - // trace "New ctx output 1 value: " - // trace cbor.diagnostic(output2.value) - // trace "New ctx output 1 datum: " - // trace cbor.diagnostic(output2.datum) - // trace "New ctx output 1 reference script: " - // trace cbor.diagnostic(output2.reference_script) - - // trace "New ctx output 2 address: " - // trace cbor.diagnostic(output3.address) - // trace "New ctx output 2 value: " - // trace cbor.diagnostic(output3.value) - // trace "New ctx output 2 datum: " - // trace cbor.diagnostic(output3.datum) - // trace "New ctx output 2 reference script: " - // trace cbor.diagnostic(output3.reference_script) - - - // Void - // } - // _ -> { - // trace "ctx doesnt have exactly 3 outputs" - // Void - // } - // } - - let result = mint(settings_policy_id, poolMintRedeemer, ctx) result } test mint_test() { mint_test_modify(identity, identity, identity, identity) - // let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" - // let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" - // let pool_address = script_address(hash_of_pool_script) - // let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - // let rberry_token_name = #"524245525259" - // let user_address = - // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - // let settings_input = mk_valid_settings_input([], 1) - // let funds_input = Input { - // output_reference: OutputReference { - // transaction_id: mk_tx_hash(0), - // output_index: 0 - // }, - // output: Output { - // address: user_address, - // value: value.from_lovelace(10_000_000_000) - // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000), - // datum: NoDatum, - // reference_script: None, - // }, - // } - // let pool_id = - // funds_input.output_reference.transaction_id.hash - // |> bytearray.concat(#"23") // '#' character - // |> - // bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) - // |> hash.blake2b_256 - // |> bytearray.drop(4) - // let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - // let pool_output = Output { - // address: pool_address, - // value: value.from_lovelace(1_002_000_000) - // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) - // |> value.add(hash_of_pool_script, new_pool_nft_token, 1), - // datum: InlineDatum(PoolDatum { - // identifier: pool_id, - // assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), - // circulating_lp: 1_000_000_000, - // fees_per_10_thousand: (5, 5), - // market_open: 0, - // fee_finalized: 0, - // protocol_fees: 2_000_000, - // }), - // reference_script: None, - // } - // let lp_output = Output { - // address: user_address, - // value: value.from_lovelace(2_000_000) - // |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000), - // datum: NoDatum, - // reference_script: None, - // } - // let ref_output = Output { - // address: user_address, - // value: value.from_lovelace(2_000_000) - // |> value.add(hash_of_pool_script, new_pool_ref_token, 1), - // datum: NoDatum, - // reference_script: None, - // } - // let poolMintRedeemer = CreatePool { - // assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), - // pool_output: 0, - // metadata_output: 2, - // } - // // let ctxOld = ScriptContext { - // // transaction: Transaction { - // // inputs: [funds_input], - // // reference_inputs: [settings_input], - // // outputs: [pool_output, lp_output, ref_output], - // // fee: value.from_lovelace(1_000_000), - // // mint: value.to_minted_value( - // // value.from_lovelace(0) - // // |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) - // // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - // // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) - // // ), - // // certificates: [], - // // withdrawals: dict.new(), - // // validity_range: interval.between(1, 2), - // // extra_signatories: [], - // // redeemers: dict.new(), - // // datums: dict.new(), - // // id: mk_tx_hash(1), - // // }, - // // purpose: Mint(hash_of_pool_script), - // // } - // let ctx = interval.between(1,2) - // |> build_txn_context() - // |> mint_assets(hash_of_pool_script, value.to_minted_value( - // value.from_lovelace(0) - // |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) - // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) - // )) - // |> add_tx_input(funds_input) - // |> add_tx_ref_input(settings_input) - - // // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] - // |> add_tx_output(ref_output) - // |> add_tx_output(lp_output) - // |> add_tx_output(pool_output) - - // // trace "Old CTX: " - // // trace cbor.diagnostic(ctxOld) - // // trace "New CTX: " - // // trace cbor.diagnostic(ctx) - - // // trace "Purpose is identical: " - // // trace cbor.diagnostic(ctxOld.purpose == ctx.purpose) - // // trace "Transaction is identical: " - // // trace cbor.diagnostic(ctxOld.transaction == ctx.transaction) - - // // trace "Transaction inputs are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.inputs == ctx.transaction.inputs) - // // trace "Transaction reference inputs are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.reference_inputs == ctx.transaction.reference_inputs) - // // trace "Transaction outputs are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.outputs == ctx.transaction.outputs) - // // trace "Transaction fee is identical: " - // // trace cbor.diagnostic(ctxOld.transaction.fee == ctx.transaction.fee) - // // trace "Transaction mint is identical: " - // // trace cbor.diagnostic(ctxOld.transaction.mint == ctx.transaction.mint) - // // trace "Transaction certificates are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.certificates == ctx.transaction.certificates) - // // trace "Transaction withdrawals are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.withdrawals == ctx.transaction.withdrawals) - // // trace "Transaction validity range is identical: " - // // trace cbor.diagnostic(ctxOld.transaction.validity_range == ctx.transaction.validity_range) - // // trace "Transaction extra signatories are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.extra_signatories == ctx.transaction.extra_signatories) - // // trace "Transaction redeemers are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.redeemers == ctx.transaction.redeemers) - // // trace "Transaction datums are identical: " - // // trace cbor.diagnostic(ctxOld.transaction.datums == ctx.transaction.datums) - // // trace "Transaction id is identical: " - // // trace cbor.diagnostic(ctxOld.transaction.id == ctx.transaction.id) - - // // trace "Old ctx outputs: " - // // trace cbor.diagnostic(ctxOld.transaction.outputs) - // // trace "New ctx outputs: " - // // trace cbor.diagnostic(ctx.transaction.outputs) - - // // // trace "Old ctx output 0: " - // // // trace cbor.diagnostic(ctxOld.transaction.outputs[0]) - // // // list.indexed_map does this - // // expect _logItOld = when ctxOld.transaction.outputs is { - // // [output1, output2, output3] -> { - // // trace "Old ctx output 0 address: " - // // trace cbor.diagnostic(output1.address) - // // trace "Old ctx output 0 value: " - // // trace cbor.diagnostic(output1.value) - // // trace "Old ctx output 0 datum: " - // // trace cbor.diagnostic(output1.datum) - // // trace "Old ctx output 0 reference script: " - // // trace cbor.diagnostic(output1.reference_script) - // // Void - // // } - // // _ -> { - // // trace "ctxOld doesnt have exactly 3 outputs" - // // Void - // // } - // // } - // // expect _logIt = when ctx.transaction.outputs is { - // // [output1, output2, output3] -> { - // // trace "New ctx output 0 address: " - // // trace cbor.diagnostic(output1.address) - // // trace "New ctx output 0 value: " - // // trace cbor.diagnostic(output1.value) - // // trace "New ctx output 0 datum: " - // // trace cbor.diagnostic(output1.datum) - // // trace "New ctx output 0 reference script: " - // // trace cbor.diagnostic(output1.reference_script) - - // // trace "New ctx output 1 address: " - // // trace cbor.diagnostic(output2.address) - // // trace "New ctx output 1 value: " - // // trace cbor.diagnostic(output2.value) - // // trace "New ctx output 1 datum: " - // // trace cbor.diagnostic(output2.datum) - // // trace "New ctx output 1 reference script: " - // // trace cbor.diagnostic(output2.reference_script) - - // // trace "New ctx output 2 address: " - // // trace cbor.diagnostic(output3.address) - // // trace "New ctx output 2 value: " - // // trace cbor.diagnostic(output3.value) - // // trace "New ctx output 2 datum: " - // // trace cbor.diagnostic(output3.datum) - // // trace "New ctx output 2 reference script: " - // // trace cbor.diagnostic(output3.reference_script) - - - // // Void - // // } - // // _ -> { - // // trace "ctx doesnt have exactly 3 outputs" - // // Void - // // } - // // } - - - // let result = mint(settings_policy_id, poolMintRedeemer, ctx) - // result } // BUG SSW-202: pool_output.address unchecked @@ -1562,7 +1298,7 @@ test mint_test_non_ada() { value.from_lovelace(0) |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - |> value.add(hash_of_pool_script, new_pool_ref_token, 1) + // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) )) |> add_tx_input(funds_input) |> add_tx_input(settings_input) From 0a683fd8d3c31f28214393ab772744d606b05c7b Mon Sep 17 00:00:00 2001 From: card Date: Thu, 1 Feb 2024 15:33:18 -0500 Subject: [PATCH 03/13] bisected: __INCORRECT_BOOLEAN error caused by let funds_input = pool_test_tx_input() --- validators/pool.ak | 361 +++++++++++++++++++++++---------------------- 1 file changed, 188 insertions(+), 173 deletions(-) diff --git a/validators/pool.ak b/validators/pool.ak index b636cd8..bc7cf06 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -1069,45 +1069,53 @@ fn scoop_swap_deposit(options: ScoopTestOptions) { // this is for test // FIXME(Elaine) figure out how to organize test globals etc // this is slightly annoying because aiken is picky about variable scope with tests -const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" -fn pool_test_tx_input() -> Input { - let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let rberry_token_name = #"524245525259" +// const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" +// fn pool_test_tx_input() -> Input { +// // let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" +// // let rberry_token_name = #"524245525259" - let sberry_policy_id = - #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let sberry_token_name = #"aaa245525259" +// // let sberry_policy_id = +// // #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" +// // let sberry_token_name = #"aaa245525259" - let user_address = - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") +// // let user_address = +// // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - let funds_input_val = - value.from_lovelace(10_000_000_000) - |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) - |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) - let funds_input = - new_tx_input( - mk_tx_hash(0).hash, - user_address, - 0, - NoDatum, - ) |> with_asset_of_tx_input(funds_input_val) - funds_input -} +// // let funds_input_val = +// // value.from_lovelace(10_000_000_000) +// // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) +// // |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) +// // let funds_input = +// // new_tx_input( +// // mk_tx_hash(0).hash, +// // user_address, +// // 0, +// // NoDatum, +// // ) |> with_asset_of_tx_input(funds_input_val) +// let funds_input = +// new_tx_input( +// mk_tx_hash(0).hash, +// wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"), +// 0, +// NoDatum, +// ) +// funds_input +// } test mint_test_two_nfts() { - let pool_id = pool_ident_from_input(pool_test_tx_input()) - let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) - // if we add on another pool NFT token to the pool output, it should fail - !mint_test_modify( - value.add(_, hash_of_pool_script, new_pool_nft_token, 1), - identity, - identity, - identity - ) + // let pool_id = pool_ident_from_input(pool_test_tx_input()) + // let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) + // // if we add on another pool NFT token to the pool output, it should fail + // !mint_test_modify( + // value.add(_, hash_of_pool_script, new_pool_nft_token, 1), + // identity, + // identity, + // identity + // ) + True } -fn pool_ident_from_input (tx_input: Input) { +fn pool_ident_from_input (tx_input: Input) -> ByteArray { tx_input.output_reference.transaction_id.hash |> bytearray.concat(#"23") // '#' character |> @@ -1122,16 +1130,22 @@ fn mint_test_modify( modify_ref_output: fn(Value) -> Value, modify_datum: fn(Datum) -> Datum) -> Bool { let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" - let pool_address = script_address(hash_of_pool_script) + let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" + // let pool_address = script_address(hash_of_pool_script) let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" let rberry_token_name = #"524245525259" - let user_address = - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - let settings_input = mk_valid_settings_input([], 1) + // let user_address = + // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + // let settings_input = mk_valid_settings_input([], 1) - let funds_input = pool_test_tx_input() + let funds_input = + new_tx_input( + mk_tx_hash(0).hash, + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"), + 0, + NoDatum, + ) let pool_id = pool_ident_from_input(funds_input) - let funds_input = pool_test_tx_input() // funds_input.output_reference.transaction_id.hash // |> bytearray.concat(#"23") // '#' character // |> @@ -1139,40 +1153,40 @@ fn mint_test_modify( // |> hash.blake2b_256 // |> bytearray.drop(4) let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - let inline_pool_datum = modify_datum(InlineDatum( - PoolDatum { - identifier: pool_id, - assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), - circulating_lp: 1_000_000_000, - fees_per_10_thousand: (5, 5), - market_open: 0, - fee_finalized: 0, - protocol_fees: 2_000_000, - } - )) - let pool_output_val = - value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) - |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - |> value.merge(value.from_lovelace(1_002_000_000)) - |> modify_pool_output - let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees - |> add_asset_to_tx_output(pool_output_val) - - let lp_output_val = - value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) - |> value.merge(value.from_lovelace(2_000_000)) - |> modify_lp_output - let lp_output = - new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - |> add_asset_to_tx_output(lp_output_val) - - let ref_output_val = - value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) - |> value.merge(value.from_lovelace(2_000_000)) - |> modify_ref_output - let ref_output = - new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - |> add_asset_to_tx_output(ref_output_val) + // let inline_pool_datum = modify_datum(InlineDatum( + // PoolDatum { + // identifier: pool_id, + // assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + // circulating_lp: 1_000_000_000, + // fees_per_10_thousand: (5, 5), + // market_open: 0, + // fee_finalized: 0, + // protocol_fees: 2_000_000, + // } + // )) + // let pool_output_val = + // value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) + // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + // |> value.merge(value.from_lovelace(1_002_000_000)) + // |> modify_pool_output + // let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees + // |> add_asset_to_tx_output(pool_output_val) + + // let lp_output_val = + // value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) + // |> value.merge(value.from_lovelace(2_000_000)) + // |> modify_lp_output + // let lp_output = + // new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + // |> add_asset_to_tx_output(lp_output_val) + + // let ref_output_val = + // value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) + // |> value.merge(value.from_lovelace(2_000_000)) + // |> modify_ref_output + // let ref_output = + // new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + // |> add_asset_to_tx_output(ref_output_val) let poolMintRedeemer = CreatePool { assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), @@ -1188,13 +1202,13 @@ fn mint_test_modify( |> value.add(hash_of_pool_script, new_pool_nft_token, 1) |> value.add(hash_of_pool_script, new_pool_ref_token, 1) )) - |> add_tx_input(funds_input) - |> add_tx_ref_input(settings_input) + // |> add_tx_input(funds_input) + // |> add_tx_ref_input(settings_input) // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] - |> add_tx_output(ref_output) - |> add_tx_output(lp_output) - |> add_tx_output(pool_output) + // |> add_tx_output(ref_output) + // |> add_tx_output(lp_output) + // |> add_tx_output(pool_output) let result = mint(settings_policy_id, poolMintRedeemer, ctx) result @@ -1218,102 +1232,103 @@ test mint_test() { // this test will fail until SSW-201 is fixed //TODO(elaine): seems the good place to parametrize this over is test mint_test_non_ada() { - let settings_policy_id = - #"00000000000000000000000000000000000000000000000000000000" - let hash_of_pool_script = - #"00000000000000000000000000000000000000000000000000000000" - let pool_address = script_address(hash_of_pool_script) - let rberry_policy_id = - #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let rberry_token_name = #"524245525259" - let sberry_policy_id = - #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let sberry_token_name = #"aaa245525259" - let user_address = - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - let settings_input = mk_valid_settings_input([], 1) - let funds_input = - Input { - output_reference: OutputReference { - transaction_id: mk_tx_hash(0), - output_index: 0, - }, - output: Output { - address: user_address, - value: value.from_lovelace(10_000_000_000) - |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) - |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000), - datum: NoDatum, - reference_script: None, - }, - } - let pool_id = - funds_input.output_reference.transaction_id.hash - |> bytearray.concat(#"23") // '#' character - |> - bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) - |> hash.blake2b_256 - |> bytearray.drop(4) - let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - let pool_output = Output { - address: pool_address, - value: value.from_lovelace(2_000_000) - |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) - |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) - |> value.add(hash_of_pool_script, new_pool_nft_token, 1), - datum: InlineDatum(PoolDatum { - identifier: pool_id, - assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), //TODO(Elaine) refactor here - circulating_lp: 1_000_000_000, - fees_per_10_thousand: (5, 5), - market_open: 0, - fee_finalized: 0, - protocol_fees: 2_000_000, - }), - reference_script: None, - } - let lp_output = Output { - address: user_address, - value: value.from_lovelace(2_000_000) - |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000), - datum: NoDatum, - reference_script: None, - } - let ref_output = Output { - address: user_address, - value: value.from_lovelace(2_000_000) - |> value.add(hash_of_pool_script, new_pool_ref_token, 1), - datum: NoDatum, - reference_script: None, - } - let pool_mint_redeemer = CreatePool { - assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), - pool_output: 0, - metadata_output: 2, - } - - let ctx = interval.between(1,2) - |> build_txn_context() - |> mint_assets(hash_of_pool_script, value.to_minted_value( - value.from_lovelace(0) - |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) - |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) - )) - |> add_tx_input(funds_input) - |> add_tx_input(settings_input) - - |> add_tx_output(pool_output) - |> add_tx_output(lp_output) - |> add_tx_output(ref_output) - - // not necessary *as long as* we have manually constructed Outputs - // but it would be nice to not manually construct anymore - // |> add_asset_to_tx_output(hash_of_pool_script) - // |> add_asset_to_tx_output(rberry_policy_id) - // |> add_asset_to_tx_output(sberry_policy_id) + True + // let settings_policy_id = + // #"00000000000000000000000000000000000000000000000000000000" + // let hash_of_pool_script = + // #"00000000000000000000000000000000000000000000000000000000" + // let pool_address = script_address(hash_of_pool_script) + // let rberry_policy_id = + // #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + // let rberry_token_name = #"524245525259" + // let sberry_policy_id = + // #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + // let sberry_token_name = #"aaa245525259" + // let user_address = + // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + // let settings_input = mk_valid_settings_input([], 1) + // let funds_input = + // Input { + // output_reference: OutputReference { + // transaction_id: mk_tx_hash(0), + // output_index: 0, + // }, + // output: Output { + // address: user_address, + // value: value.from_lovelace(10_000_000_000) + // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) + // |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000), + // datum: NoDatum, + // reference_script: None, + // }, + // } + // let pool_id = + // funds_input.output_reference.transaction_id.hash + // |> bytearray.concat(#"23") // '#' character + // |> + // bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) + // |> hash.blake2b_256 + // |> bytearray.drop(4) + // let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) + // let pool_output = Output { + // address: pool_address, + // value: value.from_lovelace(2_000_000) + // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) + // |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) + // |> value.add(hash_of_pool_script, new_pool_nft_token, 1), + // datum: InlineDatum(PoolDatum { + // identifier: pool_id, + // assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), //TODO(Elaine) refactor here + // circulating_lp: 1_000_000_000, + // fees_per_10_thousand: (5, 5), + // market_open: 0, + // fee_finalized: 0, + // protocol_fees: 2_000_000, + // }), + // reference_script: None, + // } + // let lp_output = Output { + // address: user_address, + // value: value.from_lovelace(2_000_000) + // |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000), + // datum: NoDatum, + // reference_script: None, + // } + // let ref_output = Output { + // address: user_address, + // value: value.from_lovelace(2_000_000) + // |> value.add(hash_of_pool_script, new_pool_ref_token, 1), + // datum: NoDatum, + // reference_script: None, + // } + // let pool_mint_redeemer = CreatePool { + // assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), + // pool_output: 0, + // metadata_output: 2, + // } + + // let ctx = interval.between(1,2) + // |> build_txn_context() + // |> mint_assets(hash_of_pool_script, value.to_minted_value( + // value.from_lovelace(0) + // |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) + // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + // // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) + // )) + // |> add_tx_input(funds_input) + // |> add_tx_input(settings_input) + + // |> add_tx_output(pool_output) + // |> add_tx_output(lp_output) + // |> add_tx_output(ref_output) + + // // not necessary *as long as* we have manually constructed Outputs + // // but it would be nice to not manually construct anymore + // // |> add_asset_to_tx_output(hash_of_pool_script) + // // |> add_asset_to_tx_output(rberry_policy_id) + // // |> add_asset_to_tx_output(sberry_policy_id) - let result = mint(settings_policy_id, pool_mint_redeemer, ctx) - result + // let result = mint(settings_policy_id, pool_mint_redeemer, ctx) + // result } \ No newline at end of file From d0d9aefed59890ffb17608371eeef165eb25f877 Mon Sep 17 00:00:00 2001 From: card Date: Thu, 1 Feb 2024 17:59:59 -0500 Subject: [PATCH 04/13] flake.nix for elaine machine --- flake.nix | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 flake.nix diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..9fc1e85 --- /dev/null +++ b/flake.nix @@ -0,0 +1,25 @@ +{ + description = "A description of your project"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + aiken.url = "github:aiken-lang/aiken/v1.0.20-alpha"; + # wont work on macos till they cut a release with the coreservices stuff + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, aiken, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ aiken.outputs.overlays.${system}.default ]; + }; + in + { + devShell = pkgs.mkShell { + buildInputs = [ pkgs.aiken ]; + }; + } + ); +} From d938350a2765c5b118e4a4d70e9b1378876bdfb0 Mon Sep 17 00:00:00 2001 From: rrruko Date: Thu, 1 Feb 2024 15:28:00 -0800 Subject: [PATCH 05/13] add test characterizing pool mint token count bug Co-authored-by: card --- validators/pool.ak | 335 +++++++++++++++++++-------------------------- 1 file changed, 139 insertions(+), 196 deletions(-) diff --git a/validators/pool.ak b/validators/pool.ak index bc7cf06..333a8cc 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -1069,39 +1069,18 @@ fn scoop_swap_deposit(options: ScoopTestOptions) { // this is for test // FIXME(Elaine) figure out how to organize test globals etc // this is slightly annoying because aiken is picky about variable scope with tests -// const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" -// fn pool_test_tx_input() -> Input { -// // let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" -// // let rberry_token_name = #"524245525259" - -// // let sberry_policy_id = -// // #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" -// // let sberry_token_name = #"aaa245525259" - -// // let user_address = -// // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - -// // let funds_input_val = -// // value.from_lovelace(10_000_000_000) -// // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) -// // |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) -// // let funds_input = -// // new_tx_input( -// // mk_tx_hash(0).hash, -// // user_address, -// // 0, -// // NoDatum, -// // ) |> with_asset_of_tx_input(funds_input_val) -// let funds_input = -// new_tx_input( -// mk_tx_hash(0).hash, -// wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"), -// 0, -// NoDatum, -// ) -// funds_input -// } - +const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" +fn pool_test_tx_input() -> Input { + let funds_input = + new_tx_input( + mk_tx_hash(0).hash, + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"), + 0, + NoDatum, + ) + funds_input +} + test mint_test_two_nfts() { // let pool_id = pool_ident_from_input(pool_test_tx_input()) // let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) @@ -1131,62 +1110,50 @@ fn mint_test_modify( modify_datum: fn(Datum) -> Datum) -> Bool { let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" - // let pool_address = script_address(hash_of_pool_script) + let pool_address = script_address(hash_of_pool_script) let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" let rberry_token_name = #"524245525259" - // let user_address = - // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - // let settings_input = mk_valid_settings_input([], 1) + let user_address = + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + let settings_input = mk_valid_settings_input([], 1) - let funds_input = - new_tx_input( - mk_tx_hash(0).hash, - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"), - 0, - NoDatum, - ) + let funds_input = pool_test_tx_input() let pool_id = pool_ident_from_input(funds_input) - // funds_input.output_reference.transaction_id.hash - // |> bytearray.concat(#"23") // '#' character - // |> - // bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) - // |> hash.blake2b_256 - // |> bytearray.drop(4) let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - // let inline_pool_datum = modify_datum(InlineDatum( - // PoolDatum { - // identifier: pool_id, - // assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), - // circulating_lp: 1_000_000_000, - // fees_per_10_thousand: (5, 5), - // market_open: 0, - // fee_finalized: 0, - // protocol_fees: 2_000_000, - // } - // )) - // let pool_output_val = - // value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) - // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - // |> value.merge(value.from_lovelace(1_002_000_000)) - // |> modify_pool_output - // let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees - // |> add_asset_to_tx_output(pool_output_val) - - // let lp_output_val = - // value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) - // |> value.merge(value.from_lovelace(2_000_000)) - // |> modify_lp_output - // let lp_output = - // new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - // |> add_asset_to_tx_output(lp_output_val) - - // let ref_output_val = - // value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) - // |> value.merge(value.from_lovelace(2_000_000)) - // |> modify_ref_output - // let ref_output = - // new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - // |> add_asset_to_tx_output(ref_output_val) + let inline_pool_datum = modify_datum(InlineDatum( + PoolDatum { + identifier: pool_id, + assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + circulating_lp: 1_000_000_000, + fees_per_10_thousand: (5, 5), + market_open: 0, + fee_finalized: 0, + protocol_fees: 2_000_000, + } + )) + let pool_output_val = + value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) + |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + |> value.merge(value.from_lovelace(1_002_000_000)) + |> modify_pool_output + let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees + |> add_asset_to_tx_output(pool_output_val) + + let lp_output_val = + value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) + |> value.merge(value.from_lovelace(2_000_000)) + |> modify_lp_output + let lp_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(lp_output_val) + + let ref_output_val = + value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) + |> value.merge(value.from_lovelace(2_000_000)) + |> modify_ref_output + let ref_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(ref_output_val) let poolMintRedeemer = CreatePool { assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), @@ -1202,13 +1169,13 @@ fn mint_test_modify( |> value.add(hash_of_pool_script, new_pool_nft_token, 1) |> value.add(hash_of_pool_script, new_pool_ref_token, 1) )) - // |> add_tx_input(funds_input) - // |> add_tx_ref_input(settings_input) + |> add_tx_input(funds_input) + |> add_tx_ref_input(settings_input) // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] - // |> add_tx_output(ref_output) - // |> add_tx_output(lp_output) - // |> add_tx_output(pool_output) + |> add_tx_output(ref_output) + |> add_tx_output(lp_output) + |> add_tx_output(pool_output) let result = mint(settings_policy_id, poolMintRedeemer, ctx) result @@ -1218,117 +1185,93 @@ test mint_test() { mint_test_modify(identity, identity, identity, identity) } -// BUG SSW-202: pool_output.address unchecked +// BUG SSW-202: pool_output.address unchecked // for testing, we can run the whole of has_expected_pool_value // test create_pool_mint_address() { // False // } -// BUG SSW-201 -// pool with pair without ADA will have an additional token -// so we should test for exotic pairs without ada -// Confirm that the correct funds (at least one asset A, at least one asset B, exactly 3 tokens, including the pool NFT) is correct here -// this test will fail until SSW-201 is fixed -//TODO(elaine): seems the good place to parametrize this over is test mint_test_non_ada() { - True - // let settings_policy_id = - // #"00000000000000000000000000000000000000000000000000000000" - // let hash_of_pool_script = - // #"00000000000000000000000000000000000000000000000000000000" - // let pool_address = script_address(hash_of_pool_script) - // let rberry_policy_id = - // #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - // let rberry_token_name = #"524245525259" - // let sberry_policy_id = - // #"aaaaa3a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - // let sberry_token_name = #"aaa245525259" - // let user_address = - // wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - // let settings_input = mk_valid_settings_input([], 1) - // let funds_input = - // Input { - // output_reference: OutputReference { - // transaction_id: mk_tx_hash(0), - // output_index: 0, - // }, - // output: Output { - // address: user_address, - // value: value.from_lovelace(10_000_000_000) - // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) - // |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000), - // datum: NoDatum, - // reference_script: None, - // }, - // } - // let pool_id = - // funds_input.output_reference.transaction_id.hash - // |> bytearray.concat(#"23") // '#' character - // |> - // bytearray.concat(int_to_ident(funds_input.output_reference.output_index)) - // |> hash.blake2b_256 - // |> bytearray.drop(4) - // let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - // let pool_output = Output { - // address: pool_address, - // value: value.from_lovelace(2_000_000) - // |> value.add(rberry_policy_id, rberry_token_name, 1_000_000_000) - // |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) - // |> value.add(hash_of_pool_script, new_pool_nft_token, 1), - // datum: InlineDatum(PoolDatum { - // identifier: pool_id, - // assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), //TODO(Elaine) refactor here - // circulating_lp: 1_000_000_000, - // fees_per_10_thousand: (5, 5), - // market_open: 0, - // fee_finalized: 0, - // protocol_fees: 2_000_000, - // }), - // reference_script: None, - // } - // let lp_output = Output { - // address: user_address, - // value: value.from_lovelace(2_000_000) - // |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000), - // datum: NoDatum, - // reference_script: None, - // } - // let ref_output = Output { - // address: user_address, - // value: value.from_lovelace(2_000_000) - // |> value.add(hash_of_pool_script, new_pool_ref_token, 1), - // datum: NoDatum, - // reference_script: None, - // } - // let pool_mint_redeemer = CreatePool { - // assets: ((sberry_policy_id, sberry_token_name), (rberry_policy_id, rberry_token_name)), - // pool_output: 0, - // metadata_output: 2, - // } - - // let ctx = interval.between(1,2) - // |> build_txn_context() - // |> mint_assets(hash_of_pool_script, value.to_minted_value( - // value.from_lovelace(0) - // |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) - // |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - // // |> value.add(hash_of_pool_script, new_pool_ref_token, 1) - // )) - // |> add_tx_input(funds_input) - // |> add_tx_input(settings_input) - - // |> add_tx_output(pool_output) - // |> add_tx_output(lp_output) - // |> add_tx_output(ref_output) - - // // not necessary *as long as* we have manually constructed Outputs - // // but it would be nice to not manually construct anymore - // // |> add_asset_to_tx_output(hash_of_pool_script) - // // |> add_asset_to_tx_output(rberry_policy_id) - // // |> add_asset_to_tx_output(sberry_policy_id) - - - // let result = mint(settings_policy_id, pool_mint_redeemer, ctx) - // result -} \ No newline at end of file + let settings_policy_id = + #"00000000000000000000000000000000000000000000000000000000" + let hash_of_pool_script = + #"00000000000000000000000000000000000000000000000000000000" + let pool_address = script_address(hash_of_pool_script) + let rberry_policy_id = + #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let rberry_token_name = #"524245525259" + let sberry_policy_id = + #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let sberry_token_name = #"534245525259" + + let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" + let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" + let pool_address = script_address(hash_of_pool_script) + let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let rberry_token_name = #"524245525259" + let user_address = + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + let settings_input = mk_valid_settings_input([], 1) + + let funds_input = pool_test_tx_input() + let pool_id = pool_ident_from_input(funds_input) + let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) + let inline_pool_datum = InlineDatum( + PoolDatum { + identifier: pool_id, + assets: ((rberry_policy_id, rberry_token_name), (sberry_policy_id, sberry_token_name)), + circulating_lp: 1_000_000_000, + fees_per_10_thousand: (5, 5), + market_open: 0, + fee_finalized: 0, + protocol_fees: 2_000_000, + } + ) + let pool_output_val = + value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) + |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) + |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + |> value.merge(value.from_lovelace(2_000_000)) + let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees + |> add_asset_to_tx_output(pool_output_val) + + let lp_output_val = + value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) + |> value.merge(value.from_lovelace(2_000_000)) + let lp_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(lp_output_val) + + let ref_output_val = + value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) + |> value.merge(value.from_lovelace(2_000_000)) + let ref_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(ref_output_val) + + let poolMintRedeemer = CreatePool { + assets: ((rberry_policy_id, rberry_token_name), (sberry_policy_id, sberry_token_name)), + pool_output: 0, + metadata_output: 2, + } + + let ctx = interval.between(1,2) + |> build_txn_context() + |> mint_assets(hash_of_pool_script, value.to_minted_value( + value.from_lovelace(0) + |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) + |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + |> value.add(hash_of_pool_script, new_pool_ref_token, 1) + )) + |> add_tx_input(funds_input) + |> add_tx_ref_input(settings_input) + + // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] + |> add_tx_output(ref_output) + |> add_tx_output(lp_output) + |> add_tx_output(pool_output) + + let result = mint(settings_policy_id, poolMintRedeemer, ctx) + !result +} From a2c20a8968eb4b72b7a8b22ce0c01e2b619e330f Mon Sep 17 00:00:00 2001 From: card Date: Thu, 1 Feb 2024 18:37:08 -0500 Subject: [PATCH 06/13] flake.lock note that this will only work on elaines machine\n\nthis is just to have a working macos version of aiken since cargo was giving trouble --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index 1820a55..043514a 100644 --- a/flake.lock +++ b/flake.lock @@ -109,11 +109,11 @@ }, "nixpkgs_3": { "locked": { - "lastModified": 1705883077, - "narHash": "sha256-ByzHHX3KxpU1+V0erFy8jpujTufimh6KaS/Iv3AciHk=", + "lastModified": 1706683685, + "narHash": "sha256-FtPPshEpxH/ewBOsdKBNhlsL2MLEFv1hEnQ19f/bFsQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "5f5210aa20e343b7e35f40c033000db0ef80d7b9", + "rev": "5ad9903c16126a7d949101687af0aa589b1d7d3d", "type": "github" }, "original": { From fa5ef83d0ad4e0335cec1a67b1ba149e8e5e7a50 Mon Sep 17 00:00:00 2001 From: card Date: Thu, 1 Feb 2024 19:24:45 -0500 Subject: [PATCH 07/13] add test for ssw-001: aiken broken on elaines machine --- validators/pool.ak | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/validators/pool.ak b/validators/pool.ak index 333a8cc..4ec8e65 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -11,7 +11,7 @@ use aiken/transaction.{ ScriptContext, Spend, Transaction, TransactionId, } use aiken/transaction/credential.{ - Address, Inline, ScriptCredential, VerificationKeyCredential, + Address, Inline, ScriptCredential, VerificationKeyCredential, from_verification_key, } use aiken/transaction/value.{MintedValue, PolicyId, Value, ada_policy_id} use calculation/process.{pool_input_to_state, process_orders} @@ -420,8 +420,9 @@ validator(settings_policy_id: PolicyId) { new_pool_nft_token, ) == 1, } - // BUG SSW-202: pool_output.address unchecked + // BUG SSW-001: pool_output.address unchecked // for testing, we can run the whole of has_expected_pool_value + // Make sure we send the pool metadata token to the metadata admin // We use an index from the redeemer to skip to the right output, in case there are multiple outputs to the metadata admin // This is safe to do for the usual reasons: if they point at a UTXO without the ref token, the transaction will fail. @@ -1086,7 +1087,7 @@ test mint_test_two_nfts() { // let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) // // if we add on another pool NFT token to the pool output, it should fail // !mint_test_modify( - // value.add(_, hash_of_pool_script, new_pool_nft_token, 1), + // value.add(_, hash_of_pool_script, new_pool_nft_token, 1), // lift this over Output // identity, // identity, // identity @@ -1104,9 +1105,9 @@ fn pool_ident_from_input (tx_input: Input) -> ByteArray { } fn mint_test_modify( - modify_pool_output: fn(Value) -> Value, - modify_lp_output: fn(Value) -> Value, - modify_ref_output: fn(Value) -> Value, + modify_pool_output: fn(Output) -> Output, + modify_lp_output: fn(Output) -> Output, + modify_ref_output: fn(Output) -> Output, modify_datum: fn(Datum) -> Datum) -> Bool { let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" @@ -1135,25 +1136,25 @@ fn mint_test_modify( value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) |> value.add(hash_of_pool_script, new_pool_nft_token, 1) |> value.merge(value.from_lovelace(1_002_000_000)) - |> modify_pool_output let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees - |> add_asset_to_tx_output(pool_output_val) + |> add_asset_to_tx_output(pool_output_val) + |> modify_pool_output let lp_output_val = value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) |> value.merge(value.from_lovelace(2_000_000)) - |> modify_lp_output let lp_output = new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added |> add_asset_to_tx_output(lp_output_val) + |> modify_lp_output let ref_output_val = value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) |> value.merge(value.from_lovelace(2_000_000)) - |> modify_ref_output let ref_output = new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added |> add_asset_to_tx_output(ref_output_val) + |> modify_ref_output let poolMintRedeemer = CreatePool { assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), @@ -1185,12 +1186,7 @@ test mint_test() { mint_test_modify(identity, identity, identity, identity) } -// BUG SSW-202: pool_output.address unchecked -// for testing, we can run the whole of has_expected_pool_value -// test create_pool_mint_address() { -// False -// } test mint_test_non_ada() { let settings_policy_id = @@ -1275,3 +1271,16 @@ test mint_test_non_ada() { let result = mint(settings_policy_id, poolMintRedeemer, ctx) !result } +// BUG SSW-001: pool_output.address unchecked +// for testing, we can run the whole of has_expected_pool_value +test mint_test_wrong_address () { + let minted = mint_test_modify( + // change pool nft output address to destination that shouldn't be possible + fn (output) { Output{..output, address: from_verification_key(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") }}, + identity, + identity, + identity + ) + !minted + // this should not be mintable, but currently is +} \ No newline at end of file From 208c8a9bbb3f3ab0b7df15b27a8c04cf356e8e64 Mon Sep 17 00:00:00 2001 From: rrruko Date: Thu, 1 Feb 2024 16:32:22 -0800 Subject: [PATCH 08/13] assert mint succeeds --- validators/pool.ak | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/validators/pool.ak b/validators/pool.ak index 4ec8e65..6ef7b62 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -1281,6 +1281,5 @@ test mint_test_wrong_address () { identity, identity ) - !minted - // this should not be mintable, but currently is -} \ No newline at end of file + minted +} From d2843def9d5c15fb942bfc0361e1c5bb8344049d Mon Sep 17 00:00:00 2001 From: rrruko Date: Thu, 1 Feb 2024 16:32:49 -0800 Subject: [PATCH 09/13] add comment for mint_test_non_ada --- validators/pool.ak | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/validators/pool.ak b/validators/pool.ak index 6ef7b62..2593295 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -1186,8 +1186,9 @@ test mint_test() { mint_test_modify(identity, identity, identity, identity) } - - +// BUG SSW-201 +// pool with pair without ADA will have an additional token +// so we should test for exotic pairs without ada test mint_test_non_ada() { let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" @@ -1271,6 +1272,7 @@ test mint_test_non_ada() { let result = mint(settings_policy_id, poolMintRedeemer, ctx) !result } + // BUG SSW-001: pool_output.address unchecked // for testing, we can run the whole of has_expected_pool_value test mint_test_wrong_address () { From fceb461c4f31ca4b49b0b3e57dd8426b4fe45da7 Mon Sep 17 00:00:00 2001 From: card Date: Wed, 28 Feb 2024 00:54:30 -0500 Subject: [PATCH 10/13] remove findings --- flake.lock | 200 --------------------------------------------- flake.nix | 25 ------ validators/pool.ak | 144 ++++---------------------------- 3 files changed, 18 insertions(+), 351 deletions(-) delete mode 100644 flake.lock delete mode 100644 flake.nix diff --git a/flake.lock b/flake.lock deleted file mode 100644 index 043514a..0000000 --- a/flake.lock +++ /dev/null @@ -1,200 +0,0 @@ -{ - "nodes": { - "aiken": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", - "rust-overlay": "rust-overlay" - }, - "locked": { - "lastModified": 1705531678, - "narHash": "sha256-jxUnn5oq6AWVCHj5qnIeFaO2w3L3+iuPv5088QmYSSA=", - "ref": "refs/heads/nix-macos-build-libs", - "rev": "328514182ed9777581aa222aca0a5431e41ef4a7", - "revCount": 2147, - "type": "git", - "url": "file:///Users/card/Dev/job/sundaeswap/aiken" - }, - "original": { - "owner": "aiken-lang", - "ref": "v1.0.20-alpha", - "repo": "aiken", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1689068808, - "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_3": { - "inputs": { - "systems": "systems_3" - }, - "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1690463849, - "narHash": "sha256-n/ej009hs6+q/wxPR+OlYrNMXXv9qVK+FRyl7IyDuG0=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "3173ae5d63ebd44ce99b70965557906800a1fb02", - "type": "github" - }, - "original": { - "owner": "nixos", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1681358109, - "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { - "locked": { - "lastModified": 1706683685, - "narHash": "sha256-FtPPshEpxH/ewBOsdKBNhlsL2MLEFv1hEnQ19f/bFsQ=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "5ad9903c16126a7d949101687af0aa589b1d7d3d", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "aiken": "aiken", - "flake-utils": "flake-utils_3", - "nixpkgs": "nixpkgs_3" - } - }, - "rust-overlay": { - "inputs": { - "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2" - }, - "locked": { - "lastModified": 1690424156, - "narHash": "sha256-Bpml+L280tHTQpwpC5/BJbU4HSvEzMvW8IZ4gAXimhE=", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "f335a0213504c7e6481c359dc1009be9cf34432c", - "type": "github" - }, - "original": { - "owner": "oxalica", - "repo": "rust-overlay", - "type": "github" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - }, - "systems_3": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/flake.nix b/flake.nix deleted file mode 100644 index 9fc1e85..0000000 --- a/flake.nix +++ /dev/null @@ -1,25 +0,0 @@ -{ - description = "A description of your project"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - aiken.url = "github:aiken-lang/aiken/v1.0.20-alpha"; - # wont work on macos till they cut a release with the coreservices stuff - flake-utils.url = "github:numtide/flake-utils"; - }; - - outputs = { self, nixpkgs, aiken, flake-utils, ... }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = import nixpkgs { - inherit system; - overlays = [ aiken.outputs.overlays.${system}.default ]; - }; - in - { - devShell = pkgs.mkShell { - buildInputs = [ pkgs.aiken ]; - }; - } - ); -} diff --git a/validators/pool.ak b/validators/pool.ak index 2593295..0c1598f 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -408,20 +408,15 @@ validator(settings_policy_id: PolicyId) { // Confirm that the correct funds (at least one asset A, at least one asset B, exactly 3 tokens, including the pool NFT) is correct here // Note: should we instead just compare the expected pool output? let funds_spent_to_pool = and { - coin_a_amt_sans_protocol_fees >= 1, - coin_b_amt >= 1, - // BUG SSW-201 - // pool with pair without ADA will have an additional token - // so we should test for exotic pairs without ada - list.length(value.flatten(pool_output.value)) <= 3, - value.quantity_of( - pool_output.value, - own_policy_id, - new_pool_nft_token, - ) == 1, - } - // BUG SSW-001: pool_output.address unchecked - // for testing, we can run the whole of has_expected_pool_value + coin_a_amt_sans_protocol_fees >= 1, + coin_b_amt >= 1, + list.length(value.flatten(pool_output.value)) <= 3, + value.quantity_of( + pool_output.value, + own_policy_id, + new_pool_nft_token, + ) == 1 + } // Make sure we send the pool metadata token to the metadata admin // We use an index from the redeemer to skip to the right output, in case there are multiple outputs to the metadata admin @@ -1067,9 +1062,6 @@ fn scoop_swap_deposit(options: ScoopTestOptions) { result } -// this is for test -// FIXME(Elaine) figure out how to organize test globals etc -// this is slightly annoying because aiken is picky about variable scope with tests const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" fn pool_test_tx_input() -> Input { let funds_input = @@ -1083,16 +1075,15 @@ fn pool_test_tx_input() -> Input { } test mint_test_two_nfts() { - // let pool_id = pool_ident_from_input(pool_test_tx_input()) - // let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) - // // if we add on another pool NFT token to the pool output, it should fail - // !mint_test_modify( - // value.add(_, hash_of_pool_script, new_pool_nft_token, 1), // lift this over Output - // identity, - // identity, - // identity - // ) - True + let pool_id = pool_ident_from_input(pool_test_tx_input()) + let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) + // if we add on another pool NFT token to the pool output, it should fail + !mint_test_modify( + fn(output) { Output { ..output, value: output.value.add(_, hash_of_pool_script, new_pool_nft_token, 1) } }, + identity, + identity, + identity + ) } fn pool_ident_from_input (tx_input: Input) -> ByteArray { @@ -1186,102 +1177,3 @@ test mint_test() { mint_test_modify(identity, identity, identity, identity) } -// BUG SSW-201 -// pool with pair without ADA will have an additional token -// so we should test for exotic pairs without ada -test mint_test_non_ada() { - let settings_policy_id = - #"00000000000000000000000000000000000000000000000000000000" - let hash_of_pool_script = - #"00000000000000000000000000000000000000000000000000000000" - let pool_address = script_address(hash_of_pool_script) - let rberry_policy_id = - #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let rberry_token_name = #"524245525259" - let sberry_policy_id = - #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let sberry_token_name = #"534245525259" - - let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" - let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" - let pool_address = script_address(hash_of_pool_script) - let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let rberry_token_name = #"524245525259" - let user_address = - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - let settings_input = mk_valid_settings_input([], 1) - - let funds_input = pool_test_tx_input() - let pool_id = pool_ident_from_input(funds_input) - let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - let inline_pool_datum = InlineDatum( - PoolDatum { - identifier: pool_id, - assets: ((rberry_policy_id, rberry_token_name), (sberry_policy_id, sberry_token_name)), - circulating_lp: 1_000_000_000, - fees_per_10_thousand: (5, 5), - market_open: 0, - fee_finalized: 0, - protocol_fees: 2_000_000, - } - ) - let pool_output_val = - value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) - |> value.add(sberry_policy_id, sberry_token_name, 1_000_000_000) - |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - |> value.merge(value.from_lovelace(2_000_000)) - let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees - |> add_asset_to_tx_output(pool_output_val) - - let lp_output_val = - value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) - |> value.merge(value.from_lovelace(2_000_000)) - let lp_output = - new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - |> add_asset_to_tx_output(lp_output_val) - - let ref_output_val = - value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) - |> value.merge(value.from_lovelace(2_000_000)) - let ref_output = - new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - |> add_asset_to_tx_output(ref_output_val) - - let poolMintRedeemer = CreatePool { - assets: ((rberry_policy_id, rberry_token_name), (sberry_policy_id, sberry_token_name)), - pool_output: 0, - metadata_output: 2, - } - - let ctx = interval.between(1,2) - |> build_txn_context() - |> mint_assets(hash_of_pool_script, value.to_minted_value( - value.from_lovelace(0) - |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) - |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - |> value.add(hash_of_pool_script, new_pool_ref_token, 1) - )) - |> add_tx_input(funds_input) - |> add_tx_ref_input(settings_input) - - // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] - |> add_tx_output(ref_output) - |> add_tx_output(lp_output) - |> add_tx_output(pool_output) - - let result = mint(settings_policy_id, poolMintRedeemer, ctx) - !result -} - -// BUG SSW-001: pool_output.address unchecked -// for testing, we can run the whole of has_expected_pool_value -test mint_test_wrong_address () { - let minted = mint_test_modify( - // change pool nft output address to destination that shouldn't be possible - fn (output) { Output{..output, address: from_verification_key(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") }}, - identity, - identity, - identity - ) - minted -} From cbfca6dde5a9b708a90a05f71419846415238706 Mon Sep 17 00:00:00 2001 From: Pi Lanningham Date: Wed, 28 Feb 2024 13:05:31 -0500 Subject: [PATCH 11/13] Move tests to the tests subfolder --- validators/pool.ak | 632 +--------------------------------- validators/settings.ak | 109 +----- validators/tests/pool.ak | 637 +++++++++++++++++++++++++++++++++++ validators/tests/settings.ak | 95 ++++++ 4 files changed, 742 insertions(+), 731 deletions(-) create mode 100644 validators/tests/pool.ak create mode 100644 validators/tests/settings.ak diff --git a/validators/pool.ak b/validators/pool.ak index 0c1598f..9379915 100644 --- a/validators/pool.ak +++ b/validators/pool.ak @@ -1,17 +1,15 @@ -use aiken/builtin use aiken/bytearray use aiken/dict use aiken/hash use aiken/interval use aiken/list use aiken/math -use aiken/option use aiken/transaction.{ - Datum, InlineDatum, Input, Mint, NoDatum, Output, OutputReference, - ScriptContext, Spend, Transaction, TransactionId, + InlineDatum, Input, Mint, Output, OutputReference, + ScriptContext, Transaction, TransactionId, } use aiken/transaction/credential.{ - Address, Inline, ScriptCredential, VerificationKeyCredential, from_verification_key, + Address, Inline, ScriptCredential, } use aiken/transaction/value.{MintedValue, PolicyId, Value, ada_policy_id} use calculation/process.{pool_input_to_state, process_orders} @@ -21,18 +19,11 @@ use shared.{ pool_nft_name, spent_output, } use sundae/multisig -use tests/examples/ex_settings.{mk_valid_settings_input} -use tests/examples/ex_shared.{ - mk_output_reference, mk_tx_hash, script_address, wallet_address, -} -use types/order.{Deposit, Destination, OrderDatum, Swap} use types/pool.{ CreatePool, MintLP, PoolDatum, PoolMintRedeemer, PoolRedeemer, PoolScoop, WithdrawFees, } use types/settings.{SettingsDatum, find_settings_datum} -use tx_util/builder.{add_asset_to_tx_output, add_tx_input, add_tx_output, add_tx_ref_input, build_txn_context, mint_assets, new_tx_input, new_tx_output, with_asset_of_tx_input} -use aiken/cbor /// The core / base "pooled AMM" script for the SundaeSwap v3 protocol /// @@ -61,7 +52,7 @@ use aiken/cbor /// /// A + B*n + C < (A + B) * n validator(settings_policy_id: PolicyId) { - fn spend(datum: PoolDatum, redeemer: PoolRedeemer, ctx: ScriptContext) { + pub fn spend(datum: PoolDatum, redeemer: PoolRedeemer, ctx: ScriptContext) { // First, we destructure the transaction right upfront, because field access is O(n), // and we want access to these fields with just a single pass over the transaction // This will be a common pattern throughout the scripts @@ -303,7 +294,7 @@ validator(settings_policy_id: PolicyId) { } } - fn mint(r: PoolMintRedeemer, ctx: ScriptContext) { + pub fn mint(r: PoolMintRedeemer, ctx: ScriptContext) { // When minting, we can be doing one of two things: minting the pool itself, or minting the LP token when r is { // For creating a new pool, one of our design objectives was to avoid requiring interaction with any global @@ -564,616 +555,7 @@ fn compare_asset_class(a: AssetClass, b: AssetClass) { } // Convert a specific integer (like a UTXO index) into a byte array, so we can construct a hashable string when minting the pool -fn int_to_ident(n: Int) -> Ident { +pub fn int_to_ident(n: Int) -> Ident { expect n < 256 bytearray.push(#"", n) -} - -// Tests - -type ScoopTestOptions { - edit_escrow_1_value: Option, - edit_escrow_2_value: Option, - edit_escrow_destination: Option
, - edit_fee: Option, - edit_swap_fees: Option<(Int,Int)>, - edit_pool_output_value: Option, - edit_settings_datum: Option, -} - -fn default_scoop_test_options() -> ScoopTestOptions { - ScoopTestOptions { - edit_escrow_1_value: None, - edit_escrow_2_value: None, - edit_escrow_destination: None, - edit_fee: None, - edit_swap_fees: None, - edit_pool_output_value: None, - edit_settings_datum: None, - } -} - -test ok_scoop_swap_swap() { - let options = default_scoop_test_options() - scoop(options) -} - -test ok_scoop_swap_deposit() { - let options = default_scoop_test_options() - scoop_swap_deposit(options) -} - -test scoop_bad_destination() fail { - let burn_addr = - wallet_address(#"12000000000000000000000000000000000000000000000000000000") - let options = - ScoopTestOptions { - ..default_scoop_test_options(), - edit_escrow_destination: Some(burn_addr), - } - scoop(options) -} - -test scoop_payouts_swapped() fail { - let dummy_policy_id = - #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let dummy_asset_name = #"53554e444145" - let options = - ScoopTestOptions { - ..default_scoop_test_options(), - edit_escrow_1_value: Some( - value.from_lovelace(2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 9_702_095), - ), - edit_escrow_2_value: Some( - value.from_lovelace(2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 9_896_088), - ), - } - scoop(options) -} - -test pool_validator_ignores_fee() { - let options = - ScoopTestOptions { - ..default_scoop_test_options(), - edit_fee: Some(value.from_lovelace(1_000_000_000)), - } - scoop(options) -} - -test scoop_high_swap_fees() { - let hash_of_pool_script = - #"00000000000000000000000000000000000000000000000000000000" - let pool_id = #"00000000000000000000000000000000000000000000000000000000" - let pool_nft_name = shared.pool_nft_name(pool_id) - let dummy_policy_id = - #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let dummy_asset_name = #"53554e444145" - let swap_fee = 100 - let options = - ScoopTestOptions { - ..default_scoop_test_options(), - edit_swap_fees: Some((swap_fee, swap_fee)), - edit_escrow_1_value: Some( - value.from_lovelace(2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 9_802_950), - ), - edit_escrow_2_value: Some( - value.from_lovelace(2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 9_611_678), - ), - edit_pool_output_value: Some( - value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) - |> value.add( - dummy_policy_id, - dummy_asset_name, - 1_000_000_000 - ( 9_802_950 + 9_611_678 ), - ) - |> value.add(hash_of_pool_script, pool_nft_name, 1), - ), - } - scoop(options) -} - -test output_missing_nft() fail { - let dummy_policy_id = - #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let dummy_asset_name = #"53554e444145" - let options = - ScoopTestOptions { - ..default_scoop_test_options(), - edit_pool_output_value: Some( - value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) - |> value.add( - dummy_policy_id, - dummy_asset_name, - 1_000_000_000 - ( 9_896_088 + 9_702_095 ), - ), - ), - } - scoop(options) -} - -test scooper_not_in_settings() fail { - let somebody = #"11111111111111111111111111111111111111111111111111111111" - let options = - ScoopTestOptions { - ..default_scoop_test_options(), - edit_settings_datum: Some( - InlineDatum( - SettingsDatum { - settings_admin: multisig.AnyOf([]), - metadata_admin: Address( - VerificationKeyCredential( - #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", - ), - None, - ), - treasury_admin: multisig.AnyOf([]), - treasury_address: Address( - VerificationKeyCredential( - #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", - ), - None, - ), - treasury_allowance: (1, 10), - authorized_scoopers: Some([somebody]), - authorized_staking_keys: [], - base_fee: 0, - simple_fee: 2_500_000, - strategy_fee: 5_000_000, - pool_creation_fee: 0, - extensions: Void, - }, - ), - ), - } - scoop(options) -} - -fn scoop(options: ScoopTestOptions) { - let settings_policy_id = - #"00000000000000000000000000000000000000000000000000000000" - let dummy_policy_id = - #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let dummy_asset_name = #"53554e444145" - let scooper = #"00000000000000000000000000000000000000000000000000000000" - let hash_of_pool_script = - #"00000000000000000000000000000000000000000000000000000000" - let hash_of_escrow_script = - #"00000000000000000000000000000000000000000000000000000000" - let user_addr = - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - let owner = - multisig.Signature( - #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", - ) - let pool_id = #"00000000000000000000000000000000000000000000000000000000" - let pool_datum = - PoolDatum { - identifier: pool_id, - assets: ((#"", #""), (dummy_policy_id, dummy_asset_name)), - circulating_lp: 1_000_000_000, - fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), - market_open: 0, - fee_finalized: 100, - protocol_fees: 2_000_000, - } - let pool_out_datum = - PoolDatum { - identifier: pool_id, - assets: ((#"", #""), (dummy_policy_id, dummy_asset_name)), - circulating_lp: 1_000_000_000, - fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), - market_open: 0, - fee_finalized: 0, - protocol_fees: 7_000_000, - } - let pool_nft_name = shared.pool_nft_name(pool_id) - let pool_address = script_address(hash_of_pool_script) - let pool_input = - Input { - output_reference: mk_output_reference(0), - output: Output { - address: pool_address, - value: value.from_lovelace(1_000_000_000 + 2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 1_000_000_000) - |> value.add(hash_of_pool_script, pool_nft_name, 1), - datum: InlineDatum(pool_datum), - reference_script: None, - }, - } - let dest = Destination { address: user_addr, datum: NoDatum } - let swap = - Swap((#"", #"", 10_000_000), (dummy_policy_id, dummy_asset_name, 0)) - let escrow_datum = - OrderDatum { - pool_ident: None, - owner, - max_protocol_fee: 2_500_000, - destination: dest, - details: swap, - extension: builtin.i_data(0), - } - let escrow_address = script_address(hash_of_escrow_script) - let escrow1_in = - Input { - output_reference: mk_output_reference(2), - output: Output { - address: escrow_address, - value: value.from_lovelace(4_500_000 + 10_000_000), - datum: InlineDatum(escrow_datum), - reference_script: None, - }, - } - let escrow2_in = - Input { - output_reference: mk_output_reference(3), - output: Output { - address: escrow_address, - value: value.from_lovelace(4_500_000 + 10_000_000), - datum: InlineDatum(escrow_datum), - reference_script: None, - }, - } - let settings_input = { - let Input { output_reference, output } = - mk_valid_settings_input([scooper], 1) - let updated_output = - Output { - ..output, - datum: option.or_else(options.edit_settings_datum, output.datum), - } - Input { output_reference, output: updated_output } - } - let escrow1_out = - Output { - address: option.or_else(options.edit_escrow_destination, user_addr), - value: option.or_else( - options.edit_escrow_1_value, - value.from_lovelace(2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 9_896_088), - ), - datum: NoDatum, - reference_script: None, - } - let escrow2_out = - Output { - address: option.or_else(options.edit_escrow_destination, user_addr), - value: option.or_else( - options.edit_escrow_2_value, - value.from_lovelace(2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 9_702_095), - ), - datum: NoDatum, - reference_script: None, - } - let pool_output = - Output { - address: pool_address, - value: option.or_else( - options.edit_pool_output_value, - value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) - |> value.add( - dummy_policy_id, - dummy_asset_name, - 1_000_000_000 - ( 9_896_088 + 9_702_095 ), - ) - |> value.add(hash_of_pool_script, pool_nft_name, 1), - ), - datum: InlineDatum(pool_out_datum), - reference_script: None, - } - let ctx = - ScriptContext { - transaction: Transaction { - inputs: [pool_input, escrow1_in, escrow2_in], - reference_inputs: [settings_input], - outputs: [pool_output, escrow1_out, escrow2_out], - fee: option.or_else(options.edit_fee, value.from_lovelace(1_000_000)), - mint: value.to_minted_value(value.from_lovelace(0)), - certificates: [], - withdrawals: dict.new(), - validity_range: interval.between(1, 2), - extra_signatories: [scooper], - redeemers: dict.new(), - datums: dict.new(), - id: mk_tx_hash(1), - }, - purpose: Spend(pool_input.output_reference), - } - let pool_redeemer = PoolScoop( - 0, - 0, - [ - (1, None, 0), - (2, None, 0), - ] - ) - let result = spend(settings_policy_id, pool_datum, pool_redeemer, ctx) - result -} - -fn scoop_swap_deposit(options: ScoopTestOptions) { - let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" - let dummy_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let dummy_asset_name = #"53554e444145" - let scooper = #"00000000000000000000000000000000000000000000000000000000" - let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" - let hash_of_escrow_script = #"00000000000000000000000000000000000000000000000000000000" - let user_addr = wallet_address( - #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513" - ) - let owner = multisig.Signature( - #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", - ) - let pool_id = #"000000000000000000000000000000000000000000000000000000000000" - let pool_datum = PoolDatum { - identifier: pool_id, - assets: ( - (#"", #""), - (dummy_policy_id, dummy_asset_name), - ), - circulating_lp: 1_000_000_000, - fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), - market_open: 0, - fee_finalized: 0, - protocol_fees: 2_000_000, - } - let pool_out_datum = PoolDatum { - identifier: pool_id, - assets: ( - (#"", #""), - (dummy_policy_id, dummy_asset_name), - ), - circulating_lp: 1_009_900_990, - fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), - market_open: 0, - fee_finalized: 0, - protocol_fees: 7_000_000, - } - let pool_nft_name = shared.pool_nft_name(pool_id) - let pool_address = script_address(hash_of_pool_script) - let pool_input = Input { - output_reference: mk_output_reference(0), - output: Output { - address: pool_address, - value: value.from_lovelace(1_000_000_000 + 2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 1_000_000_000) - |> value.add(hash_of_pool_script, pool_nft_name, 1), - datum: InlineDatum(pool_datum), - reference_script: None, - }, - } - let dest = Destination { address: user_addr, datum: NoDatum } - let swap = - Swap( - (#"", #"", 10_000_000), - (dummy_policy_id, dummy_asset_name, 0), - ) - let deposit = - Deposit(( - (#"", #"", 10_000_000), - (dummy_policy_id, dummy_asset_name, 10_000_000), - )) - let escrow_datum_1 = OrderDatum { - pool_ident: None, - owner: owner, - max_protocol_fee: 2_500_000, - destination: dest, - details: swap, - extension: builtin.i_data(0), - } - let escrow_datum_2 = OrderDatum { - pool_ident: None, - owner: owner, - max_protocol_fee: 2_500_000, - destination: dest, - details: deposit, - extension: builtin.i_data(0), - } - let escrow_address = script_address(hash_of_escrow_script) - let escrow1_in = Input { - output_reference: mk_output_reference(2), - output: Output { - address: escrow_address, - value: value.from_lovelace(4_500_000 + 10_000_000), - datum: InlineDatum(escrow_datum_1), - reference_script: None, - }, - } - let escrow2_in = Input { - output_reference: mk_output_reference(3), - output: Output { - address: escrow_address, - value: value.from_lovelace(4_500_000 + 10_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 10_000_000), - datum: InlineDatum(escrow_datum_2), - reference_script: None, - }, - } - let settings_input = { - let Input {output_reference, output} = mk_valid_settings_input([scooper], 1) - let updated_output = Output { - ..output, - datum: option.or_else(options.edit_settings_datum, output.datum) - } - Input { - output_reference: output_reference, - output: updated_output, - } - } - let escrow1_out = Output { - address: option.or_else(options.edit_escrow_destination, user_addr), - value: option.or_else(options.edit_escrow_1_value, - value.from_lovelace(2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 9_896_088)), - datum: NoDatum, - reference_script: None, - } - let escrow2_out = Output { - address: option.or_else(options.edit_escrow_destination, user_addr), - value: value.from_lovelace(2_000_000) - |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 9_900_990) - |> value.add(dummy_policy_id, dummy_asset_name, 196_990), - datum: NoDatum, - reference_script: None, - } - let pool_output = Output { - address: pool_address, - value: option.or_else(options.edit_pool_output_value, - value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) - |> value.add(dummy_policy_id, dummy_asset_name, 1_000_000_000 - 9_896_088 + 10_000_000 - 196_990) - |> value.add(hash_of_pool_script, pool_nft_name, 1)), - datum: InlineDatum(pool_out_datum), - reference_script: None, - } - - let ctx = ScriptContext { - transaction: Transaction { - inputs: [pool_input, escrow1_in, escrow2_in], - reference_inputs: [settings_input], - outputs: [pool_output, escrow1_out, escrow2_out], - fee: option.or_else(options.edit_fee, value.from_lovelace(1_000_000)), - mint: value.to_minted_value( - value.from_lovelace(0) - |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 9_900_990) - ), - certificates: [], - withdrawals: dict.new(), - validity_range: interval.between(1, 2), - extra_signatories: [scooper], - redeemers: dict.new(), - datums: dict.new(), - id: mk_tx_hash(1), - }, - purpose: Spend(pool_input.output_reference), - } - let pool_redeemer = PoolScoop( - 0, - 0, - [ - (1, None, 0), - (2, None, 0), - ] - ) - let result = spend(settings_policy_id, pool_datum, pool_redeemer, ctx) - result -} - -const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" -fn pool_test_tx_input() -> Input { - let funds_input = - new_tx_input( - mk_tx_hash(0).hash, - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"), - 0, - NoDatum, - ) - funds_input -} - -test mint_test_two_nfts() { - let pool_id = pool_ident_from_input(pool_test_tx_input()) - let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) - // if we add on another pool NFT token to the pool output, it should fail - !mint_test_modify( - fn(output) { Output { ..output, value: output.value.add(_, hash_of_pool_script, new_pool_nft_token, 1) } }, - identity, - identity, - identity - ) -} - -fn pool_ident_from_input (tx_input: Input) -> ByteArray { - tx_input.output_reference.transaction_id.hash - |> bytearray.concat(#"23") // '#' character - |> - bytearray.concat(int_to_ident(tx_input.output_reference.output_index)) - |> hash.blake2b_256 - |> bytearray.drop(4) -} - -fn mint_test_modify( - modify_pool_output: fn(Output) -> Output, - modify_lp_output: fn(Output) -> Output, - modify_ref_output: fn(Output) -> Output, - modify_datum: fn(Datum) -> Datum) -> Bool { - let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" - let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" - let pool_address = script_address(hash_of_pool_script) - let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" - let rberry_token_name = #"524245525259" - let user_address = - wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") - let settings_input = mk_valid_settings_input([], 1) - - let funds_input = pool_test_tx_input() - let pool_id = pool_ident_from_input(funds_input) - let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) - let inline_pool_datum = modify_datum(InlineDatum( - PoolDatum { - identifier: pool_id, - assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), - circulating_lp: 1_000_000_000, - fees_per_10_thousand: (5, 5), - market_open: 0, - fee_finalized: 0, - protocol_fees: 2_000_000, - } - )) - let pool_output_val = - value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) - |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - |> value.merge(value.from_lovelace(1_002_000_000)) - let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees - |> add_asset_to_tx_output(pool_output_val) - |> modify_pool_output - - let lp_output_val = - value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) - |> value.merge(value.from_lovelace(2_000_000)) - let lp_output = - new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - |> add_asset_to_tx_output(lp_output_val) - |> modify_lp_output - - let ref_output_val = - value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) - |> value.merge(value.from_lovelace(2_000_000)) - let ref_output = - new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added - |> add_asset_to_tx_output(ref_output_val) - |> modify_ref_output - - let poolMintRedeemer = CreatePool { - assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), - pool_output: 0, - metadata_output: 2, - } - - let ctx = interval.between(1,2) - |> build_txn_context() - |> mint_assets(hash_of_pool_script, value.to_minted_value( - value.from_lovelace(0) - |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) - |> value.add(hash_of_pool_script, new_pool_nft_token, 1) - |> value.add(hash_of_pool_script, new_pool_ref_token, 1) - )) - |> add_tx_input(funds_input) - |> add_tx_ref_input(settings_input) - - // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] - |> add_tx_output(ref_output) - |> add_tx_output(lp_output) - |> add_tx_output(pool_output) - - let result = mint(settings_policy_id, poolMintRedeemer, ctx) - result -} - -test mint_test() { - mint_test_modify(identity, identity, identity, identity) -} - +} \ No newline at end of file diff --git a/validators/settings.ak b/validators/settings.ak index 790d928..d0954b3 100644 --- a/validators/settings.ak +++ b/validators/settings.ak @@ -1,27 +1,15 @@ use aiken/list -use aiken/interval -use aiken/bytearray -use aiken/transaction.{InlineDatum, NoDatum, Spend, Mint, ScriptContext, OutputReference, Transaction, TransactionId} -use aiken/transaction/credential.{VerificationKeyCredential, Address, from_script} +use aiken/transaction.{InlineDatum, Mint, ScriptContext, OutputReference, Transaction} use aiken/transaction/value use sundae/multisig use types/settings.{SettingsDatum, SettingsRedeemer, SettingsAdminUpdate, TreasuryAdminUpdate, settings_nft_name} -use tx_util/builder.{ - build_txn_context, - mint_assets, - add_tx_input, - add_tx_output, - new_tx_output, - new_tx_input, - with_asset_of_tx_input, -} use shared.{spent_output} /// The settings validator lets the settings and treasury admins update global settings for the protocol /// /// It is parameterized by the protocol_boot_utxo, a constant to make it an NFT by the usual trick. validator(protocol_boot_utxo: OutputReference) { - fn spend(d: SettingsDatum, redeemer: SettingsRedeemer, ctx: ScriptContext) { + pub fn spend(d: SettingsDatum, redeemer: SettingsRedeemer, ctx: ScriptContext) { // Find our own input so we know the datum / our own address let own_input = spent_output(ctx) let own_address = own_input.address @@ -119,7 +107,7 @@ validator(protocol_boot_utxo: OutputReference) { } // Let us mint the settings NFT exactly once, by checking that one of the inputs is the protocol_boot_utxo - fn mint(_r: Data, ctx: ScriptContext) { + pub fn mint(_r: Data, ctx: ScriptContext) { expect Mint(own_policy_id) = ctx.purpose // Check that we mint *only* one token, and it's exactly our own policy id, and the settings NFT name @@ -140,94 +128,3 @@ validator(protocol_boot_utxo: OutputReference) { } } - -fn test_mint_settings(settings_nfts_count: Int) { - let settings_nft_name = "settings" - let settings_nft_policy = #"00" - - let settings_nft = value.to_minted_value(value.from_asset(settings_nft_policy, settings_nft_name, settings_nfts_count)) - - let settings_datum = mk_valid_settings_datum([]) // Some([]) for authorized_scoopers means no one can scoop - - let settings_output = new_tx_output( - from_script(settings_nft_policy), - 2_000_000, - InlineDatum(settings_datum) - ) - - let protocol_boot_utxo = OutputReference { transaction_id: TransactionId { hash: #"00"}, output_index: 0 } - let protocol_boot_utxo_policy = #"00" - - let protocol_boot_utxo_input = new_tx_input( - protocol_boot_utxo.transaction_id.hash, - from_script(protocol_boot_utxo_policy), - 2_000_000, - NoDatum, - ) |> with_asset_of_tx_input(value.from_asset(protocol_boot_utxo_policy, "boot utxo name", 1)) - let ctx = - interval.between(1, 2) - |> build_txn_context() - |> mint_assets(settings_nft_policy, settings_nft) - |> add_tx_input(protocol_boot_utxo_input) - |> add_tx_output(settings_output) - - - let minted = mint(protocol_boot_utxo, Void, ctx) - minted -} - -test mint_invalid_settings_multiple_nft() { - !test_mint_settings(2) -} - -test mint_valid_settings() { - test_mint_settings(1) -} - -//TODO: import? -fn mk_tx_hash(n: Int) -> TransactionId { - expect n < 256 - let h = - bytearray.push( - #"00000000000000000000000000000000000000000000000000000000000000", - n, - ) - TransactionId { hash: h } -} - -fn mk_valid_settings_datum( - scoopers: List, -) -> SettingsDatum { - SettingsDatum { - settings_admin: multisig.Signature( - #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad", - ), - metadata_admin: Address( - VerificationKeyCredential( - #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", - ), - None, - ), - treasury_admin: multisig.Signature( - #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad", - ), - treasury_address: Address( - VerificationKeyCredential( - #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", - ), - None, - ), - treasury_allowance: (1, 10), - authorized_scoopers: Some(scoopers), - authorized_staking_keys: [ - VerificationKeyCredential( - #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad" - ), - ], - base_fee: 0, - simple_fee: 2_500_000, - strategy_fee: 5_000_000, - pool_creation_fee: 0, - extensions: Void, - } -} \ No newline at end of file diff --git a/validators/tests/pool.ak b/validators/tests/pool.ak new file mode 100644 index 0000000..59705af --- /dev/null +++ b/validators/tests/pool.ak @@ -0,0 +1,637 @@ +use aiken/builtin +use aiken/bytearray +use aiken/dict +use aiken/hash +use aiken/interval +use aiken/option +use aiken/transaction.{ + Datum, InlineDatum, Input, NoDatum, Output, OutputReference, + ScriptContext, Spend, Transaction, TransactionId, +} +use aiken/transaction/credential.{ + Address, VerificationKeyCredential, +} +use aiken/transaction/value.{Value} +use shared.{ + pool_lp_name, +} +use sundae/multisig +use tests/examples/ex_settings.{mk_valid_settings_input} +use tests/examples/ex_shared.{ + mk_output_reference, mk_tx_hash, script_address, wallet_address, +} +use types/order.{Deposit, Destination, OrderDatum, Swap} +use types/pool.{ + CreatePool, PoolDatum, PoolScoop, +} +use types/settings.{SettingsDatum} +use tx_util/builder.{add_asset_to_tx_output, add_tx_input, add_tx_output, add_tx_ref_input, build_txn_context, mint_assets, new_tx_input, new_tx_output} +use pool as pool_validator + + +type ScoopTestOptions { + edit_escrow_1_value: Option, + edit_escrow_2_value: Option, + edit_escrow_destination: Option
, + edit_fee: Option, + edit_swap_fees: Option<(Int,Int)>, + edit_pool_output_value: Option, + edit_settings_datum: Option, +} + +fn default_scoop_test_options() -> ScoopTestOptions { + ScoopTestOptions { + edit_escrow_1_value: None, + edit_escrow_2_value: None, + edit_escrow_destination: None, + edit_fee: None, + edit_swap_fees: None, + edit_pool_output_value: None, + edit_settings_datum: None, + } +} + +test ok_scoop_swap_swap() { + let options = default_scoop_test_options() + scoop(options) +} + +test ok_scoop_swap_deposit() { + let options = default_scoop_test_options() + scoop_swap_deposit(options) +} + +test scoop_bad_destination() fail { + let burn_addr = + wallet_address(#"12000000000000000000000000000000000000000000000000000000") + let options = + ScoopTestOptions { + ..default_scoop_test_options(), + edit_escrow_destination: Some(burn_addr), + } + scoop(options) +} + +test scoop_payouts_swapped() fail { + let dummy_policy_id = + #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let dummy_asset_name = #"53554e444145" + let options = + ScoopTestOptions { + ..default_scoop_test_options(), + edit_escrow_1_value: Some( + value.from_lovelace(2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 9_702_095), + ), + edit_escrow_2_value: Some( + value.from_lovelace(2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 9_896_088), + ), + } + scoop(options) +} + +test pool_validator_ignores_fee() { + let options = + ScoopTestOptions { + ..default_scoop_test_options(), + edit_fee: Some(value.from_lovelace(1_000_000_000)), + } + scoop(options) +} + +test scoop_high_swap_fees() { + let hash_of_pool_script = + #"00000000000000000000000000000000000000000000000000000000" + let pool_id = #"00000000000000000000000000000000000000000000000000000000" + let pool_nft_name = shared.pool_nft_name(pool_id) + let dummy_policy_id = + #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let dummy_asset_name = #"53554e444145" + let swap_fee = 100 + let options = + ScoopTestOptions { + ..default_scoop_test_options(), + edit_swap_fees: Some((swap_fee, swap_fee)), + edit_escrow_1_value: Some( + value.from_lovelace(2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 9_802_950), + ), + edit_escrow_2_value: Some( + value.from_lovelace(2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 9_611_678), + ), + edit_pool_output_value: Some( + value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) + |> value.add( + dummy_policy_id, + dummy_asset_name, + 1_000_000_000 - ( 9_802_950 + 9_611_678 ), + ) + |> value.add(hash_of_pool_script, pool_nft_name, 1), + ), + } + scoop(options) +} + +test output_missing_nft() fail { + let dummy_policy_id = + #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let dummy_asset_name = #"53554e444145" + let options = + ScoopTestOptions { + ..default_scoop_test_options(), + edit_pool_output_value: Some( + value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) + |> value.add( + dummy_policy_id, + dummy_asset_name, + 1_000_000_000 - ( 9_896_088 + 9_702_095 ), + ), + ), + } + scoop(options) +} + +test scooper_not_in_settings() fail { + let somebody = #"11111111111111111111111111111111111111111111111111111111" + let options = + ScoopTestOptions { + ..default_scoop_test_options(), + edit_settings_datum: Some( + InlineDatum( + SettingsDatum { + settings_admin: multisig.AnyOf([]), + metadata_admin: Address( + VerificationKeyCredential( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ), + None, + ), + treasury_admin: multisig.AnyOf([]), + treasury_address: Address( + VerificationKeyCredential( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ), + None, + ), + treasury_allowance: (1, 10), + authorized_scoopers: Some([somebody]), + authorized_staking_keys: [], + base_fee: 0, + simple_fee: 2_500_000, + strategy_fee: 5_000_000, + pool_creation_fee: 0, + extensions: Void, + }, + ), + ), + } + scoop(options) +} + +fn scoop(options: ScoopTestOptions) { + let settings_policy_id = + #"00000000000000000000000000000000000000000000000000000000" + let dummy_policy_id = + #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let dummy_asset_name = #"53554e444145" + let scooper = #"00000000000000000000000000000000000000000000000000000000" + let hash_of_pool_script = + #"00000000000000000000000000000000000000000000000000000000" + let hash_of_escrow_script = + #"00000000000000000000000000000000000000000000000000000000" + let user_addr = + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + let owner = + multisig.Signature( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ) + let pool_id = #"00000000000000000000000000000000000000000000000000000000" + let pool_datum = + PoolDatum { + identifier: pool_id, + assets: ((#"", #""), (dummy_policy_id, dummy_asset_name)), + circulating_lp: 1_000_000_000, + fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), + market_open: 0, + fee_finalized: 100, + protocol_fees: 2_000_000, + } + let pool_out_datum = + PoolDatum { + identifier: pool_id, + assets: ((#"", #""), (dummy_policy_id, dummy_asset_name)), + circulating_lp: 1_000_000_000, + fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), + market_open: 0, + fee_finalized: 0, + protocol_fees: 7_000_000, + } + let pool_nft_name = shared.pool_nft_name(pool_id) + let pool_address = script_address(hash_of_pool_script) + let pool_input = + Input { + output_reference: mk_output_reference(0), + output: Output { + address: pool_address, + value: value.from_lovelace(1_000_000_000 + 2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 1_000_000_000) + |> value.add(hash_of_pool_script, pool_nft_name, 1), + datum: InlineDatum(pool_datum), + reference_script: None, + }, + } + let dest = Destination { address: user_addr, datum: NoDatum } + let swap = + Swap((#"", #"", 10_000_000), (dummy_policy_id, dummy_asset_name, 0)) + let escrow_datum = + OrderDatum { + pool_ident: None, + owner, + max_protocol_fee: 2_500_000, + destination: dest, + details: swap, + extension: builtin.i_data(0), + } + let escrow_address = script_address(hash_of_escrow_script) + let escrow1_in = + Input { + output_reference: mk_output_reference(2), + output: Output { + address: escrow_address, + value: value.from_lovelace(4_500_000 + 10_000_000), + datum: InlineDatum(escrow_datum), + reference_script: None, + }, + } + let escrow2_in = + Input { + output_reference: mk_output_reference(3), + output: Output { + address: escrow_address, + value: value.from_lovelace(4_500_000 + 10_000_000), + datum: InlineDatum(escrow_datum), + reference_script: None, + }, + } + let settings_input = { + let Input { output_reference, output } = + mk_valid_settings_input([scooper], 1) + let updated_output = + Output { + ..output, + datum: option.or_else(options.edit_settings_datum, output.datum), + } + Input { output_reference, output: updated_output } + } + let escrow1_out = + Output { + address: option.or_else(options.edit_escrow_destination, user_addr), + value: option.or_else( + options.edit_escrow_1_value, + value.from_lovelace(2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 9_896_088), + ), + datum: NoDatum, + reference_script: None, + } + let escrow2_out = + Output { + address: option.or_else(options.edit_escrow_destination, user_addr), + value: option.or_else( + options.edit_escrow_2_value, + value.from_lovelace(2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 9_702_095), + ), + datum: NoDatum, + reference_script: None, + } + let pool_output = + Output { + address: pool_address, + value: option.or_else( + options.edit_pool_output_value, + value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) + |> value.add( + dummy_policy_id, + dummy_asset_name, + 1_000_000_000 - ( 9_896_088 + 9_702_095 ), + ) + |> value.add(hash_of_pool_script, pool_nft_name, 1), + ), + datum: InlineDatum(pool_out_datum), + reference_script: None, + } + let ctx = + ScriptContext { + transaction: Transaction { + inputs: [pool_input, escrow1_in, escrow2_in], + reference_inputs: [settings_input], + outputs: [pool_output, escrow1_out, escrow2_out], + fee: option.or_else(options.edit_fee, value.from_lovelace(1_000_000)), + mint: value.to_minted_value(value.from_lovelace(0)), + certificates: [], + withdrawals: dict.new(), + validity_range: interval.between(1, 2), + extra_signatories: [scooper], + redeemers: dict.new(), + datums: dict.new(), + id: mk_tx_hash(1), + }, + purpose: Spend(pool_input.output_reference), + } + let pool_redeemer = PoolScoop( + 0, + 0, + [ + (1, None, 0), + (2, None, 0), + ] + ) + let result = pool_validator.spend(settings_policy_id, pool_datum, pool_redeemer, ctx) + result +} + +fn scoop_swap_deposit(options: ScoopTestOptions) { + let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" + let dummy_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let dummy_asset_name = #"53554e444145" + let scooper = #"00000000000000000000000000000000000000000000000000000000" + let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" + let hash_of_escrow_script = #"00000000000000000000000000000000000000000000000000000000" + let user_addr = wallet_address( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513" + ) + let owner = multisig.Signature( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ) + let pool_id = #"000000000000000000000000000000000000000000000000000000000000" + let pool_datum = PoolDatum { + identifier: pool_id, + assets: ( + (#"", #""), + (dummy_policy_id, dummy_asset_name), + ), + circulating_lp: 1_000_000_000, + fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), + market_open: 0, + fee_finalized: 0, + protocol_fees: 2_000_000, + } + let pool_out_datum = PoolDatum { + identifier: pool_id, + assets: ( + (#"", #""), + (dummy_policy_id, dummy_asset_name), + ), + circulating_lp: 1_009_900_990, + fees_per_10_thousand: option.or_else(options.edit_swap_fees, (5,5)), + market_open: 0, + fee_finalized: 0, + protocol_fees: 7_000_000, + } + let pool_nft_name = shared.pool_nft_name(pool_id) + let pool_address = script_address(hash_of_pool_script) + let pool_input = Input { + output_reference: mk_output_reference(0), + output: Output { + address: pool_address, + value: value.from_lovelace(1_000_000_000 + 2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 1_000_000_000) + |> value.add(hash_of_pool_script, pool_nft_name, 1), + datum: InlineDatum(pool_datum), + reference_script: None, + }, + } + let dest = Destination { address: user_addr, datum: NoDatum } + let swap = + Swap( + (#"", #"", 10_000_000), + (dummy_policy_id, dummy_asset_name, 0), + ) + let deposit = + Deposit(( + (#"", #"", 10_000_000), + (dummy_policy_id, dummy_asset_name, 10_000_000), + )) + let escrow_datum_1 = OrderDatum { + pool_ident: None, + owner: owner, + max_protocol_fee: 2_500_000, + destination: dest, + details: swap, + extension: builtin.i_data(0), + } + let escrow_datum_2 = OrderDatum { + pool_ident: None, + owner: owner, + max_protocol_fee: 2_500_000, + destination: dest, + details: deposit, + extension: builtin.i_data(0), + } + let escrow_address = script_address(hash_of_escrow_script) + let escrow1_in = Input { + output_reference: mk_output_reference(2), + output: Output { + address: escrow_address, + value: value.from_lovelace(4_500_000 + 10_000_000), + datum: InlineDatum(escrow_datum_1), + reference_script: None, + }, + } + let escrow2_in = Input { + output_reference: mk_output_reference(3), + output: Output { + address: escrow_address, + value: value.from_lovelace(4_500_000 + 10_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 10_000_000), + datum: InlineDatum(escrow_datum_2), + reference_script: None, + }, + } + let settings_input = { + let Input {output_reference, output} = mk_valid_settings_input([scooper], 1) + let updated_output = Output { + ..output, + datum: option.or_else(options.edit_settings_datum, output.datum) + } + Input { + output_reference: output_reference, + output: updated_output, + } + } + let escrow1_out = Output { + address: option.or_else(options.edit_escrow_destination, user_addr), + value: option.or_else(options.edit_escrow_1_value, + value.from_lovelace(2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 9_896_088)), + datum: NoDatum, + reference_script: None, + } + let escrow2_out = Output { + address: option.or_else(options.edit_escrow_destination, user_addr), + value: value.from_lovelace(2_000_000) + |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 9_900_990) + |> value.add(dummy_policy_id, dummy_asset_name, 196_990), + datum: NoDatum, + reference_script: None, + } + let pool_output = Output { + address: pool_address, + value: option.or_else(options.edit_pool_output_value, + value.from_lovelace(1_000_000_000 + 20_000_000 + 5_000_000 + 2_000_000) + |> value.add(dummy_policy_id, dummy_asset_name, 1_000_000_000 - 9_896_088 + 10_000_000 - 196_990) + |> value.add(hash_of_pool_script, pool_nft_name, 1)), + datum: InlineDatum(pool_out_datum), + reference_script: None, + } + + let ctx = ScriptContext { + transaction: Transaction { + inputs: [pool_input, escrow1_in, escrow2_in], + reference_inputs: [settings_input], + outputs: [pool_output, escrow1_out, escrow2_out], + fee: option.or_else(options.edit_fee, value.from_lovelace(1_000_000)), + mint: value.to_minted_value( + value.from_lovelace(0) + |> value.add(hash_of_pool_script, pool_lp_name(pool_id), 9_900_990) + ), + certificates: [], + withdrawals: dict.new(), + validity_range: interval.between(1, 2), + extra_signatories: [scooper], + redeemers: dict.new(), + datums: dict.new(), + id: mk_tx_hash(1), + }, + purpose: Spend(pool_input.output_reference), + } + let pool_redeemer = PoolScoop( + 0, + 0, + [ + (1, None, 0), + (2, None, 0), + ] + ) + let result = pool_validator.spend(settings_policy_id, pool_datum, pool_redeemer, ctx) + result +} + +const hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" +fn pool_test_tx_input() -> Input { + let funds_input = + new_tx_input( + mk_tx_hash(0).hash, + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513"), + 0, + NoDatum, + ) + funds_input +} + +test mint_test_two_nfts() { + let pool_id = pool_ident_from_input(pool_test_tx_input()) + let (_, new_pool_nft_token, _) = shared.pool_token_names(pool_id) + // if we add on another pool NFT token to the pool output, it should fail + !mint_test_modify( + fn(output) { Output { ..output, value: value.add(output.value, hash_of_pool_script, new_pool_nft_token, 1) } }, + identity, + identity, + identity + ) +} + +fn pool_ident_from_input (tx_input: Input) -> ByteArray { + tx_input.output_reference.transaction_id.hash + |> bytearray.concat(#"23") // '#' character + |> + bytearray.concat(pool_validator.int_to_ident(tx_input.output_reference.output_index)) + |> hash.blake2b_256 + |> bytearray.drop(4) +} + +fn mint_test_modify( + modify_pool_output: fn(Output) -> Output, + modify_lp_output: fn(Output) -> Output, + modify_ref_output: fn(Output) -> Output, + modify_datum: fn(Datum) -> Datum) -> Bool { + let settings_policy_id = #"00000000000000000000000000000000000000000000000000000000" + let hash_of_pool_script = #"00000000000000000000000000000000000000000000000000000000" + let pool_address = script_address(hash_of_pool_script) + let rberry_policy_id = #"9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + let rberry_token_name = #"524245525259" + let user_address = + wallet_address(#"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513") + let settings_input = mk_valid_settings_input([], 1) + + let funds_input = pool_test_tx_input() + let pool_id = pool_ident_from_input(funds_input) + let (new_pool_ref_token, new_pool_nft_token, new_pool_lp_token) = shared.pool_token_names(pool_id) + let inline_pool_datum = modify_datum(InlineDatum( + PoolDatum { + identifier: pool_id, + assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + circulating_lp: 1_000_000_000, + fees_per_10_thousand: (5, 5), + market_open: 0, + fee_finalized: 0, + protocol_fees: 2_000_000, + } + )) + let pool_output_val = + value.from_asset(rberry_policy_id, rberry_token_name, 1_000_000_000) + |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + |> value.merge(value.from_lovelace(1_002_000_000)) + let pool_output = new_tx_output(pool_address, 0, inline_pool_datum) // 1_002_000_000 = 1_000_000_000 ADA for pool + 2_000_000 ADA for protocol_fees + |> add_asset_to_tx_output(pool_output_val) + |> modify_pool_output + + let lp_output_val = + value.from_asset(hash_of_pool_script, pool_lp_name(pool_id), 1_000_000_000) + |> value.merge(value.from_lovelace(2_000_000)) + let lp_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(lp_output_val) + |> modify_lp_output + + let ref_output_val = + value.from_asset(hash_of_pool_script, new_pool_ref_token, 1) + |> value.merge(value.from_lovelace(2_000_000)) + let ref_output = + new_tx_output(user_address, 0, NoDatum) // we can probably get rid of the rider, it gets auto added + |> add_asset_to_tx_output(ref_output_val) + |> modify_ref_output + + let poolMintRedeemer = CreatePool { + assets: ((#"", #""), (rberry_policy_id, rberry_token_name)), + pool_output: 0, + metadata_output: 2, + } + + let ctx = interval.between(1,2) + |> build_txn_context() + |> mint_assets(hash_of_pool_script, value.to_minted_value( + value.from_lovelace(0) + |> value.add(hash_of_pool_script, new_pool_lp_token, 1_000_000_000) + |> value.add(hash_of_pool_script, new_pool_nft_token, 1) + |> value.add(hash_of_pool_script, new_pool_ref_token, 1) + )) + |> add_tx_input(funds_input) + |> add_tx_ref_input(settings_input) + + // these must be in reverse order like so, in order to get [pool_output, lp_output, ref_output] + |> add_tx_output(ref_output) + |> add_tx_output(lp_output) + |> add_tx_output(pool_output) + + let result = pool_validator.mint(settings_policy_id, poolMintRedeemer, ctx) + result +} + +test mint_test() { + mint_test_modify(identity, identity, identity, identity) +} + diff --git a/validators/tests/settings.ak b/validators/tests/settings.ak new file mode 100644 index 0000000..f72e7b8 --- /dev/null +++ b/validators/tests/settings.ak @@ -0,0 +1,95 @@ +use aiken/interval +use aiken/transaction.{InlineDatum, NoDatum, OutputReference, TransactionId} +use aiken/transaction/credential.{VerificationKeyCredential, Address, from_script} +use aiken/transaction/value +use sundae/multisig +use types/settings.{SettingsDatum, settings_nft_name} +use tx_util/builder.{ + build_txn_context, + mint_assets, + add_tx_input, + add_tx_output, + new_tx_output, + new_tx_input, + with_asset_of_tx_input, +} +use settings as settings_validator + +fn test_mint_settings(settings_nfts_count: Int) { + let settings_nft_policy = #"00" + + let settings_nft = value.to_minted_value(value.from_asset(settings_nft_policy, settings_nft_name, settings_nfts_count)) + + let settings_datum = mk_valid_settings_datum([]) // Some([]) for authorized_scoopers means no one can scoop + + let settings_output = new_tx_output( + from_script(settings_nft_policy), + 2_000_000, + InlineDatum(settings_datum) + ) + + let protocol_boot_utxo = OutputReference { transaction_id: TransactionId { hash: #"00"}, output_index: 0 } + let protocol_boot_utxo_policy = #"00" + + let protocol_boot_utxo_input = new_tx_input( + protocol_boot_utxo.transaction_id.hash, + from_script(protocol_boot_utxo_policy), + 2_000_000, + NoDatum, + ) |> with_asset_of_tx_input(value.from_asset(protocol_boot_utxo_policy, "boot utxo name", 1)) + let ctx = + interval.between(1, 2) + |> build_txn_context() + |> mint_assets(settings_nft_policy, settings_nft) + |> add_tx_input(protocol_boot_utxo_input) + |> add_tx_output(settings_output) + + + let minted = settings_validator.mint(protocol_boot_utxo, Void, ctx) + minted +} + +test mint_invalid_settings_multiple_nft() { + !test_mint_settings(2) +} + +test mint_valid_settings() { + test_mint_settings(1) +} + +fn mk_valid_settings_datum( + scoopers: List, +) -> SettingsDatum { + SettingsDatum { + settings_admin: multisig.Signature( + #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad", + ), + metadata_admin: Address( + VerificationKeyCredential( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ), + None, + ), + treasury_admin: multisig.Signature( + #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad", + ), + treasury_address: Address( + VerificationKeyCredential( + #"6af53ff4f054348ad825c692dd9db8f1760a8e0eacf9af9f99306513", + ), + None, + ), + treasury_allowance: (1, 10), + authorized_scoopers: Some(scoopers), + authorized_staking_keys: [ + VerificationKeyCredential( + #"725011d2c296eb3341e159b6c5c6991de11e81062b95108c9aa024ad" + ), + ], + base_fee: 0, + simple_fee: 2_500_000, + strategy_fee: 5_000_000, + pool_creation_fee: 0, + extensions: Void, + } +} \ No newline at end of file From c2528fded7897241c908febcdf75b227b344e439 Mon Sep 17 00:00:00 2001 From: Pi Lanningham Date: Wed, 28 Feb 2024 13:21:09 -0500 Subject: [PATCH 12/13] Small cleanup to the validators to avoid diffing --- validators/settings.ak | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/validators/settings.ak b/validators/settings.ak index d0954b3..cb68090 100644 --- a/validators/settings.ak +++ b/validators/settings.ak @@ -1,5 +1,5 @@ use aiken/list -use aiken/transaction.{InlineDatum, Mint, ScriptContext, OutputReference, Transaction} +use aiken/transaction.{InlineDatum, Mint, ScriptContext, OutputReference} use aiken/transaction/value use sundae/multisig use types/settings.{SettingsDatum, SettingsRedeemer, SettingsAdminUpdate, TreasuryAdminUpdate, settings_nft_name} @@ -121,6 +121,7 @@ validator(protocol_boot_utxo: OutputReference) { list.any(ctx.transaction.inputs, fn(input) { input.output_reference == protocol_boot_utxo }) + and { mints_exactly_one_settings_nft, spends_protocol_boot_utxo, From 189ba3ec3cd46312eeecae4b71dd341994c335f7 Mon Sep 17 00:00:00 2001 From: Pi Lanningham Date: Wed, 28 Feb 2024 13:22:41 -0500 Subject: [PATCH 13/13] Update aiken version --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a51db02..030110d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,7 +13,7 @@ jobs: - uses: aiken-lang/setup-aiken@v0.1.0 with: - version: v1.0.20-alpha + version: v1.0.24-alpha - run: | # Run the tests