Skip to content

Commit 41aa6f7

Browse files
committed
fix: address PR feedback on Ethereum integration guide
1 parent 3795697 commit 41aa6f7

1 file changed

Lines changed: 46 additions & 26 deletions

File tree

docs/guides/chain-fusion/ethereum.mdx

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ The EVM RPC canister offers two styles of API:
5656
- **Typed Candid-RPC methods** like `eth_getBlockByNumber` and `eth_getTransactionReceipt` — these query multiple providers by default and return a `MultiResult` with built-in consensus.
5757
- **Raw JSON-RPC** via the `request` method — sends a single JSON-RPC request to one provider. More flexible, but you handle parsing and consensus yourself.
5858

59+
{/* Needs human verification: `canister:name` import syntax (e.g., `import EvmRpc "canister:evm_rpc"`) may not work with icp-cli; the Motoko team is redesigning canister discovery. The Motoko examples below use this syntax — verify the correct import approach with icp-cli before shipping. */}
60+
5961
### Get the latest block (typed API)
6062

6163
<Tabs syncKey="lang">
@@ -132,6 +134,8 @@ async fn get_latest_block() -> Block {
132134

133135
Always handle all three result variants: `Consistent(Ok(...))`, `Consistent(Err(...))`, and `Inconsistent(...)`. Ignoring `Inconsistent` will cause your canister to trap when providers disagree.
134136

137+
> **Tip:** For queries like `eth_getBlockByNumber(Latest)`, use `ConsensusStrategy::Threshold { total: Some(3), min: 2 }` (2-of-3 agreement) instead of the default `Equality` strategy, since providers may be 1-2 blocks apart. Pass this as the consensus config parameter (third argument in Motoko, via the client builder in Rust with `evm_rpc_client`).
138+
135139
### Get ETH balance (raw JSON-RPC)
136140

137141
The `request` method sends a raw JSON-RPC payload to a single provider. This is useful for methods not covered by the typed API, or when you want direct control over the request.
@@ -378,7 +382,7 @@ persistent actor {
378382
derivation_path = [caller];
379383
key_id = {
380384
curve = #secp256k1;
381-
name = "key_1"; // Use "dfx_test_key" locally, "key_1" on mainnet
385+
name = "key_1"; // Use the test key "dfx_test_key" for local testing (this is a protocol-level key name)
382386
};
383387
});
384388
public_key;
@@ -405,7 +409,7 @@ async fn get_public_key() -> Vec<u8> {
405409
derivation_path: vec![],
406410
key_id: EcdsaKeyId {
407411
curve: EcdsaCurve::Secp256k1,
408-
name: "key_1".to_string(), // Use "dfx_test_key" locally
412+
name: "key_1".to_string(), // Use the test key "dfx_test_key" for local testing (this is a protocol-level key name)
409413
},
410414
};
411415

@@ -611,44 +615,49 @@ canisters:
611615
612616
### Local deployment
613617
618+
{/* Needs human verification: how to deploy EVM RPC canister locally with icp-cli — `icp deps` subcommand does not exist. The recommended approach (from the evm-rpc skill) is to use `icp deploy -e local` with environments defined in `icp.yaml` to deploy both the backend and the EVM RPC canister pre-built WASM together. On mainnet, the EVM RPC canister is already deployed at `7hfb6-caaaa-aaaar-qadga-cai` and only the backend needs to be deployed. */}
619+
620+
The `icp.yaml` above uses environments to separate local and mainnet deployment. Add an `environments` block to control which canisters are deployed where:
621+
622+
```yaml
623+
environments:
624+
- name: local
625+
network: local
626+
canisters: [backend, evm_rpc]
627+
- name: ic
628+
network: ic
629+
canisters: [backend]
630+
settings:
631+
backend:
632+
environment_variables:
633+
PUBLIC_CANISTER_ID:evm_rpc: "7hfb6-caaaa-aaaar-qadga-cai"
634+
```
635+
636+
Then deploy locally with:
637+
614638
```bash
615639
# Start local replica
616640
icp network start -d
617641
618-
# Pull and deploy the EVM RPC canister
619-
icp deps pull
620-
icp deps init evm_rpc --argument '(record {})'
621-
icp deps deploy
622-
623-
# Deploy your backend
624-
icp deploy backend
642+
# Deploy both backend and evm_rpc for local development
643+
icp deploy -e local
625644
```
626645

646+
On mainnet, only the backend is deployed — the EVM RPC canister is already available at `7hfb6-caaaa-aaaar-qadga-cai`.
647+
627648
### Testing via icp-cli
628649

629-
```bash
630-
# Get the latest block (typed API, multi-provider consensus)
631-
icp canister call evm_rpc eth_getBlockByNumber '(
632-
variant { EthMainnet = null },
633-
null,
634-
variant { Latest }
635-
)' --with-cycles=10000000000
636-
637-
# Get ETH balance (raw JSON-RPC, single provider)
638-
icp canister call evm_rpc request '(
639-
variant { EthMainnet = variant { PublicNode } },
640-
"{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\",\"latest\"],\"id\":1}",
641-
1000
642-
)' --with-cycles=10000000000
650+
{/* Needs human verification: cycle attachment for EVM RPC canister calls via icp-cli. The `--with-cycles` flag does not exist in icp-cli; `--cycles` only works with `--proxy` to forward cycles through a proxy canister. There is no direct way to attach cycles to an `icp canister call` without a proxy. The examples below call the canister directly without cycle attachment — this works for query methods like `requestCost` and `getProviders` but update calls like `request` and `eth_getBlockByNumber` require cycles. For testing update calls, consider calling from another canister (which can attach cycles) rather than directly from the CLI. */}
643651

644-
# Estimate cost before calling
652+
```bash
653+
# Estimate cost before calling (no cycles needed for this query)
645654
icp canister call evm_rpc requestCost '(
646655
variant { EthMainnet = variant { PublicNode } },
647656
"{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBalance\",\"params\":[\"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\",\"latest\"],\"id\":1}",
648657
1000
649658
)'
650659
651-
# List available providers
660+
# List available providers (no cycles needed)
652661
icp canister call evm_rpc getProviders
653662
```
654663

@@ -662,7 +671,18 @@ icp deploy backend -e ic
662671

663672
### Rust type definitions
664673

665-
The Rust examples above reference several types (`RpcServices`, `MultiResult`, `Block`, etc.) that must match the EVM RPC canister's Candid interface. See the [EVM RPC canister Candid interface](https://github.com/dfinity/evm-rpc-canister) for the complete type definitions, or use the skill reference at [skills.internetcomputer.org](https://skills.internetcomputer.org) for copy-pasteable Rust type stubs.
674+
> **Note:** The `evm_rpc_client` crate (from the `ic-evm-rpc` package) provides a typed client API for the EVM RPC canister and is the recommended approach for Rust canisters. It handles cycle attachment, argument encoding, response decoding, and re-exports all Candid types from `evm_rpc_types`. The examples in this guide use the lower-level `ic-cdk` `Call` API for illustration purposes — for production use, prefer `evm_rpc_client`. See the [evm-rpc skill](https://skills.internetcomputer.org) for a complete `evm_rpc_client`-based implementation.
675+
>
676+
> Add it to your `Cargo.toml`:
677+
> ```toml
678+
> [dependencies]
679+
> evm_rpc_client = "0.4"
680+
> evm_rpc_types = "3"
681+
> ic-canister-runtime = "0.2"
682+
> ic-cdk = "0.20"
683+
> ```
684+
685+
The lower-level examples above reference several types (`RpcServices`, `MultiResult`, `Block`, etc.) that must match the EVM RPC canister's Candid interface. See the [EVM RPC canister Candid interface](https://github.com/dfinity/evm-rpc-canister) for the complete type definitions.
666686

667687
## Common mistakes
668688

0 commit comments

Comments
 (0)