← Index
Source: docs/features/jayverse-rabbit.md (auto-generated by scripts/generate-docs-html.mjs — edit the .md, not this file)

Jayverse — Account Abstraction (ERC-4337)

Purpose: give a Jayverse user a smart account so common cross-service actions — starting with a bet on a verex market — become gasless, one-click, and atomic, without ever holding Sepolia ETH or signing twice.

Design draft for review — not built. Source: 09-02-jayverse.md §1 "Rabbit as Portal" (jay's comment: "We start with AA which is ERC 4337. Show me the user scenario … the flow … It would be great to cooperate with my existing services."). Builds on the shipped /live/aa blocks and agentic-aa.md. Hub: README.md.


Phases (build order)

Phase Focus What we implement
1 (MVP) Gasless one-click bet thirdweb ERC-4337 smart account + sponsorGas paymaster; app/markets/ grid + Bet drawer; lib/aa-bet.ts batches approve + placeOrder into one sponsored UserOp against a real verex market; surfaces UserOp hash + receipt. Reuses /live/aa.
2 Local ↔ Sepolia bundler switch lib/aa-bundler.ts environment selector; scripts/aa-self-relay.mjs (call EntryPoint.handleOps on anvil); thirdweb bundler on Sepolia. Keyed by explicit AA_MODE, not chainId.
3 Identity & UX breadth one shared smart-account address across services; ERC-20 gas payment; recovery / social-login owner — handed to jayverse-wallet.

1. What we build (the basic feature)

One buildable slice, nothing more:

Explicitly out of scope for v1: an autonomous decision loop (that is the mandate console, see §6), ERC-20 gas payment (we sponsor first), cross-chain, and account recovery/social login.


2. User scenario

Mina has used Rabbit before and has MetaMask connected, but her wallet holds only a little test USDC and no Sepolia ETH.

  1. Mina opens the Markets tab in the Rabbit portal and sees a live verex market: "Will Jayverse ship the DeFi service before Oct?" — YES 0.62 / NO 0.38.
  2. She clicks Bet on YES, types 5 USDC, clicks Place bet.
  3. Rabbit tells her she has no smart account yet and shows Enable one-click betting. She clicks it and approves one MetaMask signature — no ETH, no gas prompt.
  4. A spinner: "Sponsoring gas… submitting your order." Behind the glass, her smart account is deployed, USDC is approved, and the order is placed — all in one bundle.
  5. Seconds later: "Bet placed — 5 USDC on YES @ 0.62. Gas paid by Jayverse." with a link to the UserOperation and the resulting on-chain tx.
  6. Her next bet skips step 3 entirely: the account already exists, so it is one click → one signature → done.

She never bought ETH, never issued a separate approval, never left the portal.


3. What the web app shows (screen by screen)

Portal → Markets (/markets, new)

Bet drawer (opens over the card)

States inside the drawer

Account page (/live/aa, existing — extended)


4. The flow (technical)

EOA is the signer/owner; the smart account is the actor; a bundler relays and a paymaster pays.

sequenceDiagram
    participant U as Mina (EOA owner)
    participant R as Rabbit portal
    participant SA as Smart account (4337)
    participant PM as Paymaster
    participant B as Bundler
    participant EP as EntryPoint
    participant VX as verex Exchange (Sepolia)

    U->>R: Click "Place bet (gasless)"
    R->>VX: GET market + quote (verex REST API)
    R->>SA: Build callData = executeBatch([approve USDC, placeOrder])
    R->>PM: Request sponsorship for this UserOp
    PM-->>R: paymasterAndData (gas covered)
    R->>U: Ask for ONE signature over the UserOp hash
    U-->>R: signature
    R->>B: eth_sendUserOperation(signed UserOp)
    B->>EP: handleOps([userOp])
    EP->>PM: validatePaymasterUserOp (pays gas)
    EP->>SA: validateUserOp (checks owner signature)
    EP->>SA: execute batch
    SA->>VX: approve(USDC) then placeOrder(...)
    VX-->>EP: order accepted
    EP-->>B: receipt
    B-->>R: UserOp receipt + tx hash
    R-->>U: "Bet placed — gas paid by Jayverse"

Key points:


5. How it cooperates with existing services

verex (prediction market) — the payoff. verex already runs a CLOB on Sepolia with a REST API serving markets/orders. Rabbit consumes that API for display and quoting, and the smart account calls verex's on-chain exchange inside the batch. verex needs no change for v1 beyond confirming its approve/placeOrder ABI; the smart-account address is just another trader to it. This is the concrete "gasless one-click bet" from verex §2 step 1, driven from the portal side. (Later, verex's planned verex-mcp lets the agent place the same order — same contract path, different caller.)

/live/aa building blocks — reuse, don't rebuild. The pillars page already runs a thirdweb ERC-4337 smart account with sponsorGas (paymaster) and sendBatchTransaction on Sepolia (AgenticPillars.tsx), plus the THIRDWEB_CLIENT_ID wiring and lib/thirdweb-client.ts. v1 is essentially those two demonstrated capabilities pointed at a real verex call instead of a no-op. The /live/aa page becomes the "how it works" explainer behind the Markets feature.

Mandate console — a deliberate contrast, not a dependency. The agent console uses ERC-7715/7710 mandates: the user pre-delegates a bounded permission (cap + expiry) to a session key, and an autonomous agent later spends within it, unattended, enforced by on-chain contracts. AA v1 is the opposite control flow — the human is in the driver's seat, pressing the button each time, and 4337's paymaster solves gas, not authority.

4337 paymaster (this doc) 7715/7710 mandate (console)
Who decides each action the human, per click the agent, within a pre-granted scope
What it removes the gas requirement the per-action signature prompt
Trust granted up front none beyond one signature a capped, time-boxed spend mandate
Failure bound atomic revert of the batch on-chain cap + expiry enforcement

They compose later: an agent holding a 7715 mandate can also route its trades through a 4337 account so its mandated bets are gasless and atomic.


6. Implementation sketch

Reused (already in the repo)

New

Stack choice

Open questions

  1. Paymaster budget & abuse. Sponsoring gas is a spend surface — per-user rate limit, per-day cap, allowlist to the verex exchange only. Where is the policy enforced (thirdweb dashboard vs our own paymaster)?
  2. Address identity across services. Should a user's Jayverse smart-account address be the identity verex/personas/bridge all recognize (one account, many services), or per- service? Leans toward one shared account — a jayverse-wallet decision.
  3. verex ABI shape. Is a single placeOrder on-chain call enough, or does the CLOB fill path need a signed order relayed to verex's API first (hybrid off-chain order / on-chain settle)? Determines what actually goes in the batch.
  4. Approval strategy. Batch a fresh exact approve each time (safe, atomic) vs a one- time larger allowance (fewer ops, wider blast radius). v1: exact approve in the batch.
  5. Owner key model. MetaMask EOA owner for v1; embedded/social-login owner and recovery are deferred to jayverse-wallet.

Estimate: ~2–3 focused days on top of /live/aa for the happy path (Markets grid, Bet drawer, batched sponsored UserOp against a real verex market), plus paymaster-policy and error-decoding hardening.


7. Local anvil vs Sepolia — the bundler switch (self-relay local)

Decision (jay, 2026-09-07): don't implement a bundler — self-relay locally, thirdweb in the cloud, behind one environment switch.

The bundler is not a Jayverse service (see README.md). It is a hosted relay that submits EntryPoint.handleOps on a public chain. That works for Sepolia, but not for a local anvil fork: anvil reports Sepolia's chainId (11155111), so thirdweb's chainId-keyed bundler would route to the real Sepolia, never to 127.0.0.1:8545. Local tests therefore need a different bundler path — and we do not write one.

Local (anvil) — self-relay Sepolia (deployed)
Bundler none — a script calls EntryPoint.handleOps([userOp], beneficiary) from a funded anvil account (we are the bundler for that one call) thirdweb (hosted)
Paymaster skip — pre-fund the smart account with anvil ETH thirdweb sponsorGas
EntryPoint canonical 0x5FF1…2789, inherited by the fork same canonical address
RPC 127.0.0.1:8545 Alchemy Sepolia
Library viem / permissionless.js (drives handleOps directly) thirdweb React SDK

Why self-relay, not Alto. Running Alto (Pimlico's open-source bundler) locally is possible and gives a byte-for-byte real bundler RPC, but it is a daemon to stand up. Self-relay is a ~20-line script that proves the whole AA flow — a UserOp validates, batches via executeBatch, and executes — with the least plumbing. Reach for Alto only if we later want the local path to exercise the real eth_sendUserOperation mempool path.

The switch. One selector (a jayverse-wallet / jayverse-rails concern) returns { mode, bundlerUrl, paymaster } for the active environment:

Gotcha — chainId collides. Because anvil fakes Sepolia's chainId, a chainId check cannot tell the two apart. The selector keys off an explicit flag (APP_MODE=local / an RPC-URL check), never chainId alone.

New for local:


Agent-payment observatory (planned — build starts 2026-09-10)

A read-only dashboard that watches the agent-payment market while it is still quiet. Agent commerce (x402, ERC-8004) is mostly demos and near-wash-trading today, but the structure — which standard wins, which facilitator settles, which chain the activity lands on — is being set now, at low volume. Human payments grow one user at a time; agent payments jump the moment a line of code changes. So the honest move is to read the structure early, not to wait for volume.

What it shows (reads on-chain, signs nothing):

Why it belongs to Rabbit: Rabbit is the agentic portal — the read/observe layer for agent activity. It sits on top of the infra the plan already has: key custody → jayverse-wallet.md, spend policy/caps → the mandate agent (§ agentic-aa), audit/authority → jayverse-auditor.md. The observatory is the missing watch surface over those.

What I'll build tomorrow (2026-09-10)

  1. Scaffold a read-only /agent-payments route in the rabbit app (no keys, no signing).
  2. Pick the data source for one facilitator first (Coinbase-hosted default): the minimal on-chain query for x402 settlements on Base (public RPC or a light indexer). Define the row schema — { agent, facilitator, service, cents, chain, settledAt, txHash }.
  3. Render a live feed grouped by facilitator, each group carrying its authority row (settle / see / fee), reusing the framing from x402-facilitator-market.
  4. Add a second facilitator + Polygon once one works, so "swap the facilitator" is a real test.
  5. Cross-link the surface to wallet (keys), the mandate agent (caps), and the Authority Auditor (audit).
  6. Promote the blinking x402-facilitator-market card to a dedicated Agent-payment observatory card once the route renders something real.

Status: planned, design-only. The live x402-facilitator-market card blinks to mark this as the active track. Nothing here is built yet.


Chainlink's oracle stack is settlement-rail infrastructure Rabbit consumes, not reimplements — see the umbrella map in README.md.

As the portal that imports each product, Rabbit also inherits every product's Chainlink dependency listed in the umbrella map. Note the stakes: the agent is the highest-authority component, so a wrong or late feed driving an autonomous action is more dangerous here than anywhere else — guard feeds (staleness / bounds) before the agent acts on them.

Every feed is a dependency with a failure mode — keep the "if wrong / late" guard in code, not only here.

The trust assumption lives in config, not code (session keys, auto-approve)

A general law surfaced by LayerZero's default-verifier problem, and it lands hardest here because the agent is the highest-authority component: a default you never chose is still a choice, and it is invisible because it lives in configuration, not code.

The habit: at every moment the question who can act alone right now has an answer; the only variable is whether anyone wrote it down where a change would break it. So record the agent's session-key scope + mandate bounds, and add a test / CI check asserting the live config equals the documented one — the trust assumption then fails loudly when it moves, the same reasoning as "keep the 'if wrong / late' feed guard in code, not only in the table."

This is exactly what the Authority Auditor renders — point it at the agent's own config and the answer becomes a row, not a footnote.