Skip to content

test: end-to-end deposit-from-CEX EIP-7702 sweep demo against a local Ethereum node#10670

Open
gregorydemay wants to merge 24 commits into
masterfrom
ic_DEFI-2096_deposit-from-cex-demo
Open

test: end-to-end deposit-from-CEX EIP-7702 sweep demo against a local Ethereum node#10670
gregorydemay wants to merge 24 commits into
masterfrom
ic_DEFI-2096_deposit-from-cex-demo

Conversation

@gregorydemay

@gregorydemay gregorydemay commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

A hermetic, monorepo-native integration test that validates the "deposit from CEX" sweep (design in rs/ethereum/cketh/docs/deposit_from_cex.md) end-to-end against a local anvil node, through the minter's productive EIP-7702 transaction layer (#10671).

It sweeps unfunded deposit addresses to the minter in minter-paid EIP-7702 transactions, checks each emits the canonical deposit event with the right IC principal, measures gas across batch sizes (1/10/20), and covers both sweeper designs — caller-gated and permissionless-with-minter-attestation — including rejection of unauthorized sweeps.

foundry and solc are vendored via Bazel (no docker, IC replica, or alloy).

Copilot AI 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.

Pull request overview

Adds a standalone, runnable Foundry/Alloy proof-of-concept under rs/ethereum/cketh/docs/ that demonstrates the “deposit from CEX” EIP-7702 sweeping flow referenced by the design doc, including single and batched sweeps, helper-based sweeps that emit canonical events, and a rejection case for non-minter sweeping.

Changes:

  • Introduces a Rust CLI demo that deploys mock contracts, simulates CEX deposits, and performs EIP-7702 single/batched sweeps (direct and via the real helper), asserting gas usage.
  • Adds Solidity demo contracts (sweeper delegates + mock USDT) and embeds their compiled bytecode artifacts for reproducible runs.
  • Documents how to run and rebuild the artifacts, plus Foundry configuration and local ignore rules.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
rs/ethereum/cketh/docs/deposit_from_cex_demo/src/main.rs Runnable Alloy-based demo driving the full sweep flow (single/batch, direct/via helper, attacker rejection) and asserting gas usage.
rs/ethereum/cketh/docs/deposit_from_cex_demo/README.md Runbook and explanation of the demo, variants, and artifact regeneration steps.
rs/ethereum/cketh/docs/deposit_from_cex_demo/foundry.toml Configures Foundry for Prague/Pectra-compatible EVM settings.
rs/ethereum/cketh/docs/deposit_from_cex_demo/contracts/CkSweeper.sol Variant A sweeper delegate + batch entry point for direct-to-minter token/ETH sweeps.
rs/ethereum/cketh/docs/deposit_from_cex_demo/contracts/CkSweeperViaHelper.sol Variant B sweeper delegate + minter-gated batch entry point that routes deposits through the helper contract.
rs/ethereum/cketh/docs/deposit_from_cex_demo/contracts/MockUSDT.sol Mock USDT-like ERC-20 with non-standard return behavior to exercise tolerant transfer/approve handling.
rs/ethereum/cketh/docs/deposit_from_cex_demo/Cargo.toml Declares a standalone (non-workspace) Rust crate for the demo.
rs/ethereum/cketh/docs/deposit_from_cex_demo/Cargo.lock Locks dependencies for reproducible demo builds.
rs/ethereum/cketh/docs/deposit_from_cex_demo/artifacts/CkSweeper.bin.hex Embedded compiled bytecode for the Variant A delegate/batcher.
rs/ethereum/cketh/docs/deposit_from_cex_demo/artifacts/CkSweeperViaHelper.bin.hex Embedded compiled bytecode for the Variant B delegate/batcher.
rs/ethereum/cketh/docs/deposit_from_cex_demo/artifacts/MockUSDT.bin.hex Embedded compiled bytecode for the mock USDT token.
rs/ethereum/cketh/docs/deposit_from_cex_demo/artifacts/CkDeposit.bin.hex Embedded compiled bytecode for the real helper contract used in Variant B.
rs/ethereum/cketh/docs/deposit_from_cex_demo/.gitignore Ignores Foundry/Cargo build outputs for the demo directory.

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

