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

test(e2e): lower cost to run on live networks #1646

Merged
merged 16 commits into from
Jun 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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