Evm
Abstract
This document defines the specification of the Ethereum Virtual Machine (EVM) as a Cosmos SDK module.
Since the introduction of Ethereum in 2015, the ability to control digital assets through [smart contracts] has attracted a large number of developers to build decentralized applications on the Ethereum Virtual Machine (EVM). This community continues to create a wide range of tools and introduce standards, which further increases the adoption rate of EVM-compatible technologies.
However, the growth of EVM-based chains such as Ethereum has revealed some scalability challenges, often referred to as the decentralization, security, and scalability trilemma. Developers are frustrated by high gas fees, slow transaction speeds and throughput, and chain-specific governance that undergoes only slow changes due to their widely deployed applications. A solution is needed to allay these concerns for developers building applications in the familiar EVM environment.
The x/evm
module provides this EVM familiarity on scalable, high-throughput proof-of-stake blockchains.
It is built as a Cosmos SDK module, allowing deployment of smart contracts, interaction with the EVM state machine (state transitions), and use of EVM tools .
It is available for Cosmos application-specific blockchains through Tendermint Core high transaction throughput, fast transaction finality and IBC to alleviate the above problems.
content
Module architecture
Note:: If you are not familiar with the overall module structure of SDK modules, please read this document first as a prerequisite .
evm/
βββ client
β βββ cli
β βββ query.go # Module CLI query command
β βββ tx.go # CLI transaction command of the module
βββkeeper
β βββ abci.go # ABCI start and end block logic
β βββ keeper.go # Process the business logic of the module.
β βββ params.go # parameter getter and setter
β βββ queryer.go # status query function
β βββ statedb.go # Functions from types/statedb passed into sdk.Context
βββ types
β βββ chain_config.go
β βββ codec.go # Encoding type registration
β βββ errors.go # Module-specific errors
β βββ events.go # Events exposed to Tendermint PubSub/Websocket
β βββ genesis.go # Genesis state of the module
β βββ journal.go # Ethereum state transition transactions
β βββ keys.go # Store keys and utility functions
β βββ logs.go # Persistent Ethereum tx login status type after chain upgrade
β βββ msg.go #EVM module transaction message
β βββ params.go # Governance parameters can be used to change suggested custom module parameters
β βββ state_object.go # EVM state object
β βββ statedb.go # Implementation of StateDb interface
β βββ storage.go # Use arrays to implement Ethereum state storage mapping to prevent non-determinism
β βββ tx_data.go # Ethereum transaction data type
βββ genesis.go # ABCI InitGenesis and ExportGenesis functions
βββ handler.go # message routing
βββ module.go # Module settings for the module manager
concept
EVM
The Ethereum Virtual Machine (EVM) is a computing engine that can be thought of as a single entity maintained by thousands of connected computers (nodes) running Ethereum clients. As a virtual machine (VM), the EVM is responsible for computing state changes deterministically, regardless of its environment (hardware and operating system). This means that given the same starting state and transaction (tx), each node must achieve the exact same result.
The EVM is considered part of the Ethereum protocol that handles deployment and execution of smart contracts.
To clearly distinguish between:
- The Ethereum protocol describes a blockchain in which all Ethereum accounts and smart contracts exist. It has only one canonical state (a data structure that holds all accounts) in any given block in the chain.
- However, the EVM is a state machine, which defines the rules for computing a new valid state from one block to another. It is an isolated runtime, which means that code running inside the EVM cannot access the network, file system, or other processes (not external APIs).
The x/evm
module implements the EVM as a Cosmos SDK module.
It allows users to interact with the EVM by submitting Ethereum transactions and executing the contained messages on a given state to induce state transitions.
state
The Ethereum state is a data structure, implemented as a Merkle Patricia Tree, that keeps all accounts on-chain. The EVM makes changes to this data structure, resulting in a new state with a different state root. Therefore, Ethereum can be viewed as a state chain that transitions from one state to another by executing transactions in blocks using the EVM. A new txs block can be described by its block header (parent hash, block number, timestamp, nonce, receipt...).
account
There are two types of accounts that can be stored in the state for a given address:
- Externally Owned Account (EOA): Has nonce (tx counter) and balance
- Smart Contract: with nonce, balance, (immutable) code hash, storage root (another Merkle Patricia Trie)
A smart contract is like a regular account on the blockchain, it also stores executable code in an Ethereum-specific binary format called EVM bytecode. They are usually written in an Ethereum high-level language, such as Solidity, which is compiled to EVM bytecode and deployed on the blockchain by submitting transactions using the Ethereum client.
structure
The EVM operates as a stack-based machine. Its main architectural components include:
- Virtual ROM: Contract code is pulled into this read-only memory when processing txs
- Machine state (volatile): changes as the EVM runs and is cleared after processing each tx
- Program counter (PC)
- Gasββ: track how much gas is used
- stack and memory: computing state changes
- Access account storage (persistent)
State transition of smart contract
Typically, smart contracts expose a public ABI, which is a list of supported ways users can interact with the contract. To interact with a contract and invoke a state transition, a user submits a transaction carrying an arbitrary amount of gas and a data payload formatted according to the ABI, specifying the interaction type and any other parameters. When a tx is received, the EVM executes the EVM bytecode of the smart contract using the tx payload.
Execute EVM bytecode
A contract's EVM bytecode consists of basic operations (addition, multiplication, store, etc.), called opcodes. Each Opcode execution requires gas paid for in tx. Therefore, the EVM is considered quasi-Turing-complete because it allows arbitrary computations, but the amount of computation during contract execution is limited to the amount of gas provided in tx.
The gas cost of each opcode reflects the cost of running these operations on actual computer hardware (e.g. ADD = 3gas
and SSTORE = 100gas
`).
To calculate the gas consumption of a transaction, multiply the gas cost by the gas price, which may vary based on the demand of the network at the time.
If the network is heavily loaded, you may need to pay a higher gas price to execute your transaction.
If the gas limit is reached (gas exception), no changes will be made to the Ethereum state unless the sender's nonce increases and their balance decreases to pay for wasting EVM time.
Smart contracts can also call other smart contracts. Every call to a new contract creates a new EVM instance (including new stack and memory). Each invocation passes the sandbox state to the next EVM. If the gas runs out, all state changes will be discarded. Otherwise, they will be preserved.
For further reading, see:
Daodst implemented as Geth
Daodst includes an implementation of the Ethereum Protocol in Golang (Geth) as a Cosmos SDK module. Geth includes an implementation of the EVM to compute state transitions. Check out the go-ethereum source code to see how the EVM opcodes are implemented. Just as Geth can run as an Ethereum node, Daodst can also run as a node to compute state transitions of the EVM. Daodst supports Geth's standard for compatibility with Web3 and EVM.
JSON-RPC
JSON-RPC is a stateless, lightweight remote procedure call (RPC) protocol. The specification mainly defines several data structures and their processing rules. It's transport-agnostic, as the concepts can be used in the same process, over sockets, over HTTP, or in many different messaging environments. It uses JSON (RFC 4627) as the data format.
** JSON-RPC example: eth_call
**
The JSON-RPC method allows you to execute messages against contracts.
Typically, you need to send a transaction to a Geth node to include it in the mempool, then the nodes propagate to each other, and eventually the transaction is included in a block and executed.
However, eth_call
allows you to send data to a contract and see what happens without committing the transaction.
In the Geth implementation, the call endpoint roughly goes through the following steps:
- The
eth_call
request is converted to call thefunc (s *PublicBlockchainAPI) Call()
function using theeth
namespace Call()
is given the transaction parameters, the block to call and an optional Parameters to modify the state to call. Then it callsDoCall()
.DoCall()
Convert arguments toethtypes.message
, instantiate the EVM and apply the message usingcore.ApplyMessage
ApplyMessage()
Call state transitionTransitionDb()
TransitionDb()
Create()
is a new contract orCall()
is a contractevm.Call()
Run the interpreter evm.interpreter.Run() to execute the message. If the execution fails, the state will revert to the snapshot taken before execution and consume gas.Run()
Execute a loop to execute opcodes.
Status DB
The StateDB
interface from go-ethereum represents the EVM database for full state queries.
EVM state transitions are enabled by this interface, implemented by Keeper
in the x/evm
module.
The implementation of this interface makes Daodst EVM compatible.
Consensus Engine
Applications using the x/evm
module interact with the Tendermint core consensus engine through the Application Blockchain Interface (ABCI).
Together, the application and Tendermint Core form a program that runs a full blockchain and combines business logic with decentralized data storage.
Ethereum transactions submitted to the x/evm
module participate in this consensus process before executing and changing application state.
We encourage understanding the basics of the Tendermint consensus engine in order to understand state transitions in detail.
Transaction Record
In each x/evm
transaction, the result contains the Ethereum log
from the state machine execution, which is used by the JSON-RPC Web3 server for filter queries and processing EVM hooks.
The tx log is stored in temporary storage during tx execution and then emitted via cosmos events after the transaction. They can be queried via gRPC and JSON-RPC.
Block Bloom
bloom is the bloom filter value (in bytes) per block available for filter queries. Block bloom values are stored in transient storage and then emitted via cosmos events during EndBlock processing. They can be queried via gRPC and JSON-RPC.
:::hint
π NOTE: Since they are not stored in state, transaction logs and block blooms are not persisted after upgrades. Users must use an archive node after upgrading to get legacy chain events. :::
state
This section provides an overview of objects stored in the state of the x/evm
module, functionality derived from the go-ethereum StateDB
interface, and its implementation via Keeper and state implementation at genesis.
State object
The x/evm
module maintains objects in the following states:
state
Description | Key | Value | Store | |
---|---|---|---|---|
Code | Smart contract bytecode | []byte{1} + []byte(address) |
[]byte{code} |
KV |
Storage | Smart contract storage | []byte{2} + [32]byte{key} |
[32]byte(value) |
KV |
Block Bloom | Block bloom filter, used to accumulate the bloom filter of the current block and send to the end blocker event. | []byte{1} + []byte(tx.Hash) |
protobuf([]Log) |
Transient |
Tx Index | The index of the current transaction in the current block. | []byte{2} |
BigEndian(uint64) |
Transient |
Log Size | The number of logs emitted so far in the current block. Log index used to determine subsequent logs. | []byte{3} |
BigEndian(uint64) |
Transient |
Gas Used | The amount of gas used by the ethereum message of the current cosmos-sdk tx, which is required when the cosmos-sdk tx contains multiple ethereum messages. | []byte{4} |
BigEndian(uint64) |
Transient |
Status DB
The StateDB
interface is implemented by StateDB
in the x/evm/statedb
module, which represents an EVM database for complete state queries of contracts and accounts.
In the Ethereum protocol, StateDB
is used to store anything in the IAVL tree and is responsible for caching and storing nested states.
// github.com/ethereum/go-ethereum/core/vm/interface.go
type StateDB interface {
CreateAccount(common.Address)
SubBalance(common.Address, *big.Int)
AddBalance(common.Address, *big.Int)
GetBalance(common.Address) *big.Int
GetNonce(common.Address) uint64
SetNonce(common.Address, uint64)
GetCodeHash(common.Address) common.Hash
GetCode(common.Address) []byte
SetCode(common.Address, []byte)
GetCodeSize(common.Address) int
AddRefund(uint64)
SubRefund(uint64)
GetRefund() uint64
GetCommittedState(common.Address, common.Hash) common.Hash
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
Suicide(common.Address) bool
HasSuicided(common.Address) bool
// Exist reports whether the given account exists in state.
// Notably this should also return true for suicided accounts.
Exist(common.Address) bool
// Empty returns whether the given account is empty. Empty
// is defined according to EIP161 (balance = nonce = code = 0).
Empty(common.Address) bool
PrepareAccessList(sender common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
AddressInAccessList(addr common.Address) bool
SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddAddressToAccessList(addr common.Address)
// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
// even if the feature/fork is not active yet
AddSlotToAccessList(addr common.Address, slot common.Hash)
RevertToSnapshot(int)
Snapshot() int
AddLog(*types.Log)
AddPreimage(common.Hash, []byte)
ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error
}
The StateDB
in the x/evm
provides the following functionalities:
CRUD for Ethereum accounts
You can create an EthAccount
instance from a provided address and use createAccount()
to set the value to be stored on the AccountKeeper
.
If an account with the given address already exists, this function also resets any pre-existing code and storage associated with that address.
An account's currency balance can be managed through BankKeeper
, which can be read using GetBalance()
and updated using AddBalance()
and SubBalance()
.
GetBalance()
returns the EVM currency balance of the provided address. The currency name is taken from the module parameters.AddBalance()
adds the given amount to the address currency balance by minting new currency and transferring it to the address. The currency name is taken from the module parameters.SubBalance()
subtracts the given amount from the address balance by transferring currency to escrow accounts and then destroying them. The currency name is taken from the module parameters. This function will perform a no-op if the amount is negative or the user does not have enough funds to transfer.
The nonce (or transaction sequence) can be obtained from the account Sequence
by the authentication module AccountKeeper
.
GetNonce()
retrieves the account with the given address and returns the tx sequence (i.e. the nonce). If the account cannot be found, the function will perform a no-op.SetNonce()
sets the given nonce as sequence of address accounts. If the account does not exist, a new account will be created from this address.
Smart contract bytecode containing arbitrary contract logic is stored on EVMKeeper
and can be queried using GetCodeHash()
, GetCode()
and GetCodeSize()
, and updated using SetCode()
.
GetCodeHash()
gets an account from storage and returns its code hash. Returns an empty code hash if the account does not exist or is not of type EthAccount.GetCode()
returns the code byte array associated with the given address. This function returns nil if the code hash from the account is empty.SetCode()
stores the code byte array to the application KVStore and sets the code hash to the given account. If the code is empty, it will be removed from storage.GetCodeSize()
returns the size of the contract code associated with this object, or zero if none.
The refunded gas needs to be tracked and stored in a separate variable so that it can be subtracted/added to the gas usage value after the EVM execution is complete. The refund value is cleared at the end of each transaction and each block.
AddRefund()
adds the given amount of gas to the refund value in memory.SubRefund()
subtracts the given amount of gas from the refund value in memory. This function will panic if the gas amount is greater than the current refund.GetRefund()
returns the amount of gas available for return after tx execution completes. The value resets to 0 on every transaction.
State is stored on EVMKeeper
.
Can be queried with GetCommittedState()
, GetState()
and updated with SetState()
.
GetCommittedState()
returns the value set in storage for the given key hash. If the key is not registered, this function returns an empty hash.GetState()
returns the in-memory dirty state for the given key hash, If not present, the submitted value is loaded from the KVStore.SetState()
sets the given hash (key, value) as state. This function removes the key from the state if the value hash is empty, The new value is first kept dirty and finally committed to KVStore.
Accounts can also be set to commit suicide. When the contract commits suicide, the account is marked as suicide, and when the code is committed, the storage and account are deleted (starting from the next block).
Suicide()
marks the given account as having committed suicide and clears the account balance in EVM tokens.HasSuicided()
queries the flags in memory to check if the account has been marked as suicidal in the current transaction. Suicided accounts will return non-zero during queries and be "cleared" after the block is committed.
To check if an account exists, use Exist()
and Empty()
.
Exist()
returns true if the given account exists in storage or has been marked as suicidal.Empty()
returns true if the address satisfies the following conditions:- nonce is 0
- Balance of evm currency is 0
- account code hash is empty
EIP2930 functionality
Supports transaction types that contain an access list, addresses and storage key lists that the transaction plans to access. Access list state is kept in memory and discarded after transaction commit.
PrepareAccessList()
handles the preparation steps for performing state transitions on EIP-2929 and EIP-2930. This method should only be called if Yolov3/Berlin/2929+2930 applies to the current number.- Add sender to access list (EIP-2929)
- Add destination to access list (EIP-2929)
- Add precompile to access list (EIP-2929)
- Add optional tx access list content (EIP-2930)
AddressInAccessList()
returns true if the address is registered.SlotInAccessList()
checks if address and slot are registered.AddAddressToAccessList()
adds the given address to the access list. If the address is already in the access list, this function performs a no-op.AddSlotToAccessList()
adds the given (address, slot) to the access list. This function performs a no-op if the address and slot are already in the access list.
Snapshot state and restore functions
The EVM uses state restoration exceptions to handle errors.
Such an exception will undo all changes made to the state in the current call (and all its sub-calls), and the caller can handle the error and not propagate.
You can use Snapshot()
to identify the current state by revision, and RevertToSnapshot()
to revert the state to a given revision to support this feature.
- Snapshot()
creates a new snapshot and returns the identifier.
- RevertToSnapshot(rev)
undoes all modifications up to the snapshot identified by rev
.
Daodst adapted the to support this, it uses a journal list in order to record For all the state modification operations done so far, the snapshot consists of a unique id and index in the log list, to revert to a snapshot it just undoes the log logs after the snapshot index in reverse order.
Ethereum transaction log
With AddLog()
, you can append a given Ethereum Log
to the list of logs associated with the transaction hash saved in the current state.
The function also fills in the tx hash, block hash, tx index and log index fields before setting the log to be stored.
Keeper
The EVM module Keeper
grants access to the state of the EVM module and implements the statedb.Keeper
interface to support StateDB
implementations.
The Keeper contains a storage key that allows the DB to write to a concrete subtree of the multistore that can only be accessed by the EVM module.
Instead of using trie and database for query and persistence (StateDB
implementation), Daodst uses Cosmos' KVStore (key-value store) and Cosmos SDK's Keeper
to facilitate state transitions.
To support interface functionality, it imports 4 modules Keepers:
auth
: CRUD accountbank
: CRUD for accounting (supply) and balancesstaking
: query historical headersfee market
: handle EIP1559 base fee forDynamicFeeTx
After activating theLondon
hard fork on theChainConfig
parameter
type Keeper struct {
// Protobuf codec
cdc codec.BinaryCodec
// Store key required for the EVM Prefix KVStore. It is required by:
// - storing account's Storage State
// - storing account's Code
// - storing Bloom filters by block height. Needed for the Web3 API.
// For the full list, check the module specification
storeKey sdk.StoreKey
// key to access the transient store, which is reset on every block during Commit
transientKey sdk.StoreKey
// module specific parameter space that can be configured through governance
paramSpace paramtypes.Subspace
// access to account state
accountKeeper types.AccountKeeper
// update balance and accounting operations with coins
bankKeeper types.BankKeeper
// access historical headers for EVM state transition execution
stakingKeeper types.StakingKeeper
// fetch EIP1559 base fee and parameters
feeMarketKeeper types.FeeMarketKeeper
// chain ID number obtained from the context's chain id
eip155ChainID *big.Int
// Tracer used to collect execution traces from the EVM transaction execution
tracer string
// trace EVM state transition execution. This value is obtained from the `--trace` flag.
// For more info check https://geth.ethereum.org/docs/dapp/tracing
debug bool
// EVM Hooks for tx post-processing
hooks types.EvmHooks
}
Genesis state
The x/evm
module GenesisState
defines the state required to initialize a chain from a previously exported height.
It contains GenesisAccounts
and module parameters
type GenesisState struct {
// accounts is an array containing the ethereum genesis accounts.
Accounts []GenesisAccount `protobuf:"bytes,1,rep,name=accounts,proto3" json:"accounts"`
// params defines all the parameters of the module.
Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"`
}
Genesis account
The GenesisAccount
type corresponds to an adaptation of the Ethereum GenesisAccount
type.
It defines an account that is initialized in the genesis state.
Its main difference is that the one on Daodst uses a custom Storage
type that uses slices instead of evm State
's map (due to non-determinism), and it does not contain a private key field.
It is also important to note that since the auth
module on the Cosmos SDK manages account state, the Address
field must correspond to an existing EthAccount
(i.e. AccountKeeper
) stored in the auth
module Keeper
.
The address uses EIP55 hexadecimal[format]
on genesis.json
.
type GenesisAccount struct {
// address defines an ethereum hex formatted address of an account
Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
// code defines the hex bytes of the account code.
Code string `protobuf:"bytes,2,opt,name=code,proto3" json:"code,omitempty"`
// storage defines the set of state key values for the account.
Storage Storage `protobuf:"bytes,3,rep,name=storage,proto3,castrepeated=Storage" json:"storage"`
}
state transition
The x/evm
module allows users to submit Ethereum transactions (Tx
) and execute the messages they contain to induce a state transition on a given state.
Users submit transactions on the client side to broadcast them to the network. When a transaction is included in a block during the consensus process, it is executed server-side. We strongly recommend that you learn the basics of the Tendermint consensus engine to learn more about state transitions.
client
:::hint
π This is based on eth_sendTransaction
JSON-RPC
:::
-
A user submits a transaction via one of the available JSON-RPC endpoints using an Ethereum-compatible client or wallet (e.g. Metamask, WalletConnect, Ledger, etc.):
- eth (public) namespace:
eth_sendTransaction
andeth_sendRawTransaction
- Personal (private) namespace:
personal_sendTransaction
- eth (public) namespace:
-
Create a MsgEthereumTx instance after filling the RPC transaction with SetTxDefaults to fill the missing tx parameter with default values
- Use ValidateBasic() to validate the
Tx
field (stateless) Tx
is signed using the key associated with the sender address and the latest Ethereum hard fork (London
,Berlin
, etc.) fromChainConfig
Tx
is constructed from the msg field using the Cosmos Config builderTx
in synchronous mode broadcasting Make sure to wait for theCheckTx
execution response. The application verifies the transaction usingCheckTx()
before adding to the consensus engine's mempool.- The JSON-RPC user receives a response with the
RLP
hash of the transaction field. This hash is different from the default hash used by SDK transactions, which computes asha256
hash of the transaction bytes.
Server
Once a block (containing "Tx") is committed during consensus, it is applied to the application in a series of ABCI messages on the server side.
Each Tx
is handled by the application by calling RunTx
.
After stateless verification of each sdk.Msg
in Tx
, AnteHandler
confirms whether Tx
is an Ethereum transaction or an SDK transaction.
As an Ethereum transaction, it contains messages, which are then processed by the x/evm
module to update the state of the application.
Preprocessor
anteHandler
runs for every transaction.
It checks whether Tx
is an Ethereum transaction and routes it to the internal ante handler.
Here, Tx
is handled using EthereumTx extension options to handle them differently than normal Cosmos SDK transactions.
antehandler
runs through a list of options and its AnteHandle
function for each Tx
:
EthSetUpContextDecorator()
adapted from cosmos-sdk's SetUpContextDecorator which ignores gas consumption by setting the gas meter to infiniteEthValidateBasicDecorator(evmKeeper)
validates fields of Ethereum type CosmosTx
messagesEthSigVerificationDecorator(evmKeeper)
verifies that the registered chain ID is the same as on the message, and that the signer address matches that defined on the message. RecheckTx won't skip it because it sets theFrom
address, which is essential for other ante handlers to work. A failure of RecheckTx will prevent the tx from being included into the block, especially when CheckTx is successful, in which case the user will not see the error message.EthAccountVerificationDecorator(ak, bankKeeper, evmKeeper)
Will verify that the sender balance is greater than the total transaction cost. If the account does not exist, i.e. cannot be found in the store, the account will be set into the store. This AnteHandler decorator will fail if:- Any message is not MsgEthereumTx
- sender address is empty
- The account balance is lower than the transaction cost
EthNonceVerificationDecorator(ak)
verifies that the transaction nonce is valid and equal to the sender account's current nonce.EthGasConsumeDecorator(evmKeeper)
verifies that the Ethereum tx message is sufficient to pay for inherent gas (during CheckTx only) and that the sender has enough balance to cover the gas cost. The intrinsic gas of a transaction is the amount of gas used by the transaction before execution. Gas is a constant value plus any cost incurred by the extra data bytes provided by the transaction. This AnteHandler decorator will fail if:- Transaction contains multiple messages
- Message is not MsgEthereumTx
- No sender account found
- the gas limit of the transaction is lower than the intrinsic gas
- User does not have enough balance to deduct transaction fee (gas_limit * gas_price)
- Transaction or block gas meter runs out of gas
CanTransferDecorator(evmKeeper, feeMarketKeeper)
creates an EVM from the message and calls the BlockContext CanTransfer function to see if the address can perform a transaction.EthIncrementSenderSequenceDecorator(ak)
handles the sequence of incrementing signers (i.e. senders). If the transaction is a contract creation, the nonce will be incremented during transaction execution, not within this AnteHandler decorator.
The options authante.NewMempoolFeeDecorator()
, authante.NewTxTimeoutHeightDecorator()
and authante.NewValidateMemoDecorator(ak)
are the same as Cosmos Tx
.
Click here to learn more about anteHandler
.
EVM module
Each sdk.Msg
in Tx
(in this case MsgEthereumTx
) after being authenticated by antehandler
Passed to the Msg Handler in the x/evm
module and run through the following steps:
- Convert
Msg
to Ethereum'sTx
type - Apply
Tx
withEVMConfig
and attempt to perform a state transition, only persisting (committing) to the underlying KVStore if the transaction has not failed:- Confirm that
EVMConfig
is created - Create an Ethereum signer using the chain configuration values from "EVMConfig"
- Set the Ethereum transaction hash to (temporary) temporary storage so that it can also be used for StateDB functions
- Generate a new EVM instance
- Verify that the EVM parameters for contract creation (
EnableCreate
) and contract execution (EnableCall
) are enabled- App message. If the
To
address isnil
, create a new contract with the code as the deployed code. Else invokes the contract at the given address with the given input as arguments - Calculate the gas used by the evm operation
- App message. If the
- Confirm that
- If the
Tx
application is successful- Execute the EVM
Tx
post-processing hook. If the hook returns an error, restore the entire "Tx"- Return gas according to the Ethereum gas bookkeeping rules
- Update block bloom filter value with log generated from tx
- Emit SDK events for transaction fields and tx logs
- Execute the EVM
trade
This section defines the sdk.Msg
concrete types that cause the state transitions defined in the previous section.
MsgEthereumTx
EVM state transitions can be achieved by using "MsgEthereumTx".
This message encapsulates the Ethereum transaction data (TxData
) as sdk.Msg
.
It contains the necessary transaction data fields.
Note that MsgEthereumTx
implements sdk.Msg
and sdk .Tx
interface.
Typically, SDK messages only implement the former, while the latter is a set of messages bundled together.
This section defines the sdk.Msg
concrete types that cause the state transitions defined in the previous section.
MsgEthereumTx
EVM state transitions can be achieved using MsgEthereumTx
.
This message encapsulates the Ethereum transaction data (TxData
) as sdk.Msg
.
It contains the necessary transaction data fields.
Note that MsgEthereumTx
implements sdk.Msg
and sdk .Tx
interface.
Typically, SDK messages only implement the former, while the latter is a set of messages bundled together.
type MsgEthereumTx struct {
// inner transaction data
Data *types.Any `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
// DEPRECATED: encoded storage size of the transaction
Size_ float64 `protobuf:"fixed64,2,opt,name=size,proto3" json:"-"`
// transaction hash in hex format
Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty" rlp:"-"`
// ethereum signer address in hex format. This address value is checked
// against the address derived from the signature (V, R, S) using the
// secp256k1 elliptic curve
From string `protobuf:"bytes,4,opt,name=from,proto3" json:"from,omitempty"`
}
This message field validation is expected to fail if:
- The
From
field is defined and the address is invalid TxData
stateless validation failed
Transaction execution is expected to fail if:
- Any custom
AnteHandler
ethereum decorator check fails:- Minimum gas requirement for transactions
- Tx sender account does not exist or does not have sufficient fee balance
- account sequence does not match transaction
Data.AccountNonce
- message signature verification failed
- EVM contract creation (i.e.
evm.Create
) failed, orevm.Call
failed
Conversion
MsgEthreumTx
can be converted to go-ethereum Transaction
and Message
types
In order to create and call evm contracts.
// AsTransaction creates an Ethereum Transaction type from the msg fields
func (msg MsgEthereumTx) AsTransaction() *ethtypes.Transaction {
txData, err := UnpackTxData(msg.Data)
if err != nil {
return nil
}
return ethtypes. NewTx(txData. AsEthereumData())
}
// AsMessage returns the transaction as a core.Message.
func (tx *Transaction) AsMessage(s Signer, baseFee *big.Int) (Message, error) {
msg := Message{
nonce: tx.Nonce(),
gasLimit: tx. Gas(),
gasPrice: new(big.Int).Set(tx.GasPrice()),
gasFeeCap: new(big.Int).Set(tx.GasFeeCap()),
gasTipCap: new(big.Int).Set(tx.GasTipCap()),
to: tx.To(),
amount: tx. Value(),
data: tx.Data(),
accessList: tx.AccessList(),
isFake: false,
}
// If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil {
msg.gasPrice = math.BigMin(msg.gasPrice.Add(msg.gasTipCap, baseFee), msg.gasFeeCap)
}
var err error
msg.from, err = Sender(s, tx)
return msg, err
}
sign
For signature verification to work, TxData
must contain the v|r|s
value from Signer
.
Sign computes the secp256k1 ECDSA signature and signs the transaction.
According to the EIP155 standard, it requires a keyring signer and chainID to sign Ethereum transactions.
This method mutates the transaction when filling the V, R, S fields of the transaction signature.
The function will fail if no sender address is defined for the message or if the sender is not registered on the keyring.
// Sign calculates a secp256k1 ECDSA signature and signs the transaction. It
// takes a keyring signer and the chainID to sign an Ethereum transaction according to
// EIP155 standard.
// This method mutates the transaction as it populates the V, R, S
// fields of the Transaction's Signature.
// The function will fail if the sender address is not defined for the msg or if
// the sender is not registered on the keyring
func (msg *MsgEthereumTx) Sign(ethSigner ethtypes.Signer, keyringSigner keyring.Signer) error {
from := msg.GetFrom()
if from.Empty() {
return fmt.Errorf("sender address not defined for message")
}
tx := msg.AsTransaction()
txHash := ethSigner.Hash(tx)
sig, _, err := keyringSigner.SignByAddress(from, txHash.Bytes())
if err != nil {
return err
}
tx, err = tx.WithSignature(ethSigner, sig)
if err != nil {
return err
}
msg.FromEthereumTx(tx)
return nil
}
TxData
MsgEthereumTx
supports 3 valid Ethereum transaction data types from go-ethereum:
LegacyTx
, AccessListTx
, and DynamicFeeTx
.
These types are defined as protobuf messages and packed into the proto.Any
interface type in the MsgEthereumTx
field.
LegacyTx
: EIP-155 transaction typeDynamicFeeTx
: EIP-1559 transaction type.AccessListTx
: EIP-2930 transaction type.
LegacyTx
Transaction data for regular Ethereum transactions.
type LegacyTx struct {
// nonce corresponds to the account nonce (transaction sequence).
Nonce uint64 `protobuf:"varint,1,opt,name=nonce,proto3" json:"nonce,omitempty"`
// gas price defines the value for each gas unit
GasPrice *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,2,opt,name=gas_price,json=gasPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_price,omitempty"`
// gas defines the gas limit defined for the transaction.
GasLimit uint64 `protobuf:"varint,3,opt,name=gas,proto3" json:"gas,omitempty"`
// hex formatted address of the recipient
To string `protobuf:"bytes,4,opt,name=to,proto3" json:"to,omitempty"`
// value defines the unsigned integer value of the transaction amount.
Amount *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,5,opt,name=value,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"value,omitempty"`
// input defines the data payload bytes of the transaction.
Data []byte `protobuf:"bytes,6,opt,name=data,proto3" json:"data,omitempty"`
// v defines the signature value
V []byte `protobuf:"bytes,7,opt,name=v,proto3" json:"v,omitempty"`
// r defines the signature value
R []byte `protobuf:"bytes,8,opt,name=r,proto3" json:"r,omitempty"`
// s define the signature value
S []byte `protobuf:"bytes,9,opt,name=s,proto3" json:"s,omitempty"`
}
This message field validation is expected to fail if:
GasPrice
is invalid (nil
, negative or out of range of int256)Fee
(gasprice * gaslimit) is invalidAmount
is invalid (negative or out of range of int256)- Invalid
To
address (invalid Ethereum hex address)
DynamicFeeTx
Transaction data for EIP-1559 dynamic fee transactions.
type DynamicFeeTx struct {
// destination EVM chain ID
ChainID *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"chainID"`
// nonce corresponds to the account nonce (transaction sequence).
Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"`
// gas tip cap defines the max value for the gas tip
GasTipCap *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=gas_tip_cap,json=gasTipCap,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_tip_cap,omitempty"`
// gas fee cap defines the max value for the gas fee
GasFeeCap *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,4,opt,name=gas_fee_cap,json=gasFeeCap,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_fee_cap,omitempty"`
// gas defines the gas limit defined for the transaction.
GasLimit uint64 `protobuf:"varint,5,opt,name=gas,proto3" json:"gas,omitempty"`
// hex formatted address of the recipient
To string `protobuf:"bytes,6,opt,name=to,proto3" json:"to,omitempty"`
// value defines the the transaction amount.
Amount *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,7,opt,name=value,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"value,omitempty"`
// input defines the data payload bytes of the transaction.
Data []byte `protobuf:"bytes,8,opt,name=data,proto3" json:"data,omitempty"`
Accesses AccessList `protobuf:"bytes,9,rep,name=accesses,proto3,castrepeated=AccessList" json:"accessList"`
// v defines the signature value
V []byte `protobuf:"bytes,10,opt,name=v,proto3" json:"v,omitempty"`
// r defines the signature value
R []byte `protobuf:"bytes,11,opt,name=r,proto3" json:"r,omitempty"`
// s define the signature value
S []byte `protobuf:"bytes,12,opt,name=s,proto3" json:"s,omitempty"`
}
This message field validation is expected to fail if:
GasTipCap
is invalid (nil
, negative value or overflow int256)GasFeeCap
is invalid (nil
, negative value or overflow int256)GasFeeCap
is less thanGasTipCap
Fee
(gas price * gas limit) is invalid (overflow int256)- Invalid
Amount
(negative or overflow int256) - Invalid
To
address (invalid Ethereum hex address) ChainID
isnil
AccessListTx
EIP-2930 Access transaction data for list transactions.
type AccessListTx struct {
// destination EVM chain ID
ChainID *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"chainID"`
// nonce corresponds to the account nonce (transaction sequence).
Nonce uint64 `protobuf:"varint,2,opt,name=nonce,proto3" json:"nonce,omitempty"`
// gas price defines the value for each gas unit
GasPrice *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,3,opt,name=gas_price,json=gasPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"gas_price,omitempty"`
// gas defines the gas limit defined for the transaction.
GasLimit uint64 `protobuf:"varint,4,opt,name=gas,proto3" json:"gas,omitempty"`
// hex formatted address of the recipient
To string `protobuf:"bytes,5,opt,name=to,proto3" json:"to,omitempty"`
// value defines the unsigned integer value of the transaction amount.
Amount *github_com_cosmos_cosmos_sdk_types.Int `protobuf:"bytes,6,opt,name=value,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Int" json:"value,omitempty"`
// input defines the data payload bytes of the transaction.
Data []byte `protobuf:"bytes,7,opt,name=data,proto3" json:"data,omitempty"`
Accesses AccessList `protobuf:"bytes,8,rep,name=accesses,proto3,castrepeated=AccessList" json:"accessList"`
// v defines the signature value
V []byte `protobuf:"bytes,9,opt,name=v,proto3" json:"v,omitempty"`
// r defines the signature value
R []byte `protobuf:"bytes,10,opt,name=r,proto3" json:"r,omitempty"`
// s define the signature value
S []byte `protobuf:"bytes,11,opt,name=s,proto3" json:"s,omitempty"`
}
This message field validation is expected to fail if:
GasPrice
is invalid (nil
, negative, or overflow int256)Fee
(gas price * gas limit) is invalid (overflow int256)- Invalid
Amount
(negative or overflow int256) - Invalid
To
address (invalid Ethereum hex address) ChainID
isnil
ABCI
The Application Blockchain Interface (ABCI) allows applications to interact with the Tendermint consensus engine.
The application maintains multiple ABCI connections to Tendermint.
Most relevant to x/evm
is Consensus Connection at Commit.
This connection is responsible for block execution and calls functions InitChain
(contains InitGenesis
), BeginBlock
, DeliverTx
, EndBlock
, Commit
.
InitChain
is only called the first time a new blockchain is started, and DeliverTx
is called for every transaction in a block.
Initialize Genesis
InitGenesis
initializes the EVM module genesis state by setting the GenesisState
field to storage.
In particular, it sets parameters and genesis accounts (state and code).
Export Genesis
The ExportGenesis
ABCI function exports the genesis state of an EVM module.
In particular, it retrieves all accounts and their bytecodes, balances and storage, transaction logs,
As well as EVM parameters and chain configuration.
Begin Block
The EVM module BeginBlock
logic is executed before the state transitions that process transactions.
The main goals of this function are:
- Set the context of the current block for block headers, storage, gas meters, etc. Once one of the StateDB functions is called during an EVM state transition, these functions are available to the Keeper.
- Set the EIP155
ChainID
number (obtained from the full chain ID) in case it was not previously set duringInitChain
End block
The EndBlock
logic of the EVM module occurs after all state transitions of the transaction have been executed.
The main goals of this function are:
- emit block bloom event
- This is due to web3 compatibility, since the ethereum header includes this type as a field. The JSON-RPC service uses this event query to construct Ethereum headers from Tendermint headers.
- The block bloom filter value is fetched from transient storage and then emitted
hooks
The x/evm
module implements an EvmHooks
interface, which can extend and customize Tx
processing logic externally.
This supports EVM contracts calling native cosmos modules
- Define log signatures and emit specific logs from smart contracts,
- Identify these logs in the native tx handling code, and
- Convert them to native module calls.
For this, the interface contains a PostTxProcessing hook to register custom Tx hooks in EvmKeeper.
These Tx
hooks are processed after the EVM state transition is complete and will not fail.
Note that there are no default hooks implemented in the EVM module.
type EvmHooks interface {
// Must be called after tx is processed successfully, if return an error, the whole transaction is reversed.
PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error
}
PostTxProcessing
PostTxProcessing
is only called after the EVM transaction has completed successfully, and delegates the call to the underlying hook.
If no hooks are registered, this function returns a nil
error. .
func (k *Keeper) PostTxProcessing(ctx sdk.Context, msg core.Message, receipt *ethtypes.Receipt) error {
if k.hooks == nil {
return nil
}
return k.hooks.PostTxProcessing(k.Ctx(), msg, receipt)
}
It executes in the same cache context as the EVM transaction, if it returns an error, the whole EVM transaction will be reverted, if hook implementors don't want to revert tx, they can always return nil
.
Errors returned by hooks are translated to VM errors Unable to process native log
, with detailed error messages stored in the return value.
Messages are sent asynchronously to native modules, and callers cannot catch and recover from errors.
** Use case: calling native ERC20 modules on Daodst **
The following is an example taken from the Daodst [erc20 module] showing how EVMHooks
support contracts that call native modules to convert ERC-20 tokens to Cosmos native tokens.
Follow the steps above.
You can define and emit a "Transfer" log signature in your smart contract like this:
event Transfer(address indexed from, address indexed to, uint256 value);
function _transfer(address sender, address recipient, uint256 amount) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
The application will register a BankSendHook
with EvmKeeper
.
It recognizes the Ethereum tx Log
and translates it into a call to the SendCoinsFromAccountToAccount
method of the bank module:
const ERC20EventTransfer = "Transfer"
// PostTxProcessing implements EvmHooks.PostTxProcessing
func (k Keeper) PostTxProcessing(
ctx sdk.Context,
msg core.Message,
receipt *ethtypes.Receipt,
) error {
params := h.k.GetParams(ctx)
if !params.EnableErc20 || !params.EnableEVMHook {
// no error is returned to allow for other post-processing txs
// to pass
return nil
}
erc20 := contracts.ERC20BurnableContract.ABI
for i, log := range receipt.Logs {
if len(log.Topics) < 3 {
continue
}
eventID := log.Topics[0] // event ID
event, err := erc20.EventByID(eventID)
if err != nil {
// invalid event for ERC20
continue
}
if event.Name != types.ERC20EventTransfer {
h.k.Logger(ctx).Info("emitted event", "name", event.Name, "signature", event.Sig)
continue
}
transferEvent, err := erc20.Unpack(event.Name, log.Data)
if err != nil {
h.k.Logger(ctx).Error("failed to unpack transfer event", "error", err.Error())
continue
}
if len(transferEvent) == 0 {
continue
}
tokens, ok := transferEvent[0].(*big.Int)
// safety check and ignore if amount not positive
if !ok || tokens == nil || tokens.Sign() != 1 {
continue
}
// check that the contract is a registered token pair
contractAddr := log.Address
id := h.k.GetERC20Map(ctx, contractAddr)
if len(id) == 0 {
// no token is registered for the caller contract
continue
}
pair, found := h.k.GetTokenPair(ctx, id)
if !found {
continue
}
// check that conversion for the pair is enabled
if !pair.Enabled {
// continue to allow transfers for the ERC20 in case the token pair is disabled
h.k.Logger(ctx).Debug(
"ERC20 token -> Cosmos coin conversion is disabled for pair",
"coin", pair.Denom, "contract", pair.Erc20Address,
)
continue
}
// ignore as the burning always transfers to the zero address
to := common.BytesToAddress(log.Topics[2].Bytes())
if !bytes.Equal(to.Bytes(), types.ModuleAddress.Bytes()) {
continue
}
// check that the event is Burn from the ERC20Burnable interface
// NOTE: assume that if they are burning the token that has been registered as a pair, they want to mint a Cosmos coin
// create the corresponding sdk.Coin that is paired with ERC20
coins := sdk.Coins{{Denom: pair.Denom, Amount: sdk.NewIntFromBigInt(tokens)}}
// Mint the coin only if ERC20 is external
switch pair.ContractOwner {
case types.OWNER_MODULE:
_, err = h.k.CallEVM(ctx, erc20, types.ModuleAddress, contractAddr, true, "burn", tokens)
case types.OWNER_EXTERNAL:
err = h.k.bankKeeper.MintCoins(ctx, types.ModuleName, coins)
default:
err = types.ErrUndefinedOwner
}
if err != nil {
h.k.Logger(ctx).Debug(
"failed to process EVM hook for ER20 -> coin conversion",
"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
)
continue
}
// Only need last 20 bytes from log.topics
from := common.BytesToAddress(log.Topics[1].Bytes())
recipient := sdk.AccAddress(from.Bytes())
// transfer the tokens from ModuleAccount to sender address
if err := h.k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, coins); err != nil {
h.k.Logger(ctx).Debug(
"failed to process EVM hook for ER20 -> coin conversion",
"tx-hash", receipt.TxHash.Hex(), "log-idx", i,
"coin", pair.Denom, "contract", pair.Erc20Address, "error", err.Error(),
)
continue
}
}
return nil
Finally, register the hook in app.go:
app.EvmKeeper = app.EvmKeeper.SetHooks(app.Erc20Keeper)
event
The x/evm
module emits Cosmos SDK events after state execution.
The EVM module emits events related to transaction fields, as well as transaction logs (Ethereum events)γ
MsgEthereumTx
Type | Property Key | Property Value |
---|---|---|
ethereum_tx | "amount" |
{amount} |
ethereum_tx | "recipient" |
{hex_address} |
ethereum_tx | "contract" |
{hex_address} |
ethereum_tx | "txHash" |
{tendermint_hex_hash} |
ethereum_tx | "ethereumTxHash" |
{hex_hash} |
ethereum_tx | "txIndex" |
{tx_index} |
ethereum_tx | "txGasUsed" |
{gas_used} |
tx_log | "txLog" |
{tx_log} |
message | "sender" |
{eth_address} |
message | "action" |
"ethereum" |
message | "module" |
"evm" |
Additionally, the EVM module emits an event for the filter query block bloom during EndBlock
.
ABCI
Type | Property Key | Property Value |
---|---|---|
block_bloom | "bloom" |
string(bloomBytes) |
parameters
The evm module contains the following parameters:
Module parameters
Key | Data Type | Default |
---|---|---|
EVMDenom |
string | "dst" |
EnableCreate |
bool | true |
EnableCall |
bool | true |
ExtraEIPs |
[]int | TBD |
ChainConfig |
ChainConfig | See ChainConfig |
:::hint
Note: SDK applications want to import the EVM module as a dependency
Will need to set own evm_denom
(i.e. not "dst"
).
:::
EnableCreate
The EnableCreate parameter toggles state transitions using the vm.Create
function.
When this parameter is disabled, it blocks all contract creation functions.
EnableCall
The EnableCall parameter toggles state transitions using the vm.Call
function.
When this parameter is disabled, it prevents transfers between accounts and execution of smart contract calls.
Extra EIPs
The additional EIPs parameter defines a set of Ethereum Improvement Proposals that can be activated on the Ethereum VM Config
applying a custom jump table (EIPs ).
:::hint Note: Some of these EIPs are already enabled via chain configuration, depending on the number of hard forks. :::
The supported activatable EIPS are:
Chain Config
ChainConfig
is a protobuf wrapper type that contains the same fields as the go-ethereum ChainConfig
parameter, but uses *sdk.Int
type instead of *big.Int
.
By default, all block configuration fields except ConstantinopleBlock
are enabled at Genesis (height 0).
Default ChainConfig
name | default |
---|---|
HomesteadBlock | 0 |
DAOForkBlock | 0 |
DAOForkSupport | true |
EIP150Block | 0 |
EIP150Hash | 0x0000000000000000000000000000000000000000000000000000000000000000 |
EIP155Block | 0 |
EIP158Block | 0 |
ByzantiumBlock | 0 |
ConstantinopleBlock | 0 |
PetersburgBlock | 0 |
IstanbulBlock | 0 |
MuirGlacierBlock | 0 |
BerlinBlock | 0 |
LondonBlock | 0 |
ArrowGlacierBlock | 0 |
GrayGlacierBlock | 0 |
MergeNetsplitBlock | 0 |
ShanghaiBlock | 0 |
CancunBlock. | 0 |
client
Users can query and interact with the evm
module using CLI, JSON-RPC, gRPC, or REST.
CLI
Below find a list of stcd
commands added using the x/evm
module.
You can get a complete list with stcd -h
command.
Inquire
The query
command allows the user to query the state of evm
.
code
Allows users to query the smart contract code for a given address.
stcd query evm code ADDRESS [flags]
# Example
$ stcd query evm code 0x7bf7b17da59880d9bcca24915679668db75f9397
# Output
code: "0xef616c92f3cfc9e92dc270d6acff9cea213cecc7020a76ee4395af09bdceb4837a1ebdb5735e11e7d3adb6104e0c3ac55180b4ddf5e54d022cc5e8837f6a4f971b"
storage
Allows users to query the store for accounts with a given key and heightγ
stcd query evm storage ADDRESS KEY [flags]
# Example
$ stcd query evm storage 0x0f54f47bf9b8e317b214ccd6a7c3e38b893cd7f0 0 --height 0
# Output
value: "0x0000000000000000000000000000000000000000000000000000000000000000"
trade
The tx
command allows the user to interact with the evm
module.
raw
Allows users to build cosmos transactions from raw Ethereum transactions.
stcd tx evm raw TX_HEX [flags]
# Example
$ stcd tx evm raw 0xf9ff74c86aefeb5f6019d77280bbb44fb695b4d45cfe97e6eed7acd62905f4a85034d5c68ed25a2e7a8eeb9baf1b84
# Output
value: "0x0000000000000000000000000000000000000000000000000000000000000000"
gRPC
Inquire
Verb | Method | Description |
---|---|---|
gRPC |
ethermint.evm.v1.Query/Account |
Get Ethereum account |
gRPC |
ethermint.evm.v1.Query/CosmosAccount |
Get the Cosmos address of an Ethereum account |
gRPC |
ethermint.evm.v1.Query/ValidatorAccount |
Get Ethereum account from validator consensus address |
gRPC |
ethermint.evm.v1.Query/Balance |
Get the EVM denomination balance of a single EthAccount. |
gRPC |
ethermint.evm.v1.Query/Storage |
Get the balance of all coins in a single account |
gRPC |
ethermint.evm.v1.Query/Code |
Get the balance of all coins in a single account |
gRPC |
ethermint.evm.v1.Query/Params |
Get the parameters of the x/evm module |
gRPC |
ethermint.evm.v1.Query/EthCall |
implements the eth_call rpc api |
gRPC |
ethermint.evm.v1.Query/EstimateGas |
implements the eth_estimateGas rpc api |
gRPC |
ethermint.evm.v1.Query/TraceTx |
implement debug_traceTransaction rpc api |
gRPC |
ethermint.evm.v1.Query/TraceBlock |
implements debug_traceBlockByNumber and debug_traceBlockByHash rpc api |
GET |
/ethermint/evm/v1/account/{address} |
Get Ethereum account |
GET |
/ethermint/evm/v1/cosmos_account/{address} |
Get the Cosmos address of the Ethereum account |
GET |
/ethermint/evm/v1/validator_account/{cons_address} |
Get Ethereum account from validator consensus address |
GET |
/ethermint/evm/v1/balances/{address} |
Get the EVM denomination balance of a single EthAccount. |
GET |
/ethermint/evm/v1/storage/{address}/{key} |
Get the balance of all coins in a single account |
GET |
/ethermint/evm/v1/codes/{address} |
Get the balance of all coins in a single account |
GET |
/ethermint/evm/v1/params |
Get the parameters of the x/evm module |
GET |
/ethermint/evm/v1/eth_call |
implements eth_call rpc api |
GET |
/ethermint/evm/v1/estimate_gas |
implements the eth_estimateGas rpc api |
GET |
/ethermint/evm/v1/trace_tx |
implements debug_traceTransaction rpc api |
GET |
/ethermint/evm/v1/trace_block |
implements debug_traceBlockByNumber and debug_traceBlockByHash rpc api |
trade
Verb | Method | Description |
---|---|---|
gRPC |
ethermint.evm.v1.Msg/EthereumTx |
Submit an Ethereum transaction |
POST |
/ethermint/evm/v1/ethereum_tx |
Submit an Ethereum transaction |