package keeper_test import ( _ "embed" "encoding/json" "math/big" "testing" "time" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "github.com/tharsis/ethermint/app" "github.com/tharsis/ethermint/crypto/ethsecp256k1" "github.com/tharsis/ethermint/encoding" "github.com/tharsis/ethermint/server/config" "github.com/tharsis/ethermint/tests" ethermint "github.com/tharsis/ethermint/types" "github.com/tharsis/ethermint/x/evm/types" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/tmhash" tmproto "github.com/tendermint/tendermint/proto/tendermint/types" tmversion "github.com/tendermint/tendermint/proto/tendermint/version" "github.com/tendermint/tendermint/version" ) var ( //go:embed ERC20Contract.json compiledContractJSON []byte ContractBin []byte ContractABI abi.ABI ) func init() { var tmp struct { Abi string Bin string } err := json.Unmarshal(compiledContractJSON, &tmp) if err != nil { panic(err) } ContractBin = common.FromHex(tmp.Bin) err = json.Unmarshal([]byte(tmp.Abi), &ContractABI) if err != nil { panic(err) } } var testTokens = sdk.NewIntWithDecimal(1000, 18) type KeeperTestSuite struct { suite.Suite ctx sdk.Context app *app.EthermintApp queryClient types.QueryClient address common.Address consAddress sdk.ConsAddress // for generate test tx clientCtx client.Context ethSigner ethtypes.Signer appCodec codec.Codec signer keyring.Signer } /// DoSetupTest setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`. func (suite *KeeperTestSuite) DoSetupTest(t require.TestingT) { checkTx := false // account key priv, err := ethsecp256k1.GenerateKey() require.NoError(t, err) suite.address = common.BytesToAddress(priv.PubKey().Address().Bytes()) suite.signer = tests.NewSigner(priv) // consensus key priv, err = ethsecp256k1.GenerateKey() require.NoError(t, err) suite.consAddress = sdk.ConsAddress(priv.PubKey().Address()) suite.app = app.Setup(checkTx) suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{ Height: 1, ChainID: "ethermint_9000-1", Time: time.Now().UTC(), ProposerAddress: suite.consAddress.Bytes(), Version: tmversion.Consensus{ Block: version.BlockProtocol, }, LastBlockId: tmproto.BlockID{ Hash: tmhash.Sum([]byte("block_id")), PartSetHeader: tmproto.PartSetHeader{ Total: 11, Hash: tmhash.Sum([]byte("partset_header")), }, }, AppHash: tmhash.Sum([]byte("app")), DataHash: tmhash.Sum([]byte("data")), EvidenceHash: tmhash.Sum([]byte("evidence")), ValidatorsHash: tmhash.Sum([]byte("validators")), NextValidatorsHash: tmhash.Sum([]byte("next_validators")), ConsensusHash: tmhash.Sum([]byte("consensus")), LastResultsHash: tmhash.Sum([]byte("last_result")), }) suite.app.EvmKeeper.WithContext(suite.ctx) queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) suite.queryClient = types.NewQueryClient(queryHelper) acc := ðermint.EthAccount{ BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0), CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(), } suite.app.AccountKeeper.SetAccount(suite.ctx, acc) valAddr := sdk.ValAddress(suite.address.Bytes()) validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{}) err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) require.NoError(t, err) err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) require.NoError(t, err) suite.app.StakingKeeper.SetValidator(suite.ctx, validator) encodingConfig := encoding.MakeConfig(app.ModuleBasics) suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) suite.appCodec = encodingConfig.Marshaler } func (suite *KeeperTestSuite) SetupTest() { suite.DoSetupTest(suite.T()) } func (suite *KeeperTestSuite) EvmDenom() string { ctx := sdk.WrapSDKContext(suite.ctx) rsp, _ := suite.queryClient.Params(ctx, &types.QueryParamsRequest{}) return rsp.Params.EvmDenom } // Commit and begin new block func (suite *KeeperTestSuite) Commit() { _ = suite.app.Commit() header := suite.ctx.BlockHeader() header.Height += 1 suite.app.BeginBlock(abci.RequestBeginBlock{ Header: header, }) // update ctx suite.ctx = suite.app.BaseApp.NewContext(false, header) suite.app.EvmKeeper.WithContext(suite.ctx) queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) suite.queryClient = types.NewQueryClient(queryHelper) } // DeployTestContract deploy a test erc20 contract and returns the contract address func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner common.Address, supply *big.Int) common.Address { ctx := sdk.WrapSDKContext(suite.ctx) chainID := suite.app.EvmKeeper.ChainID() ctorArgs, err := ContractABI.Pack("", owner, supply) require.NoError(t, err) data := append(ContractBin, ctorArgs...) args, err := json.Marshal(&types.CallArgs{ From: &suite.address, Data: (*hexutil.Bytes)(&data), }) require.NoError(t, err) res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{ Args: args, GasCap: uint64(config.DefaultGasCap), }) require.NoError(t, err) nonce := suite.app.EvmKeeper.GetNonce(suite.address) erc20DeployTx := types.NewTxContract( chainID, nonce, nil, // amount res.Gas, // gasLimit nil, // gasPrice data, // input nil, // accesses ) erc20DeployTx.From = suite.address.Hex() err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer) require.NoError(t, err) rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx) require.NoError(t, err) require.Empty(t, rsp.VmError) return crypto.CreateAddress(suite.address, nonce) } func (suite *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAddr common.Address, from common.Address, to common.Address, amount *big.Int) *types.MsgEthereumTx { ctx := sdk.WrapSDKContext(suite.ctx) chainID := suite.app.EvmKeeper.ChainID() transferData, err := ContractABI.Pack("transfer", to, amount) require.NoError(t, err) args, err := json.Marshal(&types.CallArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)}) require.NoError(t, err) res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{ Args: args, GasCap: 25_000_000, }) require.NoError(t, err) nonce := suite.app.EvmKeeper.GetNonce(suite.address) ercTransferTx := types.NewTx( chainID, nonce, &contractAddr, nil, res.Gas, nil, transferData, nil, ) ercTransferTx.From = suite.address.Hex() err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer) require.NoError(t, err) rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, ercTransferTx) require.NoError(t, err) require.Empty(t, rsp.VmError) return ercTransferTx } func TestKeeperTestSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) }