From c23f77cec9add1f4ad59b9d6b8476b1b9c9a167f Mon Sep 17 00:00:00 2001 From: Makram Date: Fri, 21 Feb 2025 14:32:16 +0200 Subject: [PATCH] [CCIP-5117] integration-tests/smoke/ccip: factor out fee test helpers (#16496) * integration-tests/smoke/ccip: factor out fee test helpers * goimports * fix comment --- .github/integration-in-memory-tests.yml | 2 +- .../changeset/testhelpers/feestest/helpers.go | 228 ++++++++++++ .../changeset/testhelpers/test_helpers.go | 5 +- .../smoke/ccip/ccip_fees_test.go | 344 +++++------------- 4 files changed, 316 insertions(+), 263 deletions(-) create mode 100644 deployment/ccip/changeset/testhelpers/feestest/helpers.go diff --git a/.github/integration-in-memory-tests.yml b/.github/integration-in-memory-tests.yml index 32a510c95c6..39d61cd03e7 100644 --- a/.github/integration-in-memory-tests.yml +++ b/.github/integration-in-memory-tests.yml @@ -21,7 +21,7 @@ runner-test-matrix: runs_on: ubuntu-latest triggers: - PR Integration CCIP Tests - test_cmd: cd integration-tests/smoke/ccip && go test ccip_fees_test.go -timeout 12m -test.parallel=2 -count=1 -json + test_cmd: cd integration-tests/smoke/ccip && go test ccip_fees_test.go -timeout 20m -test.parallel=2 -count=1 -json - id: smoke/ccip/ccip_messaging_test.go:* path: integration-tests/smoke/ccip/ccip_messaging_test.go diff --git a/deployment/ccip/changeset/testhelpers/feestest/helpers.go b/deployment/ccip/changeset/testhelpers/feestest/helpers.go new file mode 100644 index 00000000000..f939c7b91bd --- /dev/null +++ b/deployment/ccip/changeset/testhelpers/feestest/helpers.go @@ -0,0 +1,228 @@ +package feestest + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/math" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink-integrations/evm/assets" + "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" + "github.com/smartcontractkit/chainlink/deployment" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_2_0/router" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" + "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/weth9" +) + +// NewFeeTokenTestCase creates a new FeeTokenTestCase to test fee token usage scenarios. +func NewFeeTokenTestCase( + t *testing.T, + env deployment.Environment, + src, dst uint64, + feeToken common.Address, + tokenAmounts []router.ClientEVMTokenAmount, + srcToken, dstToken *burn_mint_erc677.BurnMintERC677, + receiver []byte, + data []byte, + assertTokenBalance, assertExecution bool, +) FeeTokenTestCase { + return FeeTokenTestCase{ + t: t, + src: src, + dst: dst, + env: env, + srcToken: srcToken, + dstToken: dstToken, + tokenAmounts: tokenAmounts, + feeToken: feeToken, + receiver: receiver, + data: data, + assertTokenBalance: assertTokenBalance, + assertExecution: assertExecution, + } +} + +type FeeTokenTestCase struct { + t *testing.T + src, dst uint64 + env deployment.Environment + srcToken, dstToken *burn_mint_erc677.BurnMintERC677 + tokenAmounts []router.ClientEVMTokenAmount + feeToken common.Address + receiver []byte + data []byte + assertTokenBalance bool + assertExecution bool +} + +func RunFeeTokenTestCase(tc FeeTokenTestCase) { + ctx := tests.Context(tc.t) + // Need to keep track of the block number for each chain so that event subscription can be done from that block. + startBlocks := make(map[uint64]*uint64) + expectedSeqNum := make(map[testhelpers.SourceDestPair]uint64) + expectedSeqNumExec := make(map[testhelpers.SourceDestPair][]uint64) + + srcChain := tc.env.Chains[tc.src] + dstChain := tc.env.Chains[tc.dst] + + state, err := changeset.LoadOnchainState(tc.env) + require.NoError(tc.t, err) + + var dstTokBalanceBefore *big.Int + if tc.assertTokenBalance { + var err error + dstTokBalanceBefore, err = tc.dstToken.BalanceOf(nil, state.Chains[tc.dst].Receiver.Address()) + require.NoError(tc.t, err) + tc.t.Logf("destination token balance before of receiver %s: %s", + state.Chains[tc.dst].Receiver.Address(), + dstTokBalanceBefore.String()) + } + + // if fee token is not native then approve the router to spend the fee token from the sender. + var feeTokenWrapper *burn_mint_erc677.BurnMintERC677 + if tc.feeToken != common.HexToAddress("0x0") { + if tc.feeToken == state.Chains[tc.src].Weth9.Address() { + // Deposit some ETH into the WETH contract + weth9, err := weth9.NewWETH9(state.Chains[tc.src].Weth9.Address(), srcChain.Client) + require.NoError(tc.t, err) + + balance, err := srcChain.Client.BalanceAt(ctx, srcChain.DeployerKey.From, nil) + require.NoError(tc.t, err) + + tc.t.Logf("balance before deposit: %s", balance.String()) + + srcChain.DeployerKey.Value = assets.Ether(100).ToInt() + tx, err := weth9.Deposit(srcChain.DeployerKey) + _, err = deployment.ConfirmIfNoError(srcChain, tx, err) + require.NoError(tc.t, err) + srcChain.DeployerKey.Value = big.NewInt(0) + } + + var err error + feeTokenWrapper, err = burn_mint_erc677.NewBurnMintERC677(tc.feeToken, srcChain.Client) + require.NoError(tc.t, err) + + bal, err := feeTokenWrapper.BalanceOf(&bind.CallOpts{ + Context: ctx, + }, srcChain.DeployerKey.From) + require.NoError(tc.t, err) + + tc.t.Logf("fee token balance before approval: %s", bal.String()) + + // Approve the router to spend fee token + tx, err := feeTokenWrapper.Approve(srcChain.DeployerKey, state.Chains[tc.src].Router.Address(), math.MaxBig256) + + _, err = deployment.ConfirmIfNoError(srcChain, tx, err) + require.NoError(tc.t, err) + } + + // get the header for the destination chain and the relevant block number + latesthdr, err := dstChain.Client.HeaderByNumber(testcontext.Get(tc.t), nil) + require.NoError(tc.t, err) + block := latesthdr.Number.Uint64() + startBlocks[tc.dst] = &block + + // Get the fee Token Balance Before, if not fee token set get native balance. + var feeTokenBalanceBefore *big.Int + if feeTokenWrapper != nil { + feeTokenBalanceBefore, err = feeTokenWrapper.BalanceOf(&bind.CallOpts{ + Context: ctx, + }, srcChain.DeployerKey.From) + require.NoError(tc.t, err) + } else { + feeTokenBalanceBefore, err = srcChain.Client.BalanceAt(ctx, srcChain.DeployerKey.From, nil) + require.NoError(tc.t, err) + } + tc.t.Logf("fee token balance before: %s, fee token enabled: %s", + feeTokenBalanceBefore.String(), tc.feeToken.String()) + + msgSentEvent := testhelpers.TestSendRequest( + tc.t, + tc.env, + state, + tc.src, + tc.dst, + false, + router.ClientEVM2AnyMessage{ + Receiver: tc.receiver, + Data: tc.data, + TokenAmounts: tc.tokenAmounts, + FeeToken: tc.feeToken, + ExtraArgs: nil, + }, + ) + + expectedSeqNum[testhelpers.SourceDestPair{ + SourceChainSelector: tc.src, + DestChainSelector: tc.dst, + }] = msgSentEvent.SequenceNumber + expectedSeqNumExec[testhelpers.SourceDestPair{ + SourceChainSelector: tc.src, + DestChainSelector: tc.dst, + }] = []uint64{msgSentEvent.SequenceNumber} + + // Check the fee token balance after the request and ensure fee tokens were spent + var feeTokenBalanceAfter *big.Int + if feeTokenWrapper != nil { + feeTokenBalanceAfter, err = feeTokenWrapper.BalanceOf(&bind.CallOpts{ + Context: ctx, + }, srcChain.DeployerKey.From) + require.NoError(tc.t, err) + } else { + feeTokenBalanceAfter, err = srcChain.Client.BalanceAt(ctx, srcChain.DeployerKey.From, nil) + require.NoError(tc.t, err) + } + tc.t.Logf("fee token balance after: %s, fee token: %s, fee paid: %s", + feeTokenBalanceAfter.String(), tc.feeToken.String(), msgSentEvent.Message.FeeTokenAmount) + // in the case we have no fee token, native is also used to pay for the tx, + // so we have to subtract that as well + if feeTokenWrapper == nil { + receipt, err := srcChain.Client.TransactionReceipt(ctx, msgSentEvent.Raw.TxHash) + require.NoError(tc.t, err) + txCostWei := new(big.Int).Mul(new(big.Int).SetUint64(receipt.GasUsed), receipt.EffectiveGasPrice) + feeTokenBalanceBefore.Sub(feeTokenBalanceBefore, txCostWei) + } + require.Equal( + tc.t, + feeTokenBalanceAfter, + new(big.Int).Sub(feeTokenBalanceBefore, msgSentEvent.Message.FeeTokenAmount), + ) + + if tc.assertExecution { + // Wait for all commit reports to land. + testhelpers.ConfirmCommitForAllWithExpectedSeqNums(tc.t, tc.env, state, expectedSeqNum, startBlocks) + + // After commit is reported on all chains, token prices should be updated in FeeQuoter. + linkAddress := state.Chains[tc.dst].LinkToken.Address() + feeQuoter := state.Chains[tc.dst].FeeQuoter + timestampedPrice, err := feeQuoter.GetTokenPrice(&bind.CallOpts{ + Context: ctx, + }, linkAddress) + require.NoError(tc.t, err) + require.Equal(tc.t, changeset.MockLinkPrice, timestampedPrice.Value) + + // Wait for all exec reports to land + testhelpers.ConfirmExecWithSeqNrsForAll(tc.t, tc.env, state, expectedSeqNumExec, startBlocks) + } + + if tc.assertTokenBalance { + require.Len(tc.t, tc.tokenAmounts, 1) + expectedTransferAmount := tc.tokenAmounts[0].Amount + + balanceAfter, err := tc.dstToken.BalanceOf(&bind.CallOpts{ + Context: ctx, + }, state.Chains[tc.dst].Receiver.Address()) + require.NoError(tc.t, err) + require.Equal( + tc.t, + new(big.Int).Add(dstTokBalanceBefore, expectedTransferAmount), + balanceAfter, + ) + } +} diff --git a/deployment/ccip/changeset/testhelpers/test_helpers.go b/deployment/ccip/changeset/testhelpers/test_helpers.go index 2d9aeca97a8..1127e34a89b 100644 --- a/deployment/ccip/changeset/testhelpers/test_helpers.go +++ b/deployment/ccip/changeset/testhelpers/test_helpers.go @@ -221,10 +221,7 @@ func CCIPSendRequest( } tx, err := r.CcipSend(cfg.Sender, cfg.DestChain, msg) - if err != nil { - return nil, 0, errors.Wrap(err, "failed to send CCIP message") - } - blockNum, err := e.Chains[cfg.SourceChain].Confirm(tx) + blockNum, err := deployment.ConfirmIfNoErrorWithABI(e.Chains[cfg.SourceChain], tx, router.RouterABI, err) if err != nil { return tx, 0, errors.Wrap(err, "failed to confirm CCIP message") } diff --git a/integration-tests/smoke/ccip/ccip_fees_test.go b/integration-tests/smoke/ccip/ccip_fees_test.go index 5507f92096a..bcad18b508d 100644 --- a/integration-tests/smoke/ccip/ccip_fees_test.go +++ b/integration-tests/smoke/ccip/ccip_fees_test.go @@ -4,24 +4,18 @@ import ( "math/big" "testing" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext" - "github.com/smartcontractkit/chainlink/deployment" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset" "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers" + "github.com/smartcontractkit/chainlink/deployment/ccip/changeset/testhelpers/feestest" testsetups "github.com/smartcontractkit/chainlink/integration-tests/testsetups/ccip" - "github.com/smartcontractkit/chainlink-integrations/evm/assets" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/v1_2_0/router" "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677" - "github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/weth9" "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -110,7 +104,7 @@ func Test_CCIPFees(t *testing.T) { ) e := tenv.Env - allChains := tenv.Env.AllChainSelectors() + allChains := e.AllChainSelectors() require.Len(t, allChains, 2, "need two chains for this test") sourceChain := allChains[0] destChain := allChains[1] @@ -136,71 +130,71 @@ func Test_CCIPFees(t *testing.T) { testhelpers.AddLanesForAll(t, &tenv, state) t.Run("Send programmable token transfer pay with Link token", func(t *testing.T) { - runFeeTokenTestCase(feeTokenTestCase{ - t: t, - dst: destChain, - src: sourceChain, - env: tenv, - tokenAmounts: []router.ClientEVMTokenAmount{ + feestest.RunFeeTokenTestCase(feestest.NewFeeTokenTestCase( + t, + e, + sourceChain, + destChain, + state.Chains[sourceChain].LinkToken.Address(), // feeToken + []router.ClientEVMTokenAmount{ { Token: srcToken.Address(), Amount: deployment.E18Mult(2), }, }, - feeToken: state.Chains[sourceChain].LinkToken.Address(), - data: []byte("hello ptt world"), - receiver: common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - srcToken: srcToken, - dstToken: dstToken, - assertTokenBalance: true, - }) + srcToken, + dstToken, + common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), // receiver + []byte("hello ptt world"), // data + true, // assertTokenBalance + true, // assertExecution + )) }) t.Run("Send programmable token transfer pay with native", func(t *testing.T) { - runFeeTokenTestCase(feeTokenTestCase{ - t: t, - + feestest.RunFeeTokenTestCase(feestest.NewFeeTokenTestCase( + t, + e, // note the order of src and dest is reversed here - src: destChain, - dst: sourceChain, - - env: tenv, - tokenAmounts: []router.ClientEVMTokenAmount{ + destChain, + sourceChain, + common.HexToAddress("0x0"), // feeToken + []router.ClientEVMTokenAmount{ { Token: dstToken.Address(), Amount: deployment.E18Mult(2), }, }, - feeToken: common.HexToAddress("0x0"), - data: []byte("hello ptt world"), - receiver: common.LeftPadBytes(state.Chains[sourceChain].Receiver.Address().Bytes(), 32), - // note the order of src and dest is reversed here - srcToken: dstToken, - dstToken: srcToken, - assertTokenBalance: true, - }) + dstToken, + srcToken, + common.LeftPadBytes(state.Chains[sourceChain].Receiver.Address().Bytes(), 32), // receiver + []byte("hello ptt world"), // data + true, // assertTokenBalance + true, // assertExecution + )) }) t.Run("Send programmable token transfer pay with wrapped native", func(t *testing.T) { - runFeeTokenTestCase(feeTokenTestCase{ - t: t, - src: sourceChain, - dst: destChain, - env: tenv, - tokenAmounts: []router.ClientEVMTokenAmount{ + feestest.RunFeeTokenTestCase(feestest.NewFeeTokenTestCase( + t, + e, + sourceChain, + destChain, + state.Chains[sourceChain].Weth9.Address(), // feeToken + []router.ClientEVMTokenAmount{ { Token: srcToken.Address(), Amount: deployment.E18Mult(2), }, }, - feeToken: state.Chains[sourceChain].Weth9.Address(), - data: []byte("hello ptt world"), - receiver: common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - srcToken: srcToken, - dstToken: dstToken, - assertTokenBalance: true, - }) + srcToken, + dstToken, + common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), // receiver + []byte("hello ptt world"), // data + true, // assertTokenBalance + true, // assertExecution + )) }) t.Run("Send programmable token transfer but revert not enough tokens", func(t *testing.T) { @@ -240,222 +234,56 @@ func Test_CCIPFees(t *testing.T) { }) t.Run("Send data-only message pay with link token", func(t *testing.T) { - runFeeTokenTestCase(feeTokenTestCase{ - t: t, - src: sourceChain, - dst: destChain, - env: tenv, + feestest.RunFeeTokenTestCase(feestest.NewFeeTokenTestCase( + t, + e, + sourceChain, + destChain, // no tokens, only data - tokenAmounts: nil, - feeToken: state.Chains[sourceChain].LinkToken.Address(), - data: []byte("hello link world"), - receiver: common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - srcToken: srcToken, - dstToken: dstToken, - assertTokenBalance: false, - }) + state.Chains[sourceChain].LinkToken.Address(), // feeToken + nil, // tokenAmounts + srcToken, + dstToken, + common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), // receiver + []byte("hello link world"), // data + false, // assertTokenBalance + true, // assertExecution + )) }) t.Run("Send message pay with native", func(t *testing.T) { - runFeeTokenTestCase(feeTokenTestCase{ - t: t, - src: sourceChain, - dst: destChain, - env: tenv, + feestest.RunFeeTokenTestCase(feestest.NewFeeTokenTestCase( + t, + e, + sourceChain, + destChain, + common.HexToAddress("0x0"), // feeToken // no tokens, only data - tokenAmounts: nil, - feeToken: common.HexToAddress("0x0"), - data: []byte("hello native world"), - receiver: common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - srcToken: srcToken, - dstToken: dstToken, - assertTokenBalance: false, - }) + nil, // tokenAmounts + srcToken, + dstToken, + common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), // receiver + []byte("hello native world"), // data + false, // assertTokenBalance + true, // assertExecution + )) }) t.Run("Send message pay with wrapped native", func(t *testing.T) { - runFeeTokenTestCase(feeTokenTestCase{ - t: t, - src: sourceChain, - dst: destChain, - env: tenv, + feestest.RunFeeTokenTestCase(feestest.NewFeeTokenTestCase( + t, + e, + sourceChain, + destChain, + state.Chains[sourceChain].Weth9.Address(), // feeToken // no tokens, only data - tokenAmounts: nil, - feeToken: state.Chains[sourceChain].Weth9.Address(), - data: []byte("hello wrapped native world"), - receiver: common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), - srcToken: srcToken, - dstToken: dstToken, - assertTokenBalance: false, - }) + nil, // tokenAmounts + srcToken, + dstToken, + common.LeftPadBytes(state.Chains[destChain].Receiver.Address().Bytes(), 32), // receiver + []byte("hello wrapped native world"), // data + false, // assertTokenBalance + true, // assertExecution + )) }) } - -type feeTokenTestCase struct { - t *testing.T - src, dst uint64 - env testhelpers.DeployedEnv - srcToken, dstToken *burn_mint_erc677.BurnMintERC677 - tokenAmounts []router.ClientEVMTokenAmount - feeToken common.Address - receiver []byte - data []byte - assertTokenBalance bool -} - -func runFeeTokenTestCase(tc feeTokenTestCase) { - ctx := tests.Context(tc.t) - // Need to keep track of the block number for each chain so that event subscription can be done from that block. - startBlocks := make(map[uint64]*uint64) - expectedSeqNum := make(map[testhelpers.SourceDestPair]uint64) - expectedSeqNumExec := make(map[testhelpers.SourceDestPair][]uint64) - - srcChain := tc.env.Env.Chains[tc.src] - dstChain := tc.env.Env.Chains[tc.dst] - - state, err := changeset.LoadOnchainState(tc.env.Env) - require.NoError(tc.t, err) - - var dstTokBalanceBefore *big.Int - if tc.assertTokenBalance { - var err error - dstTokBalanceBefore, err = tc.dstToken.BalanceOf(nil, state.Chains[tc.dst].Receiver.Address()) - require.NoError(tc.t, err) - tc.t.Logf("destination token balance before of receiver %s: %s", - state.Chains[tc.dst].Receiver.Address(), - dstTokBalanceBefore.String()) - } - - // if fee token is not native then approve the router to spend the fee token from the sender. - var feeTokenWrapper *burn_mint_erc677.BurnMintERC677 - if tc.feeToken != common.HexToAddress("0x0") { - if tc.feeToken == state.Chains[tc.src].Weth9.Address() { - // Deposit some ETH into the WETH contract - weth9, err := weth9.NewWETH9(state.Chains[tc.src].Weth9.Address(), srcChain.Client) - require.NoError(tc.t, err) - - balance, err := srcChain.Client.BalanceAt(ctx, srcChain.DeployerKey.From, nil) - require.NoError(tc.t, err) - - tc.t.Logf("balance before deposit: %s", balance.String()) - - srcChain.DeployerKey.Value = assets.Ether(100).ToInt() - tx, err := weth9.Deposit(srcChain.DeployerKey) - _, err = deployment.ConfirmIfNoError(srcChain, tx, err) - require.NoError(tc.t, err) - srcChain.DeployerKey.Value = big.NewInt(0) - } - - var err error - feeTokenWrapper, err = burn_mint_erc677.NewBurnMintERC677(tc.feeToken, srcChain.Client) - require.NoError(tc.t, err) - - // Approve the router to spend fee token - tx, err := feeTokenWrapper.Approve(srcChain.DeployerKey, state.Chains[tc.src].Router.Address(), math.MaxBig256) - - _, err = deployment.ConfirmIfNoError(srcChain, tx, err) - require.NoError(tc.t, err) - } - - // get the header for the destination chain and the relevant block number - latesthdr, err := dstChain.Client.HeaderByNumber(testcontext.Get(tc.t), nil) - require.NoError(tc.t, err) - block := latesthdr.Number.Uint64() - startBlocks[tc.dst] = &block - - // Get the fee Token Balance Before, if not fee token set get native balance. - var feeTokenBalanceBefore *big.Int - if feeTokenWrapper != nil { - feeTokenBalanceBefore, err = feeTokenWrapper.BalanceOf(&bind.CallOpts{ - Context: ctx, - }, srcChain.DeployerKey.From) - require.NoError(tc.t, err) - } else { - feeTokenBalanceBefore, err = srcChain.Client.BalanceAt(ctx, srcChain.DeployerKey.From, nil) - require.NoError(tc.t, err) - } - tc.t.Logf("fee token balance before: %s, fee token enabled: %s", - feeTokenBalanceBefore.String(), tc.feeToken.String()) - - msgSentEvent := testhelpers.TestSendRequest( - tc.t, - tc.env.Env, - state, - tc.src, - tc.dst, - false, - router.ClientEVM2AnyMessage{ - Receiver: tc.receiver, - Data: tc.data, - TokenAmounts: tc.tokenAmounts, - FeeToken: tc.feeToken, - ExtraArgs: nil, - }, - ) - - expectedSeqNum[testhelpers.SourceDestPair{ - SourceChainSelector: tc.src, - DestChainSelector: tc.dst, - }] = msgSentEvent.SequenceNumber - expectedSeqNumExec[testhelpers.SourceDestPair{ - SourceChainSelector: tc.src, - DestChainSelector: tc.dst, - }] = []uint64{msgSentEvent.SequenceNumber} - - // Check the fee token balance after the request and ensure fee tokens were spent - var feeTokenBalanceAfter *big.Int - if feeTokenWrapper != nil { - feeTokenBalanceAfter, err = feeTokenWrapper.BalanceOf(&bind.CallOpts{ - Context: ctx, - }, srcChain.DeployerKey.From) - require.NoError(tc.t, err) - } else { - feeTokenBalanceAfter, err = srcChain.Client.BalanceAt(ctx, srcChain.DeployerKey.From, nil) - require.NoError(tc.t, err) - } - tc.t.Logf("fee token balance after: %s, fee token: %s, fee paid: %s", - feeTokenBalanceAfter.String(), tc.feeToken.String(), msgSentEvent.Message.FeeTokenAmount) - // in the case we have no fee token, native is also used to pay for the tx, - // so we have to subtract that as well - if feeTokenWrapper == nil { - receipt, err := srcChain.Client.TransactionReceipt(ctx, msgSentEvent.Raw.TxHash) - require.NoError(tc.t, err) - txCostWei := new(big.Int).Mul(new(big.Int).SetUint64(receipt.GasUsed), receipt.EffectiveGasPrice) - feeTokenBalanceBefore.Sub(feeTokenBalanceBefore, txCostWei) - } - require.Equal( - tc.t, - feeTokenBalanceAfter, - new(big.Int).Sub(feeTokenBalanceBefore, msgSentEvent.Message.FeeTokenAmount), - ) - - // Wait for all commit reports to land. - testhelpers.ConfirmCommitForAllWithExpectedSeqNums(tc.t, tc.env.Env, state, expectedSeqNum, startBlocks) - - // After commit is reported on all chains, token prices should be updated in FeeQuoter. - linkAddress := state.Chains[tc.dst].LinkToken.Address() - feeQuoter := state.Chains[tc.dst].FeeQuoter - timestampedPrice, err := feeQuoter.GetTokenPrice(&bind.CallOpts{ - Context: ctx, - }, linkAddress) - require.NoError(tc.t, err) - require.Equal(tc.t, changeset.MockLinkPrice, timestampedPrice.Value) - - // Wait for all exec reports to land - testhelpers.ConfirmExecWithSeqNrsForAll(tc.t, tc.env.Env, state, expectedSeqNumExec, startBlocks) - - if tc.assertTokenBalance { - require.Len(tc.t, tc.tokenAmounts, 1) - expectedTransferAmount := tc.tokenAmounts[0].Amount - - balanceAfter, err := tc.dstToken.BalanceOf(&bind.CallOpts{ - Context: ctx, - }, state.Chains[tc.dst].Receiver.Address()) - require.NoError(tc.t, err) - require.Equal( - tc.t, - new(big.Int).Add(dstTokBalanceBefore, expectedTransferAmount), - balanceAfter, - ) - } -}