diff --git a/chains/solana/contracts/programs/ccip-router/README.md b/chains/solana/contracts/programs/ccip-router/README.md index b4e6988f7..3731da277 100644 --- a/chains/solana/contracts/programs/ccip-router/README.md +++ b/chains/solana/contracts/programs/ccip-router/README.md @@ -2,106 +2,6 @@ Collapsed Router + OnRamp + OffRamp Contracts Implementation for CCIP in SVM. -## Messages - -### Initialization - -`initialize` - -1. Initialize the Config PDA with the SVM Chain Selector, the Default Extra Args and the Supported Destination Chain Selectors and the Sequence Number with 0. - -#### Initialization Accounts - -1. `PDA("config", program_id)`: **Init**, it is used to store the default configuration for Extra Args, the SVM Chain Selector and the supported list of Destination Chain Selectors. -1. `signer`: The sender of the message. - -### Update Config - -`add_chain_selector`, `remove_chain_selector`, `update_svm_chain_selector`, `update_default_gas_limit` & `update_default_allow_out_of_order_execution` - -1. Update the Config PDA with the SVM Chain Selector. -1. Update the Config PDA with the Default Extra Args. -1. Init/Close the Chain State PDA with the Supported Destination Chain Selectors (add/remove). - -#### Update Config Accounts - -1. `PDA("config", program_id)`: **Mut**, it is used to store the default configuration for Extra Args, the SVM Chain Selector and the supported list of Destination Chain Selectors. -1. `PDA("chain_state", chain_selector, program_id)`: **Init/Close**, it stores the latest sequence number per destination chain [only for `add_chain_selector` & `remove_chain_selector`]. -1. `signer`: The sender of the message. - -### Send Message - -`ccipSend` - -1. Check if the Destination Chain Selector is supported (enforced by the `chain_state` account). -1. Emit `CCIPMessageSent` event. -1. Update the Sequence Number. - -#### Send Message Accounts - -1. `PDA("config", program_id)`: **Read only**, it is used to read the default configuration for Extra Args, the SVM Chain Selector and the supported list of Destination Chain Selectors. -1. `PDA("chain_state", chain_selector, program_id)`: **Mut**, increases the latest sequence number per destination chain. -1. `signer`: The sender of the message. - -### Commit Report - -`commit` - -1. Check if the Source Chain Selector is supported. -1. Check if the Interval is valid. -1. Check if the Merkle Root is not empty and is new. -1. Update the Source Chain Reports with the new report. -1. Emit `CommitReportAccepted` event. -1. Emit `Transmitted` event. - -#### Commit Report Accounts - -1. `PDA("chain_state", chain_selector, program_id)`: **Read only**, checks if the Source Chain Selector is supported. -1. `PDA("commit_report", chain_selector, merkle_root, program_id)`: **Init**, stores the Merkle Root for the new Report. -1. `signer`: The sender of the message. - -### Execute Report - -`executeReport` - -1. Validates that the report was commited -1. Validates that the report was not executed (if not emit `SkippedAlreadyExecutedMessage` or `AlreadyAttempted` events) -1. Validates that the Merkle Proof is correct -1. Executes the message -1. Emit `ExecutionStateChanged` event - -#### Execute Report Accounts - -1. `PDA("config", program_id)`: **Read only**, checks if the Chain Selectors are supported. -1. `PDA("commit_report", chain_selector, merkle_root, program_id)`: **Mut**, verifies the Merkle Root for the Report and updates state. -1. `signer`: The sender of the message. - -## Future Work - -- _EMIT_: When Emitting `ExecutionStateChanged` events in the execute report, there are two values that are not being correctly populated: `message_id` & `new_state`. -- _EXTRA ARGS_: - - What should we do when they are empty? Not serialize them in the hash? Now it's not allowed to be empty in the client. - - Fix override for extra args: now it only works for `gasLimit`, it should work for `allowOutOfOrderExecution` too. - - Decide if it makes sense to have a param named `gasLimit` -- _TYPES_ - - [blocked] Use `pub type Selector = u64;` instead of just `u64` in `ccip_send` args. It seems like there are some issues when parsing that in the Typescript tests, not only as a param in messages but also inside the Message struct. - - Anchor v0.29.0 bug that requires parameter naming to match type - https://github.com/coral-xyz/anchor/issues/2820 - - Anchor-Go does not support code generation for aliased types - - Attempted changes ([#92](https://github.com/smartcontractkit/chainlink-internal-integrations/pull/92/commits/2c700c430a78f3e63831d7cd0565bcc7206a1eeb)) for future reference - - Use `[u128; 2]` to store the `Interval` in the `commit` message, this way we will be able to handle a maximum of 128 messages per report to be compatible with EVM. -- _ENABLE/DISABLE CHAINS_: Currently you can add/remove chain selectors, but maybe we need a way to enable/disable them (and only as destination or source). -- _UNIFY ERRORS_: Review the type of errors, and decide if we should have more granularity of them. -- _EXTERNAL EXECUTION_: Understand and documents the limitations over the external execution of the messages. - -## Future Work (Production Readiness) - -- _FEES_: Add fees for the OnRamp flow [WIP] -- _TOKEN POOLS_: Add the flow related to sending tokens and burning/minting in the Token Pools -- _RMN_: Add the flow related to RMN and cursed/blessed lanes or messages -- _RATE LIMIT_: Add rate limit for the ccipSend/executeReports messages -- _NONCES_: Use nonces for the ccipSend message per user, so they can execute ordered transactions on destination chain. -- _ORDERED EXECUTION_: Validate nonces when executing reports in the Off Ramp. - ## Testing - The Rust Tests execute logic specific to the program, to run them use the command @@ -110,8 +10,11 @@ Collapsed Router + OnRamp + OffRamp Contracts Implementation for CCIP in SVM. cargo test ``` -- The Anchor Tests are in the `tests` folder, written in Typescript and use the `@project-serum/solana-web3` library. The tests are run in a local network, so the tests are fast and don't require any real SVM network. To run them, use the command +- The Golang Tests are in the `../contracts/tests` folder, using generated bindings from `gagliardetto/anchor-go` library. The tests are run in a local network, so the tests are fast and don't require any real SVM network. To run them, use the command ```bash - anchor test + anchor build # build contract artifacts + go test ./... -count=1 -failfast ``` + + Note: the [solana-test-validator](https://docs.anza.xyz/cli/examples/test-validator) must be installed diff --git a/chains/solana/contracts/programs/ccip-router/src/extra_args.rs b/chains/solana/contracts/programs/ccip-router/src/extra_args.rs new file mode 100644 index 000000000..3d6230705 --- /dev/null +++ b/chains/solana/contracts/programs/ccip-router/src/extra_args.rs @@ -0,0 +1,61 @@ +use anchor_lang::prelude::*; + +use crate::DestChainConfig; + +// NOTE: this file contains ExtraArgs for SVM -> remote chains +// these are onramp specific extraArgs for ccipSend only + +// bytes4(keccak256("CCIP EVMExtraArgsV2")); +pub const EVM_EXTRA_ARGS_V2_TAG: u32 = 0x181dcf10; + +// bytes4(keccak256("CCIP SVMExtraArgsV1")); +pub const SVM_EXTRA_ARGS_V1_TAG: u32 = 0x1f3b3aba; + +// Extra Args of message when SVM -> EVM +#[derive(Clone, AnchorSerialize, AnchorDeserialize, Default)] +pub struct EVMExtraArgsV2 { + pub gas_limit: u128, // message gas limit for EVM execution + pub allow_out_of_order_execution: bool, // user configurable OOO execution that must match with the DestChainConfig.EnforceOutOfOrderExecution +} + +impl EVMExtraArgsV2 { + pub fn serialize_with_tag(&self) -> Vec { + let mut buffer = EVM_EXTRA_ARGS_V2_TAG.to_be_bytes().to_vec(); + let mut data = self.try_to_vec().unwrap(); + + buffer.append(&mut data); + buffer + } + pub fn default_config(cfg: &DestChainConfig) -> EVMExtraArgsV2 { + EVMExtraArgsV2 { + gas_limit: cfg.default_tx_gas_limit as u128, + ..Default::default() + } + } +} + +// Extra Args of message when SVM -> SVM +#[derive(Clone, AnchorSerialize, AnchorDeserialize, Default)] +pub struct SVMExtraArgsV1 { + pub compute_units: u32, // compute units are used by the offchain to add computeUnitLimits to the execute stage + pub account_is_writable_bitmap: u64, + pub allow_out_of_order_execution: bool, // SVM chains require OOO, but users are required to explicitly state OOO is allowed in extraArgs + pub token_receiver: [u8; 32], // cannot be 0-address if tokens are sent in message + pub accounts: Vec<[u8; 32]>, // account list for messaging on remote SVM chain +} + +impl SVMExtraArgsV1 { + pub fn serialize_with_tag(&self) -> Vec { + let mut buffer = SVM_EXTRA_ARGS_V1_TAG.to_be_bytes().to_vec(); + let mut data = self.try_to_vec().unwrap(); + + buffer.append(&mut data); + buffer + } + pub fn default_config(cfg: &DestChainConfig) -> SVMExtraArgsV1 { + SVMExtraArgsV1 { + compute_units: cfg.default_tx_gas_limit, + ..Default::default() + } + } +} diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs index e7cfd49eb..3f14e1f50 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/admin.rs @@ -165,32 +165,6 @@ pub fn update_svm_chain_selector( Ok(()) } -pub fn update_default_gas_limit( - ctx: Context, - new_gas_limit: u128, -) -> Result<()> { - let mut config = ctx.accounts.config.load_mut()?; - - config.default_gas_limit = new_gas_limit; - - Ok(()) -} - -pub fn update_default_allow_out_of_order_execution( - ctx: Context, - new_allow_out_of_order_execution: bool, -) -> Result<()> { - let mut config = ctx.accounts.config.load_mut()?; - - let mut v = 0_u8; - if new_allow_out_of_order_execution { - v = 1; - } - config.default_allow_out_of_order_execution = v; - - Ok(()) -} - pub fn update_enable_manual_execution_after( ctx: Context, new_enable_manual_execution_after: i64, diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/fee_quoter.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/fee_quoter.rs index b5771359f..16f221f19 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/fee_quoter.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/fee_quoter.rs @@ -11,6 +11,7 @@ use crate::{ }; use super::messages::ramps::validate_svm2any; +use super::onramp::ProcessedExtraArgs; use super::pools::CCIP_LOCK_OR_BURN_V1_RET_BYTES; use super::price_math::get_validated_token_price; use super::price_math::{Exponential, Usd18Decimals}; @@ -32,13 +33,14 @@ pub const SVM_2_EVM_MESSAGE_FIXED_BYTES: U256 = U256::new(32 * 15); /// uint32 destGasAmount takes 1 slot. pub const SVM_2_EVM_MESSAGE_FIXED_BYTES_PER_TOKEN: U256 = U256::new(32 * ((2 * 3) + 3)); +// fee_for_msg returns the required fee for ccipSend and validated extraArgs parameters for the destination chain family pub fn fee_for_msg( message: &SVM2AnyMessage, dest_chain: &DestChain, fee_token_config: &BillingTokenConfig, additional_token_configs: &[Option], additional_token_configs_for_dest_chain: &[PerChainPerTokenConfig], -) -> Result { +) -> Result<(SVMTokenAmount, ProcessedExtraArgs)> { let fee_token = if message.fee_token == Pubkey::default() { native_mint::ID // Wrapped SOL } else { @@ -52,7 +54,7 @@ pub fn fee_for_msg( additional_token_configs_for_dest_chain.len() == message.token_amounts.len(), CcipRouterError::InvalidInputsMissingTokenConfig ); - validate_svm2any(message, dest_chain, fee_token_config)?; + let processed_extra_args = validate_svm2any(message, dest_chain, fee_token_config)?; let fee_token_price = get_validated_token_price(fee_token_config)?; let PackedPrice { @@ -67,12 +69,7 @@ pub fn fee_for_msg( additional_token_configs_for_dest_chain, )?; - let gas_limit = U256::new( - message - .extra_args - .gas_limit - .unwrap_or_else(|| dest_chain.config.default_tx_gas_limit.into()), - ); + let gas_limit = U256::new(processed_extra_args.gas_limit); // Calculate calldata gas cost while accounting for EIP-7623 variable calldata gas pricing // This logic works for EVMs post Pectra upgrade, while being backwards compatible with pre-Pectra EVMs. @@ -127,10 +124,13 @@ pub fn fee_for_msg( .try_into() .map_err(|_| CcipRouterError::InvalidTokenPrice)?; - Ok(SVMTokenAmount { - token: fee_token, - amount: fee_token_amount, - }) + Ok(( + SVMTokenAmount { + token: fee_token, + amount: fee_token_amount, + }, + processed_extra_args, + )) } fn data_availability_cost( @@ -423,7 +423,8 @@ pub mod tests { &[], &[] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, amount: 48282184443231661 @@ -444,7 +445,8 @@ pub mod tests { &[], &[] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, // Increases proportionally to the network fee component of the sum @@ -504,7 +506,8 @@ pub mod tests { &[Some(token_config)], &[per_chain_per_token] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, amount: 52911699750913573, @@ -538,7 +541,8 @@ pub mod tests { &[Some(another_token_config)], &[another_per_chain_per_token_config] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, // Increases proportionally to the min_fee @@ -575,7 +579,8 @@ pub mod tests { &[Some(another_token_config.clone())], &[another_per_chain_per_token_config.clone()] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, amount: 36654452956230811 @@ -593,7 +598,8 @@ pub mod tests { &[Some(another_token_config)], &[another_per_chain_per_token_config] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, // Slight increase in price @@ -628,7 +634,8 @@ pub mod tests { &[None], &[another_per_chain_per_token_config.clone()] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, amount: 35758615812975735 @@ -646,7 +653,8 @@ pub mod tests { &[None], &[another_per_chain_per_token_config.clone()] ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, amount: 35758615812975735 @@ -682,7 +690,8 @@ pub mod tests { &tokens, &per_chains ) - .unwrap(), + .unwrap() + .0, SVMTokenAmount { token: native_mint::ID, // Increases proportionally to the number of tokens diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs index eae2ca0f5..7d07b57ed 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/messages.rs @@ -66,8 +66,9 @@ pub mod ramps { use ethnum::U256; use crate::{ + v1::onramp::{process_extra_args, ProcessedExtraArgs}, BillingTokenConfig, CcipRouterError, DestChain, SVM2AnyMessage, SVMTokenAmount, - CCIP_RECEIVE_DISCRIMINATOR, CHAIN_FAMILY_SELECTOR_EVM, + CCIP_RECEIVE_DISCRIMINATOR, CHAIN_FAMILY_SELECTOR_EVM, CHAIN_FAMILY_SELECTOR_SVM, }; const U160_MAX: U256 = U256::from_words(u32::MAX as u128, u128::MAX); @@ -100,7 +101,7 @@ pub mod ramps { msg: &SVM2AnyMessage, dest_chain: &DestChain, token_config: &BillingTokenConfig, - ) -> Result<()> { + ) -> Result { require!( dest_chain.config.is_enabled, CcipRouterError::DestinationChainDisabled @@ -120,42 +121,48 @@ pub mod ramps { CcipRouterError::UnsupportedNumberOfTokens ); + let processed_extra_args = process_extra_args( + &dest_chain.config, + &msg.extra_args, + !msg.token_amounts.is_empty(), + )?; + require_gte!( dest_chain.config.max_per_msg_gas_limit as u128, - msg.extra_args - .gas_limit - .unwrap_or(dest_chain.config.default_tx_gas_limit as u128), + processed_extra_args.gas_limit, CcipRouterError::MessageGasLimitTooHigh, ); require!( !dest_chain.config.enforce_out_of_order - || msg.extra_args.allow_out_of_order_execution.unwrap_or(false), + || processed_extra_args.allow_out_of_order_execution, CcipRouterError::ExtraArgOutOfOrderExecutionMustBeTrue, ); - validate_dest_family_address(msg, dest_chain.config.chain_family_selector) + validate_dest_family_address(msg, dest_chain.config.chain_family_selector)?; + + Ok(processed_extra_args) } fn validate_dest_family_address( msg: &SVM2AnyMessage, chain_family_selector: [u8; 4], ) -> Result<()> { - const PRECOMPILE_SPACE: u32 = 1024; - let selector = u32::from_be_bytes(chain_family_selector); - // Only EVM is supported as a destination family. - require_eq!( - selector, - CHAIN_FAMILY_SELECTOR_EVM, - CcipRouterError::UnsupportedChainFamilySelector - ); + match selector { + CHAIN_FAMILY_SELECTOR_EVM => validate_evm_dest_address(&msg.receiver), + CHAIN_FAMILY_SELECTOR_SVM => validate_svm_dest_address(&msg.receiver), + _ => Err(CcipRouterError::UnsupportedChainFamilySelector.into()), + } + } - require_eq!(msg.receiver.len(), 32, CcipRouterError::InvalidEVMAddress); + fn validate_evm_dest_address(address: &[u8]) -> Result<()> { + const PRECOMPILE_SPACE: u32 = 1024; + + require_eq!(address.len(), 32, CcipRouterError::InvalidEVMAddress); let address: U256 = U256::from_be_bytes( - msg.receiver - .clone() + address .try_into() .map_err(|_| CcipRouterError::InvalidEncoding)?, ); @@ -173,6 +180,13 @@ pub mod ramps { Ok(()) } + fn validate_svm_dest_address(address: &[u8]) -> Result<()> { + require_eq!(address.len(), 32, CcipRouterError::InvalidSVMAddress); + Pubkey::try_from_slice(address) + .map(|_| ()) + .map_err(|_| CcipRouterError::InvalidSVMAddress.into()) + } + pub fn is_writable(bitmap: &u64, index: u8) -> bool { index < 64 && (bitmap & 1 << index != 0) // check valid index and that bit at index is 1 } @@ -182,7 +196,8 @@ pub mod ramps { use super::super::super::fee_quoter::{PackedPrice, UnpackedDoubleU224}; use super::super::super::price_math::Usd18Decimals; use super::*; - use crate::{ExtraArgsInput, SVMTokenAmount, TimestampedPackedU224}; + use crate::{EVMExtraArgsV2, EVM_EXTRA_ARGS_V2_TAG}; + use crate::{SVMTokenAmount, TimestampedPackedU224}; use anchor_lang::solana_program::pubkey::Pubkey; use anchor_spl::token::spl_token::native_mint; @@ -277,6 +292,68 @@ pub mod ramps { ); } + #[test] + fn message_exceeds_gas_limit_fails_to_validate() { + let mut message = sample_message(); + message.extra_args = EVMExtraArgsV2 { + gas_limit: 1_000_000_000, + allow_out_of_order_execution: false, + } + .serialize_with_tag(); + assert_eq!( + validate_svm2any(&message, &sample_dest_chain(), &sample_billing_config()) + .unwrap_err(), + CcipRouterError::MessageGasLimitTooHigh.into() + ); + } + + #[test] + fn validate_out_of_order_execution() { + let mut dest_chain_enforce = sample_dest_chain(); + dest_chain_enforce.config.enforce_out_of_order = true; + let mut dest_chain_not_enforce = sample_dest_chain(); + dest_chain_not_enforce.config.enforce_out_of_order = false; + + let mut message_ooo = sample_message(); + message_ooo.extra_args = EVMExtraArgsV2 { + gas_limit: 1_000, + allow_out_of_order_execution: true, + } + .serialize_with_tag(); + let mut message_not_ooo = sample_message(); + message_not_ooo.extra_args = EVMExtraArgsV2 { + gas_limit: 1_000, + allow_out_of_order_execution: false, + } + .serialize_with_tag(); + + // allowed cases + validate_svm2any(&message_ooo, &dest_chain_enforce, &sample_billing_config()).unwrap(); + validate_svm2any( + &message_ooo, + &dest_chain_not_enforce, + &sample_billing_config(), + ) + .unwrap(); + validate_svm2any( + &message_not_ooo, + &dest_chain_not_enforce, + &sample_billing_config(), + ) + .unwrap(); + + // not allowed cases + assert_eq!( + validate_svm2any( + &message_not_ooo, + &dest_chain_enforce, + &sample_billing_config() + ) + .unwrap_err(), + CcipRouterError::ExtraArgOutOfOrderExecutionMustBeTrue.into() + ); + } + pub fn sample_message() -> SVM2AnyMessage { let mut receiver = vec![0u8; 32]; @@ -289,10 +366,7 @@ pub mod ramps { data: vec![], token_amounts: vec![], fee_token: native_mint::ID, - extra_args: ExtraArgsInput { - gas_limit: None, - allow_out_of_order_execution: None, - }, + extra_args: EVM_EXTRA_ARGS_V2_TAG.to_be_bytes().to_vec(), // empty extraArgs, use defaults } } diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/offramp.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/offramp.rs index 0fb72b053..ecd689ffe 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/offramp.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/offramp.rs @@ -854,7 +854,7 @@ mod tests { use ethnum::U256; use super::*; - use crate::{Any2SVMRampMessage, Any2SVMTokenTransfer, SVMExtraArgs}; + use crate::{Any2SVMRampExtraArgs, Any2SVMRampMessage, Any2SVMTokenTransfer}; /// Builds a message and hash it, it's compared with a known hash #[test] @@ -891,7 +891,7 @@ mod tests { amount: U256::from_le_bytes([1; 32]).into(), }] .to_vec(), - extra_args: SVMExtraArgs { + extra_args: Any2SVMRampExtraArgs { compute_units: 1000, is_writable_bitmap: 1, }, diff --git a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs index 41dc1f66e..7f0a0e385 100644 --- a/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs +++ b/chains/solana/contracts/programs/ccip-router/src/instructions/v1/onramp.rs @@ -1,5 +1,3 @@ -use std::cell::Ref; - use anchor_lang::prelude::*; use anchor_spl::token_interface; @@ -12,11 +10,12 @@ use super::pools::{ }; use super::price_math::get_validated_token_price; -use crate::seed; +use crate::{seed, CHAIN_FAMILY_SELECTOR_EVM, CHAIN_FAMILY_SELECTOR_SVM}; use crate::{ - AnyExtraArgs, BillingTokenConfig, CCIPMessageSent, CcipRouterError, CcipSend, Config, - DestChainConfig, ExtraArgsInput, GetFee, Nonce, PerChainPerTokenConfig, RampMessageHeader, - SVM2AnyMessage, SVM2AnyRampMessage, SVM2AnyTokenTransfer, SVMTokenAmount, + BillingTokenConfig, CCIPMessageSent, CcipRouterError, CcipSend, DestChainConfig, + EVMExtraArgsV2, GetFee, Nonce, PerChainPerTokenConfig, RampMessageHeader, SVM2AnyMessage, + SVM2AnyRampMessage, SVM2AnyTokenTransfer, SVMExtraArgsV1, SVMTokenAmount, + EVM_EXTRA_ARGS_V2_TAG, SVM_EXTRA_ARGS_V1_TAG, }; pub fn get_fee<'info>( @@ -55,6 +54,7 @@ pub fn get_fee<'info>( &token_billing_config_accounts, &per_chain_per_token_config_accounts, )? + .0 .amount) } @@ -107,7 +107,7 @@ pub fn ccip_send<'info>( }) .collect::>>()?; - let fee = fee_for_msg( + let (fee, processed_extra_args) = fee_for_msg( &message, dest_chain, &ctx.accounts.fee_token_config.config, @@ -166,10 +166,12 @@ pub fn ccip_send<'info>( let sender = ctx.accounts.authority.key.to_owned(); let receiver = message.receiver.clone(); let source_chain_selector = config.svm_chain_selector; - let extra_args = extra_args_or_default(config, message.extra_args); - let nonce_counter_account: &mut Account<'info, Nonce> = &mut ctx.accounts.nonce; - let final_nonce = bump_nonce(nonce_counter_account, extra_args).unwrap(); + let final_nonce = bump_nonce( + nonce_counter_account, + processed_extra_args.allow_out_of_order_execution, + ) + .unwrap(); let token_count = message.token_amounts.len(); require!( @@ -188,7 +190,7 @@ pub fn ccip_send<'info>( sequence_number: dest_chain.state.sequence_number, nonce: final_nonce, }, - extra_args, + extra_args: processed_extra_args.bytes, fee_token: message.fee_token, fee_token_amount: fee.amount.into(), fee_value_juels: link_fee.amount.into(), @@ -307,24 +309,95 @@ fn token_transfer( ///////////// // Helpers // ///////////// -fn extra_args_or_default(default_config: Ref, extra_args: ExtraArgsInput) -> AnyExtraArgs { - let mut result_args = AnyExtraArgs { - gas_limit: default_config.default_gas_limit.to_owned(), - allow_out_of_order_execution: default_config.default_allow_out_of_order_execution != 0, - }; - if let Some(gas_limit) = extra_args.gas_limit { - gas_limit.clone_into(&mut result_args.gas_limit) +// ProcessedExtraArgs contains serialized extra args and unpacks commonly used parameters +#[derive(Debug)] +pub struct ProcessedExtraArgs { + pub bytes: Vec, + pub gas_limit: u128, + pub allow_out_of_order_execution: bool, +} + +// process_extra_args returns serialized extraArgs, gas_limit, allow_out_of_order_execution +// it calls the chain-specific extra args validation logic +pub fn process_extra_args( + dest_config: &DestChainConfig, + extra_args: &[u8], + message_contains_tokens: bool, +) -> Result { + require_gte!(extra_args.len(), 4, CcipRouterError::InvalidInputs); + + let tag: [u8; 4] = extra_args[..4].try_into().unwrap(); + let mut data = &extra_args[4..]; + + match u32::from_be_bytes(dest_config.chain_family_selector) { + CHAIN_FAMILY_SELECTOR_EVM => parse_and_validate_evm_extra_args(dest_config, tag, &mut data), + CHAIN_FAMILY_SELECTOR_SVM => { + parse_and_validate_svm_extra_args(dest_config, tag, &mut data, message_contains_tokens) + } + + _ => Err(CcipRouterError::InvalidChainFamilySelector.into()), } +} - if let Some(allow_out_of_order_execution) = extra_args.allow_out_of_order_execution { - allow_out_of_order_execution.clone_into(&mut result_args.allow_out_of_order_execution) +fn parse_and_validate_evm_extra_args( + cfg: &DestChainConfig, + tag: [u8; 4], + data: &mut &[u8], +) -> Result { + match u32::from_be_bytes(tag) { + EVM_EXTRA_ARGS_V2_TAG => { + let args = if data.is_empty() { + EVMExtraArgsV2::default_config(cfg) + } else { + EVMExtraArgsV2::deserialize(data)? + }; + Ok(ProcessedExtraArgs { + bytes: args.serialize_with_tag(), + gas_limit: args.gas_limit, + allow_out_of_order_execution: args.allow_out_of_order_execution, + }) + } + _ => Err(CcipRouterError::InvalidExtraArgsTag.into()), } +} - result_args +fn parse_and_validate_svm_extra_args( + cfg: &DestChainConfig, + tag: [u8; 4], + data: &mut &[u8], + message_contains_tokens: bool, +) -> Result { + match u32::from_be_bytes(tag) { + SVM_EXTRA_ARGS_V1_TAG => { + let args = if data.is_empty() { + SVMExtraArgsV1::default_config(cfg) + } else { + SVMExtraArgsV1::deserialize(data)? + }; + + // token_receiver != 0 when tokens are present + // token_receiver == 0 when tokens are not present + let receiver_is_zero_address = args.token_receiver == [0; 32]; + require!( + message_contains_tokens != receiver_is_zero_address, + CcipRouterError::InvalidTokenReceiver + ); + + Ok(ProcessedExtraArgs { + bytes: args.serialize_with_tag(), + gas_limit: args.compute_units as u128, + allow_out_of_order_execution: args.allow_out_of_order_execution, + }) + } + _ => Err(CcipRouterError::InvalidExtraArgsTag.into()), + } } -fn bump_nonce(nonce_counter_account: &mut Account, extra_args: AnyExtraArgs) -> Result { +fn bump_nonce( + nonce_counter_account: &mut Account, + allow_out_of_order_execution: bool, +) -> Result { // Avoid Re-initialization attack as the account is init_if_needed // https://solana.com/developers/courses/program-security/reinitialization-attacks#add-is-initialized-check if nonce_counter_account.version == 0 { @@ -334,9 +407,9 @@ fn bump_nonce(nonce_counter_account: &mut Account, extra_args: AnyExtraAr nonce_counter_account.counter = 0; } - // TODO: Require config.enforce_out_of_order => extra_args.allow_out_of_order_execution + // config.enforce_out_of_order checked in `validate_svm2any` let mut final_nonce = 0; - if !extra_args.allow_out_of_order_execution { + if !allow_out_of_order_execution { nonce_counter_account.counter = nonce_counter_account.counter.checked_add(1).unwrap(); final_nonce = nonce_counter_account.counter; } @@ -470,7 +543,7 @@ mod tests { fee_quoter::tests::sample_additional_token, messages::ramps::tests::sample_dest_chain, }; - use super::*; + use super::{process_extra_args, *}; /// Builds a message and hash it, it's compared with a known hash #[test] @@ -490,10 +563,11 @@ mod tests { 0, 0, 0, 0, ] .to_vec(), - extra_args: AnyExtraArgs { + extra_args: EVMExtraArgsV2 { gas_limit: 1, allow_out_of_order_execution: true, - }, + } + .serialize_with_tag(), fee_token: Pubkey::try_from("DS2tt4BX7YwCw7yrDNwbAdnYrxjeCPeGJbHmZEYC8RTb").unwrap(), fee_token_amount: 50u32.into(), token_amounts: [SVM2AnyTokenTransfer { @@ -513,7 +587,7 @@ mod tests { let hash_result = hash(&message); assert_eq!( - "009bc51872fe41ea096bd881bf52e3daf07c80e112ffeeba6aa503d8281b6bfd", + "2335e7898faa4e7e8816a6b1e0cf47ea2a18bb66bca205d0cb3ae4a8ce5c72f7", hex::encode(hash_result) ); } @@ -612,4 +686,103 @@ mod tests { CcipRouterError::SourceTokenDataTooLarge.into() ); } + + #[test] + fn process_extra_args_matches_family() { + let evm_dest_chain = sample_dest_chain(); + let mut svm_dest_chain = sample_dest_chain(); + svm_dest_chain.config.chain_family_selector = CHAIN_FAMILY_SELECTOR_SVM.to_be_bytes(); + let evm_extra_args = EVM_EXTRA_ARGS_V2_TAG.to_be_bytes().to_vec(); + let svm_extra_args = SVM_EXTRA_ARGS_V1_TAG.to_be_bytes().to_vec(); + let mut none_dest_chain = sample_dest_chain(); + none_dest_chain.config.chain_family_selector = [0; 4]; + + // match family + process_extra_args(&evm_dest_chain.config, &evm_extra_args, false).unwrap(); + process_extra_args(&svm_dest_chain.config, &svm_extra_args, false).unwrap(); + assert_eq!( + process_extra_args(&none_dest_chain.config, &evm_extra_args, false).unwrap_err(), + CcipRouterError::InvalidChainFamilySelector.into() + ); + assert_eq!( + process_extra_args(&none_dest_chain.config, &[0; 0], false).unwrap_err(), + CcipRouterError::InvalidInputs.into() + ); + + // evm - default case + let extra_args = + process_extra_args(&evm_dest_chain.config, &evm_extra_args, false).unwrap(); + assert_eq!(extra_args.bytes[..4], EVM_EXTRA_ARGS_V2_TAG.to_be_bytes()); + assert_eq!( + extra_args.gas_limit, + evm_dest_chain.config.default_tx_gas_limit as u128 + ); + assert_eq!(extra_args.allow_out_of_order_execution, false); + + // evm - passed in data + let extra_args = process_extra_args( + &evm_dest_chain.config, + &EVMExtraArgsV2 { + gas_limit: 100, + allow_out_of_order_execution: true, + } + .serialize_with_tag(), + false, + ) + .unwrap(); + assert_eq!(extra_args.bytes[..4], EVM_EXTRA_ARGS_V2_TAG.to_be_bytes()); + assert_eq!(extra_args.gas_limit, 100); + assert_eq!(extra_args.allow_out_of_order_execution, true); + + // evm - fail to match + assert_eq!( + process_extra_args(&evm_dest_chain.config, &svm_extra_args, false).unwrap_err(), + CcipRouterError::InvalidExtraArgsTag.into() + ); + + // svm - default case + let extra_args = + process_extra_args(&svm_dest_chain.config, &svm_extra_args, false).unwrap(); + assert_eq!(extra_args.bytes[..4], SVM_EXTRA_ARGS_V1_TAG.to_be_bytes()); + assert_eq!( + extra_args.gas_limit, + svm_dest_chain.config.default_tx_gas_limit as u128 + ); + assert_eq!(extra_args.allow_out_of_order_execution, false); + + // svm - contains tokens but no receiver address + assert_eq!( + process_extra_args(&svm_dest_chain.config, &svm_extra_args, true).unwrap_err(), + CcipRouterError::InvalidTokenReceiver.into(), + ); + + // svm - passed in data + let args = SVMExtraArgsV1 { + compute_units: 100, + account_is_writable_bitmap: 3, + allow_out_of_order_execution: true, + token_receiver: Pubkey::try_from("DS2tt4BX7YwCw7yrDNwbAdnYrxjeCPeGJbHmZEYC8RTa") + .unwrap() + .to_bytes(), + accounts: vec![ + Pubkey::try_from("DS2tt4BX7YwCw7yrDNwbAdnYrxjeCPeGJbHmZEYC8RTa") + .unwrap() + .to_bytes(), + Pubkey::try_from("DS2tt4BX7YwCw7yrDNwbAdnYrxjeCPeGJbHmZEYC8RTa") + .unwrap() + .to_bytes(), + ], + }; + let extra_args = + process_extra_args(&svm_dest_chain.config, &args.serialize_with_tag(), true).unwrap(); + assert_eq!(extra_args.bytes, args.serialize_with_tag()); + assert_eq!(extra_args.gas_limit, 100); + assert_eq!(extra_args.allow_out_of_order_execution, true); + + // svm - fail to match + assert_eq!( + process_extra_args(&svm_dest_chain.config, &evm_extra_args, false).unwrap_err(), + CcipRouterError::InvalidExtraArgsTag.into() + ); + } } diff --git a/chains/solana/contracts/programs/ccip-router/src/lib.rs b/chains/solana/contracts/programs/ccip-router/src/lib.rs index 423afaa24..a2bca9bbe 100644 --- a/chains/solana/contracts/programs/ccip-router/src/lib.rs +++ b/chains/solana/contracts/programs/ccip-router/src/lib.rs @@ -21,6 +21,9 @@ use crate::messages::*; mod instructions; use crate::instructions::*; +mod extra_args; +use crate::extra_args::*; + // Anchor discriminators for CPI calls const CCIP_RECEIVE_DISCRIMINATOR: [u8; 8] = [0x0b, 0xf4, 0x09, 0xf9, 0x2c, 0x53, 0x2f, 0xf5]; // ccip_receive const TOKENPOOL_LOCK_OR_BURN_DISCRIMINATOR: [u8; 8] = @@ -52,15 +55,11 @@ pub mod ccip_router { /// /// * `ctx` - The context containing the accounts required for initialization. /// * `svm_chain_selector` - The chain selector for SVM. - /// * `default_gas_limit` - The default gas limit for other destination chains. - /// * `default_allow_out_of_order_execution` - Whether out-of-order execution is allowed by default for other destination chains. /// * `enable_execution_after` - The minimum amount of time required between a message has been committed and can be manually executed. #[allow(clippy::too_many_arguments)] pub fn initialize( ctx: Context, svm_chain_selector: u64, - default_gas_limit: u128, - default_allow_out_of_order_execution: bool, enable_execution_after: i64, fee_aggregator: Pubkey, link_token_mint: Pubkey, @@ -70,15 +69,10 @@ pub mod ccip_router { require!(config.version == 0, CcipRouterError::InvalidInputs); // assert uninitialized state - AccountLoader doesn't work with constraint config.version = 1; config.svm_chain_selector = svm_chain_selector; - config.default_gas_limit = default_gas_limit; config.enable_manual_execution_after = enable_execution_after; config.link_token_mint = link_token_mint; config.max_fee_juels_per_msg = max_fee_juels_per_msg; - if default_allow_out_of_order_execution { - config.default_allow_out_of_order_execution = 1; - } - config.owner = ctx.accounts.authority.key(); config.fee_aggregator = fee_aggregator; @@ -239,39 +233,6 @@ pub mod ccip_router { v1::admin::update_svm_chain_selector(ctx, new_chain_selector) } - /// Updates the default gas limit in the router configuration. - /// - /// This change affects the default value for gas limit on every other destination chain. - /// The Admin is the only one able to update the default gas limit. - /// - /// # Arguments - /// - /// * `ctx` - The context containing the accounts required for updating the configuration. - /// * `new_gas_limit` - The new default gas limit. - pub fn update_default_gas_limit( - ctx: Context, - new_gas_limit: u128, - ) -> Result<()> { - v1::admin::update_default_gas_limit(ctx, new_gas_limit) - } - - /// Updates the default setting for allowing out-of-order execution for other destination chains. - /// The Admin is the only one able to update this config. - /// - /// # Arguments - /// - /// * `ctx` - The context containing the accounts required for updating the configuration. - /// * `new_allow_out_of_order_execution` - The new setting for allowing out-of-order execution. - pub fn update_default_allow_out_of_order_execution( - ctx: Context, - new_allow_out_of_order_execution: bool, - ) -> Result<()> { - v1::admin::update_default_allow_out_of_order_execution( - ctx, - new_allow_out_of_order_execution, - ) - } - /// Updates the minimum amount of time required between a message being committed and when it can be manually executed. /// /// This is part of the OffRamp Configuration for SVM. @@ -691,4 +652,12 @@ pub enum CcipRouterError { ExtraArgOutOfOrderExecutionMustBeTrue, #[msg("Invalid writability bitmap")] InvalidWritabilityBitmap, + #[msg("Invalid extra args tag")] + InvalidExtraArgsTag, + #[msg("Invalid chain family selector")] + InvalidChainFamilySelector, + #[msg("Invalid token receiver")] + InvalidTokenReceiver, + #[msg("Invalid SVM address")] + InvalidSVMAddress, } diff --git a/chains/solana/contracts/programs/ccip-router/src/messages.rs b/chains/solana/contracts/programs/ccip-router/src/messages.rs index 9d0899696..be6eef8ea 100644 --- a/chains/solana/contracts/programs/ccip-router/src/messages.rs +++ b/chains/solana/contracts/programs/ccip-router/src/messages.rs @@ -2,7 +2,9 @@ use std::convert::Into; use anchor_lang::prelude::*; +// https://github.com/smartcontractkit/chainlink/blob/ff8a597fd9df653f8967427498eaa5a04b19febb/contracts/src/v0.8/ccip/libraries/Internal.sol#L276 pub const CHAIN_FAMILY_SELECTOR_EVM: u32 = 0x2812d52c; +pub const CHAIN_FAMILY_SELECTOR_SVM: u32 = 0x1e10bdc4; #[derive(Clone, Copy, AnchorSerialize, AnchorDeserialize)] // Family-agnostic header for OnRamp & OffRamp messages. @@ -35,25 +37,20 @@ pub struct ExecutionReportSingleChain { pub proofs: Vec<[u8; 32]>, } +// Any2SVMRampExtraArgs is used during the execute or manual execute calls (offramp only) #[derive(Clone, AnchorSerialize, AnchorDeserialize)] -pub struct SVMExtraArgs { +pub struct Any2SVMRampExtraArgs { pub compute_units: u32, pub is_writable_bitmap: u64, // part of the message to avoid calculating it onchain } -impl SVMExtraArgs { +impl Any2SVMRampExtraArgs { pub fn len(&self) -> usize { 4 // compute units + 8 // isWritable bitmap } } -#[derive(Clone, Copy, AnchorSerialize, AnchorDeserialize)] -pub struct AnyExtraArgs { - pub gas_limit: u128, - pub allow_out_of_order_execution: bool, -} - #[derive(Clone, AnchorSerialize, AnchorDeserialize)] pub struct Any2SVMRampMessage { pub header: RampMessageHeader, @@ -64,7 +61,7 @@ pub struct Any2SVMRampMessage { // (Logic receiver is passed into relevant instructions through `remaining_accounts`) pub token_receiver: Pubkey, pub token_amounts: Vec, - pub extra_args: SVMExtraArgs, + pub extra_args: Any2SVMRampExtraArgs, pub on_ramp_address: Vec, } @@ -91,7 +88,7 @@ pub struct SVM2AnyRampMessage { pub sender: Pubkey, // sender address on the source chain pub data: Vec, // arbitrary data payload supplied by the message sender pub receiver: Vec, // receiver address on the destination chain - pub extra_args: AnyExtraArgs, // destination-chain specific extra args, such as the gasLimit for EVM chains + pub extra_args: Vec, // destination-chain specific extra args, such as the gasLimit for EVM chains pub fee_token: Pubkey, pub token_amounts: Vec, pub fee_token_amount: CrossChainAmount, @@ -146,7 +143,7 @@ pub struct SVM2AnyMessage { pub data: Vec, pub token_amounts: Vec, pub fee_token: Pubkey, // pass zero address if native SOL - pub extra_args: ExtraArgsInput, + pub extra_args: Vec, } #[derive(Clone, AnchorSerialize, AnchorDeserialize, Default, Debug, PartialEq, Eq)] @@ -155,12 +152,6 @@ pub struct SVMTokenAmount { pub amount: u64, // u64 - amount local to solana } -#[derive(Clone, Copy, AnchorSerialize, AnchorDeserialize)] -pub struct ExtraArgsInput { - pub gas_limit: Option, - pub allow_out_of_order_execution: Option, -} - #[derive(AnchorSerialize, AnchorDeserialize, Clone, Copy, Default, Debug)] pub struct CrossChainAmount { le_bytes: [u8; 32], diff --git a/chains/solana/contracts/programs/ccip-router/src/state.rs b/chains/solana/contracts/programs/ccip-router/src/state.rs index 70ce34439..db528127c 100644 --- a/chains/solana/contracts/programs/ccip-router/src/state.rs +++ b/chains/solana/contracts/programs/ccip-router/src/state.rs @@ -5,23 +5,18 @@ use anchor_lang::prelude::*; #[derive(InitSpace, AnchorSerialize, AnchorDeserialize)] pub struct Config { pub version: u8, - pub default_allow_out_of_order_execution: u8, // bytemuck::Pod compliant required for zero_copy - _padding0: [u8; 6], + _padding0: [u8; 7], pub svm_chain_selector: u64, - pub default_gas_limit: u128, + pub enable_manual_execution_after: i64, // Expressed as Unix time (i.e. seconds since the Unix epoch). _padding1: [u8; 8], + pub max_fee_juels_per_msg: u128, pub owner: Pubkey, pub proposed_owner: Pubkey, - pub enable_manual_execution_after: i64, // Expressed as Unix time (i.e. seconds since the Unix epoch). _padding2: [u8; 8], - pub ocr3: [Ocr3Config; 2], - // TODO: token pool global config - // TODO: billing global configs' - pub max_fee_juels_per_msg: u128, pub link_token_mint: Pubkey, pub fee_aggregator: Pubkey, // Allowed address to withdraw billed fees to (will use ATAs derived from it) } diff --git a/chains/solana/contracts/target/idl/ccip_router.json b/chains/solana/contracts/target/idl/ccip_router.json index bd348828f..7dc857e1c 100644 --- a/chains/solana/contracts/target/idl/ccip_router.json +++ b/chains/solana/contracts/target/idl/ccip_router.json @@ -32,8 +32,6 @@ "", "* `ctx` - The context containing the accounts required for initialization.", "* `svm_chain_selector` - The chain selector for SVM.", - "* `default_gas_limit` - The default gas limit for other destination chains.", - "* `default_allow_out_of_order_execution` - Whether out-of-order execution is allowed by default for other destination chains.", "* `enable_execution_after` - The minimum amount of time required between a message has been committed and can be manually executed." ], "accounts": [ @@ -83,14 +81,6 @@ "name": "svmChainSelector", "type": "u64" }, - { - "name": "defaultGasLimit", - "type": "u128" - }, - { - "name": "defaultAllowOutOfOrderExecution", - "type": "bool" - }, { "name": "enableExecutionAfter", "type": "i64" @@ -461,78 +451,6 @@ } ] }, - { - "name": "updateDefaultGasLimit", - "docs": [ - "Updates the default gas limit in the router configuration.", - "", - "This change affects the default value for gas limit on every other destination chain.", - "The Admin is the only one able to update the default gas limit.", - "", - "# Arguments", - "", - "* `ctx` - The context containing the accounts required for updating the configuration.", - "* `new_gas_limit` - The new default gas limit." - ], - "accounts": [ - { - "name": "config", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "newGasLimit", - "type": "u128" - } - ] - }, - { - "name": "updateDefaultAllowOutOfOrderExecution", - "docs": [ - "Updates the default setting for allowing out-of-order execution for other destination chains.", - "The Admin is the only one able to update this config.", - "", - "# Arguments", - "", - "* `ctx` - The context containing the accounts required for updating the configuration.", - "* `new_allow_out_of_order_execution` - The new setting for allowing out-of-order execution." - ], - "accounts": [ - { - "name": "config", - "isMut": true, - "isSigner": false - }, - { - "name": "authority", - "isMut": false, - "isSigner": true - }, - { - "name": "systemProgram", - "isMut": false, - "isSigner": false - } - ], - "args": [ - { - "name": "newAllowOutOfOrderExecution", - "type": "bool" - } - ] - }, { "name": "updateEnableManualExecutionAfter", "docs": [ @@ -1557,16 +1475,12 @@ "name": "version", "type": "u8" }, - { - "name": "defaultAllowOutOfOrderExecution", - "type": "u8" - }, { "name": "padding0", "type": { "array": [ "u8", - 6 + 7 ] } }, @@ -1575,8 +1489,8 @@ "type": "u64" }, { - "name": "defaultGasLimit", - "type": "u128" + "name": "enableManualExecutionAfter", + "type": "i64" }, { "name": "padding1", @@ -1587,6 +1501,10 @@ ] } }, + { + "name": "maxFeeJuelsPerMsg", + "type": "u128" + }, { "name": "owner", "type": "publicKey" @@ -1595,10 +1513,6 @@ "name": "proposedOwner", "type": "publicKey" }, - { - "name": "enableManualExecutionAfter", - "type": "i64" - }, { "name": "padding2", "type": { @@ -1619,10 +1533,6 @@ ] } }, - { - "name": "maxFeeJuelsPerMsg", - "type": "u128" - }, { "name": "linkTokenMint", "type": "publicKey" @@ -1985,6 +1895,62 @@ ] } }, + { + "name": "EVMExtraArgsV2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "gasLimit", + "type": "u128" + }, + { + "name": "allowOutOfOrderExecution", + "type": "bool" + } + ] + } + }, + { + "name": "SVMExtraArgsV1", + "type": { + "kind": "struct", + "fields": [ + { + "name": "computeUnits", + "type": "u32" + }, + { + "name": "accountIsWritableBitmap", + "type": "u64" + }, + { + "name": "allowOutOfOrderExecution", + "type": "bool" + }, + { + "name": "tokenReceiver", + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "accounts", + "type": { + "vec": { + "array": [ + "u8", + 32 + ] + } + } + } + ] + } + }, { "name": "RampMessageHeader", "type": { @@ -2066,7 +2032,7 @@ } }, { - "name": "SVMExtraArgs", + "name": "Any2SVMRampExtraArgs", "type": { "kind": "struct", "fields": [ @@ -2081,22 +2047,6 @@ ] } }, - { - "name": "AnyExtraArgs", - "type": { - "kind": "struct", - "fields": [ - { - "name": "gasLimit", - "type": "u128" - }, - { - "name": "allowOutOfOrderExecution", - "type": "bool" - } - ] - } - }, { "name": "Any2SVMRampMessage", "type": { @@ -2131,7 +2081,7 @@ { "name": "extraArgs", "type": { - "defined": "SVMExtraArgs" + "defined": "Any2SVMRampExtraArgs" } }, { @@ -2166,9 +2116,7 @@ }, { "name": "extraArgs", - "type": { - "defined": "AnyExtraArgs" - } + "type": "bytes" }, { "name": "feeToken", @@ -2284,9 +2232,7 @@ }, { "name": "extraArgs", - "type": { - "defined": "ExtraArgsInput" - } + "type": "bytes" } ] } @@ -2307,26 +2253,6 @@ ] } }, - { - "name": "ExtraArgsInput", - "type": { - "kind": "struct", - "fields": [ - { - "name": "gasLimit", - "type": { - "option": "u128" - } - }, - { - "name": "allowOutOfOrderExecution", - "type": { - "option": "bool" - } - } - ] - } - }, { "name": "CrossChainAmount", "type": { @@ -2835,6 +2761,18 @@ }, { "name": "InvalidWritabilityBitmap" + }, + { + "name": "InvalidExtraArgsTag" + }, + { + "name": "InvalidChainFamilySelector" + }, + { + "name": "InvalidTokenReceiver" + }, + { + "name": "InvalidSVMAddress" } ] } diff --git a/chains/solana/contracts/tests/ccip/ccip_router_test.go b/chains/solana/contracts/tests/ccip/ccip_router_test.go index aa0ea8117..09f951c9e 100644 --- a/chains/solana/contracts/tests/ccip/ccip_router_test.go +++ b/chains/solana/contracts/tests/ccip/ccip_router_test.go @@ -58,7 +58,6 @@ func TestCCIPRouter(t *testing.T) { require.NoError(t, gerr) var nonceEvmPDA solana.PublicKey - var nonceSvmPDA solana.PublicKey // billing type AccountsPerToken struct { @@ -145,6 +144,7 @@ func TestCCIPRouter(t *testing.T) { // Small enough to fit in u160, big enough to not fall in the precompile space. validReceiverAddress := [32]byte{} validReceiverAddress[12] = 1 + emptyEVMExtraArgsV2 := testutils.MustSerializeExtraArgs(t, struct{}{}, ccip.EVMExtraArgsV2Tag) var commitLookupTable map[solana.PublicKey]solana.PublicKeySlice @@ -363,9 +363,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("Config", func(t *testing.T) { t.Run("Is initialized", func(t *testing.T) { invalidSVMChainSelector := uint64(17) - defaultGasLimit := bin.Uint128{Lo: 3000, Hi: 0, Endianness: nil} defaultMaxFeeJuelsPerMsg := bin.Uint128{Lo: 300000000, Hi: 0, Endianness: nil} - allowOutOfOrderExecution := true // get program data account data, err := solanaGoClient.GetAccountInfoWithOpts(ctx, config.CcipRouterProgram, &rpc.GetAccountInfoOpts{ @@ -382,8 +380,6 @@ func TestCCIPRouter(t *testing.T) { instruction, err := ccip_router.NewInitializeInstruction( invalidSVMChainSelector, - defaultGasLimit, - allowOutOfOrderExecution, config.EnableExecutionAfter, // fee aggregator address, will be changed in later test anotherUser.PublicKey(), @@ -413,53 +409,9 @@ func TestCCIPRouter(t *testing.T) { require.NoError(t, err, "failed to get account info") } require.Equal(t, uint64(17), configAccount.SvmChainSelector) - require.Equal(t, defaultGasLimit, configAccount.DefaultGasLimit) - require.Equal(t, uint8(1), configAccount.DefaultAllowOutOfOrderExecution) nonceEvmPDA, err = state.FindNoncePDA(config.EvmChainSelector, user.PublicKey(), config.CcipRouterProgram) require.NoError(t, err) - nonceSvmPDA, err = state.FindNoncePDA(config.SVMChainSelector, user.PublicKey(), config.CcipRouterProgram) - require.NoError(t, err) - }) - - t.Run("When admin updates the default gas limit it's updated", func(t *testing.T) { - newGasLimit := bin.Uint128{Lo: 5000, Hi: 0} - - instruction, err := ccip_router.NewUpdateDefaultGasLimitInstruction( - newGasLimit, - config.RouterConfigPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - require.NoError(t, err) - result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) - require.NotNil(t, result) - - var configAccount ccip_router.Config - err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) - require.NoError(t, err, "failed to get account info") - require.Equal(t, uint64(17), configAccount.SvmChainSelector) - require.Equal(t, newGasLimit, configAccount.DefaultGasLimit) - require.Equal(t, uint8(1), configAccount.DefaultAllowOutOfOrderExecution) - }) - - t.Run("When admin updates the default allow out of order execution it's updated", func(t *testing.T) { - instruction, err := ccip_router.NewUpdateDefaultAllowOutOfOrderExecutionInstruction( - false, - config.RouterConfigPDA, - admin.PublicKey(), - solana.SystemProgramID, - ).ValidateAndBuild() - require.NoError(t, err) - result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) - require.NotNil(t, result) - - var configAccount ccip_router.Config - err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) - require.NoError(t, err, "failed to get account info") - require.Equal(t, uint64(17), configAccount.SvmChainSelector) - require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, configAccount.DefaultGasLimit) - require.Equal(t, uint8(0), configAccount.DefaultAllowOutOfOrderExecution) }) t.Run("When admin updates the solana chain selector it's updated", func(t *testing.T) { @@ -477,8 +429,6 @@ func TestCCIPRouter(t *testing.T) { err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.SVMChainSelector, configAccount.SvmChainSelector) - require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, configAccount.DefaultGasLimit) - require.Equal(t, uint8(0), configAccount.DefaultAllowOutOfOrderExecution) }) type InvalidChainBillingInputTest struct { @@ -2024,8 +1974,9 @@ func TestCCIPRouter(t *testing.T) { t.Run("getFee", func(t *testing.T) { t.Run("Fee is retrieved for a correctly formatted message", func(t *testing.T) { message := ccip_router.SVM2AnyMessage{ - Receiver: validReceiverAddress[:], - FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + FeeToken: wsol.mint, + ExtraArgs: emptyEVMExtraArgsV2, } raw := ccip_router.NewGetFeeInstruction(config.EvmChainSelector, message, config.RouterConfigPDA, config.EvmDestChainStatePDA, wsol.billingConfigPDA) @@ -2043,6 +1994,7 @@ func TestCCIPRouter(t *testing.T) { Receiver: validReceiverAddress[:], FeeToken: wsol.mint, TokenAmounts: []ccip_router.SVMTokenAmount{{Token: token0.Mint.PublicKey(), Amount: 1}}, + ExtraArgs: emptyEVMExtraArgsV2, } // Set some fees that will result in some appreciable change in the message fee @@ -2083,8 +2035,9 @@ func TestCCIPRouter(t *testing.T) { for _, address := range [][32]byte{tooBigAddress, tooSmallAddress} { message := ccip_router.SVM2AnyMessage{ - Receiver: address[:], - FeeToken: wsol.mint, + Receiver: address[:], + FeeToken: wsol.mint, + ExtraArgs: emptyEVMExtraArgsV2, } raw := ccip_router.NewGetFeeInstruction(config.EvmChainSelector, message, config.RouterConfigPDA, config.EvmDestChainStatePDA, wsol.billingConfigPDA) @@ -2107,9 +2060,10 @@ func TestCCIPRouter(t *testing.T) { destinationChainStatePDA, err := state.FindDestChainStatePDA(destinationChainSelector, config.CcipRouterProgram) require.NoError(t, err) message := ccip_router.SVM2AnyMessage{ - FeeToken: wsol.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } raw := ccip_router.NewCcipSendInstruction( @@ -2142,9 +2096,10 @@ func TestCCIPRouter(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA message := ccip_router.SVM2AnyMessage{ - FeeToken: wsol.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } raw := ccip_router.NewCcipSendInstruction( @@ -2190,8 +2145,8 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, validReceiverAddress[:], ccipMessageSentEvent.Message.Receiver) data := [3]uint8{4, 5, 6} require.Equal(t, data[:], ccipMessageSentEvent.Message.Data) - require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, ccipMessageSentEvent.Message.ExtraArgs.GasLimit) // default gas limit - require.Equal(t, false, ccipMessageSentEvent.Message.ExtraArgs.AllowOutOfOrderExecution) // default OOO Execution + require.Equal(t, bin.Uint128{Lo: uint64(validDestChainConfig.DefaultTxGasLimit), Hi: 0}, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).GasLimit) // default gas limit + require.Equal(t, false, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).AllowOutOfOrderExecution) // default OOO Execution require.Equal(t, uint64(15), ccipMessageSentEvent.Message.Header.SourceChainSelector) require.Equal(t, uint64(21), ccipMessageSentEvent.Message.Header.DestChainSelector) require.Equal(t, uint64(1), ccipMessageSentEvent.Message.Header.SequenceNumber) @@ -2209,10 +2164,10 @@ func TestCCIPRouter(t *testing.T) { FeeToken: wsol.mint, Receiver: validReceiverAddress[:], Data: []byte{4, 5, 6}, - ExtraArgs: ccip_router.ExtraArgsInput{ - GasLimit: &bin.Uint128{Lo: 99, Hi: 0}, - AllowOutOfOrderExecution: &trueValue, - }, + ExtraArgs: testutils.MustSerializeExtraArgs(t, ccip_router.EVMExtraArgsV2{ + GasLimit: bin.Uint128{Lo: 99, Hi: 0}, + AllowOutOfOrderExecution: trueValue, + }, ccip.EVMExtraArgsV2Tag), } raw := ccip_router.NewCcipSendInstruction( @@ -2258,8 +2213,8 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, validReceiverAddress[:], ccipMessageSentEvent.Message.Receiver) data := [3]uint8{4, 5, 6} require.Equal(t, data[:], ccipMessageSentEvent.Message.Data) - require.Equal(t, bin.Uint128{Lo: 99, Hi: 0}, ccipMessageSentEvent.Message.ExtraArgs.GasLimit) // check it's overwritten - require.Equal(t, true, ccipMessageSentEvent.Message.ExtraArgs.AllowOutOfOrderExecution) // check it's overwritten + require.Equal(t, bin.Uint128{Lo: 99, Hi: 0}, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).GasLimit) // check it's overwritten + require.Equal(t, true, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).AllowOutOfOrderExecution) // check it's overwritten require.Equal(t, uint64(15), ccipMessageSentEvent.Message.Header.SourceChainSelector) require.Equal(t, uint64(21), ccipMessageSentEvent.Message.Header.DestChainSelector) require.Equal(t, uint64(2), ccipMessageSentEvent.Message.Header.SequenceNumber) @@ -2273,9 +2228,9 @@ func TestCCIPRouter(t *testing.T) { FeeToken: wsol.mint, Receiver: validReceiverAddress[:], Data: []byte{4, 5, 6}, - ExtraArgs: ccip_router.ExtraArgsInput{ - GasLimit: &bin.Uint128{Lo: 99, Hi: 0}, - }, + ExtraArgs: testutils.MustSerializeExtraArgs(t, ccip_router.EVMExtraArgsV2{ + GasLimit: bin.Uint128{Lo: 99, Hi: 0}, + }, ccip.EVMExtraArgsV2Tag), } raw := ccip_router.NewCcipSendInstruction( @@ -2321,8 +2276,8 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, validReceiverAddress[:], ccipMessageSentEvent.Message.Receiver) data := [3]uint8{4, 5, 6} require.Equal(t, data[:], ccipMessageSentEvent.Message.Data) - require.Equal(t, bin.Uint128{Lo: 99, Hi: 0}, ccipMessageSentEvent.Message.ExtraArgs.GasLimit) // check it's overwritten - require.Equal(t, false, ccipMessageSentEvent.Message.ExtraArgs.AllowOutOfOrderExecution) // check it's default value + require.Equal(t, bin.Uint128{Lo: 99, Hi: 0}, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).GasLimit) // check it's overwritten + require.Equal(t, false, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).AllowOutOfOrderExecution) // check it's default value require.Equal(t, uint64(15), ccipMessageSentEvent.Message.Header.SourceChainSelector) require.Equal(t, uint64(21), ccipMessageSentEvent.Message.Header.DestChainSelector) require.Equal(t, uint64(3), ccipMessageSentEvent.Message.Header.SequenceNumber) @@ -2332,14 +2287,14 @@ func TestCCIPRouter(t *testing.T) { t.Run("When sending a CCIP Message with allow out of order ExtraArgs overrides Emits CCIPMessageSent", func(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA - trueValue := true message := ccip_router.SVM2AnyMessage{ FeeToken: wsol.mint, Receiver: validReceiverAddress[:], Data: []byte{4, 5, 6}, - ExtraArgs: ccip_router.ExtraArgsInput{ - AllowOutOfOrderExecution: &trueValue, - }, + ExtraArgs: testutils.MustSerializeExtraArgs(t, ccip_router.EVMExtraArgsV2{ + AllowOutOfOrderExecution: true, + GasLimit: bin.Uint128{Lo: 5000, Hi: 0}, + }, ccip.EVMExtraArgsV2Tag), } raw := ccip_router.NewCcipSendInstruction( @@ -2385,8 +2340,8 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, validReceiverAddress[:], ccipMessageSentEvent.Message.Receiver) data := [3]uint8{4, 5, 6} require.Equal(t, data[:], ccipMessageSentEvent.Message.Data) - require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, ccipMessageSentEvent.Message.ExtraArgs.GasLimit) // default gas limit - require.Equal(t, true, ccipMessageSentEvent.Message.ExtraArgs.AllowOutOfOrderExecution) // check it's overwritten + require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).GasLimit) // default gas limit + require.Equal(t, true, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).AllowOutOfOrderExecution) // check it's overwritten require.Equal(t, uint64(15), ccipMessageSentEvent.Message.Header.SourceChainSelector) require.Equal(t, uint64(21), ccipMessageSentEvent.Message.Header.DestChainSelector) require.Equal(t, uint64(4), ccipMessageSentEvent.Message.Header.SequenceNumber) @@ -2400,9 +2355,9 @@ func TestCCIPRouter(t *testing.T) { FeeToken: token2022.mint, Receiver: validReceiverAddress[:], Data: []byte{4, 5, 6}, - ExtraArgs: ccip_router.ExtraArgsInput{ - GasLimit: &bin.Uint128{Lo: 0, Hi: 0}, - }, + ExtraArgs: testutils.MustSerializeExtraArgs(t, ccip_router.EVMExtraArgsV2{ + GasLimit: bin.Uint128{Lo: 0, Hi: 0}, + }, ccip.EVMExtraArgsV2Tag), } raw := ccip_router.NewCcipSendInstruction( @@ -2448,93 +2403,22 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, validReceiverAddress[:], ccipMessageSentEvent.Message.Receiver) data := [3]uint8{4, 5, 6} require.Equal(t, data[:], ccipMessageSentEvent.Message.Data) - require.Equal(t, bin.Uint128{Lo: 0, Hi: 0}, ccipMessageSentEvent.Message.ExtraArgs.GasLimit) - require.Equal(t, false, ccipMessageSentEvent.Message.ExtraArgs.AllowOutOfOrderExecution) + require.Equal(t, bin.Uint128{Lo: 0, Hi: 0}, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).GasLimit) + require.Equal(t, false, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).AllowOutOfOrderExecution) require.Equal(t, uint64(15), ccipMessageSentEvent.Message.Header.SourceChainSelector) require.Equal(t, uint64(21), ccipMessageSentEvent.Message.Header.DestChainSelector) require.Equal(t, uint64(5), ccipMessageSentEvent.Message.Header.SequenceNumber) require.Equal(t, uint64(3), ccipMessageSentEvent.Message.Header.Nonce) }) - t.Run("When gasLimit is too high, it fails", func(t *testing.T) { - destinationChainSelector := config.EvmChainSelector - destinationChainStatePDA := config.EvmDestChainStatePDA - message := ccip_router.SVM2AnyMessage{ - FeeToken: token2022.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, - ExtraArgs: ccip_router.ExtraArgsInput{ - GasLimit: &bin.Uint128{Lo: 0, Hi: 1_000_000_000}, - }, - } - - raw := ccip_router.NewCcipSendInstruction( - destinationChainSelector, - message, - []byte{}, - config.RouterConfigPDA, - destinationChainStatePDA, - nonceEvmPDA, - user.PublicKey(), - solana.SystemProgramID, - token2022.program, - token2022.mint, - token2022.billingConfigPDA, - token2022.billingConfigPDA, - token2022.userATA, - token2022.billingATA, - config.BillingSignerPDA, - config.ExternalTokenPoolsSignerPDA, - ) - raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() - instruction, err := raw.ValidateAndBuild() - require.NoError(t, err) - testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.MessageGasLimitTooHigh_CcipRouterError.String()}) - }) - - t.Run("When out of order execution is enforced, it fails when not enabled", func(t *testing.T) { - destinationChainSelector := config.SVMChainSelector // SVM dest chain requires out of order execution - destinationChainStatePDA := config.SVMDestChainStatePDA - falseVal := false - message := ccip_router.SVM2AnyMessage{ - FeeToken: token2022.mint, - Receiver: validReceiverAddress[:], - ExtraArgs: ccip_router.ExtraArgsInput{ - AllowOutOfOrderExecution: &falseVal, - }, - } - - raw := ccip_router.NewCcipSendInstruction( - destinationChainSelector, - message, - []byte{}, - config.RouterConfigPDA, - destinationChainStatePDA, - nonceSvmPDA, - user.PublicKey(), - solana.SystemProgramID, - token2022.program, - token2022.mint, - token2022.billingConfigPDA, - token2022.billingConfigPDA, - token2022.userATA, - token2022.billingATA, - config.BillingSignerPDA, - config.ExternalTokenPoolsSignerPDA, - ) - raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() - instruction, err := raw.ValidateAndBuild() - require.NoError(t, err) - testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.ExtraArgOutOfOrderExecutionMustBeTrue_CcipRouterError.String()}) - }) - t.Run("When sending a message with an invalid nonce account, it fails", func(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA message := ccip_router.SVM2AnyMessage{ - FeeToken: wsol.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } raw := ccip_router.NewCcipSendInstruction( @@ -2567,9 +2451,10 @@ func TestCCIPRouter(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA message := ccip_router.SVM2AnyMessage{ - FeeToken: wsol.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } raw := ccip_router.NewCcipSendInstruction( @@ -2601,9 +2486,10 @@ func TestCCIPRouter(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA message := ccip_router.SVM2AnyMessage{ - FeeToken: wsol.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: wsol.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } raw := ccip_router.NewCcipSendInstruction( @@ -2655,9 +2541,10 @@ func TestCCIPRouter(t *testing.T) { raw := ccip_router.NewCcipSendInstruction( destinationChainSelector, ccip_router.SVM2AnyMessage{ - FeeToken: messageMint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: messageMint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, }, []byte{}, config.RouterConfigPDA, @@ -2693,9 +2580,10 @@ func TestCCIPRouter(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA message := ccip_router.SVM2AnyMessage{ - FeeToken: token2022.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: token2022.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } anotherUserNonceEVMPDA, err := state.FindNoncePDA(config.EvmChainSelector, anotherUser.PublicKey(), config.CcipRouterProgram) require.NoError(t, err) @@ -2728,9 +2616,10 @@ func TestCCIPRouter(t *testing.T) { destinationChainSelector := config.EvmChainSelector destinationChainStatePDA := config.EvmDestChainStatePDA message := ccip_router.SVM2AnyMessage{ - FeeToken: token2022.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: token2022.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } anotherUserNonceEVMPDA, err := state.FindNoncePDA(config.EvmChainSelector, anotherUser.PublicKey(), config.CcipRouterProgram) require.NoError(t, err) @@ -2778,8 +2667,8 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, validReceiverAddress[:], ccipMessageSentEvent.Message.Receiver) data := [3]uint8{4, 5, 6} require.Equal(t, data[:], ccipMessageSentEvent.Message.Data) - require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, ccipMessageSentEvent.Message.ExtraArgs.GasLimit) - require.Equal(t, false, ccipMessageSentEvent.Message.ExtraArgs.AllowOutOfOrderExecution) + require.Equal(t, bin.Uint128{Lo: uint64(validDestChainConfig.DefaultTxGasLimit), Hi: 0}, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).GasLimit) + require.Equal(t, false, testutils.MustDeserializeExtraArgs(t, &ccip_router.EVMExtraArgsV2{}, ccipMessageSentEvent.Message.ExtraArgs, ccip.EVMExtraArgsV2Tag).AllowOutOfOrderExecution) require.Equal(t, uint64(15), ccipMessageSentEvent.Message.Header.SourceChainSelector) require.Equal(t, uint64(21), ccipMessageSentEvent.Message.Header.DestChainSelector) require.Equal(t, uint64(6), ccipMessageSentEvent.Message.Header.SequenceNumber) @@ -2805,6 +2694,7 @@ func TestCCIPRouter(t *testing.T) { Amount: 1, }, }, + ExtraArgs: emptyEVMExtraArgsV2, } userTokenAccount, ok := token0.User[user.PublicKey()] @@ -2904,6 +2794,7 @@ func TestCCIPRouter(t *testing.T) { Amount: 2, }, }, + ExtraArgs: emptyEVMExtraArgsV2, } userTokenAccount0, ok := token0.User[user.PublicKey()] @@ -2975,6 +2866,7 @@ func TestCCIPRouter(t *testing.T) { Amount: 1, }, }, + ExtraArgs: emptyEVMExtraArgsV2, } userTokenAccount, ok := token0.User[user.PublicKey()] @@ -3113,9 +3005,10 @@ func TestCCIPRouter(t *testing.T) { for _, token := range billingTokens { t.Run("using "+token.name, func(t *testing.T) { message := ccip_router.SVM2AnyMessage{ - FeeToken: token.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: token.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } rawGetFeeIx := ccip_router.NewGetFeeInstruction(config.EvmChainSelector, message, config.RouterConfigPDA, config.EvmDestChainStatePDA, token.billingConfigPDA) ix, err := rawGetFeeIx.ValidateAndBuild() @@ -3163,9 +3056,10 @@ func TestCCIPRouter(t *testing.T) { t.Run("When sending a Valid CCIP Message but the user does not have enough funds of the fee token, it fails", func(t *testing.T) { message := ccip_router.SVM2AnyMessage{ - FeeToken: token2022.mint, - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: token2022.mint, + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } noncePDA, err := state.FindNoncePDA(config.EvmChainSelector, tokenlessUser.PublicKey(), config.CcipRouterProgram) @@ -3206,9 +3100,10 @@ func TestCCIPRouter(t *testing.T) { zeroPubkey := solana.PublicKeyFromBytes(make([]byte, 32)) message := ccip_router.SVM2AnyMessage{ - FeeToken: zeroPubkey, // will pay with native SOL - Receiver: validReceiverAddress[:], - Data: []byte{4, 5, 6}, + FeeToken: zeroPubkey, // will pay with native SOL + Receiver: validReceiverAddress[:], + Data: []byte{4, 5, 6}, + ExtraArgs: emptyEVMExtraArgsV2, } // getFee @@ -5265,7 +5160,7 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, common.AddComputeUnitLimit(300_000)) event := ccip.EventCommitReportAccepted{} require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) @@ -5333,7 +5228,7 @@ func TestCCIPRouter(t *testing.T) { sourceChainSelector := config.EvmChainSelector msgAccounts := []solana.PublicKey{} message, _ := testutils.CreateNextMessage(ctx, solanaGoClient, t, msgAccounts) - message.ExtraArgs = ccip_router.SVMExtraArgs{} + message.ExtraArgs = ccip_router.Any2SVMRampExtraArgs{} message.Data = []byte{} message.TokenReceiver = config.ReceiverExternalExecutionConfigPDA message.TokenAmounts = []ccip_router.Any2SVMTokenTransfer{{ diff --git a/chains/solana/contracts/tests/testutils/wrapped.go b/chains/solana/contracts/tests/testutils/wrapped.go index 4b32f3b64..4d8d717cf 100644 --- a/chains/solana/contracts/tests/testutils/wrapped.go +++ b/chains/solana/contracts/tests/testutils/wrapped.go @@ -106,3 +106,14 @@ func MustMarshalBorsh(t *testing.T, v interface{}) []byte { require.NoError(t, err) return bz } + +func MustSerializeExtraArgs(t *testing.T, data interface{}, tag string) []byte { + b, err := ccip.SerializeExtraArgs(data, tag) + require.NoError(t, err) + return b +} + +func MustDeserializeExtraArgs[A any](t *testing.T, obj A, data []byte, tag string) A { + require.NoError(t, ccip.DeserializeExtraArgs(obj, data, tag)) + return obj +} diff --git a/chains/solana/contracts/tests/txsizing_test.go b/chains/solana/contracts/tests/txsizing_test.go index 3fab5f835..55da45306 100644 --- a/chains/solana/contracts/tests/txsizing_test.go +++ b/chains/solana/contracts/tests/txsizing_test.go @@ -84,7 +84,7 @@ func TestTransactionSizing(t *testing.T) { Data: []byte{}, TokenAmounts: []ccip_router.SVMTokenAmount{}, // no tokens FeeToken: [32]byte{}, // solana fee token - ExtraArgs: ccip_router.ExtraArgsInput{}, // default options + ExtraArgs: []byte{}, // default options } sendSingleMinimalToken := ccip_router.SVM2AnyMessage{ Receiver: make([]byte, 20), @@ -94,7 +94,7 @@ func TestTransactionSizing(t *testing.T) { Amount: 0, }}, // one token FeeToken: [32]byte{}, - ExtraArgs: ccip_router.ExtraArgsInput{}, // default options + ExtraArgs: []byte{}, // default options } ixCcipSend := func(msg ccip_router.SVM2AnyMessage, tokenIndexes []byte, addAccounts solana.PublicKeySlice) solana.Instruction { base := ccip_router.NewCcipSendInstruction( @@ -185,7 +185,7 @@ func TestTransactionSizing(t *testing.T) { Data: []byte{}, TokenReceiver: [32]byte{}, TokenAmounts: []ccip_router.Any2SVMTokenTransfer{}, - ExtraArgs: ccip_router.SVMExtraArgs{ + ExtraArgs: ccip_router.Any2SVMRampExtraArgs{ ComputeUnits: 0, IsWritableBitmap: 0, }, @@ -214,7 +214,7 @@ func TestTransactionSizing(t *testing.T) { ExtraData: []byte{}, Amount: ccip_router.CrossChainAmount{LeBytes: [32]uint8{}}, }}, - ExtraArgs: ccip_router.SVMExtraArgs{ + ExtraArgs: ccip_router.Any2SVMRampExtraArgs{ ComputeUnits: 0, IsWritableBitmap: 0, }, diff --git a/chains/solana/gobindings/ccip_router/Initialize.go b/chains/solana/gobindings/ccip_router/Initialize.go index e91f209df..a36ae1d4b 100644 --- a/chains/solana/gobindings/ccip_router/Initialize.go +++ b/chains/solana/gobindings/ccip_router/Initialize.go @@ -18,17 +18,13 @@ import ( // // * `ctx` - The context containing the accounts required for initialization. // * `svm_chain_selector` - The chain selector for SVM. -// * `default_gas_limit` - The default gas limit for other destination chains. -// * `default_allow_out_of_order_execution` - Whether out-of-order execution is allowed by default for other destination chains. // * `enable_execution_after` - The minimum amount of time required between a message has been committed and can be manually executed. type Initialize struct { - SvmChainSelector *uint64 - DefaultGasLimit *ag_binary.Uint128 - DefaultAllowOutOfOrderExecution *bool - EnableExecutionAfter *int64 - FeeAggregator *ag_solanago.PublicKey - LinkTokenMint *ag_solanago.PublicKey - MaxFeeJuelsPerMsg *ag_binary.Uint128 + SvmChainSelector *uint64 + EnableExecutionAfter *int64 + FeeAggregator *ag_solanago.PublicKey + LinkTokenMint *ag_solanago.PublicKey + MaxFeeJuelsPerMsg *ag_binary.Uint128 // [0] = [WRITE] config // @@ -62,18 +58,6 @@ func (inst *Initialize) SetSvmChainSelector(svmChainSelector uint64) *Initialize return inst } -// SetDefaultGasLimit sets the "defaultGasLimit" parameter. -func (inst *Initialize) SetDefaultGasLimit(defaultGasLimit ag_binary.Uint128) *Initialize { - inst.DefaultGasLimit = &defaultGasLimit - return inst -} - -// SetDefaultAllowOutOfOrderExecution sets the "defaultAllowOutOfOrderExecution" parameter. -func (inst *Initialize) SetDefaultAllowOutOfOrderExecution(defaultAllowOutOfOrderExecution bool) *Initialize { - inst.DefaultAllowOutOfOrderExecution = &defaultAllowOutOfOrderExecution - return inst -} - // SetEnableExecutionAfter sets the "enableExecutionAfter" parameter. func (inst *Initialize) SetEnableExecutionAfter(enableExecutionAfter int64) *Initialize { inst.EnableExecutionAfter = &enableExecutionAfter @@ -209,12 +193,6 @@ func (inst *Initialize) Validate() error { if inst.SvmChainSelector == nil { return errors.New("SvmChainSelector parameter is not set") } - if inst.DefaultGasLimit == nil { - return errors.New("DefaultGasLimit parameter is not set") - } - if inst.DefaultAllowOutOfOrderExecution == nil { - return errors.New("DefaultAllowOutOfOrderExecution parameter is not set") - } if inst.EnableExecutionAfter == nil { return errors.New("EnableExecutionAfter parameter is not set") } @@ -268,14 +246,12 @@ func (inst *Initialize) EncodeToTree(parent ag_treeout.Branches) { ParentFunc(func(instructionBranch ag_treeout.Branches) { // Parameters of the instruction: - instructionBranch.Child("Params[len=7]").ParentFunc(func(paramsBranch ag_treeout.Branches) { - paramsBranch.Child(ag_format.Param(" SvmChainSelector", *inst.SvmChainSelector)) - paramsBranch.Child(ag_format.Param(" DefaultGasLimit", *inst.DefaultGasLimit)) - paramsBranch.Child(ag_format.Param("DefaultAllowOutOfOrderExecution", *inst.DefaultAllowOutOfOrderExecution)) - paramsBranch.Child(ag_format.Param(" EnableExecutionAfter", *inst.EnableExecutionAfter)) - paramsBranch.Child(ag_format.Param(" FeeAggregator", *inst.FeeAggregator)) - paramsBranch.Child(ag_format.Param(" LinkTokenMint", *inst.LinkTokenMint)) - paramsBranch.Child(ag_format.Param(" MaxFeeJuelsPerMsg", *inst.MaxFeeJuelsPerMsg)) + instructionBranch.Child("Params[len=5]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param(" SvmChainSelector", *inst.SvmChainSelector)) + paramsBranch.Child(ag_format.Param("EnableExecutionAfter", *inst.EnableExecutionAfter)) + paramsBranch.Child(ag_format.Param(" FeeAggregator", *inst.FeeAggregator)) + paramsBranch.Child(ag_format.Param(" LinkTokenMint", *inst.LinkTokenMint)) + paramsBranch.Child(ag_format.Param(" MaxFeeJuelsPerMsg", *inst.MaxFeeJuelsPerMsg)) }) // Accounts of the instruction: @@ -299,16 +275,6 @@ func (obj Initialize) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) if err != nil { return err } - // Serialize `DefaultGasLimit` param: - err = encoder.Encode(obj.DefaultGasLimit) - if err != nil { - return err - } - // Serialize `DefaultAllowOutOfOrderExecution` param: - err = encoder.Encode(obj.DefaultAllowOutOfOrderExecution) - if err != nil { - return err - } // Serialize `EnableExecutionAfter` param: err = encoder.Encode(obj.EnableExecutionAfter) if err != nil { @@ -337,16 +303,6 @@ func (obj *Initialize) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err err if err != nil { return err } - // Deserialize `DefaultGasLimit`: - err = decoder.Decode(&obj.DefaultGasLimit) - if err != nil { - return err - } - // Deserialize `DefaultAllowOutOfOrderExecution`: - err = decoder.Decode(&obj.DefaultAllowOutOfOrderExecution) - if err != nil { - return err - } // Deserialize `EnableExecutionAfter`: err = decoder.Decode(&obj.EnableExecutionAfter) if err != nil { @@ -374,8 +330,6 @@ func (obj *Initialize) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err err func NewInitializeInstruction( // Parameters: svmChainSelector uint64, - defaultGasLimit ag_binary.Uint128, - defaultAllowOutOfOrderExecution bool, enableExecutionAfter int64, feeAggregator ag_solanago.PublicKey, linkTokenMint ag_solanago.PublicKey, @@ -391,8 +345,6 @@ func NewInitializeInstruction( tokenPoolsSigner ag_solanago.PublicKey) *Initialize { return NewInitializeInstructionBuilder(). SetSvmChainSelector(svmChainSelector). - SetDefaultGasLimit(defaultGasLimit). - SetDefaultAllowOutOfOrderExecution(defaultAllowOutOfOrderExecution). SetEnableExecutionAfter(enableExecutionAfter). SetFeeAggregator(feeAggregator). SetLinkTokenMint(linkTokenMint). diff --git a/chains/solana/gobindings/ccip_router/UpdateDefaultAllowOutOfOrderExecution.go b/chains/solana/gobindings/ccip_router/UpdateDefaultAllowOutOfOrderExecution.go deleted file mode 100644 index bfc2d3844..000000000 --- a/chains/solana/gobindings/ccip_router/UpdateDefaultAllowOutOfOrderExecution.go +++ /dev/null @@ -1,171 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package ccip_router - -import ( - "errors" - ag_binary "github.com/gagliardetto/binary" - ag_solanago "github.com/gagliardetto/solana-go" - ag_format "github.com/gagliardetto/solana-go/text/format" - ag_treeout "github.com/gagliardetto/treeout" -) - -// Updates the default setting for allowing out-of-order execution for other destination chains. -// The Admin is the only one able to update this config. -// -// # Arguments -// -// * `ctx` - The context containing the accounts required for updating the configuration. -// * `new_allow_out_of_order_execution` - The new setting for allowing out-of-order execution. -type UpdateDefaultAllowOutOfOrderExecution struct { - NewAllowOutOfOrderExecution *bool - - // [0] = [WRITE] config - // - // [1] = [SIGNER] authority - // - // [2] = [] systemProgram - ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` -} - -// NewUpdateDefaultAllowOutOfOrderExecutionInstructionBuilder creates a new `UpdateDefaultAllowOutOfOrderExecution` instruction builder. -func NewUpdateDefaultAllowOutOfOrderExecutionInstructionBuilder() *UpdateDefaultAllowOutOfOrderExecution { - nd := &UpdateDefaultAllowOutOfOrderExecution{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), - } - return nd -} - -// SetNewAllowOutOfOrderExecution sets the "newAllowOutOfOrderExecution" parameter. -func (inst *UpdateDefaultAllowOutOfOrderExecution) SetNewAllowOutOfOrderExecution(newAllowOutOfOrderExecution bool) *UpdateDefaultAllowOutOfOrderExecution { - inst.NewAllowOutOfOrderExecution = &newAllowOutOfOrderExecution - return inst -} - -// SetConfigAccount sets the "config" account. -func (inst *UpdateDefaultAllowOutOfOrderExecution) SetConfigAccount(config ag_solanago.PublicKey) *UpdateDefaultAllowOutOfOrderExecution { - inst.AccountMetaSlice[0] = ag_solanago.Meta(config).WRITE() - return inst -} - -// GetConfigAccount gets the "config" account. -func (inst *UpdateDefaultAllowOutOfOrderExecution) GetConfigAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[0] -} - -// SetAuthorityAccount sets the "authority" account. -func (inst *UpdateDefaultAllowOutOfOrderExecution) SetAuthorityAccount(authority ag_solanago.PublicKey) *UpdateDefaultAllowOutOfOrderExecution { - inst.AccountMetaSlice[1] = ag_solanago.Meta(authority).SIGNER() - return inst -} - -// GetAuthorityAccount gets the "authority" account. -func (inst *UpdateDefaultAllowOutOfOrderExecution) GetAuthorityAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] -} - -// SetSystemProgramAccount sets the "systemProgram" account. -func (inst *UpdateDefaultAllowOutOfOrderExecution) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *UpdateDefaultAllowOutOfOrderExecution { - inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) - return inst -} - -// GetSystemProgramAccount gets the "systemProgram" account. -func (inst *UpdateDefaultAllowOutOfOrderExecution) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] -} - -func (inst UpdateDefaultAllowOutOfOrderExecution) Build() *Instruction { - return &Instruction{BaseVariant: ag_binary.BaseVariant{ - Impl: inst, - TypeID: Instruction_UpdateDefaultAllowOutOfOrderExecution, - }} -} - -// ValidateAndBuild validates the instruction parameters and accounts; -// if there is a validation error, it returns the error. -// Otherwise, it builds and returns the instruction. -func (inst UpdateDefaultAllowOutOfOrderExecution) ValidateAndBuild() (*Instruction, error) { - if err := inst.Validate(); err != nil { - return nil, err - } - return inst.Build(), nil -} - -func (inst *UpdateDefaultAllowOutOfOrderExecution) Validate() error { - // Check whether all (required) parameters are set: - { - if inst.NewAllowOutOfOrderExecution == nil { - return errors.New("NewAllowOutOfOrderExecution parameter is not set") - } - } - - // Check whether all (required) accounts are set: - { - if inst.AccountMetaSlice[0] == nil { - return errors.New("accounts.Config is not set") - } - if inst.AccountMetaSlice[1] == nil { - return errors.New("accounts.Authority is not set") - } - if inst.AccountMetaSlice[2] == nil { - return errors.New("accounts.SystemProgram is not set") - } - } - return nil -} - -func (inst *UpdateDefaultAllowOutOfOrderExecution) EncodeToTree(parent ag_treeout.Branches) { - parent.Child(ag_format.Program(ProgramName, ProgramID)). - // - ParentFunc(func(programBranch ag_treeout.Branches) { - programBranch.Child(ag_format.Instruction("UpdateDefaultAllowOutOfOrderExecution")). - // - ParentFunc(func(instructionBranch ag_treeout.Branches) { - - // Parameters of the instruction: - instructionBranch.Child("Params[len=1]").ParentFunc(func(paramsBranch ag_treeout.Branches) { - paramsBranch.Child(ag_format.Param("NewAllowOutOfOrderExecution", *inst.NewAllowOutOfOrderExecution)) - }) - - // Accounts of the instruction: - instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta(" config", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) - }) - }) - }) -} - -func (obj UpdateDefaultAllowOutOfOrderExecution) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `NewAllowOutOfOrderExecution` param: - err = encoder.Encode(obj.NewAllowOutOfOrderExecution) - if err != nil { - return err - } - return nil -} -func (obj *UpdateDefaultAllowOutOfOrderExecution) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `NewAllowOutOfOrderExecution`: - err = decoder.Decode(&obj.NewAllowOutOfOrderExecution) - if err != nil { - return err - } - return nil -} - -// NewUpdateDefaultAllowOutOfOrderExecutionInstruction declares a new UpdateDefaultAllowOutOfOrderExecution instruction with the provided parameters and accounts. -func NewUpdateDefaultAllowOutOfOrderExecutionInstruction( - // Parameters: - newAllowOutOfOrderExecution bool, - // Accounts: - config ag_solanago.PublicKey, - authority ag_solanago.PublicKey, - systemProgram ag_solanago.PublicKey) *UpdateDefaultAllowOutOfOrderExecution { - return NewUpdateDefaultAllowOutOfOrderExecutionInstructionBuilder(). - SetNewAllowOutOfOrderExecution(newAllowOutOfOrderExecution). - SetConfigAccount(config). - SetAuthorityAccount(authority). - SetSystemProgramAccount(systemProgram) -} diff --git a/chains/solana/gobindings/ccip_router/UpdateDefaultAllowOutOfOrderExecution_test.go b/chains/solana/gobindings/ccip_router/UpdateDefaultAllowOutOfOrderExecution_test.go deleted file mode 100644 index 70a067474..000000000 --- a/chains/solana/gobindings/ccip_router/UpdateDefaultAllowOutOfOrderExecution_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package ccip_router - -import ( - "bytes" - ag_gofuzz "github.com/gagliardetto/gofuzz" - ag_require "github.com/stretchr/testify/require" - "strconv" - "testing" -) - -func TestEncodeDecode_UpdateDefaultAllowOutOfOrderExecution(t *testing.T) { - fu := ag_gofuzz.New().NilChance(0) - for i := 0; i < 1; i++ { - t.Run("UpdateDefaultAllowOutOfOrderExecution"+strconv.Itoa(i), func(t *testing.T) { - { - params := new(UpdateDefaultAllowOutOfOrderExecution) - fu.Fuzz(params) - params.AccountMetaSlice = nil - buf := new(bytes.Buffer) - err := encodeT(*params, buf) - ag_require.NoError(t, err) - got := new(UpdateDefaultAllowOutOfOrderExecution) - err = decodeT(got, buf.Bytes()) - got.AccountMetaSlice = nil - ag_require.NoError(t, err) - ag_require.Equal(t, params, got) - } - }) - } -} diff --git a/chains/solana/gobindings/ccip_router/UpdateDefaultGasLimit.go b/chains/solana/gobindings/ccip_router/UpdateDefaultGasLimit.go deleted file mode 100644 index 5975d6313..000000000 --- a/chains/solana/gobindings/ccip_router/UpdateDefaultGasLimit.go +++ /dev/null @@ -1,173 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package ccip_router - -import ( - "errors" - ag_binary "github.com/gagliardetto/binary" - ag_solanago "github.com/gagliardetto/solana-go" - ag_format "github.com/gagliardetto/solana-go/text/format" - ag_treeout "github.com/gagliardetto/treeout" -) - -// Updates the default gas limit in the router configuration. -// -// This change affects the default value for gas limit on every other destination chain. -// The Admin is the only one able to update the default gas limit. -// -// # Arguments -// -// * `ctx` - The context containing the accounts required for updating the configuration. -// * `new_gas_limit` - The new default gas limit. -type UpdateDefaultGasLimit struct { - NewGasLimit *ag_binary.Uint128 - - // [0] = [WRITE] config - // - // [1] = [SIGNER] authority - // - // [2] = [] systemProgram - ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` -} - -// NewUpdateDefaultGasLimitInstructionBuilder creates a new `UpdateDefaultGasLimit` instruction builder. -func NewUpdateDefaultGasLimitInstructionBuilder() *UpdateDefaultGasLimit { - nd := &UpdateDefaultGasLimit{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), - } - return nd -} - -// SetNewGasLimit sets the "newGasLimit" parameter. -func (inst *UpdateDefaultGasLimit) SetNewGasLimit(newGasLimit ag_binary.Uint128) *UpdateDefaultGasLimit { - inst.NewGasLimit = &newGasLimit - return inst -} - -// SetConfigAccount sets the "config" account. -func (inst *UpdateDefaultGasLimit) SetConfigAccount(config ag_solanago.PublicKey) *UpdateDefaultGasLimit { - inst.AccountMetaSlice[0] = ag_solanago.Meta(config).WRITE() - return inst -} - -// GetConfigAccount gets the "config" account. -func (inst *UpdateDefaultGasLimit) GetConfigAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[0] -} - -// SetAuthorityAccount sets the "authority" account. -func (inst *UpdateDefaultGasLimit) SetAuthorityAccount(authority ag_solanago.PublicKey) *UpdateDefaultGasLimit { - inst.AccountMetaSlice[1] = ag_solanago.Meta(authority).SIGNER() - return inst -} - -// GetAuthorityAccount gets the "authority" account. -func (inst *UpdateDefaultGasLimit) GetAuthorityAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] -} - -// SetSystemProgramAccount sets the "systemProgram" account. -func (inst *UpdateDefaultGasLimit) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *UpdateDefaultGasLimit { - inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) - return inst -} - -// GetSystemProgramAccount gets the "systemProgram" account. -func (inst *UpdateDefaultGasLimit) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] -} - -func (inst UpdateDefaultGasLimit) Build() *Instruction { - return &Instruction{BaseVariant: ag_binary.BaseVariant{ - Impl: inst, - TypeID: Instruction_UpdateDefaultGasLimit, - }} -} - -// ValidateAndBuild validates the instruction parameters and accounts; -// if there is a validation error, it returns the error. -// Otherwise, it builds and returns the instruction. -func (inst UpdateDefaultGasLimit) ValidateAndBuild() (*Instruction, error) { - if err := inst.Validate(); err != nil { - return nil, err - } - return inst.Build(), nil -} - -func (inst *UpdateDefaultGasLimit) Validate() error { - // Check whether all (required) parameters are set: - { - if inst.NewGasLimit == nil { - return errors.New("NewGasLimit parameter is not set") - } - } - - // Check whether all (required) accounts are set: - { - if inst.AccountMetaSlice[0] == nil { - return errors.New("accounts.Config is not set") - } - if inst.AccountMetaSlice[1] == nil { - return errors.New("accounts.Authority is not set") - } - if inst.AccountMetaSlice[2] == nil { - return errors.New("accounts.SystemProgram is not set") - } - } - return nil -} - -func (inst *UpdateDefaultGasLimit) EncodeToTree(parent ag_treeout.Branches) { - parent.Child(ag_format.Program(ProgramName, ProgramID)). - // - ParentFunc(func(programBranch ag_treeout.Branches) { - programBranch.Child(ag_format.Instruction("UpdateDefaultGasLimit")). - // - ParentFunc(func(instructionBranch ag_treeout.Branches) { - - // Parameters of the instruction: - instructionBranch.Child("Params[len=1]").ParentFunc(func(paramsBranch ag_treeout.Branches) { - paramsBranch.Child(ag_format.Param("NewGasLimit", *inst.NewGasLimit)) - }) - - // Accounts of the instruction: - instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta(" config", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) - }) - }) - }) -} - -func (obj UpdateDefaultGasLimit) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `NewGasLimit` param: - err = encoder.Encode(obj.NewGasLimit) - if err != nil { - return err - } - return nil -} -func (obj *UpdateDefaultGasLimit) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `NewGasLimit`: - err = decoder.Decode(&obj.NewGasLimit) - if err != nil { - return err - } - return nil -} - -// NewUpdateDefaultGasLimitInstruction declares a new UpdateDefaultGasLimit instruction with the provided parameters and accounts. -func NewUpdateDefaultGasLimitInstruction( - // Parameters: - newGasLimit ag_binary.Uint128, - // Accounts: - config ag_solanago.PublicKey, - authority ag_solanago.PublicKey, - systemProgram ag_solanago.PublicKey) *UpdateDefaultGasLimit { - return NewUpdateDefaultGasLimitInstructionBuilder(). - SetNewGasLimit(newGasLimit). - SetConfigAccount(config). - SetAuthorityAccount(authority). - SetSystemProgramAccount(systemProgram) -} diff --git a/chains/solana/gobindings/ccip_router/UpdateDefaultGasLimit_test.go b/chains/solana/gobindings/ccip_router/UpdateDefaultGasLimit_test.go deleted file mode 100644 index f323c69e6..000000000 --- a/chains/solana/gobindings/ccip_router/UpdateDefaultGasLimit_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package ccip_router - -import ( - "bytes" - ag_gofuzz "github.com/gagliardetto/gofuzz" - ag_require "github.com/stretchr/testify/require" - "strconv" - "testing" -) - -func TestEncodeDecode_UpdateDefaultGasLimit(t *testing.T) { - fu := ag_gofuzz.New().NilChance(0) - for i := 0; i < 1; i++ { - t.Run("UpdateDefaultGasLimit"+strconv.Itoa(i), func(t *testing.T) { - { - params := new(UpdateDefaultGasLimit) - fu.Fuzz(params) - params.AccountMetaSlice = nil - buf := new(bytes.Buffer) - err := encodeT(*params, buf) - ag_require.NoError(t, err) - got := new(UpdateDefaultGasLimit) - err = decodeT(got, buf.Bytes()) - got.AccountMetaSlice = nil - ag_require.NoError(t, err) - ag_require.Equal(t, params, got) - } - }) - } -} diff --git a/chains/solana/gobindings/ccip_router/accounts.go b/chains/solana/gobindings/ccip_router/accounts.go index d41ae49e4..c64c0f95b 100644 --- a/chains/solana/gobindings/ccip_router/accounts.go +++ b/chains/solana/gobindings/ccip_router/accounts.go @@ -9,20 +9,18 @@ import ( ) type Config struct { - Version uint8 - DefaultAllowOutOfOrderExecution uint8 - Padding0 [6]uint8 - SvmChainSelector uint64 - DefaultGasLimit ag_binary.Uint128 - Padding1 [8]uint8 - Owner ag_solanago.PublicKey - ProposedOwner ag_solanago.PublicKey - EnableManualExecutionAfter int64 - Padding2 [8]uint8 - Ocr3 [2]Ocr3Config - MaxFeeJuelsPerMsg ag_binary.Uint128 - LinkTokenMint ag_solanago.PublicKey - FeeAggregator ag_solanago.PublicKey + Version uint8 + Padding0 [7]uint8 + SvmChainSelector uint64 + EnableManualExecutionAfter int64 + Padding1 [8]uint8 + MaxFeeJuelsPerMsg ag_binary.Uint128 + Owner ag_solanago.PublicKey + ProposedOwner ag_solanago.PublicKey + Padding2 [8]uint8 + Ocr3 [2]Ocr3Config + LinkTokenMint ag_solanago.PublicKey + FeeAggregator ag_solanago.PublicKey } var ConfigDiscriminator = [8]byte{155, 12, 170, 224, 30, 250, 204, 130} @@ -38,11 +36,6 @@ func (obj Config) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { if err != nil { return err } - // Serialize `DefaultAllowOutOfOrderExecution` param: - err = encoder.Encode(obj.DefaultAllowOutOfOrderExecution) - if err != nil { - return err - } // Serialize `Padding0` param: err = encoder.Encode(obj.Padding0) if err != nil { @@ -53,8 +46,8 @@ func (obj Config) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { if err != nil { return err } - // Serialize `DefaultGasLimit` param: - err = encoder.Encode(obj.DefaultGasLimit) + // Serialize `EnableManualExecutionAfter` param: + err = encoder.Encode(obj.EnableManualExecutionAfter) if err != nil { return err } @@ -63,6 +56,11 @@ func (obj Config) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { if err != nil { return err } + // Serialize `MaxFeeJuelsPerMsg` param: + err = encoder.Encode(obj.MaxFeeJuelsPerMsg) + if err != nil { + return err + } // Serialize `Owner` param: err = encoder.Encode(obj.Owner) if err != nil { @@ -73,11 +71,6 @@ func (obj Config) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { if err != nil { return err } - // Serialize `EnableManualExecutionAfter` param: - err = encoder.Encode(obj.EnableManualExecutionAfter) - if err != nil { - return err - } // Serialize `Padding2` param: err = encoder.Encode(obj.Padding2) if err != nil { @@ -88,11 +81,6 @@ func (obj Config) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { if err != nil { return err } - // Serialize `MaxFeeJuelsPerMsg` param: - err = encoder.Encode(obj.MaxFeeJuelsPerMsg) - if err != nil { - return err - } // Serialize `LinkTokenMint` param: err = encoder.Encode(obj.LinkTokenMint) if err != nil { @@ -125,11 +113,6 @@ func (obj *Config) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) if err != nil { return err } - // Deserialize `DefaultAllowOutOfOrderExecution`: - err = decoder.Decode(&obj.DefaultAllowOutOfOrderExecution) - if err != nil { - return err - } // Deserialize `Padding0`: err = decoder.Decode(&obj.Padding0) if err != nil { @@ -140,8 +123,8 @@ func (obj *Config) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) if err != nil { return err } - // Deserialize `DefaultGasLimit`: - err = decoder.Decode(&obj.DefaultGasLimit) + // Deserialize `EnableManualExecutionAfter`: + err = decoder.Decode(&obj.EnableManualExecutionAfter) if err != nil { return err } @@ -150,6 +133,11 @@ func (obj *Config) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) if err != nil { return err } + // Deserialize `MaxFeeJuelsPerMsg`: + err = decoder.Decode(&obj.MaxFeeJuelsPerMsg) + if err != nil { + return err + } // Deserialize `Owner`: err = decoder.Decode(&obj.Owner) if err != nil { @@ -160,11 +148,6 @@ func (obj *Config) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) if err != nil { return err } - // Deserialize `EnableManualExecutionAfter`: - err = decoder.Decode(&obj.EnableManualExecutionAfter) - if err != nil { - return err - } // Deserialize `Padding2`: err = decoder.Decode(&obj.Padding2) if err != nil { @@ -175,11 +158,6 @@ func (obj *Config) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) if err != nil { return err } - // Deserialize `MaxFeeJuelsPerMsg`: - err = decoder.Decode(&obj.MaxFeeJuelsPerMsg) - if err != nil { - return err - } // Deserialize `LinkTokenMint`: err = decoder.Decode(&obj.LinkTokenMint) if err != nil { diff --git a/chains/solana/gobindings/ccip_router/instructions.go b/chains/solana/gobindings/ccip_router/instructions.go index 73c00dcae..4aaa8cbf6 100644 --- a/chains/solana/gobindings/ccip_router/instructions.go +++ b/chains/solana/gobindings/ccip_router/instructions.go @@ -44,8 +44,6 @@ var ( // // * `ctx` - The context containing the accounts required for initialization. // * `svm_chain_selector` - The chain selector for SVM. - // * `default_gas_limit` - The default gas limit for other destination chains. - // * `default_allow_out_of_order_execution` - Whether out-of-order execution is allowed by default for other destination chains. // * `enable_execution_after` - The minimum amount of time required between a message has been committed and can be manually executed. Instruction_Initialize = ag_binary.TypeID([8]byte{175, 175, 109, 31, 13, 152, 155, 237}) @@ -144,26 +142,6 @@ var ( // * `new_chain_selector` - The new chain selector for SVM. Instruction_UpdateSvmChainSelector = ag_binary.TypeID([8]byte{164, 212, 71, 101, 166, 113, 26, 93}) - // Updates the default gas limit in the router configuration. - // - // This change affects the default value for gas limit on every other destination chain. - // The Admin is the only one able to update the default gas limit. - // - // # Arguments - // - // * `ctx` - The context containing the accounts required for updating the configuration. - // * `new_gas_limit` - The new default gas limit. - Instruction_UpdateDefaultGasLimit = ag_binary.TypeID([8]byte{201, 34, 231, 229, 247, 252, 77, 210}) - - // Updates the default setting for allowing out-of-order execution for other destination chains. - // The Admin is the only one able to update this config. - // - // # Arguments - // - // * `ctx` - The context containing the accounts required for updating the configuration. - // * `new_allow_out_of_order_execution` - The new setting for allowing out-of-order execution. - Instruction_UpdateDefaultAllowOutOfOrderExecution = ag_binary.TypeID([8]byte{44, 54, 136, 71, 177, 17, 18, 241}) - // Updates the minimum amount of time required between a message being committed and when it can be manually executed. // // This is part of the OffRamp Configuration for SVM. @@ -411,10 +389,6 @@ func InstructionIDToName(id ag_binary.TypeID) string { return "UpdateDestChainConfig" case Instruction_UpdateSvmChainSelector: return "UpdateSvmChainSelector" - case Instruction_UpdateDefaultGasLimit: - return "UpdateDefaultGasLimit" - case Instruction_UpdateDefaultAllowOutOfOrderExecution: - return "UpdateDefaultAllowOutOfOrderExecution" case Instruction_UpdateEnableManualExecutionAfter: return "UpdateEnableManualExecutionAfter" case Instruction_RegisterTokenAdminRegistryViaGetCcipAdmin: @@ -499,12 +473,6 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( { "update_svm_chain_selector", (*UpdateSvmChainSelector)(nil), }, - { - "update_default_gas_limit", (*UpdateDefaultGasLimit)(nil), - }, - { - "update_default_allow_out_of_order_execution", (*UpdateDefaultAllowOutOfOrderExecution)(nil), - }, { "update_enable_manual_execution_after", (*UpdateEnableManualExecutionAfter)(nil), }, diff --git a/chains/solana/gobindings/ccip_router/types.go b/chains/solana/gobindings/ccip_router/types.go index 8276b1109..f8a2a6162 100644 --- a/chains/solana/gobindings/ccip_router/types.go +++ b/chains/solana/gobindings/ccip_router/types.go @@ -216,6 +216,105 @@ func (obj *MerkleRoot) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err err return nil } +type EVMExtraArgsV2 struct { + GasLimit ag_binary.Uint128 + AllowOutOfOrderExecution bool +} + +func (obj EVMExtraArgsV2) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `GasLimit` param: + err = encoder.Encode(obj.GasLimit) + if err != nil { + return err + } + // Serialize `AllowOutOfOrderExecution` param: + err = encoder.Encode(obj.AllowOutOfOrderExecution) + if err != nil { + return err + } + return nil +} + +func (obj *EVMExtraArgsV2) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `GasLimit`: + err = decoder.Decode(&obj.GasLimit) + if err != nil { + return err + } + // Deserialize `AllowOutOfOrderExecution`: + err = decoder.Decode(&obj.AllowOutOfOrderExecution) + if err != nil { + return err + } + return nil +} + +type SVMExtraArgsV1 struct { + ComputeUnits uint32 + AccountIsWritableBitmap uint64 + AllowOutOfOrderExecution bool + TokenReceiver [32]uint8 + Accounts [][32]uint8 +} + +func (obj SVMExtraArgsV1) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `ComputeUnits` param: + err = encoder.Encode(obj.ComputeUnits) + if err != nil { + return err + } + // Serialize `AccountIsWritableBitmap` param: + err = encoder.Encode(obj.AccountIsWritableBitmap) + if err != nil { + return err + } + // Serialize `AllowOutOfOrderExecution` param: + err = encoder.Encode(obj.AllowOutOfOrderExecution) + if err != nil { + return err + } + // Serialize `TokenReceiver` param: + err = encoder.Encode(obj.TokenReceiver) + if err != nil { + return err + } + // Serialize `Accounts` param: + err = encoder.Encode(obj.Accounts) + if err != nil { + return err + } + return nil +} + +func (obj *SVMExtraArgsV1) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `ComputeUnits`: + err = decoder.Decode(&obj.ComputeUnits) + if err != nil { + return err + } + // Deserialize `AccountIsWritableBitmap`: + err = decoder.Decode(&obj.AccountIsWritableBitmap) + if err != nil { + return err + } + // Deserialize `AllowOutOfOrderExecution`: + err = decoder.Decode(&obj.AllowOutOfOrderExecution) + if err != nil { + return err + } + // Deserialize `TokenReceiver`: + err = decoder.Decode(&obj.TokenReceiver) + if err != nil { + return err + } + // Deserialize `Accounts`: + err = decoder.Decode(&obj.Accounts) + if err != nil { + return err + } + return nil +} + type RampMessageHeader struct { MessageId [32]uint8 SourceChainSelector uint64 @@ -348,12 +447,12 @@ func (obj *ExecutionReportSingleChain) UnmarshalWithDecoder(decoder *ag_binary.D return nil } -type SVMExtraArgs struct { +type Any2SVMRampExtraArgs struct { ComputeUnits uint32 IsWritableBitmap uint64 } -func (obj SVMExtraArgs) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { +func (obj Any2SVMRampExtraArgs) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { // Serialize `ComputeUnits` param: err = encoder.Encode(obj.ComputeUnits) if err != nil { @@ -367,7 +466,7 @@ func (obj SVMExtraArgs) MarshalWithEncoder(encoder *ag_binary.Encoder) (err erro return nil } -func (obj *SVMExtraArgs) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { +func (obj *Any2SVMRampExtraArgs) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { // Deserialize `ComputeUnits`: err = decoder.Decode(&obj.ComputeUnits) if err != nil { @@ -381,46 +480,13 @@ func (obj *SVMExtraArgs) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err e return nil } -type AnyExtraArgs struct { - GasLimit ag_binary.Uint128 - AllowOutOfOrderExecution bool -} - -func (obj AnyExtraArgs) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `GasLimit` param: - err = encoder.Encode(obj.GasLimit) - if err != nil { - return err - } - // Serialize `AllowOutOfOrderExecution` param: - err = encoder.Encode(obj.AllowOutOfOrderExecution) - if err != nil { - return err - } - return nil -} - -func (obj *AnyExtraArgs) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `GasLimit`: - err = decoder.Decode(&obj.GasLimit) - if err != nil { - return err - } - // Deserialize `AllowOutOfOrderExecution`: - err = decoder.Decode(&obj.AllowOutOfOrderExecution) - if err != nil { - return err - } - return nil -} - type Any2SVMRampMessage struct { Header RampMessageHeader Sender []byte Data []byte TokenReceiver ag_solanago.PublicKey TokenAmounts []Any2SVMTokenTransfer - ExtraArgs SVMExtraArgs + ExtraArgs Any2SVMRampExtraArgs OnRampAddress []byte } @@ -507,7 +573,7 @@ type SVM2AnyRampMessage struct { Sender ag_solanago.PublicKey Data []byte Receiver []byte - ExtraArgs AnyExtraArgs + ExtraArgs []byte FeeToken ag_solanago.PublicKey TokenAmounts []SVM2AnyTokenTransfer FeeTokenAmount CrossChainAmount @@ -749,7 +815,7 @@ type SVM2AnyMessage struct { Data []byte TokenAmounts []SVMTokenAmount FeeToken ag_solanago.PublicKey - ExtraArgs ExtraArgsInput + ExtraArgs []byte } func (obj SVM2AnyMessage) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { @@ -843,81 +909,6 @@ func (obj *SVMTokenAmount) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err return nil } -type ExtraArgsInput struct { - GasLimit *ag_binary.Uint128 `bin:"optional"` - AllowOutOfOrderExecution *bool `bin:"optional"` -} - -func (obj ExtraArgsInput) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `GasLimit` param (optional): - { - if obj.GasLimit == nil { - err = encoder.WriteBool(false) - if err != nil { - return err - } - } else { - err = encoder.WriteBool(true) - if err != nil { - return err - } - err = encoder.Encode(obj.GasLimit) - if err != nil { - return err - } - } - } - // Serialize `AllowOutOfOrderExecution` param (optional): - { - if obj.AllowOutOfOrderExecution == nil { - err = encoder.WriteBool(false) - if err != nil { - return err - } - } else { - err = encoder.WriteBool(true) - if err != nil { - return err - } - err = encoder.Encode(obj.AllowOutOfOrderExecution) - if err != nil { - return err - } - } - } - return nil -} - -func (obj *ExtraArgsInput) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `GasLimit` (optional): - { - ok, err := decoder.ReadBool() - if err != nil { - return err - } - if ok { - err = decoder.Decode(&obj.GasLimit) - if err != nil { - return err - } - } - } - // Deserialize `AllowOutOfOrderExecution` (optional): - { - ok, err := decoder.ReadBool() - if err != nil { - return err - } - if ok { - err = decoder.Decode(&obj.AllowOutOfOrderExecution) - if err != nil { - return err - } - } - } - return nil -} - type CrossChainAmount struct { LeBytes [32]uint8 } @@ -1676,6 +1667,10 @@ const ( MessageGasLimitTooHigh_CcipRouterError ExtraArgOutOfOrderExecutionMustBeTrue_CcipRouterError InvalidWritabilityBitmap_CcipRouterError + InvalidExtraArgsTag_CcipRouterError + InvalidChainFamilySelector_CcipRouterError + InvalidTokenReceiver_CcipRouterError + InvalidSVMAddress_CcipRouterError ) func (value CcipRouterError) String() string { @@ -1764,6 +1759,14 @@ func (value CcipRouterError) String() string { return "ExtraArgOutOfOrderExecutionMustBeTrue" case InvalidWritabilityBitmap_CcipRouterError: return "InvalidWritabilityBitmap" + case InvalidExtraArgsTag_CcipRouterError: + return "InvalidExtraArgsTag" + case InvalidChainFamilySelector_CcipRouterError: + return "InvalidChainFamilySelector" + case InvalidTokenReceiver_CcipRouterError: + return "InvalidTokenReceiver" + case InvalidSVMAddress_CcipRouterError: + return "InvalidSVMAddress" default: return "" } diff --git a/chains/solana/utils/ccip/ccip_messages.go b/chains/solana/utils/ccip/ccip_messages.go index c4e446c45..967280f1f 100644 --- a/chains/solana/utils/ccip/ccip_messages.go +++ b/chains/solana/utils/ccip/ccip_messages.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "encoding/hex" + "fmt" bin "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go" @@ -16,6 +17,9 @@ import ( "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" ) +const EVMExtraArgsV2Tag = "181dcf10" +const SVMExtraArgsV1Tag = "1f3b3aba" + var leafDomainSeparator = [32]byte{} func HashCommitReport(ctx [2][32]byte, report ccip_router.CommitInput) ([]byte, error) { @@ -95,7 +99,7 @@ func CreateDefaultMessageWith(sourceChainSelector uint64, sequenceNumber uint64) }, Sender: []byte{1, 2, 3}, Data: []byte{4, 5, 6}, - ExtraArgs: ccip_router.SVMExtraArgs{ + ExtraArgs: ccip_router.Any2SVMRampExtraArgs{ ComputeUnits: 1000, IsWritableBitmap: GenerateBitMapForIndexes([]int{0, 1}), }, @@ -280,7 +284,6 @@ func HashSVMToAnyMessage(msg ccip_router.SVM2AnyRampMessage) ([]byte, error) { } // GenerateBitMapForIndexes generates a bitmap for the given indexes. - func GenerateBitMapForIndexes(indexes []int) uint64 { var bitmap uint64 @@ -290,3 +293,26 @@ func GenerateBitMapForIndexes(indexes []int) uint64 { return bitmap } + +func SerializeExtraArgs(data interface{}, tag string) ([]byte, error) { + tagBytes, err := hex.DecodeString(tag) + if err != nil { + return nil, err + } + v, err := bin.MarshalBorsh(data) + return append(tagBytes, v...), err +} + +func DeserializeExtraArgs(obj interface{}, data []byte, tag string) error { + tagBytes, err := hex.DecodeString(tag) + if err != nil { + return err + } + + if !bytes.Equal(data[:4], tagBytes) { + return fmt.Errorf("Mismatched tag: %s != %s", hex.EncodeToString(data[:4]), tag) + } + + err = bin.UnmarshalBorsh(obj, data[4:]) + return err +} diff --git a/chains/solana/utils/ccip/ccip_messages_test.go b/chains/solana/utils/ccip/ccip_messages_test.go index 23c93aa9e..b18145c09 100644 --- a/chains/solana/utils/ccip/ccip_messages_test.go +++ b/chains/solana/utils/ccip/ccip_messages_test.go @@ -36,7 +36,7 @@ func TestMessageHashing(t *testing.T) { SequenceNumber: 89, Nonce: 90, }, - ExtraArgs: ccip_router.SVMExtraArgs{ + ExtraArgs: ccip_router.Any2SVMRampExtraArgs{ ComputeUnits: 1000, IsWritableBitmap: GenerateBitMapForIndexes([]int{0}), }, @@ -62,6 +62,12 @@ func TestMessageHashing(t *testing.T) { t.Run("SVMToAny", func(t *testing.T) { t.Parallel() + extraArgs, err := SerializeExtraArgs(ccip_router.EVMExtraArgsV2{ + GasLimit: bin.Uint128{Lo: 1}, + AllowOutOfOrderExecution: true, + }, EVMExtraArgsV2Tag) + require.NoError(t, err) + h, err := HashSVMToAnyMessage(ccip_router.SVM2AnyRampMessage{ Header: ccip_router.RampMessageHeader{ MessageId: [32]uint8{}, @@ -70,13 +76,10 @@ func TestMessageHashing(t *testing.T) { SequenceNumber: 30, Nonce: 40, }, - Sender: solana.MustPublicKeyFromBase58("DS2tt4BX7YwCw7yrDNwbAdnYrxjeCPeGJbHmZEYC8RTa"), - Data: []byte{4, 5, 6}, - Receiver: sender, - ExtraArgs: ccip_router.AnyExtraArgs{ - GasLimit: bin.Uint128{Lo: 1}, - AllowOutOfOrderExecution: true, - }, + Sender: solana.MustPublicKeyFromBase58("DS2tt4BX7YwCw7yrDNwbAdnYrxjeCPeGJbHmZEYC8RTa"), + Data: []byte{4, 5, 6}, + Receiver: sender, + ExtraArgs: extraArgs, FeeToken: solana.MustPublicKeyFromBase58("DS2tt4BX7YwCw7yrDNwbAdnYrxjeCPeGJbHmZEYC8RTb"), FeeTokenAmount: ccip_router.CrossChainAmount{LeBytes: tokens.ToLittleEndianU256(50)}, FeeValueJuels: ccip_router.CrossChainAmount{LeBytes: tokens.ToLittleEndianU256(500)}, @@ -91,6 +94,6 @@ func TestMessageHashing(t *testing.T) { }, }) require.NoError(t, err) - require.Equal(t, "009bc51872fe41ea096bd881bf52e3daf07c80e112ffeeba6aa503d8281b6bfd", hex.EncodeToString(h)) + require.Equal(t, "2335e7898faa4e7e8816a6b1e0cf47ea2a18bb66bca205d0cb3ae4a8ce5c72f7", hex.EncodeToString(h)) }) }