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-4160 Token Transfer Tests #15278

Merged
merged 36 commits into from
Nov 29, 2024
Merged
Changes from 4 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3bfac63
CCIP-4160 test refactoring
0xsuryansh Nov 18, 2024
4617621
resolving imports and using local dev env
0xsuryansh Nov 19, 2024
24dd4f6
comments fix
0xsuryansh Nov 19, 2024
8bae382
comments fix
0xsuryansh Nov 19, 2024
b4a4a98
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 20, 2024
25c1b15
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 21, 2024
94a2527
test update with common methods
0xsuryansh Nov 21, 2024
9be1966
import fix for lint
0xsuryansh Nov 21, 2024
18f5f2e
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 22, 2024
7f8fd25
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 22, 2024
3d969d6
refactor: updated test with new setup
0xsuryansh Nov 22, 2024
e2182dd
fix: lint (imports)
0xsuryansh Nov 22, 2024
8273eac
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 26, 2024
1bc99b3
Yolo
mateusz-sekara Nov 27, 2024
e1a80af
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 27, 2024
691300c
Merge remote-tracking branch 'origin/ms/self-serve-addr-test' into CC…
0xsuryansh Nov 27, 2024
8f4d4c5
feat: self serve pool test + ability to add custom deployer for token…
0xsuryansh Nov 27, 2024
36df2f6
fix : Use LocalDevEnvironment
0xsuryansh Nov 27, 2024
faca5a5
temp: trying out with AdditionalSimulatedPvtKeys
0xsuryansh Nov 27, 2024
211e668
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 27, 2024
c53e872
fix: lints and unused function
0xsuryansh Nov 28, 2024
1a03b58
Merge branch 'develop' into CCIP-4160_Implement_token_transfer_tests
0xsuryansh Nov 28, 2024
9037d57
fix: lints and unused function
0xsuryansh Nov 28, 2024
f829199
fix: imports for lint
0xsuryansh Nov 28, 2024
eee7c93
Move tt tests to a separate file
mateusz-sekara Nov 28, 2024
947b68d
Move tt tests to a separate file
mateusz-sekara Nov 28, 2024
eedb8fa
Fix
mateusz-sekara Nov 28, 2024
8308df9
Fix
mateusz-sekara Nov 28, 2024
6b726d7
Merge remote-tracking branch 'origin/develop' into CCIP-4160_Implemen…
mateusz-sekara Nov 28, 2024
20a017e
Fix
mateusz-sekara Nov 28, 2024
97c74df
Fix
mateusz-sekara Nov 28, 2024
7f3fb4f
Fix
mateusz-sekara Nov 28, 2024
b870f35
Fix
mateusz-sekara Nov 29, 2024
be9e925
Fix
mateusz-sekara Nov 29, 2024
c7d3987
Merge remote-tracking branch 'origin/develop' into CCIP-4160_Implemen…
mateusz-sekara Nov 29, 2024
d2fa64a
Fix
mateusz-sekara Nov 29, 2024
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
327 changes: 219 additions & 108 deletions integration-tests/smoke/ccip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import (
"testing"

"github.com/ethereum/go-ethereum/common"

"github.com/stretchr/testify/require"

jobv1 "github.com/smartcontractkit/chainlink-protos/job-distributor/v1/job"
"github.com/smartcontractkit/chainlink-testing-framework/lib/utils/testcontext"

"github.com/smartcontractkit/chainlink/deployment"
ccdeploy "github.com/smartcontractkit/chainlink/deployment/ccip"
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
"github.com/smartcontractkit/chainlink/integration-tests/ccip-tests/testsetups"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/ccip/generated/router"
"github.com/smartcontractkit/chainlink/v2/core/gethwrappers/shared/generated/burn_mint_erc677"
"github.com/smartcontractkit/chainlink/v2/core/logger"
)

Expand Down Expand Up @@ -112,21 +113,16 @@ func TestInitialDeployOnLocal(t *testing.T) {

func TestTokenTransfer(t *testing.T) {
t.Parallel()
lggr := logger.TestLogger(t)
ctx := ccdeploy.Context(t)
tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr)

tenv, state := setupEnvironment(t)
0xsuryansh marked this conversation as resolved.
Show resolved Hide resolved
e := tenv.Env
state, err := ccdeploy.LoadOnchainState(e)
require.NoError(t, err)

output, err := changeset.DeployPrerequisites(e, changeset.DeployPrerequisiteConfig{
output, err := changeset.DeployPrerequisites(tenv.Env, changeset.DeployPrerequisiteConfig{
ChainSelectors: e.AllChainSelectors(),
})

require.NoError(t, err)
require.NoError(t, e.ExistingAddresses.Merge(output.AddressBook))

// Apply migration
// Deploy CCIP contracts
output, err = changeset.InitialDeploy(e, ccdeploy.DeployCCIPContractConfig{
HomeChainSel: tenv.HomeChainSel,
FeedChainSel: tenv.FeedChainSel,
Expand All @@ -137,29 +133,16 @@ func TestTokenTransfer(t *testing.T) {
})
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)

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,
_, err := e.Offchain.ProposeJob(ccdeploy.Context(t),
&jobv1.ProposeJobRequest{
NodeId: nodeID,
Spec: job,
Expand All @@ -168,105 +151,233 @@ func TestTokenTransfer(t *testing.T) {
}
}

// Add all lanes
// 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)

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)),
)
require.NoError(t, err)
_, err = e.Chains[tenv.HomeChainSel].Confirm(tx)
require.NoError(t, err)
// Deploy and approve tokens.
srcToken1, dstToken1 := deployAndApproveTokens(t, tenv, state, "Token1")
mateusz-sekara marked this conversation as resolved.
Show resolved Hide resolved
srcToken2, dstToken2 := deployAndApproveTokens(t, tenv, state, "Token2")

// Test scenarios are defined here
scenarios := []struct {
name string
srcChain uint64
dstChain uint64
tokenAmounts []router.ClientEVMTokenAmount
receiver common.Address
data []byte
mateusz-sekara marked this conversation as resolved.
Show resolved Hide resolved
}{
{
name: "Send token to EOA",
srcChain: tenv.HomeChainSel,
dstChain: tenv.FeedChainSel,
0xsuryansh marked this conversation as resolved.
Show resolved Hide resolved
tokenAmounts: []router.ClientEVMTokenAmount{
{
Token: srcToken1.Address(),
Amount: big.NewInt(1e18),
},
},
receiver: e.Chains[tenv.FeedChainSel].DeployerKey.From,
data: []byte(""),
mateusz-sekara marked this conversation as resolved.
Show resolved Hide resolved
},
{
name: "Send token to contract",
srcChain: tenv.HomeChainSel,
dstChain: tenv.FeedChainSel,
tokenAmounts: []router.ClientEVMTokenAmount{
{
Token: srcToken1.Address(),
Amount: big.NewInt(1e18),
},
},
receiver: state.Chains[tenv.FeedChainSel].Receiver.Address(),
data: []byte(""),
},
{
name: "Send 2 tokens to receiver",
srcChain: tenv.HomeChainSel,
dstChain: tenv.FeedChainSel,
tokenAmounts: []router.ClientEVMTokenAmount{
{
Token: srcToken1.Address(),
Amount: big.NewInt(1e18),
},
{
Token: srcToken2.Address(),
Amount: big.NewInt(2e18),
},
},
receiver: e.Chains[tenv.FeedChainSel].DeployerKey.From,
data: []byte(""),
},
{
name: "Send N tokens to contract",
srcChain: tenv.HomeChainSel,
dstChain: tenv.FeedChainSel,
tokenAmounts: []router.ClientEVMTokenAmount{
{
Token: srcToken1.Address(),
Amount: big.NewInt(1e18),
},
{
Token: srcToken2.Address(),
Amount: big.NewInt(2e18),
},
{
Token: srcToken1.Address(),
Amount: big.NewInt(3e18),
},
},
receiver: state.Chains[tenv.FeedChainSel].Receiver.Address(),
data: []byte(""),
},
}

tx, err = dstToken.Mint(
e.Chains[tenv.FeedChainSel].DeployerKey,
e.Chains[tenv.FeedChainSel].DeployerKey.From,
new(big.Int).Mul(twoCoins, big.NewInt(10)),
)
require.NoError(t, err)
_, err = e.Chains[tenv.FeedChainSel].Confirm(tx)
require.NoError(t, err)
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.name, func(t *testing.T) {

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)
startBlocks := make(map[uint64]*uint64)
expectedSeqNum := make(map[uint64]uint64)

tokens := map[uint64][]router.ClientEVMTokenAmount{
tenv.HomeChainSel: {{
Token: srcToken.Address(),
Amount: twoCoins,
}},
tenv.FeedChainSel: {{
Token: dstToken.Address(),
Amount: twoCoins,
}},
}

for src := range e.Chains {
for dest, destChain := range e.Chains {
if src == dest {
continue
}
destChain := e.Chains[scenario.dstChain]
latesthdr, err := destChain.Client.HeaderByNumber(testcontext.Get(t), nil)
require.NoError(t, err)
block := latesthdr.Number.Uint64()
startBlocks[dest] = &block
startBlocks[scenario.dstChain] = &block

// Fetch initial balances and aggregate total amounts
initialBalances := make(map[common.Address]*big.Int)
totalAmountsTransferred := make(map[common.Address]*big.Int)

for _, tokenAmount := range scenario.tokenAmounts {
dstToken := getDestinationToken(scenario, tenv, srcToken1, dstToken1, srcToken2, dstToken2, tokenAmount.Token)
require.NotNil(t, dstToken, "Destination token not found")

// Fetch initial balance
if _, exists := initialBalances[dstToken.Address()]; !exists {
balance, err := dstToken.BalanceOf(nil, scenario.receiver)
require.NoError(t, err)
mateusz-sekara marked this conversation as resolved.
Show resolved Hide resolved
initialBalances[dstToken.Address()] = balance
}

// Initialize and sum up total amounts
if _, exists := totalAmountsTransferred[dstToken.Address()]; !exists {
totalAmountsTransferred[dstToken.Address()] = big.NewInt(0)
}
totalAmountsTransferred[dstToken.Address()].Add(totalAmountsTransferred[dstToken.Address()], tokenAmount.Amount)
}

var (
receiver = common.LeftPadBytes(state.Chains[dest].Receiver.Address().Bytes(), 32)
data = []byte("hello world")
feeToken = common.HexToAddress("0x0")
)
if src == tenv.HomeChainSel && dest == tenv.FeedChainSel {
msgSentEvent := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{
Receiver: receiver,
Data: data,
TokenAmounts: tokens[src],
FeeToken: feeToken,
ExtraArgs: nil,
})
expectedSeqNum[dest] = msgSentEvent.SequenceNumber
} else {
msgSentEvent := ccdeploy.TestSendRequest(t, e, state, src, dest, false, router.ClientEVM2AnyMessage{
Receiver: receiver,
Data: data,
TokenAmounts: nil,
FeeToken: feeToken,
ExtraArgs: nil,
})
expectedSeqNum[dest] = msgSentEvent.SequenceNumber
msg := router.ClientEVM2AnyMessage{
Receiver: common.LeftPadBytes(scenario.receiver.Bytes(), 32),
Data: scenario.data,
TokenAmounts: scenario.tokenAmounts,
FeeToken: common.HexToAddress("0x0"),
ExtraArgs: nil,
}
}

// Send the message
msgSentEvent := ccdeploy.TestSendRequest(t, e, state, scenario.srcChain, scenario.dstChain, false, msg)
expectedSeqNum[scenario.dstChain] = msgSentEvent.SequenceNumber

// Wait for commit and execution
ccdeploy.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks)
ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks)

