Skip to content
ev4mrzPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

Session Key Smart Wallet — ETHGlobal PoC

An ERC-4337 smart account owned by a passkey (WebAuthn / secp256r1) instead of a raw private key, that can delegate a scoped, time-boxed session key so a dApp can send a burst of transactions with zero further wallet prompts. Demoed with a tiny on-chain clicker game: create your wallet once (Face ID / Touch ID), grant a session key once, then click away.

Why

Every wallet interaction today means a signature prompt. That's fine for a single high-value transfer, but it kills UX for anything interactive — games, trading bots, subscriptions. Session keys let a user pre-authorize a narrow slice of what a dApp can do (which contract, which function, until when, how many times) and then get out of the way.

Architecture

Browser (passkey)                 EntryPoint (ERC-4337, v0.6)             Chain
  |                                        |                                |
  |--WebAuthn assertion (owner sig)------->|                                |
  |            mode 0: passkey/P-256       |--validateUserOp-->  SmartAccount
  |                                        |                       |
  |--ECDSA sig (session key)-------------->|                       |--scoped to
  |            mode 1: secp256k1           |                       |   target+selector
  |                                        |                       |   +expiry+usage cap
  |                                        |--execute()----------->GameContract.click()
  • SmartAccount.sol — ERC-4337 IAccount. Signature is mode byte || payload:
    • 0x00: WebAuthn assertion, verified on-chain against the owner's P-256 public key via [base-org/webauthn-sol] (RIP-7212 precompile with a FreshCryptoLib fallback).
    • 0x01: plain ECDSA from a session key, checked against that key's SessionKeyPermission (target contract, function selector, validUntil, maxUses). Only reachable through execute(), so a session key can never touch anything outside its scope.
  • SmartAccountFactory.sol — CREATE2 factory, deterministic account address per owner pubkey + salt (standard ERC-4337 counterfactual-deploy pattern).
  • GameContract.sol — trivial on-chain clicker used as the demo target.
  • Mock bundler (frontend/src/app/api/bundler) — for local/demo reliability this relays a signed UserOperation straight to EntryPoint.handleOps using a funded Anvil dev key, instead of depending on a real bundler network. Swapping in Pimlico (eth_sendUserOperation) is the natural next step for a testnet deployment.

What's novel here / judging angles

  • Passkey-native account abstraction: no seed phrase, no browser extension — the owner key lives in the device's secure enclave.
  • Scoped session keys: not just "any tx for N hours" but target + selector + expiry + usage cap, enforced entirely inside _validateSignature — no separate session-key registry contract needed.
  • From-scratch ERC-4337 account (built on the reference EntryPoint/BaseAccount, not a forked wallet SDK), so the validation logic is fully auditable in ~200 lines.

Repo layout

contracts/   Foundry project (SmartAccount, SmartAccountFactory, GameContract, tests)
frontend/    Next.js app: passkey creation, session key demo, clicker UI

Running the demo locally

# 0. Install contract dependencies (vendored libs are gitignored)
cd contracts
forge install eth-infinitism/account-abstraction@v0.6.0 --no-git
forge install OpenZeppelin/openzeppelin-contracts --no-git
forge install base-org/webauthn-sol --no-git
git clone --branch v4.9.3 --depth 1 https://github.com/OpenZeppelin/openzeppelin-contracts lib/openzeppelin-contracts-v4
git -C lib/webauthn-sol submodule update --init --recursive
# if FreshCryptoLib fails to check out (broken submodule ref upstream at time of
# writing), clone it manually and pin to the commit webauthn-sol expects:
#   git clone https://github.com/rdubois-crypto/FreshCryptoLib lib/webauthn-sol/lib/FreshCryptoLib
#   git -C lib/webauthn-sol/lib/FreshCryptoLib checkout 76f3f135b7b27d2aa519f265b56bfc49a2573ab5

# 1. Local chain
anvil

# 2. Deploy contracts
forge test                 # 4 tests covering grant/click/expiry/scope/revocation
forge script script/Deploy.s.sol \
  --rpc-url http://127.0.0.1:8545 \
  --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  --broadcast
# copy the printed EntryPoint / SmartAccountFactory / GameContract addresses into
# frontend/.env.local if they differ from the defaults already there

# 3. Frontend
cd ../frontend
npm install
npm run dev
# open http://localhost:3000 in a browser with a platform authenticator
# (Touch ID / Face ID / Windows Hello / a security key) — WebAuthn requires
# a real authenticator and can't be driven from curl or headless tooling.

Demo flow in the browser:

  1. Create wallet — triggers a passkey creation prompt, deploys a SmartAccount owned by that passkey, funds it.
  2. Grant session key — one more passkey prompt, authorizing a session key (generated locally, stored in localStorage for the demo) to call GameContract.click() up to 50 times over the next 24h.
  3. Click — sends a session-key-signed UserOperation through the mock bundler. No further prompts.

Known limitations (PoC scope, called out deliberately)

  • Session key material is kept in localStorage in cleartext — fine for a demo, not for production (would live in memory / be wrapped by the OS keystore).
  • The "bundler" is a single relayer key calling handleOps directly, not a real bundler mempool — good enough to prove the account + session-key logic end-to-end without adding external infra dependency risk to a hackathon demo.
  • No paymaster: the account pre-funds its own EntryPoint deposit. Sponsoring gas via a paymaster is the natural next feature.
  • SmartAccountFactory.getAddress doesn't fold owner pubkey into the CREATE2 salt computation (only entryPoint + salt are), since the owner key is set via initialize() after deployment — fine for a single demo account per salt, would need revisiting for multi-owner factories.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages