Document Version: 1.0 Last Updated: 2026-02-08
This document analyzes the security properties, trust assumptions, and attack vectors of the Waiola Private RFQ system. It is deliberately honest about what the system does and does not protect against.
In a standard Uniswap v4 swap, the swap parameters (token pair, direction, amount) are visible in the mempool before execution. This enables:
- Front-running by MEV bots
- Copy-trading by adversarial participants
- Quote fading by makers who see incoming intent
Waiola reduces this leakage by moving quote negotiation offchain:
- Quote request happens over direct HTTP (not broadcast to mempool)
- Quote terms (exact output amount) are hidden inside a ZK proof
- Settlement happens through a Uniswap v4 hook that validates the proof
Waiola does NOT hide:
- The swap itself -- once the transaction is submitted, it's visible onchain
- Token amounts -- the AMM settlement (input/output) is visible in event logs
- Participant addresses -- taker and maker addresses are visible
- The commitment -- once posted to the registry, it's public
This is an important distinction. Waiola provides pre-trade privacy (quote negotiation is private), not post-trade privacy (the executed swap is visible). For fully encrypted onchain state, you would need FHE (Fully Homomorphic Encryption), which is a different architecture entirely.
Threat: An attacker observes a valid swap transaction and resubmits the same hookData to execute the swap again.
Protection: The RFQRegistry maintains a used flag per commitment. After the first successful swap, consumeQuote() sets used = true. Any subsequent attempt reverts with CommitmentAlreadyUsed.
Strength: Strong. The consumption is atomic -- it happens inside beforeSwap, which means if the swap reverts, the consumption also reverts. There is no window where a commitment is consumed but the swap hasn't executed.
Residual Risk: None. The replay protection is airtight.
Threat: An attacker forges a maker's EIP-712 signature to create fake quotes.
Protection: The hook verifies the signature using ECDSA.recover and checks that the recovered address matches the maker stored in the commitment. The EIP-712 domain separator includes the chain ID and hook address, preventing cross-chain and cross-contract signature replay.
Strength: Strong. ECDSA signature forgery requires knowledge of the maker's private key. The EIP-712 domain binding prevents cross-context replay.
Residual Risk: If a maker's private key is compromised, an attacker could create valid signatures. This is a fundamental limitation of all signature-based systems, not specific to Waiola.
Threat: An attacker submits an invalid or fabricated ZK proof to bypass quote validation.
Protection: The HonkVerifier contract (auto-generated from the Noir circuit) performs full proof verification. The hook reverts with InvalidProof if verification fails. Additionally, the hook verifies that public inputs in the proof match the execution context (commitment, pool, taker, amount).
Strength: Strong. The soundness of the Honk proof system ensures that an invalid proof is rejected with overwhelming probability. The public input matching prevents proof reuse across different contexts.
Residual Risk: Theoretical soundness breaks in the proof system itself, which would require a fundamental cryptographic breakthrough. This is standard risk for all ZK systems.
Threat: An attacker takes a commitment made for Pool A and tries to use it in Pool B to extract value.
Protection: The commitment includes poolKeyHash as a public input. The hook computes keccak256(abi.encode(key)) from the actual pool being swapped and verifies it matches the committed pool. Additionally, the registry stores poolKeyHash per commitment.
Strength: Strong. Pool binding is enforced at three levels: in the commitment hash (Poseidon2), in the registry data, and in the ZK proof's public inputs.
Residual Risk: None for cross-pool attacks.
Threat: An attacker observes a committed quote for a specific taker and tries to execute the swap from a different address.
Protection: The taker address is included as a public input in the ZK proof. The hook verifies that publicInputs[2] (taker in proof) matches sender (the actual swap caller). The commitment hash also binds the taker.
Strength: Strong. The taker is bound at the circuit level and verified onchain.
Residual Risk: None for taker impersonation.
Threat: A taker holds a quote beyond its intended validity and executes it when market conditions have changed significantly against the maker.
Protection: Every quote has an expiry timestamp. The hook checks block.timestamp < quote.expiry and reverts with QuoteExpired if the quote is stale. The expiry is also included as a public input in the ZK proof.
Strength: Moderate. Expiry prevents indefinite quote holding, but within the validity window (typically 5 minutes), the quote is valid regardless of market movement. This is standard RFQ behavior -- the maker sets the validity window and prices accordingly.
Residual Risk: Within the validity window, market movements could make the quote unfavorable for the maker. Makers should set short validity windows and price in the risk.
Threat: An attacker sees the commitQuote() transaction in the mempool and either:
- Front-runs with a higher gas price to commit the same quote first
- Uses the visible commitment to inform their own trading strategy
Protection:
- For (1): The commitment hash is computed from the quote details including the taker address. A different caller committing the same hash doesn't help them -- only the original taker can execute the swap.
- For (2): The commitment alone reveals very limited information -- it's a Poseidon2 hash that doesn't expose the quote terms.
Strength: Moderate. The commitment is privacy-preserving (Poseidon2 hash), but the fact that a commitment was posted is visible. An observer knows "someone is about to execute an RFQ swap on this pool" without knowing the exact terms.
Residual Risk: Metadata leakage at commitment time. An adversary who monitors the registry can detect that an RFQ swap is about to happen on a specific pool. This is inherent to any onchain commitment scheme.
Threat: After the commitment is posted onchain but before the swap executes, MEV bots observe the commitment and sandwich the swap transaction.
Protection: Limited. The commitment hash doesn't reveal the exact swap terms, but the swap transaction itself (which includes hookData with the full quote) is visible in the mempool.
Strength: Weak for the swap transaction itself. The commitment provides some privacy (quote terms are hidden in the hash), but the actual swap transaction reveals the direction and amount.
Residual Risk: This is the primary privacy limitation. While quote negotiation is private, the final swap execution is visible in the mempool and susceptible to standard MEV extraction. Mitigations include:
- Using Flashbots Protect or private mempools for the swap transaction
- Block builders with MEV protection
- These are external to Waiola and up to the taker
Threat: A maker refuses to provide quotes to certain takers, or colludes with other makers to provide unfavorable quotes.
Protection: None within the protocol. Maker behavior offchain is not enforced.
Strength: None. This is an inherent limitation of RFQ systems where maker participation is voluntary.
Residual Risk: Maker collusion is possible. Mitigations include:
- Multiple makers discoverable via ENS (competition)
- Policy hash on ENS provides transparency about maker terms
- Takers can compare quotes from multiple makers
- Future work: solver networks with mandatory quoting
Threat: A maker changes their ENS records to point to a malicious endpoint or update the policy hash without updating the actual policy.
Protection:
- Policy hash is stored onchain (ENS text record). Takers fetch the policy from the endpoint and verify its hash matches the onchain record.
- If the hash doesn't match, the taker rejects the policy.
- ENS records require a transaction signed by the ENS name owner, so unauthorized changes aren't possible.
Strength: Moderate. Hash verification catches policy changes, but if the maker owns the ENS name, they can update both the policy and the hash simultaneously.
Residual Risk: A maker can change their policy at any time (they own the ENS name). But this is transparent -- takers see the new policy and hash. The risk is that a taker caches an old policy and doesn't re-verify.
Threat: An attacker floods the registry with fake commitments, or the maker server with quote requests.
Protection:
- Registry:
commitQuote()costs gas, so flooding requires spending ETH. There is no global state that can be blocked -- each commitment is independent. - Maker server: Standard HTTP rate limiting (not implemented in MVP but straightforward to add).
Strength: Moderate. The economic cost of onchain spam is the primary protection.
Residual Risk: Maker server availability depends on traditional infrastructure protection.
| Assumption | Consequence if Violated |
|---|---|
| Maker's private key is secure | Forged signatures, unauthorized quotes |
| Uniswap v4 PoolManager is correct | Hook may not be called, incorrect swap execution |
| Noir/Barretenberg proof system is sound | Invalid proofs could pass verification |
| Poseidon2 is collision-resistant | Commitment collisions, potential replay |
| EVM/Solidity execution is deterministic | Non-deterministic verification results |
| ENS resolution is honest | Taker directed to wrong maker |
| HTTP channel is not eavesdropped | Quote terms leaked during negotiation |
| Phase | What's Private | What's Visible |
|---|---|---|
| ENS Discovery | Nothing -- ENS records are public | Maker endpoint, policy hash |
| Quote Request | Quote terms (HTTP) | That a request was made (if network monitored) |
| Quote Response | quotedOut, salt | Nothing (direct HTTP) |
| Proof Generation | quotedOut, salt (inside proof) | Nothing (local computation) |
| Commitment | Quote terms (Poseidon2 hash) | Commitment hash, expiry, maker, pool |
| Swap Execution | quotedOut (in ZK proof) | amountIn, amountOut, taker, maker, pool |
| Post-Swap | Nothing new | All swap effects visible on-chain |
| Approach | Pre-Trade Privacy | Post-Trade Privacy | Onchain Verifiability | Complexity |
|---|---|---|---|---|
| Public AMM | None | None | Full | Low |
| Public RFQ (UniswapX) | Low | None | Full | Medium |
| Waiola (Private RFQ + ZK) | High | None | Full | Medium |
| FHE-based (iceberg, etc.) | High | High | Full | Very High |
| Offchain OTC | High | High | None | Low |
Waiola occupies a practical middle ground: strong pre-trade privacy with full onchain verifiability, without the complexity and chain requirements of FHE.
- Use short quote validity windows (5 minutes or less)
- Monitor for unusual quote patterns
- Price in the risk of market movement during validity window
- Use HTTPS for the quote endpoint
- Rotate private keys periodically
- Always verify the policy hash against ENS before trusting a quote
- Use private mempools (e.g., Flashbots Protect) for the swap transaction
- Compare quotes from multiple makers when possible
- Don't reuse quotes -- each quote has a unique salt and commitment
- Private mempool integration: Submit swap transactions via Flashbots or similar to prevent T8 (MEV after commitment)
- Encrypted quote channel: Use maker's public key (from ENS) to encrypt quote requests, preventing network-level eavesdropping
- FHE state: Encrypt the commitment registry to hide even the commitment metadata
- Multi-party computation: Enable multiple makers to provide quotes without seeing each other's terms
- Time-locked commitments: Auto-expire commitments to reduce the metadata leakage window
- Solver networks: Mandatory quoting to address maker collusion (T9)