// Fetch final balances and assert
for tokenAddress, totalAmount := range totalAmountsTransferred {
dstToken := getTokenByAddress(dstToken1, dstToken2, tokenAddress)
require.NotNil(t, dstToken, "Destination token not found for address %s", tokenAddress.Hex())

finalBalance, err := dstToken.BalanceOf(nil, scenario.receiver)
require.NoError(t, err)

initialBalance := initialBalances[dstToken.Address()]
expectedBalance := new(big.Int).Add(initialBalance, totalAmount)

require.Equal(t, expectedBalance, finalBalance, "Incorrect balance for token %s", dstToken.Address().Hex())
}
})
}
}

// Wait for all commit reports to land.
ccdeploy.ConfirmCommitForAllWithExpectedSeqNums(t, e, state, expectedSeqNum, startBlocks)
func getTokenByAddress(dstToken1, dstToken2 *burn_mint_erc677.BurnMintERC677, tokenAddress common.Address) *burn_mint_erc677.BurnMintERC677 {
if dstToken1.Address() == tokenAddress {
return dstToken1
} else if dstToken2.Address() == tokenAddress {
return dstToken2
}
return nil
}

// After commit is reported on all chains, token prices should be updated in FeeQuoter.
for dest := range e.Chains {
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)
// Helper function to determine the destination token
func getDestinationToken(scenario struct {
name string
srcChain uint64
dstChain uint64
tokenAmounts []router.ClientEVMTokenAmount
receiver common.Address
data []byte
}, tenv ccdeploy.DeployedEnv, srcToken1, dstToken1, srcToken2, dstToken2 *burn_mint_erc677.BurnMintERC677, tokenAddress common.Address) *burn_mint_erc677.BurnMintERC677 {
0xsuryansh marked this conversation as resolved.
Show resolved Hide resolved
if scenario.srcChain == tenv.HomeChainSel && scenario.dstChain == tenv.FeedChainSel {
if tokenAddress == srcToken1.Address() {
return dstToken1
} else if tokenAddress == srcToken2.Address() {
return dstToken2
}
} else if scenario.srcChain == tenv.FeedChainSel && scenario.dstChain == tenv.HomeChainSel {
if tokenAddress == dstToken1.Address() {
return srcToken1
} else if tokenAddress == dstToken2.Address() {
return srcToken2
}
}
return nil
}

// Wait for all exec reports to land
ccdeploy.ConfirmExecWithSeqNrForAll(t, e, state, expectedSeqNum, startBlocks)
func setupEnvironment(t *testing.T) (ccdeploy.DeployedEnv, ccdeploy.CCIPOnChainState) {
0xsuryansh marked this conversation as resolved.
Show resolved Hide resolved
lggr := logger.TestLogger(t)
tenv, _, _ := testsetups.NewLocalDevEnvironmentWithDefaultPrice(t, lggr)
state, err := ccdeploy.LoadOnchainState(tenv.Env)
require.NoError(t, err)
return tenv, state
}

func deployAndApproveTokens(t *testing.T, e ccdeploy.DeployedEnv, state ccdeploy.CCIPOnChainState, tokenName string) (*burn_mint_erc677.BurnMintERC677, *burn_mint_erc677.BurnMintERC677) {
srcToken, _, dstToken, _, err := ccdeploy.DeployTransferableToken(
logger.TestLogger(t),
e.Env.Chains,
e.HomeChainSel,
e.FeedChainSel,
state,
e.Env.ExistingAddresses,
tokenName,
)
require.NoError(t, err)

tenTokens := new(big.Int).Mul(big.NewInt(1e18), big.NewInt(10))
mintAndApprove(t, e, state, srcToken, e.HomeChainSel, tenTokens)
mintAndApprove(t, e, state, dstToken, e.FeedChainSel, tenTokens)

balance, err := dstToken.BalanceOf(nil, state.Chains[tenv.FeedChainSel].Receiver.Address())
return srcToken, dstToken
}

func mintAndApprove(t *testing.T, e ccdeploy.DeployedEnv, state ccdeploy.CCIPOnChainState, token *burn_mint_erc677.BurnMintERC677, chainSel uint64, amount *big.Int) {
mateusz-sekara marked this conversation as resolved.
Show resolved Hide resolved
tx, err := token.Mint(
e.Env.Chains[chainSel].DeployerKey,
e.Env.Chains[chainSel].DeployerKey.From,
amount,
)
require.NoError(t, err)
_, err = e.Env.Chains[chainSel].Confirm(tx)
require.NoError(t, err)

tx, err = token.Approve(
e.Env.Chains[chainSel].DeployerKey,
state.Chains[chainSel].Router.Address(),
amount,
)
require.NoError(t, err)
_, err = e.Env.Chains[chainSel].Confirm(tx)
require.NoError(t, err)
require.Equal(t, twoCoins, balance)
}
Loading