-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CCIP-5117] integration-tests/smoke/ccip: factor out fee test helpers (…
…#16496) * integration-tests/smoke/ccip: factor out fee test helpers * goimports * fix comment
- Loading branch information
Showing
4 changed files
with
316 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
228 changes: 228 additions & 0 deletions
228
deployment/ccip/changeset/testhelpers/feestest/helpers.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
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, | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.