From 0931df5b2b7cac7767e05880bd1754aa5f89a4b4 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Thu, 5 Mar 2026 16:18:00 +0100 Subject: [PATCH 1/5] Test additions --- .../changeset/testhelpers/test_adapter_sui.go | 3 +- .../changeset/testhelpers/test_sui_helpers.go | 167 +++++++++- .../smoke/ccip/ccip_sui_fees_test.go | 290 ++++++++++++++++++ 3 files changed, 442 insertions(+), 18 deletions(-) create mode 100644 integration-tests/smoke/ccip/ccip_sui_fees_test.go diff --git a/deployment/ccip/changeset/testhelpers/test_adapter_sui.go b/deployment/ccip/changeset/testhelpers/test_adapter_sui.go index c62d6ebe932..9d1672b5fa0 100644 --- a/deployment/ccip/changeset/testhelpers/test_adapter_sui.go +++ b/deployment/ccip/changeset/testhelpers/test_adapter_sui.go @@ -59,8 +59,7 @@ func (a *SuiAdapter) BuildMessage(components MessageComponents) (any, error) { } func (a *SuiAdapter) NativeFeeToken() string { - // TODO: - return "" + return "native" } func (a *SuiAdapter) GetExtraArgs(receiver []byte, sourceFamily string, opts ...ExtraArgOpt) ([]byte, error) { diff --git a/deployment/ccip/changeset/testhelpers/test_sui_helpers.go b/deployment/ccip/changeset/testhelpers/test_sui_helpers.go index cd3e0f702be..2f863237218 100644 --- a/deployment/ccip/changeset/testhelpers/test_sui_helpers.go +++ b/deployment/ccip/changeset/testhelpers/test_sui_helpers.go @@ -2,6 +2,7 @@ package testhelpers import ( "context" + "encoding/base64" "encoding/hex" "errors" "fmt" @@ -47,13 +48,15 @@ import ( const TokenSymbolLINK = "LINK" type SuiSendRequest struct { - Receiver []byte - Data []byte - ExtraArgs []byte - FeeToken string - FeeTokenStore string - TokenAmounts []SuiTokenAmount - TokenReceiverATA []byte + Receiver []byte + Data []byte + ExtraArgs []byte + FeeToken string + FeeTokenStore string + FeeTokenCoinType string // optional: e.g. "0x2::sui::SUI"; defaults to LINK if empty + FeeTokenMetadataId string // optional: CoinMetadata object ID for the fee token; defaults to LINK if empty + TokenAmounts []SuiTokenAmount + TokenReceiverATA []byte } type SuiTokenAmount struct { @@ -155,14 +158,32 @@ func SendSuiCCIPRequest(e cldf.Environment, cfg *ccipclient.CCIPSendReqConfig) ( // getValidatedFee msg := cfg.Message.(SuiSendRequest) + // Resolve fee token coin type and metadata: use provided values or default to LINK + feeTokenCoinType := linkTokenPkgID + "::link::LINK" + feeTokenMetadataID := linkTokenObjectMetadataID + if msg.FeeTokenCoinType != "" { + feeTokenCoinType = msg.FeeTokenCoinType + } + if msg.FeeTokenMetadataId != "" { + feeTokenMetadataID = msg.FeeTokenMetadataId + } + + sourceTokens := []string{linkTokenObjectMetadataID} + sourceUsdPerToken := []*big.Int{bigIntSourceUsdPerToken} + if feeTokenMetadataID != linkTokenObjectMetadataID { + sourceTokens = append(sourceTokens, feeTokenMetadataID) + suiUsdPerToken, _ := new(big.Int).SetString("3000000000000000000000000000", 10) + sourceUsdPerToken = append(sourceUsdPerToken, suiUsdPerToken) + } + // Update Prices on FeeQuoter with minted LinkToken _, err = operations.ExecuteOperation(e.OperationsBundle, ccipops.FeeQuoterUpdatePricesWithOwnerCapOp, deps.SuiChain, ccipops.FeeQuoterUpdatePricesWithOwnerCapInput{ CCIPPackageId: ccipPackageID, CCIPObjectRef: ccipObjectRefID, OwnerCapObjectId: ccipOwnerCapID, - SourceTokens: []string{linkTokenObjectMetadataID}, - SourceUsdPerToken: []*big.Int{bigIntSourceUsdPerToken}, + SourceTokens: sourceTokens, + SourceUsdPerToken: sourceUsdPerToken, GasDestChainSelectors: []uint64{cfg.DestChain}, GasUsdPerUnitGas: []*big.Int{bigIntGasUsdPerUnitGas}, }) @@ -422,15 +443,17 @@ func SendSuiCCIPRequest(e cldf.Environment, cfg *ccipclient.CCIPSendReqConfig) ( cfg.DestChain, msg.Receiver, // receiver msg.Data, - createTokenTransferParamsResult, // tokenParams from the original create_token_transfer_params - suiBind.Object{Id: linkTokenObjectMetadataID}, // feeTokenMetadata + createTokenTransferParamsResult, // tokenParams from the original create_token_transfer_params + suiBind.Object{Id: feeTokenMetadataID}, // feeTokenMetadata suiBind.Object{Id: msg.FeeToken}, msg.ExtraArgs, // extraArgs } + ccipSendTypeArgs := []string{feeTokenCoinType} + encodedOnRampCCIPSendCall, err := onRampContract.EncodeCallArgsWithGenerics( "ccip_send", - typeArgsListLinkTokenPkgID, + ccipSendTypeArgs, []string{}, paramTypesCCIPSend, paramValuesCCIPSend, @@ -555,17 +578,17 @@ func SendSuiCCIPRequest(e cldf.Environment, cfg *ccipclient.CCIPSendReqConfig) ( return nil, errors.New("failed to decode parameters for token pool function: " + err.Error()) } - typeArgsList = []string{linkTokenPkgID + "::link::LINK"} + typeArgsList = []string{feeTokenCoinType} typeParamsList = []string{} paramValues = []any{ suiBind.Object{Id: ccipObjectRefID}, suiBind.Object{Id: onRampStateObjectID}, suiBind.Object{Id: "0x6"}, cfg.DestChain, - msg.Receiver, // receiver (TODO: replace this with sender Address use environment.NormalizeTo32Bytes(ethereumAddress) from sui repo) + msg.Receiver, msg.Data, - extractedAny2SuiMessageResult, // tokenParams - suiBind.Object{Id: linkTokenObjectMetadataID}, // feeTokenMetadata + extractedAny2SuiMessageResult, // tokenParams + suiBind.Object{Id: feeTokenMetadataID}, // feeTokenMetadata suiBind.Object{Id: msg.FeeToken}, msg.ExtraArgs, // extraArgs } @@ -1136,3 +1159,115 @@ func extractFields[T any](configs []TokenPoolRateLimiterConfig, selector func(To } return result } + +const SuiNativeCoinType = "0x2::sui::SUI" + +func GetSuiNativeCoinMetadataId(ctx context.Context, client sui.ISuiAPI) (string, error) { + rsp, err := client.SuiXGetCoinMetadata(ctx, models.SuiXGetCoinMetadataRequest{ + CoinType: SuiNativeCoinType, + }) + if err != nil { + return "", fmt.Errorf("failed to get SUI native CoinMetadata: %w", err) + } + return rsp.Id, nil +} + +func SplitSuiCoinForFee( + t *testing.T, + ctx context.Context, + suiChain cldf_sui.Chain, + amount uint64, +) string { + signerAddr, err := suiChain.Signer.GetAddress() + require.NoError(t, err) + + coins, err := suiChain.Client.SuiXGetCoins(ctx, models.SuiXGetCoinsRequest{ + Owner: signerAddr, + CoinType: SuiNativeCoinType, + Limit: 50, + }) + require.NoError(t, err) + require.NotEmpty(t, coins.Data, "no SUI coins found for signer") + + sourceCoin := coins.Data[0] + + txnMeta, err := suiChain.Client.TransferSui(ctx, models.TransferSuiRequest{ + Signer: signerAddr, + SuiObjectId: sourceCoin.CoinObjectId, + GasBudget: "100000000", + Recipient: signerAddr, + Amount: strconv.FormatUint(amount, 10), + }) + require.NoError(t, err, "failed to create TransferSui transaction") + + decodedTx, err := base64.StdEncoding.DecodeString(txnMeta.TxBytes) + require.NoError(t, err, "failed to decode tx bytes") + + tx, err := suiBind.SignAndSendTx(ctx, suiChain.Signer, suiChain.Client, decodedTx, true) + require.NoError(t, err, "failed to execute TransferSui") + require.Equal(t, "success", tx.Effects.Status.Status, "TransferSui transaction failed") + + for _, oc := range tx.ObjectChanges { + if oc.Type == "created" && strings.Contains(oc.ObjectType, "0x2::coin::Coin<0x2::sui::SUI>") { + t.Logf("Split SUI coin: new object %s with amount %d", oc.ObjectId, amount) + return oc.ObjectId + } + } + + t.Fatal("failed to find newly created SUI coin object in TransferSui response") + return "" +} + +func RegisterSuiNativeFeeToken( + t *testing.T, + e cldf.Environment, + suiChainSel uint64, + suiCoinMetadataId string, +) { + state, err := stateview.LoadOnchainState(e) + require.NoError(t, err) + + suiChain := e.BlockChains.SuiChains()[suiChainSel] + + deps := suideps.Deps{ + SuiChain: sui_ops.OpTxDeps{ + Client: suiChain.Client, + Signer: suiChain.Signer, + GetCallOpts: func() *suiBind.CallOpts { + b := uint64(400_000_000) + return &suiBind.CallOpts{ + Signer: suiChain.Signer, + WaitForExecution: true, + GasBudget: &b, + } + }, + }, + } + + ccipPackageID := state.SuiChains[suiChainSel].CCIPMockV2PackageId + if ccipPackageID == "" { + ccipPackageID = state.SuiChains[suiChainSel].CCIPAddress + } + ccipObjectRefID := state.SuiChains[suiChainSel].CCIPObjectRef + ccipOwnerCapID := state.SuiChains[suiChainSel].CCIPOwnerCapObjectId + + _, err = operations.ExecuteOperation(e.OperationsBundle, ccipops.FeeQuoterApplyFeeTokenUpdatesOp, deps.SuiChain, + ccipops.FeeQuoterApplyFeeTokenUpdatesInput{ + CCIPPackageId: ccipPackageID, + StateObjectId: ccipObjectRefID, + OwnerCapObjectId: ccipOwnerCapID, + FeeTokensToRemove: []string{}, + FeeTokensToAdd: []string{suiCoinMetadataId}, + }) + require.NoError(t, err, "failed to register SUI as fee token") + + _, err = operations.ExecuteOperation(e.OperationsBundle, ccipops.FeeQuoterApplyPremiumMultiplierWeiPerEthUpdatesOp, deps.SuiChain, + ccipops.FeeQuoterApplyPremiumMultiplierWeiPerEthUpdatesInput{ + CCIPPackageId: ccipPackageID, + StateObjectId: ccipObjectRefID, + OwnerCapObjectId: ccipOwnerCapID, + Tokens: []string{suiCoinMetadataId}, + PremiumMultiplierWeiPerEth: []uint64{900_000_000_000_000_000}, + }) + require.NoError(t, err, "failed to set premium multiplier for SUI fee token") +} diff --git a/integration-tests/smoke/ccip/ccip_sui_fees_test.go b/integration-tests/smoke/ccip/ccip_sui_fees_test.go new file mode 100644 index 00000000000..2cf02e14275 --- /dev/null +++ b/integration-tests/smoke/ccip/ccip_sui_fees_test.go @@ -0,0 +1,290 @@ +package ccip + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + chain_selectors "github.com/smartcontractkit/chain-selectors" + + "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" + "github.com/smartcontractkit/chainlink-deployments-framework/chain" + sui_deployment "github.com/smartcontractkit/chainlink-sui/deployment" + + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" + ccipclient "github.com/smartcontractkit/chainlink/deployment/ccip/shared/client" + "github.com/smartcontractkit/chainlink/deployment/ccip/shared/stateview" + + testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip" +) + +func Test_CCIP_Fees_Sui2EVM(t *testing.T) { + ctx := testhelpers.Context(t) + + e, _, _ := testsetups.NewIntegrationEnvironment( + t, + testhelpers.WithNumOfChains(2), + testhelpers.WithSuiChains(1), + ) + + evmChainSelectors := e.Env.BlockChains.ListChainSelectors(chain.WithFamily(chain_selectors.FamilyEVM)) + suiChainSelectors := e.Env.BlockChains.ListChainSelectors(chain.WithFamily(chain_selectors.FamilySui)) + + sourceChain := suiChainSelectors[0] + destChain := evmChainSelectors[0] + + state, err := stateview.LoadOnchainState(e.Env) + require.NoError(t, err) + + t.Log("Source chain (SUI):", sourceChain, "Dest chain (EVM):", destChain) + + err = testhelpers.AddLaneWithDefaultPricesAndFeeQuoterConfig(t, &e, state, sourceChain, destChain, false) + require.NoError(t, err) + + suiChain := e.Env.BlockChains.SuiChains()[sourceChain] + + suiCoinMetadataId, err := testhelpers.GetSuiNativeCoinMetadataId(ctx, suiChain.Client) + require.NoError(t, err, "failed to get SUI CoinMetadata ID") + t.Log("SUI CoinMetadata ID:", suiCoinMetadataId) + + testhelpers.RegisterSuiNativeFeeToken(t, e.Env, sourceChain, suiCoinMetadataId) + + state, err = stateview.LoadOnchainState(e.Env) + require.NoError(t, err) + + receiver := state.Chains[destChain].Receiver.Address() + + linkFeeTokenOutput := mintLinkTokenOnSui(t, e.Env, sourceChain, 1000000000000) + linkFeeToken := linkFeeTokenOutput.Objects.MintedLinkTokenObjectId + + t.Run("Send message with LINK fee token", func(t *testing.T) { + msg := testhelpers.SuiSendRequest{ + Receiver: common.LeftPadBytes(receiver.Bytes(), 32), + Data: []byte("Hello EVM, from SUI with LINK fee!"), + FeeToken: linkFeeToken, + ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), + } + + msgSentEvent := testhelpers.TestSendRequest(t, e.Env, state, sourceChain, destChain, false, msg) + require.NotNil(t, msgSentEvent) + t.Log("LINK fee message sent, seqNum:", msgSentEvent.SequenceNumber) + + seqNum := ccipocr3.SeqNum(msgSentEvent.SequenceNumber) + expectedSeqNums := map[testhelpers.SourceDestPair]ccipocr3.SeqNumRange{ + {SourceChainSelector: sourceChain, DestChainSelector: destChain}: ccipocr3.NewSeqNumRange(seqNum, seqNum), + } + startBlocks := map[uint64]*uint64{} + block, err := testhelpers.LatestBlock(ctx, e.Env, destChain) + require.NoError(t, err) + startBlocks[destChain] = &block + + err = testhelpers.ConfirmMultipleCommits(t, e.Env, state, startBlocks, false, expectedSeqNums) + require.NoError(t, err) + + execStates := testhelpers.ConfirmExecWithSeqNrsForAll( + t, e.Env, state, + testhelpers.SeqNumberRangeToSlice(expectedSeqNums), + startBlocks, + ) + for _, states := range execStates { + for _, s := range states { + require.Equal(t, testhelpers.EXECUTION_STATE_SUCCESS, s) + } + } + }) + + t.Run("Send message with native SUI fee token", func(t *testing.T) { + suiFeeCoinId := testhelpers.SplitSuiCoinForFee(t, ctx, suiChain, 10_000_000_000) + + msg := testhelpers.SuiSendRequest{ + Receiver: common.LeftPadBytes(receiver.Bytes(), 32), + Data: []byte("Hello EVM, from SUI with native SUI fee!"), + FeeToken: suiFeeCoinId, + FeeTokenCoinType: testhelpers.SuiNativeCoinType, + FeeTokenMetadataId: suiCoinMetadataId, + ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), + } + + msgSentEvent := testhelpers.TestSendRequest(t, e.Env, state, sourceChain, destChain, false, msg) + require.NotNil(t, msgSentEvent) + t.Log("Native SUI fee message sent, seqNum:", msgSentEvent.SequenceNumber) + + seqNum := ccipocr3.SeqNum(msgSentEvent.SequenceNumber) + expectedSeqNums := map[testhelpers.SourceDestPair]ccipocr3.SeqNumRange{ + {SourceChainSelector: sourceChain, DestChainSelector: destChain}: ccipocr3.NewSeqNumRange(seqNum, seqNum), + } + startBlocks := map[uint64]*uint64{} + block, err := testhelpers.LatestBlock(ctx, e.Env, destChain) + require.NoError(t, err) + startBlocks[destChain] = &block + + err = testhelpers.ConfirmMultipleCommits(t, e.Env, state, startBlocks, false, expectedSeqNums) + require.NoError(t, err) + + execStates := testhelpers.ConfirmExecWithSeqNrsForAll( + t, e.Env, state, + testhelpers.SeqNumberRangeToSlice(expectedSeqNums), + startBlocks, + ) + for _, states := range execStates { + for _, s := range states { + require.Equal(t, testhelpers.EXECUTION_STATE_SUCCESS, s) + } + } + }) + + t.Run("Token transfer with LINK fee token", func(t *testing.T) { + updatedEnv, evmToken, _, err := testhelpers.HandleTokenAndBurnMintTokenPoolDeploymentForSUI( + e.Env, sourceChain, destChain, []testhelpers.TokenPoolRateLimiterConfig{ + { + RemoteChainSelector: destChain, + OutboundIsEnabled: false, + OutboundCapacity: 100000, + OutboundRate: 100, + InboundIsEnabled: false, + InboundCapacity: 100000, + InboundRate: 100, + }, + }) + require.NoError(t, err) + e.Env = updatedEnv + + state, err = stateview.LoadOnchainState(e.Env) + require.NoError(t, err) + + tokenOutput := mintLinkTokenOnSui(t, e.Env, sourceChain, 1_000_000_000) + feeOutput := mintLinkTokenOnSui(t, e.Env, sourceChain, 100_000_000_000) + + tcs := []testhelpers.TestTransferRequest{ + { + Name: "Token transfer with LINK fee", + SourceChain: sourceChain, + DestChain: destChain, + Receiver: updatedEnv.BlockChains.EVMChains()[destChain].DeployerKey.From.Bytes(), + ExpectedStatus: testhelpers.EXECUTION_STATE_SUCCESS, + FeeToken: feeOutput.Objects.MintedLinkTokenObjectId, + SuiTokens: []testhelpers.SuiTokenAmount{ + { + TokenPoolType: sui_deployment.TokenPoolTypeBurnMint, + Token: tokenOutput.Objects.MintedLinkTokenObjectId, + Amount: 1_000_000_000, + }, + }, + ExpectedTokenBalances: []testhelpers.ExpectedBalance{ + { + Token: evmToken.Address().Bytes(), + Amount: big.NewInt(1e18), + }, + }, + }, + } + + startBlocks, expectedSeqNums, expectedExecutionStates, expectedTokenBalances := testhelpers.TransferMultiple(ctx, t, updatedEnv, state, tcs) + + err = testhelpers.ConfirmMultipleCommits(t, updatedEnv, state, startBlocks, false, expectedSeqNums) + require.NoError(t, err) + + execStates := testhelpers.ConfirmExecWithSeqNrsForAll( + t, updatedEnv, state, + testhelpers.SeqNumberRangeToSlice(expectedSeqNums), + startBlocks, + ) + require.Equal(t, expectedExecutionStates, execStates) + testhelpers.WaitForTokenBalances(ctx, t, updatedEnv, expectedTokenBalances) + }) + + t.Run("Token transfer with native SUI fee token", func(t *testing.T) { + state, err = stateview.LoadOnchainState(e.Env) + require.NoError(t, err) + + tokenOutput := mintLinkTokenOnSui(t, e.Env, sourceChain, 1_000_000_000) + suiFeeCoinId := testhelpers.SplitSuiCoinForFee(t, ctx, suiChain, 10_000_000_000) + + msg := testhelpers.SuiSendRequest{ + Receiver: common.LeftPadBytes(e.Env.BlockChains.EVMChains()[destChain].DeployerKey.From.Bytes(), 32), + Data: []byte{}, + FeeToken: suiFeeCoinId, + FeeTokenCoinType: testhelpers.SuiNativeCoinType, + FeeTokenMetadataId: suiCoinMetadataId, + ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), + TokenAmounts: []testhelpers.SuiTokenAmount{ + { + TokenPoolType: sui_deployment.TokenPoolTypeBurnMint, + Token: tokenOutput.Objects.MintedLinkTokenObjectId, + Amount: 1_000_000_000, + }, + }, + } + + msgSentEvent := testhelpers.TestSendRequest(t, e.Env, state, sourceChain, destChain, false, msg) + require.NotNil(t, msgSentEvent) + t.Log("Native SUI fee token transfer sent, seqNum:", msgSentEvent.SequenceNumber) + + seqNum := ccipocr3.SeqNum(msgSentEvent.SequenceNumber) + expectedSeqNums := map[testhelpers.SourceDestPair]ccipocr3.SeqNumRange{ + {SourceChainSelector: sourceChain, DestChainSelector: destChain}: ccipocr3.NewSeqNumRange(seqNum, seqNum), + } + startBlocks := map[uint64]*uint64{} + block, err := testhelpers.LatestBlock(ctx, e.Env, destChain) + require.NoError(t, err) + startBlocks[destChain] = &block + + err = testhelpers.ConfirmMultipleCommits(t, e.Env, state, startBlocks, false, expectedSeqNums) + require.NoError(t, err) + + execStates := testhelpers.ConfirmExecWithSeqNrsForAll( + t, e.Env, state, + testhelpers.SeqNumberRangeToSlice(expectedSeqNums), + startBlocks, + ) + for _, states := range execStates { + for _, s := range states { + require.Equal(t, testhelpers.EXECUTION_STATE_SUCCESS, s) + } + } + }) + + t.Run("Send with invalid fee token should fail", func(t *testing.T) { + msg := testhelpers.SuiSendRequest{ + Receiver: common.LeftPadBytes(receiver.Bytes(), 32), + Data: []byte("should fail"), + FeeToken: "0x0000000000000000000000000000000000000000000000000000000000000bad", + ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), + } + + baseOpts := []ccipclient.SendReqOpts{ + ccipclient.WithSourceChain(sourceChain), + ccipclient.WithDestChain(destChain), + ccipclient.WithTestRouter(false), + ccipclient.WithMessage(msg), + } + _, err := testhelpers.SendRequest(e.Env, state, baseOpts...) + require.Error(t, err, "expected error for invalid fee token") + t.Log("Invalid fee token correctly rejected:", err) + }) + + t.Run("Send with insufficient SUI fee token balance should fail", func(t *testing.T) { + tinyCoinId := testhelpers.SplitSuiCoinForFee(t, ctx, suiChain, 1) + + msg := testhelpers.SuiSendRequest{ + Receiver: common.LeftPadBytes(receiver.Bytes(), 32), + Data: []byte("should fail - insufficient balance"), + FeeToken: tinyCoinId, + FeeTokenCoinType: testhelpers.SuiNativeCoinType, + FeeTokenMetadataId: suiCoinMetadataId, + ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), + } + + baseOpts := []ccipclient.SendReqOpts{ + ccipclient.WithSourceChain(sourceChain), + ccipclient.WithDestChain(destChain), + ccipclient.WithTestRouter(false), + ccipclient.WithMessage(msg), + } + _, err := testhelpers.SendRequest(e.Env, state, baseOpts...) + require.Error(t, err, "expected error for insufficient fee token balance") + t.Log("Insufficient balance correctly rejected:", err) + }) +} From 5409ea38cea159dd0bd04ef6aae894060dc6f509 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Thu, 5 Mar 2026 16:28:19 +0100 Subject: [PATCH 2/5] Linting --- .../changeset/testhelpers/test_sui_helpers.go | 24 +++++++++---------- .../smoke/ccip/ccip_sui_fees_test.go | 14 +++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/deployment/ccip/changeset/testhelpers/test_sui_helpers.go b/deployment/ccip/changeset/testhelpers/test_sui_helpers.go index 2f863237218..f0a4f221b6f 100644 --- a/deployment/ccip/changeset/testhelpers/test_sui_helpers.go +++ b/deployment/ccip/changeset/testhelpers/test_sui_helpers.go @@ -54,7 +54,7 @@ type SuiSendRequest struct { FeeToken string FeeTokenStore string FeeTokenCoinType string // optional: e.g. "0x2::sui::SUI"; defaults to LINK if empty - FeeTokenMetadataId string // optional: CoinMetadata object ID for the fee token; defaults to LINK if empty + FeeTokenMetadataID string // optional: CoinMetadata object ID for the fee token; defaults to LINK if empty TokenAmounts []SuiTokenAmount TokenReceiverATA []byte } @@ -164,8 +164,8 @@ func SendSuiCCIPRequest(e cldf.Environment, cfg *ccipclient.CCIPSendReqConfig) ( if msg.FeeTokenCoinType != "" { feeTokenCoinType = msg.FeeTokenCoinType } - if msg.FeeTokenMetadataId != "" { - feeTokenMetadataID = msg.FeeTokenMetadataId + if msg.FeeTokenMetadataID != "" { + feeTokenMetadataID = msg.FeeTokenMetadataID } sourceTokens := []string{linkTokenObjectMetadataID} @@ -443,8 +443,8 @@ func SendSuiCCIPRequest(e cldf.Environment, cfg *ccipclient.CCIPSendReqConfig) ( cfg.DestChain, msg.Receiver, // receiver msg.Data, - createTokenTransferParamsResult, // tokenParams from the original create_token_transfer_params - suiBind.Object{Id: feeTokenMetadataID}, // feeTokenMetadata + createTokenTransferParamsResult, // tokenParams from the original create_token_transfer_params + suiBind.Object{Id: feeTokenMetadataID}, // feeTokenMetadata suiBind.Object{Id: msg.FeeToken}, msg.ExtraArgs, // extraArgs } @@ -587,8 +587,8 @@ func SendSuiCCIPRequest(e cldf.Environment, cfg *ccipclient.CCIPSendReqConfig) ( cfg.DestChain, msg.Receiver, msg.Data, - extractedAny2SuiMessageResult, // tokenParams - suiBind.Object{Id: feeTokenMetadataID}, // feeTokenMetadata + extractedAny2SuiMessageResult, // tokenParams + suiBind.Object{Id: feeTokenMetadataID}, // feeTokenMetadata suiBind.Object{Id: msg.FeeToken}, msg.ExtraArgs, // extraArgs } @@ -1162,7 +1162,7 @@ func extractFields[T any](configs []TokenPoolRateLimiterConfig, selector func(To const SuiNativeCoinType = "0x2::sui::SUI" -func GetSuiNativeCoinMetadataId(ctx context.Context, client sui.ISuiAPI) (string, error) { +func GetSuiNativeCoinMetadataID(ctx context.Context, client sui.ISuiAPI) (string, error) { rsp, err := client.SuiXGetCoinMetadata(ctx, models.SuiXGetCoinMetadataRequest{ CoinType: SuiNativeCoinType, }) @@ -1173,8 +1173,8 @@ func GetSuiNativeCoinMetadataId(ctx context.Context, client sui.ISuiAPI) (string } func SplitSuiCoinForFee( - t *testing.T, ctx context.Context, + t *testing.T, suiChain cldf_sui.Chain, amount uint64, ) string { @@ -1222,7 +1222,7 @@ func RegisterSuiNativeFeeToken( t *testing.T, e cldf.Environment, suiChainSel uint64, - suiCoinMetadataId string, + suiCoinMetadataID string, ) { state, err := stateview.LoadOnchainState(e) require.NoError(t, err) @@ -1257,7 +1257,7 @@ func RegisterSuiNativeFeeToken( StateObjectId: ccipObjectRefID, OwnerCapObjectId: ccipOwnerCapID, FeeTokensToRemove: []string{}, - FeeTokensToAdd: []string{suiCoinMetadataId}, + FeeTokensToAdd: []string{suiCoinMetadataID}, }) require.NoError(t, err, "failed to register SUI as fee token") @@ -1266,7 +1266,7 @@ func RegisterSuiNativeFeeToken( CCIPPackageId: ccipPackageID, StateObjectId: ccipObjectRefID, OwnerCapObjectId: ccipOwnerCapID, - Tokens: []string{suiCoinMetadataId}, + Tokens: []string{suiCoinMetadataID}, PremiumMultiplierWeiPerEth: []uint64{900_000_000_000_000_000}, }) require.NoError(t, err, "failed to set premium multiplier for SUI fee token") diff --git a/integration-tests/smoke/ccip/ccip_sui_fees_test.go b/integration-tests/smoke/ccip/ccip_sui_fees_test.go index 2cf02e14275..65f9b11460b 100644 --- a/integration-tests/smoke/ccip/ccip_sui_fees_test.go +++ b/integration-tests/smoke/ccip/ccip_sui_fees_test.go @@ -45,7 +45,7 @@ func Test_CCIP_Fees_Sui2EVM(t *testing.T) { suiChain := e.Env.BlockChains.SuiChains()[sourceChain] - suiCoinMetadataId, err := testhelpers.GetSuiNativeCoinMetadataId(ctx, suiChain.Client) + suiCoinMetadataId, err := testhelpers.GetSuiNativeCoinMetadataID(ctx, suiChain.Client) require.NoError(t, err, "failed to get SUI CoinMetadata ID") t.Log("SUI CoinMetadata ID:", suiCoinMetadataId) @@ -96,14 +96,14 @@ func Test_CCIP_Fees_Sui2EVM(t *testing.T) { }) t.Run("Send message with native SUI fee token", func(t *testing.T) { - suiFeeCoinId := testhelpers.SplitSuiCoinForFee(t, ctx, suiChain, 10_000_000_000) + suiFeeCoinId := testhelpers.SplitSuiCoinForFee(ctx, t, suiChain, 10_000_000_000) msg := testhelpers.SuiSendRequest{ Receiver: common.LeftPadBytes(receiver.Bytes(), 32), Data: []byte("Hello EVM, from SUI with native SUI fee!"), FeeToken: suiFeeCoinId, FeeTokenCoinType: testhelpers.SuiNativeCoinType, - FeeTokenMetadataId: suiCoinMetadataId, + FeeTokenMetadataID: suiCoinMetadataId, ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), } @@ -200,14 +200,14 @@ func Test_CCIP_Fees_Sui2EVM(t *testing.T) { require.NoError(t, err) tokenOutput := mintLinkTokenOnSui(t, e.Env, sourceChain, 1_000_000_000) - suiFeeCoinId := testhelpers.SplitSuiCoinForFee(t, ctx, suiChain, 10_000_000_000) + suiFeeCoinId := testhelpers.SplitSuiCoinForFee(ctx, t, suiChain, 10_000_000_000) msg := testhelpers.SuiSendRequest{ Receiver: common.LeftPadBytes(e.Env.BlockChains.EVMChains()[destChain].DeployerKey.From.Bytes(), 32), Data: []byte{}, FeeToken: suiFeeCoinId, FeeTokenCoinType: testhelpers.SuiNativeCoinType, - FeeTokenMetadataId: suiCoinMetadataId, + FeeTokenMetadataID: suiCoinMetadataId, ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), TokenAmounts: []testhelpers.SuiTokenAmount{ { @@ -266,14 +266,14 @@ func Test_CCIP_Fees_Sui2EVM(t *testing.T) { }) t.Run("Send with insufficient SUI fee token balance should fail", func(t *testing.T) { - tinyCoinId := testhelpers.SplitSuiCoinForFee(t, ctx, suiChain, 1) + tinyCoinId := testhelpers.SplitSuiCoinForFee(ctx, t, suiChain, 1) msg := testhelpers.SuiSendRequest{ Receiver: common.LeftPadBytes(receiver.Bytes(), 32), Data: []byte("should fail - insufficient balance"), FeeToken: tinyCoinId, FeeTokenCoinType: testhelpers.SuiNativeCoinType, - FeeTokenMetadataId: suiCoinMetadataId, + FeeTokenMetadataID: suiCoinMetadataId, ExtraArgs: testhelpers.MakeBCSEVMExtraArgsV2(big.NewInt(300000), false), } From 9a26be68cc264a5d6a4f28b0c82ad7618c968675 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Thu, 5 Mar 2026 17:12:59 +0100 Subject: [PATCH 3/5] Add SUI fee token test to CI integration test matrix Register Test_CCIP_Fees_Sui2EVM in the in-memory integration tests workflow so it runs on PR and nightly triggers. Made-with: Cursor --- .github/integration-in-memory-tests.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/integration-in-memory-tests.yml b/.github/integration-in-memory-tests.yml index 0acfab9bd15..3de12ad58b1 100644 --- a/.github/integration-in-memory-tests.yml +++ b/.github/integration-in-memory-tests.yml @@ -964,6 +964,20 @@ runner-test-matrix: install_plugins_public: true free_disk_space: true + - id: smoke/ccip/ccip_sui_fees_test.go:Test_CCIP_Fees_Sui2EVM + path: integration-tests/smoke/ccip/ccip_sui_fees_test.go + test_env_type: in-memory + runs_on: ubuntu-latest + triggers: + - PR Integration CCIP Tests + - Nightly Integration CCIP Tests + test_cmd: | + go test ./smoke/ccip -run "Test_CCIP_Fees_Sui2EVM" -timeout 15m -test.parallel=1 -count=1 -json + test_go_project_path: integration-tests + sui_cli_version: mainnet-1.60.1 + install_plugins_public: true + free_disk_space: true + # - id: smoke/ccip/ccip_sui_upgrade_test.go:Test_CCIP_Upgrade_EVM2Sui # path: integration-tests/smoke/ccip/ccip_sui_upgrade_test.go # test_env_type: in-memory From 274825594d53e5f8661c11e04df7207b2def7af0 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Thu, 5 Mar 2026 17:34:55 +0100 Subject: [PATCH 4/5] Retrigger CI for SUI fee token test validation Made-with: Cursor From cb63df8d7fc2e9716ccb5f8d9dde89eb22a4f1f0 Mon Sep 17 00:00:00 2001 From: Damjan Smickovski Date: Thu, 5 Mar 2026 18:00:25 +0100 Subject: [PATCH 5/5] Retrigger --- deployment/ccip/changeset/testhelpers/test_sui_helpers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/deployment/ccip/changeset/testhelpers/test_sui_helpers.go b/deployment/ccip/changeset/testhelpers/test_sui_helpers.go index f0a4f221b6f..92ff8e1ea21 100644 --- a/deployment/ccip/changeset/testhelpers/test_sui_helpers.go +++ b/deployment/ccip/changeset/testhelpers/test_sui_helpers.go @@ -1270,4 +1270,5 @@ func RegisterSuiNativeFeeToken( PremiumMultiplierWeiPerEth: []uint64{900_000_000_000_000_000}, }) require.NoError(t, err, "failed to set premium multiplier for SUI fee token") + }