From 2de0bf9a70480351025b9f6d0f48eebd1b8bc2aa Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 14 Nov 2024 11:26:18 -0500 Subject: [PATCH] additional e2e integration test first version --- integration-tests/smoke/ccip_test.go | 458 +++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) diff --git a/integration-tests/smoke/ccip_test.go b/integration-tests/smoke/ccip_test.go index 5b0ba285527..a5e7e69d76f 100644 --- a/integration-tests/smoke/ccip_test.go +++ b/integration-tests/smoke/ccip_test.go @@ -1,6 +1,7 @@ package smoke import ( + "fmt" "math/big" "testing" @@ -276,3 +277,460 @@ func TestTokenTransfer(t *testing.T) { require.NoError(t, err) require.Equal(t, twoCoins, balance) } + +func Test_PricingForTokenTransfers(t *testing.T) { + // Deplot the environment + lggr := logger.TestLogger(t) + ctx := ccdeploy.Context(t) + tenv, _, _ := testsetups.NewLocalDevEnvironment(t, lggr) + + // Load the test state + e := tenv.Env + state, err := ccdeploy.LoadOnchainState(e) + require.NoError(t, err) + + feeds := state.Chains[tenv.FeedChainSel].USDFeeds + tokenConfig := ccdeploy.NewTokenConfig() + tokenConfig.UpsertTokenInfo(ccdeploy.LinkSymbol, + pluginconfig.TokenInfo{ + AggregatorAddress: cciptypes.UnknownEncodedAddress(feeds[ccdeploy.LinkSymbol].Address().String()), + Decimals: ccdeploy.LinkDecimals, + DeviationPPB: cciptypes.NewBigIntFromInt64(1e9), + }, + ) + + // Apply migration + output, err := changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{ + HomeChainSel: tenv.HomeChainSel, + FeedChainSel: tenv.FeedChainSel, + ChainsToDeploy: e.AllChainSelectors(), + TokenConfig: tokenConfig, + MCMSConfig: ccdeploy.NewTestMCMSConfig(t, e), + OCRSecrets: deployment.XXXGenerateTestOCRSecrets(), + }) + require.NoError(t, err) + require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook)) + // Get new state after migration and mock USDC token deployment. + state, err = ccdeploy.LoadOnchainState(e) + require.NoError(t, err) + + // Deploy the token to test transfering + srcToken, _, dstToken, _, err := ccdeploy.DeployTransferableToken( + lggr, + tenv.Env.Chains, + tenv.HomeChainSel, + tenv.FeedChainSel, + state, + e.ExistingAddresses, + "MY_TOKEN", + ) + require.NoError(t, err) + + // Ensure capreg logs are up to date. + ccdeploy.ReplayLogs(t, e.Offchain, tenv.ReplayBlocks) + + // Apply the jobs. + for nodeID, jobs := range output.JobSpecs { + for _, job := range jobs { + // Note these auto-accept + _, err := e.Offchain.ProposeJob(ctx, + &jobv1.ProposeJobRequest{ + NodeId: nodeID, + Spec: job, + }) + require.NoError(t, err) + } + } + + // Add all lanes + require.NoError(t, ccdeploy.AddLanesForAll(e, state)) + // 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) + // Send a message from each chain to every other chain. + expectedSeqNum := make(map[uint64]uint64) + + // Mint 2 tokens to be transfered + twoCoins := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(2)) + tx, err := srcToken.Mint( + e.Chains[tenv.HomeChainSel].DeployerKey, + e.Chains[tenv.HomeChainSel].DeployerKey.From, + new(big.Int).Mul(twoCoins, big.NewInt(10)), + ) + + if err != nil { + fmt.Printf("Error minting token: %v\n", err) + } + + // Confirm that mint tx + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + // Mint a destination token + tx, err = dstToken.Mint( + e.Chains[tenv.FeedChainSel].DeployerKey, + e.Chains[tenv.FeedChainSel].DeployerKey.From, + new(big.Int).Mul(twoCoins, big.NewInt(10)), + ) + + // Confirm the mint tx + require.NoError(t, err) + _, err = e.Chains[tenv.FeedChainSel].Confirm(tx) + require.NoError(t, err) + + linkToken := state.Chains[tenv.HomeChainSel].LinkToken + + // Grant mint and burn roles to the deployer key for the newly deployed linkToken + // Since those roles are not granted automatically + tx, err = linkToken.GrantMintAndBurnRoles(e.Chains[tenv.HomeChainSel].DeployerKey, e.Chains[tenv.HomeChainSel].DeployerKey.From) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + // Mint link token and confirm the tx + tx, err = linkToken.Mint( + e.Chains[tenv.HomeChainSel].DeployerKey, + e.Chains[tenv.HomeChainSel].DeployerKey.From, + new(big.Int).Mul(twoCoins, big.NewInt(10)), + ) + require.NoError(t, err) + + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + t.Run("Send Token Pay with Link token home chain -> remote", func(t *testing.T) { + // Approve the router to spend the tokens and confirm the tx's + tx, err = srcToken.Approve(e.Chains[tenv.HomeChainSel].DeployerKey, state.Chains[tenv.HomeChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + tx, err = dstToken.Approve(e.Chains[tenv.FeedChainSel].DeployerKey, state.Chains[tenv.FeedChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.FeedChainSel].Confirm(tx) + require.NoError(t, err) + // Approve to spend link token + tx, err = linkToken.Approve(e.Chains[tenv.HomeChainSel].DeployerKey, state.Chains[tenv.HomeChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + // Create two ClientEVMTokenAmount structs to be passed to the router + tokens := map[uint64][]router.ClientEVMTokenAmount{ + tenv.HomeChainSel: {{ + Token: srcToken.Address(), + Amount: twoCoins, + }}, + tenv.FeedChainSel: {{ + Token: dstToken.Address(), + Amount: twoCoins, + }}, + } + + // Assign Src to the Home Chain Selector and destChain to the Feed Chain Selector + src := tenv.HomeChainSel + dest, destChain := tenv.FeedChainSel, e.Chains[tenv.FeedChainSel] + + // get the header for the destination chain and the relevant block number + latesthdr, err := destChain.Client.HeaderByNumber(testcontext.Get(t), nil) + require.NoError(t, err) + block := latesthdr.Number.Uint64() + startBlocks[dest] = &block + + // Send to the receiver on the destination chain paying with LINK token + var ( + receiver = common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32) + data = []byte("hello world") + feeToken = linkToken.Address() + ) + + ccipMessage := router.ClientEVM2AnyMessage{ + Receiver: receiver, + Data: data, + TokenAmounts: tokens[src], + FeeToken: feeToken, + ExtraArgs: nil, + } + + // Get the fee Token Balance Before + feeTokenBalanceBefore, _ := linkToken.BalanceOf(nil, e.Chains[tenv.HomeChainSel].DeployerKey.From) + + // Check the fee Amount + srcFee, err := state.Chains[src].Router.GetFee(nil, dest, ccipMessage) + require.NoError(t, err) + + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, ccipMessage) + expectedSeqNum[dest] = seqNum + + // Check the fee token balance after the request and ensure fee tokens were spent + feeTokenBalanceAfter, _ := linkToken.BalanceOf(nil, e.Chains[tenv.HomeChainSel].DeployerKey.From) + require.Equal(t, feeTokenBalanceAfter, new(big.Int).Sub(feeTokenBalanceBefore, srcFee)) + + // Wait for all commit reports to land. + ccdeploy.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks) + + // After commit is reported on all chains, token prices should be updated in FeeQuoter. + linkAddress := state.Chains[dest].LinkToken.Address() + feeQuoter := state.Chains[dest].FeeQuoter + timestampedPrice, err := feeQuoter.GetTokenPrice(nil, linkAddress) + require.NoError(t, err) + require.Equal(t, ccdeploy.MockLinkPrice, timestampedPrice.Value) + + // Wait for all exec reports to land + ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks) + + balance, err := dstToken.BalanceOf(nil, state.Chains[tenv.FeedChainSel].Receiver.Address()) + require.NoError(t, err) + require.Equal(t, twoCoins, balance) + + // Delete from the expcted seq num the chain that was just tested so that we don't pass it to the + // commit report in the next test + delete(expectedSeqNum, dest) + }) + + t.Run("Send Token Pay with native remote chain -> home", func(t *testing.T) { + // Approve the router to spend the tokens and confirm the tx's + tx, err = srcToken.Approve(e.Chains[tenv.HomeChainSel].DeployerKey, state.Chains[tenv.HomeChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + tx, err = dstToken.Approve(e.Chains[tenv.FeedChainSel].DeployerKey, state.Chains[tenv.FeedChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.FeedChainSel].Confirm(tx) + require.NoError(t, err) + + // Create two ClientEVMTokenAmount structs to be passed to the router + tokens := map[uint64][]router.ClientEVMTokenAmount{ + tenv.HomeChainSel: {{ + Token: dstToken.Address(), + Amount: twoCoins, + }}, + tenv.FeedChainSel: {{ + Token: srcToken.Address(), + Amount: twoCoins, + }}, + } + + // Assign Src to the Home Chain Selector and destChain to the Feed Chain Selector + src := tenv.FeedChainSel + dest, destChain := tenv.HomeChainSel, e.Chains[tenv.HomeChainSel] + + // get the header for the destination chain and the relevant block number + latesthdr, err := destChain.Client.HeaderByNumber(testcontext.Get(t), nil) + require.NoError(t, err) + block := latesthdr.Number.Uint64() + startBlocks[dest] = &block + + // Send to the receiver on the destination chain paying with native token + var ( + receiver = common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32) + data = []byte("hello world") + feeToken = common.HexToAddress("0x0") + ) + + ccipMessage := router.ClientEVM2AnyMessage{ + Receiver: receiver, + Data: data, + TokenAmounts: tokens[dest], + FeeToken: feeToken, + ExtraArgs: nil, + } + + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, ccipMessage) + expectedSeqNum[dest] = seqNum + + // Wait for all commit reports to land. + ccdeploy.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks) + + // After commit is reported on all chains, token prices should be updated in FeeQuoter. + linkAddress := state.Chains[dest].LinkToken.Address() + feeQuoter := state.Chains[dest].FeeQuoter + timestampedPrice, err := feeQuoter.GetTokenPrice(nil, linkAddress) + require.NoError(t, err) + require.Equal(t, ccdeploy.MockLinkPrice, timestampedPrice.Value) + + // Wait for all exec reports to land + ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks) + + balance, err := srcToken.BalanceOf(nil, state.Chains[tenv.HomeChainSel].Receiver.Address()) + require.NoError(t, err) + require.Equal(t, twoCoins, balance) + + delete(expectedSeqNum, dest) + }) + + t.Run("Send Token pay with wrapped native home chain -> remote", func(t *testing.T) { + WETH := state.Chains[tenv.HomeChainSel].Weth9 + + // We need to acquire some WETH to send with the tx so we deposit some ETH into the WETH contract + // but deployerKey is a reference so we need to dereference it and then reassign it + depositOps := *e.Chains[tenv.HomeChainSel].DeployerKey + depositOps.Value = twoCoins + tx, err = WETH.Deposit(&depositOps) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + // Approve the router to spend the tokens and confirm the tx's + tx, err = srcToken.Approve(e.Chains[tenv.HomeChainSel].DeployerKey, state.Chains[tenv.HomeChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + print("APPROVED SRC TO ROUTER") + + tx, err = dstToken.Approve(e.Chains[tenv.FeedChainSel].DeployerKey, state.Chains[tenv.FeedChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.FeedChainSel].Confirm(tx) + require.NoError(t, err) + // Approve to spend WETH token as fee + tx, err = WETH.Approve(e.Chains[tenv.HomeChainSel].DeployerKey, state.Chains[tenv.HomeChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + // Create two ClientEVMTokenAmount structs to be passed to the router + tokens := map[uint64][]router.ClientEVMTokenAmount{ + tenv.HomeChainSel: {{ + Token: srcToken.Address(), + Amount: twoCoins, + }}, + tenv.FeedChainSel: {{ + Token: dstToken.Address(), + Amount: twoCoins, + }}, + } + + // Assign Src to the Home Chain Selector and destChain to the Feed Chain Selector + src := tenv.HomeChainSel + dest, destChain := tenv.FeedChainSel, e.Chains[tenv.FeedChainSel] + + // get the header for the destination chain and the relevant block number + latesthdr, err := destChain.Client.HeaderByNumber(testcontext.Get(t), nil) + require.NoError(t, err) + block := latesthdr.Number.Uint64() + startBlocks[dest] = &block + + // Send to the receiver on the destination chain paying with LINK token + var ( + receiver = common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32) + data = []byte("") + feeToken = state.Chains[src].Weth9.Address() + ) + + ccipMessage := router.ClientEVM2AnyMessage{ + Receiver: receiver, + Data: data, + TokenAmounts: tokens[src], + FeeToken: feeToken, + ExtraArgs: nil, + } + + // Get the fee Token Balance Before + feeTokenBalanceBefore, err := WETH.BalanceOf(nil, e.Chains[src].DeployerKey.From) + require.NoError(t, err) + + // Check the fee Amount + srcFee, err := state.Chains[src].Router.GetFee(nil, dest, ccipMessage) + require.NoError(t, err) + + seqNum := ccdeploy.TestSendRequest(t, e, state, src, dest, false, ccipMessage) + expectedSeqNum[dest] = seqNum + + // Check the fee token balance after the request and ensure fee tokens were spent + feeTokenBalanceAfter, err := WETH.BalanceOf(nil, e.Chains[tenv.HomeChainSel].DeployerKey.From) + require.NoError(t, err) + require.Equal(t, feeTokenBalanceAfter, new(big.Int).Sub(feeTokenBalanceBefore, srcFee)) + + // Wait for all commit reports to land. + ccdeploy.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks) + + // After commit is reported on all chains, token prices should be updated in FeeQuoter. + linkAddress := state.Chains[dest].LinkToken.Address() + feeQuoter := state.Chains[dest].FeeQuoter + timestampedPrice, err := feeQuoter.GetTokenPrice(nil, linkAddress) + require.NoError(t, err) + require.Equal(t, ccdeploy.MockLinkPrice, timestampedPrice.Value) + + // Wait for all exec reports to land + ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks) + + balance, err := dstToken.BalanceOf(nil, state.Chains[dest].Receiver.Address()) + require.NoError(t, err) + + // The balance should be 4 since we've already sent 2 tokens over this lane direction in the + // first part of the test, so balance should already be two + require.Equal(t, new(big.Int).Mul(twoCoins, big.NewInt(2)), balance) + }) + + t.Run("Send Token but revert not enough tokens", func(t *testing.T) { + // Approve the router to spend the tokens and confirm the tx's + tx, err = srcToken.Approve(e.Chains[tenv.HomeChainSel].DeployerKey, state.Chains[tenv.HomeChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.HomeChainSel].Confirm(tx) + require.NoError(t, err) + + tx, err = dstToken.Approve(e.Chains[tenv.FeedChainSel].DeployerKey, state.Chains[tenv.FeedChainSel].Router.Address(), twoCoins) + require.NoError(t, err) + _, err = e.Chains[tenv.FeedChainSel].Confirm(tx) + require.NoError(t, err) + + existingBalanceSrc, err := dstToken.BalanceOf(nil, e.Chains[tenv.HomeChainSel].DeployerKey.From) + require.NoError(t, err) + + existingBalanceDst, err := dstToken.BalanceOf(nil, e.Chains[tenv.FeedChainSel].DeployerKey.From) + require.NoError(t, err) + + // Assign Src to the Home Chain Selector and destChain to the Feed Chain Selector + src := tenv.FeedChainSel + dest, destChain := tenv.HomeChainSel, e.Chains[tenv.HomeChainSel] + + // Create two ClientEVMTokenAmount structs to be passed to the router + tokens := map[uint64][]router.ClientEVMTokenAmount{ + tenv.HomeChainSel: {{ + Token: dstToken.Address(), + // Send twice as many tokens as available on purpose + Amount: new(big.Int).Mul(existingBalanceSrc, big.NewInt(2)), + }}, + tenv.FeedChainSel: {{ + Token: srcToken.Address(), + Amount: new(big.Int).Mul(existingBalanceDst, big.NewInt(10)), + }}, + } + + // get the header for the destination chain and the relevant block number + latesthdr, err := destChain.Client.HeaderByNumber(testcontext.Get(t), nil) + require.NoError(t, err) + block := latesthdr.Number.Uint64() + startBlocks[dest] = &block + + // Send to the receiver on the destination chain paying with native token + var ( + receiver = common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32) + data = []byte("") + feeToken = common.HexToAddress("0x0") + ) + + ccipMessage := router.ClientEVM2AnyMessage{ + Receiver: receiver, + Data: data, + TokenAmounts: tokens[dest], + FeeToken: feeToken, + ExtraArgs: nil, + } + + // Send the CCP Request + tx, _, err := ccdeploy.CCIPSendRequest( + e, + state, + src, dest, + false, + ccipMessage, + ) + + // Check if the transaction reverted + require.Error(t, err) + require.Nil(t, tx) + }) + +}