From 9444d72c6020bd6a7b5e07eac56c1b197c6ece35 Mon Sep 17 00:00:00 2001 From: r4bbit <445106+0x-r4bbit@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:35:30 +0200 Subject: [PATCH] feat(amm): route user deposits and LP burns through ATA program Add `owner` and `ata_program_id` parameters to `add_liquidity`, `remove_liquidity`, `swap_exact_input`, and `swap_exact_output`. User deposit-side transfers now emit `ATA::Transfer` chained calls instead of `Token::Transfer` directly, and LP burns emit `ATA::Burn` instead of `Token::Burn`. Vault withdrawal chained calls are unchanged. - Add `ata_program_id` field to `AddLiquidity`, `RemoveLiquidity`, `SwapExactInput`, and `SwapExactOutput` instruction variants in `amm_core` - Add `ata_core` dependency to `amm_program` and guest crates - Update guest binary, unit tests, and integration tests to supply the new `owner` account and `ata_program_id` at every call site - Regenerate `artifacts/amm-idl.json` Closes #11 --- Cargo.lock | 1 + amm/Cargo.toml | 1 + amm/core/src/lib.rs | 30 +++-- amm/methods/guest/Cargo.lock | 12 ++ amm/methods/guest/Cargo.toml | 1 + amm/methods/guest/src/bin/amm.rs | 16 +++ amm/src/add.rs | 31 +++-- amm/src/remove.rs | 34 ++--- amm/src/swap.rs | 48 +++++-- amm/src/tests.rs | 212 ++++++++++++++++++++++++++----- artifacts/amm-idl.json | 42 ++++++ integration_tests/tests/amm.rs | 13 ++ 12 files changed, 359 insertions(+), 82 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9732e53..7cdfff4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -54,6 +54,7 @@ name = "amm_program" version = "0.1.0" dependencies = [ "amm_core", + "ata_core", "nssa_core", "token_core", ] diff --git a/amm/Cargo.toml b/amm/Cargo.toml index 04be123..92224e0 100644 --- a/amm/Cargo.toml +++ b/amm/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1", features = ["host"] } amm_core = { path = "core" } +ata_core = { path = "../ata/core" } token_core = { path = "../token/core" } diff --git a/amm/core/src/lib.rs b/amm/core/src/lib.rs index eeac604..9a29fb4 100644 --- a/amm/core/src/lib.rs +++ b/amm/core/src/lib.rs @@ -44,13 +44,15 @@ pub enum Instruction { /// - Vault Holding Account for Token A (initialized) /// - Vault Holding Account for Token B (initialized) /// - Pool Liquidity Token Definition (initialized) - /// - User Holding Account for Token A (authorized) - /// - User Holding Account for Token B (authorized) - /// - User Holding Account for Pool Liquidity + /// - Owner account (authorized) + /// - User ATA for Token A + /// - User ATA for Token B + /// - User ATA for Pool Liquidity AddLiquidity { min_amount_liquidity: u128, max_amount_to_add_token_a: u128, max_amount_to_add_token_b: u128, + ata_program_id: ProgramId, }, /// Removes liquidity from the Pool @@ -60,13 +62,15 @@ pub enum Instruction { /// - Vault Holding Account for Token A (initialized) /// - Vault Holding Account for Token B (initialized) /// - Pool Liquidity Token Definition (initialized) - /// - User Holding Account for Token A (initialized) - /// - User Holding Account for Token B (initialized) - /// - User Holding Account for Pool Liquidity (authorized) + /// - Owner account (authorized) + /// - User ATA for Token A (initialized) + /// - User ATA for Token B (initialized) + /// - User ATA for Pool Liquidity RemoveLiquidity { remove_liquidity_amount: u128, min_amount_to_remove_token_a: u128, min_amount_to_remove_token_b: u128, + ata_program_id: ProgramId, }, /// Swap some quantity of Tokens (either Token A or Token B) @@ -76,13 +80,14 @@ pub enum Instruction { /// - AMM Pool (initialized) /// - Vault Holding Account for Token A (initialized) /// - Vault Holding Account for Token B (initialized) - /// - User Holding Account for Token A - /// - User Holding Account for Token B Either User Holding Account for Token A or Token B is - /// authorized. + /// - Owner account (authorized) + /// - User ATA for Token A + /// - User ATA for Token B SwapExactInput { swap_amount_in: u128, min_amount_out: u128, token_definition_id_in: AccountId, + ata_program_id: ProgramId, }, /// Swap tokens specifying the exact desired output amount, @@ -92,13 +97,14 @@ pub enum Instruction { /// - AMM Pool (initialized) /// - Vault Holding Account for Token A (initialized) /// - Vault Holding Account for Token B (initialized) - /// - User Holding Account for Token A - /// - User Holding Account for Token B Either User Holding Account for Token A or Token B is - /// authorized. + /// - Owner account (authorized) + /// - User ATA for Token A + /// - User ATA for Token B SwapExactOutput { exact_amount_out: u128, max_amount_in: u128, token_definition_id_in: AccountId, + ata_program_id: ProgramId, }, /// Sync pool reserves with current vault balances. diff --git a/amm/methods/guest/Cargo.lock b/amm/methods/guest/Cargo.lock index cc8d9a8..a2475c2 100644 --- a/amm/methods/guest/Cargo.lock +++ b/amm/methods/guest/Cargo.lock @@ -35,6 +35,7 @@ version = "0.1.0" dependencies = [ "amm_core", "amm_program", + "ata_core", "borsh", "nssa_core", "risc0-zkvm", @@ -59,6 +60,7 @@ name = "amm_program" version = "0.1.0" dependencies = [ "amm_core", + "ata_core", "nssa_core", "token_core", ] @@ -304,6 +306,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "ata_core" +version = "0.1.0" +dependencies = [ + "borsh", + "nssa_core", + "risc0-zkvm", + "serde", +] + [[package]] name = "atomic-waker" version = "1.1.2" diff --git a/amm/methods/guest/Cargo.toml b/amm/methods/guest/Cargo.toml index 385c4ac..5572d1c 100644 --- a/amm/methods/guest/Cargo.toml +++ b/amm/methods/guest/Cargo.toml @@ -14,6 +14,7 @@ spel-framework = { git = "https://github.com/logos-co/spel.git", tag = "v0.2.0-r nssa_core = { git = "https://github.com/logos-blockchain/logos-execution-zone.git", tag = "v0.2.0-rc1" } risc0-zkvm = { version = "=3.0.5", default-features = false } amm_core = { path = "../../core" } +ata_core = { path = "../../../ata/core" } amm_program = { path = "../..", package = "amm_program" } token_core = { path = "../../../token/core" } serde = { version = "1.0", features = ["derive"] } diff --git a/amm/methods/guest/src/bin/amm.rs b/amm/methods/guest/src/bin/amm.rs index 17db95d..62c1cbc 100644 --- a/amm/methods/guest/src/bin/amm.rs +++ b/amm/methods/guest/src/bin/amm.rs @@ -56,24 +56,28 @@ mod amm { vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, pool_definition_lp: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, user_holding_lp: AccountWithMetadata, min_amount_liquidity: u128, max_amount_to_add_token_a: u128, max_amount_to_add_token_b: u128, + ata_program_id: ProgramId, ) -> SpelResult { let (post_states, chained_calls) = amm_program::add::add_liquidity( pool, vault_a, vault_b, pool_definition_lp, + owner, user_holding_a, user_holding_b, user_holding_lp, NonZeroU128::new(min_amount_liquidity).expect("min_amount_liquidity must be nonzero"), max_amount_to_add_token_a, max_amount_to_add_token_b, + ata_program_id, ); Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) } @@ -85,18 +89,21 @@ mod amm { vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, pool_definition_lp: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, user_holding_lp: AccountWithMetadata, remove_liquidity_amount: u128, min_amount_to_remove_token_a: u128, min_amount_to_remove_token_b: u128, + ata_program_id: ProgramId, ) -> SpelResult { let (post_states, chained_calls) = amm_program::remove::remove_liquidity( pool, vault_a, vault_b, pool_definition_lp, + owner, user_holding_a, user_holding_b, user_holding_lp, @@ -104,6 +111,7 @@ mod amm { .expect("remove_liquidity_amount must be nonzero"), min_amount_to_remove_token_a, min_amount_to_remove_token_b, + ata_program_id, ); Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) } @@ -114,21 +122,25 @@ mod amm { pool: AccountWithMetadata, vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, swap_amount_in: u128, min_amount_out: u128, token_definition_id_in: AccountId, + ata_program_id: ProgramId, ) -> SpelResult { let (post_states, chained_calls) = amm_program::swap::swap_exact_input( pool, vault_a, vault_b, + owner, user_holding_a, user_holding_b, swap_amount_in, min_amount_out, token_definition_id_in, + ata_program_id, ); Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) } @@ -139,21 +151,25 @@ mod amm { pool: AccountWithMetadata, vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, exact_amount_out: u128, max_amount_in: u128, token_definition_id_in: AccountId, + ata_program_id: ProgramId, ) -> SpelResult { let (post_states, chained_calls) = amm_program::swap::swap_exact_output( pool, vault_a, vault_b, + owner, user_holding_a, user_holding_b, exact_amount_out, max_amount_in, token_definition_id_in, + ata_program_id, ); Ok(SpelOutput::with_chained_calls(post_states, chained_calls)) } diff --git a/amm/src/add.rs b/amm/src/add.rs index 1dff79e..3d84329 100644 --- a/amm/src/add.rs +++ b/amm/src/add.rs @@ -6,7 +6,7 @@ use amm_core::{ }; use nssa_core::{ account::{AccountWithMetadata, Data}, - program::{AccountPostState, ChainedCall}, + program::{AccountPostState, ChainedCall, ProgramId}, }; #[expect(clippy::too_many_arguments, reason = "TODO: Fix later")] @@ -15,12 +15,14 @@ pub fn add_liquidity( vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, pool_definition_lp: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, user_holding_lp: AccountWithMetadata, min_amount_liquidity: NonZeroU128, max_amount_to_add_token_a: u128, max_amount_to_add_token_b: u128, + ata_program_id: ProgramId, ) -> (Vec, Vec) { // 1. Fetch Pool state let pool_def_data = PoolDefinition::try_from(&pool.account.data) @@ -138,25 +140,27 @@ pub fn add_liquidity( }; pool_post.data = Data::from(&pool_post_definition); - let token_program_id = user_holding_a.account.program_owner; + let token_program_id = vault_a.account.program_owner; - // Chain call for Token A (UserHoldingA -> Vault_A) + // Chain call for Token A (owner's ATA -> Vault_A via ATA program) let call_token_a = ChainedCall::new( - token_program_id, - vec![user_holding_a.clone(), vault_a.clone()], - &token_core::Instruction::Transfer { - amount_to_transfer: actual_amount_a, + ata_program_id, + vec![owner.clone(), user_holding_a.clone(), vault_a.clone()], + &ata_core::Instruction::Transfer { + ata_program_id, + amount: actual_amount_a, }, ); - // Chain call for Token B (UserHoldingB -> Vault_B) + // Chain call for Token B (owner's ATA -> Vault_B via ATA program) let call_token_b = ChainedCall::new( - token_program_id, - vec![user_holding_b.clone(), vault_b.clone()], - &token_core::Instruction::Transfer { - amount_to_transfer: actual_amount_b, + ata_program_id, + vec![owner.clone(), user_holding_b.clone(), vault_b.clone()], + &ata_core::Instruction::Transfer { + ata_program_id, + amount: actual_amount_b, }, ); - // Chain call for LP (mint new tokens for user_holding_lp) + // Chain call for LP (mint new tokens for user's LP ATA) let mut pool_definition_lp_auth = pool_definition_lp.clone(); pool_definition_lp_auth.is_authorized = true; let call_token_lp = ChainedCall::new( @@ -175,6 +179,7 @@ pub fn add_liquidity( AccountPostState::new(vault_a.account.clone()), AccountPostState::new(vault_b.account.clone()), AccountPostState::new(pool_definition_lp.account.clone()), + AccountPostState::new(owner.account.clone()), AccountPostState::new(user_holding_a.account.clone()), AccountPostState::new(user_holding_b.account.clone()), AccountPostState::new(user_holding_lp.account.clone()), diff --git a/amm/src/remove.rs b/amm/src/remove.rs index 951c639..8146be3 100644 --- a/amm/src/remove.rs +++ b/amm/src/remove.rs @@ -1,12 +1,11 @@ use std::num::NonZeroU128; use amm_core::{ - assert_supported_fee_tier, compute_liquidity_token_pda_seed, compute_vault_pda_seed, - PoolDefinition, MINIMUM_LIQUIDITY, + assert_supported_fee_tier, compute_vault_pda_seed, PoolDefinition, MINIMUM_LIQUIDITY, }; use nssa_core::{ account::{AccountWithMetadata, Data}, - program::{AccountPostState, ChainedCall}, + program::{AccountPostState, ChainedCall, ProgramId}, }; #[expect(clippy::too_many_arguments, reason = "TODO: Fix later")] @@ -15,12 +14,14 @@ pub fn remove_liquidity( vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, pool_definition_lp: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, user_holding_lp: AccountWithMetadata, remove_liquidity_amount: NonZeroU128, min_amount_to_remove_token_a: u128, min_amount_to_remove_token_b: u128, + ata_program_id: ProgramId, ) -> (Vec, Vec) { let remove_liquidity_amount: u128 = remove_liquidity_amount.into(); @@ -143,9 +144,9 @@ pub fn remove_liquidity( pool_post.data = Data::from(&pool_post_definition); - let token_program_id = user_holding_a.account.program_owner; + let token_program_id = vault_a.account.program_owner; - // Chaincall for Token A withdraw + // Chaincall for Token A withdraw (vault PDA -> user's ATA) let call_token_a = ChainedCall::new( token_program_id, vec![running_vault_a, user_holding_a.clone()], @@ -157,7 +158,7 @@ pub fn remove_liquidity( pool.account_id, pool_def_data.definition_token_a_id, )]); - // Chaincall for Token B withdraw + // Chaincall for Token B withdraw (vault PDA -> user's ATA) let call_token_b = ChainedCall::new( token_program_id, vec![running_vault_b, user_holding_b.clone()], @@ -169,17 +170,19 @@ pub fn remove_liquidity( pool.account_id, pool_def_data.definition_token_b_id, )]); - // Chaincall for LP adjustment - let mut pool_definition_lp_auth = pool_definition_lp.clone(); - pool_definition_lp_auth.is_authorized = true; + // Chaincall for LP burn (owner's LP ATA -> burn via ATA program) let call_token_lp = ChainedCall::new( - token_program_id, - vec![pool_definition_lp_auth, user_holding_lp.clone()], - &token_core::Instruction::Burn { - amount_to_burn: delta_lp, + ata_program_id, + vec![ + owner.clone(), + user_holding_lp.clone(), + pool_definition_lp.clone(), + ], + &ata_core::Instruction::Burn { + ata_program_id, + amount: delta_lp, }, - ) - .with_pda_seeds(vec![compute_liquidity_token_pda_seed(pool.account_id)]); + ); let chained_calls = vec![call_token_lp, call_token_b, call_token_a]; @@ -188,6 +191,7 @@ pub fn remove_liquidity( AccountPostState::new(vault_a.account.clone()), AccountPostState::new(vault_b.account.clone()), AccountPostState::new(pool_definition_lp.account.clone()), + AccountPostState::new(owner.account.clone()), AccountPostState::new(user_holding_a.account.clone()), AccountPostState::new(user_holding_b.account.clone()), AccountPostState::new(user_holding_lp.account.clone()), diff --git a/amm/src/swap.rs b/amm/src/swap.rs index 51c4d80..196824f 100644 --- a/amm/src/swap.rs +++ b/amm/src/swap.rs @@ -4,7 +4,7 @@ use amm_core::{ pub use amm_core::{compute_liquidity_token_pda_seed, compute_vault_pda_seed, PoolDefinition}; use nssa_core::{ account::{AccountId, AccountWithMetadata, Data}, - program::{AccountPostState, ChainedCall}, + program::{AccountPostState, ChainedCall, ProgramId}, }; /// Validates swap setup: checks pool liquidity is ready, vaults match, and reserves are sufficient. @@ -56,6 +56,7 @@ fn create_swap_post_states( pool_def_data: PoolDefinition, vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, deposit_a: u128, @@ -86,6 +87,7 @@ fn create_swap_post_states( AccountPostState::new(pool_post), AccountPostState::new(vault_a.account), AccountPostState::new(vault_b.account), + AccountPostState::new(owner.account), AccountPostState::new(user_holding_a.account), AccountPostState::new(user_holding_b.account), ] @@ -97,17 +99,20 @@ pub fn swap_exact_input( pool: AccountWithMetadata, vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, swap_amount_in: u128, min_amount_out: u128, token_in_id: AccountId, + ata_program_id: ProgramId, ) -> (Vec, Vec) { let pool_def_data = validate_swap_setup(&pool, &vault_a, &vault_b); let (chained_calls, [deposit_a, withdraw_a], [deposit_b, withdraw_b]) = if token_in_id == pool_def_data.definition_token_a_id { let (chained_calls, deposit_a, withdraw_b) = swap_logic( + owner.clone(), user_holding_a.clone(), vault_a.clone(), vault_b.clone(), @@ -118,11 +123,13 @@ pub fn swap_exact_input( pool_def_data.reserve_a, pool_def_data.reserve_b, pool.account_id, + ata_program_id, ); (chained_calls, [deposit_a, 0], [0, withdraw_b]) } else if token_in_id == pool_def_data.definition_token_b_id { let (chained_calls, deposit_b, withdraw_a) = swap_logic( + owner.clone(), user_holding_b.clone(), vault_b.clone(), vault_a.clone(), @@ -133,6 +140,7 @@ pub fn swap_exact_input( pool_def_data.reserve_b, pool_def_data.reserve_a, pool.account_id, + ata_program_id, ); (chained_calls, [0, withdraw_a], [deposit_b, 0]) @@ -145,6 +153,7 @@ pub fn swap_exact_input( pool_def_data, vault_a, vault_b, + owner, user_holding_a, user_holding_b, deposit_a, @@ -158,6 +167,7 @@ pub fn swap_exact_input( #[expect(clippy::too_many_arguments, reason = "TODO: Fix later")] fn swap_logic( + owner: AccountWithMetadata, user_deposit: AccountWithMetadata, vault_deposit: AccountWithMetadata, vault_withdraw: AccountWithMetadata, @@ -168,6 +178,7 @@ fn swap_logic( reserve_deposit_vault_amount: u128, reserve_withdraw_vault_amount: u128, pool_id: AccountId, + ata_program_id: ProgramId, ) -> (Vec, u128, u128) { let effective_amount_in = swap_amount_in .checked_mul(FEE_BPS_DENOMINATOR - fee_bps) @@ -195,14 +206,16 @@ fn swap_logic( ); assert!(withdraw_amount != 0, "Withdraw amount should be nonzero"); - let token_program_id = user_deposit.account.program_owner; + let token_program_id = vault_withdraw.account.program_owner; let mut chained_calls = Vec::new(); + // Deposit: owner's ATA -> vault via ATA program chained_calls.push(ChainedCall::new( - token_program_id, - vec![user_deposit, vault_deposit], - &token_core::Instruction::Transfer { - amount_to_transfer: swap_amount_in, + ata_program_id, + vec![owner, user_deposit, vault_deposit], + &ata_core::Instruction::Transfer { + ata_program_id, + amount: swap_amount_in, }, )); @@ -216,6 +229,7 @@ fn swap_logic( .definition_id(), ); + // Withdrawal: vault PDA -> user's ATA (no ATA auth needed for recipient) chained_calls.push( ChainedCall::new( token_program_id, @@ -236,17 +250,20 @@ pub fn swap_exact_output( pool: AccountWithMetadata, vault_a: AccountWithMetadata, vault_b: AccountWithMetadata, + owner: AccountWithMetadata, user_holding_a: AccountWithMetadata, user_holding_b: AccountWithMetadata, exact_amount_out: u128, max_amount_in: u128, token_in_id: AccountId, + ata_program_id: ProgramId, ) -> (Vec, Vec) { let pool_def_data = validate_swap_setup(&pool, &vault_a, &vault_b); let (chained_calls, [deposit_a, withdraw_a], [deposit_b, withdraw_b]) = if token_in_id == pool_def_data.definition_token_a_id { let (chained_calls, deposit_a, withdraw_b) = exact_output_swap_logic( + owner.clone(), user_holding_a.clone(), vault_a.clone(), vault_b.clone(), @@ -257,11 +274,13 @@ pub fn swap_exact_output( pool_def_data.reserve_b, pool_def_data.fees, pool.account_id, + ata_program_id, ); (chained_calls, [deposit_a, 0], [0, withdraw_b]) } else if token_in_id == pool_def_data.definition_token_b_id { let (chained_calls, deposit_b, withdraw_a) = exact_output_swap_logic( + owner.clone(), user_holding_b.clone(), vault_b.clone(), vault_a.clone(), @@ -272,6 +291,7 @@ pub fn swap_exact_output( pool_def_data.reserve_a, pool_def_data.fees, pool.account_id, + ata_program_id, ); (chained_calls, [0, withdraw_a], [deposit_b, 0]) @@ -284,6 +304,7 @@ pub fn swap_exact_output( pool_def_data, vault_a, vault_b, + owner, user_holding_a, user_holding_b, deposit_a, @@ -297,6 +318,7 @@ pub fn swap_exact_output( #[expect(clippy::too_many_arguments, reason = "TODO: Fix later")] fn exact_output_swap_logic( + owner: AccountWithMetadata, user_deposit: AccountWithMetadata, vault_deposit: AccountWithMetadata, vault_withdraw: AccountWithMetadata, @@ -307,6 +329,7 @@ fn exact_output_swap_logic( reserve_withdraw_vault_amount: u128, fee_bps: u128, pool_id: AccountId, + ata_program_id: ProgramId, ) -> (Vec, u128, u128) { // Guard: exact_amount_out must be nonzero assert_ne!(exact_amount_out, 0, "Exact amount out must be nonzero"); @@ -346,14 +369,16 @@ fn exact_output_swap_logic( "Required input exceeds maximum amount in" ); - let token_program_id = user_deposit.account.program_owner; + let token_program_id = vault_withdraw.account.program_owner; let mut chained_calls = Vec::new(); + // Deposit: owner's ATA -> vault via ATA program chained_calls.push(ChainedCall::new( - token_program_id, - vec![user_deposit, vault_deposit], - &token_core::Instruction::Transfer { - amount_to_transfer: deposit_amount, + ata_program_id, + vec![owner, user_deposit, vault_deposit], + &ata_core::Instruction::Transfer { + ata_program_id, + amount: deposit_amount, }, )); @@ -367,6 +392,7 @@ fn exact_output_swap_logic( .definition_id(), ); + // Withdrawal: vault PDA -> user's ATA (no ATA auth needed for recipient) chained_calls.push( ChainedCall::new( token_program_id, diff --git a/amm/src/tests.rs b/amm/src/tests.rs index 5f7f433..43bae96 100644 --- a/amm/src/tests.rs +++ b/amm/src/tests.rs @@ -24,6 +24,7 @@ use crate::{ const TOKEN_PROGRAM_ID: ProgramId = [15; 8]; const AMM_PROGRAM_ID: ProgramId = [42; 8]; +const ATA_PROGRAM_ID: ProgramId = [99; 8]; struct BalanceForTests; struct ChainedCallForTests; @@ -224,13 +225,15 @@ impl BalanceForTests { impl ChainedCallForTests { fn cc_swap_token_a_test_1() -> ChainedCall { ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::vault_a_init(), ], - &token_core::Instruction::Transfer { - amount_to_transfer: BalanceForTests::add_max_amount_a(), + &ata_core::Instruction::Transfer { + ata_program_id: ATA_PROGRAM_ID, + amount: BalanceForTests::add_max_amount_a(), }, ) } @@ -275,13 +278,15 @@ impl ChainedCallForTests { fn cc_swap_token_b_test_2() -> ChainedCall { ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::vault_b_init(), ], - &token_core::Instruction::Transfer { - amount_to_transfer: BalanceForTests::add_max_amount_b(), + &ata_core::Instruction::Transfer { + ata_program_id: ATA_PROGRAM_ID, + amount: BalanceForTests::add_max_amount_b(), }, ) } @@ -293,13 +298,15 @@ impl ChainedCallForTests { let swap_amount: u128 = 500; ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::vault_a_init(), ], - &token_core::Instruction::Transfer { - amount_to_transfer: swap_amount, + &ata_core::Instruction::Transfer { + ata_program_id: ATA_PROGRAM_ID, + amount: swap_amount, }, ) } @@ -349,26 +356,30 @@ impl ChainedCallForTests { let swap_amount: u128 = 201; ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::vault_b_init(), ], - &token_core::Instruction::Transfer { - amount_to_transfer: swap_amount, + &ata_core::Instruction::Transfer { + ata_program_id: ATA_PROGRAM_ID, + amount: swap_amount, }, ) } fn cc_swap_rounding_boundary_token_a_in() -> ChainedCall { ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::vault_a_init(), ], - &token_core::Instruction::Transfer { - amount_to_transfer: 3, + &ata_core::Instruction::Transfer { + ata_program_id: ATA_PROGRAM_ID, + amount: 3, }, ) } @@ -392,26 +403,30 @@ impl ChainedCallForTests { fn cc_add_token_a() -> ChainedCall { ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::vault_a_init(), ], - &token_core::Instruction::Transfer { - amount_to_transfer: BalanceForTests::add_successful_amount_a(), + &ata_core::Instruction::Transfer { + ata_program_id: ATA_PROGRAM_ID, + amount: BalanceForTests::add_successful_amount_a(), }, ) } fn cc_add_token_b() -> ChainedCall { ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::vault_b_init(), ], - &token_core::Instruction::Transfer { - amount_to_transfer: BalanceForTests::add_successful_amount_b(), + &ata_core::Instruction::Transfer { + ata_program_id: ATA_PROGRAM_ID, + amount: BalanceForTests::add_successful_amount_b(), }, ) } @@ -470,22 +485,18 @@ impl ChainedCallForTests { } fn cc_remove_pool_lp() -> ChainedCall { - let mut pool_lp_auth = AccountWithMetadataForTests::pool_lp_init(); - pool_lp_auth.is_authorized = true; - ChainedCall::new( - TOKEN_PROGRAM_ID, + ATA_PROGRAM_ID, vec![ - pool_lp_auth, + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_lp_init(), + AccountWithMetadataForTests::pool_lp_init(), ], - &token_core::Instruction::Burn { - amount_to_burn: BalanceForTests::remove_amount_lp(), + &ata_core::Instruction::Burn { + ata_program_id: ATA_PROGRAM_ID, + amount: BalanceForTests::remove_amount_lp(), }, ) - .with_pda_seeds(vec![compute_liquidity_token_pda_seed( - IdForTests::pool_definition_id(), - )]) } fn cc_new_definition_token_a() -> ChainedCall { @@ -611,6 +622,10 @@ impl IdForTests { IdForTests::token_b_definition_id(), ) } + + fn owner_id() -> AccountId { + AccountId::new([48; 32]) + } } impl AccountWithMetadataForTests { @@ -1370,6 +1385,19 @@ impl AccountWithMetadataForTests { account_id: IdForTests::pool_definition_id(), } } + + fn owner() -> AccountWithMetadata { + AccountWithMetadata { + account: Account { + program_owner: ProgramId::default(), + balance: 0u128, + data: Data::default(), + nonce: Nonce(0), + }, + is_authorized: true, + account_id: IdForTests::owner_id(), + } + } } #[test] @@ -1395,12 +1423,14 @@ fn test_call_add_liquidity_vault_a_omitted() { AccountWithMetadataForTests::vault_a_with_wrong_id(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1412,12 +1442,14 @@ fn test_call_add_liquidity_vault_b_omitted() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_with_wrong_id(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1429,12 +1461,14 @@ fn test_call_add_liquidity_lp_definition_mismatch() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_with_wrong_id(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1446,12 +1480,14 @@ fn test_call_add_liquidity_zero_balance_1() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), 0, BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1463,12 +1499,14 @@ fn test_call_add_liquidity_zero_balance_2() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), 0, BalanceForTests::add_max_amount_a(), + ATA_PROGRAM_ID, ); } @@ -1480,12 +1518,14 @@ fn test_call_add_liquidity_vault_a_balance_below_reserve() { AccountWithMetadataForTests::vault_a_init_low(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1497,12 +1537,14 @@ fn test_call_add_liquidity_vault_b_balance_below_reserve() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init_low(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1514,12 +1556,14 @@ fn test_call_add_liquidity_vault_insufficient_balance_1() { AccountWithMetadataForTests::vault_a_init_zero(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1531,12 +1575,14 @@ fn test_call_add_liquidity_vault_insufficient_balance_2() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init_zero(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1548,12 +1594,14 @@ fn test_call_add_liquidity_actual_amount_zero_1() { AccountWithMetadataForTests::vault_a_init_low(), AccountWithMetadataForTests::vault_b_init_high(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1565,12 +1613,14 @@ fn test_call_add_liquidity_actual_amount_zero_2() { AccountWithMetadataForTests::vault_a_init_high(), AccountWithMetadataForTests::vault_b_init_low(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a_low(), BalanceForTests::add_max_amount_b_low(), + ATA_PROGRAM_ID, ); } @@ -1582,12 +1632,14 @@ fn test_call_add_liquidity_reserves_zero_1() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1599,12 +1651,14 @@ fn test_call_add_liquidity_reserves_zero_2() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1616,12 +1670,14 @@ fn test_call_add_liquidity_payable_lp_zero() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a_low(), BalanceForTests::add_max_amount_b_low(), + ATA_PROGRAM_ID, ); } @@ -1632,12 +1688,14 @@ fn test_call_add_liquidity_chained_call_successsful() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::add_min_amount_lp()).unwrap(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_b(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -1664,12 +1722,14 @@ fn test_call_remove_liquidity_vault_a_omitted() { AccountWithMetadataForTests::vault_a_with_wrong_id(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), BalanceForTests::remove_min_amount_a(), BalanceForTests::remove_min_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1681,12 +1741,14 @@ fn test_call_remove_liquidity_vault_b_omitted() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_with_wrong_id(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), BalanceForTests::remove_min_amount_a(), BalanceForTests::remove_min_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1698,12 +1760,14 @@ fn test_call_remove_liquidity_lp_def_mismatch() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_with_wrong_id(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), BalanceForTests::remove_min_amount_a(), BalanceForTests::remove_min_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1715,6 +1779,7 @@ fn test_call_remove_liquidity_insufficient_liquidity_amount() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_a(), /* different token account than lp to @@ -1723,6 +1788,7 @@ fn test_call_remove_liquidity_insufficient_liquidity_amount() { NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), BalanceForTests::remove_min_amount_a(), BalanceForTests::remove_min_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1736,12 +1802,14 @@ fn test_call_remove_liquidity_insufficient_balance_1() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp_1()).unwrap(), BalanceForTests::remove_min_amount_a(), BalanceForTests::remove_min_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1754,12 +1822,14 @@ fn test_call_remove_liquidity_pool_at_minimum_liquidity() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_with_balance(MINIMUM_LIQUIDITY), NonZero::new(MINIMUM_LIQUIDITY).unwrap(), 1, 1, + ATA_PROGRAM_ID, ); } @@ -1774,12 +1844,14 @@ fn test_call_remove_liquidity_exceeds_unlocked_supply() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_with_balance(BalanceForTests::lp_supply_init()), NonZero::new(BalanceForTests::lp_supply_init()).unwrap(), 1, 1, + ATA_PROGRAM_ID, ); } @@ -1793,12 +1865,14 @@ fn test_call_remove_liquidity_insufficient_balance_2() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), BalanceForTests::remove_min_amount_a(), BalanceForTests::remove_min_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1810,12 +1884,14 @@ fn test_call_remove_liquidity_min_bal_zero_1() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), 0, BalanceForTests::remove_min_amount_b(), + ATA_PROGRAM_ID, ); } @@ -1827,12 +1903,14 @@ fn test_call_remove_liquidity_min_bal_zero_2() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), BalanceForTests::remove_min_amount_a(), 0, + ATA_PROGRAM_ID, ); } @@ -1843,12 +1921,14 @@ fn test_call_remove_liquidity_chained_call_successful() { AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(BalanceForTests::remove_amount_lp()).unwrap(), BalanceForTests::remove_min_amount_a(), BalanceForTests::remove_min_amount_b_low(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -2105,11 +2185,13 @@ fn test_call_swap_incorrect_token_type() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::min_amount_out(), IdForTests::token_lp_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2120,11 +2202,13 @@ fn test_call_swap_vault_a_omitted() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_with_wrong_id(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::min_amount_out(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2135,11 +2219,13 @@ fn test_call_swap_vault_b_omitted() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_with_wrong_id(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::min_amount_out(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2150,11 +2236,13 @@ fn test_call_swap_reserves_vault_mismatch_1() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init_low(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::min_amount_out(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2165,11 +2253,13 @@ fn test_call_swap_reserves_vault_mismatch_2() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init_low(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::min_amount_out(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2180,11 +2270,13 @@ fn test_call_swap_below_minimum_liquidity() { AccountWithMetadataForTests::pool_definition_below_minimum_liquidity(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::min_amount_out(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2200,11 +2292,13 @@ fn test_call_swap_rejects_unsupported_fee_tier() { pool, AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_a_low(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2215,11 +2309,13 @@ fn test_call_swap_below_min_out() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::min_amount_out_too_high(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2230,11 +2326,13 @@ fn test_call_swap_effective_amount_zero() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 1, 0, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2245,11 +2343,13 @@ fn test_call_swap_output_rounds_to_zero() { AccountWithMetadataForTests::pool_definition_init_low_balances(), AccountWithMetadataForTests::vault_a_init_low(), AccountWithMetadataForTests::vault_b_init_low(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 2, 0, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2260,11 +2360,13 @@ fn test_call_swap_exact_input_rejects_amount_that_rounds_down_below_target_outpu AccountWithMetadataForTests::pool_definition_swap_rounding_boundary_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 2, 1, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2274,11 +2376,13 @@ fn test_call_swap_exact_input_accepts_smallest_amount_for_rounded_boundary() { AccountWithMetadataForTests::pool_definition_swap_rounding_boundary_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 3, 1, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -2307,11 +2411,13 @@ fn test_call_swap_chained_call_successful_1() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::add_max_amount_a_low(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -2339,11 +2445,13 @@ fn test_call_swap_chained_call_successful_2() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_b(), BalanceForTests::min_amount_out(), IdForTests::token_b_definition_id(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -2372,11 +2480,13 @@ fn call_swap_exact_output_incorrect_token_type() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::max_amount_in(), IdForTests::token_lp_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2387,11 +2497,13 @@ fn call_swap_exact_output_vault_a_omitted() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_with_wrong_id(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::max_amount_in(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2402,11 +2514,13 @@ fn call_swap_exact_output_vault_b_omitted() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_with_wrong_id(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::max_amount_in(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2417,11 +2531,13 @@ fn call_swap_exact_output_reserves_vault_mismatch_1() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init_low(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::max_amount_in(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2432,11 +2548,13 @@ fn call_swap_exact_output_reserves_vault_mismatch_2() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init_low(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::max_amount_in(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2447,11 +2565,13 @@ fn call_swap_exact_output_below_minimum_liquidity() { AccountWithMetadataForTests::pool_definition_below_minimum_liquidity(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::add_max_amount_a(), BalanceForTests::max_amount_in(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2462,11 +2582,13 @@ fn call_swap_exact_output_exceeds_max_in() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 166_u128, 100_u128, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2477,11 +2599,13 @@ fn call_swap_exact_output_zero() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 0_u128, 500_u128, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2492,11 +2616,13 @@ fn call_swap_exact_output_exceeds_reserve() { AccountWithMetadataForTests::pool_definition_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::vault_b_reserve_init(), BalanceForTests::max_amount_in(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2506,11 +2632,13 @@ fn call_swap_exact_output_chained_call_successful() { AccountWithMetadataForTests::pool_definition_swap_exact_output_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), BalanceForTests::max_amount_in(), BalanceForTests::vault_b_reserve_init(), IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -2539,11 +2667,13 @@ fn call_swap_exact_output_chained_call_successful_2() { AccountWithMetadataForTests::pool_definition_swap_exact_output_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 285, 300, IdForTests::token_b_definition_id(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -2575,11 +2705,13 @@ fn call_swap_exact_output_fee_enforced() { AccountWithMetadataForTests::pool_definition_swap_exact_output_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 166_u128, // exact_amount_out: token_b 499_u128, // max_amount_in: still one short after fee rounding IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2593,11 +2725,13 @@ fn call_swap_exact_output_rejects_max_in_that_rounds_down_below_target_output() AccountWithMetadataForTests::pool_definition_swap_rounding_boundary_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 1, 2, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2607,11 +2741,13 @@ fn call_swap_exact_output_accepts_smallest_max_in_for_rounded_boundary() { AccountWithMetadataForTests::pool_definition_swap_rounding_boundary_init(), AccountWithMetadataForTests::vault_a_init(), AccountWithMetadataForTests::vault_b_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 1, 3, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); let pool_post = post_states[0].clone(); @@ -2698,12 +2834,14 @@ fn swap_exact_output_overflow_protection() { pool, vault_a, vault_b, + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 2, // exact_amount_out: small, valid (< reserve_b) 1, // max_amount_in: tiny — real deposit would be enormous, but // overflow wraps it to 0, making 0 <= 1 pass silently IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } @@ -2876,12 +3014,14 @@ fn test_minimum_liquidity_lock_and_remove_all_user_lp() { AccountForTests::vault_a_init(), AccountForTests::vault_b_init(), AccountForTests::pool_lp_init(), + AccountForTests::owner(), AccountForTests::user_holding_a(), AccountForTests::user_holding_b(), AccountForTests::user_holding_lp_with_balance(user_lp), NonZero::new(user_lp).unwrap(), 1, 1, + ATA_PROGRAM_ID, ); let pool_after_remove = @@ -2966,12 +3106,14 @@ fn test_donation_then_add_liquidity_sync_mitigates_mispricing() { donated_vault_a.clone(), donated_vault_b.clone(), AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(1).unwrap(), 100, 50, + ATA_PROGRAM_ID, ); let unsynced_pool_post = PoolDefinition::try_from(&post_unsynced[0].account().data).unwrap(); let unsynced_delta_lp = @@ -2996,12 +3138,14 @@ fn test_donation_then_add_liquidity_sync_mitigates_mispricing() { donated_vault_a_for_synced_add, donated_vault_b_for_synced_add, AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(1).unwrap(), 100, 50, + ATA_PROGRAM_ID, ); let synced_pool_post = PoolDefinition::try_from(&post_synced[0].account().data).unwrap(); let synced_delta_lp = synced_pool_post.liquidity_pool_supply @@ -3093,12 +3237,14 @@ fn add_liquidity_overflow_protection() { vault_a, vault_b, AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), AccountWithMetadataForTests::user_holding_lp_init(), NonZero::new(1).unwrap(), 500, 2, // max_amount_b=2 → reserve_a * 2 overflows + ATA_PROGRAM_ID, ); } @@ -3177,12 +3323,14 @@ fn remove_liquidity_overflow_protection() { vault_a, vault_b, AccountWithMetadataForTests::pool_lp_init(), + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), user_lp, NonZero::new(2).unwrap(), // remove_amount=2 → reserve_a * 2 overflows 1, 1, + ATA_PROGRAM_ID, ); } @@ -3248,11 +3396,13 @@ fn swap_exact_input_overflow_protection() { pool, vault_a, vault_b, + AccountWithMetadataForTests::owner(), AccountWithMetadataForTests::user_holding_a(), AccountWithMetadataForTests::user_holding_b(), 3, 1, IdForTests::token_a_definition_id(), + ATA_PROGRAM_ID, ); } diff --git a/artifacts/amm-idl.json b/artifacts/amm-idl.json index 3003f2f..ffbd256 100644 --- a/artifacts/amm-idl.json +++ b/artifacts/amm-idl.json @@ -1,3 +1,5 @@ + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.53s + Running `target/debug/idl-gen amm/methods/guest/src/bin/amm.rs` { "version": "0.1.0", "name": "amm", @@ -100,6 +102,12 @@ "signer": false, "init": false }, + { + "name": "owner", + "writable": false, + "signer": false, + "init": false + }, { "name": "user_holding_a", "writable": false, @@ -131,6 +139,10 @@ { "name": "max_amount_to_add_token_b", "type": "u128" + }, + { + "name": "ata_program_id", + "type": "program_id" } ] }, @@ -161,6 +173,12 @@ "signer": false, "init": false }, + { + "name": "owner", + "writable": false, + "signer": false, + "init": false + }, { "name": "user_holding_a", "writable": false, @@ -192,6 +210,10 @@ { "name": "min_amount_to_remove_token_b", "type": "u128" + }, + { + "name": "ata_program_id", + "type": "program_id" } ] }, @@ -216,6 +238,12 @@ "signer": false, "init": false }, + { + "name": "owner", + "writable": false, + "signer": false, + "init": false + }, { "name": "user_holding_a", "writable": false, @@ -241,6 +269,10 @@ { "name": "token_definition_id_in", "type": "account_id" + }, + { + "name": "ata_program_id", + "type": "program_id" } ] }, @@ -265,6 +297,12 @@ "signer": false, "init": false }, + { + "name": "owner", + "writable": false, + "signer": false, + "init": false + }, { "name": "user_holding_a", "writable": false, @@ -290,6 +328,10 @@ { "name": "token_definition_id_in", "type": "account_id" + }, + { + "name": "ata_program_id", + "type": "program_id" } ] }, diff --git a/integration_tests/tests/amm.rs b/integration_tests/tests/amm.rs index d121029..87ad3ef 100644 --- a/integration_tests/tests/amm.rs +++ b/integration_tests/tests/amm.rs @@ -38,6 +38,10 @@ impl Ids { amm_methods::AMM_ID } + fn ata_program() -> nssa_core::program::ProgramId { + ata_methods::ATA_ID + } + fn token_a_definition() -> AccountId { AccountId::new([3; 32]) } @@ -1018,6 +1022,7 @@ fn execute_swap_a_to_b(state: &mut V03State, swap_amount_in: u128, min_amount_ou swap_amount_in, min_amount_out, token_definition_id_in: Ids::token_a_definition(), + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1045,6 +1050,7 @@ fn execute_swap_b_to_a(state: &mut V03State, swap_amount_in: u128, min_amount_ou swap_amount_in, min_amount_out, token_definition_id_in: Ids::token_b_definition(), + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1077,6 +1083,7 @@ fn execute_add_liquidity( min_amount_liquidity, max_amount_to_add_token_a, max_amount_to_add_token_b, + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1115,6 +1122,7 @@ fn execute_remove_liquidity( remove_liquidity_amount, min_amount_to_remove_token_a, min_amount_to_remove_token_b, + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1178,6 +1186,7 @@ fn amm_remove_liquidity() { remove_liquidity_amount: Balances::remove_lp(), min_amount_to_remove_token_a: Balances::remove_min_a(), min_amount_to_remove_token_b: Balances::remove_min_b(), + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1240,6 +1249,7 @@ fn amm_remove_liquidity_insufficient_user_lp_fails() { remove_liquidity_amount: Balances::remove_lp(), min_amount_to_remove_token_a: Balances::remove_min_a(), min_amount_to_remove_token_b: Balances::remove_min_b(), + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1464,6 +1474,7 @@ fn amm_add_liquidity() { min_amount_liquidity: Balances::add_min_lp(), max_amount_to_add_token_a: Balances::add_max_a(), max_amount_to_add_token_b: Balances::add_max_b(), + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1526,6 +1537,7 @@ fn amm_swap_b_to_a() { swap_amount_in: Balances::swap_amount_in(), min_amount_out: Balances::swap_min_out(), token_definition_id_in: Ids::token_b_definition(), + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new( @@ -1577,6 +1589,7 @@ fn amm_swap_a_to_b() { swap_amount_in: Balances::swap_amount_in(), min_amount_out: Balances::swap_min_out(), token_definition_id_in: Ids::token_a_definition(), + ata_program_id: Ids::ata_program(), }; let message = public_transaction::Message::try_new(