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)