Docs
BeforeCoin gives an agent's trades and payments a check it can't skip. You call the API before acting, and the chain refuses anything that wasn't cleared.
Quickstart
Run the whole system on a throwaway local chain. It needs Foundry, Node 22 or newer, pnpm and Bun. It uses no real funds and no keys.
git clone --recursive https://github.com/sagarjethi/ethmumbai.git
cd ethmumbai
pnpm install
pnpm start
This starts the website, the dashboard, three verifiers and the API. In another terminal, run every attack:
pnpm demo
Clear a swap
POST /v1/clear/swap. Authenticate with Authorization: Bearer clr_…, or send no key and pay with x402. The server answers 402 with payment terms, and an x402 client pays and retries.
{
"action": {
"kind": "swap",
"agentName": "alice.agents.clearance.eth",
"account": "0xYourClearanceAccount",
"pool": {
"currency0": "0x…", "currency1": "0x…",
"fee": 3000, "tickSpacing": 60, "hooks": "0xClearanceHook"
},
"zeroForOne": true,
"amountIn": "100000000",
"reason": "Optional. Treated as untrusted and never read by rules."
}
}
When cleared, the response carries hookData. Pass it to your account's swapCleared:
account.swapCleared(pool, zeroForOne, amountIn, minOut, hookData)
A clearance lasts 90 seconds, works once, and only for the account it names. It also carries a minimum output, which the wallet enforces, so liquidity pulled after the check can't hurt the trade.
Exact-output swaps are always refused, because a hook can't bound their input before the swap runs.
Verdicts
| Verdict | Meaning | What to do |
|---|---|---|
ALLOW | Enough verifiers signed the full amount. | Send the swap with hookData. |
REDUCE | They signed a smaller amount, in clearance.maxAmountIn. | Send that amount or less. |
HUMAN_REQUIRED | It's over the owner's limit. | Send the owner to checkpoint.url. |
BLOCK | There are no signatures. Each verifier's reasons are in evaluations. | Don't retry. The chain will refuse it. |
Missing or stale data always gives BLOCK, never a pass.
Owner approval
- Send the owner to
checkpoint.url, which starts a fresh World ID login. - Check progress at
GET /v1/human/:id. It ends asapproved,deniedorexpired. - Call
/v1/clear/swapagain with the same action, plus"checkpointId".
Each verifier checks the approval's token itself. It only counts if it's from the owner, fresh, and for this exact action. It clears one trade.
Independent check
When a response has "attestationRequired": true, the hook also needs a finding on the ledger. Ask for it, then send the swap:
POST /v1/decisions/:id/attest
A Chainlink CRE workflow re-reads the owner's limits, confirms the quorum against the contract and re-quotes the market. It writes either an attestation or a refusal to ClearanceLedger. The first report stands. A scheduled run also picks up anything waiting at GET /v1/attestations/pending.
Clear a payment
The account's isValidSignature only accepts the agent key's signature plus verifier signatures over the exact EIP-3009 payment. To pay, your x402 signer calls POST /v1/clear/payment with the authorization and the agent key's signature, then appends the returned signatures to the agent's own. See apps/agent/src/agent.ts.
MCP server
apps/mcp lets an agent call BeforeCoin as a tool. It speaks stdio, and the client starts it for you. With pnpm start running, create a key in the dashboard's API keys tab, then use this config with absolute paths to your clone:
{
"mcpServers": {
"beforecoin": {
"command": "/absolute/path/to/ethmumbai/node_modules/.bin/tsx",
"args": ["/absolute/path/to/ethmumbai/apps/mcp/src/index.ts"],
"env": {
"CLEARANCE_API_URL": "https://your-api",
"CLEARANCE_API_KEY": "clr_your_key"
}
}
}
}
- Claude Code: put it in
.mcp.jsonat your project root. - Cursor: put it in
.cursor/mcp.jsonor~/.cursor/mcp.json, then approve the server.
A client started from a desktop app may not find node on its PATH, so add a PATH entry to env if the server won't start.
| Tool | What it does |
|---|---|
clearance_check_swap | Asks verifiers to clear a swap. A BLOCK is a normal answer and not a tool error, so read verdict. |
clearance_attest | Requests the independent check when a clearance has attestationRequired. |
clearance_get_proof | Reads a decision's signatures, reasons and evidence hashes. |
clearance_check_token | Lists admin functions in a token's bytecode. |
clearance_checkpoint_status | Reads whether the owner has answered a checkpoint. |
The server has no payment tool yet, so payments go through your x402 signer over REST.
Agent skill
skills/beforecoin tells an agent to get a check before every swap or payment, what each verdict means, how to submit the result to its wallet, and never to route around a refusal.
For Claude Code, copy it to one of the skill folders in Claude Code's docs:
mkdir -p ~/.claude/skills && cp -R skills/beforecoin ~/.claude/skills/ # all your projects
mkdir -p .claude/skills && cp -R skills/beforecoin .claude/skills/ # one project
For another agent, give it SKILL.md as instructions, and let it read references/ and run the script. It takes a swap as JSON, needs only Node, and reads CLEARANCE_API_URL and CLEARANCE_API_KEY:
node skills/beforecoin/scripts/check.ts swap.json
It exits 0 only for ALLOW, so a refusal or an error can't fall through to a swap. tests/skill.test.ts runs it against a throwaway stack. No model has been run with the skill loaded yet, so how well agents follow it is untested.
Public endpoints
| Endpoint | Returns |
|---|---|
GET /v1/proofs/:id | A decision, its signatures, each verifier's reasons and evidence hash, and any attestation. |
GET /v1/tokens/:address/check | Admin functions found in a token's bytecode, following proxies. |
GET /v1/track-record | Verdict counts and how often each verifier disagreed. |
GET /v1/attestations/pending | Clearances still waiting for an independent check. |
Owner limits
Text records on the agent's ENS name. A missing or invalid record blocks every action.
| Record | Meaning |
|---|---|
clearance.account | The one wallet this name may act for. |
clearance.maxSizeUsd | Above this, the owner must approve. |
clearance.humanCapUsd | A hard cap, even with approval. |
clearance.maxImpactBps | Largest price impact. Bigger trades are reduced. |
clearance.requiredVerifiers | Signatures needed. It can raise the contract's floor, not lower it. |
clearance.allowedTokens | Comma-separated token addresses. |
clearance.allowedPools | Comma-separated pool ids you trust. Others get refused, whatever price they show. |
clearance.dailyPaymentUsd | The most the agent can pay in a day, across all payees. |
clearance.maxPaymentUsd | Largest single x402 payment. |
clearance.allowedPayees | Comma-separated payee addresses. |
clearance.ownerSub | The owner's World ID subject, so only they can approve. |
Errors
| Status | Meaning |
|---|---|
400 | The request body is invalid. issues says where. |
401 | The API key is unknown or revoked. |
402 | Payment is required. x402 clients handle this. |
404 | This deployment doesn't know that agent. |
429 | The key's monthly checks are used up, or you sent too many requests. |
A refusal on the chain has a named reason, wrapped by the PoolManager: NoClearance, RouterNotAllowed, WrongAgent, WrongPool, WrongDirection, WrongChain, ClearanceExpired, ExactOutputNotClearable, AmountAboveClearance, NonceUsed, NoQuorum or NotAttested. The wallet adds PoolNotCleared, SpendCapExceeded, TooFewSignatures and NotAgent.
Contracts
| Contract | Job |
|---|---|
ClearanceHook | The v4 hook that checks every swap in its pools. |
ClearanceAccount | The agent's wallet, and the real enforcer. |
VerifierSet | Current verifiers and the quorum. |
ClearanceLedger | Findings from the CRE workflow. |
ClearanceRouter | A small exact-input router that reports its real caller. |