project screenshot 1
project screenshot 2
project screenshot 3

toadnado

Toadnado is a cross layer mixer where the privacy is shared between L1 and L2.

toadnado

Created At

ETHGlobal Brussels

Winner of

Blockscout - Best use of Blockscout Block Explorer

Prize Pool

Scroll - Best Build Reading L1 State with L1SLOAD

Project Description

Toadnado is a mixer that allows you to deposit from both L2 (scroll) and L1. You can then anonymously withdraw on L2 to a new address. This doesn't reveal from which address you deposited, or even which chain. The anonymity set spans across both L1 and L2 :D

Today's bridges do not preserve privacy. We build Toadnado to prove that they can, and it's cool! We hope it will inspire future bridge protocols to add better privacy.

We used scrolls new L1SLOAD opcode to read the L1 commitment-root atomically from L1. ☝️🤓

"Let my people go, so that they may worship me. If you refuse to let them go, I will plague your whole country with frogs." — Exodus 8:1–4

How it's Made

Toadnado has 2 contracts, on L1 and L2.

The L1 contract only allows for deposits. The users deposits by sending 0.01 eth to the contract and a commitment hash. The commitment hash is generated by hashing the nullifier-pre-image and a secret. The commitment hash is added to the commitment-merkle-tree. Every deposit causes the commitment tree root to update, up to 20 historic roots are then stored in a mapping for future reference.

The L2 contract also allows for deposits exactly like the L1 contract. But the L2 contract also allows for withdraws. Withdraws are verified by a zero knowledge circuit written in noir. The circuit uses a merkle proof of the combined roots from L1 and L2, and the pre-image of the commitment of the deposit to verify that a user can withdraw. The circuit has the nullifier-hash, combined-commitment-root and recipient-address as public inputs. The contract then uses the nullifier-hash to prevent double spends, recipient-address to send the funds to and combined-commitment-root to verify that its a valid deposit.

The combined-commitment-root is generated inside the contract by hashing the L1-commitment-root and L2-commitment-root together. This allows us to hide whether a deposit came from L1 or L2 .

The L1-commitment-root is retrieved by scrolls new L1SLOAD opcode.

We took an incredible amount of inspiration from tornadocash. We have used part of their merkle tree contract logic and core design patterns to build our project. Please consider following @FreeAlexeyRoman on twitter and support them where you can.

background image mobile

Join the mailing list

Get the latest news and updates