---
# System prepended metadata

title: FLAX Privacy Layer Simplified

---

# FLAX Privacy Layer Simplified

## Introduction
![](https://i.imgur.com/2KMdwZ9.png)

FLAX is an EVM compatible dApp that allows users to:
- anonymously interact with any on-chain application that works with ERC20/721/1155 assets
- make fully confidential payments (hidden sender, recipient, and amount)


FLAX overcomes two shortcomings of existing privacy solutions for Ethereum:

1. Backwards incompatibility -- requirement of changes made to existing DeFi smart contracts (Ex. Aztec)
2. Requirement of leaving and rejoining the anonymity pool in separate transactions (Ex. TornadoCash, Zkopru)

With FLAX, users are able to use the FLAX Wallet contract as if it is their own smart contract wallet. There is no need to leave the anonymity pool as users are still able to interact with current DeFi and NFT applications.

## How does FLAX add privacy?
### Current State of Privacy on the Blockchain
For Ethereum and most other blockchains, every transaction in the chain is fully public and transparent. Whenever a user sends ether, swaps ERC20 tokens, or mints an NFT, everyone can see which address sent the assets and which address received them. The following image is taken from Etherscan, a site that clearly illustrates the details of every transaction. 

![](https://i.imgur.com/KnyJ0O1.png)

One can regain some privacy by ensuring to use a new address for each transaction, but this comes with the following challenges:
1. Having enough balance to pay for gas for each address used (solved by third party relayers, but these relayers can be easily blocked or banned)
2. Managing all of the different addresses used

For the above reasons, this strategy is too difficult for many to use.  

**Coin Mixers**
Another popular way to achieve some extent of privacy is the use of coin mixers. Coin mixers utilize zkSNARKs to enable users to anonymously transfer their assets to fresh addresses. The most popular example of this is TornadoCash. Users will deposit their assets into a single smart contract (pool), then, retrieve their assets to a fresh address. The main limitations to this strategy are: 
1. Users must have ether in the receiving address to be able to retrieve and use the assets
2. A user should use a new receiving address every time they want to perform an anonymous transaction - this involves address management

### Enter FLAX
...

## High Level Architecture

FLAX uses one large multi-asset pool, known as the Vault, to store every user's funds. All user interactions with dApps are initiated from the FLAX Wallet contract without revealing the identity of the user.
* Vault.sol - responsible for holding all of the funds and is only callable by the Wallet. 
* Wallet.sol - 
    * Manages all of the note commitments and therefore keeps track (cryptographically) of which users own which notes. 
    * Makes the external contract calls for the user.
    * The entry point for users when they want to perform a DeFi transaction or a smart contract call. 

The Wallet will request the funds needed from the Vault, and then perform the contract call. Once the contract call is made, the Wallet will send back any remaining or new funds back to the Vault. The Wallet will then create new note commitments for these values for the user. The following diagram illustrates a high level view of the workflow:

![](https://i.imgur.com/Rq7h5S0.png)

### Batching
In order to get the lowest gas cost per user interaction, FLAX will batch multiple transactions into one and submit them on-chain via a sequencer. This also enables the sequencer to pay gas in Ether for users, and the users can refund the sequencer with allowed ERC20 notes. The sequencer therefore must bundle all of the users' transaction data into one transaction call.

A function call of multiple user transactions to the Vault is structured in the following format:

1. Bundle - Contains an Operation for each user's message
2. Operation - Contains the spending and refund note commitment information and a series of actions for the user
3. Action - An external function call (ex. DeFi Swap)

These data types are represented as structs and are explained in more detail in later sections.

### FLAX API (External Functions)

#### Wallet.sol

**[1] ProcessBundle(Bundle bundle) returns (bool[] successes, bytes[][] results)**
* Performs every Operation inside the bundle
* For each Operation, handles:
    * Spend Txs - Verifies user owns the notes listed in the spend tx. Gathers specified funds from the Vault for each valid spend tx.
    * Actions - Makes an external call to the user-defined smart contract with the user-defined data for each Action. Note: An action can not call the Vault.
    * Refund Txs - Creates new note commitments for the user for each asset type with positive balance. Sends the funds to the Vault. Asset types must be known in advance.
* The Wallet will contain 0 funds before this call, and 0 funds after. 

**[2] DepositFunds(Deposit deposit)**
* Creates new note commitments for the deposited amount for the user calling this function.
* Caller must be the owner of the deposit tokens.
    * This is to prevent any user from making your deposits for you if you have approved the vault to spend the tokens
* User must first approve the **Vault** to spend the deposited amount.
    * The Vault is the one making the transfer to prevent users from using processBundle with a malicious action to transfer the funds how they like
* Calls the Vault.makeDeposit(deposit), and the Vault will call (assetType).transfer(deposit.spender, address(this), deposit.value).

**[3] BatchDepositFunds(Deposit[] deposits, Signature[] sigs)**
* Makes a series of deposits in one call. Meant to be called by the sequencer.
* The signatures must be from the address that owns the tokens being deposited and they must have signed their respective deposit.
* Users must first approve the **Vault** to spend the deposited amount.
* Calls the Vault.makeBatchDeposit(deposits), and the Vault will call Vault.makeDeposit(deposits[i]) for each deposit.

**[4] Commit8FromQueue()**
* Commits 8 leaves in the leafQueue to the noteCommitmentTree. The sequencer is responsible for calling this function periodically. Note: User's cannot spend note commitments in the queue. They must be commited to the tree via this function in order to be spent.

#### Vault.sol
These are the exposed functions that only the Wallet can call.

**[1] RequestFunds(uint256[] values, address[] assetTypes) onlyWallet**
* Transfers the given value for each asset type to the Wallet.
* Called by the Wallet after the Wallet verified valid SpendTxs corresponding to the given values and asset types.
* Not callable by processBundles since actions cannot call the Vault.

**[2] makeDeposit(IFLAXTeller.Deposit calldata deposit) onlyWallet returns (bool)**
* Calls assetType.transfer(deposit.spender, address(this), deposit.value) - essentially transfering approved funds to this contract.
* Returns the success status of the transfer call.

**[3] makeBatchDeposit(Deposit[] deposits, uint256 numApprovedDeposits) onlyWallet returns (uint256[], uint256)**
* Calls Vault.makeDeposit(deposits[i]) for each deposit.
* Returns an array of the indexes of the successful deposits, and the number of successful deposits.


## Appendix

### Future Features
* **Proof aggregation** - We can reduce gas costs by aggregating multiple users' spend proofs into a single proof. See https://eprint.iacr.org/2021/529.pdf
* **Note joining** - We can currently only spend one note at a time. We want to enable joining notes together via a single spend proof. This will reduce spend costs and enable easier confidential payments.
* **Easier confidential payments** - enabled by note joining

### Gas Estimates
Note: Got these from Wei Jie's slides from 0xPARC group - https://docs.google.com/presentation/d/1G1zQjTKPKclUtwaYidek07eceL7BB1rZ0nMH39dU3yw/edit#slide=id.p
https://ethresear.ch/t/gas-and-circuit-constraint-benchmarks-of-binary-and-quinary-incremental-merkle-trees-using-the-poseidon-hash-function/7446
* Groth16 proof verification: ~200k gas + ~21k gas per public input
* Aggregate proof (8 aggregated spend proofs) verification: ~350-400k gas
* Poseidon (2 inputs): 53189 gas, 240 constraints
* Poseidon (5 inputs): 121984 gas, ??? constraints
* SHA256 (2 inputs): 2179 gas, 409926 constraints
* Proof generation (1.1M constraints):
  * ~ 7 seconds on an Intel i7 1.80GHz laptop with rapidsnark (optimised for Intel x86)
  * ~ 17 seconds with zkutil (Rust/bellman)
  * Minutes with snarkjs (Nodejs)
* Binary Merkle Tree (Poseidon Hash)
    * Depth = 32, Gas Cost = 1.32 million (for entire transaction)
* Quinary Merkle Tree (Poseidon Hash)
    * Depth = 15, Gas Cost = 1.78 million (for entire transaction)

Gas costs for performOperation:
* 1 merkle tree insert per spend ~1.3 million gas
* 1 proof verification per spend ~200k gas + 21k per pub input
* 1 Poseidon hash per refund tx ~50k gas
* 1 merkle tree insert per refund tx ~1.3 million gas
* 2 contract.balanceOf calls per ERC20 interacted with

Gas costs for performOperation consisting of n ops, m spends, and p refunds:
* 1 Merkle tree insertion for 8 note commitments ~1.4 mil
  * Note: 1 Merkle tree insertion for 16 note commitments is ~1.6mil
* 1 proof verification per spend ~m*(200k + 7*21k)
* 1 poseidon hash per refund ~p*50k
* 2 transfer and balance calls per asset involved ~4.1k for transfer, ~1.4k for balanceOf (depends on ERC20 impl)
* Total estimated overhead (up to 8 note commitments, (m + p) <= 8): ~1.4mil + m*(200k + 7* 21k) + p*50k
* Assuming 4 operations batched (n = 4), 1 spend and 1 refund per operation (8 total potential note commitments commited in a batch to the merkle tree):
  * 1.4 million + 4*(200k + 147k) + 4*50k
  * = ~2.988 million gas for the bundle
  * = ~747k gas per user operation
* Assuming 8 operations batched (n = 8), 1 spend and 1 refund per operation (16 total potential note commitments commited in a batch to the merkle tree):
  * 1.6 million + 8*(200k + 147k) + 8*50k
  * = ~4.776 million gas for the bundle
  * = ~597k gas per user operation
* Assuming 8 operations batched (n=8) with *spend proof aggregation*, 1 aggregate spend proof, 1 refund per operation (still 16 potential note commitments added to tree between spends and refunds):
  * 1.6 million + ~350k + 8*50k
  * = ~2.35 million gas for the bundle
  * = ~293k gas per user operation

### Proving time estimates

Approach 1: 12k constraints, <5s with wasmsnark, per numbers provided here https://github.com/tornadocash/tornado-core/tree/master/circuits
Approach 2: 16k constraints, ~5s with wasmsnark

### Links
* FLAX Paper - https://eprint.iacr.org/2021/1249.pdf
