ZKP2P is a privacy preserving trustless P2P fiat onramp that can integrate with any web2 payment rails (e.g. Venmo) without permission from the payment network itself. We build upon the 0xParc / PSE ZK-Email libraries to prove the contents in a payment email and bring that data on-chain in a privacy preserving manner to unlock escrowed assets on-chain.
Our current PoC enables trustless and privacy-preserving USDC to USD trades on the Goerli network using Venmo as the off-chain payment rails. Our PoC is live! Give it a try at https://zkp2p.xyz. 😄
We began hacking on this submission at ZKHack Lisbon in early April, where we built a working v0. Since then, we have reworked the flow to prioritize privacy. We modified our circuits and smart contracts to preserve the privacy of our users' Venmo IDs, implemented full client-side proving to prevent private data leakage to servers, and added optimizations for faster client-side proving.
There are 2 actors in the system:
- On-rampers: Users who intend to trade their USD on Venmo to USDC on-chain.
- Off-rampers: Users who intend to trade their USDC on-chain to USD on Venmo.
Our circuits are built on top of the zk-email-verify circuits. We borrow the following circuits and scripts written by the zk-email team:
- RSA Signature verification: Verifies a signature signed by a 2048 bit RSA public key
- SHA256 Partial hashing: Completes the sha256 hash given a pre-computed state. Allows us to calculate first part of the partial hashes outside the circuit thus reducing proving time.
- Regex circuit generator script: To perform arbitrary regex checks within the circom framework, they implemented a Python script that converts a given regex into a deterministic finite automata (DFA) and then represents the DFA via gate operations in circom code.
We used their regex circuit generator scripts to generate our own regex circuits for parsing a Venmo payment email. Currently we have the following regex circuits:
- Venmo Off-ramper ID Regex: This regex circuit extracts out the venmo ID of the payee (user who was paid) from a venmo payment email's body. Written in circom.
- Venmo Amount Regex: This regex circuit extracts out the amount sent from a venmo payment email's header. Written in circom.
This is our main circuit written in circom and it performs the following operations:
- It accepts the email as an input and checks if the email header and body are signed by Venmo using RSA and SHA-256, ensuring the email's authenticity.
- It extracts the venmo ID of the payee (user who was paid) from the email's body and hashes it.
- It extracts the amount of USD transacted in the venmo email from the email's header and packs it into smaller chunks.
- Public inputs to the circuit:
- RSA signature modulus against which the signature was checked. Venmo's DNS public key. [17 signals]
- Order ID specified by the on-ramper generating the proof [To prevent frontrunning; 1 signal]
- Public outputs from the circuit:
- Hash of the payee's Venmo ID [1 signal]
- Amount of USD paid to payee [3 signals]
Following table contains information about our main circuit:
Metric | Value |
---|---|
constraints | 6618823 |
public inputs | 18 |
public outputs | 4 |
private inputs | 7478 |
wires | 6400562 |
labels | 30385320 |
To preserve privacy of the user's email, we opted for generating ZK proofs in the browser. We then added the following optimizations to make client side proving faster:
The size of our proving key is 3.5GB which makes it difficult to download and generate proofs. So we chunked our keys into 10 parts using sampriti's snarkjs fork. We then compressed each one of them and hosted them on a public AWS S3 bucket. Sizes of the chunked proving keys in total is 1.95 GB. A reduction in size of 45%.
The chunked proving keys are downloaded when the user generates the proof for the very first time. These chunked keys are stored in their compressed form in the local storage of the browser on the user's machine, so that the user doesn't has to download the proving key again and utilize network bandwidth.
Following table presents steps taken to generate proof on the client end along with time taken for each step:
Step | Time |
---|---|
Chunked keys download (only once) | 207s (3.4s) |
Proof generaiton | 623s (10.3 mins) |
We set up an AWS EC2 z1d.12x large
instance as our development server. We chose this instance because it has one of the fastest single core performance for our circuit compilation and witness generation needs. It has 48 cores which speeds up our proving key generation. We followed the best practices for large circuits guide to setup our server.
Following table presents the various steps of circuit development alng with time taken to perform each step:
Step | Time |
---|---|
Compilation | 563.87s (9.3 mins) |
Proving key generation | 782s (13 mins) |
Chunked proving key generation | 3 hours |
Witness geeration | 60s |
Proof generation using rapidsnark | 9.2s |
We used the Groth16 proving system because it produces proofs smaller in size compared to other proving systems, which translates into lower gas costs when verifying proof on-chain. Groth16 proofs can be verified quickly which is essential for on-chain verification.
Groth16 requires a trusted setup per circuit, but we have not done any trusted setup for our PoC deployed on https://zkp2p.xyz.
The smart contracts are written in Solidity and deployed on the Goerli and Mantle (L2) test network. You can find them here:
Goerli
- Ramp - 0x945D14a5c63769f4cf008a2994810940cc0DFd5C
- FakeUSDC - 0xf6426A1fdE02c3d6f10b4af107cDd7669574E74C
Mantle (L2)
- Ramp - 0xa8B1e2A1235E2B30B32233b9176867A05B48dc3e
- FakeUSDC - 0xA6E289496d6479a5c7cAD48745990ef534FD3314
The ramp contract provides endpoints for different actors in the system to turstlessly coordinate with each other. It offers the following functionality:
For on-rampers:
- Posting Orders: Allows on-rampers to post an on-ramp order specifying the amount to receive, max amount to pay, and an encryption public key.
- On-Ramping: Enables users to perform the on-ramp operation by providing ZK proof of off-chain payment to the off-ramper. The function verifies the proof and releases the escrowed funds to the on-ramper.
- Canceling Orders: Allows the creator of an order to cancel it if it is still open and has not been filled or canceled.
For off-rampers:
- Claiming Orders: Allows off-rampers to claim a posted order by providing their Venmo ID, the order nonce, an encrypted Venmo ID, and a minimum amount to pay.
- Clawback claims: Enables off-rampers to claw back funds from a submitted claim if the claim has expired or the order has been canceled or filled.
The verifier contract is for verifying zk-SNARK proofs using the Groth16 proving system. It is generated using the SnarkJS template and hosts the on-chain verification logic. It is extended by the main Ramp contract.
An ERC20 token deployed on Goerli network for testing purposes.
Our team aimed to create an end-to-end application that showcases the power of zero-knowledge proofs in fostering trustless interactions with existing Web 2.0 services. Drawing inspiration from the zk-email protocol, which enables users to prove their Twitter handle ownership through the verification of a DKIM-signed confirmation email, we recognized the potential for enhancing user experiences in onboarding and offboarding processes.
Onboarding, in particular, presents a problem for users seeking trustless end-to-end solutions. New retail users face substantial barriers when attempting to onboard funds into the Web3 ecosystem. They must either register with a centralized exchange like Coinbase with prohibitive KYC requirements and long confirmation windows, or use centralized fiat onramps with high fees.
Additionally, crypto natives struggle to offboard funds for use in the real world, as only institutional accounts have direct access to convert USDC or USDT to USD. Existing P2P solutions, such as LocalBitcoins and OTC desks, either necessitate in-person meetings or rely on trusted intermediaries.
To address these issues, we designed a user-friendly onboarding flow that mitigates the need for trusted coordination between counterparties via a centralized intermediary. Our solution uses trustless technologies like smart contracts and zk-SNARKS. On-rampers can post their orders on-chain, allowing multiple off-rampers to submit competitive bids on-chain. The on-ramper then confirms the best order, peforms the off-chain payment, generates a proof from the email confirmation, and submits that on chain to unlock the USDC.
Our initial v0 implemention implemented in early April leaked users Venmo IDs on-chain and did not support client side proof generation. We have iterated on our initial v0 implemention since then and added privacy as a first class citizen ensuring no venmo IDs are leaked on-chain. Only the parties involved in the transaciton know their counterpartie's Venmo ID to facilitate the off-chain transaction. We added supported for full client side proving to preserve maximum privacy and not reveal contents of the email other than what is necessary.
Even now, our solution requires up to ten minutes for client-side proof generation, yet it remains more competitive than the existing alternatives. It caters to only Venmo users and can only be used to on-board to USDC. As we move toward production, we will continue to optimize client side proof generation, add support for more payment services and on-chain tokens including ETH for gas token, improve security and enhance the overall user experience.