Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CCIP-5117] integration-tests/smoke/ccip: factor out fee test helpers #16496

Merged
merged 3 commits into from
Feb 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/integration-in-memory-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was taking really long on my PC but I think there's an issue w/ my PC. Anyway, doesn't hurt to bump, will revisit this once we drop round times significantly and make the tests faster.


- id: smoke/ccip/ccip_messaging_test.go:*
path: integration-tests/smoke/ccip/ccip_messaging_test.go
Expand Down
228 changes: 228 additions & 0 deletions deployment/ccip/changeset/testhelpers/feestest/helpers.go
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code is basically copy/paste of what was in ccip_fees_test.go, just made public and moved to deployment so we can use it in CLD

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,
)
}
}
5 changes: 1 addition & 4 deletions deployment/ccip/changeset/testhelpers/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Comment on lines -227 to +224
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prints out revert reason, useful

if err != nil {
return tx, 0, errors.Wrap(err, "failed to confirm CCIP message")
}
Expand Down
Loading
Loading