From 7fd3c01fe7804bcc9a395633417b805b3da96e14 Mon Sep 17 00:00:00 2001 From: codewithgun Date: Mon, 20 Jan 2025 15:14:51 +0800 Subject: [PATCH 1/6] fix: error --- .../initialize_permissionless_pool_with_config.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs b/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs index 448cbcb..8003b06 100644 --- a/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs +++ b/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs @@ -169,6 +169,7 @@ pub fn handle_initialize_customizable_permissionless_pool_with_config( pub struct DynamicAmmInitializePermissionlessPoolWithConfigPoolPdaCreator<'info> { /// CHECK: Creator authority #[account( + mut, seeds = [b"creator"], bump )] @@ -334,12 +335,12 @@ pub fn handle_initialize_customizable_permissionless_pool_with_pda_creator( b_vault_lp_mint: ctx.accounts.b_vault_lp_mint.to_account_info(), a_vault_lp: ctx.accounts.a_vault_lp.to_account_info(), b_vault_lp: ctx.accounts.b_vault_lp.to_account_info(), - payer_token_a: ctx.accounts.payer_token_a.to_account_info(), - payer_token_b: ctx.accounts.payer_token_b.to_account_info(), + payer_token_a: ctx.accounts.creator_token_a.to_account_info(), + payer_token_b: ctx.accounts.creator_token_b.to_account_info(), payer_pool_lp: ctx.accounts.creator_pool_lp.to_account_info(), protocol_token_a_fee: ctx.accounts.protocol_token_a_fee.to_account_info(), protocol_token_b_fee: ctx.accounts.protocol_token_b_fee.to_account_info(), - payer: ctx.accounts.payer.to_account_info(), + payer: ctx.accounts.creator_authority.to_account_info(), rent: ctx.accounts.rent.to_account_info(), mint_metadata: ctx.accounts.mint_metadata.to_account_info(), metadata_program: ctx.accounts.metadata_program.to_account_info(), From 047644ab2865e70ce1bef8c0c06238fc781d3fbd Mon Sep 17 00:00:00 2001 From: codewithgun Date: Mon, 20 Jan 2025 17:02:46 +0800 Subject: [PATCH 2/6] example: lock liquidity --- ...tialize_permissionless_pool_with_config.rs | 4 +- .../dynamic_amm_cpi/lock_liquidity.rs | 414 ++++++++++++++++++ .../src/instructions/dynamic_amm_cpi/mod.rs | 3 + programs/cpi-example/src/lib.rs | 23 +- .../tests/dynamic_amm_init_pool.rs | 22 +- .../tests/dynamic_amm_lock_liquidity.rs | 331 ++++++++++++++ 6 files changed, 783 insertions(+), 14 deletions(-) create mode 100644 programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs create mode 100644 programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs diff --git a/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs b/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs index 8003b06..861efad 100644 --- a/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs +++ b/programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs @@ -166,7 +166,7 @@ pub fn handle_initialize_customizable_permissionless_pool_with_config( } #[derive(Accounts)] -pub struct DynamicAmmInitializePermissionlessPoolWithConfigPoolPdaCreator<'info> { +pub struct DynamicAmmInitializePermissionlessPoolWithConfigPdaCreator<'info> { /// CHECK: Creator authority #[account( mut, @@ -302,7 +302,7 @@ pub struct DynamicAmmInitializePermissionlessPoolWithConfigPoolPdaCreator<'info> /// /// Returns a `Result` indicating success or failure. pub fn handle_initialize_customizable_permissionless_pool_with_pda_creator( - ctx: Context, + ctx: Context, token_a_amount: u64, token_b_amount: u64, activation_point: Option, diff --git a/programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs b/programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs new file mode 100644 index 0000000..9d94f71 --- /dev/null +++ b/programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs @@ -0,0 +1,414 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Mint, TokenAccount}; +use dynamic_amm::state::Pool; + +#[derive(Accounts)] +pub struct DynamicAmmLockLiquidity<'info> { + /// CHECK: Pool account (PDA) + #[account( + mut, + has_one = lp_mint, + )] + pub pool: Box>, + + /// CHECK: Pool LP mint + pub lp_mint: Box>, + + /// CHECK: Lock escrow for user 0 + #[account(mut)] + pub lock_escrow_0: UncheckedAccount<'info>, + + /// CHECK: Lock escrow for user 1 + #[account(mut)] + pub lock_escrow_1: UncheckedAccount<'info>, + + /// CHECK: Payer lp token account + #[account( + mut, + token::mint = lp_mint, + )] + pub source_lp_tokens: Box>, + + /// CHECK: Escrow vault 0 + #[account( + init_if_needed, + associated_token::mint = lp_mint, + associated_token::authority = lock_escrow_0, + payer = payer + )] + pub escrow_vault_0: Box>, + + /// CHECK: Escrow vault 1 + #[account( + init_if_needed, + associated_token::mint = lp_mint, + associated_token::authority = lock_escrow_1, + payer = payer + )] + pub escrow_vault_1: Box>, + + /// Wallet that hold LP tokens and wish to lock user 0 and 1 + #[account(mut)] + pub payer: Signer<'info>, + + /// CHECK: User 0 account + pub user_0: UncheckedAccount<'info>, + + /// CHECK: User 1 account + pub user_1: UncheckedAccount<'info>, + + /// CHECK: Vault account for token a. token a of the pool will be deposit / withdraw from this vault account. + pub a_vault: UncheckedAccount<'info>, + + /// CHECK: Vault account for token b. token b of the pool will be deposit / withdraw from this vault account. + pub b_vault: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + pub a_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + pub b_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault a + pub a_vault_lp_mint: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault b + pub b_vault_lp_mint: UncheckedAccount<'info>, + + /// CHECK: Dynamic AMM program + #[account(address = dynamic_amm::ID)] + pub dynamic_amm_program: UncheckedAccount<'info>, + + /// CHECK: System program + pub system_program: UncheckedAccount<'info>, + + /// CHECK: Token program. + pub token_program: UncheckedAccount<'info>, + + /// CHECK: Associated token program + pub associated_token_program: UncheckedAccount<'info>, +} + +struct LockUserAccountsAndInfo<'b, 'info> { + pub lp_amount: u64, + pub lock_escrow: &'b UncheckedAccount<'info>, + pub escrow_vault: &'b Account<'info, TokenAccount>, + pub owner: &'b UncheckedAccount<'info>, +} + +/// Lock liquidity of a user to multiple users. Each user can claim fee on locked liquidity based on their allocation. +/// +/// # Arguments +/// +/// * `ctx` - The context containing accounts and programs. +/// * `allocations` - The percentage of liquidity to be locked for each user. The values must add up to 10_000. +/// +/// # Returns +/// +/// Returns a `Result` indicating success or failure. +pub fn handle_lock_liquidity(ctx: Context, allocations: [u16; 2]) -> Result<()> { + let total_bps: u32 = allocations + .iter() + .map(|alloc| Into::::into(*alloc)) + .sum(); + + assert!(total_bps == 10_000, "Invalid total bps"); + + let user_0_lp_amount: u64 = u128::from(ctx.accounts.source_lp_tokens.amount) + .checked_mul(allocations[0].into()) + .unwrap() + .checked_div(10_000) + .unwrap() + .try_into() + .unwrap(); + + let user_1_lp_amount = ctx + .accounts + .source_lp_tokens + .amount + .checked_sub(user_0_lp_amount) + .unwrap(); + + let user_accounts_and_info = [ + LockUserAccountsAndInfo { + lp_amount: user_0_lp_amount, + lock_escrow: &ctx.accounts.lock_escrow_0, + escrow_vault: ctx.accounts.escrow_vault_0.as_ref(), + owner: &ctx.accounts.user_0, + }, + LockUserAccountsAndInfo { + lp_amount: user_1_lp_amount, + lock_escrow: &ctx.accounts.lock_escrow_1, + escrow_vault: ctx.accounts.escrow_vault_1.as_ref(), + owner: &ctx.accounts.user_1, + }, + ]; + + for LockUserAccountsAndInfo { + lp_amount, + lock_escrow, + escrow_vault, + owner, + } in user_accounts_and_info + { + // 1. Initialize stake escrow + let accounts = dynamic_amm::cpi::accounts::CreateLockEscrow { + pool: ctx.accounts.pool.to_account_info(), + lock_escrow: lock_escrow.to_account_info(), + owner: owner.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + payer: ctx.accounts.payer.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + }; + + let cpi_context = + CpiContext::new(ctx.accounts.dynamic_amm_program.to_account_info(), accounts); + dynamic_amm::cpi::create_lock_escrow(cpi_context)?; + + // 2. Lock user LP to user + let accounts = dynamic_amm::cpi::accounts::Lock { + pool: ctx.accounts.pool.to_account_info(), + lock_escrow: lock_escrow.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + owner: ctx.accounts.payer.to_account_info(), + source_tokens: ctx.accounts.source_lp_tokens.to_account_info(), + escrow_vault: escrow_vault.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + a_vault: ctx.accounts.a_vault.to_account_info(), + b_vault: ctx.accounts.b_vault.to_account_info(), + a_vault_lp_mint: ctx.accounts.a_vault_lp_mint.to_account_info(), + b_vault_lp_mint: ctx.accounts.b_vault_lp_mint.to_account_info(), + a_vault_lp: ctx.accounts.a_vault_lp.to_account_info(), + b_vault_lp: ctx.accounts.b_vault_lp.to_account_info(), + }; + + let cpi_context = + CpiContext::new(ctx.accounts.dynamic_amm_program.to_account_info(), accounts); + dynamic_amm::cpi::lock(cpi_context, lp_amount)?; + } + + Ok(()) +} + +#[derive(Accounts)] +pub struct DynamicAmmLockLiquidityPdaCreator<'info> { + /// CHECK: Pool account (PDA) + #[account( + mut, + has_one = lp_mint, + )] + pub pool: Box>, + + /// CHECK: Pool LP mint + pub lp_mint: Box>, + + /// CHECK: Lock escrow for pool creator. PDA. + #[account(mut)] + pub lock_escrow_creator: UncheckedAccount<'info>, + + /// CHECK: Lock escrow for user 0 + #[account(mut)] + pub lock_escrow_0: UncheckedAccount<'info>, + + /// CHECK: Pool creator authority. PDA. + #[account( + mut, + seeds = [b"creator"], + bump + )] + pub creator_authority: UncheckedAccount<'info>, + + /// CHECK: Creator lp token account + #[account( + mut, + token::mint = lp_mint, + token::authority = creator_authority, + )] + pub source_lp_tokens: Box>, + + /// CHECK: Escrow vault pool creator + #[account( + init_if_needed, + associated_token::mint = lp_mint, + associated_token::authority = lock_escrow_creator, + payer = payer + )] + pub escrow_vault_creator: Box>, + + /// CHECK: Escrow vault 0 + #[account( + init_if_needed, + associated_token::mint = lp_mint, + associated_token::authority = lock_escrow_0, + payer = payer + )] + pub escrow_vault_0: Box>, + + /// CHECK: CPI example program admin. Only admin can call this instruction. Also funder for account rental. + #[account( + mut, + constraint = crate::assert_eq_admin(payer.key() + ))] + pub payer: Signer<'info>, + + /// CHECK: User 0 account + pub user_0: UncheckedAccount<'info>, + + /// CHECK: Vault account for token a. token a of the pool will be deposit / withdraw from this vault account. + pub a_vault: UncheckedAccount<'info>, + + /// CHECK: Vault account for token b. token b of the pool will be deposit / withdraw from this vault account. + pub b_vault: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + pub a_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + pub b_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault a + pub a_vault_lp_mint: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault b + pub b_vault_lp_mint: UncheckedAccount<'info>, + + /// CHECK: Dynamic AMM program + #[account(address = dynamic_amm::ID)] + pub dynamic_amm_program: UncheckedAccount<'info>, + + /// CHECK: System program + pub system_program: UncheckedAccount<'info>, + + /// CHECK: Token program. + pub token_program: UncheckedAccount<'info>, + + /// CHECK: Associated token program + pub associated_token_program: UncheckedAccount<'info>, +} + +/// Lock liquidity of pool creator PDA to self and an user. Each can claim fee on locked liquidity based on their allocation. +/// +/// # Arguments +/// +/// * `ctx` - The context containing accounts and programs. +/// * `allocations` - The percentage of liquidity to be locked for each user. The values must add up to 10_000. +/// +/// # Returns +/// +/// Returns a `Result` indicating success or failure. +pub fn handle_lock_liquidity_pda_creator( + ctx: Context, + allocations: [u16; 2], +) -> Result<()> { + let total_bps: u32 = allocations + .iter() + .map(|alloc| Into::::into(*alloc)) + .sum(); + + assert!(total_bps == 10_000, "Invalid total bps"); + + let pda_creator_lp_amount: u64 = u128::from(ctx.accounts.source_lp_tokens.amount) + .checked_mul(allocations[0].into()) + .unwrap() + .checked_div(10_000) + .unwrap() + .try_into() + .unwrap(); + + let user_1_lp_amount = ctx + .accounts + .source_lp_tokens + .amount + .checked_sub(pda_creator_lp_amount) + .unwrap(); + + // 1. Initialize lock escrow for pool creator PDA + let accounts = dynamic_amm::cpi::accounts::CreateLockEscrow { + pool: ctx.accounts.pool.to_account_info(), + lock_escrow: ctx.accounts.lock_escrow_creator.to_account_info(), + owner: ctx.accounts.creator_authority.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + payer: ctx.accounts.payer.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + }; + + let cpi_context = CpiContext::new( + ctx.accounts.dynamic_amm_program.to_account_info(), + accounts, + ); + + dynamic_amm::cpi::create_lock_escrow(cpi_context)?; + + // 2. Lock pool creator PDA LP to pool creator PDA lock escrow + let accounts = dynamic_amm::cpi::accounts::Lock { + pool: ctx.accounts.pool.to_account_info(), + lock_escrow: ctx.accounts.lock_escrow_creator.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + owner: ctx.accounts.creator_authority.to_account_info(), + source_tokens: ctx.accounts.source_lp_tokens.to_account_info(), + escrow_vault: ctx.accounts.escrow_vault_creator.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + a_vault: ctx.accounts.a_vault.to_account_info(), + b_vault: ctx.accounts.b_vault.to_account_info(), + a_vault_lp_mint: ctx.accounts.a_vault_lp_mint.to_account_info(), + b_vault_lp_mint: ctx.accounts.b_vault_lp_mint.to_account_info(), + a_vault_lp: ctx.accounts.a_vault_lp.to_account_info(), + b_vault_lp: ctx.accounts.b_vault_lp.to_account_info(), + }; + + let seeds = [ + b"creator".as_ref(), + &[*ctx.bumps.get("creator_authority").unwrap()], + ]; + + let signer_seeds = &[&seeds[..]]; + + msg!("Lock for pda"); + + let cpi_context = + CpiContext::new_with_signer(ctx.accounts.dynamic_amm_program.to_account_info(), accounts, signer_seeds); + dynamic_amm::cpi::lock(cpi_context, pda_creator_lp_amount)?; + + msg!("Lock for pda done"); + + + // 3. Initialize lock escrow for user 1 + let accounts = dynamic_amm::cpi::accounts::CreateLockEscrow { + pool: ctx.accounts.pool.to_account_info(), + lock_escrow: ctx.accounts.lock_escrow_0.to_account_info(), + owner: ctx.accounts.user_0.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + payer: ctx.accounts.payer.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + }; + + let cpi_context = CpiContext::new( + ctx.accounts.dynamic_amm_program.to_account_info(), + accounts, + ); + + dynamic_amm::cpi::create_lock_escrow(cpi_context)?; + + // 4. Lock pool creator PDA LP to user 1 lock escrow + let accounts = dynamic_amm::cpi::accounts::Lock { + pool: ctx.accounts.pool.to_account_info(), + lock_escrow: ctx.accounts.lock_escrow_0.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + owner: ctx.accounts.creator_authority.to_account_info(), + source_tokens: ctx.accounts.source_lp_tokens.to_account_info(), + escrow_vault: ctx.accounts.escrow_vault_0.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + a_vault: ctx.accounts.a_vault.to_account_info(), + b_vault: ctx.accounts.b_vault.to_account_info(), + a_vault_lp_mint: ctx.accounts.a_vault_lp_mint.to_account_info(), + b_vault_lp_mint: ctx.accounts.b_vault_lp_mint.to_account_info(), + a_vault_lp: ctx.accounts.a_vault_lp.to_account_info(), + b_vault_lp: ctx.accounts.b_vault_lp.to_account_info(), + }; + + let cpi_context = + CpiContext::new_with_signer(ctx.accounts.dynamic_amm_program.to_account_info(), accounts, signer_seeds); + dynamic_amm::cpi::lock(cpi_context, user_1_lp_amount)?; + + Ok(()) +} diff --git a/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs b/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs index 743f345..a772704 100644 --- a/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs +++ b/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs @@ -6,3 +6,6 @@ pub use initialize_customizable_permissionless_pool::*; pub mod initialize_permissionless_pool_with_config; pub use initialize_permissionless_pool_with_config::*; + +pub mod lock_liquidity; +pub use lock_liquidity::*; diff --git a/programs/cpi-example/src/lib.rs b/programs/cpi-example/src/lib.rs index b131882..d401130 100644 --- a/programs/cpi-example/src/lib.rs +++ b/programs/cpi-example/src/lib.rs @@ -6,6 +6,10 @@ use m3m3::InitializeVaultParams; pub mod instructions; pub use instructions::*; +fn assert_eq_admin(_key: Pubkey) -> bool { + true +} + declare_id!("4JTNRRQpgLusbEhGnzTuE9kgPgMLXQX1wqBzU52GduqH"); #[program] @@ -62,7 +66,7 @@ pub mod cpi_example { // NOTE: Creator authority PDA will be holding the LP pub fn initialize_dynamic_amm_permission_pool_with_config_pda_creator( - ctx: Context, + ctx: Context, token_a_amount: u64, token_b_amount: u64, activation_point: Option, @@ -94,4 +98,21 @@ pub mod cpi_example { ) -> Result<()> { instructions::dynamic_amm_cpi::swap::handle_dynamic_amm_swap(ctx, amount_in, min_amount_out) } + + pub fn dynamic_amm_lock_liquidity( + ctx: Context, + allocations: [u16; 2], + ) -> Result<()> { + instructions::dynamic_amm_cpi::lock_liquidity::handle_lock_liquidity(ctx, allocations) + } + + pub fn dynamic_amm_lock_liquidity_pda_creator( + ctx: Context, + allocations: [u16; 2], + ) -> Result<()> { + instructions::dynamic_amm_cpi::lock_liquidity::handle_lock_liquidity_pda_creator( + ctx, + allocations, + ) + } } diff --git a/programs/cpi-example/tests/dynamic_amm_init_pool.rs b/programs/cpi-example/tests/dynamic_amm_init_pool.rs index 80f5754..3e7ea54 100644 --- a/programs/cpi-example/tests/dynamic_amm_init_pool.rs +++ b/programs/cpi-example/tests/dynamic_amm_init_pool.rs @@ -30,7 +30,7 @@ async fn test_initialize_customizable_permissionless_pool_with_pda_creator() { test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let sol_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; @@ -45,7 +45,7 @@ async fn test_initialize_customizable_permissionless_pool_with_pda_creator() { let creator_pool_lp = get_associated_token_address(&creator_authority, &lp_mint); let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(sol_vault.key, pool_key); + let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); @@ -65,11 +65,11 @@ async fn test_initialize_customizable_permissionless_pool_with_pda_creator() { token_a_mint: JUP, token_b_mint: USDC, a_vault: jup_vault.key, - b_vault: sol_vault.key, + b_vault: usdc_vault.key, a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: sol_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: sol_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, a_vault_lp, @@ -137,7 +137,7 @@ async fn test_initialize_customizable_permissionless_pool() { test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let sol_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; @@ -145,7 +145,7 @@ async fn test_initialize_customizable_permissionless_pool() { let lp_mint = derive_lp_mint(pool_key); let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(sol_vault.key, pool_key); + let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); @@ -163,11 +163,11 @@ async fn test_initialize_customizable_permissionless_pool() { token_a_mint: JUP, token_b_mint: USDC, a_vault: jup_vault.key, - b_vault: sol_vault.key, + b_vault: usdc_vault.key, a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: sol_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: sol_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, a_vault_lp, @@ -355,7 +355,7 @@ async fn test_initialize_permissionless_pool_with_config_pda_pool_creator() { let metadata_pda = derive_metadata_key(lp_mint); let accounts = - cpi_example::accounts::DynamicAmmInitializePermissionlessPoolWithConfigPoolPdaCreator { + cpi_example::accounts::DynamicAmmInitializePermissionlessPoolWithConfigPdaCreator { pool: pool_key, creator_authority, creator_token_a, diff --git a/programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs b/programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs new file mode 100644 index 0000000..d51adc2 --- /dev/null +++ b/programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs @@ -0,0 +1,331 @@ +mod helpers; +use anchor_lang::{InstructionData, ToAccountMetas}; +use anchor_spl::associated_token::get_associated_token_address; +use dynamic_amm::instructions::CustomizableParams; +use helpers::dynamic_amm_utils::setup_vault_from_cluster; +use helpers::dynamic_amm_utils::*; +use helpers::process_and_assert_ok; +use helpers::*; +use solana_program_test::*; +use solana_sdk::compute_budget::ComputeBudgetInstruction; +use solana_sdk::instruction::Instruction; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Keypair; +use solana_sdk::signer::Signer; +use solana_sdk::{system_program, sysvar}; + +#[tokio::test] +async fn test_lock_liquidity_pda_creator() { + let mock_user = Keypair::new(); + + let mut test = ProgramTest::new( + "cpi_example", + cpi_example::id(), + processor!(cpi_example::entry), + ); + test.prefer_bpf(true); + + test.add_program("dynamic_amm", dynamic_amm::ID, None); + test.add_program("dynamic_vault", dynamic_vault::ID, None); + test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); + + let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + + let (mut banks_client, _, _) = test.start().await; + + let (creator_authority, _bump) = Pubkey::find_program_address(&[b"creator"], &cpi_example::ID); + + let creator_token_a = get_associated_token_address(&creator_authority, &JUP); + let creator_token_b = get_associated_token_address(&creator_authority, &USDC); + + let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); + let lp_mint = derive_lp_mint(pool_key); + + let creator_pool_lp = get_associated_token_address(&creator_authority, &lp_mint); + + let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); + let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); + + let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); + let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + + let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); + let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); + + let metadata_pda = derive_metadata_key(lp_mint); + + // 1. Initialize pool + let accounts = + cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPoolPdaCreator { + pool: pool_key, + creator_authority, + creator_token_a, + creator_token_b, + lp_mint, + token_a_mint: JUP, + token_b_mint: USDC, + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_token_vault: jup_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + payer: mock_user.pubkey(), + token_program: anchor_spl::token::ID, + a_vault_lp, + b_vault_lp, + protocol_token_a_fee, + protocol_token_b_fee, + creator_pool_lp, + payer_token_a, + payer_token_b, + rent: sysvar::rent::ID, + metadata_program: METAPLEX_PROGRAM_ID, + mint_metadata: metadata_pda, + vault_program: dynamic_vault::ID, + associated_token_program: anchor_spl::associated_token::ID, + system_program: system_program::ID, + dynamic_amm_program: dynamic_amm::ID, + } + .to_account_metas(None); + + let ix_data = + cpi_example::instruction::InitializeDynamicAmmCustomizablePermissionlessPoolPdaCreator { + token_a_amount: 100_000_000, + token_b_amount: 100_000_000, + params: CustomizableParams { + trade_fee_numerator: 10_000, + activation_point: None, + has_alpha_vault: false, + activation_type: 1, + padding: [0u8; 90], + }, + } + .data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + instruction, + ], + &mock_user, + &[&mock_user], + &mut banks_client, + ) + .await; + + // 2. Lock liquidity 50/50 to pda creator + user + let user = Keypair::new(); + let allocations = [5000_u16; 2]; + + let lock_escrow_creator = derive_lock_escrow(pool_key, creator_authority); + let lock_escrow_0 = derive_lock_escrow(pool_key, user.pubkey()); + + let escrow_vault_creator = get_associated_token_address(&lock_escrow_creator, &lp_mint); + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); + + let accounts = cpi_example::accounts::DynamicAmmLockLiquidityPdaCreator { + pool: pool_key, + creator_authority, + lock_escrow_creator, + lp_mint, + lock_escrow_0, + source_lp_tokens: creator_pool_lp, + escrow_vault_0, + escrow_vault_creator, + payer: mock_user.pubkey(), + user_0: user.pubkey(), + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_vault_lp, + b_vault_lp, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + dynamic_amm_program: dynamic_amm::ID, + system_program: system_program::ID, + token_program: anchor_spl::token::ID, + associated_token_program: anchor_spl::associated_token::ID, + } + .to_account_metas(None); + + let ix_data = + cpi_example::instruction::DynamicAmmLockLiquidityPdaCreator { allocations }.data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + instruction, + ], + &mock_user, + &[&mock_user], + &mut banks_client, + ) + .await; +} + +#[tokio::test] +async fn test_lock_liquidity() { + let mock_user = Keypair::new(); + + let mut test = ProgramTest::new( + "cpi_example", + cpi_example::id(), + processor!(cpi_example::entry), + ); + test.prefer_bpf(true); + + test.add_program("dynamic_amm", dynamic_amm::ID, None); + test.add_program("dynamic_vault", dynamic_vault::ID, None); + test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); + + let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + + let (mut banks_client, _, _) = test.start().await; + + let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); + let lp_mint = derive_lp_mint(pool_key); + + let payer_pool_lp = get_associated_token_address(&mock_user.pubkey(), &lp_mint); + + let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); + let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); + + let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); + let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + + let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); + let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); + + let metadata_pda = derive_metadata_key(lp_mint); + + // 1. Initialize pool + let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPool { + pool: pool_key, + lp_mint, + token_a_mint: JUP, + token_b_mint: USDC, + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_token_vault: jup_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + payer: mock_user.pubkey(), + token_program: anchor_spl::token::ID, + a_vault_lp, + b_vault_lp, + protocol_token_a_fee, + protocol_token_b_fee, + payer_pool_lp, + payer_token_a, + payer_token_b, + rent: sysvar::rent::ID, + metadata_program: METAPLEX_PROGRAM_ID, + mint_metadata: metadata_pda, + vault_program: dynamic_vault::ID, + associated_token_program: anchor_spl::associated_token::ID, + system_program: system_program::ID, + dynamic_amm_program: dynamic_amm::ID, + } + .to_account_metas(None); + + let ix_data = cpi_example::instruction::InitializeDynamicAmmCustomizablePermissionlessPool { + token_a_amount: 100_000_000, + token_b_amount: 100_000_000, + params: CustomizableParams { + trade_fee_numerator: 10_000, + activation_point: None, + has_alpha_vault: false, + activation_type: 1, + padding: [0u8; 90], + }, + } + .data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + instruction, + ], + &mock_user, + &[&mock_user], + &mut banks_client, + ) + .await; + + // 2. Lock liquidity 50/50 to user 0 + user 1 + let user_0 = mock_user.pubkey(); + let user_1_kp = Keypair::new(); + let user_1 = user_1_kp.pubkey(); + + let allocations = [5000_u16; 2]; + + let lock_escrow_0 = derive_lock_escrow(pool_key, user_0); + let lock_escrow_1 = derive_lock_escrow(pool_key, user_1); + + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); + let escrow_vault_1 = get_associated_token_address(&lock_escrow_1, &lp_mint); + + let accounts = cpi_example::accounts::DynamicAmmLockLiquidity { + pool: pool_key, + lock_escrow_1, + lp_mint, + lock_escrow_0, + source_lp_tokens: payer_pool_lp, + escrow_vault_0, + escrow_vault_1, + payer: mock_user.pubkey(), + user_0, + user_1, + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_vault_lp, + b_vault_lp, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + dynamic_amm_program: dynamic_amm::ID, + system_program: system_program::ID, + token_program: anchor_spl::token::ID, + associated_token_program: anchor_spl::associated_token::ID, + } + .to_account_metas(None); + + let ix_data = cpi_example::instruction::DynamicAmmLockLiquidity { allocations }.data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + instruction, + ], + &mock_user, + &[&mock_user], + &mut banks_client, + ) + .await; +} From 5befa76d9195c426adf54d71f1bad2e8e28b5447 Mon Sep 17 00:00:00 2001 From: codewithgun Date: Mon, 20 Jan 2025 18:16:06 +0800 Subject: [PATCH 3/6] example: claim fee --- programs/cpi-example/Cargo.toml | 1 + .../instructions/dynamic_amm_cpi/claim_fee.rs | 274 ++++++++++ .../dynamic_amm_cpi/lock_liquidity.rs | 5 - .../src/instructions/dynamic_amm_cpi/mod.rs | 3 + programs/cpi-example/src/lib.rs | 12 + .../tests/dynamic_amm_claim_fee.rs | 488 ++++++++++++++++++ 6 files changed, 778 insertions(+), 5 deletions(-) create mode 100644 programs/cpi-example/src/instructions/dynamic_amm_cpi/claim_fee.rs create mode 100644 programs/cpi-example/tests/dynamic_amm_claim_fee.rs diff --git a/programs/cpi-example/Cargo.toml b/programs/cpi-example/Cargo.toml index 6293dad..047e634 100644 --- a/programs/cpi-example/Cargo.toml +++ b/programs/cpi-example/Cargo.toml @@ -37,3 +37,4 @@ assert_matches = "1.5.0" solana-client = "1.16.0" solana-account-decoder = "1.16.0" bincode = "1.3.3" +spl-associated-token-account = "2.2.0" diff --git a/programs/cpi-example/src/instructions/dynamic_amm_cpi/claim_fee.rs b/programs/cpi-example/src/instructions/dynamic_amm_cpi/claim_fee.rs new file mode 100644 index 0000000..38ebca2 --- /dev/null +++ b/programs/cpi-example/src/instructions/dynamic_amm_cpi/claim_fee.rs @@ -0,0 +1,274 @@ +use anchor_lang::prelude::*; +use anchor_spl::associated_token::get_associated_token_address_with_program_id; +use dynamic_amm::state::Pool; + +#[derive(Accounts)] +pub struct DynamicAmmClaimFee<'info> { + /// Check: Pool account (PDA) + #[account(mut)] + pub pool: UncheckedAccount<'info>, + + /// Check: Pool LP mint + #[account(mut)] + pub lp_mint: UncheckedAccount<'info>, + + /// CHECK: Lock escrow of user + #[account(mut)] + pub lock_escrow: UncheckedAccount<'info>, + + /// CHECK: Lock escrow owner + #[account(mut)] + pub owner: Signer<'info>, + + /// CHECK: Token vault of lock escrow + #[account(mut)] + pub escrow_vault: UncheckedAccount<'info>, + + /// CHECK: Token account of vault A + #[account(mut)] + pub a_token_vault: UncheckedAccount<'info>, + + /// CHECK: Token account of vault B + #[account(mut)] + pub b_token_vault: UncheckedAccount<'info>, + + /// CHECK: Vault account for token a. token a of the pool will be deposit / withdraw from this vault account. + #[account(mut)] + pub a_vault: UncheckedAccount<'info>, + + /// CHECK: Vault account for token b. token b of the pool will be deposit / withdraw from this vault account. + #[account(mut)] + pub b_vault: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + #[account(mut)] + pub a_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + #[account(mut)] + pub b_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault a + #[account(mut)] + pub a_vault_lp_mint: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault b + #[account(mut)] + pub b_vault_lp_mint: UncheckedAccount<'info>, + + #[account(mut)] + /// User token A account. Used to receive fee + pub user_a_token: UncheckedAccount<'info>, + #[account(mut)] + /// User token B account. Used to receive fee + pub user_b_token: UncheckedAccount<'info>, + + /// CHECK: Token program + pub token_program: UncheckedAccount<'info>, + + /// CHECK: Dynamic AMM + #[account( + address = dynamic_amm::ID + )] + pub dynamic_amm: UncheckedAccount<'info>, + + /// CHECK: Dynamic vault + pub dynamic_vault: UncheckedAccount<'info>, +} + +/// Claims the fee for a user from the locked liquidity in the pool. +/// +/// # Arguments +/// +/// * `ctx` - The context containing accounts and programs. +/// +/// # Returns +/// +/// Returns a `Result` indicating success or failure. + +pub fn handle_claim_fee(ctx: Context) -> Result<()> { + let accounts = dynamic_amm::cpi::accounts::ClaimFee { + pool: ctx.accounts.pool.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + lock_escrow: ctx.accounts.lock_escrow.to_account_info(), + owner: ctx.accounts.owner.to_account_info(), + // Unused anymore, but still remained for compatibility. Passing escrow_vault can save 1 account + source_tokens: ctx.accounts.escrow_vault.to_account_info(), + a_vault: ctx.accounts.a_vault.to_account_info(), + b_vault: ctx.accounts.b_vault.to_account_info(), + a_vault_lp: ctx.accounts.a_vault_lp.to_account_info(), + b_vault_lp: ctx.accounts.b_vault_lp.to_account_info(), + a_vault_lp_mint: ctx.accounts.a_vault_lp_mint.to_account_info(), + b_vault_lp_mint: ctx.accounts.b_vault_lp_mint.to_account_info(), + user_a_token: ctx.accounts.user_a_token.to_account_info(), + user_b_token: ctx.accounts.user_b_token.to_account_info(), + vault_program: ctx.accounts.dynamic_vault.to_account_info(), + escrow_vault: ctx.accounts.escrow_vault.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + a_token_vault: ctx.accounts.a_token_vault.to_account_info(), + b_token_vault: ctx.accounts.b_token_vault.to_account_info(), + }; + + let cpi_context = CpiContext::new(ctx.accounts.dynamic_amm.to_account_info(), accounts); + + // Claim max fee + dynamic_amm::cpi::claim_fee(cpi_context, u64::MAX) +} + +#[derive(Accounts)] +pub struct DynamicAmmClaimFeePdaCreator<'info> { + /// Check: Pool account (PDA) + #[account(mut)] + pub pool: Box>, + + /// Check: Pool LP mint + #[account(mut)] + pub lp_mint: UncheckedAccount<'info>, + + /// CHECK: Pool creator authority. PDA. + #[account( + mut, + seeds = [b"creator"], + bump + )] + pub creator_authority: UncheckedAccount<'info>, + + /// CHECK: Lock escrow of creator PDA + #[account(mut)] + pub lock_escrow: UncheckedAccount<'info>, + + /// CHECK: Only admin can claim fee for creator PDA. + #[account( + constraint = crate::assert_eq_admin(cpi_example_admin.key()) + )] + pub cpi_example_admin: Signer<'info>, + + /// CHECK: Token vault of lock escrow + #[account(mut)] + pub escrow_vault: UncheckedAccount<'info>, + + /// CHECK: Token account of vault A + #[account(mut)] + pub a_token_vault: UncheckedAccount<'info>, + + /// CHECK: Token account of vault B + #[account(mut)] + pub b_token_vault: UncheckedAccount<'info>, + + /// CHECK: Vault account for token a. token a of the pool will be deposit / withdraw from this vault account. + #[account(mut)] + pub a_vault: UncheckedAccount<'info>, + + /// CHECK: Vault account for token b. token b of the pool will be deposit / withdraw from this vault account. + #[account(mut)] + pub b_vault: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + #[account(mut)] + pub a_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault. + #[account(mut)] + pub b_vault_lp: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault a + #[account(mut)] + pub a_vault_lp_mint: UncheckedAccount<'info>, + + /// CHECK: LP token mint of vault b + #[account(mut)] + pub b_vault_lp_mint: UncheckedAccount<'info>, + + #[account(mut)] + /// Creator token A account. Used to receive fee + pub creator_a_token: UncheckedAccount<'info>, + #[account(mut)] + /// Creator token B account. Used to receive fee + pub creator_b_token: UncheckedAccount<'info>, + + /// CHECK: Token program + pub token_program: UncheckedAccount<'info>, + + /// CHECK: Dynamic AMM + #[account( + address = dynamic_amm::ID + )] + pub dynamic_amm: UncheckedAccount<'info>, + + /// CHECK: Dynamic vault + pub dynamic_vault: UncheckedAccount<'info>, +} + +/// Claims fee for creator PDA. +/// +/// This instruction is used to claim fee for creator PDA. The claimed fee will be hold by creator PDA. +/// +/// # Arguments +/// +/// * `ctx` - The context containing accounts and programs. +/// +/// # Returns +/// +/// Returns a `Result` indicating success or failure. +pub fn handle_claim_fee_pda_creator(ctx: Context) -> Result<()> { + let creator_a_token_key = get_associated_token_address_with_program_id( + &ctx.accounts.creator_authority.key(), + &ctx.accounts.pool.token_a_mint, + &ctx.accounts.token_program.key(), + ); + + let creator_b_token_key = get_associated_token_address_with_program_id( + &ctx.accounts.creator_authority.key(), + &ctx.accounts.pool.token_b_mint, + &ctx.accounts.token_program.key(), + ); + + assert_eq!( + creator_a_token_key, + ctx.accounts.creator_a_token.key(), + "Invalid creator_a_token" + ); + assert_eq!( + creator_b_token_key, + ctx.accounts.creator_b_token.key(), + "Invalid creator_b_token" + ); + + let accounts = dynamic_amm::cpi::accounts::ClaimFee { + pool: ctx.accounts.pool.to_account_info(), + lp_mint: ctx.accounts.lp_mint.to_account_info(), + lock_escrow: ctx.accounts.lock_escrow.to_account_info(), + owner: ctx.accounts.creator_authority.to_account_info(), + // Unused anymore, but still remained for compatibility. Passing escrow_vault can save 1 account + source_tokens: ctx.accounts.escrow_vault.to_account_info(), + a_vault: ctx.accounts.a_vault.to_account_info(), + b_vault: ctx.accounts.b_vault.to_account_info(), + a_vault_lp: ctx.accounts.a_vault_lp.to_account_info(), + b_vault_lp: ctx.accounts.b_vault_lp.to_account_info(), + a_vault_lp_mint: ctx.accounts.a_vault_lp_mint.to_account_info(), + b_vault_lp_mint: ctx.accounts.b_vault_lp_mint.to_account_info(), + user_a_token: ctx.accounts.creator_a_token.to_account_info(), + user_b_token: ctx.accounts.creator_b_token.to_account_info(), + vault_program: ctx.accounts.dynamic_vault.to_account_info(), + escrow_vault: ctx.accounts.escrow_vault.to_account_info(), + token_program: ctx.accounts.token_program.to_account_info(), + a_token_vault: ctx.accounts.a_token_vault.to_account_info(), + b_token_vault: ctx.accounts.b_token_vault.to_account_info(), + }; + + let seeds = [ + b"creator".as_ref(), + &[*ctx.bumps.get("creator_authority").unwrap()], + ]; + + let signer_seeds = &[&seeds[..]]; + + let cpi_context = CpiContext::new_with_signer( + ctx.accounts.dynamic_amm.to_account_info(), + accounts, + signer_seeds, + ); + + // Claim max fee + dynamic_amm::cpi::claim_fee(cpi_context, u64::MAX) +} diff --git a/programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs b/programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs index 9d94f71..1995897 100644 --- a/programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs +++ b/programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs @@ -363,15 +363,10 @@ pub fn handle_lock_liquidity_pda_creator( let signer_seeds = &[&seeds[..]]; - msg!("Lock for pda"); - let cpi_context = CpiContext::new_with_signer(ctx.accounts.dynamic_amm_program.to_account_info(), accounts, signer_seeds); dynamic_amm::cpi::lock(cpi_context, pda_creator_lp_amount)?; - msg!("Lock for pda done"); - - // 3. Initialize lock escrow for user 1 let accounts = dynamic_amm::cpi::accounts::CreateLockEscrow { pool: ctx.accounts.pool.to_account_info(), diff --git a/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs b/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs index a772704..19f19c5 100644 --- a/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs +++ b/programs/cpi-example/src/instructions/dynamic_amm_cpi/mod.rs @@ -9,3 +9,6 @@ pub use initialize_permissionless_pool_with_config::*; pub mod lock_liquidity; pub use lock_liquidity::*; + +pub mod claim_fee; +pub use claim_fee::*; diff --git a/programs/cpi-example/src/lib.rs b/programs/cpi-example/src/lib.rs index d401130..57a4bf6 100644 --- a/programs/cpi-example/src/lib.rs +++ b/programs/cpi-example/src/lib.rs @@ -106,6 +106,7 @@ pub mod cpi_example { instructions::dynamic_amm_cpi::lock_liquidity::handle_lock_liquidity(ctx, allocations) } + // NOTE: Creator authority PDA lock LP token hold to self + other user pub fn dynamic_amm_lock_liquidity_pda_creator( ctx: Context, allocations: [u16; 2], @@ -115,4 +116,15 @@ pub mod cpi_example { allocations, ) } + + pub fn dynamic_amm_claim_fee(ctx: Context) -> Result<()> { + instructions::dynamic_amm_cpi::claim_fee::handle_claim_fee(ctx) + } + + // NOTE: Creator authority PDA claim fee. LP token must lock to creator authority PDA. + pub fn dynamic_amm_claim_fee_pda_creator( + ctx: Context, + ) -> Result<()> { + instructions::dynamic_amm_cpi::claim_fee::handle_claim_fee_pda_creator(ctx) + } } diff --git a/programs/cpi-example/tests/dynamic_amm_claim_fee.rs b/programs/cpi-example/tests/dynamic_amm_claim_fee.rs new file mode 100644 index 0000000..0f7a553 --- /dev/null +++ b/programs/cpi-example/tests/dynamic_amm_claim_fee.rs @@ -0,0 +1,488 @@ +mod helpers; +use anchor_lang::AccountDeserialize; +use anchor_lang::{InstructionData, ToAccountMetas}; +use anchor_spl::associated_token::get_associated_token_address; +use dynamic_amm::instructions::CustomizableParams; +use dynamic_amm::state::Pool; +use dynamic_vault::state::Vault; +use helpers::dynamic_amm_utils::setup_vault_from_cluster; +use helpers::dynamic_amm_utils::*; +use helpers::process_and_assert_ok; +use helpers::*; +use solana_program_test::*; +use solana_sdk::compute_budget::ComputeBudgetInstruction; +use solana_sdk::instruction::Instruction; +use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Keypair; +use solana_sdk::signer::Signer; +use solana_sdk::{system_program, sysvar}; + +async fn generate_swap_fees(banks_client: &mut BanksClient, pool: Pubkey, user: &Keypair) { + let pool_account = banks_client.get_account(pool).await.unwrap().unwrap(); + let pool_state = Pool::try_deserialize(&mut pool_account.data.as_ref()).unwrap(); + + let a_vault_account = banks_client + .get_account(pool_state.a_vault) + .await + .unwrap() + .unwrap(); + let a_vault_state = Vault::try_deserialize(&mut a_vault_account.data.as_ref()).unwrap(); + + let b_vault_account = banks_client + .get_account(pool_state.b_vault) + .await + .unwrap() + .unwrap(); + let b_vault_state = Vault::try_deserialize(&mut b_vault_account.data.as_ref()).unwrap(); + + for (in_token, out_token) in [ + (pool_state.token_a_mint, pool_state.token_b_mint), + (pool_state.token_b_mint, pool_state.token_a_mint), + ] { + let in_token_ata = get_associated_token_address(&user.pubkey(), &in_token); + let out_token_ata = get_associated_token_address(&user.pubkey(), &out_token); + + let protocol_token_fee = if in_token.eq(&pool_state.token_a_mint) { + pool_state.protocol_token_a_fee + } else { + pool_state.protocol_token_b_fee + }; + + let accounts = dynamic_amm::accounts::Swap { + pool, + user_source_token: in_token_ata, + user_destination_token: out_token_ata, + a_vault: pool_state.a_vault, + b_vault: pool_state.b_vault, + a_token_vault: a_vault_state.token_vault, + b_token_vault: b_vault_state.token_vault, + a_vault_lp: pool_state.a_vault_lp, + b_vault_lp: pool_state.b_vault_lp, + a_vault_lp_mint: a_vault_state.lp_mint, + b_vault_lp_mint: b_vault_state.lp_mint, + token_program: anchor_spl::token::ID, + protocol_token_fee, + user: user.pubkey(), + vault_program: dynamic_vault::ID, + } + .to_account_metas(None); + + let ix_data = dynamic_amm::instruction::Swap { + in_amount: 1_000_000, + minimum_out_amount: 0, + } + .data(); + + let instruction = Instruction { + program_id: dynamic_amm::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok(&[instruction], user, &[&user], banks_client).await; + } +} + +#[tokio::test] +async fn test_claim_fee_pda_creator() { + let mock_user = Keypair::new(); + + let mut test = ProgramTest::new( + "cpi_example", + cpi_example::id(), + processor!(cpi_example::entry), + ); + test.prefer_bpf(true); + + test.add_program("dynamic_amm", dynamic_amm::ID, None); + test.add_program("dynamic_vault", dynamic_vault::ID, None); + test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); + + let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + + let (mut banks_client, _, _) = test.start().await; + + let (creator_authority, _bump) = Pubkey::find_program_address(&[b"creator"], &cpi_example::ID); + + let creator_token_a = get_associated_token_address(&creator_authority, &JUP); + let creator_token_b = get_associated_token_address(&creator_authority, &USDC); + + let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); + let lp_mint = derive_lp_mint(pool_key); + + let creator_pool_lp = get_associated_token_address(&creator_authority, &lp_mint); + + let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); + let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); + + let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); + let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + + let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); + let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); + + let metadata_pda = derive_metadata_key(lp_mint); + + // 1. Initialize pool + let accounts = + cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPoolPdaCreator { + pool: pool_key, + creator_authority, + creator_token_a, + creator_token_b, + lp_mint, + token_a_mint: JUP, + token_b_mint: USDC, + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_token_vault: jup_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + payer: mock_user.pubkey(), + token_program: anchor_spl::token::ID, + a_vault_lp, + b_vault_lp, + protocol_token_a_fee, + protocol_token_b_fee, + creator_pool_lp, + payer_token_a, + payer_token_b, + rent: sysvar::rent::ID, + metadata_program: METAPLEX_PROGRAM_ID, + mint_metadata: metadata_pda, + vault_program: dynamic_vault::ID, + associated_token_program: anchor_spl::associated_token::ID, + system_program: system_program::ID, + dynamic_amm_program: dynamic_amm::ID, + } + .to_account_metas(None); + + let ix_data = + cpi_example::instruction::InitializeDynamicAmmCustomizablePermissionlessPoolPdaCreator { + token_a_amount: 100_000_000, + token_b_amount: 100_000_000, + params: CustomizableParams { + trade_fee_numerator: 10_000, + activation_point: None, + has_alpha_vault: false, + activation_type: 1, + padding: [0u8; 90], + }, + } + .data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + instruction, + ], + &mock_user, + &[&mock_user], + &mut banks_client, + ) + .await; + + // 2. Lock liquidity 50/50 to pda creator + user + let user = Keypair::new(); + let allocations = [5000_u16; 2]; + + let lock_escrow_creator = derive_lock_escrow(pool_key, creator_authority); + let lock_escrow_0 = derive_lock_escrow(pool_key, user.pubkey()); + + let escrow_vault_creator = get_associated_token_address(&lock_escrow_creator, &lp_mint); + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); + + let accounts = cpi_example::accounts::DynamicAmmLockLiquidityPdaCreator { + pool: pool_key, + creator_authority, + lock_escrow_creator, + lp_mint, + lock_escrow_0, + source_lp_tokens: creator_pool_lp, + escrow_vault_0, + escrow_vault_creator, + payer: mock_user.pubkey(), + user_0: user.pubkey(), + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_vault_lp, + b_vault_lp, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + dynamic_amm_program: dynamic_amm::ID, + system_program: system_program::ID, + token_program: anchor_spl::token::ID, + associated_token_program: anchor_spl::associated_token::ID, + } + .to_account_metas(None); + + let ix_data = + cpi_example::instruction::DynamicAmmLockLiquidityPdaCreator { allocations }.data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok(&[instruction], &mock_user, &[&mock_user], &mut banks_client).await; + + // 3. Generate some swap fee + generate_swap_fees(&mut banks_client, pool_key, &mock_user).await; + + // 4. Claim fee + let accounts = cpi_example::accounts::DynamicAmmClaimFeePdaCreator { + pool: pool_key, + lp_mint, + creator_authority, + lock_escrow: lock_escrow_creator, + escrow_vault: escrow_vault_creator, + a_token_vault: jup_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, + cpi_example_admin: mock_user.pubkey(), + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_vault_lp, + b_vault_lp, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + creator_a_token: creator_token_a, + creator_b_token: creator_token_b, + token_program: anchor_spl::token::ID, + dynamic_amm: dynamic_amm::ID, + dynamic_vault: dynamic_vault::ID, + } + .to_account_metas(None); + + let ix_data = cpi_example::instruction::DynamicAmmClaimFeePdaCreator {}.data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok(&[instruction], &mock_user, &[&mock_user], &mut banks_client).await; +} + +#[tokio::test] +async fn test_claim_fee() { + let mock_user = Keypair::new(); + + let mut test = ProgramTest::new( + "cpi_example", + cpi_example::id(), + processor!(cpi_example::entry), + ); + test.prefer_bpf(true); + + test.add_program("dynamic_amm", dynamic_amm::ID, None); + test.add_program("dynamic_vault", dynamic_vault::ID, None); + test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); + + let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + + let (mut banks_client, _, _) = test.start().await; + + let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); + let lp_mint = derive_lp_mint(pool_key); + + let payer_pool_lp = get_associated_token_address(&mock_user.pubkey(), &lp_mint); + + let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); + let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); + + let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); + let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + + let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); + let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); + + let metadata_pda = derive_metadata_key(lp_mint); + + // 1. Initialize pool + let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPool { + pool: pool_key, + lp_mint, + token_a_mint: JUP, + token_b_mint: USDC, + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_token_vault: jup_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + payer: mock_user.pubkey(), + token_program: anchor_spl::token::ID, + a_vault_lp, + b_vault_lp, + protocol_token_a_fee, + protocol_token_b_fee, + payer_pool_lp, + payer_token_a, + payer_token_b, + rent: sysvar::rent::ID, + metadata_program: METAPLEX_PROGRAM_ID, + mint_metadata: metadata_pda, + vault_program: dynamic_vault::ID, + associated_token_program: anchor_spl::associated_token::ID, + system_program: system_program::ID, + dynamic_amm_program: dynamic_amm::ID, + } + .to_account_metas(None); + + let ix_data = cpi_example::instruction::InitializeDynamicAmmCustomizablePermissionlessPool { + token_a_amount: 100_000_000, + token_b_amount: 100_000_000, + params: CustomizableParams { + trade_fee_numerator: 10_000, + activation_point: None, + has_alpha_vault: false, + activation_type: 1, + padding: [0u8; 90], + }, + } + .data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok( + &[ + ComputeBudgetInstruction::set_compute_unit_limit(1_400_000), + instruction, + ], + &mock_user, + &[&mock_user], + &mut banks_client, + ) + .await; + + // 2. Lock liquidity 50/50 to user 0 + user 1 + let user_0 = mock_user.pubkey(); + let user_1_kp = Keypair::new(); + let user_1 = user_1_kp.pubkey(); + + let allocations = [5000_u16; 2]; + + let lock_escrow_0 = derive_lock_escrow(pool_key, user_0); + let lock_escrow_1 = derive_lock_escrow(pool_key, user_1); + + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); + let escrow_vault_1 = get_associated_token_address(&lock_escrow_1, &lp_mint); + + let accounts = cpi_example::accounts::DynamicAmmLockLiquidity { + pool: pool_key, + lock_escrow_1, + lp_mint, + lock_escrow_0, + source_lp_tokens: payer_pool_lp, + escrow_vault_0, + escrow_vault_1, + payer: mock_user.pubkey(), + user_0, + user_1, + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_vault_lp, + b_vault_lp, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + dynamic_amm_program: dynamic_amm::ID, + system_program: system_program::ID, + token_program: anchor_spl::token::ID, + associated_token_program: anchor_spl::associated_token::ID, + } + .to_account_metas(None); + + let ix_data = cpi_example::instruction::DynamicAmmLockLiquidity { allocations }.data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok(&[instruction], &mock_user, &[&mock_user], &mut banks_client).await; + + // 3. Generate some swap fees + generate_swap_fees(&mut banks_client, pool_key, &mock_user).await; + + // 4. Claim fee for user 0 + 1 + for user in [&mock_user, &user_1_kp] { + let lock_escrow = derive_lock_escrow(pool_key, user.pubkey()); + let escrow_vault = get_associated_token_address(&lock_escrow, &lp_mint); + + let user_token_a = + get_associated_token_address(&user.pubkey(), &jup_vault.vault_state.token_mint); + + let user_token_b = + get_associated_token_address(&user.pubkey(), &usdc_vault.vault_state.token_mint); + + let init_user_token_a_ix = + spl_associated_token_account::instruction::create_associated_token_account_idempotent( + &mock_user.pubkey(), + &user.pubkey(), + &jup_vault.vault_state.token_mint, + &anchor_spl::token::ID, + ); + + let init_user_token_b_ix = + spl_associated_token_account::instruction::create_associated_token_account_idempotent( + &mock_user.pubkey(), + &user.pubkey(), + &usdc_vault.vault_state.token_mint, + &anchor_spl::token::ID, + ); + + let accounts = cpi_example::accounts::DynamicAmmClaimFee { + pool: pool_key, + a_vault: jup_vault.key, + b_vault: usdc_vault.key, + a_vault_lp, + b_vault_lp, + a_token_vault: jup_vault.vault_state.token_vault, + b_token_vault: usdc_vault.vault_state.token_vault, + a_vault_lp_mint: jup_vault.vault_state.lp_mint, + b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + lock_escrow, + escrow_vault, + lp_mint, + owner: user.pubkey(), + user_a_token: user_token_a, + user_b_token: user_token_b, + dynamic_amm: dynamic_amm::ID, + dynamic_vault: dynamic_vault::ID, + token_program: anchor_spl::token::ID, + } + .to_account_metas(None); + + let ix_data = cpi_example::instruction::DynamicAmmClaimFee {}.data(); + + let instruction = Instruction { + program_id: cpi_example::ID, + accounts, + data: ix_data, + }; + + process_and_assert_ok( + &[init_user_token_a_ix, init_user_token_b_ix, instruction], + &mock_user, + &[&mock_user, &user], + &mut banks_client, + ) + .await; + } +} From 2c9ffdb5b684999ddbeeb85423dbdb8c6c3cf211 Mon Sep 17 00:00:00 2001 From: codewithgun Date: Mon, 20 Jan 2025 18:20:53 +0800 Subject: [PATCH 4/6] update readme --- Cargo.lock | 1 + README.md | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 056f479..bbfc28c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1054,6 +1054,7 @@ dependencies = [ "solana-client", "solana-program-test", "solana-sdk", + "spl-associated-token-account 2.2.0", "stake_for_fee_interface", ] diff --git a/README.md b/README.md index b6c234a..6e745bf 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Meteora-CPI-examples -The repository containing examples for CPI (Cross-Program Invocation) to swap at [DLMM](https://github.com/meteoraAg/dlmm-sdk) and [Dynamic AMM](https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk) programs. +The repository containing examples for CPI (Cross-Program Invocation) to [DLMM](https://github.com/meteoraAg/dlmm-sdk) and [Dynamic AMM](https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk) programs. Disclaimer: This repository only serves as examples and is not intended for production use. @@ -18,6 +18,9 @@ Disclaimer: This repository only serves as examples and is not intended for prod - [CPI to Dynamic AMM initialize pool example](programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_customizable_permissionless_pool.rs) - [CPI to Dynamic AMM initialize pool with config example](programs/cpi-example/src/instructions/dynamic_amm_cpi/initialize_permissionless_pool_with_config.rs) +- [CPI to Dynamic AMM lock liquidity example](programs/cpi-example/src/instructions/dynamic_amm_cpi/lock_liquidity.rs) +- [CPI to Dynamic AMM claim fee example](programs/cpi-example/src/instructions/dynamic_amm_cpi/claim_fee.rs) + - [CPI to M3m3 initialize vault example](programs/cpi-example/src/instructions/m3m3_cpi/initialize_vault.rs) - [Tests](programs/cpi-example/tests/) From 565e37513d07f01dc46eab2604060a35a6b999c0 Mon Sep 17 00:00:00 2001 From: codewithgun Date: Mon, 20 Jan 2025 21:31:49 +0800 Subject: [PATCH 5/6] chore: CI test --- .github/workflows/ci-pr-main.yml | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/ci-pr-main.yml diff --git a/.github/workflows/ci-pr-main.yml b/.github/workflows/ci-pr-main.yml new file mode 100644 index 0000000..851dc75 --- /dev/null +++ b/.github/workflows/ci-pr-main.yml @@ -0,0 +1,36 @@ +name: CPI example + +on: + pull_request: + branches: + - main + +jobs: + program_changed_files: + runs-on: ubuntu-20.04 + outputs: + program: ${{steps.changed-files-specific.outputs.any_changed}} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Get specific changed files + id: changed-files-specific + uses: tj-actions/changed-files@v18.6 + with: + files: | + programs/cpi-example + + cpi_example_test: + runs-on: ubuntu-20.04 + needs: program_changed_files + if: needs.program_changed_files.outputs.program == 'true' + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.76 + override: true + - uses: Swatinem/rust-cache@v2 + - run: cargo test -- --nocapture + shell: bash From d4be42177c616205b5e23e03c0ace6d72be61fe3 Mon Sep 17 00:00:00 2001 From: codewithgun Date: Thu, 23 Jan 2025 17:59:08 +0800 Subject: [PATCH 6/6] chore: fix based on comment --- Cargo.lock | 31 +- programs/cpi-example/Cargo.toml | 8 +- .../tests/dynamic_amm_claim_fee.rs | 256 ++++++++-------- .../tests/dynamic_amm_init_pool.rs | 287 +++++++++--------- .../tests/dynamic_amm_lock_liquidity.rs | 196 ++++++------ .../tests/helpers/dynamic_amm_utils.rs | 93 +----- .../cpi-example/tests/helpers/m3m3_utils.rs | 17 -- programs/cpi-example/tests/helpers/mod.rs | 1 - .../tests/m3m3_initialize_vault.rs | 112 +++---- 9 files changed, 473 insertions(+), 528 deletions(-) delete mode 100644 programs/cpi-example/tests/helpers/m3m3_utils.rs diff --git a/Cargo.lock b/Cargo.lock index bbfc28c..75d4296 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -968,6 +968,29 @@ dependencies = [ "unreachable", ] +[[package]] +name = "common" +version = "0.0.1" +source = "git+https://github.com/meteoraAg/stake-for-fee-sdk?rev=60b1d2e8e3629e1c99377843fb377665247e82b1#60b1d2e8e3629e1c99377843fb377665247e82b1" +dependencies = [ + "solana-sdk", + "stake_for_fee_interface", +] + +[[package]] +name = "common" +version = "0.0.5" +source = "git+https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk?rev=726c425a439d1458a4c82abe5e8ae927aede9089#726c425a439d1458a4c82abe5e8ae927aede9089" +dependencies = [ + "anchor-lang", + "anchor-spl", + "dynamic-amm", + "dynamic-vault", + "mpl-token-metadata", + "solana-sdk", + "spl-associated-token-account 2.2.0", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1047,6 +1070,8 @@ dependencies = [ "assert_matches", "bincode", "bytemuck", + "common 0.0.1", + "common 0.0.5", "dynamic-amm", "dynamic-vault", "lb_clmm", @@ -1338,7 +1363,7 @@ dependencies = [ [[package]] name = "dynamic-amm" version = "0.6.1" -source = "git+https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk?rev=ae46f2a44edbd8231467effd530c1eeecc231a1b#ae46f2a44edbd8231467effd530c1eeecc231a1b" +source = "git+https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk?rev=726c425a439d1458a4c82abe5e8ae927aede9089#726c425a439d1458a4c82abe5e8ae927aede9089" dependencies = [ "anchor-lang", "anchor-spl", @@ -1348,7 +1373,7 @@ dependencies = [ [[package]] name = "dynamic-vault" version = "0.1.0" -source = "git+https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk?rev=ae46f2a44edbd8231467effd530c1eeecc231a1b#ae46f2a44edbd8231467effd530c1eeecc231a1b" +source = "git+https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk?rev=726c425a439d1458a4c82abe5e8ae927aede9089#726c425a439d1458a4c82abe5e8ae927aede9089" dependencies = [ "anchor-lang", "anchor-spl", @@ -4870,7 +4895,7 @@ dependencies = [ [[package]] name = "stake_for_fee_interface" version = "0.1.0" -source = "git+https://github.com/meteoraAg/stake-for-fee-sdk?rev=796ed09b500a92c36927915d22cce28ef78b2a8d#796ed09b500a92c36927915d22cce28ef78b2a8d" +source = "git+https://github.com/meteoraAg/stake-for-fee-sdk?rev=60b1d2e8e3629e1c99377843fb377665247e82b1#60b1d2e8e3629e1c99377843fb377665247e82b1" dependencies = [ "borsh 0.10.3", "num-derive 0.3.3", diff --git a/programs/cpi-example/Cargo.toml b/programs/cpi-example/Cargo.toml index 047e634..45a8d01 100644 --- a/programs/cpi-example/Cargo.toml +++ b/programs/cpi-example/Cargo.toml @@ -23,15 +23,17 @@ dlmm = { git = "https://github.com/MeteoraAg/dlmm-sdk/", package = "lb_clmm", re ] } dynamic-amm = { git = "https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk", features = [ "cpi", -], rev = "ae46f2a44edbd8231467effd530c1eeecc231a1b" } -m3m3 = { git = "https://github.com/meteoraAg/stake-for-fee-sdk", package = "stake_for_fee_interface", rev = "796ed09b500a92c36927915d22cce28ef78b2a8d" } +], rev = "726c425a439d1458a4c82abe5e8ae927aede9089", package = "dynamic-amm" } +dynamic-amm-common = { git = "https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk", rev = "726c425a439d1458a4c82abe5e8ae927aede9089", package = "common" } +m3m3 = { git = "https://github.com/meteoraAg/stake-for-fee-sdk", package = "stake_for_fee_interface", rev = "60b1d2e8e3629e1c99377843fb377665247e82b1" } +m3m3-common = { git = "https://github.com/meteoraAg/stake-for-fee-sdk", rev = "60b1d2e8e3629e1c99377843fb377665247e82b1", package = "common" } [dev-dependencies] solana-program-test = "1.16.0" solana-sdk = "1.16.0" dynamic-vault = { git = "https://github.com/mercurial-finance/mercurial-dynamic-amm-sdk", features = [ "cpi", -], rev = "ae46f2a44edbd8231467effd530c1eeecc231a1b" } +], rev = "726c425a439d1458a4c82abe5e8ae927aede9089" } bytemuck = { version = "1.13.1", features = ["derive", "min_const_generics"] } assert_matches = "1.5.0" solana-client = "1.16.0" diff --git a/programs/cpi-example/tests/dynamic_amm_claim_fee.rs b/programs/cpi-example/tests/dynamic_amm_claim_fee.rs index 0f7a553..89af8e4 100644 --- a/programs/cpi-example/tests/dynamic_amm_claim_fee.rs +++ b/programs/cpi-example/tests/dynamic_amm_claim_fee.rs @@ -4,9 +4,10 @@ use anchor_lang::{InstructionData, ToAccountMetas}; use anchor_spl::associated_token::get_associated_token_address; use dynamic_amm::instructions::CustomizableParams; use dynamic_amm::state::Pool; +use dynamic_amm_common::dynamic_amm::ix_account_builder::IxAccountBuilder; +use dynamic_amm_common::dynamic_amm::pda::{derive_lock_escrow_key, METAPLEX_PROGRAM_ID}; use dynamic_vault::state::Vault; use helpers::dynamic_amm_utils::setup_vault_from_cluster; -use helpers::dynamic_amm_utils::*; use helpers::process_and_assert_ok; use helpers::*; use solana_program_test::*; @@ -98,60 +99,62 @@ async fn test_claim_fee_pda_creator() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; let (creator_authority, _bump) = Pubkey::find_program_address(&[b"creator"], &cpi_example::ID); - let creator_token_a = get_associated_token_address(&creator_authority, &JUP); - let creator_token_b = get_associated_token_address(&creator_authority, &USDC); - - let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); - let lp_mint = derive_lp_mint(pool_key); - - let creator_pool_lp = get_associated_token_address(&creator_authority, &lp_mint); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + let init_pool_accounts = + IxAccountBuilder::initialize_customizable_permissionless_constant_product_pool( + JUP, + USDC, + creator_authority, + account_fetcher, + ) + .await + .unwrap(); let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); - let metadata_pda = derive_metadata_key(lp_mint); - // 1. Initialize pool let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPoolPdaCreator { - pool: pool_key, + pool: init_pool_accounts.pool, creator_authority, - creator_token_a, - creator_token_b, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + creator_token_a: init_pool_accounts.payer_token_a, + creator_token_b: init_pool_accounts.payer_token_b, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - creator_pool_lp, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + creator_pool_lp: init_pool_accounts.payer_pool_lp, payer_token_a, payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -194,29 +197,30 @@ async fn test_claim_fee_pda_creator() { let user = Keypair::new(); let allocations = [5000_u16; 2]; - let lock_escrow_creator = derive_lock_escrow(pool_key, creator_authority); - let lock_escrow_0 = derive_lock_escrow(pool_key, user.pubkey()); + let lock_escrow_creator = derive_lock_escrow_key(init_pool_accounts.pool, creator_authority); + let lock_escrow_0 = derive_lock_escrow_key(init_pool_accounts.pool, user.pubkey()); - let escrow_vault_creator = get_associated_token_address(&lock_escrow_creator, &lp_mint); - let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); + let escrow_vault_creator = + get_associated_token_address(&lock_escrow_creator, &init_pool_accounts.lp_mint); + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &init_pool_accounts.lp_mint); let accounts = cpi_example::accounts::DynamicAmmLockLiquidityPdaCreator { - pool: pool_key, + pool: init_pool_accounts.pool, creator_authority, lock_escrow_creator, - lp_mint, + lp_mint: init_pool_accounts.lp_mint, lock_escrow_0, - source_lp_tokens: creator_pool_lp, + source_lp_tokens: init_pool_accounts.payer_pool_lp, escrow_vault_0, escrow_vault_creator, payer: mock_user.pubkey(), user_0: user.pubkey(), - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_vault_lp, - b_vault_lp, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, dynamic_amm_program: dynamic_amm::ID, system_program: system_program::ID, token_program: anchor_spl::token::ID, @@ -236,26 +240,26 @@ async fn test_claim_fee_pda_creator() { process_and_assert_ok(&[instruction], &mock_user, &[&mock_user], &mut banks_client).await; // 3. Generate some swap fee - generate_swap_fees(&mut banks_client, pool_key, &mock_user).await; + generate_swap_fees(&mut banks_client, init_pool_accounts.pool, &mock_user).await; // 4. Claim fee let accounts = cpi_example::accounts::DynamicAmmClaimFeePdaCreator { - pool: pool_key, - lp_mint, + pool: init_pool_accounts.pool, + lp_mint: init_pool_accounts.lp_mint, creator_authority, lock_escrow: lock_escrow_creator, escrow_vault: escrow_vault_creator, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, cpi_example_admin: mock_user.pubkey(), - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_vault_lp, - b_vault_lp, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, - creator_a_token: creator_token_a, - creator_b_token: creator_token_b, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, + creator_a_token: init_pool_accounts.payer_token_a, + creator_b_token: init_pool_accounts.payer_token_b, token_program: anchor_spl::token::ID, dynamic_amm: dynamic_amm::ID, dynamic_vault: dynamic_vault::ID, @@ -288,51 +292,53 @@ async fn test_claim_fee() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; - let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); - let lp_mint = derive_lp_mint(pool_key); - - let payer_pool_lp = get_associated_token_address(&mock_user.pubkey(), &lp_mint); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); - - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); - - let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); - let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let metadata_pda = derive_metadata_key(lp_mint); + let init_pool_accounts = + IxAccountBuilder::initialize_customizable_permissionless_constant_product_pool( + JUP, + USDC, + mock_user.pubkey(), + account_fetcher, + ) + .await + .unwrap(); // 1. Initialize pool let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPool { - pool: pool_key, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + pool: init_pool_accounts.pool, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - payer_pool_lp, - payer_token_a, - payer_token_b, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + payer_pool_lp: init_pool_accounts.payer_pool_lp, + payer_token_a: init_pool_accounts.payer_token_a, + payer_token_b: init_pool_accounts.payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -377,29 +383,29 @@ async fn test_claim_fee() { let allocations = [5000_u16; 2]; - let lock_escrow_0 = derive_lock_escrow(pool_key, user_0); - let lock_escrow_1 = derive_lock_escrow(pool_key, user_1); + let lock_escrow_0 = derive_lock_escrow_key(init_pool_accounts.pool, user_0); + let lock_escrow_1 = derive_lock_escrow_key(init_pool_accounts.pool, user_1); - let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); - let escrow_vault_1 = get_associated_token_address(&lock_escrow_1, &lp_mint); + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &init_pool_accounts.lp_mint); + let escrow_vault_1 = get_associated_token_address(&lock_escrow_1, &init_pool_accounts.lp_mint); let accounts = cpi_example::accounts::DynamicAmmLockLiquidity { - pool: pool_key, + pool: init_pool_accounts.pool, lock_escrow_1, - lp_mint, + lp_mint: init_pool_accounts.lp_mint, lock_escrow_0, - source_lp_tokens: payer_pool_lp, + source_lp_tokens: init_pool_accounts.payer_pool_lp, escrow_vault_0, escrow_vault_1, payer: mock_user.pubkey(), user_0, user_1, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_vault_lp, - b_vault_lp, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, dynamic_amm_program: dynamic_amm::ID, system_program: system_program::ID, token_program: anchor_spl::token::ID, @@ -418,24 +424,24 @@ async fn test_claim_fee() { process_and_assert_ok(&[instruction], &mock_user, &[&mock_user], &mut banks_client).await; // 3. Generate some swap fees - generate_swap_fees(&mut banks_client, pool_key, &mock_user).await; + generate_swap_fees(&mut banks_client, init_pool_accounts.pool, &mock_user).await; // 4. Claim fee for user 0 + 1 for user in [&mock_user, &user_1_kp] { - let lock_escrow = derive_lock_escrow(pool_key, user.pubkey()); - let escrow_vault = get_associated_token_address(&lock_escrow, &lp_mint); + let lock_escrow = derive_lock_escrow_key(init_pool_accounts.pool, user.pubkey()); + let escrow_vault = get_associated_token_address(&lock_escrow, &init_pool_accounts.lp_mint); let user_token_a = - get_associated_token_address(&user.pubkey(), &jup_vault.vault_state.token_mint); + get_associated_token_address(&user.pubkey(), &init_pool_accounts.token_a_mint); let user_token_b = - get_associated_token_address(&user.pubkey(), &usdc_vault.vault_state.token_mint); + get_associated_token_address(&user.pubkey(), &init_pool_accounts.token_b_mint); let init_user_token_a_ix = spl_associated_token_account::instruction::create_associated_token_account_idempotent( &mock_user.pubkey(), &user.pubkey(), - &jup_vault.vault_state.token_mint, + &init_pool_accounts.token_a_mint, &anchor_spl::token::ID, ); @@ -443,23 +449,23 @@ async fn test_claim_fee() { spl_associated_token_account::instruction::create_associated_token_account_idempotent( &mock_user.pubkey(), &user.pubkey(), - &usdc_vault.vault_state.token_mint, + &init_pool_accounts.token_b_mint, &anchor_spl::token::ID, ); let accounts = cpi_example::accounts::DynamicAmmClaimFee { - pool: pool_key, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_vault_lp, - b_vault_lp, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + pool: init_pool_accounts.pool, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, lock_escrow, escrow_vault, - lp_mint, + lp_mint: init_pool_accounts.lp_mint, owner: user.pubkey(), user_a_token: user_token_a, user_b_token: user_token_b, diff --git a/programs/cpi-example/tests/dynamic_amm_init_pool.rs b/programs/cpi-example/tests/dynamic_amm_init_pool.rs index 3e7ea54..f117541 100644 --- a/programs/cpi-example/tests/dynamic_amm_init_pool.rs +++ b/programs/cpi-example/tests/dynamic_amm_init_pool.rs @@ -2,6 +2,8 @@ mod helpers; use anchor_lang::{InstructionData, ToAccountMetas}; use anchor_spl::associated_token::get_associated_token_address; use dynamic_amm::instructions::CustomizableParams; +use dynamic_amm_common::dynamic_amm::ix_account_builder::IxAccountBuilder; +use dynamic_amm_common::dynamic_amm::pda::METAPLEX_PROGRAM_ID; use helpers::dynamic_amm_utils::setup_vault_from_cluster; use helpers::dynamic_amm_utils::*; use helpers::process_and_assert_ok; @@ -29,59 +31,61 @@ async fn test_initialize_customizable_permissionless_pool_with_pda_creator() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; let (creator_authority, _bump) = Pubkey::find_program_address(&[b"creator"], &cpi_example::ID); - let creator_token_a = get_associated_token_address(&creator_authority, &JUP); - let creator_token_b = get_associated_token_address(&creator_authority, &USDC); - - let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); - let lp_mint = derive_lp_mint(pool_key); - - let creator_pool_lp = get_associated_token_address(&creator_authority, &lp_mint); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + let init_pool_accounts = + IxAccountBuilder::initialize_customizable_permissionless_constant_product_pool( + JUP, + USDC, + creator_authority, + account_fetcher, + ) + .await + .unwrap(); let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); - let metadata_pda = derive_metadata_key(lp_mint); - let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPoolPdaCreator { - pool: pool_key, + pool: init_pool_accounts.pool, creator_authority, - creator_token_a, - creator_token_b, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + creator_token_a: init_pool_accounts.payer_token_a, + creator_token_b: init_pool_accounts.payer_token_b, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - creator_pool_lp, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + creator_pool_lp: init_pool_accounts.payer_pool_lp, payer_token_a, payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -136,50 +140,52 @@ async fn test_initialize_customizable_permissionless_pool() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; - let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); - let lp_mint = derive_lp_mint(pool_key); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); - - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); - - let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); - let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); - - let payer_pool_lp = get_associated_token_address(&mock_user.pubkey(), &lp_mint); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let metadata_pda = derive_metadata_key(lp_mint); + let init_pool_accounts = + IxAccountBuilder::initialize_customizable_permissionless_constant_product_pool( + JUP, + USDC, + mock_user.pubkey(), + account_fetcher, + ) + .await + .unwrap(); let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPool { - pool: pool_key, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + pool: init_pool_accounts.pool, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - payer_pool_lp, - payer_token_a, - payer_token_b, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + payer_pool_lp: init_pool_accounts.payer_pool_lp, + payer_token_a: init_pool_accounts.payer_token_a, + payer_token_b: init_pool_accounts.payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -233,52 +239,54 @@ async fn test_initialize_permissionless_pool_with_config() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let sol_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; - + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; setup_pool_config_from_cluster(&mut test, CONFIG).await; let (mut banks_client, _, _) = test.start().await; - let pool_key = derive_permissionless_constant_product_pool_with_config_key(JUP, USDC, CONFIG); - let lp_mint = derive_lp_mint(pool_key); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(sol_vault.key, pool_key); - - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); - - let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); - let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); - - let payer_pool_lp = get_associated_token_address(&mock_user.pubkey(), &lp_mint); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let metadata_pda = derive_metadata_key(lp_mint); + let init_pool_accounts = + IxAccountBuilder::initialize_permissionless_constant_product_pool_with_config_accounts( + JUP, + USDC, + CONFIG, + mock_user.pubkey(), + account_fetcher, + ) + .await + .unwrap(); let accounts = cpi_example::accounts::DynamicAmmInitializePermissionlessPoolWithConfig { - pool: pool_key, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: sol_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: sol_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: sol_vault.vault_state.lp_mint, + pool: init_pool_accounts.pool, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - payer_pool_lp, - payer_token_a, - payer_token_b, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + payer_pool_lp: init_pool_accounts.payer_pool_lp, + payer_token_a: init_pool_accounts.payer_token_a, + payer_token_b: init_pool_accounts.payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -327,65 +335,68 @@ async fn test_initialize_permissionless_pool_with_config_pda_pool_creator() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let sol_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; setup_pool_config_from_cluster(&mut test, CONFIG).await; let (mut banks_client, _, _) = test.start().await; let (creator_authority, _bump) = Pubkey::find_program_address(&[b"creator"], &cpi_example::ID); - let creator_token_a = get_associated_token_address(&creator_authority, &JUP); - let creator_token_b = get_associated_token_address(&creator_authority, &USDC); - - let pool_key = derive_permissionless_constant_product_pool_with_config_key(JUP, USDC, CONFIG); - let lp_mint = derive_lp_mint(pool_key); - - let creator_pool_lp = get_associated_token_address(&creator_authority, &lp_mint); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(sol_vault.key, pool_key); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + let init_pool_accounts = + IxAccountBuilder::initialize_permissionless_constant_product_pool_with_config_accounts( + JUP, + USDC, + CONFIG, + creator_authority, + account_fetcher, + ) + .await + .unwrap(); let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); - let metadata_pda = derive_metadata_key(lp_mint); - let accounts = cpi_example::accounts::DynamicAmmInitializePermissionlessPoolWithConfigPdaCreator { - pool: pool_key, + pool: init_pool_accounts.pool, creator_authority, - creator_token_a, - creator_token_b, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: sol_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: sol_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: sol_vault.vault_state.lp_mint, + creator_token_a: init_pool_accounts.payer_token_a, + creator_token_b: init_pool_accounts.payer_token_b, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - creator_pool_lp, - config: CONFIG, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + creator_pool_lp: init_pool_accounts.payer_pool_lp, payer_token_a, payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, dynamic_amm_program: dynamic_amm::ID, + config: CONFIG, } .to_account_metas(None); diff --git a/programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs b/programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs index d51adc2..b0b93e9 100644 --- a/programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs +++ b/programs/cpi-example/tests/dynamic_amm_lock_liquidity.rs @@ -2,8 +2,9 @@ mod helpers; use anchor_lang::{InstructionData, ToAccountMetas}; use anchor_spl::associated_token::get_associated_token_address; use dynamic_amm::instructions::CustomizableParams; +use dynamic_amm_common::dynamic_amm::ix_account_builder::IxAccountBuilder; +use dynamic_amm_common::dynamic_amm::pda::{derive_lock_escrow_key, METAPLEX_PROGRAM_ID}; use helpers::dynamic_amm_utils::setup_vault_from_cluster; -use helpers::dynamic_amm_utils::*; use helpers::process_and_assert_ok; use helpers::*; use solana_program_test::*; @@ -29,60 +30,62 @@ async fn test_lock_liquidity_pda_creator() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; let (creator_authority, _bump) = Pubkey::find_program_address(&[b"creator"], &cpi_example::ID); - let creator_token_a = get_associated_token_address(&creator_authority, &JUP); - let creator_token_b = get_associated_token_address(&creator_authority, &USDC); - - let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); - let lp_mint = derive_lp_mint(pool_key); - - let creator_pool_lp = get_associated_token_address(&creator_authority, &lp_mint); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); + let init_pool_accounts = + IxAccountBuilder::initialize_customizable_permissionless_constant_product_pool( + JUP, + USDC, + creator_authority, + account_fetcher, + ) + .await + .unwrap(); let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); - let metadata_pda = derive_metadata_key(lp_mint); - // 1. Initialize pool let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPoolPdaCreator { - pool: pool_key, + pool: init_pool_accounts.pool, creator_authority, - creator_token_a, - creator_token_b, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + creator_token_a: init_pool_accounts.payer_token_a, + creator_token_b: init_pool_accounts.payer_token_b, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - creator_pool_lp, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + creator_pool_lp: init_pool_accounts.payer_pool_lp, payer_token_a, payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -125,29 +128,30 @@ async fn test_lock_liquidity_pda_creator() { let user = Keypair::new(); let allocations = [5000_u16; 2]; - let lock_escrow_creator = derive_lock_escrow(pool_key, creator_authority); - let lock_escrow_0 = derive_lock_escrow(pool_key, user.pubkey()); + let lock_escrow_creator = derive_lock_escrow_key(init_pool_accounts.pool, creator_authority); + let lock_escrow_0 = derive_lock_escrow_key(init_pool_accounts.pool, user.pubkey()); - let escrow_vault_creator = get_associated_token_address(&lock_escrow_creator, &lp_mint); - let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); + let escrow_vault_creator = + get_associated_token_address(&lock_escrow_creator, &init_pool_accounts.lp_mint); + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &init_pool_accounts.lp_mint); let accounts = cpi_example::accounts::DynamicAmmLockLiquidityPdaCreator { - pool: pool_key, + pool: init_pool_accounts.pool, creator_authority, lock_escrow_creator, - lp_mint, + lp_mint: init_pool_accounts.lp_mint, lock_escrow_0, - source_lp_tokens: creator_pool_lp, + source_lp_tokens: init_pool_accounts.payer_pool_lp, escrow_vault_0, escrow_vault_creator, payer: mock_user.pubkey(), user_0: user.pubkey(), - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_vault_lp, - b_vault_lp, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, dynamic_amm_program: dynamic_amm::ID, system_program: system_program::ID, token_program: anchor_spl::token::ID, @@ -191,51 +195,53 @@ async fn test_lock_liquidity() { test.add_program("dynamic_vault", dynamic_vault::ID, None); test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; - let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); - let lp_mint = derive_lp_mint(pool_key); - - let payer_pool_lp = get_associated_token_address(&mock_user.pubkey(), &lp_mint); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); - - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); - - let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); - let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let metadata_pda = derive_metadata_key(lp_mint); + let init_pool_accounts = + IxAccountBuilder::initialize_customizable_permissionless_constant_product_pool( + JUP, + USDC, + mock_user.pubkey(), + account_fetcher, + ) + .await + .unwrap(); // 1. Initialize pool let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPool { - pool: pool_key, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + pool: init_pool_accounts.pool, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - payer_pool_lp, - payer_token_a, - payer_token_b, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + payer_pool_lp: init_pool_accounts.payer_pool_lp, + payer_token_a: init_pool_accounts.payer_token_a, + payer_token_b: init_pool_accounts.payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -280,29 +286,29 @@ async fn test_lock_liquidity() { let allocations = [5000_u16; 2]; - let lock_escrow_0 = derive_lock_escrow(pool_key, user_0); - let lock_escrow_1 = derive_lock_escrow(pool_key, user_1); + let lock_escrow_0 = derive_lock_escrow_key(init_pool_accounts.pool, user_0); + let lock_escrow_1 = derive_lock_escrow_key(init_pool_accounts.pool, user_1); - let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &lp_mint); - let escrow_vault_1 = get_associated_token_address(&lock_escrow_1, &lp_mint); + let escrow_vault_0 = get_associated_token_address(&lock_escrow_0, &init_pool_accounts.lp_mint); + let escrow_vault_1 = get_associated_token_address(&lock_escrow_1, &init_pool_accounts.lp_mint); let accounts = cpi_example::accounts::DynamicAmmLockLiquidity { - pool: pool_key, + pool: init_pool_accounts.pool, lock_escrow_1, - lp_mint, + lp_mint: init_pool_accounts.lp_mint, lock_escrow_0, - source_lp_tokens: payer_pool_lp, + source_lp_tokens: init_pool_accounts.payer_pool_lp, escrow_vault_0, escrow_vault_1, payer: mock_user.pubkey(), user_0, user_1, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_vault_lp, - b_vault_lp, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, dynamic_amm_program: dynamic_amm::ID, system_program: system_program::ID, token_program: anchor_spl::token::ID, diff --git a/programs/cpi-example/tests/helpers/dynamic_amm_utils.rs b/programs/cpi-example/tests/helpers/dynamic_amm_utils.rs index f524c45..0021320 100644 --- a/programs/cpi-example/tests/helpers/dynamic_amm_utils.rs +++ b/programs/cpi-example/tests/helpers/dynamic_amm_utils.rs @@ -3,6 +3,7 @@ use anchor_spl::{ associated_token::get_associated_token_address, token::spl_token::state::AccountState, }; use dynamic_amm::state::Pool; +use dynamic_amm_common::dynamic_vault::pda::derive_vault_key; use dynamic_vault::state::Vault; use solana_client::nonblocking::rpc_client::RpcClient; use solana_program_test::ProgramTest; @@ -10,96 +11,6 @@ use solana_sdk::{account::Account, pubkey::Pubkey}; use super::{utils::add_packable_account, RPC}; -const VAULT_BASE_KEY: Pubkey = solana_sdk::pubkey!("HWzXGcGHy4tcpYfaRDCyLNzXqBTv3E6BttpCH2vJxArv"); -pub const METAPLEX_PROGRAM_ID: Pubkey = - solana_sdk::pubkey!("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"); - -/// get first key, this is same as max(key1, key2) -pub fn get_first_key(key1: Pubkey, key2: Pubkey) -> Pubkey { - if key1 > key2 { - return key1; - } - key2 -} -/// get second key, this is same as min(key1, key2) -pub fn get_second_key(key1: Pubkey, key2: Pubkey) -> Pubkey { - if key1 > key2 { - return key2; - } - key1 -} - -pub fn derive_protocol_fee_key(mint_key: Pubkey, pool_key: Pubkey) -> Pubkey { - Pubkey::find_program_address( - &[b"fee", mint_key.as_ref(), pool_key.as_ref()], - &dynamic_amm::ID, - ) - .0 -} - -pub fn derive_metadata_key(lp_mint: Pubkey) -> Pubkey { - Pubkey::find_program_address( - &[b"metadata", METAPLEX_PROGRAM_ID.as_ref(), lp_mint.as_ref()], - &METAPLEX_PROGRAM_ID, - ) - .0 -} - -pub fn derive_vault_lp_key(vault_key: Pubkey, pool_key: Pubkey) -> Pubkey { - Pubkey::find_program_address(&[vault_key.as_ref(), pool_key.as_ref()], &dynamic_amm::ID).0 -} - -pub fn derive_lp_mint(pool_key: Pubkey) -> Pubkey { - Pubkey::find_program_address(&[b"lp_mint", pool_key.as_ref()], &dynamic_amm::ID).0 -} - -pub fn derive_lock_escrow(pool_key: Pubkey, owner_key: Pubkey) -> Pubkey { - Pubkey::find_program_address( - &[b"lock_escrow", pool_key.as_ref(), owner_key.as_ref()], - &dynamic_amm::ID, - ) - .0 -} - -pub fn derive_customizable_permissionless_constant_product_pool_key( - mint_a: Pubkey, - mint_b: Pubkey, -) -> Pubkey { - Pubkey::find_program_address( - &[ - b"pool", - get_first_key(mint_a, mint_b).as_ref(), - get_second_key(mint_a, mint_b).as_ref(), - ], - &dynamic_amm::ID, - ) - .0 -} - -pub fn derive_permissionless_constant_product_pool_with_config_key( - mint_a: Pubkey, - mint_b: Pubkey, - config: Pubkey, -) -> Pubkey { - Pubkey::find_program_address( - &[ - get_first_key(mint_a, mint_b).as_ref(), - get_second_key(mint_a, mint_b).as_ref(), - config.as_ref(), - ], - &dynamic_amm::ID, - ) - .0 -} - -pub fn derive_vault_address(mint: Pubkey) -> Pubkey { - Pubkey::find_program_address( - &[b"vault", &mint.to_bytes(), &VAULT_BASE_KEY.to_bytes()], - &dynamic_vault::ID, - ) - .0 -} - pub struct VaultSetupContext { pub key: Pubkey, pub vault_state: Vault, @@ -119,7 +30,7 @@ pub async fn setup_vault_from_cluster( ) -> VaultSetupContext { let rpc_client = RpcClient::new(RPC.to_owned()); - let vault_key = derive_vault_address(mint); + let vault_key = derive_vault_key(mint); let vault_account = rpc_client.get_account(&vault_key).await.unwrap(); let vault_state = Vault::try_deserialize(&mut vault_account.data.as_ref()).unwrap(); diff --git a/programs/cpi-example/tests/helpers/m3m3_utils.rs b/programs/cpi-example/tests/helpers/m3m3_utils.rs deleted file mode 100644 index 1502a26..0000000 --- a/programs/cpi-example/tests/helpers/m3m3_utils.rs +++ /dev/null @@ -1,17 +0,0 @@ -use solana_sdk::pubkey::Pubkey; - -pub fn derive_m3m3_vault_key(pool_key: Pubkey) -> Pubkey { - Pubkey::find_program_address(&[b"vault", pool_key.as_ref()], &m3m3::ID).0 -} - -pub fn derive_top_staker_list_key(vault_key: Pubkey) -> Pubkey { - Pubkey::find_program_address(&[b"list", vault_key.as_ref()], &m3m3::ID).0 -} - -pub fn derive_full_balance_list_key(vault_key: Pubkey) -> Pubkey { - Pubkey::find_program_address(&[b"balance", vault_key.as_ref()], &m3m3::ID).0 -} - -pub fn derive_m3m3_event_authority_key() -> Pubkey { - Pubkey::find_program_address(&[b"__event_authority"], &m3m3::ID).0 -} diff --git a/programs/cpi-example/tests/helpers/mod.rs b/programs/cpi-example/tests/helpers/mod.rs index d6abf60..c41764f 100644 --- a/programs/cpi-example/tests/helpers/mod.rs +++ b/programs/cpi-example/tests/helpers/mod.rs @@ -2,7 +2,6 @@ use solana_sdk::pubkey::Pubkey; pub mod dlmm_utils; pub mod dynamic_amm_utils; -pub mod m3m3_utils; mod utils; pub use utils::process_and_assert_ok; diff --git a/programs/cpi-example/tests/m3m3_initialize_vault.rs b/programs/cpi-example/tests/m3m3_initialize_vault.rs index bd64cbe..b173468 100644 --- a/programs/cpi-example/tests/m3m3_initialize_vault.rs +++ b/programs/cpi-example/tests/m3m3_initialize_vault.rs @@ -2,15 +2,15 @@ mod helpers; use anchor_lang::{InstructionData, ToAccountMetas}; use anchor_spl::associated_token::get_associated_token_address; use dynamic_amm::instructions::CustomizableParams; +use dynamic_amm_common::dynamic_amm::ix_account_builder::IxAccountBuilder; +use dynamic_amm_common::dynamic_amm::pda::derive_lock_escrow_key; +use dynamic_amm_common::dynamic_amm::pda::METAPLEX_PROGRAM_ID; use helpers::dynamic_amm_utils::setup_vault_from_cluster; -use helpers::dynamic_amm_utils::*; use helpers::process_and_assert_ok; use helpers::*; use m3m3::InitializeVaultParams; -use m3m3_utils::derive_full_balance_list_key; -use m3m3_utils::derive_m3m3_event_authority_key; -use m3m3_utils::derive_m3m3_vault_key; -use m3m3_utils::derive_top_staker_list_key; +use m3m3_common::pda::*; + use solana_program_test::*; use solana_sdk::compute_budget::ComputeBudgetInstruction; use solana_sdk::instruction::Instruction; @@ -34,51 +34,53 @@ async fn test_initialize_m3m3_vault() { test.add_program("metaplex", METAPLEX_PROGRAM_ID, None); test.add_program("m3m3", m3m3::ID, None); - let jup_vault = setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; - let usdc_vault = setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, JUP, mock_user.pubkey()).await; + setup_vault_from_cluster(&mut test, USDC, mock_user.pubkey()).await; let (mut banks_client, _, _) = test.start().await; // 1. Create pool - let pool_key = derive_customizable_permissionless_constant_product_pool_key(JUP, USDC); - let lp_mint = derive_lp_mint(pool_key); - - let a_vault_lp = derive_vault_lp_key(jup_vault.key, pool_key); - let b_vault_lp = derive_vault_lp_key(usdc_vault.key, pool_key); - - let protocol_token_a_fee = derive_protocol_fee_key(JUP, pool_key); - let protocol_token_b_fee = derive_protocol_fee_key(USDC, pool_key); - - let payer_token_a = get_associated_token_address(&mock_user.pubkey(), &JUP); - let payer_token_b = get_associated_token_address(&mock_user.pubkey(), &USDC); - - let payer_pool_lp = get_associated_token_address(&mock_user.pubkey(), &lp_mint); + let account_fetcher = |address| { + let mut banks_client = banks_client.clone(); + async move { + let account = banks_client.get_account(address).await.unwrap().unwrap(); + Ok(account) + } + }; - let metadata_pda = derive_metadata_key(lp_mint); + let init_pool_accounts = + IxAccountBuilder::initialize_customizable_permissionless_constant_product_pool( + JUP, + USDC, + mock_user.pubkey(), + account_fetcher, + ) + .await + .unwrap(); let accounts = cpi_example::accounts::DynamicAmmInitializeCustomizablePermissionlessPool { - pool: pool_key, - lp_mint, - token_a_mint: JUP, - token_b_mint: USDC, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_token_vault: jup_vault.vault_state.token_vault, - b_token_vault: usdc_vault.vault_state.token_vault, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, + pool: init_pool_accounts.pool, + lp_mint: init_pool_accounts.lp_mint, + token_a_mint: init_pool_accounts.token_a_mint, + token_b_mint: init_pool_accounts.token_b_mint, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_token_vault: init_pool_accounts.a_token_vault, + b_token_vault: init_pool_accounts.b_token_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, - a_vault_lp, - b_vault_lp, - protocol_token_a_fee, - protocol_token_b_fee, - payer_pool_lp, - payer_token_a, - payer_token_b, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + protocol_token_a_fee: init_pool_accounts.protocol_token_a_fee, + protocol_token_b_fee: init_pool_accounts.protocol_token_b_fee, + payer_pool_lp: init_pool_accounts.payer_pool_lp, + payer_token_a: init_pool_accounts.payer_token_a, + payer_token_b: init_pool_accounts.payer_token_b, rent: sysvar::rent::ID, metadata_program: METAPLEX_PROGRAM_ID, - mint_metadata: metadata_pda, + mint_metadata: init_pool_accounts.mint_metadata, vault_program: dynamic_vault::ID, associated_token_program: anchor_spl::associated_token::ID, system_program: system_program::ID, @@ -117,38 +119,38 @@ async fn test_initialize_m3m3_vault() { .await; // 2. Create lock escrow + lock + initialize m3m3 vault - let m3m3_vault = derive_m3m3_vault_key(pool_key); - let lock_escrow = derive_lock_escrow(pool_key, m3m3_vault); - let escrow_vault = get_associated_token_address(&lock_escrow, &lp_mint); + let m3m3_vault = derive_m3m3_vault_key(init_pool_accounts.pool); + let lock_escrow = derive_lock_escrow_key(init_pool_accounts.pool, m3m3_vault); + let escrow_vault = get_associated_token_address(&lock_escrow, &init_pool_accounts.lp_mint); let m3m3_event_authority = derive_m3m3_event_authority_key(); let stake_token_vault = - get_associated_token_address(&m3m3_vault, &jup_vault.vault_state.token_mint); + get_associated_token_address(&m3m3_vault, &init_pool_accounts.token_a_mint); let quote_token_vault = - get_associated_token_address(&m3m3_vault, &usdc_vault.vault_state.token_mint); + get_associated_token_address(&m3m3_vault, &init_pool_accounts.token_b_mint); let top_staker_list = derive_top_staker_list_key(m3m3_vault); let full_balance_list = derive_full_balance_list_key(m3m3_vault); let accounts = cpi_example::accounts::InitializeM3m3Vault { - pool: pool_key, + pool: init_pool_accounts.pool, lock_escrow, - a_vault: jup_vault.key, - b_vault: usdc_vault.key, - a_vault_lp_mint: jup_vault.vault_state.lp_mint, - b_vault_lp_mint: usdc_vault.vault_state.lp_mint, - a_vault_lp, - b_vault_lp, - lp_mint, - source_lp_tokens: payer_pool_lp, + a_vault: init_pool_accounts.a_vault, + b_vault: init_pool_accounts.b_vault, + a_vault_lp_mint: init_pool_accounts.a_vault_lp_mint, + b_vault_lp_mint: init_pool_accounts.b_vault_lp_mint, + a_vault_lp: init_pool_accounts.a_vault_lp, + b_vault_lp: init_pool_accounts.b_vault_lp, + lp_mint: init_pool_accounts.lp_mint, + source_lp_tokens: init_pool_accounts.payer_pool_lp, payer: mock_user.pubkey(), token_program: anchor_spl::token::ID, escrow_vault, m3m3_event_authority, - stake_mint: jup_vault.vault_state.token_mint, + stake_mint: init_pool_accounts.token_a_mint, stake_token_vault, top_staker_list, full_balance_list, m3m3_vault, - quote_mint: usdc_vault.vault_state.token_mint, + quote_mint: init_pool_accounts.token_b_mint, quote_token_vault, system_program: system_program::ID, dynamic_amm_program: dynamic_amm::ID,