Skip to content

Commit

Permalink
test(e2e): lower cost to run on live networks (#1646)
Browse files Browse the repository at this point in the history
* add cost summary to e2e test suite runs

* lower cost of test

* refactor initial funding to use new BankSend()

* reduce gas used for initial funding

* return all sdk funds on shutdown

* enable refunds when running against live network

* save more cosmos coins! require less total overall

* track erc20s on Chain

* refactor erc20 funding with new TransferErc20()

* return all registered erc20 balance

* lower erc20 e2e test values

* withdraw earn position & convert back to erc20

* lower gas fees in e2e tests

* fix env variable typo

* add readme note about how to run on live network

* log total spend required for e2e tests
  • Loading branch information
pirtleshell committed Jun 30, 2023
1 parent 7cff7be commit 5cfa33f
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 54 deletions.
2 changes: 1 addition & 1 deletion tests/e2e/.env.live-network-example
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
66 changes: 40 additions & 26 deletions tests/e2e/e2e_convert_cosmos_coins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package e2e_test

import (
"context"
"fmt"
"math/big"
"time"

Expand All @@ -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) {
Expand All @@ -31,28 +34,26 @@ 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)

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(),
Expand All @@ -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)
Expand All @@ -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)

///////////////////////////////
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)

///////////////////////////////
Expand Down Expand Up @@ -237,7 +237,7 @@ func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() {
user,
suite.Kava,
2e5,
sdk.NewCoins(ukava(1e4)),
sdk.NewCoins(ukava(200)),
[]sdk.Msg{&convertFromErc20Msg},
"",
).GetTx()
Expand Down Expand Up @@ -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))
Expand All @@ -282,7 +284,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls()
&ethtypes.LegacyTx{
Nonce: nonce,
GasPrice: minEvmGasPrice,
Gas: 1e6,
Gas: 1e5,
To: &contractAddress.Address,
Data: data,
},
Expand All @@ -306,7 +308,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls()
&ethtypes.LegacyTx{
Nonce: nonce,
GasPrice: minEvmGasPrice,
Gas: 1e6,
Gas: 1e5,
To: &contractAddress.Address,
Data: data,
},
Expand All @@ -324,23 +326,22 @@ 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()
suite.NoError(err)
transferFromTxData := &ethtypes.LegacyTx{
Nonce: nonce,
GasPrice: minEvmGasPrice,
Gas: 1e6,
Gas: 1e5,
To: &contractAddress.Address,
Value: &big.Int{},
Data: util.BuildErc20TransferFromCallData(alice.EvmAddress, bob.EvmAddress, amount),
Expand All @@ -360,7 +361,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() {
Tx: ethtypes.NewTx(&ethtypes.LegacyTx{
Nonce: nonce,
GasPrice: minEvmGasPrice,
Gas: 1e6,
Gas: 1e5,
To: &contractAddress.Address,
Value: &big.Int{},
Data: util.BuildErc20ApproveCallData(bob.EvmAddress, amount),
Expand Down Expand Up @@ -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)
Expand All @@ -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)
}
24 changes: 22 additions & 2 deletions tests/e2e/e2e_evm_contracts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
4 changes: 2 additions & 2 deletions tests/e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
13 changes: 13 additions & 0 deletions tests/e2e/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 38 additions & 10 deletions tests/e2e/testutil/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"encoding/hex"
"fmt"
"log"
"math/big"
"os"
"time"

Expand Down Expand Up @@ -47,6 +48,8 @@ type SigningAccount struct {
EvmAddress common.Address
SdkAddress sdk.AccAddress

gasDenom string

l *log.Logger
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand All @@ -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
}
Loading

0 comments on commit 5cfa33f

Please sign in to comment.