From 5ea13f0046045c0b9f998d2b078afd1766fe8497 Mon Sep 17 00:00:00 2001 From: banegil Date: Tue, 28 Jan 2025 16:15:47 +0100 Subject: [PATCH 1/2] on_receive_trasnfer fix --- .../programs/bridge-escrow/src/lib.rs | 122 ++++++------------ 1 file changed, 39 insertions(+), 83 deletions(-) diff --git a/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs b/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs index 6b2ffa30..b067c17c 100644 --- a/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs +++ b/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs @@ -13,7 +13,6 @@ const BRIDGE_CONTRACT_PUBKEY: &str = "2HLLVco5HvwWriNbUhmVwA2pCetRkpgrqwnjcsZdyTKT"; const AUCTIONEER_SEED: &[u8] = b"auctioneer"; -const FEE_VAULT_SEED: &[u8] = b"fee_vault"; const INTENT_SEED: &[u8] = b"intent"; const DUMMY_SEED: &[u8] = b"dummy"; @@ -26,6 +25,27 @@ mod tests; declare_id!("AhfoGVmS19tvkEG2hBuZJ1D6qYEjyFmXZ1qPoFD6H4Mj"); +pub fn check_staking_caller(ix_sysvar: &AccountInfo) -> Result<()> { + let caller_program_id = + solana_program::sysvar::instructions::get_instruction_relative( + 0, ix_sysvar, + )? + .program_id; + check_staking_program(&caller_program_id) +} + +pub fn check_staking_program( + program_id: &Pubkey +) -> Result<()> { + let expected_program_ids = [ + Pubkey::from_str(BRIDGE_CONTRACT_PUBKEY).unwrap(), + ]; + match expected_program_ids.contains(program_id) { + false => Err(ErrorCode::InvalidCPICall.into()), + true => Ok(()), + } +} + #[program] pub mod bridge_escrow { // use anchor_client::solana_sdk::signer::Signer; @@ -73,17 +93,8 @@ pub mod bridge_escrow { ErrorCode::SrcUserNotUserIn ); - - // Calculate the amount_in and fee_amount - let mut amount_in = new_intent.amount_in; - if new_intent.single_domain { - amount_in -= new_intent.amount_in / 1000; // 0.1% deduction - } else { - amount_in -= (new_intent.amount_in * 29) / 10000; // 0.29% deduction - } - let fee_amount = new_intent.amount_in - amount_in; + // Step 2: Escrow the funds (same as before) - // Step 2: Escrow the funds (same as before) : `amount_in` let cpi_accounts = SplTransfer { from: ctx.accounts.user_token_account.to_account_info(), to: ctx.accounts.escrow_token_account.to_account_info(), @@ -93,19 +104,7 @@ pub mod bridge_escrow { let cpi_program = ctx.accounts.token_program.to_account_info(); let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - token::transfer(cpi_ctx, amount_in)?; - - // Step 2.5: Store token fees (fee_amount) in the fee vault - token::transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - SplTransfer { - from: ctx.accounts.user_token_account.to_account_info(), - to: ctx.accounts.fee_token_account.to_account_info(), - authority: ctx.accounts.user.to_account_info(), - } - ), fee_amount - )?; + token::transfer(cpi_ctx, new_intent.amount_in)?; events::emit(events::Event::EscrowFunds(events::EscrowFunds { amount: new_intent.amount_in, @@ -126,6 +125,13 @@ pub mod bridge_escrow { ErrorCode::InvalidTimeout ); + let mut amount_in = new_intent.amount_in; + if new_intent.single_domain { + amount_in -= new_intent.amount_in / 1000; // 0.1% deduction + } else { + amount_in -= (new_intent.amount_in * 29) / 10000; // 0.29% deduction + } + intent.intent_id = new_intent.intent_id.clone(); intent.user_in = new_intent.user_in.key(); @@ -217,6 +223,8 @@ pub mod bridge_escrow { intent_id: String, memo: String, ) -> Result<()> { + check_staking_caller(&ctx.accounts.instruction)?; + // Split and extract memo fields let parts: Vec<&str> = memo.split(',').collect(); require!(parts.len() == 5, ErrorCode::InvalidMemoFormat); // Ensure memo has 5 parts @@ -596,30 +604,6 @@ pub mod bridge_escrow { Ok(()) } - - pub fn collect_fees(ctx: Context) -> Result<()> { - // Create CPI accounts for the transfer - let cpi_accounts: SplTransfer<'_> = SplTransfer { - from: ctx.accounts.fee_token_account.to_account_info(), - to: ctx.accounts.auctioneer_token_account.to_account_info(), - authority: ctx.accounts.auctioneer_state.to_account_info(), - }; - - // Derive signer seeds for the PDA - let bump = ctx.bumps.auctioneer_state; - let seeds = &[AUCTIONEER_SEED, core::slice::from_ref(&bump)]; - let seeds = seeds.as_ref(); - let signer_seeds = core::slice::from_ref(&seeds); - - // Create CPI context with the signer seeds - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer_seeds); - - // Perform the token transfer - token::transfer(cpi_ctx, ctx.accounts.fee_token_account.amount)?; - - Ok(()) - } } // Define the Auctioneer account @@ -686,6 +670,9 @@ pub struct Initialize<'info> { #[derive(Accounts)] #[instruction(intent_id: String, memo: String)] pub struct ReceiveTransferContext<'info> { + #[account(address = Pubkey::from_str(BRIDGE_CONTRACT_PUBKEY).unwrap())] + /// CHECK: + pub ibc_program: Option>, #[account(seeds = [AUCTIONEER_SEED], bump)] pub auctioneer_state: Account<'info, Auctioneer>, @@ -902,9 +889,6 @@ pub struct EscrowAndStoreIntent<'info> { // Box the escrow token account as it's mutable and holds token data #[account(init_if_needed, payer = user, associated_token::mint = token_mint, associated_token::authority = auctioneer_state)] pub escrow_token_account: Box>, - - #[account(init_if_needed, payer = user, seeds = [FEE_VAULT_SEED, token_mint.key().as_ref()], bump, token::mint = token_mint, token::authority = auctioneer_state)] - pub fee_token_account: Box>, // From StoreIntent // Box the intent account, as it's a new account with considerable space allocated @@ -1003,36 +987,6 @@ pub struct OnTimeoutCrossChain<'info> { pub system_program: Program<'info, System>, } -#[derive(Accounts)] -pub struct CollectFees<'info> { - #[account(mut, signer)] - pub auctioneer: Signer<'info>, // The auctioneer must sign the transaction and act as the payer - - #[account( - seeds = [AUCTIONEER_SEED], - bump, - constraint = auctioneer_state.authority == *auctioneer.key - )] - pub auctioneer_state: Account<'info, Auctioneer>, // PDA managing the escrow account - - #[account(mut, seeds = [FEE_VAULT_SEED, token_mint.key().as_ref()], bump, token::mint = token_mint, token::authority = auctioneer_state)] - pub fee_token_account: Box>, - - #[account( - init_if_needed, - payer = auctioneer, // Ensure payer is mutable - associated_token::mint = token_mint, - associated_token::authority = auctioneer - )] - pub auctioneer_token_account: Account<'info, TokenAccount>, // Auctioneer's USDC token account - - pub token_mint: Account<'info, Mint>, // USDC token mint - - pub token_program: Program<'info, Token>, // SPL Token program - pub associated_token_program: Program<'info, AssociatedToken>, // Associated Token program - pub system_program: Program<'info, System>, // System program for creating accounts -} - // Define custom errors #[error_code] pub enum ErrorCode { @@ -1101,5 +1055,7 @@ pub enum ErrorCode { #[msg("fee_collector != accounts.fee_collector.as_ref().unwrap().key")] InvalidFeeCollector, #[msg("User in (signer) is not intent.user_in")] - UserInNotIntentUserIn -} + UserInNotIntentUserIn, + #[msg("Invalid cpi call")] + InvalidCPICall +} \ No newline at end of file From bfbbd3c10bd5c60d7580cadc3fcf2aeda991ff06 Mon Sep 17 00:00:00 2001 From: doradelta <64730977+doradelta@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:15:32 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Dhruv D Jain --- solana/bridge-escrow/programs/bridge-escrow/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs b/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs index b067c17c..e3f1b1d5 100644 --- a/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs +++ b/solana/bridge-escrow/programs/bridge-escrow/src/lib.rs @@ -25,7 +25,7 @@ mod tests; declare_id!("AhfoGVmS19tvkEG2hBuZJ1D6qYEjyFmXZ1qPoFD6H4Mj"); -pub fn check_staking_caller(ix_sysvar: &AccountInfo) -> Result<()> { +pub fn check_solana_ibc_caller(ix_sysvar: &AccountInfo) -> Result<()> { let caller_program_id = solana_program::sysvar::instructions::get_instruction_relative( 0, ix_sysvar, @@ -34,7 +34,7 @@ pub fn check_staking_caller(ix_sysvar: &AccountInfo) -> Result<()> { check_staking_program(&caller_program_id) } -pub fn check_staking_program( +pub fn check_solana_ibc_program( program_id: &Pubkey ) -> Result<()> { let expected_program_ids = [