diff --git a/tests/e2e/.env.live-network-example b/tests/e2e/.env.live-network-example index 446488bb..69a7adbf 100644 --- a/tests/e2e/.env.live-network-example +++ b/tests/e2e/.env.live-network-example @@ -8,7 +8,7 @@ E2E_RUN_KVTOOL_NETWORKS=false # Configure the endpoints for connecting to the running network here. E2E_KAVA_RPC_URL='http://localhost:26657' E2E_KAVA_GRPC_URL='http://localhost:9090' -E2E_KAVA_EMV_RPC_URL='http://localhost:8545' +E2E_KAVA_EVM_RPC_URL='http://localhost:8545' # E2E_INCLUDE_IBC_TESTS is not currently supported for running tests against a live network. E2E_INCLUDE_IBC_TESTS=false diff --git a/tests/e2e/e2e_convert_cosmos_coins_test.go b/tests/e2e/e2e_convert_cosmos_coins_test.go index 7ea21520..fc88bc14 100644 --- a/tests/e2e/e2e_convert_cosmos_coins_test.go +++ b/tests/e2e/e2e_convert_cosmos_coins_test.go @@ -2,6 +2,7 @@ package e2e_test import ( "context" + "fmt" "math/big" "time" @@ -16,6 +17,8 @@ import ( evmutiltypes "github.com/kava-labs/kava/x/evmutil/types" ) +const initialCosmosCoinConversionDenomFunds = int64(1e4) + func setupConvertToCoinTest( suite *IntegrationTestSuite, accountName string, ) (denom string, initialFunds sdk.Coins, user *testutil.SigningAccount) { @@ -31,8 +34,8 @@ func setupConvertToCoinTest( tokenInfo := params.Params.AllowedCosmosDenoms[0] denom = tokenInfo.CosmosDenom initialFunds = sdk.NewCoins( - sdk.NewInt64Coin(suite.Kava.StakingDenom, 1e6), // gas money - sdk.NewInt64Coin(denom, 1e6), // conversion-enabled cosmos coin + sdk.NewInt64Coin(suite.Kava.StakingDenom, 1e5), // gas money + sdk.NewInt64Coin(denom, initialCosmosCoinConversionDenomFunds), // conversion-enabled cosmos coin ) user = suite.Kava.NewFundedAccount(accountName, initialFunds) @@ -40,19 +43,17 @@ func setupConvertToCoinTest( return denom, initialFunds, user } -// amount must be less than initial funds (1e6) +// amount must be less than initial funds (initialCosmosCoinConversionDenomFunds) func (suite *IntegrationTestSuite) setupAccountWithCosmosCoinERC20Balance( accountName string, amount int64, ) (user *testutil.SigningAccount, contractAddress *evmutiltypes.InternalEVMAddress, denom string, sdkBalance sdk.Coins) { - if amount > 1e6 { - panic("test erc20 amount must be less than 1e6") + if amount > initialCosmosCoinConversionDenomFunds { + panic(fmt.Sprintf("test erc20 amount must be less than %d", initialCosmosCoinConversionDenomFunds)) } denom, sdkBalance, user = setupConvertToCoinTest(suite, accountName) convertAmount := sdk.NewInt64Coin(denom, amount) - fee := sdk.NewCoins(ukava(7500)) - // setup user to have erc20 balance msg := evmutiltypes.NewMsgConvertCosmosCoinToERC20( user.SdkAddress.String(), @@ -61,8 +62,8 @@ func (suite *IntegrationTestSuite) setupAccountWithCosmosCoinERC20Balance( ) tx := util.KavaMsgRequest{ Msgs: []sdk.Msg{&msg}, - GasLimit: 2e6, - FeeAmount: fee, + GasLimit: 4e5, + FeeAmount: sdk.NewCoins(ukava(400)), Data: "converting sdk coin to erc20", } res := user.SignAndBroadcastKavaTx(tx) @@ -87,8 +88,7 @@ func (suite *IntegrationTestSuite) setupAccountWithCosmosCoinERC20Balance( func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() { denom, initialFunds, user := setupConvertToCoinTest(suite, "cosmo-coin-converter") - fee := sdk.NewCoins(ukava(7500)) - convertAmount := int64(5e5) + convertAmount := int64(5e3) initialModuleBalance := suite.Kava.GetModuleBalances(evmutiltypes.ModuleName).AmountOf(denom) /////////////////////////////// @@ -102,7 +102,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() { tx := util.KavaMsgRequest{ Msgs: []sdk.Msg{&convertToErc20Msg}, GasLimit: 2e6, - FeeAmount: fee, + FeeAmount: sdk.NewCoins(ukava(2000)), Data: "converting sdk coin to erc20", } res := user.SignAndBroadcastKavaTx(tx) @@ -144,7 +144,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() { tx = util.KavaMsgRequest{ Msgs: []sdk.Msg{&convertFromErc20Msg}, GasLimit: 2e5, - FeeAmount: fee, + FeeAmount: sdk.NewCoins(ukava(200)), Data: "converting erc20 to cosmos coin", } res = user.SignAndBroadcastKavaTx(tx) @@ -168,7 +168,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() { func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() { denom, initialFunds, user := setupConvertToCoinTest(suite, "cosmo-coin-converter-eip712") - convertAmount := int64(5e5) + convertAmount := int64(5e3) initialModuleBalance := suite.Kava.GetModuleBalances(evmutiltypes.ModuleName).AmountOf(denom) /////////////////////////////// @@ -237,7 +237,7 @@ func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() { user, suite.Kava, 2e5, - sdk.NewCoins(ukava(1e4)), + sdk.NewCoins(ukava(200)), []sdk.Msg{&convertFromErc20Msg}, "", ).GetTx() @@ -270,7 +270,9 @@ func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() { } func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls() { - user, contractAddress, _, _ := suite.setupAccountWithCosmosCoinERC20Balance("cosmo-coin-converter-unhappy", 1e6) + // give them erc20 balance so we know that's not preventing these method calls + // this test sacrifices the 1 coin by never returning it to the sdk. + user, contractAddress, _, _ := suite.setupAccountWithCosmosCoinERC20Balance("cosmo-coin-converter-unhappy", 1) suite.Run("users can't mint()", func() { data := util.BuildErc20MintCallData(user.EvmAddress, big.NewInt(1)) @@ -282,7 +284,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls() ðtypes.LegacyTx{ Nonce: nonce, GasPrice: minEvmGasPrice, - Gas: 1e6, + Gas: 1e5, To: &contractAddress.Address, Data: data, }, @@ -306,7 +308,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls() ðtypes.LegacyTx{ Nonce: nonce, GasPrice: minEvmGasPrice, - Gas: 1e6, + Gas: 1e5, To: &contractAddress.Address, Data: data, }, @@ -324,15 +326,14 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls() // - check approval flow of erc20. alice approves bob to move their funds // - check complex conversion flow. bob converts funds they receive on evm back to sdk.Coin func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() { - fee := sdk.NewCoins(ukava(7500)) - initialAliceAmount := int64(2e5) + initialAliceAmount := int64(2e3) alice, contractAddress, denom, _ := suite.setupAccountWithCosmosCoinERC20Balance( "cosmo-coin-converter-complex-alice", initialAliceAmount, ) - gasMoney := sdk.NewCoins(ukava(1e6)) + gasMoney := sdk.NewCoins(ukava(1e5)) bob := suite.Kava.NewFundedAccount("cosmo-coin-converter-complex-bob", gasMoney) - amount := big.NewInt(1e5) + amount := big.NewInt(1e3) // test assumes this is half of alice's balance. // bob can't move alice's funds nonce, err := bob.NextNonce() @@ -340,7 +341,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() { transferFromTxData := ðtypes.LegacyTx{ Nonce: nonce, GasPrice: minEvmGasPrice, - Gas: 1e6, + Gas: 1e5, To: &contractAddress.Address, Value: &big.Int{}, Data: util.BuildErc20TransferFromCallData(alice.EvmAddress, bob.EvmAddress, amount), @@ -360,7 +361,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() { Tx: ethtypes.NewTx(ðtypes.LegacyTx{ Nonce: nonce, GasPrice: minEvmGasPrice, - Gas: 1e6, + Gas: 1e5, To: &contractAddress.Address, Value: &big.Int{}, Data: util.BuildErc20ApproveCallData(bob.EvmAddress, amount), @@ -410,8 +411,8 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() { ) convertTx := util.KavaMsgRequest{ Msgs: []sdk.Msg{&convertMsg}, - GasLimit: 2e6, - FeeAmount: fee, + GasLimit: 2e5, + FeeAmount: sdk.NewCoins(ukava(200)), Data: "bob converts his new erc20 to an sdk.Coin", } convertRes := bob.SignAndBroadcastKavaTx(convertTx) @@ -423,4 +424,17 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() { // bob should have sdk balance balance := suite.Kava.QuerySdkForBalances(bob.SdkAddress).AmountOf(denom) suite.Equal(sdk.NewIntFromBigInt(amount), balance) + + // alice should have the remaining balance + erc20Balance = suite.Kava.GetErc20Balance(contractAddress.Address, alice.EvmAddress) + suite.BigIntsEqual(amount, erc20Balance, "expected alice to have half initial funds remaining") + + // convert alice's remaining balance back to sdk coins + convertMsg = evmutiltypes.NewMsgConvertCosmosCoinFromERC20( + alice.EvmAddress.Hex(), + alice.SdkAddress.String(), + sdk.NewInt64Coin(denom, amount.Int64()), + ) + convertRes = alice.SignAndBroadcastKavaTx(convertTx) + suite.NoError(convertRes.Err) } diff --git a/tests/e2e/e2e_evm_contracts_test.go b/tests/e2e/e2e_evm_contracts_test.go index 174a94f5..1153dac2 100644 --- a/tests/e2e/e2e_evm_contracts_test.go +++ b/tests/e2e/e2e_evm_contracts_test.go @@ -47,7 +47,7 @@ func (suite *IntegrationTestSuite) TestEthCallToGreeterContract() { func (suite *IntegrationTestSuite) TestEthCallToErc20() { randoReceiver := util.SdkToEvmAddress(app.RandomAddress()) - amount := big.NewInt(1e3) + amount := big.NewInt(1) // make unauthenticated eth_call query to check balance beforeBalance := suite.Kava.GetErc20Balance(suite.DeployedErc20.Address, randoReceiver) @@ -108,7 +108,7 @@ func (suite *IntegrationTestSuite) TestEip712BasicMessageAuthorization() { // Note that this test works because the deployed erc20 is configured in evmutil & earn params. func (suite *IntegrationTestSuite) TestEip712ConvertToCoinAndDepositToEarn() { - amount := sdk.NewInt(1e4) // .04 USDC + amount := sdk.NewInt(1e2) // 0.0002 USDC sdkDenom := suite.DeployedErc20.CosmosDenom // create new funded account @@ -172,4 +172,24 @@ func (suite *IntegrationTestSuite) TestEip712ConvertToCoinAndDepositToEarn() { suite.NoError(err) suite.Len(earnRes.Deposits, 1) suite.Equal(sdk.NewDecFromInt(amount), earnRes.Deposits[0].Shares.AmountOf(sdkDenom)) + + // withdraw deposit & convert back to erc20 (this allows refund to recover erc20s used in test) + withdraw := earntypes.NewMsgWithdraw( + depositor.SdkAddress.String(), + sdk.NewCoin(sdkDenom, amount), + earntypes.STRATEGY_TYPE_SAVINGS, + ) + convertBack := evmutiltypes.NewMsgConvertCoinToERC20( + depositor.SdkAddress.String(), + depositor.EvmAddress.Hex(), + sdk.NewCoin(sdkDenom, amount), + ) + withdrawAndConvertBack := util.KavaMsgRequest{ + Msgs: []sdk.Msg{withdraw, &convertBack}, + GasLimit: 3e5, + FeeAmount: sdk.NewCoins(ukava(300)), + Data: "withdrawing from earn & converting back to erc20", + } + lastRes := depositor.SignAndBroadcastKavaTx(withdrawAndConvertBack) + suite.NoError(lastRes.Err) } diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 8bb15b00..06d2b838 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -57,7 +57,7 @@ func (suite *IntegrationTestSuite) TestChainID() { // example test that funds a new account & queries its balance func (suite *IntegrationTestSuite) TestFundedAccount() { - funds := ukava(1e7) + funds := ukava(1e3) acc := suite.Kava.NewFundedAccount("example-acc", sdk.NewCoins(funds)) // check that the sdk & evm signers are for the same account @@ -125,7 +125,7 @@ func (suite *IntegrationTestSuite) TestIbcTransfer() { ibcAcc := suite.Ibc.NewFundedAccount("ibc-transfer-ibc-side", sdk.NewCoins()) gasLimit := int64(2e5) - fee := ukava(7500) + fee := ukava(200) fundsToSend := ukava(5e4) // .005 KAVA transferMsg := ibctypes.NewMsgTransfer( diff --git a/tests/e2e/readme.md b/tests/e2e/readme.md index 2119964f..573e4d0f 100644 --- a/tests/e2e/readme.md +++ b/tests/e2e/readme.md @@ -21,6 +21,19 @@ The variables in `.env` will not override variables that are already present in ie. Running `E2E_INCLUDE_IBC_TESTS=false make test-e2e` will disable the ibc tests regardless of how the variable is set in `.env`. +### Running on Live Network + +The end-to-end tests support being run on a live network. The primary toggle for setting up the suite to use a live network is the `E2E_RUN_KVTOOL_NETWORKS` flag. When set exactly to `false`, the configuration requires the following three environment variables: +* `E2E_KAVA_RPC_URL` +* `E2E_KAVA_GRPC_URL` +* `E2E_KAVA_EVM_RPC_URL` + +See an example environment configuration with full description of all supported configurations in [`.env.live-network-example`](./.env.live-network-example). This example expects a local kvtool network to be running: `kvtool testnet bootstrap`. + +When run against a live network, the suite will automatically return all the sdk funds sent to `SigningAccount`s on the chain, and will return any ERC20 balance from those accounts if the ERC20 is registered via `Chain.RegisterERC20`. The pre-deployed ERC20 that is required for the tests is registered on setup. + +At this time, live-network tests do not support `E2E_INCLUDE_IBC_TESTS=true` and they do not support automated upgrades. + ## `Chain`s A `testutil.Chain` is the abstraction around details, query clients, & signing accounts for interacting with a diff --git a/tests/e2e/testutil/account.go b/tests/e2e/testutil/account.go index 085cbde4..1f2eea99 100644 --- a/tests/e2e/testutil/account.go +++ b/tests/e2e/testutil/account.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "log" + "math/big" "os" "time" @@ -47,6 +48,8 @@ type SigningAccount struct { EvmAddress common.Address SdkAddress sdk.AccAddress + gasDenom string + l *log.Logger } @@ -106,6 +109,8 @@ func (chain *Chain) AddNewSigningAccount(name string, hdPath *hd.BIP44Params, ch mnemonic: mnemonic, l: logger, + gasDenom: chain.StakingDenom, + evmPrivKey: privKey, evmSigner: evmSigner, evmReqChan: evmReqChan, @@ -170,6 +175,7 @@ func (a *SigningAccount) SignAndBroadcastEvmTx(req util.EvmTxRequest) EvmTxRespo } // if we don't have a tx receipt within a given timeout, fail the request + a.l.Printf("awaiting evm tx receipt for tx %s\n", res.TxHash) response.Receipt, response.Err = util.WaitForEvmTxReceipt(a.evmSigner.EvmClient, res.TxHash, 10*time.Second) return response @@ -201,18 +207,11 @@ func (chain *Chain) NewFundedAccount(name string, funds sdk.Coins) *SigningAccou return acc } + // TODO: verify whale has funds. + whale := chain.GetAccount(FundedAccountName) whale.l.Printf("attempting to fund created account (%s=%s)\n", name, acc.SdkAddress.String()) - res := whale.SignAndBroadcastKavaTx( - util.KavaMsgRequest{ - Msgs: []sdk.Msg{ - banktypes.NewMsgSend(whale.SdkAddress, acc.SdkAddress, funds), - }, - GasLimit: 2e5, - FeeAmount: sdk.NewCoins(sdk.NewCoin(chain.StakingDenom, sdkmath.NewInt(75000))), - Data: fmt.Sprintf("initial funding of account %s", name), - }, - ) + res := whale.BankSend(acc.SdkAddress, funds) require.NoErrorf(chain.t, res.Err, "failed to fund new account %s: %s", name, res.Err) @@ -225,3 +224,32 @@ func (chain *Chain) NewFundedAccount(name string, funds sdk.Coins) *SigningAccou func (a *SigningAccount) NextNonce() (uint64, error) { return a.evmSigner.EvmClient.PendingNonceAt(context.Background(), a.EvmAddress) } + +// BankSend is a helper method for sending funds via x/bank's MsgSend +func (a *SigningAccount) BankSend(to sdk.AccAddress, amount sdk.Coins) util.KavaMsgResponse { + return a.SignAndBroadcastKavaTx( + util.KavaMsgRequest{ + Msgs: []sdk.Msg{banktypes.NewMsgSend(a.SdkAddress, to, amount)}, + GasLimit: 2e5, // 200,000 gas + FeeAmount: sdk.NewCoins(sdk.NewCoin(a.gasDenom, sdkmath.NewInt(200))), // assume min gas price of .001ukava + Data: fmt.Sprintf("sending %s to %s", amount, to), + }, + ) +} + +// TransferErc20 is a helper method for sending an erc20 token +func (a *SigningAccount) TransferErc20(contract, to common.Address, amount *big.Int) (EvmTxResponse, error) { + data := util.BuildErc20TransferCallData(to, amount) + nonce, err := a.NextNonce() + if err != nil { + return EvmTxResponse{}, err + } + + req := util.EvmTxRequest{ + Tx: ethtypes.NewTransaction(nonce, contract, big.NewInt(0), 1e5, big.NewInt(1e10), data), + Data: fmt.Sprintf("fund %s with ERC20 balance (%s)", to.Hex(), amount.String()), + } + + res := a.SignAndBroadcastEvmTx(req) + return res, res.Err +} diff --git a/tests/e2e/testutil/chain.go b/tests/e2e/testutil/chain.go index 160eb3f3..3f757aaf 100644 --- a/tests/e2e/testutil/chain.go +++ b/tests/e2e/testutil/chain.go @@ -42,6 +42,7 @@ type Chain struct { EvmClient *ethclient.Client ContractAddrs map[string]common.Address + erc20s map[common.Address]struct{} EncodingConfig kavaparams.EncodingConfig @@ -66,6 +67,7 @@ func NewChain(t *testing.T, details *runner.ChainDetails, fundedAccountMnemonic StakingDenom: details.StakingDenom, ChainId: details.ChainId, ContractAddrs: make(map[string]common.Address), + erc20s: make(map[common.Address]struct{}), } chain.EncodingConfig = app.MakeEncodingConfig() @@ -118,6 +120,57 @@ func (chain *Chain) Shutdown() { } } +// ReturnAllFunds loops through all SigningAccounts and sends all their funds back to the +// initially funded account. +func (chain *Chain) ReturnAllFunds() { + whale := chain.GetAccount(FundedAccountName) + fmt.Println(chain.erc20s) + for _, a := range chain.accounts { + if a.SdkAddress.String() != whale.SdkAddress.String() { + // NOTE: assumes all cosmos coin conversion funds have been converted back to sdk. + + // return all erc20 balance + for erc20Addr := range chain.erc20s { + erc20Bal := chain.GetErc20Balance(erc20Addr, a.EvmAddress) + // if account has no balance, do nothing + if erc20Bal.Cmp(big.NewInt(0)) == 0 { + continue + } + _, err := a.TransferErc20(erc20Addr, whale.EvmAddress, erc20Bal) + if err != nil { + a.l.Printf("FAILED TO RETURN ERC20 FUNDS (contract: %s, balance: %d): %s\n", + erc20Addr, erc20Bal, err, + ) + } + } + + // get sdk balance of account + balance := chain.QuerySdkForBalances(a.SdkAddress) + // assumes 200,000 gas w/ min fee of .001 + gas := sdk.NewInt64Coin(chain.StakingDenom, 200) + + // ensure they have enough gas to return funds + if balance.AmountOf(chain.StakingDenom).LT(gas.Amount) { + a.l.Printf("ACCOUNT LACKS GAS MONEY TO RETURN FUNDS: %s\n", balance) + continue + } + + // send it all back (minus gas) to the whale! + res := a.BankSend(whale.SdkAddress, balance.Sub(gas)) + if res.Err != nil { + a.l.Printf("failed to return funds: %s\n", res.Err) + } + } + } +} + +// RegisterErc20 is a method to record the address of erc20s on this chain. +// The full balances of each registered erc20 will be returned to the funded +// account when ReturnAllFunds is called. +func (chain *Chain) RegisterErc20(address common.Address) { + chain.erc20s[address] = struct{}{} +} + // QuerySdkForBalances gets the balance of a particular address on this Chain. func (chain *Chain) QuerySdkForBalances(addr sdk.AccAddress) sdk.Coins { res, err := chain.Bank.AllBalances(context.Background(), &banktypes.QueryAllBalancesRequest{ diff --git a/tests/e2e/testutil/config.go b/tests/e2e/testutil/config.go index 367e8fb4..6cf38d81 100644 --- a/tests/e2e/testutil/config.go +++ b/tests/e2e/testutil/config.go @@ -110,7 +110,7 @@ func ParseLiveNetworkConfig() LiveNetworkConfig { return LiveNetworkConfig{ KavaRpcUrl: nonemptyStringEnv("E2E_KAVA_RPC_URL"), KavaGrpcUrl: nonemptyStringEnv("E2E_KAVA_GRPC_URL"), - KavaEvmRpcUrl: nonemptyStringEnv("E2E_KAVA_EMV_RPC_URL"), + KavaEvmRpcUrl: nonemptyStringEnv("E2E_KAVA_EVM_RPC_URL"), } } diff --git a/tests/e2e/testutil/init_evm.go b/tests/e2e/testutil/init_evm.go index 85abd00b..9dabb591 100644 --- a/tests/e2e/testutil/init_evm.go +++ b/tests/e2e/testutil/init_evm.go @@ -6,10 +6,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/kava-labs/kava/tests/e2e/contracts/greeter" - "github.com/kava-labs/kava/tests/util" "github.com/kava-labs/kava/x/earn/types" evmutiltypes "github.com/kava-labs/kava/x/evmutil/types" ) @@ -41,6 +39,7 @@ func (suite *E2eTestSuite) InitKavaEvmData() { if !found { panic(fmt.Sprintf("erc20 %s must be enabled for conversion to cosmos coin", erc20Addr)) } + suite.Kava.RegisterErc20(suite.DeployedErc20.Address) // expect the erc20's cosmos denom to be a supported earn vault _, err = suite.Kava.Earn.Vault( @@ -65,15 +64,7 @@ func (suite *E2eTestSuite) InitKavaEvmData() { func (suite *E2eTestSuite) FundKavaErc20Balance(toAddress common.Address, amount *big.Int) EvmTxResponse { // funded account should have erc20 balance whale := suite.Kava.GetAccount(FundedAccountName) - - data := util.BuildErc20TransferCallData(toAddress, amount) - nonce, err := suite.Kava.EvmClient.PendingNonceAt(context.Background(), whale.EvmAddress) + res, err := whale.TransferErc20(suite.DeployedErc20.Address, toAddress, amount) suite.NoError(err) - - req := util.EvmTxRequest{ - Tx: ethtypes.NewTransaction(nonce, suite.DeployedErc20.Address, big.NewInt(0), 1e5, big.NewInt(1e10), data), - Data: fmt.Sprintf("fund %s with ERC20 balance (%s)", toAddress.Hex(), amount.String()), - } - - return whale.SignAndBroadcastEvmTx(req) + return res } diff --git a/tests/e2e/testutil/suite.go b/tests/e2e/testutil/suite.go index 4f2082c9..82ad211f 100644 --- a/tests/e2e/testutil/suite.go +++ b/tests/e2e/testutil/suite.go @@ -8,8 +8,12 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/suite" + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/kava-labs/kava/app" "github.com/kava-labs/kava/tests/e2e/runner" + "github.com/kava-labs/kava/tests/util" ) const ( @@ -48,6 +52,31 @@ type E2eTestSuite struct { UpgradeHeight int64 DeployedErc20 DeployedErc20 + + cost costSummary + enableRefunds bool +} + +// costSummary wraps info about what funds get irrecoverably spent by the test suite run +type costSummary struct { + sdkAddress string + evmAddress string + + erc20BalanceBefore *big.Int + erc20BalanceAfter *big.Int + + sdkBalanceBefore sdk.Coins + sdkBalanceAfter sdk.Coins +} + +// String implements fmt.Stringer +func (s costSummary) String() string { + before := sdk.NewCoins(s.sdkBalanceBefore...).Add(sdk.NewCoin("erc20", sdkmath.NewIntFromBigInt(s.erc20BalanceBefore))) + after := sdk.NewCoins(s.sdkBalanceAfter...).Add(sdk.NewCoin("erc20", sdkmath.NewIntFromBigInt(s.erc20BalanceAfter))) + cost, _ := before.SafeSub(after...) + return fmt.Sprintf("Cost Summary for %s (%s):\nbefore:\n%s\nafter:\n%s\ncost:\n%s\n", + s.sdkAddress, s.evmAddress, util.PrettyPrintCoins(before), util.PrettyPrintCoins(after), util.PrettyPrintCoins(cost), + ) } // SetupSuite is run before all tests. It initializes chain connections and sets up the @@ -91,6 +120,14 @@ func (suite *E2eTestSuite) SetupSuite() { } suite.InitKavaEvmData() + + whale := suite.Kava.GetAccount(FundedAccountName) + suite.cost = costSummary{ + sdkAddress: whale.SdkAddress.String(), + evmAddress: whale.EvmAddress.Hex(), + sdkBalanceBefore: suite.Kava.QuerySdkForBalances(whale.SdkAddress), + erc20BalanceBefore: suite.Kava.GetErc20Balance(suite.DeployedErc20.Address, whale.EvmAddress), + } } // TearDownSuite is run after all tests have run. @@ -98,6 +135,25 @@ func (suite *E2eTestSuite) SetupSuite() { func (suite *E2eTestSuite) TearDownSuite() { fmt.Println("tearing down test suite.") + whale := suite.Kava.GetAccount(FundedAccountName) + + if suite.enableRefunds { + suite.cost.sdkBalanceAfter = suite.Kava.QuerySdkForBalances(whale.SdkAddress) + suite.cost.erc20BalanceAfter = suite.Kava.GetErc20Balance(suite.DeployedErc20.Address, whale.EvmAddress) + fmt.Println("==BEFORE REFUNDS==") + fmt.Println(suite.cost) + + fmt.Println("attempting to return all unused funds") + suite.Kava.ReturnAllFunds() + + fmt.Println("==AFTER REFUNDS==") + } + + // calculate & output cost summary for funded account + suite.cost.sdkBalanceAfter = suite.Kava.QuerySdkForBalances(whale.SdkAddress) + suite.cost.erc20BalanceAfter = suite.Kava.GetErc20Balance(suite.DeployedErc20.Address, whale.EvmAddress) + fmt.Println(suite.cost) + // TODO: track asset denoms & then return all funds to initial funding account. // close all account request channels @@ -113,6 +169,7 @@ func (suite *E2eTestSuite) TearDownSuite() { func (suite *E2eTestSuite) SetupKvtoolNodeRunner() *runner.KvtoolRunner { // upgrade tests are only supported on kvtool networks suite.UpgradeHeight = suite.config.Kvtool.KavaUpgradeHeight + suite.enableRefunds = false runnerConfig := runner.KvtoolRunnerConfig{ KavaConfigTemplate: suite.config.Kvtool.KavaConfigTemplate, @@ -137,6 +194,7 @@ func (suite *E2eTestSuite) SetupLiveNetworkNodeRunner() *runner.LiveNodeRunner { if suite.config.IncludeIbcTests { panic("ibc tests not supported for live network configuration") } + suite.enableRefunds = true runnerConfig := runner.LiveNodeRunnerConfig{ KavaRpcUrl: suite.config.LiveNetwork.KavaRpcUrl, diff --git a/tests/util/strings.go b/tests/util/strings.go new file mode 100644 index 00000000..2d3a9515 --- /dev/null +++ b/tests/util/strings.go @@ -0,0 +1,20 @@ +package util + +import ( + "fmt" + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func PrettyPrintCoins(coins sdk.Coins) string { + if len(coins) == 0 { + return "" + } + + out := make([]string, 0, len(coins)) + for _, coin := range coins { + out = append(out, coin.String()) + } + return fmt.Sprintf("- %s", strings.Join(out, "\n- ")) +}