Spending
- No spend without a proof. Transfers, withdrawals and swaps all go through
_verifyProof. A failed proof reverts withInvalidProof. - 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_IDis 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 insetFeeConfig. - 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/pkYmust hash tomsg.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.