diff --git a/bindings/bind/call.go b/bindings/bind/call.go index 9ceaffc4c..b0ca1be91 100644 --- a/bindings/bind/call.go +++ b/bindings/bind/call.go @@ -338,13 +338,14 @@ func ExecutePTB(ctx context.Context, opts *CallOpts, chainClient client.Bindings ptb.SetSender(models.SuiAddress(signerAddress)) } - if ptb.Data.V1.GasData.Budget == nil { - budget := DefaultGasBudget - if opts.GasBudget != nil { - budget = *opts.GasBudget - } - ptb.SetGasBudget(budget) + budget := DefaultGasBudget + if opts.GasBudget != nil { + budget = *opts.GasBudget + } + if ptb.Data.V1.GasData.Budget != nil && *ptb.Data.V1.GasData.Budget > budget { + budget = *ptb.Data.V1.GasData.Budget } + ptb.SetGasBudget(budget) if ptb.Data.V1.GasData.Price == nil { gasPrice, gasPriceErr := chainClient.GetReferenceGasPrice(ctx) diff --git a/bindings/generated/ccip/ccip/fee_quoter/fee_quoter.go b/bindings/generated/ccip/ccip/fee_quoter/fee_quoter.go index db7c352e2..c7227877c 100644 --- a/bindings/generated/ccip/ccip/fee_quoter/fee_quoter.go +++ b/bindings/generated/ccip/ccip/fee_quoter/fee_quoter.go @@ -18,12 +18,13 @@ var ( _ = big.NewInt ) -const FunctionInfo = `[{"package":"ccip","module":"fee_quoter","name":"apply_dest_chain_config_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"dest_chain_selector","type":"u64"},{"name":"is_enabled","type":"bool"},{"name":"max_number_of_tokens_per_msg","type":"u16"},{"name":"max_data_bytes","type":"u32"},{"name":"max_per_msg_gas_limit","type":"u32"},{"name":"dest_gas_overhead","type":"u32"},{"name":"dest_gas_per_payload_byte_base","type":"u8"},{"name":"dest_gas_per_payload_byte_high","type":"u8"},{"name":"dest_gas_per_payload_byte_threshold","type":"u16"},{"name":"dest_data_availability_overhead_gas","type":"u32"},{"name":"dest_gas_per_data_availability_byte","type":"u16"},{"name":"dest_data_availability_multiplier_bps","type":"u16"},{"name":"chain_family_selector","type":"vector"},{"name":"enforce_out_of_order","type":"bool"},{"name":"default_token_fee_usd_cents","type":"u16"},{"name":"default_token_dest_gas_overhead","type":"u32"},{"name":"default_tx_gas_limit","type":"u32"},{"name":"gas_multiplier_wei_per_eth","type":"u64"},{"name":"gas_price_staleness_threshold","type":"u32"},{"name":"network_fee_usd_cents","type":"u32"}]},{"package":"ccip","module":"fee_quoter","name":"apply_fee_token_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"fee_tokens_to_remove","type":"vector
"},{"name":"fee_tokens_to_add","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"apply_premium_multiplier_wei_per_eth_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"tokens","type":"vector
"},{"name":"premium_multiplier_wei_per_eth","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"apply_token_transfer_fee_config_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"dest_chain_selector","type":"u64"},{"name":"add_tokens","type":"vector
"},{"name":"add_min_fee_usd_cents","type":"vector"},{"name":"add_max_fee_usd_cents","type":"vector"},{"name":"add_deci_bps","type":"vector"},{"name":"add_dest_gas_overhead","type":"vector"},{"name":"add_dest_bytes_overhead","type":"vector"},{"name":"add_is_enabled","type":"vector"},{"name":"remove_tokens","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"convert_token_amount","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"from_token","type":"address"},{"name":"from_token_amount","type":"u64"},{"name":"to_token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"destroy_fee_quoter_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"cap","type":"FeeQuoterCap"}]},{"package":"ccip","module":"fee_quoter","name":"get_dest_chain_config","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"}]},{"package":"ccip","module":"fee_quoter","name":"get_dest_chain_config_fields","parameters":[{"name":"dest_chain_config","type":"DestChainConfig"}]},{"package":"ccip","module":"fee_quoter","name":"get_dest_chain_gas_price","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"}]},{"package":"ccip","module":"fee_quoter","name":"get_fee_tokens","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"fee_quoter","name":"get_premium_multiplier_wei_per_eth","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"get_static_config","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"fee_quoter","name":"get_static_config_fields","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"fee_quoter","name":"get_timestamped_price_fields","parameters":[{"name":"tp","type":"TimestampedPrice"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_and_gas_prices","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"clock","type":"clock::Clock"},{"name":"token","type":"address"},{"name":"dest_chain_selector","type":"u64"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_price","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_prices","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"tokens","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_receiver","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"},{"name":"extra_args","type":"vector"},{"name":"default_token_receiver","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_transfer_fee_config","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"},{"name":"token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_transfer_fee_config_fields","parameters":[{"name":"cfg","type":"TokenTransferFeeConfig"}]},{"package":"ccip","module":"fee_quoter","name":"get_validated_fee","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"clock","type":"clock::Clock"},{"name":"dest_chain_selector","type":"u64"},{"name":"receiver","type":"vector"},{"name":"data","type":"vector"},{"name":"local_token_addresses","type":"vector
"},{"name":"local_token_amounts","type":"vector"},{"name":"fee_token","type":"address"},{"name":"extra_args","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"initialize","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"max_fee_juels_per_msg","type":"u256"},{"name":"link_token","type":"address"},{"name":"token_price_staleness_threshold","type":"u64"},{"name":"fee_tokens","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"new_fee_quoter_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"}]},{"package":"ccip","module":"fee_quoter","name":"process_message_args","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"},{"name":"fee_token","type":"address"},{"name":"fee_token_amount","type":"u64"},{"name":"extra_args","type":"vector"},{"name":"local_token_addresses","type":"vector
"},{"name":"dest_token_addresses","type":"vector>"},{"name":"dest_pool_datas","type":"vector>"}]},{"package":"ccip","module":"fee_quoter","name":"type_and_version","parameters":null},{"package":"ccip","module":"fee_quoter","name":"update_prices","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"_","type":"FeeQuoterCap"},{"name":"clock","type":"clock::Clock"},{"name":"source_tokens","type":"vector
"},{"name":"source_usd_per_token","type":"vector"},{"name":"gas_dest_chain_selectors","type":"vector"},{"name":"gas_usd_per_unit_gas","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"update_prices_with_owner_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"clock","type":"clock::Clock"},{"name":"source_tokens","type":"vector
"},{"name":"source_usd_per_token","type":"vector"},{"name":"gas_dest_chain_selectors","type":"vector"},{"name":"gas_usd_per_unit_gas","type":"vector"}]}]` +const FunctionInfo = `[{"package":"ccip","module":"fee_quoter","name":"apply_dest_chain_config_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"dest_chain_selector","type":"u64"},{"name":"is_enabled","type":"bool"},{"name":"max_number_of_tokens_per_msg","type":"u16"},{"name":"max_data_bytes","type":"u32"},{"name":"max_per_msg_gas_limit","type":"u32"},{"name":"dest_gas_overhead","type":"u32"},{"name":"dest_gas_per_payload_byte_base","type":"u8"},{"name":"dest_gas_per_payload_byte_high","type":"u8"},{"name":"dest_gas_per_payload_byte_threshold","type":"u16"},{"name":"dest_data_availability_overhead_gas","type":"u32"},{"name":"dest_gas_per_data_availability_byte","type":"u16"},{"name":"dest_data_availability_multiplier_bps","type":"u16"},{"name":"chain_family_selector","type":"vector"},{"name":"enforce_out_of_order","type":"bool"},{"name":"default_token_fee_usd_cents","type":"u16"},{"name":"default_token_dest_gas_overhead","type":"u32"},{"name":"default_tx_gas_limit","type":"u32"},{"name":"gas_multiplier_wei_per_eth","type":"u64"},{"name":"gas_price_staleness_threshold","type":"u32"},{"name":"network_fee_usd_cents","type":"u32"}]},{"package":"ccip","module":"fee_quoter","name":"apply_fee_token_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"fee_tokens_to_remove","type":"vector
"},{"name":"fee_tokens_to_add","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"apply_premium_multiplier_wei_per_eth_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"tokens","type":"vector
"},{"name":"premium_multiplier_wei_per_eth","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"apply_token_transfer_fee_config_updates","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"dest_chain_selector","type":"u64"},{"name":"add_tokens","type":"vector
"},{"name":"add_min_fee_usd_cents","type":"vector"},{"name":"add_max_fee_usd_cents","type":"vector"},{"name":"add_deci_bps","type":"vector"},{"name":"add_dest_gas_overhead","type":"vector"},{"name":"add_dest_bytes_overhead","type":"vector"},{"name":"add_is_enabled","type":"vector"},{"name":"remove_tokens","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"convert_token_amount","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"from_token","type":"address"},{"name":"from_token_amount","type":"u64"},{"name":"to_token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"destroy_fee_quoter_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"cap","type":"FeeQuoterCap"}]},{"package":"ccip","module":"fee_quoter","name":"get_dest_chain_config","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"}]},{"package":"ccip","module":"fee_quoter","name":"get_dest_chain_config_fields","parameters":[{"name":"dest_chain_config","type":"DestChainConfig"}]},{"package":"ccip","module":"fee_quoter","name":"get_dest_chain_gas_price","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"}]},{"package":"ccip","module":"fee_quoter","name":"get_fee_tokens","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"fee_quoter","name":"get_premium_multiplier_wei_per_eth","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"get_static_config","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"fee_quoter","name":"get_static_config_fields","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"fee_quoter","name":"get_timestamped_price_fields","parameters":[{"name":"tp","type":"TimestampedPrice"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_and_gas_prices","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"clock","type":"clock::Clock"},{"name":"token","type":"address"},{"name":"dest_chain_selector","type":"u64"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_price","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_prices","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"tokens","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_receiver","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"},{"name":"extra_args","type":"vector"},{"name":"default_token_receiver","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_transfer_fee_config","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"},{"name":"token","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"get_token_transfer_fee_config_fields","parameters":[{"name":"cfg","type":"TokenTransferFeeConfig"}]},{"package":"ccip","module":"fee_quoter","name":"get_validated_fee","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"clock","type":"clock::Clock"},{"name":"dest_chain_selector","type":"u64"},{"name":"receiver","type":"vector"},{"name":"data","type":"vector"},{"name":"local_token_addresses","type":"vector
"},{"name":"local_token_amounts","type":"vector"},{"name":"fee_token","type":"address"},{"name":"extra_args","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"initialize","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"max_fee_juels_per_msg","type":"u256"},{"name":"link_token","type":"address"},{"name":"token_price_staleness_threshold","type":"u64"},{"name":"fee_tokens","type":"vector
"}]},{"package":"ccip","module":"fee_quoter","name":"new_fee_quoter_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"}]},{"package":"ccip","module":"fee_quoter","name":"new_fee_quoter_cap_and_transfer","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"recipient","type":"address"}]},{"package":"ccip","module":"fee_quoter","name":"process_message_args","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"dest_chain_selector","type":"u64"},{"name":"fee_token","type":"address"},{"name":"fee_token_amount","type":"u64"},{"name":"extra_args","type":"vector"},{"name":"local_token_addresses","type":"vector
"},{"name":"dest_token_addresses","type":"vector>"},{"name":"dest_pool_datas","type":"vector>"}]},{"package":"ccip","module":"fee_quoter","name":"type_and_version","parameters":null},{"package":"ccip","module":"fee_quoter","name":"update_prices","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"_","type":"FeeQuoterCap"},{"name":"clock","type":"clock::Clock"},{"name":"source_tokens","type":"vector
"},{"name":"source_usd_per_token","type":"vector"},{"name":"gas_dest_chain_selectors","type":"vector"},{"name":"gas_usd_per_unit_gas","type":"vector"}]},{"package":"ccip","module":"fee_quoter","name":"update_prices_with_owner_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"clock","type":"clock::Clock"},{"name":"source_tokens","type":"vector
"},{"name":"source_usd_per_token","type":"vector"},{"name":"gas_dest_chain_selectors","type":"vector"},{"name":"gas_usd_per_unit_gas","type":"vector"}]}]` type IFeeQuoter interface { TypeAndVersion(ctx context.Context, opts *bind.CallOpts) (*models.SuiTransactionBlockResponse, error) Initialize(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, maxFeeJuelsPerMsg *big.Int, linkToken string, tokenPriceStalenessThreshold uint64, feeTokens []string) (*models.SuiTransactionBlockResponse, error) NewFeeQuoterCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object) (*models.SuiTransactionBlockResponse, error) + NewFeeQuoterCapAndTransfer(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, recipient string) (*models.SuiTransactionBlockResponse, error) GetTokenPrice(ctx context.Context, opts *bind.CallOpts, ref bind.Object, token string) (*models.SuiTransactionBlockResponse, error) GetTimestampedPriceFields(ctx context.Context, opts *bind.CallOpts, tp TimestampedPrice) (*models.SuiTransactionBlockResponse, error) GetTokenPrices(ctx context.Context, opts *bind.CallOpts, ref bind.Object, tokens []string) (*models.SuiTransactionBlockResponse, error) @@ -53,6 +54,8 @@ type IFeeQuoter interface { McmsUpdatePricesWithOwnerCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, clock bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) McmsApplyPremiumMultiplierWeiPerEthUpdates(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) DestroyFeeQuoterCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, cap bind.Object) (*models.SuiTransactionBlockResponse, error) + McmsNewFeeQuoterCapAndTransfer(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) + McmsDestroyFeeQuoterCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object, cap bind.Object) (*models.SuiTransactionBlockResponse, error) DevInspect() IFeeQuoterDevInspect Encoder() FeeQuoterEncoder Bound() bind.IBoundContract @@ -87,6 +90,8 @@ type FeeQuoterEncoder interface { InitializeWithArgs(args ...any) (*bind.EncodedCall, error) NewFeeQuoterCap(ref bind.Object, ownerCap bind.Object) (*bind.EncodedCall, error) NewFeeQuoterCapWithArgs(args ...any) (*bind.EncodedCall, error) + NewFeeQuoterCapAndTransfer(ref bind.Object, ownerCap bind.Object, recipient string) (*bind.EncodedCall, error) + NewFeeQuoterCapAndTransferWithArgs(args ...any) (*bind.EncodedCall, error) GetTokenPrice(ref bind.Object, token string) (*bind.EncodedCall, error) GetTokenPriceWithArgs(args ...any) (*bind.EncodedCall, error) GetTimestampedPriceFields(tp TimestampedPrice) (*bind.EncodedCall, error) @@ -145,6 +150,10 @@ type FeeQuoterEncoder interface { McmsApplyPremiumMultiplierWeiPerEthUpdatesWithArgs(args ...any) (*bind.EncodedCall, error) DestroyFeeQuoterCap(ref bind.Object, ownerCap bind.Object, cap bind.Object) (*bind.EncodedCall, error) DestroyFeeQuoterCapWithArgs(args ...any) (*bind.EncodedCall, error) + McmsNewFeeQuoterCapAndTransfer(ref bind.Object, registry bind.Object, params bind.Object) (*bind.EncodedCall, error) + McmsNewFeeQuoterCapAndTransferWithArgs(args ...any) (*bind.EncodedCall, error) + McmsDestroyFeeQuoterCap(ref bind.Object, registry bind.Object, params bind.Object, cap bind.Object) (*bind.EncodedCall, error) + McmsDestroyFeeQuoterCapWithArgs(args ...any) (*bind.EncodedCall, error) } type FeeQuoterContract struct { @@ -321,6 +330,16 @@ func (c *FeeQuoterContract) NewFeeQuoterCap(ctx context.Context, opts *bind.Call return c.ExecuteTransaction(ctx, opts, encoded) } +// NewFeeQuoterCapAndTransfer executes the new_fee_quoter_cap_and_transfer Move function. +func (c *FeeQuoterContract) NewFeeQuoterCapAndTransfer(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, recipient string) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.feeQuoterEncoder.NewFeeQuoterCapAndTransfer(ref, ownerCap, recipient) + if err != nil { + return nil, fmt.Errorf("failed to encode function call: %w", err) + } + + return c.ExecuteTransaction(ctx, opts, encoded) +} + // GetTokenPrice executes the get_token_price Move function. func (c *FeeQuoterContract) GetTokenPrice(ctx context.Context, opts *bind.CallOpts, ref bind.Object, token string) (*models.SuiTransactionBlockResponse, error) { encoded, err := c.feeQuoterEncoder.GetTokenPrice(ref, token) @@ -611,6 +630,26 @@ func (c *FeeQuoterContract) DestroyFeeQuoterCap(ctx context.Context, opts *bind. return c.ExecuteTransaction(ctx, opts, encoded) } +// McmsNewFeeQuoterCapAndTransfer executes the mcms_new_fee_quoter_cap_and_transfer Move function. +func (c *FeeQuoterContract) McmsNewFeeQuoterCapAndTransfer(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.feeQuoterEncoder.McmsNewFeeQuoterCapAndTransfer(ref, registry, params) + if err != nil { + return nil, fmt.Errorf("failed to encode function call: %w", err) + } + + return c.ExecuteTransaction(ctx, opts, encoded) +} + +// McmsDestroyFeeQuoterCap executes the mcms_destroy_fee_quoter_cap Move function. +func (c *FeeQuoterContract) McmsDestroyFeeQuoterCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object, cap bind.Object) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.feeQuoterEncoder.McmsDestroyFeeQuoterCap(ref, registry, params, cap) + if err != nil { + return nil, fmt.Errorf("failed to encode function call: %w", err) + } + + return c.ExecuteTransaction(ctx, opts, encoded) +} + // TypeAndVersion executes the type_and_version Move function using DevInspect to get return values. // // Returns: 0x1::string::String @@ -1335,6 +1374,38 @@ func (c feeQuoterEncoder) NewFeeQuoterCapWithArgs(args ...any) (*bind.EncodedCal }) } +// NewFeeQuoterCapAndTransfer encodes a call to the new_fee_quoter_cap_and_transfer Move function. +func (c feeQuoterEncoder) NewFeeQuoterCapAndTransfer(ref bind.Object, ownerCap bind.Object, recipient string) (*bind.EncodedCall, error) { + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("new_fee_quoter_cap_and_transfer", typeArgsList, typeParamsList, []string{ + "&CCIPObjectRef", + "&OwnerCap", + "address", + }, []any{ + ref, + ownerCap, + recipient, + }, nil) +} + +// NewFeeQuoterCapAndTransferWithArgs encodes a call to the new_fee_quoter_cap_and_transfer Move function using arbitrary arguments. +// This method allows passing both regular values and transaction.Argument values for PTB chaining. +func (c feeQuoterEncoder) NewFeeQuoterCapAndTransferWithArgs(args ...any) (*bind.EncodedCall, error) { + expectedParams := []string{ + "&CCIPObjectRef", + "&OwnerCap", + "address", + } + + if len(args) != len(expectedParams) { + return nil, fmt.Errorf("expected %d arguments, got %d", len(expectedParams), len(args)) + } + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("new_fee_quoter_cap_and_transfer", typeArgsList, typeParamsList, expectedParams, args, nil) +} + // GetTokenPrice encodes a call to the get_token_price Move function. func (c feeQuoterEncoder) GetTokenPrice(ref bind.Object, token string) (*bind.EncodedCall, error) { typeArgsList := []string{} @@ -2495,3 +2566,70 @@ func (c feeQuoterEncoder) DestroyFeeQuoterCapWithArgs(args ...any) (*bind.Encode typeParamsList := []string{} return c.EncodeCallArgsWithGenerics("destroy_fee_quoter_cap", typeArgsList, typeParamsList, expectedParams, args, nil) } + +// McmsNewFeeQuoterCapAndTransfer encodes a call to the mcms_new_fee_quoter_cap_and_transfer Move function. +func (c feeQuoterEncoder) McmsNewFeeQuoterCapAndTransfer(ref bind.Object, registry bind.Object, params bind.Object) (*bind.EncodedCall, error) { + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_new_fee_quoter_cap_and_transfer", typeArgsList, typeParamsList, []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + }, []any{ + ref, + registry, + params, + }, nil) +} + +// McmsNewFeeQuoterCapAndTransferWithArgs encodes a call to the mcms_new_fee_quoter_cap_and_transfer Move function using arbitrary arguments. +// This method allows passing both regular values and transaction.Argument values for PTB chaining. +func (c feeQuoterEncoder) McmsNewFeeQuoterCapAndTransferWithArgs(args ...any) (*bind.EncodedCall, error) { + expectedParams := []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + } + + if len(args) != len(expectedParams) { + return nil, fmt.Errorf("expected %d arguments, got %d", len(expectedParams), len(args)) + } + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_new_fee_quoter_cap_and_transfer", typeArgsList, typeParamsList, expectedParams, args, nil) +} + +// McmsDestroyFeeQuoterCap encodes a call to the mcms_destroy_fee_quoter_cap Move function. +func (c feeQuoterEncoder) McmsDestroyFeeQuoterCap(ref bind.Object, registry bind.Object, params bind.Object, cap bind.Object) (*bind.EncodedCall, error) { + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_destroy_fee_quoter_cap", typeArgsList, typeParamsList, []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + "ccip::fee_quoter::FeeQuoterCap", + }, []any{ + ref, + registry, + params, + cap, + }, nil) +} + +// McmsDestroyFeeQuoterCapWithArgs encodes a call to the mcms_destroy_fee_quoter_cap Move function using arbitrary arguments. +// This method allows passing both regular values and transaction.Argument values for PTB chaining. +func (c feeQuoterEncoder) McmsDestroyFeeQuoterCapWithArgs(args ...any) (*bind.EncodedCall, error) { + expectedParams := []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + "ccip::fee_quoter::FeeQuoterCap", + } + + if len(args) != len(expectedParams) { + return nil, fmt.Errorf("expected %d arguments, got %d", len(expectedParams), len(args)) + } + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_destroy_fee_quoter_cap", typeArgsList, typeParamsList, expectedParams, args, nil) +} diff --git a/bindings/generated/ccip/ccip/rmn_remote/rmn_remote.go b/bindings/generated/ccip/ccip/rmn_remote/rmn_remote.go index 8174039ed..1f9a01876 100644 --- a/bindings/generated/ccip/ccip/rmn_remote/rmn_remote.go +++ b/bindings/generated/ccip/ccip/rmn_remote/rmn_remote.go @@ -18,7 +18,7 @@ var ( _ = big.NewInt ) -const FunctionInfo = `[{"package":"ccip","module":"rmn_remote","name":"create_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"}]},{"package":"ccip","module":"rmn_remote","name":"create_curser_cap_and_transfer","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"recipient","type":"address"}]},{"package":"ccip","module":"rmn_remote","name":"curse","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"curse_multiple","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subjects","type":"vector>"}]},{"package":"ccip","module":"rmn_remote","name":"curse_multiple_with_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"curser_cap","type":"CurserCap"},{"name":"subjects","type":"vector>"}]},{"package":"ccip","module":"rmn_remote","name":"curse_with_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"curser_cap","type":"CurserCap"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"deregister_curser_cap_ids","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"cap_ids","type":"vector
"}]},{"package":"ccip","module":"rmn_remote","name":"get_allowed_curser_cap_ids","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"get_cursed_subjects","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"get_local_chain_selector","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"get_report_digest_header","parameters":null},{"package":"ccip","module":"rmn_remote","name":"get_versioned_config","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"initialize","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"local_chain_selector","type":"u64"}]},{"package":"ccip","module":"rmn_remote","name":"initialize_allowed_curser_caps","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"initial_cap_ids","type":"vector
"}]},{"package":"ccip","module":"rmn_remote","name":"is_cursed","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"is_cursed_global","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"is_cursed_u128","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"subject_value","type":"u128"}]},{"package":"ccip","module":"rmn_remote","name":"is_curser_cap_allowed","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"cap_id","type":"address"}]},{"package":"ccip","module":"rmn_remote","name":"register_curser_cap_ids","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"cap_ids","type":"vector
"}]},{"package":"ccip","module":"rmn_remote","name":"set_config","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"rmn_home_contract_config_digest","type":"vector"},{"name":"signer_onchain_public_keys","type":"vector>"},{"name":"node_indexes","type":"vector"},{"name":"f_sign","type":"u64"}]},{"package":"ccip","module":"rmn_remote","name":"type_and_version","parameters":null},{"package":"ccip","module":"rmn_remote","name":"uncurse","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"uncurse_multiple","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subjects","type":"vector>"}]}]` +const FunctionInfo = `[{"package":"ccip","module":"rmn_remote","name":"create_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"}]},{"package":"ccip","module":"rmn_remote","name":"create_curser_cap_and_transfer","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"recipient","type":"address"}]},{"package":"ccip","module":"rmn_remote","name":"curse","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"curse_multiple","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subjects","type":"vector>"}]},{"package":"ccip","module":"rmn_remote","name":"curse_multiple_with_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"curser_cap","type":"CurserCap"},{"name":"subjects","type":"vector>"}]},{"package":"ccip","module":"rmn_remote","name":"curse_with_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"curser_cap","type":"CurserCap"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"deregister_curser_cap_ids","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"cap_ids","type":"vector
"}]},{"package":"ccip","module":"rmn_remote","name":"get_allowed_curser_cap_ids","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"get_cursed_subjects","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"get_local_chain_selector","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"get_report_digest_header","parameters":null},{"package":"ccip","module":"rmn_remote","name":"get_versioned_config","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"initialize","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"local_chain_selector","type":"u64"}]},{"package":"ccip","module":"rmn_remote","name":"initialize_allowed_curser_caps","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"initial_cap_ids","type":"vector
"}]},{"package":"ccip","module":"rmn_remote","name":"is_cursed","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"is_cursed_global","parameters":[{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip","module":"rmn_remote","name":"is_cursed_u128","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"subject_value","type":"u128"}]},{"package":"ccip","module":"rmn_remote","name":"is_curser_cap_allowed","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"cap_id","type":"address"}]},{"package":"ccip","module":"rmn_remote","name":"mint_and_register_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"fast_registry","type":"FastRegistry"}]},{"package":"ccip","module":"rmn_remote","name":"register_curser_cap","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"fast_registry","type":"FastRegistry"},{"name":"curser_cap","type":"CurserCap"}]},{"package":"ccip","module":"rmn_remote","name":"register_curser_cap_ids","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"cap_ids","type":"vector
"}]},{"package":"ccip","module":"rmn_remote","name":"set_config","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"rmn_home_contract_config_digest","type":"vector"},{"name":"signer_onchain_public_keys","type":"vector>"},{"name":"node_indexes","type":"vector"},{"name":"f_sign","type":"u64"}]},{"package":"ccip","module":"rmn_remote","name":"type_and_version","parameters":null},{"package":"ccip","module":"rmn_remote","name":"uncurse","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subject","type":"vector"}]},{"package":"ccip","module":"rmn_remote","name":"uncurse_multiple","parameters":[{"name":"ref","type":"CCIPObjectRef"},{"name":"owner_cap","type":"OwnerCap"},{"name":"subjects","type":"vector>"}]}]` type IRmnRemote interface { TypeAndVersion(ctx context.Context, opts *bind.CallOpts) (*models.SuiTransactionBlockResponse, error) @@ -33,6 +33,8 @@ type IRmnRemote interface { CurseMultipleWithCurserCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, curserCap bind.Object, subjects [][]byte) (*models.SuiTransactionBlockResponse, error) CreateCurserCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object) (*models.SuiTransactionBlockResponse, error) CreateCurserCapAndTransfer(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, recipient string) (*models.SuiTransactionBlockResponse, error) + MintAndRegisterCurserCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object) (*models.SuiTransactionBlockResponse, error) + RegisterCurserCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object, curserCap bind.Object) (*models.SuiTransactionBlockResponse, error) InitializeAllowedCurserCaps(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, initialCapIds []string) (*models.SuiTransactionBlockResponse, error) RegisterCurserCapIds(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, capIds []string) (*models.SuiTransactionBlockResponse, error) DeregisterCurserCapIds(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, capIds []string) (*models.SuiTransactionBlockResponse, error) @@ -103,6 +105,10 @@ type RmnRemoteEncoder interface { CreateCurserCapWithArgs(args ...any) (*bind.EncodedCall, error) CreateCurserCapAndTransfer(ref bind.Object, ownerCap bind.Object, recipient string) (*bind.EncodedCall, error) CreateCurserCapAndTransferWithArgs(args ...any) (*bind.EncodedCall, error) + MintAndRegisterCurserCap(ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object) (*bind.EncodedCall, error) + MintAndRegisterCurserCapWithArgs(args ...any) (*bind.EncodedCall, error) + RegisterCurserCap(ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object, curserCap bind.Object) (*bind.EncodedCall, error) + RegisterCurserCapWithArgs(args ...any) (*bind.EncodedCall, error) InitializeAllowedCurserCaps(ref bind.Object, ownerCap bind.Object, initialCapIds []string) (*bind.EncodedCall, error) InitializeAllowedCurserCapsWithArgs(args ...any) (*bind.EncodedCall, error) RegisterCurserCapIds(ref bind.Object, ownerCap bind.Object, capIds []string) (*bind.EncodedCall, error) @@ -364,6 +370,26 @@ func (c *RmnRemoteContract) CreateCurserCapAndTransfer(ctx context.Context, opts return c.ExecuteTransaction(ctx, opts, encoded) } +// MintAndRegisterCurserCap executes the mint_and_register_curser_cap Move function. +func (c *RmnRemoteContract) MintAndRegisterCurserCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.rmnRemoteEncoder.MintAndRegisterCurserCap(ref, ownerCap, fastRegistry) + if err != nil { + return nil, fmt.Errorf("failed to encode function call: %w", err) + } + + return c.ExecuteTransaction(ctx, opts, encoded) +} + +// RegisterCurserCap executes the register_curser_cap Move function. +func (c *RmnRemoteContract) RegisterCurserCap(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object, curserCap bind.Object) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.rmnRemoteEncoder.RegisterCurserCap(ref, ownerCap, fastRegistry, curserCap) + if err != nil { + return nil, fmt.Errorf("failed to encode function call: %w", err) + } + + return c.ExecuteTransaction(ctx, opts, encoded) +} + // InitializeAllowedCurserCaps executes the initialize_allowed_curser_caps Move function. func (c *RmnRemoteContract) InitializeAllowedCurserCaps(ctx context.Context, opts *bind.CallOpts, ref bind.Object, ownerCap bind.Object, initialCapIds []string) (*models.SuiTransactionBlockResponse, error) { encoded, err := c.rmnRemoteEncoder.InitializeAllowedCurserCaps(ref, ownerCap, initialCapIds) @@ -1268,6 +1294,73 @@ func (c rmnRemoteEncoder) CreateCurserCapAndTransferWithArgs(args ...any) (*bind return c.EncodeCallArgsWithGenerics("create_curser_cap_and_transfer", typeArgsList, typeParamsList, expectedParams, args, nil) } +// MintAndRegisterCurserCap encodes a call to the mint_and_register_curser_cap Move function. +func (c rmnRemoteEncoder) MintAndRegisterCurserCap(ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object) (*bind.EncodedCall, error) { + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mint_and_register_curser_cap", typeArgsList, typeParamsList, []string{ + "&mut CCIPObjectRef", + "&OwnerCap", + "&mut FastRegistry", + }, []any{ + ref, + ownerCap, + fastRegistry, + }, nil) +} + +// MintAndRegisterCurserCapWithArgs encodes a call to the mint_and_register_curser_cap Move function using arbitrary arguments. +// This method allows passing both regular values and transaction.Argument values for PTB chaining. +func (c rmnRemoteEncoder) MintAndRegisterCurserCapWithArgs(args ...any) (*bind.EncodedCall, error) { + expectedParams := []string{ + "&mut CCIPObjectRef", + "&OwnerCap", + "&mut FastRegistry", + } + + if len(args) != len(expectedParams) { + return nil, fmt.Errorf("expected %d arguments, got %d", len(expectedParams), len(args)) + } + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mint_and_register_curser_cap", typeArgsList, typeParamsList, expectedParams, args, nil) +} + +// RegisterCurserCap encodes a call to the register_curser_cap Move function. +func (c rmnRemoteEncoder) RegisterCurserCap(ref bind.Object, ownerCap bind.Object, fastRegistry bind.Object, curserCap bind.Object) (*bind.EncodedCall, error) { + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("register_curser_cap", typeArgsList, typeParamsList, []string{ + "&mut CCIPObjectRef", + "&OwnerCap", + "&mut FastRegistry", + "ccip::rmn_remote::CurserCap", + }, []any{ + ref, + ownerCap, + fastRegistry, + curserCap, + }, nil) +} + +// RegisterCurserCapWithArgs encodes a call to the register_curser_cap Move function using arbitrary arguments. +// This method allows passing both regular values and transaction.Argument values for PTB chaining. +func (c rmnRemoteEncoder) RegisterCurserCapWithArgs(args ...any) (*bind.EncodedCall, error) { + expectedParams := []string{ + "&mut CCIPObjectRef", + "&OwnerCap", + "&mut FastRegistry", + "ccip::rmn_remote::CurserCap", + } + + if len(args) != len(expectedParams) { + return nil, fmt.Errorf("expected %d arguments, got %d", len(expectedParams), len(args)) + } + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("register_curser_cap", typeArgsList, typeParamsList, expectedParams, args, nil) +} + // InitializeAllowedCurserCaps encodes a call to the initialize_allowed_curser_caps Move function. func (c rmnRemoteEncoder) InitializeAllowedCurserCaps(ref bind.Object, ownerCap bind.Object, initialCapIds []string) (*bind.EncodedCall, error) { typeArgsList := []string{} diff --git a/bindings/generated/ccip/ccip/token_admin_registry/token_admin_registry.go b/bindings/generated/ccip/ccip/token_admin_registry/token_admin_registry.go index f5b56e56d..c7012b9ed 100644 --- a/bindings/generated/ccip/ccip/token_admin_registry/token_admin_registry.go +++ b/bindings/generated/ccip/ccip/token_admin_registry/token_admin_registry.go @@ -45,6 +45,8 @@ type ITokenAdminRegistry interface { McmsUnregisterPool(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) McmsTransferAdminRole(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) McmsAcceptAdminRole(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) + McmsInitializeLocalDecimals(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) + McmsBackfillLocalDecimals(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) DevInspect() ITokenAdminRegistryDevInspect Encoder() TokenAdminRegistryEncoder Bound() bind.IBoundContract @@ -113,6 +115,10 @@ type TokenAdminRegistryEncoder interface { McmsTransferAdminRoleWithArgs(args ...any) (*bind.EncodedCall, error) McmsAcceptAdminRole(ref bind.Object, registry bind.Object, params bind.Object) (*bind.EncodedCall, error) McmsAcceptAdminRoleWithArgs(args ...any) (*bind.EncodedCall, error) + McmsInitializeLocalDecimals(ref bind.Object, registry bind.Object, params bind.Object) (*bind.EncodedCall, error) + McmsInitializeLocalDecimalsWithArgs(args ...any) (*bind.EncodedCall, error) + McmsBackfillLocalDecimals(ref bind.Object, registry bind.Object, params bind.Object) (*bind.EncodedCall, error) + McmsBackfillLocalDecimalsWithArgs(args ...any) (*bind.EncodedCall, error) } type TokenAdminRegistryContract struct { @@ -448,6 +454,26 @@ func (c *TokenAdminRegistryContract) McmsAcceptAdminRole(ctx context.Context, op return c.ExecuteTransaction(ctx, opts, encoded) } +// McmsInitializeLocalDecimals executes the mcms_initialize_local_decimals Move function. +func (c *TokenAdminRegistryContract) McmsInitializeLocalDecimals(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.tokenAdminRegistryEncoder.McmsInitializeLocalDecimals(ref, registry, params) + if err != nil { + return nil, fmt.Errorf("failed to encode function call: %w", err) + } + + return c.ExecuteTransaction(ctx, opts, encoded) +} + +// McmsBackfillLocalDecimals executes the mcms_backfill_local_decimals Move function. +func (c *TokenAdminRegistryContract) McmsBackfillLocalDecimals(ctx context.Context, opts *bind.CallOpts, ref bind.Object, registry bind.Object, params bind.Object) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.tokenAdminRegistryEncoder.McmsBackfillLocalDecimals(ref, registry, params) + if err != nil { + return nil, fmt.Errorf("failed to encode function call: %w", err) + } + + return c.ExecuteTransaction(ctx, opts, encoded) +} + // TypeAndVersion executes the type_and_version Move function using DevInspect to get return values. // // Returns: 0x1::string::String @@ -1626,3 +1652,67 @@ func (c tokenAdminRegistryEncoder) McmsAcceptAdminRoleWithArgs(args ...any) (*bi typeParamsList := []string{} return c.EncodeCallArgsWithGenerics("mcms_accept_admin_role", typeArgsList, typeParamsList, expectedParams, args, nil) } + +// McmsInitializeLocalDecimals encodes a call to the mcms_initialize_local_decimals Move function. +func (c tokenAdminRegistryEncoder) McmsInitializeLocalDecimals(ref bind.Object, registry bind.Object, params bind.Object) (*bind.EncodedCall, error) { + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_initialize_local_decimals", typeArgsList, typeParamsList, []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + }, []any{ + ref, + registry, + params, + }, nil) +} + +// McmsInitializeLocalDecimalsWithArgs encodes a call to the mcms_initialize_local_decimals Move function using arbitrary arguments. +// This method allows passing both regular values and transaction.Argument values for PTB chaining. +func (c tokenAdminRegistryEncoder) McmsInitializeLocalDecimalsWithArgs(args ...any) (*bind.EncodedCall, error) { + expectedParams := []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + } + + if len(args) != len(expectedParams) { + return nil, fmt.Errorf("expected %d arguments, got %d", len(expectedParams), len(args)) + } + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_initialize_local_decimals", typeArgsList, typeParamsList, expectedParams, args, nil) +} + +// McmsBackfillLocalDecimals encodes a call to the mcms_backfill_local_decimals Move function. +func (c tokenAdminRegistryEncoder) McmsBackfillLocalDecimals(ref bind.Object, registry bind.Object, params bind.Object) (*bind.EncodedCall, error) { + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_backfill_local_decimals", typeArgsList, typeParamsList, []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + }, []any{ + ref, + registry, + params, + }, nil) +} + +// McmsBackfillLocalDecimalsWithArgs encodes a call to the mcms_backfill_local_decimals Move function using arbitrary arguments. +// This method allows passing both regular values and transaction.Argument values for PTB chaining. +func (c tokenAdminRegistryEncoder) McmsBackfillLocalDecimalsWithArgs(args ...any) (*bind.EncodedCall, error) { + expectedParams := []string{ + "&mut CCIPObjectRef", + "&mut Registry", + "ExecutingCallbackParams", + } + + if len(args) != len(expectedParams) { + return nil, fmt.Errorf("expected %d arguments, got %d", len(expectedParams), len(args)) + } + typeArgsList := []string{} + typeParamsList := []string{} + return c.EncodeCallArgsWithGenerics("mcms_backfill_local_decimals", typeArgsList, typeParamsList, expectedParams, args, nil) +} diff --git a/bindings/mcms_encoder.go b/bindings/mcms_encoder.go index 02efc575c..dfeb6708f 100644 --- a/bindings/mcms_encoder.go +++ b/bindings/mcms_encoder.go @@ -12,6 +12,8 @@ import ( module_fee_quoter "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/fee_quoter" module_rmn_remote "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/rmn_remote" module_state_object "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/state_object" + module_token_admin_registry "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/token_admin_registry" + module_upgrade_registry "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/upgrade_registry" module_offramp "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_offramp/offramp" module_onramp "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_onramp/onramp" module_router "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_router" @@ -132,6 +134,31 @@ func (e *CCIPEntrypointArgEncoder) EncodeEntryPointArg(executingCallbackParams * } return feeQuoter.Encoder().McmsUpdatePricesWithOwnerCapWithArgs(ccipRef, registryObj, clock, executingCallbackParams) + case "apply_fee_token_updates": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return feeQuoter.Encoder().McmsApplyFeeTokenUpdatesWithArgs(ccipRef, registryObj, executingCallbackParams) + case "apply_dest_chain_config_updates": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return feeQuoter.Encoder().McmsApplyDestChainConfigUpdatesWithArgs(ccipRef, registryObj, executingCallbackParams) + case "apply_token_transfer_fee_config_updates": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return feeQuoter.Encoder().McmsApplyTokenTransferFeeConfigUpdatesWithArgs(ccipRef, registryObj, executingCallbackParams) + case "apply_premium_multiplier_wei_per_eth_updates": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return feeQuoter.Encoder().McmsApplyPremiumMultiplierWeiPerEthUpdatesWithArgs(ccipRef, registryObj, executingCallbackParams) + case "new_fee_quoter_cap_and_transfer": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return feeQuoter.Encoder().McmsNewFeeQuoterCapAndTransferWithArgs(ccipRef, registryObj, executingCallbackParams) + case "destroy_fee_quoter_cap": + deserializer := bcs.NewDeserializer(data) + deserializer.ReadFixedBytes(SuiAddressLength) + deserializer.ReadFixedBytes(SuiAddressLength) + capBytes := deserializer.ReadFixedBytes(SuiAddressLength) + ccipRef := bind.Object{Id: stateObjID} + c := bind.Object{Id: toHexString(capBytes)} + return feeQuoter.Encoder().McmsDestroyFeeQuoterCapWithArgs(ccipRef, registryObj, executingCallbackParams, c) + default: + return nil, fmt.Errorf("unsupported fee_quoter MCMS function: %q", function) } // STATE OBJECT @@ -363,6 +390,64 @@ func (e *CCIPEntrypointArgEncoder) EncodeEntryPointArg(executingCallbackParams * return nil, fmt.Errorf("unsupported rmn_remote MCMS function: %q", function) } + // TOKEN ADMIN REGISTRY + case "token_admin_registry": + tokenAdminRegistry, err := module_token_admin_registry.NewTokenAdminRegistry(target, nil) + if err != nil { + return nil, err + } + switch function { + case "initialize_local_decimals": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return tokenAdminRegistry.Encoder().McmsInitializeLocalDecimalsWithArgs( + ccipRef, + registryObj, + executingCallbackParams, + ) + case "backfill_local_decimals": + deserializer := bcs.NewDeserializer(data) + deserializer.ReadFixedBytes(SuiAddressLength) + ccipRef := bind.Object{Id: toHexString(deserializer.ReadFixedBytes(SuiAddressLength))} + return tokenAdminRegistry.Encoder().McmsBackfillLocalDecimalsWithArgs( + ccipRef, + registryObj, + executingCallbackParams, + ) + case "unregister_pool": + deserializer := bcs.NewDeserializer(data) + deserializer.ReadFixedBytes(SuiAddressLength) + ccipRef := bind.Object{Id: toHexString(deserializer.ReadFixedBytes(SuiAddressLength))} + return tokenAdminRegistry.Encoder().McmsUnregisterPoolWithArgs(ccipRef, registryObj, executingCallbackParams) + case "transfer_admin_role": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return tokenAdminRegistry.Encoder().McmsTransferAdminRoleWithArgs(ccipRef, registryObj, executingCallbackParams) + case "accept_admin_role": + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + return tokenAdminRegistry.Encoder().McmsAcceptAdminRoleWithArgs(ccipRef, registryObj, executingCallbackParams) + default: + return nil, fmt.Errorf("unsupported token_admin_registry MCMS function: %q", function) + } + + // UPGRADE REGISTRY + case "upgrade_registry": + upgradeRegistry, err := module_upgrade_registry.NewUpgradeRegistry(target, nil) + if err != nil { + return nil, err + } + ccipRef := bind.Object{Id: toHexString(deserializeFirst32Bytes(data))} + switch function { + case "block_version": + return upgradeRegistry.Encoder().McmsBlockVersionWithArgs(ccipRef, registryObj, executingCallbackParams) + case "unblock_version": + return upgradeRegistry.Encoder().McmsUnblockVersionWithArgs(ccipRef, registryObj, executingCallbackParams) + case "block_function": + return upgradeRegistry.Encoder().McmsBlockFunctionWithArgs(ccipRef, registryObj, executingCallbackParams) + case "unblock_function": + return upgradeRegistry.Encoder().McmsUnblockFunctionWithArgs(ccipRef, registryObj, executingCallbackParams) + default: + return nil, fmt.Errorf("unsupported upgrade_registry MCMS function: %q", function) + } + // MANAGED TOKEN case "managed_token": managedToken, err := module_managed_token.NewManagedToken(target, nil) diff --git a/bindings/mcms_encoder_test.go b/bindings/mcms_encoder_test.go index 3406d9c9d..4f99ad9f4 100644 --- a/bindings/mcms_encoder_test.go +++ b/bindings/mcms_encoder_test.go @@ -124,9 +124,8 @@ func TestEncodeEntryPointArg_FeeQuoter(t *testing.T) { assert.Equal(t, stateObjID, ccipRefFromResult, "CcipRef should match stateObjID (from BCS data)") }) - t.Run("fallback_case", func(t *testing.T) { - // Test an unhandled function that falls back to default encoding - data := []byte{} + t.Run("apply_fee_token_updates", func(t *testing.T) { + data := serializeAddress(stateObjID) result, err := encoder.EncodeEntryPointArg( executingCallbackParams, @@ -135,14 +134,118 @@ func TestEncodeEntryPointArg_FeeQuoter(t *testing.T) { "apply_fee_token_updates", stateObjID, data, - []string{"0x1::sui::SUI"}, + nil, ) require.NoError(t, err) - assert.NotNil(t, result) assert.Equal(t, "fee_quoter", result.Module.ModuleName) assert.Equal(t, "mcms_apply_fee_token_updates", result.Function) }) + + t.Run("apply_dest_chain_config_updates", func(t *testing.T) { + data := serializeAddress(stateObjID) + + result, err := encoder.EncodeEntryPointArg( + executingCallbackParams, + target, + "fee_quoter", + "apply_dest_chain_config_updates", + stateObjID, + data, + nil, + ) + + require.NoError(t, err) + assert.Equal(t, "mcms_apply_dest_chain_config_updates", result.Function) + }) + + t.Run("apply_token_transfer_fee_config_updates", func(t *testing.T) { + data := serializeAddress(stateObjID) + + result, err := encoder.EncodeEntryPointArg( + executingCallbackParams, + target, + "fee_quoter", + "apply_token_transfer_fee_config_updates", + stateObjID, + data, + nil, + ) + + require.NoError(t, err) + assert.Equal(t, "mcms_apply_token_transfer_fee_config_updates", result.Function) + }) + + t.Run("apply_premium_multiplier_wei_per_eth_updates", func(t *testing.T) { + data := serializeAddress(stateObjID) + + result, err := encoder.EncodeEntryPointArg( + executingCallbackParams, + target, + "fee_quoter", + "apply_premium_multiplier_wei_per_eth_updates", + stateObjID, + data, + nil, + ) + + require.NoError(t, err) + assert.Equal(t, "mcms_apply_premium_multiplier_wei_per_eth_updates", result.Function) + }) + + t.Run("new_fee_quoter_cap_and_transfer", func(t *testing.T) { + data := serializeAddress(stateObjID) + + result, err := encoder.EncodeEntryPointArg( + executingCallbackParams, + target, + "fee_quoter", + "new_fee_quoter_cap_and_transfer", + stateObjID, + data, + nil, + ) + + require.NoError(t, err) + assert.Equal(t, "mcms_new_fee_quoter_cap_and_transfer", result.Function) + }) + + t.Run("destroy_fee_quoter_cap", func(t *testing.T) { + ownerCapID := "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + capID := "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + data := serializeAddresses(stateObjID, ownerCapID, capID) + + result, err := encoder.EncodeEntryPointArg( + executingCallbackParams, + target, + "fee_quoter", + "destroy_fee_quoter_cap", + stateObjID, + data, + nil, + ) + + require.NoError(t, err) + assert.Equal(t, "mcms_destroy_fee_quoter_cap", result.Function) + require.Len(t, result.CallArgs, 4) + capFromResult, err := extractObjectID(result.CallArgs[3]) + require.NoError(t, err) + assert.Equal(t, capID, capFromResult) + }) + + t.Run("unsupported_function", func(t *testing.T) { + _, err := encoder.EncodeEntryPointArg( + executingCallbackParams, + target, + "fee_quoter", + "unknown_function", + stateObjID, + nil, + nil, + ) + require.Error(t, err) + assert.Contains(t, err.Error(), "unsupported fee_quoter MCMS function") + }) } func TestEncodeEntryPointArg_Offramp(t *testing.T) { @@ -1256,3 +1359,202 @@ func TestEncodeEntryPointArg_RmnRemoteDeregisterCurserCapIds(t *testing.T) { require.NoError(t, err) require.Equal(t, "mcms_deregister_curser_cap_ids", encoded.Function) } + +func TestEncodeEntryPointArg_TokenAdminRegistryInitializeLocalDecimals(t *testing.T) { + t.Parallel() + + registryObjID := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + deployerStateObjID := "0x8888888888888888888888888888888888888888888888888888888888888888" + encoder := NewCCIPEntrypointArgEncoder(registryObjID, deployerStateObjID) + + ccipRef := "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ownerCap := "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + data := serializeAddresses(ccipRef, ownerCap) + + paramsArg := transaction.Argument{} + encoded, err := encoder.EncodeEntryPointArg( + ¶msArg, + ccipRef, + "token_admin_registry", + "initialize_local_decimals", + ccipRef, + data, + nil, + ) + require.NoError(t, err) + require.Equal(t, "mcms_initialize_local_decimals", encoded.Function) + require.Equal(t, "token_admin_registry", encoded.Module.ModuleName) +} + +func TestEncodeEntryPointArg_TokenAdminRegistryBackfillLocalDecimals(t *testing.T) { + t.Parallel() + + registryObjID := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + deployerStateObjID := "0x8888888888888888888888888888888888888888888888888888888888888888" + encoder := NewCCIPEntrypointArgEncoder(registryObjID, deployerStateObjID) + + ccipRef := "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ownerCap := "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + coinMetadata := "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + data := serializeAddresses(ownerCap, ccipRef, coinMetadata) + data = append(data, 9) + + paramsArg := transaction.Argument{} + encoded, err := encoder.EncodeEntryPointArg( + ¶msArg, + ccipRef, + "token_admin_registry", + "backfill_local_decimals", + ccipRef, + data, + nil, + ) + require.NoError(t, err) + require.Equal(t, "mcms_backfill_local_decimals", encoded.Function) + require.Equal(t, "token_admin_registry", encoded.Module.ModuleName) +} + +func TestEncodeEntryPointArg_TokenAdminRegistryUnregisterPool(t *testing.T) { + t.Parallel() + + registryObjID := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + deployerStateObjID := "0x8888888888888888888888888888888888888888888888888888888888888888" + encoder := NewCCIPEntrypointArgEncoder(registryObjID, deployerStateObjID) + + ccipRef := "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ownerCap := "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + coinMetadata := "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + data := serializeAddresses(ownerCap, ccipRef, coinMetadata) + paramsArg := transaction.Argument{} + + encoded, err := encoder.EncodeEntryPointArg( + ¶msArg, + ccipRef, + "token_admin_registry", + "unregister_pool", + ccipRef, + data, + nil, + ) + require.NoError(t, err) + require.Equal(t, "mcms_unregister_pool", encoded.Function) + require.Equal(t, "token_admin_registry", encoded.Module.ModuleName) +} + +func TestEncodeEntryPointArg_UpgradeRegistry(t *testing.T) { + t.Parallel() + + registryObjID := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + deployerStateObjID := "0x8888888888888888888888888888888888888888888888888888888888888888" + encoder := NewCCIPEntrypointArgEncoder(registryObjID, deployerStateObjID) + + ccipRef := "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ownerCap := "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + target := ccipRef + data := serializeAddresses(ccipRef, ownerCap) + paramsArg := transaction.Argument{} + + testCases := []struct { + function string + wantMcmsFunc string + }{ + {"block_version", "mcms_block_version"}, + {"unblock_version", "mcms_unblock_version"}, + {"block_function", "mcms_block_function"}, + {"unblock_function", "mcms_unblock_function"}, + } + + for _, tc := range testCases { + t.Run(tc.function, func(t *testing.T) { + t.Parallel() + encoded, err := encoder.EncodeEntryPointArg( + ¶msArg, + target, + "upgrade_registry", + tc.function, + ccipRef, + data, + nil, + ) + require.NoError(t, err) + require.Equal(t, "upgrade_registry", encoded.Module.ModuleName) + require.Equal(t, tc.wantMcmsFunc, encoded.Function) + }) + } + + t.Run("unsupported_function", func(t *testing.T) { + t.Parallel() + _, err := encoder.EncodeEntryPointArg( + ¶msArg, + target, + "upgrade_registry", + "unknown_function", + ccipRef, + data, + nil, + ) + require.Error(t, err) + require.Contains(t, err.Error(), "unsupported upgrade_registry MCMS function") + }) +} + +func TestEncodeEntryPointArg_TokenAdminRegistryPoolOps(t *testing.T) { + t.Parallel() + + registryObjID := "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + deployerStateObjID := "0x8888888888888888888888888888888888888888888888888888888888888888" + encoder := NewCCIPEntrypointArgEncoder(registryObjID, deployerStateObjID) + + ccipRef := "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ownerCap := "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + coinMetadata := "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + newAdmin := "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + data := serializeAddresses(ccipRef, coinMetadata) + dataWithAdmin := serializeAddresses(ccipRef, coinMetadata, newAdmin) + unregisterData := serializeAddresses(ownerCap, ccipRef, coinMetadata) + paramsArg := transaction.Argument{} + + testCases := []struct { + name string + function string + want string + data []byte + }{ + {"unregister_pool", "unregister_pool", "mcms_unregister_pool", unregisterData}, + {"transfer_admin_role", "transfer_admin_role", "mcms_transfer_admin_role", dataWithAdmin}, + {"accept_admin_role", "accept_admin_role", "mcms_accept_admin_role", data}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + encoded, err := encoder.EncodeEntryPointArg( + ¶msArg, + ccipRef, + "token_admin_registry", + tc.function, + ccipRef, + tc.data, + nil, + ) + require.NoError(t, err) + require.Equal(t, "token_admin_registry", encoded.Module.ModuleName) + require.Equal(t, tc.want, encoded.Function) + }) + } + + t.Run("unsupported_function", func(t *testing.T) { + t.Parallel() + _, err := encoder.EncodeEntryPointArg( + ¶msArg, + ccipRef, + "token_admin_registry", + "unknown_function", + ccipRef, + serializeAddress(ccipRef), + nil, + ) + require.Error(t, err) + require.Contains(t, err.Error(), "unsupported token_admin_registry MCMS function") + }) +} diff --git a/contracts/ccip/ccip/sources/fee_quoter.move b/contracts/ccip/ccip/sources/fee_quoter.move index 8f9de2ffb..7049fa6dc 100644 --- a/contracts/ccip/ccip/sources/fee_quoter.move +++ b/contracts/ccip/ccip/sources/fee_quoter.move @@ -14,6 +14,7 @@ use std::string::{Self, String}; use sui::clock; use sui::event; use sui::table; +use sui::transfer; const VERSION: u8 = 2; @@ -317,6 +318,26 @@ public fun new_fee_quoter_cap( } } +/// Mint a `FeeQuoterCap` and send it to `recipient`. Used when the cap must be held +/// off-registry between MCMS executions before offramp provisioning. +public fun new_fee_quoter_cap_and_transfer( + ref: &CCIPObjectRef, + owner_cap: &OwnerCap, + recipient: address, + ctx: &mut TxContext, +) { + verify_function_allowed( + ref, + string::utf8(b"fee_quoter"), + string::utf8(b"new_fee_quoter_cap_and_transfer"), + VERSION, + ); + assert!(object::id(owner_cap) == state_object::owner_cap_id(ref), EInvalidOwnerCap); + + let cap = new_fee_quoter_cap(ref, owner_cap, ctx); + transfer::public_transfer(cap, recipient); +} + public fun get_token_price(ref: &CCIPObjectRef, token: address): TimestampedPrice { verify_function_allowed( ref, @@ -2101,6 +2122,71 @@ public fun destroy_fee_quoter_cap(ref: &CCIPObjectRef, owner_cap: &OwnerCap, cap object::delete(id); } +/// Slow-MCMS wrapper that mints a `FeeQuoterCap` and transfers it to a pinned recipient. +/// Void return so a standalone MCMS leaf does not leave an unconsumed `FeeQuoterCap`. +public fun mcms_new_fee_quoter_cap_and_transfer( + ref: &mut CCIPObjectRef, + registry: &mut Registry, + params: ExecutingCallbackParams, + ctx: &mut TxContext, +) { + let (owner_cap, function, data) = mcms_registry::get_callback_params_with_caps< + state_object::McmsCallback, + OwnerCap, + >( + registry, + state_object::mcms_callback(), + params, + ); + assert!( + function == string::utf8(b"new_fee_quoter_cap_and_transfer"), + EInvalidFunction, + ); + + let mut stream = bcs_stream::new(data); + bcs_stream::validate_obj_addrs( + vector[object::id_address(ref), object::id_address(owner_cap)], + &mut stream, + ); + let recipient = bcs_stream::deserialize_address(&mut stream); + bcs_stream::assert_is_consumed(&stream); + + new_fee_quoter_cap_and_transfer(ref, owner_cap, recipient, ctx); +} + +/// Slow-MCMS wrapper that destroys an existing `FeeQuoterCap`. The cap object is passed +/// as a PTB argument and its address is pinned in callback data. +public fun mcms_destroy_fee_quoter_cap( + ref: &mut CCIPObjectRef, + registry: &mut Registry, + params: ExecutingCallbackParams, + cap: FeeQuoterCap, + _ctx: &mut TxContext, +) { + let (owner_cap, function, data) = mcms_registry::get_callback_params_with_caps< + state_object::McmsCallback, + OwnerCap, + >( + registry, + state_object::mcms_callback(), + params, + ); + assert!(function == string::utf8(b"destroy_fee_quoter_cap"), EInvalidFunction); + + let mut stream = bcs_stream::new(data); + bcs_stream::validate_obj_addrs( + vector[ + object::id_address(ref), + object::id_address(owner_cap), + object::id_address(&cap), + ], + &mut stream, + ); + bcs_stream::assert_is_consumed(&stream); + + destroy_fee_quoter_cap(ref, owner_cap, cap); +} + #[test] fun test_decode_generic_extra_args_v2() { let dest_chain_config = DestChainConfig { diff --git a/contracts/ccip/ccip/sources/rmn_remote.move b/contracts/ccip/ccip/sources/rmn_remote.move index f128edd11..ea2e49374 100644 --- a/contracts/ccip/ccip/sources/rmn_remote.move +++ b/contracts/ccip/ccip/sources/rmn_remote.move @@ -338,6 +338,78 @@ public fun create_curser_cap_and_transfer( transfer::public_transfer(cap, recipient); } +/// Mint a `CurserCap` and register it as the package cap on a fast MCMS Registry. +/// Owner-only. Callable directly from a signer PTB or via `mcms_mint_and_register_curser_cap`. +public fun mint_and_register_curser_cap( + ref: &mut CCIPObjectRef, + owner_cap: &OwnerCap, + fast_registry: &mut FastRegistry, + ctx: &mut TxContext, +) { + verify_function_allowed( + ref, + string::utf8(b"rmn_remote"), + string::utf8(b"mint_and_register_curser_cap"), + VERSION, + ); + assert!(object::id(owner_cap) == state_object::owner_cap_id(ref), EInvalidOwnerCap); + + let publisher_wrapper = fast_mcms_registry::create_publisher_wrapper( + ownable::borrow_publisher(owner_cap), + state_object::mcms_callback(), + ); + + let curser_cap = CurserCap { id: object::new(ctx) }; + let cap_id = object::id_address(&curser_cap); + + fast_mcms_registry::register_entrypoint( + fast_registry, + publisher_wrapper, + state_object::mcms_callback(), + curser_cap, + vector[b"rmn_remote"], + ctx, + ); + + ensure_curser_cap_allowlisted(ref, owner_cap, cap_id, ctx); +} + +/// Register an existing `CurserCap` on a fast MCMS Registry. Owner-only. +/// Callable directly from a signer PTB or via `mcms_register_curser_cap`. +public fun register_curser_cap( + ref: &mut CCIPObjectRef, + owner_cap: &OwnerCap, + fast_registry: &mut FastRegistry, + curser_cap: CurserCap, + ctx: &mut TxContext, +) { + verify_function_allowed( + ref, + string::utf8(b"rmn_remote"), + string::utf8(b"register_curser_cap"), + VERSION, + ); + assert!(object::id(owner_cap) == state_object::owner_cap_id(ref), EInvalidOwnerCap); + + let publisher_wrapper = fast_mcms_registry::create_publisher_wrapper( + ownable::borrow_publisher(owner_cap), + state_object::mcms_callback(), + ); + + let cap_id = object::id_address(&curser_cap); + + fast_mcms_registry::register_entrypoint( + fast_registry, + publisher_wrapper, + state_object::mcms_callback(), + curser_cap, + vector[b"rmn_remote"], + ctx, + ); + + ensure_curser_cap_allowlisted(ref, owner_cap, cap_id, ctx); +} + // ================================================================ // | CurserCap Allowlist (revocation) | // ================================================================ @@ -921,30 +993,7 @@ public fun mcms_register_curser_cap( ); bcs_stream::assert_is_consumed(&stream); - verify_function_allowed( - ref, - string::utf8(b"rmn_remote"), - string::utf8(b"register_curser_cap"), - VERSION, - ); - - let publisher_wrapper = fast_mcms_registry::create_publisher_wrapper( - ownable::borrow_publisher(owner_cap), - state_object::mcms_callback(), - ); - - let cap_id = object::id_address(&curser_cap); - - fast_mcms_registry::register_entrypoint( - fast_registry, - publisher_wrapper, - state_object::mcms_callback(), - curser_cap, - vector[b"rmn_remote"], - ctx, - ); - - ensure_curser_cap_allowlisted(ref, owner_cap, cap_id, ctx); + register_curser_cap(ref, owner_cap, fast_registry, curser_cap, ctx); } /// Slow-MCMS wrapper that mints a `CurserCap` and atomically registers it in @@ -978,32 +1027,7 @@ public fun mcms_mint_and_register_curser_cap( ); bcs_stream::assert_is_consumed(&stream); - verify_function_allowed( - ref, - string::utf8(b"rmn_remote"), - string::utf8(b"mint_and_register_curser_cap"), - VERSION, - ); - - let publisher_wrapper = fast_mcms_registry::create_publisher_wrapper( - ownable::borrow_publisher(owner_cap), - state_object::mcms_callback(), - ); - - assert!(object::id(owner_cap) == state_object::owner_cap_id(ref), EInvalidOwnerCap); - let curser_cap = CurserCap { id: object::new(ctx) }; - let cap_id = object::id_address(&curser_cap); - - fast_mcms_registry::register_entrypoint( - fast_registry, - publisher_wrapper, - state_object::mcms_callback(), - curser_cap, - vector[b"rmn_remote"], - ctx, - ); - - ensure_curser_cap_allowlisted(ref, owner_cap, cap_id, ctx); + mint_and_register_curser_cap(ref, owner_cap, fast_registry, ctx); } // ================================================================ diff --git a/contracts/ccip/ccip/sources/token_admin_registry.move b/contracts/ccip/ccip/sources/token_admin_registry.move index d8ec24b8c..fa6d49421 100644 --- a/contracts/ccip/ccip/sources/token_admin_registry.move +++ b/contracts/ccip/ccip/sources/token_admin_registry.move @@ -555,7 +555,8 @@ public fun unregister_pool( remove_pool_config(ref, coin_metadata_address); } -fun unregister_pool_via_mcms( +fun unregister_pool_as_owner( + owner_cap: &OwnerCap, ref: &mut CCIPObjectRef, coin_metadata_address: address, ) { @@ -565,8 +566,9 @@ fun unregister_pool_via_mcms( string::utf8(b"unregister_pool"), VERSION, ); - let state = state_object::borrow(ref); + assert!(object::id(owner_cap) == state_object::owner_cap_id(ref), EInvalidOwnerCap); + let state = state_object::borrow(ref); assert!(state.token_configs.contains(coin_metadata_address), ETokenNotRegistered); remove_pool_config(ref, coin_metadata_address); @@ -770,7 +772,7 @@ public fun mcms_unregister_pool( params: ExecutingCallbackParams, _ctx: &mut TxContext, ) { - let (_owner_cap, function, data) = mcms_registry::get_callback_params_with_caps< + let (owner_cap, function, data) = mcms_registry::get_callback_params_with_caps< state_object::McmsCallback, OwnerCap, >( @@ -782,14 +784,14 @@ public fun mcms_unregister_pool( let mut stream = bcs_stream::new(data); bcs_stream::validate_obj_addrs( - vector[object::id_address(ref)], + vector[object::id_address(owner_cap), object::id_address(ref)], &mut stream, ); let coin_metadata_address = bcs_stream::deserialize_address(&mut stream); bcs_stream::assert_is_consumed(&stream); - unregister_pool_via_mcms(ref, coin_metadata_address); + unregister_pool_as_owner(owner_cap, ref, coin_metadata_address); } public fun mcms_transfer_admin_role( @@ -854,6 +856,61 @@ public fun mcms_accept_admin_role( accept_admin_role_internal(ref, coin_metadata_address, mcms_registry::get_multisig_address()); } +public fun mcms_initialize_local_decimals( + ref: &mut CCIPObjectRef, + registry: &mut Registry, + params: ExecutingCallbackParams, + ctx: &mut TxContext, +) { + let (owner_cap, function, data) = mcms_registry::get_callback_params_with_caps< + state_object::McmsCallback, + OwnerCap, + >( + registry, + state_object::mcms_callback(), + params, + ); + assert!(function == string::utf8(b"initialize_local_decimals"), EInvalidFunction); + + let mut stream = bcs_stream::new(data); + bcs_stream::validate_obj_addrs( + vector[object::id_address(ref), object::id_address(owner_cap)], + &mut stream, + ); + bcs_stream::assert_is_consumed(&stream); + + initialize_local_decimals(ref, owner_cap, ctx); +} + +public fun mcms_backfill_local_decimals( + ref: &mut CCIPObjectRef, + registry: &mut Registry, + params: ExecutingCallbackParams, + _ctx: &mut TxContext, +) { + let (owner_cap, function, data) = mcms_registry::get_callback_params_with_caps< + state_object::McmsCallback, + OwnerCap, + >( + registry, + state_object::mcms_callback(), + params, + ); + assert!(function == string::utf8(b"backfill_local_decimals"), EInvalidFunction); + + let mut stream = bcs_stream::new(data); + bcs_stream::validate_obj_addrs( + vector[object::id_address(owner_cap), object::id_address(ref)], + &mut stream, + ); + + let coin_metadata_address = bcs_stream::deserialize_address(&mut stream); + let local_decimals = bcs_stream::deserialize_u8(&mut stream); + bcs_stream::assert_is_consumed(&stream); + + backfill_local_decimals(owner_cap, ref, coin_metadata_address, local_decimals); +} + #[test_only] public fun insert_token_configs_for_test( ref: &mut CCIPObjectRef, diff --git a/contracts/ccip/ccip/tests/fee_quoter_mcms_test.move b/contracts/ccip/ccip/tests/fee_quoter_mcms_test.move index be7f6915f..3ff91e966 100644 --- a/contracts/ccip/ccip/tests/fee_quoter_mcms_test.move +++ b/contracts/ccip/ccip/tests/fee_quoter_mcms_test.move @@ -15,6 +15,7 @@ use sui::clock::{Self, Clock}; use sui::test_scenario::{Self as ts, Scenario}; const OWNER: address = @0x123; +const RECIPIENT: address = @0x456; const TOKEN_1: address = @0x1000; const TOKEN_2: address = @0x2000; const LINK_TOKEN: address = @0x4000; @@ -435,3 +436,78 @@ public fun test_mcms_update_prices_with_owner_cap() { env.tear_down(); } + +#[test] +public fun test_mcms_new_fee_quoter_cap_and_transfer() { + let mut env = setup(); + + let owner_cap = ts::take_from_sender(&env.scenario); + + let mut data = vector[]; + data.append(bcs::to_bytes(&object::id_address(&env.ref))); + data.append(bcs::to_bytes(&object::id_address(&owner_cap))); + data.append(bcs::to_bytes(&RECIPIENT)); + + transfer_ownership_to_mcms(&mut env, owner_cap); + + let params = mcms_registry::test_create_executing_callback_params( + @ccip, + string::utf8(FEE_QUOTER_MODULE_NAME), + string::utf8(b"new_fee_quoter_cap_and_transfer"), + data, + x"0000000000000000000000000000000000000000000000000000000000000006", + 0, + 1, + ); + + fee_quoter::mcms_new_fee_quoter_cap_and_transfer( + &mut env.ref, + &mut env.registry, + params, + env.scenario.ctx(), + ); + + env.scenario.next_tx(RECIPIENT); + assert!(ts::has_most_recent_for_address(RECIPIENT)); + + env.tear_down(); +} + +#[test] +public fun test_mcms_destroy_fee_quoter_cap() { + let mut env = setup(); + + let owner_cap = ts::take_from_sender(&env.scenario); + let cap = fee_quoter::new_fee_quoter_cap(&mut env.ref, &owner_cap, env.scenario.ctx()); + sui::transfer::public_transfer(cap, OWNER); + + env.scenario.next_tx(OWNER); + let cap = ts::take_from_sender(&env.scenario); + + let mut data = vector[]; + data.append(bcs::to_bytes(&object::id_address(&env.ref))); + data.append(bcs::to_bytes(&object::id_address(&owner_cap))); + data.append(bcs::to_bytes(&object::id_address(&cap))); + + transfer_ownership_to_mcms(&mut env, owner_cap); + + let params = mcms_registry::test_create_executing_callback_params( + @ccip, + string::utf8(FEE_QUOTER_MODULE_NAME), + string::utf8(b"destroy_fee_quoter_cap"), + data, + x"0000000000000000000000000000000000000000000000000000000000000007", + 0, + 1, + ); + + fee_quoter::mcms_destroy_fee_quoter_cap( + &mut env.ref, + &mut env.registry, + params, + cap, + env.scenario.ctx(), + ); + + env.tear_down(); +} diff --git a/contracts/ccip/ccip/tests/rmn_remote_tests.move b/contracts/ccip/ccip/tests/rmn_remote_tests.move index 03ef27517..058be4655 100644 --- a/contracts/ccip/ccip/tests/rmn_remote_tests.move +++ b/contracts/ccip/ccip/tests/rmn_remote_tests.move @@ -917,6 +917,64 @@ public fun test_mcms_curse_with_curser_cap_wrong_function_name() { tear_down_fast_registry(scenario, owner_cap, ref, fast_registry); } +// === Fast registry registration (direct entry) === + +#[test] +public fun test_mint_and_register_curser_cap_direct_succeeds() { + let (mut scenario, owner_cap, mut ref) = set_up_test(); + let ctx = scenario.ctx(); + initialize_rmn_remote(&mut ref, &owner_cap, TEST_CHAIN_SELECTOR, ctx); + + fast_mcms_registry::test_init(ctx); + scenario.next_tx(ADMIN_ADDRESS); + let mut fast_registry = test_scenario::take_shared(&scenario); + + rmn_remote::mint_and_register_curser_cap( + &mut ref, + &owner_cap, + &mut fast_registry, + scenario.ctx(), + ); + + let allowed = fast_mcms_registry::get_allowed_modules( + &fast_registry, + sui::address::to_ascii_string(@ccip), + ); + assert!(allowed == vector[b"rmn_remote"]); + + test_scenario::return_shared(fast_registry); + tear_down_test(scenario, owner_cap, ref); +} + +#[test] +public fun test_register_curser_cap_direct_succeeds() { + let (mut scenario, owner_cap, mut ref) = set_up_test(); + let ctx = scenario.ctx(); + initialize_rmn_remote(&mut ref, &owner_cap, TEST_CHAIN_SELECTOR, ctx); + + fast_mcms_registry::test_init(ctx); + scenario.next_tx(ADMIN_ADDRESS); + let mut fast_registry = test_scenario::take_shared(&scenario); + + let curser_cap = rmn_remote::create_curser_cap(&mut ref, &owner_cap, scenario.ctx()); + rmn_remote::register_curser_cap( + &mut ref, + &owner_cap, + &mut fast_registry, + curser_cap, + scenario.ctx(), + ); + + let allowed = fast_mcms_registry::get_allowed_modules( + &fast_registry, + sui::address::to_ascii_string(@ccip), + ); + assert!(allowed == vector[b"rmn_remote"]); + + test_scenario::return_shared(fast_registry); + tear_down_test(scenario, owner_cap, ref); +} + // === Slow-MCMS bootstrap tests (two Registries) === fun append_pinned_obj_addrs(data: &mut vector, addrs: vector
) { diff --git a/contracts/ccip/ccip/tests/token_admin_registry_tests.move b/contracts/ccip/ccip/tests/token_admin_registry_tests.move index 1b5d4174b..e476cdd29 100644 --- a/contracts/ccip/ccip/tests/token_admin_registry_tests.move +++ b/contracts/ccip/ccip/tests/token_admin_registry_tests.move @@ -66,6 +66,66 @@ fun initialize_state_and_registry(scenario: &mut Scenario, admin: address) { }; } +fun initialize_state_without_local_decimals(scenario: &mut Scenario, admin: address) { + scenario.next_tx(admin); + { + let ctx = scenario.ctx(); + mcms_account::test_init(ctx); + mcms_registry::test_init(ctx); + mcms_deployer::test_init(ctx); + state_object::test_init(ctx); + }; + + scenario.next_tx(admin); + { + let mut ref = scenario.take_shared(); + let owner_cap = scenario.take_from_sender(); + let ctx = scenario.ctx(); + + upgrade_registry::initialize(&mut ref, &owner_cap, ctx); + registry::initialize(&mut ref, &owner_cap, ctx); + + scenario.return_to_sender(owner_cap); + ts::return_shared(ref); + }; +} + +fun transfer_ccip_ownership_to_mcms(scenario: &mut Scenario, owner_cap: OwnerCap) { + scenario.next_tx(CCIP_ADMIN); + { + let mut ref = scenario.take_shared(); + state_object::transfer_ownership( + &mut ref, + &owner_cap, + mcms_registry::get_multisig_address(), + scenario.ctx(), + ); + ts::return_shared(ref); + }; + + scenario.next_tx(mcms_registry::get_multisig_address()); + { + let mut ref = scenario.take_shared(); + state_object::accept_ownership(&mut ref, scenario.ctx()); + ts::return_shared(ref); + }; + + scenario.next_tx(CCIP_ADMIN); + { + let mut ref = scenario.take_shared(); + let mut registry = scenario.take_shared(); + state_object::execute_ownership_transfer_to_mcms( + &mut ref, + owner_cap, + &mut registry, + mcms_registry::get_multisig_address(), + scenario.ctx(), + ); + ts::return_shared(ref); + ts::return_shared(registry); + }; +} + fun create_test_token( scenario: &mut Scenario, ): (coin::TreasuryCap, coin::CoinMetadata) { @@ -1338,3 +1398,180 @@ fun test_backfill_local_decimals_not_initialized() { ts::end(scenario); } + +// ================================ MCMS Local Decimals Tests ================================ + +#[test] +public fun test_mcms_initialize_local_decimals() { + let mut scenario = create_test_scenario(CCIP_ADMIN); + initialize_state_without_local_decimals(&mut scenario, CCIP_ADMIN); + + let mut data = vector[]; + scenario.next_tx(CCIP_ADMIN); + { + let mut ref = scenario.take_shared(); + let owner_cap = scenario.take_from_sender(); + + data.append(bcs::to_bytes(&object::id_address(&ref))); + data.append(bcs::to_bytes(&object::id_address(&owner_cap))); + + ts::return_shared(ref); + transfer_ccip_ownership_to_mcms(&mut scenario, owner_cap); + }; + + scenario.next_tx(mcms_registry::get_multisig_address()); + { + let mut ref = scenario.take_shared(); + let mut registry = scenario.take_shared(); + + let params = mcms_registry::test_create_executing_callback_params( + @ccip, + string::utf8(b"token_admin_registry"), + string::utf8(b"initialize_local_decimals"), + data, + x"0000000000000000000000000000000000000000000000000000000000000001", + 0, + 1, + ); + + registry::mcms_initialize_local_decimals(&mut ref, &mut registry, params, scenario.ctx()); + + let token_addr = @0xABC; + registry::backfill_local_decimals( + mcms_registry::get_cap(®istry, @ccip.to_ascii_string()), + &mut ref, + token_addr, + 9, + ); + assert!(registry::test_get_local_decimals(&ref, token_addr) == 9); + + ts::return_shared(ref); + ts::return_shared(registry); + }; + + ts::end(scenario); +} + +#[test] +public fun test_mcms_backfill_local_decimals() { + let mut scenario = create_test_scenario(CCIP_ADMIN); + initialize_state_without_local_decimals(&mut scenario, CCIP_ADMIN); + + let mut init_data = vector[]; + let mut backfill_data = vector[]; + let token_addr = @0xDEF; + let local_decimals: u8 = 6; + + scenario.next_tx(CCIP_ADMIN); + { + let mut ref = scenario.take_shared(); + let owner_cap = scenario.take_from_sender(); + + let ref_id = object::id_address(&ref); + let owner_cap_id = object::id_address(&owner_cap); + + init_data.append(bcs::to_bytes(&ref_id)); + init_data.append(bcs::to_bytes(&owner_cap_id)); + + backfill_data.append(bcs::to_bytes(&owner_cap_id)); + backfill_data.append(bcs::to_bytes(&ref_id)); + backfill_data.append(bcs::to_bytes(&token_addr)); + backfill_data.append(bcs::to_bytes(&local_decimals)); + + ts::return_shared(ref); + transfer_ccip_ownership_to_mcms(&mut scenario, owner_cap); + }; + + scenario.next_tx(mcms_registry::get_multisig_address()); + { + let mut ref = scenario.take_shared(); + let mut registry = scenario.take_shared(); + + let init_params = mcms_registry::test_create_executing_callback_params( + @ccip, + string::utf8(b"token_admin_registry"), + string::utf8(b"initialize_local_decimals"), + init_data, + x"0000000000000000000000000000000000000000000000000000000000000001", + 0, + 2, + ); + registry::mcms_initialize_local_decimals(&mut ref, &mut registry, init_params, scenario.ctx()); + + let backfill_params = mcms_registry::test_create_executing_callback_params( + @ccip, + string::utf8(b"token_admin_registry"), + string::utf8(b"backfill_local_decimals"), + backfill_data, + x"0000000000000000000000000000000000000000000000000000000000000001", + 1, + 2, + ); + registry::mcms_backfill_local_decimals(&mut ref, &mut registry, backfill_params, scenario.ctx()); + + assert!(registry::test_get_local_decimals(&ref, token_addr) == local_decimals); + + ts::return_shared(ref); + ts::return_shared(registry); + }; + + ts::end(scenario); +} + +#[test] +public fun test_mcms_unregister_pool() { + let mut scenario = create_test_scenario(TOKEN_ADMIN_ADDRESS); + let (treasury_cap, coin_metadata) = create_test_token(&mut scenario); + let local_token = object::id_address(&coin_metadata); + + initialize_state_and_registry(&mut scenario, CCIP_ADMIN); + + let mut unregister_data = vector[]; + scenario.next_tx(CCIP_ADMIN); + { + let mut ref = scenario.take_shared(); + let owner_cap = scenario.take_from_sender(); + + register_test_pool( + &mut ref, + &treasury_cap, + &coin_metadata, + TOKEN_ADMIN_ADDRESS, + scenario.ctx(), + ); + assert!(registry::is_pool_registered(&ref, local_token)); + + unregister_data.append(bcs::to_bytes(&object::id_address(&owner_cap))); + unregister_data.append(bcs::to_bytes(&object::id_address(&ref))); + unregister_data.append(bcs::to_bytes(&local_token)); + + transfer::public_transfer(treasury_cap, CCIP_ADMIN); + ts::return_shared(ref); + transfer_ccip_ownership_to_mcms(&mut scenario, owner_cap); + }; + + scenario.next_tx(mcms_registry::get_multisig_address()); + { + let mut ref = scenario.take_shared(); + let mut registry = scenario.take_shared(); + + let params = mcms_registry::test_create_executing_callback_params( + @ccip, + string::utf8(b"token_admin_registry"), + string::utf8(b"unregister_pool"), + unregister_data, + x"0000000000000000000000000000000000000000000000000000000000000001", + 0, + 1, + ); + + registry::mcms_unregister_pool(&mut ref, &mut registry, params, scenario.ctx()); + assert!(!registry::is_pool_registered(&ref, local_token)); + + ts::return_shared(ref); + ts::return_shared(registry); + }; + + transfer::public_freeze_object(coin_metadata); + ts::end(scenario); +} diff --git a/deployment/ops/ccip/op_fee_quoter.go b/deployment/ops/ccip/op_fee_quoter.go index cf3627196..2207946f4 100644 --- a/deployment/ops/ccip/op_fee_quoter.go +++ b/deployment/ops/ccip/op_fee_quoter.go @@ -125,10 +125,13 @@ var applyUpdatesHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input Fe opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction( + tx, err := contract.ApplyFeeTokenUpdates( b.GetContext(), opts, - encodedCall, + bind.Object{Id: input.StateObjectId}, + bind.Object{Id: input.OwnerCapObjectId}, + input.FeeTokensToRemove, + input.FeeTokensToAdd, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute ApplyFeeTokenUpdates on FeeQuoter: %w", err) @@ -205,10 +208,20 @@ var applyTokenTransferFeeHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction( + tx, err := contract.ApplyTokenTransferFeeConfigUpdates( b.GetContext(), opts, - encodedCall, + bind.Object{Id: input.StateObjectId}, + bind.Object{Id: input.OwnerCapObjectId}, + input.DestChainSelector, + input.AddTokens, + input.AddMinFeeUsdCents, + input.AddMaxFeeUsdCents, + input.AddDeciBps, + input.AddDestGasOverhead, + input.AddDestBytesOverhead, + input.AddIsEnabled, + input.RemoveTokens, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute ApplyTokenTransferFeeConfigUpdates on FeeQuoter: %w", err) @@ -307,10 +320,31 @@ var applyDestChainConfigHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction( + tx, err := contract.ApplyDestChainConfigUpdates( b.GetContext(), opts, - encodedCall, + bind.Object{Id: input.StateObjectId}, + bind.Object{Id: input.OwnerCapObjectId}, + input.DestChainSelector, + input.IsEnabled, + input.MaxNumberOfTokensPerMsg, + input.MaxDataBytes, + input.MaxPerMsgGasLimit, + input.DestGasOverhead, + input.DestGasPerPayloadByteBase, + input.DestGasPerPayloadByteHigh, + input.DestGasPerPayloadByteThreshold, + input.DestDataAvailabilityOverheadGas, + input.DestGasPerDataAvailabilityByte, + input.DestDataAvailabilityMultiplierBps, + input.ChainFamilySelector, + input.EnforceOutOfOrder, + input.DefaultTokenFeeUsdCents, + input.DefaultTokenDestGasOverhead, + input.DefaultTxGasLimit, + input.GasMultiplierWeiPerEth, + input.GasPriceStalenessThreshold, + input.NetworkFeeUsdCents, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute ApplyDestChainConfigUpdates on FeeQuoter: %w", err) @@ -373,10 +407,13 @@ var applyPremiumMultiplierHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction( + tx, err := contract.ApplyPremiumMultiplierWeiPerEthUpdates( b.GetContext(), opts, - encodedCall, + bind.Object{Id: input.StateObjectId}, + bind.Object{Id: input.OwnerCapObjectId}, + input.Tokens, + input.PremiumMultiplierWeiPerEth, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute ApplyPremiumMultiplierWeiPerEthUpdates on FeeQuoter: %w", err) @@ -495,6 +532,7 @@ type NewFeeQuoterCapInput struct { CCIPPackageId string CCIPObjectRef string OwnerCapObjectId string + RecipientAddress string } var newFeeQuoterCapHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input NewFeeQuoterCapInput) (output sui_ops.OpTxResult[NewFeeQuoterCapObjects], err error) { @@ -503,13 +541,41 @@ var newFeeQuoterCapHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("failed to create fee quoter contract: %w", err) } + ref := bind.Object{Id: input.CCIPObjectRef} + ownerCap := bind.Object{Id: input.OwnerCapObjectId} + + if deps.Signer == nil { + if input.RecipientAddress == "" { + return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("recipient address is required for MCMS new_fee_quoter_cap_and_transfer") + } + + encodedCall, err := contract.Encoder().NewFeeQuoterCapAndTransfer(ref, ownerCap, input.RecipientAddress) + if err != nil { + return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("failed to encode new_fee_quoter_cap_and_transfer: %w", err) + } + call, err := sui_ops.ToTransactionCall(encodedCall, input.CCIPObjectRef) + if err != nil { + return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("failed to convert encoded call to TransactionCall: %w", err) + } + + b.Logger.Infow("Skipping execution of new_fee_quoter_cap_and_transfer as no signer provided", + "recipient", input.RecipientAddress, + ) + return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{ + Digest: "", + PackageId: input.CCIPPackageId, + Objects: NewFeeQuoterCapObjects{}, + Call: call, + }, nil + } + opts := deps.GetCallOpts() opts.Signer = deps.Signer tx, err := contract.NewFeeQuoterCap( b.GetContext(), opts, - bind.Object{Id: input.CCIPObjectRef}, - bind.Object{Id: input.OwnerCapObjectId}, + ref, + ownerCap, ) if err != nil { return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("failed to execute new_fee_quoter_cap: %w", err) @@ -520,13 +586,23 @@ var newFeeQuoterCapHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("failed to find fee quoter cap object ID in tx: %w", err1) } + encodedCall, err := contract.Encoder().NewFeeQuoterCap(ref, ownerCap) + if err != nil { + return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("failed to encode new_fee_quoter_cap: %w", err) + } + call, err := sui_ops.ToTransactionCall(encodedCall, input.CCIPObjectRef) + if err != nil { + return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{}, fmt.Errorf("failed to convert encoded call to TransactionCall: %w", err) + } + return sui_ops.OpTxResult[NewFeeQuoterCapObjects]{ Digest: tx.Digest, PackageId: input.CCIPPackageId, Objects: NewFeeQuoterCapObjects{ FeeQuoterCapObjectId: feeQuoterCapObjectId, }, - }, err + Call: call, + }, nil } var FeeQuoterNewFeeQuoterCapOp = cld_ops.NewOperation( @@ -550,14 +626,36 @@ var destroyFeeQuoterCapHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, i return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create fee quoter contract: %w", err) } + ref := bind.Object{Id: input.CCIPObjectRef} + ownerCap := bind.Object{Id: input.OwnerCapObjectId} + cap := bind.Object{Id: input.FeeQuoterCapObjectId} + + encodedCall, err := contract.Encoder().DestroyFeeQuoterCap(ref, ownerCap, cap) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode destroy_fee_quoter_cap: %w", err) + } + call, err := sui_ops.ToTransactionCall(encodedCall, input.CCIPObjectRef) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to convert encoded call to TransactionCall: %w", err) + } + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of DestroyFeeQuoterCap as per no Signer provided") + return sui_ops.OpTxResult[NoObjects]{ + Digest: "", + PackageId: input.CCIPPackageId, + Objects: NoObjects{}, + Call: call, + }, nil + } + opts := deps.GetCallOpts() opts.Signer = deps.Signer tx, err := contract.DestroyFeeQuoterCap( b.GetContext(), opts, - bind.Object{Id: input.CCIPObjectRef}, - bind.Object{Id: input.OwnerCapObjectId}, - bind.Object{Id: input.FeeQuoterCapObjectId}, + ref, + ownerCap, + cap, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute destroy_fee_quoter_cap: %w", err) @@ -566,7 +664,9 @@ var destroyFeeQuoterCapHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, i return sui_ops.OpTxResult[NoObjects]{ Digest: tx.Digest, PackageId: input.CCIPPackageId, - }, err + Objects: NoObjects{}, + Call: call, + }, nil } var FeeQuoterDestroyFeeQuoterCapOp = cld_ops.NewOperation( diff --git a/deployment/ops/ccip/op_fee_quoter_mcms_test.go b/deployment/ops/ccip/op_fee_quoter_mcms_test.go new file mode 100644 index 000000000..ea9c8750d --- /dev/null +++ b/deployment/ops/ccip/op_fee_quoter_mcms_test.go @@ -0,0 +1,266 @@ +package ccipops + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_fee_quoter "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/fee_quoter" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" +) + +func feeQuoterTestBundle(t *testing.T) cld_ops.Bundle { + t.Helper() + return cld_ops.NewBundle( + func() context.Context { return t.Context() }, + logger.Test(t), + cld_ops.NewMemoryReporter(), + ) +} + +func TestFeeQuoterApplyFeeTokenUpdatesOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + feeQuoterTestBundle(t), + FeeQuoterApplyFeeTokenUpdatesOp, + sui_ops.OpTxDeps{}, + FeeQuoterApplyFeeTokenUpdatesInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + FeeTokensToRemove: []string{}, + FeeTokensToAdd: []string{testCoinMetadata}, + }, + ) + require.NoError(t, err) + + contract, err := module_fee_quoter.NewFeeQuoter(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().ApplyFeeTokenUpdates( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + []string{}, + []string{testCoinMetadata}, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestFeeQuoterApplyTokenTransferFeeConfigUpdatesOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + const destChainSelector uint64 = 16015286601757825753 + + report, err := cld_ops.ExecuteOperation( + feeQuoterTestBundle(t), + FeeQuoterApplyTokenTransferFeeConfigUpdatesOp, + sui_ops.OpTxDeps{}, + FeeQuoterApplyTokenTransferFeeConfigUpdatesInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + DestChainSelector: destChainSelector, + AddTokens: []string{testCoinMetadata}, + AddMinFeeUsdCents: []uint32{1}, + AddMaxFeeUsdCents: []uint32{2}, + AddDeciBps: []uint16{3}, + AddDestGasOverhead: []uint32{4}, + AddDestBytesOverhead: []uint32{5}, + AddIsEnabled: []bool{true}, + RemoveTokens: []string{}, + }, + ) + require.NoError(t, err) + + contract, err := module_fee_quoter.NewFeeQuoter(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().ApplyTokenTransferFeeConfigUpdates( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + destChainSelector, + []string{testCoinMetadata}, + []uint32{1}, + []uint32{2}, + []uint16{3}, + []uint32{4}, + []uint32{5}, + []bool{true}, + []string{}, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestFeeQuoterApplyDestChainConfigUpdatesOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + const destChainSelector uint64 = 16015286601757825753 + + report, err := cld_ops.ExecuteOperation( + feeQuoterTestBundle(t), + FeeQuoterApplyDestChainConfigUpdatesOp, + sui_ops.OpTxDeps{}, + FeeQuoterApplyDestChainConfigUpdatesInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + DestChainSelector: destChainSelector, + IsEnabled: true, + MaxNumberOfTokensPerMsg: 2, + MaxDataBytes: 100, + MaxPerMsgGasLimit: 200, + DestGasOverhead: 300, + DestGasPerPayloadByteBase: 1, + DestGasPerPayloadByteHigh: 2, + DestGasPerPayloadByteThreshold: 3, + DestDataAvailabilityOverheadGas: 400, + DestGasPerDataAvailabilityByte: 5, + DestDataAvailabilityMultiplierBps: 6, + ChainFamilySelector: []byte{0x28, 0x12, 0xd5, 0x2c}, + EnforceOutOfOrder: false, + DefaultTokenFeeUsdCents: 7, + DefaultTokenDestGasOverhead: 800, + DefaultTxGasLimit: 900, + GasMultiplierWeiPerEth: 100, + GasPriceStalenessThreshold: 200, + NetworkFeeUsdCents: 10, + }, + ) + require.NoError(t, err) + + contract, err := module_fee_quoter.NewFeeQuoter(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().ApplyDestChainConfigUpdates( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + destChainSelector, + true, + uint16(2), + uint32(100), + uint32(200), + uint32(300), + byte(1), + byte(2), + uint16(3), + uint32(400), + uint16(5), + uint16(6), + []byte{0x28, 0x12, 0xd5, 0x2c}, + false, + uint16(7), + uint32(800), + uint32(900), + uint64(100), + uint32(200), + uint32(10), + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestFeeQuoterApplyPremiumMultiplierWeiPerEthUpdatesOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + feeQuoterTestBundle(t), + FeeQuoterApplyPremiumMultiplierWeiPerEthUpdatesOp, + sui_ops.OpTxDeps{}, + FeeQuoterApplyPremiumMultiplierWeiPerEthUpdatesInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + Tokens: []string{testCoinMetadata}, + PremiumMultiplierWeiPerEth: []uint64{77}, + }, + ) + require.NoError(t, err) + + contract, err := module_fee_quoter.NewFeeQuoter(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().ApplyPremiumMultiplierWeiPerEthUpdates( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + []string{testCoinMetadata}, + []uint64{77}, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestFeeQuoterNewFeeQuoterCapOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + const recipient = "0x1111111111111111111111111111111111111111111111111111111111111111" + + report, err := cld_ops.ExecuteOperation( + feeQuoterTestBundle(t), + FeeQuoterNewFeeQuoterCapOp, + sui_ops.OpTxDeps{}, + NewFeeQuoterCapInput{ + CCIPPackageId: testCCIPPackageID, + CCIPObjectRef: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + RecipientAddress: recipient, + }, + ) + require.NoError(t, err) + require.Equal(t, "new_fee_quoter_cap_and_transfer", report.Output.Call.Function) + + contract, err := module_fee_quoter.NewFeeQuoter(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().NewFeeQuoterCapAndTransfer( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + recipient, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestFeeQuoterDestroyFeeQuoterCapOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + const feeQuoterCapID = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + + report, err := cld_ops.ExecuteOperation( + feeQuoterTestBundle(t), + FeeQuoterDestroyFeeQuoterCapOp, + sui_ops.OpTxDeps{}, + DestroyFeeQuoterCapInput{ + CCIPPackageId: testCCIPPackageID, + CCIPObjectRef: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + FeeQuoterCapObjectId: feeQuoterCapID, + }, + ) + require.NoError(t, err) + require.Equal(t, "destroy_fee_quoter_cap", report.Output.Call.Function) + + contract, err := module_fee_quoter.NewFeeQuoter(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().DestroyFeeQuoterCap( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + bind.Object{Id: feeQuoterCapID}, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} diff --git a/deployment/ops/ccip/op_token_admin_registry.go b/deployment/ops/ccip/op_token_admin_registry.go index 1fe357ca2..d9e36199e 100644 --- a/deployment/ops/ccip/op_token_admin_registry.go +++ b/deployment/ops/ccip/op_token_admin_registry.go @@ -10,6 +10,7 @@ import ( "github.com/smartcontractkit/chainlink-sui/bindings/bind" module_token_admin_registry "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/token_admin_registry" sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/rmn" ) type InitTARObjects struct { @@ -74,6 +75,27 @@ var initLocalDecimalsHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, inp return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create token admin registry contract: %w", err) } + encodedCall, err := contract.Encoder().InitializeLocalDecimals( + bind.Object{Id: input.StateObjectId}, + bind.Object{Id: input.OwnerCapObjectId}, + ) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode InitializeLocalDecimals call: %w", err) + } + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to convert encoded call to TransactionCall: %w", err) + } + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of InitializeLocalDecimals on TokenAdminRegistry as per no Signer provided") + return sui_ops.OpTxResult[NoObjects]{ + Digest: "", + PackageId: input.CCIPPackageId, + Objects: NoObjects{}, + Call: call, + }, nil + } + opts := deps.GetCallOpts() opts.Signer = deps.Signer tx, err := contract.InitializeLocalDecimals( @@ -90,6 +112,7 @@ var initLocalDecimalsHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, inp Digest: tx.Digest, PackageId: input.CCIPPackageId, Objects: NoObjects{}, + Call: call, }, nil } @@ -114,6 +137,32 @@ var backfillLocalDecimalsHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create token admin registry contract: %w", err) } + encodedCall, err := contract.Encoder().BackfillLocalDecimals( + bind.Object{Id: input.OwnerCapObjectId}, + bind.Object{Id: input.StateObjectId}, + input.CoinMetadataAddress, + input.LocalDecimals, + ) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode BackfillLocalDecimals call: %w", err) + } + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to convert encoded call to TransactionCall: %w", err) + } + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of BackfillLocalDecimals on TokenAdminRegistry as per no Signer provided", + "CoinMetadataAddress", input.CoinMetadataAddress, + "LocalDecimals", input.LocalDecimals, + ) + return sui_ops.OpTxResult[NoObjects]{ + Digest: "", + PackageId: input.CCIPPackageId, + Objects: NoObjects{}, + Call: call, + }, nil + } + opts := deps.GetCallOpts() opts.Signer = deps.Signer tx, err := contract.BackfillLocalDecimals( @@ -132,6 +181,7 @@ var backfillLocalDecimalsHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, Digest: tx.Digest, PackageId: input.CCIPPackageId, Objects: NoObjects{}, + Call: call, }, nil } @@ -149,6 +199,7 @@ var TokenAdminRegistryBackfillLocalDecimalsOp = cld_ops.NewOperation( type UnregisterPoolInput struct { CCIPPackageId string CCIPObjectRef string + OwnerCapObjectId string CoinMetadataAddress string } @@ -158,13 +209,20 @@ var unregisterPoolHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create token admin registry contract: %w", err) } - encodedCall, err := contract.Encoder().UnregisterPool(bind.Object{Id: input.CCIPObjectRef}, input.CoinMetadataAddress) + data, err := rmn.SerializeMcmsObjectAddrs( + input.OwnerCapObjectId, + input.CCIPObjectRef, + input.CoinMetadataAddress, + ) if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode UnregisterPool call: %w", err) + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode unregister_pool MCMS callback data: %w", err) } - call, err := sui_ops.ToTransactionCall(encodedCall, input.CCIPObjectRef) - if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to convert encoded call to TransactionCall: %w", err) + call := sui_ops.TransactionCall{ + PackageID: input.CCIPPackageId, + Module: "token_admin_registry", + Function: "unregister_pool", + Data: data, + StateObjID: input.CCIPObjectRef, } if deps.Signer == nil { b.Logger.Infow("Skipping execution of UnregisterPool on TokenAdminRegistry as per no Signer provided", "CoinMetadataAddress", input.CoinMetadataAddress) @@ -178,10 +236,11 @@ var unregisterPoolHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction( + tx, err := contract.UnregisterPool( b.GetContext(), opts, - encodedCall, + bind.Object{Id: input.CCIPObjectRef}, + input.CoinMetadataAddress, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute UnregisterPool on TokenAdminRegistry: %w", err) @@ -241,10 +300,12 @@ var transferAdminRoleHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, inp opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction( + tx, err := contract.TransferAdminRole( b.GetContext(), opts, - encodedCall, + bind.Object{Id: input.CCIPObjectRef}, + input.CoinMetadataAddress, + input.NewAdmin, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute TransferAdminRole on TokenAdminRegistry: %w", err) @@ -303,10 +364,11 @@ var acceptAdminRoleHandler = func(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction( + tx, err := contract.AcceptAdminRole( b.GetContext(), opts, - encodedCall, + bind.Object{Id: input.CCIPObjectRef}, + input.CoinMetadataAddress, ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute AcceptAdminRole on TokenAdminRegistry: %w", err) diff --git a/deployment/ops/ccip/op_token_admin_registry_mcms_test.go b/deployment/ops/ccip/op_token_admin_registry_mcms_test.go new file mode 100644 index 000000000..ff6fc6a30 --- /dev/null +++ b/deployment/ops/ccip/op_token_admin_registry_mcms_test.go @@ -0,0 +1,237 @@ +package ccipops + +import ( + "context" + "testing" + + "github.com/aptos-labs/aptos-go-sdk/bcs" + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_token_admin_registry "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/token_admin_registry" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/rmn" +) + +const ( + testCCIPPackageID = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + testStateObjectID = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + testOwnerCapID = "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + testCoinMetadata = "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + testNewAdmin = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" +) + +func testBundle(t *testing.T) cld_ops.Bundle { + t.Helper() + return cld_ops.NewBundle( + func() context.Context { return t.Context() }, + logger.Test(t), + cld_ops.NewMemoryReporter(), + ) +} + +func TestTokenAdminRegistryInitializeLocalDecimalsOp_EncodesProposalLeaf(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + testBundle(t), + TokenAdminRegistryInitializeLocalDecimalsOp, + sui_ops.OpTxDeps{}, + InitLocalDecimalsInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + }, + ) + require.NoError(t, err) + require.Equal(t, "token_admin_registry", report.Output.Call.Module) + require.Equal(t, "initialize_local_decimals", report.Output.Call.Function) + require.Equal(t, testStateObjectID, report.Output.Call.StateObjID) + require.Len(t, report.Output.Call.Data, 64) + require.Empty(t, report.Output.Digest) +} + +func TestTokenAdminRegistryInitializeLocalDecimalsOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + testBundle(t), + TokenAdminRegistryInitializeLocalDecimalsOp, + sui_ops.OpTxDeps{}, + InitLocalDecimalsInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + }, + ) + require.NoError(t, err) + + contract, err := module_token_admin_registry.NewTokenAdminRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().InitializeLocalDecimals( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestTokenAdminRegistryBackfillLocalDecimalsOp_EncodesProposalLeaf(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + testBundle(t), + TokenAdminRegistryBackfillLocalDecimalsOp, + sui_ops.OpTxDeps{}, + BackfillLocalDecimalsInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + CoinMetadataAddress: testCoinMetadata, + LocalDecimals: 9, + }, + ) + require.NoError(t, err) + require.Equal(t, "token_admin_registry", report.Output.Call.Module) + require.Equal(t, "backfill_local_decimals", report.Output.Call.Function) + require.Equal(t, testStateObjectID, report.Output.Call.StateObjID) + require.Len(t, report.Output.Call.Data, 97) + require.Empty(t, report.Output.Digest) +} + +func TestTokenAdminRegistryBackfillLocalDecimalsOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + testBundle(t), + TokenAdminRegistryBackfillLocalDecimalsOp, + sui_ops.OpTxDeps{}, + BackfillLocalDecimalsInput{ + CCIPPackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + CoinMetadataAddress: testCoinMetadata, + LocalDecimals: 6, + }, + ) + require.NoError(t, err) + + contract, err := module_token_admin_registry.NewTokenAdminRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().BackfillLocalDecimals( + bind.Object{Id: testOwnerCapID}, + bind.Object{Id: testStateObjectID}, + testCoinMetadata, + byte(6), + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) + + d := bcs.NewDeserializer(report.Output.Call.Data) + require.Equal(t, testOwnerCapID[2:], bytesToHex(d.ReadFixedBytes(32))) + require.Equal(t, testStateObjectID[2:], bytesToHex(d.ReadFixedBytes(32))) + require.Equal(t, testCoinMetadata[2:], bytesToHex(d.ReadFixedBytes(32))) + require.Equal(t, uint8(6), d.U8()) +} + +func bytesToHex(b []byte) string { + const hex = "0123456789abcdef" + out := make([]byte, len(b)*2) + for i, v := range b { + out[i*2] = hex[v>>4] + out[i*2+1] = hex[v&0x0f] + } + return string(out) +} + +func TestTokenAdminRegistryUnregisterPoolOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + testBundle(t), + TokenAdminRegistryUnregisterPoolOp, + sui_ops.OpTxDeps{}, + UnregisterPoolInput{ + CCIPPackageId: testCCIPPackageID, + CCIPObjectRef: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + CoinMetadataAddress: testCoinMetadata, + }, + ) + require.NoError(t, err) + require.Equal(t, "token_admin_registry", report.Output.Call.Module) + require.Equal(t, "unregister_pool", report.Output.Call.Function) + require.Equal(t, testStateObjectID, report.Output.Call.StateObjID) + + expected, err := rmn.SerializeMcmsObjectAddrs(testOwnerCapID, testStateObjectID, testCoinMetadata) + require.NoError(t, err) + require.Equal(t, expected, report.Output.Call.Data) + + d := bcs.NewDeserializer(report.Output.Call.Data) + require.Equal(t, testOwnerCapID[2:], bytesToHex(d.ReadFixedBytes(32))) + require.Equal(t, testStateObjectID[2:], bytesToHex(d.ReadFixedBytes(32))) + require.Equal(t, testCoinMetadata[2:], bytesToHex(d.ReadFixedBytes(32))) +} + +func TestTokenAdminRegistryTransferAdminRoleOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + testBundle(t), + TokenAdminRegistryTransferAdminRoleOp, + sui_ops.OpTxDeps{}, + TransferAdminRoleInput{ + CCIPPackageId: testCCIPPackageID, + CCIPObjectRef: testStateObjectID, + CoinMetadataAddress: testCoinMetadata, + NewAdmin: testNewAdmin, + }, + ) + require.NoError(t, err) + + contract, err := module_token_admin_registry.NewTokenAdminRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().TransferAdminRole( + bind.Object{Id: testStateObjectID}, + testCoinMetadata, + testNewAdmin, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestTokenAdminRegistryAcceptAdminRoleOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + testBundle(t), + TokenAdminRegistryAcceptAdminRoleOp, + sui_ops.OpTxDeps{}, + AcceptAdminRoleInput{ + CCIPPackageId: testCCIPPackageID, + CCIPObjectRef: testStateObjectID, + CoinMetadataAddress: testCoinMetadata, + }, + ) + require.NoError(t, err) + + contract, err := module_token_admin_registry.NewTokenAdminRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().AcceptAdminRole( + bind.Object{Id: testStateObjectID}, + testCoinMetadata, + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} diff --git a/deployment/ops/ccip/op_upgrade_registry_mcms_test.go b/deployment/ops/ccip/op_upgrade_registry_mcms_test.go new file mode 100644 index 000000000..af67a374d --- /dev/null +++ b/deployment/ops/ccip/op_upgrade_registry_mcms_test.go @@ -0,0 +1,155 @@ +package ccipops + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_upgrade_registry "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/upgrade_registry" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" +) + +const testModuleName = "fee_quoter" +const testFunctionName = "apply_fee_token_updates" + +func upgradeRegistryTestBundle(t *testing.T) cld_ops.Bundle { + t.Helper() + return cld_ops.NewBundle( + func() context.Context { return t.Context() }, + logger.Test(t), + cld_ops.NewMemoryReporter(), + ) +} + +func TestBlockVersionOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + upgradeRegistryTestBundle(t), + BlockVersionOp, + sui_ops.OpTxDeps{}, + BlockVersionInput{ + PackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + ModuleName: testModuleName, + Version: 1, + }, + ) + require.NoError(t, err) + + contract, err := module_upgrade_registry.NewUpgradeRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().BlockVersion( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + testModuleName, + byte(1), + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestUnblockVersionOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + upgradeRegistryTestBundle(t), + UnblockVersionOp, + sui_ops.OpTxDeps{}, + UnblockVersionInput{ + PackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + ModuleName: testModuleName, + Version: 1, + }, + ) + require.NoError(t, err) + + contract, err := module_upgrade_registry.NewUpgradeRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().UnblockVersion( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + testModuleName, + byte(1), + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestBlockFunctionOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + upgradeRegistryTestBundle(t), + BlockFunctionOp, + sui_ops.OpTxDeps{}, + BlockFunctionInput{ + PackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + ModuleName: testModuleName, + FunctionName: testFunctionName, + Version: 1, + }, + ) + require.NoError(t, err) + + contract, err := module_upgrade_registry.NewUpgradeRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().BlockFunction( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + testModuleName, + testFunctionName, + byte(1), + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} + +func TestUnblockFunctionOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + upgradeRegistryTestBundle(t), + UnblockFunctionOp, + sui_ops.OpTxDeps{}, + UnblockFunctionInput{ + PackageId: testCCIPPackageID, + StateObjectId: testStateObjectID, + OwnerCapObjectId: testOwnerCapID, + ModuleName: testModuleName, + FunctionName: testFunctionName, + Version: 1, + }, + ) + require.NoError(t, err) + + contract, err := module_upgrade_registry.NewUpgradeRegistry(testCCIPPackageID, nil) + require.NoError(t, err) + encodedCall, err := contract.Encoder().UnblockFunction( + bind.Object{Id: testStateObjectID}, + bind.Object{Id: testOwnerCapID}, + testModuleName, + testFunctionName, + byte(1), + ) + require.NoError(t, err) + expected, err := sui_ops.ToTransactionCall(encodedCall, testStateObjectID) + require.NoError(t, err) + require.Equal(t, expected.Data, report.Output.Call.Data) +} diff --git a/deployment/ops/ccip_burn_mint_token_pool/op_burn_mint_token_pool_mcms_test.go b/deployment/ops/ccip_burn_mint_token_pool/op_burn_mint_token_pool_mcms_test.go new file mode 100644 index 000000000..085d2c459 --- /dev/null +++ b/deployment/ops/ccip_burn_mint_token_pool/op_burn_mint_token_pool_mcms_test.go @@ -0,0 +1,133 @@ +package burnminttokenpoolops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_burn_mint_token_pool "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_token_pools/burn_mint_token_pool" + "github.com/smartcontractkit/chainlink-sui/deployment" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestBurnMintTokenPoolDualModeOps_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + typeArgs := []string{mcmstest.CoinTypeArg} + contract, err := module_burn_mint_token_pool.NewBurnMintTokenPool(mcmstest.PackageID, nil) + require.NoError(t, err) + + t.Run("apply_chain_updates", func(t *testing.T) { + t.Parallel() + input := BurnMintTokenPoolApplyChainUpdatesInput{ + BurnMintPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelectorsToRemove: []uint64{}, + RemoteChainSelectorsToAdd: []uint64{}, + RemotePoolAddressesToAdd: [][]string{}, + RemoteTokenAddressesToAdd: []string{}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), BurnMintTokenPoolApplyChainUpdatesOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().ApplyChainUpdates(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelectorsToRemove, input.RemoteChainSelectorsToAdd, [][][]byte{}, [][]byte{}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("set_chain_rate_limiter", func(t *testing.T) { + t.Parallel() + input := BurnMintTokenPoolSetChainRateLimiterInput{ + BurnMintPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelectors: []uint64{mcmstest.DestChainSel}, + OutboundIsEnableds: []bool{true}, + OutboundCapacities: []uint64{1000}, + OutboundRates: []uint64{100}, + InboundIsEnableds: []bool{true}, + InboundCapacities: []uint64{1000}, + InboundRates: []uint64{100}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), BurnMintTokenPoolSetChainRateLimiterOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().SetChainRateLimiterConfigs(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, bind.Object{Id: "0x6"}, input.RemoteChainSelectors, input.OutboundIsEnableds, input.OutboundCapacities, input.OutboundRates, input.InboundIsEnableds, input.InboundCapacities, input.InboundRates) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("add_remote_pool", func(t *testing.T) { + t.Parallel() + input := BurnMintTokenPoolAddRemotePoolInput{ + BurnMintTokenPoolPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelector: mcmstest.DestChainSel, + RemotePoolAddress: "pool-address", + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), BurnMintTokenPoolAddRemotePoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().AddRemotePool(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelector, []byte(input.RemotePoolAddress)) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("set_allowlist_enabled", func(t *testing.T) { + t.Parallel() + input := BurnMintTokenPoolSetAllowlistEnabledInput{ + BurnMintPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + Enabled: true, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), BurnMintTokenPoolSetAllowlistEnabledOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().SetAllowlistEnabled(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.Enabled) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("apply_allowlist_updates", func(t *testing.T) { + t.Parallel() + input := BurnMintTokenPoolApplyAllowlistUpdatesInput{ + BurnMintPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + Removes: []string{}, + Adds: []string{mcmstest.Recipient}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), BurnMintTokenPoolApplyAllowlistUpdatesOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().ApplyAllowlistUpdates(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.Removes, input.Adds) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("remove_remote_pool", func(t *testing.T) { + t.Parallel() + poolAddr, err := deployment.StrToBytes(mcmstest.CoinMetadata) + require.NoError(t, err) + input := BurnMintTokenPoolRemoveRemotePoolInput{ + BurnMintPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelector: mcmstest.DestChainSel, + RemotePoolAddress: mcmstest.CoinMetadata, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), BurnMintTokenPoolRemoveRemotePoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().RemoveRemotePool(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelector, poolAddr) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) +} diff --git a/deployment/ops/ccip_burn_mint_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go b/deployment/ops/ccip_burn_mint_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go new file mode 100644 index 000000000..4562c0a42 --- /dev/null +++ b/deployment/ops/ccip_burn_mint_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go @@ -0,0 +1,36 @@ +package burnminttokenpoolops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_burn_mint_token_pool "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_token_pools/burn_mint_token_pool" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestExecuteOwnershipTransferToMcmsBurnMintTokenPoolOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + typeArgs := []string{mcmstest.CoinTypeArg} + input := ExecuteOwnershipTransferToMcmsBurnMintTokenPoolInput{ + BurnMintTokenPoolPackageId: mcmstest.PackageID, + TypeArgs: typeArgs, + OwnerCapObjectId: mcmstest.OwnerCapID, + StateObjectId: mcmstest.StateObjectID, + RegistryObjectId: mcmstest.RegistryID, + To: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ExecuteOwnershipTransferToMcmsBurnMintTokenPoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + + contract, err := module_burn_mint_token_pool.NewBurnMintTokenPool(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().ExecuteOwnershipTransferToMcms(typeArgs, bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.RegistryObjectId}, input.To) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) +} diff --git a/deployment/ops/ccip_lock_release_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go b/deployment/ops/ccip_lock_release_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go new file mode 100644 index 000000000..6c86512c5 --- /dev/null +++ b/deployment/ops/ccip_lock_release_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go @@ -0,0 +1,36 @@ +package lockreleasetokenpoolops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_lock_release_token_pool "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_token_pools/lock_release_token_pool" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestExecuteOwnershipTransferToMcmsLockReleaseTokenPoolOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + typeArgs := []string{mcmstest.CoinTypeArg} + input := ExecuteOwnershipTransferToMcmsLockReleaseTokenPoolInput{ + LockReleaseTokenPoolPackageId: mcmstest.PackageID, + TypeArgs: typeArgs, + OwnerCapObjectId: mcmstest.OwnerCapID, + StateObjectId: mcmstest.StateObjectID, + RegistryObjectId: mcmstest.RegistryID, + To: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ExecuteOwnershipTransferToMcmsLockReleaseTokenPoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + + contract, err := module_lock_release_token_pool.NewLockReleaseTokenPool(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().ExecuteOwnershipTransferToMcms(typeArgs, bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.RegistryObjectId}, input.To) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) +} diff --git a/deployment/ops/ccip_lock_release_token_pool/op_lock_release_token_pool_mcms_test.go b/deployment/ops/ccip_lock_release_token_pool/op_lock_release_token_pool_mcms_test.go new file mode 100644 index 000000000..adb41f438 --- /dev/null +++ b/deployment/ops/ccip_lock_release_token_pool/op_lock_release_token_pool_mcms_test.go @@ -0,0 +1,131 @@ +package lockreleasetokenpoolops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_lock_release_token_pool "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_token_pools/lock_release_token_pool" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestLockReleaseTokenPoolDualModeOps_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + typeArgs := []string{mcmstest.CoinTypeArg} + contract, err := module_lock_release_token_pool.NewLockReleaseTokenPool(mcmstest.PackageID, nil) + require.NoError(t, err) + + t.Run("apply_chain_updates", func(t *testing.T) { + t.Parallel() + input := LockReleaseTokenPoolApplyChainUpdatesInput{ + LockReleasePackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelectorsToRemove: []uint64{}, + RemoteChainSelectorsToAdd: []uint64{}, + RemotePoolAddressesToAdd: [][]string{}, + RemoteTokenAddressesToAdd: []string{}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), LockReleaseTokenPoolApplyChainUpdatesOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().ApplyChainUpdates(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelectorsToRemove, input.RemoteChainSelectorsToAdd, [][][]byte{}, [][]byte{}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("set_chain_rate_limiter", func(t *testing.T) { + t.Parallel() + input := LockReleaseTokenPoolSetChainRateLimiterInput{ + LockReleasePackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelectors: []uint64{mcmstest.DestChainSel}, + OutboundIsEnableds: []bool{true}, + OutboundCapacities: []uint64{1000}, + OutboundRates: []uint64{100}, + InboundIsEnableds: []bool{true}, + InboundCapacities: []uint64{1000}, + InboundRates: []uint64{100}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), LockReleaseTokenPoolSetChainRateLimiterOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().SetChainRateLimiterConfigs(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, bind.Object{Id: "0x6"}, input.RemoteChainSelectors, input.OutboundIsEnableds, input.OutboundCapacities, input.OutboundRates, input.InboundIsEnableds, input.InboundCapacities, input.InboundRates) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("add_remote_pool", func(t *testing.T) { + t.Parallel() + input := LockReleaseTokenPoolAddRemotePoolInput{ + LockReleaseTokenPoolPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelector: mcmstest.DestChainSel, + RemotePoolAddress: "pool-address", + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), LockReleaseTokenPoolAddRemotePoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().AddRemotePool(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelector, []byte(input.RemotePoolAddress)) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("set_allowlist_enabled", func(t *testing.T) { + t.Parallel() + input := LockReleaseTokenPoolSetAllowlistEnabledInput{ + LockReleaseTokenPoolPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + Enabled: true, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), LockReleaseTokenPoolSetAllowlistEnabledOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().SetAllowlistEnabled(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.Enabled) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("apply_allowlist_updates", func(t *testing.T) { + t.Parallel() + input := LockReleaseTokenPoolApplyAllowlistUpdatesInput{ + LockReleaseTokenPoolPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + Removes: []string{}, + Adds: []string{mcmstest.Recipient}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), LockReleaseTokenPoolApplyAllowlistUpdatesOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().ApplyAllowlistUpdates(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.Removes, input.Adds) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("remove_remote_pool", func(t *testing.T) { + t.Parallel() + poolAddr := []byte(mcmstest.CoinMetadata) + input := LockReleaseTokenPoolRemoveRemotePoolInput{ + LockReleaseTokenPoolPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelector: mcmstest.DestChainSel, + RemotePoolAddress: mcmstest.CoinMetadata, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), LockReleaseTokenPoolRemoveRemotePoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().RemoveRemotePool(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelector, poolAddr) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) +} diff --git a/deployment/ops/ccip_managed_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go b/deployment/ops/ccip_managed_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go new file mode 100644 index 000000000..0b0303127 --- /dev/null +++ b/deployment/ops/ccip_managed_token_pool/op_execute_ownership_transfer_to_mcms_mcms_test.go @@ -0,0 +1,36 @@ +package managedtokenpoolops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_managed_token_pool "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_token_pools/managed_token_pool" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestExecuteOwnershipTransferToMcmsManagedTokenPoolOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + typeArgs := []string{mcmstest.CoinTypeArg} + input := ExecuteOwnershipTransferToMcmsManagedTokenPoolInput{ + ManagedTokenPoolPackageId: mcmstest.PackageID, + TypeArgs: typeArgs, + OwnerCapObjectId: mcmstest.OwnerCapID, + StateObjectId: mcmstest.StateObjectID, + RegistryObjectId: mcmstest.RegistryID, + To: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ExecuteOwnershipTransferToMcmsManagedTokenPoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + + contract, err := module_managed_token_pool.NewManagedTokenPool(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().ExecuteOwnershipTransferToMcms(typeArgs, bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.RegistryObjectId}, input.To) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) +} diff --git a/deployment/ops/ccip_managed_token_pool/op_managed_token_pool_mcms_test.go b/deployment/ops/ccip_managed_token_pool/op_managed_token_pool_mcms_test.go new file mode 100644 index 000000000..59872547f --- /dev/null +++ b/deployment/ops/ccip_managed_token_pool/op_managed_token_pool_mcms_test.go @@ -0,0 +1,131 @@ +package managedtokenpoolops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_managed_token_pool "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_token_pools/managed_token_pool" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestManagedTokenPoolDualModeOps_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + typeArgs := []string{mcmstest.CoinTypeArg} + contract, err := module_managed_token_pool.NewManagedTokenPool(mcmstest.PackageID, nil) + require.NoError(t, err) + + t.Run("apply_chain_updates", func(t *testing.T) { + t.Parallel() + input := ManagedTokenPoolApplyChainUpdatesInput{ + ManagedTokenPoolPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelectorsToRemove: []uint64{}, + RemoteChainSelectorsToAdd: []uint64{}, + RemotePoolAddressesToAdd: [][]string{}, + RemoteTokenAddressesToAdd: []string{}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ManagedTokenPoolApplyChainUpdatesOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().ApplyChainUpdates(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelectorsToRemove, input.RemoteChainSelectorsToAdd, [][][]byte{}, [][]byte{}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("add_remote_pool", func(t *testing.T) { + t.Parallel() + input := ManagedTokenPoolAddRemotePoolInput{ + ManagedTokenPoolPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelector: mcmstest.DestChainSel, + RemotePoolAddress: "pool-address", + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ManagedTokenPoolAddRemotePoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().AddRemotePool(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelector, []byte(input.RemotePoolAddress)) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("remove_remote_pool", func(t *testing.T) { + t.Parallel() + poolAddr := []byte(mcmstest.CoinMetadata) + input := ManagedTokenPoolRemoveRemotePoolInput{ + ManagedTokenPoolPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelector: mcmstest.DestChainSel, + RemotePoolAddress: mcmstest.CoinMetadata, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ManagedTokenPoolRemoveRemotePoolOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().RemoveRemotePool(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.RemoteChainSelector, poolAddr) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("set_chain_rate_limiter", func(t *testing.T) { + t.Parallel() + input := ManagedTokenPoolSetChainRateLimiterInput{ + ManagedTokenPoolPackageId: mcmstest.PackageID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + RemoteChainSelectors: []uint64{mcmstest.DestChainSel}, + OutboundIsEnableds: []bool{true}, + OutboundCapacities: []uint64{1000}, + OutboundRates: []uint64{100}, + InboundIsEnableds: []bool{true}, + InboundCapacities: []uint64{1000}, + InboundRates: []uint64{100}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ManagedTokenPoolSetChainRateLimiterOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().SetChainRateLimiterConfigs(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, bind.Object{Id: "0x6"}, input.RemoteChainSelectors, input.OutboundIsEnableds, input.OutboundCapacities, input.OutboundRates, input.InboundIsEnableds, input.InboundCapacities, input.InboundRates) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("set_allowlist_enabled", func(t *testing.T) { + t.Parallel() + input := ManagedTokenPoolSetAllowlistEnabledInput{ + ManagedTokenPoolPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + Enabled: true, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ManagedTokenPoolSetAllowlistEnabledOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().SetAllowlistEnabled(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.Enabled) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("apply_allowlist_updates", func(t *testing.T) { + t.Parallel() + input := ManagedTokenPoolApplyAllowlistUpdatesInput{ + ManagedTokenPoolPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCap: mcmstest.OwnerCapID, + CoinObjectTypeArg: mcmstest.CoinTypeArg, + Removes: []string{}, + Adds: []string{mcmstest.Recipient}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ManagedTokenPoolApplyAllowlistUpdatesOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().ApplyAllowlistUpdates(typeArgs, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCap}, input.Removes, input.Adds) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) +} diff --git a/deployment/ops/ccip_offramp/op_execute_ownership_transfer_to_mcms_mcms_test.go b/deployment/ops/ccip_offramp/op_execute_ownership_transfer_to_mcms_mcms_test.go new file mode 100644 index 000000000..44515cc53 --- /dev/null +++ b/deployment/ops/ccip_offramp/op_execute_ownership_transfer_to_mcms_mcms_test.go @@ -0,0 +1,35 @@ +package offrampops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_offramp "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_offramp/offramp" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestExecuteOwnershipTransferToMcmsOffRampOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + input := ExecuteOwnershipTransferToMcmsOffRampInput{ + OffRampPackageId: mcmstest.PackageID, + OffRampRefObjectId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + OffRampStateObjectId: mcmstest.CoinMetadata, + RegistryObjectId: mcmstest.RegistryID, + To: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ExecuteOwnershipTransferToMcmsOffRampOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + + contract, err := module_offramp.NewOfframp(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().ExecuteOwnershipTransferToMcms(bind.Object{Id: input.OffRampRefObjectId}, bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.OffRampStateObjectId}, bind.Object{Id: input.RegistryObjectId}, input.To) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.OffRampStateObjectId, nil) +} diff --git a/deployment/ops/ccip_offramp/op_offramp_mcms_test.go b/deployment/ops/ccip_offramp/op_offramp_mcms_test.go new file mode 100644 index 000000000..a5e29b803 --- /dev/null +++ b/deployment/ops/ccip_offramp/op_offramp_mcms_test.go @@ -0,0 +1,94 @@ +package offrampops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_offramp "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_offramp/offramp" + module_ownable "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_onramp/ownable" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestOffRampDualModeOps_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + offramp, err := module_offramp.NewOfframp(mcmstest.PackageID, nil) + require.NoError(t, err) + ownable, err := module_ownable.NewOwnable(mcmstest.PackageID, nil) + require.NoError(t, err) + + t.Run("set_ocr3_config", func(t *testing.T) { + t.Parallel() + input := SetOCR3ConfigInput{ + OffRampPackageId: mcmstest.PackageID, + CCIPObjectRefId: mcmstest.StateObjectID, + OffRampStateId: mcmstest.CoinMetadata, + OwnerCapObjectId: mcmstest.OwnerCapID, + ConfigDigest: make([]byte, 32), + OCRPluginType: 0, + BigF: 1, + IsSignatureVerificationEnabled: false, + Signers: [][]byte{}, + Transmitters: []string{}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), SetOCR3ConfigOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := offramp.Encoder().SetOcr3Config(bind.Object{Id: input.CCIPObjectRefId}, bind.Object{Id: input.OffRampStateId}, bind.Object{Id: input.OwnerCapObjectId}, input.ConfigDigest, input.OCRPluginType, input.BigF, input.IsSignatureVerificationEnabled, input.Signers, input.Transmitters) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.OffRampStateId, nil) + }) + + t.Run("apply_source_chain_config_updates", func(t *testing.T) { + t.Parallel() + input := ApplySourceChainConfigUpdateInput{ + CCIPObjectRef: mcmstest.StateObjectID, + OffRampPackageId: mcmstest.PackageID, + OffRampStateId: mcmstest.CoinMetadata, + OwnerCapObjectId: mcmstest.OwnerCapID, + SourceChainsSelectors: []uint64{mcmstest.DestChainSel}, + SourceChainsIsEnabled: []bool{true}, + SourceChainsIsRMNVerificationDisabled: []bool{false}, + SourceChainsOnRamp: [][]byte{{0x01}}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ApplySourceChainConfigUpdatesOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := offramp.Encoder().ApplySourceChainConfigUpdates(bind.Object{Id: input.CCIPObjectRef}, bind.Object{Id: input.OffRampStateId}, bind.Object{Id: input.OwnerCapObjectId}, input.SourceChainsSelectors, input.SourceChainsIsEnabled, input.SourceChainsIsRMNVerificationDisabled, input.SourceChainsOnRamp) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.OffRampStateId, nil) + }) + + t.Run("add_package_id", func(t *testing.T) { + t.Parallel() + input := AddPackageIdOffRampInput{ + PackageId: mcmstest.PackageID, + StateObjectId: mcmstest.CoinMetadata, + OwnerCapObjectId: mcmstest.OwnerCapID, + NewPackageId: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), AddPackageIdOffRampOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := offramp.Encoder().AddPackageId(bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.NewPackageId) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) + + t.Run("accept_ownership", func(t *testing.T) { + t.Parallel() + input := AcceptOwnershipOffRampInput{ + OffRampPackageId: mcmstest.PackageID, + OffRampRefObjectId: mcmstest.StateObjectID, + OffRampStateObjectId: mcmstest.CoinMetadata, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), AcceptOwnershipOffRampOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := ownable.Encoder().AcceptOwnershipWithArgs(bind.Object{Id: input.OffRampStateObjectId}) + require.NoError(t, err) + encoded.Module.ModuleName = "offramp" + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.OffRampRefObjectId, nil) + }) +} diff --git a/deployment/ops/ccip_onramp/op_execute_ownership_transfer_to_mcms_mcms_test.go b/deployment/ops/ccip_onramp/op_execute_ownership_transfer_to_mcms_mcms_test.go new file mode 100644 index 000000000..c8f867699 --- /dev/null +++ b/deployment/ops/ccip_onramp/op_execute_ownership_transfer_to_mcms_mcms_test.go @@ -0,0 +1,35 @@ +package onrampops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_onramp "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_onramp/onramp" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestExecuteOwnershipTransferToMcmsOnRampOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + input := ExecuteOwnershipTransferToMcmsOnRampInput{ + OnRampPackageId: mcmstest.PackageID, + OnRampRefObjectId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + OnRampStateObjectId: mcmstest.CoinMetadata, + RegistryObjectId: mcmstest.RegistryID, + To: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ExecuteOwnershipTransferToMcmsOnRampOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + + contract, err := module_onramp.NewOnramp(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().ExecuteOwnershipTransferToMcms(bind.Object{Id: input.OnRampRefObjectId}, bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.OnRampStateObjectId}, bind.Object{Id: input.RegistryObjectId}, input.To) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.OnRampStateObjectId, nil) +} diff --git a/deployment/ops/ccip_onramp/op_onramp_mcms_test.go b/deployment/ops/ccip_onramp/op_onramp_mcms_test.go new file mode 100644 index 000000000..9955209f4 --- /dev/null +++ b/deployment/ops/ccip_onramp/op_onramp_mcms_test.go @@ -0,0 +1,141 @@ +package onrampops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_onramp "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_onramp/onramp" + module_ownable "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_onramp/ownable" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestOnRampDualModeOps_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + onramp, err := module_onramp.NewOnramp(mcmstest.PackageID, nil) + require.NoError(t, err) + ownable, err := module_ownable.NewOwnable(mcmstest.PackageID, nil) + require.NoError(t, err) + + t.Run("apply_dest_chain_config_updates", func(t *testing.T) { + t.Parallel() + input := ApplyDestChainConfigureOnRampInput{ + OnRampPackageId: mcmstest.PackageID, + CCIPObjectRefId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + StateObjectId: mcmstest.CoinMetadata, + DestChainSelector: []uint64{mcmstest.DestChainSel}, + DestChainAllowListEnabled: []bool{true}, + DestChainRouters: []string{mcmstest.Recipient}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ApplyDestChainConfigUpdateOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := onramp.Encoder().ApplyDestChainConfigUpdates(bind.Object{Id: input.CCIPObjectRefId}, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.DestChainSelector, input.DestChainAllowListEnabled, input.DestChainRouters) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) + + t.Run("apply_allowlist_updates", func(t *testing.T) { + t.Parallel() + input := ApplyAllowListUpdatesInput{ + OnRampPackageId: mcmstest.PackageID, + CCIPObjectRefId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + StateObjectId: mcmstest.CoinMetadata, + DestChainSelector: []uint64{mcmstest.DestChainSel}, + DestChainAllowListEnabled: []bool{true}, + DestChainAddAllowedSenders: [][]string{{mcmstest.Recipient}}, + DestChainRemoveAllowedSenders: [][]string{}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ApplyAllowListUpdateOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := onramp.Encoder().ApplyAllowlistUpdates(bind.Object{Id: input.CCIPObjectRefId}, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.DestChainSelector, input.DestChainAllowListEnabled, input.DestChainAddAllowedSenders, input.DestChainRemoveAllowedSenders) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) + + t.Run("set_dynamic_config", func(t *testing.T) { + t.Parallel() + input := SetDynamicConfigInput{ + OnRampPackageId: mcmstest.PackageID, + CCIPObjectRefId: mcmstest.StateObjectID, + StateObjectId: mcmstest.CoinMetadata, + OwnerCapObjectId: mcmstest.OwnerCapID, + FeeAggregator: mcmstest.Recipient, + AllowListAdmin: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), SetDynamicConfigOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := onramp.Encoder().SetDynamicConfig(bind.Object{Id: input.CCIPObjectRefId}, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.FeeAggregator, input.AllowListAdmin) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) + + t.Run("withdraw_fee_tokens", func(t *testing.T) { + t.Parallel() + typeArgs := []string{mcmstest.CoinTypeArg} + input := WithdrawFeeTokensInput{ + OnRampPackageId: mcmstest.PackageID, + CCIPObjectRefId: mcmstest.StateObjectID, + StateObjectId: mcmstest.CoinMetadata, + OwnerCapObjectId: mcmstest.OwnerCapID, + FeeTokenMetadataId: mcmstest.RegistryID, + TypeArg: mcmstest.CoinTypeArg, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), WithdrawFeeTokensOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := onramp.Encoder().WithdrawFeeTokens(typeArgs, bind.Object{Id: input.CCIPObjectRefId}, bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.FeeTokenMetadataId}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, typeArgs) + }) + + t.Run("add_package_id", func(t *testing.T) { + t.Parallel() + input := AddPackageIdInput{ + PackageId: mcmstest.PackageID, + StateObjectId: mcmstest.CoinMetadata, + OwnerCapObjectId: mcmstest.OwnerCapID, + NewPackageId: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), AddPackageIdOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := onramp.Encoder().AddPackageId(bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.NewPackageId) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) + + t.Run("remove_package_id", func(t *testing.T) { + t.Parallel() + input := RemovePackageIdOnRampInput{ + OnRampPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.CoinMetadata, + OwnerCapObjectId: mcmstest.OwnerCapID, + PackageId: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), RemovePackageIdOnRampOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := onramp.Encoder().RemovePackageId(bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.PackageId) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) + + t.Run("accept_ownership", func(t *testing.T) { + t.Parallel() + input := AcceptOwnershipOnRampInput{ + OnRampPackageId: mcmstest.PackageID, + CCIPObjectRefId: mcmstest.StateObjectID, + StateObjectId: mcmstest.CoinMetadata, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), AcceptOwnershipOnRampOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := ownable.Encoder().AcceptOwnershipWithArgs(bind.Object{Id: input.StateObjectId}) + require.NoError(t, err) + encoded.Module.ModuleName = "onramp" + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.CCIPObjectRefId, nil) + }) +} diff --git a/deployment/ops/ccip_router/op_execute_ownership_transfer_to_mcms_mcms_test.go b/deployment/ops/ccip_router/op_execute_ownership_transfer_to_mcms_mcms_test.go new file mode 100644 index 000000000..28404ff69 --- /dev/null +++ b/deployment/ops/ccip_router/op_execute_ownership_transfer_to_mcms_mcms_test.go @@ -0,0 +1,34 @@ +package routerops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_router "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_router" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestExecuteOwnershipTransferToMcmsRouterOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + input := ExecuteOwnershipTransferToMcmsRouterInput{ + RouterPackageId: mcmstest.PackageID, + OwnerCapObjectId: mcmstest.OwnerCapID, + RouterStateObjectId: mcmstest.StateObjectID, + RegistryObjectId: mcmstest.RegistryID, + To: mcmstest.Recipient, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), ExecuteOwnershipTransferToMcmsRouterOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + + contract, err := module_router.NewRouter(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().ExecuteOwnershipTransferToMcms(bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.RouterStateObjectId}, bind.Object{Id: input.RegistryObjectId}, input.To) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.RouterStateObjectId, nil) +} diff --git a/deployment/ops/ccip_router/op_router_mcms_test.go b/deployment/ops/ccip_router/op_router_mcms_test.go new file mode 100644 index 000000000..96f1c9c79 --- /dev/null +++ b/deployment/ops/ccip_router/op_router_mcms_test.go @@ -0,0 +1,50 @@ +package routerops + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_router "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip_router" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestRouterDualModeOps_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + router, err := module_router.NewRouter(mcmstest.PackageID, nil) + require.NoError(t, err) + + t.Run("set_on_ramps", func(t *testing.T) { + t.Parallel() + input := SetOnRampsInput{ + RouterPackageId: mcmstest.PackageID, + RouterStateObjectId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + DestChainSelectors: []uint64{mcmstest.DestChainSel}, + OnRampAddresses: []string{mcmstest.Recipient}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), SetOnRampsOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := router.Encoder().SetOnRamps(bind.Object{Id: input.OwnerCapObjectId}, bind.Object{Id: input.RouterStateObjectId}, input.DestChainSelectors, input.OnRampAddresses) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.RouterStateObjectId, nil) + }) + + t.Run("accept_ownership", func(t *testing.T) { + t.Parallel() + input := AcceptOwnershipInput{ + RouterPackageId: mcmstest.PackageID, + RouterStateObjectId: mcmstest.StateObjectID, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), AcceptOwnershipOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := router.Encoder().AcceptOwnership(bind.Object{Id: input.RouterStateObjectId}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.RouterStateObjectId, nil) + }) +} diff --git a/deployment/ops/mcmstest/helpers.go b/deployment/ops/mcmstest/helpers.go new file mode 100644 index 000000000..739ff5085 --- /dev/null +++ b/deployment/ops/mcmstest/helpers.go @@ -0,0 +1,49 @@ +package mcmstest + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" +) + +const ( + PackageID = "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + StateObjectID = "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + OwnerCapID = "0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc" + CoinMetadata = "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" + RegistryID = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + Recipient = "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + CoinTypeArg = "0x2::sui::SUI" + DestChainSel = uint64(16015286601757825753) +) + +func Bundle(t *testing.T) cld_ops.Bundle { + t.Helper() + return cld_ops.NewBundle( + func() context.Context { return t.Context() }, + logger.Test(t), + cld_ops.NewMemoryReporter(), + ) +} + +func AssertProposalDataMatches(t *testing.T, got []byte, encodedCall *bind.EncodedCall, stateObjID string, typeArgs []string) { + t.Helper() + var ( + expected sui_ops.TransactionCall + err error + ) + if len(typeArgs) > 0 { + expected, err = sui_ops.ToTransactionCallWithTypeArgs(encodedCall, stateObjID, typeArgs) + } else { + expected, err = sui_ops.ToTransactionCall(encodedCall, stateObjID) + } + require.NoError(t, err) + require.Equal(t, expected.Data, got) +} diff --git a/deployment/ops/rmn/op_curse_uncurse_mcms_test.go b/deployment/ops/rmn/op_curse_uncurse_mcms_test.go new file mode 100644 index 000000000..f4d65b70e --- /dev/null +++ b/deployment/ops/rmn/op_curse_uncurse_mcms_test.go @@ -0,0 +1,246 @@ +package rmn + +import ( + "testing" + + "github.com/stretchr/testify/require" + + cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + module_rmn_remote "github.com/smartcontractkit/chainlink-sui/bindings/generated/ccip/ccip/rmn_remote" + sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcmstest" +) + +func TestRmnDualModeOps_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + subject := testSubject(0xab) + + t.Run("curse_chain", func(t *testing.T) { + t.Parallel() + input := CurseUncurseChainInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + Subjects: [][]byte{subject}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), CurseChainOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().CurseMultiple(bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.Subjects) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) + + t.Run("uncurse_chain", func(t *testing.T) { + t.Parallel() + input := CurseUncurseChainInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + Subjects: [][]byte{subject}, + } + report, err := cld_ops.ExecuteOperation(mcmstest.Bundle(t), UncurseChainOp, sui_ops.OpTxDeps{}, input) + require.NoError(t, err) + encoded, err := contract.Encoder().UncurseMultiple(bind.Object{Id: input.StateObjectId}, bind.Object{Id: input.OwnerCapObjectId}, input.Subjects) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, input.StateObjectId, nil) + }) +} + +func TestMcmsCreateCurserCapAndTransferOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + McmsCreateCurserCapAndTransferOp, + sui_ops.OpTxDeps{}, + McmsCreateCurserCapAndTransferInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + SlowOwnerCapObjectId: mcmstest.OwnerCapID, + RecipientAddress: mcmstest.Recipient, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().CreateCurserCapAndTransfer(bind.Object{Id: mcmstest.StateObjectID}, bind.Object{Id: mcmstest.OwnerCapID}, mcmstest.Recipient) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} + +func TestMcmsInitializeAllowedCurserCapsOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + McmsInitializeAllowedCurserCapsOp, + sui_ops.OpTxDeps{}, + McmsInitializeAllowedCurserCapsInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + SlowOwnerCapObjectId: mcmstest.OwnerCapID, + InitialCurserCapIds: []string{mcmstest.RegistryID}, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().InitializeAllowedCurserCaps(bind.Object{Id: mcmstest.StateObjectID}, bind.Object{Id: mcmstest.OwnerCapID}, []string{mcmstest.RegistryID}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} + +func TestMcmsRegisterCurserCapIdsOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + McmsRegisterCurserCapIdsOp, + sui_ops.OpTxDeps{}, + McmsRegisterCurserCapIdsInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + SlowOwnerCapObjectId: mcmstest.OwnerCapID, + CurserCapObjectIds: []string{mcmstest.RegistryID}, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().RegisterCurserCapIds(bind.Object{Id: mcmstest.StateObjectID}, bind.Object{Id: mcmstest.OwnerCapID}, []string{mcmstest.RegistryID}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} + +func TestMcmsDeregisterCurserCapIdsOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + McmsDeregisterCurserCapIdsOp, + sui_ops.OpTxDeps{}, + McmsDeregisterCurserCapIdsInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + SlowOwnerCapObjectId: mcmstest.OwnerCapID, + CurserCapObjectIds: []string{mcmstest.RegistryID}, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().DeregisterCurserCapIds(bind.Object{Id: mcmstest.StateObjectID}, bind.Object{Id: mcmstest.OwnerCapID}, []string{mcmstest.RegistryID}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} + +func TestMcmsMintAndRegisterCurserCapOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + McmsMintAndRegisterCurserCapOp, + sui_ops.OpTxDeps{}, + McmsMintAndRegisterCurserCapInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + SlowOwnerCapObjectId: mcmstest.OwnerCapID, + FastRegistryObjectId: mcmstest.RegistryID, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().MintAndRegisterCurserCap( + bind.Object{Id: mcmstest.StateObjectID}, + bind.Object{Id: mcmstest.OwnerCapID}, + bind.Object{Id: mcmstest.RegistryID}, + ) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} + +func TestMcmsRegisterCurserCapOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + McmsRegisterCurserCapOp, + sui_ops.OpTxDeps{}, + McmsRegisterCurserCapInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + SlowOwnerCapObjectId: mcmstest.OwnerCapID, + FastRegistryObjectId: mcmstest.RegistryID, + CurserCapObjectId: mcmstest.CoinMetadata, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().RegisterCurserCap( + bind.Object{Id: mcmstest.StateObjectID}, + bind.Object{Id: mcmstest.OwnerCapID}, + bind.Object{Id: mcmstest.RegistryID}, + bind.Object{Id: mcmstest.CoinMetadata}, + ) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} + +func TestCreateCurserCapOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + CreateCurserCapOp, + sui_ops.OpTxDeps{}, + CreateCurserCapInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + OwnerCapObjectId: mcmstest.OwnerCapID, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().CreateCurserCap(bind.Object{Id: mcmstest.StateObjectID}, bind.Object{Id: mcmstest.OwnerCapID}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} + +func TestCurseWithCurserCapOp_ProposalDataMatchesBindingEncoder(t *testing.T) { + t.Parallel() + + subject := testSubject(0xab) + report, err := cld_ops.ExecuteOperation( + mcmstest.Bundle(t), + CurseWithCurserCapOp, + sui_ops.OpTxDeps{}, + CurseWithCurserCapInput{ + CCIPPackageId: mcmstest.PackageID, + StateObjectId: mcmstest.StateObjectID, + CurserCapObjectId: mcmstest.RegistryID, + Subjects: [][]byte{subject}, + }, + ) + require.NoError(t, err) + + contract, err := module_rmn_remote.NewRmnRemote(mcmstest.PackageID, nil) + require.NoError(t, err) + encoded, err := contract.Encoder().CurseMultipleWithCurserCap(bind.Object{Id: mcmstest.StateObjectID}, bind.Object{Id: mcmstest.RegistryID}, [][]byte{subject}) + require.NoError(t, err) + mcmstest.AssertProposalDataMatches(t, report.Output.Call.Data, encoded, mcmstest.StateObjectID, nil) +} diff --git a/deployment/ops/rmn/op_curser_cap.go b/deployment/ops/rmn/op_curser_cap.go index ef8fb70f5..b5db0298a 100644 --- a/deployment/ops/rmn/op_curser_cap.go +++ b/deployment/ops/rmn/op_curser_cap.go @@ -63,7 +63,7 @@ func createCurserCapHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input Creat opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction(b.GetContext(), opts, encodedCall) + tx, err := contract.CreateCurserCap(b.GetContext(), opts, ref, ownerCap) if err != nil { return sui_ops.OpTxResult[CreateCurserCapObjects]{}, fmt.Errorf("failed to execute create_curser_cap: %w", err) } @@ -88,8 +88,8 @@ func createCurserCapHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input Creat // McmsMintAndRegisterCurserCapInput builds a slow-MCMS proposal leaf that atomically // mints a CurserCap and registers it in the fast MCMS Registry. // -// MCMS-only: there is no direct Move entrypoint for this flow. Use CreateCurserCapOp -// when an EOA holds OwnerCap and only needs to mint the cap object. +// Direct path: EOA holds OwnerCap and signs the PTB (deps.Signer set). +// MCMS path: proposal leaf uses inner function mint_and_register_curser_cap, routed by bindings/mcms_encoder.go. type McmsMintAndRegisterCurserCapInput struct { CCIPPackageId string StateObjectId string @@ -105,41 +105,55 @@ var McmsMintAndRegisterCurserCapOp = cld_ops.NewOperation( ) func mcmsMintAndRegisterCurserCapHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input McmsMintAndRegisterCurserCapInput) (sui_ops.OpTxResult[NoObjects], error) { - if deps.Signer != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("mint_and_register_curser_cap must run through slow MCMS proposal execution, not direct signer PTB") + contract, err := module_rmn_remote.NewRmnRemote(input.CCIPPackageId, deps.Client) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create RMN Remote contract: %w", err) } - data, err := SerializeMcmsObjectAddrs( - input.StateObjectId, - input.SlowOwnerCapObjectId, - input.FastRegistryObjectId, - ) + ref := bind.Object{Id: input.StateObjectId} + ownerCap := bind.Object{Id: input.SlowOwnerCapObjectId} + fastRegistry := bind.Object{Id: input.FastRegistryObjectId} + + encodedCall, err := contract.Encoder().MintAndRegisterCurserCap(ref, ownerCap, fastRegistry) if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to serialize mint_and_register_curser_cap data: %w", err) + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode mint_and_register_curser_cap: %w", err) } - call := sui_ops.TransactionCall{ - PackageID: input.CCIPPackageId, - Module: "rmn_remote", - Function: "mint_and_register_curser_cap", - Data: data, - StateObjID: input.StateObjectId, - TypeArgs: []string{}, + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to build transaction call: %w", err) } - b.Logger.Infow("Encoded mint_and_register_curser_cap MCMS leaf", - "fastRegistry", input.FastRegistryObjectId, - ) + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of mint_and_register_curser_cap as no signer provided", + "fastRegistry", input.FastRegistryObjectId, + ) + return sui_ops.OpTxResult[NoObjects]{ + PackageId: input.CCIPPackageId, + Call: call, + }, nil + } + + opts := deps.GetCallOpts() + opts.Signer = deps.Signer + tx, err := contract.MintAndRegisterCurserCap(b.GetContext(), opts, ref, ownerCap, fastRegistry) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute mint_and_register_curser_cap: %w", err) + } + + b.Logger.Infow("CurserCap minted and registered in fast registry", "digest", tx.Digest, "fastRegistry", input.FastRegistryObjectId) return sui_ops.OpTxResult[NoObjects]{ + Digest: tx.Digest, PackageId: input.CCIPPackageId, Call: call, }, nil } -// McmsCreateCurserCapAndTransferInput builds a slow-MCMS proposal leaf that mints a -// CurserCap and transfers it to RecipientAddress. Use before McmsRegisterCurserCapOp -// in a follow-on proposal once the cap object ID is known from the mint tx. +// McmsCreateCurserCapAndTransferInput mints a CurserCap and transfers it to RecipientAddress. +// +// Direct path: EOA holds OwnerCap and signs the PTB (deps.Signer set). +// MCMS path: proposal leaf uses inner function create_curser_cap_and_transfer, routed by bindings/mcms_encoder.go. type McmsCreateCurserCapAndTransferInput struct { CCIPPackageId string StateObjectId string @@ -155,36 +169,49 @@ var McmsCreateCurserCapAndTransferOp = cld_ops.NewOperation( ) func mcmsCreateCurserCapAndTransferHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input McmsCreateCurserCapAndTransferInput) (sui_ops.OpTxResult[NoObjects], error) { - if deps.Signer != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("create_curser_cap_and_transfer must run through slow MCMS proposal execution, not direct signer PTB") - } if input.RecipientAddress == "" { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("recipient address is required for create_curser_cap_and_transfer") } - data, err := SerializeMcmsObjectAddrs( - input.StateObjectId, - input.SlowOwnerCapObjectId, - input.RecipientAddress, - ) + contract, err := module_rmn_remote.NewRmnRemote(input.CCIPPackageId, deps.Client) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create RMN Remote contract: %w", err) + } + + ref := bind.Object{Id: input.StateObjectId} + ownerCap := bind.Object{Id: input.SlowOwnerCapObjectId} + + encodedCall, err := contract.Encoder().CreateCurserCapAndTransfer(ref, ownerCap, input.RecipientAddress) if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to serialize create_curser_cap_and_transfer data: %w", err) + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode create_curser_cap_and_transfer: %w", err) } - call := sui_ops.TransactionCall{ - PackageID: input.CCIPPackageId, - Module: "rmn_remote", - Function: "create_curser_cap_and_transfer", - Data: data, - StateObjID: input.StateObjectId, - TypeArgs: []string{}, + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to build transaction call: %w", err) } - b.Logger.Infow("Encoded create_curser_cap_and_transfer MCMS leaf", - "recipient", input.RecipientAddress, - ) + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of create_curser_cap_and_transfer as no signer provided", + "recipient", input.RecipientAddress, + ) + return sui_ops.OpTxResult[NoObjects]{ + PackageId: input.CCIPPackageId, + Call: call, + }, nil + } + + opts := deps.GetCallOpts() + opts.Signer = deps.Signer + tx, err := contract.CreateCurserCapAndTransfer(b.GetContext(), opts, ref, ownerCap, input.RecipientAddress) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute create_curser_cap_and_transfer: %w", err) + } + + b.Logger.Infow("CurserCap minted and transferred", "digest", tx.Digest, "recipient", input.RecipientAddress) return sui_ops.OpTxResult[NoObjects]{ + Digest: tx.Digest, PackageId: input.CCIPPackageId, Call: call, }, nil @@ -194,7 +221,9 @@ func mcmsCreateCurserCapAndTransferHandler(b cld_ops.Bundle, deps sui_ops.OpTxDe // existing on-chain CurserCap in the fast MCMS Registry. The cap object ID is // pinned in callback data and validated on-chain at execution. // -// MCMS-only: use McmsMintAndRegisterCurserCapOp when the cap does not exist yet. +// Direct path: EOA holds OwnerCap and signs the PTB (deps.Signer set). +// MCMS path: proposal leaf uses inner function register_curser_cap, routed by bindings/mcms_encoder.go. +// Use McmsMintAndRegisterCurserCapOp when the cap does not exist yet. type McmsRegisterCurserCapInput struct { CCIPPackageId string StateObjectId string @@ -211,45 +240,65 @@ var McmsRegisterCurserCapOp = cld_ops.NewOperation( ) func mcmsRegisterCurserCapHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input McmsRegisterCurserCapInput) (sui_ops.OpTxResult[NoObjects], error) { - if deps.Signer != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("register_curser_cap must run through slow MCMS proposal execution, not direct signer PTB") - } if input.CurserCapObjectId == "" { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("curser cap object id is required for register_curser_cap") } - data, err := SerializeMcmsObjectAddrs( - input.StateObjectId, - input.SlowOwnerCapObjectId, - input.FastRegistryObjectId, - input.CurserCapObjectId, - ) + contract, err := module_rmn_remote.NewRmnRemote(input.CCIPPackageId, deps.Client) if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to serialize register_curser_cap data: %w", err) + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create RMN Remote contract: %w", err) } - call := sui_ops.TransactionCall{ - PackageID: input.CCIPPackageId, - Module: "rmn_remote", - Function: "register_curser_cap", - Data: data, - StateObjID: input.StateObjectId, - TypeArgs: []string{}, + ref := bind.Object{Id: input.StateObjectId} + ownerCap := bind.Object{Id: input.SlowOwnerCapObjectId} + fastRegistry := bind.Object{Id: input.FastRegistryObjectId} + curserCap := bind.Object{Id: input.CurserCapObjectId} + + encodedCall, err := contract.Encoder().RegisterCurserCap(ref, ownerCap, fastRegistry, curserCap) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode register_curser_cap: %w", err) } - b.Logger.Infow("Encoded register_curser_cap MCMS leaf", + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to build transaction call: %w", err) + } + + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of register_curser_cap as no signer provided", + "fastRegistry", input.FastRegistryObjectId, + "curserCap", input.CurserCapObjectId, + ) + return sui_ops.OpTxResult[NoObjects]{ + PackageId: input.CCIPPackageId, + Call: call, + }, nil + } + + opts := deps.GetCallOpts() + opts.Signer = deps.Signer + tx, err := contract.RegisterCurserCap(b.GetContext(), opts, ref, ownerCap, fastRegistry, curserCap) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute register_curser_cap: %w", err) + } + + b.Logger.Infow("CurserCap registered in fast registry", + "digest", tx.Digest, "fastRegistry", input.FastRegistryObjectId, "curserCap", input.CurserCapObjectId, ) return sui_ops.OpTxResult[NoObjects]{ + Digest: tx.Digest, PackageId: input.CCIPPackageId, Call: call, }, nil } -// McmsInitializeAllowedCurserCapsInput builds a slow-MCMS proposal leaf that -// initializes the CurserCap allowlist with an optional initial cap ID set. +// McmsInitializeAllowedCurserCapsInput initializes the CurserCap allowlist with an optional initial cap ID set. +// +// Direct path: EOA holds OwnerCap and signs the PTB (deps.Signer set). +// MCMS path: proposal leaf uses inner function initialize_allowed_curser_caps, routed by bindings/mcms_encoder.go. type McmsInitializeAllowedCurserCapsInput struct { CCIPPackageId string StateObjectId string @@ -265,39 +314,54 @@ var McmsInitializeAllowedCurserCapsOp = cld_ops.NewOperation( ) func mcmsInitializeAllowedCurserCapsHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input McmsInitializeAllowedCurserCapsInput) (sui_ops.OpTxResult[NoObjects], error) { - if deps.Signer != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("initialize_allowed_curser_caps must run through slow MCMS proposal execution, not direct signer PTB") + contract, err := module_rmn_remote.NewRmnRemote(input.CCIPPackageId, deps.Client) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create RMN Remote contract: %w", err) } - data, err := SerializeMcmsObjectAddrsWithAddressVector( - []string{input.StateObjectId, input.SlowOwnerCapObjectId}, - input.InitialCurserCapIds, - ) + ref := bind.Object{Id: input.StateObjectId} + ownerCap := bind.Object{Id: input.SlowOwnerCapObjectId} + + encodedCall, err := contract.Encoder().InitializeAllowedCurserCaps(ref, ownerCap, input.InitialCurserCapIds) if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to serialize initialize_allowed_curser_caps data: %w", err) + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode initialize_allowed_curser_caps: %w", err) } - call := sui_ops.TransactionCall{ - PackageID: input.CCIPPackageId, - Module: "rmn_remote", - Function: "initialize_allowed_curser_caps", - Data: data, - StateObjID: input.StateObjectId, - TypeArgs: []string{}, + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to build transaction call: %w", err) } - b.Logger.Infow("Encoded initialize_allowed_curser_caps MCMS leaf", - "initialCurserCapIds", input.InitialCurserCapIds, - ) + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of initialize_allowed_curser_caps as no signer provided", + "initialCurserCapIds", input.InitialCurserCapIds, + ) + return sui_ops.OpTxResult[NoObjects]{ + PackageId: input.CCIPPackageId, + Call: call, + }, nil + } + + opts := deps.GetCallOpts() + opts.Signer = deps.Signer + tx, err := contract.InitializeAllowedCurserCaps(b.GetContext(), opts, ref, ownerCap, input.InitialCurserCapIds) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute initialize_allowed_curser_caps: %w", err) + } + + b.Logger.Infow("CurserCap allowlist initialized", "digest", tx.Digest, "initialCurserCapIds", input.InitialCurserCapIds) return sui_ops.OpTxResult[NoObjects]{ + Digest: tx.Digest, PackageId: input.CCIPPackageId, Call: call, }, nil } -// McmsRegisterCurserCapIdsInput builds a slow-MCMS proposal leaf that adds cap -// object IDs to the on-chain allowlist. +// McmsRegisterCurserCapIdsInput adds cap object IDs to the on-chain allowlist. +// +// Direct path: EOA holds OwnerCap and signs the PTB (deps.Signer set). +// MCMS path: proposal leaf uses inner function register_curser_cap_ids, routed by bindings/mcms_encoder.go. type McmsRegisterCurserCapIdsInput struct { CCIPPackageId string StateObjectId string @@ -313,42 +377,58 @@ var McmsRegisterCurserCapIdsOp = cld_ops.NewOperation( ) func mcmsRegisterCurserCapIdsHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input McmsRegisterCurserCapIdsInput) (sui_ops.OpTxResult[NoObjects], error) { - if deps.Signer != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("register_curser_cap_ids must run through slow MCMS proposal execution, not direct signer PTB") - } if len(input.CurserCapObjectIds) == 0 { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("at least one curser cap object id is required") } - data, err := SerializeMcmsObjectAddrsWithAddressVector( - []string{input.StateObjectId, input.SlowOwnerCapObjectId}, - input.CurserCapObjectIds, - ) + contract, err := module_rmn_remote.NewRmnRemote(input.CCIPPackageId, deps.Client) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create RMN Remote contract: %w", err) + } + + ref := bind.Object{Id: input.StateObjectId} + ownerCap := bind.Object{Id: input.SlowOwnerCapObjectId} + + encodedCall, err := contract.Encoder().RegisterCurserCapIds(ref, ownerCap, input.CurserCapObjectIds) if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to serialize register_curser_cap_ids data: %w", err) + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode register_curser_cap_ids: %w", err) } - call := sui_ops.TransactionCall{ - PackageID: input.CCIPPackageId, - Module: "rmn_remote", - Function: "register_curser_cap_ids", - Data: data, - StateObjID: input.StateObjectId, - TypeArgs: []string{}, + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to build transaction call: %w", err) } - b.Logger.Infow("Encoded register_curser_cap_ids MCMS leaf", - "curserCapIds", input.CurserCapObjectIds, - ) + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of register_curser_cap_ids as no signer provided", + "curserCapIds", input.CurserCapObjectIds, + ) + return sui_ops.OpTxResult[NoObjects]{ + PackageId: input.CCIPPackageId, + Call: call, + }, nil + } + + opts := deps.GetCallOpts() + opts.Signer = deps.Signer + tx, err := contract.RegisterCurserCapIds(b.GetContext(), opts, ref, ownerCap, input.CurserCapObjectIds) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute register_curser_cap_ids: %w", err) + } + + b.Logger.Infow("CurserCap IDs registered on allowlist", "digest", tx.Digest, "curserCapIds", input.CurserCapObjectIds) return sui_ops.OpTxResult[NoObjects]{ + Digest: tx.Digest, PackageId: input.CCIPPackageId, Call: call, }, nil } -// McmsDeregisterCurserCapIdsInput builds a slow-MCMS proposal leaf that removes -// CurserCap object IDs from the on-chain allowlist, revoking curse authority. +// McmsDeregisterCurserCapIdsInput removes CurserCap object IDs from the on-chain allowlist. +// +// Direct path: EOA holds OwnerCap and signs the PTB (deps.Signer set). +// MCMS path: proposal leaf uses inner function deregister_curser_cap_ids, routed by bindings/mcms_encoder.go. type McmsDeregisterCurserCapIdsInput struct { CCIPPackageId string StateObjectId string @@ -364,35 +444,49 @@ var McmsDeregisterCurserCapIdsOp = cld_ops.NewOperation( ) func mcmsDeregisterCurserCapIdsHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input McmsDeregisterCurserCapIdsInput) (sui_ops.OpTxResult[NoObjects], error) { - if deps.Signer != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("deregister_curser_cap_ids must run through slow MCMS proposal execution, not direct signer PTB") - } if len(input.CurserCapObjectIds) == 0 { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("at least one curser cap object id is required") } - data, err := SerializeMcmsObjectAddrsWithAddressVector( - []string{input.StateObjectId, input.SlowOwnerCapObjectId}, - input.CurserCapObjectIds, - ) + contract, err := module_rmn_remote.NewRmnRemote(input.CCIPPackageId, deps.Client) if err != nil { - return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to serialize deregister_curser_cap_ids data: %w", err) + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to create RMN Remote contract: %w", err) } - call := sui_ops.TransactionCall{ - PackageID: input.CCIPPackageId, - Module: "rmn_remote", - Function: "deregister_curser_cap_ids", - Data: data, - StateObjID: input.StateObjectId, - TypeArgs: []string{}, + ref := bind.Object{Id: input.StateObjectId} + ownerCap := bind.Object{Id: input.SlowOwnerCapObjectId} + + encodedCall, err := contract.Encoder().DeregisterCurserCapIds(ref, ownerCap, input.CurserCapObjectIds) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to encode deregister_curser_cap_ids: %w", err) } - b.Logger.Infow("Encoded deregister_curser_cap_ids MCMS leaf", - "curserCapIds", input.CurserCapObjectIds, - ) + call, err := sui_ops.ToTransactionCall(encodedCall, input.StateObjectId) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to build transaction call: %w", err) + } + + if deps.Signer == nil { + b.Logger.Infow("Skipping execution of deregister_curser_cap_ids as no signer provided", + "curserCapIds", input.CurserCapObjectIds, + ) + return sui_ops.OpTxResult[NoObjects]{ + PackageId: input.CCIPPackageId, + Call: call, + }, nil + } + + opts := deps.GetCallOpts() + opts.Signer = deps.Signer + tx, err := contract.DeregisterCurserCapIds(b.GetContext(), opts, ref, ownerCap, input.CurserCapObjectIds) + if err != nil { + return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute deregister_curser_cap_ids: %w", err) + } + + b.Logger.Infow("CurserCap IDs deregistered from allowlist", "digest", tx.Digest, "curserCapIds", input.CurserCapObjectIds) return sui_ops.OpTxResult[NoObjects]{ + Digest: tx.Digest, PackageId: input.CCIPPackageId, Call: call, }, nil @@ -451,7 +545,13 @@ func curseWithCurserCapHandler(b cld_ops.Bundle, deps sui_ops.OpTxDeps, input Cu opts := deps.GetCallOpts() opts.Signer = deps.Signer - tx, err := contract.Bound().ExecuteTransaction(b.GetContext(), opts, encodedCall) + tx, err := contract.CurseMultipleWithCurserCap( + b.GetContext(), + opts, + bind.Object{Id: input.StateObjectId}, + bind.Object{Id: input.CurserCapObjectId}, + input.Subjects, + ) if err != nil { return sui_ops.OpTxResult[NoObjects]{}, fmt.Errorf("failed to execute curse_with_curser_cap: %w", err) } diff --git a/deployment/ops/rmn/op_curser_cap_test.go b/deployment/ops/rmn/op_curser_cap_test.go index 8eee0a724..9b66ce84c 100644 --- a/deployment/ops/rmn/op_curser_cap_test.go +++ b/deployment/ops/rmn/op_curser_cap_test.go @@ -179,28 +179,6 @@ func TestMcmsMintAndRegisterCurserCapOp_EncodesProposalLeaf(t *testing.T) { require.Len(t, report.Output.Call.Data, 96) } -func TestMcmsMintAndRegisterCurserCapOp_ProposalDataMatchesManualBCS(t *testing.T) { - t.Parallel() - - bundle := testBundle(t) - report, err := cld_ops.ExecuteOperation( - bundle, - McmsMintAndRegisterCurserCapOp, - sui_ops.OpTxDeps{Signer: nil}, - McmsMintAndRegisterCurserCapInput{ - CCIPPackageId: testCCIPPackageID, - StateObjectId: testStateObjectID, - SlowOwnerCapObjectId: testOwnerCapID, - FastRegistryObjectId: testFastRegistry, - }, - ) - require.NoError(t, err) - - expected, err := SerializeMcmsObjectAddrs(testStateObjectID, testOwnerCapID, testFastRegistry) - require.NoError(t, err) - require.Equal(t, expected, report.Output.Call.Data) -} - func TestMcmsCreateCurserCapAndTransferOp_EncodesProposalLeaf(t *testing.T) { t.Parallel() @@ -262,29 +240,6 @@ func TestMcmsRegisterCurserCapOp_EncodesProposalLeaf(t *testing.T) { require.Len(t, report.Output.Call.Data, 128) } -func TestMcmsRegisterCurserCapOp_ProposalDataMatchesManualBCS(t *testing.T) { - t.Parallel() - - bundle := testBundle(t) - report, err := cld_ops.ExecuteOperation( - bundle, - McmsRegisterCurserCapOp, - sui_ops.OpTxDeps{Signer: nil}, - McmsRegisterCurserCapInput{ - CCIPPackageId: testCCIPPackageID, - StateObjectId: testStateObjectID, - SlowOwnerCapObjectId: testOwnerCapID, - FastRegistryObjectId: testFastRegistry, - CurserCapObjectId: testCurserCapID, - }, - ) - require.NoError(t, err) - - expected, err := SerializeMcmsObjectAddrs(testStateObjectID, testOwnerCapID, testFastRegistry, testCurserCapID) - require.NoError(t, err) - require.Equal(t, expected, report.Output.Call.Data) -} - func TestMcmsRegisterCurserCapOp_RequiresCurserCapObjectId(t *testing.T) { t.Parallel() @@ -304,50 +259,6 @@ func TestMcmsRegisterCurserCapOp_RequiresCurserCapObjectId(t *testing.T) { require.Contains(t, err.Error(), "curser cap object id") } -func TestMcmsRegisterCurserCapOp_RejectsDirectSigner(t *testing.T) { - t.Parallel() - - bundle := testBundle(t) - _, err := cld_ops.ExecuteOperation( - bundle, - McmsRegisterCurserCapOp, - sui_ops.OpTxDeps{Signer: stubSigner{}}, - McmsRegisterCurserCapInput{ - CCIPPackageId: testCCIPPackageID, - StateObjectId: testStateObjectID, - SlowOwnerCapObjectId: testOwnerCapID, - FastRegistryObjectId: testFastRegistry, - CurserCapObjectId: testCurserCapID, - }, - ) - require.Error(t, err) - require.Contains(t, err.Error(), "slow MCMS proposal") -} - -func TestMcmsMintAndRegisterCurserCapOp_RejectsDirectSigner(t *testing.T) { - t.Parallel() - - bundle := testBundle(t) - _, err := cld_ops.ExecuteOperation( - bundle, - McmsMintAndRegisterCurserCapOp, - sui_ops.OpTxDeps{Signer: stubSigner{}}, - McmsMintAndRegisterCurserCapInput{ - CCIPPackageId: testCCIPPackageID, - StateObjectId: testStateObjectID, - SlowOwnerCapObjectId: testOwnerCapID, - FastRegistryObjectId: testFastRegistry, - }, - ) - require.Error(t, err) - require.Contains(t, err.Error(), "slow MCMS proposal") -} - -type stubSigner struct{} - -func (stubSigner) Sign([]byte) ([]string, error) { return nil, nil } -func (stubSigner) GetAddress() (string, error) { return "0x1", nil } - func TestCurseWithCurserCapOp_EncodesProposalLeaf(t *testing.T) { t.Parallel() diff --git a/integration-tests/mcms/token_pool_test.go b/integration-tests/mcms/token_pool_test.go index 92a33bfd9..77e95decd 100644 --- a/integration-tests/mcms/token_pool_test.go +++ b/integration-tests/mcms/token_pool_test.go @@ -787,6 +787,7 @@ func RunUnregisterLnRTokenPoolProposal(s *TokenPoolTestSuite) { ccipops.UnregisterPoolInput{ CCIPPackageId: s.ccipPackageId, CCIPObjectRef: s.ccipObjects.CCIPObjectRefObjectId, + OwnerCapObjectId: s.ccipObjects.OwnerCapObjectId, CoinMetadataAddress: s.lnrTokenObjects.CoinMetadataObjectId, }, },