mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-11-10 10:05:18 +00:00
feat: e2e test eip712 signing & erc20 interactions (#1535)
* add helpers & tests for erc20 eth_call query & transfer * make encoding config public * add evm client & raw evm signer to account * test eip712 signing and broadcast * update for cosmos v46 * update kvtool * temporarily disable ibc tests & skip shutdown * disable all but eip712 test and massively simplify * add EIP712 tx builder & setup basic MsgSend test * reenable all tests * add eip712 test that deposits erc20 into earn * update kvtool to master branch
This commit is contained in:
parent
91e7933a55
commit
3366b3b3e3
@ -1,4 +1,5 @@
|
||||
# E2E_KAVA_FUNDED_ACCOUNT_MNEMONIC is for a funded account used to intialize all new testing accounts.
|
||||
# Should be funded with KAVA and have an ERC20 balance
|
||||
E2E_KAVA_FUNDED_ACCOUNT_MNEMONIC='tent fitness boat among census primary pipe nose dream glance cave turtle electric fabric jacket shaft easy myself genuine this sibling pulse word unfold'
|
||||
|
||||
# E2E_KVTOOL_KAVA_CONFIG_TEMPLATE is the kvtool template used to start the chain. See the `kava.configTemplate` flag in kvtool.
|
||||
@ -22,3 +23,7 @@ E2E_KAVA_UPGRADE_NAME=
|
||||
E2E_KAVA_UPGRADE_HEIGHT=
|
||||
# E2E_KAVA_UPGRADE_BASE_IMAGE_TAG is the tag of the docker image the chain should upgrade from.
|
||||
E2E_KAVA_UPGRADE_BASE_IMAGE_TAG=
|
||||
|
||||
# E2E_KAVA_ERC20_ADDRESS is the address of a pre-deployed ERC20 token.
|
||||
# The E2E_KAVA_FUNDED_ACCOUNT_MNEMONIC account should have a balance.
|
||||
E2E_KAVA_ERC20_ADDRESS=0xeA7100edA2f805356291B0E55DaD448599a72C6d
|
||||
|
@ -1,9 +1,18 @@
|
||||
package e2e_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
sdkerrors "cosmossdk.io/errors"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
earntypes "github.com/kava-labs/kava/x/earn/types"
|
||||
evmutiltypes "github.com/kava-labs/kava/x/evmutil/types"
|
||||
|
||||
"github.com/kava-labs/kava/tests/e2e/contracts/greeter"
|
||||
"github.com/kava-labs/kava/tests/util"
|
||||
@ -35,3 +44,132 @@ func (suite *IntegrationTestSuite) TestEthCallToGreeterContract() {
|
||||
suite.Equal("what's up!", beforeGreeting)
|
||||
suite.Equal(updatedGreeting, afterGreeting)
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestEthCallToErc20() {
|
||||
randoReceiver := util.SdkToEvmAddress(app.RandomAddress())
|
||||
amount := big.NewInt(1e6)
|
||||
|
||||
// make unauthenticated eth_call query to check balance
|
||||
beforeBalance := suite.GetErc20Balance(randoReceiver)
|
||||
|
||||
// make authenticate eth_call to transfer tokens
|
||||
res := suite.FundKavaErc20Balance(randoReceiver, amount)
|
||||
suite.NoError(res.Err)
|
||||
|
||||
// make another unauthenticated eth_call query to check new balance
|
||||
afterBalance := suite.GetErc20Balance(randoReceiver)
|
||||
|
||||
suite.BigIntsEqual(big.NewInt(0), beforeBalance, "expected before balance to be zero")
|
||||
suite.BigIntsEqual(amount, afterBalance, "unexpected post-transfer balance")
|
||||
}
|
||||
|
||||
func (suite *IntegrationTestSuite) TestEip712BasicMessageAuthorization() {
|
||||
// create new funded account
|
||||
sender := suite.Kava.NewFundedAccount("eip712-msgSend", sdk.NewCoins(ukava(10e6)))
|
||||
receiver := app.RandomAddress()
|
||||
|
||||
// setup message for sending 1KAVA to random receiver
|
||||
msgs := []sdk.Msg{
|
||||
banktypes.NewMsgSend(sender.SdkAddress, receiver, sdk.NewCoins(ukava(1e6))),
|
||||
}
|
||||
|
||||
// create tx
|
||||
tx := suite.NewEip712TxBuilder(
|
||||
sender,
|
||||
suite.Kava,
|
||||
1e6,
|
||||
sdk.NewCoins(ukava(1e4)),
|
||||
msgs,
|
||||
"this is a memo",
|
||||
).GetTx()
|
||||
|
||||
txBytes, err := suite.Kava.EncodingConfig.TxConfig.TxEncoder()(tx)
|
||||
suite.NoError(err)
|
||||
|
||||
// broadcast tx
|
||||
res, err := suite.Kava.Tx.BroadcastTx(context.Background(), &txtypes.BroadcastTxRequest{
|
||||
TxBytes: txBytes,
|
||||
Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC,
|
||||
})
|
||||
suite.NoError(err)
|
||||
suite.Equal(sdkerrors.SuccessABCICode, res.TxResponse.Code)
|
||||
|
||||
_, err = util.WaitForSdkTxCommit(suite.Kava.Tx, res.TxResponse.TxHash, 6*time.Second)
|
||||
suite.NoError(err)
|
||||
|
||||
// check that the message was processed & the kava is transferred.
|
||||
balRes, err := suite.Kava.Bank.Balance(context.Background(), &banktypes.QueryBalanceRequest{
|
||||
Address: receiver.String(),
|
||||
Denom: "ukava",
|
||||
})
|
||||
suite.NoError(err)
|
||||
suite.Equal(sdk.NewInt(1e6), balRes.Balance.Amount)
|
||||
}
|
||||
|
||||
// Note that this test works because the deployed erc20 is configured in evmutil & earn params.
|
||||
func (suite *IntegrationTestSuite) TestEip712ConvertToCoinAndDepositToEarn() {
|
||||
amount := sdk.NewInt(10e6) // 10 USDC
|
||||
sdkDenom := "erc20/multichain/usdc"
|
||||
|
||||
// create new funded account
|
||||
depositor := suite.Kava.NewFundedAccount("eip712-earn-depositor", sdk.NewCoins(ukava(1e6)))
|
||||
// give them erc20 balance to deposit
|
||||
fundRes := suite.FundKavaErc20Balance(depositor.EvmAddress, amount.BigInt())
|
||||
suite.NoError(fundRes.Err)
|
||||
|
||||
// setup messages for convert to coin & deposit into earn
|
||||
convertMsg := evmutiltypes.NewMsgConvertERC20ToCoin(
|
||||
evmutiltypes.NewInternalEVMAddress(depositor.EvmAddress),
|
||||
depositor.SdkAddress,
|
||||
evmutiltypes.NewInternalEVMAddress(suite.DeployedErc20Address),
|
||||
amount,
|
||||
)
|
||||
depositMsg := earntypes.NewMsgDeposit(
|
||||
depositor.SdkAddress.String(),
|
||||
sdk.NewCoin(sdkDenom, amount),
|
||||
earntypes.STRATEGY_TYPE_SAVINGS,
|
||||
)
|
||||
msgs := []sdk.Msg{
|
||||
// convert to coin
|
||||
&convertMsg,
|
||||
// deposit into earn
|
||||
depositMsg,
|
||||
}
|
||||
|
||||
// create tx
|
||||
tx := suite.NewEip712TxBuilder(
|
||||
depositor,
|
||||
suite.Kava,
|
||||
1e6,
|
||||
sdk.NewCoins(ukava(1e4)),
|
||||
msgs,
|
||||
"depositing my USDC into Earn!",
|
||||
).GetTx()
|
||||
|
||||
txBytes, err := suite.Kava.EncodingConfig.TxConfig.TxEncoder()(tx)
|
||||
suite.NoError(err)
|
||||
|
||||
// broadcast tx
|
||||
res, err := suite.Kava.Tx.BroadcastTx(context.Background(), &txtypes.BroadcastTxRequest{
|
||||
TxBytes: txBytes,
|
||||
Mode: txtypes.BroadcastMode_BROADCAST_MODE_SYNC,
|
||||
})
|
||||
suite.NoError(err)
|
||||
suite.Equal(sdkerrors.SuccessABCICode, res.TxResponse.Code)
|
||||
|
||||
_, err = util.WaitForSdkTxCommit(suite.Kava.Tx, res.TxResponse.TxHash, 6*time.Second)
|
||||
suite.NoError(err)
|
||||
|
||||
// check that depositor no longer has erc20 balance
|
||||
balance := suite.GetErc20Balance(depositor.EvmAddress)
|
||||
suite.BigIntsEqual(big.NewInt(0), balance, "expected no erc20 balance")
|
||||
|
||||
// check that account has an earn deposit position
|
||||
earnRes, err := suite.Kava.Earn.Deposits(context.Background(), &earntypes.QueryDepositsRequest{
|
||||
Depositor: depositor.SdkAddress.String(),
|
||||
Denom: sdkDenom,
|
||||
})
|
||||
suite.NoError(err)
|
||||
suite.Len(earnRes.Deposits, 1)
|
||||
suite.Equal(sdk.NewDecFromInt(amount), earnRes.Deposits[0].Shares.AmountOf(sdkDenom))
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 6022e137686e6ab648174b3487c3ee82ec7056ae
|
||||
Subproject commit 9111087667b4ec136a4dd684762e41515a240a0f
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
sdkmath "cosmossdk.io/math"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/cosmos/go-bip39"
|
||||
@ -17,6 +18,7 @@ import (
|
||||
ethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/evmos/ethermint/crypto/ethsecp256k1"
|
||||
emtests "github.com/evmos/ethermint/tests"
|
||||
emtypes "github.com/evmos/ethermint/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@ -28,6 +30,7 @@ type SigningAccount struct {
|
||||
name string
|
||||
mnemonic string
|
||||
|
||||
evmPrivKey *ethsecp256k1.PrivKey
|
||||
evmSigner *util.EvmSigner
|
||||
evmReqChan chan<- util.EvmTxRequest
|
||||
evmResChan <-chan util.EvmTxResponse
|
||||
@ -66,7 +69,7 @@ func (chain *Chain) AddNewSigningAccount(name string, hdPath *hd.BIP44Params, ch
|
||||
|
||||
kavaSigner := util.NewKavaSigner(
|
||||
chainId,
|
||||
chain.encodingConfig,
|
||||
chain.EncodingConfig,
|
||||
chain.Auth,
|
||||
chain.Tx,
|
||||
privKey,
|
||||
@ -100,6 +103,7 @@ func (chain *Chain) AddNewSigningAccount(name string, hdPath *hd.BIP44Params, ch
|
||||
mnemonic: mnemonic,
|
||||
l: logger,
|
||||
|
||||
evmPrivKey: privKey,
|
||||
evmSigner: evmSigner,
|
||||
evmReqChan: evmReqChan,
|
||||
evmResChan: evmResChan,
|
||||
@ -168,6 +172,11 @@ func (a *SigningAccount) SignAndBroadcastEvmTx(req util.EvmTxRequest) EvmTxRespo
|
||||
return response
|
||||
}
|
||||
|
||||
func (a *SigningAccount) SignRawEvmData(msg []byte) ([]byte, types.PubKey, error) {
|
||||
keyringSigner := emtests.NewSigner(a.evmPrivKey)
|
||||
return keyringSigner.SignByAddress(a.SdkAddress, msg)
|
||||
}
|
||||
|
||||
// NewFundedAccount creates a SigningAccount for a random account & funds the account from the whale.
|
||||
func (chain *Chain) NewFundedAccount(name string, funds sdk.Coins) *SigningAccount {
|
||||
entropy, err := bip39.NewEntropy(128)
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
@ -25,9 +26,8 @@ import (
|
||||
|
||||
// Chain wraps query clients & accounts for a network
|
||||
type Chain struct {
|
||||
encodingConfig kavaparams.EncodingConfig
|
||||
accounts map[string]*SigningAccount
|
||||
t *testing.T
|
||||
accounts map[string]*SigningAccount
|
||||
t *testing.T
|
||||
|
||||
StakingDenom string
|
||||
ChainId string
|
||||
@ -35,10 +35,13 @@ type Chain struct {
|
||||
EvmClient *ethclient.Client
|
||||
ContractAddrs map[string]common.Address
|
||||
|
||||
EncodingConfig kavaparams.EncodingConfig
|
||||
|
||||
Auth authtypes.QueryClient
|
||||
Bank banktypes.QueryClient
|
||||
Community communitytypes.QueryClient
|
||||
Earn earntypes.QueryClient
|
||||
Evm evmtypes.QueryClient
|
||||
Tm tmservice.ServiceClient
|
||||
Tx txtypes.ServiceClient
|
||||
}
|
||||
@ -53,7 +56,7 @@ func NewChain(t *testing.T, details *runner.ChainDetails, fundedAccountMnemonic
|
||||
ChainId: details.ChainId,
|
||||
ContractAddrs: make(map[string]common.Address),
|
||||
}
|
||||
chain.encodingConfig = app.MakeEncodingConfig()
|
||||
chain.EncodingConfig = app.MakeEncodingConfig()
|
||||
|
||||
grpcUrl := fmt.Sprintf("http://localhost:%s", details.GrpcPort)
|
||||
grpcConn, err := util.NewGrpcConnection(grpcUrl)
|
||||
@ -71,6 +74,7 @@ func NewChain(t *testing.T, details *runner.ChainDetails, fundedAccountMnemonic
|
||||
chain.Bank = banktypes.NewQueryClient(grpcConn)
|
||||
chain.Community = communitytypes.NewQueryClient(grpcConn)
|
||||
chain.Earn = earntypes.NewQueryClient(grpcConn)
|
||||
chain.Evm = evmtypes.NewQueryClient(grpcConn)
|
||||
chain.Tm = tmservice.NewServiceClient(grpcConn)
|
||||
chain.Tx = txtypes.NewServiceClient(grpcConn)
|
||||
|
||||
|
@ -31,6 +31,9 @@ type SuiteConfig struct {
|
||||
// Tag of kava docker image that will be upgraded to the current image before tests are run, if upgrade is enabled.
|
||||
KavaUpgradeBaseImageTag string
|
||||
|
||||
// The contract address of a deployed ERC-20 token
|
||||
KavaErc20Address string
|
||||
|
||||
// When true, the chains will remain running after tests complete (pass or fail)
|
||||
SkipShutdown bool
|
||||
}
|
||||
@ -41,6 +44,7 @@ func ParseSuiteConfig() SuiteConfig {
|
||||
// new accounts created during tests. it will be available under Accounts["whale"]
|
||||
FundedAccountMnemonic: nonemptyStringEnv("E2E_KAVA_FUNDED_ACCOUNT_MNEMONIC"),
|
||||
KavaConfigTemplate: nonemptyStringEnv("E2E_KVTOOL_KAVA_CONFIG_TEMPLATE"),
|
||||
KavaErc20Address: nonemptyStringEnv("E2E_KAVA_ERC20_ADDRESS"),
|
||||
IncludeIbcTests: mustParseBool("E2E_INCLUDE_IBC_TESTS"),
|
||||
IncludeAutomatedUpgrade: mustParseBool("E2E_INCLUDE_AUTOMATED_UPGRADE"),
|
||||
}
|
||||
|
107
tests/e2e/testutil/eip712.go
Normal file
107
tests/e2e/testutil/eip712.go
Normal file
@ -0,0 +1,107 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/evmos/ethermint/ethereum/eip712"
|
||||
emtypes "github.com/evmos/ethermint/types"
|
||||
evmtypes "github.com/evmos/ethermint/x/evm/types"
|
||||
)
|
||||
|
||||
func (suite *E2eTestSuite) NewEip712TxBuilder(
|
||||
acc *SigningAccount, chain *Chain, gas uint64, gasAmount sdk.Coins, msgs []sdk.Msg, memo string,
|
||||
) client.TxBuilder {
|
||||
// get account details
|
||||
var accDetails authtypes.AccountI
|
||||
a, err := chain.Auth.Account(context.Background(), &authtypes.QueryAccountRequest{
|
||||
Address: acc.SdkAddress.String(),
|
||||
})
|
||||
suite.NoError(err)
|
||||
err = chain.EncodingConfig.InterfaceRegistry.UnpackAny(a.Account, &accDetails)
|
||||
suite.NoError(err)
|
||||
|
||||
// get nonce & acc number
|
||||
nonce := accDetails.GetSequence()
|
||||
accNumber := accDetails.GetAccountNumber()
|
||||
|
||||
// get chain id
|
||||
pc, err := emtypes.ParseChainID(chain.ChainId)
|
||||
suite.NoError(err)
|
||||
ethChainId := pc.Uint64()
|
||||
|
||||
evmParams, err := chain.Evm.Params(context.Background(), &evmtypes.QueryParamsRequest{})
|
||||
suite.NoError(err)
|
||||
|
||||
fee := legacytx.NewStdFee(gas, gasAmount)
|
||||
|
||||
// build EIP712 tx
|
||||
// -- untyped data
|
||||
untypedData := eip712.ConstructUntypedEIP712Data(
|
||||
chain.ChainId,
|
||||
accNumber,
|
||||
nonce,
|
||||
0, // no timeout
|
||||
fee,
|
||||
msgs,
|
||||
memo,
|
||||
nil,
|
||||
)
|
||||
// -- typed data
|
||||
typedData, err := eip712.WrapTxToTypedData(ethChainId, msgs, untypedData, &eip712.FeeDelegationOptions{
|
||||
FeePayer: acc.SdkAddress,
|
||||
}, evmParams.Params)
|
||||
suite.NoError(err)
|
||||
|
||||
// -- raw data hash!
|
||||
data, err := eip712.ComputeTypedDataHash(typedData)
|
||||
suite.NoError(err)
|
||||
|
||||
// -- sign the hash
|
||||
signature, pubKey, err := acc.SignRawEvmData(data)
|
||||
suite.NoError(err)
|
||||
signature[crypto.RecoveryIDOffset] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||
|
||||
// add ExtensionOptionsWeb3Tx extension
|
||||
var option *codectypes.Any
|
||||
option, err = codectypes.NewAnyWithValue(&emtypes.ExtensionOptionsWeb3Tx{
|
||||
FeePayer: acc.SdkAddress.String(),
|
||||
TypedDataChainID: ethChainId,
|
||||
FeePayerSig: signature,
|
||||
})
|
||||
suite.NoError(err)
|
||||
|
||||
// create cosmos sdk tx builder
|
||||
txBuilder := chain.EncodingConfig.TxConfig.NewTxBuilder()
|
||||
builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder)
|
||||
suite.True(ok)
|
||||
|
||||
builder.SetExtensionOptions(option)
|
||||
builder.SetFeeAmount(fee.Amount)
|
||||
builder.SetGasLimit(fee.Gas)
|
||||
|
||||
sigsV2 := signing.SignatureV2{
|
||||
PubKey: pubKey,
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
},
|
||||
Sequence: nonce,
|
||||
}
|
||||
|
||||
err = builder.SetSignatures(sigsV2)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
err = builder.SetMsgs(msgs...)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
builder.SetMemo(memo)
|
||||
|
||||
return builder
|
||||
}
|
@ -1,12 +1,29 @@
|
||||
package testutil
|
||||
|
||||
import "github.com/kava-labs/kava/tests/e2e/contracts/greeter"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"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"
|
||||
)
|
||||
|
||||
// InitKavaEvmData is run after the chain is running, but before the tests are run.
|
||||
// It is used to initialize some EVM state, such as deploying contracts.
|
||||
func (suite *E2eTestSuite) InitKavaEvmData() {
|
||||
whale := suite.Kava.GetAccount(FundedAccountName)
|
||||
|
||||
// ensure funded account has nonzero erc20 balance
|
||||
balance := suite.GetErc20Balance(whale.EvmAddress)
|
||||
if balance.Cmp(big.NewInt(0)) != 1 {
|
||||
panic(fmt.Sprintf("expected funded account (%s) to have erc20 balance", whale.EvmAddress.Hex()))
|
||||
}
|
||||
|
||||
// deploy an example contract
|
||||
greeterAddr, _, _, err := greeter.DeployGreeter(
|
||||
whale.evmSigner.Auth,
|
||||
@ -16,3 +33,29 @@ func (suite *E2eTestSuite) InitKavaEvmData() {
|
||||
suite.NoError(err, "failed to deploy a contract to the EVM")
|
||||
suite.Kava.ContractAddrs["greeter"] = greeterAddr
|
||||
}
|
||||
|
||||
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(whale.EvmAddress, toAddress, amount)
|
||||
nonce, err := suite.Kava.EvmClient.PendingNonceAt(context.Background(), whale.EvmAddress)
|
||||
suite.NoError(err)
|
||||
|
||||
req := util.EvmTxRequest{
|
||||
Tx: ethtypes.NewTransaction(nonce, suite.DeployedErc20Address, 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)
|
||||
}
|
||||
|
||||
func (suite *E2eTestSuite) GetErc20Balance(address common.Address) *big.Int {
|
||||
resData, err := suite.Kava.EvmClient.CallContract(context.Background(), ethereum.CallMsg{
|
||||
To: &suite.DeployedErc20Address,
|
||||
Data: util.BuildErc20BalanceOfCallData(address),
|
||||
}, nil)
|
||||
suite.NoError(err)
|
||||
|
||||
return new(big.Int).SetBytes(resData)
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package testutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/kava-labs/kava/app"
|
||||
@ -30,7 +32,8 @@ type E2eTestSuite struct {
|
||||
Kava *Chain
|
||||
Ibc *Chain
|
||||
|
||||
UpgradeHeight int64
|
||||
UpgradeHeight int64
|
||||
DeployedErc20Address common.Address
|
||||
}
|
||||
|
||||
func (suite *E2eTestSuite) SetupSuite() {
|
||||
@ -41,6 +44,7 @@ func (suite *E2eTestSuite) SetupSuite() {
|
||||
suiteConfig := ParseSuiteConfig()
|
||||
suite.config = suiteConfig
|
||||
suite.UpgradeHeight = suiteConfig.KavaUpgradeHeight
|
||||
suite.DeployedErc20Address = common.HexToAddress(suiteConfig.KavaErc20Address)
|
||||
|
||||
runnerConfig := runner.Config{
|
||||
KavaConfigTemplate: suiteConfig.KavaConfigTemplate,
|
||||
@ -105,3 +109,8 @@ func (suite *E2eTestSuite) SkipIfUpgradeDisabled() {
|
||||
func (suite *E2eTestSuite) KavaHomePath() string {
|
||||
return filepath.Join("kvtool", "full_configs", "generated", "kava", "initstate", ".kava")
|
||||
}
|
||||
|
||||
// BigIntsEqual is a helper method for comparing the equality of two big ints
|
||||
func (suite *E2eTestSuite) BigIntsEqual(expected *big.Int, actual *big.Int, msg string) {
|
||||
suite.Truef(expected.Cmp(actual) == 0, "%s (expected: %s, actual: %s)", msg, expected.String(), actual.String())
|
||||
}
|
||||
|
40
tests/util/erc20.go
Normal file
40
tests/util/erc20.go
Normal file
@ -0,0 +1,40 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// EvmContractMethodId encodes a method signature to the method id used in eth calldata.
|
||||
func EvmContractMethodId(signature string) []byte {
|
||||
transferFnSignature := []byte(signature)
|
||||
hash := sha3.NewLegacyKeccak256()
|
||||
hash.Write(transferFnSignature)
|
||||
return hash.Sum(nil)[:4]
|
||||
}
|
||||
|
||||
func BuildErc20TransferCallData(from common.Address, to common.Address, amount *big.Int) []byte {
|
||||
methodId := EvmContractMethodId("transfer(address,uint256)")
|
||||
paddedAddress := common.LeftPadBytes(to.Bytes(), 32)
|
||||
paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
|
||||
|
||||
var data []byte
|
||||
data = append(data, methodId...)
|
||||
data = append(data, paddedAddress...)
|
||||
data = append(data, paddedAmount...)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func BuildErc20BalanceOfCallData(address common.Address) []byte {
|
||||
methodId := EvmContractMethodId("balanceOf(address)")
|
||||
paddedAddress := common.LeftPadBytes(address.Bytes(), 32)
|
||||
|
||||
var data []byte
|
||||
data = append(data, methodId...)
|
||||
data = append(data, paddedAddress...)
|
||||
|
||||
return data
|
||||
}
|
@ -2,10 +2,13 @@ package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kava-labs/kava/app/params"
|
||||
"google.golang.org/grpc/codes"
|
||||
grpcstatus "google.golang.org/grpc/status"
|
||||
|
||||
errorsmod "cosmossdk.io/errors"
|
||||
sdkclient "github.com/cosmos/cosmos-sdk/client"
|
||||
@ -19,6 +22,10 @@ import (
|
||||
tmmempool "github.com/tendermint/tendermint/mempool"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSdkBroadcastTimeout = errors.New("timed out waiting for tx to be committed to block")
|
||||
)
|
||||
|
||||
type KavaMsgRequest struct {
|
||||
Msgs []sdk.Msg
|
||||
GasLimit uint64
|
||||
@ -420,3 +427,35 @@ func Sign(
|
||||
func GetAccAddress(privKey cryptotypes.PrivKey) sdk.AccAddress {
|
||||
return privKey.PubKey().Address().Bytes()
|
||||
}
|
||||
|
||||
// WaitForSdkTxCommit polls the chain until the tx hash is found or times out.
|
||||
// Returns an error immediately if tx hash is empty
|
||||
func WaitForSdkTxCommit(txClient txtypes.ServiceClient, txHash string, timeout time.Duration) (*sdk.TxResponse, error) {
|
||||
if txHash == "" {
|
||||
return nil, fmt.Errorf("tx hash is empty")
|
||||
}
|
||||
var err error
|
||||
var txRes *sdk.TxResponse
|
||||
var res *txtypes.GetTxResponse
|
||||
outOfTime := time.After(timeout)
|
||||
for {
|
||||
select {
|
||||
case <-outOfTime:
|
||||
err = ErrSdkBroadcastTimeout
|
||||
default:
|
||||
res, err = txClient.GetTx(context.Background(), &txtypes.GetTxRequest{Hash: txHash})
|
||||
if err != nil {
|
||||
status, ok := grpcstatus.FromError(err)
|
||||
if ok && status.Code() == codes.NotFound {
|
||||
// tx still not committed to a block. retry!
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
txRes = res.TxResponse
|
||||
}
|
||||
break
|
||||
}
|
||||
return txRes, err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user