From 49c2674d936c052a5f7e77ccd0154ad6a95d02b4 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Tue, 14 Apr 2026 11:13:53 -0500 Subject: [PATCH 01/10] implement serializeExtraArgsSVMV1 --- build/devenv/evm/impl.go | 48 +++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/build/devenv/evm/impl.go b/build/devenv/evm/impl.go index bc27c5757..c25003f9d 100644 --- a/build/devenv/evm/impl.go +++ b/build/devenv/evm/impl.go @@ -861,20 +861,42 @@ func serializeExtraArgsV3(opts cciptestinterfaces.MessageOptions) []byte { return extraArgs } -func serializeExtraArgsSVMV1(_ cciptestinterfaces.MessageOptions) []byte { - // // Extra args tag for chains that use the Solana VM. - // bytes4 public constant SVM_EXTRA_ARGS_V1_TAG = 0x1f3b3aba; +func serializeExtraArgsSVMV1(opts cciptestinterfaces.MessageOptions) []byte { + svmExtraArgsV1Type, err := abi.NewType("tuple", "SVMExtraArgsV1", []abi.ArgumentMarshaling{ + {Name: "computeUnits", Type: "uint32"}, + {Name: "accountIsWritableBitmap", Type: "uint64"}, + {Name: "allowOutOfOrderExecution", Type: "bool"}, + {Name: "tokenReceiver", Type: "bytes32"}, + {Name: "accounts", Type: "bytes32[]"}, + }) + if err != nil { + panic(fmt.Sprintf("failed to create SVMExtraArgsV1 tuple type: %v", err)) + } + + arguments := abi.Arguments{{Type: svmExtraArgsV1Type, Name: "extraArgs"}} - // struct SVMExtraArgsV1 { - // uint32 computeUnits; - // uint64 accountIsWritableBitmap; - // bool allowOutOfOrderExecution; - // bytes32 tokenReceiver; - // // Additional accounts needed for execution of CCIP receiver. Must be empty if message.receiver is zero. - // // Token transfer related accounts are specified in the token pool lookup table on SVM. - // bytes32[] accounts; - // } - return nil // TODO: implement when solana ported to 1.7 tests. + type SVMExtraArgsV1 struct { + ComputeUnits uint32 + AccountIsWritableBitmap uint64 + AllowOutOfOrderExecution bool + TokenReceiver [32]byte + Accounts [][32]byte + } + + packed, err := arguments.Pack(SVMExtraArgsV1{ + ComputeUnits: uint32(opts.ExecutionGasLimit), //nolint:gosec + AccountIsWritableBitmap: 0, + AllowOutOfOrderExecution: opts.OutOfOrderExecution, + TokenReceiver: [32]byte{}, + Accounts: [][32]byte{}, + }) + if err != nil { + panic(fmt.Sprintf("failed to pack SVMExtraArgsV1: %v", err)) + } + + // bytes4 public constant SVM_EXTRA_ARGS_V1_TAG = 0x1f3b3aba; + tag, _ := hexutil.Decode("0x1f3b3aba") + return append(tag, packed...) } func (m *CCIP17EVM) ExposeMetrics( From 5bcd368756720bbfbb731c513eadf318188ad70c Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Tue, 14 Apr 2026 16:23:22 -0500 Subject: [PATCH 02/10] add tokenReceiverAllowed field to PartialRemoteChainConfig --- build/devenv/cciptestinterfaces/interface.go | 6 ++++++ build/devenv/implcommon.go | 1 + 2 files changed, 7 insertions(+) diff --git a/build/devenv/cciptestinterfaces/interface.go b/build/devenv/cciptestinterfaces/interface.go index 8a54f2f4f..a267d8782 100644 --- a/build/devenv/cciptestinterfaces/interface.go +++ b/build/devenv/cciptestinterfaces/interface.go @@ -218,6 +218,12 @@ type ChainLaneProfile struct { DefaultInboundCCVs []datastore.AddressRef DefaultOutboundCCVs []datastore.AddressRef + // TokenReceiverAllowed controls whether the OnRamp on the source chain allows a + // non-empty tokenReceiver in extraArgs for messages destined to this chain. + // Required for SVM destinations where tokenReceiver is always present in SVMExtraArgsV1. + // When nil, the existing on-chain value is preserved (defaults to false on fresh deployments). + TokenReceiverAllowed *bool + GasForVerification uint32 } diff --git a/build/devenv/implcommon.go b/build/devenv/implcommon.go index 4ed1a972d..6a9902ca4 100644 --- a/build/devenv/implcommon.go +++ b/build/devenv/implcommon.go @@ -261,6 +261,7 @@ func buildPartialChainConfig( ExecutorDestChainConfig: local.ExecutorDestChainConfig, AddressBytesLength: remote.AddressBytesLength, BaseExecutionGasCost: remote.BaseExecutionGasCost, + TokenReceiverAllowed: remote.TokenReceiverAllowed, } } From 9b30aaaea65bd26169a4ec571899400dff87763e Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Wed, 15 Apr 2026 18:37:22 -0500 Subject: [PATCH 03/10] update after refactor --- build/devenv/evm/impl.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build/devenv/evm/impl.go b/build/devenv/evm/impl.go index 0c438108e..934e56b83 100644 --- a/build/devenv/evm/impl.go +++ b/build/devenv/evm/impl.go @@ -123,6 +123,7 @@ func init() { // provides backward compatibility with the previous FamilyEVM/FamilyCanton // combined switch case. cciptestinterfaces.RegisterExtraArgsSerializer(chainsel.FamilyCanton, SerializeEVMExtraArgs) + cciptestinterfaces.RegisterExtraArgsSerializer(chainsel.FamilySolana, serializeExtraArgsSVMV1) } type CCIP17EVMConfig struct { @@ -790,6 +791,16 @@ func SerializeEVMExtraArgs(opts cciptestinterfaces.MessageOptions) []byte { } } +// SerializeSVMExtraArgs is the Solana family's ExtraArgsSerializer, handling versions 1. +func SerializeSVMExtraArgs(opts cciptestinterfaces.MessageOptions) []byte { + switch opts.Version { + case 1: + return serializeExtraArgsSVMV1(opts) + default: + panic(fmt.Sprintf("unsupported EVM message extra args version: %d", opts.Version)) + } +} + func serializeExtraArgsV1(opts cciptestinterfaces.MessageOptions) []byte { evmExtraArgsV1Type, err := abi.NewType("tuple", "EVMExtraArgsV1", []abi.ArgumentMarshaling{ {Name: "gasLimit", Type: "uint256"}, From 63cc1e71238aa238e1e2829d4cb4e9f07846f4f2 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Wed, 15 Apr 2026 18:43:26 -0500 Subject: [PATCH 04/10] update --- build/devenv/evm/impl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/devenv/evm/impl.go b/build/devenv/evm/impl.go index 934e56b83..cc8c414c1 100644 --- a/build/devenv/evm/impl.go +++ b/build/devenv/evm/impl.go @@ -900,7 +900,7 @@ func serializeExtraArgsSVMV1(opts cciptestinterfaces.MessageOptions) []byte { } packed, err := arguments.Pack(SVMExtraArgsV1{ - ComputeUnits: uint32(opts.ExecutionGasLimit), //nolint:gosec + ComputeUnits: opts.ExecutionGasLimit, AccountIsWritableBitmap: 0, AllowOutOfOrderExecution: opts.OutOfOrderExecution, TokenReceiver: [32]byte{}, From 27f600cf065e7c5b93901a92463ad8223f381df2 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Wed, 15 Apr 2026 20:05:02 -0500 Subject: [PATCH 05/10] fix isEVM error --- integration/pkg/accessors/evm/factory_constructor.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/integration/pkg/accessors/evm/factory_constructor.go b/integration/pkg/accessors/evm/factory_constructor.go index 7c143c55f..04befdb1e 100644 --- a/integration/pkg/accessors/evm/factory_constructor.go +++ b/integration/pkg/accessors/evm/factory_constructor.go @@ -41,17 +41,15 @@ func CreateEVMAccessorFactory(lggr logger.Logger, genericConfig chainaccess.Gene // TODO: This could be a helper on the generic config object. for _, selector := range genericConfig.ChainConfig.GetAllChainSelectors() { // Verify chain family. - isEvm, err := chainsel.IsEvm(uint64(selector)) - if err != nil { - return nil, fmt.Errorf("failed to determine if selector(%d) is evm: %w", selector, err) - } + // TODO IsEvm() will be fixed in chain-selectors/pull/186 + isEvm, _ := chainsel.IsEvm(uint64(selector)) if !isEvm { lggr.Debugw("skipping non-EVM chain selector in EVM accessor factory construction", "chainSelector", selector) continue } var info Info - if err = genericConfig.GetConcreteConfig(selector, &info); err != nil { + if err := genericConfig.GetConcreteConfig(selector, &info); err != nil { return nil, fmt.Errorf("failed to decode EVM info for selector(%d): %w", selector, err) } From dc8bdd19f0eb55b87267cb9e8fbba4922df2ba85 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Wed, 15 Apr 2026 20:47:17 -0500 Subject: [PATCH 06/10] filter blockchain outputs by chain family when proposing verifier and executor jobs --- build/devenv/environment.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 1c917e5de..5054ad02f 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -1930,7 +1930,7 @@ func proposeJobsToExecutors( return fmt.Errorf("failed to get chain config loader for family %s: %w", exec.ChainFamily, err) } - blockchainInfos, err := loader(blockchainOutputs) + blockchainInfos, err := loader(filterOutputsByFamily(blockchainOutputs, exec.ChainFamily)) if err != nil { return fmt.Errorf("failed to load chain config for family %s: %w", exec.ChainFamily, err) } @@ -2259,7 +2259,7 @@ func proposeJobsToStandaloneVerifiers( return fmt.Errorf("failed to get chain config loader for family %s: %w", ver.ChainFamily, err) } - blockchainInfos, err := loader(blockchainOutputs) + blockchainInfos, err := loader(filterOutputsByFamily(blockchainOutputs, ver.ChainFamily)) if err != nil { return fmt.Errorf("failed to load chain config for family %s: %w", ver.ChainFamily, err) } @@ -2298,3 +2298,14 @@ func proposeJobsToStandaloneVerifiers( return g.Wait() } + +// filterOutputsByFamily returns only the blockchain outputs matching the given chain family. +func filterOutputsByFamily(outputs []*blockchain.Output, family string) []*blockchain.Output { + var filtered []*blockchain.Output + for _, out := range outputs { + if out.Family == family { + filtered = append(filtered, out) + } + } + return filtered +} From 11da650af6bde6dc3f94de46b31dcaf3dac49e30 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Thu, 16 Apr 2026 14:00:37 -0500 Subject: [PATCH 07/10] update --- build/devenv/evm/impl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/devenv/evm/impl.go b/build/devenv/evm/impl.go index cc8c414c1..eb5247c6c 100644 --- a/build/devenv/evm/impl.go +++ b/build/devenv/evm/impl.go @@ -123,7 +123,7 @@ func init() { // provides backward compatibility with the previous FamilyEVM/FamilyCanton // combined switch case. cciptestinterfaces.RegisterExtraArgsSerializer(chainsel.FamilyCanton, SerializeEVMExtraArgs) - cciptestinterfaces.RegisterExtraArgsSerializer(chainsel.FamilySolana, serializeExtraArgsSVMV1) + cciptestinterfaces.RegisterExtraArgsSerializer(chainsel.FamilySolana, SerializeSVMExtraArgs) } type CCIP17EVMConfig struct { From f5483e3c7742418f8e9322b1f35b736be7bbf70e Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Thu, 16 Apr 2026 21:13:30 -0500 Subject: [PATCH 08/10] fix lint --- build/devenv/cciptestinterfaces/interface.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/devenv/cciptestinterfaces/interface.go b/build/devenv/cciptestinterfaces/interface.go index 2d77c6e20..696d3c250 100644 --- a/build/devenv/cciptestinterfaces/interface.go +++ b/build/devenv/cciptestinterfaces/interface.go @@ -220,7 +220,7 @@ type ChainLaneProfile struct { DefaultExecutorQualifier string DefaultInboundCCVs []datastore.AddressRef DefaultOutboundCCVs []datastore.AddressRef - TokenReceiverAllowed *bool + TokenReceiverAllowed *bool GasForVerification *uint32 AllowedFinalityConfig *finality.Config } From 7b0e08f5abc21453523b894fa2c8fb5f7cccbfd3 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Mon, 20 Apr 2026 18:19:01 -0500 Subject: [PATCH 09/10] remove filterOutputsByFamily --- build/devenv/environment.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 07217386f..843300aa0 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -1930,7 +1930,7 @@ func proposeJobsToExecutors( return fmt.Errorf("failed to get chain config loader for family %s: %w", exec.ChainFamily, err) } - blockchainInfos, err := loader(filterOutputsByFamily(blockchainOutputs, exec.ChainFamily)) + blockchainInfos, err := loader(blockchainOutputs) if err != nil { return fmt.Errorf("failed to load chain config for family %s: %w", exec.ChainFamily, err) } @@ -2259,7 +2259,7 @@ func proposeJobsToStandaloneVerifiers( return fmt.Errorf("failed to get chain config loader for family %s: %w", ver.ChainFamily, err) } - blockchainInfos, err := loader(filterOutputsByFamily(blockchainOutputs, ver.ChainFamily)) + blockchainInfos, err := loader(blockchainOutputs) if err != nil { return fmt.Errorf("failed to load chain config for family %s: %w", ver.ChainFamily, err) } From 21c9c4d0d55dec5c2f4bbc122629594e8f98ece1 Mon Sep 17 00:00:00 2001 From: Joe Huang Date: Mon, 20 Apr 2026 19:26:43 -0500 Subject: [PATCH 10/10] rm unused, and filter evm only --- build/devenv/environment.go | 11 ----------- build/devenv/services/chainconfig/evm.go | 3 +++ 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/build/devenv/environment.go b/build/devenv/environment.go index 843300aa0..174fc8d88 100644 --- a/build/devenv/environment.go +++ b/build/devenv/environment.go @@ -2298,14 +2298,3 @@ func proposeJobsToStandaloneVerifiers( return g.Wait() } - -// filterOutputsByFamily returns only the blockchain outputs matching the given chain family. -func filterOutputsByFamily(outputs []*blockchain.Output, family string) []*blockchain.Output { - var filtered []*blockchain.Output - for _, out := range outputs { - if out.Family == family { - filtered = append(filtered, out) - } - } - return filtered -} diff --git a/build/devenv/services/chainconfig/evm.go b/build/devenv/services/chainconfig/evm.go index e9bfc458c..e8e47e77a 100644 --- a/build/devenv/services/chainconfig/evm.go +++ b/build/devenv/services/chainconfig/evm.go @@ -14,6 +14,9 @@ import ( func EVMChainConfigLoader(outputs []*ctfblockchain.Output) (map[string]any, error) { infos := make(map[string]any) for _, output := range outputs { + if output.Family != chainsel.FamilyEVM { + continue + } info := &evm.Info{ ChainID: output.ChainID, Type: output.Type,