mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10:05:18 +00:00
test(e2e): lower cost to run on live networks (#1646)
* 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
This commit is contained in:
parent
7cff7bec23
commit
5cfa33f638
@ -8,7 +8,7 @@ E2E_RUN_KVTOOL_NETWORKS=false
|
|||||||
# Configure the endpoints for connecting to the running network here.
|
# Configure the endpoints for connecting to the running network here.
|
||||||
E2E_KAVA_RPC_URL='http://localhost:26657'
|
E2E_KAVA_RPC_URL='http://localhost:26657'
|
||||||
E2E_KAVA_GRPC_URL='http://localhost:9090'
|
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 is not currently supported for running tests against a live network.
|
||||||
E2E_INCLUDE_IBC_TESTS=false
|
E2E_INCLUDE_IBC_TESTS=false
|
||||||
|
@ -2,6 +2,7 @@ package e2e_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ import (
|
|||||||
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
|
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const initialCosmosCoinConversionDenomFunds = int64(1e4)
|
||||||
|
|
||||||
func setupConvertToCoinTest(
|
func setupConvertToCoinTest(
|
||||||
suite *IntegrationTestSuite, accountName string,
|
suite *IntegrationTestSuite, accountName string,
|
||||||
) (denom string, initialFunds sdk.Coins, user *testutil.SigningAccount) {
|
) (denom string, initialFunds sdk.Coins, user *testutil.SigningAccount) {
|
||||||
@ -31,8 +34,8 @@ func setupConvertToCoinTest(
|
|||||||
tokenInfo := params.Params.AllowedCosmosDenoms[0]
|
tokenInfo := params.Params.AllowedCosmosDenoms[0]
|
||||||
denom = tokenInfo.CosmosDenom
|
denom = tokenInfo.CosmosDenom
|
||||||
initialFunds = sdk.NewCoins(
|
initialFunds = sdk.NewCoins(
|
||||||
sdk.NewInt64Coin(suite.Kava.StakingDenom, 1e6), // gas money
|
sdk.NewInt64Coin(suite.Kava.StakingDenom, 1e5), // gas money
|
||||||
sdk.NewInt64Coin(denom, 1e6), // conversion-enabled cosmos coin
|
sdk.NewInt64Coin(denom, initialCosmosCoinConversionDenomFunds), // conversion-enabled cosmos coin
|
||||||
)
|
)
|
||||||
|
|
||||||
user = suite.Kava.NewFundedAccount(accountName, initialFunds)
|
user = suite.Kava.NewFundedAccount(accountName, initialFunds)
|
||||||
@ -40,19 +43,17 @@ func setupConvertToCoinTest(
|
|||||||
return denom, initialFunds, user
|
return denom, initialFunds, user
|
||||||
}
|
}
|
||||||
|
|
||||||
// amount must be less than initial funds (1e6)
|
// amount must be less than initial funds (initialCosmosCoinConversionDenomFunds)
|
||||||
func (suite *IntegrationTestSuite) setupAccountWithCosmosCoinERC20Balance(
|
func (suite *IntegrationTestSuite) setupAccountWithCosmosCoinERC20Balance(
|
||||||
accountName string, amount int64,
|
accountName string, amount int64,
|
||||||
) (user *testutil.SigningAccount, contractAddress *evmutiltypes.InternalEVMAddress, denom string, sdkBalance sdk.Coins) {
|
) (user *testutil.SigningAccount, contractAddress *evmutiltypes.InternalEVMAddress, denom string, sdkBalance sdk.Coins) {
|
||||||
if amount > 1e6 {
|
if amount > initialCosmosCoinConversionDenomFunds {
|
||||||
panic("test erc20 amount must be less than 1e6")
|
panic(fmt.Sprintf("test erc20 amount must be less than %d", initialCosmosCoinConversionDenomFunds))
|
||||||
}
|
}
|
||||||
|
|
||||||
denom, sdkBalance, user = setupConvertToCoinTest(suite, accountName)
|
denom, sdkBalance, user = setupConvertToCoinTest(suite, accountName)
|
||||||
convertAmount := sdk.NewInt64Coin(denom, amount)
|
convertAmount := sdk.NewInt64Coin(denom, amount)
|
||||||
|
|
||||||
fee := sdk.NewCoins(ukava(7500))
|
|
||||||
|
|
||||||
// setup user to have erc20 balance
|
// setup user to have erc20 balance
|
||||||
msg := evmutiltypes.NewMsgConvertCosmosCoinToERC20(
|
msg := evmutiltypes.NewMsgConvertCosmosCoinToERC20(
|
||||||
user.SdkAddress.String(),
|
user.SdkAddress.String(),
|
||||||
@ -61,8 +62,8 @@ func (suite *IntegrationTestSuite) setupAccountWithCosmosCoinERC20Balance(
|
|||||||
)
|
)
|
||||||
tx := util.KavaMsgRequest{
|
tx := util.KavaMsgRequest{
|
||||||
Msgs: []sdk.Msg{&msg},
|
Msgs: []sdk.Msg{&msg},
|
||||||
GasLimit: 2e6,
|
GasLimit: 4e5,
|
||||||
FeeAmount: fee,
|
FeeAmount: sdk.NewCoins(ukava(400)),
|
||||||
Data: "converting sdk coin to erc20",
|
Data: "converting sdk coin to erc20",
|
||||||
}
|
}
|
||||||
res := user.SignAndBroadcastKavaTx(tx)
|
res := user.SignAndBroadcastKavaTx(tx)
|
||||||
@ -87,8 +88,7 @@ func (suite *IntegrationTestSuite) setupAccountWithCosmosCoinERC20Balance(
|
|||||||
func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() {
|
func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() {
|
||||||
denom, initialFunds, user := setupConvertToCoinTest(suite, "cosmo-coin-converter")
|
denom, initialFunds, user := setupConvertToCoinTest(suite, "cosmo-coin-converter")
|
||||||
|
|
||||||
fee := sdk.NewCoins(ukava(7500))
|
convertAmount := int64(5e3)
|
||||||
convertAmount := int64(5e5)
|
|
||||||
initialModuleBalance := suite.Kava.GetModuleBalances(evmutiltypes.ModuleName).AmountOf(denom)
|
initialModuleBalance := suite.Kava.GetModuleBalances(evmutiltypes.ModuleName).AmountOf(denom)
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
@ -102,7 +102,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() {
|
|||||||
tx := util.KavaMsgRequest{
|
tx := util.KavaMsgRequest{
|
||||||
Msgs: []sdk.Msg{&convertToErc20Msg},
|
Msgs: []sdk.Msg{&convertToErc20Msg},
|
||||||
GasLimit: 2e6,
|
GasLimit: 2e6,
|
||||||
FeeAmount: fee,
|
FeeAmount: sdk.NewCoins(ukava(2000)),
|
||||||
Data: "converting sdk coin to erc20",
|
Data: "converting sdk coin to erc20",
|
||||||
}
|
}
|
||||||
res := user.SignAndBroadcastKavaTx(tx)
|
res := user.SignAndBroadcastKavaTx(tx)
|
||||||
@ -144,7 +144,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() {
|
|||||||
tx = util.KavaMsgRequest{
|
tx = util.KavaMsgRequest{
|
||||||
Msgs: []sdk.Msg{&convertFromErc20Msg},
|
Msgs: []sdk.Msg{&convertFromErc20Msg},
|
||||||
GasLimit: 2e5,
|
GasLimit: 2e5,
|
||||||
FeeAmount: fee,
|
FeeAmount: sdk.NewCoins(ukava(200)),
|
||||||
Data: "converting erc20 to cosmos coin",
|
Data: "converting erc20 to cosmos coin",
|
||||||
}
|
}
|
||||||
res = user.SignAndBroadcastKavaTx(tx)
|
res = user.SignAndBroadcastKavaTx(tx)
|
||||||
@ -168,7 +168,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoinsToFromERC20() {
|
|||||||
func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() {
|
func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() {
|
||||||
denom, initialFunds, user := setupConvertToCoinTest(suite, "cosmo-coin-converter-eip712")
|
denom, initialFunds, user := setupConvertToCoinTest(suite, "cosmo-coin-converter-eip712")
|
||||||
|
|
||||||
convertAmount := int64(5e5)
|
convertAmount := int64(5e3)
|
||||||
initialModuleBalance := suite.Kava.GetModuleBalances(evmutiltypes.ModuleName).AmountOf(denom)
|
initialModuleBalance := suite.Kava.GetModuleBalances(evmutiltypes.ModuleName).AmountOf(denom)
|
||||||
|
|
||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
@ -237,7 +237,7 @@ func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() {
|
|||||||
user,
|
user,
|
||||||
suite.Kava,
|
suite.Kava,
|
||||||
2e5,
|
2e5,
|
||||||
sdk.NewCoins(ukava(1e4)),
|
sdk.NewCoins(ukava(200)),
|
||||||
[]sdk.Msg{&convertFromErc20Msg},
|
[]sdk.Msg{&convertFromErc20Msg},
|
||||||
"",
|
"",
|
||||||
).GetTx()
|
).GetTx()
|
||||||
@ -270,7 +270,9 @@ func (suite *IntegrationTestSuite) TestEIP712ConvertCosmosCoinsToFromERC20() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls() {
|
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() {
|
suite.Run("users can't mint()", func() {
|
||||||
data := util.BuildErc20MintCallData(user.EvmAddress, big.NewInt(1))
|
data := util.BuildErc20MintCallData(user.EvmAddress, big.NewInt(1))
|
||||||
@ -282,7 +284,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls()
|
|||||||
ðtypes.LegacyTx{
|
ðtypes.LegacyTx{
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
GasPrice: minEvmGasPrice,
|
GasPrice: minEvmGasPrice,
|
||||||
Gas: 1e6,
|
Gas: 1e5,
|
||||||
To: &contractAddress.Address,
|
To: &contractAddress.Address,
|
||||||
Data: data,
|
Data: data,
|
||||||
},
|
},
|
||||||
@ -306,7 +308,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls()
|
|||||||
ðtypes.LegacyTx{
|
ðtypes.LegacyTx{
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
GasPrice: minEvmGasPrice,
|
GasPrice: minEvmGasPrice,
|
||||||
Gas: 1e6,
|
Gas: 1e5,
|
||||||
To: &contractAddress.Address,
|
To: &contractAddress.Address,
|
||||||
Data: data,
|
Data: data,
|
||||||
},
|
},
|
||||||
@ -324,15 +326,14 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ForbiddenERC20Calls()
|
|||||||
// - check approval flow of erc20. alice approves bob to move their funds
|
// - 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
|
// - check complex conversion flow. bob converts funds they receive on evm back to sdk.Coin
|
||||||
func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() {
|
func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() {
|
||||||
fee := sdk.NewCoins(ukava(7500))
|
initialAliceAmount := int64(2e3)
|
||||||
initialAliceAmount := int64(2e5)
|
|
||||||
alice, contractAddress, denom, _ := suite.setupAccountWithCosmosCoinERC20Balance(
|
alice, contractAddress, denom, _ := suite.setupAccountWithCosmosCoinERC20Balance(
|
||||||
"cosmo-coin-converter-complex-alice", initialAliceAmount,
|
"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)
|
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
|
// bob can't move alice's funds
|
||||||
nonce, err := bob.NextNonce()
|
nonce, err := bob.NextNonce()
|
||||||
@ -340,7 +341,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() {
|
|||||||
transferFromTxData := ðtypes.LegacyTx{
|
transferFromTxData := ðtypes.LegacyTx{
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
GasPrice: minEvmGasPrice,
|
GasPrice: minEvmGasPrice,
|
||||||
Gas: 1e6,
|
Gas: 1e5,
|
||||||
To: &contractAddress.Address,
|
To: &contractAddress.Address,
|
||||||
Value: &big.Int{},
|
Value: &big.Int{},
|
||||||
Data: util.BuildErc20TransferFromCallData(alice.EvmAddress, bob.EvmAddress, amount),
|
Data: util.BuildErc20TransferFromCallData(alice.EvmAddress, bob.EvmAddress, amount),
|
||||||
@ -360,7 +361,7 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() {
|
|||||||
Tx: ethtypes.NewTx(ðtypes.LegacyTx{
|
Tx: ethtypes.NewTx(ðtypes.LegacyTx{
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
GasPrice: minEvmGasPrice,
|
GasPrice: minEvmGasPrice,
|
||||||
Gas: 1e6,
|
Gas: 1e5,
|
||||||
To: &contractAddress.Address,
|
To: &contractAddress.Address,
|
||||||
Value: &big.Int{},
|
Value: &big.Int{},
|
||||||
Data: util.BuildErc20ApproveCallData(bob.EvmAddress, amount),
|
Data: util.BuildErc20ApproveCallData(bob.EvmAddress, amount),
|
||||||
@ -410,8 +411,8 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() {
|
|||||||
)
|
)
|
||||||
convertTx := util.KavaMsgRequest{
|
convertTx := util.KavaMsgRequest{
|
||||||
Msgs: []sdk.Msg{&convertMsg},
|
Msgs: []sdk.Msg{&convertMsg},
|
||||||
GasLimit: 2e6,
|
GasLimit: 2e5,
|
||||||
FeeAmount: fee,
|
FeeAmount: sdk.NewCoins(ukava(200)),
|
||||||
Data: "bob converts his new erc20 to an sdk.Coin",
|
Data: "bob converts his new erc20 to an sdk.Coin",
|
||||||
}
|
}
|
||||||
convertRes := bob.SignAndBroadcastKavaTx(convertTx)
|
convertRes := bob.SignAndBroadcastKavaTx(convertTx)
|
||||||
@ -423,4 +424,17 @@ func (suite *IntegrationTestSuite) TestConvertCosmosCoins_ERC20Magic() {
|
|||||||
// bob should have sdk balance
|
// bob should have sdk balance
|
||||||
balance := suite.Kava.QuerySdkForBalances(bob.SdkAddress).AmountOf(denom)
|
balance := suite.Kava.QuerySdkForBalances(bob.SdkAddress).AmountOf(denom)
|
||||||
suite.Equal(sdk.NewIntFromBigInt(amount), balance)
|
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)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ func (suite *IntegrationTestSuite) TestEthCallToGreeterContract() {
|
|||||||
|
|
||||||
func (suite *IntegrationTestSuite) TestEthCallToErc20() {
|
func (suite *IntegrationTestSuite) TestEthCallToErc20() {
|
||||||
randoReceiver := util.SdkToEvmAddress(app.RandomAddress())
|
randoReceiver := util.SdkToEvmAddress(app.RandomAddress())
|
||||||
amount := big.NewInt(1e3)
|
amount := big.NewInt(1)
|
||||||
|
|
||||||
// make unauthenticated eth_call query to check balance
|
// make unauthenticated eth_call query to check balance
|
||||||
beforeBalance := suite.Kava.GetErc20Balance(suite.DeployedErc20.Address, randoReceiver)
|
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.
|
// Note that this test works because the deployed erc20 is configured in evmutil & earn params.
|
||||||
func (suite *IntegrationTestSuite) TestEip712ConvertToCoinAndDepositToEarn() {
|
func (suite *IntegrationTestSuite) TestEip712ConvertToCoinAndDepositToEarn() {
|
||||||
amount := sdk.NewInt(1e4) // .04 USDC
|
amount := sdk.NewInt(1e2) // 0.0002 USDC
|
||||||
sdkDenom := suite.DeployedErc20.CosmosDenom
|
sdkDenom := suite.DeployedErc20.CosmosDenom
|
||||||
|
|
||||||
// create new funded account
|
// create new funded account
|
||||||
@ -172,4 +172,24 @@ func (suite *IntegrationTestSuite) TestEip712ConvertToCoinAndDepositToEarn() {
|
|||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Len(earnRes.Deposits, 1)
|
suite.Len(earnRes.Deposits, 1)
|
||||||
suite.Equal(sdk.NewDecFromInt(amount), earnRes.Deposits[0].Shares.AmountOf(sdkDenom))
|
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)
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func (suite *IntegrationTestSuite) TestChainID() {
|
|||||||
|
|
||||||
// example test that funds a new account & queries its balance
|
// example test that funds a new account & queries its balance
|
||||||
func (suite *IntegrationTestSuite) TestFundedAccount() {
|
func (suite *IntegrationTestSuite) TestFundedAccount() {
|
||||||
funds := ukava(1e7)
|
funds := ukava(1e3)
|
||||||
acc := suite.Kava.NewFundedAccount("example-acc", sdk.NewCoins(funds))
|
acc := suite.Kava.NewFundedAccount("example-acc", sdk.NewCoins(funds))
|
||||||
|
|
||||||
// check that the sdk & evm signers are for the same account
|
// 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())
|
ibcAcc := suite.Ibc.NewFundedAccount("ibc-transfer-ibc-side", sdk.NewCoins())
|
||||||
|
|
||||||
gasLimit := int64(2e5)
|
gasLimit := int64(2e5)
|
||||||
fee := ukava(7500)
|
fee := ukava(200)
|
||||||
|
|
||||||
fundsToSend := ukava(5e4) // .005 KAVA
|
fundsToSend := ukava(5e4) // .005 KAVA
|
||||||
transferMsg := ibctypes.NewMsgTransfer(
|
transferMsg := ibctypes.NewMsgTransfer(
|
||||||
|
@ -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
|
ie. Running `E2E_INCLUDE_IBC_TESTS=false make test-e2e` will disable the ibc tests regardless of how
|
||||||
the variable is set in `.env`.
|
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
|
## `Chain`s
|
||||||
|
|
||||||
A `testutil.Chain` is the abstraction around details, query clients, & signing accounts for interacting with a
|
A `testutil.Chain` is the abstraction around details, query clients, & signing accounts for interacting with a
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ type SigningAccount struct {
|
|||||||
EvmAddress common.Address
|
EvmAddress common.Address
|
||||||
SdkAddress sdk.AccAddress
|
SdkAddress sdk.AccAddress
|
||||||
|
|
||||||
|
gasDenom string
|
||||||
|
|
||||||
l *log.Logger
|
l *log.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +109,8 @@ func (chain *Chain) AddNewSigningAccount(name string, hdPath *hd.BIP44Params, ch
|
|||||||
mnemonic: mnemonic,
|
mnemonic: mnemonic,
|
||||||
l: logger,
|
l: logger,
|
||||||
|
|
||||||
|
gasDenom: chain.StakingDenom,
|
||||||
|
|
||||||
evmPrivKey: privKey,
|
evmPrivKey: privKey,
|
||||||
evmSigner: evmSigner,
|
evmSigner: evmSigner,
|
||||||
evmReqChan: evmReqChan,
|
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
|
// 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)
|
response.Receipt, response.Err = util.WaitForEvmTxReceipt(a.evmSigner.EvmClient, res.TxHash, 10*time.Second)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
@ -201,18 +207,11 @@ func (chain *Chain) NewFundedAccount(name string, funds sdk.Coins) *SigningAccou
|
|||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: verify whale has funds.
|
||||||
|
|
||||||
whale := chain.GetAccount(FundedAccountName)
|
whale := chain.GetAccount(FundedAccountName)
|
||||||
whale.l.Printf("attempting to fund created account (%s=%s)\n", name, acc.SdkAddress.String())
|
whale.l.Printf("attempting to fund created account (%s=%s)\n", name, acc.SdkAddress.String())
|
||||||
res := whale.SignAndBroadcastKavaTx(
|
res := whale.BankSend(acc.SdkAddress, funds)
|
||||||
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),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
require.NoErrorf(chain.t, res.Err, "failed to fund new account %s: %s", name, res.Err)
|
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) {
|
func (a *SigningAccount) NextNonce() (uint64, error) {
|
||||||
return a.evmSigner.EvmClient.PendingNonceAt(context.Background(), a.EvmAddress)
|
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
|
||||||
|
}
|
||||||
|
@ -42,6 +42,7 @@ type Chain struct {
|
|||||||
|
|
||||||
EvmClient *ethclient.Client
|
EvmClient *ethclient.Client
|
||||||
ContractAddrs map[string]common.Address
|
ContractAddrs map[string]common.Address
|
||||||
|
erc20s map[common.Address]struct{}
|
||||||
|
|
||||||
EncodingConfig kavaparams.EncodingConfig
|
EncodingConfig kavaparams.EncodingConfig
|
||||||
|
|
||||||
@ -66,6 +67,7 @@ func NewChain(t *testing.T, details *runner.ChainDetails, fundedAccountMnemonic
|
|||||||
StakingDenom: details.StakingDenom,
|
StakingDenom: details.StakingDenom,
|
||||||
ChainId: details.ChainId,
|
ChainId: details.ChainId,
|
||||||
ContractAddrs: make(map[string]common.Address),
|
ContractAddrs: make(map[string]common.Address),
|
||||||
|
erc20s: make(map[common.Address]struct{}),
|
||||||
}
|
}
|
||||||
chain.EncodingConfig = app.MakeEncodingConfig()
|
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.
|
// QuerySdkForBalances gets the balance of a particular address on this Chain.
|
||||||
func (chain *Chain) QuerySdkForBalances(addr sdk.AccAddress) sdk.Coins {
|
func (chain *Chain) QuerySdkForBalances(addr sdk.AccAddress) sdk.Coins {
|
||||||
res, err := chain.Bank.AllBalances(context.Background(), &banktypes.QueryAllBalancesRequest{
|
res, err := chain.Bank.AllBalances(context.Background(), &banktypes.QueryAllBalancesRequest{
|
||||||
|
@ -110,7 +110,7 @@ func ParseLiveNetworkConfig() LiveNetworkConfig {
|
|||||||
return LiveNetworkConfig{
|
return LiveNetworkConfig{
|
||||||
KavaRpcUrl: nonemptyStringEnv("E2E_KAVA_RPC_URL"),
|
KavaRpcUrl: nonemptyStringEnv("E2E_KAVA_RPC_URL"),
|
||||||
KavaGrpcUrl: nonemptyStringEnv("E2E_KAVA_GRPC_URL"),
|
KavaGrpcUrl: nonemptyStringEnv("E2E_KAVA_GRPC_URL"),
|
||||||
KavaEvmRpcUrl: nonemptyStringEnv("E2E_KAVA_EMV_RPC_URL"),
|
KavaEvmRpcUrl: nonemptyStringEnv("E2E_KAVA_EVM_RPC_URL"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,10 +6,8 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"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/e2e/contracts/greeter"
|
||||||
"github.com/kava-labs/kava/tests/util"
|
|
||||||
"github.com/kava-labs/kava/x/earn/types"
|
"github.com/kava-labs/kava/x/earn/types"
|
||||||
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
|
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
|
||||||
)
|
)
|
||||||
@ -41,6 +39,7 @@ func (suite *E2eTestSuite) InitKavaEvmData() {
|
|||||||
if !found {
|
if !found {
|
||||||
panic(fmt.Sprintf("erc20 %s must be enabled for conversion to cosmos coin", erc20Addr))
|
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
|
// expect the erc20's cosmos denom to be a supported earn vault
|
||||||
_, err = suite.Kava.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 {
|
func (suite *E2eTestSuite) FundKavaErc20Balance(toAddress common.Address, amount *big.Int) EvmTxResponse {
|
||||||
// funded account should have erc20 balance
|
// funded account should have erc20 balance
|
||||||
whale := suite.Kava.GetAccount(FundedAccountName)
|
whale := suite.Kava.GetAccount(FundedAccountName)
|
||||||
|
res, err := whale.TransferErc20(suite.DeployedErc20.Address, toAddress, amount)
|
||||||
data := util.BuildErc20TransferCallData(toAddress, amount)
|
|
||||||
nonce, err := suite.Kava.EvmClient.PendingNonceAt(context.Background(), whale.EvmAddress)
|
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
return res
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,12 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/stretchr/testify/suite"
|
"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/app"
|
||||||
"github.com/kava-labs/kava/tests/e2e/runner"
|
"github.com/kava-labs/kava/tests/e2e/runner"
|
||||||
|
"github.com/kava-labs/kava/tests/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -48,6 +52,31 @@ type E2eTestSuite struct {
|
|||||||
|
|
||||||
UpgradeHeight int64
|
UpgradeHeight int64
|
||||||
DeployedErc20 DeployedErc20
|
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
|
// SetupSuite is run before all tests. It initializes chain connections and sets up the
|
||||||
@ -91,6 +120,14 @@ func (suite *E2eTestSuite) SetupSuite() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suite.InitKavaEvmData()
|
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.
|
// TearDownSuite is run after all tests have run.
|
||||||
@ -98,6 +135,25 @@ func (suite *E2eTestSuite) SetupSuite() {
|
|||||||
func (suite *E2eTestSuite) TearDownSuite() {
|
func (suite *E2eTestSuite) TearDownSuite() {
|
||||||
fmt.Println("tearing down test suite.")
|
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.
|
// TODO: track asset denoms & then return all funds to initial funding account.
|
||||||
|
|
||||||
// close all account request channels
|
// close all account request channels
|
||||||
@ -113,6 +169,7 @@ func (suite *E2eTestSuite) TearDownSuite() {
|
|||||||
func (suite *E2eTestSuite) SetupKvtoolNodeRunner() *runner.KvtoolRunner {
|
func (suite *E2eTestSuite) SetupKvtoolNodeRunner() *runner.KvtoolRunner {
|
||||||
// upgrade tests are only supported on kvtool networks
|
// upgrade tests are only supported on kvtool networks
|
||||||
suite.UpgradeHeight = suite.config.Kvtool.KavaUpgradeHeight
|
suite.UpgradeHeight = suite.config.Kvtool.KavaUpgradeHeight
|
||||||
|
suite.enableRefunds = false
|
||||||
|
|
||||||
runnerConfig := runner.KvtoolRunnerConfig{
|
runnerConfig := runner.KvtoolRunnerConfig{
|
||||||
KavaConfigTemplate: suite.config.Kvtool.KavaConfigTemplate,
|
KavaConfigTemplate: suite.config.Kvtool.KavaConfigTemplate,
|
||||||
@ -137,6 +194,7 @@ func (suite *E2eTestSuite) SetupLiveNetworkNodeRunner() *runner.LiveNodeRunner {
|
|||||||
if suite.config.IncludeIbcTests {
|
if suite.config.IncludeIbcTests {
|
||||||
panic("ibc tests not supported for live network configuration")
|
panic("ibc tests not supported for live network configuration")
|
||||||
}
|
}
|
||||||
|
suite.enableRefunds = true
|
||||||
|
|
||||||
runnerConfig := runner.LiveNodeRunnerConfig{
|
runnerConfig := runner.LiveNodeRunnerConfig{
|
||||||
KavaRpcUrl: suite.config.LiveNetwork.KavaRpcUrl,
|
KavaRpcUrl: suite.config.LiveNetwork.KavaRpcUrl,
|
||||||
|
20
tests/util/strings.go
Normal file
20
tests/util/strings.go
Normal file
@ -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- "))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user