diff --git a/.github/workflows/pull-request-develop.yml b/.github/workflows/pull-request-develop.yml index abfcd5317..d487981f3 100644 --- a/.github/workflows/pull-request-develop.yml +++ b/.github/workflows/pull-request-develop.yml @@ -97,7 +97,6 @@ jobs: nix develop --command go test -v ./bindings/... -tags="integration" - name: Run Operations Tests - if: false # disabled until MCMS package is updated for the new Sui gRPC client env: TEST_DB_URL: postgres://localhost:5432/chainlink_test?sslmode=disable&user=postgres&password=postgres run: | @@ -142,7 +141,6 @@ jobs: nix develop --command sui genesis --force --with-faucet - name: Run System Integration Tests - if: false # disabled until MCMS package is updated for the new Sui gRPC client env: TEST_DB_URL: postgres://localhost:5432/chainlink_test?sslmode=disable&user=postgres&password=postgres run: | diff --git a/bindings/bind/bcs_deserialize.go b/bindings/bind/bcs_deserialize.go index 536f49e90..0b81363dc 100644 --- a/bindings/bind/bcs_deserialize.go +++ b/bindings/bind/bcs_deserialize.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "io" "math/big" "reflect" "strings" @@ -28,7 +29,7 @@ func DeserializeBCS(data []byte, moveTypes []string) ([]any, error) { deserializer := mystenbcs.NewDecoder(reader) ret := make([]any, 0, len(moveTypes)) for _, moveType := range moveTypes { - decoded, _, err := bcsDeserializeType(deserializer, moveType) + decoded, _, err := bcsDeserializeType(reader, deserializer, moveType) if err != nil { return ret, err } @@ -41,11 +42,7 @@ func DeserializeBCS(data []byte, moveTypes []string) ([]any, error) { return ret, nil } -func bcsDeserializeType(deserializer *mystenbcs.Decoder, moveType string) (any, reflect.Type, error) { - if _, ok := bcsStructDecoders[moveType]; ok { - return nil, nil, fmt.Errorf("struct decoder path not supported in DeserializeBCS for type %s", moveType) - } - +func bcsDeserializeType(reader io.Reader, deserializer *mystenbcs.Decoder, moveType string) (any, reflect.Type, error) { switch { case moveType == "bool": var res bool @@ -72,7 +69,7 @@ func bcsDeserializeType(deserializer *mystenbcs.Decoder, moveType string) (any, typ, err := bcsDecode(deserializer, &res) return res, typ, err case strings.HasPrefix(moveType, "vector<") && strings.HasSuffix(moveType, ">"): - return bcsDeserializeSlice(deserializer, moveType) + return bcsDeserializeSlice(reader, deserializer, moveType) case moveType == "address": return bcsDeserializeAddress(deserializer) case moveType == "u128": @@ -80,7 +77,8 @@ func bcsDeserializeType(deserializer *mystenbcs.Decoder, moveType string) (any, case moveType == "u256": return bcsDeserializeBigInt(deserializer, moveType, 32) default: - return nil, nil, fmt.Errorf("unsupported type for BCS deserialization: %s", moveType) + // custom move structs are decoded by their IDs + return bcsDeserializeAddress(deserializer) } } @@ -91,17 +89,28 @@ func bcsDecode(deserializer *mystenbcs.Decoder, target any) (reflect.Type, error return reflect.TypeOf(target).Elem(), nil } -func bcsDeserializeSlice(deserializer *mystenbcs.Decoder, moveType string) (any, reflect.Type, error) { +func bcsDeserializeSlice(reader io.Reader, deserializer *mystenbcs.Decoder, moveType string) (any, reflect.Type, error) { innerType := moveType[len("vector<") : len(moveType)-1] - var length uint64 - if _, err := deserializer.Decode(&length); err != nil { + + // vector uses ULEB128 length + raw bytes; mystenbcs handles this natively. + if innerType == "u8" { + var res []byte + typ, err := bcsDecode(deserializer, &res) + return res, typ, err + } + + length, _, err := mystenbcs.ULEB128Decode[uint64](reader) + if err != nil { return nil, nil, fmt.Errorf("failed to decode vector length: %w", err) } + if length > uint64(^uint(0)>>1) { + return nil, nil, fmt.Errorf("vector length %d out of range", length) + } elements := make([]any, length) var elemType reflect.Type for i := uint64(0); i < length; i++ { - dec, refT, err := bcsDeserializeType(deserializer, innerType) + dec, refT, err := bcsDeserializeType(reader, deserializer, innerType) if err != nil { return nil, nil, err } diff --git a/bindings/bind/compile.go b/bindings/bind/compile.go index 3ca856c0e..c089b5592 100644 --- a/bindings/bind/compile.go +++ b/bindings/bind/compile.go @@ -299,6 +299,7 @@ func compilePackageInternal(packageName contracts.Package, namedAddresses map[st filepath.Join(dstRoot, "ccip", "ccip_offramp"), filepath.Join(dstRoot, "ccip", "ccip_burn_mint_token"), filepath.Join(dstRoot, "ccip", "ccip_dummy_receiver"), + filepath.Join(dstRoot, "ccip", "ccip_broken_receiver"), filepath.Join(dstRoot, "ccip", "managed_token"), filepath.Join(dstRoot, "ccip", "managed_token_faucet"), filepath.Join(dstRoot, "ccip", "mock_eth_token"), @@ -479,7 +480,7 @@ func compilePackageInternal(packageName contracts.Package, namedAddresses map[st } } - if packageName == contracts.CCIPDummyReceiver { + if packageName == contracts.CCIPDummyReceiver || packageName == contracts.CCIPBrokenReceiver { mcmsAddr := namedAddresses["mcms"] if !isZeroAddress(mcmsAddr) { mcmsDir := filepath.Join(dstRoot, "mcms", "mcms") diff --git a/bindings/bind/grpc_response.go b/bindings/bind/grpc_response.go index ae6d3908b..b2e884b38 100644 --- a/bindings/bind/grpc_response.go +++ b/bindings/bind/grpc_response.go @@ -38,6 +38,18 @@ func mapExecuteResponseToModels(resp *suirpcv2.ExecuteTransactionResponse) (*mod result.ObjectChanges = mapChangedObjectsToModels(tx.Effects.GetChangedObjects()) } + if tx.GetEvents() != nil { + events := tx.GetEvents().GetEvents() + for _, event := range events { + result.Events = append(result.Events, models.SuiEventResponse{ + PackageId: event.GetPackageId(), + TransactionModule: event.GetModule(), + Type: event.GetEventType(), + ParsedJson: event.GetJson().AsInterface().(map[string]any), + }) + } + } + return result, nil } @@ -181,6 +193,12 @@ func transactionChangedObjectsReadMaskPaths() []string { "effects.changed_objects.output_version", "effects.changed_objects.output_digest", "effects.changed_objects.input_version", + "events", + "events.events", + "events.events.package_id", + "events.events.module", + "events.events.event_type", + "events.events.json", } } diff --git a/bindings/bind/publish.go b/bindings/bind/publish.go index caecc75bd..931b401bf 100644 --- a/bindings/bind/publish.go +++ b/bindings/bind/publish.go @@ -134,7 +134,7 @@ func FindObjectIdFromPublishTx(tx models.SuiTransactionBlockResponse, module, ob // FindCoinObjectIdFromTx finds a coin object ID from a transaction response by looking for created objects of type Coin func FindCoinObjectIdFromTx(tx models.SuiTransactionBlockResponse, coinType string) (string, error) { - expectedType := fmt.Sprintf("0x2::coin::Coin<%s>", coinType) + expectedType := fmt.Sprintf("0x0000000000000000000000000000000000000000000000000000000000000002::coin::Coin<%s>", coinType) for _, change := range tx.ObjectChanges { if change.Type == "created" && change.ObjectType == expectedType { diff --git a/bindings/bind/type_converter.go b/bindings/bind/type_converter.go index 6847db400..98539f9b3 100644 --- a/bindings/bind/type_converter.go +++ b/bindings/bind/type_converter.go @@ -280,7 +280,7 @@ func convertPureValueToCallArg(typeName string, value any) (*transaction.CallArg case "vector": valueToEncode, err = convertToByteArray(value) - case "0x1::string::String": + case "0x1::string::String", "ascii::String": str, ok := value.(string) if !ok { return nil, fmt.Errorf("expected string, got %T", value) @@ -554,7 +554,7 @@ func convertVectorToBCS(innerType string, value any) (any, error) { return result, nil - case "0x1::string::String": + case "0x1::string::String", "ascii::String": result := make([]string, rv.Len()) for i := range rv.Len() { elem := rv.Index(i).Interface() diff --git a/bindings/generated/ccip/ccip_dummy_receiver/ccip_dummy_receiver/dummy_receiver.go b/bindings/generated/ccip/ccip_dummy_receiver/ccip_dummy_receiver/dummy_receiver.go index 87c55cb39..9ab108e00 100644 --- a/bindings/generated/ccip/ccip_dummy_receiver/ccip_dummy_receiver/dummy_receiver.go +++ b/bindings/generated/ccip/ccip_dummy_receiver/ccip_dummy_receiver/dummy_receiver.go @@ -18,7 +18,7 @@ var ( _ = big.NewInt ) -const FunctionInfo = `[{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"ccip_receive","parameters":[{"name":"expected_message_id","type":"vector"},{"name":"ref","type":"CCIPObjectRef"},{"name":"message","type":"client::Any2SuiMessage"},{"name":"_","type":"Clock"},{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_counter","parameters":[{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_dest_token_amounts","parameters":[{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_token_amount_amount","parameters":[{"name":"token_amount","type":"TokenAmount"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_token_amount_token","parameters":[{"name":"token_amount","type":"TokenAmount"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_token_receiver","parameters":[{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_and_send_coin","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"_","type":"OwnerCap"},{"name":"coin_receiving","type":"Receiving>"},{"name":"recipient","type":"address"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_and_send_coin_no_owner_cap","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"coin_receiving","type":"Receiving>"},{"name":"recipient","type":"address"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_coin","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"_","type":"OwnerCap"},{"name":"coin_receiving","type":"Receiving>"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_coin_no_owner_cap","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"coin_receiving","type":"Receiving>"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"register_receiver","parameters":[{"name":"owner_cap","type":"OwnerCap"},{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"type_and_version","parameters":null}]` +const FunctionInfo = `[{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"ccip_receive","parameters":[{"name":"expected_message_id","type":"vector"},{"name":"ref","type":"CCIPObjectRef"},{"name":"message","type":"client::Any2SuiMessage"},{"name":"clock","type":"Clock"},{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_counter","parameters":[{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_dest_token_amounts","parameters":[{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_token_amount_amount","parameters":[{"name":"token_amount","type":"TokenAmount"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_token_amount_token","parameters":[{"name":"token_amount","type":"TokenAmount"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"get_token_receiver","parameters":[{"name":"state","type":"CCIPReceiverState"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_and_send_coin","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"_","type":"OwnerCap"},{"name":"coin_receiving","type":"Receiving>"},{"name":"recipient","type":"address"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_and_send_coin_no_owner_cap","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"coin_receiving","type":"Receiving>"},{"name":"recipient","type":"address"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_coin","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"_","type":"OwnerCap"},{"name":"coin_receiving","type":"Receiving>"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"receive_coin_no_owner_cap","parameters":[{"name":"state","type":"CCIPReceiverState"},{"name":"coin_receiving","type":"Receiving>"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"register_receiver","parameters":[{"name":"owner_cap","type":"OwnerCap"},{"name":"ref","type":"CCIPObjectRef"}]},{"package":"ccip_dummy_receiver","module":"dummy_receiver","name":"type_and_version","parameters":null}]` type IDummyReceiver interface { TypeAndVersion(ctx context.Context, opts *bind.CallOpts) (*models.SuiTransactionBlockResponse, error) @@ -32,7 +32,7 @@ type IDummyReceiver interface { ReceiveCoin(ctx context.Context, opts *bind.CallOpts, typeArgs []string, state bind.Object, param bind.Object, coinReceiving bind.Object) (*models.SuiTransactionBlockResponse, error) ReceiveAndSendCoinNoOwnerCap(ctx context.Context, opts *bind.CallOpts, typeArgs []string, state bind.Object, coinReceiving bind.Object, recipient string) (*models.SuiTransactionBlockResponse, error) ReceiveCoinNoOwnerCap(ctx context.Context, opts *bind.CallOpts, typeArgs []string, state bind.Object, coinReceiving bind.Object) (*models.SuiTransactionBlockResponse, error) - CcipReceive(ctx context.Context, opts *bind.CallOpts, expectedMessageId []byte, ref bind.Object, message bind.Object, param bind.Object, state bind.Object) (*models.SuiTransactionBlockResponse, error) + CcipReceive(ctx context.Context, opts *bind.CallOpts, expectedMessageId []byte, ref bind.Object, message bind.Object, clock bind.Object, state bind.Object) (*models.SuiTransactionBlockResponse, error) DevInspect() IDummyReceiverDevInspect Encoder() DummyReceiverEncoder Bound() bind.IBoundContract @@ -72,7 +72,7 @@ type DummyReceiverEncoder interface { ReceiveAndSendCoinNoOwnerCapWithArgs(typeArgs []string, args ...any) (*bind.EncodedCall, error) ReceiveCoinNoOwnerCap(typeArgs []string, state bind.Object, coinReceiving bind.Object) (*bind.EncodedCall, error) ReceiveCoinNoOwnerCapWithArgs(typeArgs []string, args ...any) (*bind.EncodedCall, error) - CcipReceive(expectedMessageId []byte, ref bind.Object, message bind.Object, param bind.Object, state bind.Object) (*bind.EncodedCall, error) + CcipReceive(expectedMessageId []byte, ref bind.Object, message bind.Object, clock bind.Object, state bind.Object) (*bind.EncodedCall, error) CcipReceiveWithArgs(args ...any) (*bind.EncodedCall, error) } @@ -267,8 +267,8 @@ func (c *DummyReceiverContract) ReceiveCoinNoOwnerCap(ctx context.Context, opts } // CcipReceive executes the ccip_receive Move function. -func (c *DummyReceiverContract) CcipReceive(ctx context.Context, opts *bind.CallOpts, expectedMessageId []byte, ref bind.Object, message bind.Object, param bind.Object, state bind.Object) (*models.SuiTransactionBlockResponse, error) { - encoded, err := c.dummyReceiverEncoder.CcipReceive(expectedMessageId, ref, message, param, state) +func (c *DummyReceiverContract) CcipReceive(ctx context.Context, opts *bind.CallOpts, expectedMessageId []byte, ref bind.Object, message bind.Object, clock bind.Object, state bind.Object) (*models.SuiTransactionBlockResponse, error) { + encoded, err := c.dummyReceiverEncoder.CcipReceive(expectedMessageId, ref, message, clock, state) if err != nil { return nil, fmt.Errorf("failed to encode function call: %w", err) } @@ -804,7 +804,7 @@ func (c dummyReceiverEncoder) ReceiveCoinNoOwnerCapWithArgs(typeArgs []string, a } // CcipReceive encodes a call to the ccip_receive Move function. -func (c dummyReceiverEncoder) CcipReceive(expectedMessageId []byte, ref bind.Object, message bind.Object, param bind.Object, state bind.Object) (*bind.EncodedCall, error) { +func (c dummyReceiverEncoder) CcipReceive(expectedMessageId []byte, ref bind.Object, message bind.Object, clock bind.Object, state bind.Object) (*bind.EncodedCall, error) { typeArgsList := []string{} typeParamsList := []string{} return c.EncodeCallArgsWithGenerics("ccip_receive", typeArgsList, typeParamsList, []string{ @@ -817,7 +817,7 @@ func (c dummyReceiverEncoder) CcipReceive(expectedMessageId []byte, ref bind.Obj expectedMessageId, ref, message, - param, + clock, state, }, nil) } diff --git a/bindings/tests/testenv/testenv.go b/bindings/tests/testenv/testenv.go index 32bed7080..059382b85 100644 --- a/bindings/tests/testenv/testenv.go +++ b/bindings/tests/testenv/testenv.go @@ -55,7 +55,7 @@ var ( refMu sync.Mutex ) -func SetupEnvironment(t *testing.T) (utils.SuiSigner, client.BindingsClient) { +func SetupEnvironment(t *testing.T) (utils.SuiSigner, client.SuiPTBClient) { t.Helper() log := logger.Test(t) @@ -147,7 +147,7 @@ func Cleanup() { // CreateTestAccount creates a new test account with funding from the faucet. // This requires the test environment to be set up first (via Setup() or SetupEnvironment()). -func CreateTestAccount(t *testing.T) (utils.SuiSigner, client.BindingsClient) { +func CreateTestAccount(t *testing.T) (utils.SuiSigner, client.SuiPTBClient) { t.Helper() refMu.Lock() @@ -256,7 +256,7 @@ func (te *TestEnvironment) cleanup() { te.cleanupLocked() } -func createPTBClient(log logger.Logger) (client.BindingsClient, error) { +func createPTBClient(log logger.Logger) (client.SuiPTBClient, error) { ptbClient, err := client.NewPTBClient(log, client.PTBClientConfig{ GrpcTarget: fmt.Sprintf("%s:%d", loopbackHost, instance.rpcPort), GrpcToken: "test", diff --git a/contracts/ccip/ccip/sources/fee_quoter.move b/contracts/ccip/ccip/sources/fee_quoter.move index 97dafcd43..8f9de2ffb 100644 --- a/contracts/ccip/ccip/sources/fee_quoter.move +++ b/contracts/ccip/ccip/sources/fee_quoter.move @@ -71,6 +71,25 @@ const SVM_TOKEN_TRANSFER_DATA_OVERHEAD: u64 = + 32 // per-chain token billing config, not always included in the token lookup table + 32; // OffRamp pool signer PDA, not included in the token lookup table; +/// The maximum number of receiver object IDs that can be passed in SuiExtraArgsV1. +const SUI_EXTRA_ARGS_MAX_RECEIVER_OBJECT_IDS: u64 = 64; + +/// Number of overhead accounts needed for message execution on SUI. +/// This is the message.receiver. +const SUI_MESSAGING_ACCOUNTS_OVERHEAD: u64 = 1; + +/// The size of each SUI account address in bytes. +const SUI_ACCOUNT_BYTE_SIZE: u64 = 32; + +/// The expected static payload size of a token transfer when BCS encoded on SUI. +/// TokenPool extra_data contents are dynamic and billed via dest_bytes_overhead. +const SUI_TOKEN_TRANSFER_DATA_OVERHEAD: u64 = + (1 + 32) // source_pool_address: ULEB128 length + 32 bytes + + 32 // dest_token_address + + 4 // dest_gas_amount + + 1 // extra_data: ULEB128 length for empty vector + + 32; // amount + const MAX_U64: u256 = 18446744073709551615; const MAX_U160: u256 = 1461501637330902918203684832716283019655932542975; const VAL_1E5: u256 = 100_000; @@ -241,6 +260,8 @@ const EInvalidSvmAccountLength: u64 = 35; const ETokenAmountMismatch: u64 = 36; const EInvalidOwnerCap: u64 = 37; const EInvalidFunction: u64 = 38; +const ETooManySuiExtraArgsReceiverObjectIds: u64 = 39; +const EInvalidSuiReceiverObjectIdLength: u64 = 40; public fun type_and_version(): String { string::utf8(b"FeeQuoter 1.6.1") @@ -708,7 +729,18 @@ public fun get_validated_fee( ) { resolve_generic_gas_limit(dest_chain_config, extra_args) } else if (chain_family_selector == CHAIN_FAMILY_SELECTOR_SUI) { - resolve_sui_gas_limit(dest_chain_config, extra_args) + let (gas, overhead) = resolve_sui_gas_limit( + dest_chain_config, + state, + dest_chain_selector, + extra_args, + receiver, + data_len, + tokens_len, + local_token_addresses, + ); + svm_payload_overhead = overhead; + gas } else if (chain_family_selector == CHAIN_FAMILY_SELECTOR_SVM) { let (gas, overhead) = resolve_svm_gas_limit( dest_chain_config, @@ -866,19 +898,91 @@ fun resolve_generic_gas_limit(dest_chain_config: &DestChainConfig, extra_args: v gas_limit } -fun resolve_sui_gas_limit(dest_chain_config: &DestChainConfig, extra_args: vector): u256 { +/// Returns `(gas_limit, sui_payload_overhead_bytes)`. +/// `sui_payload_overhead_bytes` is the SUI-specific payload size counted in maxDataBytes validation +/// but not already included in `data_len` or per-token `dest_bytes_overhead` billing. +fun resolve_sui_gas_limit( + dest_chain_config: &DestChainConfig, + state: &FeeQuoterState, + dest_chain_selector: u64, + extra_args: vector, + receiver: vector, + data_len: u64, + tokens_len: u64, + local_token_addresses: vector
, +): (u256, u64) { let ( gas_limit, allow_out_of_order_execution, - _token_receiver, - _receiver_object_ids, + token_receiver, + receiver_object_ids, ) = decode_sui_extra_args(extra_args); assert!(gas_limit <= (dest_chain_config.max_per_msg_gas_limit as u64), EMessageGasLimitTooHigh); assert!( !dest_chain_config.enforce_out_of_order || allow_out_of_order_execution, EExtraArgOutOfOrderExecutionMustBeTrue, ); - gas_limit as u256 + + let receiver_object_ids_length = receiver_object_ids.length(); + assert!( + receiver_object_ids_length <= SUI_EXTRA_ARGS_MAX_RECEIVER_OBJECT_IDS, + ETooManySuiExtraArgsReceiverObjectIds, + ); + + let mut i = 0; + while (i < receiver_object_ids_length) { + assert!(receiver_object_ids[i].length() == 32, EInvalidSuiReceiverObjectIdLength); + i = i + 1; + }; + + let mut sui_payload_overhead = tokens_len * SUI_TOKEN_TRANSFER_DATA_OVERHEAD; + + assert!(receiver.length() == 32, EInvalid32BytesAddress); + let receiver_uint = eth_abi::decode_u256_value(receiver); + if (receiver_uint == 0) { + assert!(receiver_object_ids_length == 0, ETooManySuiExtraArgsReceiverObjectIds); + } else { + sui_payload_overhead = + sui_payload_overhead + ( + receiver_object_ids_length + SUI_MESSAGING_ACCOUNTS_OVERHEAD + ) * SUI_ACCOUNT_BYTE_SIZE; + }; + + if (tokens_len > 0) { + assert!( + token_receiver.length() == 32 + && eth_abi::decode_u256_value(token_receiver) != 0, + EInvalidTokenReceiver, + ); + }; + + let mut sui_expanded_data_length = data_len + sui_payload_overhead; + + let mut i = 0; + while (i < tokens_len) { + let local_token_address = local_token_addresses[i]; + let destBytesOverhead = get_token_transfer_fee_config_internal( + state, + dest_chain_selector, + local_token_address, + ).dest_bytes_overhead; + + if (destBytesOverhead > 0) { + sui_expanded_data_length = sui_expanded_data_length + (destBytesOverhead as u64); + } else { + sui_expanded_data_length = + sui_expanded_data_length + (CCIP_LOCK_OR_BURN_V1_RET_BYTES as u64); + }; + + i = i + 1; + }; + + assert!( + sui_expanded_data_length <= (dest_chain_config.max_data_bytes as u64), + EMessageTooLarge, + ); + + (gas_limit as u256, sui_payload_overhead) } fun check_svm_writable_bitmap(account_is_writable_bitmap: u64, accounts_length: u64) { diff --git a/contracts/ccip/ccip/tests/state_object_upgrade_test.move b/contracts/ccip/ccip/tests/state_object_upgrade_test.move index 2ba444499..39e1849a6 100644 --- a/contracts/ccip/ccip/tests/state_object_upgrade_test.move +++ b/contracts/ccip/ccip/tests/state_object_upgrade_test.move @@ -11,6 +11,7 @@ use sui::test_scenario::{Self as ts, Scenario}; const ADMIN: address = @0xA; const PACKAGE_OWNER: address = @0xB; +const TEST_PACKAGE: address = @0xCCCC; fun init_mcms_and_ccip(ctx: &mut TxContext) { mcms_account::test_init(ctx); @@ -62,16 +63,14 @@ fun test_upgrade_flow_updates_state_correctly() { ts::next_tx(&mut scenario, PACKAGE_OWNER); let mut deployer_state = ts::take_shared(&scenario); - let registry = ts::take_shared(&scenario); let ctx = ts::ctx(&mut scenario); - let upgrade_cap = package::test_publish(@ccip.to_id(), ctx); + let upgrade_cap = package::test_publish(TEST_PACKAGE.to_id(), ctx); let old_package_address = upgrade_cap.package().to_address(); let cap_id = object::id(&upgrade_cap); - mcms_deployer::register_upgrade_cap( + mcms_deployer::test_register_upgrade_cap( &mut deployer_state, - ®istry, upgrade_cap, ctx, ); @@ -80,7 +79,6 @@ fun test_upgrade_flow_updates_state_correctly() { assert!(mcms_deployer::has_upgrade_cap(&deployer_state, old_package_address), 0); ts::return_shared(deployer_state); - ts::return_shared(registry); // Perform upgrade: authorize -> upgrade -> commit ts::next_tx(&mut scenario, ADMIN); @@ -165,21 +163,18 @@ fun test_cannot_authorize_upgrade_with_old_package_address_after_upgrade() { { ts::next_tx(&mut scenario, PACKAGE_OWNER); let mut deployer_state = ts::take_shared(&scenario); - let registry = ts::take_shared(&scenario); let ctx = ts::ctx(&mut scenario); - let upgrade_cap = package::test_publish(@ccip.to_id(), ctx); + let upgrade_cap = package::test_publish(TEST_PACKAGE.to_id(), ctx); old_package_address = upgrade_cap.package().to_address(); - mcms_deployer::register_upgrade_cap( + mcms_deployer::test_register_upgrade_cap( &mut deployer_state, - ®istry, upgrade_cap, ctx, ); ts::return_shared(deployer_state); - ts::return_shared(registry); }; // Perform one upgrade @@ -247,21 +242,18 @@ fun test_multiple_upgrades_chain_correctly() { { ts::next_tx(&mut scenario, PACKAGE_OWNER); let mut deployer_state = ts::take_shared(&scenario); - let registry = ts::take_shared(&scenario); let ctx = ts::ctx(&mut scenario); - let upgrade_cap = package::test_publish(@ccip.to_id(), ctx); + let upgrade_cap = package::test_publish(TEST_PACKAGE.to_id(), ctx); current_package_address = upgrade_cap.package().to_address(); - mcms_deployer::register_upgrade_cap( + mcms_deployer::test_register_upgrade_cap( &mut deployer_state, - ®istry, upgrade_cap, ctx, ); ts::return_shared(deployer_state); - ts::return_shared(registry); }; // Perform 3 consecutive upgrades diff --git a/contracts/ccip/ccip/tests/sui_fee_billing_tests.move b/contracts/ccip/ccip/tests/sui_fee_billing_tests.move new file mode 100644 index 000000000..fe4a709ca --- /dev/null +++ b/contracts/ccip/ccip/tests/sui_fee_billing_tests.move @@ -0,0 +1,319 @@ +#[allow(implicit_const_copy)] +#[test_only] +module ccip::sui_fee_billing_tests; + +use ccip::client; +use ccip::fee_quoter; +use ccip::ownable::OwnerCap; +use ccip::state_object::{Self, CCIPObjectRef}; +use ccip::upgrade_registry; +use sui::clock; +use sui::test_scenario; + +const CHAIN_FAMILY_SELECTOR_SUI: vector = x"c4e05953"; +const ONE_E_18: u256 = 1_000_000_000_000_000_000; +const FEE_TOKEN: address = + @0x1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b; +const TOKEN_2: address = @0x000000000000000000000000F4030086522a5bEEa4988F8cA5B36dbC97BeE88c; +const SUI_RECEIVER: vector = + x"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; +const TOKEN_RECEIVER: vector = + x"aabbccdd11223344aabbccdd11223344aabbccdd11223344aabbccdd11223344"; +const OBJ1: vector = + x"0101010101010101010101010101010101010101010101010101010101010101"; +const OBJ2: vector = + x"0202020202020202020202020202020202020202020202020202020202020202"; +const OBJ3: vector = + x"0303030303030303030303030303030303030303030303030303030303030303"; +const OBJ4: vector = + x"0404040404040404040404040404040404040404040404040404040404040404"; +const OBJ5: vector = + x"0505050505050505050505050505050505050505050505050505050505050505"; +const OBJ6: vector = + x"0606060606060606060606060606060606060606060606060606060606060606"; +const OBJ7: vector = + x"0707070707070707070707070707070707070707070707070707070707070707"; +const OBJ8: vector = + x"0808080808080808080808080808080808080808080808080808080808080808"; +const OBJ9: vector = + x"0909090909090909090909090909090909090909090909090909090909090909"; +const OBJ10: vector = + x"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"; +const ADMIN: address = @0x1; + +fun setup(): (test_scenario::Scenario, OwnerCap, CCIPObjectRef) { + let mut scenario = test_scenario::begin(ADMIN); + let ctx = scenario.ctx(); + state_object::test_init(ctx); + scenario.next_tx(ADMIN); + let owner_cap = scenario.take_from_sender(); + let ref = scenario.take_shared(); + (scenario, owner_cap, ref) +} + +fun init_fee_quoter(ref: &mut CCIPObjectRef, cap: &OwnerCap, ctx: &mut TxContext) { + upgrade_registry::initialize(ref, cap, ctx); + fee_quoter::initialize( + ref, + cap, + 200 * ONE_E_18, + FEE_TOKEN, + 1000, + vector[FEE_TOKEN, TOKEN_2], + ctx, + ); +} + +fun setup_sui_dest(ref: &mut CCIPObjectRef, cap: &OwnerCap, ctx: &mut TxContext) { + fee_quoter::apply_dest_chain_config_updates( + ref, + cap, + 100, + true, + 10, + 30_000, + 3_000_000, + 300_000, + 16, + 40, + 300, + 100, + 16, + 600, + CHAIN_FAMILY_SELECTOR_SUI, + false, + 50, + 90_000, + 200_000, + ONE_E_18 as u64, + 1_000_000, + 50, + ctx, + ); +} + +fun setup_prices( + ref: &mut CCIPObjectRef, + fq_cap: &fee_quoter::FeeQuoterCap, + clock: &clock::Clock, + ctx: &mut TxContext, +) { + fee_quoter::update_prices( + ref, + fq_cap, + clock, + vector[FEE_TOKEN, TOKEN_2], + vector[150_000_000_000 * ONE_E_18, 150_000_000_000 * ONE_E_18], + vector[100], + vector[7_500_000_000_000], + ctx, + ); +} + +fun cleanup(scenario: test_scenario::Scenario, cap: OwnerCap, ref: CCIPObjectRef) { + test_scenario::return_to_sender(&scenario, cap); + test_scenario::return_shared(ref); + test_scenario::end(scenario); +} + +fun many_object_ids(count: u64): vector> { + let mut ids = vector[]; + let mut i = 0; + while (i < count) { + ids.push_back(x"2234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdea"); + i = i + 1; + }; + ids +} + +#[test] +public fun test_sui_fee_increases_with_receiver_object_id_count() { + let (mut scenario, cap, mut ref) = setup(); + let ctx = scenario.ctx(); + init_fee_quoter(&mut ref, &cap, ctx); + let fq_cap = fee_quoter::new_fee_quoter_cap(&ref, &cap, ctx); + let mut clock = clock::create_for_testing(ctx); + clock::increment_for_testing(&mut clock, 20000); + setup_prices(&mut ref, &fq_cap, &clock, ctx); + setup_sui_dest(&mut ref, &cap, ctx); + fee_quoter::apply_premium_multiplier_wei_per_eth_updates( + &mut ref, + &cap, + vector[FEE_TOKEN], + vector[ONE_E_18 as u64], + ctx, + ); + + let fee_0_ids = fee_quoter::get_validated_fee( + &ref, + &clock, + 100, + SUI_RECEIVER, + b"test_payload", + vector[], + vector[], + FEE_TOKEN, + client::encode_sui_extra_args_v1(200_000, false, TOKEN_RECEIVER, vector[]), + ); + let fee_10_ids = fee_quoter::get_validated_fee( + &ref, + &clock, + 100, + SUI_RECEIVER, + b"test_payload", + vector[], + vector[], + FEE_TOKEN, + client::encode_sui_extra_args_v1( + 200_000, + false, + TOKEN_RECEIVER, + vector[OBJ1, OBJ2, OBJ3, OBJ4, OBJ5, OBJ6, OBJ7, OBJ8, OBJ9, OBJ10], + ), + ); + + assert!(fee_0_ids > 0, 1); + assert!(fee_10_ids > fee_0_ids, 2); + + fee_quoter::destroy_fee_quoter_cap(&ref, &cap, fq_cap); + clock::destroy_for_testing(clock); + cleanup(scenario, cap, ref); +} + +#[test] +#[expected_failure(abort_code = fee_quoter::ETooManySuiExtraArgsReceiverObjectIds)] +public fun test_sui_fee_reverts_when_too_many_receiver_object_ids() { + let (mut scenario, cap, mut ref) = setup(); + let ctx = scenario.ctx(); + init_fee_quoter(&mut ref, &cap, ctx); + let fq_cap = fee_quoter::new_fee_quoter_cap(&ref, &cap, ctx); + let mut clock = clock::create_for_testing(ctx); + clock::increment_for_testing(&mut clock, 20000); + setup_prices(&mut ref, &fq_cap, &clock, ctx); + setup_sui_dest(&mut ref, &cap, ctx); + fee_quoter::apply_premium_multiplier_wei_per_eth_updates( + &mut ref, + &cap, + vector[FEE_TOKEN], + vector[ONE_E_18 as u64], + ctx, + ); + + fee_quoter::get_validated_fee( + &ref, + &clock, + 100, + SUI_RECEIVER, + b"test_payload", + vector[], + vector[], + FEE_TOKEN, + client::encode_sui_extra_args_v1(200_000, false, TOKEN_RECEIVER, many_object_ids(65)), + ); + + fee_quoter::destroy_fee_quoter_cap(&ref, &cap, fq_cap); + clock::destroy_for_testing(clock); + cleanup(scenario, cap, ref); +} + +#[test] +#[expected_failure(abort_code = fee_quoter::ETooManySuiExtraArgsReceiverObjectIds)] +public fun test_sui_fee_reverts_zero_receiver_with_object_ids() { + let (mut scenario, cap, mut ref) = setup(); + let ctx = scenario.ctx(); + init_fee_quoter(&mut ref, &cap, ctx); + let fq_cap = fee_quoter::new_fee_quoter_cap(&ref, &cap, ctx); + let mut clock = clock::create_for_testing(ctx); + clock::increment_for_testing(&mut clock, 20000); + setup_prices(&mut ref, &fq_cap, &clock, ctx); + setup_sui_dest(&mut ref, &cap, ctx); + fee_quoter::apply_premium_multiplier_wei_per_eth_updates( + &mut ref, + &cap, + vector[FEE_TOKEN], + vector[ONE_E_18 as u64], + ctx, + ); + + let zero_receiver = x"0000000000000000000000000000000000000000000000000000000000000000"; + fee_quoter::get_validated_fee( + &ref, + &clock, + 100, + zero_receiver, + b"test_payload", + vector[], + vector[], + FEE_TOKEN, + client::encode_sui_extra_args_v1(200_000, false, TOKEN_RECEIVER, vector[OBJ1]), + ); + + fee_quoter::destroy_fee_quoter_cap(&ref, &cap, fq_cap); + clock::destroy_for_testing(clock); + cleanup(scenario, cap, ref); +} + +#[test] +#[expected_failure(abort_code = fee_quoter::EMessageTooLarge)] +public fun test_sui_expanded_payload_respects_max_data_bytes() { + let (mut scenario, cap, mut ref) = setup(); + let ctx = scenario.ctx(); + init_fee_quoter(&mut ref, &cap, ctx); + let fq_cap = fee_quoter::new_fee_quoter_cap(&ref, &cap, ctx); + let mut clock = clock::create_for_testing(ctx); + clock::increment_for_testing(&mut clock, 20000); + setup_prices(&mut ref, &fq_cap, &clock, ctx); + + // Configure with a low max_data_bytes (512) so object IDs push it over the limit + fee_quoter::apply_dest_chain_config_updates( + &mut ref, + &cap, + 100, + true, + 10, + 512, + 3_000_000, + 300_000, + 16, + 40, + 300, + 100, + 16, + 600, + CHAIN_FAMILY_SELECTOR_SUI, + false, + 50, + 90_000, + 200_000, + ONE_E_18 as u64, + 1_000_000, + 50, + ctx, + ); + + fee_quoter::apply_premium_multiplier_wei_per_eth_updates( + &mut ref, + &cap, + vector[FEE_TOKEN], + vector[ONE_E_18 as u64], + ctx, + ); + + // 10 object IDs = (10 + 1) * 32 = 352 bytes overhead + 12 bytes data = 364. Should pass. + // But 15 IDs = (15 + 1) * 32 = 512 + 12 = 524 > 512. Should fail. + fee_quoter::get_validated_fee( + &ref, + &clock, + 100, + SUI_RECEIVER, + b"test_payload", + vector[], + vector[], + FEE_TOKEN, + client::encode_sui_extra_args_v1(200_000, false, TOKEN_RECEIVER, many_object_ids(15)), + ); + + fee_quoter::destroy_fee_quoter_cap(&ref, &cap, fq_cap); + clock::destroy_for_testing(clock); + cleanup(scenario, cap, ref); +} diff --git a/contracts/ccip/ccip/tests/token_admin_registry_tests.move b/contracts/ccip/ccip/tests/token_admin_registry_tests.move index f32238c80..1b5d4174b 100644 --- a/contracts/ccip/ccip/tests/token_admin_registry_tests.move +++ b/contracts/ccip/ccip/tests/token_admin_registry_tests.move @@ -378,7 +378,7 @@ public fun test_register() { local_token, ); assert!( - token_type == ascii::string(b"5ef4b483da6644c84aa78eae4f51a9bfb1fb4554d5134ac98892e931fcbdd6bf::token_admin_registry_tests::TOKEN_ADMIN_REGISTRY_TESTS"), + token_type == type_name::into_string(type_name::with_defining_ids()), ); assert!(type_proof == type_name::into_string(type_name::with_defining_ids())); @@ -416,7 +416,7 @@ public fun test_register() { local_token, ); assert!( - token_type == ascii::string(b"5ef4b483da6644c84aa78eae4f51a9bfb1fb4554d5134ac98892e931fcbdd6bf::token_admin_registry_tests::TOKEN_ADMIN_REGISTRY_TESTS"), + token_type == type_name::into_string(type_name::with_defining_ids()), ); // Since TypeProof and TypeProof2 have the same package ID, the type proof should remain as TypeProof assert!(type_proof == type_name::into_string(type_name::with_defining_ids())); diff --git a/contracts/ccip/ccip_broken_receiver/Move.lock b/contracts/ccip/ccip_broken_receiver/Move.lock new file mode 100644 index 000000000..276ab11b5 --- /dev/null +++ b/contracts/ccip/ccip_broken_receiver/Move.lock @@ -0,0 +1,35 @@ +# Generated by move; do not edit +# This file should be checked in. + +[move] +version = 4 + +[pinned.testnet.MoveStdlib] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/move-stdlib", rev = "c2428b3aaf9c24270b609001e56d96cb10c76d28" } +use_environment = "testnet" +manifest_digest = "C4FE4C91DE74CBF223B2E380AE40F592177D21870DC2D7EB6227D2D694E05363" +deps = {} + +[pinned.testnet.Sui] +source = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "c2428b3aaf9c24270b609001e56d96cb10c76d28" } +use_environment = "testnet" +manifest_digest = "7AFB66695545775FBFBB2D3078ADFD084244D5002392E837FDE21D9EA1C6D01C" +deps = { MoveStdlib = "MoveStdlib" } + +[pinned.testnet.ccip] +source = { local = "../ccip" } +use_environment = "testnet" +manifest_digest = "B41543A1928002B1ABE6F16D69A3C1BE6BCA4D7A4DB7D2F9048274EFE11ED724" +deps = { mcms = "mcms", std = "MoveStdlib", sui = "Sui" } + +[pinned.testnet.ccip_broken_receiver] +source = { root = true } +use_environment = "testnet" +manifest_digest = "C7C879F0A4269E2362771F19494E1AD03A480D84F6E8221262E457CF21A2249B" +deps = { ccip = "ccip", std = "MoveStdlib", sui = "Sui" } + +[pinned.testnet.mcms] +source = { local = "../../mcms/mcms" } +use_environment = "testnet" +manifest_digest = "5745706258F61D6CE210904B3E6AE87A73CE9D31A6F93BE4718C442529332A87" +deps = { std = "MoveStdlib", sui = "Sui" } diff --git a/contracts/ccip/ccip_broken_receiver/Move.toml b/contracts/ccip/ccip_broken_receiver/Move.toml new file mode 100644 index 000000000..acf6474d7 --- /dev/null +++ b/contracts/ccip/ccip_broken_receiver/Move.toml @@ -0,0 +1,6 @@ +[package] +name = "ccip_broken_receiver" +edition = "2024" + +[dependencies] +ccip = { local = "../ccip" } diff --git a/contracts/ccip/ccip_broken_receiver/sources/broken_receiver.move b/contracts/ccip/ccip_broken_receiver/sources/broken_receiver.move new file mode 100644 index 000000000..19e368d92 --- /dev/null +++ b/contracts/ccip/ccip_broken_receiver/sources/broken_receiver.move @@ -0,0 +1,56 @@ +// THIS CONTRACT IS ONLY FOR TESTING PURPOSES. +// It exposes a ccip_receive with a generic type parameter to produce a normalized ABI +// containing {"Vector": {"TypeParameter": 0}}, which triggers the poison ABI path +// in the relayer's DecodeParameters/decodeParam. +module ccip_broken_receiver::broken_receiver; + +use ccip::client; +use ccip::offramp_state_helper as osh; +use ccip::publisher_wrapper; +use ccip::receiver_registry; +use ccip::state_object::CCIPObjectRef; +use std::string::{Self, String}; +use sui::dynamic_field as df; +use sui::package::{Self, Publisher}; + +public struct BROKEN_RECEIVER has drop {} + +public struct OwnerCap has key, store { + id: UID, +} + +public struct BrokenReceiverProof has drop {} + +public struct PublisherKey has copy, drop, store {} + +public fun type_and_version(): String { + string::utf8(b"BrokenReceiver 1.0.0-test") +} + +fun init(otw: BROKEN_RECEIVER, ctx: &mut TxContext) { + let mut owner_cap = OwnerCap { + id: object::new(ctx), + }; + + let publisher = package::claim(otw, ctx); + df::add(&mut owner_cap.id, PublisherKey {}, publisher); + + transfer::transfer(owner_cap, ctx.sender()); +} + +public fun register_receiver(owner_cap: &OwnerCap, ref: &mut CCIPObjectRef) { + let publisher: &Publisher = df::borrow(&owner_cap.id, PublisherKey {}); + let publisher_wrapper = publisher_wrapper::create(publisher, BrokenReceiverProof {}); + receiver_registry::register_receiver(ref, publisher_wrapper, BrokenReceiverProof {}); +} + +/// This function has a generic type parameter T, producing a normalized ABI with +/// {"Vector": {"TypeParameter": 0}} that the relayer cannot decode. +public fun ccip_receive( + _items: vector, + _expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, +) { + osh::consume_any2sui_message(ref, message, BrokenReceiverProof {}); +} diff --git a/contracts/ccip/ccip_dummy_receiver/sources/ccip_dummy_receiver.move b/contracts/ccip/ccip_dummy_receiver/sources/ccip_dummy_receiver.move index e0ed0322e..3a6b7ce2a 100644 --- a/contracts/ccip/ccip_dummy_receiver/sources/ccip_dummy_receiver.move +++ b/contracts/ccip/ccip_dummy_receiver/sources/ccip_dummy_receiver.move @@ -1,4 +1,8 @@ -// THIS CONTRACT IS ONLY FOR TESTING PURPOSES. IT IS NOT INTENDED FOR PRODUCTION USE. +/// Reference receiver for tests and documentation. Not for production. +/// +/// Implements the singleton tail-argument pattern for V1 destination execution. +/// Tail slots: shared `Clock` at `@0x6` and the single `CCIPReceiverState` +/// from `init`. See `contracts/ccip/docs/receiver-integration-guide.md`. module ccip_dummy_receiver::dummy_receiver; use ccip::client; @@ -16,8 +20,11 @@ use sui::transfer::Receiving; const EMessageIdMismatch: u64 = 0; +/// One-time witness for package publish. Consumed in `init`. public struct DUMMY_RECEIVER has drop {} +/// Admin capability for registration and coin receive helpers. +/// Stored by the deployer; not passed to `ccip_receive`. public struct OwnerCap has key, store { id: UID, receiver_address: address, @@ -32,6 +39,8 @@ public struct ReceivedMessage has copy, drop { dest_token_amounts: vector, } +/// Singleton delivery state. Created once in `init`; `has key` only so this +/// module retains transfer and share control. public struct CCIPReceiverState has key { id: UID, counter: u64, @@ -45,6 +54,7 @@ public struct CCIPReceiverState has key { dest_token_amounts: vector, } +/// Type proof for `receiver_registry` and `consume_any2sui_message`. public struct DummyReceiverProof has drop {} public struct PublisherKey has copy, drop, store {} @@ -58,6 +68,7 @@ public fun type_and_version(): String { string::utf8(b"DummyReceiver 1.6.0") } +/// Sole constructor for `CCIPReceiverState`. fun init(otw: DUMMY_RECEIVER, ctx: &mut TxContext) { let state = CCIPReceiverState { id: object::new(ctx), @@ -110,9 +121,7 @@ public fun get_token_amount_amount(token_amount: &TokenAmount): u256 { token_amount.amount } -// if coin objects are sent to an object (in this case, the receiver state object), this function must be implemented -// in order to "receive" those coin objects. otherwise, the coin objects will be locked in the object until the package -// is upgraded with such a function to receive coin objects from this object. +/// Accepts coins sent to the shared state object via transfer-to-object. public fun receive_and_send_coin( state: &mut CCIPReceiverState, _: &OwnerCap, @@ -149,13 +158,17 @@ public fun receive_coin_no_owner_cap( transfer::public_receive>(&mut state.id, coin_receiving) } +/// CCIP entrypoint. Both tail types are singletons so manual executors cannot +/// substitute an alternate object of the same type. public fun ccip_receive( expected_message_id: vector, ref: &CCIPObjectRef, message: client::Any2SuiMessage, - _: &Clock, // this is a precompile, but remain the same across all messages - state: &mut CCIPReceiverState, // this is a singleton, but remain the same across all messages + clock: &Clock, + state: &mut CCIPReceiverState, ) { + clock; // read-only tail slot example; use `clock.timestamp_ms()` when needed + let ( message_id, source_chain_selector, diff --git a/contracts/ccip/docs/receiver-integration-guide.md b/contracts/ccip/docs/receiver-integration-guide.md new file mode 100644 index 000000000..3d2b688de --- /dev/null +++ b/contracts/ccip/docs/receiver-integration-guide.md @@ -0,0 +1,366 @@ +# CCIP Sui Receiver Integration Guide + +This guide explains how to build a CCIP message receiver on Sui that remains safe under permissionless manual execution. It applies to the V1 destination execution format shipped today. + +Reference implementation: [`ccip_dummy_receiver`](../ccip_dummy_receiver/sources/ccip_dummy_receiver.move). + +CCIP receivers are third-party applications. You are responsible for your object model, security properties, and whether your design is safe under manual execution. + +### Summary + +Destination V1 intentionally omits `receiver_object_ids` from the OCR execution report and merkle leaf. Source senders still encode those IDs in `extra_args`, and the relayer uses them when building the execution PTB, but the committed report on Sui does not bind them. That is deliberate: if a sender specified wrong object IDs and execution fails or the message sits unexecuted, an honest party can retry manual execution or a corrected relayer run with the right IDs instead of leaving the message permanently stuck. The tradeoff is that tail object choice is not authenticated on destination — your receiver design must prevent an executor from substituting a different valid object of the same type. + +--- + +## Threat model + +On the **source** chain, senders pass Sui-specific extra args via `client::encode_sui_extra_args_v1`. The source `message_id` commits to those bytes, including `receiver_object_ids`. + +On the **destination**, the V1 offramp OCR execution report and leaf hash authenticate: + +- receiver package address +- message body fields in `Any2SuiMessage` +- token destination via `token_receiver` + +They do **not** authenticate which object references are passed as PTB tail arguments to `ccip_receive`. + +After `permissionless_execution_threshold_seconds`, any address may call `offramp::manually_init_execute` and assemble the execution PTB. If your receiver accepts multiple valid shared objects of the same tail type, an executor can deliver a committed message to the wrong object while the protocol marks execution successful. The message cannot be replayed against the intended object. + +Token delivery is unaffected: `token_receiver` is in the destination leaf hash. + +--- + +## `receiver_object_ids` + +### What it is + +`receiver_object_ids` is a vector of 32-byte Sui object IDs, one per object-reference parameter in your `ccip_receive` function after the three protocol-fixed arguments: + +| Index | Parameter | Set by | +|------:|-----------|--------| +| 0 | `expected_message_id: vector` | Relayer | +| 1 | `ref: &CCIPObjectRef` | Relayer | +| 2 | `message: Any2SuiMessage` | Relayer via `extract_any2sui_message` | +| 3+ | Your `&T` / `&mut T` tail args | Relayer, one ID per slot in ABI order | + +Senders encode IDs on the source chain: + +```move +let extra_args = client::encode_sui_extra_args_v1( + gas_limit, + allow_out_of_order_execution, + token_receiver_bytes, // 32 bytes + vector[ + bcs::to_bytes(&clock_id), + bcs::to_bytes(&state_id), + ], +); +``` + +Count invariant: + +```text +len(receiver_object_ids) == number of object-reference parameters after message +``` + +The relayer reads `receiverObjectIds` from source extra args metadata when building the execution PTB and appends matching object inputs to the `ccip_receive` call. + +### Why IDs are not in the OCR execution report + +Destination V1 deliberately does **not** include `receiver_object_ids` in the merkle leaf or deserialized execution report. That is an intentional tradeoff: + +| Benefit | Cost | +|---------|------| +| Wrong or stale IDs can be corrected at manual execution time | Tail object choice is not cryptographically bound on destination | +| Receiver upgrades or singleton rotation do not permanently stick messages | Receiver design must prevent object substitution | + +If a sender supplies a bad object ID that does not exist or has the wrong type, the PTB fails before submission or reverts. A manual executor or updated relayer run can retry with the correct singleton ID without the message being permanently stuck. + +Binding IDs into the OCR report would make typos and operational changes permanent. The middle-path mitigation is **receiver design**, not protocol-level ID binding. + +--- + +## Execution PTB + +CCIP message execution is always a single atomic PTB. Typical command order: + +1. `offramp::init_execute` or `offramp::manually_init_execute` +2. Token pool `release_or_mint` when the report includes tokens +3. `offramp_state_helper::extract_any2sui_message` +4. `your_package::ccip_receive` with tail object inputs +5. `offramp::finish_execute` + +All steps succeed or the entire transaction reverts. Shared objects used in the PTB must be passed with the correct mutability: read-only refs for `&T`, mutable refs for `&mut T`. + +--- + +## Safe receiver shapes + +A receiver is safe from manual-execution object substitution when an attacker-controlled PTB cannot cause a committed message to mutate unintended state. + +### Tier 1 — structurally safe + +These patterns need no per-call object ID checks. + +#### Stateless — no tail objects + +```move +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, +) { + let (msg_id, _, _, data, _, _, _) = + osh::consume_any2sui_message(ref, message, MyProof {}); + assert!(msg_id == expected_message_id, EMessageIdMismatch); + // handle data; no mutable tail state +} +``` + +`receiver_object_ids = []` + +#### Singleton state — one shared object per tail type + +```move +public struct ReceiverState has key { + id: UID, + accounts: Table, +} + +fun init(otw: MY_PKG, ctx: &mut TxContext) { + let state = ReceiverState { id: object::new(ctx), accounts: table::new(ctx) }; + transfer::share_object(state); + // no other function creates ReceiverState +} + +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, + state: &mut ReceiverState, +) { + let (msg_id, _, sender, data, _, _, _) = + osh::consume_any2sui_message(ref, message, MyProof {}); + assert!(msg_id == expected_message_id, EMessageIdMismatch); + let account = state.accounts.borrow_mut(parse_recipient(&data)); + apply(account, &data); +} +``` + +`receiver_object_ids = [state_id]` + +Requirements: + +- Exactly one valid instance of each tail type for the deployment lifetime +- No public or entry function that creates a second instance +- Prefer `has key` without `store` on delivery state so only your module controls share and transfer + +#### Multi-singleton — several tail types, each a singleton + +The dummy receiver uses `&Clock` at `@0x6` plus one `CCIPReceiverState`: + +```move +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, + clock: &Clock, + state: &mut CCIPReceiverState, +) { /* ... */ } +``` + +`receiver_object_ids = [clock_object_id, state_object_id]` + +System singletons like `Clock` satisfy the invariant by design. + +#### Two-step pull — singleton inbox, authorized claim + +`ccip_receive` only writes to a shared inbox. State owners pull later with an explicit capability: + +```move +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, + inbox: &mut Inbox, +) { + let (msg_id, _, _, data, _, _, _) = + osh::consume_any2sui_message(ref, message, MyProof {}); + assert!(msg_id == expected_message_id, EMessageIdMismatch); + inbox.pending.add(msg_id, Pending { recipient: decode_recipient(&data), data }); +} + +public fun claim( + inbox: &mut Inbox, + account: &mut Account, + cap: &AccountOwnerCap, + expected_message_id: vector, +) { + assert!(cap.account_id == object::id(account), EWrongCap); + let pending = inbox.pending.remove(expected_message_id); + assert!(pending.recipient == cap.owner, EWrongRecipient); + apply(account, pending.data); +} +``` + +Binding moves to `claim`, where the owner signs the PTB. + +--- + +## Non-singleton external objects + +When logical recipients are separate shared or owned objects, do **not** pass them as mutable tail arguments unless you add explicit binding checks. Prefer one of these patterns. + +### Hub routing — recommended default + +One singleton hub; route using fields from the authenticated message: + +```move +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, + hub: &mut Hub, +) { + let (msg_id, _, sender, data, _, _, _) = + osh::consume_any2sui_message(ref, message, MyProof {}); + assert!(msg_id == expected_message_id, EMessageIdMismatch); + + let target = decode_target_address(&data); + assert!(is_allowed_recipient(sender, target), EUnauthorizedTarget); + + let entry = hub.accounts.borrow_mut(target); + apply(entry, &data); +} +``` + +The executor can only pass the one `Hub`. Routing uses `data` and `sender`, which are authenticated in `Any2SuiMessage`. + +Define a versioned payload schema in `data` so senders and receivers agree on encoding. + +### ID binding from message `data` — advanced + +If a non-singleton object must appear as a tail arg, bind it to an ID encoded in authenticated `data` **before** any mutation: + +```move +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, + vault: &mut Vault, +) { + let (msg_id, _, _, data, _, _, _) = + osh::consume_any2sui_message(ref, message, MyProof {}); + assert!(msg_id == expected_message_id, EMessageIdMismatch); + + let expected_vault_id = decode_vault_id(&data); + assert!(object::id_address(vault) == expected_vault_id, EWrongVault); + + apply(vault, &data); +} +``` + +This works but places security on per-function discipline. Verify the assert on every mutation path in your own code review and tests. Prefer hub routing or inbox pull when possible. + +**Do not** read expected IDs from offchain relayer metadata inside the receiver. `Any2SuiMessage` does not carry source `extra_args`; only message fields are available onchain. + +### Sender-pinned routing + +When each source sender may only affect their own row: + +```move +let entry = hub.by_sender.borrow_mut(sender_as_address); +``` + +Suitable for per-sender accounts without embedding a target ID in `data`. Not suitable when one sender must deliver to arbitrary recipients unless `data` carries an authorized target and you validate it. + +--- + +## Bad patterns + +### Multiple shared vaults as tail args without binding + +```move +// UNSAFE: attacker passes vault_B while message was intended for vault_A +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, + vault: &mut Vault, +) { + let (_, _, _, data, _, _, _) = + osh::consume_any2sui_message(ref, message, MyProof {}); + vault.balance = vault.balance + decode_amount(&data); +} +``` + +Any shared `Vault` of this type is a valid PTB input after the manual execution threshold. + +### Public factory for tail state + +```move +// UNSAFE: enables a second CCIPReceiverState for substitution +public fun create_receiver_state(ctx: &mut TxContext): CCIPReceiverState { + CCIPReceiverState { id: object::new(ctx), /* ... */ } +} +``` + +Singleton receivers must create delivery state only in `init`, not via public factories. + +### Trusting the relayer without onchain checks + +```move +// UNSAFE: assumes the PTB caller always passes the intended object +public fun ccip_receive( + expected_message_id: vector, + ref: &CCIPObjectRef, + message: client::Any2SuiMessage, + state: &mut UserState, +) { + let (msg_id, _, _, data, _, _, _) = + osh::consume_any2sui_message(ref, message, MyProof {}); + assert!(msg_id == expected_message_id, EMessageIdMismatch); + state.apply(&data); +} +``` + +Manual executors are not your relayer. Design for attacker-controlled PTBs. + +### Per-user owned state as the only tail arg without inbox pull + +Owned objects are harder to substitute in permissionless PTBs because the owner must sign. They also complicate relayer-driven execution. Use inbox + `claim` instead of relying on ownership alone. + +--- + +## Security checklist + +Before deploying your receiver, verify: + +- [ ] Every type in `ccip_receive` tail args is listed +- [ ] Each tail type is a singleton, or binding is enforced before mutation, or only an inbox is mutated +- [ ] No public or entry path creates a second valid tail object +- [ ] `consume_any2sui_message` and `expected_message_id` check run before state changes +- [ ] Substitution or wrong-target tests exist in the receiver test suite +- [ ] `receiver_object_ids` length matches the tail object parameter count + +--- + +## Quick reference + +| Pattern | Tail args | `receiver_object_ids` | Manual exec safe? | +|---------|-----------|----------------------|-------------------| +| Stateless | none | `[]` | Yes | +| Singleton state | `&mut State` | `[state_id]` | Yes | +| Multi-singleton | `&Clock`, `&mut State` | `[clock_id, state_id]` | Yes | +| Hub routing | `&mut Hub` | `[hub_id]` | Yes | +| Inbox + claim | `&mut Inbox` | `[inbox_id]` | Yes | +| Multi vault tail, no assert | `&mut Vault` | `[vault_id]` | **No** | +| Multi vault + ID in `data` | `&mut Vault` | `[vault_id]` | Yes if assert enforced | + +--- + +## Related reading + +- [`ccip_dummy_receiver`](../ccip_dummy_receiver/sources/ccip_dummy_receiver.move) — canonical singleton example +- [`ccip_offramp` README](../ccip_offramp/README.md) — execution PTB flow +- [`client::encode_sui_extra_args_v1`](../ccip/sources/client.move) — source extra args encoding diff --git a/contracts/ccip/mock_ccip_v2/fee_quoter.move b/contracts/ccip/mock_ccip_v2/fee_quoter.move index 1d2e550f0..e5ae88f8b 100644 --- a/contracts/ccip/mock_ccip_v2/fee_quoter.move +++ b/contracts/ccip/mock_ccip_v2/fee_quoter.move @@ -71,6 +71,25 @@ const SVM_TOKEN_TRANSFER_DATA_OVERHEAD: u64 = + 32 // per-chain token billing config, not always included in the token lookup table + 32; // OffRamp pool signer PDA, not included in the token lookup table; +/// The maximum number of receiver object IDs that can be passed in SuiExtraArgsV1. +const SUI_EXTRA_ARGS_MAX_RECEIVER_OBJECT_IDS: u64 = 64; + +/// Number of overhead accounts needed for message execution on SUI. +/// This is the message.receiver. +const SUI_MESSAGING_ACCOUNTS_OVERHEAD: u64 = 1; + +/// The size of each SUI account address in bytes. +const SUI_ACCOUNT_BYTE_SIZE: u64 = 32; + +/// The expected static payload size of a token transfer when BCS encoded on SUI. +/// TokenPool extra_data contents are dynamic and billed via dest_bytes_overhead. +const SUI_TOKEN_TRANSFER_DATA_OVERHEAD: u64 = + (1 + 32) // source_pool_address: ULEB128 length + 32 bytes + + 32 // dest_token_address + + 4 // dest_gas_amount + + 1 // extra_data: ULEB128 length for empty vector + + 32; // amount + const MAX_U64: u256 = 18446744073709551615; const MAX_U160: u256 = 1461501637330902918203684832716283019655932542975; const VAL_1E5: u256 = 100_000; @@ -241,6 +260,8 @@ const EInvalidSvmAccountLength: u64 = 35; const ETokenAmountMismatch: u64 = 36; const EInvalidOwnerCap: u64 = 37; const EInvalidFunction: u64 = 38; +const ETooManySuiExtraArgsReceiverObjectIds: u64 = 39; +const EInvalidSuiReceiverObjectIdLength: u64 = 40; public fun type_and_version(): String { string::utf8(b"FeeQuoter 1.6.2") @@ -709,7 +730,18 @@ public fun get_validated_fee( ) { resolve_generic_gas_limit(dest_chain_config, extra_args) } else if (chain_family_selector == CHAIN_FAMILY_SELECTOR_SUI) { - resolve_sui_gas_limit(dest_chain_config, extra_args) + let (gas, overhead) = resolve_sui_gas_limit( + dest_chain_config, + state, + dest_chain_selector, + extra_args, + receiver, + data_len, + tokens_len, + local_token_addresses, + ); + svm_payload_overhead = overhead; + gas } else if (chain_family_selector == CHAIN_FAMILY_SELECTOR_SVM) { let (gas, overhead) = resolve_svm_gas_limit( dest_chain_config, @@ -867,19 +899,91 @@ fun resolve_generic_gas_limit(dest_chain_config: &DestChainConfig, extra_args: v gas_limit } -fun resolve_sui_gas_limit(dest_chain_config: &DestChainConfig, extra_args: vector): u256 { +/// Returns `(gas_limit, sui_payload_overhead_bytes)`. +/// `sui_payload_overhead_bytes` is the SUI-specific payload size counted in maxDataBytes validation +/// but not already included in `data_len` or per-token `dest_bytes_overhead` billing. +fun resolve_sui_gas_limit( + dest_chain_config: &DestChainConfig, + state: &FeeQuoterState, + dest_chain_selector: u64, + extra_args: vector, + receiver: vector, + data_len: u64, + tokens_len: u64, + local_token_addresses: vector
, +): (u256, u64) { let ( gas_limit, allow_out_of_order_execution, - _token_receiver, - _receiver_object_ids, + token_receiver, + receiver_object_ids, ) = decode_sui_extra_args(extra_args); assert!(gas_limit <= (dest_chain_config.max_per_msg_gas_limit as u64), EMessageGasLimitTooHigh); assert!( !dest_chain_config.enforce_out_of_order || allow_out_of_order_execution, EExtraArgOutOfOrderExecutionMustBeTrue, ); - gas_limit as u256 + + let receiver_object_ids_length = receiver_object_ids.length(); + assert!( + receiver_object_ids_length <= SUI_EXTRA_ARGS_MAX_RECEIVER_OBJECT_IDS, + ETooManySuiExtraArgsReceiverObjectIds, + ); + + let mut i = 0; + while (i < receiver_object_ids_length) { + assert!(receiver_object_ids[i].length() == 32, EInvalidSuiReceiverObjectIdLength); + i = i + 1; + }; + + let mut sui_payload_overhead = tokens_len * SUI_TOKEN_TRANSFER_DATA_OVERHEAD; + + assert!(receiver.length() == 32, EInvalid32BytesAddress); + let receiver_uint = eth_abi::decode_u256_value(receiver); + if (receiver_uint == 0) { + assert!(receiver_object_ids_length == 0, ETooManySuiExtraArgsReceiverObjectIds); + } else { + sui_payload_overhead = + sui_payload_overhead + ( + receiver_object_ids_length + SUI_MESSAGING_ACCOUNTS_OVERHEAD + ) * SUI_ACCOUNT_BYTE_SIZE; + }; + + if (tokens_len > 0) { + assert!( + token_receiver.length() == 32 + && eth_abi::decode_u256_value(token_receiver) != 0, + EInvalidTokenReceiver, + ); + }; + + let mut sui_expanded_data_length = data_len + sui_payload_overhead; + + let mut i = 0; + while (i < tokens_len) { + let local_token_address = local_token_addresses[i]; + let destBytesOverhead = get_token_transfer_fee_config_internal( + state, + dest_chain_selector, + local_token_address, + ).dest_bytes_overhead; + + if (destBytesOverhead > 0) { + sui_expanded_data_length = sui_expanded_data_length + (destBytesOverhead as u64); + } else { + sui_expanded_data_length = + sui_expanded_data_length + (CCIP_LOCK_OR_BURN_V1_RET_BYTES as u64); + }; + + i = i + 1; + }; + + assert!( + sui_expanded_data_length <= (dest_chain_config.max_data_bytes as u64), + EMessageTooLarge, + ); + + (gas_limit as u256, sui_payload_overhead) } fun check_svm_writable_bitmap(account_is_writable_bitmap: u64, accounts_length: u64) { diff --git a/contracts/contracts.go b/contracts/contracts.go index 3c10d4efb..59d6e40b9 100644 --- a/contracts/contracts.go +++ b/contracts/contracts.go @@ -13,6 +13,7 @@ type Package string const ( // CCIP CCIP = Package("ccip") + CCIPBrokenReceiver = Package("ccip_broken_receiver") CCIPDummyReceiver = Package("ccip_dummy_receiver") CCIPOfframp = Package("ccip_offramp") CCIPOnramp = Package("ccip_onramp") @@ -41,6 +42,7 @@ const ( var Contracts map[Package]string = map[Package]string{ // CCIP CCIP: filepath.Join("ccip", "ccip"), + CCIPBrokenReceiver: filepath.Join("ccip", "ccip_broken_receiver"), CCIPDummyReceiver: filepath.Join("ccip", "ccip_dummy_receiver"), CCIPBnM: filepath.Join("ccip", "ccip_burn_mint_token"), CCIPOfframp: filepath.Join("ccip", "ccip_offramp"), diff --git a/contracts/mcms/mcms/sources/mcms_deployer.move b/contracts/mcms/mcms/sources/mcms_deployer.move index 99c8771cc..afcdfb1be 100644 --- a/contracts/mcms/mcms/sources/mcms_deployer.move +++ b/contracts/mcms/mcms/sources/mcms_deployer.move @@ -168,3 +168,27 @@ public fun has_upgrade_cap(state: &DeployerState, package_address: address): boo public fun test_init(ctx: &mut TxContext) { init(MCMS_DEPLOYER {}, ctx); } + +#[test_only] +/// Register an upgrade cap without requiring MCMS registry check. +/// Needed because in tests @self resolves to 0x0, which Sui >= 1.73 +/// rejects in `authorize_upgrade` (uses 0x0 as a sentinel for in-progress upgrades). +public fun test_register_upgrade_cap( + state: &mut DeployerState, + upgrade_cap: UpgradeCap, + ctx: &mut TxContext, +) { + let package_address = upgrade_cap.package().to_address(); + let version = upgrade_cap.version(); + let policy = upgrade_cap.policy(); + + state.cap_to_package.add(object::id(&upgrade_cap), package_address); + state.upgrade_caps.add(package_address, upgrade_cap); + + event::emit(UpgradeCapRegistered { + prev_owner: ctx.sender(), + package_address, + version, + policy, + }); +} diff --git a/contracts/scripts/test.sh b/contracts/scripts/test.sh index e07f5c416..768fedee7 100755 --- a/contracts/scripts/test.sh +++ b/contracts/scripts/test.sh @@ -19,10 +19,11 @@ PACKAGES=( ) # Sui ≥1.66 uses on-chain-like gas metering in unit tests with a ~1M default budget. -# Some tests (e.g. MCMS multi-step flows) exceed that and fail as "Test timed out". -SUI_TEST_GAS_LIMIT="${SUI_TEST_GAS_LIMIT:-500000000}" +# Some tests (e.g. MCMS multi-step flows, BCS deserialization of large extra_args) exceed that. +SUI_TEST_GAS_LIMIT="${SUI_TEST_GAS_LIMIT:-2000000000}" +SUI_BUILD_ENV="${SUI_BUILD_ENV:-testnet}" # run tests for pkg in "${PACKAGES[@]}"; do - sui move test --path "$pkg" --gas-limit "$SUI_TEST_GAS_LIMIT" + sui move test --path "$pkg" --build-env "$SUI_BUILD_ENV" --gas-limit "$SUI_TEST_GAS_LIMIT" done diff --git a/deployment/go.mod b/deployment/go.mod index e2d7d57f6..57080660f 100644 --- a/deployment/go.mod +++ b/deployment/go.mod @@ -8,16 +8,16 @@ replace github.com/fbsobreira/gotron-sdk => github.com/smartcontractkit/chainlin replace github.com/smartcontractkit/chainlink-sui => ../ require ( - github.com/Masterminds/semver/v3 v3.4.0 + github.com/Masterminds/semver/v3 v3.5.0 github.com/block-vision/sui-go-sdk v1.2.1 github.com/ethereum/go-ethereum v1.17.3 github.com/google/go-cmp v0.7.0 github.com/smartcontractkit/chain-selectors v1.0.100 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16 github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 - github.com/smartcontractkit/chainlink-deployments-framework v0.98.0 - github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67 - github.com/smartcontractkit/mcms v0.42.0 + github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a + github.com/smartcontractkit/chainlink-sui v0.0.0 + github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d github.com/stretchr/testify v1.11.1 golang.org/x/sync v0.20.0 ) @@ -81,7 +81,7 @@ require ( github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.8.0 // indirect github.com/hashicorp/yamux v0.1.2 // indirect @@ -113,7 +113,6 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/run v1.2.0 // indirect - github.com/onsi/gomega v1.36.2 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.3.0 // indirect @@ -130,8 +129,8 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc // indirect - github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d // indirect - github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // indirect @@ -164,9 +163,9 @@ require ( go.mongodb.org/mongo-driver v1.17.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect @@ -175,12 +174,12 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.19.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/otel/log v0.20.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/goleak v1.3.0 // indirect @@ -190,16 +189,16 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/crypto v0.51.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect - golang.org/x/net v0.54.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sys v0.44.0 // indirect + golang.org/x/sys v0.45.0 // indirect golang.org/x/term v0.43.0 // indirect golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.45.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/grpc v1.81.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/deployment/go.sum b/deployment/go.sum index 67a79eb03..65b0c86dd 100644 --- a/deployment/go.sum +++ b/deployment/go.sum @@ -13,8 +13,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= @@ -28,20 +28,26 @@ github.com/apache/arrow-go/v18 v18.3.1 h1:oYZT8FqONiK74JhlH3WKVv+2NKYoyZ7C2ioD4D github.com/apache/arrow-go/v18 v18.3.1/go.mod h1:12QBya5JZT6PnBihi5NJTzbACrDGXYkrgjujz3MRQXU= github.com/aptos-labs/aptos-go-sdk v1.13.0 h1:epv7K/tIbAEO2RfogwGacICBig8rrigJj24fDsy6KTg= github.com/aptos-labs/aptos-go-sdk v1.13.0/go.mod h1:FTgKp0RLfEefllCdkCj0jPU14xWk11yA7SFVfCDLUj8= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.7.0 h1:yjDs35SlGvKwRNSykujfjdMxMhMQQM0TnIjJaHB+Zio= github.com/avast/retry-go/v4 v4.7.0/go.mod h1:ZMPDa3sY2bKgpLtap9JRUgk2yTAba7cgiFhqxY2Sg6Q= -github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= -github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= +github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= +github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= +github.com/aws/aws-sdk-go-v2 v1.41.11 h1:9PRf7jyTMEUM6fuNRAJa2mO/skJfrF50rENJwf2LXqw= +github.com/aws/aws-sdk-go-v2 v1.41.11/go.mod h1:iiUX27gOXRuYaoeUVXhUpPwjJHzISfPAjjcuhUbLSVs= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 h1:8sPbKi1/KRHwl5oR3qN9mUXestCeHuaRutxylnr/eVY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27/go.mod h1:QV9IVIopJ1dpQUno0f9VYDUwOEjj8u0iEJ4JiZVre3Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 h1:9d8AoASQY9UwrOSmiJ7uSM0MGUPFhnenwSvpaFfat2c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27/go.mod h1:x0rldpsnUQaQIs4Rh+Vwm9Z/0vI6BxadGtsgJfZFb8s= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= @@ -58,8 +64,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= -github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= -github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.0 h1:ZoFioDKJxkSIW2otF9T0aPtNlUwhdVCcuZh/rzH9Hus= +github.com/aws/smithy-go v1.27.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -185,8 +191,6 @@ github.com/digital-asset/dazl-client/v8 v8.9.0 h1:F2qTUWtHAjhGyRGV+xTim+VAFwM99F github.com/digital-asset/dazl-client/v8 v8.9.0/go.mod h1:q1KevCJ8FpH8je2MnnjN8/QUfhstB4fKpyKyqDtqFh0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -342,8 +346,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpS github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -395,6 +399,8 @@ github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5Xum github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -467,8 +473,12 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4= +github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= +github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y= github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -484,8 +494,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= -github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -572,8 +582,8 @@ github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 h1:S4OC0+OBK github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2/go.mod h1:8zLRYR5npGjaOXgPSKat5+oOh+UHd8OdbS18iqX9F6Y= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= -github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= +github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= @@ -581,24 +591,26 @@ github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97M github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/smartcontractkit/ccip-owner-contracts v0.1.0 h1:GiBDtlx7539o7AKlDV+9LsA7vTMPv+0n7ClhSFnZFAk= +github.com/smartcontractkit/ccip-owner-contracts v0.1.0/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= github.com/smartcontractkit/chain-selectors v1.0.100 h1:wpiSpmI/eFjY+wx/nPr5VuNF4hki0prIBMKEaQWn3g4= github.com/smartcontractkit/chain-selectors v1.0.100/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc h1:Um9FBcf0JNSFuGbxgccDG1vM3cNrMGy0SdJ7r6VbX0o= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc/go.mod h1:zfE2R7887kiwXkGTHKPe5NBgwhFwIC3pnA2uAxrbvig= github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1 h1:p0nFrTYrOQzDhWYm6suaM5CoWiXV5NV7llHnp6/Kn/8= github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1/go.mod h1:1XxxpkgCmG/z6y30yRuVrcxre6zixIVX3xzi706Db/8= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d h1:xdFpzbApEMz4Rojg2Y2OjFlrh0wu7eB10V2tSZGW5y8= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d/go.mod h1:bgmqE7x9xwmIVr8PqLbC0M5iPm4AV2DBl596lO6S5Sw= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 h1:Z4t2ZY+ZyGWxtcXvPr11y4o3CGqhg3frJB5jXkCSvWA= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 h1:jkChf04hhdiMBApbb+lLDxHMY62Md6UeM7v++GSw3K8= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139/go.mod h1:wuhagkM/lU0GbV2YcrROOH0GlsfXJYwm6qmpa4CK70w= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 h1:tw3K4UkH5XfW5SoyYkvAlbzrccoGSLdz/XkxD6nyGC8= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139/go.mod h1:1WcontO9PeuKdUf5HXfs3nuICtzUvFNnyCmrHkTCF9Y= github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16 h1:kG7DnjoCDJUt2htCqVxTA4IvQyR+a6mOmqlG1v7KMRE= github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16/go.mod h1:kMRGxNzyB5O6sqQlJEgBG/g49mzRvlcqbqMrzlhL+JY= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 h1:5z3LQ27MJmhiaeqp9S2TzbF5Wm4GGvUKAYOtE9AauR8= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= -github.com/smartcontractkit/chainlink-deployments-framework v0.98.0 h1:Ov/KOEtubOHXX8oa9UtARhHmkQNCOIjWNt+Zi0AuzHM= -github.com/smartcontractkit/chainlink-deployments-framework v0.98.0/go.mod h1:24dwRW1PYolrlxSth///ddG3auGqR+50xaJiXfUHhkg= +github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a h1:z23/Q2MhxA2WJEOzX9s9H+TB8nU+U3qotwGPKLAOoPY= +github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a/go.mod h1:OdH025Wn1W4UXny4TeczG3K+b0acF0+OjInmzQt+po0= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 h1:5bxDnwI0wuPoC0H5H3H2n9CnQPb5iakR6UmAY4j8KUg= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= @@ -607,10 +619,12 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202510021 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= -github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= -github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.19 h1:inTH0/PrEaVv4iLdGsdcrP/rX7KMrq/Roosr5nIA8io= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.19/go.mod h1:BALK9cj8sk12e15UF6uDhifHgIApa+6N11TcQfInEro= +github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= +github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 h1:8M+2pA0qx9rXaxmpKouUHj983vQCGzztHkG0XjE5Eew= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5/go.mod h1:kHYJnZUqiPF7/xN5273prV+srrLJkS77GbBXHLKQpx0= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 h1:eieKLvYuzwBPh/FdbUS1gnIanI86zgWby1L10o90g4o= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745/go.mod h1:8vXLeG//BxDF86GWRytzGIy6jc70htD1r/KtPfjrsK0= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 h1:XRNxgcNqagXu6e4smJuS1crRK5cUAcCVd7u+iLduHDM= @@ -623,8 +637,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12i github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e h1:poXTj5cFVM6XfC4HICIDYkDVc/A6OYB0eeID0wU2JQE= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e/go.mod h1:PLdNK6GlqfxIWXzziPkU7dCAVlVFeYkyyW7AQY0R+4Q= -github.com/smartcontractkit/mcms v0.42.0 h1:zzs+auX6BL6sRIVpgVbLUviwrvWi8Fxo5dOP+9Wx/gk= -github.com/smartcontractkit/mcms v0.42.0/go.mod h1:39OxzRApGN7HG+JGbjxdCxyo5lvV0H0REUPyh3CzDGU= +github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d h1:lMJ0pY2mUtDqUl+CraggP9k9396VgihfX1RdfyUBLy8= +github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d/go.mod h1:R+h5CZoiZoxGToN4hvQwbb1dsKgoZ+gUOgWTNDxGsDQ= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= @@ -659,8 +673,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/testcontainers/testcontainers-go v0.41.0 h1:mfpsD0D36YgkxGj2LrIyxuwQ9i2wCKAD+ESsYM1wais= -github.com/testcontainers/testcontainers-go v0.41.0/go.mod h1:pdFrEIfaPl24zmBjerWTTYaY0M6UHsqA1YSvsoU40MI= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0 h1:AOtFXssrDlLm84A2sTTR/AhvJiYbrIuCO59d+Ro9Tb0= github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0/go.mod h1:k2a09UKhgSp6vNpliIY0QSgm4Hi7GXVTzWvWgUemu/8= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -709,12 +723,12 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= @@ -731,23 +745,25 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmc go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= -go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= -go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= +go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs= +go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= -go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY= +go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -808,8 +824,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -849,8 +865,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -890,17 +906,17 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= -google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/deployment/ops/mcms/op_proposal_generate_test.go b/deployment/ops/mcms/op_proposal_generate_test.go index f9eaa0801..6a3cc4894 100644 --- a/deployment/ops/mcms/op_proposal_generate_test.go +++ b/deployment/ops/mcms/op_proposal_generate_test.go @@ -1,11 +1,12 @@ package mcmsops import ( - "encoding/json" + "context" + "math/big" "testing" "time" - "github.com/block-vision/sui-go-sdk/models" + suirpcv2 "github.com/block-vision/sui-go-sdk/pb/sui/rpc/v2" cselectors "github.com/smartcontractkit/chain-selectors" mocksui "github.com/smartcontractkit/mcms/sdk/sui/mocks/sui" "github.com/smartcontractkit/mcms/types" @@ -22,6 +23,47 @@ import ( "github.com/smartcontractkit/chainlink-sui/deployment/utils" ) +func mockSharedSuiObject(objectID string) *suirpcv2.Object { + digest := "9WzSXdwbky8tNbH7juvyaui4QzMUYEjdCEKMrMgLhXHT" + version := uint64(1) + sharedVersion := uint64(1) + ownerKind := suirpcv2.Owner_SHARED + + return &suirpcv2.Object{ + ObjectId: &objectID, + Version: &version, + Digest: &digest, + Owner: &suirpcv2.Owner{ + Kind: &ownerKind, + Version: &sharedVersion, + }, + } +} + +func setupProposalGenerateMockClient(t *testing.T) *mocksui.SuiPTBClient { + t.Helper() + + mockClient := mocksui.NewSuiPTBClient(t) + mockClient.On("ReadObjectId", mock.Anything, mock.Anything). + Return( + func(_ context.Context, objectID string) *suirpcv2.Object { + return mockSharedSuiObject(objectID) + }, + func(_ context.Context, _ string) error { + return nil + }, + ). + Maybe() + mockClient.On("GetReferenceGasPrice", mock.Anything). + Return(big.NewInt(1000), nil). + Maybe() + mockClient.On("SimulatePTB", mock.Anything, mock.Anything). + Return([]any{uint64(1)}, nil). + Maybe() + + return mockClient +} + func newTestBundle(t *testing.T, registry *cld_ops.OperationRegistry) cld_ops.Bundle { t.Helper() reporter := cld_ops.NewMemoryReporter() @@ -44,29 +86,7 @@ func TestMCMSDynamicProposalGenerateSeq(t *testing.T) { ccipops.AcceptOwnershipStateObjectOp.AsUntyped(), ) - mockClient := mocksui.NewISuiAPI(t) - // This response doesn't matter much - mockClient.EXPECT().SuiGetObject(mock.Anything, mock.Anything). - Return(models.SuiObjectResponse{ - Data: &models.SuiObjectData{ - ObjectId: "0xf2facb344885659b11e707838ee131b407654f75f6589984af462c13de41ef84", - Version: "3", - Digest: "4TRR2ZC9r7UUDUeke2DUhHdRQkZWYjkygHrRSNVM4YmX", - Owner: nil, - }, - Error: nil, - }, nil) - // This is the response from getOpCount - mockClient.EXPECT().SuiDevInspectTransactionBlock(mock.Anything, mock.Anything). - Return(models.SuiTransactionBlockResponse{ - Effects: models.SuiEffects{ - Status: models.ExecutionStatus{ - Status: "success", - Error: "", - }, - }, - Results: json.RawMessage(`[{"returnValues":[[[1,0,0,0,0,0,0,0],"u64"]]}]`), // Returns 1 - }, nil) + mockClient := setupProposalGenerateMockClient(t) // Create mock dependencies deps := sui_ops.OpTxDeps{ Client: mockClient, diff --git a/deployment/utils/mcms.go b/deployment/utils/mcms.go index 44b75291f..8aeccf272 100644 --- a/deployment/utils/mcms.go +++ b/deployment/utils/mcms.go @@ -6,12 +6,12 @@ import ( "fmt" "time" - "github.com/block-vision/sui-go-sdk/sui" "github.com/smartcontractkit/mcms" suisdk "github.com/smartcontractkit/mcms/sdk/sui" "github.com/smartcontractkit/mcms/types" sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" + cslclient "github.com/smartcontractkit/chainlink-sui/relayer/client" "github.com/smartcontractkit/chainlink-sui/relayer/signer" ) @@ -26,7 +26,7 @@ type TimelockConfig struct { type GenerateProposalInput struct { ChainSelector uint64 - Client sui.ISuiAPI + Client cslclient.BindingsClient MCMSPackageID string MCMSStateObjID string AccountObjID string diff --git a/go.md b/go.md index 149851d20..e9f6766cd 100644 --- a/go.md +++ b/go.md @@ -80,8 +80,10 @@ flowchart LR chainlink-ccip --> chainlink-common chainlink-ccip --> chainlink-protos/rmn/v1.6/go click chainlink-ccip href "https://github.com/smartcontractkit/chainlink-ccip" - chainlink-ccip/chains/solana --> chainlink-ccip + chainlink-ccip/chains/evm + click chainlink-ccip/chains/evm href "https://github.com/smartcontractkit/chainlink-ccip" chainlink-ccip/chains/solana --> chainlink-ccip/chains/solana/gobindings + chainlink-ccip/chains/solana --> chainlink-common click chainlink-ccip/chains/solana href "https://github.com/smartcontractkit/chainlink-ccip" chainlink-ccip/chains/solana/gobindings click chainlink-ccip/chains/solana/gobindings href "https://github.com/smartcontractkit/chainlink-ccip" @@ -108,6 +110,7 @@ flowchart LR chainlink-common/pkg/values click chainlink-common/pkg/values href "https://github.com/smartcontractkit/chainlink-common" chainlink-deployments-framework --> ccip-owner-contracts + chainlink-deployments-framework --> chainlink-ccip/chains/evm chainlink-deployments-framework --> chainlink-protos/job-distributor chainlink-deployments-framework --> chainlink-protos/op-catalog chainlink-deployments-framework --> chainlink-testing-framework/seth @@ -174,6 +177,7 @@ flowchart LR subgraph chainlink-ccip-repo[chainlink-ccip] chainlink-ccip + chainlink-ccip/chains/evm chainlink-ccip/chains/solana chainlink-ccip/chains/solana/gobindings chainlink-ccip/deployment diff --git a/integration-tests/deploy/common.go b/integration-tests/deploy/common.go index d4abd94a8..9fac348f2 100644 --- a/integration-tests/deploy/common.go +++ b/integration-tests/deploy/common.go @@ -6,8 +6,6 @@ import ( "context" "fmt" - "github.com/block-vision/sui-go-sdk/models" - "github.com/block-vision/sui-go-sdk/sui" cselectors "github.com/smartcontractkit/chain-selectors" "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-deployments-framework/chain" @@ -16,18 +14,20 @@ import ( cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" "github.com/stretchr/testify/suite" + "github.com/smartcontractkit/chainlink-sui/bindings/bind" "github.com/smartcontractkit/chainlink-sui/bindings/tests/testenv" bindutils "github.com/smartcontractkit/chainlink-sui/bindings/utils" "github.com/smartcontractkit/chainlink-sui/deployment" "github.com/smartcontractkit/chainlink-sui/deployment/changesets" opregistry "github.com/smartcontractkit/chainlink-sui/deployment/ops/registry" + "github.com/smartcontractkit/chainlink-sui/relayer/client" ) type DeployTestSuite struct { suite.Suite lggr logger.Logger signer bindutils.SuiSigner - client sui.ISuiAPI + client client.SuiPTBClient env cldf.Environment // Cached deployment addresses @@ -89,15 +89,9 @@ func (s *DeployTestSuite) findUnusedManagedTokenMinterCapID() (string, error) { if typeAndVersion.Type == deployment.SuiManagedTokenMinterCapID { if _, exists := typeAndVersion.Labels[changesets.CCIPBnMSymbol]; exists { // Check if this object still exists on-chain (not consumed/deleted by faucet) - resp, err := s.client.SuiGetObject(ctx, models.SuiGetObjectRequest{ - ObjectId: addr, - Options: models.SuiObjectDataOptions{ - ShowOwner: true, - ShowType: true, - }, - }) - // If the object exists (no error and no error in response), it's the unused one - if err == nil && resp.Error == nil && resp.Data != nil { + resp, err := bind.ReadObject(ctx, addr, s.client) + // If the object exists (no error and has data), it's the unused one + if err == nil && resp != nil && resp.Data != nil { unusedMintCapID = addr s.T().Logf("Found unused managed token minter cap ID: %s", addr) break diff --git a/integration-tests/go.mod b/integration-tests/go.mod index be1b3c244..500fed214 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -5,14 +5,13 @@ go 1.26.2 require ( github.com/block-vision/sui-go-sdk v1.2.1 github.com/ethereum/go-ethereum v1.17.3 - github.com/holiman/uint256 v1.3.2 github.com/smartcontractkit/chain-selectors v1.0.100 github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16 github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 - github.com/smartcontractkit/chainlink-deployments-framework v0.98.0 - github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67 + github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a + github.com/smartcontractkit/chainlink-sui v0.0.0 github.com/smartcontractkit/chainlink-sui/deployment v0.0.0-20250903045200-c3d973201e55 - github.com/smartcontractkit/mcms v0.42.0 + github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d github.com/stretchr/testify v1.11.1 ) @@ -26,7 +25,7 @@ replace github.com/smartcontractkit/chainlink-sui/deployment => ../deployment require ( filippo.io/edwards25519 v1.1.1 // indirect github.com/BurntSushi/toml v1.5.0 // indirect - github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Masterminds/semver/v3 v3.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/XSAM/otelsql v0.37.0 // indirect @@ -84,12 +83,13 @@ require ( github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.8.0 // indirect github.com/hashicorp/yamux v0.1.2 // indirect github.com/hasura/go-graphql-client v0.15.1 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect + github.com/holiman/uint256 v1.3.2 // indirect github.com/invopop/jsonschema v0.13.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect @@ -132,8 +132,8 @@ require ( github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc // indirect github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1 // indirect - github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d // indirect - github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // indirect @@ -167,9 +167,9 @@ require ( go.mongodb.org/mongo-driver v1.17.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect @@ -178,12 +178,12 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.19.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/otel/log v0.20.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -192,17 +192,17 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/crypto v0.51.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect - golang.org/x/net v0.54.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.44.0 // indirect + golang.org/x/sys v0.45.0 // indirect golang.org/x/term v0.43.0 // indirect golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.45.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/grpc v1.81.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/integration-tests/go.sum b/integration-tests/go.sum index 0d6a35ac8..ca855e93e 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -13,8 +13,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= @@ -28,20 +28,26 @@ github.com/apache/arrow-go/v18 v18.3.1 h1:oYZT8FqONiK74JhlH3WKVv+2NKYoyZ7C2ioD4D github.com/apache/arrow-go/v18 v18.3.1/go.mod h1:12QBya5JZT6PnBihi5NJTzbACrDGXYkrgjujz3MRQXU= github.com/aptos-labs/aptos-go-sdk v1.13.0 h1:epv7K/tIbAEO2RfogwGacICBig8rrigJj24fDsy6KTg= github.com/aptos-labs/aptos-go-sdk v1.13.0/go.mod h1:FTgKp0RLfEefllCdkCj0jPU14xWk11yA7SFVfCDLUj8= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.7.0 h1:yjDs35SlGvKwRNSykujfjdMxMhMQQM0TnIjJaHB+Zio= github.com/avast/retry-go/v4 v4.7.0/go.mod h1:ZMPDa3sY2bKgpLtap9JRUgk2yTAba7cgiFhqxY2Sg6Q= -github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= -github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= +github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= +github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= +github.com/aws/aws-sdk-go-v2 v1.41.11 h1:9PRf7jyTMEUM6fuNRAJa2mO/skJfrF50rENJwf2LXqw= +github.com/aws/aws-sdk-go-v2 v1.41.11/go.mod h1:iiUX27gOXRuYaoeUVXhUpPwjJHzISfPAjjcuhUbLSVs= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 h1:8sPbKi1/KRHwl5oR3qN9mUXestCeHuaRutxylnr/eVY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27/go.mod h1:QV9IVIopJ1dpQUno0f9VYDUwOEjj8u0iEJ4JiZVre3Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 h1:9d8AoASQY9UwrOSmiJ7uSM0MGUPFhnenwSvpaFfat2c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27/go.mod h1:x0rldpsnUQaQIs4Rh+Vwm9Z/0vI6BxadGtsgJfZFb8s= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= @@ -58,8 +64,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= -github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= -github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.0 h1:ZoFioDKJxkSIW2otF9T0aPtNlUwhdVCcuZh/rzH9Hus= +github.com/aws/smithy-go v1.27.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -185,8 +191,6 @@ github.com/digital-asset/dazl-client/v8 v8.9.0 h1:F2qTUWtHAjhGyRGV+xTim+VAFwM99F github.com/digital-asset/dazl-client/v8 v8.9.0/go.mod h1:q1KevCJ8FpH8je2MnnjN8/QUfhstB4fKpyKyqDtqFh0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -342,8 +346,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpS github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -395,6 +399,8 @@ github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5Xum github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -467,8 +473,12 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4= +github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= +github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y= github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -484,8 +494,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= -github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -572,8 +582,8 @@ github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 h1:S4OC0+OBK github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2/go.mod h1:8zLRYR5npGjaOXgPSKat5+oOh+UHd8OdbS18iqX9F6Y= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= -github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= +github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= @@ -581,24 +591,26 @@ github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97M github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/smartcontractkit/ccip-owner-contracts v0.1.0 h1:GiBDtlx7539o7AKlDV+9LsA7vTMPv+0n7ClhSFnZFAk= +github.com/smartcontractkit/ccip-owner-contracts v0.1.0/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= github.com/smartcontractkit/chain-selectors v1.0.100 h1:wpiSpmI/eFjY+wx/nPr5VuNF4hki0prIBMKEaQWn3g4= github.com/smartcontractkit/chain-selectors v1.0.100/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc h1:Um9FBcf0JNSFuGbxgccDG1vM3cNrMGy0SdJ7r6VbX0o= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc/go.mod h1:zfE2R7887kiwXkGTHKPe5NBgwhFwIC3pnA2uAxrbvig= github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1 h1:p0nFrTYrOQzDhWYm6suaM5CoWiXV5NV7llHnp6/Kn/8= github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1/go.mod h1:1XxxpkgCmG/z6y30yRuVrcxre6zixIVX3xzi706Db/8= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d h1:xdFpzbApEMz4Rojg2Y2OjFlrh0wu7eB10V2tSZGW5y8= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d/go.mod h1:bgmqE7x9xwmIVr8PqLbC0M5iPm4AV2DBl596lO6S5Sw= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 h1:Z4t2ZY+ZyGWxtcXvPr11y4o3CGqhg3frJB5jXkCSvWA= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 h1:jkChf04hhdiMBApbb+lLDxHMY62Md6UeM7v++GSw3K8= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139/go.mod h1:wuhagkM/lU0GbV2YcrROOH0GlsfXJYwm6qmpa4CK70w= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 h1:tw3K4UkH5XfW5SoyYkvAlbzrccoGSLdz/XkxD6nyGC8= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139/go.mod h1:1WcontO9PeuKdUf5HXfs3nuICtzUvFNnyCmrHkTCF9Y= github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16 h1:kG7DnjoCDJUt2htCqVxTA4IvQyR+a6mOmqlG1v7KMRE= github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16/go.mod h1:kMRGxNzyB5O6sqQlJEgBG/g49mzRvlcqbqMrzlhL+JY= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 h1:5z3LQ27MJmhiaeqp9S2TzbF5Wm4GGvUKAYOtE9AauR8= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= -github.com/smartcontractkit/chainlink-deployments-framework v0.98.0 h1:Ov/KOEtubOHXX8oa9UtARhHmkQNCOIjWNt+Zi0AuzHM= -github.com/smartcontractkit/chainlink-deployments-framework v0.98.0/go.mod h1:24dwRW1PYolrlxSth///ddG3auGqR+50xaJiXfUHhkg= +github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a h1:z23/Q2MhxA2WJEOzX9s9H+TB8nU+U3qotwGPKLAOoPY= +github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a/go.mod h1:OdH025Wn1W4UXny4TeczG3K+b0acF0+OjInmzQt+po0= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 h1:5bxDnwI0wuPoC0H5H3H2n9CnQPb5iakR6UmAY4j8KUg= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= @@ -607,10 +619,12 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202510021 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= -github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= -github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.19 h1:inTH0/PrEaVv4iLdGsdcrP/rX7KMrq/Roosr5nIA8io= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.19/go.mod h1:BALK9cj8sk12e15UF6uDhifHgIApa+6N11TcQfInEro= +github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= +github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 h1:8M+2pA0qx9rXaxmpKouUHj983vQCGzztHkG0XjE5Eew= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5/go.mod h1:kHYJnZUqiPF7/xN5273prV+srrLJkS77GbBXHLKQpx0= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 h1:eieKLvYuzwBPh/FdbUS1gnIanI86zgWby1L10o90g4o= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745/go.mod h1:8vXLeG//BxDF86GWRytzGIy6jc70htD1r/KtPfjrsK0= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 h1:XRNxgcNqagXu6e4smJuS1crRK5cUAcCVd7u+iLduHDM= @@ -623,8 +637,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12i github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e h1:poXTj5cFVM6XfC4HICIDYkDVc/A6OYB0eeID0wU2JQE= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e/go.mod h1:PLdNK6GlqfxIWXzziPkU7dCAVlVFeYkyyW7AQY0R+4Q= -github.com/smartcontractkit/mcms v0.42.0 h1:zzs+auX6BL6sRIVpgVbLUviwrvWi8Fxo5dOP+9Wx/gk= -github.com/smartcontractkit/mcms v0.42.0/go.mod h1:39OxzRApGN7HG+JGbjxdCxyo5lvV0H0REUPyh3CzDGU= +github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d h1:lMJ0pY2mUtDqUl+CraggP9k9396VgihfX1RdfyUBLy8= +github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d/go.mod h1:R+h5CZoiZoxGToN4hvQwbb1dsKgoZ+gUOgWTNDxGsDQ= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= @@ -659,8 +673,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/testcontainers/testcontainers-go v0.41.0 h1:mfpsD0D36YgkxGj2LrIyxuwQ9i2wCKAD+ESsYM1wais= -github.com/testcontainers/testcontainers-go v0.41.0/go.mod h1:pdFrEIfaPl24zmBjerWTTYaY0M6UHsqA1YSvsoU40MI= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0 h1:AOtFXssrDlLm84A2sTTR/AhvJiYbrIuCO59d+Ro9Tb0= github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0/go.mod h1:k2a09UKhgSp6vNpliIY0QSgm4Hi7GXVTzWvWgUemu/8= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -709,12 +723,12 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= @@ -731,23 +745,25 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmc go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= -go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= -go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= +go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs= +go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= -go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY= +go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -810,8 +826,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -851,8 +867,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -892,17 +908,17 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= -google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/integration-tests/mcms/ccip_test.go b/integration-tests/mcms/ccip_test.go index 50bb0d795..1b5fa6547 100644 --- a/integration-tests/mcms/ccip_test.go +++ b/integration-tests/mcms/ccip_test.go @@ -89,17 +89,11 @@ func (s *CCIPMCMSTestSuite) Test_CCIP_MCMS() { // on-chain registry validation against the original package's proof type. func (s *CCIPMCMSTestSuite) RunLatestUpgradedCCIPProposal() { // 1. Record the current package_ids lists before the proposal. - ccipStateObjectBefore, err := s.client.SuiGetObject(s.T().Context(), models.SuiGetObjectRequest{ - ObjectId: s.ccipObjects.CCIPObjectRefObjectId, - Options: models.SuiObjectDataOptions{ShowContent: true}, - }) + ccipStateObjectBefore, err := bind.ReadObject(s.T().Context(), s.ccipObjects.CCIPObjectRefObjectId, s.client) s.Require().NoError(err) ccipPkgIdsBefore := ccipStateObjectBefore.Data.Content.Fields["package_ids"].([]any) - offrampStateObjectBefore, err := s.client.SuiGetObject(s.T().Context(), models.SuiGetObjectRequest{ - ObjectId: s.ccipOfframpObjects.StateObjectId, - Options: models.SuiObjectDataOptions{ShowContent: true}, - }) + offrampStateObjectBefore, err := bind.ReadObject(s.T().Context(), s.ccipOfframpObjects.StateObjectId, s.client) s.Require().NoError(err) offrampPkgIdsBefore := offrampStateObjectBefore.Data.Content.Fields["package_ids"].([]any) @@ -157,10 +151,7 @@ func (s *CCIPMCMSTestSuite) RunLatestUpgradedCCIPProposal() { s.ExecuteProposalE2e(&report.Output, s.bypasserConfig, 0) // 4a. Verify CCIP package_ids did NOT grow: the upgraded add_package_id is a no-op. - ccipStateObjectAfter, err := s.client.SuiGetObject(s.T().Context(), models.SuiGetObjectRequest{ - ObjectId: s.ccipObjects.CCIPObjectRefObjectId, - Options: models.SuiObjectDataOptions{ShowContent: true}, - }) + ccipStateObjectAfter, err := bind.ReadObject(s.T().Context(), s.ccipObjects.CCIPObjectRefObjectId, s.client) s.Require().NoError(err) ccipPkgIdsAfter := ccipStateObjectAfter.Data.Content.Fields["package_ids"].([]any) @@ -170,10 +161,7 @@ func (s *CCIPMCMSTestSuite) RunLatestUpgradedCCIPProposal() { "latestCcipPackageId should not be in CCIP package_ids since upgraded add_package_id is a no-op") // 4b. Verify Offramp package_ids did NOT grow: the upgraded add_package_id is also a no-op. - offrampStateObjectAfter, err := s.client.SuiGetObject(s.T().Context(), models.SuiGetObjectRequest{ - ObjectId: s.ccipOfframpObjects.StateObjectId, - Options: models.SuiObjectDataOptions{ShowContent: true}, - }) + offrampStateObjectAfter, err := bind.ReadObject(s.T().Context(), s.ccipOfframpObjects.StateObjectId, s.client) s.Require().NoError(err) offrampPkgIdsAfter := offrampStateObjectAfter.Data.Content.Fields["package_ids"].([]any) @@ -341,10 +329,7 @@ func RunTestCCIPFeeQuoterProposal(s *CCIPMCMSTestSuite) { require.Equal(s.T(), expectedPremiumMultiplier, actualPremiumMultiplier) // Add check to verify latest ccip package was added to state object fetching the object directly - ccipStateObject, err := s.client.SuiGetObject(s.T().Context(), models.SuiGetObjectRequest{ - ObjectId: s.ccipObjects.CCIPObjectRefObjectId, - Options: models.SuiObjectDataOptions{ShowContent: true}, - }) + ccipStateObject, err := bind.ReadObject(s.T().Context(), s.ccipObjects.CCIPObjectRefObjectId, s.client) require.NoError(s.T(), err) require.Contains(s.T(), ccipStateObject.Data.Content.Fields["package_ids"].([]any), s.latestCcipPackageId) } diff --git a/integration-tests/mcms/common.go b/integration-tests/mcms/common.go index 731e06d58..613a6573d 100644 --- a/integration-tests/mcms/common.go +++ b/integration-tests/mcms/common.go @@ -14,7 +14,6 @@ import ( "time" "github.com/block-vision/sui-go-sdk/models" - "github.com/block-vision/sui-go-sdk/sui" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/smartcontractkit/mcms" @@ -53,6 +52,7 @@ import ( opregistry "github.com/smartcontractkit/chainlink-sui/deployment/ops/registry" bindutils "github.com/smartcontractkit/chainlink-sui/bindings/utils" + cslclient "github.com/smartcontractkit/chainlink-sui/relayer/client" ) type RoleConfig struct { @@ -90,7 +90,7 @@ func CreateConfig(role suisdk.TimelockRole, count int, quorum uint8) *RoleConfig type MCMSTestSuite struct { suite.Suite - client sui.ISuiAPI + client cslclient.SuiPTBClient signer bindutils.SuiSigner chainSelector types.ChainSelector @@ -762,7 +762,6 @@ func validateVersionIncrement(t *testing.T, oldVer, newVer any) error { return nil } - // parseVersion parses a version value from interface{} to float64 func parseVersion(t *testing.T, version any, versionType string) (float64, bool) { t.Helper() @@ -784,3 +783,4 @@ func parseVersion(t *testing.T, version any, versionType string) (float64, bool) return 0, false } } + diff --git a/integration-tests/offramp/ccip_receivers_test.go b/integration-tests/offramp/ccip_receivers_test.go new file mode 100644 index 000000000..3cf0db8db --- /dev/null +++ b/integration-tests/offramp/ccip_receivers_test.go @@ -0,0 +1,212 @@ +//go:build integration + +package offramp_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-sui/bindings/bind" + "github.com/smartcontractkit/chainlink-sui/bindings/packages/ccip" + "github.com/smartcontractkit/chainlink-sui/bindings/packages/mcms" + "github.com/smartcontractkit/chainlink-sui/contracts" + "github.com/smartcontractkit/chainlink-sui/relayer/chainwriter/ptb/offramp" + rel "github.com/smartcontractkit/chainlink-sui/relayer/signer" + "github.com/smartcontractkit/chainlink-sui/relayer/testutils" +) + +const testMcmsOwner = "0x2" + +func compileAddrsDummyReceiver(mcmsPackageID, ccipPackageID, signer string) map[string]string { + return map[string]string{ + "ccip": ccipPackageID, + "ccip_dummy_receiver": "0x0", + "mcms": mcmsPackageID, + "mcms_owner": testMcmsOwner, + "signer": signer, + } +} + +func compileAddrsBrokenReceiver(mcmsPackageID, ccipPackageID, signer string) map[string]string { + return map[string]string{ + "ccip": ccipPackageID, + "mcms": mcmsPackageID, + "mcms_owner": testMcmsOwner, + "signer": signer, + } +} + +// TestBrokenReceiverABI_NoPanic verifies that the relayer's DecodeParameters handles +// a registered receiver with generic type parameters (TypeParameter in normalized ABI) +// without panicking. This is the core fix for report 71024. +func TestBrokenReceiverABI_NoPanic(t *testing.T) { + lggr := logger.Test(t) + gasBudget := int64(1_000_000_000) + + // Start dedicated Sui node + cmd, err := testutils.StartSuiNode(testutils.CLI) + require.NoError(t, err) + t.Cleanup(func() { + if cmd.Process != nil { + if perr := cmd.Process.Kill(); perr != nil { + t.Logf("Failed to kill Sui node process: %v", perr) + } + } + }) + + keystoreInstance, accountAddress, publicKeyBytes := testutils.SetupTestSigner(t, context.Background(), lggr, gasBudget) + lggr.Infow("Using account", "address", accountAddress) + + require.Eventually(t, func() bool { + return testutils.FundWithFaucet(lggr, testutils.SuiLocalnet, accountAddress) == nil + }, 10*time.Second, time.Second) + + // Setup client + ptbClient, _, _ := testutils.SetupClients(t, testutils.LocalGrpcURL, keystoreInstance, lggr, gasBudget) + + signer := keystoreInstance.GetSuiSigner(context.Background(), fmt.Sprintf("%064x", publicKeyBytes)) + privateKeySigner := rel.NewPrivateKeySigner(signer.PriKey) + + gasBudgetU64 := uint64(gasBudget) + opts := &bind.CallOpts{ + Signer: privateKeySigner, + WaitForExecution: true, + GasBudget: &gasBudgetU64, + } + + mcmsPackage, _, err := mcms.PublishMCMS(context.Background(), opts, ptbClient, testutils.LocalURL) + require.NoError(t, err, "failed to publish MCMS") + mcmsPackageId := mcmsPackage.Address() + + ccipPackage, _, err := ccip.PublishCCIP(context.Background(), opts, ptbClient, mcmsPackageId, testMcmsOwner, testutils.LocalURL) + require.NoError(t, err, "failed to publish CCIP") + ccipPackageId := ccipPackage.Address() + + // Publish the broken receiver + brokenReceiverArtifact, err := bind.CompilePackage( + contracts.CCIPBrokenReceiver, + compileAddrsBrokenReceiver(mcmsPackageId, ccipPackageId, accountAddress), + false, + testutils.LocalURL, + ) + require.NoError(t, err, "failed to compile broken receiver") + + brokenReceiverPackageId, _, err := bind.PublishPackage(context.Background(), opts, ptbClient, bind.PublishRequest{ + CompiledModules: brokenReceiverArtifact.Modules, + Dependencies: brokenReceiverArtifact.Dependencies, + }) + require.NoError(t, err, "failed to publish broken receiver") + lggr.Infow("Published broken receiver", "packageId", brokenReceiverPackageId) + + // Fetch the normalized module — this is the data the relayer would get from chain + normalizedModule, err := ptbClient.GetNormalizedModule(context.Background(), brokenReceiverPackageId, "broken_receiver") + require.NoError(t, err, "failed to get normalized module") + + functionSig, ok := normalizedModule.ExposedFunctions["ccip_receive"] + require.True(t, ok, "ccip_receive not found in normalized module") + + funcMap, ok := functionSig.(map[string]any) + require.True(t, ok, "function signature is not a map") + + params, ok := funcMap["parameters"].([]any) + require.True(t, ok, "parameters field is not an array") + require.Greater(t, len(params), 0) + + lggr.Infow("Broken receiver normalized ABI", "parameters", params) + + // The critical assertion: DecodeParameters must not panic and must return an error. + // Before the fix this panicked with: + // interface conversion: interface {} is float64, not map[string]interface {} + assert.NotPanics(t, func() { + result, decodeErr := offramp.DecodeParameters(lggr, funcMap, "parameters") + assert.Error(t, decodeErr, "DecodeParameters should return error for TypeParameter ABI") + assert.Nil(t, result) + assert.Contains(t, decodeErr.Error(), "TypeParameter") + }) +} + +// TestValidReceiverABI_DecodesSuccessfully verifies that the fix does not break +// legitimate receivers. The dummy receiver's ccip_receive has a concrete signature +// (no generics) and DecodeParameters must succeed on its normalized ABI. +func TestValidReceiverABI_DecodesSuccessfully(t *testing.T) { + lggr := logger.Test(t) + gasBudget := int64(1_000_000_000) + + // Start dedicated Sui node + cmd, err := testutils.StartSuiNode(testutils.CLI) + require.NoError(t, err) + t.Cleanup(func() { + if cmd.Process != nil { + if perr := cmd.Process.Kill(); perr != nil { + t.Logf("Failed to kill Sui node process: %v", perr) + } + } + }) + + keystoreInstance, accountAddress, publicKeyBytes := testutils.SetupTestSigner(t, context.Background(), lggr, gasBudget) + lggr.Infow("Using account", "address", accountAddress) + + require.Eventually(t, func() bool { + return testutils.FundWithFaucet(lggr, testutils.SuiLocalnet, accountAddress) == nil + }, 10*time.Second, time.Second) + + ptbClient, _, _ := testutils.SetupClients(t, testutils.LocalGrpcURL, keystoreInstance, lggr, gasBudget) + + signer := keystoreInstance.GetSuiSigner(context.Background(), fmt.Sprintf("%064x", publicKeyBytes)) + privateKeySigner := rel.NewPrivateKeySigner(signer.PriKey) + + gasBudgetU64 := uint64(gasBudget) + opts := &bind.CallOpts{ + Signer: privateKeySigner, + WaitForExecution: true, + GasBudget: &gasBudgetU64, + } + + mcmsPackage, _, err := mcms.PublishMCMS(context.Background(), opts, ptbClient, testutils.LocalURL) + require.NoError(t, err, "failed to publish MCMS") + mcmsPackageId := mcmsPackage.Address() + + ccipPackage, _, err := ccip.PublishCCIP(context.Background(), opts, ptbClient, mcmsPackageId, testMcmsOwner, testutils.LocalURL) + require.NoError(t, err, "failed to publish CCIP") + ccipPackageId := ccipPackage.Address() + + // Publish the dummy receiver (valid, concrete ccip_receive signature) + dummyReceiverArtifact, err := bind.CompilePackage( + contracts.CCIPDummyReceiver, + compileAddrsDummyReceiver(mcmsPackageId, ccipPackageId, accountAddress), + false, + testutils.LocalURL, + ) + require.NoError(t, err, "failed to compile dummy receiver") + + dummyReceiverPackageId, _, err := bind.PublishPackage(context.Background(), opts, ptbClient, bind.PublishRequest{ + CompiledModules: dummyReceiverArtifact.Modules, + Dependencies: dummyReceiverArtifact.Dependencies, + }) + require.NoError(t, err, "failed to publish dummy receiver") + lggr.Infow("Published dummy receiver", "packageId", dummyReceiverPackageId) + + // Fetch the normalized module + normalizedModule, err := ptbClient.GetNormalizedModule(context.Background(), dummyReceiverPackageId, "dummy_receiver") + require.NoError(t, err) + + functionSig, ok := normalizedModule.ExposedFunctions["ccip_receive"] + require.True(t, ok, "ccip_receive not found") + + funcMap, ok := functionSig.(map[string]any) + require.True(t, ok) + + // DecodeParameters must succeed for valid receivers + result, err := offramp.DecodeParameters(lggr, funcMap, "parameters") + require.NoError(t, err, "DecodeParameters should succeed for dummy receiver ABI") + require.NotNil(t, result) + require.Greater(t, len(result), 0, "should decode at least one parameter type") + + lggr.Infow("Dummy receiver decoded parameter types", "paramTypes", result) +} diff --git a/integration-tests/onramp/ccip_onramp_test.go b/integration-tests/onramp/ccip_onramp_test.go deleted file mode 100644 index 3d7765c58..000000000 --- a/integration-tests/onramp/ccip_onramp_test.go +++ /dev/null @@ -1,656 +0,0 @@ -//go:build integration - -package ccip_test - -import ( - "context" - "fmt" - "math/big" - "testing" - "time" - - "github.com/smartcontractkit/chainlink-common/pkg/logger" - commonTypes "github.com/smartcontractkit/chainlink-common/pkg/types" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-sui/integration-tests/onramp/environment" - "github.com/smartcontractkit/chainlink-sui/relayer/chainwriter" - cwConfig "github.com/smartcontractkit/chainlink-sui/relayer/chainwriter/config" - "github.com/smartcontractkit/chainlink-sui/relayer/client" - "github.com/smartcontractkit/chainlink-sui/relayer/testutils" -) - -type ContractAddresses struct { - CCIPPackageID string - CCIPOnrampPackageID string - LinkLockReleaseTokenPool string - CCIPTokenPoolPackageID string - CCIPTokenPoolStateObjectId string -} - -// TestCCIPSuiOnRamp tests the CCIP onramp send functionality -func TestCCIPSuiOnRamp(t *testing.T) { - lggr := logger.Test(t) - - localChainSelector := uint64(1) - destChainSelector := uint64(2) - - gasBudget := int64(1_000_000_000) - - // Start dedicated Sui node for this test - cmd, err := testutils.StartSuiNode(testutils.CLI) - require.NoError(t, err) - t.Cleanup(func() { - if cmd.Process != nil { - if perr := cmd.Process.Kill(); perr != nil { - t.Logf("Failed to kill Sui node process: %v", perr) - } - } - }) - - // Wait for the node to be fully ready - time.Sleep(3 * time.Second) - - c := context.Background() - ctx, cancel := context.WithCancel(c) - defer cancel() - - keystoreInstance, accountAddress, publicKeyBytes := testutils.SetupTestSigner(t, context.Background(), lggr, gasBudget) - lggr.Infow("Using account", "address", accountAddress) - - // Fund the account for gas payments - for range 3 { - err := testutils.FundWithFaucet(lggr, "localnet", accountAddress) - require.NoError(t, err) - } - - t.Run("CCIP SUI messaging", func(t *testing.T) { - t.Skip("Skipping CCIP SUI messaging test in favor of E2E tests. Re-enable and run in CI for details on errors.") - envSettings := environment.SetupTestEnvironment(t, localChainSelector, destChainSelector, gasBudget) - linkTokenType := fmt.Sprintf("%s::mock_link_token::MOCK_LINK_TOKEN", envSettings.MockLinkReport.Output.PackageId) - ethTokenType := fmt.Sprintf("%s::mock_eth_token::MOCK_ETH_TOKEN", envSettings.MockEthTokenReport.Output.PackageId) - - _, txManager, _ := testutils.SetupClients(t, testutils.LocalUrl, keystoreInstance, lggr, gasBudget) - tokenPoolDetails := testutils.TokenToolDetails{ - TokenPoolPackageId: envSettings.LockReleaseTokenPoolReport.Output.LockReleaseTPPackageID, - TokenPoolType: testutils.TokenPoolTypeLockRelease, - } - ethTokenPoolDetails := testutils.TokenToolDetails{ - TokenPoolPackageId: envSettings.BurnMintTokenPoolReport.Output.BurnMintTPPackageID, - TokenPoolType: testutils.TokenPoolTypeBurnMint, - } - - err = txManager.Start(ctx) - require.NoError(t, err) - - chainWriterConfig, err := testutils.ConfigureOnRampChainWriter( - lggr, - envSettings.CCIPReport.Output.CCIPPackageId, - envSettings.OnRampReport.Output.CCIPOnRampPackageId, - []testutils.TokenToolDetails{tokenPoolDetails, ethTokenPoolDetails}, - publicKeyBytes, - linkTokenType, - linkTokenType, - ethTokenType, - ) - require.NoError(t, err) - - lggr.Infow("chainWriterConfig", "chainWriterConfig", chainWriterConfig) - chainWriter, err := chainwriter.NewSuiChainWriter(lggr, txManager, chainWriterConfig, false) - require.NoError(t, err) - - err = chainWriter.Start(ctx) - require.NoError(t, err) - - t.Cleanup(func() { - txManager.Close() - chainWriter.Close() - }) - - tokenAmount := uint64(500000) // 500K tokens for transfer - feeAmount := uint64(100000) // 100K tokens for fee payment - - gasBudget := int64(1_000_000_000_000) - - mintedCoinId1, mintedCoinId2 := environment.GetLinkCoins(t, envSettings, linkTokenType, accountAddress, lggr, tokenAmount, feeAmount) - - // Create array with both coins for the PTB arguments - linkCoins := []string{mintedCoinId1, mintedCoinId2} - - // Set up arguments for the PTB - ptbArgs := createCCIPSendPTBArgsForBMAndLRTokenPools( - lggr, - destChainSelector, - linkTokenType, - ethTokenType, - envSettings.MockLinkReport.Output.Objects.CoinMetadataObjectId, - envSettings.MockEthTokenReport.Output.Objects.CoinMetadataObjectId, - linkCoins, - envSettings.EthCoins, - envSettings.CCIPReport.Output.Objects.CCIPObjectRefObjectId, - environment.ClockObjectId, - envSettings.OnRampReport.Output.Objects.StateObjectId, - envSettings.LockReleaseTokenPoolReport.Output.Objects.StateObjectId, - envSettings.BurnMintTokenPoolReport.Output.Objects.StateObjectId, - environment.EthereumAddress, - ) - txID := "ccip_send_test_message" - - lggr.Infow("ptbArgs", "ptbArgs", ptbArgs) - - lggr.Infow("Submitting transaction", - "txID", txID, - "accountAddress", accountAddress, - "ptbArgs", ptbArgs, - "chainWriterConfig", chainWriterConfig) - - offrampPackageId := envSettings.OnRampReport.Output.CCIPOnRampPackageId - - err = chainWriter.SubmitTransaction(ctx, - cwConfig.PTBChainWriterModuleName, - "message_passing", - &ptbArgs, - txID, - offrampPackageId, - &commonTypes.TxMeta{GasLimit: big.NewInt(gasBudget)}, - nil, - ) - require.NoError(t, err) - - require.Eventually(t, func() bool { - status, statusErr := chainWriter.GetTransactionStatus(ctx, txID) - if statusErr != nil { - return false - } - - return status == commonTypes.Finalized - }, 5*time.Second, 1*time.Second, "Transaction final state not reached") - - // Create a PTB client to query events (the basic sui.ISuiAPI doesn't have QueryEvents) - ptbClient, err := client.NewPTBClient(lggr, testutils.LocalUrl, nil, 10*time.Second, nil, 5, "WaitForLocalExecution") - require.NoError(t, err, "Failed to create PTB client for event querying") - - // Query for ReceivedMessage events emitted by the dummy receiver - eventFilter := client.EventFilterByMoveEventModule{ - Package: envSettings.OnRampReport.Output.CCIPOnRampPackageId, - Module: "onramp", - Event: "CCIPMessageSent", - } - - // Query events with a small limit since we expect only one event - limit := uint(10) - eventsResponse, err := ptbClient.QueryEvents(ctx, eventFilter, &limit, nil, nil) - lggr.Debugw("eventsResponse", "eventsResponse", eventsResponse) - require.NoError(t, err, "Failed to query events") - require.NotEmpty(t, eventsResponse.Data, "Expected at least one ReceivedMessage event") - lggr.Infow("mostRecentEvent", "mostRecentEvent", eventsResponse.Data) - }) - - t.Run("CCIP SUI messaging with Lock Release Token Pool", func(t *testing.T) { - envSettings := environment.SetupTestEnvironment(t, localChainSelector, destChainSelector, gasBudget) - linkTokenType := fmt.Sprintf("%s::mock_link_token::MOCK_LINK_TOKEN", envSettings.MockLinkReport.Output.PackageId) - ethTokenType := fmt.Sprintf("%s::mock_eth_token::MOCK_ETH_TOKEN", envSettings.MockEthTokenReport.Output.PackageId) - - _, txManager, _ := testutils.SetupClients(t, testutils.LocalUrl, keystoreInstance, lggr, gasBudget) - - tokenAmount := uint64(500000) // 500K tokens for transfer - feeAmount := uint64(100000) // 100K tokens for fee payment - - mintedCoinId1, mintedCoinId2 := environment.GetLinkCoins(t, envSettings, linkTokenType, accountAddress, lggr, tokenAmount, feeAmount) - - // Create array with both coins for the PTB arguments - linkCoins := []string{mintedCoinId1, mintedCoinId2} - - // Create chain writer config for Lock Release Token Pool only - lrTokenPoolDetails := testutils.TokenToolDetails{ - TokenPoolPackageId: envSettings.LockReleaseTokenPoolReport.Output.LockReleaseTPPackageID, - TokenPoolType: testutils.TokenPoolTypeLockRelease, - } - - lrChainWriterConfig, err := testutils.ConfigureOnRampChainWriter( - lggr, - envSettings.CCIPReport.Output.CCIPPackageId, - envSettings.OnRampReport.Output.CCIPOnRampPackageId, - []testutils.TokenToolDetails{lrTokenPoolDetails}, - publicKeyBytes, - linkTokenType, - linkTokenType, - ethTokenType, - ) - require.NoError(t, err) - - lrChainWriter, err := chainwriter.NewSuiChainWriter(lggr, txManager, lrChainWriterConfig, false) - require.NoError(t, err) - - err = txManager.Start(ctx) - require.NoError(t, err) - - err = lrChainWriter.Start(ctx) - require.NoError(t, err) - - t.Cleanup(func() { - txManager.Close() - lrChainWriter.Close() - }) - - // Set up arguments for the PTB - only Lock Release Token Pool - ptbArgs := createCCIPSendPTBArgsForLRTokenPool( - lggr, - destChainSelector, - linkTokenType, - envSettings.MockLinkReport.Output.Objects.CoinMetadataObjectId, - linkCoins, - envSettings.CCIPReport.Output.Objects.CCIPObjectRefObjectId, - environment.ClockObjectId, - envSettings.OnRampReport.Output.Objects.StateObjectId, - envSettings.LockReleaseTokenPoolReport.Output.Objects.StateObjectId, - environment.EthereumAddress, - ) - txID := "ccip_send_lock_release_token_pool" - - err = lrChainWriter.SubmitTransaction(ctx, - cwConfig.PTBChainWriterModuleName, - "token_transfer_with_messaging", - &ptbArgs, - txID, - accountAddress, - &commonTypes.TxMeta{GasLimit: big.NewInt(gasBudget)}, - nil, - ) - require.NoError(t, err) - - require.Eventually(t, func() bool { - status, statusErr := lrChainWriter.GetTransactionStatus(ctx, txID) - if statusErr != nil { - return false - } - - return status == commonTypes.Finalized - }, 5*time.Second, 1*time.Second, "Transaction final state not reached") - }) - - t.Run("CCIP SUI messaging with Burn Mint Token Pool", func(t *testing.T) { - envSettings := environment.SetupTestEnvironment(t, localChainSelector, destChainSelector, gasBudget) - linkTokenType := fmt.Sprintf("%s::mock_link_token::MOCK_LINK_TOKEN", envSettings.MockLinkReport.Output.PackageId) - ethTokenType := fmt.Sprintf("%s::mock_eth_token::MOCK_ETH_TOKEN", envSettings.MockEthTokenReport.Output.PackageId) - - _, txManager, _ := testutils.SetupClients(t, testutils.LocalUrl, keystoreInstance, lggr, gasBudget) - - tokenAmount := uint64(500000) // 500K tokens for transfer - feeAmount := uint64(100000) // 100K tokens for fee payment - - gasBudget := int64(1_000_000_000) - - _, mintedCoinId2 := environment.GetLinkCoins(t, envSettings, linkTokenType, accountAddress, lggr, tokenAmount, feeAmount) - - // Create chain writer config for Burn Mint Token Pool only - bmTokenPoolDetails := testutils.TokenToolDetails{ - TokenPoolPackageId: envSettings.BurnMintTokenPoolReport.Output.BurnMintTPPackageID, - TokenPoolType: testutils.TokenPoolTypeBurnMint, - } - - bmChainWriterConfig, err := testutils.ConfigureOnRampChainWriter( - lggr, - envSettings.CCIPReport.Output.CCIPPackageId, - envSettings.OnRampReport.Output.CCIPOnRampPackageId, - []testutils.TokenToolDetails{bmTokenPoolDetails}, - publicKeyBytes, - linkTokenType, - linkTokenType, - ethTokenType, - ) - require.NoError(t, err) - - lggr.Debugw("bmChainWriterConfig", "bmChainWriterConfig", bmChainWriterConfig) - - bmChainWriter, err := chainwriter.NewSuiChainWriter(lggr, txManager, bmChainWriterConfig, false) - require.NoError(t, err) - - err = txManager.Start(ctx) - require.NoError(t, err) - - err = bmChainWriter.Start(ctx) - require.NoError(t, err) - - t.Cleanup(func() { - txManager.Close() - bmChainWriter.Close() - }) - - // Set up arguments for the PTB - only Burn Mint Token Pool - ptbArgs := createCCIPSendPTBArgsForBMTokenPool( - lggr, - destChainSelector, - linkTokenType, - ethTokenType, - envSettings.MockLinkReport.Output.Objects.CoinMetadataObjectId, - envSettings.MockEthTokenReport.Output.Objects.CoinMetadataObjectId, - mintedCoinId2, // fee token - envSettings.EthCoins[0], // token to transfer - envSettings.CCIPReport.Output.Objects.CCIPObjectRefObjectId, - environment.ClockObjectId, - envSettings.OnRampReport.Output.Objects.StateObjectId, - envSettings.BurnMintTokenPoolReport.Output.Objects.StateObjectId, - environment.EthereumAddress, - ) - txID := "ccip_send_burn_mint_token_pool" - - offrampPackageId := envSettings.OnRampReport.Output.CCIPOnRampPackageId - - err = bmChainWriter.SubmitTransaction(ctx, - cwConfig.PTBChainWriterModuleName, - "token_transfer_with_messaging", - &ptbArgs, - txID, - offrampPackageId, - &commonTypes.TxMeta{GasLimit: big.NewInt(gasBudget)}, - nil, - ) - require.NoError(t, err) - - require.Eventually(t, func() bool { - status, statusErr := bmChainWriter.GetTransactionStatus(ctx, txID) - if statusErr != nil { - t.Logf("Failed to get transaction status: %v, retrying...", statusErr) - return false - } - - return status == commonTypes.Finalized - }, 5*time.Second, 1*time.Second, "Transaction final state not reached") - }) -} - -func TestCCIPSuiOnRampWithManagedTokenPool(t *testing.T) { - lggr := logger.Test(t) - - localChainSelector := uint64(1) - destChainSelector := uint64(2) - - gasBudget := int64(1_000_000_000) - - // Wait a bit to ensure previous test's node is fully shut down - time.Sleep(2 * time.Second) - - // Start dedicated Sui node for this test - cmd, err := testutils.StartSuiNode(testutils.CLI) - require.NoError(t, err) - t.Cleanup(func() { - if cmd.Process != nil { - if perr := cmd.Process.Kill(); perr != nil { - t.Logf("Failed to kill Sui node process: %v", perr) - } - } - }) - - // Wait for the node to be fully ready - time.Sleep(3 * time.Second) - - accountAddress, publicKeyBytes, signer, keystoreInstance, client, deps, bundle := environment.BasicSetUp(t, lggr, gasBudget) - - // Fund the account for gas payments - for range 3 { - err := testutils.FundWithFaucet(lggr, "localnet", accountAddress) - require.NoError(t, err) - } - - envSettings := environment.SetupTestEnvironmentForManagedTokenPool(t, client, signer, accountAddress, bundle, deps, localChainSelector, destChainSelector, keystoreInstance) - - lggr.Infow("Using account", "address", accountAddress) - - _, txManager, _ := testutils.SetupClients(t, testutils.LocalUrl, keystoreInstance, lggr, gasBudget) - - ethManagedTokenPoolDetails := testutils.TokenToolDetails{ - TokenPoolPackageId: envSettings.ManagedTokenPoolReport.Output.ManagedTPPackageId, - TokenPoolType: testutils.TokenPoolTypeManaged, - } - - linkTokenType := fmt.Sprintf("%s::mock_link_token::MOCK_LINK_TOKEN", envSettings.MockLinkReport.Output.PackageId) - ethTokenType := fmt.Sprintf("%s::mock_eth_token::MOCK_ETH_TOKEN", envSettings.MockEthTokenReport.Output.PackageId) - - chainWriterConfig, err := testutils.ConfigureOnRampChainWriter( - lggr, - envSettings.CCIPReport.Output.CCIPPackageId, - envSettings.OnRampReport.Output.CCIPOnRampPackageId, - []testutils.TokenToolDetails{ethManagedTokenPoolDetails}, - publicKeyBytes, - linkTokenType, - linkTokenType, - ethTokenType, - ) - require.NoError(t, err) - - lggr.Infow("chainWriterConfig", "chainWriterConfig", chainWriterConfig) - chainWriter, err := chainwriter.NewSuiChainWriter(lggr, txManager, chainWriterConfig, false) - require.NoError(t, err) - - c := context.Background() - ctx, cancel := context.WithCancel(c) - defer cancel() - - // Start the TxManager first (required for broadcasting transactions) - err = txManager.Start(ctx) - require.NoError(t, err) - - err = chainWriter.Start(ctx) - require.NoError(t, err) - - t.Cleanup(func() { - txManager.Close() - chainWriter.Close() - }) - - t.Run("CCIP SUI messaging with 1 managed TP", func(t *testing.T) { - tokenAmount := uint64(500000) // 500K tokens for transfer - feeAmount := uint64(100000) // 100K tokens for fee payment - - mintedCoinId0, _ := environment.GetLinkCoins(t, envSettings, linkTokenType, accountAddress, lggr, tokenAmount, feeAmount) - - // Set up arguments for the PTB - ptbArgs := createCCIPSendPTBArgsForManagedTokenPool( - lggr, - destChainSelector, - linkTokenType, - ethTokenType, - envSettings.MockLinkReport.Output.Objects.CoinMetadataObjectId, - envSettings.MockEthTokenReport.Output.Objects.CoinMetadataObjectId, - mintedCoinId0, - envSettings.EthCoins[0], - envSettings.CCIPReport.Output.Objects.CCIPObjectRefObjectId, - environment.ClockObjectId, - envSettings.OnRampReport.Output.Objects.StateObjectId, - envSettings.ManagedTokenReport.Output.Objects.StateObjectId, - envSettings.ManagedTokenPoolReport.Output.Objects.StateObjectId, - environment.EthereumAddress, - ) - txID := "ccip_send_test_token" - - offrampPackageId := envSettings.OnRampReport.Output.CCIPOnRampPackageId - - err = chainWriter.SubmitTransaction(ctx, - cwConfig.PTBChainWriterModuleName, - "token_transfer_with_messaging", - &ptbArgs, - txID, - offrampPackageId, - &commonTypes.TxMeta{GasLimit: big.NewInt(gasBudget)}, - nil, - ) - require.NoError(t, err) - - require.Eventually(t, func() bool { - status, statusErr := chainWriter.GetTransactionStatus(ctx, txID) - if statusErr != nil { - return false - } - - return status == commonTypes.Finalized - }, 5*time.Second, 1*time.Second, "Transaction final state not reached") - }) -} - -// createCCIPSendPTBArgsForBMAndLRTokenPools creates PTBArgMapping for a CCIP send operation -func createCCIPSendPTBArgsForBMAndLRTokenPools( - lggr logger.Logger, - destChainSelector uint64, - linkTokenType string, - ethTokenType string, - linkTokenMetadata string, - ethTokenMetadata string, - linkTokenCoinObjects []string, - ethTokenCoinObjects []string, - ccipObjectRef string, - clockObject string, - ccipOnrampState string, - tokenPoolState string, - ethTokenPoolState string, - ethereumAddress string, -) map[string]any { - lggr.Infow("createCCIPSendPTBArgsForBMAndLRTokenPools", "destChainSelector", destChainSelector, "linkTokenType", linkTokenType, "linkTokenMetadata", linkTokenMetadata, "linkTokenCoinObjects", linkTokenCoinObjects, "ccipObjectRef", ccipObjectRef, "clockObject", clockObject, "ccipOnrampState", ccipOnrampState, "tokenPoolState", tokenPoolState) - - // Remove 0x prefix if present - evmAddressBytes := environment.NormalizeTo32Bytes(ethereumAddress) - - lggr.Infow("evmAddressBytes", "evmAddressBytes", evmAddressBytes) - - return map[string]any{ - "ccip_object_ref": ccipObjectRef, - "ccip_object_ref_mutable": ccipObjectRef, // Same object, different parameter name - "clock": environment.ClockObjectId, - "destination_chain_selector": destChainSelector, - "link_lock_release_token_pool_state": tokenPoolState, - "eth_burn_mint_token_pool_state": ethTokenPoolState, - "c_link": linkTokenCoinObjects[0], - "c_eth": ethTokenCoinObjects[0], - "onramp_state": ccipOnrampState, - "receiver": evmAddressBytes, - "data": []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - "fee_token_metadata": linkTokenMetadata, - "fee_token": linkTokenCoinObjects[1], - "extra_args": []byte{}, // Empty array to use default gas limit - "token_receiver": evmAddressBytes, - } -} - -// createCCIPSendPTBArgsForLRTokenPool creates PTBArgMapping for a CCIP send operation with Lock Release Token Pool only -func createCCIPSendPTBArgsForLRTokenPool( - lggr logger.Logger, - destChainSelector uint64, - linkTokenType string, - linkTokenMetadata string, - linkTokenCoinObjects []string, - ccipObjectRef string, - clockObject string, - ccipOnrampState string, - tokenPoolState string, - ethereumAddress string, -) map[string]any { - lggr.Infow("createCCIPSendPTBArgsForLRTokenPool", "destChainSelector", destChainSelector, "linkTokenType", linkTokenType, "linkTokenMetadata", linkTokenMetadata, "linkTokenCoinObjects", linkTokenCoinObjects, "ccipObjectRef", ccipObjectRef, "clockObject", clockObject, "ccipOnrampState", ccipOnrampState, "tokenPoolState", tokenPoolState) - - // Remove 0x prefix if present - evmAddressBytes := environment.NormalizeTo32Bytes(ethereumAddress) - - lggr.Infow("evmAddressBytes", "evmAddressBytes", evmAddressBytes) - - return map[string]any{ - "ccip_object_ref": ccipObjectRef, - "ccip_object_ref_mutable": ccipObjectRef, // Same object, different parameter name - "clock": environment.ClockObjectId, - "destination_chain_selector": destChainSelector, - "link_lock_release_token_pool_state": tokenPoolState, - "c_link": linkTokenCoinObjects[0], - "onramp_state": ccipOnrampState, - "receiver": evmAddressBytes, - "data": []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - "fee_token_metadata": linkTokenMetadata, - "fee_token": linkTokenCoinObjects[1], - "extra_args": []byte{}, // Empty array to use default gas limit - "token_receiver": evmAddressBytes, - } -} - -// createCCIPSendPTBArgsForBMTokenPool creates PTBArgMapping for a CCIP send operation with Burn Mint Token Pool only -func createCCIPSendPTBArgsForBMTokenPool( - lggr logger.Logger, - destChainSelector uint64, - linkTokenType string, - ethTokenType string, - linkTokenMetadata string, - ethTokenMetadata string, - feeTokenCoinObject string, - ethTokenCoinObject string, - ccipObjectRef string, - clockObject string, - ccipOnrampState string, - ethTokenPoolState string, - ethereumAddress string, -) map[string]any { - lggr.Infow("createCCIPSendPTBArgsForBMTokenPool", "destChainSelector", destChainSelector, "ethTokenType", ethTokenType, "ethTokenMetadata", ethTokenMetadata, "ethTokenCoinObject", ethTokenCoinObject, "ccipObjectRef", ccipObjectRef, "clockObject", clockObject, "ccipOnrampState", ccipOnrampState, "ethTokenPoolState", ethTokenPoolState) - - // Remove 0x prefix if present - evmAddressBytes := environment.NormalizeTo32Bytes(ethereumAddress) - - lggr.Infow("evmAddressBytes", "evmAddressBytes", evmAddressBytes) - - return map[string]any{ - "ccip_object_ref": ccipObjectRef, - "ccip_object_ref_mutable": ccipObjectRef, // Same object, different parameter name - "clock": environment.ClockObjectId, - "destination_chain_selector": destChainSelector, - "eth_burn_mint_token_pool_state": ethTokenPoolState, - "c_eth": ethTokenCoinObject, - "onramp_state": ccipOnrampState, - "receiver": evmAddressBytes, - "data": []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - "fee_token_metadata": linkTokenMetadata, - "fee_token": feeTokenCoinObject, - "extra_args": []byte{}, // Empty array to use default gas limit - "token_receiver": evmAddressBytes, - } -} - -func createCCIPSendPTBArgsForManagedTokenPool( - lggr logger.Logger, - destChainSelector uint64, - linkTokenType string, - ethTokenType string, - linkTokenMetadata string, - ethTokenMetadata string, - linkTokenCoinObject string, - ethTokenCoinObject string, - ccipObjectRef string, - clockObject string, - ccipOnrampState string, - ethManagedTokenState string, - ethManagedTokenPoolState string, - ethereumAddress string, -) map[string]any { - lggr.Infow("createCCIPSendPTBArgsForManagedTokenPool", "destChainSelector", destChainSelector, "linkTokenType", linkTokenType, "linkTokenMetadata", linkTokenMetadata, "linkTokenCoinObject", linkTokenCoinObject, "ethTokenCoinObject", ethTokenCoinObject, "ccipObjectRef", ccipObjectRef, "clockObject", clockObject, "ccipOnrampState", ccipOnrampState, "ethManagedTokenState", ethManagedTokenState, "ethManagedTokenPoolState", ethManagedTokenPoolState) - - // Remove 0x prefix if present - evmAddressBytes := environment.NormalizeTo32Bytes(ethereumAddress) - - lggr.Infow("evmAddressBytes", "evmAddressBytes", evmAddressBytes) - - return map[string]any{ - "ccip_object_ref": ccipObjectRef, - "ccip_object_ref_mutable": ccipObjectRef, // Same object, different parameter name - "clock": environment.ClockObjectId, - "destination_chain_selector": destChainSelector, - "eth_managed_token_state": ethManagedTokenState, - "eth_managed_token_pool_state": ethManagedTokenPoolState, - "c_managed_eth": ethTokenCoinObject, - "onramp_state": ccipOnrampState, - "receiver": evmAddressBytes, - "data": []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - "fee_token_metadata": linkTokenMetadata, - "fee_token": linkTokenCoinObject, - "extra_args": []byte{}, // Empty array to use default gas limit - "deny_list": environment.DenyListObjectId, - "token_receiver": evmAddressBytes, - } -} - -// Helper function to convert a string to a string pointer -func strPtr(s string) *string { - return &s -} diff --git a/integration-tests/onramp/environment/setup.go b/integration-tests/onramp/environment/setup.go deleted file mode 100644 index 6b3e15a74..000000000 --- a/integration-tests/onramp/environment/setup.go +++ /dev/null @@ -1,551 +0,0 @@ -package environment - -import ( - "context" - "encoding/hex" - "fmt" - "math/big" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/holiman/uint256" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-sui/bindings/bind" - mockethtoken "github.com/smartcontractkit/chainlink-sui/bindings/packages/mock_eth_token" - mocklinktoken "github.com/smartcontractkit/chainlink-sui/bindings/packages/mock_link_token" - sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" - ccipops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip" - burnmintops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip_burn_mint_token_pool" - lockreleaseops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip_lock_release_token_pool" - managedtokenpoolops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip_managed_token_pool" - onrampops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip_onramp" - managedtokenops "github.com/smartcontractkit/chainlink-sui/deployment/ops/managed_token" - mcmsops "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcms" - mockethtokenops "github.com/smartcontractkit/chainlink-sui/deployment/ops/mock_eth_token" - mocklinktokenops "github.com/smartcontractkit/chainlink-sui/deployment/ops/mock_link_token" - "github.com/smartcontractkit/chainlink-sui/relayer/client" - rel "github.com/smartcontractkit/chainlink-sui/relayer/signer" - "github.com/smartcontractkit/chainlink-sui/relayer/testutils" -) - -// Constants used across the environment setup -const ( - EvmReceiverAddress = "0x80226fc0ee2b096224eeac085bb9a8cba1146f7d" - EthereumAddress = "0x80226fc0ee2b096224eeac085bb9a8cba1146f7d" - ClockObjectId = "0x6" - DenyListObjectId = "0x403" -) - -// EnvironmentSettings holds all the deployed contract information and client settings -// needed for running CCIP onramp integration tests. -type EnvironmentSettings struct { - AccountAddress string - // Deployment reports - MockLinkReport *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mocklinktokenops.DeployMockLinkTokenObjects]] - MockEthTokenReport *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mockethtokenops.DeployMockEthTokenObjects]] - ManagedTokenReport *cld_ops.SequenceReport[managedtokenops.DeployAndInitManagedTokenInput, managedtokenops.DeployManagedTokenOutput] - CCIPReport *cld_ops.SequenceReport[ccipops.DeployAndInitCCIPSeqInput, ccipops.DeployCCIPSeqOutput] - OnRampReport *cld_ops.SequenceReport[onrampops.DeployAndInitCCIPOnRampSeqInput, onrampops.DeployCCIPOnRampSeqOutput] - LockReleaseTokenPoolReport *cld_ops.SequenceReport[lockreleaseops.DeployAndInitLockReleaseTokenPoolInput, lockreleaseops.DeployLockReleaseTokenPoolOutput] - BurnMintTokenPoolReport *cld_ops.SequenceReport[burnmintops.DeployAndInitBurnMintTokenPoolInput, burnmintops.DeployBurnMintTokenPoolOutput] - ManagedTokenPoolReport *cld_ops.SequenceReport[managedtokenpoolops.DeployAndInitManagedTokenPoolInput, managedtokenpoolops.DeployManagedTokenPoolOutput] - - EthereumPoolAddress []byte - EthCoins []string - - // Signers - Signer rel.SuiSigner - - // Client - Client client.SuiPTBClient -} - -// BasicSetUp performs basic environment setup including account creation, client setup, -// and bundle initialization. This is the foundation for all test environments. -func BasicSetUp(t *testing.T, lggr logger.Logger, gasLimit int64) (string, []byte, rel.SuiSigner, *testutils.TestKeystore, client.SuiPTBClient, sui_ops.OpTxDeps, cld_ops.Bundle) { - t.Helper() - - keystoreInstance, accountAddress, publicKeyBytes := testutils.SetupTestSigner(t, context.Background(), lggr, gasLimit) - ptbClient, _, _ := testutils.SetupClients(t, testutils.LocalUrl, keystoreInstance, lggr, gasLimit) - - signer := keystoreInstance.GetSuiSigner(context.Background(), fmt.Sprintf("%064x", publicKeyBytes)) - privateKeySigner := rel.NewPrivateKeySigner(signer.PriKey) - - gasBudget := uint64(gasLimit) - deps := sui_ops.OpTxDeps{ - Client: ptbClient, - Signer: privateKeySigner, - GetCallOpts: func() *bind.CallOpts { - return &bind.CallOpts{ - Signer: privateKeySigner, - WaitForExecution: true, - GasBudget: &gasBudget, - } - }, - } - - reporter := cld_ops.NewMemoryReporter() - bundle := cld_ops.NewBundle( - context.Background, - logger.Test(t), - reporter, - ) - - return accountAddress, publicKeyBytes, privateKeySigner, keystoreInstance, ptbClient, deps, bundle -} - -// UpdatePrices sets token prices in the fee quoter contract. -// This is critical for fee calculations in CCIP operations. -func UpdatePrices( - t *testing.T, - reportCCIP *cld_ops.SequenceReport[ccipops.DeployAndInitCCIPSeqInput, ccipops.DeployCCIPSeqOutput], - reportMockLink *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mocklinktokenops.DeployMockLinkTokenObjects]], - deps sui_ops.OpTxDeps, - bundle cld_ops.Bundle, - destChainSelector uint64, - lggr logger.Logger, -) { - t.Helper() - - // **CRITICAL**: Set token prices in the fee quoter - // The fee quoter needs to know USD prices to calculate fees - // Set LINK token price to $5.00 USD (5 * 1e18 = 5e18) - linkTokenPrice := big.NewInt(0) - linkTokenPrice.SetString("5000000000000000000", 10) // $5.00 in 1e18 format - - // Set gas price for destination chain to 20 gwei (20 * 1e9 = 2e10) - gasPrice := big.NewInt(20000000000) // 20 gwei in wei - - updatePricesInput := ccipops.FeeQuoterUpdateTokenPricesInput{ - CCIPPackageId: reportCCIP.Output.CCIPPackageId, - CCIPObjectRef: reportCCIP.Output.Objects.CCIPObjectRefObjectId, - FeeQuoterCapId: reportCCIP.Output.Objects.FeeQuoterCapObjectId, - SourceTokens: []string{reportMockLink.Output.Objects.CoinMetadataObjectId}, - SourceUsdPerToken: []*big.Int{linkTokenPrice}, - GasDestChainSelectors: []uint64{destChainSelector}, - GasUsdPerUnitGas: []*big.Int{gasPrice}, - } - - _, err := cld_ops.ExecuteOperation(bundle, ccipops.FeeQuoterUpdateTokenPricesOp, deps, updatePricesInput) - require.NoError(t, err, "failed to update token prices in fee quoter") - - lggr.Debugw("Updated token prices in fee quoter", "linkPrice", linkTokenPrice.String(), "gasPrice", gasPrice.String()) -} - -// DeployCCIPAndOnrampAndTokens deploys all the core CCIP infrastructure including -// mock tokens, MCMS contracts, CCIP core contracts, and onramp contracts. -func DeployCCIPAndOnrampAndTokens( - t *testing.T, - localChainSelector uint64, - destChainSelector uint64, - keystoreInstance *testutils.TestKeystore, - signerAddr string, - bundle cld_ops.Bundle, - deps sui_ops.OpTxDeps, - lggr logger.Logger, -) ( - *cld_ops.SequenceReport[ccipops.DeployAndInitCCIPSeqInput, ccipops.DeployCCIPSeqOutput], - *cld_ops.SequenceReport[onrampops.DeployAndInitCCIPOnRampSeqInput, onrampops.DeployCCIPOnRampSeqOutput], - *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mocklinktokenops.DeployMockLinkTokenObjects]], - *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mockethtokenops.DeployMockEthTokenObjects]], - *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mcmsops.DeployMCMSObjects]], -) { - t.Helper() - - // Deploy LINK - mockLinkReport, err := cld_ops.ExecuteOperation(bundle, mocklinktokenops.DeployMockLinkTokenOp, deps, cld_ops.EmptyInput{}) - require.NoError(t, err, "failed to deploy LINK token") - - // Deploy Mock ETH Token - mockEthTokenReport, err := cld_ops.ExecuteOperation(bundle, mockethtokenops.DeployMockEthTokenOp, deps, cld_ops.EmptyInput{}) - require.NoError(t, err, "failed to deploy Mock ETH token") - - configDigest, err := uint256.FromHex("0xe3b1c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") - require.NoError(t, err, "failed to convert config digest to uint256") - - // Deploy MCMs - reportMCMs, err := cld_ops.ExecuteOperation(bundle, mcmsops.DeployMCMSOp, deps, cld_ops.EmptyInput{}) - require.NoError(t, err, "failed to deploy MCMS Package") - lggr.Debugw("MCMS deployment report", "output", reportMCMs.Output) - - lggr.Debugw("LINK report", "output", mockLinkReport.Output) - lggr.Debugw("Mock ETH token report", "output", mockEthTokenReport.Output) - - // Create 20-byte Ethereum addresses for RMN Remote signers - ethAddr1, err := hex.DecodeString("8a1b2c3d4e5f60718293a4b5c6d7e8f901234567") - require.NoError(t, err, "failed to decode eth address 1") - ethAddr2, err := hex.DecodeString("7b8c9dab0c1d2e3f405162738495a6b7c8d9e0f1") - require.NoError(t, err, "failed to decode eth address 2") - ethAddr3, err := hex.DecodeString("1234567890abcdef1234567890abcdef12345678") - require.NoError(t, err, "failed to decode eth address 3") - - reportCCIP, err := cld_ops.ExecuteSequence(bundle, ccipops.DeployAndInitCCIPSequence, deps, ccipops.DeployAndInitCCIPSeqInput{ - LinkTokenCoinMetadataObjectId: mockLinkReport.Output.Objects.CoinMetadataObjectId, - LocalChainSelector: localChainSelector, - DestChainSelector: destChainSelector, - DeployCCIPInput: ccipops.DeployCCIPInput{ - McmsPackageId: reportMCMs.Output.PackageId, - McmsOwner: signerAddr, - }, - MaxFeeJuelsPerMsg: "100000000", - TokenPriceStalenessThreshold: 60, - // Fee Quoter configuration - AddMinFeeUsdCents: []uint32{3000}, - AddMaxFeeUsdCents: []uint32{30000}, - AddDeciBps: []uint16{1000}, - AddDestGasOverhead: []uint32{1000000}, - AddDestBytesOverhead: []uint32{1000}, - AddIsEnabled: []bool{true}, - RemoveTokens: []string{}, - // Fee Quoter destination chain configuration - IsEnabled: true, - MaxNumberOfTokensPerMsg: 2, - MaxDataBytes: 2000, - MaxPerMsgGasLimit: 5000000, - DestGasOverhead: 1000000, - DestGasPerPayloadByteBase: byte(2), - DestGasPerPayloadByteHigh: byte(5), - DestGasPerPayloadByteThreshold: uint16(10), - DestDataAvailabilityOverheadGas: 300000, - DestGasPerDataAvailabilityByte: 4, - DestDataAvailabilityMultiplierBps: 1, - ChainFamilySelector: []byte{0x28, 0x12, 0xd5, 0x2c}, - EnforceOutOfOrder: false, - DefaultTokenFeeUsdCents: 3, - DefaultTokenDestGasOverhead: 100000, - DefaultTxGasLimit: 500000, - GasMultiplierWeiPerEth: 100, - GasPriceStalenessThreshold: 1000000000, - NetworkFeeUsdCents: 10, - // Premium multiplier updates - PremiumMultiplierWeiPerEth: []uint64{10}, - - RmnHomeContractConfigDigest: configDigest.Bytes(), - SignerOnchainPublicKeys: [][]byte{ethAddr1, ethAddr2, ethAddr3}, - NodeIndexes: []uint64{0, 1, 2}, - FSign: uint64(1), - }) - require.NoError(t, err, "failed to execute CCIP deploy sequence") - require.NotEmpty(t, reportCCIP.Output.CCIPPackageId, "CCIP package ID should not be empty") - - seqOnrampInput := onrampops.DeployAndInitCCIPOnRampSeqInput{ - DeployCCIPOnRampInput: onrampops.DeployCCIPOnRampInput{ - CCIPPackageId: reportCCIP.Output.CCIPPackageId, - MCMSPackageId: reportMCMs.Output.PackageId, - MCMSOwnerPackageId: signerAddr, - }, - OnRampInitializeInput: onrampops.OnRampInitializeInput{ - NonceManagerCapId: reportCCIP.Output.Objects.NonceManagerCapObjectId, // this is from NonceManager init Op - SourceTransferCapId: reportCCIP.Output.Objects.SourceTransferCapObjectId, // this is from CCIP package publish - ChainSelector: destChainSelector, - FeeAggregator: signerAddr, - AllowListAdmin: signerAddr, - DestChainSelectors: []uint64{destChainSelector}, - DestChainAllowListEnabled: []bool{true}, - DestChainRouters: []string{"0x0"}, - }, - ApplyDestChainConfigureOnRampInput: onrampops.ApplyDestChainConfigureOnRampInput{ - CCIPObjectRefId: reportCCIP.Output.Objects.CCIPObjectRefObjectId, - DestChainSelector: []uint64{destChainSelector}, - DestChainAllowListEnabled: []bool{false}, - DestChainRouters: []string{"0x1"}, - }, - ApplyAllowListUpdatesInput: onrampops.ApplyAllowListUpdatesInput{ - CCIPObjectRefId: reportCCIP.Output.Objects.CCIPObjectRefObjectId, - DestChainSelector: []uint64{destChainSelector}, - DestChainAllowListEnabled: []bool{false}, - DestChainAddAllowedSenders: [][]string{{}}, - DestChainRemoveAllowedSenders: [][]string{{}}, - }, - } - // Run onRamp deploy & Apply dest chain update sequence - reportOnRamp, err := cld_ops.ExecuteSequence(bundle, onrampops.DeployAndInitCCIPOnRampSequence, deps, seqOnrampInput) - require.NoError(t, err, "failed to execute CCIP OnRamp deploy sequence") - - return &reportCCIP, &reportOnRamp, &mockLinkReport, &mockEthTokenReport, &reportMCMs -} - -// SetupTestEnvironment sets up a complete test environment with CCIP infrastructure -// and both lock/release and burn/mint token pools. -func SetupTestEnvironment(t *testing.T, localChainSelector uint64, destChainSelector uint64, gasLimit int64) *EnvironmentSettings { - t.Helper() - - lggr := logger.Test(t) - lggr.Debugw("Starting Sui node") - - os.Setenv("SUI_RPC_URL", testutils.LocalUrl) - os.Setenv("SUI_CONFIG_DIR", filepath.Join(os.Getenv("HOME"), ".sui", "sui_config")) - - accountAddress, _, signer, keystoreInstance, client, deps, bundle := BasicSetUp(t, lggr, gasLimit) - signerAddr, err := signer.GetAddress() - require.NoError(t, err) - - reportCCIP, reportOnRamp, reportMockLinkToken, reportMockEthToken, reportMCMs := DeployCCIPAndOnrampAndTokens(t, localChainSelector, destChainSelector, keystoreInstance, signerAddr, bundle, deps, lggr) - - linkTokenType := fmt.Sprintf("%s::mock_link_token::MOCK_LINK_TOKEN", reportMockLinkToken.Output.PackageId) - ethTokenType := fmt.Sprintf("%s::mock_eth_token::MOCK_ETH_TOKEN", reportMockEthToken.Output.PackageId) - - ethereumPoolAddressString := EvmReceiverAddress - remoteTokenAddressString := EvmReceiverAddress - - linkTokenPoolReport := SetupTokenPool( - t, - deps, - reportCCIP, - reportMCMs, - reportMockLinkToken, - signerAddr, - accountAddress, - linkTokenType, - ethereumPoolAddressString, - remoteTokenAddressString, - destChainSelector, - bundle, - client, - lggr, - ) - - ethCoins := GetEthCoins(t, client, signer, reportMockEthToken.Output.PackageId, reportMockEthToken.Output.Objects.TreasuryCapObjectId, ethTokenType, accountAddress, lggr, 1000000, 1000000) - - ethTokenPoolReport := SetupEthTokenPoolBurnMint( - t, - deps, - reportCCIP, - reportMCMs, - reportMockEthToken, - signerAddr, - accountAddress, - ethTokenType, - ethereumPoolAddressString, - remoteTokenAddressString, - destChainSelector, - bundle, - client, - lggr, - ) - - UpdatePrices(t, reportCCIP, reportMockLinkToken, deps, bundle, destChainSelector, lggr) - - return &EnvironmentSettings{ - AccountAddress: accountAddress, - MockLinkReport: reportMockLinkToken, - MockEthTokenReport: reportMockEthToken, - CCIPReport: reportCCIP, - OnRampReport: reportOnRamp, - LockReleaseTokenPoolReport: linkTokenPoolReport, - BurnMintTokenPoolReport: ethTokenPoolReport, - EthCoins: ethCoins, - Signer: signer, - Client: client, - } -} - -// SetupTestEnvironmentForManagedTokenPool sets up a test environment specifically -// for managed token pool testing. -func SetupTestEnvironmentForManagedTokenPool(t *testing.T, client sui.ISuiAPI, signer rel.SuiSigner, accountAddress string, bundle cld_ops.Bundle, deps sui_ops.OpTxDeps, localChainSelector uint64, destChainSelector uint64, keystoreInstance *testutils.TestKeystore) *EnvironmentSettings { - t.Helper() - - lggr := logger.Test(t) - lggr.Debugw("Starting Sui node") - signerAddr, err := signer.GetAddress() - require.NoError(t, err) - - reportCCIP, reportOnRamp, reportMockLinkToken, reportMockEthToken, reportMCMs := DeployCCIPAndOnrampAndTokens(t, localChainSelector, destChainSelector, keystoreInstance, signerAddr, bundle, deps, lggr) - - ethTokenType := fmt.Sprintf("%s::mock_eth_token::MOCK_ETH_TOKEN", reportMockEthToken.Output.PackageId) - ethCoins := GetEthCoins(t, client, signer, reportMockEthToken.Output.PackageId, reportMockEthToken.Output.Objects.TreasuryCapObjectId, ethTokenType, accountAddress, lggr, 1000000, 1000000) - - ethereumPoolAddressString := EvmReceiverAddress - remoteTokenAddressString := EvmReceiverAddress - - // Setup managed token pool for ETH token - managedTokenPoolReport, managedTokenReport := SetupManagedTokenPool( - t, - deps, - reportCCIP, - reportMCMs, - reportMockEthToken, - signerAddr, - accountAddress, - ethTokenType, - ethereumPoolAddressString, - remoteTokenAddressString, - destChainSelector, - bundle, - client, - lggr, - ) - - UpdatePrices(t, reportCCIP, reportMockLinkToken, deps, bundle, destChainSelector, lggr) - - return &EnvironmentSettings{ - AccountAddress: accountAddress, - MockLinkReport: reportMockLinkToken, - MockEthTokenReport: reportMockEthToken, - CCIPReport: reportCCIP, - OnRampReport: reportOnRamp, - ManagedTokenPoolReport: managedTokenPoolReport, - ManagedTokenReport: managedTokenReport, - EthCoins: ethCoins, - Signer: signer, - Client: client, - } -} - -// GetLinkCoins mints LINK tokens for testing CCIP operations. -// Returns two coin IDs: one for token transfer and one for fee payment. -func GetLinkCoins(t *testing.T, envSettings *EnvironmentSettings, linkTokenType string, accountAddress string, lggr logger.Logger, tokenAmount uint64, feeAmount uint64) (string, string) { - t.Helper() - - // Mint LINK tokens for the CCIP send operation - // We need two separate coins: one for the token transfer and one for the fee payment - - // Use the setup account to mint tokens (since it owns the TreasuryCapObjectId) - // but then transfer them to the transaction account - deps := sui_ops.OpTxDeps{ - Client: envSettings.Client, - Signer: envSettings.Signer, - GetCallOpts: func() *bind.CallOpts { - b := uint64(1_000_000_000) - return &bind.CallOpts{ - Signer: envSettings.Signer, - WaitForExecution: true, - GasBudget: &b, - } - }, - } - - // Create LINK token contract instance - linkContract, err := mocklinktoken.NewMockLinkToken(envSettings.MockLinkReport.Output.PackageId, envSettings.Client) - require.NoError(t, err, "failed to create LINK token contract") - - // Use MintAndTransfer to mint directly to the transaction account - // This avoids the ownership issue by minting directly to the account that will use the coins - - // Mint first coin for token transfer directly to transaction account - mintTx1, err := linkContract.MockLinkToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: envSettings.MockLinkReport.Output.Objects.TreasuryCapObjectId}, - tokenAmount, - accountAddress, // Mint directly to transaction account - ) - require.NoError(t, err, "failed to mint and transfer LINK tokens for transfer") - - lggr.Debugw("Minted and transferred LINK tokens for transfer", "amount", tokenAmount, "txDigest", mintTx1.Digest, "recipient", accountAddress) - - // Find the first minted coin object ID from the transaction - mintedCoinId1, err := bind.FindCoinObjectIdFromTx(*mintTx1, linkTokenType) - require.NoError(t, err, "failed to find first minted coin object ID") - lggr.Infow("First mintedCoinId", "coin", mintedCoinId1) - - // Mint second coin for fee payment directly to transaction account - mintTx2, err := linkContract.MockLinkToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: envSettings.MockLinkReport.Output.Objects.TreasuryCapObjectId}, - feeAmount, - accountAddress, // Mint directly to transaction account - ) - require.NoError(t, err, "failed to mint and transfer LINK tokens for fee") - - lggr.Debugw("Minted and transferred LINK tokens for fee", "amount", feeAmount, "txDigest", mintTx2.Digest, "recipient", accountAddress) - - // Find the second minted coin object ID from the transaction - mintedCoinId2, err := bind.FindCoinObjectIdFromTx(*mintTx2, linkTokenType) - require.NoError(t, err, "failed to find second minted coin object ID") - lggr.Infow("Second mintedCoinId", "coin", mintedCoinId2) - - return mintedCoinId1, mintedCoinId2 -} - -// GetEthCoins mints ETH tokens for testing CCIP operations. -// Returns an array of coin IDs for use in testing. -func GetEthCoins(t *testing.T, client sui.ISuiAPI, signer rel.SuiSigner, ethTokenPackageId string, treasuryCapObjectId string, ethTokenType string, accountAddress string, lggr logger.Logger, tokenAmount uint64, feeAmount uint64) []string { - t.Helper() - - // Mint ETH tokens for the CCIP send operation - // We need two separate coins: one for the token transfer and one for the fee payment - - // Use the setup account to mint tokens (since it owns the TreasuryCapObjectId) - // but then transfer them to the transaction account - deps := sui_ops.OpTxDeps{ - Client: client, - Signer: signer, - GetCallOpts: func() *bind.CallOpts { - b := uint64(1_000_000_000) - return &bind.CallOpts{ - Signer: signer, - WaitForExecution: true, - GasBudget: &b, - } - }, - } - - // Create ETH token contract instance - ethContract, err := mockethtoken.NewMockEthToken(ethTokenPackageId, client) - require.NoError(t, err, "failed to create ETH token contract") - - // Use MintAndTransfer to mint directly to the transaction account - // This avoids the ownership issue by minting directly to the account that will use the coins - - // Mint first coin for token transfer directly to transaction account - mintTx1, err := ethContract.MockEthToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: treasuryCapObjectId}, - tokenAmount, - accountAddress, // Mint directly to transaction account - ) - require.NoError(t, err, "failed to mint and transfer ETH tokens for transfer") - - lggr.Debugw("Minted and transferred ETH tokens for transfer", "amount", tokenAmount, "txDigest", mintTx1.Digest, "recipient", accountAddress) - - // Find the first minted coin object ID from the transaction - mintedCoinId1, err := bind.FindCoinObjectIdFromTx(*mintTx1, ethTokenType) - require.NoError(t, err, "failed to find first minted coin object ID") - lggr.Infow("First ETH mintedCoinId", "coin", mintedCoinId1) - - // Mint second coin for fee payment directly to transaction account - mintTx2, err := ethContract.MockEthToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: treasuryCapObjectId}, - feeAmount, - accountAddress, // Mint directly to transaction account - ) - require.NoError(t, err, "failed to mint and transfer ETH tokens for fee") - - lggr.Debugw("Minted and transferred ETH tokens for fee", "amount", feeAmount, "txDigest", mintTx2.Digest, "recipient", accountAddress) - - // Find the second minted coin object ID from the transaction - mintedCoinId2, err := bind.FindCoinObjectIdFromTx(*mintTx2, ethTokenType) - require.NoError(t, err, "failed to find second minted coin object ID") - lggr.Infow("Second ETH mintedCoinId", "coin", mintedCoinId2) - - return []string{mintedCoinId1, mintedCoinId2} -} - -// NormalizeTo32Bytes converts an address string to a 32-byte representation. -// This is used for converting Ethereum addresses to the format expected by Sui contracts. -func NormalizeTo32Bytes(address string) []byte { - addressHex := address - if strings.HasPrefix(address, "0x") { - addressHex = address[2:] - } - addressBytesFull, _ := hex.DecodeString(addressHex) - addressBytes := addressBytesFull - if len(addressBytesFull) > 32 { - addressBytes = addressBytesFull[len(addressBytesFull)-32:] - } else if len(addressBytesFull) < 32 { - // pad left with zeros - padding := make([]byte, 32-len(addressBytesFull)) - addressBytes = append(padding, addressBytesFull...) - } - return addressBytes -} diff --git a/integration-tests/onramp/environment/token_pools.go b/integration-tests/onramp/environment/token_pools.go deleted file mode 100644 index 3f9c939d9..000000000 --- a/integration-tests/onramp/environment/token_pools.go +++ /dev/null @@ -1,505 +0,0 @@ -package environment - -import ( - "context" - "testing" - - "github.com/block-vision/sui-go-sdk/models" - "github.com/block-vision/sui-go-sdk/sui" - "github.com/smartcontractkit/chainlink-common/pkg/logger" - cld_ops "github.com/smartcontractkit/chainlink-deployments-framework/operations" - "github.com/stretchr/testify/require" - - "github.com/smartcontractkit/chainlink-sui/bindings/bind" - managedtoken "github.com/smartcontractkit/chainlink-sui/bindings/packages/managed_token" - mockethtoken "github.com/smartcontractkit/chainlink-sui/bindings/packages/mock_eth_token" - mocklinktoken "github.com/smartcontractkit/chainlink-sui/bindings/packages/mock_link_token" - sui_ops "github.com/smartcontractkit/chainlink-sui/deployment/ops" - ccipops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip" - burnmintops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip_burn_mint_token_pool" - lockreleaseops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip_lock_release_token_pool" - managedtokenpoolops "github.com/smartcontractkit/chainlink-sui/deployment/ops/ccip_managed_token_pool" - managedtokenops "github.com/smartcontractkit/chainlink-sui/deployment/ops/managed_token" - mcmsops "github.com/smartcontractkit/chainlink-sui/deployment/ops/mcms" - mockethtokenops "github.com/smartcontractkit/chainlink-sui/deployment/ops/mock_eth_token" - mocklinktokenops "github.com/smartcontractkit/chainlink-sui/deployment/ops/mock_link_token" - rel "github.com/smartcontractkit/chainlink-sui/relayer/signer" -) - -// SetupEthTokenPoolBurnMint sets up a burn/mint token pool for ETH tokens. -// This type of pool mints/burns tokens on demand rather than locking them. -func SetupEthTokenPoolBurnMint( - t *testing.T, - deps sui_ops.OpTxDeps, - reportCCIP *cld_ops.SequenceReport[ccipops.DeployAndInitCCIPSeqInput, ccipops.DeployCCIPSeqOutput], - reportMCMs *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mcmsops.DeployMCMSObjects]], - reportMockEthToken *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mockethtokenops.DeployMockEthTokenObjects]], - signerAddr string, - accountAddress string, - tokenType string, - ethereumPoolAddressString string, - remoteTokenAddressString string, - destChainSelector uint64, - bundle cld_ops.Bundle, - client sui.ISuiAPI, - lggr logger.Logger, -) *cld_ops.SequenceReport[burnmintops.DeployAndInitBurnMintTokenPoolInput, burnmintops.DeployBurnMintTokenPoolOutput] { - t.Helper() - - lggr.Debugw("Setting up ETH burn-mint token pool") - - // Deploy and initialize the burn mint token pool - seqBurnMintDeployInput := burnmintops.DeployAndInitBurnMintTokenPoolInput{ - BurnMintTokenPoolDeployInput: burnmintops.BurnMintTokenPoolDeployInput{ - CCIPPackageId: reportCCIP.Output.CCIPPackageId, - MCMSAddress: reportMCMs.Output.PackageId, - MCMSOwnerAddress: accountAddress, - }, - // Initialization parameters - CoinObjectTypeArg: tokenType, - CCIPObjectRefObjectId: reportCCIP.Output.Objects.CCIPObjectRefObjectId, - CoinMetadataObjectId: reportMockEthToken.Output.Objects.CoinMetadataObjectId, - TreasuryCapObjectId: reportMockEthToken.Output.Objects.TreasuryCapObjectId, - TokenPoolAdministrator: accountAddress, - - // Chain updates - adding the destination chain - RemoteChainSelectorsToRemove: []uint64{}, - RemoteChainSelectorsToAdd: []uint64{destChainSelector}, // Destination chain selector - RemotePoolAddressesToAdd: [][]string{{ethereumPoolAddressString}}, // 32-byte remote pool address - RemoteTokenAddressesToAdd: []string{remoteTokenAddressString}, // 32-byte remote token address - // Rate limiter configurations - RemoteChainSelectors: []uint64{destChainSelector}, // Destination chain selector - OutboundIsEnableds: []bool{false}, - OutboundCapacities: []uint64{1000000}, // 1M tokens capacity - OutboundRates: []uint64{100000}, // 100K tokens per time window - InboundIsEnableds: []bool{false}, - InboundCapacities: []uint64{1000000}, // 1M tokens capacity - InboundRates: []uint64{100000}, // 100K tokens per time window - } - - tokenPoolBurnMintReport, err := cld_ops.ExecuteSequence(bundle, burnmintops.DeployAndInitBurnMintTokenPoolSequence, deps, seqBurnMintDeployInput) - require.NoError(t, err, "failed to deploy and initialize Burn Mint Token Pool") - - lggr.Debugw("ETH Token Pool Burn Mint deployment report", "output", tokenPoolBurnMintReport.Output) - - // Note: Burn mint pools don't need liquidity provision like lock-release pools - // because they mint/burn tokens on demand rather than locking them - - return &tokenPoolBurnMintReport -} - -// SetupManagedTokenPool sets up a managed token pool with associated managed token. -// This includes deploying both the managed token and the managed token pool. -func SetupManagedTokenPool( - t *testing.T, - deps sui_ops.OpTxDeps, - reportCCIP *cld_ops.SequenceReport[ccipops.DeployAndInitCCIPSeqInput, ccipops.DeployCCIPSeqOutput], - reportMCMs *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mcmsops.DeployMCMSObjects]], - reportMockEthToken *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mockethtokenops.DeployMockEthTokenObjects]], - signerAddr string, - accountAddress string, - tokenType string, - ethereumPoolAddressString string, - remoteTokenAddressString string, - destChainSelector uint64, - bundle cld_ops.Bundle, - client sui.ISuiAPI, - lggr logger.Logger, -) ( - *cld_ops.SequenceReport[managedtokenpoolops.DeployAndInitManagedTokenPoolInput, managedtokenpoolops.DeployManagedTokenPoolOutput], - *cld_ops.SequenceReport[managedtokenops.DeployAndInitManagedTokenInput, managedtokenops.DeployManagedTokenOutput], -) { - t.Helper() - - lggr.Debugw("Setting up managed token pool") - - // Deploy and initialize the managed token first - seqManagedTokenDeployInput := managedtokenops.DeployAndInitManagedTokenInput{ - ManagedTokenDeployInput: managedtokenops.ManagedTokenDeployInput{ - MCMSAddress: reportMCMs.Output.PackageId, - MCMSOwnerAddress: accountAddress, - }, - // Initialization parameters - CoinObjectTypeArg: tokenType, - TreasuryCapObjectId: reportMockEthToken.Output.Objects.TreasuryCapObjectId, - DenyCapObjectId: "", // Optional - not using deny cap for this example - // Configure a new minter - MinterAddress: signerAddr, - Allowance: 1000000, // 1M tokens allowance - IsUnlimited: false, - } - - managedTokenReport, err := cld_ops.ExecuteSequence(bundle, managedtokenops.DeployAndInitManagedTokenSequence, deps, seqManagedTokenDeployInput) - require.NoError(t, err, "failed to deploy and initialize Managed Token") - - lggr.Debugw("Managed Token deployment report", "output", managedTokenReport.Output) - - mintCapObjectId := configureNewMinter( - t, - client, - deps.Signer, - managedTokenReport.Output.ManagedTokenPackageId, - tokenType, - managedTokenReport.Output.Objects.StateObjectId, - managedTokenReport.Output.Objects.OwnerCapObjectId, - signerAddr, - 0, - true, - lggr, - ) - - // Now deploy and initialize the managed token pool - seqManagedTokenPoolDeployInput := managedtokenpoolops.DeployAndInitManagedTokenPoolInput{ - // Deploy inputs - CCIPPackageId: reportCCIP.Output.CCIPPackageId, - ManagedTokenPackageId: managedTokenReport.Output.ManagedTokenPackageId, - MCMSAddress: reportMCMs.Output.PackageId, - MCMSOwnerAddress: accountAddress, - // Initialize inputs - CoinObjectTypeArg: tokenType, - CCIPObjectRefObjectId: reportCCIP.Output.Objects.CCIPObjectRefObjectId, - ManagedTokenStateObjectId: managedTokenReport.Output.Objects.StateObjectId, - ManagedTokenOwnerCapId: managedTokenReport.Output.Objects.OwnerCapObjectId, - CoinMetadataObjectId: reportMockEthToken.Output.Objects.CoinMetadataObjectId, - MintCapObjectId: mintCapObjectId, - TokenPoolAdministrator: accountAddress, - // Chain updates - adding the destination chain - RemoteChainSelectorsToRemove: []uint64{}, - RemoteChainSelectorsToAdd: []uint64{destChainSelector}, // Destination chain selector - RemotePoolAddressesToAdd: [][]string{{ethereumPoolAddressString}}, // 32-byte remote pool address - RemoteTokenAddressesToAdd: []string{remoteTokenAddressString}, // 32-byte remote token address - // Rate limiter configurations - RemoteChainSelectors: []uint64{destChainSelector}, // Destination chain selector - OutboundIsEnableds: []bool{false}, - OutboundCapacities: []uint64{1000000}, // 1M tokens capacity - OutboundRates: []uint64{100000}, // 100K tokens per time window - InboundIsEnableds: []bool{false}, - InboundCapacities: []uint64{1000000}, // 1M tokens capacity - InboundRates: []uint64{100000}, // 100K tokens per time window - } - - managedTokenPoolReport, err := cld_ops.ExecuteSequence(bundle, managedtokenpoolops.DeployAndInitManagedTokenPoolSequence, deps, seqManagedTokenPoolDeployInput) - require.NoError(t, err, "failed to deploy and initialize Managed Token Pool") - - lggr.Debugw("Managed Token Pool deployment report", "output", managedTokenPoolReport.Output) - - return &managedTokenPoolReport, &managedTokenReport -} - -// ConfigureManagedTokenMinter configures a new minter for a managed token contract. -// This function calls the configure_new_minter operation on the managed token, -// which allows the specified address to mint tokens up to the given allowance. -// -// Parameters: -// - managedTokenPackageId: The package ID of the deployed managed token -// - tokenType: The fully qualified token type (e.g., "package_id::token::TOKEN_TYPE") -// - stateObjectId: The state object ID of the managed token -// - ownerCapObjectId: The owner capability object ID for authorization -// - minterAddress: The address that will be granted minting permissions -// - allowance: Maximum number of tokens this minter can mint -// - isUnlimited: If true, the minter has unlimited minting capability -func ConfigureManagedTokenMinter( - t *testing.T, - deps sui_ops.OpTxDeps, - managedTokenPackageId string, - tokenType string, - stateObjectId string, - ownerCapObjectId string, - minterAddress string, - allowance uint64, - isUnlimited bool, - bundle cld_ops.Bundle, - lggr logger.Logger, -) { - t.Helper() - - lggr.Debugw("Configuring managed token minter", - "packageId", managedTokenPackageId, - "minterAddress", minterAddress, - "allowance", allowance, - "isUnlimited", isUnlimited) - - configureInput := managedtokenops.ManagedTokenConfigureNewMinterInput{ - ManagedTokenPackageId: managedTokenPackageId, - CoinObjectTypeArg: tokenType, - StateObjectId: stateObjectId, - OwnerCapObjectId: ownerCapObjectId, - MinterAddress: minterAddress, - Allowance: allowance, - IsUnlimited: isUnlimited, - } - - _, err := cld_ops.ExecuteOperation(bundle, managedtokenops.ManagedTokenConfigureNewMinterOp, deps, configureInput) - require.NoError(t, err, "failed to configure new minter for managed token") - - lggr.Debugw("Successfully configured managed token minter") -} - -// configureNewMinter configures a new minter for a managed token. -// This function follows the pattern of getEthCoins/getLinkCoins by handling all the complexity internally. -// It configures minting permissions for the specified address and returns the mint cap object ID. -// -// Returns the mint cap object ID that was transferred to the minter address. -func configureNewMinter( - t *testing.T, - client sui.ISuiAPI, - signer rel.SuiSigner, - managedTokenPackageId string, - tokenType string, - stateObjectId string, - ownerCapObjectId string, - minterAddress string, - allowance uint64, - isUnlimited bool, - lggr logger.Logger, -) string { - t.Helper() - - lggr.Debugw("Configuring managed token minter", - "packageId", managedTokenPackageId, - "minterAddress", minterAddress, - "allowance", allowance, - "isUnlimited", isUnlimited) - - // Set up dependencies locally - deps := sui_ops.OpTxDeps{ - Client: client, - Signer: signer, - GetCallOpts: func() *bind.CallOpts { - b := uint64(1_000_000_000) - return &bind.CallOpts{ - Signer: signer, - WaitForExecution: true, - GasBudget: &b, - } - }, - } - - // Create managed token contract instance - managedTokenContract, err := managedtoken.NewCCIPManagedToken(managedTokenPackageId, client) - require.NoError(t, err, "failed to create managed token contract") - - // Call ConfigureNewMinter directly on the contract - configureTx, err := managedTokenContract.ManagedToken().ConfigureNewMinter( - context.Background(), - deps.GetCallOpts(), - []string{tokenType}, - bind.Object{Id: stateObjectId}, - bind.Object{Id: ownerCapObjectId}, - minterAddress, - allowance, - isUnlimited, - ) - require.NoError(t, err, "failed to configure new minter for managed token") - - // Find the mint cap object ID that was transferred to the minter - mintCapObjectId, err := bind.FindObjectIdFromPublishTx(*configureTx, "managed_token", "MintCap") - require.NoError(t, err, "failed to find mint cap object ID from configure minter transaction") - - lggr.Infow("Successfully configured managed token minter", - "minter", minterAddress, - "allowance", allowance, - "isUnlimited", isUnlimited, - "mintCapObjectId", mintCapObjectId) - - return mintCapObjectId -} - -// SetupTokenPool sets up a lock/release token pool for LINK tokens. -// This type of pool locks tokens when sending and releases them when receiving. -func SetupTokenPool( - t *testing.T, - deps sui_ops.OpTxDeps, - reportCCIP *cld_ops.SequenceReport[ccipops.DeployAndInitCCIPSeqInput, ccipops.DeployCCIPSeqOutput], - reportMCMs *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mcmsops.DeployMCMSObjects]], - mockLinkReport *cld_ops.Report[cld_ops.EmptyInput, sui_ops.OpTxResult[mocklinktokenops.DeployMockLinkTokenObjects]], - signerAddr string, - accountAddress string, - linkTokenType string, - ethereumPoolAddressString string, - remoteTokenAddressString string, - destChainSelector uint64, - bundle cld_ops.Bundle, - client sui.ISuiAPI, - lggr logger.Logger, -) *cld_ops.SequenceReport[lockreleaseops.DeployAndInitLockReleaseTokenPoolInput, lockreleaseops.DeployLockReleaseTokenPoolOutput] { - t.Helper() - - lggr.Debugw("Setting up token pool") - // Create a context for the operation - c := context.Background() - ctx, cancel := context.WithCancel(c) - defer cancel() - - // Deploy and initialize the lock release token pool - seqLockReleaseDeployInput := lockreleaseops.DeployAndInitLockReleaseTokenPoolInput{ - LockReleaseTokenPoolDeployInput: lockreleaseops.LockReleaseTokenPoolDeployInput{ - CCIPPackageId: reportCCIP.Output.CCIPPackageId, - MCMSAddress: reportMCMs.Output.PackageId, - MCMSOwnerAddress: accountAddress, - }, - // Initialization parameters - CoinObjectTypeArg: linkTokenType, - CCIPObjectRefObjectId: reportCCIP.Output.Objects.CCIPObjectRefObjectId, - CoinMetadataObjectId: mockLinkReport.Output.Objects.CoinMetadataObjectId, - TreasuryCapObjectId: mockLinkReport.Output.Objects.TreasuryCapObjectId, - TokenPoolAdministrator: accountAddress, - Rebalancer: signerAddr, - - // Chain updates - adding the destination chain - RemoteChainSelectorsToRemove: []uint64{}, - RemoteChainSelectorsToAdd: []uint64{destChainSelector}, // Destination chain selector - RemotePoolAddressesToAdd: [][]string{{ethereumPoolAddressString}}, // 32-byte remote pool address - RemoteTokenAddressesToAdd: []string{remoteTokenAddressString}, // 32-byte remote token address - // Rate limiter configurations - RemoteChainSelectors: []uint64{destChainSelector}, // Destination chain selector - OutboundIsEnableds: []bool{false}, - OutboundCapacities: []uint64{1000000}, // 1M tokens capacity - OutboundRates: []uint64{100000}, // 100K tokens per time window - InboundIsEnableds: []bool{false}, - InboundCapacities: []uint64{1000000}, // 1M tokens capacity - InboundRates: []uint64{100000}, // 100K tokens per time window - } - - tokenPoolLockReleaseReport, err := cld_ops.ExecuteSequence(bundle, lockreleaseops.DeployAndInitLockReleaseTokenPoolSequence, deps, seqLockReleaseDeployInput) - require.NoError(t, err, "failed to deploy and initialize Lock Release Token Pool") - - lggr.Debugw("Token Pool Lock Release deployment report", "output", tokenPoolLockReleaseReport.Output) - - // Provide liquidity to the lock release token pool - // First, mint some LINK tokens using the LINK token contract - liquidityAmount := uint64(1000000) // 1M tokens for liquidity - - // Create LINK token contract instance - linkContract, err := mocklinktoken.NewMockLinkToken(mockLinkReport.Output.PackageId, client) - require.NoError(t, err, "failed to create LINK token contract") - - // Mint LINK tokens to the signer's address - mintTx, err := linkContract.MockLinkToken().Mint( - ctx, - deps.GetCallOpts(), - bind.Object{Id: mockLinkReport.Output.Objects.TreasuryCapObjectId}, - liquidityAmount, - ) - require.NoError(t, err, "failed to mint LINK tokens for liquidity") - - lggr.Debugw("Minted LINK tokens for liquidity", "amount", liquidityAmount, "txDigest", mintTx.Digest) - - // Find the minted coin object ID from the transaction - mintedCoinId, err := bind.FindCoinObjectIdFromTx(*mintTx, linkTokenType) - require.NoError(t, err, "failed to find minted coin object ID") - - lggr.Debugw("Minted coin ID", "mintedCoinId", mintedCoinId) - - // Provide the minted tokens as liquidity to the pool - provideLiquidityInput := lockreleaseops.LockReleaseTokenPoolProvideLiquidityInput{ - LockReleaseTokenPoolPackageId: tokenPoolLockReleaseReport.Output.LockReleaseTPPackageID, - StateObjectId: tokenPoolLockReleaseReport.Output.Objects.StateObjectId, - RebalancerCapObjectId: tokenPoolLockReleaseReport.Output.Objects.RebalancerCapObjectId, - Coin: mintedCoinId, - CoinObjectTypeArg: linkTokenType, - } - - _, err = cld_ops.ExecuteOperation(bundle, lockreleaseops.LockReleaseTokenPoolProvideLiquidityOp, deps, provideLiquidityInput) - require.NoError(t, err, "failed to provide liquidity to Lock Release Token Pool") - - lggr.Debugw("Provided liquidity to Lock Release Token Pool", "amount", liquidityAmount) - - return &tokenPoolLockReleaseReport -} - -// MintTestTokens mints tokens for testing purposes. -// This is a helper function to mint both transfer and fee tokens. -func MintTestTokens( - t *testing.T, - client sui.ISuiAPI, - signer rel.SuiSigner, - packageId, treasuryCapId, tokenType, recipient string, - transferAmount, feeAmount uint64, - lggr logger.Logger, -) (transferCoin, feeCoin string) { - t.Helper() - - deps := sui_ops.OpTxDeps{ - Client: client, - Signer: signer, - GetCallOpts: func() *bind.CallOpts { - b := uint64(1_000_000_000) - return &bind.CallOpts{ - Signer: signer, - WaitForExecution: true, - GasBudget: &b, - } - }, - } - - // Determine contract type and create appropriate instance - var mintTransferTx, mintFeeTx *models.SuiTransactionBlockResponse - var err error - - // Check if this is a LINK token or ETH token based on the package structure - if packageId != "" { - // Try LINK token first - if linkContract, linkErr := mocklinktoken.NewMockLinkToken(packageId, client); linkErr == nil { - // Mint transfer token - mintTransferTx, err = linkContract.MockLinkToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: treasuryCapId}, - transferAmount, - recipient, - ) - require.NoError(t, err, "failed to mint and transfer LINK tokens for transfer") - - // Mint fee token - mintFeeTx, err = linkContract.MockLinkToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: treasuryCapId}, - feeAmount, - recipient, - ) - require.NoError(t, err, "failed to mint and transfer LINK tokens for fee") - } else { - // Try ETH token - ethContract, ethErr := mockethtoken.NewMockEthToken(packageId, client) - require.NoError(t, ethErr, "failed to create token contract") - - // Mint transfer token - mintTransferTx, err = ethContract.MockEthToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: treasuryCapId}, - transferAmount, - recipient, - ) - require.NoError(t, err, "failed to mint and transfer ETH tokens for transfer") - - // Mint fee token - mintFeeTx, err = ethContract.MockEthToken().MintAndTransfer( - context.Background(), - deps.GetCallOpts(), - bind.Object{Id: treasuryCapId}, - feeAmount, - recipient, - ) - require.NoError(t, err, "failed to mint and transfer ETH tokens for fee") - } - } - - // Find coin object IDs from transactions - transferCoinId, err := bind.FindCoinObjectIdFromTx(*mintTransferTx, tokenType) - require.NoError(t, err, "failed to find transfer coin object ID") - - feeCoinId, err := bind.FindCoinObjectIdFromTx(*mintFeeTx, tokenType) - require.NoError(t, err, "failed to find fee coin object ID") - - lggr.Infow("Successfully minted test tokens", - "transferCoin", transferCoinId, - "feeCoin", feeCoinId, - "transferAmount", transferAmount, - "feeAmount", feeAmount, - "recipient", recipient) - - return transferCoinId, feeCoinId -} diff --git a/relayer/chainwriter/ptb/offramp/execute.go b/relayer/chainwriter/ptb/offramp/execute.go index cfa0987db..99e405f72 100644 --- a/relayer/chainwriter/ptb/offramp/execute.go +++ b/relayer/chainwriter/ptb/offramp/execute.go @@ -7,7 +7,9 @@ import ( "bytes" "context" "encoding/hex" + "errors" "fmt" + "math/big" "strings" "github.com/block-vision/sui-go-sdk/models" @@ -298,8 +300,11 @@ func AppendPTBCommandForTokenPool( return nil, fmt.Errorf("missing function signature for token pool function not found in module (%s)", OfframpTokenPoolFunctionName) } - // Figure out the parameter types from the normalized module of the token pool - paramTypes, err := DecodeParameters(lggr, functionSignature.(map[string]any), "parameters") + funcMap, ok := functionSignature.(map[string]any) + if !ok || funcMap == nil { + return nil, fmt.Errorf("invalid function signature shape for %q (%T)", OfframpTokenPoolFunctionName, functionSignature) + } + paramTypes, err := DecodeParameters(lggr, funcMap, "parameters") if err != nil { return nil, fmt.Errorf("failed to decode parameters for token pool function: %w", err) } @@ -345,40 +350,47 @@ func ProcessReceivers( receiverRegistryDevInspect := receiverRegistryPkg.DevInspect() receiverCommandsResults := make([]transaction.Argument, 0) - // Generate receiver call commands for _, message := range messages { - // If there is no receiver, skip this message if len(message.Receiver) == 0 || message.Receiver == nil { lggr.Errorw("unexpected nil or zero length receiver, skipping message in offramp execution...", "message", message) continue } - // Check if receiver is a zero address (0x0....0 // 32 bytes of 0) if bytes.Equal(message.Receiver, codec.AccountZero) { lggr.Debugw("receiver is zero address, skipping message in offramp execution...", "message", message) continue } - // Parse the receiver address into a hex string + // Mirror on-chain gating: skip receiver call when on-chain would not populate the message. + // On-chain: has_valid_message_receiver = (!data.is_empty() || gas_limit != 0) && is_registered_receiver + if !needsAppDelivery(message, extraArgs) { + lggr.Debugw("message has no data and zero gas limit, skipping receiver call", + "receiver", hex.EncodeToString(message.Receiver)) + continue + } + receiverPackageId := "0x" + hex.EncodeToString(message.Receiver) isRegistered, err := receiverRegistryDevInspect.IsRegisteredReceiver(ctx, callOpts, bind.Object{Id: addressMappings.CcipObjectRef}, receiverPackageId) if err != nil { return nil, fmt.Errorf("failed to check if receiver is registered in offramp execution: %w", err) } - // If the receiver is not registered, fail the entire execution if !isRegistered { - return nil, fmt.Errorf("receiver is not registered in offramp execution: %s", message.Receiver) + lggr.Warnw("receiver not registered, skipping receiver call (on-chain will not populate message)", + "receiver", receiverPackageId) + continue } receiverConfig, err := receiverRegistryDevInspect.GetReceiverConfig(ctx, callOpts, bind.Object{Id: addressMappings.CcipObjectRef}, receiverPackageId) if err != nil { + // RPC/network error — propagate so the caller can retry later. return nil, fmt.Errorf("failed to get receiver config in offramp execution: %w", err) } receiverNormalizedModule, err := ptbClient.GetNormalizedModule(ctx, receiverPackageId, receiverConfig.ModuleName) if err != nil { - return nil, fmt.Errorf("failed to get normalized module for token pool: %w", err) + // RPC/network error — propagate so the caller can retry later. + return nil, fmt.Errorf("failed to get normalized module for receiver: %w", err) } receiverCommandResult, err := AppendPTBCommandForReceiver( @@ -397,7 +409,21 @@ func ProcessReceivers( extraArgs, ) if err != nil { - return nil, err + if errors.Is(err, ErrUnsupportedReceiverABI) { + // Permanent failure: the receiver's on-chain ABI uses shapes the relayer + // cannot handle (e.g. TypeParameter/generics, missing ccip_receive). + // Skip the receiver leg so the PTB is submitted without it. + // On-chain, populate_message will have set ReceiverParams.message to Some, + // so finish_execute → deconstruct_receiver_params will abort with + // ECCIPReceiveFailed. The message remains UNTOUCHED (atomic rollback) + // and available for manually_init_execute once the receiver is fixed + // or unregistered. + lggr.Errorw("skipping receiver command due to unsupported ABI; PTB will fail on-chain", + "receiver", receiverPackageId, + "error", err) + continue + } + return nil, fmt.Errorf("failed to build receiver command for %s: %w", receiverPackageId, err) } receiverCommandsResults = append(receiverCommandsResults, *receiverCommandResult) } @@ -405,6 +431,22 @@ func ProcessReceivers( return receiverCommandsResults, nil } +//nolint:staticcheck // ccipocr3.Message is a deprecated alias; matches ExecuteReport.Messages until ccipocr3common migration. +func needsAppDelivery(message ccipocr3.Message, extraArgs map[string]any) bool { + if len(message.Data) > 0 { + return true + } + if val, ok := extraArgs["gasLimit"]; ok { + switch gl := val.(type) { + case *big.Int: + return gl != nil && gl.Sign() > 0 + case uint64: + return gl > 0 + } + } + return false +} + func AppendPTBCommandForReceiver( ctx context.Context, lggr logger.Logger, @@ -474,41 +516,21 @@ func AppendPTBCommandForReceiver( // Use the normalized module to populate the paramTypes and paramValues for the bound contract functionSignature, ok := normalizedModule.ExposedFunctions[functionName] if !ok { - return nil, fmt.Errorf("missing function signature for receiver function not found in module (%s)", functionName) + return nil, fmt.Errorf("%w: function %q not found in module", ErrUnsupportedReceiverABI, functionName) } - // Figure out the parameter types from the normalized module of the token pool - paramTypes, err = DecodeParameters(lggr, functionSignature.(map[string]any), "parameters") + funcMap, err := exposedFunctionSignature(functionName, functionSignature) if err != nil { - return nil, fmt.Errorf("failed to decode parameters for token pool function: %w", err) + return nil, err } - - lggr.Debugw("calling receiver", "paramTypes", paramTypes, "paramValues", paramValues) - - // Append extra args to the paramValues for the receiver call. - receiverObjectIds, ok := extraArgs["receiverObjectIds"] - if !ok { - return nil, fmt.Errorf("missing extra args for receiver function not found in module (%s)", functionName) + paramTypes, err = DecodeParameters(lggr, funcMap, "parameters") + if err != nil { + return nil, fmt.Errorf("%w: failed to decode receiver parameters: %w", ErrUnsupportedReceiverABI, err) } - // note: we cannot expect receiverObjectIds to be [][]byte, so check for []any type - var extraArgsValues [][]byte - switch vals := receiverObjectIds.(type) { - case [][]byte: - extraArgsValues = vals - case []any: - for _, v := range vals { - b, ok := v.([]byte) - if !ok { - lggr.Error("unexpected element type in receiverObjectIds", "type", fmt.Sprintf("%T", v)) - continue - } - extraArgsValues = append(extraArgsValues, b) - } - default: - lggr.Error("unexpected receiverObjectIds type", "type", fmt.Sprintf("%T", receiverObjectIds)) - } + lggr.Debugw("calling receiver", "paramTypes", paramTypes, "paramValues", paramValues) + extraArgsValues := extractReceiverObjectIDs(lggr, extraArgs) for _, value := range extraArgsValues { objectId := hex.EncodeToString(value) paramValues = append(paramValues, bind.Object{Id: "0x" + objectId}) @@ -533,3 +555,32 @@ func AppendPTBCommandForReceiver( return receiverCommandResult, nil } + +// extractReceiverObjectIDs extracts receiver object IDs from extraArgs, +// handling missing keys, nil values, and both [][]byte and []any representations. +func extractReceiverObjectIDs(lggr logger.Logger, extraArgs map[string]any) [][]byte { + raw, ok := extraArgs["receiverObjectIds"] + if !ok || raw == nil { + lggr.Warnw("receiverObjectIds not present in extraArgs, defaulting to empty") + return nil + } + + switch vals := raw.(type) { + case [][]byte: + return vals + case []any: + var out [][]byte + for _, v := range vals { + b, ok := v.([]byte) + if !ok { + lggr.Errorw("unexpected element type in receiverObjectIds", "type", fmt.Sprintf("%T", v)) + continue + } + out = append(out, b) + } + return out + default: + lggr.Errorw("unexpected receiverObjectIds type", "type", fmt.Sprintf("%T", raw)) + return nil + } +} diff --git a/relayer/chainwriter/ptb/offramp/execute_test.go b/relayer/chainwriter/ptb/offramp/execute_test.go new file mode 100644 index 000000000..86daa2a2a --- /dev/null +++ b/relayer/chainwriter/ptb/offramp/execute_test.go @@ -0,0 +1,484 @@ +//nolint:staticcheck,revive // ccipocr3.Message is a deprecated alias; method names match SuiPTBClient interface. +package offramp + +import ( + "context" + "math/big" + "testing" + + "github.com/block-vision/sui-go-sdk/models" + suirpcv2 "github.com/block-vision/sui-go-sdk/pb/sui/rpc/v2" + suisigner "github.com/block-vision/sui-go-sdk/signer" + "github.com/block-vision/sui-go-sdk/transaction" + "github.com/patrickmn/go-cache" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + "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" + "github.com/smartcontractkit/chainlink-sui/relayer/client" + "github.com/smartcontractkit/chainlink-sui/relayer/signer" +) + +// stubPTBClient is a minimal no-op implementation of client.SuiPTBClient +// for tests that exercise code paths before any RPC call is made. +type stubPTBClient struct{} + +var _ client.SuiPTBClient = (*stubPTBClient)(nil) + +func (s *stubPTBClient) MoveCall(context.Context, client.MoveCallRequest) (client.TxnMetaData, error) { + return client.TxnMetaData{}, nil +} +func (s *stubPTBClient) SendTransaction(context.Context, *suirpcv2.ExecuteTransactionRequest) (*suirpcv2.ExecuteTransactionResponse, error) { + return nil, nil +} +func (s *stubPTBClient) ReadOwnedObjects(context.Context, string, []byte) ([]*suirpcv2.Object, error) { + return nil, nil +} +func (s *stubPTBClient) ReadFilterOwnedObjectIds(context.Context, string, string, []byte) ([]*suirpcv2.Object, error) { + return nil, nil +} +func (s *stubPTBClient) ReadObjectId(context.Context, string) (*suirpcv2.Object, error) { + return nil, nil +} +func (s *stubPTBClient) ReadFunction(context.Context, string, string, string, []any, []string, []string) ([]any, error) { + return nil, nil +} +func (s *stubPTBClient) SimulatePTB(context.Context, []byte) ([]any, error) { + return nil, nil +} +func (s *stubPTBClient) SignAndSendTransaction(context.Context, string, []byte) (*suirpcv2.ExecuteTransactionResponse, error) { + return nil, nil +} +func (s *stubPTBClient) QueryEvents(context.Context, client.EventFilterByMoveEventModule, *uint, *client.EventId, *client.QuerySortOptions) (*models.PaginatedEventsResponse, error) { + return nil, nil +} +func (s *stubPTBClient) QueryTransactions(context.Context, string, *suirpcv2.Checkpoint, *uint64) ([]*suirpcv2.ExecutedTransaction, error) { + return nil, nil +} +func (s *stubPTBClient) GetTransactionStatus(context.Context, string) (client.TransactionResult, error) { + return client.TransactionResult{}, nil +} +func (s *stubPTBClient) GetCoinsByAddress(context.Context, string) ([]*suirpcv2.Object, error) { + return nil, nil +} +func (s *stubPTBClient) QueryCoinsByAddress(context.Context, string, string) ([]*suirpcv2.Object, error) { + return nil, nil +} +func (s *stubPTBClient) EstimateGas(context.Context, *transaction.Transaction) (uint64, error) { + return 0, nil +} +func (s *stubPTBClient) GetReferenceGasPrice(context.Context) (*big.Int, error) { + return big.NewInt(1000), nil +} +func (s *stubPTBClient) FinishPTBAndSend(context.Context, *suisigner.Signer, *transaction.Transaction, client.TransactionRequestType) (*suirpcv2.ExecuteTransactionResponse, error) { + return nil, nil +} +func (s *stubPTBClient) GetBlockById(context.Context, string) (*suirpcv2.Checkpoint, error) { + return nil, nil +} +func (s *stubPTBClient) BlockByDigest(context.Context, string) (*suirpcv2.Checkpoint, error) { + return nil, nil +} +func (s *stubPTBClient) GetLatestEpoch(context.Context) (*suirpcv2.Epoch, error) { + return nil, nil +} +func (s *stubPTBClient) GetLatestCheckpoint(context.Context) (*suirpcv2.Checkpoint, error) { + return nil, nil +} +func (s *stubPTBClient) GetCheckpointData(context.Context, uint64) (*client.CheckpointData, error) { + return nil, nil +} +func (s *stubPTBClient) GetNormalizedModule(context.Context, string, string) (models.GetNormalizedMoveModuleResponse, error) { + return models.GetNormalizedMoveModuleResponse{}, nil +} +func (s *stubPTBClient) GetSUIBalance(context.Context, string) (*suirpcv2.Balance, error) { + return nil, nil +} +func (s *stubPTBClient) LoadModulePackageIds(context.Context, string, string) ([]string, error) { + return nil, nil +} +func (s *stubPTBClient) GetLatestPackageId(_ context.Context, id, _ string) (string, error) { + return id, nil +} +func (s *stubPTBClient) GetCoinMetadata(context.Context, string) (models.CoinMetadataResponse, error) { + return models.CoinMetadataResponse{}, nil +} +func (s *stubPTBClient) GetCache() *cache.Cache { return nil } +func (s *stubPTBClient) GetCachedValue(string) (any, bool) { return nil, false } +func (s *stubPTBClient) SetCachedValue(string, any) {} +func (s *stubPTBClient) GetCachedValues([]string) (map[string]any, bool) { return nil, false } +func (s *stubPTBClient) SetCachedValues(map[string]any) {} +func (s *stubPTBClient) HashTxBytes(b []byte) []byte { return nil } +func (s *stubPTBClient) GetCCIPPackageID(context.Context, string) (string, error) { return "", nil } +func (s *stubPTBClient) GetValuesFromPackageOwnedObjectField(context.Context, string, string, string, []string) (map[string]string, error) { + return nil, nil +} +func (s *stubPTBClient) GetParentObjectID(context.Context, string, string, string) (string, error) { + return "", nil +} + +func TestDecodeOffRampExecCallArgs(t *testing.T) { + t.Run("valid input", func(t *testing.T) { + args := map[string]any{ + "ReportContext": [2][32]byte{}, + "Report": []byte{0x01, 0x02}, + "Info": ccipocr3.ExecuteReportInfo{}, + "ExtraData": ExtraDataDecoded{}, + } + result, err := DecodeOffRampExecCallArgs(args) + require.NoError(t, err) + assert.NotNil(t, result) + assert.Equal(t, []byte{0x01, 0x02}, result.Report) + }) + + t.Run("empty map returns zero-value struct", func(t *testing.T) { + result, err := DecodeOffRampExecCallArgs(map[string]any{}) + require.NoError(t, err) + assert.NotNil(t, result) + }) + + t.Run("nil map returns nil result without error", func(t *testing.T) { + result, err := DecodeOffRampExecCallArgs(nil) + require.NoError(t, err) + assert.Nil(t, result) + }) +} + +func TestIsValidTokenPoolConfig(t *testing.T) { + tests := []struct { + name string + config module_token_admin_registry.TokenConfig + valid bool + }{ + { + name: "all fields present", + config: module_token_admin_registry.TokenConfig{ + TokenPoolPackageId: "0x1", + TokenPoolModule: "pool", + TokenType: "0x2::coin::COIN", + }, + valid: true, + }, + { + name: "missing package ID", + config: module_token_admin_registry.TokenConfig{TokenPoolModule: "pool", TokenType: "0x2"}, + valid: false, + }, + { + name: "missing module", + config: module_token_admin_registry.TokenConfig{TokenPoolPackageId: "0x1", TokenType: "0x2"}, + valid: false, + }, + { + name: "missing token type", + config: module_token_admin_registry.TokenConfig{TokenPoolPackageId: "0x1", TokenPoolModule: "pool"}, + valid: false, + }, + { + name: "all empty", + config: module_token_admin_registry.TokenConfig{}, + valid: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.valid, IsValidTokenPoolConfig(&tc.config)) + }) + } +} + +func TestNeedsAppDelivery(t *testing.T) { + tests := []struct { + name string + message ccipocr3.Message + extraArgs map[string]any + expected bool + }{ + { + name: "empty data and no gasLimit in extraArgs", + message: ccipocr3.Message{Data: nil}, + extraArgs: map[string]any{}, + expected: false, + }, + { + name: "empty data and zero gasLimit (big.Int)", + message: ccipocr3.Message{Data: []byte{}}, + extraArgs: map[string]any{"gasLimit": big.NewInt(0)}, + expected: false, + }, + { + name: "empty data and nil gasLimit (big.Int)", + message: ccipocr3.Message{Data: nil}, + extraArgs: map[string]any{"gasLimit": (*big.Int)(nil)}, + expected: false, + }, + { + name: "empty data and zero gasLimit (uint64)", + message: ccipocr3.Message{Data: []byte{}}, + extraArgs: map[string]any{"gasLimit": uint64(0)}, + expected: false, + }, + { + name: "empty data and negative gasLimit (big.Int)", + message: ccipocr3.Message{Data: nil}, + extraArgs: map[string]any{"gasLimit": big.NewInt(-1)}, + expected: false, + }, + { + name: "non-empty data and zero gasLimit", + message: ccipocr3.Message{Data: []byte{0x01}}, + extraArgs: map[string]any{"gasLimit": big.NewInt(0)}, + expected: true, + }, + { + name: "non-empty data and no extraArgs", + message: ccipocr3.Message{Data: []byte{0xab, 0xcd}}, + extraArgs: map[string]any{}, + expected: true, + }, + { + name: "empty data and positive gasLimit (big.Int)", + message: ccipocr3.Message{Data: nil}, + extraArgs: map[string]any{"gasLimit": big.NewInt(200000)}, + expected: true, + }, + { + name: "empty data and positive gasLimit (uint64)", + message: ccipocr3.Message{Data: []byte{}}, + extraArgs: map[string]any{"gasLimit": uint64(100000)}, + expected: true, + }, + { + name: "both data and gasLimit present", + message: ccipocr3.Message{Data: []byte{0x01}}, + extraArgs: map[string]any{"gasLimit": big.NewInt(500000)}, + expected: true, + }, + { + name: "gasLimit is unexpected type (string) — treated as absent", + message: ccipocr3.Message{Data: nil}, + extraArgs: map[string]any{"gasLimit": "not-a-number"}, + expected: false, + }, + { + name: "nil extraArgs map", + message: ccipocr3.Message{Data: nil}, + extraArgs: nil, + expected: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result := needsAppDelivery(tc.message, tc.extraArgs) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestExtractReceiverObjectIDs(t *testing.T) { + lggr := logger.Test(t) + + tests := []struct { + name string + args map[string]any + expected [][]byte + }{ + { + name: "key missing", + args: map[string]any{}, + expected: nil, + }, + { + name: "key explicitly nil", + args: map[string]any{"receiverObjectIds": nil}, + expected: nil, + }, + { + name: "nil map", + args: nil, + expected: nil, + }, + { + name: "[][]byte value", + args: map[string]any{"receiverObjectIds": [][]byte{{0x01}, {0x02, 0x03}}}, + expected: [][]byte{{0x01}, {0x02, 0x03}}, + }, + { + name: "empty [][]byte", + args: map[string]any{"receiverObjectIds": [][]byte{}}, + expected: [][]byte{}, + }, + { + name: "[]any with valid []byte elements", + args: map[string]any{"receiverObjectIds": []any{[]byte{0xaa}, []byte{0xbb}}}, + expected: [][]byte{{0xaa}, {0xbb}}, + }, + { + name: "[]any with mixed types skips non-byte elements", + args: map[string]any{"receiverObjectIds": []any{[]byte{0xaa}, "bad", []byte{0xcc}}}, + expected: [][]byte{{0xaa}, {0xcc}}, + }, + { + name: "[]any all non-byte returns nil", + args: map[string]any{"receiverObjectIds": []any{"a", 42}}, + expected: nil, + }, + { + name: "unexpected type returns nil", + args: map[string]any{"receiverObjectIds": "not-a-slice"}, + expected: nil, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result := extractReceiverObjectIDs(lggr, tc.args) + assert.Equal(t, tc.expected, result) + }) + } +} + +// validSuiAddress returns a zero-padded Sui address suitable for test fixtures. +func validSuiAddress() string { + return "0x0000000000000000000000000000000000000000000000000000000000000001" +} + +func testAddressMappings() *OffRampAddressMappings { + return &OffRampAddressMappings{ + CcipPackageId: validSuiAddress(), + CcipObjectRef: validSuiAddress(), + CcipOwnerCap: validSuiAddress(), + ClockObject: "0x6", + OffRampPackageId: validSuiAddress(), + OffRampState: validSuiAddress(), + } +} + +func testCallOpts() *bind.CallOpts { + return &bind.CallOpts{ + Signer: signer.NewDevInspectSigner(validSuiAddress()), + WaitForExecution: true, + } +} + +func TestProcessReceivers_SkipPaths(t *testing.T) { + lggr := logger.Test(t) + ctx := context.Background() + fakeClient := &stubPTBClient{} + ptb := transaction.NewTransaction() + mappings := testAddressMappings() + callOpts := testCallOpts() + var receiverParams *transaction.Argument + + tests := []struct { + name string + messages []ccipocr3.Message + extraArgs map[string]any + }{ + { + name: "nil receiver", + messages: []ccipocr3.Message{{Receiver: nil, Data: []byte{0x01}}}, + extraArgs: map[string]any{"gasLimit": big.NewInt(100000)}, + }, + { + name: "empty receiver", + messages: []ccipocr3.Message{{Receiver: []byte{}, Data: []byte{0x01}}}, + extraArgs: map[string]any{"gasLimit": big.NewInt(100000)}, + }, + { + name: "zero-address receiver", + messages: []ccipocr3.Message{{Receiver: make([]byte, 32), Data: []byte{0x01}}}, + extraArgs: map[string]any{"gasLimit": big.NewInt(100000)}, + }, + { + name: "no app delivery needed", + messages: []ccipocr3.Message{{ + Receiver: append(make([]byte, 31), 0x01), + Data: nil, + }}, + extraArgs: map[string]any{"gasLimit": big.NewInt(0)}, + }, + { + name: "no messages", + messages: []ccipocr3.Message{}, + extraArgs: map[string]any{}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + results, err := ProcessReceivers( + ctx, lggr, fakeClient, ptb, + tc.messages, mappings, callOpts, receiverParams, tc.extraArgs, + ) + require.NoError(t, err) + assert.Empty(t, results) + }) + } +} + +func TestProcessReceivers_SkipsZeroAddressReceiver(t *testing.T) { + // A 32-byte zero address receiver should be skipped. + zeroAddr := make([]byte, 32) + msg := ccipocr3.Message{ + Receiver: zeroAddr, + Data: []byte{0x01}, + } + // Verify our check matches codec.AccountZero pattern + allZero := true + for _, b := range msg.Receiver { + if b != 0 { + allZero = false + break + } + } + assert.True(t, allZero) +} + +func TestNeedsAppDelivery_ReturnsFalseForTokenOnlyMessage(t *testing.T) { + receiver := make([]byte, 32) + receiver[31] = 0x01 + + msg := ccipocr3.Message{ + Receiver: receiver, + Data: nil, + } + extraArgs := map[string]any{"gasLimit": big.NewInt(0)} + + assert.False(t, needsAppDelivery(msg, extraArgs), + "token-only message should not need app delivery") +} + +func TestNeedsAppDelivery_ReturnsTrueForDataMessage(t *testing.T) { + receiver := make([]byte, 32) + receiver[31] = 0x01 + + msg := ccipocr3.Message{ + Receiver: receiver, + Data: []byte{0xde, 0xad}, + } + extraArgs := map[string]any{} + + assert.True(t, needsAppDelivery(msg, extraArgs), + "message with data should need app delivery") +} + +func TestNeedsAppDelivery_ReturnsTrueForPositiveGasLimit(t *testing.T) { + receiver := make([]byte, 32) + receiver[31] = 0x01 + + msg := ccipocr3.Message{ + Receiver: receiver, + Data: nil, + } + extraArgs := map[string]any{"gasLimit": big.NewInt(100000)} + + assert.True(t, needsAppDelivery(msg, extraArgs), + "message with gasLimit > 0 should need app delivery") +} diff --git a/relayer/chainwriter/ptb/offramp/helpers.go b/relayer/chainwriter/ptb/offramp/helpers.go index bf8c42498..01b3793c6 100644 --- a/relayer/chainwriter/ptb/offramp/helpers.go +++ b/relayer/chainwriter/ptb/offramp/helpers.go @@ -2,6 +2,7 @@ package offramp import ( "context" + "errors" "fmt" "strings" @@ -11,6 +12,11 @@ import ( "github.com/smartcontractkit/chainlink-sui/relayer/client" ) +// ErrUnsupportedReceiverABI indicates a receiver's on-chain ABI contains shapes +// that the relayer cannot build a PTB command for (e.g. generic TypeParameter). +// This is a permanent failure: retrying with the same receiver will always fail. +var ErrUnsupportedReceiverABI = errors.New("unsupported receiver ABI") + func AnyPointer[T any](v T) *T { return &v } @@ -137,7 +143,11 @@ type SuiArgumentMetadata struct { Type string `json:"type"` } -func decodeParam(lggr logger.Logger, param any, reference string) SuiArgumentMetadata { +func decodeParam(lggr logger.Logger, param any, reference string) (SuiArgumentMetadata, error) { + if param == nil { + return SuiArgumentMetadata{}, errors.New("nil parameter") + } + // Handle primitive types (strings like "U64", "Bool", etc.) if str, ok := param.(string); ok { return SuiArgumentMetadata{ @@ -147,51 +157,191 @@ func decodeParam(lggr logger.Logger, param any, reference string) SuiArgumentMet Reference: reference, TypeArguments: []TypeParameter{}, Type: ParseParamType(lggr, str), + }, nil + } + + m, ok := param.(map[string]any) + if !ok { + return SuiArgumentMetadata{}, fmt.Errorf("expected map[string]any, got %T", param) + } + if len(m) == 0 { + return SuiArgumentMetadata{}, errors.New("empty parameter map") + } + if len(m) != 1 { + return SuiArgumentMetadata{}, fmt.Errorf("expected exactly one ABI wrapper key, got %d", len(m)) + } + + var k string + var v any + for key, val := range m { + k, v = key, val + } + + switch k { + case "Struct": + return decodeStructParam(lggr, v, reference) + case "Vector": + decoded, err := decodeParam(lggr, v, reference) + if err != nil { + return SuiArgumentMetadata{}, err + } + decoded.Reference = "Vector" + decoded.Type = ParseParamType(lggr, v) + return decoded, nil + case "Reference", "MutableReference": + return decodeParam(lggr, v, k) + case "TypeParameter": + return SuiArgumentMetadata{}, fmt.Errorf("%w: TypeParameter (generic parameters are not supported)", ErrUnsupportedReceiverABI) + default: + vMap, ok := v.(map[string]any) + if !ok { + return SuiArgumentMetadata{}, fmt.Errorf("%w: key %q has non-map value of type %T", ErrUnsupportedReceiverABI, k, v) + } + innerRaw, exists := vMap["Struct"] + if !exists { + return SuiArgumentMetadata{}, fmt.Errorf("%w: key %q missing inner Struct", ErrUnsupportedReceiverABI, k) + } + inner, ok := innerRaw.(map[string]any) + if !ok { + return SuiArgumentMetadata{}, fmt.Errorf("%w: key %q Struct value is %T, not map", ErrUnsupportedReceiverABI, k, innerRaw) + } + typeArguments, err := decodeTypeArguments(inner) + if err != nil { + return SuiArgumentMetadata{}, fmt.Errorf("key %q: %w", k, err) + } + address, module, name, err := extractStructFields(inner) + if err != nil { + return SuiArgumentMetadata{}, fmt.Errorf("key %q: %w", k, err) } + return SuiArgumentMetadata{ + Address: address, + Module: module, + Name: name, + Reference: k, + TypeArguments: typeArguments, + Type: ParseParamType(lggr, v), + }, nil } +} - // Handle complex types (maps) - m := param.(map[string]any) - for k, v := range m { - switch k { - case "Struct": - // Direct struct - s := v.(map[string]any) - typeArguments := []TypeParameter{} - for _, ta := range s["typeArguments"].([]any) { - typeArgument := ta.(map[string]any) - typeArguments = append(typeArguments, TypeParameter{TypeParameter: typeArgument["TypeParameter"].(float64)}) - } - return SuiArgumentMetadata{ - Address: s["address"].(string), - Module: s["module"].(string), - Name: s["name"].(string), - Reference: reference, - TypeArguments: typeArguments, - Type: ParseParamType(lggr, v), - } - case "Reference", "MutableReference", "Vector": - // Reference and MutableReference are the same thing - // We need to unwrap the struct - return decodeParam(lggr, v, k) - default: - inner := v.(map[string]any)["Struct"].(map[string]any) - typeArguments := []TypeParameter{} - for _, ta := range inner["typeArguments"].([]any) { - typeArgument := ta.(map[string]any) - typeArguments = append(typeArguments, TypeParameter{TypeParameter: typeArgument["TypeParameter"].(float64)}) - } - return SuiArgumentMetadata{ - Address: inner["address"].(string), - Module: inner["module"].(string), - Name: inner["name"].(string), - Reference: k, - TypeArguments: typeArguments, - Type: ParseParamType(lggr, v), - } +func decodeStructParam(lggr logger.Logger, v any, reference string) (SuiArgumentMetadata, error) { + s, ok := v.(map[string]any) + if !ok { + return SuiArgumentMetadata{}, fmt.Errorf("struct value is %T, expected map[string]any", v) + } + typeArguments, err := decodeTypeArguments(s) + if err != nil { + return SuiArgumentMetadata{}, fmt.Errorf("struct: %w", err) + } + address, module, name, err := extractStructFields(s) + if err != nil { + return SuiArgumentMetadata{}, fmt.Errorf("struct: %w", err) + } + return SuiArgumentMetadata{ + Address: address, + Module: module, + Name: name, + Reference: reference, + TypeArguments: typeArguments, + Type: ParseParamType(lggr, v), + }, nil +} + +func decodeTypeArguments(s map[string]any) ([]TypeParameter, error) { + taRaw, exists := s["typeArguments"] + if !exists { + return []TypeParameter{}, nil + } + taSlice, ok := taRaw.([]any) + if !ok { + return nil, fmt.Errorf("typeArguments is %T, expected []any", taRaw) + } + typeArguments := make([]TypeParameter, 0, len(taSlice)) + for i, ta := range taSlice { + taMap, ok := ta.(map[string]any) + if !ok { + return nil, fmt.Errorf("typeArguments[%d] is %T, expected map[string]any", i, ta) } + tpRaw, exists := taMap["TypeParameter"] + if !exists { + return nil, fmt.Errorf("typeArguments[%d] missing TypeParameter key", i) + } + tp, ok := tpRaw.(float64) + if !ok { + return nil, fmt.Errorf("typeArguments[%d].TypeParameter is %T, expected float64", i, tpRaw) + } + typeArguments = append(typeArguments, TypeParameter{TypeParameter: tp}) + } + return typeArguments, nil +} + +func extractStructFields(s map[string]any) (address, module, name string, err error) { + addrRaw, ok := s["address"] + if !ok { + return "", "", "", errors.New("missing field \"address\"") + } + address, ok = addrRaw.(string) + if !ok { + return "", "", "", fmt.Errorf("field \"address\" is %T, expected string", addrRaw) + } + modRaw, ok := s["module"] + if !ok { + return "", "", "", errors.New("missing field \"module\"") + } + module, ok = modRaw.(string) + if !ok { + return "", "", "", fmt.Errorf("field \"module\" is %T, expected string", modRaw) + } + nameRaw, ok := s["name"] + if !ok { + return "", "", "", errors.New("missing field \"name\"") + } + name, ok = nameRaw.(string) + if !ok { + return "", "", "", fmt.Errorf("field \"name\" is %T, expected string", nameRaw) + } + return address, module, name, nil +} + +const ( + moveStringType = "0x1::string::String" + moveASCIIStringType = "ascii::String" +) + +func structFieldsToMoveType(address, module, name string) string { + switch { + case module == "string" && name == "String": + return moveStringType + case module == "ascii" && name == "String": + return moveASCIIStringType + default: + return "object_id" + } +} + +func structMapToMoveType(m map[string]any) string { + address, module, name, err := extractStructFields(m) + if err != nil { + return "object_id" } - return SuiArgumentMetadata{} + return structFieldsToMoveType(address, module, name) +} + +func isPureParamType(paramType string) bool { + if strings.HasPrefix(paramType, "u") || paramType == "bool" { + return true + } + return paramType == moveStringType || + paramType == moveASCIIStringType || + strings.Contains(paramType, "::string::String") || + strings.Contains(paramType, "::ascii::String") +} + +func formatValueParamType(paramType string) string { + if strings.Contains(paramType, "::") { + return paramType + } + return strings.ToLower(paramType) } func ParseParamType(lggr logger.Logger, param interface{}) string { @@ -220,7 +370,7 @@ func ParseParamType(lggr logger.Logger, param interface{}) string { } // Case 2: map structure (e.g., Vector, Reference, Struct) - if m, ok := param.(map[string]interface{}); ok { + if m, ok := param.(map[string]any); ok { if vectorVal, ok := m["Vector"]; ok { return "vector<" + ParseParamType(lggr, vectorVal) + ">" } @@ -230,19 +380,14 @@ func ParseParamType(lggr logger.Logger, param interface{}) string { if mutRefVal, ok := m["MutableReference"]; ok { return ParseParamType(lggr, mutRefVal) } - if _, ok := m["Struct"]; ok { - // Special case for strings - if m["address"] == "String" { - return "string" + if structVal, ok := m["Struct"]; ok { + if inner, ok := structVal.(map[string]any); ok { + return structMapToMoveType(inner) } return "object_id" } - // Handle direct struct content (when called from decodeParam with unwrapped struct) - if address, ok := m["address"]; ok { - if address == "String" { - return "string" - } - return "object_id" + if _, ok := m["address"]; ok { + return structMapToMoveType(m) } } @@ -250,17 +395,25 @@ func ParseParamType(lggr logger.Logger, param interface{}) string { return "unknown" } +func exposedFunctionSignature(functionName string, raw any) (map[string]any, error) { + m, ok := raw.(map[string]any) + if !ok || m == nil { + return nil, fmt.Errorf("%w: function %q has invalid signature shape (%T)", ErrUnsupportedReceiverABI, functionName, raw) + } + return m, nil +} + func DecodeParameters(lggr logger.Logger, function map[string]any, key string) ([]string, error) { parametersRaw, exists := function[key] if !exists || parametersRaw == nil { lggr.Errorw("key field is missing or nil", "function", function, "key", key) - return nil, fmt.Errorf("key field is missing or nil") + return nil, errors.New("key field is missing or nil") } parameters, ok := parametersRaw.([]any) if !ok { lggr.Errorw("key field is not an array", "parametersRaw", parametersRaw, "key", key) - return nil, fmt.Errorf("key field is not an array") + return nil, errors.New("key field is not an array") } lggr.Debugw("Raw parameters", "parameters", parameters, "key", key) @@ -268,7 +421,11 @@ func DecodeParameters(lggr logger.Logger, function map[string]any, key string) ( defaultReference := "Reference" decodedParameters := make([]SuiArgumentMetadata, len(parameters)) for i, parameter := range parameters { - decodedParameters[i] = decodeParam(lggr, parameter, defaultReference) + decoded, err := decodeParam(lggr, parameter, defaultReference) + if err != nil { + return nil, fmt.Errorf("failed to decode parameter %d: %w", i, err) + } + decodedParameters[i] = decoded } lggr.Debugw("decoded parameters", "decodedParameters", decodedParameters) @@ -280,8 +437,7 @@ func DecodeParameters(lggr logger.Logger, function map[string]any, key string) ( } if param.Reference == "Reference" { - if strings.HasPrefix(param.Type, "u") || param.Type == "bool" { - // It's a primitive, not an object reference + if isPureParamType(param.Type) { paramTypes = append(paramTypes, param.Type) } else { paramTypes = append(paramTypes, "&object") @@ -299,7 +455,7 @@ func DecodeParameters(lggr logger.Logger, function map[string]any, key string) ( continue } - paramTypes = append(paramTypes, strings.ToLower(param.Type)) + paramTypes = append(paramTypes, formatValueParamType(param.Type)) } return paramTypes, nil diff --git a/relayer/chainwriter/ptb/offramp/helpers_test.go b/relayer/chainwriter/ptb/offramp/helpers_test.go new file mode 100644 index 000000000..24a5930ed --- /dev/null +++ b/relayer/chainwriter/ptb/offramp/helpers_test.go @@ -0,0 +1,603 @@ +package offramp + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/logger" +) + +func TestExposedFunctionSignature_InvalidShape(t *testing.T) { + tests := []struct { + name string + raw any + }{ + {name: "string", raw: "not-a-map"}, + {name: "nil", raw: nil}, + {name: "float64", raw: float64(1)}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.NotPanics(t, func() { + m, err := exposedFunctionSignature("ccip_receive", tc.raw) + require.Error(t, err) + assert.Nil(t, m) + assert.ErrorIs(t, err, ErrUnsupportedReceiverABI) + }) + }) + } +} + +func TestExposedFunctionSignature_ValidMap(t *testing.T) { + m, err := exposedFunctionSignature("ccip_receive", map[string]any{"parameters": []any{}}) + require.NoError(t, err) + assert.Equal(t, map[string]any{"parameters": []any{}}, m) +} + +func TestDecodeParam_PoisonABI_TypeParameter(t *testing.T) { + lggr := logger.Test(t) + + tests := []struct { + name string + param any + }{ + { + name: "Vector wrapping TypeParameter", + param: map[string]any{"Vector": map[string]any{"TypeParameter": float64(0)}}, + }, + { + name: "top-level TypeParameter", + param: map[string]any{"TypeParameter": float64(0)}, + }, + { + name: "Reference wrapping Vector wrapping TypeParameter", + param: map[string]any{"Reference": map[string]any{"Vector": map[string]any{"TypeParameter": float64(0)}}}, + }, + { + name: "MutableReference wrapping TypeParameter", + param: map[string]any{"MutableReference": map[string]any{"TypeParameter": float64(1)}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result, err := decodeParam(lggr, tc.param, "Reference") + require.ErrorIs(t, err, ErrUnsupportedReceiverABI, + "expected ErrUnsupportedReceiverABI, got: %v", err) + assert.Contains(t, err.Error(), "TypeParameter") + assert.Equal(t, SuiArgumentMetadata{}, result) + }) + } +} + +func TestDecodeParam_UnsupportedABI_DefaultBranch(t *testing.T) { + lggr := logger.Test(t) + + tests := []struct { + name string + param any + }{ + { + name: "unknown key with non-map value", + param: map[string]any{"SomeUnknownKey": float64(99)}, + }, + { + name: "unknown key with map missing Struct", + param: map[string]any{"SomeKey": map[string]any{"NotStruct": "x"}}, + }, + { + name: "unknown key with non-map Struct value", + param: map[string]any{"SomeKey": map[string]any{"Struct": "not-a-map"}}, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, err := decodeParam(lggr, tc.param, "Reference") + require.Error(t, err) + assert.ErrorIs(t, err, ErrUnsupportedReceiverABI, + "expected ErrUnsupportedReceiverABI, got: %v", err) + }) + } +} + +func TestDecodeParam_MultiKeyWrapperMap_RejectsDeterministically(t *testing.T) { + lggr := logger.Test(t) + + poison := map[string]any{ + "Vector": map[string]any{"TypeParameter": float64(0)}, + "TypeParameter": float64(0), + } + + for i := 0; i < 50; i++ { + result, err := decodeParam(lggr, poison, "Reference") + require.Error(t, err, "iteration %d", i) + assert.Contains(t, err.Error(), "exactly one ABI wrapper key") + assert.Equal(t, SuiArgumentMetadata{}, result) + } +} + +func TestDecodeParam_MultiKeyWrapperMap_NestedRejects(t *testing.T) { + lggr := logger.Test(t) + + param := map[string]any{ + "Vector": map[string]any{ + "Struct": map[string]any{"address": "0x1", "module": "m", "name": "S", "typeArguments": []any{}}, + "TypeParameter": float64(0), + }, + } + + result, err := decodeParam(lggr, param, "Reference") + require.Error(t, err) + assert.Contains(t, err.Error(), "exactly one ABI wrapper key") + assert.Equal(t, SuiArgumentMetadata{}, result) +} + +func TestDecodeParam_MalformedInput_NotUnsupportedABI(t *testing.T) { + lggr := logger.Test(t) + + tests := []struct { + name string + param any + }{ + { + name: "nil parameter", + param: nil, + }, + { + name: "float64 instead of map or string", + param: float64(42), + }, + { + name: "integer instead of map or string", + param: int(7), + }, + { + name: "Struct with nil value", + param: map[string]any{"Struct": nil}, + }, + { + name: "Struct with wrong type value", + param: map[string]any{"Struct": "not-a-map"}, + }, + { + name: "Struct missing address field", + param: map[string]any{"Struct": map[string]any{"module": "m", "name": "S", "typeArguments": []any{}}}, + }, + { + name: "Struct with non-string address", + param: map[string]any{"Struct": map[string]any{"address": 123, "module": "m", "name": "S", "typeArguments": []any{}}}, + }, + { + name: "Struct with bad typeArguments type", + param: map[string]any{"Struct": map[string]any{"address": "0x1", "module": "m", "name": "S", "typeArguments": "not-array"}}, + }, + { + name: "Struct with typeArgument missing TypeParameter key", + param: map[string]any{"Struct": map[string]any{"address": "0x1", "module": "m", "name": "S", "typeArguments": []any{map[string]any{"NotTypeParameter": float64(0)}}}}, + }, + { + name: "empty map", + param: map[string]any{}, + }, + { + name: "multiple top-level wrapper keys", + param: map[string]any{ + "Vector": map[string]any{"U8": nil}, + "TypeParameter": float64(0), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + assert.NotPanics(t, func() { + _, err := decodeParam(lggr, tc.param, "Reference") + require.Error(t, err, "expected error for input: %v", tc.param) + assert.NotErrorIs(t, err, ErrUnsupportedReceiverABI, + "should NOT be ErrUnsupportedReceiverABI for malformed input: %v", err) + }) + }) + } +} + +func TestDecodeParam_ValidABI(t *testing.T) { + lggr := logger.Test(t) + + tests := []struct { + name string + param any + reference string + expected SuiArgumentMetadata + }{ + { + name: "primitive U64", + param: "U64", + reference: "Reference", + expected: SuiArgumentMetadata{ + Name: "U64", + Reference: "Reference", + TypeArguments: []TypeParameter{}, + Type: "u64", + }, + }, + { + name: "primitive Bool", + param: "Bool", + reference: "Reference", + expected: SuiArgumentMetadata{ + Name: "Bool", + Reference: "Reference", + TypeArguments: []TypeParameter{}, + Type: "bool", + }, + }, + { + name: "Vector of U8", + param: map[string]any{"Vector": "U8"}, + reference: "Reference", + expected: SuiArgumentMetadata{ + Name: "U8", + Reference: "Vector", + TypeArguments: []TypeParameter{}, + Type: "u8", + }, + }, + { + name: "nested Vector of U8", + param: map[string]any{"Vector": map[string]any{"Vector": "U8"}}, + reference: "Reference", + expected: SuiArgumentMetadata{ + Name: "U8", + Reference: "Vector", + TypeArguments: []TypeParameter{}, + Type: "vector", + }, + }, + { + name: "Struct with no type arguments", + param: map[string]any{"Struct": map[string]any{ + "address": "0x1", + "module": "state_object", + "name": "CCIPObjectRef", + "typeArguments": []any{}, + }}, + reference: "Reference", + expected: SuiArgumentMetadata{ + Address: "0x1", + Module: "state_object", + Name: "CCIPObjectRef", + Reference: "Reference", + TypeArguments: []TypeParameter{}, + Type: "object_id", + }, + }, + { + name: "MutableReference wrapping Struct", + param: map[string]any{"MutableReference": map[string]any{"Struct": map[string]any{ + "address": "0xabc", + "module": "dummy_receiver", + "name": "CCIPReceiverState", + "typeArguments": []any{}, + }}}, + reference: "Reference", + expected: SuiArgumentMetadata{ + Address: "0xabc", + Module: "dummy_receiver", + Name: "CCIPReceiverState", + Reference: "MutableReference", + TypeArguments: []TypeParameter{}, + Type: "object_id", + }, + }, + { + name: "Struct with one type argument", + param: map[string]any{"Struct": map[string]any{ + "address": "0xpool", + "module": "burn_mint", + "name": "BurnMintTokenPoolState", + "typeArguments": []any{map[string]any{"TypeParameter": float64(0)}}, + }}, + reference: "MutableReference", + expected: SuiArgumentMetadata{ + Address: "0xpool", + Module: "burn_mint", + Name: "BurnMintTokenPoolState", + Reference: "MutableReference", + TypeArguments: []TypeParameter{{TypeParameter: 0}}, + Type: "object_id", + }, + }, + { + name: "0x1::string::String struct", + param: map[string]any{"Struct": map[string]any{ + "address": "0x1", + "module": "string", + "name": "String", + "typeArguments": []any{}, + }}, + reference: "Reference", + expected: SuiArgumentMetadata{ + Address: "0x1", + Module: "string", + Name: "String", + Reference: "Reference", + TypeArguments: []TypeParameter{}, + Type: moveStringType, + }, + }, + { + name: "Vector of 0x1::string::String", + param: map[string]any{"Vector": map[string]any{"Struct": map[string]any{ + "address": "0x1", + "module": "string", + "name": "String", + "typeArguments": []any{}, + }}}, + reference: "Reference", + expected: SuiArgumentMetadata{ + Address: "0x1", + Module: "string", + Name: "String", + Reference: "Vector", + TypeArguments: []TypeParameter{}, + Type: moveStringType, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result, err := decodeParam(lggr, tc.param, tc.reference) + require.NoError(t, err) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestDecodeParameters_PoisonABI_ReturnsError(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{ + "parameters": []any{ + map[string]any{"Vector": map[string]any{"TypeParameter": float64(0)}}, + "U64", + }, + } + + assert.NotPanics(t, func() { + result, err := DecodeParameters(lggr, function, "parameters") + require.Error(t, err) + assert.Nil(t, result) + assert.ErrorIs(t, err, ErrUnsupportedReceiverABI, + "expected ErrUnsupportedReceiverABI to propagate through DecodeParameters, got: %v", err) + }) +} + +func TestDecodeParameters_StringParam(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{ + "parameters": []any{ + map[string]any{"Struct": map[string]any{ + "address": "0x1", "module": "string", "name": "String", "typeArguments": []any{}, + }}, + }, + } + + result, err := DecodeParameters(lggr, function, "parameters") + require.NoError(t, err) + assert.Equal(t, []string{moveStringType}, result) +} + +func TestDecodeParameters_AsciiStringParam(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{ + "parameters": []any{ + map[string]any{"Struct": map[string]any{ + "address": "0x1", "module": "ascii", "name": "String", "typeArguments": []any{}, + }}, + }, + } + + result, err := DecodeParameters(lggr, function, "parameters") + require.NoError(t, err) + assert.Equal(t, []string{moveASCIIStringType}, result) +} + +func TestDecodeParameters_VectorAsciiString(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{ + "parameters": []any{ + map[string]any{"Vector": map[string]any{"Struct": map[string]any{ + "address": "0x1", "module": "ascii", "name": "String", "typeArguments": []any{}, + }}}, + }, + } + + result, err := DecodeParameters(lggr, function, "parameters") + require.NoError(t, err) + assert.Equal(t, []string{"vector<" + moveASCIIStringType + ">"}, result) +} + +func TestDecodeParameters_VectorString(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{ + "parameters": []any{ + map[string]any{"Vector": map[string]any{"Struct": map[string]any{ + "address": "0x1", "module": "string", "name": "String", "typeArguments": []any{}, + }}}, + }, + } + + result, err := DecodeParameters(lggr, function, "parameters") + require.NoError(t, err) + assert.Equal(t, []string{"vector<" + moveStringType + ">"}, result) +} + +func TestParseParamType_String(t *testing.T) { + lggr := logger.Test(t) + + inner := map[string]any{ + "address": "0x1", "module": "string", "name": "String", "typeArguments": []any{}, + } + wrapper := map[string]any{"Struct": inner} + + assert.Equal(t, moveStringType, ParseParamType(lggr, inner)) + assert.Equal(t, moveStringType, ParseParamType(lggr, wrapper)) + assert.Equal(t, "vector<"+moveStringType+">", ParseParamType(lggr, map[string]any{"Vector": wrapper})) +} + +func suiStringStructABI() map[string]any { + return map[string]any{ + "address": "0x1", "module": "string", "name": "String", "typeArguments": []any{}, + } +} + +func suiObjectStructABI(address, module, name string) map[string]any { + return map[string]any{ + "address": address, "module": module, "name": name, "typeArguments": []any{}, + } +} + +// TestDecodeParameters_ComplexSignature exercises a mixed parameter list resembling a rich +// Move entry function: object ref, address, vectors, std::string, and primitives. +func TestDecodeParameters_ComplexSignature(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{ + "parameters": []any{ + // &CCIPObjectRef (object reference) + map[string]any{"Reference": map[string]any{"Struct": suiObjectStructABI( + "0xccip", "state_object", "CCIPObjectRef", + )}}, + // address primitive (Move Address) + "Address", + // vector + map[string]any{"Vector": "U8"}, + // vector (vector of object-like structs) + map[string]any{"Vector": map[string]any{"Struct": suiObjectStructABI( + "0xpool", "burn_mint", "BurnMintTokenPoolState", + )}}, + // 0x1::string::String by value + map[string]any{"Struct": suiStringStructABI()}, + // vector<0x1::string::String> + map[string]any{"Vector": map[string]any{"Struct": suiStringStructABI()}}, + // bool + "Bool", + }, + } + + result, err := DecodeParameters(lggr, function, "parameters") + require.NoError(t, err) + + // Address uses default Reference label and object_id typing → &object today (not bindings' "address"). + expected := []string{ + "&object", + "&object", + "vector", + "vector", + moveStringType, + "vector<" + moveStringType + ">", + "bool", + } + assert.Equal(t, expected, result) + + decoded := make([]SuiArgumentMetadata, len(function["parameters"].([]any))) + for i, parameter := range function["parameters"].([]any) { + meta, decodeErr := decodeParam(lggr, parameter, "Reference") + require.NoError(t, decodeErr) + decoded[i] = meta + } + + assert.Equal(t, "object_id", decoded[0].Type) + assert.Equal(t, "CCIPObjectRef", decoded[0].Name) + assert.Equal(t, "object_id", decoded[1].Type) + assert.Equal(t, "Address", decoded[1].Name) + assert.Equal(t, "u8", decoded[2].Type) + assert.Equal(t, "object_id", decoded[3].Type) + assert.Equal(t, "BurnMintTokenPoolState", decoded[3].Name) + assert.Equal(t, moveStringType, decoded[4].Type) + assert.Equal(t, moveStringType, decoded[5].Type) + assert.Equal(t, "bool", decoded[6].Type) +} + +func TestDecodeParameters_NestedVectorU8(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{ + "parameters": []any{ + map[string]any{"Vector": map[string]any{"Vector": "U8"}}, + }, + } + + result, err := DecodeParameters(lggr, function, "parameters") + require.NoError(t, err) + assert.Equal(t, []string{"vector>"}, result) +} + +func TestDecodeParameters_ValidDummyReceiverSignature(t *testing.T) { + lggr := logger.Test(t) + + // Matches the normalized ABI of dummy_receiver::ccip_receive: + // ccip_receive(expected_message_id: vector, ref: &CCIPObjectRef, message: Any2SuiMessage, _: &Clock, state: &mut CCIPReceiverState) + function := map[string]any{ + "parameters": []any{ + map[string]any{"Vector": "U8"}, + map[string]any{"Reference": map[string]any{"Struct": map[string]any{ + "address": "0xccip", "module": "state_object", "name": "CCIPObjectRef", "typeArguments": []any{}, + }}}, + map[string]any{"Struct": map[string]any{ + "address": "0xccip", "module": "client", "name": "Any2SuiMessage", "typeArguments": []any{}, + }}, + map[string]any{"Reference": map[string]any{"Struct": map[string]any{ + "address": "0x2", "module": "clock", "name": "Clock", "typeArguments": []any{}, + }}}, + map[string]any{"MutableReference": map[string]any{"Struct": map[string]any{ + "address": "0xreceiver", "module": "dummy_receiver", "name": "CCIPReceiverState", "typeArguments": []any{}, + }}}, + map[string]any{"MutableReference": map[string]any{"Struct": map[string]any{ + "address": "0x2", "module": "tx_context", "name": "TxContext", "typeArguments": []any{}, + }}}, + }, + } + + result, err := DecodeParameters(lggr, function, "parameters") + require.NoError(t, err) + + // TxContext is skipped. Any2SuiMessage is a Struct with default reference "Reference" → "&object" + expected := []string{ + "vector", + "&object", + "&object", + "&object", + "&mut object", + } + assert.Equal(t, expected, result) +} + +func TestDecodeParameters_MissingKey(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{"return": []any{}} + + result, err := DecodeParameters(lggr, function, "parameters") + require.Error(t, err) + assert.Nil(t, result) +} + +func TestDecodeParameters_NilValue(t *testing.T) { + lggr := logger.Test(t) + + function := map[string]any{"parameters": nil} + + result, err := DecodeParameters(lggr, function, "parameters") + require.Error(t, err) + assert.Nil(t, result) +} diff --git a/relayer/client/grpc_client.go b/relayer/client/grpc_client.go index 1f1f85631..18b6cd75d 100644 --- a/relayer/client/grpc_client.go +++ b/relayer/client/grpc_client.go @@ -158,8 +158,6 @@ func NewPTBClient(log logger.Logger, cfg PTBClientConfig) (*PTBClient, error) { } func (c *PTBClient) WithRateLimit(ctx context.Context, methodName string, f func(ctx context.Context) error) error { - start := time.Now() - weight := int64(1) if weightValue, ok := RateLimitWeights[methodName]; ok { weight = weightValue @@ -182,7 +180,6 @@ func (c *PTBClient) WithRateLimit(ctx context.Context, methodName string, f func // ensure cleanup on exit defer func() { c.rateLimiter.Release(weight) - c.log.Debugw("WithRateLimit released", "methodName", methodName, "duration", time.Since(start)) }() // run the user function with the timeout context @@ -565,8 +562,6 @@ func (c *PTBClient) simulatePTBInternal(ctx context.Context, txExecService suirp return nil, fmt.Errorf("failed to simulate transaction: %w", err) } - c.log.Debugw("SimulatePTB RPC response", "RPC response", response) - if response.Transaction != nil && response.Transaction.Effects != nil && response.Transaction.Effects.Status != nil { if !response.Transaction.Effects.Status.GetSuccess() { errMsg := response.Transaction.Effects.Status.GetError() @@ -839,9 +834,9 @@ var transactionEventsReadMaskPaths = []string{ } // TODO: this should be the responsibility of the indexer, not the client -// hydrateTransactionEventsIfNeeded fetches full event payloads when a checkpoint +// HydrateTransactionEvents fetches full event payloads when a checkpoint // transaction reports an events_digest but omits inline TransactionEvents. -func (c *PTBClient) hydrateTransactionEventsIfNeeded(ctx context.Context, tx *suirpcv2.ExecutedTransaction) { +func (c *PTBClient) HydrateTransactionEvents(ctx context.Context, tx *suirpcv2.ExecutedTransaction) { if tx == nil { return } @@ -909,7 +904,7 @@ func (c *PTBClient) GetCheckpointData(ctx context.Context, checkpointSequenceNum transactions := response.GetCheckpoint().GetTransactions() for _, tx := range transactions { - c.hydrateTransactionEventsIfNeeded(ctx, tx) + c.HydrateTransactionEvents(ctx, tx) } result = &CheckpointData{ diff --git a/relayer/codec/decoder.go b/relayer/codec/decoder.go index cd2b5f3ed..10f6cd09e 100644 --- a/relayer/codec/decoder.go +++ b/relayer/codec/decoder.go @@ -32,6 +32,13 @@ const ( // Response parsing constants maxByteValue = 255 + + // proofWireBytes is the fixed on-wire size of each merkle proof element. + proofWireBytes = 32 + + // tokenTransferMinWireBytes is the minimum BCS size of Any2SuiTokenTransfer: + // uleb128(0) source pool + 32 dest token + 4 dest gas + uleb128(0) extra data + 32 amount. + tokenTransferMinWireBytes = 70 ) // DecodeSuiJsonValue decodes Sui JSON response data into the provided target. @@ -423,15 +430,36 @@ func DeserializeExecutionReport(data []byte) (*ExecutionReport, error) { // 1. Read source_chain_selector (u64) sourceChainSelector := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sourceChainSelector: %w", err) + } // 2. Read message header messageID := make([]byte, 32) deserializer.ReadFixedBytesInto(messageID) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize messageID: %w", err) + } headerSourceChain := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize headerSourceChain: %w", err) + } + destChainSelector := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize destChainSelector: %w", err) + } + sequenceNumber := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sequenceNumber: %w", err) + } + nonce := deserializer.U64() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize nonce: %w", err) + } if sourceChainSelector != headerSourceChain { return nil, fmt.Errorf("source chain selector mismatch: %d != %d", sourceChainSelector, headerSourceChain) @@ -447,31 +475,68 @@ func DeserializeExecutionReport(data []byte) (*ExecutionReport, error) { // 3. Read sender (vector) sender := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sender: %w", err) + } // 4. Read data (vector) msgData := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize data: %w", err) + } // 5. Read receiver (address) receiver := deserializer.ReadFixedBytes(32) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize receiver: %w", err) + } // 6. Read gas_limit (u256) gasLimit := deserializer.U256() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize gas_limit: %w", err) + } tokenReceiver := [32]byte{} deserializer.ReadFixedBytesInto(tokenReceiver[:]) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize tokenReceiver: %w", err) + } // 7. Read token_amounts vector tokenAmountsLen := deserializer.Uleb128() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize token_amounts length: %w", err) + } + remaining := deserializer.Remaining() + if remaining < 0 || uint64(tokenAmountsLen)*tokenTransferMinWireBytes > uint64(remaining) { + return nil, fmt.Errorf("failed to deserialize execution report: token_amounts length %d exceeds remaining %d bytes", tokenAmountsLen, remaining) + } tokenAmounts := make([]Any2SuiTokenTransfer, tokenAmountsLen) for i := range tokenAmountsLen { sourcePoolAddr := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize sourcePoolAddr: %w", err) + } destToken := deserializer.ReadFixedBytes(32) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize destToken: %w", err) + } destGas := deserializer.U32() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize destGas: %w", err) + } extraData := deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize extraData: %w", err) + } amount := deserializer.U256() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize amount: %w", err) + } tokenAmounts[i] = Any2SuiTokenTransfer{ SourcePoolAddress: sourcePoolAddr, @@ -494,18 +559,37 @@ func DeserializeExecutionReport(data []byte) (*ExecutionReport, error) { // 8. Read offchain_token_data (vector>) offchainDataLen := deserializer.Uleb128() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize offchain_token_data length: %w", err) + } + if int(offchainDataLen) > deserializer.Remaining() { + return nil, fmt.Errorf("failed to deserialize execution report: offchain_token_data length %d exceeds remaining %d bytes", offchainDataLen, deserializer.Remaining()) + } offchainData := make([][]byte, offchainDataLen) for i := range offchainDataLen { offchainData[i] = deserializer.ReadBytes() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize offchainData at index %d: %w", i, err) + } } // 9. Read proofs (vector>) proofsLen := deserializer.Uleb128() + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize proofs length: %w", err) + } + remaining = deserializer.Remaining() + if remaining < 0 || uint64(proofsLen)*proofWireBytes > uint64(remaining) { + return nil, fmt.Errorf("failed to deserialize execution report: proofs length %d exceeds remaining %d bytes", proofsLen, remaining) + } proofs := make([][]byte, proofsLen) for i := range proofsLen { - proofs[i] = deserializer.ReadFixedBytes(32) + proofs[i] = deserializer.ReadFixedBytes(proofWireBytes) + if err := deserializer.Error(); err != nil { + return nil, fmt.Errorf("failed to deserialize proof at index %d: %w", i, err) + } } if err := deserializer.Error(); err != nil { diff --git a/relayer/codec/decoder_test.go b/relayer/codec/decoder_test.go index cf525401a..e2705b9a8 100644 --- a/relayer/codec/decoder_test.go +++ b/relayer/codec/decoder_test.go @@ -2697,6 +2697,169 @@ func TestCustomReportDeserializer(t *testing.T) { }) } +func TestDeserializeExecutionReport_RejectsOversizedVectorLength(t *testing.T) { + t.Parallel() + + // Build a minimal valid prefix up to token_amounts length, then inject a + // ULEB128 encoding of 0xFFFFFFFF. Without the bounds check this would + // trigger a ~343 GB allocation and a fatal OOM. + var buf []byte + + u64 := func(v uint64) { + b := make([]byte, 8) + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) + buf = append(buf, b...) + } + fixed32 := func() { buf = append(buf, make([]byte, 32)...) } + uleb := func(v uint32) { + for { + b := byte(v & 0x7f) + v >>= 7 + if v != 0 { + b |= 0x80 + } + buf = append(buf, b) + if v == 0 { + break + } + } + } + + u64(1) // sourceChainSelector + fixed32() // messageID + u64(1) // headerSourceChain (must match sourceChainSelector) + u64(2) // destChainSelector + u64(3) // sequenceNumber + u64(4) // nonce + uleb(0) // sender length (empty) + uleb(0) // data length (empty) + fixed32() // receiver + fixed32() // gasLimit (u256) + fixed32() // tokenReceiver + uleb(0xFFFFFFFF) // token_amounts length -- the malicious value + + report, err := DeserializeExecutionReport(buf) + require.Nil(t, report) + require.Error(t, err) + require.Contains(t, err.Error(), "token_amounts length") + require.Contains(t, err.Error(), "exceeds remaining") +} + +func TestDeserializeExecutionReport_RejectsTokenAmountsLengthExceedingWireBudget(t *testing.T) { + t.Parallel() + + var buf []byte + + u64 := func(v uint64) { + b := make([]byte, 8) + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) + buf = append(buf, b...) + } + fixed32 := func() { buf = append(buf, make([]byte, 32)...) } + uleb := func(v uint32) { + for { + b := byte(v & 0x7f) + v >>= 7 + if v != 0 { + b |= 0x80 + } + buf = append(buf, b) + if v == 0 { + break + } + } + } + + u64(1) // sourceChainSelector + fixed32() // messageID + u64(1) // headerSourceChain + u64(2) // destChainSelector + u64(3) // sequenceNumber + u64(4) // nonce + uleb(0) // sender + uleb(0) // data + fixed32() // receiver + fixed32() // gasLimit + fixed32() // tokenReceiver + uleb(100) // token_amounts length: 100 elements need 7000 bytes + buf = append(buf, make([]byte, 100)...) // only 100 bytes remain on the wire + + report, err := DeserializeExecutionReport(buf) + require.Nil(t, report) + require.Error(t, err) + require.Contains(t, err.Error(), "token_amounts length") + require.Contains(t, err.Error(), "exceeds remaining") +} + +func TestDeserializeExecutionReport_RejectsProofsLengthExceedingWireBudget(t *testing.T) { + t.Parallel() + + var buf []byte + + u64 := func(v uint64) { + b := make([]byte, 8) + b[0] = byte(v) + b[1] = byte(v >> 8) + b[2] = byte(v >> 16) + b[3] = byte(v >> 24) + b[4] = byte(v >> 32) + b[5] = byte(v >> 40) + b[6] = byte(v >> 48) + b[7] = byte(v >> 56) + buf = append(buf, b...) + } + fixed32 := func() { buf = append(buf, make([]byte, 32)...) } + uleb := func(v uint32) { + for { + b := byte(v & 0x7f) + v >>= 7 + if v != 0 { + b |= 0x80 + } + buf = append(buf, b) + if v == 0 { + break + } + } + } + + u64(1) // sourceChainSelector + fixed32() // messageID + u64(1) // headerSourceChain + u64(2) // destChainSelector + u64(3) // sequenceNumber + u64(4) // nonce + uleb(0) // sender + uleb(0) // data + fixed32() // receiver + fixed32() // gasLimit + fixed32() // tokenReceiver + uleb(0) // token_amounts + uleb(0) // offchain_token_data + uleb(10) // proofs length: 10 elements need 320 bytes + buf = append(buf, make([]byte, 100)...) // only 100 bytes remain on the wire + + report, err := DeserializeExecutionReport(buf) + require.Nil(t, report) + require.Error(t, err) + require.Contains(t, err.Error(), "proofs length") + require.Contains(t, err.Error(), "exceeds remaining") +} + func TestDeserializeExecutionReportFromPure(t *testing.T) { t.Parallel() diff --git a/relayer/monitor/metrics.go b/relayer/monitor/metrics.go index 5d10c9f40..a49d8f1b8 100644 --- a/relayer/monitor/metrics.go +++ b/relayer/monitor/metrics.go @@ -49,3 +49,27 @@ func (g *GaugeAccBalance) GetAttributes(account string, chainInfo config.ChainIn attribute.String("network_name_full", utils.ValOrUnknown(chainInfo.NetworkNameFull)), ) } + +// Define a metrics for RPC response speeds +type GaugeRPCTxDuration struct { + // rpc_tx_duration + gauge metric.Int64Gauge +} + +func NewGaugeRPCTxDuration(unitStr string) (*GaugeRPCTxDuration, error) { + name := "rpc_tx_duration" + description := "Duration of RPC transaction in milliseconds" + gauge, err := beholder.GetMeter().Int64Gauge(name, metric.WithUnit(unitStr), metric.WithDescription(description)) + if err != nil { + return nil, fmt.Errorf("failed to create new gauge %s: %+w", name, err) + } + + return &GaugeRPCTxDuration{gauge}, nil +} + +func (g *GaugeRPCTxDuration) Record(ctx context.Context, duration int64, method string, responseCode string) { + g.gauge.Record(ctx, duration, metric.WithAttributes( + attribute.String("method", method), + attribute.String("responseCode", responseCode), + )) +} diff --git a/relayer/testutils/node.go b/relayer/testutils/node.go index a82fb87c1..b57363ce4 100644 --- a/relayer/testutils/node.go +++ b/relayer/testutils/node.go @@ -77,54 +77,29 @@ func StartSuiNode(nodeType NodeEnvType) (*exec.Cmd, error) { } func waitForConnection(url string, timeout time.Duration, backoffDelay time.Duration) error { - // Parse the URL to extract host and port parsedURL, err := netUrl.Parse(url) if err != nil { return fmt.Errorf("invalid URL %s: %w", url, err) } - - host := parsedURL.Host - if host == "" { - // Handle case where URL might just be "host:port" - host = parsedURL.Path - } - - // Add default port if missing - if !strings.Contains(host, ":") { - if parsedURL.Scheme == "https" { - host += ":443" - } else { - host += ":80" - } + if parsedURL.Host == "" { + return fmt.Errorf("invalid URL %s: missing host", url) } - // Use exponential backoff for retries deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + d := net.Dialer{ + Timeout: 5 * time.Second, + } - for attempt := 1; time.Now().Before(deadline); attempt++ { - conn, err := net.DialTimeout("tcp", host, 1*time.Second) + conn, err := d.Dial("tcp", parsedURL.Host) if err == nil { conn.Close() return nil } - - // Calculate next backoff with exponential increase - nextBackoff := backoffDelay * time.Duration(attempt) - - // Don't sleep longer than remaining time - remainingTime := time.Until(deadline) - if remainingTime < nextBackoff { - nextBackoff = remainingTime - } - - if remainingTime <= 0 { - break - } - - time.Sleep(nextBackoff) + time.Sleep(backoffDelay) } - return fmt.Errorf("timed out waiting for %s after %s", host, timeout) + return fmt.Errorf("timed out waiting for %s after %s", parsedURL.Host, timeout) } func GetFaucetHost(network string) string { diff --git a/relayer/txm/broadcaster.go b/relayer/txm/broadcaster.go index 1179a6fee..dd4098788 100644 --- a/relayer/txm/broadcaster.go +++ b/relayer/txm/broadcaster.go @@ -132,7 +132,7 @@ func broadcastTransactions(loopCtx context.Context, txm *SuiTxm, transactions [] continue } - txm.lggr.Infow("Transaction broadcasted", "response", resp, "txID", tx.TransactionID) + txm.lggr.Infow("Transaction broadcasted", "txID", tx.TransactionID) err = txm.transactionRepository.UpdateTransactionDigest(tx.TransactionID, resp.GetTransaction().GetDigest()) if err != nil { diff --git a/scripts/go.mod b/scripts/go.mod index c11b29fc6..87e2b61f9 100644 --- a/scripts/go.mod +++ b/scripts/go.mod @@ -13,7 +13,7 @@ require github.com/smartcontractkit/chainlink-sui/deployment v0.0.0-000101010000 require ( filippo.io/edwards25519 v1.1.1 // indirect github.com/BurntSushi/toml v1.5.0 // indirect - github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Masterminds/semver/v3 v3.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect github.com/XSAM/otelsql v0.37.0 // indirect @@ -73,7 +73,7 @@ require ( github.com/grafana/pyroscope-go/godeltaprof v0.1.9 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-plugin v1.8.0 // indirect github.com/hashicorp/yamux v0.1.2 // indirect @@ -122,23 +122,23 @@ require ( github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 // indirect github.com/smartcontractkit/chain-selectors v1.0.100 // indirect github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc // indirect - github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d // indirect - github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 // indirect + github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 // indirect github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16 // indirect github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 // indirect github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 // indirect - github.com/smartcontractkit/chainlink-deployments-framework v0.98.0 // indirect + github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a // indirect github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 // indirect github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 // indirect github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b // indirect github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 // indirect - github.com/smartcontractkit/chainlink-sui v0.0.0-20260527160341-aa3adc0abf67 // indirect + github.com/smartcontractkit/chainlink-sui v0.0.0 // indirect github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 // indirect github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 // indirect github.com/smartcontractkit/freeport v0.1.3-0.20250828155247-add56fa28aad // indirect github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 // indirect github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e // indirect - github.com/smartcontractkit/mcms v0.42.0 // indirect + github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d // indirect github.com/spf13/cast v1.10.0 // indirect github.com/stellar/go-stellar-sdk v0.5.0 // indirect github.com/stellar/go-xdr v0.0.0-20260312225820-cc2b0611aabf // indirect @@ -162,9 +162,9 @@ require ( go.mongodb.org/mongo-driver v1.17.2 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect @@ -173,12 +173,12 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.19.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/otel/log v0.20.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -187,17 +187,17 @@ require ( go.yaml.in/yaml/v2 v2.4.2 // indirect golang.org/x/crypto v0.51.0 // indirect golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect - golang.org/x/net v0.54.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.44.0 // indirect + golang.org/x/sys v0.45.0 // indirect golang.org/x/term v0.43.0 // indirect golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.45.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/grpc v1.81.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/scripts/go.sum b/scripts/go.sum index 67a79eb03..65b0c86dd 100644 --- a/scripts/go.sum +++ b/scripts/go.sum @@ -13,8 +13,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= -github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= -github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= +github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU= @@ -28,20 +28,26 @@ github.com/apache/arrow-go/v18 v18.3.1 h1:oYZT8FqONiK74JhlH3WKVv+2NKYoyZ7C2ioD4D github.com/apache/arrow-go/v18 v18.3.1/go.mod h1:12QBya5JZT6PnBihi5NJTzbACrDGXYkrgjujz3MRQXU= github.com/aptos-labs/aptos-go-sdk v1.13.0 h1:epv7K/tIbAEO2RfogwGacICBig8rrigJj24fDsy6KTg= github.com/aptos-labs/aptos-go-sdk v1.13.0/go.mod h1:FTgKp0RLfEefllCdkCj0jPU14xWk11yA7SFVfCDLUj8= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/avast/retry-go/v4 v4.7.0 h1:yjDs35SlGvKwRNSykujfjdMxMhMQQM0TnIjJaHB+Zio= github.com/avast/retry-go/v4 v4.7.0/go.mod h1:ZMPDa3sY2bKgpLtap9JRUgk2yTAba7cgiFhqxY2Sg6Q= -github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k= -github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= +github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ= +github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk= +github.com/aws/aws-sdk-go-v2 v1.41.11 h1:9PRf7jyTMEUM6fuNRAJa2mO/skJfrF50rENJwf2LXqw= +github.com/aws/aws-sdk-go-v2 v1.41.11/go.mod h1:iiUX27gOXRuYaoeUVXhUpPwjJHzISfPAjjcuhUbLSVs= github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0= github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g= github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8= github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 h1:8sPbKi1/KRHwl5oR3qN9mUXestCeHuaRutxylnr/eVY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27/go.mod h1:QV9IVIopJ1dpQUno0f9VYDUwOEjj8u0iEJ4JiZVre3Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 h1:9d8AoASQY9UwrOSmiJ7uSM0MGUPFhnenwSvpaFfat2c= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27/go.mod h1:x0rldpsnUQaQIs4Rh+Vwm9Z/0vI6BxadGtsgJfZFb8s= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= @@ -58,8 +64,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU= github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk= -github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= -github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.0 h1:ZoFioDKJxkSIW2otF9T0aPtNlUwhdVCcuZh/rzH9Hus= +github.com/aws/smithy-go v1.27.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -185,8 +191,6 @@ github.com/digital-asset/dazl-client/v8 v8.9.0 h1:F2qTUWtHAjhGyRGV+xTim+VAFwM99F github.com/digital-asset/dazl-client/v8 v8.9.0/go.mod h1:q1KevCJ8FpH8je2MnnjN8/QUfhstB4fKpyKyqDtqFh0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -342,8 +346,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpS github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 h1:sGm2vDRFUrQJO/Veii4h4zG2vvqG6uWNkBHSTqXOZk0= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2/go.mod h1:wd1YpapPLivG6nQgbf7ZkG1hhSOXDhhn4MLTknx2aAc= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -395,6 +399,8 @@ github.com/jhump/protoreflect v1.17.0 h1:qOEr613fac2lOuTgWN4tPAtLL7fUSbuJL5X5Xum github.com/jhump/protoreflect v1.17.0/go.mod h1:h9+vUUL38jiBzck8ck+6G/aeMX8Z4QUY/NiJPwPNi+8= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -467,8 +473,12 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8= github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4= +github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs= +github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw= +github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g= +github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U= +github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y= github.com/moby/spdystream v0.5.1/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= @@ -484,8 +494,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= -github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1 h1:mPMvm6X6tf4w8y7j9YIt6V9jfWhL6QlbEc7CCmeQlWk= github.com/mostynb/zstdpool-freelist v0.0.0-20201229113212-927304c0c3b1/go.mod h1:ye2e/VUEtE2BHE+G/QcKkcLQVAEJoYRFj5VUOQatCRE= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -572,8 +582,8 @@ github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2 h1:S4OC0+OBK github.com/segmentio/go-loggly v0.5.1-0.20171222203950-eb91657e62b2/go.mod h1:8zLRYR5npGjaOXgPSKat5+oOh+UHd8OdbS18iqX9F6Y= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil/v4 v4.26.2 h1:X8i6sicvUFih4BmYIGT1m2wwgw2VG9YgrDTi7cIRGUI= -github.com/shirou/gopsutil/v4 v4.26.2/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= +github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= @@ -581,24 +591,26 @@ github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 h1:aQKxg3+2p+IFXXg97M github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3/go.mod h1:9/etS5gpQq9BJsJMWg1wpLbfuSnkm8dPF6FdW2JXVhA= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/smartcontractkit/ccip-owner-contracts v0.1.0 h1:GiBDtlx7539o7AKlDV+9LsA7vTMPv+0n7ClhSFnZFAk= +github.com/smartcontractkit/ccip-owner-contracts v0.1.0/go.mod h1:NnT6w4Kj42OFFXhSx99LvJZWPpMjmo4+CpDEWfw61xY= github.com/smartcontractkit/chain-selectors v1.0.100 h1:wpiSpmI/eFjY+wx/nPr5VuNF4hki0prIBMKEaQWn3g4= github.com/smartcontractkit/chain-selectors v1.0.100/go.mod h1:qy7whtgG5g+7z0jt0nRyii9bLND9m15NZTzuQPkMZ5w= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc h1:Um9FBcf0JNSFuGbxgccDG1vM3cNrMGy0SdJ7r6VbX0o= github.com/smartcontractkit/chainlink-aptos v0.0.0-20260428085939-5c70de12dbfc/go.mod h1:zfE2R7887kiwXkGTHKPe5NBgwhFwIC3pnA2uAxrbvig= github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1 h1:p0nFrTYrOQzDhWYm6suaM5CoWiXV5NV7llHnp6/Kn/8= github.com/smartcontractkit/chainlink-ccip v0.1.1-solana.0.20260428205619-2db1389501a1/go.mod h1:1XxxpkgCmG/z6y30yRuVrcxre6zixIVX3xzi706Db/8= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d h1:xdFpzbApEMz4Rojg2Y2OjFlrh0wu7eB10V2tSZGW5y8= -github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260121163256-85accaf3d28d/go.mod h1:bgmqE7x9xwmIVr8PqLbC0M5iPm4AV2DBl596lO6S5Sw= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5 h1:Z4t2ZY+ZyGWxtcXvPr11y4o3CGqhg3frJB5jXkCSvWA= -github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20250912190424-fd2e35d7deb5/go.mod h1:xtZNi6pOKdC3sLvokDvXOhgHzT+cyBqH/gWwvxTxqrg= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139 h1:jkChf04hhdiMBApbb+lLDxHMY62Md6UeM7v++GSw3K8= +github.com/smartcontractkit/chainlink-ccip/chains/solana v0.0.0-20260129103204-4c8453dd8139/go.mod h1:wuhagkM/lU0GbV2YcrROOH0GlsfXJYwm6qmpa4CK70w= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139 h1:tw3K4UkH5XfW5SoyYkvAlbzrccoGSLdz/XkxD6nyGC8= +github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings v0.0.0-20260129103204-4c8453dd8139/go.mod h1:1WcontO9PeuKdUf5HXfs3nuICtzUvFNnyCmrHkTCF9Y= github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16 h1:kG7DnjoCDJUt2htCqVxTA4IvQyR+a6mOmqlG1v7KMRE= github.com/smartcontractkit/chainlink-ccip/deployment v0.0.0-20260311190822-5cbfc939dd16/go.mod h1:kMRGxNzyB5O6sqQlJEgBG/g49mzRvlcqbqMrzlhL+JY= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89 h1:5z3LQ27MJmhiaeqp9S2TzbF5Wm4GGvUKAYOtE9AauR8= github.com/smartcontractkit/chainlink-common v0.11.2-0.20260506120607-7f10be016c89/go.mod h1:G2AII0QmWzXx8Ag9IKnGN3h/gwwNnhHUOCviJievdvo= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10 h1:FJAFgXS9oqASnkS03RE1HQwYQQxrO4l46O5JSzxqLgg= github.com/smartcontractkit/chainlink-common/pkg/chipingress v0.0.10/go.mod h1:oiDa54M0FwxevWwyAX773lwdWvFYYlYHHQV1LQ5HpWY= -github.com/smartcontractkit/chainlink-deployments-framework v0.98.0 h1:Ov/KOEtubOHXX8oa9UtARhHmkQNCOIjWNt+Zi0AuzHM= -github.com/smartcontractkit/chainlink-deployments-framework v0.98.0/go.mod h1:24dwRW1PYolrlxSth///ddG3auGqR+50xaJiXfUHhkg= +github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a h1:z23/Q2MhxA2WJEOzX9s9H+TB8nU+U3qotwGPKLAOoPY= +github.com/smartcontractkit/chainlink-deployments-framework v0.109.1-0.20260604174622-e26b8cddfa0a/go.mod h1:OdH025Wn1W4UXny4TeczG3K+b0acF0+OjInmzQt+po0= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735 h1:5bxDnwI0wuPoC0H5H3H2n9CnQPb5iakR6UmAY4j8KUg= github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260505131349-78e491b80735/go.mod h1:Jqt53s27Tr0jDl8mdBXg1xhu6F8Fci8JOuq43tgHOM8= github.com/smartcontractkit/chainlink-protos/job-distributor v0.18.0 h1:q+VDPcxWrj5k9QizSYfUOSMnDH3Sd5HvbPguZOgfXTY= @@ -607,10 +619,12 @@ github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-202510021 github.com/smartcontractkit/chainlink-protos/linking-service/go v0.0.0-20251002192024-d2ad9222409b/go.mod h1:qSTSwX3cBP3FKQwQacdjArqv0g6QnukjV4XuzO6UyoY= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9 h1:hhevsu8k7tlDRrYZmgAh7V4avGQDMvus1bwIlial3Ps= github.com/smartcontractkit/chainlink-protos/node-platform v0.0.0-20260211172625-dff40e83b3c9/go.mod h1:dkR2uYg9XYJuT1JASkPzWE51jjFkVb86P7a/yXe5/GM= -github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4 h1:AEnxv4HM3WD1RbQkRiFyb9cJ6YKAcqBp1CpIcFdZfuo= -github.com/smartcontractkit/chainlink-protos/op-catalog v0.0.4/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.19 h1:inTH0/PrEaVv4iLdGsdcrP/rX7KMrq/Roosr5nIA8io= -github.com/smartcontractkit/chainlink-testing-framework/framework v0.15.19/go.mod h1:BALK9cj8sk12e15UF6uDhifHgIApa+6N11TcQfInEro= +github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0 h1:hGEJFD2X3oNIPXQbtIPxCJyg5CcKglRCYBmESS+gmeQ= +github.com/smartcontractkit/chainlink-protos/op-catalog v0.1.0/go.mod h1:PjZD54vr6rIKEKQj6HNA4hllvYI/QpT+Zefj3tqkFAs= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4 h1:8M+2pA0qx9rXaxmpKouUHj983vQCGzztHkG0XjE5Eew= +github.com/smartcontractkit/chainlink-testing-framework/framework v0.16.4/go.mod h1:nyOjn4ADJGqRMe3+4ZXSV+J/7nWb1H2Vx8Qk57eLRYA= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5 h1:RwZXxdIAOyjp6cwc9Quxgr38k8r7ACz+Lxh9o/A6oH0= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.51.5/go.mod h1:kHYJnZUqiPF7/xN5273prV+srrLJkS77GbBXHLKQpx0= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745 h1:eieKLvYuzwBPh/FdbUS1gnIanI86zgWby1L10o90g4o= github.com/smartcontractkit/chainlink-ton v1.0.5-0.20260514223130-48bc90aca745/go.mod h1:8vXLeG//BxDF86GWRytzGIy6jc70htD1r/KtPfjrsK0= github.com/smartcontractkit/chainlink-tron/relayer v0.0.11-0.20250908203554-5bd9d2fe9513 h1:XRNxgcNqagXu6e4smJuS1crRK5cUAcCVd7u+iLduHDM= @@ -623,8 +637,8 @@ github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7 h1:12i github.com/smartcontractkit/grpc-proxy v0.0.0-20240830132753-a7e17fec5ab7/go.mod h1:FX7/bVdoep147QQhsOPkYsPEXhGZjeYx6lBSaSXtZOA= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e h1:poXTj5cFVM6XfC4HICIDYkDVc/A6OYB0eeID0wU2JQE= github.com/smartcontractkit/libocr v0.0.0-20260304194147-a03701e2c02e/go.mod h1:PLdNK6GlqfxIWXzziPkU7dCAVlVFeYkyyW7AQY0R+4Q= -github.com/smartcontractkit/mcms v0.42.0 h1:zzs+auX6BL6sRIVpgVbLUviwrvWi8Fxo5dOP+9Wx/gk= -github.com/smartcontractkit/mcms v0.42.0/go.mod h1:39OxzRApGN7HG+JGbjxdCxyo5lvV0H0REUPyh3CzDGU= +github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d h1:lMJ0pY2mUtDqUl+CraggP9k9396VgihfX1RdfyUBLy8= +github.com/smartcontractkit/mcms v0.45.2-0.20260604181544-da0bd7da623d/go.mod h1:R+h5CZoiZoxGToN4hvQwbb1dsKgoZ+gUOgWTNDxGsDQ= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= @@ -659,8 +673,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDd github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/testcontainers/testcontainers-go v0.41.0 h1:mfpsD0D36YgkxGj2LrIyxuwQ9i2wCKAD+ESsYM1wais= -github.com/testcontainers/testcontainers-go v0.41.0/go.mod h1:pdFrEIfaPl24zmBjerWTTYaY0M6UHsqA1YSvsoU40MI= +github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY= +github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30= github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0 h1:AOtFXssrDlLm84A2sTTR/AhvJiYbrIuCO59d+Ro9Tb0= github.com/testcontainers/testcontainers-go/modules/postgres v0.41.0/go.mod h1:k2a09UKhgSp6vNpliIY0QSgm4Hi7GXVTzWvWgUemu/8= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -709,12 +723,12 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= @@ -731,23 +745,25 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmc go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= -go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= -go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= +go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs= +go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= -go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY= +go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -808,8 +824,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= @@ -849,8 +865,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= -golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -890,17 +906,17 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210401141331-865547bb08e2/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw= -google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/sui.nix b/sui.nix index e5c1d2631..8ee0225bb 100644 --- a/sui.nix +++ b/sui.nix @@ -9,7 +9,7 @@ stdenv.mkDerivation rec { src = if stdenv.hostPlatform.isDarwin then pkgs.fetchzip { url = "https://github.com/MystenLabs/sui/releases/download/mainnet-v${version}/sui-mainnet-v${version}-macos-arm64.tgz"; # Assume is a M1 Mac - sha256 = "sha256-rtONzChS4tJmqCXWtW6fceb10xNQyZUwcFJMMTT+ZAE="; # Should be replaced when bumping versions + sha256 = "sha256-fSIt7V78vLJQfWKvzwJn6A+ER0JjvWHTDw7hdgdPSUc="; # Should be replaced when bumping versions stripRoot = false; } else if stdenv.isLinux then