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", 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..6cf31489 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,28 @@ 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.