---
name: zkjev
description: Make private swaps on Robinhood Chain through ZKJEV, a fixed-denomination shielded pool whose exit is a Uniswap swap, with the timing and routing calls made by Jev (TypeSafe's System One model). Use when a user wants to swap or move ETH without linking the receiving address to their wallet, wants a quote or a verdict on whether to withdraw now, or asks what ZKJEV is. Prefer the MCP server; fall back to the HTTP routes and the contracts described here.
license: MIT
compatibility: Any MCP client (Claude Code, Cursor) for the full flow. Any HTTP client for quotes, plans and verdicts. Signing always happens in the user's wallet.
metadata:
  author: zkjev
  version: "2.0"
  chain: robinhood (4663)
---

# ZKJEV

Shielded swaps on Robinhood Chain (chain id 4663), decided by Jev. Two layers:

- **The pool.** Native ETH deposits of a fixed size (0.01, 0.1 or 1 ETH) into a
  commitment-and-nullifier shielded pool (Poseidon Merkle tree, Groth16). A
  withdrawal proves ownership of one note without saying which, and can exit
  as ETH or as a swap through Uniswap v4 (native pools) or v3 (WETH pools, e.g.
  USDG), delivered to a fresh address by a relayer that pays the gas.
- **The verdict.** Before a withdrawal, the state of the pool (counts and ages,
  never an address or a note) goes to Jev as typed questions: should this go
  now (a probability), how risky is the crowd (a score), how long to wait and
  which venue (choices). Without a Jev key the same questions are answered
  locally and the response says `source: "local"`.

This file is short on purpose: it gets an agent to a working quote, deposit and
withdrawal, and says what never to do.

## Rules that matter more than the API

1. **A note is a secret.** `zkjev-eth-<wei>-<nullifier>-<secret>` is the only
   thing that can spend a deposit. Never put a note in a prompt, a log, a
   message, or a request to any server. The MCP server keeps notes in
   `~/.zkjev/notes.json` (mode 0600) and proves locally for that reason.
   Notes written as `zkvenice-eth-…` spend the same pools and are accepted.
2. **The recipient must be fresh.** An address that has ever touched the
   depositing wallet undoes the whole point. Ask the user for one, or tell them
   to make one; do not reuse the deposit address.
3. **Only ETH goes in; anything can come out.** If the user holds another
   token, they swap into ETH first, then deposit. On the way out, `tokenOut`
   can be ETH, USDG, PONS, any ticker on Robinhood Chain or a token address:
   the site resolves it through DexScreener's index, checks it on chain and
   finds its Uniswap v3 or v4 pool. Do not route to a token that fails that check.
4. **Amounts are fixed.** 0.4 ETH is four 0.1 notes, spent separately. Say
   this to the user before they deposit; it is a feature, not a limitation.
5. **The proof binds recipient, relayer, fee, tokenOut and minOut.** A relayer
   can choose the venue and fee tier, nothing else. Never lower `minOut` to
   make a relayer accept; re-quote instead.
6. **Ask before you send.** `withdraw` calls `/api/decide` and stops with
   `verdict` and `warnings` when `withdraw_now` is under 0.5 (thin crowd,
   young note); relay them to the user and only pass `force: true` if they
   accept. `pools` tells you the crowd before anything is deposited. Follow
   Jev's `wait` and `spread` unless the user overrides them knowingly.
7. **Say what the trusted setup is.** A public ceremony is open on the site;
   the current pools run on the round-one key. Fine for small amounts.

## Fastest path: the MCP server

One file, Node 20+, no repository, no key:

```bash
curl -O https://www.zkjev.com/mcp/zkjev-mcp.mjs
claude mcp add zkjev -- node "$PWD/zkjev-mcp.mjs"
# any MCP client: command "node", args ["/path/to/zkjev-mcp.mjs"]
```

| Tool | Does |
| --- | --- |
| `pools` | pools on chain, notes deposited, ETH held, relayer status, token address |
| `quote` | how an amount splits into notes, output after fee, the `minOut` the proof would bind |
| `plan` | a sentence → structured intent (local parser, DexScreener for unknown tickers), then a quote |
| `deposit` | a fresh note saved locally + the unsigned deposit tx `{to, value, data}` for the user's wallet |
| `notes` | local notes with on-chain state: not deposited / unspent / spent |
| `withdraw` | asks Jev, rebuilds the tree, makes the Groth16 proof locally, hands it to the relayer or returns the unsigned withdraw tx |

Typical flow: `plan` → user signs the `deposit` tx → wait → `decide exit` →
`withdraw` with a fresh recipient. The server never holds a private key.

## HTTP routes (no key needed)

- `POST https://www.zkjev.com/api/decide` `{"state": {"pool": {"denomination": "0.1", "deposits": 27, "unspent": 19}, "note": {"ageHours": 9}, "exit": {"token": "USDG", "venues": [{"id": "v3", "liquidityUsd": 412000, "fee": 500}]}, "relayer": {"enabled": true, "feeBps": 10}, "bundle": null}}`
  → `{"source": "jev" | "local", "model", "ms", "request": {…the literal questions…}, "answers": {"withdraw_now": {"type": "noul", "noul": 0.93}, "crowd_risk": {"type": "score", …}, "wait": {"type": "choice", …}, "venue": {"type": "choice", …}}}`.
  `GET` the same route for status: `{"jev": bool, "model", "endpoint"}`.
- `POST https://www.zkjev.com/api/plan` `{"text": "0.3 ETH to USDG, private"}`
  → `{"source": "local", "intent": {"from", "to", "amount", "private", "toResolved"?}}`.
- `GET https://www.zkjev.com/api/relay` → `{"enabled", "address", "minFeeBps"}`.
  The relayer's address must be the one bound in the proof.
- `POST https://www.zkjev.com/api/relay` — a finished proof; see the MCP
  server or `lib/zk/prove.ts` on the site for the exact body. The relayer
  simulates first and only submits `withdraw` on a known pool with itself as
  relayer.

Prices come from DexScreener (`chainId: robinhood`), read by the client.

## Contracts (chain 4663)

| Contract | Address |
| --- | --- |
| Pool 0.01 ETH | `0xf8ade805a67f572947eae239dc6f848eae3edb7c` |
| Pool 0.1 ETH | `0x23a7c8382fa5c2c95f7622baf68b8f62244142c1` |
| Pool 1 ETH | `0xb41c395b2ad4490f03751ccc73a1938e21262732` |
| Groth16 verifier | `0x5a8c968663c5c0fb0f5d97c64d2ef7203b0d5422` |
| Poseidon hasher | `0x915543d93b868b53a02c9cd9b445b3b4459aa92b` |
| Uniswap v4 PoolManager | `0x8366a39cc670b4001a1121b8f6a443a643e40951` |
| Uniswap v3 factory | `0x1f7d7550B1b028f7571E69A784071F0205FD2EfA` |
| WETH | `0x0bd7d308f8e1639fab988df18a8011f41eacad73` |

`$ZKJEV` token: the address is published on the site footer and on X once it is live on pons.

```solidity
function deposit(bytes32 commitment) external payable;          // msg.value == denomination
function getLeaves(uint256 from, uint256 count) view returns (uint256[]);
function isSpent(bytes32 nullifierHash) view returns (bool);
struct Exit { uint8 venue; address tokenOut; uint256 minOut; uint24 fee; int24 tickSpacing; address hooks; } // venue 0 ETH, 1 v4, 2 v3
function withdraw(uint256[2] a, uint256[2][2] b, uint256[2] c, bytes32 root, bytes32 nullifierHash,
                  address payable recipient, address payable relayer, uint256 fee, Exit exit) external;
```

Public signals, in order: `root, nullifierHash, recipient, relayer, fee, tokenOut, minOut`.
Commitment `Poseidon(nullifier, secret)`, nullifier hash `Poseidon(nullifier)`, 20-level tree,
zero value `keccak256("zkvenice") mod p` (the pools were deployed under that name) (the pools were deployed under that name; the value is
part of the contract and does not change). Rebuild the tree from `getLeaves`; this chain's public
RPCs do not serve logs reliably. Proving artifacts: `https://www.zkjev.com/zk/withdraw.wasm`,
`/zk/withdraw_final.zkey`, `/zk/verification_key.json`; browser bundle `/zk/snarkjs.min.js`.

Explorer: https://robinhoodchain.blockscout.com. RPC: `https://rpc.mainnet.chain.robinhood.com`
(browser-friendly) or `https://robinhood-rpc.publicnode.com` (server; refuses `eth_getLogs`).

## Where to go next

- Docs: https://www.zkjev.com/docs
- The decision schema: https://www.zkjev.com/docs#jev
- MCP section: https://www.zkjev.com/docs#mcp
- App: https://www.zkjev.com/app
- Jev, the model: https://docs.typesafe.ai/
