Japanese version: /ja/doc/introduction.html
What is Kasane
TL;DR
Kasane is a project that provides EVM execution on ICP canisters.
It exposes two public access paths: the canister Candid API and a Gateway JSON-RPC endpoint.
Its JSON-RPC is not full Ethereum compatibility; it is a restricted compatibility layer for development workflows.
The compatibility target is Ethereum JSON-RPC plus EVM execution semantics, while OP/Superchain compatibility is out of scope.
What You Can Do
- Use Ethereum-style JSON-RPC for basic reads and transaction submission
- Call EVM functionality directly through canister query/update methods
- Pull
export_blocksin an indexer and persist data to Postgres
Key Constraints (Current Implementation)
- Ethereum JSON-RPC compatibility is partial (see compatibility pages for unsupported methods)
- Node operation workflows are out of scope for this book
Compatibility Positioning
- Explicit target: restricted Ethereum JSON-RPC compatibility
- Non-goal: OP Stack / Superchain compatibility
- Important: success of
eth_sendRawTransactionmeans submit success only; execution success must be checked with receiptstatus=0x1
Intended Readers
- EVM dApp developers
- Smart contract developers
- Backend integration developers
- Indexer developers
Sources
README.md(operational summary and compatibility policy)tools/rpc-gateway/README.md(Gateway compatibility matrix)crates/ic-evm-wrapper/evm_canister.did(public interface)
Source of Truth for Compatibility
- Canonical JSON-RPC policy:
./rpc/overview.md - Detailed method differences:
./compatibility/json-rpc-deviations.md
Japanese version: /ja/doc/quickstart.html
Quickstart (Gateway + Candid)
TL;DR
- Default network is public testnet (
chain_id=4801360, RPC:https://rpc-testnet.kasane.network). - There are two paths: Path A (Gateway JSON-RPC) / Path B (direct canister Candid).
- Do not use submit response as final success signal; use
eth_getTransactionReceipt.status. tx_idandeth_tx_hashare different identifiers.- For deploy/call, standardize on signed raw-tx submission flow.
What You Can / Cannot Do
You Can
- Verify connectivity (chain id / block number)
- Send native transfers (submit signed raw tx)
- Monitor receipt (success/failure)
- Read state via query methods (balance/code/storage/call/estimate)
You Cannot (Current)
- Use Ethereum-standard pending/mempool workflows (
eth_pendingTransactions, etc.) - Note: with direct canister calls, per-transaction tracking is possible via
get_pending(tx_id) - Use WebSocket subscriptions (
eth_subscribe) - Use full-compatible
eth_getLogs(filter constraints apply)
For compatibility details, see ./rpc/overview.md and ./compatibility/json-rpc-deviations.md.
Prerequisites
- Public RPC:
https://rpc-testnet.kasane.network - chain id:
4801360 - canister id (current testnet value):
4c52m-aiaaa-aaaam-agwwa-cai dfx(if using canister query/update path)- Signed raw tx if sending through Gateway
Path A: Gateway JSON-RPC
1) Connectivity Check
RPC_URL="https://rpc-testnet.kasane.network"
curl -s -X POST "$RPC_URL" -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'
curl -s -X POST "$RPC_URL" -H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":2,"method":"eth_blockNumber","params":[]}'
Expected:
eth_chainIdreturns0x4944d0(decimal4801360)eth_blockNumberreturns a0x...value
2) Transfer (Signed Raw Tx)
RAW_TX="0x<signed_raw_tx_hex>"
curl -s -X POST "$RPC_URL" -H 'content-type: application/json' \
--data "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"eth_sendRawTransaction\",\"params\":[\"$RAW_TX\"]}"
Expected:
resultreturns0x...tx hash (Gateway resolvestx_idtoeth_tx_hash)
3) Receipt Monitoring (Success Determination)
TX_HASH="0x<tx_hash_from_send>"
curl -s -X POST "$RPC_URL" -H 'content-type: application/json' \
--data "{\"jsonrpc\":\"2.0\",\"id\":4,\"method\":\"eth_getTransactionReceipt\",\"params\":[\"$TX_HASH\"]}"
Interpretation:
status == 0x1: execution succeededstatus == 0x0: execution failed (submit can succeed while execution fails)result == null: not yet mined/included
Typical Errors
-32602 invalid params: invalid argument format-32001 resource not found: pruned boundary or missing target-32000 state unavailable: migration/corrupt state, etc.
Pitfalls
- Treating
eth_sendRawTransactionsuccess as completion - Confusing
tx_idwitheth_tx_hash
Path B: Direct canister Candid call
1) Connectivity Check (query)
CANISTER_ID="4c52m-aiaaa-aaaam-agwwa-cai"
NETWORK="ic"
dfx canister call --network "$NETWORK" --query "$CANISTER_ID" rpc_eth_chain_id '( )'
dfx canister call --network "$NETWORK" --query "$CANISTER_ID" rpc_eth_block_number '( )'
2) submit_ic_tx (IcSynthetic)
submit_ic_tx accepts a Candid record with:
to: opt vec nat8(Somemust be 20 bytes,Nonemeans create)value: natgas_limit: nat64nonce: nat64max_fee_per_gas: natmax_priority_fee_per_gas: natdata: vec nat8
In IcSynthetic, payload does not include from.
The wrapper injects msg_caller() and canister_self() and passes it to core as TxIn::IcSynthetic to derive sender.
# Example: to=0x...01 / value=0 / gas_limit=500000 / data=""
# Note: set fee at or above current minimum acceptance conditions (min_gas_price/min_priority_fee).
TO_BYTES="$(python - <<'PY'
to = bytes.fromhex('0000000000000000000000000000000000000001')
print('; '.join(str(b) for b in to))
PY
)"
dfx canister call --network "$NETWORK" "$CANISTER_ID" submit_ic_tx "(record {
to = opt vec { $TO_BYTES };
value = 0 : nat;
gas_limit = 500000 : nat64;
nonce = 0 : nat64;
max_fee_per_gas = 500000000000 : nat;
max_priority_fee_per_gas = 250000000000 : nat;
data = vec { };
})"
2.0) submit_ic_tx Send Procedure
Fixing this order reduces operational mistakes.
- Verify chain/network before sending
- Ensure
rpc_eth_chain_idis4801360
- Ensure
- Check sender nonce
- Call
expected_nonce_by_address(20 bytes)and get current nonce
- Call
- Decide fee/gas
- Refer to
rpc_eth_gas_price/rpc_eth_max_priority_fee_per_gas; set values at or above minimum
- Refer to
- Send
submit_ic_tx(record)once- Store returned
tx_id
- Store returned
- Track execution result
- Check status with
get_pending(tx_id) - Once
get_receipt(tx_id)is available, determine success/failure bystatus
- Check status with
Notes:
submit_ic_txsuccess means “accepted”. Determine execution success fromreceipt.status.tx_idis an internal key and different frometh_tx_hash.
2.1) submit_ic_tx Validation Flow (Important)
- pre-submit guard
- reject anonymous (
auth.anonymous_forbidden) - reject writes during migration/cycle state (
ops.write.*)
- reject anonymous (
- decode/validation
- payload size/format
- sender derivation failure:
arg.principal_to_evm_derivation_failed - invalid fee condition:
submit.invalid_fee - nonce mismatch:
submit.nonce_too_low/submit.nonce_gap/submit.nonce_conflict
- on success, returns
tx_id; mining is asynchronous (auto-production)
3) Nonce Retrieval
ADDR_BLOB='"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00"'
dfx canister call --network "$NETWORK" --query "$CANISTER_ID" expected_nonce_by_address "(blob $ADDR_BLOB)"
Notes:
expected_nonce_by_addressaccepts only 20-byte addresses- 32-byte input (for example mistaken bytes32-encoded principal) returns an explicit error
4) Raw tx submission (EthSigned)
Use existing helper eth_raw_tx to create raw tx bytes:
CHAIN_ID=4801360
PRIVKEY="<YOUR_PRIVKEY_HEX>"
RAW_TX_BYTES=$(cargo run -q -p ic-evm-core --features local-signer-bin --bin eth_raw_tx -- \
--mode raw \
--privkey "$PRIVKEY" \
--to "0000000000000000000000000000000000000001" \
--value "0" \
--gas-price "500000000000" \
--gas-limit "21000" \
--nonce "0" \
--chain-id "$CHAIN_ID")
dfx canister call --network "$NETWORK" "$CANISTER_ID" rpc_eth_send_raw_transaction "(vec { $RAW_TX_BYTES })"
5) tx_id Tracking (IcSynthetic)
submit_ic_tx returns internal key tx_id, not eth_tx_hash.
Track with:
get_pending(tx_id)(Queued/Included/Dropped/Unknown)get_receipt(tx_id)(execution result)
6) eth_sendRawTransaction Return Caveat (Gateway)
- Normally returns
eth_tx_hash. - But if internal
tx_id -> eth_tx_hashresolution fails, returns-32000(submit succeeded but eth hash is unavailable).
Pitfalls
- Calling query methods as update and failing
- Violating 20-byte address requirement (
expected_nonce_by_address) - Treating
submit_ic_txtx_idaseth_tx_hash - Treating
submit_ic_txsuccess as execution success
Deploy & Call (Operational Procedure)
Prerequisites
- Bytecode has been built (
datafield set to deploy bytecode) - Signing environment is ready (private key / matching chain id)
- Nonce and fee are pre-fetched and configured
Execution Flow
- Estimate deploy tx gas using
eth_estimateGas. - Build deploy raw tx and submit via
eth_sendRawTransaction. - Track returned
eth_tx_hashwitheth_getTransactionReceipt. - Confirm deployed address appears in
receipt.contractAddress.
Sources
README.mdtools/rpc-gateway/README.mddocs/api/rpc_eth_send_raw_transaction_payload.mdcrates/evm-core/src/test_bin/eth_raw_tx.rscrates/ic-evm-wrapper/evm_canister.didtools/rpc-gateway/src/handlers.tscrates/ic-evm-wrapper/src/lib.rs(submit_ic_tx,expected_nonce_by_address)crates/evm-core/src/chain.rs(TxIn::IcSynthetic, submit validation)
Japanese version: /ja/doc/concepts/accounts-keys.html
Accounts & Keys
TL;DR
- Sender derivation path differs between
EthSignedandIcSynthetic. expected_nonce_by_addressassumes a 20-byte address.IcSyntheticdoes not takefromin payload; sender is derived from caller context.
What You Can Do
- Use sender recovered from Ethereum signed tx
- Use principal-derived sender (
IcSynthetic)
Constraint
- Treating bytes32-like values as 20-byte addresses is invalid.
Difference Between EthSigned and IcSynthetic
EthSigned- Recovers sender from signed raw tx
- Includes chain id validation
IcSynthetic- Wrapper injects
msg_caller()andcanister_self()and submits asTxIn::IcSynthetic - Payload has no
from - Sender derivation failure yields
AddressDerivationFailed-class errors
- Wrapper injects
Must-Know Points for IcSynthetic
- Input to
submit_ic_txis Candidrecord(to/value/gas_limit/nonce/max_fee_per_gas/max_priority_fee_per_gas/data) - Canonical stored bytes use
to_flag(0/1)representation - Use
expected_nonce_by_addressfor nonce - Return value is
tx_id, noteth_tx_hash - Execution finality is decided from later block receipts, not submit return
Safe Usage
- Resolve the caller’s 20-byte address
- Fetch nonce via
expected_nonce_by_address - Send
submit_ic_tx - Track with
get_pending(tx_id)/get_receipt(tx_id)
For canonical pending/mempool policy, see ../rpc/overview.md.
Pitfalls
- Submitting principal-encoded values directly as address
- Passing non-20-byte value to
expected_nonce_by_address - Treating
tx_idaseth_tx_hash - Treating submit success as execution success
Sources
crates/ic-evm-wrapper/src/lib.rs(expected_nonce_by_address)crates/evm-core/src/tx_decode.rs(IcSynthetic/EthSigned)crates/evm-core/src/chain.rs(TxIn::IcSynthetic)README.md
Japanese version: /ja/doc/concepts/transactions-fees.html
Transactions & Fees
TL;DR
- Supported tx types: Legacy/2930/1559. 4844/7702 are unsupported.
- Fee model is base fee + priority fee (with constraints).
Fee Model
compute_next_base_feeupdates next block base feecompute_effective_gas_pricecomputes effective price
Pitfalls
- Sending tx with mismatched chain id
- Violating priority/max fee consistency constraints
Sources
crates/evm-core/src/tx_decode.rscrates/evm-core/src/base_fee.rscrates/evm-core/src/revm_exec.rs
Japanese version: /ja/doc/concepts/blocks-receipts-logs.html
Blocks, Receipts, Logs
TL;DR
- Blocks/receipts/logs are exposed via Candid types.
- Receipt includes
status,gas_used,effective_gas_price,contract_address, andlogs.
Key Points
logs[].logIndexis the in-block ordinal index- receipt lookup can return
Found/NotFound/PossiblyPruned/Pruned
Pitfalls
- Designing as if pruned history is always permanently queryable
- Mixing
tx_idandeth_tx_hashlookup paths
Sources
crates/ic-evm-wrapper/evm_canister.didcrates/ic-evm-rpc/src/lib.rstools/rpc-gateway/src/handlers.ts
Japanese version: /ja/doc/concepts/finality-reorg.html
Finality & Reorg
TL;DR
- Current implementation assumes a single block producer (sequencer), not a reorg-first model.
- Submit and execute are separated, so receipt monitoring is required.
Behavior
eth_sendRawTransactionsubmits tx- Execution finality appears in subsequent blocks
Cautions
- Treating submit success as state finality
- Bringing Ethereum L1 fork/reorg assumptions directly into integration logic
Sources
README.mdtools/rpc-gateway/README.mdcrates/ic-evm-wrapper/src/lib.rs
Japanese version: /ja/doc/compatibility/ethereum-differences.html
Ethereum Differences
TL;DR
- Compatibility target is Ethereum JSON-RPC plus EVM execution semantics (not full parity).
- Accepted tx types: Legacy / EIP-2930 / EIP-1559. EIP-4844 and EIP-7702 are currently rejected.
- Kasane does not add or override opcodes; it follows the active
revmspec (currently PRAGUE). - Some pending/mempool APIs are not implemented (
eth_pendingTransactions, etc.). - The canister Candid API provides per-transaction tracking via
get_pending(tx_id). - The runtime assumes a single block producer (sequencer), not a reorg-driven model.
Note: the canonical pending/mempool policy is ../rpc/overview.md.
Scope
Supported
- Submit, execute, and read receipts for Ethereum-signed transactions
eth_call/eth_estimateGas(with restrictions)- Read blocks/transactions/receipts/logs
Restricted Areas
- 4844 blob tx / 7702 authorization tx are currently unsupported
- mempool, filter, and WebSocket subscriptions are partially supported or unsupported
Transaction Compatibility
- Supported
- Legacy (RLP)
- EIP-2930 (
tx_type=1) - EIP-1559 (
tx_type=2)
- Currently rejected
- EIP-4844 (
type=0x03) - EIP-7702 (
type=0x04)
- EIP-4844 (
Opcode Differences
- Kasane does not introduce custom opcode behavior.
- Effective opcode set depends on
revmspec selection; current default isPRAGUE.
Fee Model Differences
base_feeis persisted and updated bycompute_next_base_fee.effective_gas_priceis derived frommax_fee,max_priority_fee, andbase_fee.eth_gasPricereturnsmax(base_fee + max(estimated_priority, min_priority), min_gas_price).
Finality/Reorg Differences
- Assumes a single block producer (sequencer).
- Blocks after
auto-productionare treated as final. - Some tags such as
latest/pending/safe/finalizedeffectively map to head-level behavior.
Common Errors
DecodeError::UnsupportedType(4844/7702)DecodeError::WrongChainIdDecodeError::LegacyChainIdMissing
Pitfalls
- Assuming Ethereum L1 pending/finality behavior applies as-is
- Sending 4844/7702 tx assuming compatibility
Sources
crates/evm-core/src/tx_decode.rscrates/evm-core/tests/phase1_eth_decode.rscrates/evm-core/src/base_fee.rscrates/evm-core/src/revm_exec.rsvendor/revm/crates/handler/src/mainnet_builder.rsvendor/revm/crates/primitives/src/hardfork.rstools/rpc-gateway/README.mdREADME.md
Japanese version: /ja/doc/compatibility/json-rpc-deviations.html
JSON-RPC Deviations
TL;DR
- Implemented methods are intentionally limited.
eth_getLogsis constrained;blockHashis conditionally supported, andtopics[0]OR arrays are supported.- blockTag support depends on method.
latestfamily plusearliest/QUANTITYare accepted, but historical support is limited. eth_sendRawTransactiondelegates to submit API; execution success must be confirmed from receipt.eth_feeHistoryis supported (blockCountaccepts number/QUANTITY/decimal string).eth_gasPricereturns an acceptance-oriented estimate, not rawbase_fee.
Scope: this page documents JSON-RPC-level differences. Canonical overall policy is ../rpc/overview.md.
Method-Level Differences (Highlights)
eth_getBalance- Accepts
latest/pending/safe/finalized/earliest/QUANTITY - Historical queries often return
exec.state.unavailableorinvalid.block_range.out_of_window
- Accepts
eth_getTransactionCount- Accepts
latest/pending/safe/finalized/earliest/QUANTITY pendingreturns pending nonceearliestand historical nonce are currently unavailable
- Accepts
eth_getCode- Accepts
latest/pending/safe/finalized/earliest/QUANTITY - Historical queries are generally unavailable
- Accepts
eth_getStorageAt- Accepts
latest/pending/safe/finalized/earliest/QUANTITY - slot accepts QUANTITY or 32-byte DATA
- Historical queries are generally unavailable
- Accepts
eth_call,eth_estimateGas- Accepts
latest/pending/safe/finalized/earliest/QUANTITY - Historical execution is unavailable (
exec.state.unavailable/invalid.block_range.out_of_window) - Unsupported fields return
-32602
- Accepts
eth_getLogsblockHashis conditionally supported (cannot combine withfromBlock/toBlock; resolved by scanning recent N blocks)- single
addressonly topics[0]OR array supported (max 16)topics[1+]conditions unsupported- oversized range returns
-32005
eth_feeHistoryblockCountacceptsnumber/QUANTITY/ decimal stringblockCount <= 256pendingis currently treated aslatest
eth_maxPriorityFeePerGas- returns
-32000(state unavailable) when observed data is insufficient
- returns
eth_gasPrice- returns
max(base_fee + max(estimated_priority, min_priority), min_gas_price)
- returns
Unsupported Methods
eth_getBlockByHasheth_newFilter/eth_getFilterChanges/eth_uninstallFiltereth_subscribe/eth_unsubscribeeth_pendingTransactions
Error Design
-32602: invalid params-32000: state unavailable / execution failed-32001: resource not found (including pruned cases)-32005: limit exceeded (logs)- Note: internal submit failures can surface as
-32603on specific paths
Pitfalls
- Reusing standard node
eth_getLogsfilters as-is (address[]ortopics[1+]) - Treating
eth_sendRawTransactionsuccess as final execution success - Assuming
blockHashineth_getLogsis always resolvable (scan window is bounded)
Sources
tools/rpc-gateway/src/handlers.tstools/rpc-gateway/README.mdcrates/ic-evm-rpc/src/lib.rs
Japanese version: /ja/doc/compatibility/precompiles-system-contracts.html
Precompiles & System Contracts
TL;DR
- Precompile failures are classified as
exec.halt.precompile_error. - Kasane uses
revmmainnet precompile set without custom address overrides. - Current default is
SpecId::default = PRAGUE, so OSAKA-added0x0100 (P256VERIFY)is not enabled by default. - Opcode-level discussion is out of scope for this page (see
ethereum-differences.md).
Operational Policy
- Use
exec.halt.precompile_erroras the primary classifier for precompile failures. - Smoke-test precompile-dependent paths before mainnet rollout.
Supported Precompiles (Current Default: PRAGUE)
| Address | Name | Introduced In | Notes |
|---|---|---|---|
0x01 | ECREC | Homestead | secp256k1 ecrecover |
0x02 | SHA256 | Homestead | |
0x03 | RIPEMD160 | Homestead | |
0x04 | ID | Homestead | identity |
0x05 | MODEXP | Byzantium | Berlin gas formula applies under Prague |
0x06 | BN254_ADD | Byzantium | gas-updated since Istanbul |
0x07 | BN254_MUL | Byzantium | gas-updated since Istanbul |
0x08 | BN254_PAIRING | Byzantium | gas-updated since Istanbul |
0x09 | BLAKE2F | Istanbul | |
0x0a | KZG_POINT_EVALUATION | Cancun | enabled in current build (c-kzg / blst / arkworks) |
0x0b | BLS12_G1ADD | Prague | EIP-2537 |
0x0c | BLS12_G1MSM | Prague | EIP-2537 |
0x0d | BLS12_G2ADD | Prague | EIP-2537 |
0x0e | BLS12_G2MSM | Prague | EIP-2537 |
0x0f | BLS12_PAIRING_CHECK | Prague | EIP-2537 |
0x10 | BLS12_MAP_FP_TO_G1 | Prague | EIP-2537 |
0x11 | BLS12_MAP_FP2_TO_G2 | Prague | EIP-2537 |
Added in OSAKA but disabled by current default
0x0100(P256VERIFY, RIP-7212)
Implementation Assumptions
evm-coreusesContext::mainnet().build_mainnet_with_inspector(...)and directly relies onrevmmainnet builder.- Precompile set is selected via
EthPrecompiles::new(spec)on the mainnet builder side. SpecId::default()isPRAGUE.
Observable Facts
- Execution error taxonomy includes
PrecompileError. - Wrapper maps it to
exec.halt.precompile_error.
Safe Usage
- For precompile-dependent features, monitor/classify
exec.halt.precompile_errorseparately from retry logic. - For precompile-critical dApps, smoke-test the exact path before mainnet rollout.
Pitfalls
- Mixing ingress validation responsibility with runtime precompile responsibility
- Assuming
0x0100 (P256VERIFY)is always enabled - Ignoring backend differences for
0x0a (KZG)(c-kzg/blst/arkworks)
Related Page
ethereum-differences.md(opcode/tx type/finality differences)
Sources
crates/ic-evm-wrapper/src/lib.rs(exec.halt.precompile_error)crates/evm-core/src/revm_exec.rscrates/evm-core/Cargo.toml(revmfeature)vendor/revm/crates/handler/src/mainnet_builder.rsvendor/revm/crates/primitives/src/hardfork.rsvendor/revm/crates/precompile/src/lib.rsvendor/revm/crates/precompile/src/id.rsvendor/revm/crates/precompile/src/bls12_381_const.rsvendor/revm/crates/precompile/src/secp256r1.rs
Japanese version: /ja/doc/rpc/overview.html
RPC Overview
TL;DR
- Gateway is the layer that translates canister Candid API into JSON-RPC 2.0.
- Compatibility is restricted, and some methods are not implemented.
- Invalid input mainly returns
-32602; state inconsistency mainly returns-32000/-32001.
Scope
- A subset of
web3_*,net_*, andeth_* - Implemented methods are canonically defined by the
handleRpcswitch
Main Use Cases and Constraints
- Supports basic reads,
call,estimate, and raw transaction submission - filter/ws/pending have compatibility differences (see dedicated pages)
Pending/Mempool Policy
- Ethereum-compatible pending/mempool APIs (for example
eth_pendingTransactions,eth_subscribe) are currently not implemented. - However, Candid provides
get_pending(tx_id)for per-transaction tracking after submit. - Determine execution success from receipt (
status), not submit return value. - See:
../quickstart.md../compatibility/json-rpc-deviations.md../compatibility/ethereum-differences.md../concepts/accounts-keys.md../_generated/interfaces.md
Sources
tools/rpc-gateway/src/handlers.ts(handleRpc)tools/rpc-gateway/README.mdcrates/ic-evm-wrapper/src/lib.rs(get_pending)
Japanese version: /ja/doc/rpc/chain-and-block.html
Chain & Block Methods
TL;DR
- Provides
eth_chainId,net_version,eth_blockNumber, andeth_getBlockByNumber. eth_getBlockByNumberreturns-32001in pruned ranges.
Methods
eth_chainId-> canisterrpc_eth_chain_idnet_version-> decimal string conversion ofrpc_eth_chain_ideth_blockNumber-> canisterrpc_eth_block_numbereth_getBlockByNumber-> canisterrpc_eth_get_block_by_number_with_status
blockTag Constraints
latest/pending/safe/finalizedare treated as head-level tags- pruned data returns
resource not found
Sources
tools/rpc-gateway/src/handlers.tscrates/ic-evm-rpc/src/lib.rs
For detailed constraints, see ../compatibility/json-rpc-deviations.md.
Japanese version: /ja/doc/rpc/transactions-and-receipts.html
Transaction & Receipt Methods
TL;DR
- Transaction lookups are keyed by
eth_tx_hash. - Receipt lookups can explicitly return
PossiblyPruned/Pruned.
Methods
eth_getTransactionByHash->rpc_eth_get_transaction_by_eth_hasheth_getTransactionReceipt->rpc_eth_get_transaction_receipt_with_status_by_eth_hash
Notes
tx_idis an internal key; useeth_tx_hashfor external integrations.- For internal operations that directly use tx_id, call
rpc_eth_get_transaction_receipt_with_status_by_tx_id. - During migration/corrupt states, methods can return
state unavailable.
Sources
tools/rpc-gateway/src/handlers.tscrates/ic-evm-rpc/src/lib.rs
Japanese version: /ja/doc/rpc/state.html
State Methods
TL;DR
- Provides
eth_getBalance,eth_getTransactionCount,eth_getCode, andeth_getStorageAt. - blockTag accepts
latest/pending/safe/finalized/earliest/QUANTITY, but historical support is limited for many methods.
Methods
eth_getBalance->rpc_eth_get_balanceeth_getTransactionCount->rpc_eth_get_transaction_count_ateth_getCode->rpc_eth_get_codeeth_getStorageAt->rpc_eth_get_storage_at
Key Constraints
- balance/code/storage: historical reads other than head-equivalent can return
state unavailable/ out-of-window - tx count:
pendingreturns pending nonce;earliestand historical nonce are currently unavailable
See ../compatibility/json-rpc-deviations.md and ./overview.md for details.
Sources
tools/rpc-gateway/src/handlers.tscrates/ic-evm-rpc/src/lib.rstools/rpc-gateway/README.md
Japanese version: /ja/doc/rpc/call-estimate-send.html
Call, Estimate, Send
TL;DR
eth_call/eth_estimateGashave callObject constraints.eth_estimateGasreturns minimum successfulgas, notgas_used.eth_sendRawTransactiondelegates to canister submit API.
Methods
eth_call->rpc_eth_call_objecteth_estimateGas->rpc_eth_estimate_gas_objecteth_sendRawTransaction->rpc_eth_send_raw_transaction
callObject Constraints
- Supported keys:
to/from/gas/gasPrice/value/data/nonce/maxFeePerGas/maxPriorityFeePerGas/chainId/type/accessList type=0x0/0x2only- Fee parameter combinations must follow validation rules
Send Operation
- Always monitor receipt
statusafter submit success
Sources
tools/rpc-gateway/README.mdtools/rpc-gateway/src/handlers.tscrates/ic-evm-rpc/src/lib.rs
Japanese version: /ja/doc/rpc/logs.html
Logs
TL;DR
eth_getLogsis a constrained implementation built onrpc_eth_get_logs_paged.- Exceeding range/filter limits returns
-32005.
Supported Patterns
- Single-address + topic0-centered retrieval
- OR arrays in
topics[0](up to 16) blockHashmode (withoutfromBlock/toBlock, within scan limits)
Constraints
topics[1+]conditions are currently unsupported- multiple addresses in one request are unsupported
Common Errors
logs.range_too_largelogs.too_many_resultsUnsupportedFilter
Sources
crates/ic-evm-rpc/src/lib.rs(rpc_eth_get_logs_paged)tools/rpc-gateway/src/handlers.ts
Japanese version: /ja/doc/rpc/errors-and-limits.html
Errors & Limits
TL;DR
- Control is applied in two layers: HTTP constraints (body/batch/depth) and RPC constraints (blockTag/filter/range).
- Main error codes are
-32602/-32000/-32001/-32005.
Gateway HTTP Limits
RPC_GATEWAY_MAX_HTTP_BODY_SIZERPC_GATEWAY_MAX_BATCH_LENRPC_GATEWAY_MAX_JSON_DEPTH
RPC Errors
-32602 invalid params-32000 state unavailable / execution failed-32001 resource not found-32005 limit exceeded
Sources
tools/rpc-gateway/src/server.tstools/rpc-gateway/src/handlers.tstools/rpc-gateway/src/config.ts
Japanese version: /ja/doc/contracts/solidity-vyper-compatibility.html
Solidity/Vyper Compatibility
TL;DR
- As core EVM execution path, you can use
eth_call/eth_estimateGas/eth_sendRawTransaction. - For compatibility details, treat Gateway implementation and compatibility matrix as source of truth.
Main Methods Available
eth_calleth_estimateGaseth_sendRawTransaction
References
tools/rpc-gateway/README.mdcrates/evm-core/src/tx_decode.rs
Japanese version: /ja/doc/contracts/tooling.html
Tooling (Hardhat/Foundry/ethers/viem)
TL;DR
- Smoke tests for ethers/viem/foundry are available in the repository.
- Start with a minimal set: reads + call/estimate + receipt monitoring.
Minimal Flow
- viem:
tools/rpc-gateway/smoke/viem_smoke.ts - ethers:
tools/rpc-gateway/smoke/ethers_smoke.ts - foundry:
tools/rpc-gateway/smoke/foundry_smoke.sh
Pitfalls
- Dropping revert data and losing root-cause visibility
- Not storing tx hash and losing observability
Sources
tools/rpc-gateway/smoke/viem_smoke.tstools/rpc-gateway/smoke/ethers_smoke.tstools/rpc-gateway/smoke/foundry_smoke.sh
Japanese version: /ja/doc/contracts/verification.html
Verification
TL;DR
- Verify feature is provided on the Explorer side.
- For preflight checks, key rotation, and monitoring operation, use
docs/ops/verify_runbook.mdas the source of truth.
Operational Steps (Overview)
- Set
EXPLORER_VERIFY_ENABLED=1and allow-listed compiler version environment variables. - Run
npm run verify:preflightintools/explorerto verify allowedsolcavailability. - Submit verification via
POST /api/verify/submitand track status viaGET /api/verify/status.
Sources
tools/explorer/README.mddocs/ops/verify_runbook.md
Japanese version: /ja/doc/integration/sdks.html
SDKs
TL;DR
- Instead of a dedicated SDK, primary usage is standard JSON-RPC clients (ethers/viem/foundry).
- Direct canister access is available through Candid.
Sources
tools/rpc-gateway/package.jsoncrates/ic-evm-wrapper/evm_canister.did
Japanese version: /ja/doc/integration/signing-nonce-retries.html
Signing, Nonce, Retries
TL;DR
- Always fetch nonce before submit.
- Ensure chain id matches
4801360. - After submit success, determine final result by polling receipt.
Recommended Flow
- Resolve sender address
- Fetch nonce with
eth_getTransactionCountorexpected_nonce_by_address - Set fees (consider base fee + priority)
- Submit via
eth_sendRawTransaction - Poll
eth_getTransactionReceipt
Pitfalls
- Hard-coding nonce
- Resending on receipt timeout and causing nonce conflicts
Sources
README.mdtools/rpc-gateway/README.mdcrates/ic-evm-wrapper/src/lib.rs
Japanese version: /ja/doc/integration/indexer-integration-points.html
Indexer Integration Points
TL;DR
- Pull entrypoint is
export_blocks(cursor,max_bytes). - Log backfill helper is
rpc_eth_get_logs_paged. - Design cursor workflow with pruning in mind.
Sources
crates/ic-evm-wrapper/evm_canister.didtools/indexer/README.mddocs/specs/indexer-v1.md
Japanese version: /ja/doc/indexer/data-model.html
Data Model
TL;DR
- Indexer is Postgres-first.
- It stores entities such as
txs,receipts,token_transfers, andops_metrics_samples.
Key Points
receipt_statusis stored ontxs- token transfers are extracted from receipt logs
Sources
tools/indexer/README.mdtools/indexer/src/db.ts
Japanese version: /ja/doc/indexer/resync-pagination-missing-data.html
Resync, Pagination, Missing Data
TL;DR
- Cursor format is fixed JSON of
block_number/segment/byte_offset. - On
Err.Pruned, continue by correcting cursor position.
Pitfalls
- Pruning progresses while indexer is down and causes history gaps
- Segment limit mismatch prevents recovery
Sources
tools/indexer/README.mddocs/specs/indexer-v1.mdtools/indexer/src/config.ts
Japanese version: /ja/doc/indexer/event-decoding-tips.html
Event Decoding Tips
TL;DR
- For ERC-20 Transfer extraction, strictly validate topic/data lengths.
- Malformed logs should be skipped per row to avoid stopping the full pipeline.
Sources
tools/indexer/README.mdtools/indexer/src/decode_receipt.ts
Japanese version: /ja/doc/security.html
Security Guide
TL;DR
- Minimum baseline: signing-key management, chain-id correctness, and receipt monitoring.
- Typical incidents come from low-fee settings, nonce mistakes, and prune-unaware assumptions.
Safe Usage
- Pin chain id (
4801360) - Fetch nonce every time
- Judge success from receipt after
eth_sendRawTransaction - Do not over-relax gateway limits
Dangerous Pitfalls
- Missing
status=0x0and treating failed execution as success - Re-sending tx signed with wrong chain id repeatedly
- Assuming pruned history remains permanently queryable
Recommended Settings
- Keep
RPC_GATEWAY_MAX_BATCH_LENwithin default range - Match
INDEXER_CHAIN_IDto target network - Match
INDEXER_MAX_SEGMENTto canister specification
Sources
tools/rpc-gateway/src/config.tstools/indexer/src/config.tstools/rpc-gateway/README.mdcrates/evm-core/tests/phase1_eth_decode.rs
Japanese version: /ja/doc/troubleshooting.html
Troubleshooting Guide
TL;DR
- First classify issues into one of: invalid input, state unavailable, or pruned.
- Observe submit and execute as separate phases.
Common Errors and Fixes
invalid params (-32602)- Cause: invalid address/hash length, invalid blockTag, callObject constraint violation
- Action: re-check hex lengths and supported keys
state unavailable (-32000)- Cause: migration in progress, critical_corrupt, execution failure
- Action: check
get_ops_status
resource not found (-32001)- Cause: pruned range or missing resource
- Action: use indexer history
limit exceeded (-32005)- Cause: too wide logs range / too many results
- Action: split by block range
Pitfalls
- Calling
eth_getLogsover a large range in one request - Resending without monitoring and amplifying nonce conflicts
Sources
tools/rpc-gateway/src/handlers.tstools/rpc-gateway/README.mdcrates/ic-evm-wrapper/src/lib.rs(get_ops_status)
Japanese version: /ja/doc/appendix/glossary.html
Glossary
tx_id: internal canister identifiereth_tx_hash: Ethereum-compatible transaction hashIcSynthetic: canister-specific transaction typeEthSigned: signed Ethereum transactionauto-production: timer-driven automatic block productionPossiblyPruned: ambiguous existence near prune boundaryPruned: already pruned and no longer queryable
Japanese version: /ja/doc/appendix/config-reference.html
Config Reference (User Scope)
TL;DR
- This page lists only the settings most frequently used by developers.
Gateway
EVM_CANISTER_ID(required)RPC_GATEWAY_IC_HOSTRPC_GATEWAY_PORT
Indexer
EVM_CANISTER_ID(required)INDEXER_DATABASE_URL(required)INDEXER_IC_HOSTINDEXER_CHAIN_ID(default:4801360)INDEXER_MAX_BYTES
Japanese version: /ja/doc/appendix/versioning-compatibility-policy.html
Versioning & Compatibility Policy
TL;DR
- The source of truth for compatibility matrix updates is the Gateway README.
- When adding methods or changing constraints, update this summary in the same PR.
Sources
tools/rpc-gateway/README.mdREADME.md
Japanese version: /ja/doc/_generated/repo-map.html
Repo Map
This page organizes “what lives where” in the Kasane repository using primary sources only.
TL;DR
- Main EVM canister implementation is
ic-evm-wrapperin the Rust workspace. - Execution logic is in
evm-core, persistence/constants inevm-db, and RPC helpers inic-evm-rpc. - Main developer-facing entry points are
tools/rpc-gateway(HTTP JSON-RPC) andcrates/ic-evm-wrapper/evm_canister.did(Candid). - Indexer is
tools/indexer(Postgres-first).
Key Directories
crates/ic-evm-wrapper- canister entrypoints (
#[ic_cdk::query]/#[ic_cdk::update]) - public Candid definition (
evm_canister.did)
- canister entrypoints (
crates/evm-core- tx decode / submit / produce / call / estimate implementation
- EVM execution and fee calculation (
revm_exec.rs,base_fee.rs)
crates/evm-db- stable state, chain constants, runtime defaults, receipt/block/tx types
crates/ic-evm-rpc- RPC helper logic called by wrapper (eth reads/transforms)
tools/rpc-gateway- translates canister Candid API to Ethereum JSON-RPC 2.0
tools/indexer- pulls export API and stores into Postgres
scripts- smoke, predeploy, and mainnet deploy helpers
docs- operational runbooks and spec notes (usable as primary references)
Entrypoints
- canister build/runtime
dfx.jsoncanisters.evm_canistercrates/ic-evm-wrapper/src/lib.rs
- gateway runtime
tools/rpc-gateway/src/main.tstools/rpc-gateway/src/server.ts
- indexer runtime
tools/indexer/src/main.ts
Dependency Outline
- Rust workspace members
Cargo.toml[workspace].members
- JSON-RPC layer responsibilities
- Gateway: request/response and limits
- canister: state/execution/persistence
Out of Scope (for this GitBook)
- node operations (validator/sequencer/full-node operations)
- internal details of upstream libraries under
vendor/
Sources
Cargo.toml(workspace members)dfx.json(canister package/candid)icp.yaml(deploy recipe)crates/ic-evm-wrapper/src/lib.rs(canister entrypoint)tools/rpc-gateway/src/main.ts(gateway entrypoint)tools/indexer/README.md(indexer responsibilities)
Japanese version: /ja/doc/_generated/interfaces.html
Interfaces
TL;DR
- External interfaces are provided via two paths:
Candid(direct canister) andHTTP JSON-RPC(gateway). - JSON-RPC is not full Ethereum compatibility; it is a constrained implementation.
- In
eth_sendRawTransaction, submit success is not execution success. Useeth_getTransactionReceipt.statusfor final success. - pending/mempool is not available as Ethereum-compatible APIs; use canister
get_pending(tx_id)to track submitted tx.
1. Candid API (Public Service)
Public definition: crates/ic-evm-wrapper/evm_canister.did
Main query methods
rpc_eth_chain_idrpc_eth_block_numberrpc_eth_get_block_by_numberrpc_eth_get_block_by_number_with_statusrpc_eth_get_transaction_by_eth_hashrpc_eth_get_transaction_by_tx_idrpc_eth_get_transaction_receipt_by_eth_hashrpc_eth_get_transaction_receipt_with_status_by_eth_hashrpc_eth_get_transaction_receipt_with_status_by_tx_idrpc_eth_get_balancerpc_eth_get_coderpc_eth_get_storage_atrpc_eth_call_objectrpc_eth_call_rawtxrpc_eth_estimate_gas_objectrpc_eth_get_logs_pagedrpc_eth_get_block_number_by_hashrpc_eth_gas_pricerpc_eth_max_priority_fee_per_gasrpc_eth_fee_historyexpected_nonce_by_addressget_receiptget_pendingexport_blocksget_ops_statushealthmetrics
Main update methods
rpc_eth_send_raw_transactionsubmit_ic_txset_block_gas_limitset_instruction_soft_limitset_prune_policyset_pruning_enabledset_log_filterprune_blocks
2. Gateway JSON-RPC
Implementation: handleRpc switch in tools/rpc-gateway/src/handlers.ts
Implemented methods
web3_clientVersionnet_versioneth_chainIdeth_blockNumbereth_gasPriceeth_maxPriorityFeePerGaseth_feeHistoryeth_syncingeth_getBlockByNumbereth_getTransactionByHasheth_getTransactionReceipteth_getBalanceeth_getTransactionCounteth_getCodeeth_getStorageAteth_getLogseth_calleth_estimateGaseth_sendRawTransaction
Unsupported methods (README compatibility matrix)
eth_getBlockByHasheth_getTransactionByBlockHashAndIndexeth_getTransactionByBlockNumberAndIndexeth_getBlockTransactionCountByHasheth_getBlockTransactionCountByNumbereth_newFiltereth_getFilterChangeseth_uninstallFiltereth_subscribeeth_unsubscribeeth_pendingTransactions
3. Canonical Constraint References
- Canonical overall policy:
../rpc/overview.md - Canonical JSON-RPC differences:
../compatibility/json-rpc-deviations.md - Canonical pending/mempool policy: “Pending/Mempool Policy” in
../rpc/overview.md
This page is intentionally limited to interface inventory. Detailed behavior differences belong to the canonical pages above.
4. Return/Identifier Notes
- Internal canister identifier:
tx_id - Ethereum-compatible identifier:
eth_tx_hash - Gateway
eth_sendRawTransactionresolves and returnseth_tx_hashfrom canister-returnedtx_id.
5. Type Boundaries (Representative)
RpcCallObjectView(Candid)EthBlockView/EthTxView/EthReceiptViewRpcBlockLookupView(NotFound/Found/Pruned)RpcReceiptLookupView(NotFound/Found/PossiblyPruned/Pruned)PendingStatusView(Queued/Included/Dropped/Unknown)
Sources
crates/ic-evm-wrapper/evm_canister.didtools/rpc-gateway/src/handlers.tstools/rpc-gateway/README.mdcrates/ic-evm-wrapper/src/lib.rs(get_pending,rpc_eth_send_raw_transaction)
Japanese version: /ja/doc/_generated/config-reference.html
Config Reference
TL;DR
- Required settings are Gateway:
EVM_CANISTER_ID, Indexer:EVM_CANISTER_ID+INDEXER_DATABASE_URL. - Default
chain_idis4801360. - Gas/mining/prune defaults are managed as Rust constants.
1. Gateway Environment Variables
Definition: tools/rpc-gateway/src/config.ts
- Required
EVM_CANISTER_ID
- Optional (default)
RPC_GATEWAY_IC_HOST(https://icp-api.io)RPC_GATEWAY_FETCH_ROOT_KEY(false)RPC_GATEWAY_IDENTITY_PEM_PATH(null)RPC_GATEWAY_HOST(127.0.0.1)RPC_GATEWAY_PORT(8545)RPC_GATEWAY_CLIENT_VERSION(kasane/phase2-gateway/v0.1.0)RPC_GATEWAY_MAX_HTTP_BODY_SIZE(262144)RPC_GATEWAY_MAX_BATCH_LEN(20)RPC_GATEWAY_MAX_JSON_DEPTH(20)RPC_GATEWAY_CORS_ORIGIN(*)
2. Indexer Environment Variables
Definition: tools/indexer/src/config.ts
- Required
EVM_CANISTER_IDINDEXER_DATABASE_URL
- Optional (default)
INDEXER_IC_HOST(https://icp-api.io)INDEXER_DB_POOL_MAX(10)INDEXER_RETENTION_DAYS(90)INDEXER_RETENTION_ENABLED(true)INDEXER_RETENTION_DRY_RUN(false)INDEXER_ARCHIVE_GC_DELETE_ORPHANS(false)INDEXER_MAX_BYTES(1200000)INDEXER_BACKOFF_INITIAL_MS(200)INDEXER_BACKOFF_MAX_MS(5000)INDEXER_IDLE_POLL_MS(1000)INDEXER_PRUNE_STATUS_POLL_MS(30000)INDEXER_OPS_METRICS_POLL_MS(30000)INDEXER_FETCH_ROOT_KEY(false)INDEXER_ARCHIVE_DIR(./archive)INDEXER_CHAIN_ID(4801360)INDEXER_ZSTD_LEVEL(3)INDEXER_MAX_SEGMENT(2)
3. Chain / Runtime Constants
chain constants
Definition: crates/evm-db/src/chain_data/constants.rs
CHAIN_ID = 4_801_360MAX_TX_SIZE = 128 * 1024MAX_TXS_PER_BLOCK = 1024MAX_PENDING_GLOBAL = 8192MAX_PENDING_PER_SENDER = 64MAX_PENDING_PER_PRINCIPAL = 32MAX_NONCE_WINDOW = 64MAX_LOGS_PER_TX = 64MAX_LOG_TOPICS = 4
runtime defaults
Definition: crates/evm-db/src/chain_data/runtime_defaults.rs
DEFAULT_MINING_INTERVAL_MS = 2000DEFAULT_BASE_FEE = 250_000_000_000DEFAULT_MIN_GAS_PRICE = 250_000_000_000DEFAULT_MIN_PRIORITY_FEE = 250_000_000_000DEFAULT_BLOCK_GAS_LIMIT = 3_000_000DEFAULT_INSTRUCTION_SOFT_LIMIT = 4_000_000_000DEFAULT_PRUNE_TIMER_INTERVAL_MS = 3_600_000DEFAULT_PRUNE_MAX_OPS_PER_TICK = 5_000MIN_PRUNE_TIMER_INTERVAL_MS = 1_000MIN_PRUNE_MAX_OPS_PER_TICK = 1
4. Risky Values / Notes
- If
INDEXER_CHAIN_IDdoes not match the real chain, signing/verification operations can fail. - Increasing
RPC_GATEWAY_MAX_HTTP_BODY_SIZEtoo much weakens DoS resistance. - Lowering
DEFAULT_MIN_GAS_PRICE/DEFAULT_MIN_PRIORITY_FEEwithout clear rationale reduces low-fee spam resistance.
Sources
tools/rpc-gateway/src/config.tstools/indexer/src/config.tscrates/evm-db/src/chain_data/constants.rscrates/evm-db/src/chain_data/runtime_defaults.rs
Japanese version: /ja/doc/_generated/open-questions.html
Open Questions
There are currently no open items in this documentation set.