diff --git a/solana/programs/token-bridge/src/legacy/processor/attest_token.rs b/solana/programs/token-bridge/src/legacy/processor/attest_token.rs index e69272d171..6c5c544059 100644 --- a/solana/programs/token-bridge/src/legacy/processor/attest_token.rs +++ b/solana/programs/token-bridge/src/legacy/processor/attest_token.rs @@ -109,40 +109,7 @@ impl<'info> fn order_account_infos<'a>( account_infos: &'a [AccountInfo<'info>], ) -> Result>> { - const NUM_ACCOUNTS: usize = 14; - const CORE_BRIDGE_PROGRAM_IDX: usize = NUM_ACCOUNTS - 1; - const SYSTEM_PROGRAM_IDX: usize = CORE_BRIDGE_PROGRAM_IDX - 1; - - let mut infos = account_infos.to_vec(); - - // This check is inclusive because Core Bridge program, System program and Token program can - // be in any order. - if infos.len() >= NUM_ACCOUNTS { - // System program needs to exist in these account infos. - let system_program_idx = infos - .iter() - .position(|info| info.key() == anchor_lang::system_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure System program is in the right index. - if system_program_idx != SYSTEM_PROGRAM_IDX { - infos.swap(SYSTEM_PROGRAM_IDX, system_program_idx); - } - - // Core Bridge program needs to exist in these account infos. - let core_bridge_program_idx = infos - .iter() - .position(|info| info.key() == core_bridge_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure Token program is in the right index. - if core_bridge_program_idx != CORE_BRIDGE_PROGRAM_IDX { - infos.swap(CORE_BRIDGE_PROGRAM_IDX, core_bridge_program_idx); - } - } - - // Done. - Ok(infos) + utils::fix_account_order(account_infos, 14, false, true) } } diff --git a/solana/programs/token-bridge/src/legacy/processor/complete_transfer/mod.rs b/solana/programs/token-bridge/src/legacy/processor/complete_transfer/mod.rs index 46dce4b05f..849a6b38e9 100644 --- a/solana/programs/token-bridge/src/legacy/processor/complete_transfer/mod.rs +++ b/solana/programs/token-bridge/src/legacy/processor/complete_transfer/mod.rs @@ -4,7 +4,7 @@ pub use native::*; mod wrapped; pub use wrapped::*; -use crate::{error::TokenBridgeError, state::RegisteredEmitter}; +use crate::{utils::fix_account_order, error::TokenBridgeError, state::RegisteredEmitter}; use anchor_lang::prelude::*; use core_bridge_program::{ legacy::utils::LegacyAnchorized, @@ -107,35 +107,5 @@ pub fn validate_token_transfer_vaa( pub fn order_complete_transfer_account_infos<'info>( account_infos: &[AccountInfo<'info>], ) -> Result>> { - const NUM_ACCOUNTS: usize = 13; - const TOKEN_PROGRAM_IDX: usize = NUM_ACCOUNTS - 1; - const SYSTEM_PROGRAM_IDX: usize = TOKEN_PROGRAM_IDX - 1; - - let mut infos = account_infos.to_vec(); - if infos.len() >= NUM_ACCOUNTS { - // System program needs to exist in these account infos. - let system_program_idx = infos - .iter() - .position(|info| info.key() == anchor_lang::system_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure System program is in the right index. - if system_program_idx != SYSTEM_PROGRAM_IDX { - infos.swap(SYSTEM_PROGRAM_IDX, system_program_idx); - } - - // Token program needs to exist in these account infos. - let token_program_idx = infos - .iter() - .position(|info| info.key() == anchor_spl::token::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure Token program is in the right index. - if token_program_idx != TOKEN_PROGRAM_IDX { - infos.swap(TOKEN_PROGRAM_IDX, token_program_idx); - } - } - - // Done. - Ok(infos) + fix_account_order(account_infos, 13, true, false) } diff --git a/solana/programs/token-bridge/src/legacy/processor/complete_transfer_with_payload/mod.rs b/solana/programs/token-bridge/src/legacy/processor/complete_transfer_with_payload/mod.rs index 8fdf785b3c..9d291ac780 100644 --- a/solana/programs/token-bridge/src/legacy/processor/complete_transfer_with_payload/mod.rs +++ b/solana/programs/token-bridge/src/legacy/processor/complete_transfer_with_payload/mod.rs @@ -4,7 +4,7 @@ pub use native::*; mod wrapped; pub use wrapped::*; -use crate::{error::TokenBridgeError, legacy::state::RegisteredEmitter}; +use crate::{error::TokenBridgeError, legacy::state::RegisteredEmitter, utils::fix_account_order}; use anchor_lang::prelude::*; use core_bridge_program::{ legacy::utils::LegacyAnchorized, @@ -86,37 +86,5 @@ pub fn validate_token_transfer_with_payload_vaa( pub fn order_complete_transfer_with_payload_account_infos<'info>( account_infos: &[AccountInfo<'info>], ) -> Result>> { - const NUM_ACCOUNTS: usize = 14; - const TOKEN_PROGRAM_IDX: usize = NUM_ACCOUNTS - 1; - const SYSTEM_PROGRAM_IDX: usize = TOKEN_PROGRAM_IDX - 1; - - let mut infos = account_infos.to_vec(); - - // This check is inclusive because System program and Token program can be in any order. - if infos.len() >= NUM_ACCOUNTS { - // System program needs to exist in these account infos. - let system_program_idx = infos - .iter() - .position(|info| info.key() == anchor_lang::system_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure System program is in the right index. - if system_program_idx != SYSTEM_PROGRAM_IDX { - infos.swap(SYSTEM_PROGRAM_IDX, system_program_idx); - } - - // Token program needs to exist in these account infos. - let token_program_idx = infos - .iter() - .position(|info| info.key() == anchor_spl::token::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure Token program is in the right index. - if token_program_idx != TOKEN_PROGRAM_IDX { - infos.swap(TOKEN_PROGRAM_IDX, token_program_idx); - } - } - - // Done. - Ok(infos) + fix_account_order(account_infos, 14, true, false) } diff --git a/solana/programs/token-bridge/src/legacy/processor/create_or_update_wrapped.rs b/solana/programs/token-bridge/src/legacy/processor/create_or_update_wrapped.rs index 4edaa58f27..156e4e8461 100644 --- a/solana/programs/token-bridge/src/legacy/processor/create_or_update_wrapped.rs +++ b/solana/programs/token-bridge/src/legacy/processor/create_or_update_wrapped.rs @@ -34,6 +34,10 @@ pub struct CreateOrUpdateWrapped<'info> { /// CHECK: Posted VAA account, which will be read via zero-copy deserialization in the /// instruction handler, which also checks this account discriminator (so there is no need to /// check PDA seeds here). + #[account( + constraint = try_attestation(&vaa, |attestation| attestation.token_chain())? + != core_bridge_sdk::SOLANA_CHAIN @ TokenBridgeError::NativeAsset, + )] vaa: AccountInfo<'info>, /// CHECK: Account representing that a VAA has been consumed. Seeds are checked when @@ -48,12 +52,12 @@ pub struct CreateOrUpdateWrapped<'info> { #[account( init_if_needed, payer = payer, - mint::decimals = try_attestation_decimals(&vaa)?, + mint::decimals = try_attestation(&vaa, |att| cap_decimals(att.decimals()))?, mint::authority = mint_authority, seeds = [ WRAPPED_MINT_SEED_PREFIX, - try_attestation_token_chain_bytes(&vaa)?.as_ref(), - try_attestation_token_address(&vaa)?.as_ref(), + try_attestation(&vaa, |att| att.token_chain())?.to_be_bytes().as_ref(), + try_attestation(&vaa, |att| att.token_address())?.as_ref(), ], bump, )] @@ -128,45 +132,6 @@ impl<'info> core_bridge_program::legacy::utils::ProcessLegacyInstruction<'info, const ANCHOR_IX_FN: fn(Context, EmptyArgs) -> Result<()> = create_or_update_wrapped; } -fn try_attestation_decimals(vaa_acc_info: &AccountInfo) -> Result { - let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?; - let msg = TokenBridgeMessage::try_from(vaa.try_payload()?) - .map_err(|_| TokenBridgeError::InvalidTokenBridgePayload)?; - msg.attestation() - .map(|attestation| cap_decimals(attestation.decimals())) - .ok_or(error!(TokenBridgeError::InvalidTokenBridgeVaa)) -} - -fn try_attestation_token_chain_bytes(vaa_acc_info: &AccountInfo) -> Result<[u8; 2]> { - let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?; - let msg = TokenBridgeMessage::try_from(vaa.try_payload()?) - .map_err(|_| TokenBridgeError::InvalidTokenBridgePayload)?; - - let token_chain = msg - .attestation() - .map(|attestation| attestation.token_chain()) - .ok_or(error!(TokenBridgeError::InvalidTokenBridgeVaa))?; - - // This token must have originated from another network. - require_neq!( - token_chain, - core_bridge_sdk::SOLANA_CHAIN, - TokenBridgeError::NativeAsset - ); - - // Done. - Ok(token_chain.to_be_bytes()) -} - -fn try_attestation_token_address(vaa_acc_info: &AccountInfo) -> Result<[u8; 32]> { - let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?; - let msg = TokenBridgeMessage::try_from(vaa.try_payload()?) - .map_err(|_| TokenBridgeError::InvalidTokenBridgePayload)?; - msg.attestation() - .map(|attestation| attestation.token_address()) - .ok_or(error!(TokenBridgeError::InvalidTokenBridgeVaa)) -} - impl<'info> CreateOrUpdateWrapped<'info> { fn constraints(ctx: &Context) -> Result<()> { let vaa = &ctx.accounts.vaa; @@ -374,14 +339,18 @@ fn handle_update_wrapped(ctx: Context) -> Result<()> { } } -fn cap_decimals(decimals: u8) -> u8 { - if decimals > MAX_DECIMALS { - MAX_DECIMALS - } else { - decimals - } +fn try_attestation(vaa_acc_info: &AccountInfo, func: F) -> Result + where F: FnOnce(&Attestation) -> T { + let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?; + let msg = TokenBridgeMessage::try_from(vaa.try_payload()?) + .map_err(|_| TokenBridgeError::InvalidTokenBridgePayload)?; + msg.attestation() + .map(func) + .ok_or(error!(TokenBridgeError::InvalidTokenBridgeVaa)) } +fn cap_decimals(decimals: u8) -> u8 { std::cmp::min(decimals, MAX_DECIMALS) } + struct FixedMeta { symbol: String, name: String, diff --git a/solana/programs/token-bridge/src/legacy/processor/transfer_tokens/mod.rs b/solana/programs/token-bridge/src/legacy/processor/transfer_tokens/mod.rs index 470bf30796..e95154f2cd 100644 --- a/solana/programs/token-bridge/src/legacy/processor/transfer_tokens/mod.rs +++ b/solana/programs/token-bridge/src/legacy/processor/transfer_tokens/mod.rs @@ -6,6 +6,8 @@ pub use wrapped::*; use anchor_lang::prelude::*; +use crate::utils::fix_account_order; + /// The Anchor context orders the accounts as: /// /// 1. `payer` @@ -33,50 +35,5 @@ use anchor_lang::prelude::*; pub(super) fn order_transfer_tokens_account_infos<'info>( account_infos: &[AccountInfo<'info>], ) -> Result>> { - const NUM_ACCOUNTS: usize = 17; - const CORE_BRIDGE_PROGRAM_IDX: usize = NUM_ACCOUNTS - 1; - const TOKEN_PROGRAM_IDX: usize = CORE_BRIDGE_PROGRAM_IDX - 1; - const SYSTEM_PROGRAM_IDX: usize = TOKEN_PROGRAM_IDX - 1; - - let mut infos = account_infos.to_vec(); - - // This check is inclusive because Core Bridge program, System program and Token program can - // be in any order. - if infos.len() >= NUM_ACCOUNTS { - // System program needs to exist in these account infos. - let system_program_idx = infos - .iter() - .position(|info| info.key() == anchor_lang::system_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure System program is in the right index. - if system_program_idx != SYSTEM_PROGRAM_IDX { - infos.swap(SYSTEM_PROGRAM_IDX, system_program_idx); - } - - // Token program needs to exist in these account infos. - let token_program_idx = infos - .iter() - .position(|info| info.key() == anchor_spl::token::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure Token program is in the right index. - if token_program_idx != TOKEN_PROGRAM_IDX { - infos.swap(TOKEN_PROGRAM_IDX, token_program_idx); - } - - // Core Bridge program needs to exist in these account infos. - let core_bridge_program_idx = infos - .iter() - .position(|info| info.key() == core_bridge_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure Token program is in the right index. - if core_bridge_program_idx != CORE_BRIDGE_PROGRAM_IDX { - infos.swap(CORE_BRIDGE_PROGRAM_IDX, core_bridge_program_idx); - } - } - - // Done. - Ok(infos) + fix_account_order(account_infos, 17, true, true) } diff --git a/solana/programs/token-bridge/src/legacy/processor/transfer_tokens_with_payload/mod.rs b/solana/programs/token-bridge/src/legacy/processor/transfer_tokens_with_payload/mod.rs index 471f6a0393..2a5fcf65a0 100644 --- a/solana/programs/token-bridge/src/legacy/processor/transfer_tokens_with_payload/mod.rs +++ b/solana/programs/token-bridge/src/legacy/processor/transfer_tokens_with_payload/mod.rs @@ -6,6 +6,8 @@ pub use wrapped::*; use anchor_lang::prelude::*; +use crate::utils::fix_account_order; + /// The Anchor context orders the accounts as: /// /// 1. `payer` @@ -34,50 +36,5 @@ use anchor_lang::prelude::*; pub(super) fn order_transfer_tokens_with_payload_account_infos<'info>( account_infos: &[AccountInfo<'info>], ) -> Result>> { - const NUM_ACCOUNTS: usize = 18; - const CORE_BRIDGE_PROGRAM_IDX: usize = NUM_ACCOUNTS - 1; - const TOKEN_PROGRAM_IDX: usize = CORE_BRIDGE_PROGRAM_IDX - 1; - const SYSTEM_PROGRAM_IDX: usize = TOKEN_PROGRAM_IDX - 1; - - let mut infos = account_infos.to_vec(); - - // This check is inclusive because Core Bridge program, System program and Token program can - // be in any order. - if infos.len() >= NUM_ACCOUNTS { - // System program needs to exist in these account infos. - let system_program_idx = infos - .iter() - .position(|info| info.key() == anchor_lang::system_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure System program is in the right index. - if system_program_idx != SYSTEM_PROGRAM_IDX { - infos.swap(SYSTEM_PROGRAM_IDX, system_program_idx); - } - - // Token program needs to exist in these account infos. - let token_program_idx = infos - .iter() - .position(|info| info.key() == anchor_spl::token::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure Token program is in the right index. - if token_program_idx != TOKEN_PROGRAM_IDX { - infos.swap(TOKEN_PROGRAM_IDX, token_program_idx); - } - - // Core Bridge program needs to exist in these account infos. - let core_bridge_program_idx = infos - .iter() - .position(|info| info.key() == core_bridge_program::ID) - .ok_or(error!(ErrorCode::InvalidProgramId))?; - - // Make sure Token program is in the right index. - if core_bridge_program_idx != CORE_BRIDGE_PROGRAM_IDX { - infos.swap(CORE_BRIDGE_PROGRAM_IDX, core_bridge_program_idx); - } - } - - // Done. - Ok(infos) + fix_account_order(account_infos, 18, true, true) } diff --git a/solana/programs/token-bridge/src/processor/governance/register_chain.rs b/solana/programs/token-bridge/src/processor/governance/register_chain.rs index 90a4acc7f7..9d0f2a2205 100644 --- a/solana/programs/token-bridge/src/processor/governance/register_chain.rs +++ b/solana/programs/token-bridge/src/processor/governance/register_chain.rs @@ -24,7 +24,7 @@ pub struct RegisterChain<'info> { init, payer = payer, space = RegisteredEmitter::INIT_SPACE, - seeds = [try_decree_foreign_chain_bytes(&vaa)?.as_ref()], + seeds = [try_decree(&vaa, |decree| decree.foreign_chain())?.to_be_bytes().as_ref()], bump, )] registered_emitter: Account<'info, LegacyAnchorized<0, RegisteredEmitter>>, @@ -38,8 +38,8 @@ pub struct RegisterChain<'info> { payer = payer, space = RegisteredEmitter::INIT_SPACE, seeds = [ - try_decree_foreign_chain_bytes(&vaa)?.as_ref(), - try_decree_foreign_emitter(&vaa)?.as_ref(), + try_decree(&vaa, |decree| decree.foreign_chain())?.to_be_bytes().as_ref(), + try_decree(&vaa, |decree| decree.foreign_emitter())?.as_ref(), ], bump, )] @@ -106,24 +106,14 @@ pub fn register_chain(ctx: Context) -> Result<()> { Ok(()) } -fn try_decree_foreign_chain_bytes(vaa_acc_info: &AccountInfo) -> Result<[u8; 2]> { +fn try_decree(vaa_acc_info: &AccountInfo, func: F) -> Result + where F: FnOnce(&wormhole_raw_vaas::token_bridge::RegisterChain) -> T { let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?; let gov_payload = TokenBridgeGovPayload::try_from(vaa.try_payload()?) .map_err(|_| error!(TokenBridgeError::InvalidGovernanceVaa))?; gov_payload .decree() .register_chain() - .map(|decree| decree.foreign_chain().to_be_bytes()) - .ok_or(error!(TokenBridgeError::InvalidGovernanceAction)) -} - -fn try_decree_foreign_emitter(vaa_acc_info: &AccountInfo) -> Result<[u8; 32]> { - let vaa = core_bridge_sdk::VaaAccount::load(vaa_acc_info)?; - let gov_payload = TokenBridgeGovPayload::try_from(vaa.try_payload()?) - .map_err(|_| error!(TokenBridgeError::InvalidGovernanceVaa))?; - gov_payload - .decree() - .register_chain() - .map(|decree| decree.foreign_emitter()) + .map(func) .ok_or(error!(TokenBridgeError::InvalidGovernanceAction)) } diff --git a/solana/programs/token-bridge/src/utils/mod.rs b/solana/programs/token-bridge/src/utils/mod.rs index f4c50a1926..7f49dab1c3 100644 --- a/solana/programs/token-bridge/src/utils/mod.rs +++ b/solana/programs/token-bridge/src/utils/mod.rs @@ -31,3 +31,47 @@ pub fn new_sender_address( None => Ok(sender_authority.key()), } } + +pub fn fix_account_order<'info>( + account_infos: &[AccountInfo<'info>], + num_accounts: usize, + requires_token_program: bool, + requires_core_bridge_program: bool, +) -> Result>> { + let mut infos = account_infos.to_vec(); + + // This check is inclusive because Core Bridge program, System program and Token program can + // be in any order. + if infos.len() >= num_accounts { + let mut expected_index = num_accounts - 1; + let mut swap_program_to_index = |program_id: &Pubkey| -> Result<()> { + let program_idx = infos + .iter() + .position(|info| info.key() == *program_id) + .ok_or(error!(ErrorCode::InvalidProgramId))?; + + // Make sure the program is in the right index. + if program_idx != expected_index { + infos.swap(expected_index, program_idx); + } + + expected_index -= 1; + + Ok(()) + }; + + //Swapping programs into their correct positions in reverse order (last to first) + //The Core Bridge program is always the last account if it is required + if requires_core_bridge_program { + swap_program_to_index(&core_bridge_program::ID)?; + } + //The Token program is either the last account or preceeds the Core Bridge program + if requires_token_program { + swap_program_to_index(&anchor_spl::token::ID)?; + } + //Finally, the System program is always required and comes before the other two + swap_program_to_index(&anchor_lang::system_program::ID)?; + } + + Ok(infos) +}