Check it yourself

How to confirm the pool's admin powers, tell shields, private transfers and withdrawals apart, and re-check every proof, using only the public chain.

You don’t have to take this site’s word for anything on this page. Every check below reads Robinhood Chain directly, needs no wallet, and sends no transaction.

Run every check at once

verify-mainnet.mjs is a single read-only script. It checks the admin powers, lists every pool transaction with its kind, and sends each proof back to the deployed verifier. In an empty folder, with Node.js 20 or newer:

npm init -y && npm i viem
curl -O https://zeronym.lat/verify/verify-mainnet.mjs
node verify-mainnet.mjs

It uses Robinhood Chain’s public RPC. Set RPC_URL to use your own, and add --json for machine-readable output. The script is short: read it before you run it.

Admin powers

The pool’s source is verified on Sourcify with an exact match, so the code you read there is the code on the chain. It is also verified on the Blockscout explorer. The admin can call only two functions:

Function What it does
setToken(token, supported) Turns deposits of a token on or off
setAdmin(admin) Hands the admin role to another address

Nothing else in the pool checks for the admin. Here is how to confirm the rest:

Claim How to check
The admin is the 2-of-3 Safe Call admin() on the pool. It returns 0xFB4ff79d6BD9563b9f9c85Ce0Aa16271eA904Ae3. On that address, getThreshold() returns 2 and getOwners() returns 3 owners
It can’t block withdrawals transact checks token support only when extAmount is positive (a deposit). Private transfers and withdrawals never read it
It can’t move funds No admin function transfers tokens. Tokens leave the pool only inside transact, to the recipient and relayer fixed by a valid proof
It can’t change the verifier verifier is immutable: set once at deploy, with no setter
It can’t cap deposits The pool has no deposit limit and no setting for one
It can’t upgrade the pool The pool is not a proxy: the EIP-1967 implementation, admin and beacon slots are empty, and its code has no DELEGATECALL or SELFDESTRUCT

What each pool transaction looks like

Every pool action is one call to transact. Its extAmount and the token movements in its receipt tell you which kind it is. Open the pool on the explorer and compare.

Kind extAmount USDG movements Pool events
Shield Positive: the deposit The deposit, from the sender’s wallet into the pool 2 NewCommitment, 2 NewNullifier
Private transfer 0 None, or only the relayer fee (0.50 USDG) from the pool to the relayer 2 NewCommitment, 2 NewNullifier
Withdrawal Negative: the amount out The amount, from the pool to the recipient, plus the relayer fee if it was relayed 2 NewCommitment, 2 NewNullifier
Withdrawal received as ETH Negative: the amount out The amount, from the pool to a one-off forwarder, then through the ETH exit to Uniswap; ETH to the recipient. Plus the relayer fee 2 NewCommitment, 2 NewNullifier, and a Settled event from the ETH exit with the ETH sent

A withdrawal received as ETH is sent to the ETH exit contract, not the pool, and the exit contract calls transact. Decode it as withdraw(args, extData, order): the first two are the pool call, and order holds the ETH recipient and the least ETH it accepted.

Pool events

If you read the pool’s logs directly, topic0 tells you the event. Each is the keccak-256 hash of the event’s signature in the verified source.

topic0 Event When
0xf3843edd…411afd NewCommitment(bytes32 commitment, uint256 index, bytes encryptedOutput) 2 per pool transaction: the new notes. encryptedOutput is the note encrypted to its owner, and only their key can read it
0x5e58f77b…f297ce NewNullifier(bytes32 nullifier) 2 per pool transaction: the notes spent
0x9acd2543…e2b15e TokenSupport(address indexed token, bool supported) Admin turns deposits of a token on or off
0x7ce7ec0b…188f5c AdminChanged(address admin) Admin role handed on

So 10 NewCommitment and 10 NewNullifier events mean 5 pool transactions. The events look the same for a shield, a private transfer and a withdrawal; the kind comes from extAmount and the token movements, as in the table above.

The pool has no stored relayer. Each transaction names its relayer and fee in extData, and the proof binds them, so nobody can swap them. The Zeronym relayer is 0xc38e587856452610f41bbb57be25a14d2eb2959a, as /api/relay reports. You can also skip it and submit from your own wallet.

In a private transfer, nothing on the chain names the sender, the recipient or the amount. The new notes are encrypted, and the spent notes appear only as nullifiers, which can’t be traced back to the notes they spend. The one exception is who submits it: through the relayer, the sender on the chain is the relayer; from your own wallet, it is your wallet.

Re-check a proof

A pool transaction can only succeed if the verifier accepts its proof: transact reverts with “Invalid transaction proof” otherwise. You can also ask the verifier again at any time:

  1. Decode the transaction’s input as transact(_args, _extData) using the pool’s verified ABI (or, for a withdrawal received as ETH, as withdraw(args, extData, order) using the ETH exit’s verified ABI).
  2. Call verifyProof(_args, _extData.token) on the pool with eth_call. It forwards the proof and its eight public inputs to the verifier and returns true.
  3. Change any public input, for example add 1 to publicAmount, and call it again. It returns false.

verifyProof checks only the proof, so it still answers for notes that are already spent. The script does steps 1 to 3 for every pool transaction.

Pool transactions so far

What the script reported as of 28 October 2026. Run it again for the current list.

Transaction Kind Public amount Proof
0x51c11aa5… Admin: turn on USDG None No proof needed
0xec843b7f… Admin: hand admin to the Safe None No proof needed
0xf9644362… Shield, from the sender’s wallet 10 USDG in Accepted. A changed copy is rejected
0x565247b1… Shield, from the sender’s wallet 1 USDG in Accepted. A changed copy is rejected
0xabe3ea39… Withdrawal received as ETH, through the relayer 1 USDG out, swapped to 0.000428 ETH, plus 0.50 USDG relayer fee Accepted. A changed copy is rejected
0x48c43ded… Withdrawal received as ETH, through the relayer 5 USDG out, swapped to 0.00214 ETH, plus 0.50 USDG relayer fee Accepted. A changed copy is rejected
0x2ffa4b90… Private transfer, through the relayer None. Only the 0.50 USDG relayer fee leaves the pool; sender, recipient and amount stay encrypted Accepted. A changed copy is rejected