These properties hold because the pool contract and verifiers enforce them. No service operator can override them without an upgrade.

Spending

  • No spend without a proof. Transfers, withdrawals and swaps all go through _verifyProof. A failed proof reverts with InvalidProof.
  • No double-spend. Each input note’s nullifier is recorded. Reusing one reverts with NullifierAlreadySpent.
  • No replay. Each signed transaction has a transaction nullifier. Reusing it reverts with TxNullifierAlreadyUsed.
  • Bound to this deployment. VIRTUAL_CHAIN_ID is the first public input of every proof. A proof for another deployment does not verify.
  • Valid roots only. The note root must be one the tree actually had (InvalidNoteTreeRoot). Transfers also check the key-registry root.

Fees

  • Protocol fees ≤ 1%. MAX_PROTOCOL_FEE_BPS = 100, checked in setFeeConfig.
  • Check cost ≤ 1% of a rejected deposit.
  • Pull gas ≤ 5% of an ERC-20 permit deposit.
  • Relayer fee is exactly what you proved. The fee and fee token are public inputs.
  • Only allowlisted relayers can collect a fee. A fee-less operation is open to anyone.

Deposits

  • Gatekeeper can’t alter a deposit. Approval and rejection are checked against the stored hash.
  • You can always reclaim a pending deposit, even while the pool is paused.
  • Gatekeeper gas is capped by the escrow and by basefee + 3 gwei.

Receiving keys

  • Only the owner registers. pkX/pkY must hash to msg.sender.
  • Registration is final. A different key for the same address is refused.
  • Encryption key must be on Grumpkin. Off-curve keys would make notes unspendable, so they are rejected.

Swaps

  • Deadline ≤ 30 minutes after execution time.
  • amountOutMin > 0.
  • Whitelisted tokens only, and only the three supported router selectors.

What is not guaranteed on-chain