Comment on lines +133 to +140
fn encode_principal(principal_text: &str) -> FixedBytes<32> {
let principal = Principal::from_text(principal_text).expect("valid principal");
let bytes = principal.as_slice();
let mut encoded = [0u8; 32];
encoded[0] = bytes.len() as u8;
encoded[1..=bytes.len()].copy_from_slice(bytes);
FixedBytes::from(encoded)
}

@github-actions github-actions Bot 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.

Auto approving mainnet-icos-revisions.json changes.

@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch from 60e0c3c to 8f0238a Compare July 8, 2026 05:09
@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch from 8f0238a to 64abe70 Compare July 8, 2026 05:10
@gregorydemay gregorydemay changed the base branch from ic_DEFI-2096_design-support-deposit-from-cex to ic_DEFI-2919_eip7702-tx-layer July 8, 2026 05:10
@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch 3 times, most recently from ce3d105 to 382bfca Compare July 8, 2026 07:23
Base automatically changed from ic_DEFI-2919_eip7702-tx-layer to master July 8, 2026 07:31
@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch from ef92401 to d7f815a Compare July 8, 2026 07:38
gregorydemay and others added 11 commits July 8, 2026 07:48
Stacked on top of the design-doc PR (#10652). Adds the runnable alloy/foundry
demo of the EIP-7702 sweep mechanism (both variants, gas assertions, attacker
rejection) that the design document references.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Vendors the foundry v1.7.1 release binaries (anvil/cast/forge/chisel) via
http_archive and adds a monorepo-native rust_test that spawns anvil and probes
it over JSON-RPC. No docker, no InternetComputer, no alloy: the skeleton
foundation for hand-rolling the deposit-from-CEX PoC's Ethereum layer on the
already-vendored k256/sha3/rlp/ethnum crates.

The pinned foundry release is >= v1.0 so anvil supports EIP-7702, unlike the
v0.3.0 @foundry oci image used by //rs/tests/cross_chain:ic_xc_cketh_test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vendor foundry binaries for all four host platforms (linux/darwin x
amd64/arm64) and select the matching one, so the test runs on macOS as well as
linux CI. Previously only linux_amd64 was vendored: on an arm64 Mac the Linux
ELF spawned (via the execvp /bin/sh fallback) and died immediately, but the
readiness loop kept polling a dead process until the 30s timeout. The loop now
detects an early child exit and fails fast with the exit status.

Inline the archive's build_file content instead of a separate
third_party/BUILD.foundry.bazel.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Relocate the hermetic anvil smoke test to rs/ethereum/cketh/minter/tests and
excludes it from the integration_tests suite, which runs against the IC test
machinery rather than a local foundry node. The platform-selecting anvil alias
and its dedicated rust_test target move alongside it into the minter BUILD.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-7702 tx layer

Fills in the migrated test with the decided variant B flow, driving a local
anvil node through the minter's own transaction layer (Eip7702TransactionRequest,
Authorization/SignedAuthorization, Signed) rather than a hand-rolled encoder or
alloy. Signing uses ic_secp256k1 locally as a stand-in for threshold ECDSA; ABI
calldata, JSON-RPC transport and event decoding are minimal helpers.

The test deploys a USDT-style ERC-20, the real ckETH helper and the sweeper
delegate, then sweeps unfunded deposit EOAs in single and batched EIP-7702
transactions (gas paid by the minter), asserts the canonical ReceivedEthOrErc20
events carry the right IC principal, and shows a non-minter sweep is rejected
before the minter finalizes it with a plain EIP-1559 transaction. Gas used by
each sweep is asserted against deterministic constants.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drops the checked-in .bin.hex artifacts and compiles the demo contracts from
their Solidity sources at test time with the vendored solc, mirroring how
ic_xc_cketh_test provisions its contracts. The real helper (CkDeposit) is
compiled from its canonical DepositHelperWithSubaccount.sol rather than a
duplicated bytecode blob, and the two demo-only sources are consumed directly
from the PoC's contracts directory.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Vendors the latest stable solc (0.8.35) pinned exactly by sha256 and compiles
the demo contracts with an explicit --evm-version prague, the fork that defines
EIP-7702 and that anvil runs. Pinning both the compiler and the EVM version
keeps the bytecode (and the asserted gas) reproducible instead of drifting with
solc's default target (now osaka/fusaka).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Makes the demo test's solc host-platform aware like the anvil binary: vendors
solc 0.8.35 for linux-amd64/linux-arm64/macos and selects it through a `solc`
alias reusing the existing config_settings. The macOS build is a universal
(x86_64 + arm64) binary, so the test runs natively on Linux and on both Intel
and Apple Silicon Macs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gregorydemay and others added 5 commits July 8, 2026 07:48
Follows the repository convention for host-platform-selected tool binaries
(bitcoind, dfx, ruff, ...): defines //:anvil and //:solc as aliases selecting on
@bazel_tools//src/conditions in the top-level BUILD, and drops the bespoke
config_settings from the minter BUILD. The demo test consumes //:anvil and
//:solc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…y PoC

Moves the two contracts the test compiles (MockUSDT.sol, CkSweeperViaHelper.sol)
into minter/tests/deposit_from_cex_demo/, next to the test that consumes them,
and removes the now-redundant standalone alloy PoC (docs/deposit_from_cex_demo:
main.rs, Cargo.*, README, foundry.toml, the pre-compiled artifacts/ bytecode and
the unused variant-A CkSweeper.sol). The productive minter test is now the
single deposit-from-CEX demo, with no duplicated contracts or bytecode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refactors the deposit-from-CEX demo into a table-driven test over the two batch
sizes from the design doc's cost-estimation section: a single deposit swept in
one batch (B=1) versus a batch of twenty (B=20). Each scenario funds fresh
unfunded deposit EOAs from the CEX and sweeps them all in one EIP-7702
transaction, asserting the canonical events and that per-deposit gas amortizes
as the batch grows (94_932 for B=1 vs 55_668/deposit for B=20).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-adds the attacker-rejection as a focused second test: the minter installs a
deposit EOA's delegation without sweeping (its authorization rides in an empty
batch), then a non-minter's sweepErc20 attempt reverts and the funds stay put,
while the minter's own sweep still succeeds via a plain EIP-1559 transaction
(the delegation persists).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review renamed Eip1559Signature to TransactionSignature and
Signed::raw_transaction_hex to raw_transaction_bytes before #10671 landed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch from ee71e0e to 05a19ab Compare July 8, 2026 07:50
gregorydemay and others added 2 commits July 8, 2026 08:01
Adds an average_gas_used field to BatchScenario (total / deposits) and asserts
it alongside the total, making the amortization explicit: 94_932 per deposit at
B=1 versus 55_668 at B=20.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Third point between B=1 and B=20 (570_151 gas, 57_015 per deposit), keeping the
per-deposit amortization curve monotonic: 94_932 -> 57_015 -> 55_668.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch from e13e8bf to 3521d47 Compare July 8, 2026 08:20
…tion

The first sweep installs the delegations (EIP-7702); a second sweep of the same
addresses, after re-funding, reuses the persisted delegations and so needs only
a regular EIP-1559 transaction. This isolates the EIP-7702 delegation-install
overhead from the steady-state sweep cost. Per batch (total / per-deposit):

  B=1:  EIP-7702 94_932/94_932   EIP-1559 63_252/63_252
  B=10: EIP-7702 570_151/57_015  EIP-1559 390_151/39_015
  B=20: EIP-7702 1_113_373/55_668 EIP-1559 753_373/37_668

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pull Bot pushed a commit to bit-cook/ic that referenced this pull request Jul 8, 2026
)

