Skip to content

About

A capability-based delegation system on account abstraction that lets users grant scoped, time-bounded session keys to an AI agent for authorizing ERC20 and DeFi transactions under oracle-enforced USD spend caps — without exposing the owner's private key.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

SessionHandler Protocol 🔑🤖⛓️

CI License: BUSL-1.1 Solidity Built with Foundry Python

Agentic wallet infrastructure.

SessionHandler Protocol gives every user a programmable smart account and lets an AI agent run it for them across DeFi — swapping, paying, providing liquidity and building on-chain reputation — from plain-language instructions. The user keeps the root key. The agent prepares each transaction, and the user approves it with a passkey on their own device — Face ID, a fingerprint or a PIN. And the protocol, not the agent, decides what is allowed to happen.

It is built from the standards the ecosystem is converging on:

  • ERC-4337 account abstraction — every agent action is a UserOperation through the canonical EntryPoint
  • ERC-7579 modular accounts — behaviour is added by installing modules, not by rewriting the account
  • ERC-8004 agent identity and reputation — the protocol's agent is a registered on-chain identity that users can review
  • Chainlink price feeds — every limit and fee is expressed in US dollars, on every chain

The reference application is a web app in web/: sign in with a browser wallet, deploy a wallet with one signature, set its rules, and talk to the assistant on the web or in Telegram.


Demo

AI agent powered smart wallet — demo video


How the protocol is built

The protocol is built in layers: a small on-chain core that enforces the rules, shared contracts that configure it, integrations with DeFi, token and identity protocols, a runtime for the agent, and the applications people actually use.

┌─────────────────────────────────────────────────────────────────────────────┐
│  APPLICATIONS       web app  ·  Telegram bot  ·  CLI agent                  │
├─────────────────────────────────────────────────────────────────────────────┤
│  SERVICE LAYER      FastAPI  ·  accounts, wallet sign-in (SIWE)             │
│                     wallet-signed contacts  ·  owner actions in-browser     │
├─────────────────────────────────────────────────────────────────────────────┤
│  AGENT RUNTIME      LangChain agent (DeepSeek by default, any chat model)   │
│                     the account is injected — the model can't choose it     │
├─────────────────────────────────────────────────────────────────────────────┤
│  INTEGRATIONS       ERC-20 · Uniswap V3 / PancakeSwap V2 · ERC-8004         │
│                     Chainlink pricing · UserOp builder and bundler          │
├─────────────────────────────────────────────────────────────────────────────┤
│  KEY CUSTODY        passkeys on users' devices — the server holds none      │
├─────────────────────────────────────────────────────────────────────────────┤
│  CORE (on-chain)    SessionHandler account  +  SpendingLimitModule hook     │
│                     every agent transaction is checked here                 │
├─────────────────────────────────────────────────────────────────────────────┤
│  SHARED CONTRACTS   SHTreasury · SHRegistry · SHOracle · SHFactory          │
├─────────────────────────────────────────────────────────────────────────────┤
│  CHAINS             Ethereum · Arbitrum · Base · BNB Chain                  │
│                     Sepolia · Anvil                                         │
└─────────────────────────────────────────────────────────────────────────────┘

Core — the smart account and its spending-limit hook

SessionHandler is each user's ERC-7579 smart account, one per user per chain. It checks its own UserOperations and accepts a signature from either the owner or one of up to five approved session keys. A session key is a passkey, one per device: the device or the user's password manager keeps it, never the server, and every signature needs Face ID, a fingerprint or the device PIN, checked on-chain through the chain's P-256 precompile. A session key can move value but can never touch the account's own settings, so nothing signed with one can loosen the wallet's rules — and it expires, so a key nobody renews stops working on its own rather than living forever.

Every transaction the account executes is wrapped by SpendingLimitModule, an ERC-7579 hook. It measures how much US-dollar value left the wallet (native coin plus the watched tokens, before vs. after) and refuses anything that would go past the owner's limit for the current window. Because it measures the result rather than reading calldata, it works the same for a transfer, a swap on any venue, or a liquidity move: a fair swap costs almost nothing against the limit, a bad-rate or draining swap costs what was actually lost. It also forbids standing token approvals — an approval must be used up within the same transaction, unless it goes to a spender the owner trusts (like the DEX router).

The account also caps what session keys can spend on gas, which the hook cannot see: a ceiling per operation and a budget per day across all of them, both set per chain at deploy and adjustable by the owner.

This hook is what turns "an AI can move my money" into "at most $X per day can leave my wallet, whatever goes wrong". Everything above it in the stack can fail — a confused model, a bad prompt, a compromised server — and the most that can leave the wallet is still bounded here. It is a standalone module, so any ERC-7579 account can install it.

Owners also keep full control of the wallet: pause the wallet, remove any device's key, change the limit and window, choose which tokens count, trust spenders, cap network fees, and withdraw.

Shared contracts — one set per chain

Contract Role
SHTreasury The admin root. Owns the registry, oracle and factory, and collects protocol fees.
SHRegistry Central settings every wallet reads live: EntryPoint, ERC-8004 registries, oracle, fee, treasury, agent id, and the module new wallets install. A change here reaches every wallet with no redeploy. Swapping the oracle takes a 2-day timelock.
SHOracle Turns token amounts into US dollars with Chainlink feeds. Refuses stale prices, and on L2s refuses to price while the sequencer is down.
SHFactory Deploys wallets with CREATE2, so the address is known before the transaction. One signature creates the wallet, installs the hook with its limit, authorizes the first device's passkey, trusts the chain's exchange contracts and funds it.
SHTreasury  (operator — admin root, fee sink)
   ├── SHRegistry ── live settings for every wallet
   │      └── SHOracle ── Chainlink USD prices (2-day timelocked swap)
   └── SHFactory ── deployWallet(...) in one transaction
            └── SessionHandler  (per user, per chain)
                   ├── validates owner + passkey signatures
                   ├── guards its own admin surface from session keys
                   ├── pays a small flat protocol fee per agent action
                   └── hook ──▶ SpendingLimitModule ──▶ SHOracle

Protocol economics. Each agent-driven execution pays a flat fee in the chain's native coin: about $0.015 at launch, and always within bounds set at deploy to about $0.005–$0.10. Owner actions are free. See docs/contracts.md.

How one instruction flows through the system

"Swap $200 of ETH for USDC"
   │
   ▼  app (web / Telegram / CLI) → API → agent runtime
   │     agent checks remaining budget, gets a quote, runs a preflight
   ▼  integrations build [approve, swap, approve 0] as one batch
   ▼  the user taps Confirm and approves with their passkey (Face ID / fingerprint / PIN),
   │     which signs that exact UserOp on their own device
   ▼  ERC-4337 EntryPoint → SessionHandler.validateUserOp
   ▼  SpendingLimitModule.preCheck  → snapshot the wallet's USD value
   ▼  the swap runs on Uniswap / PancakeSwap
   ▼  SpendingLimitModule.postCheck → value lost ≤ remaining limit? else revert
   ▼  receipt back to the user in plain language

What the agent can do

The agent has 43 tools across four areas (each network shows it only its own exchange's liquidity tools):

  • Payments — native and ERC-20 transfers to contacts the owner has saved on the web, each one signed by the owner's wallet (the contact list is the destination allowlist; the agent can read it, never edit it)
  • DeFi trading and liquidity — quotes, one swap tool for every swap shape, Uniswap V3 liquidity positions (price ranges to pick from, deposits, fee collection, withdrawals) and PancakeSwap V2 liquidity on BNB Chain, wrapping, with the wallet's checks run before any write
  • Budget awareness — remaining limit, whether a planned spend fits, live USD prices
  • Identity and reputation (ERC-8004) — look up agents, read and give feedback, resolve registration files; the protocol's own agent is registered on-chain and user wallets act as its reviewers

The LLM is DeepSeek (deepseek-flash) by default; any LangChain chat model can be swapped in with a small edit to app/smart_wallet_agent.py.

Security model in one paragraph

The owner key never leaves the user's browser wallet — deploying and every owner action are signed there, so the service is non-custodial. Session keys are passkeys on the user's own devices, so the server holds no key that can move funds, and every agent transaction waits for the user's Face ID, fingerprint or PIN on that exact operation. The agent is handed the user's account by the runtime and cannot pick another. And every agent transaction, no matter which layer produced it, passes through the spending-limit hook on-chain, with its gas capped per operation and per day. API keys are masked in every error, log line and tool result before they leave the server. Full analysis: THREAT_MODEL.md.


Networks

Network Chain ID DEX Status
Ethereum mainnet 1 Uniswap V3 fork-tested
Arbitrum One 42161 Uniswap V3 fork-tested, with L2 sequencer check
Base 8453 Uniswap V3 fork-tested, with L2 sequencer check
BNB Chain 56 PancakeSwap V2 fork-tested, live deploy path
Sepolia 11155111 Uniswap V3 live testnet + fork
Anvil 31337 mocks local

Celo has partial scaffolding in the Python layer but no contract deployment path yet. New chains are added with the add-network skill in .claude/skills/.


Prerequisites

  • Foundry
  • Python 3.12+
  • Node 22.22+ — only for the web app in web/
  • An LLM API key — DEEPSEEK_API_KEY by default, or any other LangChain chat model
  • An Alchemy API key — for live Sepolia, the mainnet / BSC / Arbitrum / Base forks, and the History tab on Base
  • (Optional) A Telegram bot from @BotFather — one bot serves every network — only for make bot

Clone and install

git clone https://github.com/Conrad-sudo/sh-protocol.git
cd sh-protocol

# Contracts
forge install

# Python back end and agent
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

# Web app (optional)
cd web && npm install

Quick start (local Sepolia fork)

make sepolia-fork                             # terminal 1: local fork with real Chainlink feeds
make setup-agent ARGS="sepolia-fork"          # terminal 2: deploy protocol → db → wallet → CLI agent

To run the full app instead: COOKIE_SECURE=0 make api and cd web && npm run dev, then open http://localhost:3000. Every network and step is explained in docs/setup.md.

Tests

forge test --no-match-path "test/fork/*"   # unit, guard, admin and invariant suites
make py-test                # the seventeen offline Python suites (no chain, no model)
make e2e-test               # full user journey against a running fork (Sepolia; ARGS=arbitrum-fork, ARGS=base-fork, ...)
make agent-smoke            # a real agent conversation against the fork
cd web && npm test && npm run e2e

CI (.github/workflows/test.yml) checks the smart contracts only, on every push: the Foundry suites with contract sizes and Slither (failing on any high-impact finding). The fork suites run nightly when their RPC secrets (MAINNET_RPC_URL, SEPOLIA_RPC_URL, BSC_RPC_URL, ARB_RPC_URL, BASE_RPC_URL) are set. The Python and web suites run locally (see the Makefile and web/package.json).

Deploying

deploy/Caddyfile runs the whole service on one server: HTTPS, security headers (a Content-Security-Policy among them, kept in step with the web app by web/src/securityHeaders.test.ts), the built web app, and /api passed to the API started with make api-prod. Its header comment lists what to set first. Before real money goes in, the protocol's admin key must be separate from the API's (THREAT_MODEL.md §4.4), and WEB_APP_URL must be the site's public https address, since passkeys are bound to that domain for life (docs/environment.md).


Documentation

Document Contents
docs/contracts.md Every contract, the spending-limit design, the fee, the test suite
docs/app.md Agent runtime, integrations, bundler, API and Telegram bot
web/README.md The web app
docs/environment.md Environment variables
docs/setup.md Local, fork and live deployments
docs/langchain-packages-migration.md Why ERC-20 / Uniswap calldata comes from external packages
docs/reference.md Makefile reference, project structure, dependencies
THREAT_MODEL.md Assets, trust boundaries, on-chain and off-chain threats

License

Business Source License 1.1 until 2029-06-12, after which it becomes MIT. The interfaces in src/interfaces/ and the test mocks in src/mocks/ stay MIT (some are adapted from Chainlink and OpenZeppelin), as do the tests and deploy scripts.

About

A capability-based delegation system on account abstraction that lets users grant scoped, time-bounded session keys to an AI agent for authorizing ERC20 and DeFi transactions under oracle-enforced USD spend caps — without exposing the owner's private key.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages