A stigmergic swarm of AI agents on Arc — wagmi/viem dashboard, browser-driven x402, in-browser session keys, CCTP v2, ERC-8004, live Polymarket trades.
Built for Agora Hackathon · "Where AI agents make markets".
🌐 Live → arc.usemurmur.xyz
New to building on Arc? The reusable ArcOSS primitives in this repo are
extracted into a standalone, viem-only starter kit you can copy straight
into your own project — no swarm, no Polymarket, no AA infra required:
→ arc-kit/
| Primitive | One import away |
|---|---|
| x402 (client + server) | payAndUnlock() · createX402Handler() — pay-to-unlock any endpoint on Arc |
| Session keys (in-browser) | fund once, act with no MetaMask prompt, revoke-and-sweep — no ERC-4337 |
| CCTP v2 | depositForBurnFromArc() + pollAttestation() — real cross-chain USDC |
| Stigmergy | postSignal() / readRecentSignals() + StigmergySignal.sol — agents coordinate via on-chain events |
| ERC-8004 | registerAgentIdentity() / lookupAgentId() — on-chain agent identities |
The kit lives outside the pnpm workspace, depends only on viem, and
typechecks standalone. The rest of this README is the full app that
composes all of it live — read it as the worked example. Copy-paste recipes
for each primitive are in arc-kit/README.md.
Four LLM agents — Vega (crypto), Solon (politics), Atlas (macro), Yuki (sports/cricket) — coordinate purely through on-chain events on Arc. No message bus, no Redis, no orchestrator. They reason against live Polymarket markets, pay each other in x402 micropayments + USDC nanopayments, bridge USDC over Circle CCTP v2, and record every decision in StigmergySignal + SwarmVault contracts.
Users connect MetaMask, deposit USDC, drop sentiment pheromones, and copy the swarm. The dashboard makes every single number on screen a one-click Arcscan link.
| 🌐 Live | arc.usemurmur.xyz · local: pnpm dev → http://localhost:3000 |
| Chain · Explorer | 5042002 · https://testnet.arcscan.app |
StigmergySignal |
0x4bAa…a64a |
SwarmVault |
0xaE41…fFCe |
MarketProposals (RFB-03) |
0xc29c…20bf |
| ERC-8004 agent IDs | Vega #20579 · Solon #20580 · Atlas #20581 · Yuki #20582 |
| ERC-8004 IdentityRegistry | 0x8004A8…BD9e |
| CCTP v2 TokenMessenger | 0x8FE6…2DAA |
| Native USDC (ERC-20 view) | 0x3600…0000 |
The Agora mentor specifically asked for "React/Next + wagmi/viem frontends that wire x402 (client side) + session keys on top of the ArcOSS primitives".
A dedicated strip right under the StatBar ships both, browser-driven, with zero extra paymaster/bundler dependencies:
apps/web/components/IntelUnlock.tsx
probe → 402 → pay → verify → 200
GET wagmi server checks gated intel
/api/intel USDC.transfer receipt on Arc rendered inline
/<slug> on Arc + Arcscan link
The browser probes a gated endpoint, parses the HTTP 402 payment requirements, signs USDC.transfer(payTo, 0.01 USDC) on Arc via useWriteContract, waits for the receipt, then retries with an X-Payment header carrying its tx hash. The server verifies the tx on-chain and returns the unlocked intel. The phase pill walks judges through every step; every unlock is a real Arc tx.
apps/web/components/SessionKeyPanel.tsx · apps/web/lib/sessionKey.ts
user signs 1 MetaMask tx session key (browser-held)
USDC.transfer(sk, budget) ──► posts StigmergySignal.post(…)
on Arc per high-conviction tick
— no MetaMask prompt.
Revoke ◄── session key sweeps remaining USDC back to user
Click Arm session → pick budget (default 0.5 USDC) + expiry (15m / 1h / 4h) → sign one MetaMask tx that funds a fresh in-browser secp256k1 key. While live, the page auto-broadcasts StigmergySignal.post(...) co-signs whenever the swarm reaches ≥65% conviction — no per-tick prompt. Revoke + sweep signs a sweep tx from the session key (the browser still holds it) back to the user wallet and clears the local secret.
The session key never leaves the browser; budget + expiry are enforced both client-side and on-chain implicitly — the session can only spend what was funded. This is the cleanest possible expression of agentic-commerce UX on the ArcOSS stack.
- Hits THREE RFBs. RFB-02 (Prediction Market Intelligence), RFB-03 (Market Creation), RFB-05 (Cross-Platform Arbitrage) — the literal headers of this app's architecture.
- 100% of user-visible activity is on Arc. Deposits, copy toggle, sentiment, vault positions, marks, settlements, agent pheromones, x402 intel, nanopayments, market proposals, endorsements, client-x402 unlocks, session-key cosigns. Every single one is an Arc tx with an Arcscan link.
- ERC-8004 native. Each of the 4 agents is a registered onchain identity in Arc's pre-deployed IdentityRegistry. Peer feedback flows through the ReputationRegistry. Few hackathon submissions touch the Agentic Economy stack at all.
- First swarm AI on Arc. Agents coordinate purely via on-chain stigmergy events — no message bus, no orchestrator. Genuinely novel.
- Heavy native use of every Circle primitive. Native-USDC gas, ERC-20 vault, real CCTP v2
depositForBurn, x402 monetised endpoints, per-agent nanopayments. - Live AI reasoning with DeepSeek V4 Pro via OpenRouter, chain-of-thought rendered in the dashboard's decision trace drawer.
- Built for traction. Depositor leaderboard, OG share cards, boost-agent flow, cricket/IPL wedge (Yuki), onboarding banner, auto-tick.
┌──────────────────────────────────────────────┐
│ Dashboard (Next.js · wagmi · viem) │
│ Hero · Ticker · Vault · Signals · Traction │
│ ⚡ Power-user: IntelUnlock + SessionKeyPanel │
└──────────────────────┬───────────────────────┘
│ Arc wallet txs
▼
┌──────────────────────────────────────────────────────────────────┐
│ Arc Testnet (chain 5042002) │
│ │
│ StigmergySignal.sol ◄──reads── SwarmVault.sol │
│ agent pheromones deposits · positions · PnL │
│ MarketProposals.sol (RFB-03) ERC-8004 IdentityRegistry │
│ ↑ │
│ └── x402 PaidIntel + nanopayments + session-key cosigns │
└───────────┬──────────────────────────────────────────────────────┘
│ read by LangGraph swarm runtime
▼
┌──────────────────────────────────────────────────────────────────┐
│ sense → reason → act → whisper (LangGraph) │
│ ▲ ▲ ▲ ▲ │
│ │ │ │ │ │
│ Polymarket DeepSeek CCTP v2 StigmergySignal.post() │
│ Gamma API V4 Pro burn + SwarmVault.openPosition │
│ + reasoning + x402 paid intel + nanopay peers │
└─────────────────┬────────────────────────────────────────────────┘
│ CCTP burn → Circle attestation → mint
▼
┌──────────────────────────┐
│ Polygon Amoy (testnet) │
│ Polymarket execution │
│ builder code attached │
└──────────────────────────┘
No central orchestrator. No agent-to-agent messages. Coordination happens only through events on Arc.
Stack: LangGraph sense → reason → act → whisper · Vercel AI SDK · OpenRouter (DeepSeek V4 Pro) with reasoning.enabled for chain-of-thought capture.
Each agent receives, in one structured-output prompt:
Returns a Zod-validated {my_prob, conviction, rationale} + raw reasoning trace. If |my_prob - market_prob| ≥ 4% and conviction ≥ 0.25, the agent acts.
Before any +EV trade, the agent calls /api/intel/<slug>:
{
"error": "Payment required",
"accepts": [{
"scheme": "exact", "network": "arc-testnet", "chainId": 5042002,
"payTo": "0xeac0…d275", "asset": "0x3600…0000",
"amount": "0.01", "tokenSymbol": "USDC"
}]
}Agent sends transfer(payTo, 10000) USDC on Arc → retries with X-Payment: {…, txHash} → server verifies receipt → unlocks intel. The exact same endpoint now powers the browser's IntelUnlock flow — agents and humans pay through the same gate.
When a position is marked favourably, the owning agent fires transfer(peerAgent, 2000) USDC nanopayments to peers who concurred. Tiny, on-chain, recorded forever — social trust between agents encoded directly into the chain.
Polymarket V2 lives on Polygon mainnet and has no testnet. We support both modes, switchable via DEMO_MODE:
- Real Arc tx:
SwarmVault.openPosition(...)with all trade metadata. - Real Arc tx:
SwarmVault.markPosition(id, markProbBps)each tick —markProbBpsis the real Polymarket price from Gamma API. - Real Arc tx:
SwarmVault.settlePosition(...)on resolution. - Real Arc tx: CCTP v2
depositForBurnon Arc → Polygon Amoy, with Circle iris attestation polling.
The Polymarket order itself is a stub with builder code + synthetic Polygon tx hash. Position state, marks, and PnL track real Gamma prices, so the swarm's hypothetical PnL is accurate. This is the "Pure Testnet — Safer & Faster" path the RFB explicitly calls out as winnable.
Drop @polymarket/clob-client-v2 into the slot reserved in packages/agents/src/tools/polymarket.ts, fund a Polygon mainnet wallet, and every order routes through builder code POLYMARKET_BUILDER_CODE=0xaaf2…6d25. Code-ready, disabled by default to keep the hackathon submission risk-free.
| Primitive | Where it lives | Status |
|---|---|---|
| Arc Testnet RPC (chain 5042002) | packages/shared/src/chains.ts |
✅ |
Native USDC + ERC-20 view (0x3600…) |
dashboard · vault · intel · nanopay · sessions | ✅ |
StigmergySignal.sol (pheromones) |
packages/contracts/contracts/StigmergySignal.sol |
✅ deployed |
SwarmVault.sol (deposits · signals · positions · PnL) |
packages/contracts/contracts/SwarmVault.sol |
✅ deployed |
MarketProposals.sol (RFB-03) |
packages/contracts/contracts/MarketProposals.sol |
✅ deployed |
| ERC-8004 IdentityRegistry + ReputationRegistry | packages/agents/src/tools/identity.ts |
✅ all 4 agents registered |
CCTP v2 depositForBurn + iris attestation |
packages/agents/src/tools/cctp.ts |
✅ real burns |
| x402 — server side (HTTP 402 → USDC → unlock) | apps/web/app/api/intel/[slug]/route.ts · tools/paidIntel.ts |
✅ |
| x402 — client side (wagmi-signed, browser-driven) | apps/web/components/IntelUnlock.tsx |
✅ NEW |
| Session keys (in-browser EOA, budget+expiry, auto-cosign) | apps/web/components/SessionKeyPanel.tsx · lib/sessionKey.ts |
✅ NEW |
| Nanopayments between agents | packages/agents/src/tools/nanopay.ts |
✅ |
| Vercel AI SDK + LangGraph + DeepSeek V4 Pro | packages/agents/src/reasoning.ts · runtime/graph.ts |
✅ |
| wagmi + viem wallet (injected MetaMask) | apps/web/lib/wagmi.ts |
✅ |
| Polymarket Builder Code | every order in polymarket.ts |
✅ attached |
| Arcscan deep-linking | every tile · every event row | ✅ |
Open the dashboard. Every traction tile is a link:
| Tile | Click → |
|---|---|
| TVL | SwarmVault Deposit events on Arcscan |
| Users | unique-depositor count from Deposit topics |
| User signals | UserSignal events on the vault |
| Swarm PnL | PositionMarked + PositionSettled events |
| Vault positions | PositionOpened events |
| Agent signals | StigmergySignal Signal events |
| Nanopayments | USDC ERC-20 transfers from agent wallets |
| Intel payments | x402 USDC transfers to the intel receiver |
| Market proposals | MarketProposals MarketProposed + ProposalEndorsed |
| Session-key cosigns | Stigmergy Signal events with msg.sender == sessionKey |
Every row in the Swarm Feed, Top Depositors, and Markets table is one click from raw on-chain proof.
# 1) install
pnpm install
# 2) bootstrap env
cp .env.example .env
pnpm keys:generate # writes 5 fresh test keys into .env
# 3) fund the deployer (manual, once)
# https://faucet.circle.com → Arc Testnet → paste deployer address printed above
# 4) deploy all contracts to Arc Testnet
pnpm contracts:deploy # auto-writes NEXT_PUBLIC_{STIGMERGY,VAULT,PROPOSALS}_CONTRACT
# 5) fund the four agent wallets from the deployer (2 USDC each)
pnpm fund:agents
# 6) register agents to ERC-8004 (one-time)
pnpm register:agents
# 7) check everything
pnpm balances # all 5 wallets should be funded
# 8) run
pnpm dev # http://localhost:3000Onboarding banner walks every first-time visitor through wallet setup.
| Command | What it does |
|---|---|
pnpm install |
Install all workspaces |
pnpm dev |
Run the dashboard (apps/web) |
pnpm build |
Production build everything |
pnpm check-types |
TypeScript check across all packages |
pnpm keys:generate |
Generate agent + deployer keys, write into .env |
pnpm balances |
Show Arc balances for all 5 wallets |
pnpm fund:agents |
Send 2 USDC from the deployer to each agent |
pnpm contracts:compile |
Hardhat compile all contracts |
pnpm contracts:deploy |
Deploy all contracts, write addrs into .env |
pnpm register:agents |
Register all 4 agents in ERC-8004 IdentityRegistry |
pnpm swarm:tick |
Run one swarm tick from the CLI |
pnpm swarm:loop |
Autonomous tick loop on SWARM_TICK_INTERVAL_MS |
The hosted instance is live at arc.usemurmur.xyz (Vercel, apps/web as root). To run your own copy:
- Push this repo to GitHub.
- On Vercel: New Project → Import the repo. Set Root Directory to
apps/web(auto-detects Next.js viaapps/web/vercel.json). - In Environment Variables, paste from
.env.example. Minimum to make the dashboard load:ARC_RPC_URLNEXT_PUBLIC_ARC_EXPLORER_URL=https://testnet.arcscan.appNEXT_PUBLIC_ARC_USDC=0x3600000000000000000000000000000000000000NEXT_PUBLIC_STIGMERGY_CONTRACT=<your deployed addr>NEXT_PUBLIC_VAULT_CONTRACT=<your deployed addr>NEXT_PUBLIC_PROPOSALS_CONTRACT=<your deployed addr>NEXT_PUBLIC_APP_URL=https://<your-vercel-domain>.vercel.appOPENROUTER_API_KEY+OPENROUTER_MODEL(live reasoning)DEPLOYER_PRIVATE_KEY+AGENT_*_PRIVATE_KEY(server-side ticking)
- Deploy. First build runs
pnpm install+pnpm contracts:compile+pnpm build --filter=web.... - Visit your URL — dashboard, OG card (
/api/og), proposals (/api/proposals), identity strip (/api/identity) all work out of the box.
Tip: if
NEXT_PUBLIC_PROPOSALS_CONTRACTis unset, the proposals panel renders an empty state but doesn't error. Same for the ERC-8004 strip if agents aren't registered yet.
When a user clicks Connect Wallet, what happens?
The page hooks the user's external MetaMask via wagmi. We never see or store their private key. Their wallet address is their permanent identity — reconnect from any device and your full history (deposits, signals, copy state, PnL, x402 unlocks, session-key activity) is there because everything is read from on-chain events on Arc.
Does the user need testnet USDC?
Yes. USDC is gas on Arc — like ETH on Sepolia. Request it once from Circle's faucet (https://faucet.circle.com → Arc Testnet). The onboarding banner walks first-time visitors through it.
How frequently are we using Polymarket?
Every swarm tick (~20s in auto-tick mode, or on-demand from the hero) fetches 40 live Polymarket markets from the Gamma API. Top 4 by 24h volume are reasoned over per tick. Each market gets a primary agent (crypto→Vega, politics→Solon, macro→Atlas, sports→Yuki) + one wildcard, so over time all 4 agents touch every market. Open positions are marked-to-market each tick against real Gamma prices.
Where does the agent's USDC come from? Real balance?
Yes — real Arc Testnet USDC. Agent wallets are funded via pnpm fund:agents (2 USDC each, transferred on-chain from the deployer). Every agent action (signal post, vault.openPosition, x402 intel buy, nanopayment, real CCTP depositForBurn) consumes real USDC. Verify with pnpm balances or click any agent address on Arcscan.
The session key — where does it live and what stops abuse?
The session private key is generated in the browser (viem generatePrivateKey()) and stored only in this browser's localStorage, scoped to the connected owner address. It never leaves the browser. Abuse is bounded by two natural limits: (1) the session can only spend what the user funded it with (a real USDC transfer), and (2) the page enforces a wall-clock expiry, after which it stops broadcasting and the user can Revoke + sweep dust back. No paymaster, no bundler, no AA infra — just one fresh EOA the browser drives directly.
"MetaMask shows 'To: 0x3600…' when I click Boost — is that wrong?"
No — that's exactly right. 0x3600… is the USDC ERC-20 contract on Arc. Token transfers always go through the token contract, so MetaMask shows the contract as the EVM to. The actual recipient (the agent's wallet, or the session key) is decoded inside the parsed "Token Transfer" panel MetaMask displays below.
"Vault not loaded — what do I do?"
Contract addresses are in .env as NEXT_PUBLIC_*. Next.js reads them at server start. If you ran pnpm contracts:deploy after pnpm dev was already running, restart pnpm dev so it picks up new .env values.
What's RFB-03 — "markets the swarm wishes existed"?
On every swarm tick, if highest-conviction decisions surface a topic Polymarket doesn't cover, the responsible agent emits MarketProposed(proposalId, agent, question, …) on the MarketProposals contract. Peers can endorse(proposalId) once each, weighted by conviction. Dedup is automatic: proposalId = keccak256(lowercase(question)). The dashboard's "Markets the swarm wishes existed" panel ranks proposals by convictionΣ + 2000bps · endorsers.
What's the ERC-8004 strip on the dashboard?
Each agent is registered onchain in Arc's pre-deployed IdentityRegistry (0x8004A8…BD9e). The strip shows token IDs, links to Arcscan, and updates as peer feedback flows through the ReputationRegistry. Run pnpm register:agents once after deploying — metadata is encoded inline as a data: URI so there's no IPFS dependency for the demo.
- 0:00 — Hook. "Four AI agents coordinate like ants — but on-chain on Arc. They trade Polymarket cross-chain via CCTP, and you can copy them with a deposit."
- 0:20 — Cold dashboard. Hero, ticker (colours), block number ticking.
- 0:35 — Onboarding. "Four steps. Add Arc Testnet, faucet USDC, connect, deposit."
- 0:55 — Deposit + signal. Deposit 2 USDC, drop a sentiment pheromone with a note. Show both Arcscan txs.
- 1:20 — ⚡ Power-user lane. Click Pay 0.01 USDC & unlock — phase pill walks
probe → 402 → pay → verify → 200. Show the payment tx on Arcscan. - 1:40 — Arm session key. Pick 0.5 USDC / 60m. Sign one MetaMask tx funding the in-browser key. Status flips to ACTIVE.
- 2:00 — Run swarm tick. Ticker fills. The session key auto-cosigns the first ≥65% conviction signal — no MetaMask prompt. Show the cosign tx in the auto-activity list.
- 2:30 — Agent card. Decision-trace drawer shows DeepSeek V4 Pro's reasoning + tool calls.
- 2:45 — Click a Traction tile. Arcscan opens on the SwarmVault events — "these numbers are the chain, not me."
- 3:00 — Close. "Every Arc primitive firing on one app. Cricket agent (Yuki) is the India wedge. Already monetising via Polymarket builder code."
- Monorepo (Turborepo + pnpm · 4 packages + web)
-
SwarmVault.soldeployed · deposits · copy · signals · positions · PnL -
StigmergySignal.soldeployed · agent pheromones -
MarketProposals.soldeployed · RFB-03 - ERC-8004 native · all 4 agents registered to Arc IdentityRegistry
- wagmi + viem wallet connect on Arc (chainId 5042002)
- Deposit / withdraw / copy / sentiment — all from the dashboard
- Live Polymarket data via Gamma API (40 markets/tick)
- LangGraph + Vercel AI SDK + DeepSeek V4 Pro with reasoning traces
- Real CCTP v2
depositForBurnon Arc + Circle attestation polling - x402 — server side PaidIntel: HTTP 402 → USDC tx on Arc → unlock
- x402 — client side (wagmi-signed) — browser drives the full flow ⚡
- Session keys (in-browser) — auto-cosign without per-tick MetaMask ⚡
- Nanopayments between agents on favourable marks
- Polymarket v2 order stub with builder code attached
- Traction panel with every tile linking to Arcscan
- Onboarding banner + live activity ticker + decision-trace drawer
- Boost agent modal — user tips any agent directly on Arc
- Auto-tick toggle — dashboard runs ticks every 30s while open
- Welcome USDC drop + LLM budget controls + OG share cards
-
pnpm dev / build / check-types / swarm:tickall green
Built with ♥ for Agora · Arc. Let's win this.
{ "market": "Will Bitcoin hit $150k by June 30, 2026?", "yes_implied_prob": 0.01, "liquidity_usd": 250000, "volume24h_usd": 1240000, "peer_agent_signals": [ { "agent": "Solon", "prob": 0.05, "conviction": 0.4, "action": "PASS" } ], "user_sentiment": { "count": 17, "avgLean": 32, "recent": [...] } }