diff --git a/changelog.md b/changelog.md index 426406bf68..d00252a146 100644 --- a/changelog.md +++ b/changelog.md @@ -19,6 +19,10 @@ * [2615](https://github.com/zeta-chain/node/pull/2615) - Refactor cleanup of outbound trackers +### Tests + +* [2726](https://github.com/zeta-chain/node/pull/2726) - add e2e tests for deposit and call, deposit and revert + ### Fixes * [2654](https://github.com/zeta-chain/node/pull/2654) - add validation for authorization list in when validating genesis state for authorization module diff --git a/cmd/zetae2e/local/local.go b/cmd/zetae2e/local/local.go index 1806f37360..3d923b32a8 100644 --- a/cmd/zetae2e/local/local.go +++ b/cmd/zetae2e/local/local.go @@ -348,6 +348,8 @@ func localE2ETest(cmd *cobra.Command, _ []string) { solanaTests := []string{ e2etests.TestSolanaDepositName, e2etests.TestSolanaWithdrawName, + e2etests.TestSolanaDepositAndCallName, + e2etests.TestSolanaDepositAndCallRefundName, } eg.Go(solanaTestRoutine(conf, deployerRunner, verbose, solanaTests...)) } diff --git a/e2e/e2etests/e2etests.go b/e2e/e2etests/e2etests.go index fe053119db..b8f29399db 100644 --- a/e2e/e2etests/e2etests.go +++ b/e2e/e2etests/e2etests.go @@ -54,8 +54,10 @@ const ( /* Solana tests */ - TestSolanaDepositName = "solana_deposit" - TestSolanaWithdrawName = "solana_withdraw" + TestSolanaDepositName = "solana_deposit" + TestSolanaWithdrawName = "solana_withdraw" + TestSolanaDepositAndCallName = "solana_deposit_and_call" + TestSolanaDepositAndCallRefundName = "solana_deposit_and_call_refund" /* Bitcoin tests @@ -365,7 +367,7 @@ var AllE2ETests = []runner.E2ETest{ TestSolanaDepositName, "deposit SOL into ZEVM", []runner.ArgDefinition{ - {Description: "amount in lamport", DefaultValue: "13370000"}, + {Description: "amount in lamport", DefaultValue: "1200000"}, }, TestSolanaDeposit, ), @@ -373,10 +375,26 @@ var AllE2ETests = []runner.E2ETest{ TestSolanaWithdrawName, "withdraw SOL from ZEVM", []runner.ArgDefinition{ - {Description: "amount in lamport", DefaultValue: "1336000"}, + {Description: "amount in lamport", DefaultValue: "1000000"}, }, TestSolanaWithdraw, ), + runner.NewE2ETest( + TestSolanaDepositAndCallName, + "deposit SOL into ZEVM and call a contract", + []runner.ArgDefinition{ + {Description: "amount in lamport", DefaultValue: "1200000"}, + }, + TestSolanaDepositAndCall, + ), + runner.NewE2ETest( + TestSolanaDepositAndCallRefundName, + "deposit SOL into ZEVM and call a contract that reverts; should refund", + []runner.ArgDefinition{ + {Description: "amount in lamport", DefaultValue: "1200000"}, + }, + TestSolanaDepositAndCallRefund, + ), /* Bitcoin tests */ diff --git a/e2e/e2etests/helpers.go b/e2e/e2etests/helpers.go index 4dc37498d9..8146ff52b8 100644 --- a/e2e/e2etests/helpers.go +++ b/e2e/e2etests/helpers.go @@ -108,6 +108,13 @@ func parseInt(t require.TestingT, s string) int { return v } +func parseBigInt(t require.TestingT, s string) *big.Int { + v, ok := big.NewInt(0).SetString(s, 10) + require.True(t, ok, "unable to parse big.Int from %q", s) + + return v +} + // bigIntFromFloat64 takes float64 (e.g. 0.001) that represents btc amount // and converts it to big.Int for downstream usage. func btcAmountFromFloat64(t require.TestingT, amount float64) *big.Int { diff --git a/e2e/e2etests/test_context_upgrade.go b/e2e/e2etests/test_context_upgrade.go index 4a71b07428..82e81f2147 100644 --- a/e2e/e2etests/test_context_upgrade.go +++ b/e2e/e2etests/test_context_upgrade.go @@ -2,7 +2,6 @@ package e2etests import ( "bytes" - "math/big" "time" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -17,8 +16,7 @@ func TestContextUpgrade(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) // parse the value from the provided arguments - value, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid value specified for TestContextUpgrade.") + value := parseBigInt(r, args[0]) data := make([]byte, 0, 32) data = append(data, r.ContextAppAddr.Bytes()...) diff --git a/e2e/e2etests/test_donation.go b/e2e/e2etests/test_donation.go index 2347e97e23..d628cce62e 100644 --- a/e2e/e2etests/test_donation.go +++ b/e2e/e2etests/test_donation.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/e2e/runner" @@ -14,8 +12,8 @@ import ( func TestDonationEther(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestDonationEther.") + // parse the donation amount + amount := parseBigInt(r, args[0]) txDonation, err := r.SendEther(r.TSSAddress, amount, []byte(constant.DonationMessage)) require.NoError(r, err) diff --git a/e2e/e2etests/test_erc20_deposit.go b/e2e/e2etests/test_erc20_deposit.go index c7f1d4fc5f..66d640f0c4 100644 --- a/e2e/e2etests/test_erc20_deposit.go +++ b/e2e/e2etests/test_erc20_deposit.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/e2e/runner" @@ -12,8 +10,8 @@ import ( func TestERC20Deposit(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestERC20Deposit.") + // parse the deposit amount + amount := parseBigInt(r, args[0]) hash := r.DepositERC20WithAmountAndMessage(r.EVMAddress(), amount, []byte{}) diff --git a/e2e/e2etests/test_erc20_deposit_restricted_address.go b/e2e/e2etests/test_erc20_deposit_restricted_address.go index 758da5a969..ea5dd81252 100644 --- a/e2e/e2etests/test_erc20_deposit_restricted_address.go +++ b/e2e/e2etests/test_erc20_deposit_restricted_address.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -13,8 +11,8 @@ import ( func TestERC20DepositRestricted(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) + // parse the deposit amount + amount := parseBigInt(r, args[0]) // deposit ERC20 to restricted address r.DepositERC20WithAmountAndMessage(ethcommon.HexToAddress(testutils.RestrictedEVMAddressTest), amount, []byte{}) diff --git a/e2e/e2etests/test_erc20_multiple_deposits.go b/e2e/e2etests/test_erc20_multiple_deposits.go index 87b8309924..8948bc3fe7 100644 --- a/e2e/e2etests/test_erc20_multiple_deposits.go +++ b/e2e/e2etests/test_erc20_multiple_deposits.go @@ -15,11 +15,9 @@ import ( func TestMultipleERC20Deposit(r *runner.E2ERunner, args []string) { require.Len(r, args, 2) - depositAmount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) - - numberOfDeposits, ok := big.NewInt(0).SetString(args[1], 10) - require.True(r, ok) + // parse the deposit amount and count + depositAmount := parseBigInt(r, args[0]) + numberOfDeposits := parseBigInt(r, args[1]) require.NotZero(r, numberOfDeposits.Int64()) initialBal, err := r.ERC20ZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) diff --git a/e2e/e2etests/test_erc20_multiple_withdraws.go b/e2e/e2etests/test_erc20_multiple_withdraws.go index 53a45d1b41..80bdcac46e 100644 --- a/e2e/e2etests/test_erc20_multiple_withdraws.go +++ b/e2e/e2etests/test_erc20_multiple_withdraws.go @@ -16,17 +16,15 @@ func TestMultipleERC20Withdraws(r *runner.E2ERunner, args []string) { approvedAmount := big.NewInt(1e18) - withdrawalAmount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) + // parse the withdrawal amount and number of withdrawals + withdrawalAmount := parseBigInt(r, args[0]) require.Equal( r, -1, withdrawalAmount.Cmp(approvedAmount), "Invalid withdrawal amount specified for TestMultipleWithdraws.", ) - - numberOfWithdrawals, ok := big.NewInt(0).SetString(args[1], 10) - require.True(r, ok) + numberOfWithdrawals := parseBigInt(r, args[1]) require.NotEmpty(r, numberOfWithdrawals.Int64()) // calculate total withdrawal to ensure it doesn't exceed approved amount. diff --git a/e2e/e2etests/test_eth_deposit.go b/e2e/e2etests/test_eth_deposit.go index c5f0701516..419dbe2298 100644 --- a/e2e/e2etests/test_eth_deposit.go +++ b/e2e/e2etests/test_eth_deposit.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/e2e/runner" @@ -13,8 +11,8 @@ import ( func TestEtherDeposit(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestEtherDeposit.") + // parse the deposit amount + amount := parseBigInt(r, args[0]) hash := r.DepositEtherWithAmount(amount) // in wei // wait for the cctx to be mined diff --git a/e2e/e2etests/test_eth_deposit_call.go b/e2e/e2etests/test_eth_deposit_call.go index 7fd928266d..9d024e146a 100644 --- a/e2e/e2etests/test_eth_deposit_call.go +++ b/e2e/e2etests/test_eth_deposit_call.go @@ -1,9 +1,6 @@ package e2etests import ( - "math/big" - - "github.com/ethereum/go-ethereum/accounts/abi/bind" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" @@ -17,8 +14,8 @@ import ( func TestEtherDepositAndCall(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - value, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestEtherDepositAndCall.") + // parse deposit amount + value := parseBigInt(r, args[0]) r.Logger.Info("Deploying example contract") exampleAddr, _, exampleContract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient) @@ -57,16 +54,7 @@ func TestEtherDepositAndCall(r *runner.E2ERunner, args []string) { utils.RequireCCTXStatus(r, cctx, types.CctxStatus_OutboundMined) // Checking example contract has been called, bar value should be set to amount - bar, err := exampleContract.Bar(&bind.CallOpts{}) - require.NoError(r, err) - require.Equal( - r, - 0, - bar.Cmp(value), - "cross-chain call failed bar value %s should be equal to amount %s", - bar.String(), - value.String(), - ) + utils.MustHaveCalledExampleContract(r, exampleContract, value) r.Logger.Info("Cross-chain call succeeded") r.Logger.Info("Deploying reverter contract") @@ -100,6 +88,5 @@ func TestEtherDepositAndCall(r *runner.E2ERunner, args []string) { r.Logger.Info("Cross-chain call to reverter reverted") // check the status message contains revert error hash in case of revert - // 0xbfb4ebcf is the hash of "Foo()" - require.Contains(r, cctx.CctxStatus.StatusMessage, "0xbfb4ebcf") + require.Contains(r, cctx.CctxStatus.StatusMessage, utils.ErrHashRevertFoo) } diff --git a/e2e/e2etests/test_eth_deposit_refund.go b/e2e/e2etests/test_eth_deposit_refund.go index 946c457927..343452cdf1 100644 --- a/e2e/e2etests/test_eth_deposit_refund.go +++ b/e2e/e2etests/test_eth_deposit_refund.go @@ -15,8 +15,8 @@ import ( func TestEtherDepositAndCallRefund(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - value, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestEtherDepositAndCallRefund.") + // parse the deposit amount + value := parseBigInt(r, args[0]) evmClient := r.EVMClient diff --git a/e2e/e2etests/test_message_passing_evm_to_zevm.go b/e2e/e2etests/test_message_passing_evm_to_zevm.go index 4a7ac7f906..6dfb1b53c2 100644 --- a/e2e/e2etests/test_message_passing_evm_to_zevm.go +++ b/e2e/e2etests/test_message_passing_evm_to_zevm.go @@ -17,8 +17,8 @@ import ( func TestMessagePassingEVMtoZEVM(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestMessagePassingEVMtoZEVM.") + // parse the amount + amount := parseBigInt(r, args[0]) // Set destination details zEVMChainID, err := r.ZEVMClient.ChainID(r.Ctx) diff --git a/e2e/e2etests/test_message_passing_evm_to_zevm_revert.go b/e2e/e2etests/test_message_passing_evm_to_zevm_revert.go index 85aca0caae..8d5a1e9469 100644 --- a/e2e/e2etests/test_message_passing_evm_to_zevm_revert.go +++ b/e2e/e2etests/test_message_passing_evm_to_zevm_revert.go @@ -22,8 +22,8 @@ func TestMessagePassingEVMtoZEVMRevert(r *runner.E2ERunner, args []string) { fungibleEthAddress := ethcommon.HexToAddress(fungibleModuleAddress) require.True(r, fungibleEthAddress != ethcommon.Address{}, "invalid fungible module address") - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) + // parse the amount + amount := parseBigInt(r, args[0]) // Set destination details zEVMChainID, err := r.ZEVMClient.ChainID(r.Ctx) diff --git a/e2e/e2etests/test_message_passing_evm_to_zevm_revert_fail.go b/e2e/e2etests/test_message_passing_evm_to_zevm_revert_fail.go index a767f619ad..271c33e9c8 100644 --- a/e2e/e2etests/test_message_passing_evm_to_zevm_revert_fail.go +++ b/e2e/e2etests/test_message_passing_evm_to_zevm_revert_fail.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" @@ -15,8 +13,8 @@ import ( func TestMessagePassingEVMtoZEVMRevertFail(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestMessagePassingEVMtoZEVMRevertFail.") + // parse the amount + amount := parseBigInt(r, args[0]) // Deploying a test contract not containing a logic for reverting the cctx testDappNoRevertEVMAddr, tx, testDappNoRevertEVM, err := testdappnorevert.DeployTestDAppNoRevert( diff --git a/e2e/e2etests/test_message_passing_external_chains.go b/e2e/e2etests/test_message_passing_external_chains.go index a533256794..96cdc2fe99 100644 --- a/e2e/e2etests/test_message_passing_external_chains.go +++ b/e2e/e2etests/test_message_passing_external_chains.go @@ -18,8 +18,8 @@ import ( func TestMessagePassingExternalChains(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestMessagePassing.") + // parse the amount + amount := parseBigInt(r, args[0]) chainID, err := r.EVMClient.ChainID(r.Ctx) require.NoError(r, err) diff --git a/e2e/e2etests/test_message_passing_external_chains_revert.go b/e2e/e2etests/test_message_passing_external_chains_revert.go index 37146a4c78..0f2f5c1c7b 100644 --- a/e2e/e2etests/test_message_passing_external_chains_revert.go +++ b/e2e/e2etests/test_message_passing_external_chains_revert.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -19,8 +17,8 @@ import ( func TestMessagePassingRevertSuccessExternalChains(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) + // parse the amount + amount := parseBigInt(r, args[0]) chainID, err := r.EVMClient.ChainID(r.Ctx) require.NoError(r, err) diff --git a/e2e/e2etests/test_message_passing_external_chains_revert_fail.go b/e2e/e2etests/test_message_passing_external_chains_revert_fail.go index 12e4589a3d..8504aaca56 100644 --- a/e2e/e2etests/test_message_passing_external_chains_revert_fail.go +++ b/e2e/e2etests/test_message_passing_external_chains_revert_fail.go @@ -19,8 +19,8 @@ import ( func TestMessagePassingRevertFailExternalChains(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestMessagePassingRevertFail.") + // parse the amount + amount := parseBigInt(r, args[0]) chainID, err := r.EVMClient.ChainID(r.Ctx) require.NoError(r, err) diff --git a/e2e/e2etests/test_message_passing_zevm_to_evm.go b/e2e/e2etests/test_message_passing_zevm_to_evm.go index dde161f2f9..4e791b57c7 100644 --- a/e2e/e2etests/test_message_passing_zevm_to_evm.go +++ b/e2e/e2etests/test_message_passing_zevm_to_evm.go @@ -16,8 +16,8 @@ import ( func TestMessagePassingZEVMtoEVM(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestMessagePassingZEVMtoEVM.") + // parse the amount + amount := parseBigInt(r, args[0]) // Set destination details EVMChainID, err := r.EVMClient.ChainID(r.Ctx) diff --git a/e2e/e2etests/test_message_passing_zevm_to_evm_revert.go b/e2e/e2etests/test_message_passing_zevm_to_evm_revert.go index 85f81f0a04..e837c1c05c 100644 --- a/e2e/e2etests/test_message_passing_zevm_to_evm_revert.go +++ b/e2e/e2etests/test_message_passing_zevm_to_evm_revert.go @@ -16,8 +16,8 @@ import ( func TestMessagePassingZEVMtoEVMRevert(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestMessagePassingZEVMtoEVMRevert.") + // parse the amount + amount := parseBigInt(r, args[0]) // Set destination details EVMChainID, err := r.EVMClient.ChainID(r.Ctx) diff --git a/e2e/e2etests/test_message_passing_zevm_to_evm_revert_fail.go b/e2e/e2etests/test_message_passing_zevm_to_evm_revert_fail.go index cc22db23a3..506bb562cd 100644 --- a/e2e/e2etests/test_message_passing_zevm_to_evm_revert_fail.go +++ b/e2e/e2etests/test_message_passing_zevm_to_evm_revert_fail.go @@ -15,8 +15,8 @@ import ( func TestMessagePassingZEVMtoEVMRevertFail(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestMessagePassingZEVMtoEVMRevertFail.") + // parse the amount + amount := parseBigInt(r, args[0]) // Deploying a test contract not containing a logic for reverting the cctx testDappNoRevertAddr, tx, testDappNoRevert, err := testdappnorevert.DeployTestDAppNoRevert( diff --git a/e2e/e2etests/test_solana_deposit.go b/e2e/e2etests/test_solana_deposit.go index 486ae89782..b1c7622c8c 100644 --- a/e2e/e2etests/test_solana_deposit.go +++ b/e2e/e2etests/test_solana_deposit.go @@ -3,7 +3,7 @@ package e2etests import ( "math/big" - "github.com/gagliardetto/solana-go" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/e2e/runner" @@ -14,25 +14,28 @@ import ( func TestSolanaDeposit(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - // parse deposit amount (in lamports) - // #nosec G115 e2e - always in range - depositAmount := big.NewInt(int64(parseInt(r, args[0]))) - - // load deployer private key - privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) + // get ERC20 SOL balance before deposit + balanceBefore, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) require.NoError(r, err) + r.Logger.Info("runner balance of SOL before deposit: %d", balanceBefore) - // create 'deposit' instruction - instruction := r.CreateDepositInstruction(privkey.PublicKey(), r.EVMAddress(), depositAmount.Uint64()) - - // create and sign the transaction - signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, privkey) + // parse deposit amount (in lamports) + depositAmount := parseBigInt(r, args[0]) - // broadcast the transaction and wait for finalization - sig, out := r.BroadcastTxSync(signedTx) - r.Logger.Info("deposit logs: %v", out.Meta.LogMessages) + // execute the deposit transaction + sig := r.SOLDepositAndCall(nil, r.EVMAddress(), depositAmount, nil) // wait for the cctx to be mined cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "solana_deposit") utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + + // get ERC20 SOL balance after deposit + balanceAfter, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) + require.NoError(r, err) + r.Logger.Info("runner balance of SOL after deposit: %d", balanceAfter) + + // the runner balance should be increased by the deposit amount + amountIncreased := new(big.Int).Sub(balanceAfter, balanceBefore) + require.Equal(r, depositAmount.String(), amountIncreased.String()) } diff --git a/e2e/e2etests/test_solana_deposit_call.go b/e2e/e2etests/test_solana_deposit_call.go new file mode 100644 index 0000000000..edb0239af9 --- /dev/null +++ b/e2e/e2etests/test_solana_deposit_call.go @@ -0,0 +1,35 @@ +package e2etests + +import ( + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + testcontract "github.com/zeta-chain/zetacore/testutil/contracts" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// TestSolanaDepositAndCall tests deposit of lamports calling a example contract +func TestSolanaDepositAndCall(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + // parse deposit amount (in lamports) + depositAmount := parseBigInt(r, args[0]) + + // deploy an example contract in ZEVM + contractAddr, _, contract, err := testcontract.DeployExample(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + r.Logger.Info("Example contract deployed at: %s", contractAddr.String()) + + // execute the deposit transaction + data := []byte("hello lamports") + sig := r.SOLDepositAndCall(nil, contractAddr, depositAmount, data) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "solana_deposit_and_call") + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_OutboundMined) + + // check if example contract has been called, bar value should be set to amount + utils.MustHaveCalledExampleContract(r, contract, depositAmount) +} diff --git a/e2e/e2etests/test_solana_deposit_refund.go b/e2e/e2etests/test_solana_deposit_refund.go new file mode 100644 index 0000000000..07fe6c64e2 --- /dev/null +++ b/e2e/e2etests/test_solana_deposit_refund.go @@ -0,0 +1,36 @@ +package e2etests + +import ( + "github.com/stretchr/testify/require" + + "github.com/zeta-chain/zetacore/e2e/runner" + "github.com/zeta-chain/zetacore/e2e/utils" + testcontract "github.com/zeta-chain/zetacore/testutil/contracts" + crosschaintypes "github.com/zeta-chain/zetacore/x/crosschain/types" +) + +// TestSolanaDepositAndCallRefund tests deposit of lamports calling a example contract +func TestSolanaDepositAndCallRefund(r *runner.E2ERunner, args []string) { + require.Len(r, args, 1) + + // parse deposit amount (in lamports) + depositAmount := parseBigInt(r, args[0]) + + // deploy a reverter contract in ZEVM + // TODO: consider removing repeated deployments of reverter contract + reverterAddr, _, _, err := testcontract.DeployReverter(r.ZEVMAuth, r.ZEVMClient) + require.NoError(r, err) + r.Logger.Info("Reverter contract deployed at: %s", reverterAddr.String()) + + // execute the deposit transaction + data := []byte("hello reverter") + sig := r.SOLDepositAndCall(nil, reverterAddr, depositAmount, data) + + // wait for the cctx to be mined + cctx := utils.WaitCctxMinedByInboundHash(r.Ctx, sig.String(), r.CctxClient, r.Logger, r.CctxTimeout) + r.Logger.CCTX(*cctx, "solana_deposit_and_refund") + utils.RequireCCTXStatus(r, cctx, crosschaintypes.CctxStatus_Reverted) + + // check the status message contains revert error hash in case of revert + require.Contains(r, cctx.CctxStatus.StatusMessage, utils.ErrHashRevertFoo) +} diff --git a/e2e/e2etests/test_solana_withdraw.go b/e2e/e2etests/test_solana_withdraw.go index 776944c9b3..6ef0354a06 100644 --- a/e2e/e2etests/test_solana_withdraw.go +++ b/e2e/e2etests/test_solana_withdraw.go @@ -13,15 +13,14 @@ import ( func TestSolanaWithdraw(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - // print balanceAfter of from address - balanceBefore, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.ZEVMAuth.From) + // get ERC20 SOL balance before withdraw + balanceBefore, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) require.NoError(r, err) - r.Logger.Info("from address %s balance of SOL before: %d", r.ZEVMAuth.From, balanceBefore) + r.Logger.Info("runner balance of SOL before withdraw: %d", balanceBefore) // parse withdraw amount (in lamports), approve amount is 1 SOL approvedAmount := new(big.Int).SetUint64(solana.LAMPORTS_PER_SOL) - // #nosec G115 e2e - always in range - withdrawAmount := big.NewInt(int64(parseInt(r, args[0]))) + withdrawAmount := parseBigInt(r, args[0]) require.Equal( r, -1, @@ -36,10 +35,10 @@ func TestSolanaWithdraw(r *runner.E2ERunner, args []string) { // withdraw r.WithdrawSOLZRC20(privkey.PublicKey(), withdrawAmount, approvedAmount) - // print balance of from address after withdraw - balanceAfter, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.ZEVMAuth.From) + // get ERC20 SOL balance after withdraw + balanceAfter, err := r.SOLZRC20.BalanceOf(&bind.CallOpts{}, r.EVMAddress()) require.NoError(r, err) - r.Logger.Info("from address %s balance of SOL after: %d", r.ZEVMAuth.From, balanceAfter) + r.Logger.Info("runner balance of SOL after withdraw: %d", balanceAfter) // check if the balance is reduced correctly amountReduced := new(big.Int).Sub(balanceBefore, balanceAfter) diff --git a/e2e/e2etests/test_stress_eth_deposit.go b/e2e/e2etests/test_stress_eth_deposit.go index 04ef846889..dc5c51c7e4 100644 --- a/e2e/e2etests/test_stress_eth_deposit.go +++ b/e2e/e2etests/test_stress_eth_deposit.go @@ -2,7 +2,6 @@ package e2etests import ( "fmt" - "math/big" "time" ethcommon "github.com/ethereum/go-ethereum/common" @@ -18,9 +17,8 @@ import ( func TestStressEtherDeposit(r *runner.E2ERunner, args []string) { require.Len(r, args, 2) - depositAmount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) - + // parse deposit amount and number of deposits + depositAmount := parseBigInt(r, args[0]) numDeposits := parseInt(r, args[1]) r.Logger.Print("starting stress test of %d deposits", numDeposits) diff --git a/e2e/e2etests/test_stress_eth_withdraw.go b/e2e/e2etests/test_stress_eth_withdraw.go index 2abd036a25..39ff484054 100644 --- a/e2e/e2etests/test_stress_eth_withdraw.go +++ b/e2e/e2etests/test_stress_eth_withdraw.go @@ -19,8 +19,8 @@ import ( func TestStressEtherWithdraw(r *runner.E2ERunner, args []string) { require.Len(r, args, 2) - withdrawalAmount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid withdrawal amount specified for TestStressEtherWithdraw.") + // parse withdraw amount and number of withdraws + withdrawalAmount := parseBigInt(r, args[0]) numWithdraws, err := strconv.Atoi(args[1]) require.NoError(r, err) diff --git a/e2e/e2etests/test_zeta_deposit.go b/e2e/e2etests/test_zeta_deposit.go index eb8021876d..00b672ee61 100644 --- a/e2e/e2etests/test_zeta_deposit.go +++ b/e2e/e2etests/test_zeta_deposit.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/e2e/runner" @@ -12,8 +10,8 @@ import ( func TestZetaDeposit(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestZetaDeposit.") + // parse deposit amount + amount := parseBigInt(r, args[0]) hash := r.DepositZetaWithAmount(r.EVMAddress(), amount) diff --git a/e2e/e2etests/test_zeta_deposit_new_address.go b/e2e/e2etests/test_zeta_deposit_new_address.go index 047fbd7042..79a1360db9 100644 --- a/e2e/e2etests/test_zeta_deposit_new_address.go +++ b/e2e/e2etests/test_zeta_deposit_new_address.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/e2e/runner" @@ -13,8 +11,8 @@ import ( func TestZetaDepositNewAddress(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) + // parse deposit amount + amount := parseBigInt(r, args[0]) newAddress := sample.EthAddress() hash := r.DepositZetaWithAmount(newAddress, amount) diff --git a/e2e/e2etests/test_zeta_deposit_restricted_address.go b/e2e/e2etests/test_zeta_deposit_restricted_address.go index d525a97d79..d760ccafbd 100644 --- a/e2e/e2etests/test_zeta_deposit_restricted_address.go +++ b/e2e/e2etests/test_zeta_deposit_restricted_address.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - ethcommon "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" @@ -13,8 +11,8 @@ import ( func TestZetaDepositRestricted(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok, "Invalid amount specified for TestZetaDepositRestricted.") + // parse the deposit amount + amount := parseBigInt(r, args[0]) // Deposit amount to restricted address r.DepositZetaWithAmount(ethcommon.HexToAddress(testutils.RestrictedEVMAddressTest), amount) diff --git a/e2e/e2etests/test_zeta_withdraw.go b/e2e/e2etests/test_zeta_withdraw.go index 25b9d3d34a..7695c28bab 100644 --- a/e2e/e2etests/test_zeta_withdraw.go +++ b/e2e/e2etests/test_zeta_withdraw.go @@ -1,8 +1,6 @@ package e2etests import ( - "math/big" - "github.com/stretchr/testify/require" "github.com/zeta-chain/zetacore/e2e/runner" @@ -13,8 +11,8 @@ import ( func TestZetaWithdraw(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) + // parse withdraw amount + amount := parseBigInt(r, args[0]) r.DepositAndApproveWZeta(amount) tx := r.WithdrawZeta(amount, true) diff --git a/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go b/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go index 6f9586de45..ac8e1f2448 100644 --- a/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go +++ b/e2e/e2etests/test_zeta_withdraw_bitcoin_revert.go @@ -14,8 +14,8 @@ import ( func TestZetaWithdrawBTCRevert(r *runner.E2ERunner, args []string) { require.Len(r, args, 1) - amount, ok := big.NewInt(0).SetString(args[0], 10) - require.True(r, ok) + // parse withdraw amount + amount := parseBigInt(r, args[0]) r.ZEVMAuth.Value = amount tx, err := r.WZeta.Deposit(r.ZEVMAuth) diff --git a/e2e/runner/solana.go b/e2e/runner/solana.go index 71d2cfb629..ba6f240279 100644 --- a/e2e/runner/solana.go +++ b/e2e/runner/solana.go @@ -31,6 +31,7 @@ func (r *E2ERunner) ComputePdaAddress() solana.PublicKey { func (r *E2ERunner) CreateDepositInstruction( signer solana.PublicKey, receiver ethcommon.Address, + data []byte, amount uint64, ) solana.Instruction { // compute the gateway PDA address @@ -51,7 +52,7 @@ func (r *E2ERunner) CreateDepositInstruction( inst.DataBytes, err = borsh.Serialize(solanacontract.DepositInstructionParams{ Discriminator: solanacontract.DiscriminatorDeposit(), Amount: amount, - Memo: receiver.Bytes(), + Memo: append(receiver.Bytes(), data...), }) require.NoError(r, err) @@ -96,9 +97,16 @@ func (r *E2ERunner) BroadcastTxSync(tx *solana.Transaction) (solana.Signature, * require.NoError(r, err) r.Logger.Info("broadcast success! tx sig %s; waiting for confirmation...", sig) + var ( + start = time.Now() + timeout = 2 * time.Minute // Solana tx expires automatically after 2 minutes + ) + // wait for the transaction to be finalized var out *rpc.GetTransactionResult for { + require.False(r, time.Since(start) > timeout, "waiting solana tx timeout") + time.Sleep(1 * time.Second) out, err = r.SolanaClient.GetTransaction(r.Ctx, sig, &rpc.GetTransactionOpts{}) if err == nil { @@ -109,6 +117,33 @@ func (r *E2ERunner) BroadcastTxSync(tx *solana.Transaction) (solana.Signature, * return sig, out } +// SOLDepositAndCall deposits an amount of ZRC20 SOL tokens (in lamports) and calls a contract (if data is provided) +func (r *E2ERunner) SOLDepositAndCall( + signerPrivKey *solana.PrivateKey, + receiver ethcommon.Address, + amount *big.Int, + data []byte, +) solana.Signature { + // if signer is not provided, use the runner account as default + if signerPrivKey == nil { + privkey, err := solana.PrivateKeyFromBase58(r.Account.SolanaPrivateKey.String()) + require.NoError(r, err) + signerPrivKey = &privkey + } + + // create 'deposit' instruction + instruction := r.CreateDepositInstruction(signerPrivKey.PublicKey(), receiver, data, amount.Uint64()) + + // create and sign the transaction + signedTx := r.CreateSignedTransaction([]solana.Instruction{instruction}, *signerPrivKey) + + // broadcast the transaction and wait for finalization + sig, out := r.BroadcastTxSync(signedTx) + r.Logger.Info("deposit logs: %v", out.Meta.LogMessages) + + return sig +} + // WithdrawSOLZRC20 withdraws an amount of ZRC20 SOL tokens func (r *E2ERunner) WithdrawSOLZRC20(to solana.PublicKey, amount *big.Int, approveAmount *big.Int) { // approve diff --git a/e2e/utils/contracts.go b/e2e/utils/contracts.go new file mode 100644 index 0000000000..605e458a22 --- /dev/null +++ b/e2e/utils/contracts.go @@ -0,0 +1,33 @@ +package utils + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/stretchr/testify/require" + + testcontract "github.com/zeta-chain/zetacore/testutil/contracts" +) + +const ( + // ErrHashRevertFoo is the keccak256 hash of custom error "Foo()" on reverter contract + ErrHashRevertFoo = "0xbfb4ebcf" +) + +// MustHaveCalledExampleContract checks if the contract has been called correctly +func MustHaveCalledExampleContract( + t require.TestingT, + contract *testcontract.Example, + amount *big.Int, +) { + bar, err := contract.Bar(&bind.CallOpts{}) + require.NoError(t, err) + require.Equal( + t, + 0, + bar.Cmp(amount), + "cross-chain call failed bar value %s should be equal to amount %s", + bar.String(), + amount.String(), + ) +} diff --git a/x/fungible/keeper/evm_test.go b/x/fungible/keeper/evm_test.go index 787d08ad87..cd45aa8cc1 100644 --- a/x/fungible/keeper/evm_test.go +++ b/x/fungible/keeper/evm_test.go @@ -2,8 +2,7 @@ package keeper_test import ( "encoding/json" - "github.com/zeta-chain/protocol-contracts/v2/pkg/erc1967proxy.sol" - "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" + "fmt" "math/big" "testing" @@ -16,8 +15,11 @@ import ( "github.com/stretchr/testify/require" "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/systemcontract.sol" "github.com/zeta-chain/protocol-contracts/v1/pkg/contracts/zevm/wzeta.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/erc1967proxy.sol" + "github.com/zeta-chain/protocol-contracts/v2/pkg/gatewayzevm.sol" "github.com/zeta-chain/protocol-contracts/v2/pkg/zrc20.sol" + "github.com/zeta-chain/zetacore/e2e/utils" "github.com/zeta-chain/zetacore/pkg/chains" "github.com/zeta-chain/zetacore/pkg/coin" "github.com/zeta-chain/zetacore/server/config" @@ -678,8 +680,7 @@ func TestKeeper_CallEVMWithData(t *testing.T) { require.True(t, types.IsContractReverted(res, err)) // check reason is included for revert error - // 0xbfb4ebcf is the hash of "Foo()" - require.ErrorContains(t, err, "reason: 0xbfb4ebcf") + require.ErrorContains(t, err, fmt.Sprintf("reason: %s", utils.ErrHashRevertFoo)) res, err = k.CallEVM( ctx,