Design doc for
[DEFI-2096](https://dfinity.atlassian.net/browse/DEFI-2096): allow
onramping ckUSDC/ckUSDT (and generally any ckERC20, later ckETH)
directly from a centralized exchange withdrawal.

Deposits currently require calling the helper smart contract, which a
CEX cannot do: a CEX withdrawal is a plain transfer from a shared hot
wallet, carrying no IC principal. The proposed design gives each IC
account a unique tECDSA-derived deposit address and sweeps funds to the
minter using EIP-7702 (Pectra), so deposit addresses never need ETH for
gas and remain key-recoverable independently of any contract code.

The doc covers the requirements, claim-based deposit detection, the
sweeper delegate, fee model, a phased delivery (ckERC20 first, then
ckETH with its balance-based detection and fixed-21k-gas constraints),
and the discussed alternatives (CREATE2 forwarders, ERC-4337,
permit-based sponsoring).

The runnable proof-of-concept the doc references lives in a stacked
follow-up, dfinity#10670.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


[DEFI-2096]:
https://dfinity.atlassian.net/browse/DEFI-2096?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: IDX GitHub Automation <infra+github-automation@dfinity.org>
Adds CkSweeperAttested, an EIP-7702 delegate matching the proposed one-time
self-attestation design: sweeping is permissionless but each sweep carries an
attestation — a secp256k1 signature by the deposit address' own key (the
minter's role via threshold ECDSA) over keccak256("ck-deposit-owner" || chainid
|| helper || principal || subaccount), verified with ecrecover == address(this).

Runs the same table-driven batch gas test through the attested sweeper with a
non-minter relayer submitting the transactions, plus a test that a forged
attestation is rejected while the genuine one credits the attested account. The
attestation adds ~4k gas/deposit over the caller-gated variant (per batch of 20:
59_645 vs 55_668 for the delegating sweep, 41_645 vs 37_668 for the steady-state
EIP-1559 sweep).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch from 35d0086 to fa6fac1 Compare July 8, 2026 08:55
…led codec

Replaces the custom Token enum + head/tail ABI encoder (including the TupleArray
special case) and the manual return/event decoding with ethers-core's ethabi
(encode/decode/Token/ParamType), already a dependency of the sibling
integration_tests. The attestation preimage stays an explicit byte concatenation
(it is a hash preimage, not ABI, and abi.encodePacked semantics differ). Gas
figures are unchanged, confirming the encoding is byte-for-byte equivalent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregorydemay gregorydemay force-pushed the ic_DEFI-2096_deposit-from-cex-demo branch from 33f2df6 to f55bd97 Compare July 8, 2026 09:33
IDX GitHub Automation and others added 3 commits July 8, 2026 09:37
The style_edition=2024 cargo-fmt check orders imports case-sensitively while the
autofix bot re-sorts them case-insensitively, so a mixed-case group (e.g.
{ParamType, Token, decode, encode}) ping-pongs between them and fails Cargo Lint.
Import only the uppercase items and fully-qualify the lowercase
ethers_core::abi::{encode,decode} and serde_json::json!, leaving no group whose
ordering depends on case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Cargo Lint CI job runs clippy with -D clippy::unseparated_literal_suffix, so
underscore-separate the u8 literals (0u8 -> 0_u8). Also declare reqwest as a
dev-dependency of the crate (it was only in the Bazel target), which cargo needs
to resolve the test, and record the edge in Cargo.lock.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gregorydemay gregorydemay changed the title docs: runnable foundry PoC for the deposit-from-CEX design test: end-to-end deposit-from-CEX EIP-7702 sweep demo against a local Ethereum node Jul 9, 2026
@github-actions github-actions Bot added test and removed docs labels Jul 9, 2026
@gregorydemay gregorydemay marked this pull request as ready for review July 9, 2026 08:54
@gregorydemay gregorydemay requested review from a team as code owners July 9, 2026 08:54
@zeropath-ai

zeropath-ai Bot commented Jul 9, 2026

Copy link
Copy Markdown

No security or compliance issues detected. Reviewed everything up to 8993359.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► rs/ethereum/cketh/minter/BUILD.bazel
    + Deposit_from_cex_demo_test target and related test wiring
► rs/ethereum/cketh/minter/Cargo.toml
    + Add reqwest dependency
► rs/ethereum/cketh/minter/tests/deposit_from_cex_demo.rs
    + New integration test file for deposit-from-CEQ demo flow
Enhancement ► rs/ethereum/cketh/minter/BUILD.bazel
    + Exclude certain tests in test suite and add a dedicated test name deposit_from_cex_demo_test
Enhancement ► MODULE.bazel
    + Add solc http_file definitions for solc v0.8.35 (linux/linux arm64/macosx) and Foundry binaries (linux amd64/arm64, darwin amd64/arm64)
Enhancement ► CARGO.lock/rs dependencies (indirect in diff)
    + Include reqwest in Rust workspace for the new test flow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants