---
name: zkvenice
description: Make private swaps on Robinhood Chain through ZKVENICE, a fixed-denomination shielded pool whose exit is a Uniswap swap, planned by an agent on Venice. Use when a user wants to swap or move ETH without linking the receiving address to their wallet, wants a quote or a plan for such a swap, or asks what ZKVENICE 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, Venice Agents) for the full flow. Any HTTP client for quotes and planning. Signing always happens in the user's wallet.
metadata:
  author: zkvenice
  version: "1.0"
  chain: robinhood (4663)
---

# ZKVENICE

Private swap aggregator on Robinhood Chain (chain id 4663). 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 agent.** The route is planned on Venice inference. The only thing sent
  to Venice is the sentence the user typed.

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.** `zkvenice-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
   `~/.zkvenice/notes.json` (mode 0600) and proves locally for that reason.
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 planner resolves it (DexScreener index, then Venice web search), 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. **Timing and crowd correlate.** `withdraw` stops and returns `warnings` when
   the pool has fewer than 10 deposits or the note is under six hours old;
   relay them to the user and only pass `force: true` if they accept. `pools`
   tells you the crowd before anything is deposited.
7. **Say what the trusted setup is.** A single development contribution until
   a public ceremony is announced on the site; fine for small amounts.

## Fastest path: the MCP server

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

```bash
curl -O https://www.zkvenice.com/mcp/zkvenice-mcp.mjs
claude mcp add zkvenice -- node "$PWD/zkvenice-mcp.mjs"
# any MCP client: command "node", args ["/path/to/zkvenice-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 through the Venice planner, 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` | 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 → `withdraw` with a
fresh recipient. The server never holds a private key.

## HTTP routes (no key needed)

- `POST https://www.zkvenice.com/api/plan` `{"text": "0.3 ETH to USDG, private"}`
  → `{"source":"venice"|"local","intent":{"from","to","amount","private"}}`.
  Sends the sentence to Venice; nothing else.
  The response carries `paid: "x402" | "key"`: the agent pays Venice per request
  from its own wallet when it can (no API key, no account), a key otherwise.
- `GET https://www.zkvenice.com/api/relay` → `{"enabled","address","minFeeBps"}`.
  The relayer's address must be the one bound in the proof.
- `POST https://www.zkvenice.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` |
| $ZKVENICE token | `0x9287ece030f3c3ef13505d721648e90108872310` |
| Uniswap v4 PoolManager | `0x8366a39cc670b4001a1121b8f6a443a643e40951` |
| Uniswap v3 factory | `0x1f7d7550B1b028f7571E69A784071F0205FD2EfA` |
| WETH | `0x0bd7d308f8e1639fab988df18a8011f41eacad73` |

```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`. Rebuild the tree from `getLeaves`; this chain's public
RPCs do not serve logs reliably. Proving artifacts: `https://www.zkvenice.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.zkvenice.com/docs
- MCP section: https://www.zkvenice.com/docs#mcp
- App: https://www.zkvenice.com/app
- Venice's own skill, for the planner side: https://venice.ai/skill.md
