From dbc52a11f773891b0f431c707e9a1f005115ff14 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Mon, 13 Oct 2025 00:36:37 -0400 Subject: [PATCH 01/13] Dynamic dispatch support for CCIP Receiver contracts --- .../ccip/ccip/sources/receiver_registry.move | 45 +- contracts/ccip/ccip_offramp/Move.toml | 7 + .../ccip/ccip_offramp/sources/offramp.move | 5 + .../mock/burn_mint_dispatchable_receiver.move | 182 +++++ .../lock_release_dispatchable_receiver.move | 182 +++++ .../mock/managed_dispatchable_receiver.move | 189 +++++ .../ccip_offramp/tests/mock/mock_token.move | 49 ++ .../tests/mock/non_dispatchable_receiver.move | 207 +++++ .../mock/regulated_dispatchable_receiver.move | 189 +++++ .../offramp_burn_mint_receiver_test.move | 640 ++++++++++++++++ .../offramp_lock_release_receiver_test.move | 641 ++++++++++++++++ .../tests/offramp_managed_receiver_test.move | 715 ++++++++++++++++++ .../offramp_regulated_receiver_test.move | 429 +++++++++++ .../ccip/ccip_offramp/tests/offramp_test.move | 352 ++++++++- .../sources/burn_mint_token_pool.move | 62 ++ .../sources/lock_release_token_pool.move | 65 ++ .../sources/managed_token_pool.move | 57 ++ .../sources/regulated_token_pool.move | 60 ++ .../tests/regulated_token_pool_test.move | 41 + .../token_pool/sources/token_pool.move | 27 + .../managed_token/sources/managed_token.move | 29 + .../sources/regulated_token.move | 24 + 22 files changed, 4162 insertions(+), 35 deletions(-) create mode 100644 contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move create mode 100644 contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move create mode 100644 contracts/ccip/ccip_offramp/tests/mock/managed_dispatchable_receiver.move create mode 100644 contracts/ccip/ccip_offramp/tests/mock/mock_token.move create mode 100644 contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move create mode 100644 contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move create mode 100644 contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move create mode 100644 contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move create mode 100644 contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move create mode 100644 contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move diff --git a/contracts/ccip/ccip/sources/receiver_registry.move b/contracts/ccip/ccip/sources/receiver_registry.move index 734ae430..353636ca 100644 --- a/contracts/ccip/ccip/sources/receiver_registry.move +++ b/contracts/ccip/ccip/sources/receiver_registry.move @@ -5,13 +5,15 @@ module ccip::receiver_registry { use std::error; use std::event::{Self, EventHandle}; use std::function_info::{Self, FunctionInfo}; + use std::table::{Self, Table}; use std::type_info::{Self, TypeInfo}; use std::fungible_asset::{Self, Metadata}; - use std::object::{Self, ExtendRef, Object, TransferRef}; + use std::object::{Self, ExtendRef, Object, TransferRef, ObjectCore}; use std::option::{Self, Option}; use std::signer; use std::string::{Self, String}; + use ccip::auth; use ccip::client; use ccip::state_object; @@ -32,6 +34,10 @@ module ccip::receiver_registry { executing_input: Option } + struct CCIPReceiveState has key { + executing_receivers: Table + } + #[event] struct ReceiverRegistered has store, drop { receiver_address: address, @@ -45,6 +51,7 @@ module ccip::receiver_registry { const E_NON_EMPTY_INPUT: u64 = 5; const E_PROOF_TYPE_ACCOUNT_MISMATCH: u64 = 6; const E_PROOF_TYPE_MODULE_MISMATCH: u64 = 7; + const E_UNAUTHORIZED: u64 = 8; #[view] public fun type_and_version(): String { @@ -65,6 +72,10 @@ module ccip::receiver_registry { }; move_to(&state_object_signer, state); + + move_to( + &state_object_signer, CCIPReceiveState { executing_receivers: table::new() } + ); } public fun register_receiver( @@ -143,6 +154,13 @@ module ccip::receiver_registry { exists(receiver_address) } + #[view] + public fun is_executing_receiver_in_progress( + receiver_address: address + ): bool acquires CCIPReceiveState { + borrow_ccip_receive_state_mut().executing_receivers.contains(receiver_address) + } + public fun get_receiver_input( receiver_address: address, _proof: ProofType ): client::Any2AptosMessage acquires CCIPReceiverRegistration { @@ -163,7 +181,7 @@ module ccip::receiver_registry { public(friend) fun start_receive( receiver_address: address, message: client::Any2AptosMessage - ): Object acquires CCIPReceiverRegistration { + ): Object acquires CCIPReceiverRegistration, CCIPReceiveState { let registration = get_registration_mut(receiver_address); assert!( @@ -173,16 +191,22 @@ module ccip::receiver_registry { registration.executing_input.fill(message); + borrow_ccip_receive_state_mut().executing_receivers.add(receiver_address, true); + registration.dispatch_metadata } - public(friend) fun finish_receive(receiver_address: address) acquires CCIPReceiverRegistration { + public(friend) fun finish_receive( + receiver_address: address + ) acquires CCIPReceiverRegistration, CCIPReceiveState { let registration = get_registration_mut(receiver_address); assert!( registration.executing_input.is_none(), error::invalid_state(E_NON_EMPTY_INPUT) ); + + borrow_ccip_receive_state_mut().executing_receivers.remove(receiver_address); } inline fun borrow_state(): &ReceiverRegistryState { @@ -193,6 +217,10 @@ module ccip::receiver_registry { borrow_global_mut(state_object::object_address()) } + inline fun borrow_ccip_receive_state_mut(): &mut CCIPReceiveState { + borrow_global_mut(state_object::object_address()) + } + inline fun get_registration_mut(receiver_address: address): &mut CCIPReceiverRegistration { assert!( exists(receiver_address), @@ -201,6 +229,17 @@ module ccip::receiver_registry { borrow_global_mut(receiver_address) } + // ============================= Migrations ============================= + + public fun initialize_ccip_receive_state(caller: &signer) { + auth::assert_only_owner(signer::address_of(caller)); + + move_to( + &state_object::object_signer(), + CCIPReceiveState { executing_receivers: table::new() } + ); + } + #[test_only] public fun init_module_for_testing(publisher: &signer) { init_module(publisher); diff --git a/contracts/ccip/ccip_offramp/Move.toml b/contracts/ccip/ccip_offramp/Move.toml index b518bea5..d67daf8b 100644 --- a/contracts/ccip/ccip_offramp/Move.toml +++ b/contracts/ccip/ccip_offramp/Move.toml @@ -18,8 +18,13 @@ mcms_register_entrypoints = "0x4001" ccip_token_pool = "0x8d62e11f76e6e92563c59e7a5e842a540f8c6c3a4ed8a32f40a5ad3425b55f86" burn_mint_token_pool = "0x8e03eb21315649c06acb9a860a72c2b8cef5bd36775402008b89af397756dad7" lock_release_token_pool = "0x2e1f4cc8fbc2c7ccd2c67ff453e00526919098304d70708ef504af944d6fede8" +managed_token_pool = "0xc0a1d7586bddbd46c9b3445cf9ad82c020dcb82818e6092c9683b51bfecc6633" +regulated_token_pool = "0xb761d8e8425c11486ecb38444aab3493baf7c45381ce973a0bc99618c35021d5" burn_mint_local_token = "0x15c084d10b071a4b180c8d050e421a533bd07d13f9d9386335709da567183768" lock_release_local_token = "0x6d1c246126d36fea774b12486de0a3737997f1b9b23806c67ca5ce72859ff5fa" +managed_token = "0xfda529bc9c3e1cd8bd3df66bd96bb20a36553ed454c909a96e6a0dadb728e896" +regulated_token = "0xb3cec8e3442cafe0c378411012bbcae6787bfc0fbdd528ee9a00aaaf0c88d1b6" +admin = "0x100" [dependencies] AptosFramework = { git = "https://github.com/aptos-labs/aptos-core.git", rev = "837d04bd9bd0dd7156da4252bcfc1d080ed591ec", subdir = "aptos-move/framework/aptos-framework" } @@ -28,3 +33,5 @@ ChainlinkCCIP = { local = "../ccip" } [dev-dependencies] BurnMintTokenPool = { local = "../ccip_token_pools/burn_mint_token_pool", addr_subst = {} } LockReleaseTokenPool = { local = "../ccip_token_pools/lock_release_token_pool", addr_subst = {} } +ManagedTokenPool = { local = "../ccip_token_pools/managed_token_pool", addr_subst = {} } +RegulatedTokenPool = { local = "../ccip_token_pools/regulated_token_pool", addr_subst = {} } diff --git a/contracts/ccip/ccip_offramp/sources/offramp.move b/contracts/ccip/ccip_offramp/sources/offramp.move index ff3a7ef8..b902144d 100644 --- a/contracts/ccip/ccip_offramp/sources/offramp.move +++ b/contracts/ccip/ccip_offramp/sources/offramp.move @@ -1806,4 +1806,9 @@ module ccip_offramp::offramp { public fun merkle_root_merkle_root(root: &MerkleRoot): vector { root.merkle_root } + + #[test_only] + public fun source_chain_config_on_ramp(config: &SourceChainConfig): vector { + config.on_ramp + } } diff --git a/contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move new file mode 100644 index 00000000..920158c0 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move @@ -0,0 +1,182 @@ +#[test_only] +/// The `dispatchable_receiver` is designed to be used with dispatchable tokens +/// with pool type `burn_mint_token_pool`. +module ccip_offramp::burn_mint_dispatchable_receiver { + use std::account; + use std::event; + use std::object::{Self, Object}; + use std::option::{Self, Option}; + use std::string::{Self, String}; + use std::fungible_asset::{Metadata}; + use std::primary_fungible_store; + use std::from_bcs; + use std::signer; + + use ccip::client; + use ccip::receiver_registry; + use burn_mint_token_pool::burn_mint_token_pool; + + #[event] + struct ReceivedMessage has store, drop { + message: String + } + + #[event] + struct ForwardedTokens has store, drop { + final_recipient: address + } + + #[event] + struct ReceivedTokensOnly has store, drop { + token_count: u64 + } + + struct CCIPReceiverState has key { + signer_cap: account::SignerCapability, + received_message_handle: event::EventHandle, + forwarded_tokens_handle: event::EventHandle, + received_tokens_only_handle: event::EventHandle + } + + const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; + const E_UNAUTHORIZED: u64 = 2; + const E_INVALID_TOKEN_ADDRESS: u64 = 3; + const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; + + #[view] + public fun type_and_version(): String { + string::utf8(b"BurnMintDispatchableReceiver 1.6.0") + } + + const MODULE_NAME: vector = b"burn_mint_dispatchable_receiver"; + + fun init_module(publisher: &signer) { + // Create a signer capability for the receiver account + let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); + + // Create a unique handle for each event type + let received_message_handle = + account::new_event_handle(publisher); + let forwarded_tokens_handle = + account::new_event_handle(publisher); + let received_tokens_only_handle = + account::new_event_handle(publisher); + + // Move all state into the single resource struct + move_to( + publisher, + CCIPReceiverState { + signer_cap, + received_message_handle, + forwarded_tokens_handle, + received_tokens_only_handle + } + ); + + receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); + } + + struct CCIPReceiverProof has drop {} + + public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + /* load state and rebuild a signer for the resource account */ + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let message = + receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); + + let data = client::get_data(&message); + + let dest_token_amounts = client::get_dest_token_amounts(&message); + + if (dest_token_amounts.length() != 0 && data.length() != 0) { + let final_recipient = from_bcs::to_address(data); + + for (i in 0..dest_token_amounts.length()) { + let token_amount_ref = &dest_token_amounts[i]; + let amount = client::get_amount(token_amount_ref); + + // For dispatchable tokens, we need to call into token pool's `transfer` function + burn_mint_token_pool::transfer(&state_signer, final_recipient, amount); + }; + + event::emit(ForwardedTokens { final_recipient }); + event::emit_event( + &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } + ); + + } else if (data.length() != 0) { + + // Convert the vector to a string + let message = string::utf8(data); + + event::emit(ReceivedMessage { message }); + event::emit_event( + &mut state.received_message_handle, ReceivedMessage { message } + ); + + } else if (dest_token_amounts.length() != 0) { + // Tokens only (no forwarding data) - keep them at receiver + // Emit event to prove receiver was called + let token_count = dest_token_amounts.length(); + event::emit(ReceivedTokensOnly { token_count }); + event::emit_event( + &mut state.received_tokens_only_handle, + ReceivedTokensOnly { token_count } + ); + }; + + // Simple abort condition for testing + if (data == b"abort") { + abort 1 + }; + + option::none() + } + + public entry fun withdraw_token( + sender: &signer, recipient: address, token_address: address + ) acquires CCIPReceiverState { + assert!( + exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT + ); + assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); + + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let fa_token = object::address_to_object(token_address); + let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); + + // Check if there are tokens available to withdraw + assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); + + primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); + } + + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } + + public fun get_received_message_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.received_message_handle) + } + + public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.forwarded_tokens_handle) + } + + public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle( + &state.received_tokens_only_handle + ) + } + + public fun received_message_get_message(event: &ReceivedMessage): String { + event.message + } +} diff --git a/contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move new file mode 100644 index 00000000..ff7aff45 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move @@ -0,0 +1,182 @@ +#[test_only] +/// The `dispatchable_receiver` is designed to be used with dispatchable tokens +/// with pool type `lock_release_token_pool`. +module ccip_offramp::lock_release_dispatchable_receiver { + use std::account; + use std::event; + use std::object::{Self, Object}; + use std::option::{Self, Option}; + use std::string::{Self, String}; + use std::fungible_asset::{Metadata}; + use std::primary_fungible_store; + use std::from_bcs; + use std::signer; + + use ccip::client; + use ccip::receiver_registry; + use lock_release_token_pool::lock_release_token_pool; + + #[event] + struct ReceivedMessage has store, drop { + message: String + } + + #[event] + struct ForwardedTokens has store, drop { + final_recipient: address + } + + #[event] + struct ReceivedTokensOnly has store, drop { + token_count: u64 + } + + struct CCIPReceiverState has key { + signer_cap: account::SignerCapability, + received_message_handle: event::EventHandle, + forwarded_tokens_handle: event::EventHandle, + received_tokens_only_handle: event::EventHandle + } + + const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; + const E_UNAUTHORIZED: u64 = 2; + const E_INVALID_TOKEN_ADDRESS: u64 = 3; + const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; + + #[view] + public fun type_and_version(): String { + string::utf8(b"LockReleaseDispatchableReceiver 1.6.0") + } + + const MODULE_NAME: vector = b"lock_release_dispatchable_receiver"; + + fun init_module(publisher: &signer) { + // Create a signer capability for the receiver account + let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); + + // Create a unique handle for each event type + let received_message_handle = + account::new_event_handle(publisher); + let forwarded_tokens_handle = + account::new_event_handle(publisher); + let received_tokens_only_handle = + account::new_event_handle(publisher); + + // Move all state into the single resource struct + move_to( + publisher, + CCIPReceiverState { + signer_cap, + received_message_handle, + forwarded_tokens_handle, + received_tokens_only_handle + } + ); + + receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); + } + + struct CCIPReceiverProof has drop {} + + public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + /* load state and rebuild a signer for the resource account */ + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let message = + receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); + + let data = client::get_data(&message); + + let dest_token_amounts = client::get_dest_token_amounts(&message); + + if (dest_token_amounts.length() != 0 && data.length() != 0) { + let final_recipient = from_bcs::to_address(data); + + for (i in 0..dest_token_amounts.length()) { + let token_amount_ref = &dest_token_amounts[i]; + let amount = client::get_amount(token_amount_ref); + + // For dispatchable tokens, we need to call into token pool's `transfer` function + lock_release_token_pool::transfer(&state_signer, final_recipient, amount); + }; + + event::emit(ForwardedTokens { final_recipient }); + event::emit_event( + &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } + ); + + } else if (data.length() != 0) { + + // Convert the vector to a string + let message = string::utf8(data); + + event::emit(ReceivedMessage { message }); + event::emit_event( + &mut state.received_message_handle, ReceivedMessage { message } + ); + + } else if (dest_token_amounts.length() != 0) { + // Tokens only (no forwarding data) - keep them at receiver + // Emit event to prove receiver was called + let token_count = dest_token_amounts.length(); + event::emit(ReceivedTokensOnly { token_count }); + event::emit_event( + &mut state.received_tokens_only_handle, + ReceivedTokensOnly { token_count } + ); + }; + + // Simple abort condition for testing + if (data == b"abort") { + abort 1 + }; + + option::none() + } + + public entry fun withdraw_token( + sender: &signer, recipient: address, token_address: address + ) acquires CCIPReceiverState { + assert!( + exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT + ); + assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); + + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let fa_token = object::address_to_object(token_address); + let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); + + // Check if there are tokens available to withdraw + assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); + + primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); + } + + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } + + public fun get_received_message_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.received_message_handle) + } + + public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.forwarded_tokens_handle) + } + + public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle( + &state.received_tokens_only_handle + ) + } + + public fun received_message_get_message(event: &ReceivedMessage): String { + event.message + } +} diff --git a/contracts/ccip/ccip_offramp/tests/mock/managed_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/managed_dispatchable_receiver.move new file mode 100644 index 00000000..c94e5313 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/mock/managed_dispatchable_receiver.move @@ -0,0 +1,189 @@ +#[test_only] +/// The `dispatchable_receiver` is designed to be used with dispatchable tokens +/// with pool type `managed_token_pool`. +module ccip_offramp::managed_dispatchable_receiver { + use std::account; + use std::event; + use std::object::{Self, Object}; + use std::option::{Self, Option}; + use std::string::{Self, String}; + use std::fungible_asset::{Metadata}; + use std::primary_fungible_store; + use std::from_bcs; + use std::signer; + + use ccip::client; + use ccip::receiver_registry; + use managed_token_pool::managed_token_pool; + + #[event] + struct ReceivedMessage has store, drop { + message: String + } + + #[event] + struct ForwardedTokens has store, drop { + final_recipient: address + } + + #[event] + struct ReceivedTokensOnly has store, drop { + token_count: u64 + } + + struct CCIPReceiverState has key { + signer_cap: account::SignerCapability, + received_message_handle: event::EventHandle, + forwarded_tokens_handle: event::EventHandle, + received_tokens_only_handle: event::EventHandle + } + + const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; + const E_UNAUTHORIZED: u64 = 2; + const E_INVALID_TOKEN_ADDRESS: u64 = 3; + const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; + + #[view] + public fun type_and_version(): String { + string::utf8(b"ManagedDispatchableReceiver 1.6.0") + } + + #[view] + public fun get_state_address(): address acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + signer::address_of(&state_signer) + } + + const MODULE_NAME: vector = b"managed_dispatchable_receiver"; + + fun init_module(publisher: &signer) { + // Create a signer capability for the receiver account + let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); + + // Create a unique handle for each event type + let received_message_handle = + account::new_event_handle(publisher); + let forwarded_tokens_handle = + account::new_event_handle(publisher); + let received_tokens_only_handle = + account::new_event_handle(publisher); + + // Move all state into the single resource struct + move_to( + publisher, + CCIPReceiverState { + signer_cap, + received_message_handle, + forwarded_tokens_handle, + received_tokens_only_handle + } + ); + + receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); + } + + struct CCIPReceiverProof has drop {} + + public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + /* load state and rebuild a signer for the resource account */ + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let message = + receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); + + let data = client::get_data(&message); + + let dest_token_amounts = client::get_dest_token_amounts(&message); + + if (dest_token_amounts.length() != 0 && data.length() != 0) { + let final_recipient = from_bcs::to_address(data); + + for (i in 0..dest_token_amounts.length()) { + let token_amount_ref = &dest_token_amounts[i]; + let amount = client::get_amount(token_amount_ref); + + // For dispatchable tokens, we need to call into token pool's `transfer` function + managed_token_pool::transfer(&state_signer, final_recipient, amount); + }; + + event::emit(ForwardedTokens { final_recipient }); + event::emit_event( + &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } + ); + + } else if (data.length() != 0) { + + // Convert the vector to a string + let message = string::utf8(data); + + event::emit(ReceivedMessage { message }); + event::emit_event( + &mut state.received_message_handle, ReceivedMessage { message } + ); + + } else if (dest_token_amounts.length() != 0) { + // Tokens only (no forwarding data) - keep them at receiver + // Emit event to prove receiver was called + let token_count = dest_token_amounts.length(); + event::emit(ReceivedTokensOnly { token_count }); + event::emit_event( + &mut state.received_tokens_only_handle, + ReceivedTokensOnly { token_count } + ); + }; + + // Simple abort condition for testing + if (data == b"abort") { + abort 1 + }; + + option::none() + } + + public entry fun withdraw_token( + sender: &signer, recipient: address, token_address: address + ) acquires CCIPReceiverState { + assert!( + exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT + ); + assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); + + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let fa_token = object::address_to_object(token_address); + let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); + + // Check if there are tokens available to withdraw + assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); + + primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); + } + + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } + + public fun get_received_message_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.received_message_handle) + } + + public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.forwarded_tokens_handle) + } + + public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle( + &state.received_tokens_only_handle + ) + } + + public fun received_message_get_message(event: &ReceivedMessage): String { + event.message + } +} diff --git a/contracts/ccip/ccip_offramp/tests/mock/mock_token.move b/contracts/ccip/ccip_offramp/tests/mock/mock_token.move new file mode 100644 index 00000000..4c6d292c --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/mock/mock_token.move @@ -0,0 +1,49 @@ +#[test_only] +module ccip_offramp::mock_token { + use std::fungible_asset::{Self, FungibleAsset, TransferRef}; + use std::object::{Object, ConstructorRef}; + use std::string::{Self}; + use std::option::{Self}; + use std::function_info; + use std::dispatchable_fungible_asset; + + public fun add_dynamic_dispatch_function( + ccip_onramp_signer: &signer, constructor_ref: &ConstructorRef + ) { + let deposit = + function_info::new_function_info( + ccip_onramp_signer, + string::utf8(b"mock_token"), + string::utf8(b"deposit") + ); + let withdraw = + function_info::new_function_info( + ccip_onramp_signer, + string::utf8(b"mock_token"), + string::utf8(b"withdraw") + ); + dispatchable_fungible_asset::register_dispatch_functions( + constructor_ref, + option::some(withdraw), + option::some(deposit), + option::none() + ); + } + + public fun deposit( + store: Object, fa: FungibleAsset, transfer_ref: &TransferRef + ) { + std::debug::print(&std::string::utf8(b"custom lock_or_burn called")); + fungible_asset::deposit_with_ref(transfer_ref, store, fa); + std::debug::print(&std::string::utf8(b"custom lock_or_burn done")); + } + + public fun withdraw( + store: Object, amount: u64, transfer_ref: &TransferRef + ): FungibleAsset { + std::debug::print(&std::string::utf8(b"custom release_or_mint called")); + let fa = fungible_asset::withdraw_with_ref(transfer_ref, store, amount); + std::debug::print(&std::string::utf8(b"custom release_or_mint done")); + fa + } +} diff --git a/contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move new file mode 100644 index 00000000..26ef1632 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move @@ -0,0 +1,207 @@ +#[test_only] +/// The `non_dispatchable_receiver` module should only be used with non-dispatchable tokens. +module ccip_offramp::non_dispatchable_receiver { + use std::account; + use std::event; + use std::object::{Self, Object}; + use std::option::{Self, Option}; + use std::string::{Self, String}; + use std::fungible_asset::{Self, Metadata}; + use std::primary_fungible_store; + use std::from_bcs; + use std::signer; + + use ccip::client; + use ccip::receiver_registry; + + #[event] + struct ReceivedMessage has store, drop { + message: String + } + + #[event] + struct ForwardedTokens has store, drop { + final_recipient: address + } + + #[event] + struct ReceivedTokensOnly has store, drop { + token_count: u64 + } + + struct CCIPReceiverState has key { + signer_cap: account::SignerCapability, + received_message_handle: event::EventHandle, + forwarded_tokens_handle: event::EventHandle, + received_tokens_only_handle: event::EventHandle + } + + const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; + const E_UNAUTHORIZED: u64 = 2; + const E_INVALID_TOKEN_ADDRESS: u64 = 3; + const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; + + #[view] + public fun type_and_version(): String { + string::utf8(b"NonDispatchableReceiver 1.6.0") + } + + const MODULE_NAME: vector = b"non_dispatchable_receiver"; + + fun init_module(publisher: &signer) { + // Create a signer capability for the receiver account + let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); + + // Create a unique handle for each event type + let received_message_handle = + account::new_event_handle(publisher); + let forwarded_tokens_handle = + account::new_event_handle(publisher); + let received_tokens_only_handle = + account::new_event_handle(publisher); + + // Move all state into the single resource struct + move_to( + publisher, + CCIPReceiverState { + signer_cap, + received_message_handle, + forwarded_tokens_handle, + received_tokens_only_handle + } + ); + + receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); + } + + struct CCIPReceiverProof has drop {} + + public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + /* load state and rebuild a signer for the resource account */ + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let message = + receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); + + let data = client::get_data(&message); + + let dest_token_amounts = client::get_dest_token_amounts(&message); + + if (dest_token_amounts.length() != 0 && data.length() != 0) { + let final_recipient = from_bcs::to_address(data); + + for (i in 0..dest_token_amounts.length()) { + let token_amount_ref = &dest_token_amounts[i]; + let token_addr = client::get_token(token_amount_ref); + let amount = client::get_amount(token_amount_ref); + + // Implement the token transfer logic here + + let fa_token = object::address_to_object(token_addr); + let fa_store_sender = + primary_fungible_store::ensure_primary_store_exists( + @ccip_offramp, fa_token + ); + let fa_store_receiver = + primary_fungible_store::ensure_primary_store_exists( + final_recipient, fa_token + ); + + // For non-dispatchable tokens, we need to use `fungible_asset::transfer` + fungible_asset::transfer( + &state_signer, + fa_store_sender, + fa_store_receiver, + amount + ); + }; + + event::emit(ForwardedTokens { final_recipient }); + event::emit_event( + &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } + ); + } else if (data.length() != 0) { + // Convert the vector to a string + let message = string::utf8(data); + + event::emit(ReceivedMessage { message }); + event::emit_event( + &mut state.received_message_handle, ReceivedMessage { message } + ); + + } else if (dest_token_amounts.length() != 0) { + // Tokens only (no forwarding data) - keep them at receiver + // Emit event to prove receiver was called + let token_count = dest_token_amounts.length(); + event::emit(ReceivedTokensOnly { token_count }); + event::emit_event( + &mut state.received_tokens_only_handle, + ReceivedTokensOnly { token_count } + ); + }; + + // Simple abort condition for testing + if (data == b"abort") { + abort 1 + }; + + option::none() + } + + public entry fun withdraw_token( + sender: &signer, recipient: address, token_address: address + ) acquires CCIPReceiverState { + assert!( + exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT + ); + assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); + + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let fa_token = object::address_to_object(token_address); + let fa_store_sender = + primary_fungible_store::ensure_primary_store_exists(@ccip_offramp, fa_token); + let fa_store_receiver = + primary_fungible_store::ensure_primary_store_exists(recipient, fa_token); + + let balance = fungible_asset::balance(fa_store_sender); + + // Check if there are tokens available to withdraw + assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); + + // For non-dispatchable tokens, we need to use `fungible_asset::transfer` + fungible_asset::transfer( + &state_signer, + fa_store_sender, + fa_store_receiver, + balance + ); + } + + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } + + public fun get_received_message_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.received_message_handle) + } + + public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.forwarded_tokens_handle) + } + + public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle( + &state.received_tokens_only_handle + ) + } + + public fun received_message_get_message(event: &ReceivedMessage): String { + event.message + } +} diff --git a/contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move new file mode 100644 index 00000000..34e6eae4 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move @@ -0,0 +1,189 @@ +#[test_only] +/// The `dispatchable_receiver` is designed to be used with dispatchable tokens +/// with pool type `regulated_token_pool`. +module ccip_offramp::regulated_dispatchable_receiver { + use std::account; + use std::event; + use std::object::{Self, Object}; + use std::option::{Self, Option}; + use std::string::{Self, String}; + use std::fungible_asset::{Metadata}; + use std::primary_fungible_store; + use std::from_bcs; + use std::signer; + + use ccip::client; + use ccip::receiver_registry; + use regulated_token_pool::regulated_token_pool; + + #[event] + struct ReceivedMessage has store, drop { + message: String + } + + #[event] + struct ForwardedTokens has store, drop { + final_recipient: address + } + + #[event] + struct ReceivedTokensOnly has store, drop { + token_count: u64 + } + + struct CCIPReceiverState has key { + signer_cap: account::SignerCapability, + received_message_handle: event::EventHandle, + forwarded_tokens_handle: event::EventHandle, + received_tokens_only_handle: event::EventHandle + } + + const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; + const E_UNAUTHORIZED: u64 = 2; + const E_INVALID_TOKEN_ADDRESS: u64 = 3; + const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; + + #[view] + public fun type_and_version(): String { + string::utf8(b"RegulatedDispatchableReceiver 1.6.0") + } + + #[view] + public fun get_state_address(): address acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + signer::address_of(&state_signer) + } + + const MODULE_NAME: vector = b"regulated_dispatchable_receiver"; + + fun init_module(publisher: &signer) { + // Create a signer capability for the receiver account + let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); + + // Create a unique handle for each event type + let received_message_handle = + account::new_event_handle(publisher); + let forwarded_tokens_handle = + account::new_event_handle(publisher); + let received_tokens_only_handle = + account::new_event_handle(publisher); + + // Move all state into the single resource struct + move_to( + publisher, + CCIPReceiverState { + signer_cap, + received_message_handle, + forwarded_tokens_handle, + received_tokens_only_handle + } + ); + + receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); + } + + struct CCIPReceiverProof has drop {} + + public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + /* load state and rebuild a signer for the resource account */ + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let message = + receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); + + let data = client::get_data(&message); + + let dest_token_amounts = client::get_dest_token_amounts(&message); + + if (dest_token_amounts.length() != 0 && data.length() != 0) { + let final_recipient = from_bcs::to_address(data); + + for (i in 0..dest_token_amounts.length()) { + let token_amount_ref = &dest_token_amounts[i]; + let amount = client::get_amount(token_amount_ref); + + // For dispatchable tokens, we need to call into token pool's `transfer` function + regulated_token_pool::transfer(&state_signer, final_recipient, amount); + }; + + event::emit(ForwardedTokens { final_recipient }); + event::emit_event( + &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } + ); + + } else if (data.length() != 0) { + + // Convert the vector to a string + let message = string::utf8(data); + + event::emit(ReceivedMessage { message }); + event::emit_event( + &mut state.received_message_handle, ReceivedMessage { message } + ); + + } else if (dest_token_amounts.length() != 0) { + // Tokens only (no forwarding data) - keep them at receiver + // Emit event to prove receiver was called + let token_count = dest_token_amounts.length(); + event::emit(ReceivedTokensOnly { token_count }); + event::emit_event( + &mut state.received_tokens_only_handle, + ReceivedTokensOnly { token_count } + ); + }; + + // Simple abort condition for testing + if (data == b"abort") { + abort 1 + }; + + option::none() + } + + public entry fun withdraw_token( + sender: &signer, recipient: address, token_address: address + ) acquires CCIPReceiverState { + assert!( + exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT + ); + assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); + + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let fa_token = object::address_to_object(token_address); + let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); + + // Check if there are tokens available to withdraw + assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); + + primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); + } + + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } + + public fun get_received_message_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.received_message_handle) + } + + public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle(&state.forwarded_tokens_handle) + } + + public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + event::emitted_events_by_handle( + &state.received_tokens_only_handle + ) + } + + public fun received_message_get_message(event: &ReceivedMessage): String { + event.message + } +} diff --git a/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move new file mode 100644 index 00000000..ad5adc9b --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move @@ -0,0 +1,640 @@ +#[test_only] +module ccip_offramp::offramp_burn_mint_receiver_test { + use std::account; + use std::fungible_asset; + use std::object; + use std::primary_fungible_store; + use std::signer; + use std::string; + use std::bcs; + use std::timestamp; + + use burn_mint_token_pool::burn_mint_token_pool; + use ccip_offramp::offramp_test; + use ccip_offramp::offramp; + use ccip_offramp::non_dispatchable_receiver; + use ccip_offramp::burn_mint_dispatchable_receiver; + use ccip::receiver_registry; + + const BURN_MINT_TOKEN_POOL: u8 = 0; + const BURN_MINT_TOKEN_SEED: vector = b"TestToken"; + const EVM_SOURCE_CHAIN_SELECTOR: u64 = 909606746561742123; + const MOCK_EVM_ADDRESS_VECTOR: vector = x"4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97"; + const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; + const GAS_LIMIT: u64 = 1000000; + + fun setup_non_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + non_dispatchable_receiver::test_init_module(ccip_offramp); + } + + fun setup_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + burn_mint_dispatchable_receiver::test_init_module(ccip_offramp); + } + + struct TestMessage has drop { + message: offramp::Any2AptosRampMessage, + merkle_root: vector, + proofs: vector> + } + + fun create_and_commit_message( + message_id: vector, + sequence_number: u64, + receiver: address, + data: vector, + token_amounts: vector + ): TestMessage { + let static_config = offramp::get_static_config(); + let dest_chain_selector = offramp::chain_selector(&static_config); + + let header = + offramp::test_create_ramp_message_header( + message_id, + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + sequence_number, + 0 + ); + + let message = + offramp::test_create_any2aptos_ramp_message( + header, + EVM_SENDER, + data, + receiver, + (GAS_LIMIT as u256), + token_amounts + ); + + let source_chain_config = + offramp::get_source_chain_config(EVM_SOURCE_CHAIN_SELECTOR); + let on_ramp = offramp::source_chain_config_on_ramp(&source_chain_config); + + let metadata_hash = + offramp::test_calculate_metadata_hash( + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + on_ramp + ); + + let message_hash = offramp::test_calculate_message_hash(&message, metadata_hash); + let merkle_root = message_hash; + let proofs = vector[]; + + offramp::test_add_root(merkle_root, timestamp::now_seconds() - 3700); + + TestMessage { message, merkle_root, proofs } + } + + fun execute_message_and_verify_success( + sequence_number: u64, + test_message: TestMessage, + offchain_token_data: vector> + ) { + let TestMessage { message, merkle_root: _, proofs } = test_message; + + let report = + offramp::test_create_execution_report( + EVM_SOURCE_CHAIN_SELECTOR, + message, + offchain_token_data, + proofs + ); + + offramp::test_execute_single_report(report); + + let execution_state = + offramp::get_execution_state(EVM_SOURCE_CHAIN_SELECTOR, sequence_number); + assert!(execution_state == 2); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_non_dispatchable_token_transfer_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false + ); + let token_addr = object::object_address(&token_obj); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 100000 + ); + + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000001", + 1, + @ccip_offramp, // receiver + vector[], + vector[token_amounts] + ); + + execute_message_and_verify_success(1, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 100000); + + let tokens_only_events = + non_dispatchable_receiver::get_received_tokens_only_events(); + assert!(tokens_only_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_non_dispatchable_message_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false + ); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + let test_data = b"Hello from EVM chain!"; + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000002", + 2, + @ccip_offramp, // receiver + test_data, + vector[] + ); + + execute_message_and_verify_success(2, test_message, vector[]); + + let received_events = non_dispatchable_receiver::get_received_message_events(); + assert!(received_events.length() == 1); + + let event = received_events.borrow(0); + let event_message = + non_dispatchable_receiver::received_message_get_message(event); + assert!(event_message == string::utf8(test_data)); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + recipient = @0x999, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_receiver_non_dispatchable_tokens_with_forwarding( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + recipient: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false + ); + let token_addr = object::object_address(&token_obj); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + let recipient_addr = signer::address_of(recipient); + account::create_account_for_test(recipient_addr); + + // Sending 200,000 tokens to receiver contract first + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 200000 + ); + + let test_data = bcs::to_bytes(&recipient_addr); + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000003", + 3, // sequence number + @ccip_offramp, // receiver + test_data, + vector[token_amounts] + ); + + execute_message_and_verify_success(3, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 0); + + let recipient_store = + primary_fungible_store::primary_store(recipient_addr, token_obj); + let recipient_balance = fungible_asset::balance(recipient_store); + assert!(recipient_balance == 200000); + + let forwarded_events = non_dispatchable_receiver::get_forwarded_tokens_events(); + assert!(forwarded_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_token_transfer_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 100000 + ); + + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000004", + 4, // sequence number + @ccip_offramp, // receiver + vector[], + vector[token_amounts] + ); + + execute_message_and_verify_success(4, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 100000); + + let tokens_only_events = + burn_mint_dispatchable_receiver::get_received_tokens_only_events(); + assert!(tokens_only_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_message_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + true + ); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let test_data = b"Hello from EVM chain!"; + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000005", + 5, // sequence number + @ccip_offramp, // receiver + test_data, + vector[] + ); + + execute_message_and_verify_success(5, test_message, vector[]); + + let received_events = + burn_mint_dispatchable_receiver::get_received_message_events(); + assert!(received_events.length() == 1); + + let event = received_events.borrow(0); + let event_message = + burn_mint_dispatchable_receiver::received_message_get_message(event); + assert!(event_message == string::utf8(test_data)); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + recipient = @0x999, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_receiver_dispatchable_tokens_with_forwarding( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + recipient: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let recipient_addr = signer::address_of(recipient); + account::create_account_for_test(recipient_addr); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 200000 + ); + + let test_data = bcs::to_bytes(&recipient_addr); + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000006", + 6, // sequence number + @ccip_offramp, // receiver + test_data, + vector[token_amounts] + ); + + execute_message_and_verify_success(6, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 0); + + let recipient_store = + primary_fungible_store::primary_store(recipient_addr, token_obj); + let recipient_balance = fungible_asset::balance(recipient_store); + assert!(recipient_balance == 200000); + + let forwarded_events = + burn_mint_dispatchable_receiver::get_forwarded_tokens_events(); + assert!(forwarded_events.length() == 1); + } + + // ================================================================ + // | Tests for execution context enforcement | + // ================================================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ), + expected_failure( + abort_code = 327689, location = burn_mint_token_pool::burn_mint_token_pool + ) + ] + fun test_transfer_outside_ccip_receive_fails( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + true + ); + + setup_dispatchable_receiver(owner, ccip_offramp); + + // Try to transfer tokens directly without ccip_receive context + // This should fail because is_executing_receiver_in_progress is false + burn_mint_token_pool::transfer(ccip_offramp, @0x999, 100000); + } +} diff --git a/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move new file mode 100644 index 00000000..fffd44f5 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move @@ -0,0 +1,641 @@ +#[test_only] +module ccip_offramp::offramp_lock_release_receiver_test { + use std::account; + use std::fungible_asset; + use std::object; + use std::primary_fungible_store; + use std::signer; + use std::string; + use std::bcs; + use std::timestamp; + + use lock_release_token_pool::lock_release_token_pool; + use ccip_offramp::offramp_test; + use ccip_offramp::offramp; + use ccip_offramp::non_dispatchable_receiver; + use ccip_offramp::lock_release_dispatchable_receiver; + use ccip::receiver_registry; + + const LOCK_RELEASE_TOKEN_POOL: u8 = 1; + const LOCK_RELEASE_TOKEN_SEED: vector = b"LockReleaseToken"; + const EVM_SOURCE_CHAIN_SELECTOR: u64 = 909606746561742123; + const MOCK_EVM_ADDRESS_VECTOR: vector = x"4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97"; + const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; + const GAS_LIMIT: u64 = 1000000; + + fun setup_non_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + non_dispatchable_receiver::test_init_module(ccip_offramp); + } + + fun setup_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + lock_release_dispatchable_receiver::test_init_module(ccip_offramp); + } + + struct TestMessage has drop { + message: offramp::Any2AptosRampMessage, + merkle_root: vector, + proofs: vector> + } + + fun create_and_commit_message( + message_id: vector, + sequence_number: u64, + receiver: address, + data: vector, + token_amounts: vector + ): TestMessage { + let static_config = offramp::get_static_config(); + let dest_chain_selector = offramp::chain_selector(&static_config); + + let header = + offramp::test_create_ramp_message_header( + message_id, + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + sequence_number, + 0 + ); + + let message = + offramp::test_create_any2aptos_ramp_message( + header, + EVM_SENDER, + data, + receiver, + (GAS_LIMIT as u256), + token_amounts + ); + + let source_chain_config = + offramp::get_source_chain_config(EVM_SOURCE_CHAIN_SELECTOR); + let on_ramp = offramp::source_chain_config_on_ramp(&source_chain_config); + + let metadata_hash = + offramp::test_calculate_metadata_hash( + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + on_ramp + ); + + let message_hash = offramp::test_calculate_message_hash(&message, metadata_hash); + let merkle_root = message_hash; + let proofs = vector[]; + + offramp::test_add_root(merkle_root, timestamp::now_seconds() - 3700); + + TestMessage { message, merkle_root, proofs } + } + + fun execute_message_and_verify_success( + sequence_number: u64, + test_message: TestMessage, + offchain_token_data: vector> + ) { + let TestMessage { message, merkle_root: _, proofs } = test_message; + + let report = + offramp::test_create_execution_report( + EVM_SOURCE_CHAIN_SELECTOR, + message, + offchain_token_data, + proofs + ); + + offramp::test_execute_single_report(report); + + let execution_state = + offramp::get_execution_state(EVM_SOURCE_CHAIN_SELECTOR, sequence_number); + assert!(execution_state == 2); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_non_dispatchable_token_transfer_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + false + ); + let token_addr = object::object_address(&token_obj); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 100000 + ); + + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000001", + 1, + @ccip_offramp, // receiver + vector[], + vector[token_amounts] + ); + + execute_message_and_verify_success(1, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 100000); + + let tokens_only_events = + non_dispatchable_receiver::get_received_tokens_only_events(); + assert!(tokens_only_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_non_dispatchable_message_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + false + ); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + let test_data = b"Hello from EVM chain!"; + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000002", + 2, + @ccip_offramp, // receiver + test_data, + vector[] + ); + + execute_message_and_verify_success(2, test_message, vector[]); + + let received_events = non_dispatchable_receiver::get_received_message_events(); + assert!(received_events.length() == 1); + + let event = received_events.borrow(0); + let event_message = + non_dispatchable_receiver::received_message_get_message(event); + assert!(event_message == string::utf8(test_data)); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + recipient = @0x999, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_receiver_non_dispatchable_tokens_with_forwarding( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + recipient: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + false + ); + let token_addr = object::object_address(&token_obj); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + let recipient_addr = signer::address_of(recipient); + account::create_account_for_test(recipient_addr); + + // Sending 200,000 tokens to receiver contract first + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 200000 + ); + + let test_data = bcs::to_bytes(&recipient_addr); + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000003", + 3, // sequence number + @ccip_offramp, // receiver + test_data, + vector[token_amounts] + ); + + execute_message_and_verify_success(3, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 0); + + let recipient_store = + primary_fungible_store::primary_store(recipient_addr, token_obj); + let recipient_balance = fungible_asset::balance(recipient_store); + assert!(recipient_balance == 200000); + + let forwarded_events = non_dispatchable_receiver::get_forwarded_tokens_events(); + assert!(forwarded_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_token_transfer_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 100000 + ); + + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000004", + 4, // sequence number + @ccip_offramp, // receiver + vector[], + vector[token_amounts] + ); + + execute_message_and_verify_success(4, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 100000); + + let tokens_only_events = + lock_release_dispatchable_receiver::get_received_tokens_only_events(); + assert!(tokens_only_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_message_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + true + ); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let test_data = b"Hello from EVM chain!"; + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000005", + 5, // sequence number + @ccip_offramp, // receiver + test_data, + vector[] + ); + + execute_message_and_verify_success(5, test_message, vector[]); + + let received_events = + lock_release_dispatchable_receiver::get_received_message_events(); + assert!(received_events.length() == 1); + + let event = received_events.borrow(0); + let event_message = + lock_release_dispatchable_receiver::received_message_get_message(event); + assert!(event_message == string::utf8(test_data)); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + recipient = @0x999, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_receiver_dispatchable_tokens_with_forwarding( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + recipient: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let recipient_addr = signer::address_of(recipient); + account::create_account_for_test(recipient_addr); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 200000 + ); + + let test_data = bcs::to_bytes(&recipient_addr); + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000006", + 6, // sequence number + @ccip_offramp, // receiver + test_data, + vector[token_amounts] + ); + + execute_message_and_verify_success(6, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 0); + + let recipient_store = + primary_fungible_store::primary_store(recipient_addr, token_obj); + let recipient_balance = fungible_asset::balance(recipient_store); + assert!(recipient_balance == 200000); + + let forwarded_events = + lock_release_dispatchable_receiver::get_forwarded_tokens_events(); + assert!(forwarded_events.length() == 1); + } + + // ================================================================ + // | Tests for execution context enforcement | + // ================================================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ), + expected_failure( + abort_code = 327691, + location = lock_release_token_pool::lock_release_token_pool + ) + ] + fun test_transfer_outside_ccip_receive_fails( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + true + ); + + setup_dispatchable_receiver(owner, ccip_offramp); + + // Try to transfer tokens directly without ccip_receive context + // This should fail because is_executing_receiver_in_progress is false + lock_release_token_pool::transfer(ccip_offramp, @0x999, 100000); + } +} diff --git a/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move new file mode 100644 index 00000000..b9889317 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move @@ -0,0 +1,715 @@ +#[test_only] +module ccip_offramp::offramp_managed_receiver_test { + use std::account; + use std::fungible_asset; + use std::object; + use std::primary_fungible_store; + use std::signer; + use std::bcs; + use std::string; + use std::timestamp; + + use ccip_offramp::offramp_test; + use ccip_offramp::offramp; + use ccip_offramp::non_dispatchable_receiver; + use ccip_offramp::managed_dispatchable_receiver; + use ccip::receiver_registry; + use managed_token::managed_token; + use managed_token_pool::managed_token_pool; + + const MANAGED_TOKEN_POOL: u8 = 2; + const MANAGED_TOKEN_SEED: vector = b"ManagedToken"; + + const EVM_SOURCE_CHAIN_SELECTOR: u64 = 909606746561742123; + const MOCK_EVM_ADDRESS_VECTOR: vector = x"4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97"; + const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; + const GAS_LIMIT: u64 = 1000000; + + fun setup_non_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + non_dispatchable_receiver::test_init_module(ccip_offramp); + } + + fun setup_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + managed_dispatchable_receiver::test_init_module(ccip_offramp); + } + + struct TestMessage has drop { + message: offramp::Any2AptosRampMessage, + merkle_root: vector, + proofs: vector> + } + + fun create_and_commit_message( + message_id: vector, + sequence_number: u64, + receiver: address, + data: vector, + token_amounts: vector + ): TestMessage { + let static_config = offramp::get_static_config(); + let dest_chain_selector = offramp::chain_selector(&static_config); + + let header = + offramp::test_create_ramp_message_header( + message_id, + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + sequence_number, + 0 + ); + + let message = + offramp::test_create_any2aptos_ramp_message( + header, + EVM_SENDER, + data, + receiver, + (GAS_LIMIT as u256), + token_amounts + ); + + let source_chain_config = + offramp::get_source_chain_config(EVM_SOURCE_CHAIN_SELECTOR); + let on_ramp = offramp::source_chain_config_on_ramp(&source_chain_config); + + let metadata_hash = + offramp::test_calculate_metadata_hash( + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + on_ramp + ); + + let message_hash = offramp::test_calculate_message_hash(&message, metadata_hash); + let merkle_root = message_hash; + let proofs = vector[]; + + offramp::test_add_root(merkle_root, timestamp::now_seconds() - 3700); + + TestMessage { message, merkle_root, proofs } + } + + fun execute_message_and_verify_success( + sequence_number: u64, + test_message: TestMessage, + offchain_token_data: vector> + ) { + let TestMessage { message, merkle_root: _, proofs } = test_message; + + let report = + offramp::test_create_execution_report( + EVM_SOURCE_CHAIN_SELECTOR, + message, + offchain_token_data, + proofs + ); + + offramp::test_execute_single_report(report); + + let execution_state = + offramp::get_execution_state(EVM_SOURCE_CHAIN_SELECTOR, sequence_number); + assert!(execution_state == 2); + } + + // ======================== NON DISPATCHABLE TESTS ======================== + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_non_dispatchable_token_transfer_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + MANAGED_TOKEN_POOL, + MANAGED_TOKEN_SEED, + false + ); + let token_addr = object::object_address(&token_obj); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + // Add to allowlist for release_or_mint + let pool_address = managed_token_pool::get_store_address(); + managed_token::apply_allowed_minter_updates( + owner, vector[], vector[pool_address] + ); + + // Add to allowlist for lock_or_burn + let pool_address = managed_token_pool::get_store_address(); + managed_token::apply_allowed_burner_updates( + owner, vector[], vector[pool_address] + ); + + // // Grant receiver state signer minter and burner role for transfer during forwarding + // let state_address = managed_dispatchable_receiver::get_state_address(); + // managed_token::apply_allowed_minter_updates(owner, vector[], vector[state_address]); + // managed_token::apply_allowed_burner_updates(owner, vector[], vector[state_address]); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 100000 + ); + + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000004", + 4, // sequence number + @ccip_offramp, // receiver + vector[], + vector[token_amounts] + ); + + execute_message_and_verify_success(4, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 100000); + + let tokens_only_events = + non_dispatchable_receiver::get_received_tokens_only_events(); + assert!(tokens_only_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_non_dispatchable_message_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + MANAGED_TOKEN_POOL, + MANAGED_TOKEN_SEED, + false + ); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + // Add to allowlist for lock_or_burn/release_or_mint + let pool_address = managed_token_pool::get_store_address(); + managed_token::apply_allowed_burner_updates( + owner, vector[], vector[pool_address] + ); + managed_token::apply_allowed_minter_updates( + owner, vector[], vector[pool_address] + ); + + let test_data = b"Hello from EVM chain!"; + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000002", + 2, + @ccip_offramp, // receiver + test_data, + vector[] + ); + + execute_message_and_verify_success(2, test_message, vector[]); + + let received_events = non_dispatchable_receiver::get_received_message_events(); + assert!(received_events.length() == 1); + + let event = received_events.borrow(0); + let event_message = + non_dispatchable_receiver::received_message_get_message(event); + assert!(event_message == string::utf8(test_data)); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + recipient = @0x999, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_receiver_non_dispatchable_tokens_with_forwarding( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + recipient: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + MANAGED_TOKEN_POOL, + MANAGED_TOKEN_SEED, + false + ); + let token_addr = object::object_address(&token_obj); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + // Add to allowlist for lock_or_burn/release_or_mint + let pool_address = managed_token_pool::get_store_address(); + managed_token::apply_allowed_burner_updates( + owner, vector[], vector[pool_address] + ); + managed_token::apply_allowed_minter_updates( + owner, vector[], vector[pool_address] + ); + + let recipient_addr = signer::address_of(recipient); + account::create_account_for_test(recipient_addr); + + // Sending 200,000 tokens to receiver contract first + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 200000 + ); + + let test_data = bcs::to_bytes(&recipient_addr); + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000003", + 3, // sequence number + @ccip_offramp, // receiver + test_data, + vector[token_amounts] + ); + + execute_message_and_verify_success(3, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 0); + + let recipient_store = + primary_fungible_store::primary_store(recipient_addr, token_obj); + let recipient_balance = fungible_asset::balance(recipient_store); + assert!(recipient_balance == 200000); + + let forwarded_events = non_dispatchable_receiver::get_forwarded_tokens_events(); + assert!(forwarded_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_token_transfer_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + MANAGED_TOKEN_POOL, + MANAGED_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + // Add to allowlist for lock_or_burn/release_or_mint + let pool_address = managed_token_pool::get_store_address(); + managed_token::apply_allowed_minter_updates( + owner, vector[], vector[pool_address] + ); + managed_token::apply_allowed_burner_updates( + owner, vector[], vector[pool_address] + ); + + // Grant receiver state signer minter and burner role for transfer during forwarding + let state_address = managed_dispatchable_receiver::get_state_address(); + managed_token::apply_allowed_minter_updates( + owner, vector[], vector[state_address] + ); + managed_token::apply_allowed_burner_updates( + owner, vector[], vector[state_address] + ); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 100000 + ); + + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000004", + 4, // sequence number + @ccip_offramp, // receiver + vector[], + vector[token_amounts] + ); + + execute_message_and_verify_success(4, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 100000); + + let tokens_only_events = + managed_dispatchable_receiver::get_received_tokens_only_events(); + assert!(tokens_only_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_message_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + MANAGED_TOKEN_POOL, + MANAGED_TOKEN_SEED, + true + ); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let test_data = b"Hello from EVM chain!"; + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000005", + 5, // sequence number + @ccip_offramp, // receiver + test_data, + vector[] + ); + + execute_message_and_verify_success(5, test_message, vector[]); + + let received_events = + managed_dispatchable_receiver::get_received_message_events(); + assert!(received_events.length() == 1); + + let event = received_events.borrow(0); + let event_message = + managed_dispatchable_receiver::received_message_get_message(event); + assert!(event_message == string::utf8(test_data)); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + recipient = @0x999, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_receiver_dispatchable_tokens_with_forwarding( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + recipient: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + MANAGED_TOKEN_POOL, + MANAGED_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + // Add to allowlist for lock_or_burn/release_or_mint + let pool_address = managed_token_pool::get_store_address(); + managed_token::apply_allowed_minter_updates( + owner, vector[], vector[pool_address] + ); + managed_token::apply_allowed_burner_updates( + owner, vector[], vector[pool_address] + ); + + // Grant receiver state signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for transfer during forwarding + let state_address = managed_dispatchable_receiver::get_state_address(); + managed_token::apply_allowed_minter_updates( + owner, vector[], vector[state_address] + ); + managed_token::apply_allowed_burner_updates( + owner, vector[], vector[state_address] + ); + + let recipient_addr = signer::address_of(recipient); + account::create_account_for_test(recipient_addr); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 200000 + ); + + let test_data = bcs::to_bytes(&recipient_addr); + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000006", + 6, // sequence number + @ccip_offramp, // receiver + test_data, + vector[token_amounts] + ); + + execute_message_and_verify_success(6, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 0); + + let recipient_store = + primary_fungible_store::primary_store(recipient_addr, token_obj); + let recipient_balance = fungible_asset::balance(recipient_store); + assert!(recipient_balance == 200000); + + let forwarded_events = + managed_dispatchable_receiver::get_forwarded_tokens_events(); + assert!(forwarded_events.length() == 1); + } + + // ================================================================ + // | Tests for execution context enforcement | + // ================================================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ), + expected_failure( + abort_code = 327689, location = managed_token_pool::managed_token_pool + ) + ] + fun test_transfer_outside_ccip_receive_fails( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + MANAGED_TOKEN_POOL, + MANAGED_TOKEN_SEED, + false + ); + + setup_non_dispatchable_receiver(owner, ccip_offramp); + + // Try to transfer tokens directly without ccip_receive context + // This should fail because is_executing_receiver_in_progress is false + managed_token_pool::transfer(ccip_offramp, @0x999, 100000); + } +} diff --git a/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move new file mode 100644 index 00000000..20a6a7d8 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move @@ -0,0 +1,429 @@ +#[test_only] +/// Regulated token is always dispatchable, therefore we test the dispatchable token transfer only +module ccip_offramp::offramp_regulated_receiver_test { + use std::account; + use std::fungible_asset; + use std::object; + use std::primary_fungible_store; + use std::signer; + use std::timestamp; + + use ccip_offramp::offramp_test; + use ccip_offramp::offramp; + use ccip_offramp::non_dispatchable_receiver; + use ccip_offramp::regulated_dispatchable_receiver; + use ccip::receiver_registry; + use regulated_token::regulated_token; + use regulated_token_pool::regulated_token_pool; + + const REGULATED_TOKEN_POOL: u8 = 3; + const REGULATED_TOKEN_SEED: vector = b"RegulatedToken"; + + const EVM_SOURCE_CHAIN_SELECTOR: u64 = 909606746561742123; + const MOCK_EVM_ADDRESS_VECTOR: vector = x"4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97"; + const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; + const GAS_LIMIT: u64 = 1000000; + + fun setup_non_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + non_dispatchable_receiver::test_init_module(ccip_offramp); + } + + fun setup_dispatchable_receiver( + owner: &signer, ccip_offramp: &signer + ) { + account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); + receiver_registry::init_module_for_testing(owner); + regulated_dispatchable_receiver::test_init_module(ccip_offramp); + } + + struct TestMessage has drop { + message: offramp::Any2AptosRampMessage, + merkle_root: vector, + proofs: vector> + } + + fun create_and_commit_message( + message_id: vector, + sequence_number: u64, + receiver: address, + data: vector, + token_amounts: vector + ): TestMessage { + let static_config = offramp::get_static_config(); + let dest_chain_selector = offramp::chain_selector(&static_config); + + let header = + offramp::test_create_ramp_message_header( + message_id, + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + sequence_number, + 0 + ); + + let message = + offramp::test_create_any2aptos_ramp_message( + header, + EVM_SENDER, + data, + receiver, + (GAS_LIMIT as u256), + token_amounts + ); + + let source_chain_config = + offramp::get_source_chain_config(EVM_SOURCE_CHAIN_SELECTOR); + let on_ramp = offramp::source_chain_config_on_ramp(&source_chain_config); + + let metadata_hash = + offramp::test_calculate_metadata_hash( + EVM_SOURCE_CHAIN_SELECTOR, + dest_chain_selector, + on_ramp + ); + + let message_hash = offramp::test_calculate_message_hash(&message, metadata_hash); + let merkle_root = message_hash; + let proofs = vector[]; + + offramp::test_add_root(merkle_root, timestamp::now_seconds() - 3700); + + TestMessage { message, merkle_root, proofs } + } + + fun execute_message_and_verify_success( + sequence_number: u64, + test_message: TestMessage, + offchain_token_data: vector> + ) { + let TestMessage { message, merkle_root: _, proofs } = test_message; + + let report = + offramp::test_create_execution_report( + EVM_SOURCE_CHAIN_SELECTOR, + message, + offchain_token_data, + proofs + ); + + offramp::test_execute_single_report(report); + + let execution_state = + offramp::get_execution_state(EVM_SOURCE_CHAIN_SELECTOR, sequence_number); + assert!(execution_state == 2); + } + + // ======================== DISPATCHABLE TESTS ======================== // + + // Regulated token is always dispatchable, therefore we test the dispatchable token transfer only + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_token_transfer_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + REGULATED_TOKEN_POOL, + REGULATED_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + // Grant pool signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for release_or_mint + let pool_address = regulated_token_pool::get_store_address(); + regulated_token::grant_role(owner, 6, pool_address); + + // Grant receiver state signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for transfer during forwarding + let state_signer_address = regulated_dispatchable_receiver::get_state_address(); + regulated_token::grant_role(owner, 6, state_signer_address); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 100000 + ); + + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000004", + 4, // sequence number + @ccip_offramp, // receiver + vector[], + vector[token_amounts] + ); + + execute_message_and_verify_success(4, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 100000); + + let tokens_only_events = + regulated_dispatchable_receiver::get_received_tokens_only_events(); + assert!(tokens_only_events.length() == 1); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_execute_dispatchable_message_only( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + REGULATED_TOKEN_POOL, + REGULATED_TOKEN_SEED, + true + ); + + setup_dispatchable_receiver(owner, ccip_offramp); + + let test_data = b"Hello from EVM chain!"; + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000005", + 5, // sequence number + @ccip_offramp, // receiver + test_data, + vector[] + ); + + execute_message_and_verify_success(5, test_message, vector[]); + + let received_events = + regulated_dispatchable_receiver::get_received_message_events(); + assert!(received_events.length() == 1); + + let event = received_events.borrow(0); + let event_message = + regulated_dispatchable_receiver::received_message_get_message(event); + assert!(event_message == std::string::utf8(test_data)); + } + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + recipient = @0x999, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_receiver_dispatchable_tokens_with_forwarding( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + recipient: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + REGULATED_TOKEN_POOL, + REGULATED_TOKEN_SEED, + true + ); + let token_addr = object::object_address(&token_obj); + + setup_dispatchable_receiver(owner, ccip_offramp); + + // Grant pool signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for release_or_mint + let pool_address = regulated_token_pool::get_store_address(); + regulated_token::grant_role(owner, 6, pool_address); + + // Grant receiver state signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for transfer during forwarding + let state_signer_address = regulated_dispatchable_receiver::get_state_address(); + regulated_token::grant_role(owner, 6, state_signer_address); + + let recipient_addr = signer::address_of(recipient); + account::create_account_for_test(recipient_addr); + + let token_amounts = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + 200000 + ); + + let test_data = std::bcs::to_bytes(&recipient_addr); + let test_message = + create_and_commit_message( + x"0000000000000000000000000000000000000000000000000000000000000006", + 6, // sequence number + @ccip_offramp, // receiver + test_data, + vector[token_amounts] + ); + + execute_message_and_verify_success(6, test_message, vector[vector[]]); + + let token_obj = object::address_to_object(token_addr); + + let receiver_store = + primary_fungible_store::primary_store(@ccip_offramp, token_obj); + let receiver_balance = fungible_asset::balance(receiver_store); + assert!(receiver_balance == 0); + + let recipient_store = + primary_fungible_store::primary_store(recipient_addr, token_obj); + let recipient_balance = fungible_asset::balance(recipient_store); + assert!(recipient_balance == 200000); + + let forwarded_events = + regulated_dispatchable_receiver::get_forwarded_tokens_events(); + assert!(forwarded_events.length() == 1); + } + + // ================================================================ + // | Tests for execution context enforcement | + // ================================================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @admin, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ), + expected_failure( + abort_code = 327689, location = regulated_token_pool::regulated_token_pool + ) + ] + fun test_transfer_outside_ccip_receive_fails( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + let (_owner_addr, _token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + REGULATED_TOKEN_POOL, + REGULATED_TOKEN_SEED, + true + ); + + setup_dispatchable_receiver(owner, ccip_offramp); + + // Try to transfer tokens directly without ccip_receive context + // This should fail because is_executing_receiver_in_progress is false + regulated_token_pool::transfer(ccip_offramp, @0x999, 100000); + } +} diff --git a/contracts/ccip/ccip_offramp/tests/offramp_test.move b/contracts/ccip/ccip_offramp/tests/offramp_test.move index 4eac8c72..1d3f846c 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_test.move @@ -10,6 +10,7 @@ module ccip_offramp::offramp_test { use aptos_framework::primary_fungible_store; use aptos_framework::timestamp; use ccip_offramp::bcs_helper; + use ccip_offramp::mock_token; use ccip::merkle_proof; use ccip::token_admin_registry; @@ -23,6 +24,10 @@ module ccip_offramp::offramp_test { use burn_mint_token_pool::burn_mint_token_pool; use lock_release_token_pool::lock_release_token_pool; + use managed_token::managed_token; + use managed_token_pool::managed_token_pool; + use regulated_token_pool::regulated_token_pool; + use regulated_token::regulated_token; const CHAIN_ID: u8 = 100; const EVM_SOURCE_CHAIN_SELECTOR: u64 = 909606746561742123; @@ -41,9 +46,13 @@ module ccip_offramp::offramp_test { const BURN_MINT_TOKEN_POOL: u8 = 0; const LOCK_RELEASE_TOKEN_POOL: u8 = 1; + const MANAGED_TOKEN_POOL: u8 = 2; + const REGULATED_TOKEN_POOL: u8 = 3; const BURN_MINT_TOKEN_SEED: vector = b"TestToken"; const LOCK_RELEASE_TOKEN_SEED: vector = b"LockReleaseToken"; + const MANAGED_TOKEN_SEED: vector = b"ManagedToken"; + const REGULATED_TOKEN_SEED: vector = b"RegulatedToken"; const MOCK_EVM_ADDRESS: address = @0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97; const MOCK_EVM_ADDRESS_VECTOR: vector = x"4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97"; @@ -61,15 +70,20 @@ module ccip_offramp::offramp_test { timestamp::update_global_time_for_test_secs(timestamp_seconds); } - fun setup( + public fun setup( aptos_framework: &signer, ccip: &signer, ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer, pool_type: u8, - seed: vector + seed: vector, + is_dispatchable: bool ): (address, Object) { let owner_addr = signer::address_of(owner); account::create_account_for_test(signer::address_of(burn_mint_token_pool)); @@ -124,10 +138,16 @@ module ccip_offramp::offramp_test { let (token_obj, token_addr) = create_test_token_and_pool( owner, + ccip_offramp, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, pool_type, - seed + seed, + is_dispatchable ); // Initialize fee quoter @@ -153,10 +173,16 @@ module ccip_offramp::offramp_test { fun create_test_token_and_pool( owner: &signer, + ccip_offramp_signer: &signer, burn_mint_token_pool: &signer, lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer, pool_type: u8, - seed: vector + seed: vector, + is_dispatchable: bool ): (Object, address) { let constructor_ref = object::create_named_object(owner, seed); @@ -178,6 +204,13 @@ module ccip_offramp::offramp_test { let burn_ref = fungible_asset::generate_burn_ref(&constructor_ref); let transfer_ref = fungible_asset::generate_transfer_ref(&constructor_ref); + // Add dynamic dispatch function to the token if it is dispatchable + if (is_dispatchable) { + mock_token::add_dynamic_dispatch_function( + ccip_offramp_signer, &constructor_ref + ); + }; + let token_addr = object::object_address(&metadata); if (pool_type == BURN_MINT_TOKEN_POOL) { @@ -210,7 +243,7 @@ module ccip_offramp::offramp_test { token_addr, signer::address_of(burn_mint_token_pool) ); - } else { + } else if (pool_type == LOCK_RELEASE_TOKEN_POOL) { lock_release_token_pool::test_init_module(lock_release_token_pool); lock_release_token_pool::initialize( owner, @@ -244,17 +277,145 @@ module ccip_offramp::offramp_test { token_addr, signer::address_of(lock_release_token_pool) ); + } else if (pool_type == MANAGED_TOKEN_POOL) { + let seed = b"MT"; + let _constructor_ref = &object::create_named_object(owner, seed); + let _managed_token_pool_constructor_ref = + &object::create_named_object(owner, b"ManagedTokenPool"); + + managed_token::init_module_for_testing(managed_token); + managed_token::initialize( + owner, + option::none(), + string::utf8(b"Managed Token"), + string::utf8(seed), + 6, + string::utf8(b"https://managedtoken.com/images/pic.png"), + string::utf8(b"https://managedtoken.com") + ); + + managed_token_pool::test_init_module(managed_token_pool); + managed_token_pool::apply_chain_updates( + owner, + vector[], + vector[EVM_SOURCE_CHAIN_SELECTOR], + vector[vector[MOCK_EVM_ADDRESS_VECTOR]], + vector[MOCK_EVM_ADDRESS_VECTOR] + ); + managed_token_pool::set_chain_rate_limiter_config( + owner, + EVM_SOURCE_CHAIN_SELECTOR, + true, + OUTBOUND_CAPACITY, + OUTBOUND_RATE, + true, + INBOUND_CAPACITY, + INBOUND_RATE + ); + + token_addr = managed_token::token_metadata(); + metadata = object::address_to_object(token_addr); + + // Set admin for token + token_admin_registry::propose_administrator( + owner, token_addr, signer::address_of(owner) + ); + token_admin_registry::accept_admin_role(owner, token_addr); + token_admin_registry::set_pool( + owner, + token_addr, + signer::address_of(managed_token_pool) + ); + // Fund managed token pool + primary_fungible_store::mint( + &mint_ref, + managed_token_pool::get_store_address(), + 1000 + ); + } else if (pool_type == REGULATED_TOKEN_POOL) { + account::create_account_for_test(signer::address_of(owner)); + account::create_account_for_test(signer::address_of(regulated_token_pool)); + account::create_account_for_test(signer::address_of(regulated_token)); + + // Create an object at @regulated_token for the ownable functionality + let regulated_token_pool_constructor_ref = + object::create_named_object(owner, b"regulated_token_pool"); + account::create_account_for_test( + object::address_from_constructor_ref( + ®ulated_token_pool_constructor_ref + ) + ); + + // Setup regulated token first (use admin as the object creator) + let regulated_token_constructor_ref = + object::create_named_object(owner, b"regulated_token"); + account::create_account_for_test( + object::address_from_constructor_ref(®ulated_token_constructor_ref) + ); + + regulated_token::init_module_for_testing(regulated_token); + regulated_token::initialize( + owner, + option::none(), + string::utf8(b"Regulated Token"), + string::utf8(b"RT"), + 6, + string::utf8( + b"https://regulatedtoken.com/images/pic.png" + ), + string::utf8(b"https://regulatedtoken.com") + ); + + regulated_token_pool::test_init_module(regulated_token_pool); + regulated_token_pool::apply_chain_updates( + owner, + vector[], + vector[EVM_SOURCE_CHAIN_SELECTOR], + vector[vector[MOCK_EVM_ADDRESS_VECTOR]], + vector[MOCK_EVM_ADDRESS_VECTOR] + ); + regulated_token_pool::set_chain_rate_limiter_config( + owner, + EVM_SOURCE_CHAIN_SELECTOR, + true, + OUTBOUND_CAPACITY, + OUTBOUND_RATE, + true, + INBOUND_CAPACITY, + INBOUND_RATE + ); + + token_addr = regulated_token::token_address(); + metadata = regulated_token::token_metadata(); + + // Set admin for token + token_admin_registry::propose_administrator( + owner, token_addr, signer::address_of(owner) + ); + token_admin_registry::accept_admin_role(owner, token_addr); + token_admin_registry::set_pool( + owner, + token_addr, + signer::address_of(regulated_token_pool) + ); + + // Fund regulated token pool + primary_fungible_store::mint( + &mint_ref, + regulated_token_pool::get_store_address(), + 1000 + ); }; let mint_ref = fungible_asset::generate_mint_ref(&constructor_ref); let burn_ref = fungible_asset::generate_burn_ref(&constructor_ref); let transfer_ref = fungible_asset::generate_transfer_ref(&constructor_ref); - // Fund lock/release token pool + // Fund lock/release token pool with sufficient liquidity for tests primary_fungible_store::mint( &mint_ref, lock_release_token_pool::get_store_address(), - 1000 + 10000000 // 10M tokens for test liquidity ); move_to( @@ -354,7 +515,11 @@ module ccip_offramp::offramp_test { ccip_offramp = @ccip_offramp, owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token ) ] fun test_initialize( @@ -363,7 +528,11 @@ module ccip_offramp::offramp_test { ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, - lock_release_token_pool: &signer + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer ) { setup( aptos_framework, @@ -372,8 +541,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ); // Verify initialization was successful @@ -762,7 +936,11 @@ module ccip_offramp::offramp_test { ccip_offramp = @ccip_offramp, owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token ) ] fun test_commit_and_execute( @@ -771,7 +949,11 @@ module ccip_offramp::offramp_test { ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, - lock_release_token_pool: &signer + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer ) { setup( aptos_framework, @@ -780,8 +962,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ); let config_digest = x"000aed76a87f048dab766bc14ecdbb966f4253e309d742585062a75abfc16c38"; @@ -943,7 +1130,11 @@ module ccip_offramp::offramp_test { ccip_offramp = @ccip_offramp, owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token ) ] fun test_manually_execute( @@ -952,7 +1143,11 @@ module ccip_offramp::offramp_test { ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, - lock_release_token_pool: &signer + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer ) { setup( aptos_framework, @@ -961,8 +1156,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ); let merkle_root = @@ -985,6 +1185,10 @@ module ccip_offramp::offramp_test { owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token, receiver = @0xbed8 ) ] @@ -995,6 +1199,10 @@ module ccip_offramp::offramp_test { owner: &signer, burn_mint_token_pool: &signer, lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer, receiver: &signer ) { test_execute_single_report_with_token_transfer( @@ -1004,9 +1212,14 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, receiver, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ) } @@ -1018,6 +1231,10 @@ module ccip_offramp::offramp_test { owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token, receiver = @0xbed8 ) ] @@ -1028,6 +1245,10 @@ module ccip_offramp::offramp_test { owner: &signer, burn_mint_token_pool: &signer, lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer, receiver: &signer ) { test_execute_single_report_with_token_transfer( @@ -1037,9 +1258,14 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, receiver, LOCK_RELEASE_TOKEN_POOL, - LOCK_RELEASE_TOKEN_SEED + LOCK_RELEASE_TOKEN_SEED, + false ) } @@ -1052,9 +1278,14 @@ module ccip_offramp::offramp_test { owner: &signer, burn_mint_token_pool: &signer, lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer, receiver: &signer, pool_type: u8, - token_seed: vector + token_seed: vector, + is_dispatchable: bool ) { let (_owner_addr, token_obj) = setup( @@ -1064,8 +1295,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, pool_type, - token_seed + token_seed, + is_dispatchable ); let token_addr = object::object_address(&token_obj); @@ -1167,7 +1403,11 @@ module ccip_offramp::offramp_test { ccip_offramp = @ccip_offramp, owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token ) ] fun test_transfer_ownership_flow( @@ -1176,7 +1416,11 @@ module ccip_offramp::offramp_test { ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, - lock_release_token_pool: &signer + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer ) { setup( aptos_framework, @@ -1185,8 +1429,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ); let new_owner = signer::address_of(aptos_framework); account::create_account_for_test(new_owner); @@ -1207,7 +1456,11 @@ module ccip_offramp::offramp_test { ccip_offramp = @ccip_offramp, owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token ) ] fun test_getters( @@ -1216,7 +1469,11 @@ module ccip_offramp::offramp_test { ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, - lock_release_token_pool: &signer + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer ) { setup( aptos_framework, @@ -1225,8 +1482,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ); let latest_price_sequence_number = offramp::get_latest_price_sequence_number(); @@ -1247,7 +1509,11 @@ module ccip_offramp::offramp_test { ccip_offramp = @ccip_offramp, owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token ) ] #[expected_failure(abort_code = 65540, location = ccip_offramp::offramp)] @@ -1257,7 +1523,11 @@ module ccip_offramp::offramp_test { ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, - lock_release_token_pool: &signer + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer ) { setup( aptos_framework, @@ -1266,8 +1536,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ); // E_UNKNOWN_SOURCE_CHAIN_SELECTOR @@ -1281,7 +1556,11 @@ module ccip_offramp::offramp_test { ccip_offramp = @ccip_offramp, owner = @0x100, burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token ) ] #[expected_failure(abort_code = 65550, location = ccip_offramp::offramp)] @@ -1291,7 +1570,11 @@ module ccip_offramp::offramp_test { ccip_offramp: &signer, owner: &signer, burn_mint_token_pool: &signer, - lock_release_token_pool: &signer + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer ) { setup( aptos_framework, @@ -1300,8 +1583,13 @@ module ccip_offramp::offramp_test { owner, burn_mint_token_pool, lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED + BURN_MINT_TOKEN_SEED, + false ); // E_INVALID_ROOT diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move index e633bf13..17bd6c8b 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move @@ -10,6 +10,7 @@ module burn_mint_token_pool::burn_mint_token_pool { use aptos_framework::fungible_asset::{BurnRef, MintRef}; use ccip::token_admin_registry; + use ccip::receiver_registry; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -34,6 +35,10 @@ module burn_mint_token_pool::burn_mint_token_pool { mint_ref: Option } + struct BurnMintTokenPoolEvents has key, store { + token_pool_events: token_pool::TokenPoolEvents + } + const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; @@ -42,6 +47,7 @@ module burn_mint_token_pool::burn_mint_token_pool { const E_UNKNOWN_FUNCTION: u64 = 6; const E_MINT_REF_NOT_SET: u64 = 7; const E_BURN_REF_NOT_SET: u64 = 8; + const E_NOT_EXECUTING_RECEIVER: u64 = 9; // ================================================================ // | Init | @@ -139,6 +145,32 @@ module burn_mint_token_pool::burn_mint_token_pool { }; move_to(&store_signer, pool); + + move_to( + &store_signer, + BurnMintTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(&store_signer) + } + ); + } + + public fun initialize_token_pool_events(caller: &signer) acquires BurnMintTokenPoolState { + assert_can_initialize(signer::address_of(caller)); + + let store_signer = + &account::create_signer_with_capability(&borrow_pool().store_signer_cap); + + assert!( + !exists(signer::address_of(store_signer)), + error::already_exists(E_ALREADY_INITIALIZED) + ); + + move_to( + store_signer, + BurnMintTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(store_signer) + } + ); } // ================================================================ @@ -355,6 +387,32 @@ module burn_mint_token_pool::burn_mint_token_pool { fa } + /// Caller must be the receiver contract address when `ccip_receive` is called. + /// Transfer the fungible asset from the receiver to `to` address. + public fun transfer( + receiver: &signer, to: address, amount: u64 + ) acquires BurnMintTokenPoolState, BurnMintTokenPoolEvents { + let receiver_addr = signer::address_of(receiver); + assert!( + receiver_registry::is_executing_receiver_in_progress(receiver_addr), + error::permission_denied(E_NOT_EXECUTING_RECEIVER) + ); + + let pool = borrow_pool_mut(); + assert!(pool.burn_ref.is_some(), E_BURN_REF_NOT_SET); + assert!(pool.mint_ref.is_some(), E_MINT_REF_NOT_SET); + + primary_fungible_store::burn(pool.burn_ref.borrow(), receiver_addr, amount); + primary_fungible_store::mint(pool.mint_ref.borrow(), to, amount); + + token_pool::emit_transfer( + &mut borrow_mut_events().token_pool_events, + receiver_addr, + to, + amount + ); + } + // ================================================================ // | Rate limit config | // ================================================================ @@ -477,6 +535,10 @@ module burn_mint_token_pool::burn_mint_token_pool { borrow_global_mut(store_address()) } + inline fun borrow_mut_events(): &mut BurnMintTokenPoolEvents { + borrow_global_mut(store_address()) + } + // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move index c384eceb..374e9225 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move @@ -9,6 +9,7 @@ module lock_release_token_pool::lock_release_token_pool { use std::signer; use std::string::{Self, String}; + use ccip::receiver_registry; use ccip::token_admin_registry; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; @@ -34,6 +35,10 @@ module lock_release_token_pool::lock_release_token_pool { rebalancer: address } + struct LockReleaseTokenPoolEvents has key, store { + token_pool_events: token_pool::TokenPoolEvents + } + const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; @@ -44,6 +49,7 @@ module lock_release_token_pool::lock_release_token_pool { const E_UNAUTHORIZED: u64 = 8; const E_INSUFFICIENT_LIQUIDITY: u64 = 9; const E_TRANSFER_REF_NOT_SET: u64 = 10; + const E_NOT_EXECUTING_RECEIVER: u64 = 11; // ================================================================ // | Init | @@ -156,6 +162,32 @@ module lock_release_token_pool::lock_release_token_pool { rebalancer }; move_to(&store_signer, pool); + + move_to( + &store_signer, + LockReleaseTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(&store_signer) + } + ); + } + + public fun initialize_token_pool_events(caller: &signer) acquires LockReleaseTokenPoolState { + assert_can_initialize(signer::address_of(caller)); + + let store_signer = + &account::create_signer_with_capability(&borrow_pool().store_signer_cap); + + assert!( + !exists(signer::address_of(store_signer)), + error::already_exists(E_ALREADY_INITIALIZED) + ); + + move_to( + store_signer, + LockReleaseTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(store_signer) + } + ); } // ================================================================ @@ -423,6 +455,35 @@ module lock_release_token_pool::lock_release_token_pool { fa } + /// Caller must be the receiver contract address when `ccip_receive` is called. + /// Transfer the fungible asset from the receiver to `to` address. + public fun transfer( + caller: &signer, to: address, amount: u64 + ) acquires LockReleaseTokenPoolState, LockReleaseTokenPoolEvents { + let caller_addr = signer::address_of(caller); + assert!( + receiver_registry::is_executing_receiver_in_progress(caller_addr), + error::permission_denied(E_NOT_EXECUTING_RECEIVER) + ); + + let pool = borrow_pool_mut(); + assert!(pool.transfer_ref.is_some(), E_TRANSFER_REF_NOT_SET); + + primary_fungible_store::transfer_with_ref( + pool.transfer_ref.borrow(), + caller_addr, + to, + amount + ); + + token_pool::emit_transfer( + &mut borrow_mut_events().token_pool_events, + caller_addr, + to, + amount + ); + } + // ================================================================ // | Rate limit config | // ================================================================ @@ -662,6 +723,10 @@ module lock_release_token_pool::lock_release_token_pool { borrow_global_mut(store_address()) } + inline fun borrow_mut_events(): &mut LockReleaseTokenPoolEvents { + borrow_global_mut(store_address()) + } + // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move index a9f6ee23..a160ea38 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move @@ -10,6 +10,7 @@ module managed_token_pool::managed_token_pool { use managed_token::managed_token; + use ccip::receiver_registry; use ccip::token_admin_registry; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; @@ -27,12 +28,17 @@ module managed_token_pool::managed_token_pool { store_signer_address: address } + struct ManagedTokenPoolEvents has key, store { + token_pool_events: token_pool::TokenPoolEvents + } + const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; const E_LOCAL_TOKEN_MISMATCH: u64 = 4; const E_INVALID_ARGUMENTS: u64 = 5; const E_UNKNOWN_FUNCTION: u64 = 6; + const E_NOT_EXECUTING_RECEIVER: u64 = 9; // ================================================================ // | Init | @@ -91,6 +97,33 @@ module managed_token_pool::managed_token_pool { }; move_to(&store_signer, pool); + + move_to( + &store_signer, + ManagedTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(&store_signer) + } + ); + } + + public fun initialize_token_pool_events(caller: &signer) acquires ManagedTokenPoolState { + let pool = borrow_pool_mut(); + ownable::assert_only_owner(signer::address_of(caller), &pool.ownable_state); + + let store_signer = + &account::create_signer_with_capability(&pool.store_signer_cap); + + assert!( + !exists(signer::address_of(store_signer)), + error::already_exists(E_ALREADY_INITIALIZED) + ); + + move_to( + store_signer, + ManagedTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(store_signer) + } + ); } // ================================================================ @@ -319,6 +352,26 @@ module managed_token_pool::managed_token_pool { fa } + /// Caller must be the receiver contract address when `ccip_receive` is called. + /// Transfer the fungible asset from the receiver to `to` address. + public fun transfer(caller: &signer, to: address, amount: u64) acquires ManagedTokenPoolEvents { + let caller_addr = signer::address_of(caller); + + assert!( + receiver_registry::is_executing_receiver_in_progress(caller_addr), + error::permission_denied(E_NOT_EXECUTING_RECEIVER) + ); + + managed_token::bridge_transfer(caller, to, amount); + + token_pool::emit_transfer( + &mut borrow_mut_events().token_pool_events, + caller_addr, + to, + amount + ); + } + // ================================================================ // | Rate limit config | // ================================================================ @@ -426,6 +479,10 @@ module managed_token_pool::managed_token_pool { borrow_global_mut(store_address()) } + inline fun borrow_mut_events(): &mut ManagedTokenPoolEvents { + borrow_global_mut(store_address()) + } + // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move index 09593040..4bfeb96c 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move @@ -10,6 +10,7 @@ module regulated_token_pool::regulated_token_pool { use regulated_token::regulated_token::{Self}; + use ccip::receiver_registry; use ccip::token_admin_registry; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; @@ -27,12 +28,18 @@ module regulated_token_pool::regulated_token_pool { store_signer_address: address } + struct RegulatedTokenPoolEvents has key, store { + token_pool_events: token_pool::TokenPoolEvents + } + const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; const E_LOCAL_TOKEN_MISMATCH: u64 = 4; const E_INVALID_ARGUMENTS: u64 = 5; const E_UNKNOWN_FUNCTION: u64 = 6; + const E_NOT_REGISTERED_RECEIVER: u64 = 7; + const E_NOT_EXECUTING_RECEIVER: u64 = 9; // ================================================================ // | Init | @@ -89,6 +96,33 @@ module regulated_token_pool::regulated_token_pool { }; move_to(&store_signer, pool); + + move_to( + &store_signer, + RegulatedTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(&store_signer) + } + ); + } + + public fun initialize_token_pool_events(caller: &signer) acquires RegulatedTokenPoolState { + let pool = borrow_pool_mut(); + ownable::assert_only_owner(signer::address_of(caller), &pool.ownable_state); + + let store_signer = + &account::create_signer_with_capability(&pool.store_signer_cap); + + assert!( + !exists(signer::address_of(store_signer)), + error::already_exists(E_ALREADY_INITIALIZED) + ); + + move_to( + store_signer, + RegulatedTokenPoolEvents { + token_pool_events: token_pool::create_transfer_events(store_signer) + } + ); } // ================================================================ @@ -308,6 +342,28 @@ module regulated_token_pool::regulated_token_pool { fa } + /// Caller must be the receiver contract address when `ccip_receive` is called. + /// Transfer the fungible asset from the receiver to `to` address. + public fun transfer(caller: &signer, to: address, amount: u64) acquires RegulatedTokenPoolEvents { + let caller_addr = signer::address_of(caller); + + assert!( + receiver_registry::is_executing_receiver_in_progress(caller_addr), + error::permission_denied(E_NOT_EXECUTING_RECEIVER) + ); + + // Call into regulated_token to perform transfer using TransferRef + // The caller (receiver) must have tokens and the bridge_transfer will check permissions + regulated_token::bridge_transfer(caller, to, amount); + + token_pool::emit_transfer( + &mut borrow_mut_events().token_pool_events, + caller_addr, + to, + amount + ); + } + // ================================================================ // | Rate limit config | // ================================================================ @@ -415,6 +471,10 @@ module regulated_token_pool::regulated_token_pool { borrow_global_mut(store_address()) } + inline fun borrow_mut_events(): &mut RegulatedTokenPoolEvents { + borrow_global_mut(store_address()) + } + // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move index feded37d..50a960b6 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move @@ -10,6 +10,7 @@ module regulated_token_pool::regulated_token_pool_test { use ccip::state_object; use ccip::auth; use ccip::token_admin_registry; + use ccip::receiver_registry; use regulated_token::regulated_token; use regulated_token_pool::regulated_token_pool; @@ -793,4 +794,44 @@ module regulated_token_pool::regulated_token_pool_test { assert!(regulated_token_pool::pending_transfer_to().is_none()); assert!(regulated_token_pool::pending_transfer_accepted().is_none()); } + + // ================================================================ + // | Tests for execution context enforcement | + // ================================================================ + + #[ + test( + admin = @admin, + regulated_token = @regulated_token, + regulated_token_pool = @regulated_token_pool, + framework = @aptos_framework, + ccip = @ccip, + random_signer = @0x999 + ), + expected_failure( + abort_code = 327689, location = regulated_token_pool::regulated_token_pool + ) + ] + fun test_transfer_outside_ccip_receive_fails( + admin: &signer, + regulated_token: &signer, + regulated_token_pool: &signer, + framework: &signer, + ccip: &signer, + random_signer: &signer + ) { + setup( + admin, + regulated_token, + regulated_token_pool, + framework, + ccip + ); + + receiver_registry::init_module_for_testing(admin); + + // Try to transfer tokens directly without ccip_receive context + // This should fail because is_executing_receiver_in_progress is false + regulated_token_pool::transfer(random_signer, @0x777, 100000); + } } diff --git a/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move b/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move index b7dc5b2e..70d21df2 100644 --- a/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move +++ b/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move @@ -35,6 +35,10 @@ module ccip_token_pool::token_pool { rebalancer_set_events: EventHandle } + struct TokenPoolEvents has key, store { + transfer_events: EventHandle + } + struct RemoteChainConfig has store, drop, copy { remote_token_address: vector, remote_pools: vector> @@ -108,6 +112,13 @@ module ccip_token_pool::token_pool { new_rebalancer: address } + #[event] + struct Transfer has store, drop { + from: address, + to: address, + amount: u64 + } + const E_NOT_ALLOWED_CALLER: u64 = 1; const E_UNKNOWN_FUNGIBLE_ASSET: u64 = 2; const E_UNKNOWN_REMOTE_CHAIN_SELECTOR: u64 = 3; @@ -149,6 +160,10 @@ module ccip_token_pool::token_pool { } } + public fun create_transfer_events(event_account: &signer): TokenPoolEvents { + TokenPoolEvents { transfer_events: account::new_event_handle(event_account) } + } + #[view] public fun get_router(): address { @ccip @@ -481,6 +496,18 @@ module ccip_token_pool::token_pool { ); } + public fun emit_transfer( + events: &mut TokenPoolEvents, + from: address, + to: address, + amount: u64 + ) { + event::emit_event( + &mut events.transfer_events, + Transfer { from, to, amount } + ); + } + // ================================================================ // | Decimals | // ================================================================ diff --git a/contracts/managed_token/sources/managed_token.move b/contracts/managed_token/sources/managed_token.move index 2dfb4360..71da13b3 100644 --- a/contracts/managed_token/sources/managed_token.move +++ b/contracts/managed_token/sources/managed_token.move @@ -336,6 +336,35 @@ module managed_token::managed_token { ); } + public fun bridge_transfer( + caller: &signer, to: address, amount: u64 + ) acquires TokenMetadataRefs, TokenState { + let caller_addr = signer::address_of(caller); + let state = &mut TokenState[token_state_address_internal()]; + + // Must be allowed as both minter and burner to transfer + assert_is_allowed_minter(caller_addr, state); + assert_is_allowed_burner(caller_addr, state); + + if (amount == 0) { return }; + + primary_fungible_store::transfer_with_ref( + &borrow_token_metadata_refs(state).transfer_ref, + caller_addr, + to, + amount + ); + + event::emit_event( + &mut state.burn_events, + Burn { burner: caller_addr, from: caller_addr, amount } + ); + event::emit_event( + &mut state.mint_events, + Mint { minter: caller_addr, to, amount } + ); + } + inline fun assert_is_allowed_minter( caller: address, state: &TokenState ) { diff --git a/contracts/regulated_token/sources/regulated_token.move b/contracts/regulated_token/sources/regulated_token.move index 0b5ea744..91fd2c64 100644 --- a/contracts/regulated_token/sources/regulated_token.move +++ b/contracts/regulated_token/sources/regulated_token.move @@ -114,6 +114,13 @@ module regulated_token::regulated_token { amount: u64 } + #[event] + struct BridgeTransfer has drop, store { + caller: address, + to: address, + amount: u64 + } + #[event] struct MinterAdded has drop, store { admin: address, @@ -592,6 +599,23 @@ module regulated_token::regulated_token { } } + public fun bridge_transfer( + caller: &signer, to: address, amount: u64 + ) acquires TokenMetadataRefs, TokenState { + let caller_addr = signer::address_of(caller); + let state_obj = token_state_object_internal(); + let token_state = &TokenState[object::object_address(&state_obj)]; + + assert_not_paused(token_state); + assert_bridge_minter_or_burner(caller, state_obj); + assert_not_frozen(caller_addr, token_state); + assert_not_frozen(to, token_state); + + primary_fungible_store::transfer_with_ref(&borrow_token_metadata_refs().transfer_ref, caller_addr, to, amount); + + event::emit(BridgeTransfer { caller: caller_addr, to, amount }); + } + /// Bridge-specific function to mint tokens directly as `FungibleAsset`. /// Required because this token has dynamic dispatch enabled /// as minting to pool and calling `fungible_asset::withdraw()` reverts. From 8d5480513858d475283af0f9728884ac7d9699d3 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Mon, 13 Oct 2025 10:26:58 -0400 Subject: [PATCH 02/13] Update bindings --- .../receiver_registry/receiver_registry.go | 42 ++++++++++++++++++- .../burn_mint_token_pool.go | 22 +++++++++- .../lock_release_token_pool.go | 22 +++++++++- .../managed_token_pool/managed_token_pool.go | 22 +++++++++- .../regulated_token_pool.go | 35 ++++++++++++---- .../token_pool/token_pool/token_pool.go | 16 ++++++- .../managed_token/managed_token.go | 13 +++++- .../regulated_token/regulated_token.go | 19 ++++++++- 8 files changed, 177 insertions(+), 14 deletions(-) diff --git a/bindings/ccip/receiver_registry/receiver_registry.go b/bindings/ccip/receiver_registry/receiver_registry.go index e1253ff5..c922b3fe 100644 --- a/bindings/ccip/receiver_registry/receiver_registry.go +++ b/bindings/ccip/receiver_registry/receiver_registry.go @@ -24,6 +24,7 @@ var ( type ReceiverRegistryInterface interface { TypeAndVersion(opts *bind.CallOpts) (string, error) IsRegisteredReceiver(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) + IsExecutingReceiverInProgress(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) // Encoder returns the encoder implementation of this module. Encoder() ReceiverRegistryEncoder @@ -32,10 +33,12 @@ type ReceiverRegistryInterface interface { type ReceiverRegistryEncoder interface { TypeAndVersion() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) IsRegisteredReceiver(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + IsExecutingReceiverInProgress(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) FinishReceive(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + InitializeCCIPReceiveState() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"ccip","module":"receiver_registry","name":"finish_receive","parameters":[{"name":"receiver_address","type":"address"}]}]` +const FunctionInfo = `[{"package":"ccip","module":"receiver_registry","name":"finish_receive","parameters":[{"name":"receiver_address","type":"address"}]},{"package":"ccip","module":"receiver_registry","name":"initialize_ccip_receive_state","parameters":null}]` func NewReceiverRegistry(address aptos.AccountAddress, client aptos.AptosRpcClient) ReceiverRegistryInterface { contract := bind.NewBoundContract(address, "ccip", "receiver_registry", client) @@ -54,6 +57,7 @@ const ( E_NON_EMPTY_INPUT uint64 = 5 E_PROOF_TYPE_ACCOUNT_MISMATCH uint64 = 6 E_PROOF_TYPE_MODULE_MISMATCH uint64 = 7 + E_UNAUTHORIZED uint64 = 8 ) // Structs @@ -65,6 +69,9 @@ type CCIPReceiverRegistration struct { DispatchMetadata bind.StdObject `move:"aptos_framework::object::Object"` } +type CCIPReceiveState struct { +} + type ReceiverRegistered struct { ReceiverAddress aptos.AccountAddress `move:"address"` ReceiverModuleName []byte `move:"vector"` @@ -125,6 +132,27 @@ func (c ReceiverRegistryContract) IsRegisteredReceiver(opts *bind.CallOpts, rece return r0, nil } +func (c ReceiverRegistryContract) IsExecutingReceiverInProgress(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) { + module, function, typeTags, args, err := c.receiverRegistryEncoder.IsExecutingReceiverInProgress(receiverAddress) + if err != nil { + return *new(bool), err + } + + callData, err := c.Call(opts, module, function, typeTags, args) + if err != nil { + return *new(bool), err + } + + var ( + r0 bool + ) + + if err := codec.DecodeAptosJsonArray(callData, &r0); err != nil { + return *new(bool), err + } + return r0, nil +} + // Entry Functions // Encoder @@ -144,6 +172,14 @@ func (c receiverRegistryEncoder) IsRegisteredReceiver(receiverAddress aptos.Acco }) } +func (c receiverRegistryEncoder) IsExecutingReceiverInProgress(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("is_executing_receiver_in_progress", nil, []string{ + "address", + }, []any{ + receiverAddress, + }) +} + func (c receiverRegistryEncoder) FinishReceive(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("finish_receive", nil, []string{ "address", @@ -151,3 +187,7 @@ func (c receiverRegistryEncoder) FinishReceive(receiverAddress aptos.AccountAddr receiverAddress, }) } + +func (c receiverRegistryEncoder) InitializeCCIPReceiveState() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("initialize_ccip_receive_state", nil, []string{}, []any{}) +} diff --git a/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go b/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go index 7b572c9a..45aa274a 100644 --- a/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go +++ b/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go @@ -86,13 +86,15 @@ type BurnMintTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertCanInitialize(callerAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"accept_ownership","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"store_address","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"accept_ownership","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"store_address","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewBurnMintTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) BurnMintTokenPoolInterface { contract := bind.NewBoundContract(address, "burn_mint_token_pool", "burn_mint_token_pool", client) @@ -112,6 +114,7 @@ const ( E_UNKNOWN_FUNCTION uint64 = 6 E_MINT_REF_NOT_SET uint64 = 7 E_BURN_REF_NOT_SET uint64 = 8 + E_NOT_EXECUTING_RECEIVER uint64 = 9 ) // Structs @@ -123,6 +126,9 @@ type BurnMintTokenPoolState struct { StoreSignerAddress aptos.AccountAddress `move:"address"` } +type BurnMintTokenPoolEvents struct { +} + type CallbackProof struct { } @@ -835,6 +841,20 @@ func (c burnMintTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddre }) } +func (c burnMintTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) +} + +func (c burnMintTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("transfer", nil, []string{ + "address", + "u64", + }, []any{ + to, + amount, + }) +} + func (c burnMintTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go b/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go index 073a4a9d..3a795570 100644 --- a/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go +++ b/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go @@ -100,13 +100,15 @@ type LockReleaseTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertCanInitialize(callerAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"accept_ownership","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"provide_liquidity","parameters":[{"name":"amount","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_rebalancer","parameters":[{"name":"rebalancer","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"store_address","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"withdraw_liquidity","parameters":[{"name":"amount","type":"u64"}]}]` +const FunctionInfo = `[{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"accept_ownership","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"provide_liquidity","parameters":[{"name":"amount","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_rebalancer","parameters":[{"name":"rebalancer","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"store_address","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"withdraw_liquidity","parameters":[{"name":"amount","type":"u64"}]}]` func NewLockReleaseTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) LockReleaseTokenPoolInterface { contract := bind.NewBoundContract(address, "lock_release_token_pool", "lock_release_token_pool", client) @@ -128,6 +130,7 @@ const ( E_UNAUTHORIZED uint64 = 8 E_INSUFFICIENT_LIQUIDITY uint64 = 9 E_TRANSFER_REF_NOT_SET uint64 = 10 + E_NOT_EXECUTING_RECEIVER uint64 = 11 ) // Structs @@ -140,6 +143,9 @@ type LockReleaseTokenPoolState struct { Rebalancer aptos.AccountAddress `move:"address"` } +type LockReleaseTokenPoolEvents struct { +} + type CallbackProof struct { } @@ -1003,6 +1009,20 @@ func (c lockReleaseTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAd }) } +func (c lockReleaseTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) +} + +func (c lockReleaseTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("transfer", nil, []string{ + "address", + "u64", + }, []any{ + to, + amount, + }) +} + func (c lockReleaseTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go b/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go index 70628575..740f6291 100644 --- a/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go +++ b/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go @@ -86,12 +86,14 @@ type ManagedTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"managed_token_pool","module":"managed_token_pool","name":"accept_ownership","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"store_address","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"managed_token_pool","module":"managed_token_pool","name":"accept_ownership","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"store_address","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewManagedTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) ManagedTokenPoolInterface { contract := bind.NewBoundContract(address, "managed_token_pool", "managed_token_pool", client) @@ -109,6 +111,7 @@ const ( E_LOCAL_TOKEN_MISMATCH uint64 = 4 E_INVALID_ARGUMENTS uint64 = 5 E_UNKNOWN_FUNCTION uint64 = 6 + E_NOT_EXECUTING_RECEIVER uint64 = 9 ) // Structs @@ -117,6 +120,9 @@ type ManagedTokenPoolState struct { StoreSignerAddress aptos.AccountAddress `move:"address"` } +type ManagedTokenPoolEvents struct { +} + type CallbackProof struct { } @@ -829,6 +835,20 @@ func (c managedTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddres }) } +func (c managedTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) +} + +func (c managedTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("transfer", nil, []string{ + "address", + "u64", + }, []any{ + to, + amount, + }) +} + func (c managedTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go b/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go index 8467f139..cf91b09c 100644 --- a/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go +++ b/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go @@ -86,12 +86,14 @@ type RegulatedTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"regulated_token_pool","module":"regulated_token_pool","name":"accept_ownership","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"store_address","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"regulated_token_pool","module":"regulated_token_pool","name":"accept_ownership","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"store_address","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewRegulatedTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) RegulatedTokenPoolInterface { contract := bind.NewBoundContract(address, "regulated_token_pool", "regulated_token_pool", client) @@ -103,12 +105,14 @@ func NewRegulatedTokenPool(address aptos.AccountAddress, client aptos.AptosRpcCl // Constants const ( - E_NOT_PUBLISHER uint64 = 1 - E_ALREADY_INITIALIZED uint64 = 2 - E_INVALID_FUNGIBLE_ASSET uint64 = 3 - E_LOCAL_TOKEN_MISMATCH uint64 = 4 - E_INVALID_ARGUMENTS uint64 = 5 - E_UNKNOWN_FUNCTION uint64 = 6 + E_NOT_PUBLISHER uint64 = 1 + E_ALREADY_INITIALIZED uint64 = 2 + E_INVALID_FUNGIBLE_ASSET uint64 = 3 + E_LOCAL_TOKEN_MISMATCH uint64 = 4 + E_INVALID_ARGUMENTS uint64 = 5 + E_UNKNOWN_FUNCTION uint64 = 6 + E_NOT_REGISTERED_RECEIVER uint64 = 7 + E_NOT_EXECUTING_RECEIVER uint64 = 9 ) // Structs @@ -117,6 +121,9 @@ type RegulatedTokenPoolState struct { StoreSignerAddress aptos.AccountAddress `move:"address"` } +type RegulatedTokenPoolEvents struct { +} + type CallbackProof struct { } @@ -829,6 +836,20 @@ func (c regulatedTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddr }) } +func (c regulatedTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) +} + +func (c regulatedTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("transfer", nil, []string{ + "address", + "u64", + }, []any{ + to, + amount, + }) +} + func (c regulatedTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go b/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go index edef7121..a22fe013 100644 --- a/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go +++ b/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go @@ -37,9 +37,10 @@ type TokenPoolEncoder interface { CalculateLocalAmount(remoteAmount *big.Int, remoteDecimals byte, localDecimals byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) CalculateLocalAmountInternal(remoteAmount *big.Int, remoteDecimals byte, localDecimals byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) Initialize(localToken aptos.AccountAddress, allowlist []aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + CreateTransferEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"ccip_token_pool","module":"token_pool","name":"initialize","parameters":[{"name":"local_token","type":"address"},{"name":"allowlist","type":"vector\u003caddress\u003e"}]}]` +const FunctionInfo = `[{"package":"ccip_token_pool","module":"token_pool","name":"create_transfer_events","parameters":null},{"package":"ccip_token_pool","module":"token_pool","name":"initialize","parameters":[{"name":"local_token","type":"address"},{"name":"allowlist","type":"vector\u003caddress\u003e"}]}]` func NewTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) TokenPoolInterface { contract := bind.NewBoundContract(address, "ccip_token_pool", "token_pool", client) @@ -71,6 +72,9 @@ type TokenPoolState struct { FaMetadata bind.StdObject `move:"aptos_framework::object::Object"` } +type TokenPoolEvents struct { +} + type RemoteChainConfig struct { RemoteTokenAddress []byte `move:"vector"` RemotePools [][]byte `move:"vector>"` @@ -133,6 +137,12 @@ type RebalancerSet struct { NewRebalancer aptos.AccountAddress `move:"address"` } +type Transfer struct { + From aptos.AccountAddress `move:"address"` + To aptos.AccountAddress `move:"address"` + Amount uint64 `move:"u64"` +} + type TokenPoolContract struct { *bind.BoundContract tokenPoolEncoder @@ -284,3 +294,7 @@ func (c tokenPoolEncoder) Initialize(localToken aptos.AccountAddress, allowlist allowlist, }) } + +func (c tokenPoolEncoder) CreateTransferEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("create_transfer_events", nil, []string{}, []any{}) +} diff --git a/bindings/managed_token/managed_token/managed_token.go b/bindings/managed_token/managed_token/managed_token.go index 2208de88..aef01b7a 100644 --- a/bindings/managed_token/managed_token/managed_token.go +++ b/bindings/managed_token/managed_token/managed_token.go @@ -70,9 +70,10 @@ type ManagedTokenEncoder interface { AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TokenStateAddressInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"managed_token","module":"managed_token","name":"accept_ownership","parameters":null},{"package":"managed_token","module":"managed_token","name":"apply_allowed_burner_updates","parameters":[{"name":"burners_to_remove","type":"vector\u003caddress\u003e"},{"name":"burners_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"apply_allowed_minter_updates","parameters":[{"name":"minters_to_remove","type":"vector\u003caddress\u003e"},{"name":"minters_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token","module":"managed_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"managed_token","module":"managed_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"token_state_address_internal","parameters":null},{"package":"managed_token","module":"managed_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"managed_token","module":"managed_token","name":"accept_ownership","parameters":null},{"package":"managed_token","module":"managed_token","name":"apply_allowed_burner_updates","parameters":[{"name":"burners_to_remove","type":"vector\u003caddress\u003e"},{"name":"burners_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"apply_allowed_minter_updates","parameters":[{"name":"minters_to_remove","type":"vector\u003caddress\u003e"},{"name":"minters_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"bridge_transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token","module":"managed_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"managed_token","module":"managed_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"token_state_address_internal","parameters":null},{"package":"managed_token","module":"managed_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewManagedToken(address aptos.AccountAddress, client aptos.AptosRpcClient) ManagedTokenInterface { contract := bind.NewBoundContract(address, "managed_token", "managed_token", client) @@ -606,3 +607,13 @@ func (c managedTokenEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddress) ( func (c managedTokenEncoder) TokenStateAddressInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("token_state_address_internal", nil, []string{}, []any{}) } + +func (c managedTokenEncoder) BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("bridge_transfer", nil, []string{ + "address", + "u64", + }, []any{ + to, + amount, + }) +} diff --git a/bindings/regulated_token/regulated_token/regulated_token.go b/bindings/regulated_token/regulated_token/regulated_token.go index cc14b559..c41c1915 100644 --- a/bindings/regulated_token/regulated_token/regulated_token.go +++ b/bindings/regulated_token/regulated_token/regulated_token.go @@ -135,6 +135,7 @@ type RegulatedTokenEncoder interface { TokenStateAddressInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TokenMetadataFromStateObj(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TokenMetadataInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertPauser(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertUnpauser(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertFreezer(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) @@ -144,7 +145,7 @@ type RegulatedTokenEncoder interface { AssertBurnerAndGetType(burner aptos.AccountAddress, stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"regulated_token","module":"regulated_token","name":"accept_admin","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"accept_ownership","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"apply_role_updates","parameters":[{"name":"role_number","type":"u8"},{"name":"addresses_to_remove","type":"vector\u003caddress\u003e"},{"name":"addresses_to_add","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_bridge_minter_or_burner","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_burner_and_get_type","parameters":[{"name":"burner","type":"address"},{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_freezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_pauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_recovery_role","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unfreezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unpauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_burn_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_recover_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"burn_frozen_funds","parameters":[{"name":"from","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"grant_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"regulated_token","module":"regulated_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"pause","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"recover_frozen_funds","parameters":[{"name":"from","type":"address"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"recover_tokens","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"revoke_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_from_state_obj","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_address_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_object_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"transfer_admin","parameters":[{"name":"new_admin","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"unpause","parameters":null}]` +const FunctionInfo = `[{"package":"regulated_token","module":"regulated_token","name":"accept_admin","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"accept_ownership","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"apply_role_updates","parameters":[{"name":"role_number","type":"u8"},{"name":"addresses_to_remove","type":"vector\u003caddress\u003e"},{"name":"addresses_to_add","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_bridge_minter_or_burner","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_burner_and_get_type","parameters":[{"name":"burner","type":"address"},{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_freezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_pauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_recovery_role","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unfreezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unpauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_burn_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_recover_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"bridge_transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"burn_frozen_funds","parameters":[{"name":"from","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"grant_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"regulated_token","module":"regulated_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"pause","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"recover_frozen_funds","parameters":[{"name":"from","type":"address"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"recover_tokens","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"revoke_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_from_state_obj","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_address_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_object_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"transfer_admin","parameters":[{"name":"new_admin","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"unpause","parameters":null}]` func NewRegulatedToken(address aptos.AccountAddress, client aptos.AptosRpcClient) RegulatedTokenInterface { contract := bind.NewBoundContract(address, "regulated_token", "regulated_token", client) @@ -229,6 +230,12 @@ type BridgeBurn struct { Amount uint64 `move:"u64"` } +type BridgeTransfer struct { + Caller aptos.AccountAddress `move:"address"` + To aptos.AccountAddress `move:"address"` + Amount uint64 `move:"u64"` +} + type MinterAdded struct { Admin aptos.AccountAddress `move:"address"` Minter aptos.AccountAddress `move:"address"` @@ -1442,6 +1449,16 @@ func (c regulatedTokenEncoder) TokenMetadataInternal() (bind.ModuleInformation, return c.BoundContract.Encode("token_metadata_internal", nil, []string{}, []any{}) } +func (c regulatedTokenEncoder) BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("bridge_transfer", nil, []string{ + "address", + "u64", + }, []any{ + to, + amount, + }) +} + func (c regulatedTokenEncoder) AssertPauser(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("assert_pauser", nil, []string{ "address", From d5d5d697e2cdf0daa64cac418c4c6a3bfbc9a0dc Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Mon, 13 Oct 2025 10:39:57 -0400 Subject: [PATCH 03/13] Fmt --- contracts/regulated_token/sources/regulated_token.move | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contracts/regulated_token/sources/regulated_token.move b/contracts/regulated_token/sources/regulated_token.move index 91fd2c64..6cf31489 100644 --- a/contracts/regulated_token/sources/regulated_token.move +++ b/contracts/regulated_token/sources/regulated_token.move @@ -611,7 +611,12 @@ module regulated_token::regulated_token { assert_not_frozen(caller_addr, token_state); assert_not_frozen(to, token_state); - primary_fungible_store::transfer_with_ref(&borrow_token_metadata_refs().transfer_ref, caller_addr, to, amount); + primary_fungible_store::transfer_with_ref( + &borrow_token_metadata_refs().transfer_ref, + caller_addr, + to, + amount + ); event::emit(BridgeTransfer { caller: caller_addr, to, amount }); } From 11d1792c79472e85d1db3d6fee10a28d8c68dd09 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Sun, 26 Oct 2025 17:17:01 -0400 Subject: [PATCH 04/13] Use Function closures for token pool lock/burn release/mint hooks --- .../receiver_registry/receiver_registry.go | 21 +- .../token_admin_registry.go | 53 ++++- .../burn_mint_token_pool.go | 21 +- .../lock_release_token_pool.go | 21 +- .../managed_token_pool/managed_token_pool.go | 21 +- .../regulated_token_pool.go | 21 +- .../token_pool/token_pool/token_pool.go | 16 +- .../managed_token/managed_token.go | 13 +- .../regulated_token/regulated_token.go | 19 +- .../ccip/sources/receiver_dispatcher.move | 9 + .../ccip/ccip/sources/receiver_registry.move | 88 +++++--- .../ccip/sources/token_admin_dispatcher.move | 51 +++++ .../ccip/sources/token_admin_registry.move | 124 ++++++++++- .../ccip/ccip_offramp/sources/offramp.move | 25 ++- .../mock/burn_mint_dispatchable_receiver.move | 182 --------------- .../lock_release_dispatchable_receiver.move | 182 --------------- ..._receiver.move => mock_ccip_receiver.move} | 59 ++--- .../tests/mock/non_dispatchable_receiver.move | 207 ------------------ .../mock/regulated_dispatchable_receiver.move | 189 ---------------- .../offramp_burn_mint_receiver_test.move | 121 ++-------- .../offramp_lock_release_receiver_test.move | 114 ++-------- .../tests/offramp_managed_receiver_test.move | 112 ++-------- .../offramp_regulated_receiver_test.move | 96 +------- .../sources/burn_mint_token_pool.move | 123 ++++++----- .../sources/lock_release_token_pool.move | 120 +++++----- .../sources/managed_token_pool.move | 138 +++++++----- .../sources/regulated_token_pool.move | 120 +++++----- .../tests/regulated_token_pool_test.move | 41 ---- .../token_pool/sources/token_pool.move | 27 --- .../sources/usdc_token_pool.move | 125 ++++++++++- .../managed_token/sources/managed_token.move | 29 --- .../sources/regulated_token.move | 29 --- 32 files changed, 842 insertions(+), 1675 deletions(-) delete mode 100644 contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move delete mode 100644 contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move rename contracts/ccip/ccip_offramp/tests/mock/{managed_dispatchable_receiver.move => mock_ccip_receiver.move} (83%) delete mode 100644 contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move delete mode 100644 contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move diff --git a/bindings/ccip/receiver_registry/receiver_registry.go b/bindings/ccip/receiver_registry/receiver_registry.go index c922b3fe..fc7ee891 100644 --- a/bindings/ccip/receiver_registry/receiver_registry.go +++ b/bindings/ccip/receiver_registry/receiver_registry.go @@ -24,7 +24,7 @@ var ( type ReceiverRegistryInterface interface { TypeAndVersion(opts *bind.CallOpts) (string, error) IsRegisteredReceiver(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) - IsExecutingReceiverInProgress(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) + IsRegisteredReceiverV2(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) // Encoder returns the encoder implementation of this module. Encoder() ReceiverRegistryEncoder @@ -33,12 +33,11 @@ type ReceiverRegistryInterface interface { type ReceiverRegistryEncoder interface { TypeAndVersion() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) IsRegisteredReceiver(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - IsExecutingReceiverInProgress(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + IsRegisteredReceiverV2(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) FinishReceive(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - InitializeCCIPReceiveState() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"ccip","module":"receiver_registry","name":"finish_receive","parameters":[{"name":"receiver_address","type":"address"}]},{"package":"ccip","module":"receiver_registry","name":"initialize_ccip_receive_state","parameters":null}]` +const FunctionInfo = `[{"package":"ccip","module":"receiver_registry","name":"finish_receive","parameters":[{"name":"receiver_address","type":"address"}]}]` func NewReceiverRegistry(address aptos.AccountAddress, client aptos.AptosRpcClient) ReceiverRegistryInterface { contract := bind.NewBoundContract(address, "ccip", "receiver_registry", client) @@ -69,7 +68,7 @@ type CCIPReceiverRegistration struct { DispatchMetadata bind.StdObject `move:"aptos_framework::object::Object"` } -type CCIPReceiveState struct { +type CCIPReceiverRegistrationV2 struct { } type ReceiverRegistered struct { @@ -132,8 +131,8 @@ func (c ReceiverRegistryContract) IsRegisteredReceiver(opts *bind.CallOpts, rece return r0, nil } -func (c ReceiverRegistryContract) IsExecutingReceiverInProgress(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) { - module, function, typeTags, args, err := c.receiverRegistryEncoder.IsExecutingReceiverInProgress(receiverAddress) +func (c ReceiverRegistryContract) IsRegisteredReceiverV2(opts *bind.CallOpts, receiverAddress aptos.AccountAddress) (bool, error) { + module, function, typeTags, args, err := c.receiverRegistryEncoder.IsRegisteredReceiverV2(receiverAddress) if err != nil { return *new(bool), err } @@ -172,8 +171,8 @@ func (c receiverRegistryEncoder) IsRegisteredReceiver(receiverAddress aptos.Acco }) } -func (c receiverRegistryEncoder) IsExecutingReceiverInProgress(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("is_executing_receiver_in_progress", nil, []string{ +func (c receiverRegistryEncoder) IsRegisteredReceiverV2(receiverAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("is_registered_receiver_v2", nil, []string{ "address", }, []any{ receiverAddress, @@ -187,7 +186,3 @@ func (c receiverRegistryEncoder) FinishReceive(receiverAddress aptos.AccountAddr receiverAddress, }) } - -func (c receiverRegistryEncoder) InitializeCCIPReceiveState() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("initialize_ccip_receive_state", nil, []string{}, []any{}) -} diff --git a/bindings/ccip/token_admin_registry/token_admin_registry.go b/bindings/ccip/token_admin_registry/token_admin_registry.go index 6974b4b1..63455917 100644 --- a/bindings/ccip/token_admin_registry/token_admin_registry.go +++ b/bindings/ccip/token_admin_registry/token_admin_registry.go @@ -26,6 +26,7 @@ type TokenAdminRegistryInterface interface { GetPools(opts *bind.CallOpts, localTokens []aptos.AccountAddress) ([]aptos.AccountAddress, error) GetPool(opts *bind.CallOpts, localToken aptos.AccountAddress) (aptos.AccountAddress, error) GetPoolLocalToken(opts *bind.CallOpts, tokenPoolAddress aptos.AccountAddress) (aptos.AccountAddress, error) + GetPoolLocalTokenV2(opts *bind.CallOpts, tokenPoolAddress aptos.AccountAddress) (aptos.AccountAddress, error) GetTokenConfig(opts *bind.CallOpts, localToken aptos.AccountAddress) (aptos.AccountAddress, aptos.AccountAddress, aptos.AccountAddress, error) GetAllConfiguredTokens(opts *bind.CallOpts, startKey aptos.AccountAddress, maxCount uint64) ([]aptos.AccountAddress, aptos.AccountAddress, bool, error) IsAdministrator(opts *bind.CallOpts, localToken aptos.AccountAddress, administrator aptos.AccountAddress) (bool, error) @@ -45,6 +46,7 @@ type TokenAdminRegistryEncoder interface { GetPools(localTokens []aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetPool(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetPoolLocalToken(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + GetPoolLocalTokenV2(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetTokenConfig(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetAllConfiguredTokens(startKey aptos.AccountAddress, maxCount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) IsAdministrator(localToken aptos.AccountAddress, administrator aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) @@ -53,6 +55,7 @@ type TokenAdminRegistryEncoder interface { ProposeAdministrator(localToken aptos.AccountAddress, administrator aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TransferAdminRole(localToken aptos.AccountAddress, newAdmin aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptAdminRole(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + NewLockOrBurnOutputV1(destTokenAddress []byte, destPoolData []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StartLockOrBurn(tokenPoolAddress aptos.AccountAddress, sender aptos.AccountAddress, remoteChainSelector uint64, receiver []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) FinishLockOrBurn(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) FinishReleaseOrMint(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) @@ -60,7 +63,7 @@ type TokenAdminRegistryEncoder interface { RegisterMCMSEntrypoint() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"ccip","module":"token_admin_registry","name":"accept_admin_role","parameters":[{"name":"local_token","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_release_or_mint","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"propose_administrator","parameters":[{"name":"local_token","type":"address"},{"name":"administrator","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"register_mcms_entrypoint","parameters":null},{"package":"ccip","module":"token_admin_registry","name":"set_pool","parameters":[{"name":"local_token","type":"address"},{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"start_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"},{"name":"sender","type":"address"},{"name":"remote_chain_selector","type":"u64"},{"name":"receiver","type":"vector\u003cu8\u003e"}]},{"package":"ccip","module":"token_admin_registry","name":"transfer_admin_role","parameters":[{"name":"local_token","type":"address"},{"name":"new_admin","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"unregister_pool","parameters":[{"name":"local_token","type":"address"}]}]` +const FunctionInfo = `[{"package":"ccip","module":"token_admin_registry","name":"accept_admin_role","parameters":[{"name":"local_token","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_release_or_mint","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"new_lock_or_burn_output_v1","parameters":[{"name":"dest_token_address","type":"vector\u003cu8\u003e"},{"name":"dest_pool_data","type":"vector\u003cu8\u003e"}]},{"package":"ccip","module":"token_admin_registry","name":"propose_administrator","parameters":[{"name":"local_token","type":"address"},{"name":"administrator","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"register_mcms_entrypoint","parameters":null},{"package":"ccip","module":"token_admin_registry","name":"set_pool","parameters":[{"name":"local_token","type":"address"},{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"start_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"},{"name":"sender","type":"address"},{"name":"remote_chain_selector","type":"u64"},{"name":"receiver","type":"vector\u003cu8\u003e"}]},{"package":"ccip","module":"token_admin_registry","name":"transfer_admin_role","parameters":[{"name":"local_token","type":"address"},{"name":"new_admin","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"unregister_pool","parameters":[{"name":"local_token","type":"address"}]}]` func NewTokenAdminRegistry(address aptos.AccountAddress, client aptos.AptosRpcClient) TokenAdminRegistryInterface { contract := bind.NewBoundContract(address, "ccip", "token_admin_registry", client) @@ -101,6 +104,7 @@ const ( E_ADMIN_NOT_SET_FOR_TOKEN uint64 = 27 E_ADMIN_ALREADY_SET_FOR_TOKEN uint64 = 28 E_ZERO_ADDRESS uint64 = 29 + E_POOL_NOT_REGISTERED uint64 = 30 ) // Structs @@ -150,6 +154,14 @@ type ReleaseOrMintOutputV1 struct { DestinationAmount uint64 `move:"u64"` } +type TokenPoolCallbacks struct { +} + +type TokenPoolConfig struct { + Callbacks TokenPoolCallbacks `move:"TokenPoolCallbacks"` + LocalToken aptos.AccountAddress `move:"address"` +} + type PoolSet struct { LocalToken aptos.AccountAddress `move:"address"` PreviousPoolAddress aptos.AccountAddress `move:"address"` @@ -272,6 +284,27 @@ func (c TokenAdminRegistryContract) GetPoolLocalToken(opts *bind.CallOpts, token return r0, nil } +func (c TokenAdminRegistryContract) GetPoolLocalTokenV2(opts *bind.CallOpts, tokenPoolAddress aptos.AccountAddress) (aptos.AccountAddress, error) { + module, function, typeTags, args, err := c.tokenAdminRegistryEncoder.GetPoolLocalTokenV2(tokenPoolAddress) + if err != nil { + return *new(aptos.AccountAddress), err + } + + callData, err := c.Call(opts, module, function, typeTags, args) + if err != nil { + return *new(aptos.AccountAddress), err + } + + var ( + r0 aptos.AccountAddress + ) + + if err := codec.DecodeAptosJsonArray(callData, &r0); err != nil { + return *new(aptos.AccountAddress), err + } + return r0, nil +} + func (c TokenAdminRegistryContract) GetTokenConfig(opts *bind.CallOpts, localToken aptos.AccountAddress) (aptos.AccountAddress, aptos.AccountAddress, aptos.AccountAddress, error) { module, function, typeTags, args, err := c.tokenAdminRegistryEncoder.GetTokenConfig(localToken) if err != nil { @@ -419,6 +452,14 @@ func (c tokenAdminRegistryEncoder) GetPoolLocalToken(tokenPoolAddress aptos.Acco }) } +func (c tokenAdminRegistryEncoder) GetPoolLocalTokenV2(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("get_pool_local_token_v2", nil, []string{ + "address", + }, []any{ + tokenPoolAddress, + }) +} + func (c tokenAdminRegistryEncoder) GetTokenConfig(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("get_token_config", nil, []string{ "address", @@ -493,6 +534,16 @@ func (c tokenAdminRegistryEncoder) AcceptAdminRole(localToken aptos.AccountAddre }) } +func (c tokenAdminRegistryEncoder) NewLockOrBurnOutputV1(destTokenAddress []byte, destPoolData []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("new_lock_or_burn_output_v1", nil, []string{ + "vector", + "vector", + }, []any{ + destTokenAddress, + destPoolData, + }) +} + func (c tokenAdminRegistryEncoder) StartLockOrBurn(tokenPoolAddress aptos.AccountAddress, sender aptos.AccountAddress, remoteChainSelector uint64, receiver []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("start_lock_or_burn", nil, []string{ "address", diff --git a/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go b/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go index 45aa274a..75051943 100644 --- a/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go +++ b/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go @@ -86,15 +86,13 @@ type BurnMintTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertCanInitialize(callerAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"accept_ownership","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"store_address","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"accept_ownership","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"store_address","parameters":null},{"package":"burn_mint_token_pool","module":"burn_mint_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewBurnMintTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) BurnMintTokenPoolInterface { contract := bind.NewBoundContract(address, "burn_mint_token_pool", "burn_mint_token_pool", client) @@ -126,9 +124,6 @@ type BurnMintTokenPoolState struct { StoreSignerAddress aptos.AccountAddress `move:"address"` } -type BurnMintTokenPoolEvents struct { -} - type CallbackProof struct { } @@ -841,20 +836,6 @@ func (c burnMintTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddre }) } -func (c burnMintTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) -} - -func (c burnMintTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("transfer", nil, []string{ - "address", - "u64", - }, []any{ - to, - amount, - }) -} - func (c burnMintTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go b/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go index 3a795570..69affcf9 100644 --- a/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go +++ b/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go @@ -100,15 +100,13 @@ type LockReleaseTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertCanInitialize(callerAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"accept_ownership","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"provide_liquidity","parameters":[{"name":"amount","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_rebalancer","parameters":[{"name":"rebalancer","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"store_address","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"withdraw_liquidity","parameters":[{"name":"amount","type":"u64"}]}]` +const FunctionInfo = `[{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"accept_ownership","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"assert_can_initialize","parameters":[{"name":"caller_address","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"provide_liquidity","parameters":[{"name":"amount","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"set_rebalancer","parameters":[{"name":"rebalancer","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"store_address","parameters":null},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"lock_release_token_pool","module":"lock_release_token_pool","name":"withdraw_liquidity","parameters":[{"name":"amount","type":"u64"}]}]` func NewLockReleaseTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) LockReleaseTokenPoolInterface { contract := bind.NewBoundContract(address, "lock_release_token_pool", "lock_release_token_pool", client) @@ -143,9 +141,6 @@ type LockReleaseTokenPoolState struct { Rebalancer aptos.AccountAddress `move:"address"` } -type LockReleaseTokenPoolEvents struct { -} - type CallbackProof struct { } @@ -1009,20 +1004,6 @@ func (c lockReleaseTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAd }) } -func (c lockReleaseTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) -} - -func (c lockReleaseTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("transfer", nil, []string{ - "address", - "u64", - }, []any{ - to, - amount, - }) -} - func (c lockReleaseTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go b/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go index 740f6291..50152df1 100644 --- a/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go +++ b/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go @@ -86,14 +86,12 @@ type ManagedTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"managed_token_pool","module":"managed_token_pool","name":"accept_ownership","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"store_address","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"managed_token_pool","module":"managed_token_pool","name":"accept_ownership","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"managed_token_pool","module":"managed_token_pool","name":"store_address","parameters":null},{"package":"managed_token_pool","module":"managed_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewManagedTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) ManagedTokenPoolInterface { contract := bind.NewBoundContract(address, "managed_token_pool", "managed_token_pool", client) @@ -120,9 +118,6 @@ type ManagedTokenPoolState struct { StoreSignerAddress aptos.AccountAddress `move:"address"` } -type ManagedTokenPoolEvents struct { -} - type CallbackProof struct { } @@ -835,20 +830,6 @@ func (c managedTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddres }) } -func (c managedTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) -} - -func (c managedTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("transfer", nil, []string{ - "address", - "u64", - }, []any{ - to, - amount, - }) -} - func (c managedTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go b/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go index cf91b09c..f4c30bc2 100644 --- a/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go +++ b/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go @@ -86,14 +86,12 @@ type RegulatedTokenPoolEncoder interface { TransferOwnership(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) MCMSEntrypoint(Metadata aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) RegisterMCMSEntrypoint(moduleName []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"regulated_token_pool","module":"regulated_token_pool","name":"accept_ownership","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"initialize_token_pool_events","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"store_address","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"regulated_token_pool","module":"regulated_token_pool","name":"accept_ownership","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"add_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_allowlist_updates","parameters":[{"name":"removes","type":"vector\u003caddress\u003e"},{"name":"adds","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"apply_chain_updates","parameters":[{"name":"remote_chain_selectors_to_remove","type":"vector\u003cu64\u003e"},{"name":"remote_chain_selectors_to_add","type":"vector\u003cu64\u003e"},{"name":"remote_pool_addresses_to_add","type":"vector\u003cvector\u003cvector\u003cu8\u003e\u003e\u003e"},{"name":"remote_token_addresses_to_add","type":"vector\u003cvector\u003cu8\u003e\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"register_mcms_entrypoint","parameters":[{"name":"module_name","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"remove_remote_pool","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"remote_pool_address","type":"vector\u003cu8\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_config","parameters":[{"name":"remote_chain_selector","type":"u64"},{"name":"outbound_is_enabled","type":"bool"},{"name":"outbound_capacity","type":"u64"},{"name":"outbound_rate","type":"u64"},{"name":"inbound_is_enabled","type":"bool"},{"name":"inbound_capacity","type":"u64"},{"name":"inbound_rate","type":"u64"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"set_chain_rate_limiter_configs","parameters":[{"name":"remote_chain_selectors","type":"vector\u003cu64\u003e"},{"name":"outbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"outbound_capacities","type":"vector\u003cu64\u003e"},{"name":"outbound_rates","type":"vector\u003cu64\u003e"},{"name":"inbound_is_enableds","type":"vector\u003cbool\u003e"},{"name":"inbound_capacities","type":"vector\u003cu64\u003e"},{"name":"inbound_rates","type":"vector\u003cu64\u003e"}]},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"store_address","parameters":null},{"package":"regulated_token_pool","module":"regulated_token_pool","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewRegulatedTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) RegulatedTokenPoolInterface { contract := bind.NewBoundContract(address, "regulated_token_pool", "regulated_token_pool", client) @@ -121,9 +119,6 @@ type RegulatedTokenPoolState struct { StoreSignerAddress aptos.AccountAddress `move:"address"` } -type RegulatedTokenPoolEvents struct { -} - type CallbackProof struct { } @@ -836,20 +831,6 @@ func (c regulatedTokenPoolEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddr }) } -func (c regulatedTokenPoolEncoder) InitializeTokenPoolEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("initialize_token_pool_events", nil, []string{}, []any{}) -} - -func (c regulatedTokenPoolEncoder) Transfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("transfer", nil, []string{ - "address", - "u64", - }, []any{ - to, - amount, - }) -} - func (c regulatedTokenPoolEncoder) StoreAddress() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("store_address", nil, []string{}, []any{}) } diff --git a/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go b/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go index a22fe013..edef7121 100644 --- a/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go +++ b/bindings/ccip_token_pools/token_pool/token_pool/token_pool.go @@ -37,10 +37,9 @@ type TokenPoolEncoder interface { CalculateLocalAmount(remoteAmount *big.Int, remoteDecimals byte, localDecimals byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) CalculateLocalAmountInternal(remoteAmount *big.Int, remoteDecimals byte, localDecimals byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) Initialize(localToken aptos.AccountAddress, allowlist []aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - CreateTransferEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"ccip_token_pool","module":"token_pool","name":"create_transfer_events","parameters":null},{"package":"ccip_token_pool","module":"token_pool","name":"initialize","parameters":[{"name":"local_token","type":"address"},{"name":"allowlist","type":"vector\u003caddress\u003e"}]}]` +const FunctionInfo = `[{"package":"ccip_token_pool","module":"token_pool","name":"initialize","parameters":[{"name":"local_token","type":"address"},{"name":"allowlist","type":"vector\u003caddress\u003e"}]}]` func NewTokenPool(address aptos.AccountAddress, client aptos.AptosRpcClient) TokenPoolInterface { contract := bind.NewBoundContract(address, "ccip_token_pool", "token_pool", client) @@ -72,9 +71,6 @@ type TokenPoolState struct { FaMetadata bind.StdObject `move:"aptos_framework::object::Object"` } -type TokenPoolEvents struct { -} - type RemoteChainConfig struct { RemoteTokenAddress []byte `move:"vector"` RemotePools [][]byte `move:"vector>"` @@ -137,12 +133,6 @@ type RebalancerSet struct { NewRebalancer aptos.AccountAddress `move:"address"` } -type Transfer struct { - From aptos.AccountAddress `move:"address"` - To aptos.AccountAddress `move:"address"` - Amount uint64 `move:"u64"` -} - type TokenPoolContract struct { *bind.BoundContract tokenPoolEncoder @@ -294,7 +284,3 @@ func (c tokenPoolEncoder) Initialize(localToken aptos.AccountAddress, allowlist allowlist, }) } - -func (c tokenPoolEncoder) CreateTransferEvents() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("create_transfer_events", nil, []string{}, []any{}) -} diff --git a/bindings/managed_token/managed_token/managed_token.go b/bindings/managed_token/managed_token/managed_token.go index aef01b7a..2208de88 100644 --- a/bindings/managed_token/managed_token/managed_token.go +++ b/bindings/managed_token/managed_token/managed_token.go @@ -70,10 +70,9 @@ type ManagedTokenEncoder interface { AcceptOwnership() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) ExecuteOwnershipTransfer(to aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TokenStateAddressInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"managed_token","module":"managed_token","name":"accept_ownership","parameters":null},{"package":"managed_token","module":"managed_token","name":"apply_allowed_burner_updates","parameters":[{"name":"burners_to_remove","type":"vector\u003caddress\u003e"},{"name":"burners_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"apply_allowed_minter_updates","parameters":[{"name":"minters_to_remove","type":"vector\u003caddress\u003e"},{"name":"minters_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"bridge_transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token","module":"managed_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"managed_token","module":"managed_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"token_state_address_internal","parameters":null},{"package":"managed_token","module":"managed_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` +const FunctionInfo = `[{"package":"managed_token","module":"managed_token","name":"accept_ownership","parameters":null},{"package":"managed_token","module":"managed_token","name":"apply_allowed_burner_updates","parameters":[{"name":"burners_to_remove","type":"vector\u003caddress\u003e"},{"name":"burners_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"apply_allowed_minter_updates","parameters":[{"name":"minters_to_remove","type":"vector\u003caddress\u003e"},{"name":"minters_to_add","type":"vector\u003caddress\u003e"}]},{"package":"managed_token","module":"managed_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"managed_token","module":"managed_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"managed_token","module":"managed_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"managed_token","module":"managed_token","name":"token_state_address_internal","parameters":null},{"package":"managed_token","module":"managed_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]}]` func NewManagedToken(address aptos.AccountAddress, client aptos.AptosRpcClient) ManagedTokenInterface { contract := bind.NewBoundContract(address, "managed_token", "managed_token", client) @@ -607,13 +606,3 @@ func (c managedTokenEncoder) ExecuteOwnershipTransfer(to aptos.AccountAddress) ( func (c managedTokenEncoder) TokenStateAddressInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("token_state_address_internal", nil, []string{}, []any{}) } - -func (c managedTokenEncoder) BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("bridge_transfer", nil, []string{ - "address", - "u64", - }, []any{ - to, - amount, - }) -} diff --git a/bindings/regulated_token/regulated_token/regulated_token.go b/bindings/regulated_token/regulated_token/regulated_token.go index c41c1915..cc14b559 100644 --- a/bindings/regulated_token/regulated_token/regulated_token.go +++ b/bindings/regulated_token/regulated_token/regulated_token.go @@ -135,7 +135,6 @@ type RegulatedTokenEncoder interface { TokenStateAddressInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TokenMetadataFromStateObj(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TokenMetadataInternal() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertPauser(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertUnpauser(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AssertFreezer(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) @@ -145,7 +144,7 @@ type RegulatedTokenEncoder interface { AssertBurnerAndGetType(burner aptos.AccountAddress, stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"regulated_token","module":"regulated_token","name":"accept_admin","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"accept_ownership","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"apply_role_updates","parameters":[{"name":"role_number","type":"u8"},{"name":"addresses_to_remove","type":"vector\u003caddress\u003e"},{"name":"addresses_to_add","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_bridge_minter_or_burner","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_burner_and_get_type","parameters":[{"name":"burner","type":"address"},{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_freezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_pauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_recovery_role","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unfreezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unpauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_burn_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_recover_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"bridge_transfer","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"burn_frozen_funds","parameters":[{"name":"from","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"grant_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"regulated_token","module":"regulated_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"pause","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"recover_frozen_funds","parameters":[{"name":"from","type":"address"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"recover_tokens","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"revoke_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_from_state_obj","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_address_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_object_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"transfer_admin","parameters":[{"name":"new_admin","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"unpause","parameters":null}]` +const FunctionInfo = `[{"package":"regulated_token","module":"regulated_token","name":"accept_admin","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"accept_ownership","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"apply_role_updates","parameters":[{"name":"role_number","type":"u8"},{"name":"addresses_to_remove","type":"vector\u003caddress\u003e"},{"name":"addresses_to_add","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_bridge_minter_or_burner","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_burner_and_get_type","parameters":[{"name":"burner","type":"address"},{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_freezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_pauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_recovery_role","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unfreezer","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"assert_unpauser","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_burn_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"batch_recover_frozen_funds","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"burn","parameters":[{"name":"from","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"burn_frozen_funds","parameters":[{"name":"from","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"execute_ownership_transfer","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"freeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"grant_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"initialize","parameters":[{"name":"max_supply","type":"0x1::option::Option\u003cu128\u003e"},{"name":"name","type":"0x1::string::String"},{"name":"symbol","type":"0x1::string::String"},{"name":"decimals","type":"u8"},{"name":"icon","type":"0x1::string::String"},{"name":"project","type":"0x1::string::String"}]},{"package":"regulated_token","module":"regulated_token","name":"mint","parameters":[{"name":"to","type":"address"},{"name":"amount","type":"u64"}]},{"package":"regulated_token","module":"regulated_token","name":"pause","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"recover_frozen_funds","parameters":[{"name":"from","type":"address"},{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"recover_tokens","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"revoke_role","parameters":[{"name":"role_number","type":"u8"},{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_from_state_obj","parameters":[{"name":"state_obj","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"token_metadata_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_address_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"token_state_object_internal","parameters":null},{"package":"regulated_token","module":"regulated_token","name":"transfer_admin","parameters":[{"name":"new_admin","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"transfer_ownership","parameters":[{"name":"to","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_account","parameters":[{"name":"account","type":"address"}]},{"package":"regulated_token","module":"regulated_token","name":"unfreeze_accounts","parameters":[{"name":"accounts","type":"vector\u003caddress\u003e"}]},{"package":"regulated_token","module":"regulated_token","name":"unpause","parameters":null}]` func NewRegulatedToken(address aptos.AccountAddress, client aptos.AptosRpcClient) RegulatedTokenInterface { contract := bind.NewBoundContract(address, "regulated_token", "regulated_token", client) @@ -230,12 +229,6 @@ type BridgeBurn struct { Amount uint64 `move:"u64"` } -type BridgeTransfer struct { - Caller aptos.AccountAddress `move:"address"` - To aptos.AccountAddress `move:"address"` - Amount uint64 `move:"u64"` -} - type MinterAdded struct { Admin aptos.AccountAddress `move:"address"` Minter aptos.AccountAddress `move:"address"` @@ -1449,16 +1442,6 @@ func (c regulatedTokenEncoder) TokenMetadataInternal() (bind.ModuleInformation, return c.BoundContract.Encode("token_metadata_internal", nil, []string{}, []any{}) } -func (c regulatedTokenEncoder) BridgeTransfer(to aptos.AccountAddress, amount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("bridge_transfer", nil, []string{ - "address", - "u64", - }, []any{ - to, - amount, - }) -} - func (c regulatedTokenEncoder) AssertPauser(stateObj aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("assert_pauser", nil, []string{ "address", diff --git a/contracts/ccip/ccip/sources/receiver_dispatcher.move b/contracts/ccip/ccip/sources/receiver_dispatcher.move index ac5b0cb1..8b0eb783 100644 --- a/contracts/ccip/ccip/sources/receiver_dispatcher.move +++ b/contracts/ccip/ccip/sources/receiver_dispatcher.move @@ -16,4 +16,13 @@ module ccip::receiver_dispatcher { dispatchable_fungible_asset::derived_supply(dispatch_metadata); receiver_registry::finish_receive(receiver_address); } + + /// Invoke receiver's callback without token dispatchable hooks + public fun dispatch_receive_v2( + caller: &signer, receiver_address: address, message: client::Any2AptosMessage + ) { + auth::assert_is_allowed_offramp(signer::address_of(caller)); + + receiver_registry::invoke_ccip_receive_v2(receiver_address, message); + } } diff --git a/contracts/ccip/ccip/sources/receiver_registry.move b/contracts/ccip/ccip/sources/receiver_registry.move index 353636ca..87b499d5 100644 --- a/contracts/ccip/ccip/sources/receiver_registry.move +++ b/contracts/ccip/ccip/sources/receiver_registry.move @@ -5,15 +5,13 @@ module ccip::receiver_registry { use std::error; use std::event::{Self, EventHandle}; use std::function_info::{Self, FunctionInfo}; - use std::table::{Self, Table}; use std::type_info::{Self, TypeInfo}; use std::fungible_asset::{Self, Metadata}; - use std::object::{Self, ExtendRef, Object, TransferRef, ObjectCore}; + use std::object::{Self, ExtendRef, Object, TransferRef}; use std::option::{Self, Option}; use std::signer; use std::string::{Self, String}; - use ccip::auth; use ccip::client; use ccip::state_object; @@ -34,8 +32,9 @@ module ccip::receiver_registry { executing_input: Option } - struct CCIPReceiveState has key { - executing_receivers: Table + struct CCIPReceiverRegistrationV2 has key { + callback: |client::Any2AptosMessage| has drop + copy + store, + proof_typeinfo: TypeInfo, } #[event] @@ -72,10 +71,6 @@ module ccip::receiver_registry { }; move_to(&state_object_signer, state); - - move_to( - &state_object_signer, CCIPReceiveState { executing_receivers: table::new() } - ); } public fun register_receiver( @@ -149,16 +144,48 @@ module ccip::receiver_registry { ); } + public fun register_receiver_v2( + receiver_account: &signer, + receiver_module_name: vector, + callback: |client::Any2AptosMessage| has drop + copy + store, + _proof: ProofType + ) acquires ReceiverRegistryState { + let receiver_address = signer::address_of(receiver_account); + assert!( + !exists(receiver_address), + error::invalid_argument(E_ALREADY_REGISTERED) + ); + + let proof_typeinfo = type_info::type_of(); + assert!( + proof_typeinfo.account_address() == receiver_address, + E_PROOF_TYPE_ACCOUNT_MISMATCH + ); + assert!( + proof_typeinfo.module_name() == receiver_module_name, + E_PROOF_TYPE_MODULE_MISMATCH + ); + + move_to( + receiver_account, + CCIPReceiverRegistrationV2 { callback, proof_typeinfo } + ); + + let state = borrow_state_mut(); + event::emit_event( + &mut state.receiver_registered_events, + ReceiverRegistered { receiver_address, receiver_module_name } + ); + } + #[view] public fun is_registered_receiver(receiver_address: address): bool { exists(receiver_address) } #[view] - public fun is_executing_receiver_in_progress( - receiver_address: address - ): bool acquires CCIPReceiveState { - borrow_ccip_receive_state_mut().executing_receivers.contains(receiver_address) + public fun is_registered_receiver_v2(receiver_address: address): bool { + exists(receiver_address) } public fun get_receiver_input( @@ -181,7 +208,7 @@ module ccip::receiver_registry { public(friend) fun start_receive( receiver_address: address, message: client::Any2AptosMessage - ): Object acquires CCIPReceiverRegistration, CCIPReceiveState { + ): Object acquires CCIPReceiverRegistration { let registration = get_registration_mut(receiver_address); assert!( @@ -191,22 +218,32 @@ module ccip::receiver_registry { registration.executing_input.fill(message); - borrow_ccip_receive_state_mut().executing_receivers.add(receiver_address, true); - registration.dispatch_metadata } public(friend) fun finish_receive( receiver_address: address - ) acquires CCIPReceiverRegistration, CCIPReceiveState { + ) acquires CCIPReceiverRegistration { let registration = get_registration_mut(receiver_address); assert!( registration.executing_input.is_none(), error::invalid_state(E_NON_EMPTY_INPUT) ); + } - borrow_ccip_receive_state_mut().executing_receivers.remove(receiver_address); + public(friend) fun invoke_ccip_receive_v2( + receiver_address: address, + message: client::Any2AptosMessage + ) acquires CCIPReceiverRegistrationV2 { + assert!( + exists(receiver_address), + error::invalid_argument(E_UNKNOWN_RECEIVER) + ); + + let registration = borrow_global(receiver_address); + + (registration.callback)(message); } inline fun borrow_state(): &ReceiverRegistryState { @@ -217,10 +254,6 @@ module ccip::receiver_registry { borrow_global_mut(state_object::object_address()) } - inline fun borrow_ccip_receive_state_mut(): &mut CCIPReceiveState { - borrow_global_mut(state_object::object_address()) - } - inline fun get_registration_mut(receiver_address: address): &mut CCIPReceiverRegistration { assert!( exists(receiver_address), @@ -229,17 +262,6 @@ module ccip::receiver_registry { borrow_global_mut(receiver_address) } - // ============================= Migrations ============================= - - public fun initialize_ccip_receive_state(caller: &signer) { - auth::assert_only_owner(signer::address_of(caller)); - - move_to( - &state_object::object_signer(), - CCIPReceiveState { executing_receivers: table::new() } - ); - } - #[test_only] public fun init_module_for_testing(publisher: &signer) { init_module(publisher); diff --git a/contracts/ccip/ccip/sources/token_admin_dispatcher.move b/contracts/ccip/ccip/sources/token_admin_dispatcher.move index 7645fbfe..f0bae95f 100644 --- a/contracts/ccip/ccip/sources/token_admin_dispatcher.move +++ b/contracts/ccip/ccip/sources/token_admin_dispatcher.move @@ -66,4 +66,55 @@ module ccip::token_admin_dispatcher { (fa, destination_amount) } + + // ============================================ + // V2 Closure-Based Dispatch Functions + // ============================================ + + public fun dispatch_lock_or_burn_v2( + caller: &signer, + token_pool_address: address, + fa: FungibleAsset, + sender: address, + remote_chain_selector: u64, + receiver: vector + ): (vector, vector) { + token_admin_registry::lock_or_burn_v2( + caller, + token_pool_address, + fa, + sender, + remote_chain_selector, + receiver + ) + } + + public fun dispatch_release_or_mint_v2( + caller: &signer, + token_pool_address: address, + sender: vector, + receiver: address, + source_amount: u256, + local_token: address, + remote_chain_selector: u64, + source_pool_address: vector, + source_pool_data: vector, + offchain_token_data: vector + ): (FungibleAsset, u64) { + let (fa, destination_amount) = + token_admin_registry::release_or_mint_v2( + caller, + token_pool_address, + sender, + receiver, + source_amount, + local_token, + remote_chain_selector, + source_pool_address, + source_pool_data, + offchain_token_data + ); + + (fa, destination_amount) + } } diff --git a/contracts/ccip/ccip/sources/token_admin_registry.move b/contracts/ccip/ccip/sources/token_admin_registry.move index f890d0e1..77c6aa21 100644 --- a/contracts/ccip/ccip/sources/token_admin_registry.move +++ b/contracts/ccip/ccip/sources/token_admin_registry.move @@ -86,6 +86,22 @@ module ccip::token_admin_registry { struct ReleaseOrMintOutputV1 has store, drop { destination_amount: u64 } + public fun new_lock_or_burn_output_v1( + dest_token_address: vector, + dest_pool_data: vector + ): LockOrBurnOutputV1 { + LockOrBurnOutputV1 { dest_token_address, dest_pool_data } + } + + struct TokenPoolCallbacks has drop, copy, store { + lock_or_burn: |fungible_asset::FungibleAsset, LockOrBurnInputV1| LockOrBurnOutputV1 has drop + copy + store, + release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop + copy + store, + } + + struct TokenPoolConfig has key { + callbacks: TokenPoolCallbacks, + local_token: address + } #[event] struct PoolSet has store, drop { @@ -142,6 +158,7 @@ module ccip::token_admin_registry { const E_ADMIN_NOT_SET_FOR_TOKEN: u64 = 27; const E_ADMIN_ALREADY_SET_FOR_TOKEN: u64 = 28; const E_ZERO_ADDRESS: u64 = 29; + const E_POOL_NOT_REGISTERED: u64 = 30; #[view] public fun type_and_version(): String { @@ -221,6 +238,12 @@ module ccip::token_admin_registry { get_registration(token_pool_address).local_token } + #[view] + /// Returns the local token address for the token pool. + public fun get_pool_local_token_v2(token_pool_address: address): address acquires TokenPoolConfig { + TokenPoolConfig[token_pool_address].local_token + } + #[view] /// returns (token_pool_address, administrator, pending_administrator) public fun get_token_config( @@ -393,6 +416,43 @@ module ccip::token_admin_registry { ); } + public fun register_pool_v2( + token_pool_account: &signer, + token_pool_module_name: vector, + local_token: address, + lock_or_burn: |fungible_asset::FungibleAsset, LockOrBurnInputV1| LockOrBurnOutputV1 has drop + copy + store, + release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop + copy + store, + _proof: ProofType + ) { + let token_pool_address = signer::address_of(token_pool_account); + assert!( + !exists(token_pool_address), + error::invalid_argument(E_ALREADY_REGISTERED) + ); + assert!( + object::object_exists(local_token), + error::invalid_argument(E_INVALID_FUNGIBLE_ASSET) + ); + + let proof_typeinfo = type_info::type_of(); + assert!( + proof_typeinfo.account_address() == token_pool_address, + error::invalid_argument(E_PROOF_NOT_AT_TOKEN_POOL_ADDRESS) + ); + assert!( + proof_typeinfo.module_name() == token_pool_module_name, + error::invalid_argument(E_PROOF_NOT_IN_TOKEN_POOL_MODULE) + ); + + move_to( + token_pool_account, + TokenPoolConfig { + callbacks: TokenPoolCallbacks { lock_or_burn, release_or_mint }, + local_token + } + ); + } + public entry fun unregister_pool( caller: &signer, local_token: address ) acquires TokenAdminRegistryState, TokenPoolRegistration { @@ -439,7 +499,7 @@ module ccip::token_admin_registry { public entry fun set_pool( caller: &signer, local_token: address, token_pool_address: address - ) acquires TokenAdminRegistryState, TokenPoolRegistration { + ) acquires TokenAdminRegistryState, TokenPoolRegistration, TokenPoolConfig { assert!( object::object_exists(local_token), error::invalid_argument(E_INVALID_FUNGIBLE_ASSET) @@ -447,8 +507,16 @@ module ccip::token_admin_registry { let caller_addr = signer::address_of(caller); + let pool_local_token = if (exists(token_pool_address)) { + get_pool_local_token_v2(token_pool_address) + } else if (exists(token_pool_address)) { + get_registration(token_pool_address).local_token + } else { + abort error::invalid_argument(E_POOL_NOT_REGISTERED) + }; + assert!( - get_registration(token_pool_address).local_token == local_token, + pool_local_token == local_token, error::invalid_argument(E_INVALID_TOKEN_FOR_POOL) ); @@ -992,6 +1060,56 @@ module ccip::token_admin_registry { output.destination_amount } + public(friend) fun lock_or_burn_v2( + caller: &signer, + token_pool_address: address, + fa: fungible_asset::FungibleAsset, + sender: address, + remote_chain_selector: u64, + receiver: vector + ): (vector, vector) acquires TokenPoolConfig { + auth::assert_is_allowed_onramp(signer::address_of(caller)); + + let pool_config = &TokenPoolConfig[token_pool_address]; + let input = LockOrBurnInputV1 { + sender, + remote_chain_selector, + receiver + }; + + let output = (pool_config.callbacks.lock_or_burn)(fa, input); + (output.dest_token_address, output.dest_pool_data) + } + + public(friend) fun release_or_mint_v2( + caller: &signer, + token_pool_address: address, + sender: vector, + receiver: address, + source_amount: u256, + local_token: address, + remote_chain_selector: u64, + source_pool_address: vector, + source_pool_data: vector, + offchain_token_data: vector + ): (fungible_asset::FungibleAsset, u64) acquires TokenPoolConfig { + auth::assert_is_allowed_offramp(signer::address_of(caller)); + + let pool_config = &TokenPoolConfig[token_pool_address]; + let input = ReleaseOrMintInputV1 { + sender, + receiver, + source_amount, + local_token, + remote_chain_selector, + source_pool_address, + source_pool_data, + offchain_token_data + }; + + (pool_config.callbacks.release_or_mint)(input) + } + inline fun borrow_state(): &TokenAdminRegistryState { borrow_global(state_object::object_address()) } @@ -1020,7 +1138,7 @@ module ccip::token_admin_registry { public fun mcms_entrypoint( _metadata: Object - ): option::Option acquires TokenAdminRegistryState, TokenPoolRegistration { + ): option::Option acquires TokenAdminRegistryState, TokenPoolRegistration, TokenPoolConfig { let (caller, function, data) = mcms_registry::get_callback_params(@ccip, McmsCallback {}); diff --git a/contracts/ccip/ccip_offramp/sources/offramp.move b/contracts/ccip/ccip_offramp/sources/offramp.move index b902144d..253dc63f 100644 --- a/contracts/ccip/ccip_offramp/sources/offramp.move +++ b/contracts/ccip/ccip_offramp/sources/offramp.move @@ -806,8 +806,14 @@ module ccip_offramp::offramp { // module. // ref: https://github.com/smartcontractkit/chainlink-ccip/blob/875e982e6437dc126710d8224dd7c792a197bea6/chains/evm/contracts/offRamp/OffRamp.sol#L633 - if ((!message.data.is_empty() || message.gas_limit != 0) - && receiver_registry::is_registered_receiver(message.receiver)) { + let is_v1_receiver = receiver_registry::is_registered_receiver(message.receiver); + let is_v2_receiver = + receiver_registry::is_registered_receiver_v2(message.receiver); + + if ((!message.data.is_empty() + || message.gas_limit != 0) + && (is_v1_receiver + || is_v2_receiver)) { let state_signer = account::create_signer_with_capability(&state.state_signer_cap); @@ -825,9 +831,16 @@ module ccip_offramp::offramp { dest_token_amounts ); - receiver_dispatcher::dispatch_receive( - &state_signer, message.receiver, any2aptos_message - ) + // Use V2 dispatch if available, else V1 + if (is_v2_receiver) { + receiver_dispatcher::dispatch_receive_v2( + &state_signer, message.receiver, any2aptos_message + ) + } else { + receiver_dispatcher::dispatch_receive( + &state_signer, message.receiver, any2aptos_message + ) + } }; } @@ -894,7 +907,7 @@ module ccip_offramp::offramp { account::create_signer_with_capability(&state.state_signer_cap); let (fa, local_amount) = - token_admin_dispatcher::dispatch_release_or_mint( + token_admin_dispatcher::dispatch_release_or_mint_v2( &state_signer, token_pool_address, sender, diff --git a/contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move deleted file mode 100644 index 920158c0..00000000 --- a/contracts/ccip/ccip_offramp/tests/mock/burn_mint_dispatchable_receiver.move +++ /dev/null @@ -1,182 +0,0 @@ -#[test_only] -/// The `dispatchable_receiver` is designed to be used with dispatchable tokens -/// with pool type `burn_mint_token_pool`. -module ccip_offramp::burn_mint_dispatchable_receiver { - use std::account; - use std::event; - use std::object::{Self, Object}; - use std::option::{Self, Option}; - use std::string::{Self, String}; - use std::fungible_asset::{Metadata}; - use std::primary_fungible_store; - use std::from_bcs; - use std::signer; - - use ccip::client; - use ccip::receiver_registry; - use burn_mint_token_pool::burn_mint_token_pool; - - #[event] - struct ReceivedMessage has store, drop { - message: String - } - - #[event] - struct ForwardedTokens has store, drop { - final_recipient: address - } - - #[event] - struct ReceivedTokensOnly has store, drop { - token_count: u64 - } - - struct CCIPReceiverState has key { - signer_cap: account::SignerCapability, - received_message_handle: event::EventHandle, - forwarded_tokens_handle: event::EventHandle, - received_tokens_only_handle: event::EventHandle - } - - const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; - const E_UNAUTHORIZED: u64 = 2; - const E_INVALID_TOKEN_ADDRESS: u64 = 3; - const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; - - #[view] - public fun type_and_version(): String { - string::utf8(b"BurnMintDispatchableReceiver 1.6.0") - } - - const MODULE_NAME: vector = b"burn_mint_dispatchable_receiver"; - - fun init_module(publisher: &signer) { - // Create a signer capability for the receiver account - let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); - - // Create a unique handle for each event type - let received_message_handle = - account::new_event_handle(publisher); - let forwarded_tokens_handle = - account::new_event_handle(publisher); - let received_tokens_only_handle = - account::new_event_handle(publisher); - - // Move all state into the single resource struct - move_to( - publisher, - CCIPReceiverState { - signer_cap, - received_message_handle, - forwarded_tokens_handle, - received_tokens_only_handle - } - ); - - receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); - } - - struct CCIPReceiverProof has drop {} - - public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { - /* load state and rebuild a signer for the resource account */ - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let message = - receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); - - let data = client::get_data(&message); - - let dest_token_amounts = client::get_dest_token_amounts(&message); - - if (dest_token_amounts.length() != 0 && data.length() != 0) { - let final_recipient = from_bcs::to_address(data); - - for (i in 0..dest_token_amounts.length()) { - let token_amount_ref = &dest_token_amounts[i]; - let amount = client::get_amount(token_amount_ref); - - // For dispatchable tokens, we need to call into token pool's `transfer` function - burn_mint_token_pool::transfer(&state_signer, final_recipient, amount); - }; - - event::emit(ForwardedTokens { final_recipient }); - event::emit_event( - &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } - ); - - } else if (data.length() != 0) { - - // Convert the vector to a string - let message = string::utf8(data); - - event::emit(ReceivedMessage { message }); - event::emit_event( - &mut state.received_message_handle, ReceivedMessage { message } - ); - - } else if (dest_token_amounts.length() != 0) { - // Tokens only (no forwarding data) - keep them at receiver - // Emit event to prove receiver was called - let token_count = dest_token_amounts.length(); - event::emit(ReceivedTokensOnly { token_count }); - event::emit_event( - &mut state.received_tokens_only_handle, - ReceivedTokensOnly { token_count } - ); - }; - - // Simple abort condition for testing - if (data == b"abort") { - abort 1 - }; - - option::none() - } - - public entry fun withdraw_token( - sender: &signer, recipient: address, token_address: address - ) acquires CCIPReceiverState { - assert!( - exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT - ); - assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); - - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let fa_token = object::address_to_object(token_address); - let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); - - // Check if there are tokens available to withdraw - assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); - - primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); - } - - public fun test_init_module(publisher: &signer) { - init_module(publisher); - } - - public fun get_received_message_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.received_message_handle) - } - - public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.forwarded_tokens_handle) - } - - public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle( - &state.received_tokens_only_handle - ) - } - - public fun received_message_get_message(event: &ReceivedMessage): String { - event.message - } -} diff --git a/contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move deleted file mode 100644 index ff7aff45..00000000 --- a/contracts/ccip/ccip_offramp/tests/mock/lock_release_dispatchable_receiver.move +++ /dev/null @@ -1,182 +0,0 @@ -#[test_only] -/// The `dispatchable_receiver` is designed to be used with dispatchable tokens -/// with pool type `lock_release_token_pool`. -module ccip_offramp::lock_release_dispatchable_receiver { - use std::account; - use std::event; - use std::object::{Self, Object}; - use std::option::{Self, Option}; - use std::string::{Self, String}; - use std::fungible_asset::{Metadata}; - use std::primary_fungible_store; - use std::from_bcs; - use std::signer; - - use ccip::client; - use ccip::receiver_registry; - use lock_release_token_pool::lock_release_token_pool; - - #[event] - struct ReceivedMessage has store, drop { - message: String - } - - #[event] - struct ForwardedTokens has store, drop { - final_recipient: address - } - - #[event] - struct ReceivedTokensOnly has store, drop { - token_count: u64 - } - - struct CCIPReceiverState has key { - signer_cap: account::SignerCapability, - received_message_handle: event::EventHandle, - forwarded_tokens_handle: event::EventHandle, - received_tokens_only_handle: event::EventHandle - } - - const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; - const E_UNAUTHORIZED: u64 = 2; - const E_INVALID_TOKEN_ADDRESS: u64 = 3; - const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; - - #[view] - public fun type_and_version(): String { - string::utf8(b"LockReleaseDispatchableReceiver 1.6.0") - } - - const MODULE_NAME: vector = b"lock_release_dispatchable_receiver"; - - fun init_module(publisher: &signer) { - // Create a signer capability for the receiver account - let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); - - // Create a unique handle for each event type - let received_message_handle = - account::new_event_handle(publisher); - let forwarded_tokens_handle = - account::new_event_handle(publisher); - let received_tokens_only_handle = - account::new_event_handle(publisher); - - // Move all state into the single resource struct - move_to( - publisher, - CCIPReceiverState { - signer_cap, - received_message_handle, - forwarded_tokens_handle, - received_tokens_only_handle - } - ); - - receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); - } - - struct CCIPReceiverProof has drop {} - - public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { - /* load state and rebuild a signer for the resource account */ - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let message = - receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); - - let data = client::get_data(&message); - - let dest_token_amounts = client::get_dest_token_amounts(&message); - - if (dest_token_amounts.length() != 0 && data.length() != 0) { - let final_recipient = from_bcs::to_address(data); - - for (i in 0..dest_token_amounts.length()) { - let token_amount_ref = &dest_token_amounts[i]; - let amount = client::get_amount(token_amount_ref); - - // For dispatchable tokens, we need to call into token pool's `transfer` function - lock_release_token_pool::transfer(&state_signer, final_recipient, amount); - }; - - event::emit(ForwardedTokens { final_recipient }); - event::emit_event( - &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } - ); - - } else if (data.length() != 0) { - - // Convert the vector to a string - let message = string::utf8(data); - - event::emit(ReceivedMessage { message }); - event::emit_event( - &mut state.received_message_handle, ReceivedMessage { message } - ); - - } else if (dest_token_amounts.length() != 0) { - // Tokens only (no forwarding data) - keep them at receiver - // Emit event to prove receiver was called - let token_count = dest_token_amounts.length(); - event::emit(ReceivedTokensOnly { token_count }); - event::emit_event( - &mut state.received_tokens_only_handle, - ReceivedTokensOnly { token_count } - ); - }; - - // Simple abort condition for testing - if (data == b"abort") { - abort 1 - }; - - option::none() - } - - public entry fun withdraw_token( - sender: &signer, recipient: address, token_address: address - ) acquires CCIPReceiverState { - assert!( - exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT - ); - assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); - - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let fa_token = object::address_to_object(token_address); - let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); - - // Check if there are tokens available to withdraw - assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); - - primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); - } - - public fun test_init_module(publisher: &signer) { - init_module(publisher); - } - - public fun get_received_message_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.received_message_handle) - } - - public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.forwarded_tokens_handle) - } - - public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle( - &state.received_tokens_only_handle - ) - } - - public fun received_message_get_message(event: &ReceivedMessage): String { - event.message - } -} diff --git a/contracts/ccip/ccip_offramp/tests/mock/managed_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move similarity index 83% rename from contracts/ccip/ccip_offramp/tests/mock/managed_dispatchable_receiver.move rename to contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move index c94e5313..013e6ee3 100644 --- a/contracts/ccip/ccip_offramp/tests/mock/managed_dispatchable_receiver.move +++ b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move @@ -1,11 +1,10 @@ #[test_only] -/// The `dispatchable_receiver` is designed to be used with dispatchable tokens -/// with pool type `managed_token_pool`. -module ccip_offramp::managed_dispatchable_receiver { +/// Compatible with dispatchable and non-dispatchable tokens +/// When transferring tokens, use `primary_fungible_store::transfer` as this triggers the dispatchable fungible asset hook +module ccip_offramp::mock_ccip_receiver { use std::account; use std::event; - use std::object::{Self, Object}; - use std::option::{Self, Option}; + use std::object::{Self}; use std::string::{Self, String}; use std::fungible_asset::{Metadata}; use std::primary_fungible_store; @@ -14,7 +13,6 @@ module ccip_offramp::managed_dispatchable_receiver { use ccip::client; use ccip::receiver_registry; - use managed_token_pool::managed_token_pool; #[event] struct ReceivedMessage has store, drop { @@ -45,17 +43,10 @@ module ccip_offramp::managed_dispatchable_receiver { #[view] public fun type_and_version(): String { - string::utf8(b"ManagedDispatchableReceiver 1.6.0") + string::utf8(b"MockCCIPReceiver 1.6.0") } - #[view] - public fun get_state_address(): address acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - signer::address_of(&state_signer) - } - - const MODULE_NAME: vector = b"managed_dispatchable_receiver"; + const MODULE_NAME: vector = b"mock_ccip_receiver"; fun init_module(publisher: &signer) { // Create a signer capability for the receiver account @@ -80,19 +71,28 @@ module ccip_offramp::managed_dispatchable_receiver { } ); - receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); + receiver_registry::register_receiver_v2( + publisher, + MODULE_NAME, + |message| ccip_receive(message), + CCIPReceiverProof {} + ); + } + + #[view] + public fun get_state_address(): address acquires CCIPReceiverState { + let state = borrow_global(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + signer::address_of(&state_signer) } struct CCIPReceiverProof has drop {} - public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + public fun ccip_receive(message: client::Any2AptosMessage) acquires CCIPReceiverState { /* load state and rebuild a signer for the resource account */ let state = borrow_global_mut(@ccip_offramp); let state_signer = account::create_signer_with_capability(&state.signer_cap); - let message = - receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); - let data = client::get_data(&message); let dest_token_amounts = client::get_dest_token_amounts(&message); @@ -102,19 +102,27 @@ module ccip_offramp::managed_dispatchable_receiver { for (i in 0..dest_token_amounts.length()) { let token_amount_ref = &dest_token_amounts[i]; + let token_addr = client::get_token(token_amount_ref); let amount = client::get_amount(token_amount_ref); - // For dispatchable tokens, we need to call into token pool's `transfer` function - managed_token_pool::transfer(&state_signer, final_recipient, amount); + // Implement the token transfer logic here + + let fa_token = object::address_to_object(token_addr); + + // Must use primary_fungible_store::transfer as token may be dispatchable + primary_fungible_store::transfer( + &state_signer, + fa_token, + final_recipient, + amount + ); }; event::emit(ForwardedTokens { final_recipient }); event::emit_event( &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } ); - } else if (data.length() != 0) { - // Convert the vector to a string let message = string::utf8(data); @@ -138,8 +146,6 @@ module ccip_offramp::managed_dispatchable_receiver { if (data == b"abort") { abort 1 }; - - option::none() } public entry fun withdraw_token( @@ -156,7 +162,6 @@ module ccip_offramp::managed_dispatchable_receiver { let fa_token = object::address_to_object(token_address); let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); - // Check if there are tokens available to withdraw assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); diff --git a/contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move deleted file mode 100644 index 26ef1632..00000000 --- a/contracts/ccip/ccip_offramp/tests/mock/non_dispatchable_receiver.move +++ /dev/null @@ -1,207 +0,0 @@ -#[test_only] -/// The `non_dispatchable_receiver` module should only be used with non-dispatchable tokens. -module ccip_offramp::non_dispatchable_receiver { - use std::account; - use std::event; - use std::object::{Self, Object}; - use std::option::{Self, Option}; - use std::string::{Self, String}; - use std::fungible_asset::{Self, Metadata}; - use std::primary_fungible_store; - use std::from_bcs; - use std::signer; - - use ccip::client; - use ccip::receiver_registry; - - #[event] - struct ReceivedMessage has store, drop { - message: String - } - - #[event] - struct ForwardedTokens has store, drop { - final_recipient: address - } - - #[event] - struct ReceivedTokensOnly has store, drop { - token_count: u64 - } - - struct CCIPReceiverState has key { - signer_cap: account::SignerCapability, - received_message_handle: event::EventHandle, - forwarded_tokens_handle: event::EventHandle, - received_tokens_only_handle: event::EventHandle - } - - const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; - const E_UNAUTHORIZED: u64 = 2; - const E_INVALID_TOKEN_ADDRESS: u64 = 3; - const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; - - #[view] - public fun type_and_version(): String { - string::utf8(b"NonDispatchableReceiver 1.6.0") - } - - const MODULE_NAME: vector = b"non_dispatchable_receiver"; - - fun init_module(publisher: &signer) { - // Create a signer capability for the receiver account - let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); - - // Create a unique handle for each event type - let received_message_handle = - account::new_event_handle(publisher); - let forwarded_tokens_handle = - account::new_event_handle(publisher); - let received_tokens_only_handle = - account::new_event_handle(publisher); - - // Move all state into the single resource struct - move_to( - publisher, - CCIPReceiverState { - signer_cap, - received_message_handle, - forwarded_tokens_handle, - received_tokens_only_handle - } - ); - - receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); - } - - struct CCIPReceiverProof has drop {} - - public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { - /* load state and rebuild a signer for the resource account */ - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let message = - receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); - - let data = client::get_data(&message); - - let dest_token_amounts = client::get_dest_token_amounts(&message); - - if (dest_token_amounts.length() != 0 && data.length() != 0) { - let final_recipient = from_bcs::to_address(data); - - for (i in 0..dest_token_amounts.length()) { - let token_amount_ref = &dest_token_amounts[i]; - let token_addr = client::get_token(token_amount_ref); - let amount = client::get_amount(token_amount_ref); - - // Implement the token transfer logic here - - let fa_token = object::address_to_object(token_addr); - let fa_store_sender = - primary_fungible_store::ensure_primary_store_exists( - @ccip_offramp, fa_token - ); - let fa_store_receiver = - primary_fungible_store::ensure_primary_store_exists( - final_recipient, fa_token - ); - - // For non-dispatchable tokens, we need to use `fungible_asset::transfer` - fungible_asset::transfer( - &state_signer, - fa_store_sender, - fa_store_receiver, - amount - ); - }; - - event::emit(ForwardedTokens { final_recipient }); - event::emit_event( - &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } - ); - } else if (data.length() != 0) { - // Convert the vector to a string - let message = string::utf8(data); - - event::emit(ReceivedMessage { message }); - event::emit_event( - &mut state.received_message_handle, ReceivedMessage { message } - ); - - } else if (dest_token_amounts.length() != 0) { - // Tokens only (no forwarding data) - keep them at receiver - // Emit event to prove receiver was called - let token_count = dest_token_amounts.length(); - event::emit(ReceivedTokensOnly { token_count }); - event::emit_event( - &mut state.received_tokens_only_handle, - ReceivedTokensOnly { token_count } - ); - }; - - // Simple abort condition for testing - if (data == b"abort") { - abort 1 - }; - - option::none() - } - - public entry fun withdraw_token( - sender: &signer, recipient: address, token_address: address - ) acquires CCIPReceiverState { - assert!( - exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT - ); - assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); - - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let fa_token = object::address_to_object(token_address); - let fa_store_sender = - primary_fungible_store::ensure_primary_store_exists(@ccip_offramp, fa_token); - let fa_store_receiver = - primary_fungible_store::ensure_primary_store_exists(recipient, fa_token); - - let balance = fungible_asset::balance(fa_store_sender); - - // Check if there are tokens available to withdraw - assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); - - // For non-dispatchable tokens, we need to use `fungible_asset::transfer` - fungible_asset::transfer( - &state_signer, - fa_store_sender, - fa_store_receiver, - balance - ); - } - - public fun test_init_module(publisher: &signer) { - init_module(publisher); - } - - public fun get_received_message_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.received_message_handle) - } - - public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.forwarded_tokens_handle) - } - - public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle( - &state.received_tokens_only_handle - ) - } - - public fun received_message_get_message(event: &ReceivedMessage): String { - event.message - } -} diff --git a/contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move deleted file mode 100644 index 34e6eae4..00000000 --- a/contracts/ccip/ccip_offramp/tests/mock/regulated_dispatchable_receiver.move +++ /dev/null @@ -1,189 +0,0 @@ -#[test_only] -/// The `dispatchable_receiver` is designed to be used with dispatchable tokens -/// with pool type `regulated_token_pool`. -module ccip_offramp::regulated_dispatchable_receiver { - use std::account; - use std::event; - use std::object::{Self, Object}; - use std::option::{Self, Option}; - use std::string::{Self, String}; - use std::fungible_asset::{Metadata}; - use std::primary_fungible_store; - use std::from_bcs; - use std::signer; - - use ccip::client; - use ccip::receiver_registry; - use regulated_token_pool::regulated_token_pool; - - #[event] - struct ReceivedMessage has store, drop { - message: String - } - - #[event] - struct ForwardedTokens has store, drop { - final_recipient: address - } - - #[event] - struct ReceivedTokensOnly has store, drop { - token_count: u64 - } - - struct CCIPReceiverState has key { - signer_cap: account::SignerCapability, - received_message_handle: event::EventHandle, - forwarded_tokens_handle: event::EventHandle, - received_tokens_only_handle: event::EventHandle - } - - const E_RESOURCE_NOT_FOUND_ON_ACCOUNT: u64 = 1; - const E_UNAUTHORIZED: u64 = 2; - const E_INVALID_TOKEN_ADDRESS: u64 = 3; - const E_NO_TOKENS_AVAILABLE_TO_WITHDRAW: u64 = 4; - - #[view] - public fun type_and_version(): String { - string::utf8(b"RegulatedDispatchableReceiver 1.6.0") - } - - #[view] - public fun get_state_address(): address acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - signer::address_of(&state_signer) - } - - const MODULE_NAME: vector = b"regulated_dispatchable_receiver"; - - fun init_module(publisher: &signer) { - // Create a signer capability for the receiver account - let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); - - // Create a unique handle for each event type - let received_message_handle = - account::new_event_handle(publisher); - let forwarded_tokens_handle = - account::new_event_handle(publisher); - let received_tokens_only_handle = - account::new_event_handle(publisher); - - // Move all state into the single resource struct - move_to( - publisher, - CCIPReceiverState { - signer_cap, - received_message_handle, - forwarded_tokens_handle, - received_tokens_only_handle - } - ); - - receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); - } - - struct CCIPReceiverProof has drop {} - - public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { - /* load state and rebuild a signer for the resource account */ - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let message = - receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); - - let data = client::get_data(&message); - - let dest_token_amounts = client::get_dest_token_amounts(&message); - - if (dest_token_amounts.length() != 0 && data.length() != 0) { - let final_recipient = from_bcs::to_address(data); - - for (i in 0..dest_token_amounts.length()) { - let token_amount_ref = &dest_token_amounts[i]; - let amount = client::get_amount(token_amount_ref); - - // For dispatchable tokens, we need to call into token pool's `transfer` function - regulated_token_pool::transfer(&state_signer, final_recipient, amount); - }; - - event::emit(ForwardedTokens { final_recipient }); - event::emit_event( - &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } - ); - - } else if (data.length() != 0) { - - // Convert the vector to a string - let message = string::utf8(data); - - event::emit(ReceivedMessage { message }); - event::emit_event( - &mut state.received_message_handle, ReceivedMessage { message } - ); - - } else if (dest_token_amounts.length() != 0) { - // Tokens only (no forwarding data) - keep them at receiver - // Emit event to prove receiver was called - let token_count = dest_token_amounts.length(); - event::emit(ReceivedTokensOnly { token_count }); - event::emit_event( - &mut state.received_tokens_only_handle, - ReceivedTokensOnly { token_count } - ); - }; - - // Simple abort condition for testing - if (data == b"abort") { - abort 1 - }; - - option::none() - } - - public entry fun withdraw_token( - sender: &signer, recipient: address, token_address: address - ) acquires CCIPReceiverState { - assert!( - exists(@ccip_offramp), E_RESOURCE_NOT_FOUND_ON_ACCOUNT - ); - assert!(signer::address_of(sender) == @ccip_offramp, E_UNAUTHORIZED); - - let state = borrow_global_mut(@ccip_offramp); - let state_signer = account::create_signer_with_capability(&state.signer_cap); - - let fa_token = object::address_to_object(token_address); - let balance = primary_fungible_store::balance(@ccip_offramp, fa_token); - - // Check if there are tokens available to withdraw - assert!(balance > 0, E_NO_TOKENS_AVAILABLE_TO_WITHDRAW); - - primary_fungible_store::transfer(&state_signer, fa_token, recipient, balance); - } - - public fun test_init_module(publisher: &signer) { - init_module(publisher); - } - - public fun get_received_message_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.received_message_handle) - } - - public fun get_forwarded_tokens_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle(&state.forwarded_tokens_handle) - } - - public fun get_received_tokens_only_events(): vector acquires CCIPReceiverState { - let state = borrow_global(@ccip_offramp); - event::emitted_events_by_handle( - &state.received_tokens_only_handle - ) - } - - public fun received_message_get_message(event: &ReceivedMessage): String { - event.message - } -} diff --git a/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move index ad5adc9b..d96b0a70 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move @@ -9,11 +9,9 @@ module ccip_offramp::offramp_burn_mint_receiver_test { use std::bcs; use std::timestamp; - use burn_mint_token_pool::burn_mint_token_pool; use ccip_offramp::offramp_test; use ccip_offramp::offramp; - use ccip_offramp::non_dispatchable_receiver; - use ccip_offramp::burn_mint_dispatchable_receiver; + use ccip_offramp::mock_ccip_receiver; use ccip::receiver_registry; const BURN_MINT_TOKEN_POOL: u8 = 0; @@ -23,20 +21,10 @@ module ccip_offramp::offramp_burn_mint_receiver_test { const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; const GAS_LIMIT: u64 = 1000000; - fun setup_non_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { + fun setup_mock_ccip_receiver(owner: &signer, ccip_offramp: &signer) { account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); receiver_registry::init_module_for_testing(owner); - non_dispatchable_receiver::test_init_module(ccip_offramp); - } - - fun setup_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { - account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); - receiver_registry::init_module_for_testing(owner); - burn_mint_dispatchable_receiver::test_init_module(ccip_offramp); + mock_ccip_receiver::test_init_module(ccip_offramp); } struct TestMessage has drop { @@ -156,11 +144,11 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false // token is non-dispatchable ); let token_addr = object::object_address(&token_obj); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let token_amounts = offramp::test_create_any2aptos_token_transfer( @@ -188,8 +176,7 @@ module ccip_offramp::offramp_burn_mint_receiver_test { let receiver_balance = fungible_asset::balance(receiver_store); assert!(receiver_balance == 100000); - let tokens_only_events = - non_dispatchable_receiver::get_received_tokens_only_events(); + let tokens_only_events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(tokens_only_events.length() == 1); } @@ -233,10 +220,10 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false // token is non-dispatchable ); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let test_data = b"Hello from EVM chain!"; let test_message = @@ -250,12 +237,11 @@ module ccip_offramp::offramp_burn_mint_receiver_test { execute_message_and_verify_success(2, test_message, vector[]); - let received_events = non_dispatchable_receiver::get_received_message_events(); + let received_events = mock_ccip_receiver::get_received_message_events(); assert!(received_events.length() == 1); let event = received_events.borrow(0); - let event_message = - non_dispatchable_receiver::received_message_get_message(event); + let event_message = mock_ccip_receiver::received_message_get_message(event); assert!(event_message == string::utf8(test_data)); } @@ -301,11 +287,11 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false // token is non-dispatchable ); let token_addr = object::object_address(&token_obj); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let recipient_addr = signer::address_of(recipient); account::create_account_for_test(recipient_addr); @@ -344,7 +330,7 @@ module ccip_offramp::offramp_burn_mint_receiver_test { let recipient_balance = fungible_asset::balance(recipient_store); assert!(recipient_balance == 200000); - let forwarded_events = non_dispatchable_receiver::get_forwarded_tokens_events(); + let forwarded_events = mock_ccip_receiver::get_forwarded_tokens_events(); assert!(forwarded_events.length() == 1); } @@ -388,11 +374,11 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - true + true // token is dispatchable ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let token_amounts = offramp::test_create_any2aptos_token_transfer( @@ -420,8 +406,7 @@ module ccip_offramp::offramp_burn_mint_receiver_test { let receiver_balance = fungible_asset::balance(receiver_store); assert!(receiver_balance == 100000); - let tokens_only_events = - burn_mint_dispatchable_receiver::get_received_tokens_only_events(); + let tokens_only_events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(tokens_only_events.length() == 1); } @@ -465,10 +450,10 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - true + true // token is dispatchable ); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let test_data = b"Hello from EVM chain!"; let test_message = @@ -482,13 +467,11 @@ module ccip_offramp::offramp_burn_mint_receiver_test { execute_message_and_verify_success(5, test_message, vector[]); - let received_events = - burn_mint_dispatchable_receiver::get_received_message_events(); + let received_events = mock_ccip_receiver::get_received_message_events(); assert!(received_events.length() == 1); let event = received_events.borrow(0); - let event_message = - burn_mint_dispatchable_receiver::received_message_get_message(event); + let event_message = mock_ccip_receiver::received_message_get_message(event); assert!(event_message == string::utf8(test_data)); } @@ -534,11 +517,11 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - true + true // token is dispatchable ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let recipient_addr = signer::address_of(recipient); account::create_account_for_test(recipient_addr); @@ -576,65 +559,7 @@ module ccip_offramp::offramp_burn_mint_receiver_test { let recipient_balance = fungible_asset::balance(recipient_store); assert!(recipient_balance == 200000); - let forwarded_events = - burn_mint_dispatchable_receiver::get_forwarded_tokens_events(); + let forwarded_events = mock_ccip_receiver::get_forwarded_tokens_events(); assert!(forwarded_events.length() == 1); } - - // ================================================================ - // | Tests for execution context enforcement | - // ================================================================ - - #[ - test( - aptos_framework = @aptos_framework, - ccip = @ccip, - ccip_offramp = @ccip_offramp, - owner = @0x100, - burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool, - managed_token_pool = @managed_token_pool, - managed_token = @managed_token, - regulated_token_pool = @regulated_token_pool, - regulated_token = @regulated_token - ), - expected_failure( - abort_code = 327689, location = burn_mint_token_pool::burn_mint_token_pool - ) - ] - fun test_transfer_outside_ccip_receive_fails( - aptos_framework: &signer, - ccip: &signer, - ccip_offramp: &signer, - owner: &signer, - burn_mint_token_pool: &signer, - lock_release_token_pool: &signer, - managed_token_pool: &signer, - managed_token: &signer, - regulated_token_pool: &signer, - regulated_token: &signer - ) { - let (_owner_addr, _token_obj) = - offramp_test::setup( - aptos_framework, - ccip, - ccip_offramp, - owner, - burn_mint_token_pool, - lock_release_token_pool, - managed_token_pool, - managed_token, - regulated_token_pool, - regulated_token, - BURN_MINT_TOKEN_POOL, - BURN_MINT_TOKEN_SEED, - true - ); - - setup_dispatchable_receiver(owner, ccip_offramp); - - // Try to transfer tokens directly without ccip_receive context - // This should fail because is_executing_receiver_in_progress is false - burn_mint_token_pool::transfer(ccip_offramp, @0x999, 100000); - } } diff --git a/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move index fffd44f5..0438d265 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move @@ -9,11 +9,9 @@ module ccip_offramp::offramp_lock_release_receiver_test { use std::bcs; use std::timestamp; - use lock_release_token_pool::lock_release_token_pool; use ccip_offramp::offramp_test; use ccip_offramp::offramp; - use ccip_offramp::non_dispatchable_receiver; - use ccip_offramp::lock_release_dispatchable_receiver; + use ccip_offramp::mock_ccip_receiver; use ccip::receiver_registry; const LOCK_RELEASE_TOKEN_POOL: u8 = 1; @@ -23,20 +21,10 @@ module ccip_offramp::offramp_lock_release_receiver_test { const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; const GAS_LIMIT: u64 = 1000000; - fun setup_non_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { + fun setup_mock_ccip_receiver(owner: &signer, ccip_offramp: &signer) { account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); receiver_registry::init_module_for_testing(owner); - non_dispatchable_receiver::test_init_module(ccip_offramp); - } - - fun setup_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { - account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); - receiver_registry::init_module_for_testing(owner); - lock_release_dispatchable_receiver::test_init_module(ccip_offramp); + mock_ccip_receiver::test_init_module(ccip_offramp); } struct TestMessage has drop { @@ -160,7 +148,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let token_amounts = offramp::test_create_any2aptos_token_transfer( @@ -188,8 +176,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { let receiver_balance = fungible_asset::balance(receiver_store); assert!(receiver_balance == 100000); - let tokens_only_events = - non_dispatchable_receiver::get_received_tokens_only_events(); + let tokens_only_events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(tokens_only_events.length() == 1); } @@ -233,10 +220,10 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - false + false // token is non-dispatchable ); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let test_data = b"Hello from EVM chain!"; let test_message = @@ -250,12 +237,11 @@ module ccip_offramp::offramp_lock_release_receiver_test { execute_message_and_verify_success(2, test_message, vector[]); - let received_events = non_dispatchable_receiver::get_received_message_events(); + let received_events = mock_ccip_receiver::get_received_message_events(); assert!(received_events.length() == 1); let event = received_events.borrow(0); - let event_message = - non_dispatchable_receiver::received_message_get_message(event); + let event_message = mock_ccip_receiver::received_message_get_message(event); assert!(event_message == string::utf8(test_data)); } @@ -305,7 +291,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let recipient_addr = signer::address_of(recipient); account::create_account_for_test(recipient_addr); @@ -344,7 +330,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { let recipient_balance = fungible_asset::balance(recipient_store); assert!(recipient_balance == 200000); - let forwarded_events = non_dispatchable_receiver::get_forwarded_tokens_events(); + let forwarded_events = mock_ccip_receiver::get_forwarded_tokens_events(); assert!(forwarded_events.length() == 1); } @@ -392,7 +378,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let token_amounts = offramp::test_create_any2aptos_token_transfer( @@ -420,8 +406,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { let receiver_balance = fungible_asset::balance(receiver_store); assert!(receiver_balance == 100000); - let tokens_only_events = - lock_release_dispatchable_receiver::get_received_tokens_only_events(); + let tokens_only_events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(tokens_only_events.length() == 1); } @@ -468,7 +453,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { true ); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let test_data = b"Hello from EVM chain!"; let test_message = @@ -482,13 +467,11 @@ module ccip_offramp::offramp_lock_release_receiver_test { execute_message_and_verify_success(5, test_message, vector[]); - let received_events = - lock_release_dispatchable_receiver::get_received_message_events(); + let received_events = mock_ccip_receiver::get_received_message_events(); assert!(received_events.length() == 1); let event = received_events.borrow(0); - let event_message = - lock_release_dispatchable_receiver::received_message_get_message(event); + let event_message = mock_ccip_receiver::received_message_get_message(event); assert!(event_message == string::utf8(test_data)); } @@ -534,11 +517,11 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - true + true // token is dispatchable ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let recipient_addr = signer::address_of(recipient); account::create_account_for_test(recipient_addr); @@ -576,66 +559,7 @@ module ccip_offramp::offramp_lock_release_receiver_test { let recipient_balance = fungible_asset::balance(recipient_store); assert!(recipient_balance == 200000); - let forwarded_events = - lock_release_dispatchable_receiver::get_forwarded_tokens_events(); + let forwarded_events = mock_ccip_receiver::get_forwarded_tokens_events(); assert!(forwarded_events.length() == 1); } - - // ================================================================ - // | Tests for execution context enforcement | - // ================================================================ - - #[ - test( - aptos_framework = @aptos_framework, - ccip = @ccip, - ccip_offramp = @ccip_offramp, - owner = @0x100, - burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool, - managed_token_pool = @managed_token_pool, - managed_token = @managed_token, - regulated_token_pool = @regulated_token_pool, - regulated_token = @regulated_token - ), - expected_failure( - abort_code = 327691, - location = lock_release_token_pool::lock_release_token_pool - ) - ] - fun test_transfer_outside_ccip_receive_fails( - aptos_framework: &signer, - ccip: &signer, - ccip_offramp: &signer, - owner: &signer, - burn_mint_token_pool: &signer, - lock_release_token_pool: &signer, - managed_token_pool: &signer, - managed_token: &signer, - regulated_token_pool: &signer, - regulated_token: &signer - ) { - let (_owner_addr, _token_obj) = - offramp_test::setup( - aptos_framework, - ccip, - ccip_offramp, - owner, - burn_mint_token_pool, - lock_release_token_pool, - managed_token_pool, - managed_token, - regulated_token_pool, - regulated_token, - LOCK_RELEASE_TOKEN_POOL, - LOCK_RELEASE_TOKEN_SEED, - true - ); - - setup_dispatchable_receiver(owner, ccip_offramp); - - // Try to transfer tokens directly without ccip_receive context - // This should fail because is_executing_receiver_in_progress is false - lock_release_token_pool::transfer(ccip_offramp, @0x999, 100000); - } } diff --git a/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move index b9889317..58bf1305 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move @@ -11,8 +11,7 @@ module ccip_offramp::offramp_managed_receiver_test { use ccip_offramp::offramp_test; use ccip_offramp::offramp; - use ccip_offramp::non_dispatchable_receiver; - use ccip_offramp::managed_dispatchable_receiver; + use ccip_offramp::mock_ccip_receiver; use ccip::receiver_registry; use managed_token::managed_token; use managed_token_pool::managed_token_pool; @@ -25,20 +24,10 @@ module ccip_offramp::offramp_managed_receiver_test { const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; const GAS_LIMIT: u64 = 1000000; - fun setup_non_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { + fun setup_mock_ccip_receiver(owner: &signer, ccip_offramp: &signer) { account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); receiver_registry::init_module_for_testing(owner); - non_dispatchable_receiver::test_init_module(ccip_offramp); - } - - fun setup_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { - account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); - receiver_registry::init_module_for_testing(owner); - managed_dispatchable_receiver::test_init_module(ccip_offramp); + mock_ccip_receiver::test_init_module(ccip_offramp); } struct TestMessage has drop { @@ -164,7 +153,7 @@ module ccip_offramp::offramp_managed_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); // Add to allowlist for release_or_mint let pool_address = managed_token_pool::get_store_address(); @@ -209,8 +198,7 @@ module ccip_offramp::offramp_managed_receiver_test { let receiver_balance = fungible_asset::balance(receiver_store); assert!(receiver_balance == 100000); - let tokens_only_events = - non_dispatchable_receiver::get_received_tokens_only_events(); + let tokens_only_events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(tokens_only_events.length() == 1); } @@ -257,7 +245,7 @@ module ccip_offramp::offramp_managed_receiver_test { false ); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); // Add to allowlist for lock_or_burn/release_or_mint let pool_address = managed_token_pool::get_store_address(); @@ -280,12 +268,11 @@ module ccip_offramp::offramp_managed_receiver_test { execute_message_and_verify_success(2, test_message, vector[]); - let received_events = non_dispatchable_receiver::get_received_message_events(); + let received_events = mock_ccip_receiver::get_received_message_events(); assert!(received_events.length() == 1); let event = received_events.borrow(0); - let event_message = - non_dispatchable_receiver::received_message_get_message(event); + let event_message = mock_ccip_receiver::received_message_get_message(event); assert!(event_message == string::utf8(test_data)); } @@ -335,7 +322,7 @@ module ccip_offramp::offramp_managed_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_non_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); // Add to allowlist for lock_or_burn/release_or_mint let pool_address = managed_token_pool::get_store_address(); @@ -383,7 +370,7 @@ module ccip_offramp::offramp_managed_receiver_test { let recipient_balance = fungible_asset::balance(recipient_store); assert!(recipient_balance == 200000); - let forwarded_events = non_dispatchable_receiver::get_forwarded_tokens_events(); + let forwarded_events = mock_ccip_receiver::get_forwarded_tokens_events(); assert!(forwarded_events.length() == 1); } @@ -431,7 +418,7 @@ module ccip_offramp::offramp_managed_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); // Add to allowlist for lock_or_burn/release_or_mint let pool_address = managed_token_pool::get_store_address(); @@ -443,7 +430,7 @@ module ccip_offramp::offramp_managed_receiver_test { ); // Grant receiver state signer minter and burner role for transfer during forwarding - let state_address = managed_dispatchable_receiver::get_state_address(); + let state_address = mock_ccip_receiver::get_state_address(); managed_token::apply_allowed_minter_updates( owner, vector[], vector[state_address] ); @@ -477,8 +464,7 @@ module ccip_offramp::offramp_managed_receiver_test { let receiver_balance = fungible_asset::balance(receiver_store); assert!(receiver_balance == 100000); - let tokens_only_events = - managed_dispatchable_receiver::get_received_tokens_only_events(); + let tokens_only_events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(tokens_only_events.length() == 1); } @@ -525,7 +511,7 @@ module ccip_offramp::offramp_managed_receiver_test { true ); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let test_data = b"Hello from EVM chain!"; let test_message = @@ -539,13 +525,11 @@ module ccip_offramp::offramp_managed_receiver_test { execute_message_and_verify_success(5, test_message, vector[]); - let received_events = - managed_dispatchable_receiver::get_received_message_events(); + let received_events = mock_ccip_receiver::get_received_message_events(); assert!(received_events.length() == 1); let event = received_events.borrow(0); - let event_message = - managed_dispatchable_receiver::received_message_get_message(event); + let event_message = mock_ccip_receiver::received_message_get_message(event); assert!(event_message == string::utf8(test_data)); } @@ -595,7 +579,7 @@ module ccip_offramp::offramp_managed_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); // Add to allowlist for lock_or_burn/release_or_mint let pool_address = managed_token_pool::get_store_address(); @@ -607,7 +591,7 @@ module ccip_offramp::offramp_managed_receiver_test { ); // Grant receiver state signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for transfer during forwarding - let state_address = managed_dispatchable_receiver::get_state_address(); + let state_address = mock_ccip_receiver::get_state_address(); managed_token::apply_allowed_minter_updates( owner, vector[], vector[state_address] ); @@ -651,65 +635,7 @@ module ccip_offramp::offramp_managed_receiver_test { let recipient_balance = fungible_asset::balance(recipient_store); assert!(recipient_balance == 200000); - let forwarded_events = - managed_dispatchable_receiver::get_forwarded_tokens_events(); + let forwarded_events = mock_ccip_receiver::get_forwarded_tokens_events(); assert!(forwarded_events.length() == 1); } - - // ================================================================ - // | Tests for execution context enforcement | - // ================================================================ - - #[ - test( - aptos_framework = @aptos_framework, - ccip = @ccip, - ccip_offramp = @ccip_offramp, - owner = @admin, - burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool, - managed_token_pool = @managed_token_pool, - managed_token = @managed_token, - regulated_token_pool = @regulated_token_pool, - regulated_token = @regulated_token - ), - expected_failure( - abort_code = 327689, location = managed_token_pool::managed_token_pool - ) - ] - fun test_transfer_outside_ccip_receive_fails( - aptos_framework: &signer, - ccip: &signer, - ccip_offramp: &signer, - owner: &signer, - burn_mint_token_pool: &signer, - lock_release_token_pool: &signer, - managed_token_pool: &signer, - managed_token: &signer, - regulated_token_pool: &signer, - regulated_token: &signer - ) { - let (_owner_addr, _token_obj) = - offramp_test::setup( - aptos_framework, - ccip, - ccip_offramp, - owner, - burn_mint_token_pool, - lock_release_token_pool, - managed_token_pool, - managed_token, - regulated_token_pool, - regulated_token, - MANAGED_TOKEN_POOL, - MANAGED_TOKEN_SEED, - false - ); - - setup_non_dispatchable_receiver(owner, ccip_offramp); - - // Try to transfer tokens directly without ccip_receive context - // This should fail because is_executing_receiver_in_progress is false - managed_token_pool::transfer(ccip_offramp, @0x999, 100000); - } } diff --git a/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move index 20a6a7d8..6f6f4fa7 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move @@ -10,8 +10,7 @@ module ccip_offramp::offramp_regulated_receiver_test { use ccip_offramp::offramp_test; use ccip_offramp::offramp; - use ccip_offramp::non_dispatchable_receiver; - use ccip_offramp::regulated_dispatchable_receiver; + use ccip_offramp::mock_ccip_receiver; use ccip::receiver_registry; use regulated_token::regulated_token; use regulated_token_pool::regulated_token_pool; @@ -24,20 +23,10 @@ module ccip_offramp::offramp_regulated_receiver_test { const EVM_SENDER: vector = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; const GAS_LIMIT: u64 = 1000000; - fun setup_non_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { + fun setup_mock_ccip_receiver(owner: &signer, ccip_offramp: &signer) { account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); receiver_registry::init_module_for_testing(owner); - non_dispatchable_receiver::test_init_module(ccip_offramp); - } - - fun setup_dispatchable_receiver( - owner: &signer, ccip_offramp: &signer - ) { - account::create_account_if_does_not_exist(signer::address_of(ccip_offramp)); - receiver_registry::init_module_for_testing(owner); - regulated_dispatchable_receiver::test_init_module(ccip_offramp); + mock_ccip_receiver::test_init_module(ccip_offramp); } struct TestMessage has drop { @@ -165,14 +154,14 @@ module ccip_offramp::offramp_regulated_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); // Grant pool signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for release_or_mint let pool_address = regulated_token_pool::get_store_address(); regulated_token::grant_role(owner, 6, pool_address); // Grant receiver state signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for transfer during forwarding - let state_signer_address = regulated_dispatchable_receiver::get_state_address(); + let state_signer_address = mock_ccip_receiver::get_state_address(); regulated_token::grant_role(owner, 6, state_signer_address); let token_amounts = @@ -201,8 +190,7 @@ module ccip_offramp::offramp_regulated_receiver_test { let receiver_balance = fungible_asset::balance(receiver_store); assert!(receiver_balance == 100000); - let tokens_only_events = - regulated_dispatchable_receiver::get_received_tokens_only_events(); + let tokens_only_events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(tokens_only_events.length() == 1); } @@ -249,7 +237,7 @@ module ccip_offramp::offramp_regulated_receiver_test { true ); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); let test_data = b"Hello from EVM chain!"; let test_message = @@ -263,13 +251,11 @@ module ccip_offramp::offramp_regulated_receiver_test { execute_message_and_verify_success(5, test_message, vector[]); - let received_events = - regulated_dispatchable_receiver::get_received_message_events(); + let received_events = mock_ccip_receiver::get_received_message_events(); assert!(received_events.length() == 1); let event = received_events.borrow(0); - let event_message = - regulated_dispatchable_receiver::received_message_get_message(event); + let event_message = mock_ccip_receiver::received_message_get_message(event); assert!(event_message == std::string::utf8(test_data)); } @@ -319,14 +305,14 @@ module ccip_offramp::offramp_regulated_receiver_test { ); let token_addr = object::object_address(&token_obj); - setup_dispatchable_receiver(owner, ccip_offramp); + setup_mock_ccip_receiver(owner, ccip_offramp); // Grant pool signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for release_or_mint let pool_address = regulated_token_pool::get_store_address(); regulated_token::grant_role(owner, 6, pool_address); // Grant receiver state signer BRIDGE_MINTER_OR_BURNER_ROLE (role 6) for transfer during forwarding - let state_signer_address = regulated_dispatchable_receiver::get_state_address(); + let state_signer_address = mock_ccip_receiver::get_state_address(); regulated_token::grant_role(owner, 6, state_signer_address); let recipient_addr = signer::address_of(recipient); @@ -365,65 +351,7 @@ module ccip_offramp::offramp_regulated_receiver_test { let recipient_balance = fungible_asset::balance(recipient_store); assert!(recipient_balance == 200000); - let forwarded_events = - regulated_dispatchable_receiver::get_forwarded_tokens_events(); + let forwarded_events = mock_ccip_receiver::get_forwarded_tokens_events(); assert!(forwarded_events.length() == 1); } - - // ================================================================ - // | Tests for execution context enforcement | - // ================================================================ - - #[ - test( - aptos_framework = @aptos_framework, - ccip = @ccip, - ccip_offramp = @ccip_offramp, - owner = @admin, - burn_mint_token_pool = @burn_mint_token_pool, - lock_release_token_pool = @lock_release_token_pool, - managed_token_pool = @managed_token_pool, - managed_token = @managed_token, - regulated_token_pool = @regulated_token_pool, - regulated_token = @regulated_token - ), - expected_failure( - abort_code = 327689, location = regulated_token_pool::regulated_token_pool - ) - ] - fun test_transfer_outside_ccip_receive_fails( - aptos_framework: &signer, - ccip: &signer, - ccip_offramp: &signer, - owner: &signer, - burn_mint_token_pool: &signer, - lock_release_token_pool: &signer, - managed_token_pool: &signer, - managed_token: &signer, - regulated_token_pool: &signer, - regulated_token: &signer - ) { - let (_owner_addr, _token_obj) = - offramp_test::setup( - aptos_framework, - ccip, - ccip_offramp, - owner, - burn_mint_token_pool, - lock_release_token_pool, - managed_token_pool, - managed_token, - regulated_token_pool, - regulated_token, - REGULATED_TOKEN_POOL, - REGULATED_TOKEN_SEED, - true - ); - - setup_dispatchable_receiver(owner, ccip_offramp); - - // Try to transfer tokens directly without ccip_receive context - // This should fail because is_executing_receiver_in_progress is false - regulated_token_pool::transfer(ccip_offramp, @0x999, 100000); - } } diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move index 17bd6c8b..023fa3b4 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move @@ -9,8 +9,7 @@ module burn_mint_token_pool::burn_mint_token_pool { use std::string::{Self, String}; use aptos_framework::fungible_asset::{BurnRef, MintRef}; - use ccip::token_admin_registry; - use ccip::receiver_registry; + use ccip::token_admin_registry::{Self, ReleaseOrMintInputV1}; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -35,10 +34,6 @@ module burn_mint_token_pool::burn_mint_token_pool { mint_ref: Option } - struct BurnMintTokenPoolEvents has key, store { - token_pool_events: token_pool::TokenPoolEvents - } - const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; @@ -79,6 +74,7 @@ module burn_mint_token_pool::burn_mint_token_pool { register_mcms_entrypoint(publisher, token_pool_module_name); }; + // Register V1 pool (for backward compatibility) token_admin_registry::register_pool( publisher, token_pool_module_name, @@ -86,6 +82,18 @@ module burn_mint_token_pool::burn_mint_token_pool { CallbackProof {} ); + let lock_or_burn_closure = |fa, input| lock_or_burn_v2(fa, input); + let release_or_mint_closure = |input| release_or_mint_v2(input); + + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + @burn_mint_local_token, + lock_or_burn_closure, + release_or_mint_closure, + CallbackProof {} + ); + // create a resource account to be the owner of the primary FungibleStore we will use. let (store_signer, store_signer_cap) = account::create_resource_account(publisher, STORE_OBJECT_SEED); @@ -145,32 +153,6 @@ module burn_mint_token_pool::burn_mint_token_pool { }; move_to(&store_signer, pool); - - move_to( - &store_signer, - BurnMintTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(&store_signer) - } - ); - } - - public fun initialize_token_pool_events(caller: &signer) acquires BurnMintTokenPoolState { - assert_can_initialize(signer::address_of(caller)); - - let store_signer = - &account::create_signer_with_capability(&borrow_pool().store_signer_cap); - - assert!( - !exists(signer::address_of(store_signer)), - error::already_exists(E_ALREADY_INITIALIZED) - ); - - move_to( - store_signer, - BurnMintTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(store_signer) - } - ); } // ================================================================ @@ -387,30 +369,69 @@ module burn_mint_token_pool::burn_mint_token_pool { fa } - /// Caller must be the receiver contract address when `ccip_receive` is called. - /// Transfer the fungible asset from the receiver to `to` address. - public fun transfer( - receiver: &signer, to: address, amount: u64 - ) acquires BurnMintTokenPoolState, BurnMintTokenPoolEvents { - let receiver_addr = signer::address_of(receiver); - assert!( - receiver_registry::is_executing_receiver_in_progress(receiver_addr), - error::permission_denied(E_NOT_EXECUTING_RECEIVER) + public fun lock_or_burn_v2( + fa: FungibleAsset, input: token_admin_registry::LockOrBurnInputV1 + ): token_admin_registry::LockOrBurnOutputV1 acquires BurnMintTokenPoolState { + let pool = borrow_pool_mut(); + let fa_amount = fungible_asset::amount(&fa); + + // Validate the operation (same as V1) + let dest_token_address = + token_pool::validate_lock_or_burn( + &mut pool.token_pool_state, + &fa, + &input, + fa_amount + ); + + // Burn the token + assert!(pool.burn_ref.is_some(), E_BURN_REF_NOT_SET); + fungible_asset::burn(pool.burn_ref.borrow(), fa); + + // Emit event + let remote_chain_selector = + token_admin_registry::get_lock_or_burn_remote_chain_selector(&input); + + token_pool::emit_locked_or_burned( + &mut pool.token_pool_state, fa_amount, remote_chain_selector ); + // Return output directly (no need to set in registry!) + token_admin_registry::new_lock_or_burn_output_v1( + dest_token_address, + b"" // empty dest_pool_data for burn/mint pools + ) + } + + /// V2 release_or_mint callback - receives input directly as parameter + public fun release_or_mint_v2( + input: ReleaseOrMintInputV1 + ): (FungibleAsset, u64) acquires BurnMintTokenPoolState { let pool = borrow_pool_mut(); - assert!(pool.burn_ref.is_some(), E_BURN_REF_NOT_SET); + let local_amount = + token_pool::calculate_release_or_mint_amount(&pool.token_pool_state, &input); + + // Validate the operation (same as V1) + token_pool::validate_release_or_mint( + &mut pool.token_pool_state, &input, local_amount + ); + + // Mint the amount for release assert!(pool.mint_ref.is_some(), E_MINT_REF_NOT_SET); + let fa = fungible_asset::mint(pool.mint_ref.borrow(), local_amount); - primary_fungible_store::burn(pool.burn_ref.borrow(), receiver_addr, amount); - primary_fungible_store::mint(pool.mint_ref.borrow(), to, amount); + let recipient = token_admin_registry::get_release_or_mint_receiver(&input); + let remote_chain_selector = + token_admin_registry::get_release_or_mint_remote_chain_selector(&input); - token_pool::emit_transfer( - &mut borrow_mut_events().token_pool_events, - receiver_addr, - to, - amount + token_pool::emit_released_or_minted( + &mut pool.token_pool_state, + recipient, + local_amount, + remote_chain_selector ); + + (fa, local_amount) } // ================================================================ @@ -535,10 +556,6 @@ module burn_mint_token_pool::burn_mint_token_pool { borrow_global_mut(store_address()) } - inline fun borrow_mut_events(): &mut BurnMintTokenPoolEvents { - borrow_global_mut(store_address()) - } - // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move index 374e9225..b2843b60 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move @@ -9,7 +9,6 @@ module lock_release_token_pool::lock_release_token_pool { use std::signer; use std::string::{Self, String}; - use ccip::receiver_registry; use ccip::token_admin_registry; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; @@ -35,10 +34,6 @@ module lock_release_token_pool::lock_release_token_pool { rebalancer: address } - struct LockReleaseTokenPoolEvents has key, store { - token_pool_events: token_pool::TokenPoolEvents - } - const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; @@ -81,6 +76,7 @@ module lock_release_token_pool::lock_release_token_pool { register_mcms_entrypoint(publisher, token_pool_module_name); }; + // Register V1 pool (for backward compatibility) token_admin_registry::register_pool( publisher, token_pool_module_name, @@ -88,6 +84,18 @@ module lock_release_token_pool::lock_release_token_pool { CallbackProof {} ); + let lock_or_burn_closure = |fa, input| lock_or_burn_v2(fa, input); + let release_or_mint_closure = |input| release_or_mint_v2(input); + + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + @lock_release_local_token, + lock_or_burn_closure, + release_or_mint_closure, + CallbackProof {} + ); + // create a resource account to be the owner of the primary FungibleStore we will use. let (store_signer, store_signer_cap) = account::create_resource_account(publisher, STORE_OBJECT_SEED); @@ -162,32 +170,6 @@ module lock_release_token_pool::lock_release_token_pool { rebalancer }; move_to(&store_signer, pool); - - move_to( - &store_signer, - LockReleaseTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(&store_signer) - } - ); - } - - public fun initialize_token_pool_events(caller: &signer) acquires LockReleaseTokenPoolState { - assert_can_initialize(signer::address_of(caller)); - - let store_signer = - &account::create_signer_with_capability(&borrow_pool().store_signer_cap); - - assert!( - !exists(signer::address_of(store_signer)), - error::already_exists(E_ALREADY_INITIALIZED) - ); - - move_to( - store_signer, - LockReleaseTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(store_signer) - } - ); } // ================================================================ @@ -455,33 +437,65 @@ module lock_release_token_pool::lock_release_token_pool { fa } - /// Caller must be the receiver contract address when `ccip_receive` is called. - /// Transfer the fungible asset from the receiver to `to` address. - public fun transfer( - caller: &signer, to: address, amount: u64 - ) acquires LockReleaseTokenPoolState, LockReleaseTokenPoolEvents { - let caller_addr = signer::address_of(caller); - assert!( - receiver_registry::is_executing_receiver_in_progress(caller_addr), - error::permission_denied(E_NOT_EXECUTING_RECEIVER) + public fun lock_or_burn_v2( + fa: FungibleAsset, input: token_admin_registry::LockOrBurnInputV1 + ): token_admin_registry::LockOrBurnOutputV1 acquires LockReleaseTokenPoolState { + let pool = borrow_pool_mut(); + let fa_amount = fungible_asset::amount(&fa); + + let dest_token_address = + token_pool::validate_lock_or_burn( + &mut pool.token_pool_state, + &fa, + &input, + fa_amount + ); + + // Lock the funds in the pool + primary_fungible_store::deposit(pool.store_signer_address, fa); + + let remote_chain_selector = + token_admin_registry::get_lock_or_burn_remote_chain_selector(&input); + + token_pool::emit_locked_or_burned( + &mut pool.token_pool_state, fa_amount, remote_chain_selector ); + token_admin_registry::new_lock_or_burn_output_v1( + dest_token_address, + token_pool::encode_local_decimals(&pool.token_pool_state) + ) + } + + public fun release_or_mint_v2( + input: token_admin_registry::ReleaseOrMintInputV1 + ): (FungibleAsset, u64) acquires LockReleaseTokenPoolState { let pool = borrow_pool_mut(); - assert!(pool.transfer_ref.is_some(), E_TRANSFER_REF_NOT_SET); + let local_amount = + token_pool::calculate_release_or_mint_amount(&pool.token_pool_state, &input); - primary_fungible_store::transfer_with_ref( - pool.transfer_ref.borrow(), - caller_addr, - to, - amount + token_pool::validate_release_or_mint( + &mut pool.token_pool_state, &input, local_amount ); - token_pool::emit_transfer( - &mut borrow_mut_events().token_pool_events, - caller_addr, - to, - amount + let store_signer = account::create_signer_with_capability(&pool.store_signer_cap); + let metadata = token_pool::get_fa_metadata(&pool.token_pool_state); + + // Withdraw the amount from the store for release + let fa = primary_fungible_store::withdraw(&store_signer, metadata, local_amount); + + let recipient = token_admin_registry::get_release_or_mint_receiver(&input); + let remote_chain_selector = + token_admin_registry::get_release_or_mint_remote_chain_selector(&input); + + token_pool::emit_released_or_minted( + &mut pool.token_pool_state, + recipient, + local_amount, + remote_chain_selector ); + + (fa, local_amount) } // ================================================================ @@ -723,10 +737,6 @@ module lock_release_token_pool::lock_release_token_pool { borrow_global_mut(store_address()) } - inline fun borrow_mut_events(): &mut LockReleaseTokenPoolEvents { - borrow_global_mut(store_address()) - } - // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move index a160ea38..ddff6ea1 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move @@ -10,8 +10,12 @@ module managed_token_pool::managed_token_pool { use managed_token::managed_token; - use ccip::receiver_registry; - use ccip::token_admin_registry; + use ccip::token_admin_registry::{ + Self, + LockOrBurnInputV1, + LockOrBurnOutputV1, + ReleaseOrMintInputV1 + }; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -28,10 +32,6 @@ module managed_token_pool::managed_token_pool { store_signer_address: address } - struct ManagedTokenPoolEvents has key, store { - token_pool_events: token_pool::TokenPoolEvents - } - const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; @@ -66,6 +66,8 @@ module managed_token_pool::managed_token_pool { }; let managed_token_address = managed_token::token_metadata(); + + // Register V1 pool (for backward compatibility) token_admin_registry::register_pool( publisher, token_pool_module_name, @@ -73,6 +75,18 @@ module managed_token_pool::managed_token_pool { CallbackProof {} ); + let lock_or_burn_closure = |fa, input| lock_or_burn_v2(fa, input); + let release_or_mint_closure = |input| release_or_mint_v2(input); + + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + managed_token_address, + lock_or_burn_closure, + release_or_mint_closure, + CallbackProof {} + ); + // create a resource account to be the owner of the primary FungibleStore we will use. let (store_signer, store_signer_cap) = account::create_resource_account(publisher, STORE_OBJECT_SEED); @@ -97,33 +111,6 @@ module managed_token_pool::managed_token_pool { }; move_to(&store_signer, pool); - - move_to( - &store_signer, - ManagedTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(&store_signer) - } - ); - } - - public fun initialize_token_pool_events(caller: &signer) acquires ManagedTokenPoolState { - let pool = borrow_pool_mut(); - ownable::assert_only_owner(signer::address_of(caller), &pool.ownable_state); - - let store_signer = - &account::create_signer_with_capability(&pool.store_signer_cap); - - assert!( - !exists(signer::address_of(store_signer)), - error::already_exists(E_ALREADY_INITIALIZED) - ); - - move_to( - store_signer, - ManagedTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(store_signer) - } - ); } // ================================================================ @@ -352,24 +339,79 @@ module managed_token_pool::managed_token_pool { fa } - /// Caller must be the receiver contract address when `ccip_receive` is called. - /// Transfer the fungible asset from the receiver to `to` address. - public fun transfer(caller: &signer, to: address, amount: u64) acquires ManagedTokenPoolEvents { - let caller_addr = signer::address_of(caller); + public fun lock_or_burn_v2( + fa: FungibleAsset, input: LockOrBurnInputV1 + ): LockOrBurnOutputV1 { + let pool = borrow_pool_mut(); + let fa_amount = fungible_asset::amount(&fa); - assert!( - receiver_registry::is_executing_receiver_in_progress(caller_addr), - error::permission_denied(E_NOT_EXECUTING_RECEIVER) + // This method validates various aspects of the lock or burn operation. If any of the + // validations fail, the transaction will abort. + let dest_token_address = + token_pool::validate_lock_or_burn( + &mut pool.token_pool_state, + &fa, + &input, + fa_amount + ); + + // Construct lock_or_burn output before we lose access to fa + let dest_pool_data = token_pool::encode_local_decimals(&pool.token_pool_state); + + // Burn the funds + let store = + primary_fungible_store::ensure_primary_store_exists( + pool.store_signer_address, fungible_asset::asset_metadata(&fa) + ); + let signer = &account::create_signer_with_capability(&pool.store_signer_cap); + fungible_asset::deposit(store, fa); + managed_token::burn(signer, pool.store_signer_address, fa_amount); + + let remote_chain_selector = + token_admin_registry::get_lock_or_burn_remote_chain_selector(&input); + + token_pool::emit_locked_or_burned( + &mut pool.token_pool_state, fa_amount, remote_chain_selector ); - managed_token::bridge_transfer(caller, to, amount); + token_admin_registry::new_lock_or_burn_output_v1( + dest_token_address, + token_pool::encode_local_decimals(&pool.token_pool_state) + ) + } + + public fun release_or_mint_v2(input: ReleaseOrMintInputV1): (FungibleAsset, u64) { + let pool = borrow_pool_mut(); + let local_amount = + token_pool::calculate_release_or_mint_amount(&pool.token_pool_state, &input); - token_pool::emit_transfer( - &mut borrow_mut_events().token_pool_events, - caller_addr, - to, - amount + token_pool::validate_release_or_mint( + &mut pool.token_pool_state, &input, local_amount ); + + // Mint the amount for release. + let local_token = token_admin_registry::get_release_or_mint_local_token(&input); + let metadata = object::address_to_object(local_token); + let store = + primary_fungible_store::ensure_primary_store_exists( + pool.store_signer_address, metadata + ); + let signer = &account::create_signer_with_capability(&pool.store_signer_cap); + managed_token::mint(signer, pool.store_signer_address, local_amount); + + let fa = fungible_asset::withdraw(signer, store, local_amount); + let recipient = token_admin_registry::get_release_or_mint_receiver(&input); + let remote_chain_selector = + token_admin_registry::get_release_or_mint_remote_chain_selector(&input); + + token_pool::emit_released_or_minted( + &mut pool.token_pool_state, + recipient, + local_amount, + remote_chain_selector + ); + + (fa, local_amount) } // ================================================================ @@ -479,10 +521,6 @@ module managed_token_pool::managed_token_pool { borrow_global_mut(store_address()) } - inline fun borrow_mut_events(): &mut ManagedTokenPoolEvents { - borrow_global_mut(store_address()) - } - // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move index 4bfeb96c..67152c94 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move @@ -10,8 +10,7 @@ module regulated_token_pool::regulated_token_pool { use regulated_token::regulated_token::{Self}; - use ccip::receiver_registry; - use ccip::token_admin_registry; + use ccip::token_admin_registry::{Self, LockOrBurnInputV1, ReleaseOrMintInputV1}; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -28,10 +27,6 @@ module regulated_token_pool::regulated_token_pool { store_signer_address: address } - struct RegulatedTokenPoolEvents has key, store { - token_pool_events: token_pool::TokenPoolEvents - } - const E_NOT_PUBLISHER: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_INVALID_FUNGIBLE_ASSET: u64 = 3; @@ -67,6 +62,8 @@ module regulated_token_pool::regulated_token_pool { }; let regulated_token_address = regulated_token::token_address(); + + // Register V1 pool (for backward compatibility) token_admin_registry::register_pool( publisher, token_pool_module_name, @@ -74,6 +71,18 @@ module regulated_token_pool::regulated_token_pool { CallbackProof {} ); + let lock_or_burn_closure = |fa, input| lock_or_burn_v2(fa, input); + let release_or_mint_closure = |input| release_or_mint_v2(input); + + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + regulated_token_address, + lock_or_burn_closure, + release_or_mint_closure, + CallbackProof {} + ); + // create a resource account to be the owner of the primary FungibleStore we will use. let (store_signer, store_signer_cap) = account::create_resource_account(publisher, STORE_OBJECT_SEED); @@ -96,33 +105,6 @@ module regulated_token_pool::regulated_token_pool { }; move_to(&store_signer, pool); - - move_to( - &store_signer, - RegulatedTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(&store_signer) - } - ); - } - - public fun initialize_token_pool_events(caller: &signer) acquires RegulatedTokenPoolState { - let pool = borrow_pool_mut(); - ownable::assert_only_owner(signer::address_of(caller), &pool.ownable_state); - - let store_signer = - &account::create_signer_with_capability(&pool.store_signer_cap); - - assert!( - !exists(signer::address_of(store_signer)), - error::already_exists(E_ALREADY_INITIALIZED) - ); - - move_to( - store_signer, - RegulatedTokenPoolEvents { - token_pool_events: token_pool::create_transfer_events(store_signer) - } - ); } // ================================================================ @@ -342,26 +324,64 @@ module regulated_token_pool::regulated_token_pool { fa } - /// Caller must be the receiver contract address when `ccip_receive` is called. - /// Transfer the fungible asset from the receiver to `to` address. - public fun transfer(caller: &signer, to: address, amount: u64) acquires RegulatedTokenPoolEvents { - let caller_addr = signer::address_of(caller); + public fun lock_or_burn_v2( + fa: FungibleAsset, input: LockOrBurnInputV1 + ): token_admin_registry::LockOrBurnOutputV1 acquires RegulatedTokenPoolState { + let pool = borrow_pool_mut(); + let fa_amount = fungible_asset::amount(&fa); - assert!( - receiver_registry::is_executing_receiver_in_progress(caller_addr), - error::permission_denied(E_NOT_EXECUTING_RECEIVER) + let dest_token_address = + token_pool::validate_lock_or_burn( + &mut pool.token_pool_state, + &fa, + &input, + fa_amount + ); + + let pool_signer = &account::create_signer_with_capability(&pool.store_signer_cap); + let sender = token_admin_registry::get_lock_or_burn_sender(&input); + regulated_token::bridge_burn(pool_signer, sender, fa); + + let remote_chain_selector = + token_admin_registry::get_lock_or_burn_remote_chain_selector(&input); + + token_pool::emit_locked_or_burned( + &mut pool.token_pool_state, fa_amount, remote_chain_selector + ); + + token_admin_registry::new_lock_or_burn_output_v1( + dest_token_address, + token_pool::encode_local_decimals(&pool.token_pool_state) + ) + } + + public fun release_or_mint_v2( + input: ReleaseOrMintInputV1 + ): (FungibleAsset, u64) acquires RegulatedTokenPoolState { + let pool = borrow_pool_mut(); + let local_amount = + token_pool::calculate_release_or_mint_amount(&pool.token_pool_state, &input); + + token_pool::validate_release_or_mint( + &mut pool.token_pool_state, &input, local_amount ); - // Call into regulated_token to perform transfer using TransferRef - // The caller (receiver) must have tokens and the bridge_transfer will check permissions - regulated_token::bridge_transfer(caller, to, amount); + // Mint the amount for release using regulated token's bridge mint function + let pool_signer = &account::create_signer_with_capability(&pool.store_signer_cap); + let receiver = token_admin_registry::get_release_or_mint_receiver(&input); + let fa = regulated_token::bridge_mint(pool_signer, receiver, local_amount); + + let remote_chain_selector = + token_admin_registry::get_release_or_mint_remote_chain_selector(&input); - token_pool::emit_transfer( - &mut borrow_mut_events().token_pool_events, - caller_addr, - to, - amount + token_pool::emit_released_or_minted( + &mut pool.token_pool_state, + receiver, + local_amount, + remote_chain_selector ); + + (fa, local_amount) } // ================================================================ @@ -471,10 +491,6 @@ module regulated_token_pool::regulated_token_pool { borrow_global_mut(store_address()) } - inline fun borrow_mut_events(): &mut RegulatedTokenPoolEvents { - borrow_global_mut(store_address()) - } - // ================================================================ // | Expose ownable | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move index 50a960b6..feded37d 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/regulated_token_pool_test.move @@ -10,7 +10,6 @@ module regulated_token_pool::regulated_token_pool_test { use ccip::state_object; use ccip::auth; use ccip::token_admin_registry; - use ccip::receiver_registry; use regulated_token::regulated_token; use regulated_token_pool::regulated_token_pool; @@ -794,44 +793,4 @@ module regulated_token_pool::regulated_token_pool_test { assert!(regulated_token_pool::pending_transfer_to().is_none()); assert!(regulated_token_pool::pending_transfer_accepted().is_none()); } - - // ================================================================ - // | Tests for execution context enforcement | - // ================================================================ - - #[ - test( - admin = @admin, - regulated_token = @regulated_token, - regulated_token_pool = @regulated_token_pool, - framework = @aptos_framework, - ccip = @ccip, - random_signer = @0x999 - ), - expected_failure( - abort_code = 327689, location = regulated_token_pool::regulated_token_pool - ) - ] - fun test_transfer_outside_ccip_receive_fails( - admin: &signer, - regulated_token: &signer, - regulated_token_pool: &signer, - framework: &signer, - ccip: &signer, - random_signer: &signer - ) { - setup( - admin, - regulated_token, - regulated_token_pool, - framework, - ccip - ); - - receiver_registry::init_module_for_testing(admin); - - // Try to transfer tokens directly without ccip_receive context - // This should fail because is_executing_receiver_in_progress is false - regulated_token_pool::transfer(random_signer, @0x777, 100000); - } } diff --git a/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move b/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move index 70d21df2..b7dc5b2e 100644 --- a/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move +++ b/contracts/ccip/ccip_token_pools/token_pool/sources/token_pool.move @@ -35,10 +35,6 @@ module ccip_token_pool::token_pool { rebalancer_set_events: EventHandle } - struct TokenPoolEvents has key, store { - transfer_events: EventHandle - } - struct RemoteChainConfig has store, drop, copy { remote_token_address: vector, remote_pools: vector> @@ -112,13 +108,6 @@ module ccip_token_pool::token_pool { new_rebalancer: address } - #[event] - struct Transfer has store, drop { - from: address, - to: address, - amount: u64 - } - const E_NOT_ALLOWED_CALLER: u64 = 1; const E_UNKNOWN_FUNGIBLE_ASSET: u64 = 2; const E_UNKNOWN_REMOTE_CHAIN_SELECTOR: u64 = 3; @@ -160,10 +149,6 @@ module ccip_token_pool::token_pool { } } - public fun create_transfer_events(event_account: &signer): TokenPoolEvents { - TokenPoolEvents { transfer_events: account::new_event_handle(event_account) } - } - #[view] public fun get_router(): address { @ccip @@ -496,18 +481,6 @@ module ccip_token_pool::token_pool { ); } - public fun emit_transfer( - events: &mut TokenPoolEvents, - from: address, - to: address, - amount: u64 - ) { - event::emit_event( - &mut events.transfer_events, - Transfer { from, to, amount } - ); - } - // ================================================================ // | Decimals | // ================================================================ diff --git a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move index 3cca95b9..e3aaf550 100644 --- a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move +++ b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move @@ -13,7 +13,12 @@ module usdc_token_pool::usdc_token_pool { use ccip::address; use ccip::eth_abi; - use ccip::token_admin_registry; + use ccip::token_admin_registry::{ + Self, + LockOrBurnInputV1, + ReleaseOrMintInputV1, + LockOrBurnOutputV1 + }; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -120,6 +125,18 @@ module usdc_token_pool::usdc_token_pool { CallbackProof {} ); + let lock_or_burn_closure = |fa, input| lock_or_burn_v2(fa, input); + let release_or_mint_closure = |input| release_or_mint_v2(input); + + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + @local_token, + lock_or_burn_closure, + release_or_mint_closure, + CallbackProof {} + ); + // create a resource account to be the owner of the primary FungibleStore we will use. let (store_signer, store_signer_cap) = account::create_resource_account(publisher, STORE_OBJECT_SEED); @@ -491,6 +508,112 @@ module usdc_token_pool::usdc_token_pool { ); } + public fun lock_or_burn_v2( + fa: FungibleAsset, input: LockOrBurnInputV1 + ): LockOrBurnOutputV1 acquires USDCTokenPoolState { + let pool = borrow_pool_mut(); + let fa_amount = fungible_asset::amount(&fa); + + let dest_token_address = + token_pool::validate_lock_or_burn( + &mut pool.token_pool_state, + &fa, + &input, + fa_amount + ); + + let store_signer = account::create_signer_with_capability(&pool.store_signer_cap); + + let remote_chain_selector = + token_admin_registry::get_lock_or_burn_remote_chain_selector(&input); + assert!( + pool.chain_to_domain.contains(remote_chain_selector), + error::invalid_argument(E_DOMAIN_NOT_FOUND) + ); + + let remote_domain_info = pool.chain_to_domain.borrow(remote_chain_selector); + + assert!( + remote_domain_info.enabled, + error::invalid_argument(E_DOMAIN_DISABLED) + ); + + let mint_recipient_bytes = + token_admin_registry::get_lock_or_burn_receiver(&input); + let mint_recipient = from_bcs::to_address(mint_recipient_bytes); + let nonce = + token_messenger::deposit_for_burn_with_caller( + &store_signer, + fa, + remote_domain_info.domain_identifier, + mint_recipient, + from_bcs::to_address(remote_domain_info.allowed_caller) + ); + + let dest_pool_data = encode_dest_pool_data(pool.local_domain_identifier, nonce); + + token_pool::emit_locked_or_burned( + &mut pool.token_pool_state, fa_amount, remote_chain_selector + ); + + token_admin_registry::new_lock_or_burn_output_v1( + dest_token_address, dest_pool_data + ) + } + + public fun release_or_mint_v2( + input: ReleaseOrMintInputV1 + ): (FungibleAsset, u64) acquires USDCTokenPoolState { + let pool = borrow_pool_mut(); + let local_amount = + token_admin_registry::get_release_or_mint_source_amount(&input) as u64; + + token_pool::validate_release_or_mint( + &mut pool.token_pool_state, &input, local_amount + ); + + let store_signer = account::create_signer_with_capability(&pool.store_signer_cap); + + let (source_domain_identifier, nonce) = + decode_dest_pool_data( + token_admin_registry::get_release_or_mint_source_pool_data(&input) + ); + let offchain_token_data = + token_admin_registry::get_release_or_mint_offchain_token_data(&input); + + let (message_bytes, attestation) = + parse_message_and_attestation(offchain_token_data); + + validate_message( + &message_bytes, + source_domain_identifier, + nonce, + pool.local_domain_identifier + ); + + let receipt = + message_transmitter::receive_message( + &store_signer, &message_bytes, &attestation + ); + + assert!(token_messenger::handle_receive_message(receipt)); + + let recipient = token_admin_registry::get_release_or_mint_receiver(&input); + let remote_chain_selector = + token_admin_registry::get_release_or_mint_remote_chain_selector(&input); + + token_pool::emit_released_or_minted( + &mut pool.token_pool_state, + recipient, + local_amount, + remote_chain_selector + ); + + let fa_metadata = token_pool::get_fa_metadata(&pool.token_pool_state); + + (fungible_asset::zero(fa_metadata), local_amount) + } + // ================================================================ // | USDC Domains | // ================================================================ diff --git a/contracts/managed_token/sources/managed_token.move b/contracts/managed_token/sources/managed_token.move index 71da13b3..2dfb4360 100644 --- a/contracts/managed_token/sources/managed_token.move +++ b/contracts/managed_token/sources/managed_token.move @@ -336,35 +336,6 @@ module managed_token::managed_token { ); } - public fun bridge_transfer( - caller: &signer, to: address, amount: u64 - ) acquires TokenMetadataRefs, TokenState { - let caller_addr = signer::address_of(caller); - let state = &mut TokenState[token_state_address_internal()]; - - // Must be allowed as both minter and burner to transfer - assert_is_allowed_minter(caller_addr, state); - assert_is_allowed_burner(caller_addr, state); - - if (amount == 0) { return }; - - primary_fungible_store::transfer_with_ref( - &borrow_token_metadata_refs(state).transfer_ref, - caller_addr, - to, - amount - ); - - event::emit_event( - &mut state.burn_events, - Burn { burner: caller_addr, from: caller_addr, amount } - ); - event::emit_event( - &mut state.mint_events, - Mint { minter: caller_addr, to, amount } - ); - } - inline fun assert_is_allowed_minter( caller: address, state: &TokenState ) { diff --git a/contracts/regulated_token/sources/regulated_token.move b/contracts/regulated_token/sources/regulated_token.move index 6cf31489..0b5ea744 100644 --- a/contracts/regulated_token/sources/regulated_token.move +++ b/contracts/regulated_token/sources/regulated_token.move @@ -114,13 +114,6 @@ module regulated_token::regulated_token { amount: u64 } - #[event] - struct BridgeTransfer has drop, store { - caller: address, - to: address, - amount: u64 - } - #[event] struct MinterAdded has drop, store { admin: address, @@ -599,28 +592,6 @@ module regulated_token::regulated_token { } } - public fun bridge_transfer( - caller: &signer, to: address, amount: u64 - ) acquires TokenMetadataRefs, TokenState { - let caller_addr = signer::address_of(caller); - let state_obj = token_state_object_internal(); - let token_state = &TokenState[object::object_address(&state_obj)]; - - assert_not_paused(token_state); - assert_bridge_minter_or_burner(caller, state_obj); - assert_not_frozen(caller_addr, token_state); - assert_not_frozen(to, token_state); - - primary_fungible_store::transfer_with_ref( - &borrow_token_metadata_refs().transfer_ref, - caller_addr, - to, - amount - ); - - event::emit(BridgeTransfer { caller: caller_addr, to, amount }); - } - /// Bridge-specific function to mint tokens directly as `FungibleAsset`. /// Required because this token has dynamic dispatch enabled /// as minting to pool and calling `fungible_asset::withdraw()` reverts. From 5e02abfc3ce545747fd9d355e5b6ec2a6df52d10 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Tue, 25 Nov 2025 16:01:18 -0500 Subject: [PATCH 05/13] Update CCIP to handle v1 and v2 ccip receiver registration dispatch --- .../ccip/sources/token_admin_dispatcher.move | 57 ++++++++++++++----- .../ccip/sources/token_admin_registry.move | 57 ++++++++++++------- 2 files changed, 78 insertions(+), 36 deletions(-) diff --git a/contracts/ccip/ccip/sources/token_admin_dispatcher.move b/contracts/ccip/ccip/sources/token_admin_dispatcher.move index f0bae95f..28f05195 100644 --- a/contracts/ccip/ccip/sources/token_admin_dispatcher.move +++ b/contracts/ccip/ccip/sources/token_admin_dispatcher.move @@ -16,17 +16,29 @@ module ccip::token_admin_dispatcher { ): (vector, vector) { auth::assert_is_allowed_onramp(signer::address_of(caller)); - let dispatch_fungible_store = - token_admin_registry::start_lock_or_burn( + // Check for V2 first (preferred - uses closures) + if (token_admin_registry::has_token_pool_config(token_pool_address)) { + token_admin_registry::lock_or_burn_v2( + caller, token_pool_address, + fa, sender, remote_chain_selector, receiver - ); + ) + } else { + let dispatch_fungible_store = + token_admin_registry::start_lock_or_burn( + token_pool_address, + sender, + remote_chain_selector, + receiver + ); - dispatchable_fungible_asset::deposit(dispatch_fungible_store, fa); + dispatchable_fungible_asset::deposit(dispatch_fungible_store, fa); - token_admin_registry::finish_lock_or_burn(token_pool_address) + token_admin_registry::finish_lock_or_burn(token_pool_address) + } } public fun dispatch_release_or_mint( @@ -43,8 +55,9 @@ module ccip::token_admin_dispatcher { ): (FungibleAsset, u64) { auth::assert_is_allowed_offramp(signer::address_of(caller)); - let (dispatch_owner, dispatch_fungible_store) = - token_admin_registry::start_release_or_mint( + if (token_admin_registry::has_token_pool_config(token_pool_address)) { + token_admin_registry::release_or_mint_v2( + caller, token_pool_address, sender, receiver, @@ -54,17 +67,31 @@ module ccip::token_admin_dispatcher { source_pool_address, source_pool_data, offchain_token_data - ); + ) + } else { + let (dispatch_owner, dispatch_fungible_store) = + token_admin_registry::start_release_or_mint( + token_pool_address, + sender, + receiver, + source_amount, + local_token, + remote_chain_selector, + source_pool_address, + source_pool_data, + offchain_token_data + ); - let fa = - dispatchable_fungible_asset::withdraw( - &dispatch_owner, dispatch_fungible_store, 0 - ); + let fa = + dispatchable_fungible_asset::withdraw( + &dispatch_owner, dispatch_fungible_store, 0 + ); - let destination_amount = - token_admin_registry::finish_release_or_mint(token_pool_address); + let destination_amount = + token_admin_registry::finish_release_or_mint(token_pool_address); - (fa, destination_amount) + (fa, destination_amount) + } } // ============================================ diff --git a/contracts/ccip/ccip/sources/token_admin_registry.move b/contracts/ccip/ccip/sources/token_admin_registry.move index 29e50016..a7ec5063 100644 --- a/contracts/ccip/ccip/sources/token_admin_registry.move +++ b/contracts/ccip/ccip/sources/token_admin_registry.move @@ -86,16 +86,20 @@ module ccip::token_admin_registry { struct ReleaseOrMintOutputV1 has store, drop { destination_amount: u64 } + public fun new_lock_or_burn_output_v1( - dest_token_address: vector, - dest_pool_data: vector + dest_token_address: vector, dest_pool_data: vector ): LockOrBurnOutputV1 { LockOrBurnOutputV1 { dest_token_address, dest_pool_data } } struct TokenPoolCallbacks has drop, copy, store { - lock_or_burn: |fungible_asset::FungibleAsset, LockOrBurnInputV1| LockOrBurnOutputV1 has drop + copy + store, - release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop + copy + store, + lock_or_burn: | + fungible_asset::FungibleAsset, + LockOrBurnInputV1 + | LockOrBurnOutputV1 has drop + copy + store, + release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop + + copy + store } struct TokenPoolConfig has key { @@ -240,10 +244,18 @@ module ccip::token_admin_registry { #[view] /// Returns the local token address for the token pool. - public fun get_pool_local_token_v2(token_pool_address: address): address acquires TokenPoolConfig { + public fun get_pool_local_token_v2( + token_pool_address: address + ): address acquires TokenPoolConfig { TokenPoolConfig[token_pool_address].local_token } + #[view] + /// Returns true if token pool has TokenPoolConfig resource + public fun has_token_pool_config(token_pool_address: address): bool { + exists(token_pool_address) + } + #[view] /// returns (token_pool_address, administrator, pending_administrator) public fun get_token_config( @@ -420,8 +432,12 @@ module ccip::token_admin_registry { token_pool_account: &signer, token_pool_module_name: vector, local_token: address, - lock_or_burn: |fungible_asset::FungibleAsset, LockOrBurnInputV1| LockOrBurnOutputV1 has drop + copy + store, - release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop + copy + store, + lock_or_burn: | + fungible_asset::FungibleAsset, + LockOrBurnInputV1 + | LockOrBurnOutputV1 has drop + copy + store, + release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop + + copy + store, _proof: ProofType ) { let token_pool_address = signer::address_of(token_pool_account); @@ -507,13 +523,14 @@ module ccip::token_admin_registry { let caller_addr = signer::address_of(caller); - let pool_local_token = if (exists(token_pool_address)) { - get_pool_local_token_v2(token_pool_address) - } else if (exists(token_pool_address)) { - get_registration(token_pool_address).local_token - } else { - abort error::invalid_argument(E_POOL_NOT_REGISTERED) - }; + let pool_local_token = + if (exists(token_pool_address)) { + get_pool_local_token_v2(token_pool_address) + } else if (exists(token_pool_address)) { + get_registration(token_pool_address).local_token + } else { + abort error::invalid_argument(E_POOL_NOT_REGISTERED) + }; assert!( pool_local_token == local_token, @@ -1071,13 +1088,10 @@ module ccip::token_admin_registry { auth::assert_is_allowed_onramp(signer::address_of(caller)); let pool_config = &TokenPoolConfig[token_pool_address]; - let input = LockOrBurnInputV1 { - sender, - remote_chain_selector, - receiver - }; + let input = LockOrBurnInputV1 { sender, remote_chain_selector, receiver }; - let output = (pool_config.callbacks.lock_or_burn)(fa, input); + let output = (pool_config.callbacks.lock_or_burn) + (fa, input); (output.dest_token_address, output.dest_pool_data) } @@ -1107,7 +1121,8 @@ module ccip::token_admin_registry { offchain_token_data }; - (pool_config.callbacks.release_or_mint)(input) + (pool_config.callbacks.release_or_mint) + (input) } inline fun borrow_state(): &TokenAdminRegistryState { From b81e6ce8cc002419adefa325f9a4b813e2d9a5c1 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Tue, 25 Nov 2025 16:01:46 -0500 Subject: [PATCH 06/13] Update tests and pool modules to handle v1 and v2 ccip receive callback upgrade --- .../token_admin_registry.go | 31 +++++ .../ccip/sources/receiver_dispatcher.move | 21 ++- .../ccip/ccip/sources/receiver_registry.move | 15 +- .../ccip/sources/token_admin_dispatcher.move | 52 ------- .../ccip/ccip_offramp/sources/offramp.move | 25 +--- .../tests/mock/mock_ccip_receiver.move | 130 +++++++++++++++++- .../sources/burn_mint_token_pool.move | 59 ++++++++ .../tests/upgrade_v2.move | 50 +++++++ .../sources/lock_release_token_pool.move | 57 ++++++++ .../tests/upgrade_v2.move | 50 +++++++ .../sources/managed_token_pool.move | 58 ++++++++ .../managed_token_pool/tests/upgrade_v2.move | 44 ++++++ .../sources/regulated_token_pool.move | 55 ++++++++ .../tests/upgrade_v2.move | 44 ++++++ .../sources/usdc_token_pool.move | 6 + .../usdc_token_pool/tests/upgrade_v2.move | 50 +++++++ 16 files changed, 650 insertions(+), 97 deletions(-) create mode 100644 contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move create mode 100644 contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move create mode 100644 contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move create mode 100644 contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move create mode 100644 contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move diff --git a/bindings/ccip/token_admin_registry/token_admin_registry.go b/bindings/ccip/token_admin_registry/token_admin_registry.go index 63455917..dad08031 100644 --- a/bindings/ccip/token_admin_registry/token_admin_registry.go +++ b/bindings/ccip/token_admin_registry/token_admin_registry.go @@ -27,6 +27,7 @@ type TokenAdminRegistryInterface interface { GetPool(opts *bind.CallOpts, localToken aptos.AccountAddress) (aptos.AccountAddress, error) GetPoolLocalToken(opts *bind.CallOpts, tokenPoolAddress aptos.AccountAddress) (aptos.AccountAddress, error) GetPoolLocalTokenV2(opts *bind.CallOpts, tokenPoolAddress aptos.AccountAddress) (aptos.AccountAddress, error) + HasTokenPoolConfig(opts *bind.CallOpts, tokenPoolAddress aptos.AccountAddress) (bool, error) GetTokenConfig(opts *bind.CallOpts, localToken aptos.AccountAddress) (aptos.AccountAddress, aptos.AccountAddress, aptos.AccountAddress, error) GetAllConfiguredTokens(opts *bind.CallOpts, startKey aptos.AccountAddress, maxCount uint64) ([]aptos.AccountAddress, aptos.AccountAddress, bool, error) IsAdministrator(opts *bind.CallOpts, localToken aptos.AccountAddress, administrator aptos.AccountAddress) (bool, error) @@ -47,6 +48,7 @@ type TokenAdminRegistryEncoder interface { GetPool(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetPoolLocalToken(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetPoolLocalTokenV2(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) + HasTokenPoolConfig(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetTokenConfig(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) GetAllConfiguredTokens(startKey aptos.AccountAddress, maxCount uint64) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) IsAdministrator(localToken aptos.AccountAddress, administrator aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) @@ -305,6 +307,27 @@ func (c TokenAdminRegistryContract) GetPoolLocalTokenV2(opts *bind.CallOpts, tok return r0, nil } +func (c TokenAdminRegistryContract) HasTokenPoolConfig(opts *bind.CallOpts, tokenPoolAddress aptos.AccountAddress) (bool, error) { + module, function, typeTags, args, err := c.tokenAdminRegistryEncoder.HasTokenPoolConfig(tokenPoolAddress) + if err != nil { + return *new(bool), err + } + + callData, err := c.Call(opts, module, function, typeTags, args) + if err != nil { + return *new(bool), err + } + + var ( + r0 bool + ) + + if err := codec.DecodeAptosJsonArray(callData, &r0); err != nil { + return *new(bool), err + } + return r0, nil +} + func (c TokenAdminRegistryContract) GetTokenConfig(opts *bind.CallOpts, localToken aptos.AccountAddress) (aptos.AccountAddress, aptos.AccountAddress, aptos.AccountAddress, error) { module, function, typeTags, args, err := c.tokenAdminRegistryEncoder.GetTokenConfig(localToken) if err != nil { @@ -460,6 +483,14 @@ func (c tokenAdminRegistryEncoder) GetPoolLocalTokenV2(tokenPoolAddress aptos.Ac }) } +func (c tokenAdminRegistryEncoder) HasTokenPoolConfig(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { + return c.BoundContract.Encode("has_token_pool_config", nil, []string{ + "address", + }, []any{ + tokenPoolAddress, + }) +} + func (c tokenAdminRegistryEncoder) GetTokenConfig(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("get_token_config", nil, []string{ "address", diff --git a/contracts/ccip/ccip/sources/receiver_dispatcher.move b/contracts/ccip/ccip/sources/receiver_dispatcher.move index 8b0eb783..1b5c4101 100644 --- a/contracts/ccip/ccip/sources/receiver_dispatcher.move +++ b/contracts/ccip/ccip/sources/receiver_dispatcher.move @@ -11,18 +11,13 @@ module ccip::receiver_dispatcher { ) { auth::assert_is_allowed_offramp(signer::address_of(caller)); - let dispatch_metadata = - receiver_registry::start_receive(receiver_address, message); - dispatchable_fungible_asset::derived_supply(dispatch_metadata); - receiver_registry::finish_receive(receiver_address); - } - - /// Invoke receiver's callback without token dispatchable hooks - public fun dispatch_receive_v2( - caller: &signer, receiver_address: address, message: client::Any2AptosMessage - ) { - auth::assert_is_allowed_offramp(signer::address_of(caller)); - - receiver_registry::invoke_ccip_receive_v2(receiver_address, message); + if (receiver_registry::is_registered_receiver_v2(receiver_address)) { + receiver_registry::invoke_ccip_receive_v2(receiver_address, message); + } else { + let dispatch_metadata = + receiver_registry::start_receive(receiver_address, message); + dispatchable_fungible_asset::derived_supply(dispatch_metadata); + receiver_registry::finish_receive(receiver_address); + } } } diff --git a/contracts/ccip/ccip/sources/receiver_registry.move b/contracts/ccip/ccip/sources/receiver_registry.move index fd4d2c8c..7860c2e0 100644 --- a/contracts/ccip/ccip/sources/receiver_registry.move +++ b/contracts/ccip/ccip/sources/receiver_registry.move @@ -34,7 +34,7 @@ module ccip::receiver_registry { struct CCIPReceiverRegistrationV2 has key { callback: |client::Any2AptosMessage| has drop + copy + store, - proof_typeinfo: TypeInfo, + proof_typeinfo: TypeInfo } #[event] @@ -167,7 +167,7 @@ module ccip::receiver_registry { ); move_to( - receiver_account, + receiver_account, CCIPReceiverRegistrationV2 { callback, proof_typeinfo } ); @@ -181,6 +181,7 @@ module ccip::receiver_registry { #[view] public fun is_registered_receiver(receiver_address: address): bool { exists(receiver_address) + || exists(receiver_address) } #[view] @@ -221,9 +222,7 @@ module ccip::receiver_registry { registration.dispatch_metadata } - public(friend) fun finish_receive( - receiver_address: address - ) acquires CCIPReceiverRegistration { + public(friend) fun finish_receive(receiver_address: address) acquires CCIPReceiverRegistration { let registration = get_registration_mut(receiver_address); assert!( @@ -233,8 +232,7 @@ module ccip::receiver_registry { } public(friend) fun invoke_ccip_receive_v2( - receiver_address: address, - message: client::Any2AptosMessage + receiver_address: address, message: client::Any2AptosMessage ) acquires CCIPReceiverRegistrationV2 { assert!( exists(receiver_address), @@ -242,8 +240,7 @@ module ccip::receiver_registry { ); let registration = borrow_global(receiver_address); - - (registration.callback)(message); + (registration.callback) (message); } inline fun borrow_state(): &ReceiverRegistryState { diff --git a/contracts/ccip/ccip/sources/token_admin_dispatcher.move b/contracts/ccip/ccip/sources/token_admin_dispatcher.move index 28f05195..bc0028bf 100644 --- a/contracts/ccip/ccip/sources/token_admin_dispatcher.move +++ b/contracts/ccip/ccip/sources/token_admin_dispatcher.move @@ -16,7 +16,6 @@ module ccip::token_admin_dispatcher { ): (vector, vector) { auth::assert_is_allowed_onramp(signer::address_of(caller)); - // Check for V2 first (preferred - uses closures) if (token_admin_registry::has_token_pool_config(token_pool_address)) { token_admin_registry::lock_or_burn_v2( caller, @@ -93,55 +92,4 @@ module ccip::token_admin_dispatcher { (fa, destination_amount) } } - - // ============================================ - // V2 Closure-Based Dispatch Functions - // ============================================ - - public fun dispatch_lock_or_burn_v2( - caller: &signer, - token_pool_address: address, - fa: FungibleAsset, - sender: address, - remote_chain_selector: u64, - receiver: vector - ): (vector, vector) { - token_admin_registry::lock_or_burn_v2( - caller, - token_pool_address, - fa, - sender, - remote_chain_selector, - receiver - ) - } - - public fun dispatch_release_or_mint_v2( - caller: &signer, - token_pool_address: address, - sender: vector, - receiver: address, - source_amount: u256, - local_token: address, - remote_chain_selector: u64, - source_pool_address: vector, - source_pool_data: vector, - offchain_token_data: vector - ): (FungibleAsset, u64) { - let (fa, destination_amount) = - token_admin_registry::release_or_mint_v2( - caller, - token_pool_address, - sender, - receiver, - source_amount, - local_token, - remote_chain_selector, - source_pool_address, - source_pool_data, - offchain_token_data - ); - - (fa, destination_amount) - } } diff --git a/contracts/ccip/ccip_offramp/sources/offramp.move b/contracts/ccip/ccip_offramp/sources/offramp.move index a0df4161..f0edd5de 100644 --- a/contracts/ccip/ccip_offramp/sources/offramp.move +++ b/contracts/ccip/ccip_offramp/sources/offramp.move @@ -808,14 +808,8 @@ module ccip_offramp::offramp { // module. // ref: https://github.com/smartcontractkit/chainlink-ccip/blob/875e982e6437dc126710d8224dd7c792a197bea6/chains/evm/contracts/offRamp/OffRamp.sol#L633 - let is_v1_receiver = receiver_registry::is_registered_receiver(message.receiver); - let is_v2_receiver = - receiver_registry::is_registered_receiver_v2(message.receiver); - - if ((!message.data.is_empty() - || message.gas_limit != 0) - && (is_v1_receiver - || is_v2_receiver)) { + if ((!message.data.is_empty() || message.gas_limit != 0) + && receiver_registry::is_registered_receiver(message.receiver)) { let state_signer = account::create_signer_with_capability(&state.state_signer_cap); @@ -833,16 +827,9 @@ module ccip_offramp::offramp { dest_token_amounts ); - // Use V2 dispatch if available, else V1 - if (is_v2_receiver) { - receiver_dispatcher::dispatch_receive_v2( - &state_signer, message.receiver, any2aptos_message - ) - } else { - receiver_dispatcher::dispatch_receive( - &state_signer, message.receiver, any2aptos_message - ) - } + receiver_dispatcher::dispatch_receive( + &state_signer, message.receiver, any2aptos_message + ) }; } @@ -909,7 +896,7 @@ module ccip_offramp::offramp { account::create_signer_with_capability(&state.state_signer_cap); let (fa, local_amount) = - token_admin_dispatcher::dispatch_release_or_mint_v2( + token_admin_dispatcher::dispatch_release_or_mint( &state_signer, token_pool_address, sender, diff --git a/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move index 013e6ee3..68321e44 100644 --- a/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move +++ b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move @@ -4,9 +4,10 @@ module ccip_offramp::mock_ccip_receiver { use std::account; use std::event; - use std::object::{Self}; + use std::object::{Self, Object}; use std::string::{Self, String}; - use std::fungible_asset::{Metadata}; + use std::fungible_asset::{Self, Metadata}; + use std::option::{Self, Option}; use std::primary_fungible_store; use std::from_bcs; use std::signer; @@ -71,10 +72,30 @@ module ccip_offramp::mock_ccip_receiver { } ); + // Default to V2 registration receiver_registry::register_receiver_v2( publisher, MODULE_NAME, - |message| ccip_receive(message), + |message| ccip_receive_v2(message), + CCIPReceiverProof {} + ); + } + + /// Register this receiver as V1 (dispatchable fungible asset mode) + /// This is used for testing V1 compatibility + public fun register_as_v1(publisher: &signer) { + receiver_registry::register_receiver(publisher, MODULE_NAME, CCIPReceiverProof {}); + } + + /// Migrate from V1 to V2 registration + /// This demonstrates the upgrade path from dispatchable FA to closures + public fun migrate_to_v2(publisher: &signer) { + // V2 registration will coexist with V1 + // The dispatcher will prefer V2 when both exist + receiver_registry::register_receiver_v2( + publisher, + MODULE_NAME, + |message| ccip_receive_v2(message), CCIPReceiverProof {} ); } @@ -88,7 +109,83 @@ module ccip_offramp::mock_ccip_receiver { struct CCIPReceiverProof has drop {} - public fun ccip_receive(message: client::Any2AptosMessage) acquires CCIPReceiverState { + /// This function should only be used with non-dispatchable tokens, + /// as it is currently incompatible with dispatchable tokens. + public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + /* load state and rebuild a signer for the resource account */ + let state = borrow_global_mut(@ccip_offramp); + let state_signer = account::create_signer_with_capability(&state.signer_cap); + + let message = + receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); + + let data = client::get_data(&message); + + let dest_token_amounts = client::get_dest_token_amounts(&message); + + if (dest_token_amounts.length() != 0 && data.length() != 0) { + let final_recipient = from_bcs::to_address(data); + + for (i in 0..dest_token_amounts.length()) { + let token_amount_ref = &dest_token_amounts[i]; + let token_addr = client::get_token(token_amount_ref); + let amount = client::get_amount(token_amount_ref); + + // Implement the token transfer logic here + + let fa_token = object::address_to_object(token_addr); + let fa_store_sender = + primary_fungible_store::ensure_primary_store_exists( + @ccip_offramp, fa_token + ); + let fa_store_receiver = + primary_fungible_store::ensure_primary_store_exists( + final_recipient, fa_token + ); + + fungible_asset::transfer( + &state_signer, + fa_store_sender, + fa_store_receiver, + amount + ); + }; + + event::emit(ForwardedTokens { final_recipient }); + event::emit_event( + &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } + ); + + } else if (data.length() != 0) { + + // Convert the vector to a string + let message = string::utf8(data); + + event::emit(ReceivedMessage { message }); + event::emit_event( + &mut state.received_message_handle, ReceivedMessage { message } + ); + + } else if (dest_token_amounts.length() != 0) { + // Tokens only (no forwarding data) - keep them at receiver + // Emit event to prove receiver was called + let token_count = dest_token_amounts.length(); + event::emit(ReceivedTokensOnly { token_count }); + event::emit_event( + &mut state.received_tokens_only_handle, + ReceivedTokensOnly { token_count } + ); + }; + + // Simple abort condition for testing + if (data == b"abort") { + abort 1 + }; + + option::none() + } + + public fun ccip_receive_v2(message: client::Any2AptosMessage) acquires CCIPReceiverState { /* load state and rebuild a signer for the resource account */ let state = borrow_global_mut(@ccip_offramp); let state_signer = account::create_signer_with_capability(&state.signer_cap); @@ -171,6 +268,31 @@ module ccip_offramp::mock_ccip_receiver { init_module(publisher); } + /// Initialize without auto-registering (for testing V1/V2 manually) + public fun test_init_state_only(publisher: &signer) { + // Create a signer capability for the receiver account + let signer_cap = account::create_test_signer_cap(signer::address_of(publisher)); + + // Create a unique handle for each event type + let received_message_handle = + account::new_event_handle(publisher); + let forwarded_tokens_handle = + account::new_event_handle(publisher); + let received_tokens_only_handle = + account::new_event_handle(publisher); + + // Move all state into the single resource struct + move_to( + publisher, + CCIPReceiverState { + signer_cap, + received_message_handle, + forwarded_tokens_handle, + received_tokens_only_handle + } + ); + } + public fun get_received_message_events(): vector acquires CCIPReceiverState { let state = borrow_global(@ccip_offramp); event::emitted_events_by_handle(&state.received_message_handle) diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move index 29b751d8..17f35994 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move @@ -85,6 +85,8 @@ module burn_mint_token_pool::burn_mint_token_pool { let lock_or_burn_closure = |fa, input| lock_or_burn_v2(fa, input); let release_or_mint_closure = |input| release_or_mint_v2(input); + // If the contract has already been deployed with V1 and needs to be upgraded to V2, + // create a new module token_admin_registry::register_pool_v2( publisher, token_pool_module_name, @@ -810,4 +812,61 @@ module burn_mint_token_pool::burn_mint_token_pool { &borrow_global(state).token_pool_state ) } + + #[test_only] + /// Used for registering the pool with V2 closure-based callbacks. + public fun create_callback_proof(): CallbackProof { + CallbackProof {} + } + + #[test_only] + public fun test_init_v1(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @ccip_burn_mint_pool. + assert!( + object::object_exists(@burn_mint_local_token), + error::invalid_argument(E_INVALID_FUNGIBLE_ASSET) + ); + let metadata = object::address_to_object(@burn_mint_local_token); + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@burn_mint_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"burn_mint_token_pool"; + + // Register the entrypoint with mcms + if (@mcms_register_entrypoints == @0x1) { + register_mcms_entrypoint(publisher, token_pool_module_name); + }; + + token_admin_registry::register_pool( + publisher, + token_pool_module_name, + @burn_mint_local_token, + CallbackProof {} + ); + + // create a resource account to be the owner of the primary FungibleStore we will use. + let (store_signer, store_signer_cap) = + account::create_resource_account(publisher, STORE_OBJECT_SEED); + + // make sure this is a valid fungible asset that is primary fungible store enabled, + // ie. created with primary_fungible_store::create_primary_store_enabled_fungible_asset + primary_fungible_store::ensure_primary_store_exists( + signer::address_of(&store_signer), metadata + ); + + move_to( + publisher, + BurnMintTokenPoolDeployment { + store_signer_cap, + ownable_state: ownable::new(&store_signer, @burn_mint_token_pool), + token_pool_state: token_pool::initialize( + &store_signer, @burn_mint_local_token, vector[] + ) + } + ); + } } diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move new file mode 100644 index 00000000..b4b45012 --- /dev/null +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move @@ -0,0 +1,50 @@ +#[test_only] +module burn_mint_token_pool::upgrade_v2 { + use std::account::{Self}; + use std::error; + use std::fungible_asset::{Metadata}; + use std::object::{Self}; + + use burn_mint_token_pool::burn_mint_token_pool; + + use ccip::token_admin_registry::{Self}; + + const E_INVALID_FUNGIBLE_ASSET: u64 = 1; + + fun init_module(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @burn_mint_token_pool. + assert!( + object::object_exists(@burn_mint_local_token), + error::invalid_argument(E_INVALID_FUNGIBLE_ASSET) + ); + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@burn_mint_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"burn_mint_token_pool"; + + let lock_or_burn_closure = + |fa, input| burn_mint_token_pool::lock_or_burn_v2(fa, input); + let release_or_mint_closure = + |input| burn_mint_token_pool::release_or_mint_v2(input); + + // If the contract has already been deployed with V1 and needs to be upgraded to V2, + // create a new module + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + @burn_mint_local_token, + lock_or_burn_closure, + release_or_mint_closure, + burn_mint_token_pool::create_callback_proof() + ); + } + + #[test_only] + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } +} diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move index 332f9203..e64e3948 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move @@ -968,6 +968,63 @@ module lock_release_token_pool::lock_release_token_pool { init_module(publisher); } + #[test_only] + /// Used for registering the pool with V2 closure-based callbacks. + public fun create_callback_proof(): CallbackProof { + CallbackProof {} + } + + #[test_only] + public fun test_init_v1(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @ccip_lock_release_pool. + assert!( + object::object_exists(@lock_release_local_token), + error::invalid_argument(E_INVALID_FUNGIBLE_ASSET) + ); + let metadata = object::address_to_object(@lock_release_local_token); + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@lock_release_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"lock_release_token_pool"; + + // Register the entrypoint with mcms + if (@mcms_register_entrypoints == @0x1) { + register_mcms_entrypoint(publisher, token_pool_module_name); + }; + + token_admin_registry::register_pool( + publisher, + token_pool_module_name, + @lock_release_local_token, + CallbackProof {} + ); + + // create a resource account to be the owner of the primary FungibleStore we will use. + let (store_signer, store_signer_cap) = + account::create_resource_account(publisher, STORE_OBJECT_SEED); + + // make sure this is a valid fungible asset that is primary fungible store enabled, + // ie. created with primary_fungible_store::create_primary_store_enabled_fungible_asset + primary_fungible_store::ensure_primary_store_exists( + signer::address_of(&store_signer), metadata + ); + + move_to( + publisher, + LockReleaseTokenPoolDeployment { + store_signer_cap, + ownable_state: ownable::new(&store_signer, @lock_release_token_pool), + token_pool_state: token_pool::initialize( + &store_signer, @lock_release_local_token, vector[] + ) + } + ); + } + #[test_only] public fun get_locked_or_burned_events( state: address diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move new file mode 100644 index 00000000..51512a51 --- /dev/null +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move @@ -0,0 +1,50 @@ +#[test_only] +module lock_release_token_pool::upgrade_v2 { + use std::account::{Self}; + use std::error; + use std::fungible_asset::{Metadata}; + use std::object::{Self}; + + use lock_release_token_pool::lock_release_token_pool; + + use ccip::token_admin_registry::{Self}; + + const E_INVALID_FUNGIBLE_ASSET: u64 = 1; + + fun init_module(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @lock_release_token_pool. + assert!( + object::object_exists(@lock_release_local_token), + error::invalid_argument(E_INVALID_FUNGIBLE_ASSET) + ); + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@lock_release_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"lock_release_token_pool"; + + let lock_or_burn_closure = + |fa, input| lock_release_token_pool::lock_or_burn_v2(fa, input); + let release_or_mint_closure = + |input| lock_release_token_pool::release_or_mint_v2(input); + + // If the contract has already been deployed with V1 and needs to be upgraded to V2, + // create a new module + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + @lock_release_local_token, + lock_or_burn_closure, + release_or_mint_closure, + lock_release_token_pool::create_callback_proof() + ); + } + + #[test_only] + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } +} diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move index f5214f2d..4b503cd8 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move @@ -737,4 +737,62 @@ module managed_token_pool::managed_token_pool { public entry fun test_init_module(owner: &signer) { init_module(owner); } + + #[test_only] + /// Used for registering the pool with V2 closure-based callbacks. + public fun create_callback_proof(): CallbackProof { + CallbackProof {} + } + + #[test_only] + + public fun test_init_v1(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @ccip_managed_pool. + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@managed_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"managed_token_pool"; + + // Register the entrypoint with mcms + if (@mcms_register_entrypoints == @0x1) { + register_mcms_entrypoint(publisher, token_pool_module_name); + }; + + let managed_token_address = managed_token::token_metadata(); + token_admin_registry::register_pool( + publisher, + token_pool_module_name, + managed_token_address, + CallbackProof {} + ); + + // create a resource account to be the owner of the primary FungibleStore we will use. + let (store_signer, store_signer_cap) = + account::create_resource_account(publisher, STORE_OBJECT_SEED); + + let metadata = object::address_to_object(managed_token_address); + + // make sure this is a valid fungible asset that is primary fungible store enabled, + // ie. created with primary_fungible_store::create_primary_store_enabled_fungible_asset + primary_fungible_store::ensure_primary_store_exists( + signer::address_of(&store_signer), metadata + ); + + let store_signer = account::create_signer_with_capability(&store_signer_cap); + + let pool = ManagedTokenPoolState { + ownable_state: ownable::new(&store_signer, @managed_token_pool), + store_signer_address: signer::address_of(&store_signer), + store_signer_cap, + token_pool_state: token_pool::initialize( + &store_signer, managed_token_address, vector[] + ) + }; + + move_to(&store_signer, pool); + } } diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move new file mode 100644 index 00000000..77fe1faf --- /dev/null +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move @@ -0,0 +1,44 @@ +#[test_only] +module managed_token_pool::upgrade_v2 { + use std::account::{Self}; + + use managed_token::managed_token; + use managed_token_pool::managed_token_pool; + + use ccip::token_admin_registry::{Self}; + + fun init_module(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @managed_token_pool. + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@managed_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"managed_token_pool"; + + let managed_token_address = managed_token::token_metadata(); + + let lock_or_burn_closure = + |fa, input| managed_token_pool::lock_or_burn_v2(fa, input); + let release_or_mint_closure = + |input| managed_token_pool::release_or_mint_v2(input); + + // If the contract has already been deployed with V1 and needs to be upgraded to V2, + // create a new module + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + managed_token_address, + lock_or_burn_closure, + release_or_mint_closure, + managed_token_pool::create_callback_proof() + ); + } + + #[test_only] + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } +} diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move index b2a285db..309889ef 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move @@ -707,4 +707,59 @@ module regulated_token_pool::regulated_token_pool { public entry fun test_init_module(owner: &signer) { init_module(owner); } + + #[test_only] + /// Used for registering the pool with V2 closure-based callbacks. + public fun create_callback_proof(): CallbackProof { + CallbackProof {} + } + + #[test_only] + public fun test_init_v1(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @regulated_token_pool. + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@regulated_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"regulated_token_pool"; + + // Register the entrypoint with mcms + if (@mcms_register_entrypoints == @0x1) { + register_mcms_entrypoint(publisher, token_pool_module_name); + }; + + let regulated_token_address = regulated_token::token_address(); + token_admin_registry::register_pool( + publisher, + token_pool_module_name, + regulated_token_address, + CallbackProof {} + ); + + // create a resource account to be the owner of the primary FungibleStore we will use. + let (store_signer, store_signer_cap) = + account::create_resource_account(publisher, STORE_OBJECT_SEED); + + let metadata = object::address_to_object(regulated_token_address); + + // make sure this is a valid fungible asset that is primary fungible store enabled, + // ie. created with primary_fungible_store::create_primary_store_enabled_fungible_asset + primary_fungible_store::ensure_primary_store_exists( + signer::address_of(&store_signer), metadata + ); + + let pool = RegulatedTokenPoolState { + ownable_state: ownable::new(&store_signer, @regulated_token_pool), + store_signer_address: signer::address_of(&store_signer), + store_signer_cap, + token_pool_state: token_pool::initialize( + &store_signer, regulated_token_address, vector[] + ) + }; + + move_to(&store_signer, pool); + } } diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move new file mode 100644 index 00000000..92a55cc9 --- /dev/null +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move @@ -0,0 +1,44 @@ +#[test_only] +module regulated_token_pool::upgrade_v2 { + use std::account::{Self}; + + use regulated_token::regulated_token::{Self}; + use regulated_token_pool::regulated_token_pool; + + use ccip::token_admin_registry::{Self}; + + fun init_module(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @regulated_token_pool. + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@regulated_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"regulated_token_pool"; + + let regulated_token_address = regulated_token::token_address(); + + let lock_or_burn_closure = + |fa, input| regulated_token_pool::lock_or_burn_v2(fa, input); + let release_or_mint_closure = + |input| regulated_token_pool::release_or_mint_v2(input); + + // If the contract has already been deployed with V1 and needs to be upgraded to V2, + // create a new module + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + regulated_token_address, + lock_or_burn_closure, + release_or_mint_closure, + regulated_token_pool::create_callback_proof() + ); + } + + #[test_only] + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } +} diff --git a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move index c5846d31..f0b0a561 100644 --- a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move +++ b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move @@ -1045,4 +1045,10 @@ module usdc_token_pool::usdc_token_pool { public fun test_init_module(publisher: &signer) { init_module(publisher); } + + #[test_only] + /// Used for registering the pool with V2 closure-based callbacks. + public fun create_callback_proof(): CallbackProof { + CallbackProof {} + } } diff --git a/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move new file mode 100644 index 00000000..c1fc4424 --- /dev/null +++ b/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move @@ -0,0 +1,50 @@ +#[test_only] +module usdc_token_pool::upgrade_v2 { + use std::account::{Self}; + use std::error; + use std::fungible_asset::{Metadata}; + use std::object::{Self}; + + use usdc_token_pool::usdc_token_pool; + + use ccip::token_admin_registry::{Self}; + + const E_INVALID_FUNGIBLE_ASSET: u64 = 1; + + fun init_module(publisher: &signer) { + // register the pool on deployment, because in the case of object code deployment, + // this is the only time we have a signer ref to @usdc_token_pool. + assert!( + object::object_exists(@local_token), + error::invalid_argument(E_INVALID_FUNGIBLE_ASSET) + ); + + // create an Account on the object for event handles. + account::create_account_if_does_not_exist(@usdc_token_pool); + + // the name of this module. if incorrect, callbacks will fail to be registered and + // register_pool will revert. + let token_pool_module_name = b"usdc_token_pool"; + + let lock_or_burn_closure = |fa, input| usdc_token_pool::lock_or_burn_v2( + fa, input + ); + let release_or_mint_closure = |input| usdc_token_pool::release_or_mint_v2(input); + + // If the contract has already been deployed with V1 and needs to be upgraded to V2, + // create a new module + token_admin_registry::register_pool_v2( + publisher, + token_pool_module_name, + @local_token, + lock_or_burn_closure, + release_or_mint_closure, + usdc_token_pool::create_callback_proof() + ); + } + + #[test_only] + public fun test_init_module(publisher: &signer) { + init_module(publisher); + } +} From 51229cf391d819f1fb7daf5fb72eebd262d16f12 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Fri, 28 Nov 2025 13:40:07 -0500 Subject: [PATCH 07/13] Update tests and pool modules to handle v1 and v2 ccip receive callback upgrade --- .../offramp_burn_mint_receiver_test.move | 18 +++-- .../offramp_lock_release_receiver_test.move | 18 +++-- .../tests/offramp_managed_receiver_test.move | 18 +++-- .../offramp_regulated_receiver_test.move | 9 ++- .../ccip/ccip_offramp/tests/offramp_test.move | 66 +++++++++++++------ 5 files changed, 89 insertions(+), 40 deletions(-) diff --git a/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move index d96b0a70..c3127672 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_burn_mint_receiver_test.move @@ -144,7 +144,8 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false // token is non-dispatchable + false, // token is non-dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -220,7 +221,8 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false // token is non-dispatchable + false, // token is non-dispatchable + false // use_v1_init ); setup_mock_ccip_receiver(owner, ccip_offramp); @@ -287,7 +289,8 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false // token is non-dispatchable + false, // token is non-dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -374,7 +377,8 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - true // token is dispatchable + true, // token is dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -450,7 +454,8 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - true // token is dispatchable + true, // token is dispatchable + false // use_v1_init ); setup_mock_ccip_receiver(owner, ccip_offramp); @@ -517,7 +522,8 @@ module ccip_offramp::offramp_burn_mint_receiver_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - true // token is dispatchable + true, // token is dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); diff --git a/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move index 0438d265..564ec8e4 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_lock_release_receiver_test.move @@ -144,7 +144,8 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -220,7 +221,8 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - false // token is non-dispatchable + false, // token is non-dispatchable + false // use_v1_init ); setup_mock_ccip_receiver(owner, ccip_offramp); @@ -287,7 +289,8 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -374,7 +377,8 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -450,7 +454,8 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); setup_mock_ccip_receiver(owner, ccip_offramp); @@ -517,7 +522,8 @@ module ccip_offramp::offramp_lock_release_receiver_test { regulated_token, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - true // token is dispatchable + true, // token is dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); diff --git a/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move index 58bf1305..43a54d94 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_managed_receiver_test.move @@ -149,7 +149,8 @@ module ccip_offramp::offramp_managed_receiver_test { regulated_token, MANAGED_TOKEN_POOL, MANAGED_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -242,7 +243,8 @@ module ccip_offramp::offramp_managed_receiver_test { regulated_token, MANAGED_TOKEN_POOL, MANAGED_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); setup_mock_ccip_receiver(owner, ccip_offramp); @@ -318,7 +320,8 @@ module ccip_offramp::offramp_managed_receiver_test { regulated_token, MANAGED_TOKEN_POOL, MANAGED_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -414,7 +417,8 @@ module ccip_offramp::offramp_managed_receiver_test { regulated_token, MANAGED_TOKEN_POOL, MANAGED_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -508,7 +512,8 @@ module ccip_offramp::offramp_managed_receiver_test { regulated_token, MANAGED_TOKEN_POOL, MANAGED_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); setup_mock_ccip_receiver(owner, ccip_offramp); @@ -575,7 +580,8 @@ module ccip_offramp::offramp_managed_receiver_test { regulated_token, MANAGED_TOKEN_POOL, MANAGED_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); diff --git a/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move b/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move index 6f6f4fa7..f9bdb9f6 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_regulated_receiver_test.move @@ -150,7 +150,8 @@ module ccip_offramp::offramp_regulated_receiver_test { regulated_token, REGULATED_TOKEN_POOL, REGULATED_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -234,7 +235,8 @@ module ccip_offramp::offramp_regulated_receiver_test { regulated_token, REGULATED_TOKEN_POOL, REGULATED_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); setup_mock_ccip_receiver(owner, ccip_offramp); @@ -301,7 +303,8 @@ module ccip_offramp::offramp_regulated_receiver_test { regulated_token, REGULATED_TOKEN_POOL, REGULATED_TOKEN_SEED, - true + true, // is_dispatchable + false // use_v1_init ); let token_addr = object::object_address(&token_obj); diff --git a/contracts/ccip/ccip_offramp/tests/offramp_test.move b/contracts/ccip/ccip_offramp/tests/offramp_test.move index a51c14ad..cbf5b7ff 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_test.move @@ -70,6 +70,8 @@ module ccip_offramp::offramp_test { timestamp::update_global_time_for_test_secs(timestamp_seconds); } + /// use_v1_init: if true, uses test_init_v1 for token pools (V1 compatibility mode) + /// if false, uses test_init_module for token pools (V2 mode, default) public fun setup( aptos_framework: &signer, ccip: &signer, @@ -83,7 +85,8 @@ module ccip_offramp::offramp_test { regulated_token: &signer, pool_type: u8, seed: vector, - is_dispatchable: bool + is_dispatchable: bool, + use_v1_init: bool ): (address, Object) { let owner_addr = signer::address_of(owner); account::create_account_for_test(signer::address_of(burn_mint_token_pool)); @@ -147,7 +150,8 @@ module ccip_offramp::offramp_test { regulated_token, pool_type, seed, - is_dispatchable + is_dispatchable, + use_v1_init ); // Initialize fee quoter @@ -182,7 +186,8 @@ module ccip_offramp::offramp_test { regulated_token: &signer, pool_type: u8, seed: vector, - is_dispatchable: bool + is_dispatchable: bool, + use_v1_init: bool ): (Object, address) { let constructor_ref = object::create_named_object(owner, seed); @@ -214,7 +219,11 @@ module ccip_offramp::offramp_test { let token_addr = object::object_address(&metadata); if (pool_type == BURN_MINT_TOKEN_POOL) { - burn_mint_token_pool::test_init_module(burn_mint_token_pool); + if (use_v1_init) { + burn_mint_token_pool::test_init_v1(burn_mint_token_pool); + } else { + burn_mint_token_pool::test_init_module(burn_mint_token_pool); + }; burn_mint_token_pool::initialize(owner, burn_ref, mint_ref); burn_mint_token_pool::apply_chain_updates( owner, @@ -244,7 +253,11 @@ module ccip_offramp::offramp_test { signer::address_of(burn_mint_token_pool) ); } else if (pool_type == LOCK_RELEASE_TOKEN_POOL) { - lock_release_token_pool::test_init_module(lock_release_token_pool); + if (use_v1_init) { + lock_release_token_pool::test_init_v1(lock_release_token_pool); + } else { + lock_release_token_pool::test_init_module(lock_release_token_pool); + }; lock_release_token_pool::initialize( owner, option::some(transfer_ref), @@ -366,7 +379,11 @@ module ccip_offramp::offramp_test { string::utf8(b"https://regulatedtoken.com") ); - regulated_token_pool::test_init_module(regulated_token_pool); + if (use_v1_init) { + regulated_token_pool::test_init_v1(regulated_token_pool); + } else { + regulated_token_pool::test_init_module(regulated_token_pool); + }; regulated_token_pool::apply_chain_updates( owner, vector[], @@ -426,7 +443,7 @@ module ccip_offramp::offramp_test { (metadata, token_addr) } - fun initialize_offramp(owner: &signer): address { + public fun initialize_offramp(owner: &signer): address { offramp::initialize( owner, DEST_CHAIN_SELECTOR, @@ -440,7 +457,7 @@ module ccip_offramp::offramp_test { offramp::get_state_address() } - fun setup_fee_quoter( + public fun setup_fee_quoter( owner: &signer, ccip_offramp: &signer, token_addr: address ) { fee_quoter::apply_fee_token_updates(owner, vector[], vector[token_addr]); @@ -547,7 +564,8 @@ module ccip_offramp::offramp_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); // Verify initialization was successful @@ -968,7 +986,8 @@ module ccip_offramp::offramp_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let config_digest = x"000aed76a87f048dab766bc14ecdbb966f4253e309d742585062a75abfc16c38"; @@ -1162,7 +1181,8 @@ module ccip_offramp::offramp_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let merkle_root = @@ -1219,7 +1239,8 @@ module ccip_offramp::offramp_test { receiver, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ) } @@ -1265,7 +1286,8 @@ module ccip_offramp::offramp_test { receiver, LOCK_RELEASE_TOKEN_POOL, LOCK_RELEASE_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ) } @@ -1285,7 +1307,8 @@ module ccip_offramp::offramp_test { receiver: &signer, pool_type: u8, token_seed: vector, - is_dispatchable: bool + is_dispatchable: bool, + use_v1_init: bool ) { let (_owner_addr, token_obj) = setup( @@ -1301,7 +1324,8 @@ module ccip_offramp::offramp_test { regulated_token, pool_type, token_seed, - is_dispatchable + is_dispatchable, + use_v1_init ); let token_addr = object::object_address(&token_obj); @@ -1435,7 +1459,8 @@ module ccip_offramp::offramp_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let new_owner = signer::address_of(aptos_framework); account::create_account_for_test(new_owner); @@ -1488,7 +1513,8 @@ module ccip_offramp::offramp_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); let latest_price_sequence_number = offramp::get_latest_price_sequence_number(); @@ -1542,7 +1568,8 @@ module ccip_offramp::offramp_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); // E_UNKNOWN_SOURCE_CHAIN_SELECTOR @@ -1589,7 +1616,8 @@ module ccip_offramp::offramp_test { regulated_token, BURN_MINT_TOKEN_POOL, BURN_MINT_TOKEN_SEED, - false + false, // is_dispatchable + false // use_v1_init ); // E_INVALID_ROOT From 8f247b3d484e13e97fdfcbf14255307d1b4faed2 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Fri, 28 Nov 2025 13:40:56 -0500 Subject: [PATCH 08/13] Refactor lock_or_burn_v2 return values --- .../sources/burn_mint_token_pool.move | 19 ++--- .../sources/lock_release_token_pool.move | 8 +-- .../sources/managed_token_pool.move | 71 ++----------------- 3 files changed, 15 insertions(+), 83 deletions(-) diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move index 17f35994..ef9bfcd5 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move @@ -7,9 +7,9 @@ module burn_mint_token_pool::burn_mint_token_pool { use std::option::{Self, Option}; use std::signer; use std::string::{Self, String}; - use aptos_framework::fungible_asset::{BurnRef, MintRef}; + use std::fungible_asset::{BurnRef, MintRef}; - use ccip::token_admin_registry::{Self, ReleaseOrMintInputV1}; + use ccip::token_admin_registry::{Self, ReleaseOrMintInputV1, LockOrBurnInputV1}; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -86,7 +86,7 @@ module burn_mint_token_pool::burn_mint_token_pool { let release_or_mint_closure = |input| release_or_mint_v2(input); // If the contract has already been deployed with V1 and needs to be upgraded to V2, - // create a new module + // create a new module and pass in `publisher` from `fun init_module(publisher: &signer)` token_admin_registry::register_pool_v2( publisher, token_pool_module_name, @@ -380,12 +380,11 @@ module burn_mint_token_pool::burn_mint_token_pool { } public fun lock_or_burn_v2( - fa: FungibleAsset, input: token_admin_registry::LockOrBurnInputV1 - ): token_admin_registry::LockOrBurnOutputV1 acquires BurnMintTokenPoolState { + fa: FungibleAsset, input: LockOrBurnInputV1 + ): (vector, vector) acquires BurnMintTokenPoolState { let pool = borrow_pool_mut(); let fa_amount = fungible_asset::amount(&fa); - // Validate the operation (same as V1) let dest_token_address = token_pool::validate_lock_or_burn( &mut pool.token_pool_state, @@ -406,14 +405,9 @@ module burn_mint_token_pool::burn_mint_token_pool { &mut pool.token_pool_state, fa_amount, remote_chain_selector ); - // Return output directly (no need to set in registry!) - token_admin_registry::new_lock_or_burn_output_v1( - dest_token_address, - b"" // empty dest_pool_data for burn/mint pools - ) + (dest_token_address, token_pool::encode_local_decimals(&pool.token_pool_state)) } - /// V2 release_or_mint callback - receives input directly as parameter public fun release_or_mint_v2( input: ReleaseOrMintInputV1 ): (FungibleAsset, u64) acquires BurnMintTokenPoolState { @@ -870,3 +864,4 @@ module burn_mint_token_pool::burn_mint_token_pool { ); } } + diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move index e64e3948..fbc99899 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move @@ -447,7 +447,7 @@ module lock_release_token_pool::lock_release_token_pool { public fun lock_or_burn_v2( fa: FungibleAsset, input: token_admin_registry::LockOrBurnInputV1 - ): token_admin_registry::LockOrBurnOutputV1 acquires LockReleaseTokenPoolState { + ): (vector, vector) acquires LockReleaseTokenPoolState { let pool = borrow_pool_mut(); let fa_amount = fungible_asset::amount(&fa); @@ -469,10 +469,7 @@ module lock_release_token_pool::lock_release_token_pool { &mut pool.token_pool_state, fa_amount, remote_chain_selector ); - token_admin_registry::new_lock_or_burn_output_v1( - dest_token_address, - token_pool::encode_local_decimals(&pool.token_pool_state) - ) + (dest_token_address, token_pool::encode_local_decimals(&pool.token_pool_state)) } public fun release_or_mint_v2( @@ -1043,3 +1040,4 @@ module lock_release_token_pool::lock_release_token_pool { ) } } + diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move index 4b503cd8..d5230200 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move @@ -10,12 +10,7 @@ module managed_token_pool::managed_token_pool { use managed_token::managed_token; - use ccip::token_admin_registry::{ - Self, - LockOrBurnInputV1, - LockOrBurnOutputV1, - ReleaseOrMintInputV1 - }; + use ccip::token_admin_registry::{Self, LockOrBurnInputV1, ReleaseOrMintInputV1}; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -349,7 +344,7 @@ module managed_token_pool::managed_token_pool { public fun lock_or_burn_v2( fa: FungibleAsset, input: LockOrBurnInputV1 - ): LockOrBurnOutputV1 { + ): (vector, vector) { let pool = borrow_pool_mut(); let fa_amount = fungible_asset::amount(&fa); @@ -363,9 +358,6 @@ module managed_token_pool::managed_token_pool { fa_amount ); - // Construct lock_or_burn output before we lose access to fa - let dest_pool_data = token_pool::encode_local_decimals(&pool.token_pool_state); - // Burn the funds let store = primary_fungible_store::ensure_primary_store_exists( @@ -382,10 +374,7 @@ module managed_token_pool::managed_token_pool { &mut pool.token_pool_state, fa_amount, remote_chain_selector ); - token_admin_registry::new_lock_or_burn_output_v1( - dest_token_address, - token_pool::encode_local_decimals(&pool.token_pool_state) - ) + (dest_token_address, token_pool::encode_local_decimals(&pool.token_pool_state)) } public fun release_or_mint_v2(input: ReleaseOrMintInputV1): (FungibleAsset, u64) { @@ -407,6 +396,7 @@ module managed_token_pool::managed_token_pool { let signer = &account::create_signer_with_capability(&pool.store_signer_cap); managed_token::mint(signer, pool.store_signer_address, local_amount); + // Calling into `fungible_asset::withdraw` works as managed token is not dispatchable let fa = fungible_asset::withdraw(signer, store, local_amount); let recipient = token_admin_registry::get_release_or_mint_receiver(&input); let remote_chain_selector = @@ -743,56 +733,5 @@ module managed_token_pool::managed_token_pool { public fun create_callback_proof(): CallbackProof { CallbackProof {} } - - #[test_only] - - public fun test_init_v1(publisher: &signer) { - // register the pool on deployment, because in the case of object code deployment, - // this is the only time we have a signer ref to @ccip_managed_pool. - - // create an Account on the object for event handles. - account::create_account_if_does_not_exist(@managed_token_pool); - - // the name of this module. if incorrect, callbacks will fail to be registered and - // register_pool will revert. - let token_pool_module_name = b"managed_token_pool"; - - // Register the entrypoint with mcms - if (@mcms_register_entrypoints == @0x1) { - register_mcms_entrypoint(publisher, token_pool_module_name); - }; - - let managed_token_address = managed_token::token_metadata(); - token_admin_registry::register_pool( - publisher, - token_pool_module_name, - managed_token_address, - CallbackProof {} - ); - - // create a resource account to be the owner of the primary FungibleStore we will use. - let (store_signer, store_signer_cap) = - account::create_resource_account(publisher, STORE_OBJECT_SEED); - - let metadata = object::address_to_object(managed_token_address); - - // make sure this is a valid fungible asset that is primary fungible store enabled, - // ie. created with primary_fungible_store::create_primary_store_enabled_fungible_asset - primary_fungible_store::ensure_primary_store_exists( - signer::address_of(&store_signer), metadata - ); - - let store_signer = account::create_signer_with_capability(&store_signer_cap); - - let pool = ManagedTokenPoolState { - ownable_state: ownable::new(&store_signer, @managed_token_pool), - store_signer_address: signer::address_of(&store_signer), - store_signer_cap, - token_pool_state: token_pool::initialize( - &store_signer, managed_token_address, vector[] - ) - }; - - move_to(&store_signer, pool); - } } + From 55d730f7c717e568f298533e1b7ffb254bd28ff6 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Fri, 28 Nov 2025 13:42:22 -0500 Subject: [PATCH 09/13] Add V1 V2 Compatibility tests using function closures --- .../token_admin_registry.go | 13 +- .../ccip/sources/token_admin_registry.move | 32 +- .../tests/mock/mock_ccip_receiver.move | 54 +- .../ccip_offramp/tests/mock/mock_token.move | 7 +- .../offramp_v1_v2_compatibility_test.move | 562 +++++++++++++++++ .../tests/{ => mock}/mock_token.move | 12 +- .../tests/onramp_migration_test.move | 3 +- .../ccip/ccip_onramp/tests/onramp_test.move | 74 ++- .../onramp_v1_v2_pool_compatibility_test.move | 595 ++++++++++++++++++ .../tests/upgrade_v2.move | 3 +- .../tests/upgrade_v2.move | 3 +- .../managed_token_pool/tests/upgrade_v2.move | 3 +- .../sources/regulated_token_pool.move | 8 +- .../tests/upgrade_v2.move | 3 +- .../sources/usdc_token_pool.move | 14 +- .../usdc_token_pool/tests/upgrade_v2.move | 3 +- 16 files changed, 1287 insertions(+), 102 deletions(-) create mode 100644 contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move rename contracts/ccip/ccip_onramp/tests/{ => mock}/mock_token.move (77%) create mode 100644 contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move diff --git a/bindings/ccip/token_admin_registry/token_admin_registry.go b/bindings/ccip/token_admin_registry/token_admin_registry.go index dad08031..ea18548a 100644 --- a/bindings/ccip/token_admin_registry/token_admin_registry.go +++ b/bindings/ccip/token_admin_registry/token_admin_registry.go @@ -57,7 +57,6 @@ type TokenAdminRegistryEncoder interface { ProposeAdministrator(localToken aptos.AccountAddress, administrator aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) TransferAdminRole(localToken aptos.AccountAddress, newAdmin aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) AcceptAdminRole(localToken aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) - NewLockOrBurnOutputV1(destTokenAddress []byte, destPoolData []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) StartLockOrBurn(tokenPoolAddress aptos.AccountAddress, sender aptos.AccountAddress, remoteChainSelector uint64, receiver []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) FinishLockOrBurn(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) FinishReleaseOrMint(tokenPoolAddress aptos.AccountAddress) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) @@ -65,7 +64,7 @@ type TokenAdminRegistryEncoder interface { RegisterMCMSEntrypoint() (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) } -const FunctionInfo = `[{"package":"ccip","module":"token_admin_registry","name":"accept_admin_role","parameters":[{"name":"local_token","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_release_or_mint","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"new_lock_or_burn_output_v1","parameters":[{"name":"dest_token_address","type":"vector\u003cu8\u003e"},{"name":"dest_pool_data","type":"vector\u003cu8\u003e"}]},{"package":"ccip","module":"token_admin_registry","name":"propose_administrator","parameters":[{"name":"local_token","type":"address"},{"name":"administrator","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"register_mcms_entrypoint","parameters":null},{"package":"ccip","module":"token_admin_registry","name":"set_pool","parameters":[{"name":"local_token","type":"address"},{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"start_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"},{"name":"sender","type":"address"},{"name":"remote_chain_selector","type":"u64"},{"name":"receiver","type":"vector\u003cu8\u003e"}]},{"package":"ccip","module":"token_admin_registry","name":"transfer_admin_role","parameters":[{"name":"local_token","type":"address"},{"name":"new_admin","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"unregister_pool","parameters":[{"name":"local_token","type":"address"}]}]` +const FunctionInfo = `[{"package":"ccip","module":"token_admin_registry","name":"accept_admin_role","parameters":[{"name":"local_token","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"finish_release_or_mint","parameters":[{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"mcms_entrypoint","parameters":[{"name":"_metadata","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"propose_administrator","parameters":[{"name":"local_token","type":"address"},{"name":"administrator","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"register_mcms_entrypoint","parameters":null},{"package":"ccip","module":"token_admin_registry","name":"set_pool","parameters":[{"name":"local_token","type":"address"},{"name":"token_pool_address","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"start_lock_or_burn","parameters":[{"name":"token_pool_address","type":"address"},{"name":"sender","type":"address"},{"name":"remote_chain_selector","type":"u64"},{"name":"receiver","type":"vector\u003cu8\u003e"}]},{"package":"ccip","module":"token_admin_registry","name":"transfer_admin_role","parameters":[{"name":"local_token","type":"address"},{"name":"new_admin","type":"address"}]},{"package":"ccip","module":"token_admin_registry","name":"unregister_pool","parameters":[{"name":"local_token","type":"address"}]}]` func NewTokenAdminRegistry(address aptos.AccountAddress, client aptos.AptosRpcClient) TokenAdminRegistryInterface { contract := bind.NewBoundContract(address, "ccip", "token_admin_registry", client) @@ -565,16 +564,6 @@ func (c tokenAdminRegistryEncoder) AcceptAdminRole(localToken aptos.AccountAddre }) } -func (c tokenAdminRegistryEncoder) NewLockOrBurnOutputV1(destTokenAddress []byte, destPoolData []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { - return c.BoundContract.Encode("new_lock_or_burn_output_v1", nil, []string{ - "vector", - "vector", - }, []any{ - destTokenAddress, - destPoolData, - }) -} - func (c tokenAdminRegistryEncoder) StartLockOrBurn(tokenPoolAddress aptos.AccountAddress, sender aptos.AccountAddress, remoteChainSelector uint64, receiver []byte) (bind.ModuleInformation, string, []aptos.TypeTag, [][]byte, error) { return c.BoundContract.Encode("start_lock_or_burn", nil, []string{ "address", diff --git a/contracts/ccip/ccip/sources/token_admin_registry.move b/contracts/ccip/ccip/sources/token_admin_registry.move index a7ec5063..8b644215 100644 --- a/contracts/ccip/ccip/sources/token_admin_registry.move +++ b/contracts/ccip/ccip/sources/token_admin_registry.move @@ -4,7 +4,7 @@ module ccip::token_admin_registry { use std::error; use std::event::{Self, EventHandle}; use std::function_info::{Self, FunctionInfo}; - use std::fungible_asset::{Self, Metadata, FungibleStore}; + use std::fungible_asset::{Self, Metadata, FungibleStore, FungibleAsset}; use std::object::{Self, Object, ExtendRef, TransferRef}; use std::option::{Self, Option}; use std::signer; @@ -87,19 +87,10 @@ module ccip::token_admin_registry { destination_amount: u64 } - public fun new_lock_or_burn_output_v1( - dest_token_address: vector, dest_pool_data: vector - ): LockOrBurnOutputV1 { - LockOrBurnOutputV1 { dest_token_address, dest_pool_data } - } - struct TokenPoolCallbacks has drop, copy, store { - lock_or_burn: | - fungible_asset::FungibleAsset, - LockOrBurnInputV1 - | LockOrBurnOutputV1 has drop + copy + store, - release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop - + copy + store + lock_or_burn: |FungibleAsset, LockOrBurnInputV1| (vector, vector) has drop + + copy + store, + release_or_mint: |ReleaseOrMintInputV1| (FungibleAsset, u64) has drop + copy + store } struct TokenPoolConfig has key { @@ -432,12 +423,9 @@ module ccip::token_admin_registry { token_pool_account: &signer, token_pool_module_name: vector, local_token: address, - lock_or_burn: | - fungible_asset::FungibleAsset, - LockOrBurnInputV1 - | LockOrBurnOutputV1 has drop + copy + store, - release_or_mint: |ReleaseOrMintInputV1| (fungible_asset::FungibleAsset, u64) has drop + lock_or_burn: |FungibleAsset, LockOrBurnInputV1| (vector, vector) has drop + copy + store, + release_or_mint: |ReleaseOrMintInputV1| (FungibleAsset, u64) has drop + copy + store, _proof: ProofType ) { let token_pool_address = signer::address_of(token_pool_account); @@ -1090,9 +1078,8 @@ module ccip::token_admin_registry { let pool_config = &TokenPoolConfig[token_pool_address]; let input = LockOrBurnInputV1 { sender, remote_chain_selector, receiver }; - let output = (pool_config.callbacks.lock_or_burn) - (fa, input); - (output.dest_token_address, output.dest_pool_data) + (pool_config.callbacks.lock_or_burn) + (fa, input) } public(friend) fun release_or_mint_v2( @@ -1106,7 +1093,7 @@ module ccip::token_admin_registry { source_pool_address: vector, source_pool_data: vector, offchain_token_data: vector - ): (fungible_asset::FungibleAsset, u64) acquires TokenPoolConfig { + ): (FungibleAsset, u64) acquires TokenPoolConfig { auth::assert_is_allowed_offramp(signer::address_of(caller)); let pool_config = &TokenPoolConfig[token_pool_address]; @@ -1351,3 +1338,4 @@ module ccip::token_admin_registry { assert!(has_more); } } + diff --git a/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move index 68321e44..512f14d7 100644 --- a/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move +++ b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move @@ -109,16 +109,11 @@ module ccip_offramp::mock_ccip_receiver { struct CCIPReceiverProof has drop {} - /// This function should only be used with non-dispatchable tokens, - /// as it is currently incompatible with dispatchable tokens. - public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { + public fun ccip_receive_v2(message: client::Any2AptosMessage) acquires CCIPReceiverState { /* load state and rebuild a signer for the resource account */ let state = borrow_global_mut(@ccip_offramp); let state_signer = account::create_signer_with_capability(&state.signer_cap); - let message = - receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); - let data = client::get_data(&message); let dest_token_amounts = client::get_dest_token_amounts(&message); @@ -134,19 +129,12 @@ module ccip_offramp::mock_ccip_receiver { // Implement the token transfer logic here let fa_token = object::address_to_object(token_addr); - let fa_store_sender = - primary_fungible_store::ensure_primary_store_exists( - @ccip_offramp, fa_token - ); - let fa_store_receiver = - primary_fungible_store::ensure_primary_store_exists( - final_recipient, fa_token - ); - fungible_asset::transfer( + // Must use primary_fungible_store::transfer as token may be dispatchable + primary_fungible_store::transfer( &state_signer, - fa_store_sender, - fa_store_receiver, + fa_token, + final_recipient, amount ); }; @@ -155,9 +143,7 @@ module ccip_offramp::mock_ccip_receiver { event::emit_event( &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } ); - } else if (data.length() != 0) { - // Convert the vector to a string let message = string::utf8(data); @@ -181,15 +167,19 @@ module ccip_offramp::mock_ccip_receiver { if (data == b"abort") { abort 1 }; - - option::none() } - public fun ccip_receive_v2(message: client::Any2AptosMessage) acquires CCIPReceiverState { + #[deprecated] + /// Legacy V1 receive function, use ccip_receive_v2 as this supports dispatchable tokens + /// Only switch to v2 once TokenPools are migrated to V2 + public fun ccip_receive(_metadata: Object): Option acquires CCIPReceiverState { /* load state and rebuild a signer for the resource account */ let state = borrow_global_mut(@ccip_offramp); let state_signer = account::create_signer_with_capability(&state.signer_cap); + let message = + receiver_registry::get_receiver_input(@ccip_offramp, CCIPReceiverProof {}); + let data = client::get_data(&message); let dest_token_amounts = client::get_dest_token_amounts(&message); @@ -205,12 +195,19 @@ module ccip_offramp::mock_ccip_receiver { // Implement the token transfer logic here let fa_token = object::address_to_object(token_addr); + let fa_store_sender = + primary_fungible_store::ensure_primary_store_exists( + @ccip_offramp, fa_token + ); + let fa_store_receiver = + primary_fungible_store::ensure_primary_store_exists( + final_recipient, fa_token + ); - // Must use primary_fungible_store::transfer as token may be dispatchable - primary_fungible_store::transfer( + fungible_asset::transfer( &state_signer, - fa_token, - final_recipient, + fa_store_sender, + fa_store_receiver, amount ); }; @@ -219,7 +216,9 @@ module ccip_offramp::mock_ccip_receiver { event::emit_event( &mut state.forwarded_tokens_handle, ForwardedTokens { final_recipient } ); + } else if (data.length() != 0) { + // Convert the vector to a string let message = string::utf8(data); @@ -243,6 +242,8 @@ module ccip_offramp::mock_ccip_receiver { if (data == b"abort") { abort 1 }; + + option::none() } public entry fun withdraw_token( @@ -314,3 +315,4 @@ module ccip_offramp::mock_ccip_receiver { event.message } } + diff --git a/contracts/ccip/ccip_offramp/tests/mock/mock_token.move b/contracts/ccip/ccip_offramp/tests/mock/mock_token.move index 4c6d292c..08293318 100644 --- a/contracts/ccip/ccip_offramp/tests/mock/mock_token.move +++ b/contracts/ccip/ccip_offramp/tests/mock/mock_token.move @@ -33,17 +33,12 @@ module ccip_offramp::mock_token { public fun deposit( store: Object, fa: FungibleAsset, transfer_ref: &TransferRef ) { - std::debug::print(&std::string::utf8(b"custom lock_or_burn called")); fungible_asset::deposit_with_ref(transfer_ref, store, fa); - std::debug::print(&std::string::utf8(b"custom lock_or_burn done")); } public fun withdraw( store: Object, amount: u64, transfer_ref: &TransferRef ): FungibleAsset { - std::debug::print(&std::string::utf8(b"custom release_or_mint called")); - let fa = fungible_asset::withdraw_with_ref(transfer_ref, store, amount); - std::debug::print(&std::string::utf8(b"custom release_or_mint done")); - fa + fungible_asset::withdraw_with_ref(transfer_ref, store, amount) } } diff --git a/contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move b/contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move new file mode 100644 index 00000000..ddba6e79 --- /dev/null +++ b/contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move @@ -0,0 +1,562 @@ +#[test_only] +/// Verifies V1 → V2 migration path and that both can coexist +module ccip_offramp::offramp_v1_v2_compatibility_test { + use std::signer; + use std::object; + use std::primary_fungible_store; + use std::timestamp; + + use ccip_offramp::offramp; + use ccip_offramp::offramp_test; + use ccip_offramp::mock_ccip_receiver; + use ccip::receiver_registry; + use ccip::token_admin_registry; + use ccip::merkle_proof; + + use burn_mint_token_pool::upgrade_v2; + + const BURN_MINT_TOKEN_POOL: u8 = 0; + const BURN_MINT_TOKEN_SEED: vector = b"TestToken"; + const EVM_SOURCE_CHAIN_SELECTOR: u64 = 909606746561742123; + const DEST_CHAIN_SELECTOR: u64 = 743186221051783445; + const MOCK_EVM_ADDRESS_VECTOR: vector = x"4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97"; + const ONRAMP_ADDRESS: vector = x"47a1f0a819457f01153f35c6b6b0d42e2e16e91e"; + + // ============================================ + // Test Helper Functions + // ============================================ + + fun create_and_execute_message( + message_id: vector, + sequence_number: u64, + receiver: address, + data: vector, + token_transfers: vector, + owner: &signer + ) { + // Configure source chain if first message + if (sequence_number == 0) { + offramp::apply_source_chain_config_updates( + owner, + vector[EVM_SOURCE_CHAIN_SELECTOR], + vector[true], // is_enabled + vector[true], // is_rmn_verification_disabled + vector[ONRAMP_ADDRESS] + ); + }; + + let nonce: u64 = 0; + let sender = x"d87929a32cf0cbdc9e2d07ffc7c33344079de727"; + let gas_limit: u256 = 100000; + + let header = + offramp::test_create_ramp_message_header( + message_id, + EVM_SOURCE_CHAIN_SELECTOR, + DEST_CHAIN_SELECTOR, + sequence_number, + nonce + ); + + let message = + offramp::test_create_any2aptos_ramp_message( + header, + sender, + data, + receiver, + gas_limit, + token_transfers + ); + + let metadata_hash = + offramp::test_calculate_metadata_hash( + EVM_SOURCE_CHAIN_SELECTOR, DEST_CHAIN_SELECTOR, ONRAMP_ADDRESS + ); + + let hashed_leaf = offramp::test_calculate_message_hash(&message, metadata_hash); + let proofs = vector[]; + let root = merkle_proof::merkle_root(hashed_leaf, proofs); + + // Commit root (with timestamp in the past to allow execution) + offramp::test_add_root(root, timestamp::now_seconds() - 3700); + + // Create execution report + let offchain_token_data: vector> = vector[vector[]]; + let execution_report = + offramp::test_create_execution_report( + EVM_SOURCE_CHAIN_SELECTOR, + message, + offchain_token_data, + vector[] + ); + + offramp::test_execute_single_report(execution_report); + + // Verify execution state is SUCCESS (2) + let execution_state = + offramp::get_execution_state(EVM_SOURCE_CHAIN_SELECTOR, sequence_number); + assert!(execution_state == 2); + } + + // ============================================ + // Test 1: V1 Receiver Works (Baseline) + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_v1_receiver_works( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + // Setup with V1 pool (use_v1_init = true) + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + // Setup receiver (need receiver_registry initialized) + receiver_registry::init_module_for_testing(owner); + mock_ccip_receiver::test_init_state_only(ccip_offramp); + + // Register as V1 (dispatchable FA mode) + mock_ccip_receiver::register_as_v1(ccip_offramp); + + // Verify V1 receiver registered (not V2) + assert!( + !receiver_registry::is_registered_receiver_v2( + signer::address_of(ccip_offramp) + ) + ); + assert!( + receiver_registry::is_registered_receiver(signer::address_of(ccip_offramp)) + ); + + // Pool is V1-only (test_init_v1 was called in setup) + // No public function to verify V1 pool registration, but successful execution proves it works + assert!( + !token_admin_registry::has_token_pool_config( + signer::address_of(burn_mint_token_pool) + ) + ); + + let token_addr = object::object_address(&token_obj); + let token_amount = 1000; + + // Create token transfer + let token_transfer = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, // source_pool_address + token_addr, // dest_token_address + 1000000, // dest_gas_amount + vector[], // extra_data + (token_amount as u256) // amount + ); + + // Execute message with tokens + create_and_execute_message( + x"0001", + 0, + signer::address_of(ccip_offramp), + vector[], // no data, just tokens + vector[token_transfer], + owner + ); + + // Verify tokens received by V1 receiver + let receiver_balance = + primary_fungible_store::balance(signer::address_of(ccip_offramp), token_obj); + assert!(receiver_balance == token_amount); + + // Verify V1 receiver callback was invoked via dispatchable FA + let events = mock_ccip_receiver::get_received_tokens_only_events(); + assert!(events.length() == 1); + } + + // ============================================ + // Test 2: V1 → V2 Migration Works + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_v1_to_v2_migration( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + // Setup with V1 pool (use_v1_init = true) + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + // Setup receiver (need receiver_registry initialized) + receiver_registry::init_module_for_testing(owner); + mock_ccip_receiver::test_init_state_only(ccip_offramp); + + // STEP 1: Register as V1 + mock_ccip_receiver::register_as_v1(ccip_offramp); + assert!( + !receiver_registry::is_registered_receiver_v2( + signer::address_of(ccip_offramp) + ) + ); + assert!( + receiver_registry::is_registered_receiver(signer::address_of(ccip_offramp)) + ); + + let token_addr = object::object_address(&token_obj); + let token_amount = 1000; + + // Execute message with V1 registration + let token_transfer_v1 = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + (token_amount as u256) + ); + + create_and_execute_message( + x"0002", + 0, + signer::address_of(ccip_offramp), + vector[], + vector[token_transfer_v1], + owner + ); + + // Verify V1 worked + let balance_after_v1 = + primary_fungible_store::balance(signer::address_of(ccip_offramp), token_obj); + assert!(balance_after_v1 == token_amount); + + // STEP 2: Upgrade pool to V2 (realistic upgrade pattern) + upgrade_v2::test_init_module(burn_mint_token_pool); + + // STEP 3: Migrate receiver to V2 + mock_ccip_receiver::migrate_to_v2(ccip_offramp); + + // Verify V2 is now active (V2 registration exists) + assert!( + receiver_registry::is_registered_receiver_v2(signer::address_of(ccip_offramp)) + ); + + // Execute message with V2 registration (dispatcher should prefer V2) + let token_transfer_v2 = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + (token_amount as u256) + ); + + create_and_execute_message( + x"0003", + 1, + signer::address_of(ccip_offramp), + vector[], + vector[token_transfer_v2], + owner + ); + + // Verify V2 worked - should now have 2x token_amount + let balance_after_v2 = + primary_fungible_store::balance(signer::address_of(ccip_offramp), token_obj); + assert!(balance_after_v2 == token_amount * 2); + + // Verify both V1 and V2 callbacks were invoked + let events = mock_ccip_receiver::get_received_tokens_only_events(); + assert!(events.length() == 2); // 1 from V1 execution, 1 from V2 execution + } + + // ============================================ + // Test 3: Direct V2 Registration Works + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_v2_receiver_direct( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + // Setup with V1 pool initially (use_v1_init = true) + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + // Upgrade pool to V2 immediately (realistic new deployment with V2) + upgrade_v2::test_init_module(burn_mint_token_pool); + + // Setup V2 receiver directly (default behavior) + receiver_registry::init_module_for_testing(owner); + mock_ccip_receiver::test_init_module(ccip_offramp); + + // Verify V2 receiver registered + assert!( + receiver_registry::is_registered_receiver_v2(signer::address_of(ccip_offramp)) + ); + + // Verify V2 pool registered + assert!( + token_admin_registry::has_token_pool_config( + signer::address_of(burn_mint_token_pool) + ) + ); + + let token_addr = object::object_address(&token_obj); + let token_amount = 1000; + + // Create token transfer + let token_transfer = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + (token_amount as u256) + ); + + // Execute message with tokens + create_and_execute_message( + x"0004", + 0, + signer::address_of(ccip_offramp), + vector[], + vector[token_transfer], + owner + ); + + // Verify tokens received by V2 receiver + let receiver_balance = + primary_fungible_store::balance(signer::address_of(ccip_offramp), token_obj); + assert!(receiver_balance == token_amount); + + // Verify V2 receiver callback was invoked + let events = mock_ccip_receiver::get_received_tokens_only_events(); + assert!(events.length() == 1); + } + + // ============================================ + // Test 4: Dispatcher Routes Correctly + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_dispatcher_routing( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + // Setup with V1 pool initially (use_v1_init = true) + let (_owner_addr, token_obj) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + // Upgrade pool to V2 before testing (mixed V1 receiver + V2 pool scenario) + upgrade_v2::test_init_module(burn_mint_token_pool); + + // Setup receiver (need receiver_registry initialized) + receiver_registry::init_module_for_testing(owner); + mock_ccip_receiver::test_init_state_only(ccip_offramp); + + let token_addr = object::object_address(&token_obj); + let token_amount = 500; + + // Phase 1: Register V1, verify dispatcher uses V1 path + mock_ccip_receiver::register_as_v1(ccip_offramp); + assert!( + !receiver_registry::is_registered_receiver_v2( + signer::address_of(ccip_offramp) + ) + ); + + let token_transfer_1 = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + (token_amount as u256) + ); + + create_and_execute_message( + x"0005", + 0, + signer::address_of(ccip_offramp), + vector[], + vector[token_transfer_1], + owner + ); + + let balance_1 = + primary_fungible_store::balance(signer::address_of(ccip_offramp), token_obj); + assert!(balance_1 == token_amount); + + // Phase 2: Add V2 registration, verify dispatcher now uses V2 path + mock_ccip_receiver::migrate_to_v2(ccip_offramp); + assert!( + receiver_registry::is_registered_receiver_v2(signer::address_of(ccip_offramp)) + ); + + let token_transfer_2 = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, + token_addr, + 1000000, + vector[], + (token_amount as u256) + ); + + create_and_execute_message( + x"0006", + 1, + signer::address_of(ccip_offramp), + vector[], + vector[token_transfer_2], + owner + ); + + // Should have received both transfers + let balance_2 = + primary_fungible_store::balance(signer::address_of(ccip_offramp), token_obj); + assert!(balance_2 == token_amount * 2); + + // Both callbacks should have been invoked + let events = mock_ccip_receiver::get_received_tokens_only_events(); + assert!(events.length() == 2); + } +} diff --git a/contracts/ccip/ccip_onramp/tests/mock_token.move b/contracts/ccip/ccip_onramp/tests/mock/mock_token.move similarity index 77% rename from contracts/ccip/ccip_onramp/tests/mock_token.move rename to contracts/ccip/ccip_onramp/tests/mock/mock_token.move index eded7cfc..5c60c113 100644 --- a/contracts/ccip/ccip_onramp/tests/mock_token.move +++ b/contracts/ccip/ccip_onramp/tests/mock/mock_token.move @@ -1,10 +1,9 @@ #[test_only] module ccip_onramp::mock_token { use std::fungible_asset::{Self, FungibleAsset, TransferRef}; - use std::object::{Self, Object, ConstructorRef}; + use std::object::{Object, ConstructorRef}; use std::string::{Self}; use std::option::{Self}; - use std::primary_fungible_store; public fun add_dynamic_dispatch_function( ccip_onramp_signer: &signer, constructor_ref: &ConstructorRef @@ -30,16 +29,15 @@ module ccip_onramp::mock_token { } public fun lock_or_burn( - store: Object, fa: FungibleAsset, _transfer_ref: &TransferRef + store: Object, fa: FungibleAsset, transfer_ref: &TransferRef ) { - fungible_asset::deposit(store, fa); + fungible_asset::deposit_with_ref(transfer_ref, store, fa); } public fun release_or_mint( store: Object, amount: u64, transfer_ref: &TransferRef ): FungibleAsset { - primary_fungible_store::withdraw_with_ref( - transfer_ref, object::owner(store), amount - ) + fungible_asset::withdraw_with_ref(transfer_ref, store, amount) } } + diff --git a/contracts/ccip/ccip_onramp/tests/onramp_migration_test.move b/contracts/ccip/ccip_onramp/tests/onramp_migration_test.move index f5a63fd3..cad9841b 100644 --- a/contracts/ccip/ccip_onramp/tests/onramp_migration_test.move +++ b/contracts/ccip/ccip_onramp/tests/onramp_migration_test.move @@ -844,7 +844,8 @@ module ccip_onramp::onramp_migration_test { lock_release_token_pool, pool_type, seed, - is_dispatchable + is_dispatchable, + false // use_v1_init ); let one_e_18 = 1_000_000_000_000_000_000; diff --git a/contracts/ccip/ccip_onramp/tests/onramp_test.move b/contracts/ccip/ccip_onramp/tests/onramp_test.move index c11abb70..19e98d2f 100644 --- a/contracts/ccip/ccip_onramp/tests/onramp_test.move +++ b/contracts/ccip/ccip_onramp/tests/onramp_test.move @@ -66,6 +66,17 @@ module ccip_onramp::onramp_test { transfer_ref: TransferRef } + public fun mint_test_tokens( + token_addr: address, recipient: address, amount: u64 + ) acquires TestToken { + let token = borrow_global(token_addr); + let recipient_store = + primary_fungible_store::ensure_primary_store_exists( + recipient, token.metadata + ); + fungible_asset::mint_to(&token.mint_ref, recipient_store, amount); + } + fun init_timestamp(aptos_framework: &signer, timestamp_seconds: u64) { timestamp::set_time_has_started_for_testing(aptos_framework); timestamp::update_global_time_for_test_secs(timestamp_seconds); @@ -81,7 +92,8 @@ module ccip_onramp::onramp_test { lock_release_token_pool: &signer, pool_type: u8, // 0 for burn_mint, 1 for lock_release seed: vector, - is_dispatchable: bool + is_dispatchable: bool, + use_v1_init: bool ): (address, Object) { let owner_addr = signer::address_of(owner); account::create_account_for_test(signer::address_of(burn_mint_token_pool)); @@ -138,7 +150,8 @@ module ccip_onramp::onramp_test { lock_release_token_pool, pool_type, seed, - is_dispatchable + is_dispatchable, + use_v1_init ); let one_e_18 = 1_000_000_000_000_000_000; @@ -185,12 +198,26 @@ module ccip_onramp::onramp_test { vector[900_000_000_000_000_000] // premium_multiplier_wei_per_eth ); + // Configure token transfer fees (needed for token transfers) + fee_quoter::apply_token_transfer_fee_config_updates( + owner, + DEST_CHAIN_SELECTOR, + vector[token_addr], + vector[50], // min_fee_usd_cents + vector[500], // max_fee_usd_cents + vector[10], // deci_bps + vector[5000], // dest_gas_overhead + vector[64], // dest_bytes_overhead + vector[true], // is_enabled + vector[] + ); + // To be able to call token_admin_dispatcher::dispatch_lock_or_burn - // Need to register onramp signer as an allowed onramp + // Need to register onramp state address as an allowed onramp auth::apply_allowed_onramp_updates( owner, vector[], // onramps_to_remove - vector[signer::address_of(ccip_onramp)] // onramps_to_add + vector[onramp::get_state_address()] // onramps_to_add ); // To be able to call fee_quoter::update_prices, need to register as an allowed offramp @@ -218,7 +245,8 @@ module ccip_onramp::onramp_test { lock_release_token_pool: &signer, pool_type: u8, // 0 for burn_mint, 1 for lock_release seed: vector, - is_dispatchable: bool + is_dispatchable: bool, + use_v1_init: bool ): (Object, address) { let constructor_ref = object::create_named_object(owner, seed); @@ -252,7 +280,11 @@ module ccip_onramp::onramp_test { eth_abi::encode_address(&mut remote_token_address, MOCK_EVM_ADDRESS); if (pool_type == BURN_MINT_TOKEN_POOL) { - burn_mint_token_pool::test_init_module(burn_mint_token_pool); + if (use_v1_init) { + burn_mint_token_pool::test_init_v1(burn_mint_token_pool); + } else { + burn_mint_token_pool::test_init_module(burn_mint_token_pool); + }; burn_mint_token_pool::initialize(owner, burn_ref, mint_ref); burn_mint_token_pool::apply_chain_updates( owner, @@ -282,7 +314,11 @@ module ccip_onramp::onramp_test { signer::address_of(burn_mint_token_pool) ); } else { - lock_release_token_pool::test_init_module(lock_release_token_pool); + if (use_v1_init) { + lock_release_token_pool::test_init_v1(lock_release_token_pool); + } else { + lock_release_token_pool::test_init_module(lock_release_token_pool); + }; lock_release_token_pool::initialize( owner, transfer_ref, @@ -333,7 +369,7 @@ module ccip_onramp::onramp_test { (metadata, token_addr) } - fun initialize_onramp(owner: &signer, router: &signer): address { + public fun initialize_onramp(owner: &signer, router: &signer): address { onramp::initialize( owner, SOURCE_CHAIN_SELECTOR, @@ -387,6 +423,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -443,6 +480,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -498,6 +536,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -535,6 +574,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -615,6 +655,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -703,6 +744,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -823,6 +865,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, LOCK_RELEASE_TOKEN_POOL, b"LockReleaseToken", + false, false ); @@ -947,6 +990,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); initialize_onramp(owner, router); @@ -987,6 +1031,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1024,6 +1069,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1063,6 +1109,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1123,6 +1170,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1172,6 +1220,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1211,6 +1260,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1277,6 +1327,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1334,6 +1385,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1385,6 +1437,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); @@ -1464,6 +1517,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); setup_mcms(mcms); @@ -1531,6 +1585,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); setup_mcms(mcms); @@ -1606,6 +1661,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); setup_mcms(mcms); @@ -1689,6 +1745,7 @@ module ccip_onramp::onramp_test { lock_release_token_pool, BURN_MINT_TOKEN_POOL, b"TestToken", + false, false ); setup_mcms(mcms); @@ -1840,3 +1897,4 @@ module ccip_onramp::onramp_test { extra_args } } + diff --git a/contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move b/contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move new file mode 100644 index 00000000..8ba4ca70 --- /dev/null +++ b/contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move @@ -0,0 +1,595 @@ +#[test_only] +/// Verifies V1 → V2 migration path and that both can coexist +module ccip_onramp::onramp_v1_v2_pool_compatibility_test { + use std::signer; + use std::object; + use std::primary_fungible_store; + + use ccip::client; + use ccip::token_admin_registry; + use ccip::eth_abi; + use ccip_onramp::onramp; + + use burn_mint_token_pool::burn_mint_token_pool; + use burn_mint_token_pool::upgrade_v2 as burn_mint_upgrade_v2; + use lock_release_token_pool::lock_release_token_pool; + use lock_release_token_pool::upgrade_v2 as lock_release_upgrade_v2; + + use ccip_onramp::onramp_test; + + const DEST_CHAIN_SELECTOR: u64 = 5678; + const TOKEN_AMOUNT: u64 = 5000; + + const SENDER: address = @0x500; + + const BURN_MINT_TOKEN_POOL: u8 = 0; + const LOCK_RELEASE_TOKEN_POOL: u8 = 1; + + const BURN_MINT_TOKEN_SEED: vector = b"TestToken"; + const LOCK_RELEASE_TOKEN_SEED: vector = b"LockReleaseToken"; + + const MOCK_EVM_ADDRESS: address = @0x4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97; + + const GAS_LIMIT: u64 = 5000000; + const ALLOW_OUT_OF_ORDER_EXECUTION: bool = true; + + fun create_extra_args_v2(): vector { + client::encode_generic_extra_args_v2( + GAS_LIMIT as u256, ALLOW_OUT_OF_ORDER_EXECUTION + ) + } + + fun encode_receiver(): vector { + let receiver = vector[]; + eth_abi::encode_address(&mut receiver, MOCK_EVM_ADDRESS); + receiver + } + + /// Helper to calculate fee and mint enough tokens for sender + fun mint_tokens_for_transfer( + token_addr: address, num_transfers: u64 + ) { + let receiver = encode_receiver(); + let extra_args = create_extra_args_v2(); + + // Calculate fee for one transfer + let fee_amount = + onramp::get_fee( + DEST_CHAIN_SELECTOR, + receiver, + vector[], // data + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + extra_args + ); + + // Mint enough for transfers + fees + let total_needed = (TOKEN_AMOUNT + fee_amount) * num_transfers; + onramp_test::mint_test_tokens(token_addr, SENDER, total_needed); + } + + // ============================================ + // Test 1: V1 Burn/Mint Pool Baseline + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + router = @0x200, + ccip = @ccip, + ccip_onramp = @ccip_onramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + sender = @0x500 + ) + ] + fun test_v1_burn_mint_pool_baseline( + aptos_framework: &signer, + router: &signer, + ccip: &signer, + ccip_onramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + sender: &signer + ) { + let (_owner_addr, token_obj) = + onramp_test::setup( + aptos_framework, + router, + ccip, + ccip_onramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + let token_addr = object::object_address(&token_obj); + + // Fund sender with enough tokens for transfer + fees + mint_tokens_for_transfer(token_addr, 1); + + // Verify V1 pool registered (not V2) + assert!( + !token_admin_registry::has_token_pool_config( + signer::address_of(burn_mint_token_pool) + ) + ); + + // Send tokens via onramp + let sender_balance_before = primary_fungible_store::balance(SENDER, token_obj); + + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], // data + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], // token_store_addresses - use primary store + token_addr, // fee_token + @0x0, // fee_token_store - use primary store + create_extra_args_v2() + ); + + // Verify tokens were burned from sender + let sender_balance_after = primary_fungible_store::balance(SENDER, token_obj); + assert!( + sender_balance_before - sender_balance_after >= TOKEN_AMOUNT + ); + + // Verify V1 callback worked - check events + let events = + burn_mint_token_pool::get_locked_or_burned_events( + burn_mint_token_pool::get_store_address() + ); + assert!(events.length() >= 1); + } + + // ============================================ + // Test 2: V1 → V2 Burn/Mint Migration + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + router = @0x200, + ccip = @ccip, + ccip_onramp = @ccip_onramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + sender = @0x500 + ) + ] + fun test_v1_to_v2_burn_mint_migration( + aptos_framework: &signer, + router: &signer, + ccip: &signer, + ccip_onramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + sender: &signer + ) { + let (_owner_addr, token_obj) = + onramp_test::setup( + aptos_framework, + router, + ccip, + ccip_onramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + let token_addr = object::object_address(&token_obj); + + // Fund sender with enough tokens for 2 transfers + fees + mint_tokens_for_transfer(token_addr, 2); + + // STEP 1: Send with V1 pool + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + create_extra_args_v2() + ); + + // Verify V1 callback worked + let events_before = + burn_mint_token_pool::get_locked_or_burned_events( + burn_mint_token_pool::get_store_address() + ); + assert!(events_before.length() == 1); + + // STEP 2: Upgrade to V2 + burn_mint_upgrade_v2::test_init_module(burn_mint_token_pool); + + // Verify V2 config now exists + assert!( + token_admin_registry::has_token_pool_config( + signer::address_of(burn_mint_token_pool) + ) + ); + + // STEP 3: Send with V2 pool + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + create_extra_args_v2() + ); + + // Verify V2 callback worked + let events_after = + burn_mint_token_pool::get_locked_or_burned_events( + burn_mint_token_pool::get_store_address() + ); + assert!(events_after.length() == 2); + } + + // ============================================ + // Test 3: V2 Burn/Mint Direct (no migration) + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + router = @0x200, + ccip = @ccip, + ccip_onramp = @ccip_onramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + sender = @0x500 + ) + ] + fun test_v2_burn_mint_direct( + aptos_framework: &signer, + router: &signer, + ccip: &signer, + ccip_onramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + sender: &signer + ) { + let (_owner_addr, token_obj) = + onramp_test::setup( + aptos_framework, + router, + ccip, + ccip_onramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + false // use_v2_init (V2) + ); + + let token_addr = object::object_address(&token_obj); + + // Fund sender with enough tokens for transfer + fees + mint_tokens_for_transfer(token_addr, 1); + + // Verify V2 config registered (already done by setup) + assert!( + token_admin_registry::has_token_pool_config( + signer::address_of(burn_mint_token_pool) + ) + ); + + // Send message using V2 pool + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + create_extra_args_v2() + ); + + // Verify V2 callback worked + let events = + burn_mint_token_pool::get_locked_or_burned_events( + burn_mint_token_pool::get_store_address() + ); + assert!(events.length() == 1); + } + + // ============================================ + // Test 4: V1 Lock/Release Pool Baseline + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + router = @0x200, + ccip = @ccip, + ccip_onramp = @ccip_onramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + sender = @0x500 + ) + ] + fun test_v1_lock_release_pool_baseline( + aptos_framework: &signer, + router: &signer, + ccip: &signer, + ccip_onramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + sender: &signer + ) { + let (_owner_addr, token_obj) = + onramp_test::setup( + aptos_framework, + router, + ccip, + ccip_onramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + let token_addr = object::object_address(&token_obj); + + // Fund sender with enough tokens for transfer + fees + mint_tokens_for_transfer(token_addr, 1); + + // Verify V1 pool registered (not V2) + assert!( + !token_admin_registry::has_token_pool_config( + signer::address_of(lock_release_token_pool) + ) + ); + + // Send tokens via onramp + let sender_balance_before = primary_fungible_store::balance(SENDER, token_obj); + + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + create_extra_args_v2() + ); + + // Verify tokens were locked from sender + let sender_balance_after = primary_fungible_store::balance(SENDER, token_obj); + assert!( + sender_balance_before - sender_balance_after >= TOKEN_AMOUNT + ); + + // Verify V1 callback worked - check events + let events = + lock_release_token_pool::get_locked_or_burned_events( + lock_release_token_pool::get_store_address() + ); + assert!(events.length() >= 1); + } + + // ============================================ + // Test 5: V1 → V2 Lock/Release Migration + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + router = @0x200, + ccip = @ccip, + ccip_onramp = @ccip_onramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + sender = @0x500 + ) + ] + fun test_v1_to_v2_lock_release_migration( + aptos_framework: &signer, + router: &signer, + ccip: &signer, + ccip_onramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + sender: &signer + ) { + let (_owner_addr, token_obj) = + onramp_test::setup( + aptos_framework, + router, + ccip, + ccip_onramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + false, // is_dispatchable + true // use_v1_init + ); + + let token_addr = object::object_address(&token_obj); + + // Fund sender with enough tokens for 2 transfers + fees + mint_tokens_for_transfer(token_addr, 2); + + // STEP 1: Send with V1 + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + create_extra_args_v2() + ); + + // Verify V1 callback worked + let events_before = + lock_release_token_pool::get_locked_or_burned_events( + lock_release_token_pool::get_store_address() + ); + assert!(events_before.length() == 1); + + // STEP 2: Upgrade to V2 + lock_release_upgrade_v2::test_init_module(lock_release_token_pool); + + // Verify V2 config now exists + assert!( + token_admin_registry::has_token_pool_config( + signer::address_of(lock_release_token_pool) + ) + ); + + // STEP 3: Send with V2 pool + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + create_extra_args_v2() + ); + + // Verify V2 callback worked + let events_after = + lock_release_token_pool::get_locked_or_burned_events( + lock_release_token_pool::get_store_address() + ); + assert!(events_after.length() == 2); + } + + // ============================================ + // Test 6: V2 Lock/Release Direct (no migration) + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + router = @0x200, + ccip = @ccip, + ccip_onramp = @ccip_onramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + sender = @0x500 + ) + ] + fun test_v2_lock_release_direct( + aptos_framework: &signer, + router: &signer, + ccip: &signer, + ccip_onramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + sender: &signer + ) { + let (_owner_addr, token_obj) = + onramp_test::setup( + aptos_framework, + router, + ccip, + ccip_onramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + LOCK_RELEASE_TOKEN_POOL, + LOCK_RELEASE_TOKEN_SEED, + false, // is_dispatchable + false // use_v2_init (V2) + ); + + let token_addr = object::object_address(&token_obj); + + // Fund sender with enough tokens for transfer + fees + mint_tokens_for_transfer(token_addr, 1); + + assert!( + token_admin_registry::has_token_pool_config( + signer::address_of(lock_release_token_pool) + ) + ); + + // Send message using V2 pool + onramp::ccip_send( + router, + sender, + DEST_CHAIN_SELECTOR, + encode_receiver(), + vector[], + vector[token_addr], + vector[TOKEN_AMOUNT], + vector[@0x0], + token_addr, + @0x0, + create_extra_args_v2() + ); + + // Verify V2 callback worked + let events = + lock_release_token_pool::get_locked_or_burned_events( + lock_release_token_pool::get_store_address() + ); + assert!(events.length() == 1); + } +} + diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move index b4b45012..7a8082e2 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move @@ -32,7 +32,7 @@ module burn_mint_token_pool::upgrade_v2 { |input| burn_mint_token_pool::release_or_mint_v2(input); // If the contract has already been deployed with V1 and needs to be upgraded to V2, - // create a new module + // create a new module and pass in `publisher` from `fun init_module(publisher: &signer)` token_admin_registry::register_pool_v2( publisher, token_pool_module_name, @@ -48,3 +48,4 @@ module burn_mint_token_pool::upgrade_v2 { init_module(publisher); } } + diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move index 51512a51..64f9aaed 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move @@ -32,7 +32,7 @@ module lock_release_token_pool::upgrade_v2 { |input| lock_release_token_pool::release_or_mint_v2(input); // If the contract has already been deployed with V1 and needs to be upgraded to V2, - // create a new module + // create a new module and pass in `publisher` from `fun init_module(publisher: &signer)` token_admin_registry::register_pool_v2( publisher, token_pool_module_name, @@ -48,3 +48,4 @@ module lock_release_token_pool::upgrade_v2 { init_module(publisher); } } + diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move index 77fe1faf..005d912e 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move @@ -26,7 +26,7 @@ module managed_token_pool::upgrade_v2 { |input| managed_token_pool::release_or_mint_v2(input); // If the contract has already been deployed with V1 and needs to be upgraded to V2, - // create a new module + // create a new module and pass in `publisher` from `fun init_module(publisher: &signer)` token_admin_registry::register_pool_v2( publisher, token_pool_module_name, @@ -42,3 +42,4 @@ module managed_token_pool::upgrade_v2 { init_module(publisher); } } + diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move index 309889ef..21970e6c 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move @@ -334,7 +334,7 @@ module regulated_token_pool::regulated_token_pool { public fun lock_or_burn_v2( fa: FungibleAsset, input: LockOrBurnInputV1 - ): token_admin_registry::LockOrBurnOutputV1 acquires RegulatedTokenPoolState { + ): (vector, vector) acquires RegulatedTokenPoolState { let pool = borrow_pool_mut(); let fa_amount = fungible_asset::amount(&fa); @@ -357,10 +357,7 @@ module regulated_token_pool::regulated_token_pool { &mut pool.token_pool_state, fa_amount, remote_chain_selector ); - token_admin_registry::new_lock_or_burn_output_v1( - dest_token_address, - token_pool::encode_local_decimals(&pool.token_pool_state) - ) + (dest_token_address, token_pool::encode_local_decimals(&pool.token_pool_state)) } public fun release_or_mint_v2( @@ -763,3 +760,4 @@ module regulated_token_pool::regulated_token_pool { move_to(&store_signer, pool); } } + diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move index 92a55cc9..91001ca3 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move @@ -26,7 +26,7 @@ module regulated_token_pool::upgrade_v2 { |input| regulated_token_pool::release_or_mint_v2(input); // If the contract has already been deployed with V1 and needs to be upgraded to V2, - // create a new module + // create a new module and pass in `publisher` from `fun init_module(publisher: &signer)` token_admin_registry::register_pool_v2( publisher, token_pool_module_name, @@ -42,3 +42,4 @@ module regulated_token_pool::upgrade_v2 { init_module(publisher); } } + diff --git a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move index f0b0a561..5b718704 100644 --- a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move +++ b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move @@ -13,12 +13,7 @@ module usdc_token_pool::usdc_token_pool { use ccip::address; use ccip::eth_abi; - use ccip::token_admin_registry::{ - Self, - LockOrBurnInputV1, - ReleaseOrMintInputV1, - LockOrBurnOutputV1 - }; + use ccip::token_admin_registry::{Self, LockOrBurnInputV1, ReleaseOrMintInputV1}; use ccip_token_pool::ownable; use ccip_token_pool::rate_limiter; use ccip_token_pool::token_pool; @@ -516,7 +511,7 @@ module usdc_token_pool::usdc_token_pool { public fun lock_or_burn_v2( fa: FungibleAsset, input: LockOrBurnInputV1 - ): LockOrBurnOutputV1 acquires USDCTokenPoolState { + ): (vector, vector) acquires USDCTokenPoolState { let pool = borrow_pool_mut(); let fa_amount = fungible_asset::amount(&fa); @@ -562,9 +557,7 @@ module usdc_token_pool::usdc_token_pool { &mut pool.token_pool_state, fa_amount, remote_chain_selector ); - token_admin_registry::new_lock_or_burn_output_v1( - dest_token_address, dest_pool_data - ) + (dest_token_address, dest_pool_data) } public fun release_or_mint_v2( @@ -1052,3 +1045,4 @@ module usdc_token_pool::usdc_token_pool { CallbackProof {} } } + diff --git a/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move index c1fc4424..0f7c55d7 100644 --- a/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move @@ -32,7 +32,7 @@ module usdc_token_pool::upgrade_v2 { let release_or_mint_closure = |input| usdc_token_pool::release_or_mint_v2(input); // If the contract has already been deployed with V1 and needs to be upgraded to V2, - // create a new module + // create a new module and pass in `publisher` from `fun init_module(publisher: &signer)` token_admin_registry::register_pool_v2( publisher, token_pool_module_name, @@ -48,3 +48,4 @@ module usdc_token_pool::upgrade_v2 { init_module(publisher); } } + From 8792318e4f2f4d4ea69d34fcd2470d02f8f8c954 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Fri, 28 Nov 2025 13:44:12 -0500 Subject: [PATCH 10/13] Formatting move files --- contracts/ccip/ccip/sources/token_admin_registry.move | 1 - contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move | 1 - contracts/ccip/ccip_onramp/tests/mock/mock_token.move | 1 - contracts/ccip/ccip_onramp/tests/onramp_test.move | 1 - .../ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move | 1 - .../burn_mint_token_pool/sources/burn_mint_token_pool.move | 1 - .../ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move | 1 - .../lock_release_token_pool/sources/lock_release_token_pool.move | 1 - .../lock_release_token_pool/tests/upgrade_v2.move | 1 - .../managed_token_pool/sources/managed_token_pool.move | 1 - .../ccip_token_pools/managed_token_pool/tests/upgrade_v2.move | 1 - .../regulated_token_pool/sources/regulated_token_pool.move | 1 - .../ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move | 1 - .../usdc_token_pool/sources/usdc_token_pool.move | 1 - .../ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move | 1 - 15 files changed, 15 deletions(-) diff --git a/contracts/ccip/ccip/sources/token_admin_registry.move b/contracts/ccip/ccip/sources/token_admin_registry.move index 8b644215..33a85f95 100644 --- a/contracts/ccip/ccip/sources/token_admin_registry.move +++ b/contracts/ccip/ccip/sources/token_admin_registry.move @@ -1338,4 +1338,3 @@ module ccip::token_admin_registry { assert!(has_more); } } - diff --git a/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move index 512f14d7..0b3a3a53 100644 --- a/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move +++ b/contracts/ccip/ccip_offramp/tests/mock/mock_ccip_receiver.move @@ -315,4 +315,3 @@ module ccip_offramp::mock_ccip_receiver { event.message } } - diff --git a/contracts/ccip/ccip_onramp/tests/mock/mock_token.move b/contracts/ccip/ccip_onramp/tests/mock/mock_token.move index 5c60c113..d22f1566 100644 --- a/contracts/ccip/ccip_onramp/tests/mock/mock_token.move +++ b/contracts/ccip/ccip_onramp/tests/mock/mock_token.move @@ -40,4 +40,3 @@ module ccip_onramp::mock_token { fungible_asset::withdraw_with_ref(transfer_ref, store, amount) } } - diff --git a/contracts/ccip/ccip_onramp/tests/onramp_test.move b/contracts/ccip/ccip_onramp/tests/onramp_test.move index 19e98d2f..d4fdb33b 100644 --- a/contracts/ccip/ccip_onramp/tests/onramp_test.move +++ b/contracts/ccip/ccip_onramp/tests/onramp_test.move @@ -1897,4 +1897,3 @@ module ccip_onramp::onramp_test { extra_args } } - diff --git a/contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move b/contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move index 8ba4ca70..3c3bae6c 100644 --- a/contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move +++ b/contracts/ccip/ccip_onramp/tests/onramp_v1_v2_pool_compatibility_test.move @@ -592,4 +592,3 @@ module ccip_onramp::onramp_v1_v2_pool_compatibility_test { assert!(events.length() == 1); } } - diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move index ef9bfcd5..f4f9f5e7 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move @@ -864,4 +864,3 @@ module burn_mint_token_pool::burn_mint_token_pool { ); } } - diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move index 7a8082e2..6d838345 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/tests/upgrade_v2.move @@ -48,4 +48,3 @@ module burn_mint_token_pool::upgrade_v2 { init_module(publisher); } } - diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move index fbc99899..4ff04c5d 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move @@ -1040,4 +1040,3 @@ module lock_release_token_pool::lock_release_token_pool { ) } } - diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move index 64f9aaed..7d42c801 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/tests/upgrade_v2.move @@ -48,4 +48,3 @@ module lock_release_token_pool::upgrade_v2 { init_module(publisher); } } - diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move index d5230200..c1ad8e00 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move @@ -734,4 +734,3 @@ module managed_token_pool::managed_token_pool { CallbackProof {} } } - diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move index 005d912e..e2be9476 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/tests/upgrade_v2.move @@ -42,4 +42,3 @@ module managed_token_pool::upgrade_v2 { init_module(publisher); } } - diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move index 21970e6c..aec3dab0 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move @@ -760,4 +760,3 @@ module regulated_token_pool::regulated_token_pool { move_to(&store_signer, pool); } } - diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move index 91001ca3..9cd54956 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/tests/upgrade_v2.move @@ -42,4 +42,3 @@ module regulated_token_pool::upgrade_v2 { init_module(publisher); } } - diff --git a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move index 5b718704..5c47a4a2 100644 --- a/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move +++ b/contracts/ccip/ccip_token_pools/usdc_token_pool/sources/usdc_token_pool.move @@ -1045,4 +1045,3 @@ module usdc_token_pool::usdc_token_pool { CallbackProof {} } } - diff --git a/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move b/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move index 0f7c55d7..62ecd44d 100644 --- a/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move +++ b/contracts/ccip/ccip_token_pools/usdc_token_pool/tests/upgrade_v2.move @@ -48,4 +48,3 @@ module usdc_token_pool::upgrade_v2 { init_module(publisher); } } - From 78b6d0013a3de3bc678c1dbe5620bc4d53fd8847 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Fri, 28 Nov 2025 14:49:18 -0500 Subject: [PATCH 11/13] Multiple token transfer test --- .../offramp_v1_v2_compatibility_test.move | 130 +++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move b/contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move index ddba6e79..bb46ace5 100644 --- a/contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move +++ b/contracts/ccip/ccip_offramp/tests/offramp_v1_v2_compatibility_test.move @@ -16,10 +16,13 @@ module ccip_offramp::offramp_v1_v2_compatibility_test { use burn_mint_token_pool::upgrade_v2; const BURN_MINT_TOKEN_POOL: u8 = 0; + const LOCK_RELEASE_TOKEN_POOL: u8 = 1; const BURN_MINT_TOKEN_SEED: vector = b"TestToken"; + const LOCK_RELEASE_TOKEN_SEED: vector = b"LockReleaseToken"; const EVM_SOURCE_CHAIN_SELECTOR: u64 = 909606746561742123; const DEST_CHAIN_SELECTOR: u64 = 743186221051783445; const MOCK_EVM_ADDRESS_VECTOR: vector = x"4838B106FCe9647Bdf1E7877BF73cE8B0BAD5f97"; + const MOCK_EVM_ADDRESS_VECTOR_2: vector = x"1234567890abcdef1234567890abcdef12345678"; const ONRAMP_ADDRESS: vector = x"47a1f0a819457f01153f35c6b6b0d42e2e16e91e"; // ============================================ @@ -58,6 +61,15 @@ module ccip_offramp::offramp_v1_v2_compatibility_test { nonce ); + // Create offchain_token_data: one empty vector per token transfer + let num_tokens = token_transfers.length(); + let offchain_token_data: vector> = vector[]; + let i = 0; + while (i < num_tokens) { + offchain_token_data.push_back(vector[]); + i = i + 1; + }; + let message = offramp::test_create_any2aptos_ramp_message( header, @@ -80,8 +92,6 @@ module ccip_offramp::offramp_v1_v2_compatibility_test { // Commit root (with timestamp in the past to allow execution) offramp::test_add_root(root, timestamp::now_seconds() - 3700); - // Create execution report - let offchain_token_data: vector> = vector[vector[]]; let execution_report = offramp::test_create_execution_report( EVM_SOURCE_CHAIN_SELECTOR, @@ -559,4 +569,120 @@ module ccip_offramp::offramp_v1_v2_compatibility_test { let events = mock_ccip_receiver::get_received_tokens_only_events(); assert!(events.length() == 2); } + + // ============================================ + // Test 5: Multi-Transfer Message with V2 Receiver + // Tests V2 receiver handling multiple token transfers in a single message + // ============================================ + + #[ + test( + aptos_framework = @aptos_framework, + ccip = @ccip, + ccip_offramp = @ccip_offramp, + owner = @0x100, + burn_mint_token_pool = @burn_mint_token_pool, + lock_release_token_pool = @lock_release_token_pool, + managed_token_pool = @managed_token_pool, + managed_token = @managed_token, + regulated_token_pool = @regulated_token_pool, + regulated_token = @regulated_token + ) + ] + fun test_multi_token_v2_receiver( + aptos_framework: &signer, + ccip: &signer, + ccip_offramp: &signer, + owner: &signer, + burn_mint_token_pool: &signer, + lock_release_token_pool: &signer, + managed_token_pool: &signer, + managed_token: &signer, + regulated_token_pool: &signer, + regulated_token: &signer + ) { + // Setup first pool: burn_mint with V2 + let (_owner_addr, token_obj_1) = + offramp_test::setup( + aptos_framework, + ccip, + ccip_offramp, + owner, + burn_mint_token_pool, + lock_release_token_pool, + managed_token_pool, + managed_token, + regulated_token_pool, + regulated_token, + BURN_MINT_TOKEN_POOL, + BURN_MINT_TOKEN_SEED, + false, // is_dispatchable + false // use_v1_init = false (V2 from start) + ); + + let token_addr_1 = object::object_address(&token_obj_1); + + receiver_registry::init_module_for_testing(owner); + mock_ccip_receiver::test_init_module(ccip_offramp); + + assert!( + receiver_registry::is_registered_receiver_v2(signer::address_of(ccip_offramp)) + ); + + // Verify pool has V2 config + assert!( + token_admin_registry::has_token_pool_config( + signer::address_of(burn_mint_token_pool) + ) + ); + + // Create 2 token transfers of the same token + // This tests that V2 receiver can handle multiple transfers in one message + // and that pool closures can be invoked multiple times sequentially + let token_amount_1 = 1000; + let token_amount_2 = 2000; + + let token_transfer_1 = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, // source_pool_address + token_addr_1, // dest_token_address + 1000000, // dest_gas_amount + vector[], // extra_data + (token_amount_1 as u256) // amount + ); + + let token_transfer_2 = + offramp::test_create_any2aptos_token_transfer( + MOCK_EVM_ADDRESS_VECTOR, // same source_pool_address + token_addr_1, // dest_token_address (same token) + 1000000, // dest_gas_amount + vector[], // extra_data + (token_amount_2 as u256) // amount + ); + + // Execute message with 2 transfers + create_and_execute_message( + x"0007", // unique message_id + 0, // sequence_number + signer::address_of(ccip_offramp), // receiver + vector[], // no data, just tokens + vector[token_transfer_1, token_transfer_2], // 2 token transfers + owner + ); + + // Verify BOTH 2 transfers were received (total amount = amount_1 + amount_2) + let total_balance = + primary_fungible_store::balance( + signer::address_of(ccip_offramp), token_obj_1 + ); + assert!( + total_balance == token_amount_1 + token_amount_2 + ); + + // Verify V2 receiver callback was invoked once with multiple tokens + // The mock receiver's ccip_receive_v2 handles multiple tokens in a loop + // and emits a single ReceivedTokensOnly event + let events = mock_ccip_receiver::get_received_tokens_only_events(); + assert!(events.length() == 1); + } } From 6dfbe5665bf0c1f82876712a28c5009c22eb4a32 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Sat, 29 Nov 2025 13:10:35 -0500 Subject: [PATCH 12/13] Remove duplicate onramp call check, slight refactor --- .../burn_mint_token_pool.go | 1 - .../lock_release_token_pool.go | 1 - .../managed_token_pool/managed_token_pool.go | 1 - .../regulated_token_pool.go | 1 - .../ccip/sources/token_admin_dispatcher.move | 4 --- .../ccip/sources/token_admin_registry.move | 27 ++++++++----------- .../sources/burn_mint_token_pool.move | 2 -- .../sources/lock_release_token_pool.move | 1 - .../sources/managed_token_pool.move | 1 - .../sources/regulated_token_pool.move | 1 - 10 files changed, 11 insertions(+), 29 deletions(-) diff --git a/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go b/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go index 6f6986f9..ee8f84a2 100644 --- a/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go +++ b/bindings/ccip_token_pools/burn_mint_token_pool/burn_mint_token_pool/burn_mint_token_pool.go @@ -114,7 +114,6 @@ const ( E_UNKNOWN_FUNCTION uint64 = 6 E_MINT_REF_NOT_SET uint64 = 7 E_BURN_REF_NOT_SET uint64 = 8 - E_NOT_EXECUTING_RECEIVER uint64 = 9 ) // Structs diff --git a/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go b/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go index 70504e03..413b73b8 100644 --- a/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go +++ b/bindings/ccip_token_pools/lock_release_token_pool/lock_release_token_pool/lock_release_token_pool.go @@ -130,7 +130,6 @@ const ( E_UNAUTHORIZED uint64 = 8 E_INSUFFICIENT_LIQUIDITY uint64 = 9 E_TRANSFER_REF_NOT_SET uint64 = 10 - E_NOT_EXECUTING_RECEIVER uint64 = 11 ) // Structs diff --git a/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go b/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go index 389c17cf..585473a2 100644 --- a/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go +++ b/bindings/ccip_token_pools/managed_token_pool/managed_token_pool/managed_token_pool.go @@ -111,7 +111,6 @@ const ( E_LOCAL_TOKEN_MISMATCH uint64 = 4 E_INVALID_ARGUMENTS uint64 = 5 E_UNKNOWN_FUNCTION uint64 = 6 - E_NOT_EXECUTING_RECEIVER uint64 = 9 ) // Structs diff --git a/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go b/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go index 64494076..e34fad23 100644 --- a/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go +++ b/bindings/ccip_token_pools/regulated_token_pool/regulated_token_pool/regulated_token_pool.go @@ -112,7 +112,6 @@ const ( E_INVALID_ARGUMENTS uint64 = 5 E_UNKNOWN_FUNCTION uint64 = 6 E_NOT_REGISTERED_RECEIVER uint64 = 7 - E_NOT_EXECUTING_RECEIVER uint64 = 9 ) // Structs diff --git a/contracts/ccip/ccip/sources/token_admin_dispatcher.move b/contracts/ccip/ccip/sources/token_admin_dispatcher.move index bc0028bf..603b1fb3 100644 --- a/contracts/ccip/ccip/sources/token_admin_dispatcher.move +++ b/contracts/ccip/ccip/sources/token_admin_dispatcher.move @@ -18,7 +18,6 @@ module ccip::token_admin_dispatcher { if (token_admin_registry::has_token_pool_config(token_pool_address)) { token_admin_registry::lock_or_burn_v2( - caller, token_pool_address, fa, sender, @@ -52,11 +51,8 @@ module ccip::token_admin_dispatcher { source_pool_data: vector, offchain_token_data: vector ): (FungibleAsset, u64) { - auth::assert_is_allowed_offramp(signer::address_of(caller)); - if (token_admin_registry::has_token_pool_config(token_pool_address)) { token_admin_registry::release_or_mint_v2( - caller, token_pool_address, sender, receiver, diff --git a/contracts/ccip/ccip/sources/token_admin_registry.move b/contracts/ccip/ccip/sources/token_admin_registry.move index 33a85f95..7d2dde78 100644 --- a/contracts/ccip/ccip/sources/token_admin_registry.move +++ b/contracts/ccip/ccip/sources/token_admin_registry.move @@ -1066,15 +1066,12 @@ module ccip::token_admin_registry { } public(friend) fun lock_or_burn_v2( - caller: &signer, token_pool_address: address, fa: fungible_asset::FungibleAsset, sender: address, remote_chain_selector: u64, receiver: vector ): (vector, vector) acquires TokenPoolConfig { - auth::assert_is_allowed_onramp(signer::address_of(caller)); - let pool_config = &TokenPoolConfig[token_pool_address]; let input = LockOrBurnInputV1 { sender, remote_chain_selector, receiver }; @@ -1083,7 +1080,6 @@ module ccip::token_admin_registry { } public(friend) fun release_or_mint_v2( - caller: &signer, token_pool_address: address, sender: vector, receiver: address, @@ -1094,19 +1090,18 @@ module ccip::token_admin_registry { source_pool_data: vector, offchain_token_data: vector ): (FungibleAsset, u64) acquires TokenPoolConfig { - auth::assert_is_allowed_offramp(signer::address_of(caller)); - let pool_config = &TokenPoolConfig[token_pool_address]; - let input = ReleaseOrMintInputV1 { - sender, - receiver, - source_amount, - local_token, - remote_chain_selector, - source_pool_address, - source_pool_data, - offchain_token_data - }; + let input = + ReleaseOrMintInputV1 { + sender, + receiver, + source_amount, + local_token, + remote_chain_selector, + source_pool_address, + source_pool_data, + offchain_token_data + }; (pool_config.callbacks.release_or_mint) (input) diff --git a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move index f4f9f5e7..702956bc 100644 --- a/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move +++ b/contracts/ccip/ccip_token_pools/burn_mint_token_pool/sources/burn_mint_token_pool.move @@ -42,7 +42,6 @@ module burn_mint_token_pool::burn_mint_token_pool { const E_UNKNOWN_FUNCTION: u64 = 6; const E_MINT_REF_NOT_SET: u64 = 7; const E_BURN_REF_NOT_SET: u64 = 8; - const E_NOT_EXECUTING_RECEIVER: u64 = 9; // ================================================================ // | Init | @@ -415,7 +414,6 @@ module burn_mint_token_pool::burn_mint_token_pool { let local_amount = token_pool::calculate_release_or_mint_amount(&pool.token_pool_state, &input); - // Validate the operation (same as V1) token_pool::validate_release_or_mint( &mut pool.token_pool_state, &input, local_amount ); diff --git a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move index 4ff04c5d..c8be7f04 100644 --- a/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move +++ b/contracts/ccip/ccip_token_pools/lock_release_token_pool/sources/lock_release_token_pool.move @@ -44,7 +44,6 @@ module lock_release_token_pool::lock_release_token_pool { const E_UNAUTHORIZED: u64 = 8; const E_INSUFFICIENT_LIQUIDITY: u64 = 9; const E_TRANSFER_REF_NOT_SET: u64 = 10; - const E_NOT_EXECUTING_RECEIVER: u64 = 11; // ================================================================ // | Init | diff --git a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move index c1ad8e00..f41c0e93 100644 --- a/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move +++ b/contracts/ccip/ccip_token_pools/managed_token_pool/sources/managed_token_pool.move @@ -33,7 +33,6 @@ module managed_token_pool::managed_token_pool { const E_LOCAL_TOKEN_MISMATCH: u64 = 4; const E_INVALID_ARGUMENTS: u64 = 5; const E_UNKNOWN_FUNCTION: u64 = 6; - const E_NOT_EXECUTING_RECEIVER: u64 = 9; // ================================================================ // | Init | diff --git a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move index aec3dab0..ebda6763 100644 --- a/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move +++ b/contracts/ccip/ccip_token_pools/regulated_token_pool/sources/regulated_token_pool.move @@ -34,7 +34,6 @@ module regulated_token_pool::regulated_token_pool { const E_INVALID_ARGUMENTS: u64 = 5; const E_UNKNOWN_FUNCTION: u64 = 6; const E_NOT_REGISTERED_RECEIVER: u64 = 7; - const E_NOT_EXECUTING_RECEIVER: u64 = 9; // ================================================================ // | Init | From 0c401863e5e9eacb9e6b7e8f9715e64c39a0f954 Mon Sep 17 00:00:00 2001 From: JohnChangUK Date: Wed, 3 Dec 2025 15:05:35 -0500 Subject: [PATCH 13/13] Add "free up disk space" job --- .github/workflows/aptos-ccip-tests.yml | 12 ++ .github/workflows/aptos-run-smoke-tests.yml | 184 ++++++++++---------- 2 files changed, 104 insertions(+), 92 deletions(-) diff --git a/.github/workflows/aptos-ccip-tests.yml b/.github/workflows/aptos-ccip-tests.yml index b10715ed..759d7a48 100644 --- a/.github/workflows/aptos-ccip-tests.yml +++ b/.github/workflows/aptos-ccip-tests.yml @@ -70,6 +70,18 @@ jobs: run: | go build -o chainlink-aptos ./cmd/chainlink-aptos/main.go + # Clear up disk space, else we run out below: + # Unhandled exception. System.IO.IOException: No space left on device : '/home/runner/actions-runner/cached/_diag/Worker_20250801-102100-utc.log' + - name: Free up disk space + run: | + df -h + sudo rm -rf /usr/share/dotnet + sudo rm -rf /usr/local/lib/android + sudo rm -rf /opt/ghc + sudo rm -rf /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force + df -h + - name: Start PostgreSQL run: | docker run -d \ diff --git a/.github/workflows/aptos-run-smoke-tests.yml b/.github/workflows/aptos-run-smoke-tests.yml index dde82387..69184f6e 100644 --- a/.github/workflows/aptos-run-smoke-tests.yml +++ b/.github/workflows/aptos-run-smoke-tests.yml @@ -23,58 +23,58 @@ jobs: DEFAULT_CORE_REF: aptos-init runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get core ref from PR body - if: github.event_name == 'pull_request' - run: | - comment=$(echo "${{ github.event.pull_request.body }}") - core_ref=$(echo $comment | grep -oP 'core ref: \K\S+' || true) - if [ ! -z "$core_ref" ]; then - echo "CUSTOM_CORE_REF=${core_ref}" >> "${GITHUB_ENV}" - fi - - - name: Checkout chainlink repo - uses: actions/checkout@v4 - with: - repository: smartcontractkit/chainlink - ref: ${{ env.CUSTOM_CORE_REF || env.DEFAULT_CORE_REF }} - path: temp/chainlink - - - name: Build chainlink image - working-directory: temp/chainlink - run: | - docker buildx build --build-arg COMMIT_SHA=$(git rev-parse HEAD) -t local_chainlink -f plugins/chainlink.Dockerfile . - - - name: Build chainlink-aptos image - run: | - docker buildx build --build-arg BASE_IMAGE=local_chainlink -t chainlink-aptos -f ./Dockerfile . - - # Clear up disk space, else we run out below: - # Unhandled exception. System.IO.IOException: No space left on device : '/home/runner/actions-runner/cached/_diag/Worker_20250801-102100-utc.log' - - name: Flatten chainlink-aptos image - run: | - set -e - df -h - docker save chainlink-aptos:latest -o chainlink-aptos.tar - docker system prune -a -f - docker load -i chainlink-aptos.tar - rm -vf chainlink-aptos.tar - df -h - - - name: Extract Go version - uses: ./.github/actions/check-go-version - id: go-version - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: ${{ env.go_version }} - - - name: Create env file - working-directory: ./ - run: | + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get core ref from PR body + if: github.event_name == 'pull_request' + run: | + comment=$(echo "${{ github.event.pull_request.body }}") + core_ref=$(echo $comment | grep -oP 'core ref: \K\S+' || true) + if [ ! -z "$core_ref" ]; then + echo "CUSTOM_CORE_REF=${core_ref}" >> "${GITHUB_ENV}" + fi + + - name: Checkout chainlink repo + uses: actions/checkout@v4 + with: + repository: smartcontractkit/chainlink + ref: ${{ env.CUSTOM_CORE_REF || env.DEFAULT_CORE_REF }} + path: temp/chainlink + + - name: Build chainlink image + working-directory: temp/chainlink + run: | + docker buildx build --build-arg COMMIT_SHA=$(git rev-parse HEAD) -t local_chainlink -f plugins/chainlink.Dockerfile . + + - name: Build chainlink-aptos image + run: | + docker buildx build --build-arg BASE_IMAGE=local_chainlink -t chainlink-aptos -f ./Dockerfile . + + # Clear up disk space, else we run out below: + # Unhandled exception. System.IO.IOException: No space left on device : '/home/runner/actions-runner/cached/_diag/Worker_20250801-102100-utc.log' + - name: Flatten chainlink-aptos image + run: | + set -e + df -h + docker save chainlink-aptos:latest -o chainlink-aptos.tar + docker system prune -a -f + docker load -i chainlink-aptos.tar + rm -vf chainlink-aptos.tar + df -h + + - name: Extract Go version + uses: ./.github/actions/check-go-version + id: go-version + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.go_version }} + + - name: Create env file + working-directory: ./ + run: | cat <> .env # Postgres config POSTGRES_IMAGE=postgres:15.2-alpine @@ -105,43 +105,43 @@ jobs: CORE_P2P_PORT=6690 EOT - - name: Run Smoke Integration Test - env: - TEST_LOG_LEVEL: debug - run: | - set -e - cd integration-tests - # TODO: remove if scripts get fixed, see https://github.com/smartcontractkit/chainlink/commit/71f72665217bc17325c27109c107fb93b96e2607 - echo "replace github.com/smartcontractkit/chainlink/core/scripts => ../temp/chainlink/core/scripts" >> go.mod - echo "replace github.com/smartcontractkit/chainlink/v2 => ../temp/chainlink" >> go.mod - go mod tidy - TEST_LOG_LEVEL=${{env.TEST_LOG_LEVEL}} go test -timeout 24h -count=1 -run TestOCR3Keystone ./smoke - - - name: Configure AWS Credentials - if: success() - uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 - with: - aws-region: ${{ secrets.QA_AWS_REGION }} - role-to-assume: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} - role-duration-seconds: 3600 - mask-aws-account-id: true - - - name: Login to Amazon ECR - if: success() - id: login-ecr - uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 - with: - mask-password: "true" - - - name: Push Image to ECR - if: success() - run: | - docker tag chainlink-aptos ${{ env.CL_ECR }}:aptos.${{ github.sha }} - docker push ${{ env.CL_ECR }}:aptos.${{ github.sha }} - - - name: Upload Test Artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: smoke-test-logs - path: ./integration-tests/logs/ + - name: Run Smoke Integration Test + env: + TEST_LOG_LEVEL: debug + run: | + set -e + cd integration-tests + # TODO: remove if scripts get fixed, see https://github.com/smartcontractkit/chainlink/commit/71f72665217bc17325c27109c107fb93b96e2607 + echo "replace github.com/smartcontractkit/chainlink/core/scripts => ../temp/chainlink/core/scripts" >> go.mod + echo "replace github.com/smartcontractkit/chainlink/v2 => ../temp/chainlink" >> go.mod + go mod tidy + TEST_LOG_LEVEL=${{env.TEST_LOG_LEVEL}} go test -timeout 24h -count=1 -run TestOCR3Keystone ./smoke + + - name: Configure AWS Credentials + if: success() + uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 + with: + aws-region: ${{ secrets.QA_AWS_REGION }} + role-to-assume: ${{ secrets.QA_AWS_ROLE_TO_ASSUME }} + role-duration-seconds: 3600 + mask-aws-account-id: true + + - name: Login to Amazon ECR + if: success() + id: login-ecr + uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 + with: + mask-password: "true" + + - name: Push Image to ECR + if: success() + run: | + docker tag chainlink-aptos ${{ env.CL_ECR }}:aptos.${{ github.sha }} + docker push ${{ env.CL_ECR }}:aptos.${{ github.sha }} + + - name: Upload Test Artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: smoke-test-logs + path: ./integration-tests/logs/ \ No newline at end of file