Architecture at a glance
What each component does
Frontend (React)
OrderPortal
createOrder to deposit tokens and register the order hash with the MerkleManager. unlock pays the Maker’s recipient once the ZK proof verifies.AdManager
createAd and fundAd to offer liquidity, then lockForOrder to reserve funds against a specific order. unlock pays the Bridger’s recipient once the ZK proof verifies.MerkleManager
MANAGER_ROLE to append entries.Verifier
unlock.wNativeToken
Backend Relayer
Proof Circuits (Noir)
The 12-step cross-chain flow
The scenario below follows a single transfer end-to-end. Each step is what actually happens on-chain or in the relayer.Maker creates an ad on the destination chain
createAd on AdManager, locking their liquidity against a specific route (for example, wETH on Stellar for ETH on Sepolia). The ad becomes discoverable in the marketplace.Bridger requests pre-authorization
Relayer returns a signed approval
Bridger creates the order on the source chain
createOrder on OrderPortal, depositing source-chain tokens. Native tokens are automatically wrapped through wNativeToken. The deposit is now locked in the contract.OrderPortal appends the order hash to the source-chain MMR
User triggers the relayer callback
Relayer fetches Merkle proof + order data
Relayer runs the deposit proof circuit
- Recomputes the order hash from the struct fields and checks it matches.
- Validates the MMR inclusion proof against the claimed root.
- Verifies the nullifier is unused.
- Outputs a compact ZK proof.
Relayer submits the proof to AdManager (destination chain)
AdManager.unlock(proof, ...) on the destination chain.Destination chain verifies and pays the Bridger
Relayer submits the proof to OrderPortal (source chain)
OrderPortal.unlock(proof, ...) on the source chain with the corresponding proof for the other side of the trade.Source chain verifies and pays the Maker
What zero-knowledge proofs give you
Trustless verification
Replay prevention
Tamper-proof history
Chain-bound orders
Security guarantees
- EIP-712 order hashes bind every trade to specific chain IDs and contract addresses, preventing cross-chain or cross-contract replay.
- Bidirectional chain linking means each contract only accepts proofs from its configured counterpart chain.
- Manager role permissions on MerkleManager restrict which contracts can append new order hashes.
- Collateral locking requires Makers to have funds locked before any order is matched — so a Bridger’s destination tokens exist before the trade starts.