Skip to main content

Overview

These functions provide a high-level API for interacting with the TornadoPool contract. They handle proof generation, merkle tree construction, and transaction submission. Source Location: src/index.js

Functions

buildMerkleTree

async function buildMerkleTree({ tornadoPool })
Builds a local merkle tree from all commitments in the TornadoPool. Required for generating proofs.
tornadoPool
Contract
required
Ethers.js contract instance of TornadoPool
Returns: MerkleTree - A fixed-merkle-tree instance containing all commitments Process:
  1. Queries all NewCommitment events from block 0
  2. Sorts events by index
  3. Extracts commitment hashes
  4. Constructs merkle tree using Poseidon hash function
Source: src/index.js:11
const { buildMerkleTree } = require('./src/index')
const { ethers } = require('hardhat')

const tornadoPool = await ethers.getContractAt('TornadoPool', poolAddress)
const tree = await buildMerkleTree({ tornadoPool })

console.log(`Tree root: ${tree.root()}`)
console.log(`Tree has ${tree.elements.length} commitments`)

prepareTransaction

async function prepareTransaction({
  tornadoPool,
  inputs = [],
  outputs = [],
  fee = 0,
  recipient = 0,
  relayer = 0,
  isL1Withdrawal = false,
  l1Fee = 0
})
Prepares a transaction by generating a ZK proof without submitting it on-chain. Useful for estimating gas or when you need to submit the transaction separately.
tornadoPool
Contract
required
Ethers.js contract instance of TornadoPool
inputs
Utxo[]
default:"[]"
Array of input UTXOs to spend (1-16 UTXOs). Will be padded with dummy UTXOs if needed.
outputs
Utxo[]
default:"[]"
Array of output UTXOs to create (1-2 UTXOs). Will be padded with dummy UTXOs if needed.
fee
BigNumber | number
default:"0"
Relayer fee in token wei. Goes to the relayer address.
recipient
string
default:"0"
Withdrawal recipient address (for withdrawals only, set to 0 for pure transfers)
relayer
string
default:"0"
Relayer address to receive fees
isL1Withdrawal
boolean
default:"false"
If true, withdraws to L1 via OmniBridge
l1Fee
BigNumber | number
default:"0"
Fee for L1 execution (only for L1 withdrawals)
Returns: Promise<{ args, extData }>
args
Proof
Proof structure to pass to contract:
  • proof - ZK-SNARK proof bytes
  • root - Merkle tree root
  • inputNullifiers - Array of nullifier hashes
  • outputCommitments - Array of commitment hashes
  • publicAmount - Public amount field
  • extDataHash - Hash of external data
extData
ExtData
External data structure:
  • recipient - Withdrawal recipient
  • extAmount - External amount (positive for deposits, negative for withdrawals)
  • relayer - Relayer address
  • fee - Relayer fee
  • encryptedOutput1 - Encrypted first output
  • encryptedOutput2 - Encrypted second output
  • isL1Withdrawal - L1 withdrawal flag
  • l1Fee - L1 execution fee
Throws:
  • Error if inputs length > 16 or outputs length > 2
Source: src/index.js:100
const Utxo = require('./src/utxo')
const { prepareTransaction } = require('./src/index')

// Create output UTXOs for recipient
const utxo1 = new Utxo({ 
  amount: ethers.utils.parseEther('5'),
  keypair: recipientKeypair 
})
const utxo2 = new Utxo({ 
  amount: ethers.utils.parseEther('5'),
  keypair: recipientKeypair 
})

const { args, extData } = await prepareTransaction({
  tornadoPool,
  inputs: [], // No inputs for deposit
  outputs: [utxo1, utxo2],
  fee: 0,
  recipient: 0,
  relayer: 0
})

// extData.extAmount will be 10 ETH (sum of outputs)
console.log(`Deposit amount: ${extData.extAmount}`)

transaction

async function transaction({ tornadoPool, ...rest })
Executes a complete transaction: prepares the proof and submits it to the TornadoPool contract.
tornadoPool
Contract
required
Ethers.js contract instance of TornadoPool
...rest
object
All other parameters from prepareTransaction (inputs, outputs, fee, etc.)
Returns: Promise<TransactionReceipt> - Ethers.js transaction receipt Process:
  1. Calls prepareTransaction to generate proof
  2. Calls tornadoPool.transact(args, extData) with gas limit of 2M
  3. Waits for transaction confirmation
  4. Returns transaction receipt
Source: src/index.js:142
const { transaction } = require('./src/index')
const Utxo = require('./src/utxo')

const output = new Utxo({ 
  amount: ethers.utils.parseEther('10'),
  keypair: myKeypair 
})

const receipt = await transaction({
  tornadoPool,
  inputs: [],
  outputs: [output, new Utxo()],
  fee: 0,
  recipient: 0,
  relayer: 0
})

console.log(`Transaction hash: ${receipt.transactionHash}`)
console.log(`Gas used: ${receipt.gasUsed}`)

registerAndTransact

async function registerAndTransact({ tornadoPool, account, ...rest })
Registers an account and executes a transaction in a single call. Useful for first-time deposits.
tornadoPool
Contract
required
Ethers.js contract instance of TornadoPool
account
Account
required
Account object with:
  • owner (address) - Account owner address
  • publicKey (bytes) - Public key for receiving notes
...rest
object
All other parameters from prepareTransaction
Returns: Promise<void> - Resolves when transaction is confirmed Process:
  1. Calls prepareTransaction to generate proof
  2. Calls tornadoPool.registerAndTransact(account, args, extData)
  3. Waits for transaction confirmation
Source: src/index.js:154
const { registerAndTransact } = require('./src/index')
const { Keypair } = require('./src/keypair')
const Utxo = require('./src/utxo')

const keypair = new Keypair()
const depositAmount = ethers.utils.parseEther('10')

// Approve tokens
await token.approve(tornadoPool.address, depositAmount)

// Register and deposit
const account = {
  owner: await signer.getAddress(),
  publicKey: keypair.toString()
}

await registerAndTransact({
  tornadoPool,
  account,
  inputs: [],
  outputs: [
    new Utxo({ amount: depositAmount, keypair })
  ]
})

console.log('Registered and deposited!')

Transaction Types by Parameters

Deposits add funds to the pool from an external account.
{
  inputs: [],
  outputs: [utxo1, utxo2],
  fee: 0,
  recipient: 0,
  relayer: 0
}
  • No inputs (empty array)
  • Outputs define how much to deposit
  • extAmount = sum of outputs (positive)
  • Must approve tokens before calling
Input/Output Padding: The library automatically pads inputs to 2 or 16 and outputs to 2 using dummy UTXOs with zero amount. You don’t need to manually add dummy UTXOs.
Gas Limit: All transaction functions use a fixed gas limit of 2,000,000. For transactions with 16 inputs, you may need to adjust this limit.

Build docs developers (and LLMs) love