From bf879609bef79efe3f1789cfd52cbcaeb129f84b Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:47:11 -0700 Subject: [PATCH] refactor: use PDAs to limit source sender + chain together --- .../programs/example-ccip-receiver/src/lib.rs | 385 +++++++----------- .../programs/test-ccip-receiver/src/lib.rs | 1 - .../target/idl/example_ccip_receiver.json | 167 ++++---- .../contracts/tests/examples/receiver_test.go | 82 +++- .../example_ccip_receiver/AddChainTo.go | 188 --------- .../example_ccip_receiver/ApproveSender.go | 207 ++++++++++ ...dChainTo_test.go => ApproveSender_test.go} | 8 +- .../example_ccip_receiver/CcipReceive.go | 33 +- .../example_ccip_receiver/EnableList.go | 169 -------- .../example_ccip_receiver/Initialize.go | 41 +- .../example_ccip_receiver/RemoveChainFrom.go | 169 -------- .../example_ccip_receiver/UnapproveSender.go | 207 ++++++++++ ...inFrom_test.go => UnapproveSender_test.go} | 8 +- .../example_ccip_receiver/WithdrawTokens.go | 264 ++++++++++++ ...bleList_test.go => WithdrawTokens_test.go} | 8 +- .../example_ccip_receiver/accounts.go | 44 +- .../example_ccip_receiver/instructions.go | 34 +- .../gobindings/example_ccip_receiver/types.go | 51 --- chains/solana/utils/state/pda.go | 7 + 19 files changed, 1109 insertions(+), 964 deletions(-) delete mode 100644 chains/solana/gobindings/example_ccip_receiver/AddChainTo.go create mode 100644 chains/solana/gobindings/example_ccip_receiver/ApproveSender.go rename chains/solana/gobindings/example_ccip_receiver/{AddChainTo_test.go => ApproveSender_test.go} (77%) delete mode 100644 chains/solana/gobindings/example_ccip_receiver/EnableList.go delete mode 100644 chains/solana/gobindings/example_ccip_receiver/RemoveChainFrom.go create mode 100644 chains/solana/gobindings/example_ccip_receiver/UnapproveSender.go rename chains/solana/gobindings/example_ccip_receiver/{RemoveChainFrom_test.go => UnapproveSender_test.go} (76%) create mode 100644 chains/solana/gobindings/example_ccip_receiver/WithdrawTokens.go rename chains/solana/gobindings/example_ccip_receiver/{EnableList_test.go => WithdrawTokens_test.go} (77%) diff --git a/chains/solana/contracts/programs/example-ccip-receiver/src/lib.rs b/chains/solana/contracts/programs/example-ccip-receiver/src/lib.rs index 952d20bba..19d751a73 100644 --- a/chains/solana/contracts/programs/example-ccip-receiver/src/lib.rs +++ b/chains/solana/contracts/programs/example-ccip-receiver/src/lib.rs @@ -1,12 +1,19 @@ use anchor_lang::prelude::*; +use anchor_spl::token_interface::{Mint, TokenAccount}; + declare_id!("CcipReceiver1111111111111111111111111111111"); pub const EXTERNAL_EXECUTION_CONFIG_SEED: &[u8] = b"external_execution_config"; +pub const APPROVED_SENDER_SEED: &[u8] = b"approved_ccip_sender"; +pub const TOKEN_ADMIN_SEED: &[u8] = b"receiver_token_admin"; /// This program an example of a CCIP Receiver Program. /// Used to test CCIP Router execute. #[program] pub mod example_ccip_receiver { + use anchor_spl::token_2022::spl_token_2022::{self, instruction::transfer_checked}; + use solana_program::program::invoke_signed; + use super::*; /// The initialization is responsibility of the External User, CCIP is not handling initialization of Accounts @@ -33,40 +40,27 @@ pub mod example_ccip_receiver { Ok(()) } - pub fn enable_list( - ctx: Context, - list_type: ListType, - enable: bool, - ) -> Result<()> { + pub fn update_router(ctx: Context, new_router: Pubkey) -> Result<()> { ctx.accounts .state - .enable_list(ctx.accounts.authority.key(), list_type, enable) + .update_router(ctx.accounts.authority.key(), new_router) } - pub fn add_chain_to( - ctx: Context, - list_type: ListType, - chain_selector: u64, + // approve_sender creates a PDA to approve the specific source chain + remote address + pub fn approve_sender( + _ctx: Context, + _chain_selector: u64, + _remote_address: Vec, ) -> Result<()> { - ctx.accounts - .state - .add_chain_to(ctx.accounts.authority.key(), chain_selector, list_type) + Ok(()) } - pub fn remove_chain_from( - ctx: Context, - list_type: ListType, - chain_selector: u64, + pub fn unapprove_sender( + _ctx: Context, + _chain_selector: u64, + _remote_address: Vec, ) -> Result<()> { - ctx.accounts - .state - .add_chain_to(ctx.accounts.authority.key(), chain_selector, list_type) - } - - pub fn update_router(ctx: Context, new_router: Pubkey) -> Result<()> { - ctx.accounts - .state - .update_router(ctx.accounts.authority.key(), new_router) + Ok(()) } pub fn transfer_ownership(ctx: Context, proposed_owner: Pubkey) -> Result<()> { @@ -80,6 +74,33 @@ pub mod example_ccip_receiver { .state .accept_ownership(ctx.accounts.authority.key()) } + + pub fn withdraw_tokens(ctx: Context, amount: u64, decimals: u8) -> Result<()> { + let mut ix = transfer_checked( + &spl_token_2022::ID, // use spl-token-2022 to compile instruction - change program later + &ctx.accounts.program_token_account.key(), + &ctx.accounts.mint.key(), + &ctx.accounts.to_token_account.key(), + &ctx.accounts.token_admin.key(), + &[], + amount, + decimals, + )?; + ix.program_id = ctx.accounts.token_program.key(); // set to user specified program + + let seeds = &[TOKEN_ADMIN_SEED, &[ctx.bumps.token_admin]]; + invoke_signed( + &ix, + &[ + ctx.accounts.program_token_account.to_account_info(), + ctx.accounts.mint.to_account_info(), + ctx.accounts.to_token_account.to_account_info(), + ctx.accounts.token_admin.to_account_info(), + ], + &[&seeds[..]], + )?; + Ok(()) + } } const ANCHOR_DISCRIMINATOR: usize = 8; @@ -94,6 +115,15 @@ pub struct Initialize<'info> { space = ANCHOR_DISCRIMINATOR + BaseState::INIT_SPACE, )] pub state: Account<'info, BaseState>, + #[account( + init, + seeds = [TOKEN_ADMIN_SEED], + bump, + payer = authority, + space = ANCHOR_DISCRIMINATOR, + )] + /// CHECK: CPI signer for tokens + pub token_admin: UncheckedAccount<'info>, #[account(mut)] pub authority: Signer<'info>, pub system_program: Program<'info, System>, @@ -104,10 +134,19 @@ pub struct Initialize<'info> { pub struct CcipReceive<'info> { // router CPI signer must be first #[account( - constraint = state.is_valid_chain(message.source_chain_selector) @ CcipReceiverError::InvalidChain, constraint = state.is_router(authority.key()) @ CcipReceiverError::OnlyRouter, )] pub authority: Signer<'info>, + #[account( + seeds = [ + APPROVED_SENDER_SEED, + message.source_chain_selector.to_le_bytes().as_ref(), + &[message.sender.len() as u8], + &message.sender, + ], + bump, + )] + pub approved_sender: Account<'info, ApprovedSender>, // if PDA does not exist, the message sender and/or source chain are not approved pub state: Account<'info, BaseState>, } @@ -126,16 +165,70 @@ pub struct UpdateConfig<'info> { } #[derive(Accounts, Debug)] -pub struct AddChainTo<'info> { +pub struct AcceptOwnership<'info> { + #[account( + mut, + seeds = [b"state"], + bump, + )] + pub state: Account<'info, BaseState>, + #[account( + address = state.proposed_owner @ CcipReceiverError::OnlyProposedOwner, + )] + pub authority: Signer<'info>, +} + +#[derive(Accounts, Debug)] +#[instruction(chain_selector: u64, remote_sender: Vec)] +pub struct ApproveSender<'info> { + #[account( + mut, + seeds = [b"state"], + bump, + )] + pub state: Account<'info, BaseState>, + #[account( + init, + seeds = [ + APPROVED_SENDER_SEED, + chain_selector.to_le_bytes().as_ref(), + &[remote_sender.len() as u8], + &remote_sender, + ], + bump, + payer = authority, + space = ANCHOR_DISCRIMINATOR + ApprovedSender::INIT_SPACE, + )] + pub approved_sender: Account<'info, ApprovedSender>, + #[account( + mut, + address = state.owner @ CcipReceiverError::OnlyOwner, + )] + pub authority: Signer<'info>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts, Debug)] +#[instruction(chain_selector: u64, remote_sender: Vec)] +pub struct UnapproveSender<'info> { #[account( mut, seeds = [b"state"], bump, - realloc = state.space_with_new_chain(), // allows sizing up and down the state (if chains were removed, this allows some recovery of account size) - realloc::payer = authority, - realloc::zero = false, )] pub state: Account<'info, BaseState>, + #[account( + mut, + seeds = [ + APPROVED_SENDER_SEED, + chain_selector.to_le_bytes().as_ref(), + &[remote_sender.len() as u8], + &remote_sender, + ], + bump, + close = authority, + )] + pub approved_sender: Account<'info, ApprovedSender>, #[account( mut, address = state.owner @ CcipReceiverError::OnlyOwner, @@ -145,7 +238,7 @@ pub struct AddChainTo<'info> { } #[derive(Accounts, Debug)] -pub struct AcceptOwnership<'info> { +pub struct WithdrawTokens<'info> { #[account( mut, seeds = [b"state"], @@ -153,7 +246,30 @@ pub struct AcceptOwnership<'info> { )] pub state: Account<'info, BaseState>, #[account( - address = state.proposed_owner @ CcipReceiverError::OnlyProposedOwner, + mut, + token::mint = mint, + token::authority = token_admin, + token::token_program = token_program, + )] + pub program_token_account: InterfaceAccount<'info, TokenAccount>, + #[account( + mut, + token::mint = mint, + token::token_program = token_program, + )] + pub to_token_account: InterfaceAccount<'info, TokenAccount>, + pub mint: InterfaceAccount<'info, Mint>, + #[account(address = *mint.to_account_info().owner)] + /// CHECK: CPI to token program + pub token_program: AccountInfo<'info>, + #[account( + seeds = [TOKEN_ADMIN_SEED], + bump, + )] + /// CHECK: CPI signer for tokens + pub token_admin: UncheckedAccount<'info>, + #[account( + address = state.owner @ CcipReceiverError::OnlyOwner, )] pub authority: Signer<'info>, } @@ -168,16 +284,8 @@ pub struct AcceptOwnership<'info> { pub struct BaseState { pub owner: Pubkey, pub proposed_owner: Pubkey, - pub router: Pubkey, - pub allow: ChainList, - pub deny: ChainList, -} -#[derive(InitSpace, Debug, Clone, AnchorSerialize, AnchorDeserialize, Default)] -pub struct ChainList { - pub is_enabled: bool, - #[max_len(0)] - pub chains: Vec, + pub router: Pubkey, } impl BaseState { @@ -214,84 +322,12 @@ impl BaseState { self.router = router; Ok(()) } - - // is_valid_chain checks that chain is allowed based on the list - // both enabled: chain must be on allow list and not on deny list - // allow enabled: chain must be on allow list - // deny enabled: chain must not be on deny list - // both disabled: all chains allowed - pub fn is_valid_chain(&self, chain_selector: u64) -> bool { - if self.allow.is_enabled && self.deny.is_enabled { - // must be within allow list and not in deny list - self.allow.chains.binary_search(&chain_selector).is_ok() - && self.deny.chains.binary_search(&chain_selector).is_err() - } else if self.allow.is_enabled && !self.deny.is_enabled { - // check allow list only - self.allow.chains.binary_search(&chain_selector).is_ok() - } else if !self.allow.is_enabled && self.deny.is_enabled { - // check deny list only, if present = not valid - self.deny.chains.binary_search(&chain_selector).is_err() - } else { - // neither list is enabled, allow everything - true - } - } - - pub fn enable_list(&mut self, owner: Pubkey, list_type: ListType, enable: bool) -> Result<()> { - require_eq!(self.owner, owner, CcipReceiverError::OnlyOwner); - let list = self.get_list(list_type); - list.is_enabled = enable; - Ok(()) - } - - pub fn add_chain_to( - &mut self, - owner: Pubkey, - chain_selector: u64, - list_type: ListType, - ) -> Result<()> { - require_eq!(self.owner, owner, CcipReceiverError::OnlyOwner); - - let list = self.get_list(list_type); - match list.chains.binary_search(&chain_selector) { - // already present - Ok(_) => (), - Err(i) => list.chains.insert(i, chain_selector), - } - - Ok(()) - } - - pub fn remove_chain_from( - &mut self, - owner: Pubkey, - chain_selector: u64, - list_type: ListType, - ) -> Result<()> { - require_eq!(self.owner, owner, CcipReceiverError::OnlyOwner); - let list = self.get_list(list_type); - let index = list.chains.binary_search(&chain_selector); - if let Ok(index) = index { - list.chains.remove(index); - } - Ok(()) - } - - // calculates the necessary space for adding the new chain to the allow/deny list - pub fn space_with_new_chain(&self) -> usize { - 8 // discriminator - + Self::INIT_SPACE // base space for ChainList - + (self.allow.chains.len() + self.deny.chains.len() + 1) * 8 // (existing chains + 1) * u64::byte_size - } - - fn get_list(&mut self, list_type: ListType) -> &mut ChainList { - match list_type { - ListType::Allow => &mut self.allow, - ListType::Deny => &mut self.deny, - } - } } +#[account] +#[derive(InitSpace, Default, Debug)] +pub struct ApprovedSender {} + #[derive(Debug, Clone, AnchorSerialize, AnchorDeserialize)] pub struct Any2SVMMessage { pub message_id: [u8; 32], @@ -307,21 +343,14 @@ pub struct SVMTokenAmount { pub amount: u64, // solana local token amount } -#[repr(u8)] -#[derive(AnchorSerialize, AnchorDeserialize)] -pub enum ListType { - Allow, - Deny, -} - #[error_code] pub enum CcipReceiverError { #[msg("Address is not router external execution PDA")] OnlyRouter, #[msg("Invalid router address")] InvalidRouter, - #[msg("Invalid chain")] - InvalidChain, + #[msg("Invalid combination of chain and sender")] + InvalidChainAndSender, #[msg("Address is not owner")] OnlyOwner, #[msg("Address is not proposed_owner")] @@ -386,106 +415,4 @@ mod tests { .update_router(state.owner, Pubkey::new_unique()) .unwrap(); } - - #[test] - fn chains() { - let mut state = create_state(); - - assert_eq!( - state - .enable_list(Pubkey::new_unique(), ListType::Allow, true) - .unwrap_err(), - CcipReceiverError::OnlyOwner.into(), - ); - assert_eq!( - state - .add_chain_to(Pubkey::new_unique(), 1, ListType::Deny) - .unwrap_err(), - CcipReceiverError::OnlyOwner.into(), - ); - assert_eq!( - state - .remove_chain_from(Pubkey::new_unique(), 1, ListType::Deny) - .unwrap_err(), - CcipReceiverError::OnlyOwner.into(), - ); - - assert!(state.allow.chains.is_empty()); - assert!(state.deny.chains.is_empty()); - - // add 2 chain selector to allow - state - .add_chain_to(state.owner, 10, ListType::Allow) - .unwrap(); - assert_eq!(state.allow.chains.len(), 1); - assert_eq!(state.allow.chains[0], 10); - state - .add_chain_to(state.owner, 40, ListType::Allow) - .unwrap(); - assert_eq!(state.allow.chains.len(), 2); - assert_eq!(state.allow.chains[0], 10); - assert_eq!(state.allow.chains[1], 40); - - // add 3 chain selectors to deny - state.add_chain_to(state.owner, 20, ListType::Deny).unwrap(); - assert_eq!(state.deny.chains.len(), 1); - assert_eq!(state.deny.chains[0], 20); - state.add_chain_to(state.owner, 40, ListType::Deny).unwrap(); - assert_eq!(state.deny.chains.len(), 2); - assert_eq!(state.deny.chains[0], 20); - assert_eq!(state.deny.chains[1], 40); - state.add_chain_to(state.owner, 21, ListType::Deny).unwrap(); - assert_eq!(state.deny.chains.len(), 3); - assert_eq!(state.deny.chains[0], 20); - assert_eq!(state.deny.chains[1], 21); - assert_eq!(state.deny.chains[2], 40); - - // remove chain selector from deny - state - .remove_chain_from(state.owner, 21, ListType::Deny) - .unwrap(); - assert_eq!(state.deny.chains.len(), 2); - assert_eq!(state.deny.chains[0], 20); - assert_eq!(state.deny.chains[1], 40); - // remove same chain selector from deny - state - .remove_chain_from(state.owner, 21, ListType::Deny) - .unwrap(); - assert_eq!(state.deny.chains.len(), 2); - assert_eq!(state.deny.chains[0], 20); - assert_eq!(state.deny.chains[1], 40); - - // no lists enabled - assert!(state.is_valid_chain(10)); // in allow list - assert!(state.is_valid_chain(20)); // in deny list - assert!(state.is_valid_chain(30)); // in neither list - assert!(state.is_valid_chain(40)); // in both lists - - // only allow list enabled - state - .enable_list(state.owner, ListType::Allow, true) - .unwrap(); - assert!(state.is_valid_chain(10)); // in allow list - assert!(!state.is_valid_chain(20)); // in deny list - assert!(!state.is_valid_chain(30)); // in neither list - assert!(state.is_valid_chain(40)); // in both lists - - // both lists enabled - state - .enable_list(state.owner, ListType::Deny, true) - .unwrap(); - assert!(state.is_valid_chain(10)); // in allow list - assert!(!state.is_valid_chain(20)); // in deny list - assert!(!state.is_valid_chain(30)); // in neither list - assert!(!state.is_valid_chain(40)); // in both lists - - // only deny list enabled - state - .enable_list(state.owner, ListType::Allow, false) - .unwrap(); - assert!(state.is_valid_chain(10)); // in allow list - assert!(!state.is_valid_chain(20)); // in deny list - assert!(state.is_valid_chain(30)); // in neither list - assert!(!state.is_valid_chain(40)); // in both lists - } } diff --git a/chains/solana/contracts/programs/test-ccip-receiver/src/lib.rs b/chains/solana/contracts/programs/test-ccip-receiver/src/lib.rs index 301c3225a..c0f7e17f6 100644 --- a/chains/solana/contracts/programs/test-ccip-receiver/src/lib.rs +++ b/chains/solana/contracts/programs/test-ccip-receiver/src/lib.rs @@ -122,7 +122,6 @@ pub struct SetData<'info> { // router CPI signer must be first #[account( constraint = counter.state.is_router(authority.key()) @ CcipReceiverError::OnlyRouter, - constraint = counter.state.is_valid_chain(message.source_chain_selector) @CcipReceiverError::InvalidChain, )] pub authority: Signer<'info>, // ccip router expects "receiver" to be second diff --git a/chains/solana/contracts/target/idl/example_ccip_receiver.json b/chains/solana/contracts/target/idl/example_ccip_receiver.json index 7883f643d..1ad9299e6 100644 --- a/chains/solana/contracts/target/idl/example_ccip_receiver.json +++ b/chains/solana/contracts/target/idl/example_ccip_receiver.json @@ -17,6 +17,11 @@ "isMut": true, "isSigner": false }, + { + "name": "tokenAdmin", + "isMut": true, + "isSigner": false + }, { "name": "authority", "isMut": true, @@ -50,6 +55,11 @@ "isMut": false, "isSigner": true }, + { + "name": "approvedSender", + "isMut": false, + "isSigner": false + }, { "name": "state", "isMut": false, @@ -66,7 +76,7 @@ ] }, { - "name": "enableList", + "name": "updateRouter", "accounts": [ { "name": "state", @@ -81,25 +91,24 @@ ], "args": [ { - "name": "listType", - "type": { - "defined": "ListType" - } - }, - { - "name": "enable", - "type": "bool" + "name": "newRouter", + "type": "publicKey" } ] }, { - "name": "addChainTo", + "name": "approveSender", "accounts": [ { "name": "state", "isMut": true, "isSigner": false }, + { + "name": "approvedSender", + "isMut": true, + "isSigner": false + }, { "name": "authority", "isMut": true, @@ -112,47 +121,53 @@ } ], "args": [ - { - "name": "listType", - "type": { - "defined": "ListType" - } - }, { "name": "chainSelector", "type": "u64" + }, + { + "name": "remoteAddress", + "type": "bytes" } ] }, { - "name": "removeChainFrom", + "name": "unapproveSender", "accounts": [ { "name": "state", "isMut": true, "isSigner": false }, + { + "name": "approvedSender", + "isMut": true, + "isSigner": false + }, { "name": "authority", - "isMut": false, + "isMut": true, "isSigner": true + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false } ], "args": [ - { - "name": "listType", - "type": { - "defined": "ListType" - } - }, { "name": "chainSelector", "type": "u64" + }, + { + "name": "remoteAddress", + "type": "bytes" } ] }, { - "name": "updateRouter", + "name": "transferOwnership", "accounts": [ { "name": "state", @@ -167,13 +182,13 @@ ], "args": [ { - "name": "newRouter", + "name": "proposedOwner", "type": "publicKey" } ] }, { - "name": "transferOwnership", + "name": "acceptOwnership", "accounts": [ { "name": "state", @@ -186,28 +201,57 @@ "isSigner": true } ], - "args": [ - { - "name": "proposedOwner", - "type": "publicKey" - } - ] + "args": [] }, { - "name": "acceptOwnership", + "name": "withdrawTokens", "accounts": [ { "name": "state", "isMut": true, "isSigner": false }, + { + "name": "programTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "toTokenAccount", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenAdmin", + "isMut": false, + "isSigner": false + }, { "name": "authority", "isMut": false, "isSigner": true } ], - "args": [] + "args": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "decimals", + "type": "u8" + } + ] } ], "accounts": [ @@ -227,42 +271,19 @@ { "name": "router", "type": "publicKey" - }, - { - "name": "allow", - "type": { - "defined": "ChainList" - } - }, - { - "name": "deny", - "type": { - "defined": "ChainList" - } } ] } - } - ], - "types": [ + }, { - "name": "ChainList", + "name": "ApprovedSender", "type": { "kind": "struct", - "fields": [ - { - "name": "isEnabled", - "type": "bool" - }, - { - "name": "chains", - "type": { - "vec": "u64" - } - } - ] + "fields": [] } - }, + } + ], + "types": [ { "name": "Any2SVMMessage", "type": { @@ -315,20 +336,6 @@ } ] } - }, - { - "name": "ListType", - "type": { - "kind": "enum", - "variants": [ - { - "name": "Allow" - }, - { - "name": "Deny" - } - ] - } } ], "events": [ @@ -361,8 +368,8 @@ }, { "code": 6002, - "name": "InvalidChain", - "msg": "Invalid chain" + "name": "InvalidChainAndSender", + "msg": "Invalid combination of chain and sender" }, { "code": 6003, diff --git a/chains/solana/contracts/tests/examples/receiver_test.go b/chains/solana/contracts/tests/examples/receiver_test.go index a53862682..7b561a9ea 100644 --- a/chains/solana/contracts/tests/examples/receiver_test.go +++ b/chains/solana/contracts/tests/examples/receiver_test.go @@ -12,6 +12,9 @@ import ( "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" ccip_receiver "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/example_ccip_receiver" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/state" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" ) func TestCcipReceiver(t *testing.T) { @@ -19,6 +22,8 @@ func TestCcipReceiver(t *testing.T) { ctx := tests.Context(t) ccip_receiver.SetProgramID(config.CcipBaseReceiver) + tokenAdmin, _, err := solana.FindProgramAddress([][]byte{[]byte("receiver_token_admin")}, config.CcipBaseReceiver) + require.NoError(t, err) owner, err := solana.NewRandomPrivateKey() require.NoError(t, err) @@ -40,6 +45,7 @@ func TestCcipReceiver(t *testing.T) { ix, err := ccip_receiver.NewInitializeInstruction( config.CcipRouterProgram, receiverState, + tokenAdmin, owner.PublicKey(), solana.SystemProgramID, ).ValidateAndBuild() @@ -49,31 +55,83 @@ func TestCcipReceiver(t *testing.T) { }) }) - t.Run("enable/disable chains", func(t *testing.T) { - ixAllow, err := ccip_receiver.NewAddChainToInstruction(ccip_receiver.Allow_ListType, config.EvmChainSelector, receiverState, owner.PublicKey(), solana.SystemProgramID).ValidateAndBuild() - require.NoError(t, err) - ixDeny, err := ccip_receiver.NewAddChainToInstruction(ccip_receiver.Deny_ListType, config.SVMChainSelector, receiverState, owner.PublicKey(), solana.SystemProgramID).ValidateAndBuild() + t.Run("enable source chain + source sender", func(t *testing.T) { + approvedSenderPDA, err := state.FindApprovedSender(config.EvmChainSelector, []byte{1, 2, 3}, config.CcipBaseReceiver) require.NoError(t, err) - ixEnableAllow, err := ccip_receiver.NewEnableListInstruction(ccip_receiver.Allow_ListType, true, receiverState, owner.PublicKey()).ValidateAndBuild() - require.NoError(t, err) - ixEnableDeny, err := ccip_receiver.NewEnableListInstruction(ccip_receiver.Deny_ListType, true, receiverState, owner.PublicKey()).ValidateAndBuild() + ixApprove, err := ccip_receiver.NewApproveSenderInstruction(config.EvmChainSelector, []byte{1, 2, 3}, receiverState, approvedSenderPDA, owner.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) + testutils.SendAndConfirm(ctx, t, solClient, []solana.Instruction{ixApprove}, owner, rpc.CommitmentConfirmed) - testutils.SendAndConfirm(ctx, t, solClient, []solana.Instruction{ixAllow, ixDeny, ixEnableAllow, ixEnableDeny}, owner, rpc.CommitmentConfirmed) + t.Run("can disable and reenable", func(t *testing.T) { + ixUnapprove, err := ccip_receiver.NewUnapproveSenderInstruction(config.EvmChainSelector, []byte{1, 2, 3}, receiverState, approvedSenderPDA, owner.PublicKey(), solana.SystemProgramID).ValidateAndBuild() + require.NoError(t, err) + testutils.SendAndConfirm(ctx, t, solClient, []solana.Instruction{ixUnapprove}, owner, rpc.CommitmentConfirmed) + + // ensure PDA closed + _, err = solClient.GetAccountInfo(ctx, approvedSenderPDA) + require.ErrorContains(t, err, "not found") + + ixApprove, err := ccip_receiver.NewApproveSenderInstruction(config.EvmChainSelector, []byte{1, 2, 3}, receiverState, approvedSenderPDA, owner.PublicKey(), solana.SystemProgramID).ValidateAndBuild() + require.NoError(t, err) + testutils.SendAndConfirm(ctx, t, solClient, []solana.Instruction{ixApprove}, owner, rpc.CommitmentConfirmed) + }) }) t.Run("check ccip_receiver constraints", func(t *testing.T) { - t.Run("invalid chain", func(t *testing.T) { + t.Run("invalid chain + sender", func(t *testing.T) { t.Parallel() - ix, err := ccip_receiver.NewCcipReceiveInstruction(ccip_receiver.Any2SVMMessage{SourceChainSelector: config.SVMChainSelector}, user.PublicKey(), receiverState).ValidateAndBuild() + approvedSenderPDA, err := state.FindApprovedSender(config.SVMChainSelector, []byte{}, config.CcipBaseReceiver) + require.NoError(t, err) + + ix, err := ccip_receiver.NewCcipReceiveInstruction(ccip_receiver.Any2SVMMessage{SourceChainSelector: config.SVMChainSelector}, user.PublicKey(), approvedSenderPDA, receiverState).ValidateAndBuild() require.NoError(t, err) - testutils.SendAndFailWith(ctx, t, solClient, []solana.Instruction{ix}, user, rpc.CommitmentConfirmed, []string{"Invalid chain"}) + testutils.SendAndFailWith(ctx, t, solClient, []solana.Instruction{ix}, user, rpc.CommitmentConfirmed, []string{"AccountNotInitialized"}) }) t.Run("invalid sender", func(t *testing.T) { t.Parallel() - ix, err := ccip_receiver.NewCcipReceiveInstruction(ccip_receiver.Any2SVMMessage{SourceChainSelector: config.EvmChainSelector}, user.PublicKey(), receiverState).ValidateAndBuild() + approvedSenderPDA, err := state.FindApprovedSender(config.EvmChainSelector, []byte{1, 2, 3}, config.CcipBaseReceiver) + require.NoError(t, err) + ix, err := ccip_receiver.NewCcipReceiveInstruction(ccip_receiver.Any2SVMMessage{SourceChainSelector: config.EvmChainSelector, Sender: []byte{1, 2, 3}}, user.PublicKey(), approvedSenderPDA, receiverState).ValidateAndBuild() require.NoError(t, err) testutils.SendAndFailWith(ctx, t, solClient, []solana.Instruction{ix}, user, rpc.CommitmentConfirmed, []string{"Address is not router external execution PDA"}) }) }) + + t.Run("token withdraw", func(t *testing.T) { + // use token pool for address derivation & state management + token, err := tokens.NewTokenPool(solana.TokenProgramID) + require.NoError(t, err) + + ixs, ixErr := tokens.CreateToken(ctx, token.Program, token.Mint.PublicKey(), owner.PublicKey(), 0, solClient, config.DefaultCommitment) + require.NoError(t, ixErr) + + ixAta, tokenAdminATA, err := tokens.CreateAssociatedTokenAccount(token.Program, token.Mint.PublicKey(), tokenAdmin, owner.PublicKey()) + require.NoError(t, err) + ixAtaOwner, ownerATA, err := tokens.CreateAssociatedTokenAccount(token.Program, token.Mint.PublicKey(), owner.PublicKey(), owner.PublicKey()) + require.NoError(t, err) + + ixMintTo, mintErr := tokens.MintTo(123, token.Program, token.Mint.PublicKey(), tokenAdminATA, owner.PublicKey()) + require.NoError(t, mintErr) + + testutils.SendAndConfirm(ctx, t, solClient, append(ixs, ixAta, ixAtaOwner, ixMintTo), owner, rpc.CommitmentConfirmed, common.AddSigners(token.Mint)) + + // withdraw + _, initBal, err := tokens.TokenBalance(ctx, solClient, tokenAdminATA, config.DefaultCommitment) + require.NoError(t, err) + require.Equal(t, 123, initBal) + _, initBalOwner, err := tokens.TokenBalance(ctx, solClient, ownerATA, config.DefaultCommitment) + require.NoError(t, err) + require.Equal(t, 0, initBalOwner) + + ix, err := ccip_receiver.NewWithdrawTokensInstruction(123, 0, receiverState, tokenAdminATA, ownerATA, token.Mint.PublicKey(), token.Program, tokenAdmin, owner.PublicKey()).ValidateAndBuild() + testutils.SendAndConfirm(ctx, t, solClient, []solana.Instruction{ix}, owner, rpc.CommitmentConfirmed) + + _, finalBal, err := tokens.TokenBalance(ctx, solClient, tokenAdminATA, config.DefaultCommitment) + require.NoError(t, err) + require.Equal(t, 0, finalBal) + _, finalBalOwner, err := tokens.TokenBalance(ctx, solClient, ownerATA, config.DefaultCommitment) + require.NoError(t, err) + require.Equal(t, 123, finalBalOwner) + }) + } diff --git a/chains/solana/gobindings/example_ccip_receiver/AddChainTo.go b/chains/solana/gobindings/example_ccip_receiver/AddChainTo.go deleted file mode 100644 index 0ae979300..000000000 --- a/chains/solana/gobindings/example_ccip_receiver/AddChainTo.go +++ /dev/null @@ -1,188 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package example_ccip_receiver - -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" -) - -// AddChainTo is the `addChainTo` instruction. -type AddChainTo struct { - ListType *ListType - ChainSelector *uint64 - - // [0] = [WRITE] state - // - // [1] = [WRITE, SIGNER] authority - // - // [2] = [] systemProgram - ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` -} - -// NewAddChainToInstructionBuilder creates a new `AddChainTo` instruction builder. -func NewAddChainToInstructionBuilder() *AddChainTo { - nd := &AddChainTo{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), - } - return nd -} - -// SetListType sets the "listType" parameter. -func (inst *AddChainTo) SetListType(listType ListType) *AddChainTo { - inst.ListType = &listType - return inst -} - -// SetChainSelector sets the "chainSelector" parameter. -func (inst *AddChainTo) SetChainSelector(chainSelector uint64) *AddChainTo { - inst.ChainSelector = &chainSelector - return inst -} - -// SetStateAccount sets the "state" account. -func (inst *AddChainTo) SetStateAccount(state ag_solanago.PublicKey) *AddChainTo { - inst.AccountMetaSlice[0] = ag_solanago.Meta(state).WRITE() - return inst -} - -// GetStateAccount gets the "state" account. -func (inst *AddChainTo) GetStateAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[0] -} - -// SetAuthorityAccount sets the "authority" account. -func (inst *AddChainTo) SetAuthorityAccount(authority ag_solanago.PublicKey) *AddChainTo { - inst.AccountMetaSlice[1] = ag_solanago.Meta(authority).WRITE().SIGNER() - return inst -} - -// GetAuthorityAccount gets the "authority" account. -func (inst *AddChainTo) GetAuthorityAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] -} - -// SetSystemProgramAccount sets the "systemProgram" account. -func (inst *AddChainTo) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *AddChainTo { - inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) - return inst -} - -// GetSystemProgramAccount gets the "systemProgram" account. -func (inst *AddChainTo) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] -} - -func (inst AddChainTo) Build() *Instruction { - return &Instruction{BaseVariant: ag_binary.BaseVariant{ - Impl: inst, - TypeID: Instruction_AddChainTo, - }} -} - -// 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 AddChainTo) ValidateAndBuild() (*Instruction, error) { - if err := inst.Validate(); err != nil { - return nil, err - } - return inst.Build(), nil -} - -func (inst *AddChainTo) Validate() error { - // Check whether all (required) parameters are set: - { - if inst.ListType == nil { - return errors.New("ListType parameter is not set") - } - if inst.ChainSelector == nil { - return errors.New("ChainSelector parameter is not set") - } - } - - // Check whether all (required) accounts are set: - { - if inst.AccountMetaSlice[0] == nil { - return errors.New("accounts.State 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 *AddChainTo) EncodeToTree(parent ag_treeout.Branches) { - parent.Child(ag_format.Program(ProgramName, ProgramID)). - // - ParentFunc(func(programBranch ag_treeout.Branches) { - programBranch.Child(ag_format.Instruction("AddChainTo")). - // - ParentFunc(func(instructionBranch ag_treeout.Branches) { - - // Parameters of the instruction: - instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { - paramsBranch.Child(ag_format.Param(" ListType", *inst.ListType)) - paramsBranch.Child(ag_format.Param("ChainSelector", *inst.ChainSelector)) - }) - - // Accounts of the instruction: - instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) - }) - }) - }) -} - -func (obj AddChainTo) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `ListType` param: - err = encoder.Encode(obj.ListType) - if err != nil { - return err - } - // Serialize `ChainSelector` param: - err = encoder.Encode(obj.ChainSelector) - if err != nil { - return err - } - return nil -} -func (obj *AddChainTo) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `ListType`: - err = decoder.Decode(&obj.ListType) - if err != nil { - return err - } - // Deserialize `ChainSelector`: - err = decoder.Decode(&obj.ChainSelector) - if err != nil { - return err - } - return nil -} - -// NewAddChainToInstruction declares a new AddChainTo instruction with the provided parameters and accounts. -func NewAddChainToInstruction( - // Parameters: - listType ListType, - chainSelector uint64, - // Accounts: - state ag_solanago.PublicKey, - authority ag_solanago.PublicKey, - systemProgram ag_solanago.PublicKey) *AddChainTo { - return NewAddChainToInstructionBuilder(). - SetListType(listType). - SetChainSelector(chainSelector). - SetStateAccount(state). - SetAuthorityAccount(authority). - SetSystemProgramAccount(systemProgram) -} diff --git a/chains/solana/gobindings/example_ccip_receiver/ApproveSender.go b/chains/solana/gobindings/example_ccip_receiver/ApproveSender.go new file mode 100644 index 000000000..84a7902ec --- /dev/null +++ b/chains/solana/gobindings/example_ccip_receiver/ApproveSender.go @@ -0,0 +1,207 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package example_ccip_receiver + +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" +) + +// ApproveSender is the `approveSender` instruction. +type ApproveSender struct { + ChainSelector *uint64 + RemoteAddress *[]byte + + // [0] = [WRITE] state + // + // [1] = [WRITE] approvedSender + // + // [2] = [WRITE, SIGNER] authority + // + // [3] = [] systemProgram + ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` +} + +// NewApproveSenderInstructionBuilder creates a new `ApproveSender` instruction builder. +func NewApproveSenderInstructionBuilder() *ApproveSender { + nd := &ApproveSender{ + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 4), + } + return nd +} + +// SetChainSelector sets the "chainSelector" parameter. +func (inst *ApproveSender) SetChainSelector(chainSelector uint64) *ApproveSender { + inst.ChainSelector = &chainSelector + return inst +} + +// SetRemoteAddress sets the "remoteAddress" parameter. +func (inst *ApproveSender) SetRemoteAddress(remoteAddress []byte) *ApproveSender { + inst.RemoteAddress = &remoteAddress + return inst +} + +// SetStateAccount sets the "state" account. +func (inst *ApproveSender) SetStateAccount(state ag_solanago.PublicKey) *ApproveSender { + inst.AccountMetaSlice[0] = ag_solanago.Meta(state).WRITE() + return inst +} + +// GetStateAccount gets the "state" account. +func (inst *ApproveSender) GetStateAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[0] +} + +// SetApprovedSenderAccount sets the "approvedSender" account. +func (inst *ApproveSender) SetApprovedSenderAccount(approvedSender ag_solanago.PublicKey) *ApproveSender { + inst.AccountMetaSlice[1] = ag_solanago.Meta(approvedSender).WRITE() + return inst +} + +// GetApprovedSenderAccount gets the "approvedSender" account. +func (inst *ApproveSender) GetApprovedSenderAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + +// SetAuthorityAccount sets the "authority" account. +func (inst *ApproveSender) SetAuthorityAccount(authority ag_solanago.PublicKey) *ApproveSender { + inst.AccountMetaSlice[2] = ag_solanago.Meta(authority).WRITE().SIGNER() + return inst +} + +// GetAuthorityAccount gets the "authority" account. +func (inst *ApproveSender) GetAuthorityAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +// SetSystemProgramAccount sets the "systemProgram" account. +func (inst *ApproveSender) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *ApproveSender { + inst.AccountMetaSlice[3] = ag_solanago.Meta(systemProgram) + return inst +} + +// GetSystemProgramAccount gets the "systemProgram" account. +func (inst *ApproveSender) GetSystemProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[3] +} + +func (inst ApproveSender) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: Instruction_ApproveSender, + }} +} + +// 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 ApproveSender) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *ApproveSender) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.ChainSelector == nil { + return errors.New("ChainSelector parameter is not set") + } + if inst.RemoteAddress == nil { + return errors.New("RemoteAddress parameter is not set") + } + } + + // Check whether all (required) accounts are set: + { + if inst.AccountMetaSlice[0] == nil { + return errors.New("accounts.State is not set") + } + if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.ApprovedSender is not set") + } + if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.Authority is not set") + } + if inst.AccountMetaSlice[3] == nil { + return errors.New("accounts.SystemProgram is not set") + } + } + return nil +} + +func (inst *ApproveSender) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("ApproveSender")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("ChainSelector", *inst.ChainSelector)) + paramsBranch.Child(ag_format.Param("RemoteAddress", *inst.RemoteAddress)) + }) + + // Accounts of the instruction: + instructionBranch.Child("Accounts[len=4]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta("approvedSender", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta(" systemProgram", inst.AccountMetaSlice[3])) + }) + }) + }) +} + +func (obj ApproveSender) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `ChainSelector` param: + err = encoder.Encode(obj.ChainSelector) + if err != nil { + return err + } + // Serialize `RemoteAddress` param: + err = encoder.Encode(obj.RemoteAddress) + if err != nil { + return err + } + return nil +} +func (obj *ApproveSender) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `ChainSelector`: + err = decoder.Decode(&obj.ChainSelector) + if err != nil { + return err + } + // Deserialize `RemoteAddress`: + err = decoder.Decode(&obj.RemoteAddress) + if err != nil { + return err + } + return nil +} + +// NewApproveSenderInstruction declares a new ApproveSender instruction with the provided parameters and accounts. +func NewApproveSenderInstruction( + // Parameters: + chainSelector uint64, + remoteAddress []byte, + // Accounts: + state ag_solanago.PublicKey, + approvedSender ag_solanago.PublicKey, + authority ag_solanago.PublicKey, + systemProgram ag_solanago.PublicKey) *ApproveSender { + return NewApproveSenderInstructionBuilder(). + SetChainSelector(chainSelector). + SetRemoteAddress(remoteAddress). + SetStateAccount(state). + SetApprovedSenderAccount(approvedSender). + SetAuthorityAccount(authority). + SetSystemProgramAccount(systemProgram) +} diff --git a/chains/solana/gobindings/example_ccip_receiver/AddChainTo_test.go b/chains/solana/gobindings/example_ccip_receiver/ApproveSender_test.go similarity index 77% rename from chains/solana/gobindings/example_ccip_receiver/AddChainTo_test.go rename to chains/solana/gobindings/example_ccip_receiver/ApproveSender_test.go index 7a88ad651..e2dbc2bab 100644 --- a/chains/solana/gobindings/example_ccip_receiver/AddChainTo_test.go +++ b/chains/solana/gobindings/example_ccip_receiver/ApproveSender_test.go @@ -10,18 +10,18 @@ import ( "testing" ) -func TestEncodeDecode_AddChainTo(t *testing.T) { +func TestEncodeDecode_ApproveSender(t *testing.T) { fu := ag_gofuzz.New().NilChance(0) for i := 0; i < 1; i++ { - t.Run("AddChainTo"+strconv.Itoa(i), func(t *testing.T) { + t.Run("ApproveSender"+strconv.Itoa(i), func(t *testing.T) { { - params := new(AddChainTo) + params := new(ApproveSender) fu.Fuzz(params) params.AccountMetaSlice = nil buf := new(bytes.Buffer) err := encodeT(*params, buf) ag_require.NoError(t, err) - got := new(AddChainTo) + got := new(ApproveSender) err = decodeT(got, buf.Bytes()) got.AccountMetaSlice = nil ag_require.NoError(t, err) diff --git a/chains/solana/gobindings/example_ccip_receiver/CcipReceive.go b/chains/solana/gobindings/example_ccip_receiver/CcipReceive.go index 983354e2e..71ff6c02e 100644 --- a/chains/solana/gobindings/example_ccip_receiver/CcipReceive.go +++ b/chains/solana/gobindings/example_ccip_receiver/CcipReceive.go @@ -20,14 +20,16 @@ type CcipReceive struct { // [0] = [SIGNER] authority // - // [1] = [] state + // [1] = [] approvedSender + // + // [2] = [] state ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` } // NewCcipReceiveInstructionBuilder creates a new `CcipReceive` instruction builder. func NewCcipReceiveInstructionBuilder() *CcipReceive { nd := &CcipReceive{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 2), + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), } return nd } @@ -49,15 +51,26 @@ func (inst *CcipReceive) GetAuthorityAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[0] } +// SetApprovedSenderAccount sets the "approvedSender" account. +func (inst *CcipReceive) SetApprovedSenderAccount(approvedSender ag_solanago.PublicKey) *CcipReceive { + inst.AccountMetaSlice[1] = ag_solanago.Meta(approvedSender) + return inst +} + +// GetApprovedSenderAccount gets the "approvedSender" account. +func (inst *CcipReceive) GetApprovedSenderAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + // SetStateAccount sets the "state" account. func (inst *CcipReceive) SetStateAccount(state ag_solanago.PublicKey) *CcipReceive { - inst.AccountMetaSlice[1] = ag_solanago.Meta(state) + inst.AccountMetaSlice[2] = ag_solanago.Meta(state) return inst } // GetStateAccount gets the "state" account. func (inst *CcipReceive) GetStateAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] + return inst.AccountMetaSlice[2] } func (inst CcipReceive) Build() *Instruction { @@ -91,6 +104,9 @@ func (inst *CcipReceive) Validate() error { return errors.New("accounts.Authority is not set") } if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.ApprovedSender is not set") + } + if inst.AccountMetaSlice[2] == nil { return errors.New("accounts.State is not set") } } @@ -111,9 +127,10 @@ func (inst *CcipReceive) EncodeToTree(parent ag_treeout.Branches) { }) // Accounts of the instruction: - instructionBranch.Child("Accounts[len=2]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta("authority", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[1])) + instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta("approvedSender", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[2])) }) }) }) @@ -142,9 +159,11 @@ func NewCcipReceiveInstruction( message Any2SVMMessage, // Accounts: authority ag_solanago.PublicKey, + approvedSender ag_solanago.PublicKey, state ag_solanago.PublicKey) *CcipReceive { return NewCcipReceiveInstructionBuilder(). SetMessage(message). SetAuthorityAccount(authority). + SetApprovedSenderAccount(approvedSender). SetStateAccount(state) } diff --git a/chains/solana/gobindings/example_ccip_receiver/EnableList.go b/chains/solana/gobindings/example_ccip_receiver/EnableList.go deleted file mode 100644 index 0bd3d6f93..000000000 --- a/chains/solana/gobindings/example_ccip_receiver/EnableList.go +++ /dev/null @@ -1,169 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package example_ccip_receiver - -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" -) - -// EnableList is the `enableList` instruction. -type EnableList struct { - ListType *ListType - Enable *bool - - // [0] = [WRITE] state - // - // [1] = [SIGNER] authority - ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` -} - -// NewEnableListInstructionBuilder creates a new `EnableList` instruction builder. -func NewEnableListInstructionBuilder() *EnableList { - nd := &EnableList{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 2), - } - return nd -} - -// SetListType sets the "listType" parameter. -func (inst *EnableList) SetListType(listType ListType) *EnableList { - inst.ListType = &listType - return inst -} - -// SetEnable sets the "enable" parameter. -func (inst *EnableList) SetEnable(enable bool) *EnableList { - inst.Enable = &enable - return inst -} - -// SetStateAccount sets the "state" account. -func (inst *EnableList) SetStateAccount(state ag_solanago.PublicKey) *EnableList { - inst.AccountMetaSlice[0] = ag_solanago.Meta(state).WRITE() - return inst -} - -// GetStateAccount gets the "state" account. -func (inst *EnableList) GetStateAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[0] -} - -// SetAuthorityAccount sets the "authority" account. -func (inst *EnableList) SetAuthorityAccount(authority ag_solanago.PublicKey) *EnableList { - inst.AccountMetaSlice[1] = ag_solanago.Meta(authority).SIGNER() - return inst -} - -// GetAuthorityAccount gets the "authority" account. -func (inst *EnableList) GetAuthorityAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] -} - -func (inst EnableList) Build() *Instruction { - return &Instruction{BaseVariant: ag_binary.BaseVariant{ - Impl: inst, - TypeID: Instruction_EnableList, - }} -} - -// 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 EnableList) ValidateAndBuild() (*Instruction, error) { - if err := inst.Validate(); err != nil { - return nil, err - } - return inst.Build(), nil -} - -func (inst *EnableList) Validate() error { - // Check whether all (required) parameters are set: - { - if inst.ListType == nil { - return errors.New("ListType parameter is not set") - } - if inst.Enable == nil { - return errors.New("Enable parameter is not set") - } - } - - // Check whether all (required) accounts are set: - { - if inst.AccountMetaSlice[0] == nil { - return errors.New("accounts.State is not set") - } - if inst.AccountMetaSlice[1] == nil { - return errors.New("accounts.Authority is not set") - } - } - return nil -} - -func (inst *EnableList) EncodeToTree(parent ag_treeout.Branches) { - parent.Child(ag_format.Program(ProgramName, ProgramID)). - // - ParentFunc(func(programBranch ag_treeout.Branches) { - programBranch.Child(ag_format.Instruction("EnableList")). - // - ParentFunc(func(instructionBranch ag_treeout.Branches) { - - // Parameters of the instruction: - instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { - paramsBranch.Child(ag_format.Param("ListType", *inst.ListType)) - paramsBranch.Child(ag_format.Param(" Enable", *inst.Enable)) - }) - - // Accounts of the instruction: - instructionBranch.Child("Accounts[len=2]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta("authority", inst.AccountMetaSlice[1])) - }) - }) - }) -} - -func (obj EnableList) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `ListType` param: - err = encoder.Encode(obj.ListType) - if err != nil { - return err - } - // Serialize `Enable` param: - err = encoder.Encode(obj.Enable) - if err != nil { - return err - } - return nil -} -func (obj *EnableList) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `ListType`: - err = decoder.Decode(&obj.ListType) - if err != nil { - return err - } - // Deserialize `Enable`: - err = decoder.Decode(&obj.Enable) - if err != nil { - return err - } - return nil -} - -// NewEnableListInstruction declares a new EnableList instruction with the provided parameters and accounts. -func NewEnableListInstruction( - // Parameters: - listType ListType, - enable bool, - // Accounts: - state ag_solanago.PublicKey, - authority ag_solanago.PublicKey) *EnableList { - return NewEnableListInstructionBuilder(). - SetListType(listType). - SetEnable(enable). - SetStateAccount(state). - SetAuthorityAccount(authority) -} diff --git a/chains/solana/gobindings/example_ccip_receiver/Initialize.go b/chains/solana/gobindings/example_ccip_receiver/Initialize.go index 98591e240..dd64dddb1 100644 --- a/chains/solana/gobindings/example_ccip_receiver/Initialize.go +++ b/chains/solana/gobindings/example_ccip_receiver/Initialize.go @@ -16,16 +16,18 @@ type Initialize struct { // [0] = [WRITE] state // - // [1] = [WRITE, SIGNER] authority + // [1] = [WRITE] tokenAdmin // - // [2] = [] systemProgram + // [2] = [WRITE, SIGNER] authority + // + // [3] = [] systemProgram ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` } // NewInitializeInstructionBuilder creates a new `Initialize` instruction builder. func NewInitializeInstructionBuilder() *Initialize { nd := &Initialize{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 3), + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 4), } return nd } @@ -47,26 +49,37 @@ func (inst *Initialize) GetStateAccount() *ag_solanago.AccountMeta { return inst.AccountMetaSlice[0] } +// SetTokenAdminAccount sets the "tokenAdmin" account. +func (inst *Initialize) SetTokenAdminAccount(tokenAdmin ag_solanago.PublicKey) *Initialize { + inst.AccountMetaSlice[1] = ag_solanago.Meta(tokenAdmin).WRITE() + return inst +} + +// GetTokenAdminAccount gets the "tokenAdmin" account. +func (inst *Initialize) GetTokenAdminAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + // SetAuthorityAccount sets the "authority" account. func (inst *Initialize) SetAuthorityAccount(authority ag_solanago.PublicKey) *Initialize { - inst.AccountMetaSlice[1] = ag_solanago.Meta(authority).WRITE().SIGNER() + inst.AccountMetaSlice[2] = ag_solanago.Meta(authority).WRITE().SIGNER() return inst } // GetAuthorityAccount gets the "authority" account. func (inst *Initialize) GetAuthorityAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] + return inst.AccountMetaSlice[2] } // SetSystemProgramAccount sets the "systemProgram" account. func (inst *Initialize) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *Initialize { - inst.AccountMetaSlice[2] = ag_solanago.Meta(systemProgram) + inst.AccountMetaSlice[3] = ag_solanago.Meta(systemProgram) return inst } // GetSystemProgramAccount gets the "systemProgram" account. func (inst *Initialize) GetSystemProgramAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[2] + return inst.AccountMetaSlice[3] } func (inst Initialize) Build() *Instruction { @@ -100,9 +113,12 @@ func (inst *Initialize) Validate() error { return errors.New("accounts.State is not set") } if inst.AccountMetaSlice[1] == nil { - return errors.New("accounts.Authority is not set") + return errors.New("accounts.TokenAdmin is not set") } if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.Authority is not set") + } + if inst.AccountMetaSlice[3] == nil { return errors.New("accounts.SystemProgram is not set") } } @@ -123,10 +139,11 @@ func (inst *Initialize) EncodeToTree(parent ag_treeout.Branches) { }) // Accounts of the instruction: - instructionBranch.Child("Accounts[len=3]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + instructionBranch.Child("Accounts[len=4]").ParentFunc(func(accountsBranch ag_treeout.Branches) { accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[1])) - accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta(" tokenAdmin", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta("systemProgram", inst.AccountMetaSlice[3])) }) }) }) @@ -155,11 +172,13 @@ func NewInitializeInstruction( router ag_solanago.PublicKey, // Accounts: state ag_solanago.PublicKey, + tokenAdmin ag_solanago.PublicKey, authority ag_solanago.PublicKey, systemProgram ag_solanago.PublicKey) *Initialize { return NewInitializeInstructionBuilder(). SetRouter(router). SetStateAccount(state). + SetTokenAdminAccount(tokenAdmin). SetAuthorityAccount(authority). SetSystemProgramAccount(systemProgram) } diff --git a/chains/solana/gobindings/example_ccip_receiver/RemoveChainFrom.go b/chains/solana/gobindings/example_ccip_receiver/RemoveChainFrom.go deleted file mode 100644 index e61a9534f..000000000 --- a/chains/solana/gobindings/example_ccip_receiver/RemoveChainFrom.go +++ /dev/null @@ -1,169 +0,0 @@ -// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. - -package example_ccip_receiver - -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" -) - -// RemoveChainFrom is the `removeChainFrom` instruction. -type RemoveChainFrom struct { - ListType *ListType - ChainSelector *uint64 - - // [0] = [WRITE] state - // - // [1] = [SIGNER] authority - ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` -} - -// NewRemoveChainFromInstructionBuilder creates a new `RemoveChainFrom` instruction builder. -func NewRemoveChainFromInstructionBuilder() *RemoveChainFrom { - nd := &RemoveChainFrom{ - AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 2), - } - return nd -} - -// SetListType sets the "listType" parameter. -func (inst *RemoveChainFrom) SetListType(listType ListType) *RemoveChainFrom { - inst.ListType = &listType - return inst -} - -// SetChainSelector sets the "chainSelector" parameter. -func (inst *RemoveChainFrom) SetChainSelector(chainSelector uint64) *RemoveChainFrom { - inst.ChainSelector = &chainSelector - return inst -} - -// SetStateAccount sets the "state" account. -func (inst *RemoveChainFrom) SetStateAccount(state ag_solanago.PublicKey) *RemoveChainFrom { - inst.AccountMetaSlice[0] = ag_solanago.Meta(state).WRITE() - return inst -} - -// GetStateAccount gets the "state" account. -func (inst *RemoveChainFrom) GetStateAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[0] -} - -// SetAuthorityAccount sets the "authority" account. -func (inst *RemoveChainFrom) SetAuthorityAccount(authority ag_solanago.PublicKey) *RemoveChainFrom { - inst.AccountMetaSlice[1] = ag_solanago.Meta(authority).SIGNER() - return inst -} - -// GetAuthorityAccount gets the "authority" account. -func (inst *RemoveChainFrom) GetAuthorityAccount() *ag_solanago.AccountMeta { - return inst.AccountMetaSlice[1] -} - -func (inst RemoveChainFrom) Build() *Instruction { - return &Instruction{BaseVariant: ag_binary.BaseVariant{ - Impl: inst, - TypeID: Instruction_RemoveChainFrom, - }} -} - -// 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 RemoveChainFrom) ValidateAndBuild() (*Instruction, error) { - if err := inst.Validate(); err != nil { - return nil, err - } - return inst.Build(), nil -} - -func (inst *RemoveChainFrom) Validate() error { - // Check whether all (required) parameters are set: - { - if inst.ListType == nil { - return errors.New("ListType parameter is not set") - } - if inst.ChainSelector == nil { - return errors.New("ChainSelector parameter is not set") - } - } - - // Check whether all (required) accounts are set: - { - if inst.AccountMetaSlice[0] == nil { - return errors.New("accounts.State is not set") - } - if inst.AccountMetaSlice[1] == nil { - return errors.New("accounts.Authority is not set") - } - } - return nil -} - -func (inst *RemoveChainFrom) EncodeToTree(parent ag_treeout.Branches) { - parent.Child(ag_format.Program(ProgramName, ProgramID)). - // - ParentFunc(func(programBranch ag_treeout.Branches) { - programBranch.Child(ag_format.Instruction("RemoveChainFrom")). - // - ParentFunc(func(instructionBranch ag_treeout.Branches) { - - // Parameters of the instruction: - instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { - paramsBranch.Child(ag_format.Param(" ListType", *inst.ListType)) - paramsBranch.Child(ag_format.Param("ChainSelector", *inst.ChainSelector)) - }) - - // Accounts of the instruction: - instructionBranch.Child("Accounts[len=2]").ParentFunc(func(accountsBranch ag_treeout.Branches) { - accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[0])) - accountsBranch.Child(ag_format.Meta("authority", inst.AccountMetaSlice[1])) - }) - }) - }) -} - -func (obj RemoveChainFrom) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `ListType` param: - err = encoder.Encode(obj.ListType) - if err != nil { - return err - } - // Serialize `ChainSelector` param: - err = encoder.Encode(obj.ChainSelector) - if err != nil { - return err - } - return nil -} -func (obj *RemoveChainFrom) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `ListType`: - err = decoder.Decode(&obj.ListType) - if err != nil { - return err - } - // Deserialize `ChainSelector`: - err = decoder.Decode(&obj.ChainSelector) - if err != nil { - return err - } - return nil -} - -// NewRemoveChainFromInstruction declares a new RemoveChainFrom instruction with the provided parameters and accounts. -func NewRemoveChainFromInstruction( - // Parameters: - listType ListType, - chainSelector uint64, - // Accounts: - state ag_solanago.PublicKey, - authority ag_solanago.PublicKey) *RemoveChainFrom { - return NewRemoveChainFromInstructionBuilder(). - SetListType(listType). - SetChainSelector(chainSelector). - SetStateAccount(state). - SetAuthorityAccount(authority) -} diff --git a/chains/solana/gobindings/example_ccip_receiver/UnapproveSender.go b/chains/solana/gobindings/example_ccip_receiver/UnapproveSender.go new file mode 100644 index 000000000..9a1b34099 --- /dev/null +++ b/chains/solana/gobindings/example_ccip_receiver/UnapproveSender.go @@ -0,0 +1,207 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package example_ccip_receiver + +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" +) + +// UnapproveSender is the `unapproveSender` instruction. +type UnapproveSender struct { + ChainSelector *uint64 + RemoteAddress *[]byte + + // [0] = [WRITE] state + // + // [1] = [WRITE] approvedSender + // + // [2] = [WRITE, SIGNER] authority + // + // [3] = [] systemProgram + ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` +} + +// NewUnapproveSenderInstructionBuilder creates a new `UnapproveSender` instruction builder. +func NewUnapproveSenderInstructionBuilder() *UnapproveSender { + nd := &UnapproveSender{ + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 4), + } + return nd +} + +// SetChainSelector sets the "chainSelector" parameter. +func (inst *UnapproveSender) SetChainSelector(chainSelector uint64) *UnapproveSender { + inst.ChainSelector = &chainSelector + return inst +} + +// SetRemoteAddress sets the "remoteAddress" parameter. +func (inst *UnapproveSender) SetRemoteAddress(remoteAddress []byte) *UnapproveSender { + inst.RemoteAddress = &remoteAddress + return inst +} + +// SetStateAccount sets the "state" account. +func (inst *UnapproveSender) SetStateAccount(state ag_solanago.PublicKey) *UnapproveSender { + inst.AccountMetaSlice[0] = ag_solanago.Meta(state).WRITE() + return inst +} + +// GetStateAccount gets the "state" account. +func (inst *UnapproveSender) GetStateAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[0] +} + +// SetApprovedSenderAccount sets the "approvedSender" account. +func (inst *UnapproveSender) SetApprovedSenderAccount(approvedSender ag_solanago.PublicKey) *UnapproveSender { + inst.AccountMetaSlice[1] = ag_solanago.Meta(approvedSender).WRITE() + return inst +} + +// GetApprovedSenderAccount gets the "approvedSender" account. +func (inst *UnapproveSender) GetApprovedSenderAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + +// SetAuthorityAccount sets the "authority" account. +func (inst *UnapproveSender) SetAuthorityAccount(authority ag_solanago.PublicKey) *UnapproveSender { + inst.AccountMetaSlice[2] = ag_solanago.Meta(authority).WRITE().SIGNER() + return inst +} + +// GetAuthorityAccount gets the "authority" account. +func (inst *UnapproveSender) GetAuthorityAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +// SetSystemProgramAccount sets the "systemProgram" account. +func (inst *UnapproveSender) SetSystemProgramAccount(systemProgram ag_solanago.PublicKey) *UnapproveSender { + inst.AccountMetaSlice[3] = ag_solanago.Meta(systemProgram) + return inst +} + +// GetSystemProgramAccount gets the "systemProgram" account. +func (inst *UnapproveSender) GetSystemProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[3] +} + +func (inst UnapproveSender) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: Instruction_UnapproveSender, + }} +} + +// 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 UnapproveSender) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *UnapproveSender) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.ChainSelector == nil { + return errors.New("ChainSelector parameter is not set") + } + if inst.RemoteAddress == nil { + return errors.New("RemoteAddress parameter is not set") + } + } + + // Check whether all (required) accounts are set: + { + if inst.AccountMetaSlice[0] == nil { + return errors.New("accounts.State is not set") + } + if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.ApprovedSender is not set") + } + if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.Authority is not set") + } + if inst.AccountMetaSlice[3] == nil { + return errors.New("accounts.SystemProgram is not set") + } + } + return nil +} + +func (inst *UnapproveSender) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("UnapproveSender")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param("ChainSelector", *inst.ChainSelector)) + paramsBranch.Child(ag_format.Param("RemoteAddress", *inst.RemoteAddress)) + }) + + // Accounts of the instruction: + instructionBranch.Child("Accounts[len=4]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta("approvedSender", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta(" systemProgram", inst.AccountMetaSlice[3])) + }) + }) + }) +} + +func (obj UnapproveSender) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `ChainSelector` param: + err = encoder.Encode(obj.ChainSelector) + if err != nil { + return err + } + // Serialize `RemoteAddress` param: + err = encoder.Encode(obj.RemoteAddress) + if err != nil { + return err + } + return nil +} +func (obj *UnapproveSender) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `ChainSelector`: + err = decoder.Decode(&obj.ChainSelector) + if err != nil { + return err + } + // Deserialize `RemoteAddress`: + err = decoder.Decode(&obj.RemoteAddress) + if err != nil { + return err + } + return nil +} + +// NewUnapproveSenderInstruction declares a new UnapproveSender instruction with the provided parameters and accounts. +func NewUnapproveSenderInstruction( + // Parameters: + chainSelector uint64, + remoteAddress []byte, + // Accounts: + state ag_solanago.PublicKey, + approvedSender ag_solanago.PublicKey, + authority ag_solanago.PublicKey, + systemProgram ag_solanago.PublicKey) *UnapproveSender { + return NewUnapproveSenderInstructionBuilder(). + SetChainSelector(chainSelector). + SetRemoteAddress(remoteAddress). + SetStateAccount(state). + SetApprovedSenderAccount(approvedSender). + SetAuthorityAccount(authority). + SetSystemProgramAccount(systemProgram) +} diff --git a/chains/solana/gobindings/example_ccip_receiver/RemoveChainFrom_test.go b/chains/solana/gobindings/example_ccip_receiver/UnapproveSender_test.go similarity index 76% rename from chains/solana/gobindings/example_ccip_receiver/RemoveChainFrom_test.go rename to chains/solana/gobindings/example_ccip_receiver/UnapproveSender_test.go index ef7fc8571..eedaef046 100644 --- a/chains/solana/gobindings/example_ccip_receiver/RemoveChainFrom_test.go +++ b/chains/solana/gobindings/example_ccip_receiver/UnapproveSender_test.go @@ -10,18 +10,18 @@ import ( "testing" ) -func TestEncodeDecode_RemoveChainFrom(t *testing.T) { +func TestEncodeDecode_UnapproveSender(t *testing.T) { fu := ag_gofuzz.New().NilChance(0) for i := 0; i < 1; i++ { - t.Run("RemoveChainFrom"+strconv.Itoa(i), func(t *testing.T) { + t.Run("UnapproveSender"+strconv.Itoa(i), func(t *testing.T) { { - params := new(RemoveChainFrom) + params := new(UnapproveSender) fu.Fuzz(params) params.AccountMetaSlice = nil buf := new(bytes.Buffer) err := encodeT(*params, buf) ag_require.NoError(t, err) - got := new(RemoveChainFrom) + got := new(UnapproveSender) err = decodeT(got, buf.Bytes()) got.AccountMetaSlice = nil ag_require.NoError(t, err) diff --git a/chains/solana/gobindings/example_ccip_receiver/WithdrawTokens.go b/chains/solana/gobindings/example_ccip_receiver/WithdrawTokens.go new file mode 100644 index 000000000..e0639db8c --- /dev/null +++ b/chains/solana/gobindings/example_ccip_receiver/WithdrawTokens.go @@ -0,0 +1,264 @@ +// Code generated by https://github.com/gagliardetto/anchor-go. DO NOT EDIT. + +package example_ccip_receiver + +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" +) + +// WithdrawTokens is the `withdrawTokens` instruction. +type WithdrawTokens struct { + Amount *uint64 + Decimals *uint8 + + // [0] = [WRITE] state + // + // [1] = [WRITE] programTokenAccount + // + // [2] = [WRITE] toTokenAccount + // + // [3] = [] mint + // + // [4] = [] tokenProgram + // + // [5] = [] tokenAdmin + // + // [6] = [SIGNER] authority + ag_solanago.AccountMetaSlice `bin:"-" borsh_skip:"true"` +} + +// NewWithdrawTokensInstructionBuilder creates a new `WithdrawTokens` instruction builder. +func NewWithdrawTokensInstructionBuilder() *WithdrawTokens { + nd := &WithdrawTokens{ + AccountMetaSlice: make(ag_solanago.AccountMetaSlice, 7), + } + return nd +} + +// SetAmount sets the "amount" parameter. +func (inst *WithdrawTokens) SetAmount(amount uint64) *WithdrawTokens { + inst.Amount = &amount + return inst +} + +// SetDecimals sets the "decimals" parameter. +func (inst *WithdrawTokens) SetDecimals(decimals uint8) *WithdrawTokens { + inst.Decimals = &decimals + return inst +} + +// SetStateAccount sets the "state" account. +func (inst *WithdrawTokens) SetStateAccount(state ag_solanago.PublicKey) *WithdrawTokens { + inst.AccountMetaSlice[0] = ag_solanago.Meta(state).WRITE() + return inst +} + +// GetStateAccount gets the "state" account. +func (inst *WithdrawTokens) GetStateAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[0] +} + +// SetProgramTokenAccountAccount sets the "programTokenAccount" account. +func (inst *WithdrawTokens) SetProgramTokenAccountAccount(programTokenAccount ag_solanago.PublicKey) *WithdrawTokens { + inst.AccountMetaSlice[1] = ag_solanago.Meta(programTokenAccount).WRITE() + return inst +} + +// GetProgramTokenAccountAccount gets the "programTokenAccount" account. +func (inst *WithdrawTokens) GetProgramTokenAccountAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[1] +} + +// SetToTokenAccountAccount sets the "toTokenAccount" account. +func (inst *WithdrawTokens) SetToTokenAccountAccount(toTokenAccount ag_solanago.PublicKey) *WithdrawTokens { + inst.AccountMetaSlice[2] = ag_solanago.Meta(toTokenAccount).WRITE() + return inst +} + +// GetToTokenAccountAccount gets the "toTokenAccount" account. +func (inst *WithdrawTokens) GetToTokenAccountAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[2] +} + +// SetMintAccount sets the "mint" account. +func (inst *WithdrawTokens) SetMintAccount(mint ag_solanago.PublicKey) *WithdrawTokens { + inst.AccountMetaSlice[3] = ag_solanago.Meta(mint) + return inst +} + +// GetMintAccount gets the "mint" account. +func (inst *WithdrawTokens) GetMintAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[3] +} + +// SetTokenProgramAccount sets the "tokenProgram" account. +func (inst *WithdrawTokens) SetTokenProgramAccount(tokenProgram ag_solanago.PublicKey) *WithdrawTokens { + inst.AccountMetaSlice[4] = ag_solanago.Meta(tokenProgram) + return inst +} + +// GetTokenProgramAccount gets the "tokenProgram" account. +func (inst *WithdrawTokens) GetTokenProgramAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[4] +} + +// SetTokenAdminAccount sets the "tokenAdmin" account. +func (inst *WithdrawTokens) SetTokenAdminAccount(tokenAdmin ag_solanago.PublicKey) *WithdrawTokens { + inst.AccountMetaSlice[5] = ag_solanago.Meta(tokenAdmin) + return inst +} + +// GetTokenAdminAccount gets the "tokenAdmin" account. +func (inst *WithdrawTokens) GetTokenAdminAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[5] +} + +// SetAuthorityAccount sets the "authority" account. +func (inst *WithdrawTokens) SetAuthorityAccount(authority ag_solanago.PublicKey) *WithdrawTokens { + inst.AccountMetaSlice[6] = ag_solanago.Meta(authority).SIGNER() + return inst +} + +// GetAuthorityAccount gets the "authority" account. +func (inst *WithdrawTokens) GetAuthorityAccount() *ag_solanago.AccountMeta { + return inst.AccountMetaSlice[6] +} + +func (inst WithdrawTokens) Build() *Instruction { + return &Instruction{BaseVariant: ag_binary.BaseVariant{ + Impl: inst, + TypeID: Instruction_WithdrawTokens, + }} +} + +// 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 WithdrawTokens) ValidateAndBuild() (*Instruction, error) { + if err := inst.Validate(); err != nil { + return nil, err + } + return inst.Build(), nil +} + +func (inst *WithdrawTokens) Validate() error { + // Check whether all (required) parameters are set: + { + if inst.Amount == nil { + return errors.New("Amount parameter is not set") + } + if inst.Decimals == nil { + return errors.New("Decimals parameter is not set") + } + } + + // Check whether all (required) accounts are set: + { + if inst.AccountMetaSlice[0] == nil { + return errors.New("accounts.State is not set") + } + if inst.AccountMetaSlice[1] == nil { + return errors.New("accounts.ProgramTokenAccount is not set") + } + if inst.AccountMetaSlice[2] == nil { + return errors.New("accounts.ToTokenAccount is not set") + } + if inst.AccountMetaSlice[3] == nil { + return errors.New("accounts.Mint is not set") + } + if inst.AccountMetaSlice[4] == nil { + return errors.New("accounts.TokenProgram is not set") + } + if inst.AccountMetaSlice[5] == nil { + return errors.New("accounts.TokenAdmin is not set") + } + if inst.AccountMetaSlice[6] == nil { + return errors.New("accounts.Authority is not set") + } + } + return nil +} + +func (inst *WithdrawTokens) EncodeToTree(parent ag_treeout.Branches) { + parent.Child(ag_format.Program(ProgramName, ProgramID)). + // + ParentFunc(func(programBranch ag_treeout.Branches) { + programBranch.Child(ag_format.Instruction("WithdrawTokens")). + // + ParentFunc(func(instructionBranch ag_treeout.Branches) { + + // Parameters of the instruction: + instructionBranch.Child("Params[len=2]").ParentFunc(func(paramsBranch ag_treeout.Branches) { + paramsBranch.Child(ag_format.Param(" Amount", *inst.Amount)) + paramsBranch.Child(ag_format.Param("Decimals", *inst.Decimals)) + }) + + // Accounts of the instruction: + instructionBranch.Child("Accounts[len=7]").ParentFunc(func(accountsBranch ag_treeout.Branches) { + accountsBranch.Child(ag_format.Meta(" state", inst.AccountMetaSlice[0])) + accountsBranch.Child(ag_format.Meta("programToken", inst.AccountMetaSlice[1])) + accountsBranch.Child(ag_format.Meta(" toToken", inst.AccountMetaSlice[2])) + accountsBranch.Child(ag_format.Meta(" mint", inst.AccountMetaSlice[3])) + accountsBranch.Child(ag_format.Meta("tokenProgram", inst.AccountMetaSlice[4])) + accountsBranch.Child(ag_format.Meta(" tokenAdmin", inst.AccountMetaSlice[5])) + accountsBranch.Child(ag_format.Meta(" authority", inst.AccountMetaSlice[6])) + }) + }) + }) +} + +func (obj WithdrawTokens) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Serialize `Amount` param: + err = encoder.Encode(obj.Amount) + if err != nil { + return err + } + // Serialize `Decimals` param: + err = encoder.Encode(obj.Decimals) + if err != nil { + return err + } + return nil +} +func (obj *WithdrawTokens) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Deserialize `Amount`: + err = decoder.Decode(&obj.Amount) + if err != nil { + return err + } + // Deserialize `Decimals`: + err = decoder.Decode(&obj.Decimals) + if err != nil { + return err + } + return nil +} + +// NewWithdrawTokensInstruction declares a new WithdrawTokens instruction with the provided parameters and accounts. +func NewWithdrawTokensInstruction( + // Parameters: + amount uint64, + decimals uint8, + // Accounts: + state ag_solanago.PublicKey, + programTokenAccount ag_solanago.PublicKey, + toTokenAccount ag_solanago.PublicKey, + mint ag_solanago.PublicKey, + tokenProgram ag_solanago.PublicKey, + tokenAdmin ag_solanago.PublicKey, + authority ag_solanago.PublicKey) *WithdrawTokens { + return NewWithdrawTokensInstructionBuilder(). + SetAmount(amount). + SetDecimals(decimals). + SetStateAccount(state). + SetProgramTokenAccountAccount(programTokenAccount). + SetToTokenAccountAccount(toTokenAccount). + SetMintAccount(mint). + SetTokenProgramAccount(tokenProgram). + SetTokenAdminAccount(tokenAdmin). + SetAuthorityAccount(authority) +} diff --git a/chains/solana/gobindings/example_ccip_receiver/EnableList_test.go b/chains/solana/gobindings/example_ccip_receiver/WithdrawTokens_test.go similarity index 77% rename from chains/solana/gobindings/example_ccip_receiver/EnableList_test.go rename to chains/solana/gobindings/example_ccip_receiver/WithdrawTokens_test.go index 463b747c6..1a979d450 100644 --- a/chains/solana/gobindings/example_ccip_receiver/EnableList_test.go +++ b/chains/solana/gobindings/example_ccip_receiver/WithdrawTokens_test.go @@ -10,18 +10,18 @@ import ( "testing" ) -func TestEncodeDecode_EnableList(t *testing.T) { +func TestEncodeDecode_WithdrawTokens(t *testing.T) { fu := ag_gofuzz.New().NilChance(0) for i := 0; i < 1; i++ { - t.Run("EnableList"+strconv.Itoa(i), func(t *testing.T) { + t.Run("WithdrawTokens"+strconv.Itoa(i), func(t *testing.T) { { - params := new(EnableList) + params := new(WithdrawTokens) fu.Fuzz(params) params.AccountMetaSlice = nil buf := new(bytes.Buffer) err := encodeT(*params, buf) ag_require.NoError(t, err) - got := new(EnableList) + got := new(WithdrawTokens) err = decodeT(got, buf.Bytes()) got.AccountMetaSlice = nil ag_require.NoError(t, err) diff --git a/chains/solana/gobindings/example_ccip_receiver/accounts.go b/chains/solana/gobindings/example_ccip_receiver/accounts.go index 0185550d6..ffc98d3e7 100644 --- a/chains/solana/gobindings/example_ccip_receiver/accounts.go +++ b/chains/solana/gobindings/example_ccip_receiver/accounts.go @@ -12,8 +12,6 @@ type BaseState struct { Owner ag_solanago.PublicKey ProposedOwner ag_solanago.PublicKey Router ag_solanago.PublicKey - Allow ChainList - Deny ChainList } var BaseStateDiscriminator = [8]byte{46, 139, 13, 192, 80, 181, 96, 46} @@ -39,16 +37,6 @@ func (obj BaseState) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) if err != nil { return err } - // Serialize `Allow` param: - err = encoder.Encode(obj.Allow) - if err != nil { - return err - } - // Serialize `Deny` param: - err = encoder.Encode(obj.Deny) - if err != nil { - return err - } return nil } @@ -81,15 +69,35 @@ func (obj *BaseState) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err erro if err != nil { return err } - // Deserialize `Allow`: - err = decoder.Decode(&obj.Allow) + return nil +} + +type ApprovedSender struct{} + +var ApprovedSenderDiscriminator = [8]byte{141, 66, 47, 213, 85, 194, 71, 166} + +func (obj ApprovedSender) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { + // Write account discriminator: + err = encoder.WriteBytes(ApprovedSenderDiscriminator[:], false) if err != nil { return err } - // Deserialize `Deny`: - err = decoder.Decode(&obj.Deny) - if err != nil { - return err + return nil +} + +func (obj *ApprovedSender) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { + // Read and check account discriminator: + { + discriminator, err := decoder.ReadTypeID() + if err != nil { + return err + } + if !discriminator.Equal(ApprovedSenderDiscriminator[:]) { + return fmt.Errorf( + "wrong discriminator: wanted %s, got %s", + "[141 66 47 213 85 194 71 166]", + fmt.Sprint(discriminator[:])) + } } return nil } diff --git a/chains/solana/gobindings/example_ccip_receiver/instructions.go b/chains/solana/gobindings/example_ccip_receiver/instructions.go index 70188427e..38b283530 100644 --- a/chains/solana/gobindings/example_ccip_receiver/instructions.go +++ b/chains/solana/gobindings/example_ccip_receiver/instructions.go @@ -40,17 +40,17 @@ var ( // But none of them could be an init, realloc or close. Instruction_CcipReceive = ag_binary.TypeID([8]byte{11, 244, 9, 249, 44, 83, 47, 245}) - Instruction_EnableList = ag_binary.TypeID([8]byte{121, 229, 134, 138, 144, 138, 19, 248}) - - Instruction_AddChainTo = ag_binary.TypeID([8]byte{210, 228, 19, 16, 203, 240, 167, 178}) + Instruction_UpdateRouter = ag_binary.TypeID([8]byte{32, 109, 12, 153, 101, 129, 64, 70}) - Instruction_RemoveChainFrom = ag_binary.TypeID([8]byte{49, 195, 5, 223, 99, 40, 40, 91}) + Instruction_ApproveSender = ag_binary.TypeID([8]byte{110, 115, 180, 233, 200, 99, 131, 255}) - Instruction_UpdateRouter = ag_binary.TypeID([8]byte{32, 109, 12, 153, 101, 129, 64, 70}) + Instruction_UnapproveSender = ag_binary.TypeID([8]byte{156, 35, 66, 182, 129, 232, 105, 176}) Instruction_TransferOwnership = ag_binary.TypeID([8]byte{65, 177, 215, 73, 53, 45, 99, 47}) Instruction_AcceptOwnership = ag_binary.TypeID([8]byte{172, 23, 43, 13, 238, 213, 85, 150}) + + Instruction_WithdrawTokens = ag_binary.TypeID([8]byte{2, 4, 225, 61, 19, 182, 106, 170}) ) // InstructionIDToName returns the name of the instruction given its ID. @@ -60,18 +60,18 @@ func InstructionIDToName(id ag_binary.TypeID) string { return "Initialize" case Instruction_CcipReceive: return "CcipReceive" - case Instruction_EnableList: - return "EnableList" - case Instruction_AddChainTo: - return "AddChainTo" - case Instruction_RemoveChainFrom: - return "RemoveChainFrom" case Instruction_UpdateRouter: return "UpdateRouter" + case Instruction_ApproveSender: + return "ApproveSender" + case Instruction_UnapproveSender: + return "UnapproveSender" case Instruction_TransferOwnership: return "TransferOwnership" case Instruction_AcceptOwnership: return "AcceptOwnership" + case Instruction_WithdrawTokens: + return "WithdrawTokens" default: return "" } @@ -99,16 +99,13 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( "ccip_receive", (*CcipReceive)(nil), }, { - "enable_list", (*EnableList)(nil), - }, - { - "add_chain_to", (*AddChainTo)(nil), + "update_router", (*UpdateRouter)(nil), }, { - "remove_chain_from", (*RemoveChainFrom)(nil), + "approve_sender", (*ApproveSender)(nil), }, { - "update_router", (*UpdateRouter)(nil), + "unapprove_sender", (*UnapproveSender)(nil), }, { "transfer_ownership", (*TransferOwnership)(nil), @@ -116,6 +113,9 @@ var InstructionImplDef = ag_binary.NewVariantDefinition( { "accept_ownership", (*AcceptOwnership)(nil), }, + { + "withdraw_tokens", (*WithdrawTokens)(nil), + }, }, ) diff --git a/chains/solana/gobindings/example_ccip_receiver/types.go b/chains/solana/gobindings/example_ccip_receiver/types.go index 5c157fc9d..248a63f8d 100644 --- a/chains/solana/gobindings/example_ccip_receiver/types.go +++ b/chains/solana/gobindings/example_ccip_receiver/types.go @@ -7,39 +7,6 @@ import ( ag_solanago "github.com/gagliardetto/solana-go" ) -type ChainList struct { - IsEnabled bool - Chains []uint64 -} - -func (obj ChainList) MarshalWithEncoder(encoder *ag_binary.Encoder) (err error) { - // Serialize `IsEnabled` param: - err = encoder.Encode(obj.IsEnabled) - if err != nil { - return err - } - // Serialize `Chains` param: - err = encoder.Encode(obj.Chains) - if err != nil { - return err - } - return nil -} - -func (obj *ChainList) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err error) { - // Deserialize `IsEnabled`: - err = decoder.Decode(&obj.IsEnabled) - if err != nil { - return err - } - // Deserialize `Chains`: - err = decoder.Decode(&obj.Chains) - if err != nil { - return err - } - return nil -} - type Any2SVMMessage struct { MessageId [32]uint8 SourceChainSelector uint64 @@ -138,21 +105,3 @@ func (obj *SVMTokenAmount) UnmarshalWithDecoder(decoder *ag_binary.Decoder) (err } return nil } - -type ListType ag_binary.BorshEnum - -const ( - Allow_ListType ListType = iota - Deny_ListType -) - -func (value ListType) String() string { - switch value { - case Allow_ListType: - return "Allow" - case Deny_ListType: - return "Deny" - default: - return "" - } -} diff --git a/chains/solana/utils/state/pda.go b/chains/solana/utils/state/pda.go index 05767ee04..da4457d4e 100644 --- a/chains/solana/utils/state/pda.go +++ b/chains/solana/utils/state/pda.go @@ -62,3 +62,10 @@ func FindNoncePDA(chainSelector uint64, user solana.PublicKey, ccipRouterProgram p, _, err := solana.FindProgramAddress([][]byte{[]byte("nonce"), chainSelectorLE, user.Bytes()}, ccipRouterProgram) return p, err } + +func FindApprovedSender(chainSelector uint64, sourceSender []byte, receiverProgram solana.PublicKey) (solana.PublicKey, error) { + chainSelectorLE := common.Uint64ToLE(chainSelector) + p, _, err := solana.FindProgramAddress([][]byte{[]byte("approved_ccip_sender"), chainSelectorLE, []byte{uint8(len(sourceSender))}, sourceSender}, receiverProgram) + return p, err + +}