EVM Gateway enables seamless interaction with EVM on Flow, mirroring the experience of engaging with any other EVM blockchain.
EVM Gateway implements the Ethereum JSON-RPC API for EVM on Flow which conforms to the Ethereum JSON-RPC specification. EVM Gateway is specifically designed to integrate with the EVM environment on the Flow blockchain. Rather than implementing the full geth
stack, the JSON-RPC API available in EVM Gateway is a lightweight implementation which uses Flow's underlying consensus and smart contract language, Cadence, to handle calls received by the EVM Gateway. For those interested in the underlying implementation details please refer to the FLIP #243 (EVM Gateway) and FLIP #223 (EVM on Flow Core) improvement proposals.
EVM Gateway is compatible with the majority of standard Ethereum JSON-RPC APIs allowing seamless integration with existing Ethereum-compatible web3 tools via HTTP. EVM Gateway honors Ethereum's JSON-RPC namespace system, grouping RPC methods into categories based on their specific purpose. Each method name is constructed using the namespace, an underscore, and the specific method name in that namespace. For example, the eth_call
method is located within the eth
namespace. See below for details on methods currently supported or planned.
The basic design of the EVM Gateway consists of a couple of components:
- Event Ingestion Engine: this component listens to all Cadence events that are emitted by the EVM core, which can be identified by the special event type ID
evm.TransactionExecuted
andevm.BlockExecuted
and decodes and index the data they contain in the payloads. - Flow Requester: this component knows how to submit transactions to Flow AN to change the EVM state. What happens behind the scenes is that EVM gateway will receive an EVM transaction payload, which will get wrapped in a Cadence transaction that calls EVM contract with that payload and then the EVM core will execute the transaction and change the state.
- JSON RPC API: this is the client API component that implements all the API according to the JSON RPC API specification.
EVM Gateway also supports the standard Ethereum JSON-RPC event subscription and filters, enabling callers to subscribe to state logs, blocks or pending transactions changes.
- TODO more coming
Operating an EVM Gateway is straightforward. It can either be deployed locally alongside the Flow emulator or configured to connect with any active Flow networks supporting EVM. Given that the EVM Gateway depends solely on Access Node APIs, it is compatible with any networks offering this API access.
Start Emulator
In order to run the gateway locally you need to start the emulator with EVM enabled:
flow emulator --evm-enabled
Make sure flow.json has the emulator account configured to address and private key we will use for starting gateway bellow.
Then you need to start the gateway:
go run cmd/main/main.go \
--flow-network-id flow-emulator \
--coinbase FACF71692421039876a5BB4F10EF7A439D8ef61E \
--coa-address f8d6e0586b0a20c7 \
--coa-key 2619878f0e2ff438d17835c2a4561cb87b4d24d72d12ec34569acd0dd4af7c21 \
--coa-resource-create \
--gas-price 0
Note that the gateway will be starting from the latest emulator block, so if emulator is run before and transactions happen in the meantime, the gateway will not fetch those historical blocks & transactions. This will be improved soon.
In this example we use coa-address
value set to service account of the emulator, same as coa-key
.
This account will by default be funded with Flow which is a requirement. For coinbase
we can
use whichever valid EVM address. It's not really useful for local running beside collecting fees. We provide also the
coa-resource-create
to auto-create resources needed on start-up on the coa
account in order to operate gateway.
gas-price
is set at 0 so we don't have to fund EOA accounts. We can set it higher but keep in mind you will then
need funded accounts for interacting with EVM.
With Docker
Run the following commands:
cd dev
docker build -t onflow/flow-evm-gateway .
docker run -d -p 127.0.0.1:8545:8545 onflow/flow-evm-gateway
To verify the service is up and running:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
it should return:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x2"
}
The application can be configured using the following flags at runtime:
Flag | Default Value | Description |
---|---|---|
--database-dir |
./db |
Path to the directory for the database. |
--rpc-host |
localhost |
Host for the JSON RPC API server. |
--rpc-port |
8545 |
Port for the JSON RPC API server. |
--ws-enabled |
false |
Enable websocket support. |
--access-node-grpc-host |
localhost:3569 |
Host to the current spork Flow access node (AN) gRPC API. |
--access-node-spork-hosts |
Previous spork AN hosts, defined following the schema: {latest height}@{host} as comma separated list (e.g. "[email protected],[email protected]" ) |
|
--evm-network-id |
testnet |
EVM network ID (options: testnet , mainnet ). |
--flow-network-id |
emulator |
Flow network ID (options: emulator , previewnet ). |
--coinbase |
(required) | Coinbase address to use for fee collection. |
--init-cadence-height |
0 | Define the Cadence block height at which to start the indexing. |
--gas-price |
1 |
Static gas price used for EVM transactions. |
--coa-address |
(required) | Flow address that holds COA account used for submitting transactions. |
--coa-key |
(required) | WARNING: Do not use this flag in production! Private key value for the COA address used for submitting transactions. |
--coa-key-file |
File path that contains JSON array of COA keys used in key-rotation mechanism, this is exclusive with coa-key flag. |
|
--coa-resource-create |
false |
Auto-create the COA resource in the Flow COA account provided if one doesn't exist. |
--log-level |
debug |
Define verbosity of the log output ('debug', 'info', 'error') |
--stream-limit |
10 | Rate-limits the events sent to the client within one second |
--stream-timeout |
3sec | Defines the timeout in seconds the server waits for the event to be sent to the client |
--filter-expiry |
5m |
Filter defines the time it takes for an idle filter to expire |
To start using EVM Gateway, ensure you have the required dependencies installed and then run the application with your desired configuration flags. For example:
./evm-gateway --rpc-host "127.0.0.1" --rpc-port 3000 --database-dir "/path/to/database"
For more detailed information on configuration and deployment, refer to the Configuration and Deployment sections.
EVM Gateway has public RPC endpoints available for the following environments:
Name | Value |
---|---|
Network Name | Migrationnet |
Description | The public RPC URL for Flow Migrationnet |
RPC Endpoint | https://evm-001.migrationtestnet1.nodes.onflow.org |
Chain ID | 646 |
Currency Symbol | FLOW |
Block Explorer | / |
Name | Value |
---|---|
Network Name | Previewnet |
Description | The public RPC URL for Flow Previewnet |
RPC Endpoint | https://previewnet.evm.nodes.onflow.org |
Chain ID | 646 |
Currency Symbol | FLOW |
Block Explorer | https://previewnet.flowdiver.io |
Name | Value |
---|---|
Network Name | Testnet |
Description | The public RPC URL for Flow Testnet |
RPC Endpoint | https://testnet.evm.nodes.onflow.org |
Chain ID | Coming Soon |
Currency Symbol | FLOW |
Block Explorer | https://testnet.flowdiver.io |
Name | Value |
---|---|
Network Name | Mainnet |
Description | The public RPC URL for Flow Mainnet |
RPC Endpoint | https://mainnet.evm.nodes.onflow.org |
Chain ID | 747 |
Currency Symbol | FLOW |
Block Explorer | https://flowdiver.io |
Listed below are the JSON-RPC namespaces and methods currently supported by the EVM Gateway:
eth
- Supported
- eth_chainId
- eth_blockNumber
- eth_coinbase
- eth_getLogs
- eth_getTransactionCount
- eth_getTransactionReceipt
- eth_getBlockByNumber
- eth_call
- eth_sendRawTransaction
- eth_getTransactionByHash
- eth_getCode
- eth_gasPrice
- eth_feeHistory
- eth_getBalance
- eth_estimateGas
- eth_getTransactionByBlockNumberAndIndex
- eth_getTransactionByBlockHashAndIndex
- eth_getBlockByHash
- eth_getBlockReceipts
- eth_getBlockTransactionCountByHash
- eth_getBlockTransactionCountByNumber
- eth_newFilter
- eth_uninstallFilter
- eth_getFilterChanges
- eth_getFilterLogs
- eth_newBlockFilter
- eth_newPendingTransactionFilter
- eth_getUncleCountByBlockHash
- eth_getUncleCountByBlockNumber
- eth_getUncleByBlockHashAndIndex
- eth_getUncleByBlockNumberAndIndex
- eth_maxPriorityFeePerGas
- eth_syncing
- Unsupported but coming soon
- eth_createAccessList
- eth_getProof
- eth_getStorageAt
- eth_accounts
- eth_sign
- eth_signTransaction
- eth_sendTransaction
- Supported
web3
- Supported
- web3_clientVersion
- web_sha3
- Supported
net
- Supported
- net_listening
- net_peerCount
- net_version
- Supported
debug
- Supported
- debug_traceTransaction
- debug_traceBlockByNumber
- debug_traceBlockByHash
- Supported
We also plan to add support for the admin
namespace in the near future.
Following is a list of pre-made JSON-RPC API calls, using curl
:
Deploy a smart contract:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xb9015f02f9015b82029a80808083124f808080b901086060604052341561000f57600080fd5b60eb8061001d6000396000f300606060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063c6888fa1146044575b600080fd5b3415604e57600080fd5b606260048080359060200190919050506078565b6040518082815260200191505060405180910390f35b60007f24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da600783026040518082815260200191505060405180910390a16007820290509190505600a165627a7a7230582040383f19d9f65246752244189b02f56e8d0980ed44e7a56c0b200458caad20bb0029c001a0c0dfc8ed68e5e2522006ac4dc7b1c0c783328311b3e860658be1bab16728ed98a048ef3ec7ff2f13ebda39f01526254e3c7ab64feb0c574703555dba316b6a88e3"],"id":1}'
Run some smart contract function calls:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xb88c02f88982029a01808083124f809499466ed2e37b892a2ee3e9cd55a98b68f5735db280a4c6888fa10000000000000000000000000000000000000000000000000000000000000006c001a0f84168f821b427dc158c4d8083bdc4b43e178cf0977a2c5eefbcbedcc4e351b0a066a747a38c6c266b9dc2136523cef04395918de37773db63d574aabde59c12eb"],"id":2}'
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xb88c02f88982029a02808083124f809499466ed2e37b892a2ee3e9cd55a98b68f5735db280a4c6888fa10000000000000000000000000000000000000000000000000000000000000007c080a0409f5baa7622655f76f7f67c1bd8cc4cdd5a8e64fab881d78c47d9d73e907befa04981b65452d66beeb80527f3e8d3d0bf18aa21cfbd9a0c5ee749192d43d72d95"],"id":3}'
Make a read-only smart contract function call:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_call","params":[{"from":"0x658bdf435d810c91414ec09147daa6db62406379","to":"0x99466ed2e37b892a2ee3e9cd55a98b68f5735db2","gas":"0x76c0","gasPrice":"0x9184e72a000","value":"0x0","input":"0xc6888fa10000000000000000000000000000000000000000000000000000000000000006"}],"id":4}'
Retrieve EVM logs based on a topic:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getLogs","params":[{"topics":["0x24abdb5865df5079dcc5ac590ff6f01d5c16edbc5fab4e195d9febd1114503da"]}],"id":5}'
Retrieve the nonce (transaction count) of an EVM address:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0x658Bdf435d810C91414eC09147DAA6DB62406379","latest"],"id":6}'
Retrieve block info given its number:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x3",false],"id":7}'
Retrieve block info given its hash:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getBlockByHash","params":["0xf0cb48c7561fb6cab8b065b12bf4cf966a6840b41db81055bf4cd98d2354228f",false],"id":8}'
Retrieve the transaction receipt given its hash:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0xaae4530246e61ae58479824ab0863f99ca50414d27aec0c269ae6a7cfc4c7f5b"],"id":9}'
Retrieve transaction info given its hash:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getTransactionByHash","params":["0xaae4530246e61ae58479824ab0863f99ca50414d27aec0c269ae6a7cfc4c7f5b"],"id":10}'
Retrieve the balance of an EVM address:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x000000000000000000000002f239b1ccbbaa9977","latest"],"id":11}'
Retrieve the number of transactions in a block, given its hash:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getBlockTransactionCountByHash","params":["0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c"],"id":12}'
Retrieve the number of transactions in a block, given its number:
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getBlockTransactionCountByNumber","params":["0x2"],"id":13}'
Retrieve transaction info given its block hash and index
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getTransactionByBlockHashAndIndex","params":["0xb47d74ea64221eb941490bdc0c9a404dacd0a8573379a45c992ac60ee3e83c3c","0x0"],"id":14}'
Retrieve transaction info given its block number and index
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getTransactionByBlockNumberAndIndex","params":["0x3","0x0"],"id":15}'
Retrieve all the transaction receipts given a block number
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_getBlockReceipts","params":["0x3"],"id":16}'
Retrieve the latest block number
curl -XPOST 'localhost:8545' --header 'Content-Type: application/json' --data-raw '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":17}'
We welcome contributions from the community! Please read our Contributing Guide for information on how to get involved.
EVM Gateway is released under the Apache License 2.0 license. See the LICENSE file for more details.