From 119ed36bf9f33335ed8d671780502bc37d9f1605 Mon Sep 17 00:00:00 2001 From: Eddy <51528872+0x777A@users.noreply.github.com> Date: Thu, 21 Nov 2024 14:30:14 +0800 Subject: [PATCH] support create token2022 nft mint with metadata extension when open position (#99) * support create token2022 nft mint with metadata extension when open position * remove unnecessary use of InterfaceAccount in V1 instruction --- .gitignore | 3 +- Anchor.toml | 11 +- Cargo.lock | 1 + README.md | 2 +- client/src/instructions/amm_instructions.rs | 4 +- .../instructions/events_instructions_parse.rs | 2 +- programs/amm/Cargo.toml | 1 + programs/amm/src/error.rs | 4 +- .../instructions/admin/collect_fund_fee.rs | 16 +- .../admin/collect_protocol_fee.rs | 16 +- .../amm/src/instructions/close_position.rs | 53 +- .../instructions/collect_remaining_rewards.rs | 4 +- programs/amm/src/instructions/create_pool.rs | 2 +- .../src/instructions/decrease_liquidity.rs | 212 ++----- .../src/instructions/decrease_liquidity_v2.rs | 137 +++++ .../src/instructions/increase_liquidity.rs | 159 +---- .../src/instructions/increase_liquidity_v2.rs | 132 +++++ .../amm/src/instructions/initialize_reward.rs | 4 +- programs/amm/src/instructions/mod.rs | 12 + .../amm/src/instructions/open_position.rs | 541 ++++++++---------- .../amm/src/instructions/open_position_v2.rs | 208 +++++++ .../open_position_with_token22_nft.rs | 217 +++++++ .../amm/src/instructions/set_reward_params.rs | 4 +- programs/amm/src/instructions/swap.rs | 55 +- programs/amm/src/instructions/swap_v2.rs | 16 +- programs/amm/src/lib.rs | 56 +- programs/amm/src/states/operation_account.rs | 2 +- programs/amm/src/states/oracle.rs | 6 +- programs/amm/src/states/personal_position.rs | 12 +- programs/amm/src/states/pool.rs | 4 +- programs/amm/src/states/tick_array.rs | 4 +- .../src/states/tickarray_bitmap_extension.rs | 2 +- programs/amm/src/util/access_control.rs | 21 - programs/amm/src/util/mod.rs | 3 - programs/amm/src/util/token.rs | 152 +++-- 35 files changed, 1280 insertions(+), 798 deletions(-) create mode 100644 programs/amm/src/instructions/decrease_liquidity_v2.rs create mode 100644 programs/amm/src/instructions/increase_liquidity_v2.rs create mode 100644 programs/amm/src/instructions/open_position_v2.rs create mode 100644 programs/amm/src/instructions/open_position_with_token22_nft.rs delete mode 100644 programs/amm/src/util/access_control.rs diff --git a/.gitignore b/.gitignore index 4bec6730..6196784a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,9 @@ KeyPairs client/target **/*.rs.bk node_modules -tests/dist +tests test-ledger +.yarn* yarn.lock Makefile sdk/node_modules diff --git a/Anchor.toml b/Anchor.toml index 86057ceb..ca31f990 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -3,19 +3,12 @@ anchor_version = "0.29" [features] seeds = false -[programs.mainnet] +[programs.localnet] amm_v3 = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK" -[programs.devnet] -amm_v3 = "devi51mZmdwUJGU9hjN27vEz64Gps7uUefqxg27EAtH" - [registry] url = "https://github.com/raydium-io/raydium-amm-v3" [provider] cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[[test.genesis]] -address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" -program = "./external-programs/spl_token_metadata.so" \ No newline at end of file +wallet = "~/.config/solana/id.json" diff --git a/Cargo.lock b/Cargo.lock index c1c03411..a5c4e6d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3293,6 +3293,7 @@ dependencies = [ "solana-program", "solana-security-txt", "spl-memo 4.0.0", + "spl-token-metadata-interface", "uint", ] diff --git a/README.md b/README.md index ef79301d..a4527316 100644 --- a/README.md +++ b/README.md @@ -52,4 +52,4 @@ Attention, check your configuration and confirm the environment you want to depl An example of calling clmm can be found [here](https://github.com/raydium-io/raydium-cpi-example/tree/master/clmm-cpi) # License -The source code is licensed under Apache 2.0. \ No newline at end of file +The source code is [licensed](https://github.com/raydium-io/raydium-clmm/blob/master/LICENSE) under Apache 2.0. \ No newline at end of file diff --git a/client/src/instructions/amm_instructions.rs b/client/src/instructions/amm_instructions.rs index 7178c22f..f5082626 100644 --- a/client/src/instructions/amm_instructions.rs +++ b/client/src/instructions/amm_instructions.rs @@ -212,7 +212,7 @@ pub fn open_position_instr( tick_upper_index: i32, tick_array_lower_start_index: i32, tick_array_upper_start_index: i32, - with_matedata: bool, + with_metadata: bool, ) -> Result> { let payer = read_keypair_file(&config.payer_path)?; let url = Cluster::Custom(config.http_url.clone(), config.ws_url.clone()); @@ -293,7 +293,7 @@ pub fn open_position_instr( tick_upper_index, tick_array_lower_start_index, tick_array_upper_start_index, - with_matedata, + with_metadata, base_flag: None, }) .instructions()?; diff --git a/client/src/instructions/events_instructions_parse.rs b/client/src/instructions/events_instructions_parse.rs index 38e9b73d..65155574 100644 --- a/client/src/instructions/events_instructions_parse.rs +++ b/client/src/instructions/events_instructions_parse.rs @@ -612,7 +612,7 @@ pub fn handle_program_instruction( amount_0_max: instr.amount_0_max, amount_1_max: instr.amount_1_max, base_flag: instr.base_flag, - with_metadata: instr.with_matedata, + with_metadata: instr.with_metadata, } } } diff --git a/programs/amm/Cargo.toml b/programs/amm/Cargo.toml index c9b17fa4..16f0ebcf 100644 --- a/programs/amm/Cargo.toml +++ b/programs/amm/Cargo.toml @@ -29,6 +29,7 @@ solana-program = "<1.17.0" spl-memo = "4.0.0" uint = { git = "https://github.com/raydium-io/parity-common", package = "uint" } mpl-token-metadata = { version = "^1.11.0", features = ["no-entrypoint"] } +spl-token-metadata-interface = { version = "=0.2.0" } bytemuck = { version = "1.19.0", features = ["derive", "min_const_generics"] } arrayref = { version = "0.3.6" } solana-security-txt = "1.1.1" diff --git a/programs/amm/src/error.rs b/programs/amm/src/error.rs index 8ddc5358..0bc87879 100644 --- a/programs/amm/src/error.rs +++ b/programs/amm/src/error.rs @@ -20,9 +20,9 @@ pub enum ErrorCode { InvaildTickIndex, #[msg("The lower tick must be below the upper tick")] TickInvaildOrder, - #[msg("The tick must be greater, or equal to the minimum tick(-221818)")] + #[msg("The tick must be greater, or equal to the minimum tick(-443636)")] TickLowerOverflow, - #[msg("The tick must be lesser than, or equal to the maximum tick(221818)")] + #[msg("The tick must be lesser than, or equal to the maximum tick(443636)")] TickUpperOverflow, #[msg("tick % tick_spacing must be zero")] TickAndSpacingNotMatch, diff --git a/programs/amm/src/instructions/admin/collect_fund_fee.rs b/programs/amm/src/instructions/admin/collect_fund_fee.rs index 6a54d506..082d7109 100644 --- a/programs/amm/src/instructions/admin/collect_fund_fee.rs +++ b/programs/amm/src/instructions/admin/collect_fund_fee.rs @@ -4,9 +4,7 @@ use crate::states::*; use crate::util::*; use anchor_lang::prelude::*; use anchor_spl::token::Token; -use anchor_spl::token_interface::Mint; -use anchor_spl::token_interface::Token2022; -use anchor_spl::token_interface::TokenAccount; +use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; #[derive(Accounts)] pub struct CollectFundFee<'info> { /// Only admin or fund_owner can collect fee now @@ -79,8 +77,8 @@ pub fn collect_fund_fee( } transfer_from_pool_vault_to_user( &ctx.accounts.pool_state, - &ctx.accounts.token_vault_0, - &ctx.accounts.recipient_token_account_0, + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.recipient_token_account_0.to_account_info(), Some(ctx.accounts.vault_0_mint.clone()), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), @@ -89,8 +87,8 @@ pub fn collect_fund_fee( transfer_from_pool_vault_to_user( &ctx.accounts.pool_state, - &ctx.accounts.token_vault_1, - &ctx.accounts.recipient_token_account_1, + &ctx.accounts.token_vault_1.to_account_info(), + &ctx.accounts.recipient_token_account_1.to_account_info(), Some(ctx.accounts.vault_1_mint.clone()), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), @@ -99,8 +97,8 @@ pub fn collect_fund_fee( check_unclaimed_fees_and_vault( &ctx.accounts.pool_state, - &mut ctx.accounts.token_vault_0, - &mut ctx.accounts.token_vault_1, + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), )?; emit!(CollectProtocolFeeEvent { diff --git a/programs/amm/src/instructions/admin/collect_protocol_fee.rs b/programs/amm/src/instructions/admin/collect_protocol_fee.rs index 94d02d7a..0c584979 100644 --- a/programs/amm/src/instructions/admin/collect_protocol_fee.rs +++ b/programs/amm/src/instructions/admin/collect_protocol_fee.rs @@ -4,9 +4,7 @@ use crate::states::*; use crate::util::*; use anchor_lang::prelude::*; use anchor_spl::token::Token; -use anchor_spl::token_interface::Mint; -use anchor_spl::token_interface::Token2022; -use anchor_spl::token_interface::TokenAccount; +use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; #[derive(Accounts)] pub struct CollectProtocolFee<'info> { @@ -89,8 +87,8 @@ pub fn collect_protocol_fee( } transfer_from_pool_vault_to_user( &ctx.accounts.pool_state, - &ctx.accounts.token_vault_0, - &ctx.accounts.recipient_token_account_0, + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.recipient_token_account_0.to_account_info(), Some(ctx.accounts.vault_0_mint.clone()), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), @@ -99,8 +97,8 @@ pub fn collect_protocol_fee( transfer_from_pool_vault_to_user( &ctx.accounts.pool_state, - &ctx.accounts.token_vault_1, - &ctx.accounts.recipient_token_account_1, + &ctx.accounts.token_vault_1.to_account_info(), + &ctx.accounts.recipient_token_account_1.to_account_info(), Some(ctx.accounts.vault_1_mint.clone()), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), @@ -109,8 +107,8 @@ pub fn collect_protocol_fee( check_unclaimed_fees_and_vault( &ctx.accounts.pool_state, - &mut ctx.accounts.token_vault_0, - &mut ctx.accounts.token_vault_1, + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), )?; emit!(CollectProtocolFeeEvent { diff --git a/programs/amm/src/instructions/close_position.rs b/programs/amm/src/instructions/close_position.rs index cd703382..d2922503 100644 --- a/programs/amm/src/instructions/close_position.rs +++ b/programs/amm/src/instructions/close_position.rs @@ -2,8 +2,8 @@ use crate::error::ErrorCode; use crate::states::*; use crate::util::{burn, close_spl_account}; use anchor_lang::prelude::*; -use anchor_spl::token::Token; -use anchor_spl::token_interface::{Mint, TokenAccount}; +use anchor_spl::token_2022::spl_token_2022; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; #[derive(Accounts)] pub struct ClosePosition<'info> { @@ -11,7 +11,7 @@ pub struct ClosePosition<'info> { #[account(mut)] pub nft_owner: Signer<'info>, - /// Unique token mint address + /// Mint address bound to the personal position. #[account( mut, address = personal_position.nft_mint, @@ -19,22 +19,16 @@ pub struct ClosePosition<'info> { )] pub position_nft_mint: Box>, - /// Token account where position NFT will be minted + /// User token account where position NFT be minted to #[account( mut, - associated_token::mint = position_nft_mint, - associated_token::authority = nft_owner, + token::mint = position_nft_mint, + token::authority = nft_owner, constraint = position_nft_account.amount == 1, token::token_program = token_program, )] pub position_nft_account: Box>, - /// To store metaplex metadata - /// CHECK: Safety check performed inside function body - // #[account(mut)] - // pub metadata_account: UncheckedAccount<'info>, - - /// Metadata for the tokenized position #[account( mut, seeds = [POSITION_SEED.as_bytes(), position_nft_mint.key().as_ref()], @@ -43,12 +37,11 @@ pub struct ClosePosition<'info> { )] pub personal_position: Box>, - /// Program to create the position manager state account + /// System program to close the position state account pub system_program: Program<'info, System>, - /// Program to create mint account and mint tokens - pub token_program: Program<'info, Token>, - // /// Reserved for upgrade - // pub token_program_2022: Program<'info, Token2022>, + + /// Token/Token2022 program to close token/mint account + pub token_program: Interface<'info, TokenInterface>, } pub fn close_position<'a, 'b, 'c, 'info>( @@ -78,24 +71,36 @@ pub fn close_position<'a, 'b, 'c, 'info>( } } + let token_program = ctx.accounts.token_program.to_account_info(); + let position_nft_mint = ctx.accounts.position_nft_mint.to_account_info(); + let personal_nft_account = ctx.accounts.position_nft_account.to_account_info(); burn( &ctx.accounts.nft_owner, - &ctx.accounts.position_nft_mint, - &ctx.accounts.position_nft_account, - &ctx.accounts.token_program, - // &ctx.accounts.token_program_2022, + &position_nft_mint, + &personal_nft_account, + &token_program, &[], 1, )?; + // close use nft token account close_spl_account( &ctx.accounts.nft_owner, &ctx.accounts.nft_owner, - &ctx.accounts.position_nft_account, - &ctx.accounts.token_program, - // &ctx.accounts.token_program_2022, + &personal_nft_account, + &token_program, &[], )?; + if *position_nft_mint.owner == spl_token_2022::id() { + // close nft mint account + close_spl_account( + &ctx.accounts.personal_position.to_account_info(), + &ctx.accounts.nft_owner, + &position_nft_mint, + &token_program, + &[&ctx.accounts.personal_position.seeds()], + )?; + } Ok(()) } diff --git a/programs/amm/src/instructions/collect_remaining_rewards.rs b/programs/amm/src/instructions/collect_remaining_rewards.rs index 5d88b4ce..63616acc 100644 --- a/programs/amm/src/instructions/collect_remaining_rewards.rs +++ b/programs/amm/src/instructions/collect_remaining_rewards.rs @@ -57,8 +57,8 @@ pub fn collect_remaining_rewards( transfer_from_pool_vault_to_user( &ctx.accounts.pool_state, - &ctx.accounts.reward_token_vault, - &ctx.accounts.funder_token_account, + &ctx.accounts.reward_token_vault.to_account_info(), + &ctx.accounts.funder_token_account.to_account_info(), Some(ctx.accounts.reward_vault_mint.clone()), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), diff --git a/programs/amm/src/instructions/create_pool.rs b/programs/amm/src/instructions/create_pool.rs index aa35ae34..d7de288d 100644 --- a/programs/amm/src/instructions/create_pool.rs +++ b/programs/amm/src/instructions/create_pool.rs @@ -28,7 +28,7 @@ pub struct CreatePool<'info> { )] pub pool_state: AccountLoader<'info, PoolState>, - /// Token_0 mint, the key must grater then token_1 mint. + /// Token_0 mint, the key must be smaller then token_1 mint. #[account( constraint = token_mint_0.key() < token_mint_1.key(), mint::token_program = token_program_0 diff --git a/programs/amm/src/instructions/decrease_liquidity.rs b/programs/amm/src/instructions/decrease_liquidity.rs index 991ebd2c..ddd56018 100644 --- a/programs/amm/src/instructions/decrease_liquidity.rs +++ b/programs/amm/src/instructions/decrease_liquidity.rs @@ -4,11 +4,11 @@ use crate::error::ErrorCode; use crate::states::*; use crate::util::{self, transfer_from_pool_vault_to_user}; use anchor_lang::prelude::*; -use anchor_spl::token::Token; -use anchor_spl::token_interface::Mint; -use anchor_spl::token_interface::{Token2022, TokenAccount}; +use anchor_spl::token::{Token, TokenAccount}; +use anchor_spl::token_2022::spl_token_2022; +use anchor_spl::token_interface::{self, Mint, Token2022}; use std::cell::RefMut; -use std::ops::DerefMut; +use std::ops::Deref; /// Memo msg for decrease liquidity pub const DECREASE_MEMO_MSG: &'static [u8] = b"raydium_decrease"; @@ -20,9 +20,10 @@ pub struct DecreaseLiquidity<'info> { /// The token account for the tokenized position #[account( constraint = nft_account.mint == personal_position.nft_mint, - token::token_program = token_program, + constraint = nft_account.amount == 1, + token::authority = nft_owner )] - pub nft_account: Box>, + pub nft_account: Box>, /// Decrease liquidity for this position #[account(mut, constraint = personal_position.pool_id == pool_state.key())] @@ -49,14 +50,14 @@ pub struct DecreaseLiquidity<'info> { mut, constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 )] - pub token_vault_0: Box>, + pub token_vault_0: Box>, /// Token_1 vault #[account( mut, constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 )] - pub token_vault_1: Box>, + pub token_vault_1: Box>, /// Stores init state for the lower tick #[account(mut, constraint = tick_array_lower.load()?.pool_id == pool_state.key())] @@ -71,14 +72,14 @@ pub struct DecreaseLiquidity<'info> { mut, token::mint = token_vault_0.mint )] - pub recipient_token_account_0: Box>, + pub recipient_token_account_0: Box>, /// The destination token account for receive amount_1 #[account( mut, token::mint = token_vault_1.mint )] - pub recipient_token_account_1: Box>, + pub recipient_token_account_1: Box>, /// SPL program to transfer out tokens pub token_program: Program<'info, Token>, @@ -93,108 +94,6 @@ pub struct DecreaseLiquidity<'info> { // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, } -#[derive(Accounts)] -pub struct DecreaseLiquidityV2<'info> { - /// The position owner or delegated authority - pub nft_owner: Signer<'info>, - - /// The token account for the tokenized position - #[account( - constraint = nft_account.mint == personal_position.nft_mint, - token::token_program = token_program, - )] - pub nft_account: Box>, - - /// Decrease liquidity for this position - #[account(mut, constraint = personal_position.pool_id == pool_state.key())] - pub personal_position: Box>, - - #[account(mut)] - pub pool_state: AccountLoader<'info, PoolState>, - - #[account( - mut, - seeds = [ - POSITION_SEED.as_bytes(), - pool_state.key().as_ref(), - &personal_position.tick_lower_index.to_be_bytes(), - &personal_position.tick_upper_index.to_be_bytes(), - ], - bump, - constraint = protocol_position.pool_id == pool_state.key(), - )] - pub protocol_position: Box>, - - /// Token_0 vault - #[account( - mut, - constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 - )] - pub token_vault_0: Box>, - - /// Token_1 vault - #[account( - mut, - constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 - )] - pub token_vault_1: Box>, - - /// Stores init state for the lower tick - #[account(mut, constraint = tick_array_lower.load()?.pool_id == pool_state.key())] - pub tick_array_lower: AccountLoader<'info, TickArrayState>, - - /// Stores init state for the upper tick - #[account(mut, constraint = tick_array_upper.load()?.pool_id == pool_state.key())] - pub tick_array_upper: AccountLoader<'info, TickArrayState>, - - /// The destination token account for receive amount_0 - #[account( - mut, - token::mint = token_vault_0.mint - )] - pub recipient_token_account_0: Box>, - - /// The destination token account for receive amount_1 - #[account( - mut, - token::mint = token_vault_1.mint - )] - pub recipient_token_account_1: Box>, - - /// SPL program to transfer out tokens - pub token_program: Program<'info, Token>, - /// Token program 2022 - pub token_program_2022: Program<'info, Token2022>, - - /// memo program - /// CHECK: - #[account( - address = spl_memo::id() - )] - pub memo_program: UncheckedAccount<'info>, - - /// The mint of token vault 0 - #[account( - address = token_vault_0.mint - )] - pub vault_0_mint: Box>, - - /// The mint of token vault 1 - #[account( - address = token_vault_1.mint - )] - pub vault_1_mint: Box>, - // remaining account - // #[account( - // seeds = [ - // POOL_TICK_ARRAY_BITMAP_SEED.as_bytes(), - // pool_state.key().as_ref(), - // ], - // bump - // )] - // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, -} - pub fn decrease_liquidity_v1<'a, 'b, 'c: 'info, 'info>( ctx: Context<'a, 'b, 'c, 'info, DecreaseLiquidity<'info>>, liquidity: u128, @@ -205,12 +104,12 @@ pub fn decrease_liquidity_v1<'a, 'b, 'c: 'info, 'info>( &ctx.accounts.pool_state, &mut ctx.accounts.protocol_position, &mut ctx.accounts.personal_position, - &mut ctx.accounts.token_vault_0, - &mut ctx.accounts.token_vault_1, + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), &ctx.accounts.tick_array_lower, &ctx.accounts.tick_array_upper, - &ctx.accounts.recipient_token_account_0, - &ctx.accounts.recipient_token_account_1, + &ctx.accounts.recipient_token_account_0.to_account_info(), + &ctx.accounts.recipient_token_account_1.to_account_info(), &ctx.accounts.token_program, None, None, @@ -223,44 +122,16 @@ pub fn decrease_liquidity_v1<'a, 'b, 'c: 'info, 'info>( ) } -pub fn decrease_liquidity_v2<'a, 'b, 'c: 'info, 'info>( - ctx: Context<'a, 'b, 'c, 'info, DecreaseLiquidityV2<'info>>, - liquidity: u128, - amount_0_min: u64, - amount_1_min: u64, -) -> Result<()> { - decrease_liquidity( - &ctx.accounts.pool_state, - &mut ctx.accounts.protocol_position, - &mut ctx.accounts.personal_position, - &mut ctx.accounts.token_vault_0, - &mut ctx.accounts.token_vault_1, - &ctx.accounts.tick_array_lower, - &ctx.accounts.tick_array_upper, - &ctx.accounts.recipient_token_account_0, - &ctx.accounts.recipient_token_account_1, - &ctx.accounts.token_program, - Some(ctx.accounts.token_program_2022.clone()), - Some(ctx.accounts.memo_program.clone()), - Some(ctx.accounts.vault_0_mint.clone()), - Some(ctx.accounts.vault_1_mint.clone()), - &ctx.remaining_accounts, - liquidity, - amount_0_min, - amount_1_min, - ) -} - pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>( pool_state_loader: &'b AccountLoader<'info, PoolState>, protocol_position: &'b mut Box>, personal_position: &'b mut Box>, - token_vault_0: &'b mut Box>, - token_vault_1: &'b mut Box>, + token_vault_0: &'b AccountInfo<'info>, + token_vault_1: &'b AccountInfo<'info>, tick_array_lower_loader: &'b AccountLoader<'info, TickArrayState>, tick_array_upper_loader: &'b AccountLoader<'info, TickArrayState>, - recipient_token_account_0: &'b Box>, - recipient_token_account_1: &'b Box>, + recipient_token_account_0: &'b AccountInfo<'info>, + recipient_token_account_1: &'b AccountInfo<'info>, token_program: &'b Program<'info, Token>, token_program_2022: Option>, _memo_program: Option>, @@ -381,7 +252,7 @@ pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>( transfer_from_pool_vault_to_user( pool_state_loader, - token_vault_0, + &token_vault_0.to_account_info(), recipient_token_account_0, vault_0_mint, token_program, @@ -391,7 +262,7 @@ pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>( transfer_from_pool_vault_to_user( pool_state_loader, - token_vault_1, + &token_vault_1.to_account_info(), recipient_token_account_1, vault_1_mint.clone(), token_program, @@ -399,11 +270,7 @@ pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>( transfer_amount_1, )?; - check_unclaimed_fees_and_vault( - pool_state_loader, - token_vault_0.deref_mut(), - token_vault_1.deref_mut(), - )?; + check_unclaimed_fees_and_vault(pool_state_loader, token_vault_0, token_vault_1)?; let reward_amounts = collect_rewards( pool_state_loader, @@ -615,10 +482,12 @@ pub fn collect_rewards<'a, 'b, 'c, 'info>( let remaining_accounts_len = remaining_accounts.len(); let mut remaining_accounts = remaining_accounts.iter(); for i in 0..remaining_accounts_len / reward_group_account_num { - let reward_token_vault = - InterfaceAccount::::try_from(remaining_accounts.next().unwrap())?; - let recipient_token_account = - InterfaceAccount::::try_from(remaining_accounts.next().unwrap())?; + let reward_token_vault = InterfaceAccount::::try_from( + remaining_accounts.next().unwrap(), + )?; + let recipient_token_account = InterfaceAccount::::try_from( + remaining_accounts.next().unwrap(), + )?; let mut reward_vault_mint: Option>> = None; if need_reward_mint { @@ -661,8 +530,8 @@ pub fn collect_rewards<'a, 'b, 'c, 'info>( transfer_from_pool_vault_to_user( &pool_state_loader, - &reward_token_vault, - &recipient_token_account, + &reward_token_vault.to_account_info(), + &recipient_token_account.to_account_info(), reward_vault_mint, &token_program, token_program_2022.clone(), @@ -696,11 +565,20 @@ fn check_required_accounts_length( pub fn check_unclaimed_fees_and_vault( pool_state_loader: &AccountLoader, - token_vault_0: &mut InterfaceAccount, - token_vault_1: &mut InterfaceAccount, + token_vault_0: &AccountInfo, + token_vault_1: &AccountInfo, ) -> Result<()> { - token_vault_0.reload()?; - token_vault_1.reload()?; + let token_vault_0_amount = spl_token_2022::extension::StateWithExtensions::< + spl_token_2022::state::Account, + >::unpack(token_vault_0.try_borrow_data()?.deref())? + .base + .amount; + + let token_vault_1_amount = spl_token_2022::extension::StateWithExtensions::< + spl_token_2022::state::Account, + >::unpack(token_vault_1.try_borrow_data()?.deref())? + .base + .amount; let pool_state = &mut pool_state_loader.load_mut()?; @@ -713,8 +591,8 @@ pub fn check_unclaimed_fees_and_vault( .checked_sub(pool_state.total_fees_claimed_token_1) .unwrap(); - if (unclaimed_fee_token_0 >= token_vault_0.amount && token_vault_0.amount != 0) - || (unclaimed_fee_token_1 >= token_vault_1.amount && token_vault_1.amount != 0) + if (unclaimed_fee_token_0 >= token_vault_0_amount && token_vault_0_amount != 0) + || (unclaimed_fee_token_1 >= token_vault_1_amount && token_vault_1_amount != 0) { pool_state.set_status_by_bit(PoolStatusBitIndex::CollectFee, PoolStatusBitFlag::Disable); } diff --git a/programs/amm/src/instructions/decrease_liquidity_v2.rs b/programs/amm/src/instructions/decrease_liquidity_v2.rs new file mode 100644 index 00000000..8d654e6a --- /dev/null +++ b/programs/amm/src/instructions/decrease_liquidity_v2.rs @@ -0,0 +1,137 @@ +use super::decrease_liquidity::decrease_liquidity; +use crate::states::*; +use anchor_lang::prelude::*; +use anchor_spl::token::Token; +use anchor_spl::token_interface::Mint; +use anchor_spl::token_interface::{Token2022, TokenAccount}; + +#[derive(Accounts)] +pub struct DecreaseLiquidityV2<'info> { + /// The position owner or delegated authority + pub nft_owner: Signer<'info>, + + /// The token account for the tokenized position + #[account( + constraint = nft_account.mint == personal_position.nft_mint, + constraint = nft_account.amount == 1, + token::authority = nft_owner, + )] + pub nft_account: Box>, + + /// Decrease liquidity for this position + #[account(mut, constraint = personal_position.pool_id == pool_state.key())] + pub personal_position: Box>, + + #[account(mut)] + pub pool_state: AccountLoader<'info, PoolState>, + + #[account( + mut, + seeds = [ + POSITION_SEED.as_bytes(), + pool_state.key().as_ref(), + &personal_position.tick_lower_index.to_be_bytes(), + &personal_position.tick_upper_index.to_be_bytes(), + ], + bump, + constraint = protocol_position.pool_id == pool_state.key(), + )] + pub protocol_position: Box>, + + /// Token_0 vault + #[account( + mut, + constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 + )] + pub token_vault_0: Box>, + + /// Token_1 vault + #[account( + mut, + constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 + )] + pub token_vault_1: Box>, + + /// Stores init state for the lower tick + #[account(mut, constraint = tick_array_lower.load()?.pool_id == pool_state.key())] + pub tick_array_lower: AccountLoader<'info, TickArrayState>, + + /// Stores init state for the upper tick + #[account(mut, constraint = tick_array_upper.load()?.pool_id == pool_state.key())] + pub tick_array_upper: AccountLoader<'info, TickArrayState>, + + /// The destination token account for receive amount_0 + #[account( + mut, + token::mint = token_vault_0.mint + )] + pub recipient_token_account_0: Box>, + + /// The destination token account for receive amount_1 + #[account( + mut, + token::mint = token_vault_1.mint + )] + pub recipient_token_account_1: Box>, + + /// SPL program to transfer out tokens + pub token_program: Program<'info, Token>, + /// Token program 2022 + pub token_program_2022: Program<'info, Token2022>, + + /// memo program + /// CHECK: + #[account( + address = spl_memo::id() + )] + pub memo_program: UncheckedAccount<'info>, + + /// The mint of token vault 0 + #[account( + address = token_vault_0.mint + )] + pub vault_0_mint: Box>, + + /// The mint of token vault 1 + #[account( + address = token_vault_1.mint + )] + pub vault_1_mint: Box>, + // remaining account + // #[account( + // seeds = [ + // POOL_TICK_ARRAY_BITMAP_SEED.as_bytes(), + // pool_state.key().as_ref(), + // ], + // bump + // )] + // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, +} + +pub fn decrease_liquidity_v2<'a, 'b, 'c: 'info, 'info>( + ctx: Context<'a, 'b, 'c, 'info, DecreaseLiquidityV2<'info>>, + liquidity: u128, + amount_0_min: u64, + amount_1_min: u64, +) -> Result<()> { + decrease_liquidity( + &ctx.accounts.pool_state, + &mut ctx.accounts.protocol_position, + &mut ctx.accounts.personal_position, + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), + &ctx.accounts.tick_array_lower, + &ctx.accounts.tick_array_upper, + &ctx.accounts.recipient_token_account_0.to_account_info(), + &ctx.accounts.recipient_token_account_1.to_account_info(), + &ctx.accounts.token_program, + Some(ctx.accounts.token_program_2022.clone()), + Some(ctx.accounts.memo_program.clone()), + Some(ctx.accounts.vault_0_mint.clone()), + Some(ctx.accounts.vault_1_mint.clone()), + &ctx.remaining_accounts, + liquidity, + amount_0_min, + amount_1_min, + ) +} diff --git a/programs/amm/src/instructions/increase_liquidity.rs b/programs/amm/src/instructions/increase_liquidity.rs index 75e6e915..01c61c38 100644 --- a/programs/amm/src/instructions/increase_liquidity.rs +++ b/programs/amm/src/instructions/increase_liquidity.rs @@ -4,100 +4,21 @@ use crate::libraries::{big_num::U128, fixed_point_64, full_math::MulDiv}; use crate::states::*; use crate::util::*; use anchor_lang::prelude::*; -use anchor_spl::token::Token; -use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; +use anchor_spl::token::{Token, TokenAccount}; +use anchor_spl::token_interface::{Mint, Token2022}; #[derive(Accounts)] pub struct IncreaseLiquidity<'info> { /// Pays to mint the position pub nft_owner: Signer<'info>, - /// The token account for nft - #[account( - constraint = nft_account.mint == personal_position.nft_mint - )] - pub nft_account: Box>, - - #[account(mut)] - pub pool_state: AccountLoader<'info, PoolState>, - - #[account( - mut, - seeds = [ - POSITION_SEED.as_bytes(), - pool_state.key().as_ref(), - &personal_position.tick_lower_index.to_be_bytes(), - &personal_position.tick_upper_index.to_be_bytes(), - ], - bump, - constraint = protocol_position.pool_id == pool_state.key(), - )] - pub protocol_position: Box>, - - /// Increase liquidity for this position - #[account(mut, constraint = personal_position.pool_id == pool_state.key())] - pub personal_position: Box>, - - /// Stores init state for the lower tick - #[account(mut, constraint = tick_array_lower.load()?.pool_id == pool_state.key())] - pub tick_array_lower: AccountLoader<'info, TickArrayState>, - - /// Stores init state for the upper tick - #[account(mut, constraint = tick_array_upper.load()?.pool_id == pool_state.key())] - pub tick_array_upper: AccountLoader<'info, TickArrayState>, - - /// The payer's token account for token_0 - #[account( - mut, - token::mint = token_vault_0.mint - )] - pub token_account_0: Box>, - - /// The token account spending token_1 to mint the position - #[account( - mut, - token::mint = token_vault_1.mint - )] - pub token_account_1: Box>, - - /// The address that holds pool tokens for token_0 - #[account( - mut, - constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 - )] - pub token_vault_0: Box>, - - /// The address that holds pool tokens for token_1 - #[account( - mut, - constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 - )] - pub token_vault_1: Box>, - - /// Program to create mint account and mint tokens - pub token_program: Program<'info, Token>, - // remaining account - // #[account( - // seeds = [ - // POOL_TICK_ARRAY_BITMAP_SEED.as_bytes(), - // pool_state.key().as_ref(), - // ], - // bump - // )] - // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, -} - -#[derive(Accounts)] -pub struct IncreaseLiquidityV2<'info> { - /// Pays to mint the position - pub nft_owner: Signer<'info>, - /// The token account for nft #[account( constraint = nft_account.mint == personal_position.nft_mint, - token::token_program = token_program, + constraint = nft_account.amount == 1, + token::authority = nft_owner )] - pub nft_account: Box>, + pub nft_account: Box>, #[account(mut)] pub pool_state: AccountLoader<'info, PoolState>, @@ -132,46 +53,31 @@ pub struct IncreaseLiquidityV2<'info> { mut, token::mint = token_vault_0.mint )] - pub token_account_0: Box>, + pub token_account_0: Box>, /// The token account spending token_1 to mint the position #[account( mut, token::mint = token_vault_1.mint )] - pub token_account_1: Box>, + pub token_account_1: Box>, /// The address that holds pool tokens for token_0 #[account( mut, constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 )] - pub token_vault_0: Box>, + pub token_vault_0: Box>, /// The address that holds pool tokens for token_1 #[account( mut, constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 )] - pub token_vault_1: Box>, + pub token_vault_1: Box>, /// Program to create mint account and mint tokens pub token_program: Program<'info, Token>, - - /// Token program 2022 - pub token_program_2022: Program<'info, Token2022>, - - /// The mint of token vault 0 - #[account( - address = token_vault_0.mint - )] - pub vault_0_mint: Box>, - - /// The mint of token vault 1 - #[account( - address = token_vault_1.mint - )] - pub vault_1_mint: Box>, // remaining account // #[account( // seeds = [ @@ -197,10 +103,10 @@ pub fn increase_liquidity_v1<'a, 'b, 'c: 'info, 'info>( &mut ctx.accounts.personal_position, &ctx.accounts.tick_array_lower, &ctx.accounts.tick_array_upper, - &ctx.accounts.token_account_0, - &ctx.accounts.token_account_1, - &ctx.accounts.token_vault_0, - &ctx.accounts.token_vault_1, + &ctx.accounts.token_account_0.to_account_info(), + &ctx.accounts.token_account_1.to_account_info(), + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), &ctx.accounts.token_program, None, None, @@ -213,35 +119,6 @@ pub fn increase_liquidity_v1<'a, 'b, 'c: 'info, 'info>( ) } -pub fn increase_liquidity_v2<'a, 'b, 'c: 'info, 'info>( - ctx: Context<'a, 'b, 'c, 'info, IncreaseLiquidityV2<'info>>, - liquidity: u128, - amount_0_max: u64, - amount_1_max: u64, - base_flag: Option, -) -> Result<()> { - increase_liquidity( - &ctx.accounts.nft_owner, - &ctx.accounts.pool_state, - &mut ctx.accounts.protocol_position, - &mut ctx.accounts.personal_position, - &ctx.accounts.tick_array_lower, - &ctx.accounts.tick_array_upper, - &ctx.accounts.token_account_0, - &ctx.accounts.token_account_1, - &ctx.accounts.token_vault_0, - &ctx.accounts.token_vault_1, - &ctx.accounts.token_program, - Some(ctx.accounts.token_program_2022.clone()), - Some(ctx.accounts.vault_0_mint.clone()), - Some(ctx.accounts.vault_1_mint.clone()), - &ctx.remaining_accounts, - liquidity, - amount_0_max, - amount_1_max, - base_flag, - ) -} pub fn increase_liquidity<'a, 'b, 'c: 'info, 'info>( nft_owner: &'b Signer<'info>, pool_state_loader: &'b AccountLoader<'info, PoolState>, @@ -249,12 +126,12 @@ pub fn increase_liquidity<'a, 'b, 'c: 'info, 'info>( personal_position: &'b mut Box>, tick_array_lower_loader: &'b AccountLoader<'info, TickArrayState>, tick_array_upper_loader: &'b AccountLoader<'info, TickArrayState>, - token_account_0: &'b Box>, - token_account_1: &'b Box>, - token_vault_0: &'b Box>, - token_vault_1: &'b Box>, + token_account_0: &'b AccountInfo<'info>, + token_account_1: &'b AccountInfo<'info>, + token_vault_0: &'b AccountInfo<'info>, + token_vault_1: &'b AccountInfo<'info>, token_program: &'b Program<'info, Token>, - token_program_2022: Option>, + token_program_2022: Option<&Program<'info, Token2022>>, vault_0_mint: Option>>, vault_1_mint: Option>>, diff --git a/programs/amm/src/instructions/increase_liquidity_v2.rs b/programs/amm/src/instructions/increase_liquidity_v2.rs new file mode 100644 index 00000000..141a3cb6 --- /dev/null +++ b/programs/amm/src/instructions/increase_liquidity_v2.rs @@ -0,0 +1,132 @@ +use super::increase_liquidity::increase_liquidity; +use crate::states::*; +use anchor_lang::prelude::*; +use anchor_spl::token::Token; +use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; + +#[derive(Accounts)] +pub struct IncreaseLiquidityV2<'info> { + /// Pays to mint the position + pub nft_owner: Signer<'info>, + + /// The token account for nft + #[account( + constraint = nft_account.mint == personal_position.nft_mint, + constraint = nft_account.amount == 1, + token::authority = nft_owner, + )] + pub nft_account: Box>, + + #[account(mut)] + pub pool_state: AccountLoader<'info, PoolState>, + + #[account( + mut, + seeds = [ + POSITION_SEED.as_bytes(), + pool_state.key().as_ref(), + &personal_position.tick_lower_index.to_be_bytes(), + &personal_position.tick_upper_index.to_be_bytes(), + ], + bump, + constraint = protocol_position.pool_id == pool_state.key(), + )] + pub protocol_position: Box>, + + /// Increase liquidity for this position + #[account(mut, constraint = personal_position.pool_id == pool_state.key())] + pub personal_position: Box>, + + /// Stores init state for the lower tick + #[account(mut, constraint = tick_array_lower.load()?.pool_id == pool_state.key())] + pub tick_array_lower: AccountLoader<'info, TickArrayState>, + + /// Stores init state for the upper tick + #[account(mut, constraint = tick_array_upper.load()?.pool_id == pool_state.key())] + pub tick_array_upper: AccountLoader<'info, TickArrayState>, + + /// The payer's token account for token_0 + #[account( + mut, + token::mint = token_vault_0.mint + )] + pub token_account_0: Box>, + + /// The token account spending token_1 to mint the position + #[account( + mut, + token::mint = token_vault_1.mint + )] + pub token_account_1: Box>, + + /// The address that holds pool tokens for token_0 + #[account( + mut, + constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 + )] + pub token_vault_0: Box>, + + /// The address that holds pool tokens for token_1 + #[account( + mut, + constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 + )] + pub token_vault_1: Box>, + + /// Program to create mint account and mint tokens + pub token_program: Program<'info, Token>, + + /// Token program 2022 + pub token_program_2022: Program<'info, Token2022>, + + /// The mint of token vault 0 + #[account( + address = token_vault_0.mint + )] + pub vault_0_mint: Box>, + + /// The mint of token vault 1 + #[account( + address = token_vault_1.mint + )] + pub vault_1_mint: Box>, + // remaining account + // #[account( + // seeds = [ + // POOL_TICK_ARRAY_BITMAP_SEED.as_bytes(), + // pool_state.key().as_ref(), + // ], + // bump + // )] + // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, +} + +pub fn increase_liquidity_v2<'a, 'b, 'c: 'info, 'info>( + ctx: Context<'a, 'b, 'c, 'info, IncreaseLiquidityV2<'info>>, + liquidity: u128, + amount_0_max: u64, + amount_1_max: u64, + base_flag: Option, +) -> Result<()> { + increase_liquidity( + &ctx.accounts.nft_owner, + &ctx.accounts.pool_state, + &mut ctx.accounts.protocol_position, + &mut ctx.accounts.personal_position, + &ctx.accounts.tick_array_lower, + &ctx.accounts.tick_array_upper, + &ctx.accounts.token_account_0.to_account_info(), + &ctx.accounts.token_account_1.to_account_info(), + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), + &ctx.accounts.token_program, + Some(&ctx.accounts.token_program_2022), + Some(ctx.accounts.vault_0_mint.clone()), + Some(ctx.accounts.vault_1_mint.clone()), + &ctx.remaining_accounts, + liquidity, + amount_0_max, + amount_1_max, + base_flag, + ) +} diff --git a/programs/amm/src/instructions/initialize_reward.rs b/programs/amm/src/instructions/initialize_reward.rs index a30bb9e2..db3cab43 100644 --- a/programs/amm/src/instructions/initialize_reward.rs +++ b/programs/amm/src/instructions/initialize_reward.rs @@ -140,8 +140,8 @@ pub fn initialize_reward( transfer_from_user_to_pool_vault( &ctx.accounts.reward_funder, - &ctx.accounts.funder_token_account, - &ctx.accounts.reward_token_vault, + &ctx.accounts.funder_token_account.to_account_info(), + &ctx.accounts.reward_token_vault.to_account_info(), Some(ctx.accounts.reward_token_mint.clone()), &ctx.accounts.reward_token_program.to_account_info(), Some(ctx.accounts.reward_token_program.to_account_info()), diff --git a/programs/amm/src/instructions/mod.rs b/programs/amm/src/instructions/mod.rs index 8c2d1e4e..18a4fdea 100644 --- a/programs/amm/src/instructions/mod.rs +++ b/programs/amm/src/instructions/mod.rs @@ -4,15 +4,27 @@ pub use create_pool::*; pub mod open_position; pub use open_position::*; +pub mod open_position_v2; +pub use open_position_v2::*; + +pub mod open_position_with_token22_nft; +pub use open_position_with_token22_nft::*; + pub mod close_position; pub use close_position::*; pub mod increase_liquidity; pub use increase_liquidity::*; +pub mod increase_liquidity_v2; +pub use increase_liquidity_v2::*; + pub mod decrease_liquidity; pub use decrease_liquidity::*; +pub mod decrease_liquidity_v2; +pub use decrease_liquidity_v2::*; + pub mod swap; pub use swap::*; diff --git a/programs/amm/src/instructions/open_position.rs b/programs/amm/src/instructions/open_position.rs index 0a2f37d2..ea8b875d 100644 --- a/programs/amm/src/instructions/open_position.rs +++ b/programs/amm/src/instructions/open_position.rs @@ -5,11 +5,19 @@ use crate::states::*; use crate::util::*; use anchor_lang::prelude::*; use anchor_lang::solana_program; +use anchor_lang::system_program::{transfer, Transfer}; use anchor_spl::associated_token::AssociatedToken; use anchor_spl::metadata::Metadata; -use anchor_spl::token::{self, Token}; -use anchor_spl::token_2022::{self, spl_token_2022::instruction::AuthorityType}; -use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; +use anchor_spl::token::{Mint, Token, TokenAccount}; +use anchor_spl::token_2022::spl_token_2022::extension::{ + BaseStateWithExtensions, StateWithExtensions, +}; +use anchor_spl::token_2022::Token2022; +use anchor_spl::token_2022::{ + self, + spl_token_2022::{self, instruction::AuthorityType}, +}; +use anchor_spl::token_interface; use mpl_token_metadata::{instruction::create_metadata_accounts_v3, state::Creator}; use std::cell::RefMut; #[cfg(feature = "enable-log")] @@ -33,9 +41,8 @@ pub struct OpenPosition<'info> { mint::decimals = 0, mint::authority = pool_state.key(), payer = payer, - mint::token_program = token_program, )] - pub position_nft_mint: Box>, + pub position_nft_mint: Box>, /// Token account where position NFT will be minted /// This account created in the contract by cpi to avoid large stack variables @@ -44,9 +51,8 @@ pub struct OpenPosition<'info> { associated_token::mint = position_nft_mint, associated_token::authority = position_nft_owner, payer = payer, - token::token_program = token_program, )] - pub position_nft_account: Box>, + pub position_nft_account: Box>, /// To store metaplex metadata /// CHECK: Safety check performed inside function body @@ -72,7 +78,7 @@ pub struct OpenPosition<'info> { )] pub protocol_position: Box>, - /// CHECK: Account to mark the lower tick as initialized + /// CHECK: Account to store data for the position's lower tick #[account( mut, seeds = [ @@ -84,7 +90,7 @@ pub struct OpenPosition<'info> { )] pub tick_array_lower: UncheckedAccount<'info>, - /// CHECK:Account to store data for the position's upper tick + /// CHECK: Account to store data for the position's upper tick #[account( mut, seeds = [ @@ -111,28 +117,28 @@ pub struct OpenPosition<'info> { mut, token::mint = token_vault_0.mint )] - pub token_account_0: Box>, + pub token_account_0: Box>, /// The token_1 account deposit token to the pool #[account( mut, token::mint = token_vault_1.mint )] - pub token_account_1: Box>, + pub token_account_1: Box>, /// The address that holds pool tokens for token_0 #[account( mut, constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 )] - pub token_vault_0: Box>, + pub token_vault_0: Box>, /// The address that holds pool tokens for token_1 #[account( mut, constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 )] - pub token_vault_1: Box>, + pub token_vault_1: Box>, /// Sysvar for token mint and ATA creation pub rent: Sysvar<'info, Rent>, @@ -159,160 +165,6 @@ pub struct OpenPosition<'info> { // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, } -#[derive(Accounts)] -#[instruction(tick_lower_index: i32, tick_upper_index: i32,tick_array_lower_start_index:i32,tick_array_upper_start_index:i32)] -pub struct OpenPositionV2<'info> { - /// Pays to mint the position - #[account(mut)] - pub payer: Signer<'info>, - - /// CHECK: Receives the position NFT - pub position_nft_owner: UncheckedAccount<'info>, - - /// Unique token mint address - #[account( - init, - mint::decimals = 0, - mint::authority = pool_state.key(), - payer = payer, - mint::token_program = token_program, - )] - pub position_nft_mint: Box>, - - /// Token account where position NFT will be minted - /// This account created in the contract by cpi to avoid large stack variables - #[account( - init, - associated_token::mint = position_nft_mint, - associated_token::authority = position_nft_owner, - payer = payer, - token::token_program = token_program, - )] - pub position_nft_account: Box>, - - /// To store metaplex metadata - /// CHECK: Safety check performed inside function body - #[account(mut)] - pub metadata_account: UncheckedAccount<'info>, - - /// Add liquidity for this pool - #[account(mut)] - pub pool_state: AccountLoader<'info, PoolState>, - - /// Store the information of market marking in range - #[account( - init_if_needed, - seeds = [ - POSITION_SEED.as_bytes(), - pool_state.key().as_ref(), - &tick_lower_index.to_be_bytes(), - &tick_upper_index.to_be_bytes(), - ], - bump, - payer = payer, - space = ProtocolPositionState::LEN - )] - pub protocol_position: Box>, - - /// CHECK: Account to mark the lower tick as initialized - #[account( - mut, - seeds = [ - TICK_ARRAY_SEED.as_bytes(), - pool_state.key().as_ref(), - &tick_array_lower_start_index.to_be_bytes(), - ], - bump, - )] - pub tick_array_lower: UncheckedAccount<'info>, - - /// CHECK:Account to store data for the position's upper tick - #[account( - mut, - seeds = [ - TICK_ARRAY_SEED.as_bytes(), - pool_state.key().as_ref(), - &tick_array_upper_start_index.to_be_bytes(), - ], - bump, - )] - pub tick_array_upper: UncheckedAccount<'info>, - - /// personal position state - #[account( - init, - seeds = [POSITION_SEED.as_bytes(), position_nft_mint.key().as_ref()], - bump, - payer = payer, - space = PersonalPositionState::LEN - )] - pub personal_position: Box>, - - /// The token_0 account deposit token to the pool - #[account( - mut, - token::mint = token_vault_0.mint - )] - pub token_account_0: Box>, - - /// The token_1 account deposit token to the pool - #[account( - mut, - token::mint = token_vault_1.mint - )] - pub token_account_1: Box>, - - /// The address that holds pool tokens for token_0 - #[account( - mut, - constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 - )] - pub token_vault_0: Box>, - - /// The address that holds pool tokens for token_1 - #[account( - mut, - constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 - )] - pub token_vault_1: Box>, - - /// Sysvar for token mint and ATA creation - pub rent: Sysvar<'info, Rent>, - - /// Program to create the position manager state account - pub system_program: Program<'info, System>, - - /// Program to create mint account and mint tokens - pub token_program: Program<'info, Token>, - /// Program to create an ATA for receiving position NFT - pub associated_token_program: Program<'info, AssociatedToken>, - - /// Program to create NFT metadata - /// CHECK: Metadata program address constraint applied - pub metadata_program: Program<'info, Metadata>, - /// Program to create mint account and mint tokens - pub token_program_2022: Program<'info, Token2022>, - /// The mint of token vault 0 - #[account( - address = token_vault_0.mint - )] - pub vault_0_mint: Box>, - /// The mint of token vault 1 - #[account( - address = token_vault_1.mint - )] - pub vault_1_mint: Box>, - // remaining account - // #[account( - // seeds = [ - // POOL_TICK_ARRAY_BITMAP_SEED.as_bytes(), - // pool_state.key().as_ref(), - // ], - // bump - // )] - // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, -} - pub fn open_position_v1<'a, 'b, 'c: 'info, 'info>( ctx: Context<'a, 'b, 'c, 'info, OpenPosition<'info>>, liquidity: u128, @@ -322,29 +174,29 @@ pub fn open_position_v1<'a, 'b, 'c: 'info, 'info>( tick_upper_index: i32, tick_array_lower_start_index: i32, tick_array_upper_start_index: i32, - with_matedata: bool, + with_metadata: bool, base_flag: Option, ) -> Result<()> { open_position( &ctx.accounts.payer, &ctx.accounts.position_nft_owner, - &ctx.accounts.position_nft_mint, - &ctx.accounts.position_nft_account, - &ctx.accounts.metadata_account, + &ctx.accounts.position_nft_mint.to_account_info(), + &ctx.accounts.position_nft_account.to_account_info(), + Some(&ctx.accounts.metadata_account), &ctx.accounts.pool_state, &ctx.accounts.tick_array_lower, &ctx.accounts.tick_array_upper, &mut ctx.accounts.protocol_position, &mut ctx.accounts.personal_position, - &ctx.accounts.token_account_0, - &ctx.accounts.token_account_1, - &ctx.accounts.token_vault_0, - &ctx.accounts.token_vault_1, + &ctx.accounts.token_account_0.to_account_info(), + &ctx.accounts.token_account_1.to_account_info(), + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), &ctx.accounts.rent, &ctx.accounts.system_program, &ctx.accounts.token_program, &ctx.accounts.associated_token_program, - &ctx.accounts.metadata_program, + Some(&ctx.accounts.metadata_program), None, None, None, @@ -358,84 +210,35 @@ pub fn open_position_v1<'a, 'b, 'c: 'info, 'info>( tick_upper_index, tick_array_lower_start_index, tick_array_upper_start_index, - with_matedata, - base_flag, - ) -} - -pub fn open_position_v2<'a, 'b, 'c: 'info, 'info>( - ctx: Context<'a, 'b, 'c, 'info, OpenPositionV2<'info>>, - liquidity: u128, - amount_0_max: u64, - amount_1_max: u64, - tick_lower_index: i32, - tick_upper_index: i32, - tick_array_lower_start_index: i32, - tick_array_upper_start_index: i32, - with_matedata: bool, - base_flag: Option, -) -> Result<()> { - open_position( - &ctx.accounts.payer, - &ctx.accounts.position_nft_owner, - &ctx.accounts.position_nft_mint, - &ctx.accounts.position_nft_account, - &ctx.accounts.metadata_account, - &ctx.accounts.pool_state, - &ctx.accounts.tick_array_lower, - &ctx.accounts.tick_array_upper, - &mut ctx.accounts.protocol_position, - &mut ctx.accounts.personal_position, - &ctx.accounts.token_account_0, - &ctx.accounts.token_account_1, - &ctx.accounts.token_vault_0, - &ctx.accounts.token_vault_1, - &ctx.accounts.rent, - &ctx.accounts.system_program, - &ctx.accounts.token_program, - &ctx.accounts.associated_token_program, - &ctx.accounts.metadata_program, - Some(ctx.accounts.token_program_2022.clone()), - Some(ctx.accounts.vault_0_mint.clone()), - Some(ctx.accounts.vault_1_mint.clone()), - &ctx.remaining_accounts, - ctx.bumps.protocol_position, - ctx.bumps.personal_position, - liquidity, - amount_0_max, - amount_1_max, - tick_lower_index, - tick_upper_index, - tick_array_lower_start_index, - tick_array_upper_start_index, - with_matedata, + with_metadata, base_flag, + false, ) } pub fn open_position<'a, 'b, 'c: 'info, 'info>( payer: &'b Signer<'info>, position_nft_owner: &'b UncheckedAccount<'info>, - position_nft_mint: &'b Box>, - position_nft_account: &'b Box>, - metadata_account: &'b UncheckedAccount<'info>, + position_nft_mint: &'b AccountInfo<'info>, + position_nft_account: &'b AccountInfo<'info>, + metadata_account: Option<&'b UncheckedAccount<'info>>, pool_state_loader: &'b AccountLoader<'info, PoolState>, tick_array_lower_loader: &'b UncheckedAccount<'info>, tick_array_upper_loader: &'b UncheckedAccount<'info>, protocol_position: &'b mut Box>, personal_position: &'b mut Box>, - token_account_0: &'b Box>, - token_account_1: &'b Box>, - token_vault_0: &'b Box>, - token_vault_1: &'b Box>, + token_account_0: &'b AccountInfo<'info>, + token_account_1: &'b AccountInfo<'info>, + token_vault_0: &'b AccountInfo<'info>, + token_vault_1: &'b AccountInfo<'info>, rent: &'b Sysvar<'info, Rent>, system_program: &'b Program<'info, System>, token_program: &'b Program<'info, Token>, _associated_token_program: &'b Program<'info, AssociatedToken>, - metadata_program: &'b Program<'info, Metadata>, - token_program_2022: Option>, - vault_0_mint: Option>>, - vault_1_mint: Option>>, + metadata_program: Option<&'b Program<'info, Metadata>>, + token_program_2022: Option<&'b Program<'info, Token2022>>, + vault_0_mint: Option>>, + vault_1_mint: Option>>, remaining_accounts: &'c [AccountInfo<'info>], protocol_position_bump: u8, @@ -447,8 +250,9 @@ pub fn open_position<'a, 'b, 'c: 'info, 'info>( tick_upper_index: i32, tick_array_lower_start_index: i32, tick_array_upper_start_index: i32, - with_matedata: bool, + with_metadata: bool, base_flag: Option, + use_metadata_extension: bool, ) -> Result<()> { let mut liquidity = liquidity; { @@ -548,7 +352,7 @@ pub fn open_position<'a, 'b, 'c: 'info, 'info>( )?; // let personal_position = &mut personal_position; - personal_position.bump = personal_position_bump; + personal_position.bump = [personal_position_bump]; personal_position.nft_mint = position_nft_mint.key(); personal_position.pool_id = pool_state_loader.key(); personal_position.tick_lower_index = tick_lower_index; @@ -576,37 +380,38 @@ pub fn open_position<'a, 'b, 'c: 'info, 'info>( deposit_amount_1_transfer_fee: amount_1_transfer_fee }); } - create_nft_with_metadata( + + mint_nft_and_remove_mint_authority( payer, pool_state_loader, + personal_position, position_nft_mint, position_nft_account, metadata_account, metadata_program, token_program, + token_program_2022, system_program, rent, - personal_position.key(), - with_matedata, - )?; - - Ok(()) + with_metadata, + use_metadata_extension, + ) } /// Add liquidity to an initialized pool pub fn add_liquidity<'b, 'c: 'info, 'info>( payer: &'b Signer<'info>, - token_account_0: &'b Box>, - token_account_1: &'b Box>, - token_vault_0: &'b Box>, - token_vault_1: &'b Box>, + token_account_0: &'b AccountInfo<'info>, + token_account_1: &'b AccountInfo<'info>, + token_vault_0: &'b AccountInfo<'info>, + token_vault_1: &'b AccountInfo<'info>, tick_array_lower_loader: &'b AccountLoad<'info, TickArrayState>, tick_array_upper_loader: &'b AccountLoad<'info, TickArrayState>, protocol_position: &mut ProtocolPositionState, - token_program_2022: Option>, + token_program_2022: Option<&Program<'info, Token2022>>, token_program: &'b Program<'info, Token>, - vault_0_mint: Option>>, - vault_1_mint: Option>>, + vault_0_mint: Option>>, + vault_1_mint: Option>>, tick_array_bitmap_extension: Option<&'c AccountInfo<'info>>, pool_state: &mut RefMut, liquidity: &mut u128, @@ -921,86 +726,210 @@ pub fn update_position( Ok((flipped_lower, flipped_upper)) } -fn create_nft_with_metadata<'info>( +fn mint_nft_and_remove_mint_authority<'info>( payer: &Signer<'info>, pool_state_loader: &AccountLoader<'info, PoolState>, - position_nft_mint: &Box>, - position_nft_account: &Box>, - metadata_account: &UncheckedAccount<'info>, - metadata_program: &Program<'info, Metadata>, + personal_position: &Account<'info, PersonalPositionState>, + position_nft_mint: &AccountInfo<'info>, + position_nft_account: &AccountInfo<'info>, + metadata_account: Option<&UncheckedAccount<'info>>, + metadata_program: Option<&Program<'info, Metadata>>, token_program: &Program<'info, Token>, + token_program_2022: Option<&Program<'info, Token2022>>, system_program: &Program<'info, System>, rent: &Sysvar<'info, Rent>, - personal_position_id: Pubkey, - with_matedata: bool, + with_metadata: bool, + use_metadata_extension: bool, ) -> Result<()> { + let pool_state_info = pool_state_loader.to_account_info(); + let position_nft_mint_info = position_nft_mint.to_account_info(); let pool_state = pool_state_loader.load()?; let seeds = pool_state.seeds(); + + let token_program_info = if position_nft_mint_info.owner == token_program.key { + token_program.to_account_info() + } else { + token_program_2022.unwrap().to_account_info() + }; + + if with_metadata { + let (name, symbol, uri) = get_metadata_data(personal_position.key()); + if use_metadata_extension { + initialize_token_metadata_extension( + payer, + &position_nft_mint_info, + &pool_state_info, + &personal_position.to_account_info(), + token_program_2022.unwrap(), + name, + symbol, + uri, + &[&seeds], + )?; + } else { + initialize_metadata_account( + payer, + &pool_state_info, + &position_nft_mint_info, + metadata_account.unwrap(), + metadata_program.unwrap(), + system_program, + rent, + name, + symbol, + uri, + &[&seeds], + )?; + } + } // Mint the NFT - token::mint_to( + token_2022::mint_to( CpiContext::new_with_signer( - token_program.to_account_info(), - token::MintTo { - mint: position_nft_mint.to_account_info(), + token_program_info.to_account_info(), + token_2022::MintTo { + mint: position_nft_mint_info.clone(), to: position_nft_account.to_account_info(), - authority: pool_state_loader.to_account_info(), + authority: pool_state_info.clone(), }, &[&seeds], ), 1, )?; - if with_matedata { - let create_metadata_ix = create_metadata_accounts_v3( - metadata_program.key(), - metadata_account.key(), - position_nft_mint.key(), - pool_state_loader.key(), - payer.key(), - pool_state_loader.key(), - String::from("Raydium Concentrated Liquidity"), - String::from("RCL"), - format!( - "https://dynamic-ipfs.raydium.io/clmm/position?id={}", - personal_position_id.to_string() - ), - Some(vec![Creator { - address: pool_state_loader.key(), - verified: true, - share: 100, - }]), - 0, - true, - false, - None, - None, - None, - ); - solana_program::program::invoke_signed( - &create_metadata_ix, - &[ - metadata_account.to_account_info(), - position_nft_mint.to_account_info(), - payer.to_account_info(), - pool_state_loader.to_account_info(), - system_program.to_account_info(), - rent.to_account_info(), - ], - &[&seeds], - )?; - } + // Disable minting token_2022::set_authority( CpiContext::new_with_signer( - token_program.to_account_info(), + token_program_info.to_account_info(), token_2022::SetAuthority { current_authority: pool_state_loader.to_account_info(), - account_or_mint: position_nft_mint.to_account_info(), + account_or_mint: position_nft_mint_info, }, &[&seeds], ), AuthorityType::MintTokens, None, + ) +} + +fn get_metadata_data(personal_position_id: Pubkey) -> (String, String, String) { + return ( + String::from("Raydium Concentrated Liquidity"), + String::from("RCL"), + format!( + "https://dynamic-ipfs.raydium.io/clmm/position?id={}", + personal_position_id.to_string() + ), + ); +} + +fn initialize_metadata_account<'info>( + payer: &Signer<'info>, + authority: &AccountInfo<'info>, + position_nft_mint: &AccountInfo<'info>, + metadata_account: &UncheckedAccount<'info>, + metadata_program: &Program<'info, Metadata>, + system_program: &Program<'info, System>, + rent: &Sysvar<'info, Rent>, + name: String, + symbol: String, + uri: String, + signers_seeds: &[&[&[u8]]], +) -> Result<()> { + let create_metadata_ix = create_metadata_accounts_v3( + metadata_program.key(), + metadata_account.key(), + position_nft_mint.key(), + authority.key(), + payer.key(), + authority.key(), + name, + symbol, + uri, + Some(vec![Creator { + address: authority.key(), + verified: true, + share: 100, + }]), + 0, + true, + false, + None, + None, + None, + ); + solana_program::program::invoke_signed( + &create_metadata_ix, + &[ + metadata_account.to_account_info(), + position_nft_mint.to_account_info(), + payer.to_account_info(), + authority.to_account_info(), + system_program.to_account_info(), + rent.to_account_info(), + ], + signers_seeds, + )?; + + Ok(()) +} + +pub fn initialize_token_metadata_extension<'info>( + payer: &Signer<'info>, + position_nft_mint: &AccountInfo<'info>, + mint_authority: &AccountInfo<'info>, + metadata_update_authority: &AccountInfo<'info>, + token_2022_program: &Program<'info, Token2022>, + name: String, + symbol: String, + uri: String, + signers_seeds: &[&[&[u8]]], +) -> Result<()> { + let metadata = spl_token_metadata_interface::state::TokenMetadata { + name, + symbol, + uri, + ..Default::default() + }; + + let mint_data = position_nft_mint.try_borrow_data()?; + let mint_state_unpacked = + StateWithExtensions::::unpack(&mint_data)?; + let new_account_len = mint_state_unpacked + .try_get_new_account_len::(&metadata)?; + let new_rent_exempt_lamports = Rent::get()?.minimum_balance(new_account_len); + let additional_lamports = new_rent_exempt_lamports.saturating_sub(position_nft_mint.lamports()); + // CPI call will borrow the account data + drop(mint_data); + + let cpi_context = CpiContext::new( + token_2022_program.to_account_info(), + Transfer { + from: payer.to_account_info(), + to: position_nft_mint.to_account_info(), + }, + ); + transfer(cpi_context, additional_lamports)?; + + solana_program::program::invoke_signed( + &spl_token_metadata_interface::instruction::initialize( + token_2022_program.key, + position_nft_mint.key, + metadata_update_authority.key, + position_nft_mint.key, + &mint_authority.key(), + metadata.name, + metadata.symbol, + metadata.uri, + ), + &[ + position_nft_mint.to_account_info(), + mint_authority.to_account_info(), + metadata_update_authority.to_account_info(), + token_2022_program.to_account_info(), + ], + signers_seeds, )?; + Ok(()) } diff --git a/programs/amm/src/instructions/open_position_v2.rs b/programs/amm/src/instructions/open_position_v2.rs new file mode 100644 index 00000000..6d3e67a0 --- /dev/null +++ b/programs/amm/src/instructions/open_position_v2.rs @@ -0,0 +1,208 @@ +use super::open_position::open_position; +use crate::states::*; +use anchor_lang::prelude::*; +use anchor_spl::associated_token::AssociatedToken; +use anchor_spl::metadata::Metadata; +use anchor_spl::token::{self, Token}; +use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; +#[derive(Accounts)] +#[instruction(tick_lower_index: i32, tick_upper_index: i32,tick_array_lower_start_index:i32,tick_array_upper_start_index:i32)] +pub struct OpenPositionV2<'info> { + /// Pays to mint the position + #[account(mut)] + pub payer: Signer<'info>, + + /// CHECK: Receives the position NFT + pub position_nft_owner: UncheckedAccount<'info>, + + /// Unique token mint address + #[account( + init, + mint::decimals = 0, + mint::authority = pool_state.key(), + payer = payer, + )] + pub position_nft_mint: Box>, + + /// Token account where position NFT will be minted + #[account( + init, + associated_token::mint = position_nft_mint, + associated_token::authority = position_nft_owner, + payer = payer, + )] + pub position_nft_account: Box>, + + /// To store metaplex metadata + /// CHECK: Safety check performed inside function body + #[account(mut)] + pub metadata_account: UncheckedAccount<'info>, + + /// Add liquidity for this pool + #[account(mut)] + pub pool_state: AccountLoader<'info, PoolState>, + + /// Store the information of market marking in range + #[account( + init_if_needed, + seeds = [ + POSITION_SEED.as_bytes(), + pool_state.key().as_ref(), + &tick_lower_index.to_be_bytes(), + &tick_upper_index.to_be_bytes(), + ], + bump, + payer = payer, + space = ProtocolPositionState::LEN + )] + pub protocol_position: Box>, + + /// CHECK: Account to store data for the position's lower tick + #[account( + mut, + seeds = [ + TICK_ARRAY_SEED.as_bytes(), + pool_state.key().as_ref(), + &tick_array_lower_start_index.to_be_bytes(), + ], + bump, + )] + pub tick_array_lower: UncheckedAccount<'info>, + + /// CHECK: Account to store data for the position's upper tick + #[account( + mut, + seeds = [ + TICK_ARRAY_SEED.as_bytes(), + pool_state.key().as_ref(), + &tick_array_upper_start_index.to_be_bytes(), + ], + bump, + )] + pub tick_array_upper: UncheckedAccount<'info>, + + /// personal position state + #[account( + init, + seeds = [POSITION_SEED.as_bytes(), position_nft_mint.key().as_ref()], + bump, + payer = payer, + space = PersonalPositionState::LEN + )] + pub personal_position: Box>, + + /// The token_0 account deposit token to the pool + #[account( + mut, + token::mint = token_vault_0.mint + )] + pub token_account_0: Box>, + + /// The token_1 account deposit token to the pool + #[account( + mut, + token::mint = token_vault_1.mint + )] + pub token_account_1: Box>, + + /// The address that holds pool tokens for token_0 + #[account( + mut, + constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 + )] + pub token_vault_0: Box>, + + /// The address that holds pool tokens for token_1 + #[account( + mut, + constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 + )] + pub token_vault_1: Box>, + + /// Sysvar for token mint and ATA creation + pub rent: Sysvar<'info, Rent>, + + /// Program to create the position manager state account + pub system_program: Program<'info, System>, + + /// Program to create mint account and mint tokens + pub token_program: Program<'info, Token>, + /// Program to create an ATA for receiving position NFT + pub associated_token_program: Program<'info, AssociatedToken>, + + /// Program to create NFT metadata + /// CHECK: Metadata program address constraint applied + pub metadata_program: Program<'info, Metadata>, + /// Program to create mint account and mint tokens + pub token_program_2022: Program<'info, Token2022>, + /// The mint of token vault 0 + #[account( + address = token_vault_0.mint + )] + pub vault_0_mint: Box>, + /// The mint of token vault 1 + #[account( + address = token_vault_1.mint + )] + pub vault_1_mint: Box>, + // remaining account + // #[account( + // seeds = [ + // POOL_TICK_ARRAY_BITMAP_SEED.as_bytes(), + // pool_state.key().as_ref(), + // ], + // bump + // )] + // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, +} + +pub fn open_position_v2<'a, 'b, 'c: 'info, 'info>( + ctx: Context<'a, 'b, 'c, 'info, OpenPositionV2<'info>>, + liquidity: u128, + amount_0_max: u64, + amount_1_max: u64, + tick_lower_index: i32, + tick_upper_index: i32, + tick_array_lower_start_index: i32, + tick_array_upper_start_index: i32, + with_matedata: bool, + base_flag: Option, +) -> Result<()> { + open_position( + &ctx.accounts.payer, + &ctx.accounts.position_nft_owner, + &ctx.accounts.position_nft_mint.to_account_info(), + &ctx.accounts.position_nft_account.to_account_info(), + Some(&ctx.accounts.metadata_account), + &ctx.accounts.pool_state, + &ctx.accounts.tick_array_lower, + &ctx.accounts.tick_array_upper, + &mut ctx.accounts.protocol_position, + &mut ctx.accounts.personal_position, + &ctx.accounts.token_account_0.to_account_info(), + &ctx.accounts.token_account_1.to_account_info(), + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), + &ctx.accounts.rent, + &ctx.accounts.system_program, + &ctx.accounts.token_program, + &ctx.accounts.associated_token_program, + Some(&ctx.accounts.metadata_program), + Some(&ctx.accounts.token_program_2022), + Some(ctx.accounts.vault_0_mint.clone()), + Some(ctx.accounts.vault_1_mint.clone()), + &ctx.remaining_accounts, + ctx.bumps.protocol_position, + ctx.bumps.personal_position, + liquidity, + amount_0_max, + amount_1_max, + tick_lower_index, + tick_upper_index, + tick_array_lower_start_index, + tick_array_upper_start_index, + with_matedata, + base_flag, + false, + ) +} diff --git a/programs/amm/src/instructions/open_position_with_token22_nft.rs b/programs/amm/src/instructions/open_position_with_token22_nft.rs new file mode 100644 index 00000000..7bed01d8 --- /dev/null +++ b/programs/amm/src/instructions/open_position_with_token22_nft.rs @@ -0,0 +1,217 @@ +use super::open_position::open_position; +use crate::states::*; +use crate::util::create_position_nft_mint_with_extensions; +use anchor_lang::prelude::*; +use anchor_spl::associated_token::{create, AssociatedToken, Create}; +use anchor_spl::token::Token; +use anchor_spl::token_interface::{Mint, Token2022, TokenAccount}; + +#[derive(Accounts)] +#[instruction(tick_lower_index: i32, tick_upper_index: i32,tick_array_lower_start_index:i32,tick_array_upper_start_index:i32)] +pub struct OpenPositionWithToken22Nft<'info> { + /// Pays to mint the position + #[account(mut)] + pub payer: Signer<'info>, + + /// CHECK: Receives the position NFT + pub position_nft_owner: UncheckedAccount<'info>, + + /// Unique token mint address, initialize in constract + #[account(mut)] + pub position_nft_mint: Signer<'info>, + + /// CHECK: ATA address where position NFT will be minted, initialize in constract + #[account(mut)] + pub position_nft_account: UncheckedAccount<'info>, + + /// Add liquidity for this pool + #[account(mut)] + pub pool_state: AccountLoader<'info, PoolState>, + + /// Store the information of market marking in range + #[account( + init_if_needed, + seeds = [ + POSITION_SEED.as_bytes(), + pool_state.key().as_ref(), + &tick_lower_index.to_be_bytes(), + &tick_upper_index.to_be_bytes(), + ], + bump, + payer = payer, + space = ProtocolPositionState::LEN + )] + pub protocol_position: Box>, + + /// CHECK: Account to store data for the position's lower tick + #[account( + mut, + seeds = [ + TICK_ARRAY_SEED.as_bytes(), + pool_state.key().as_ref(), + &tick_array_lower_start_index.to_be_bytes(), + ], + bump, + )] + pub tick_array_lower: UncheckedAccount<'info>, + + /// CHECK: Account to store data for the position's upper tick + #[account( + mut, + seeds = [ + TICK_ARRAY_SEED.as_bytes(), + pool_state.key().as_ref(), + &tick_array_upper_start_index.to_be_bytes(), + ], + bump, + )] + pub tick_array_upper: UncheckedAccount<'info>, + + /// personal position state + #[account( + init, + seeds = [POSITION_SEED.as_bytes(), position_nft_mint.key().as_ref()], + bump, + payer = payer, + space = PersonalPositionState::LEN + )] + pub personal_position: Box>, + + /// The token_0 account deposit token to the pool + #[account( + mut, + token::mint = token_vault_0.mint + )] + pub token_account_0: Box>, + + /// The token_1 account deposit token to the pool + #[account( + mut, + token::mint = token_vault_1.mint + )] + pub token_account_1: Box>, + + /// The address that holds pool tokens for token_0 + #[account( + mut, + constraint = token_vault_0.key() == pool_state.load()?.token_vault_0 + )] + pub token_vault_0: Box>, + + /// The address that holds pool tokens for token_1 + #[account( + mut, + constraint = token_vault_1.key() == pool_state.load()?.token_vault_1 + )] + pub token_vault_1: Box>, + + /// Sysvar for token mint and ATA creation + pub rent: Sysvar<'info, Rent>, + + /// Program to create the position manager state account + pub system_program: Program<'info, System>, + + /// Program to transfer for token account + pub token_program: Program<'info, Token>, + + /// Program to create an ATA for receiving position NFT + pub associated_token_program: Program<'info, AssociatedToken>, + + /// Program to create NFT mint/token account and transfer for token22 account + pub token_program_2022: Program<'info, Token2022>, + + /// The mint of token vault 0 + #[account( + address = token_vault_0.mint + )] + pub vault_0_mint: Box>, + + /// The mint of token vault 1 + #[account( + address = token_vault_1.mint + )] + pub vault_1_mint: Box>, + // remaining account + // #[account( + // seeds = [ + // POOL_TICK_ARRAY_BITMAP_SEED.as_bytes(), + // pool_state.key().as_ref(), + // ], + // bump + // )] + // pub tick_array_bitmap: AccountLoader<'info, TickArrayBitmapExtension>, +} + +pub fn open_position_with_token22_nft<'a, 'b, 'c: 'info, 'info>( + ctx: Context<'a, 'b, 'c, 'info, OpenPositionWithToken22Nft<'info>>, + liquidity: u128, + amount_0_max: u64, + amount_1_max: u64, + tick_lower_index: i32, + tick_upper_index: i32, + tick_array_lower_start_index: i32, + tick_array_upper_start_index: i32, + with_matedata: bool, + base_flag: Option, +) -> Result<()> { + create_position_nft_mint_with_extensions( + &ctx.accounts.payer, + &ctx.accounts.position_nft_mint, + &ctx.accounts.pool_state.to_account_info(), + &ctx.accounts.personal_position.to_account_info(), + &ctx.accounts.system_program, + &ctx.accounts.token_program_2022, + with_matedata, + )?; + + // create user position nft account + create(CpiContext::new( + ctx.accounts.associated_token_program.to_account_info(), + Create { + payer: ctx.accounts.payer.to_account_info(), + associated_token: ctx.accounts.position_nft_account.to_account_info(), + authority: ctx.accounts.position_nft_owner.to_account_info(), + mint: ctx.accounts.position_nft_mint.to_account_info(), + system_program: ctx.accounts.system_program.to_account_info(), + token_program: ctx.accounts.token_program_2022.to_account_info(), + }, + ))?; + + open_position( + &ctx.accounts.payer, + &ctx.accounts.position_nft_owner, + &ctx.accounts.position_nft_mint, + &ctx.accounts.position_nft_account, + None, + &ctx.accounts.pool_state, + &ctx.accounts.tick_array_lower, + &ctx.accounts.tick_array_upper, + &mut ctx.accounts.protocol_position, + &mut ctx.accounts.personal_position, + &ctx.accounts.token_account_0.to_account_info(), + &ctx.accounts.token_account_1.to_account_info(), + &ctx.accounts.token_vault_0.to_account_info(), + &ctx.accounts.token_vault_1.to_account_info(), + &ctx.accounts.rent, + &ctx.accounts.system_program, + &ctx.accounts.token_program, + &ctx.accounts.associated_token_program, + None, + Some(&ctx.accounts.token_program_2022), + Some(ctx.accounts.vault_0_mint.clone()), + Some(ctx.accounts.vault_1_mint.clone()), + &ctx.remaining_accounts, + ctx.bumps.protocol_position, + ctx.bumps.personal_position, + liquidity, + amount_0_max, + amount_1_max, + tick_lower_index, + tick_upper_index, + tick_array_lower_start_index, + tick_array_upper_start_index, + with_matedata, + base_flag, + true, + ) +} diff --git a/programs/amm/src/instructions/set_reward_params.rs b/programs/amm/src/instructions/set_reward_params.rs index 49dce681..ff2ac000 100644 --- a/programs/amm/src/instructions/set_reward_params.rs +++ b/programs/amm/src/instructions/set_reward_params.rs @@ -109,8 +109,8 @@ pub fn set_reward_params<'a, 'b, 'c: 'info, 'info>( transfer_from_user_to_pool_vault( &ctx.accounts.authority, - &authority_token_account, - &reward_token_vault, + &authority_token_account.to_account_info(), + &reward_token_vault.to_account_info(), Some(Box::new(reward_vault_mint)), &ctx.accounts.token_program, Some(ctx.accounts.token_program_2022.to_account_info()), diff --git a/programs/amm/src/instructions/swap.rs b/programs/amm/src/instructions/swap.rs index 3153f17e..2cd09566 100644 --- a/programs/amm/src/instructions/swap.rs +++ b/programs/amm/src/instructions/swap.rs @@ -5,8 +5,7 @@ use crate::libraries::{ use crate::states::*; use crate::util::*; use anchor_lang::prelude::*; -use anchor_spl::token::Token; -use anchor_spl::token_interface::TokenAccount; +use anchor_spl::token::{Token, TokenAccount}; use std::cell::RefMut; use std::collections::VecDeque; #[cfg(feature = "enable-log")] @@ -27,32 +26,20 @@ pub struct SwapSingle<'info> { pub pool_state: AccountLoader<'info, PoolState>, /// The user token account for input token - #[account( - mut, - token::token_program = token_program, - )] - pub input_token_account: Box>, + #[account(mut)] + pub input_token_account: Box>, /// The user token account for output token - #[account( - mut, - token::token_program = token_program, - )] - pub output_token_account: Box>, + #[account(mut)] + pub output_token_account: Box>, /// The vault token account for input token - #[account( - mut, - token::token_program = token_program, - )] - pub input_vault: Box>, + #[account(mut)] + pub input_vault: Box>, /// The vault token account for output token - #[account( - mut, - token::token_program = token_program, - )] - pub output_vault: Box>, + #[account(mut)] + pub output_vault: Box>, /// The program account for the most recent oracle observation #[account(mut, address = pool_state.load()?.observation_key)] @@ -70,16 +57,16 @@ pub struct SwapAccounts<'b, 'info> { pub signer: Signer<'info>, /// The user token account for input token - pub input_token_account: Box>, + pub input_token_account: Box>, /// The user token account for output token - pub output_token_account: Box>, + pub output_token_account: Box>, /// The vault token account for input token - pub input_vault: Box>, + pub input_vault: Box>, /// The vault token account for output token - pub output_vault: Box>, + pub output_vault: Box>, /// SPL program for token transfers pub token_program: Program<'info, Token>, @@ -667,8 +654,8 @@ pub fn exact_internal<'b, 'c: 'info, 'info>( // x -> y, deposit x token from user to pool vault. transfer_from_user_to_pool_vault( &ctx.signer, - &token_account_0, - &vault_0, + &token_account_0.to_account_info(), + &vault_0.to_account_info(), None, &ctx.token_program, None, @@ -681,8 +668,8 @@ pub fn exact_internal<'b, 'c: 'info, 'info>( // x -> y,transfer y token from pool vault to user. transfer_from_pool_vault_to_user( &ctx.pool_state, - &vault_1, - &token_account_1, + &vault_1.to_account_info(), + &token_account_1.to_account_info(), None, &ctx.token_program, None, @@ -691,8 +678,8 @@ pub fn exact_internal<'b, 'c: 'info, 'info>( } else { transfer_from_user_to_pool_vault( &ctx.signer, - &token_account_1, - &vault_1, + &token_account_1.to_account_info(), + &vault_1.to_account_info(), None, &ctx.token_program, None, @@ -704,8 +691,8 @@ pub fn exact_internal<'b, 'c: 'info, 'info>( } transfer_from_pool_vault_to_user( &ctx.pool_state, - &vault_0, - &token_account_0, + &vault_0.to_account_info(), + &token_account_0.to_account_info(), None, &ctx.token_program, None, diff --git a/programs/amm/src/instructions/swap_v2.rs b/programs/amm/src/instructions/swap_v2.rs index 5c361a39..fa2d40be 100644 --- a/programs/amm/src/instructions/swap_v2.rs +++ b/programs/amm/src/instructions/swap_v2.rs @@ -223,8 +223,8 @@ pub fn exact_internal_v2<'c: 'info, 'info>( // x -> y, deposit x token from user to pool vault. transfer_from_user_to_pool_vault( &ctx.payer, - &token_account_0, - &vault_0, + &token_account_0.to_account_info(), + &vault_0.to_account_info(), Some(vault_0_mint), &ctx.token_program, Some(ctx.token_program_2022.to_account_info()), @@ -237,8 +237,8 @@ pub fn exact_internal_v2<'c: 'info, 'info>( // x -> y,transfer y token from pool vault to user. transfer_from_pool_vault_to_user( &ctx.pool_state, - &vault_1, - &token_account_1, + &vault_1.to_account_info(), + &token_account_1.to_account_info(), Some(vault_1_mint), &ctx.token_program, Some(ctx.token_program_2022.to_account_info()), @@ -261,8 +261,8 @@ pub fn exact_internal_v2<'c: 'info, 'info>( ); transfer_from_user_to_pool_vault( &ctx.payer, - &token_account_1, - &vault_1, + &token_account_1.to_account_info(), + &vault_1.to_account_info(), Some(vault_1_mint), &ctx.token_program, Some(ctx.token_program_2022.to_account_info()), @@ -274,8 +274,8 @@ pub fn exact_internal_v2<'c: 'info, 'info>( } transfer_from_pool_vault_to_user( &ctx.pool_state, - &vault_0, - &token_account_0, + &vault_0.to_account_info(), + &token_account_0.to_account_info(), Some(vault_0_mint), &ctx.token_program, Some(ctx.token_program_2022.to_account_info()), diff --git a/programs/amm/src/lib.rs b/programs/amm/src/lib.rs index 58a9d6a8..e83462ce 100644 --- a/programs/amm/src/lib.rs +++ b/programs/amm/src/lib.rs @@ -8,7 +8,6 @@ use anchor_lang::prelude::*; use core as core_; use instructions::*; use states::*; -use util::access_control::*; #[cfg(not(feature = "no-entrypoint"))] solana_security_txt::security_txt! { @@ -259,6 +258,7 @@ pub mod amm_v3 { instructions::collect_fund_fee(ctx, amount_0_requested, amount_1_requested) } + /// #[deprecated(note = "Use `open_position_with_token22_nft` instead.")] /// Creates a new position wrapped in a NFT /// /// # Arguments @@ -296,6 +296,7 @@ pub mod amm_v3 { ) } + /// #[deprecated(note = "Use `open_position_with_token22_nft` instead.")] /// Creates a new position wrapped in a NFT, support Token2022 /// /// # Arguments @@ -319,7 +320,7 @@ pub mod amm_v3 { liquidity: u128, amount_0_max: u64, amount_1_max: u64, - with_matedata: bool, + with_metadata: bool, base_flag: Option, ) -> Result<()> { instructions::open_position_v2( @@ -331,12 +332,52 @@ pub mod amm_v3 { tick_upper_index, tick_array_lower_start_index, tick_array_upper_start_index, - with_matedata, + with_metadata, base_flag, ) } - /// Close a position, the nft mint and nft account + /// Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position. + /// + /// # Arguments + /// + /// * `ctx` - The context of accounts + /// * `tick_lower_index` - The low boundary of market + /// * `tick_upper_index` - The upper boundary of market + /// * `tick_array_lower_start_index` - The start index of tick array which include tick low + /// * `tick_array_upper_start_index` - The start index of tick array which include tick upper + /// * `liquidity` - The liquidity to be added, if zero, and the base_flage is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity + /// * `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check + /// * `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check + /// * `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max + /// + pub fn open_position_with_token22_nft<'a, 'b, 'c: 'info, 'info>( + ctx: Context<'a, 'b, 'c, 'info, OpenPositionWithToken22Nft<'info>>, + tick_lower_index: i32, + tick_upper_index: i32, + tick_array_lower_start_index: i32, + tick_array_upper_start_index: i32, + liquidity: u128, + amount_0_max: u64, + amount_1_max: u64, + with_metadata: bool, + base_flag: Option, + ) -> Result<()> { + instructions::open_position_with_token22_nft( + ctx, + liquidity, + amount_0_max, + amount_1_max, + tick_lower_index, + tick_upper_index, + tick_array_lower_start_index, + tick_array_upper_start_index, + with_metadata, + base_flag, + ) + } + + /// Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner. /// /// # Arguments /// @@ -348,6 +389,7 @@ pub mod amm_v3 { instructions::close_position(ctx) } + /// #[deprecated(note = "Use `increase_liquidity_v2` instead.")] /// Increases liquidity with a exist position, with amount paid by `payer` /// /// # Arguments @@ -357,7 +399,6 @@ pub mod amm_v3 { /// * `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check /// * `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check /// - #[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))] pub fn increase_liquidity<'a, 'b, 'c: 'info, 'info>( ctx: Context<'a, 'b, 'c, 'info, IncreaseLiquidity<'info>>, liquidity: u128, @@ -378,7 +419,6 @@ pub mod amm_v3 { /// * `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check /// * `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max /// - #[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))] pub fn increase_liquidity_v2<'a, 'b, 'c: 'info, 'info>( ctx: Context<'a, 'b, 'c, 'info, IncreaseLiquidityV2<'info>>, liquidity: u128, @@ -392,6 +432,7 @@ pub mod amm_v3 { instructions::increase_liquidity_v2(ctx, liquidity, amount_0_max, amount_1_max, base_flag) } + /// #[deprecated(note = "Use `decrease_liquidity_v2` instead.")] /// Decreases liquidity with a exist position /// /// # Arguments @@ -401,7 +442,6 @@ pub mod amm_v3 { /// * `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity /// * `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity /// - #[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))] pub fn decrease_liquidity<'a, 'b, 'c: 'info, 'info>( ctx: Context<'a, 'b, 'c, 'info, DecreaseLiquidity<'info>>, liquidity: u128, @@ -420,7 +460,6 @@ pub mod amm_v3 { /// * `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity /// * `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity /// - #[access_control(is_authorized_for_token(&ctx.accounts.nft_owner, &ctx.accounts.nft_account))] pub fn decrease_liquidity_v2<'a, 'b, 'c: 'info, 'info>( ctx: Context<'a, 'b, 'c, 'info, DecreaseLiquidityV2<'info>>, liquidity: u128, @@ -430,6 +469,7 @@ pub mod amm_v3 { instructions::decrease_liquidity_v2(ctx, liquidity, amount_0_min, amount_1_min) } + /// #[deprecated(note = "Use `swap_v2` instead.")] /// Swaps one token for as much as possible of another token across a single pool /// /// # Arguments diff --git a/programs/amm/src/states/operation_account.rs b/programs/amm/src/states/operation_account.rs index 5dfe2361..f9867d38 100644 --- a/programs/amm/src/states/operation_account.rs +++ b/programs/amm/src/states/operation_account.rs @@ -7,7 +7,7 @@ pub const WHITE_MINT_SIZE_USIZE: usize = 100; /// Holds the current owner of the factory #[account(zero_copy(unsafe))] -#[repr(packed)] +#[repr(C, packed)] #[derive(Debug)] pub struct OperationState { /// Bump to identify PDA diff --git a/programs/amm/src/states/oracle.rs b/programs/amm/src/states/oracle.rs index 03aea8ba..de860dfe 100644 --- a/programs/amm/src/states/oracle.rs +++ b/programs/amm/src/states/oracle.rs @@ -12,7 +12,7 @@ pub const OBSERVATION_UPDATE_DURATION_DEFAULT: u32 = 15; /// The element of observations in ObservationState #[zero_copy(unsafe)] -#[repr(packed)] +#[repr(C, packed)] #[derive(Default, Debug)] pub struct Observation { /// The block timestamp of the observation @@ -28,7 +28,7 @@ impl Observation { } #[account(zero_copy(unsafe))] -#[repr(packed)] +#[repr(C, packed)] #[cfg_attr(feature = "client", derive(Debug))] pub struct ObservationState { /// Whether the ObservationState is initialized @@ -72,7 +72,7 @@ impl ObservationState { Ok(()) } - // Writes an oracle observation to the account + /// Writes an oracle observation to the account /// /// # Arguments /// diff --git a/programs/amm/src/states/personal_position.rs b/programs/amm/src/states/personal_position.rs index 1f8415b9..6a437141 100644 --- a/programs/amm/src/states/personal_position.rs +++ b/programs/amm/src/states/personal_position.rs @@ -3,11 +3,13 @@ use crate::pool::REWARD_NUM; use crate::util::get_recent_epoch; use anchor_lang::prelude::*; +use super::POSITION_SEED; + #[account] #[derive(Default, Debug)] pub struct PersonalPositionState { /// Bump to identify PDA - pub bump: u8, + pub bump: [u8; 1], /// Mint address of the tokenized position pub nft_mint: Pubkey, @@ -48,6 +50,14 @@ impl PersonalPositionState { pub const LEN: usize = 8 + 1 + 32 + 32 + 4 + 4 + 16 + 16 + 16 + 8 + 8 + PositionRewardInfo::LEN * REWARD_NUM + 64; + pub fn seeds(&self) -> [&[u8]; 3] { + [ + &POSITION_SEED.as_bytes(), + self.nft_mint.as_ref(), + self.bump.as_ref(), + ] + } + pub fn update_rewards( &mut self, reward_growths_inside: [u128; REWARD_NUM], diff --git a/programs/amm/src/states/pool.rs b/programs/amm/src/states/pool.rs index 181d3670..2809bbbc 100644 --- a/programs/amm/src/states/pool.rs +++ b/programs/amm/src/states/pool.rs @@ -53,7 +53,7 @@ pub enum PoolStatusBitFlag { /// PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]` /// #[account(zero_copy(unsafe))] -#[repr(packed)] +#[repr(C, packed)] #[derive(Default, Debug)] pub struct PoolState { /// Bump to identify PDA @@ -596,7 +596,7 @@ pub enum RewardState { } #[zero_copy(unsafe)] -#[repr(packed)] +#[repr(C, packed)] #[derive(Default, Debug, PartialEq, Eq)] pub struct RewardInfo { /// Reward state diff --git a/programs/amm/src/states/tick_array.rs b/programs/amm/src/states/tick_array.rs index 18495cbf..0dc7172f 100644 --- a/programs/amm/src/states/tick_array.rs +++ b/programs/amm/src/states/tick_array.rs @@ -14,7 +14,7 @@ pub const TICK_ARRAY_SIZE: i32 = 60; // pub const MIN_TICK_ARRAY_START_INDEX: i32 = -443636; // pub const MAX_TICK_ARRAY_START_INDEX: i32 = 306600; #[account(zero_copy(unsafe))] -#[repr(packed)] +#[repr(C, packed)] pub struct TickArrayState { pub pool_id: Pubkey, pub start_tick_index: i32, @@ -265,7 +265,7 @@ impl Default for TickArrayState { } #[zero_copy(unsafe)] -#[repr(packed)] +#[repr(C, packed)] #[derive(Default, Debug)] pub struct TickState { pub tick: i32, diff --git a/programs/amm/src/states/tickarray_bitmap_extension.rs b/programs/amm/src/states/tickarray_bitmap_extension.rs index 28879f07..6814c68f 100644 --- a/programs/amm/src/states/tickarray_bitmap_extension.rs +++ b/programs/amm/src/states/tickarray_bitmap_extension.rs @@ -14,7 +14,7 @@ use std::ops::BitXor; const EXTENSION_TICKARRAY_BITMAP_SIZE: usize = 14; #[account(zero_copy(unsafe))] -#[repr(packed)] +#[repr(C, packed)] #[derive(Debug)] pub struct TickArrayBitmapExtension { pub pool_id: Pubkey, diff --git a/programs/amm/src/util/access_control.rs b/programs/amm/src/util/access_control.rs deleted file mode 100644 index f45a6230..00000000 --- a/programs/amm/src/util/access_control.rs +++ /dev/null @@ -1,21 +0,0 @@ -use crate::error::ErrorCode; -use anchor_lang::prelude::*; -use anchor_spl::token_interface::TokenAccount; - -/// Ensures that the signer is the owner or a delgated authority for the position NFT -/// -/// # Arguments -/// -/// * `signer` - The signer address -/// * `token_account` - The token account holding the position NFT -/// -pub fn is_authorized_for_token<'info>( - signer: &Signer<'info>, - token_account: &Box>, -) -> Result<()> { - require!( - token_account.amount == 1 && token_account.owner == signer.key(), - ErrorCode::NotApproved - ); - Ok(()) -} diff --git a/programs/amm/src/util/mod.rs b/programs/amm/src/util/mod.rs index 5c8a8853..0e5e509f 100644 --- a/programs/amm/src/util/mod.rs +++ b/programs/amm/src/util/mod.rs @@ -4,8 +4,5 @@ pub use token::*; pub mod system; pub use system::*; -pub mod access_control; -pub use access_control::*; - pub mod account_load; pub use account_load::*; diff --git a/programs/amm/src/util/token.rs b/programs/amm/src/util/token.rs index 3cc54af9..24490ca3 100644 --- a/programs/amm/src/util/token.rs +++ b/programs/amm/src/util/token.rs @@ -1,23 +1,26 @@ +use super::get_recent_epoch; +use crate::error::ErrorCode; use crate::states::*; -use anchor_lang::prelude::*; -use anchor_spl::{ - token::{self, Token}, - token_2022::{ +use anchor_lang::{ + prelude::*, + system_program::{create_account, CreateAccount}, +}; +use anchor_spl::token::{self, Token}; +use anchor_spl::token_2022::{ + self, + spl_token_2022::{ self, - spl_token_2022::{ - self, - extension::{ - transfer_fee::{TransferFeeConfig, MAX_FEE_BASIS_POINTS}, - BaseStateWithExtensions, ExtensionType, StateWithExtensions, - }, + extension::{ + metadata_pointer, + transfer_fee::{TransferFeeConfig, MAX_FEE_BASIS_POINTS}, + BaseStateWithExtensions, ExtensionType, StateWithExtensions, }, }, - token_interface::{Mint, TokenAccount}, + Token2022, }; +use anchor_spl::token_interface::{initialize_mint2, InitializeMint2, Mint}; use std::collections::HashSet; -use super::get_recent_epoch; - const MINT_WHITELIST: [&'static str; 4] = [ "HVbpJAQGNpkgBaYBZQBR1t7yFdvaYVp2vCQQfKKEN4tM", "Crn4x1Y2HUKko7ox2EZMT6N2t2ZyH7eKtwkBGVnhEq1g", @@ -36,8 +39,8 @@ pub fn invoke_memo_instruction<'info>( pub fn transfer_from_user_to_pool_vault<'info>( signer: &Signer<'info>, - from: &InterfaceAccount<'info, TokenAccount>, - to_vault: &InterfaceAccount<'info, TokenAccount>, + from: &AccountInfo<'info>, + to_vault: &AccountInfo<'info>, mint: Option>>, token_program: &AccountInfo<'info>, token_program_2022: Option>, @@ -83,8 +86,8 @@ pub fn transfer_from_user_to_pool_vault<'info>( pub fn transfer_from_pool_vault_to_user<'info>( pool_state_loader: &AccountLoader<'info, PoolState>, - from_vault: &InterfaceAccount<'info, TokenAccount>, - to: &InterfaceAccount<'info, TokenAccount>, + from_vault: &AccountInfo<'info>, + to: &AccountInfo<'info>, mint: Option>>, token_program: &AccountInfo<'info>, token_program_2022: Option>, @@ -133,21 +136,14 @@ pub fn transfer_from_pool_vault_to_user<'info>( pub fn close_spl_account<'a, 'b, 'c, 'info>( owner: &AccountInfo<'info>, destination: &AccountInfo<'info>, - close_account: &InterfaceAccount<'info, TokenAccount>, - token_program: &Program<'info, Token>, - // token_program_2022: &Program<'info, Token2022>, + close_account: &AccountInfo<'info>, + token_program: &AccountInfo<'info>, signers_seeds: &[&[&[u8]]], ) -> Result<()> { - let token_program_info = token_program.to_account_info(); - let close_account_info = close_account.to_account_info(); - // if close_account_info.owner == token_program_2022.key { - // token_program_info = token_program_2022.to_account_info() - // } - token_2022::close_account(CpiContext::new_with_signer( - token_program_info, + token_program.to_account_info(), token_2022::CloseAccount { - account: close_account_info, + account: close_account.to_account_info(), destination: destination.to_account_info(), authority: owner.to_account_info(), }, @@ -157,18 +153,14 @@ pub fn close_spl_account<'a, 'b, 'c, 'info>( pub fn burn<'a, 'b, 'c, 'info>( owner: &Signer<'info>, - mint: &InterfaceAccount<'info, Mint>, - burn_account: &InterfaceAccount<'info, TokenAccount>, - token_program: &Program<'info, Token>, - // token_program_2022: &Program<'info, Token2022>, + mint: &AccountInfo<'info>, + burn_account: &AccountInfo<'info>, + token_program: &AccountInfo<'info>, signers_seeds: &[&[&[u8]]], amount: u64, ) -> Result<()> { let mint_info = mint.to_account_info(); let token_program_info: AccountInfo<'_> = token_program.to_account_info(); - // if mint_info.owner == token_program_2022.key { - // token_program_info = token_program_2022.to_account_info() - // } token_2022::burn( CpiContext::new_with_signer( token_program_info, @@ -251,9 +243,99 @@ pub fn is_supported_mint(mint_account: &InterfaceAccount) -> Result && e != ExtensionType::MetadataPointer && e != ExtensionType::TokenMetadata && e != ExtensionType::InterestBearingConfig + && e != ExtensionType::MintCloseAuthority { return Ok(false); } } Ok(true) } + +pub fn create_position_nft_mint_with_extensions<'info>( + payer: &Signer<'info>, + position_nft_mint: &AccountInfo<'info>, + mint_authority: &AccountInfo<'info>, + mint_close_authority: &AccountInfo<'info>, + system_program: &Program<'info, System>, + token_2022_program: &Program<'info, Token2022>, + with_matedata: bool, +) -> Result<()> { + let extensions = if with_matedata { + [ + ExtensionType::MintCloseAuthority, + ExtensionType::MetadataPointer, + ] + .to_vec() + } else { + [ExtensionType::MintCloseAuthority].to_vec() + }; + let space = + ExtensionType::try_calculate_account_len::(&extensions)?; + + let lamports = Rent::get()?.minimum_balance(space); + + // create mint account + create_account( + CpiContext::new( + system_program.to_account_info(), + CreateAccount { + from: payer.to_account_info(), + to: position_nft_mint.to_account_info(), + }, + ), + lamports, + space as u64, + token_2022_program.key, + )?; + + // initialize token extensions + for e in extensions { + match e { + ExtensionType::MetadataPointer => { + let ix = metadata_pointer::instruction::initialize( + token_2022_program.key, + position_nft_mint.key, + None, + Some(position_nft_mint.key()), + )?; + solana_program::program::invoke( + &ix, + &[ + token_2022_program.to_account_info(), + position_nft_mint.to_account_info(), + ], + )?; + } + ExtensionType::MintCloseAuthority => { + let ix = spl_token_2022::instruction::initialize_mint_close_authority( + token_2022_program.key, + position_nft_mint.key, + Some(mint_close_authority.key), + )?; + solana_program::program::invoke( + &ix, + &[ + token_2022_program.to_account_info(), + position_nft_mint.to_account_info(), + ], + )?; + } + _ => { + return err!(ErrorCode::NotSupportMint); + } + } + } + + // initialize mint account + initialize_mint2( + CpiContext::new( + token_2022_program.to_account_info(), + InitializeMint2 { + mint: position_nft_mint.to_account_info(), + }, + ), + 0, + &mint_authority.key(), + None, + ) +}