ERC-4337 P-256 smart-account master (#164): plan + contracts E0–E8 + live mainnet infra & demo#171
Merged
Merged
Conversation
…uthn-gated master, 14 tests green
…FAILED (codex P2) +3 tests
…in-contract K11 + scopeNonce); 59 tests
…+ E3 status + cutover ripples
…ch.md §5 anchor; deployed-contracts.md→redirect
…rotation, independent of primary passkey); +6 tests
…ship = Sybil gate) +6 tests; unsafe-mode bundler runner
…ter demo landed on phase1-wire-demo.sh (phase6); ran green on Heima mainnet (acct 0x3e79925F, addSigner via UserOp, no secp256k1 key)
…ver redeploy notes
…operator-runbook-wire.md
…n --light; opt out --skip-6 / AGENTKEYS_ERC4337_E8=0) + runbook
…hase-0 agentkeys-cli guard (#164) - erc4337-master-e8.sh: fresh (default) vs reuse (auto when $CI) modes; reuse uses a persistent passkey+fixed salt (deterministic account), funds only when low; delta assertion (count +1) works for both. Fix _has_code (cast code '0x' != empty). - webauthn-sign.py: keygen idempotent (load-if-exists) so reuse keeps its address. - phase1-wire-demo.sh: 0.2b real-mode guard fails loud if the on-PATH agentkeys lacks the 'agent' subcommand (the stale-binary cascade). - runbook: document the two E8 modes + env vars.
…he stale-binary cascade) + runbook 0.2b now runs 'cargo build --release -p agentkeys-cli' so the master-side CLI Phase P uses ($REPO_ROOT/target/release/agentkeys — already preferred at P.0/P.1) is current, then verifies the actual binary has the 'agent' subcommand. Opt out AGENTKEYS_SKIP_CLI_BUILD=1. Runbook: real-mode prereq + troubleshooting row document the build + manual fallback.
…sorbed the → byte into the var name (set -u: 'BEFORE: unbound variable')
… smoke, not a phase Per arch.md §9 the 4337 account binds to the MASTER ONBOARDING ceremony (K11 at stage 2, register the account at stage 4) = #164 E7, landing with the registry cutover. The standalone erc4337-master-e8.sh uses a throwaway software passkey, so it proves the mechanism but is NOT the ceremony (the real master K11 is in the platform authenticator → needs a Touch ID assert). Remove phase6 + the call + the --real header note from phase1-wire-demo.sh (keep the 0.2b Phase-P CLI guard). Runbook + plan reframed; #164 E8 row → mechanism smoke, E7 → ceremony binding.
- #1 HIGH: VerifyingPaymaster.getHash now binds the paymaster gas limits (paymasterAndData[20:52]) so a valid broker sig can't be reused with inflated limits. +test_RejectsTamperedGasLimits. - #3 MED: recover() dedups guardians by (pubX,pubY), not just credIdHash, so one physical key under two credIds can't satisfy an M>=2 quorum. +test. - #2 HIGH (deployment-ordering): warn in AgentKeysScope @dev + threat-model §10 — never deploy the thinned scope before the registry cutover (else an EOA master could setScope with no biometric). - #4/#5 documented in threat-model §10 (malleability is nonce-mitigated; recover-to- unusable-signer is operator error). 73 tests green.
hanwencheng
added a commit
that referenced
this pull request
Jun 2, 2026
…4337, #168 Cancun, #166 bootstrap) The chain side this plan waited on has landed; reconcile the web-wiring plan with it and correct two now-false facts. - Top status banner: ERC-4337 P-256 master account is IMPLEMENTED (#171, Solution A) — EntryPoint v0.7 + P256Account + factory + VerifyingPaymaster live on Heima mainnet; #164 closed. Heima is Cancun, not London (#168). Bootstrap front-run fixed (#166). Defers to docs/plan/chain/erc4337-master-account.md. - §11 (gating): retitled '✅ RESOLVED by #171 (historical)' + a correction note; the 'does-NOT-auto-fix' block rewritten as 'all addressed' (bootstrap #166; full-intent structural via userOpHash; agent-bind/multi-device #171 E3/E4/E5); fixed the false 'London / no PUSH0' claim → Cancun + pure-Solidity P-256 (~707k gas, no RIP-7212). - §4.3 + §12 X4: the K11→chain bridge is now an ERC-4337 UserOp (passkey-signs userOpHash → broker-gated bundler / handleOps → P256Account); reference erc4337-webauthn-sign.py + erc4337-master-e8.sh. Retired the cast/--assertion-file path. - §5a/§5b: onboarding stage 2 derives the P256Account (CREATE2 from passkey); stage 4 = one initCode+registerFirstMasterDevice UserOp (= chain-plan E7, routed through #163); bind/grant via UserOp; setScopeWithWebauthn→setScope (E3). §6 W2 + §9 risk updated. - security-review.md: header marks every finding addressed (#166/#171); Cancun. - data-model.md phone-first note + arch.md §22c.3: 'decided/London/delegate' → 'implemented #171 / Cancun #168 / passkey-signed UserOp'. Incorporates #163 comments (codex prerequisites + Kailai's Cancun/P-256 feasibility).
hanwencheng
added a commit
that referenced
this pull request
Jun 2, 2026
…162) * docs(web-flow): plan to wire parent-control UI to real backends (learn from wire demo) Add docs/plan/web-flow/wire-real-paths.md — an execution plan for turning every narrated / in-memory-stub path in the parent-control UI + daemon ui-bridge into the SAME real calls harness/phase1-wire-demo.sh makes (broker auth, broker cap-mint, on-chain cast writes, S3-backed memory worker). Key decisions captured: - Daemon-as-orchestrator: browser → daemon ui-bridge only; daemon makes the real broker/chain/worker calls (the data-model.md seam). - Reuse what exists: the daemon's proxy.rs broker client (reqwest + bearer + fail-closed) for broker calls; shell out to the existing agentkeys CLI + scripts/heima-*.sh (cast) for chain writes — no new Rust chain client. - The load-bearing K11 bridge: browser does navigator.credentials WebAuthn → daemon injects the assertion into the chain tx via a new --assertion-file mode on heima-scope-set.sh (avoids a double Touch ID). - Per-flow wiring tables (onboarding §9 stages 0–4, pairing §10.2, memory) mapping UI surface → data-model.md endpoint → real backend call → arch ref. - Sequenced phases W0–W6, harness-parity test, and reconciliation of stale specs (superseded bootstrap endpoints, --upgrade no-op). Cross-linked from issue-9step-flow.md as the execution detail for P2.1–P2.4. * docs(web-flow): phone-first host model + verified gating decision + WASM lift scope Amends the wiring plan for the phone-first reality (most operators have only a phone, no desktop), keeping one consistent implementation across hosts: - wire-real-paths.md §0.5 (host-model decision): factor the master-plane logic into one portable agentkeys-core hosted as WASM (web) / native lib (mobile, via UniFFI) / daemon (desktop), all behind the same lib/client AgentKeysClient contract. The daemon is demoted to one host, not a requirement; the broker is the only always-on component; the master plane is event-driven + biometric-gated (push-woken). - wire-real-paths.md §11 (gating decision, VERIFIED): read SidecarRegistry.sol + AgentKeysScope.sol. Every master write is msg.sender-bound to the operator secp256k1 key; the K11 P-256 assertion is an additional gate, not a substitute. => no relayer / key-free path without a contract change. Phone holds the secp256k1 key in the Keychain (SE is P-256-only, so it seals the K11 passkey, not the EVM key); browser/WASM cannot custody it and must delegate the broadcast. Fork (A) keep msg.sender-bound vs (B) move to assertion-only auth — recommend (A) for the phone MVP. - wire-real-paths.md §12 (WASM lift scope): agentkeys-core carve-out, wasm-bindgen exports, CoreBackend, WebAuthn interop, chain-write delegation — shared with the future mobile UniFFI shell. - data-model.md: flag the browser-direct prohibition as desktop-first, relaxed for the master plane (chain writes still constrained per §11). - arch.md §22c.3: master control-plane host-model paragraph (defers to the plan). * docs(web-flow): adversarial security review of §11 gating (codex) — (A) not sound as written Codex adversarial review of the §11 gating decision, verified against the contracts. Verdict: fork (A) is the right direction (contracts are NOT assertion-only-safe) but NOT sound as written. Findings (full doc: wire-real-paths-security-review.md): - CRITICAL: registerFirstMasterDevice unauthenticated first-call-wins → front-runnable operator lockout (SidecarRegistry.sol:100-123). - HIGH: registerAgentDevice/revokeAgentDevice are msg.sender-only, no K11 (:214-251) — a compromised master EVM key binds rogue agents with no biometric. - HIGH: add-master K11 challenge omits newActorOmni + K11 cred/pubkey/attestation (:167-193). - HIGH: 'phone holds the key' = software secp256k1 root (not SE-sealed) — weaker than the K11 hardware promise; model it as a first-class key. - HIGH: single global operatorMasterWallet (:66) ⇒ multi-device/recovery story incomplete. - HIGH: browser→host delegation needs a native confirmation that re-derives the challenge. - MEDIUM: AgentKeysScope doesn't update WebAuthn signCount; fork (B) unsafe until full-intent K11 binding lands on every path. Confirms the relayer analysis: non-custodial relayer impossible under (A) without meta-tx/ERC-4337 (EIP-2771 sponsors gas but still needs the secp key). §11 recommendation updated to reflect 'not sound as written' + required-changes checklist. * docs(web-flow): record ERC-4337 P-256 smart-account master as the confirmed decision Supersedes the §11 fork-A-as-MVP framing. The master becomes an ERC-4337 smart account whose validateUserOp verifies a P-256 (K11/passkey) signature; a bundler broadcasts, an optional paymaster sponsors gas. Resolves the codex findings: - removes the software-secp256k1 root (clients sign UserOps with the SE-sealed passkey only); - key-free + relayer in one (no custodial relayer; bundler + paymaster); - account address is the stable master → multiple passkeys + quorum recovery (multi-device gap); - web + mobile become symmetric full masters → the browser→host delegation hop dissolves; - reuses existing on-chain P-256 verify (K11Verifier.sol). Residual (folded into the contract-hardening issue): authenticated first-master bootstrap (CRITICAL), full-intent binding in validateUserOp, and Heima EntryPoint + Solidity-P-256 (London-level, no RIP-7212). Updated §0.5 table, §11 (decision block), §12 (X4/scope), the security-review doc, and arch.md §22c.3. * docs(web-flow): revise #162 plan for the landed chain work (#171 ERC-4337, #168 Cancun, #166 bootstrap) The chain side this plan waited on has landed; reconcile the web-wiring plan with it and correct two now-false facts. - Top status banner: ERC-4337 P-256 master account is IMPLEMENTED (#171, Solution A) — EntryPoint v0.7 + P256Account + factory + VerifyingPaymaster live on Heima mainnet; #164 closed. Heima is Cancun, not London (#168). Bootstrap front-run fixed (#166). Defers to docs/plan/chain/erc4337-master-account.md. - §11 (gating): retitled '✅ RESOLVED by #171 (historical)' + a correction note; the 'does-NOT-auto-fix' block rewritten as 'all addressed' (bootstrap #166; full-intent structural via userOpHash; agent-bind/multi-device #171 E3/E4/E5); fixed the false 'London / no PUSH0' claim → Cancun + pure-Solidity P-256 (~707k gas, no RIP-7212). - §4.3 + §12 X4: the K11→chain bridge is now an ERC-4337 UserOp (passkey-signs userOpHash → broker-gated bundler / handleOps → P256Account); reference erc4337-webauthn-sign.py + erc4337-master-e8.sh. Retired the cast/--assertion-file path. - §5a/§5b: onboarding stage 2 derives the P256Account (CREATE2 from passkey); stage 4 = one initCode+registerFirstMasterDevice UserOp (= chain-plan E7, routed through #163); bind/grant via UserOp; setScopeWithWebauthn→setScope (E3). §6 W2 + §9 risk updated. - security-review.md: header marks every finding addressed (#166/#171); Cancun. - data-model.md phone-first note + arch.md §22c.3: 'decided/London/delegate' → 'implemented #171 / Cancun #168 / passkey-signed UserOp'. Incorporates #163 comments (codex prerequisites + Kailai's Cancun/P-256 feasibility).
hanwencheng
added a commit
that referenced
this pull request
Jun 2, 2026
…cs/plan/ (#173) Contracts: merge docs/contracts.md (#171 superset) and the spec/ deployed-contracts redirect into a single canonical docs/spec/deployed-contracts.md; restore the ABI-summary + re-deploy sections #171 trimmed; add an explicit prod-vs-test EVM deployer-wallet table (local 0xdE64..63Bc, test/CI 0x9FE9..F259). docs/contracts.md is now a redirect. Repoint arch.md §5 + CLAUDE.md + operator-runbook-wire + the erc4337 plan. Plans: remove docs/spec/plans/. Active plans -> docs/plan/ (incl. the canonical milestones-roadmap + execution-plan); shipped/outdated -> docs/archived/ (issue-64/, issue-107-mcp-server-phase1, issue-144, issue-74-dev-key-service, agent-initiated-pairing-method-a, design-spec, eng-review-test-plan). Rewrite ~50 references repo-wide (docs, wiki, Rust doc-comments, Cargo.toml, harness/scripts, pm/, README) and depth-correct relative links in moved files. Rewrite docs/plan/README.md index. Verified: broken-link scan shows zero broken links to any moved/contract file; code changes are comment/doc-string only (no logic touched).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resolves the design + first implementation of #164 — migrate the master authority to an ERC-4337 P-256 smart-account (passkey-only master, no exportable secp256k1 key on any device). Plan → contracts → live mainnet infra → end-to-end demo, sequenced E0–E8. Also folds in the #168 EVM-level correction and consolidates the deployed-address registry.
Auth model (locked): Solution A — account-only / full-intent. The passkey signs the
userOpHash, which commits the entire UserOp (callData+ nonce + chainId + entryPoint), so the signature is a provably-complete full-intent authorization. That lets us retire the hand-rolled per-op K11 challenge + nonces in the registry/scope and replace them withmsg.sender == masterAccount+ the EntryPoint nonce.Verified on Heima mainnet (chainId 212013)
0x6672E1b315332167aBA12E0B1d3532a7e9B1ADE90x1ccCe65b22De81aDA4F378FeAf7503d93f5d27a3(CREATE2 determinism smoke-verified)addSigner) via a WebAuthn-signed UserOp throughhandleOps— acct0x3e79925F41E46CA87DD1103a572af7449CCd25a9,activeSignerCount 1→2.verifyAssertion → true, zero gas) before any spend.What landed
docs/plan/chain/erc4337-threat-model.md): userOpHash-is-full-intent, ED-aware funding + Sybil resistance (broker-co-signed sponsorship gate), bundler trust, review checklist.P256AccountFactorydeployed live + recorded.P256Account(WebAuthn via the deployed K11Verifier; multi-passkey signer set;execute/executeBatch) +P256AccountFactory(CREATE2). Codex-reviewed, 1 P2 fixed (verifier/decode reverts →SIG_VALIDATION_FAILED).AgentKeysScopethinned to account-auth (in-contract K11 +scopeNonceretired;setScopeWithWebauthn→setScope). E4 — agent bind/revoke closed structurally (master = account ⇒ passkey-gated, no new code).VerifyingPaymaster(broker-co-signed sponsorship = the Sybil gate; ED-aware) +scripts/erc4337-bundler.sh(unsafe-mode runner — Heima's Frontier RPC has nodebug_traceCall).harness/scripts/erc4337-webauthn-sign.pyWebAuthn UserOp signer.harness/erc4337-master-e8.sh, landed onharness/phase1-wire-demo.shasphase6(default in--real, skipped in--light); opt out--skip-6/AGENTKEYS_ERC4337_E8=0. Runbook updated.docs/contracts.mdis the new single deployed-address registry (anchored fromarch.md§5;docs/spec/deployed-contracts.md→ redirect;CLAUDE.mdref updated). docs: Heima EVM level is Cancun, not London (correct the rationale; keep the foundry pin) #168 stale "London EVM" comments corrected to Cancun inP256Verifier.sol/SidecarRegistry.sol/P256Verifier.t.sol.What did NOT land (residuals — by design)
addSigner(what E8 exercised) but not yetrecover(). Cutover redeploys the factory (E3/E5-complete account) + registry + scope, and updates the broker scope-mint,heima-scope-set.sh(setScopeWithWebauthn→setScope),operator-workstation.env(env_setthe EntryPoint/factory),verify-heima-contracts.sh, and arch.md §10/§12. CI skips first-master, so nothing breaks meanwhile. Full list in the plan §3.1.handleOpsproves the path; the bundler is the always-on automation layer.Reviewer notes
P256Account.sol,P256AccountFactory.sol,VerifyingPaymaster.sol. The threat model (docs/plan/chain/erc4337-threat-model.md§9) is the review checklist; this PR satisfies the codex pass, human review still recommended before the cutover redeploy.Relationships: resolves #164 · unblocks #163 · builds on #166, #168 · spawned #170.
🤖 Generated with Claude Code