Skip to content

chore: migrate rust/basic_ethereum to icp-cli#1395

Merged
marc0olo merged 29 commits into
masterfrom
chore/migrate-rust-basic-ethereum-to-icp-cli
Jun 19, 2026
Merged

chore: migrate rust/basic_ethereum to icp-cli#1395
marc0olo merged 29 commits into
masterfrom
chore/migrate-rust-basic-ethereum-to-icp-cli

Conversation

@marc0olo

@marc0olo marc0olo commented Jun 16, 2026

Copy link
Copy Markdown
Member

Summary

Migrates rust/basic_ethereum from dfx to icp-cli.

Removed: dfx.json, BUILD.md, basic_ethereum.did, .devcontainer/

Changed:

  • Rust source moved from src/ to backend/ flat layout
  • EcdsaKeyName enum removed — ECDSA key name is now passed as a plain string ("test_key_1", "key_1") matching what the IC management canister expects, consistent with other examples
  • icp.yaml with two environments: local (backend + pre-built EVM RPC canister) and ic (backend only on ICP mainnet, defaults to Sepolia + test_key_1, with a comment showing how to adapt for production Mainnet + key_1)
  • IC crates (ic-secp256k1, ic-sha3, ic-ethereum-types) switched from IC git repo to crates.io

Added:

  • transaction_count_with_client function demonstrating the high-level EvmRpcClient pattern (accepts an Ethereum address directly, like get_balance)
  • test.sh with 5 tests: address derivation (threshold ECDSA) + get_balance and transaction_count_with_client against a known funded Sepolia address using real HTTPS outcalls
  • CI workflow .github/workflows/basic_ethereum.yml

Test plan

  • icp network start -d && icp deploy && bash test.sh passes locally
  • CI workflow passes

🤖 Generated with Claude Code

marc0olo and others added 2 commits June 17, 2026 18:59
Replaces dfx.json with icp.yaml using @dfinity/rust@v3.3.0 recipe.
Moves Rust source from src/ into backend/, adds workspace Cargo.toml,
Makefile with local-testable ethereum_address tests, updated README with
icp-cli deploy instructions preserving Ethereum integration details,
and CI workflow with icp-dev-env-rust:1.0.0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
….0.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@marc0olo marc0olo force-pushed the chore/migrate-rust-basic-ethereum-to-icp-cli branch from 2d51380 to 4a9e7a5 Compare June 17, 2026 17:00
marc0olo and others added 17 commits June 18, 2026 13:06
…image 1.0.1

- Cargo.toml: add path = "lib.rs" (files are at backend/*.rs not src/)
- lib.rs: replace hardcoded mainnet EVM RPC principal with
  ic_cdk::api::env_var_value("PUBLIC_CANISTER_ID:evm_rpc") — read at
  runtime as a canister environment variable
- icp.yaml: add environments block; local uses auto-injected
  PUBLIC_CANISTER_ID:evm_rpc, production sets EVM_RPC_CANISTER_ID to
  the mainnet canister (7hfb6-caaaa-aaaar-qadga-cai)
- CI: bump image to icp-dev-env-rust:1.0.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace evm-rpc-canister-types (ic-cdk 0.14 conflict) with:
- evm_rpc_types 3.1.1: Candid types for EVM RPC canister
- evm_rpc_client 0.4.0: high-level client with automatic cycle management

transaction_count: raw inter-canister call using evm_rpc_types + ic_cdk
transaction_count_with_client: uses EvmRpcClient for automatic cycle
  management, consensus across providers, and cleaner API

Also update rust-toolchain.toml to 1.88 (required by ic-cdk 0.20)
and fix ic_cdk API changes: caller() -> msg_caller(), tuple error
unwrapping -> single error, EcdsaPublicKeyResponse -> EcdsaPublicKeyResult.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ain.toml

The dev-env image already pins the toolchain; a per-example pin is
redundant and risks drifting out of sync. Matches the pattern used in
all other migrated Rust examples.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ic_cdk::export_candid!() was missing, causing candid-extractor to fail
  with "get_candid_pointer not found"
- Bump pre-built EVM RPC canister from v2.2.0 to v2.8.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The local icp-cli network supports real HTTPS outcalls, so get_balance
and transaction_count work against live Ethereum Sepolia data locally.

test.sh:
- Test 4: get_balance with canister's own Sepolia address — proves
  the EVM RPC canister's HTTPS outcall to PublicNode/Ankr works
- Test 5: transaction_count_with_client — demonstrates EvmRpcClient
  high-level API vs raw inter-canister call in Test 4

README:
- Remove "mainnet only" notes for get_balance and transaction_count
- Explain local HTTPS outcall support and what test.sh covers
- Document send_eth flow with Alchemy Sepolia faucet
- Fix security links to specific anchors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… setup

evm_rpc_services() defaulted to None (all providers) which includes
API-key-required providers like Alchemy (provider 6), breaking local tests.
Switch to explicit PublicNode (free, no API key) so the example works
out of the box.

Add README section explaining how to configure API keys locally and
switch to multi-provider mode for production.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ia address

Test 4 now uses 0x378a452B20d1f06008C06c581b1656BdC5313c0C (from the
original README) which has known Sepolia ETH, and asserts balance > 0.
This is a stronger check proving real on-chain data is returned, not just
that the call succeeds with 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…thereum address directly

Change the signature from Option<Principal> to Option<String> to mirror
get_balance — any Ethereum address can be queried, not just ones derived
from IC principals. Passing null still falls back to the canister's own
derived address.

This creates a clear educational contrast:
  - transaction_count: raw inter-canister call, derives address from principal
  - transaction_count_with_client: EvmRpcClient, accepts any ETH address

Test 5 now uses the known funded Sepolia address and asserts count >= 3.
README updated to show both use cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ging/production)

- local: deploys backend + evm_rpc pre-built locally; icp-cli auto-injects
  PUBLIC_CANISTER_ID:evm_rpc
- staging: backend only on ICP mainnet, Sepolia + TestKey1, points to shared
  EVM RPC canister (7hfb6-caaaa-aaaar-qadga-cai)
- production: backend only on ICP mainnet, Mainnet + ProductionKey1, same
  shared EVM RPC canister

Also fixes env var name mismatch: production env was setting EVM_RPC_CANISTER_ID
but lib.rs reads PUBLIC_CANISTER_ID:evm_rpc (auto-injected by icp-cli pattern).

README updated to show icp deploy -e staging / -e production instead of
manual --argument flags.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…he canister's

The ethereum_address(null), get_balance(null), and
transaction_count_with_client(null, ...) functions all derive the address
from the *caller's* IC principal, not the canister itself. The canister
manages one Ethereum address per user.

Updated README and test.sh labels accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… default

test_key_1 works in icp-cli's PocketIC environment; dfx_test_key was a
dfx-specific artifact that is no longer needed.

TestKey1 is now the default EcdsaKeyName, used for both local and staging.
This lets staging drop its init_args entirely — local and staging share
identical defaults (Sepolia + test_key_1). Only production needs init_args.

icp.yaml is now visibly simpler: two environment blocks with no init_args,
one with.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onCount is outgoing-only

eth_getTransactionCount returns the nonce — only outgoing transactions
increment it. The known Sepolia address has 2 outgoing txs (nonce = 2),
not 3 total (1 incoming + 2 outgoing).

Updated test assertion and comments accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…alls in CI

icp canister call uses the anonymous principal when no identity exists,
causing ethereum_address to trap. Create a named plaintext identity at
test start — --storage-mode plaintext is required in CI containers where
no keyring daemon is running.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…r icp identity new

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 'queries the canister's Sepolia balance' -> 'queries a known funded
  Sepolia address's balance' (test uses an external address, not any
  canister's balance)
- 'funded canister wallet' -> 'funded Ethereum address' (consistent with
  the rest of the README which uses caller-centric language)
- 'Both environments' -> 'Both mainnet environments' (local also deploys
  the evm_rpc canister, so this was ambiguous)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@marc0olo marc0olo marked this pull request as ready for review June 18, 2026 15:10
@marc0olo marc0olo requested review from a team as code owners June 18, 2026 15:10
@marc0olo marc0olo requested a review from Copilot June 18, 2026 15:10

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the rust/basic_ethereum example from the legacy dfx layout/tooling to icp-cli, updating project structure, build configuration, CI, and Rust canister code to use the current EVM RPC types/client patterns.

Changes:

  • Replaced dfx.json/ICP Ninja artifacts with icp.yaml, a Rust workspace layout (backend/), and an icp-cli-driven CI workflow.
  • Updated Rust canister code to use evm_rpc_types / evm_rpc_client, ic-cdk = 0.20, ic-cdk-management-canister, and runtime env var lookup for the EVM RPC canister ID.
  • Added a test.sh script that exercises address derivation and live Sepolia reads (balance + nonce) via HTTPS outcalls.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
rust/basic_ethereum/test.sh Adds an icp-cli test script covering address derivation and live Sepolia RPC reads.
rust/basic_ethereum/rust-toolchain.toml Simplifies toolchain config to just the wasm target.
rust/basic_ethereum/README.md Updates documentation from dfx/ICP Ninja to icp-cli deploy/test flows and new RPC client usage.
rust/basic_ethereum/icp.yaml Defines icp-cli canisters/environments, including pre-built evm_rpc locally and mainnet env var wiring.
rust/basic_ethereum/dfx.json Removed (dfx migration).
rust/basic_ethereum/Cargo.toml Converts to a workspace root referencing backend/.
rust/basic_ethereum/Cargo.lock Updates lockfile for the new crate split and dependency set.
rust/basic_ethereum/BUILD.md Removed (ICP Ninja artifact).
rust/basic_ethereum/basic_ethereum.did Removed (backend-only; interface now exported from Rust).
rust/basic_ethereum/backend/Cargo.toml New backend crate manifest with updated dependencies (ic-cdk 0.20, evm_rpc_* crates).
rust/basic_ethereum/backend/lib.rs Updates EVM RPC integration + adds transaction_count_with_client + exports Candid.
rust/basic_ethereum/backend/state.rs Updates RPC service selection + switches to ic-cdk-management-canister API.
rust/basic_ethereum/backend/ethereum_wallet.rs Switches ECDSA signing calls to ic-cdk-management-canister.
rust/basic_ethereum/backend/ecdsa.rs Updates ECDSA public key response type import.
rust/basic_ethereum/.devcontainer/devcontainer.json Removed (repo uses root devcontainer).
.github/workflows/basic_ethereum.yml Adds CI job to deploy/test with icp-cli in the Rust dev env container.
Comments suppressed due to low confidence (2)

rust/basic_ethereum/backend/lib.rs:23

  • The comment describes EVM_RPC_CANISTER_ID, but the code actually reads PUBLIC_CANISTER_ID:evm_rpc. Keeping these aligned avoids confusion when configuring environments via icp.yaml.
    rust/basic_ethereum/backend/lib.rs:28
  • The expect message references EVM_RPC_CANISTER_ID, but the actual env var being parsed is PUBLIC_CANISTER_ID:evm_rpc. This makes failures harder to diagnose.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread rust/basic_ethereum/test.sh
marc0olo and others added 2 commits June 18, 2026 17:16
…ctual --storage flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l env var name

Both referenced EVM_RPC_CANISTER_ID (old name) instead of the actual
env var PUBLIC_CANISTER_ID:evm_rpc read by the code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.

Comment thread rust/basic_ethereum/test.sh
Comment thread rust/basic_ethereum/test.sh Outdated
Comment thread rust/basic_ethereum/backend/Cargo.toml
Comment thread rust/basic_ethereum/icp.yaml

@gregorydemay gregorydemay left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @marc0olo !

Comment thread rust/basic_ethereum/backend/Cargo.toml Outdated
Comment thread rust/basic_ethereum/test.sh
marc0olo and others added 2 commits June 18, 2026 18:20
…tring, rename staging to ic

EcdsaKeyName enum added unnecessary indirection — callers now pass the
key name directly ("test_key_1", "key_1") matching what the IC
management canister expects, same pattern as other examples.

icp.yaml:
- Remove production environment (not needed for an example)
- Rename staging -> ic (standard icp-cli environment name)
- Explicit init_args on ic environment with string key format
- Comment showing how to adapt init_args for production (Mainnet + key_1)

README: simplified deploy section, removed environment table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Switch ic-secp256k1, ic-sha3, ic-ethereum-types from IC git repo to
  crates.io (0.3.0, 1.0.0, 1.0.0) — no need to pull the full IC repo
- Pin getrandom from wildcard '*' to '0.2'
- Fix grep pattern for Candid nat values: '[0-9][0-9_]*' + tr -d '_'
  to handle underscore digit separators (e.g. 1_000_000)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread rust/basic_ethereum/icp.yaml

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

rust/basic_ethereum/backend/state.rs:114

  • This trap message drops the structured context (rejection code / message) and the key name involved, making ECDSA misconfiguration failures harder to diagnose. Including at least the key name in the message would make debugging deployments significantly easier.

Comment thread rust/basic_ethereum/icp.yaml
Comment thread rust/basic_ethereum/rust-toolchain.toml
marc0olo and others added 5 commits June 18, 2026 18:48
…comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Makes misconfiguration failures diagnosable — e.g. a typo in the
ecdsa_key_name init arg now shows which key name was attempted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…testing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

rust/basic_ethereum/backend/lib.rs:21

  • This comment mentions “staging/production”, but icp.yaml only defines local and ic environments. Keeping this aligned with the actual environment names avoids confusion about where the PUBLIC_CANISTER_ID:evm_rpc value comes from.

Comment thread rust/basic_ethereum/icp.yaml
Comment thread rust/basic_ethereum/README.md Outdated
…with doc link

authorize/updateProvider no longer exist in evm_rpc-v2.8.0. Rather than
maintaining version-specific admin CLI commands in this README, point to
the EVM RPC canister documentation instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@marc0olo marc0olo merged commit 8f077c8 into master Jun 19, 2026
6 checks passed
@marc0olo marc0olo deleted the chore/migrate-rust-basic-ethereum-to-icp-cli branch June 19, 2026 06:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants