Overview
This page documents all the key data structures, classes, and type definitions used in the Tornado Nova system.
Contract Types
These types mirror the Solidity structs used in the smart contracts.
ExtData
interface ExtData {
recipient : string // Address (hex string, 20 bytes)
extAmount : BigNumber // External amount (positive for deposit, negative for withdrawal)
relayer : string // Relayer address (hex string, 20 bytes)
fee : BigNumber // Relayer fee amount
encryptedOutput1 : string // Encrypted first output UTXO (hex string)
encryptedOutput2 : string // Encrypted second output UTXO (hex string)
isL1Withdrawal : boolean // Whether this is an L1 withdrawal
l1Fee : BigNumber // L1 execution fee (only for L1 withdrawals)
}
External data structure passed to transaction functions. Contains public transaction information.
Source: contracts/TornadoPool.sol:42
Withdrawal recipient address. Use toFixedHex(address, 20) to format. Set to 0 for pure transfers.
External amount in token wei:
Positive : Deposit (adding funds to pool)
Negative : Withdrawal (removing funds from pool)
Zero : Pure transfer (no external funds movement)
Formula: extAmount = sum(outputs) - sum(inputs) + fee Address to receive the relayer fee. Use toFixedHex(address, 20) to format.
Fee paid to relayer for submitting transaction. Enables privacy by separating payer from recipient.
First output UTXO encrypted for recipient. Contains amount and blinding factor. Format: 0x + hex (24 bytes nonce + 32 bytes ephemeral key + ciphertext).
Second output UTXO (often used for change back to sender).
If true, funds are bridged to L1 via OmniBridge and unwrapped to ETH.
Fee for L1 transaction execution. Deducted from withdrawal amount and sent to L1 fee receiver.
Proof
interface Proof {
proof : string // ZK-SNARK proof bytes (hex string)
root : string // Merkle tree root (bytes32)
inputNullifiers : string [] // Array of nullifier hashes (bytes32[])
outputCommitments : string [] // Array of 2 commitment hashes (bytes32[2])
publicAmount : BigNumber // Public amount field for ZK circuit
extDataHash : string // Hash of ExtData (bytes32)
}
Proof structure containing ZK-SNARK proof and transaction data.
Source: contracts/TornadoPool.sol:53
ZK-SNARK proof bytes generated by the prover. Hex string starting with 0x.
Merkle tree root at the time of proof generation. Must be a known root (stored in contract history).
Array of nullifier hashes for input UTXOs being spent. Length must be 2 or 16. Each nullifier can only be used once.
Array of exactly 2 commitment hashes for output UTXOs being created.
Public amount in field element format. Calculated as (extAmount - fee) mod FIELD_SIZE.
Keccak256 hash of ExtData structure, modulo FIELD_SIZE. Used to bind external data to the proof.
Account
interface Account {
owner : string // Ethereum address
publicKey : string // Public key bytes (hex string, 64 bytes)
}
Account structure for registering users in the system.
Sources:
contracts/TornadoPool.sol:62
contracts/bridge/L1Unwrapper.sol:32
Ethereum address of the account owner. Must match msg.sender when registering.
Combined public key (64 bytes = 128 hex chars):
Bytes 0-31: Poseidon public key for signature verification
Bytes 32-63: x25519 encryption key for receiving encrypted notes
Get from: keypair.toString()
JavaScript Classes
Keypair
class Keypair {
privkey : string // Private key (hex string with 0x prefix)
pubkey : BigNumber // Poseidon public key
encryptionKey : string // x25519 encryption key (base64)
constructor ( privkey ?: string )
toString () : string
address () : string
static fromString ( str : string ) : Keypair
sign ( commitment : BigNumberish , merklePath : BigNumberish ) : BigNumber
encrypt ( bytes : Buffer ) : string
decrypt ( data : string ) : Buffer
}
Keypair class for managing private/public keys, signing, and encryption.
Source: src/keypair.js:36
constructor constructor ( privkey ?: string )
Creates a new keypair. If no private key is provided, generates a random one. Private key as hex string with ‘0x’ prefix. If omitted, generates random key.
const keypair1 = new Keypair () // Random
const keypair2 = new Keypair ( '0x1234...' ) // From existing key
toString / address toString (): string
address (): string
Returns the keypair as a string (address format). Both methods are identical. Returns: 128-character hex string (with ‘0x’ prefix):
First 64 chars (32 bytes): Poseidon pubkey
Last 64 chars (32 bytes): Encryption key
const address = keypair . toString ()
// "0x1abc...def" (128 hex chars)
fromString static fromString ( str : string ): Keypair
Creates a keypair from an address string (public keys only, no private key). 128-character hex string (with or without ‘0x’ prefix)
Returns: Keypair with privkey: nullconst publicKeypair = Keypair . fromString ( address )
// Can encrypt but cannot decrypt or sign
sign sign ( commitment : BigNumberish , merklePath : BigNumberish ): BigNumber
Signs a commitment with the private key. Used for nullifier generation. Returns: Signature as poseidonHash(privkey, commitment, merklePath)const signature = keypair . sign ( utxo . getCommitment (), utxo . index )
encrypt encrypt ( bytes : Buffer ): string
Encrypts data using the keypair’s encryption key. Returns: Hex string with encrypted dataconst encrypted = keypair . encrypt ( Buffer . from ( 'secret' ))
decrypt decrypt ( data : string ): Buffer
Decrypts data using the private key. Requires private key. Hex string with encrypted data
Returns: Decrypted data as Bufferconst decrypted = keypair . decrypt ( encrypted )
console . log ( decrypted . toString ( 'utf8' ))
Utxo
class Utxo {
amount : BigNumber // UTXO amount in wei
blinding : BigNumber // Random blinding factor
keypair : Keypair // Owner's keypair
index : number | null // Index in merkle tree
constructor ({ amount ?, keypair ?, blinding ?, index ? })
getCommitment () : BigNumber
getNullifier () : BigNumber
encrypt () : string
static decrypt ( keypair : Keypair , data : string , index : number ) : Utxo
}
UTXO (Unspent Transaction Output) class representing a private note.
Source: src/utxo.js:6
constructor constructor ({
amount? : BigNumberish ,
keypair? : Keypair ,
blinding? : BigNumberish ,
index? : number | null
})
Creates a new UTXO. keypair
Keypair
default: "new Keypair()"
Owner’s keypair. Random keypair generated if omitted.
blinding
BigNumberish
default: "randomBN()"
Random blinding factor for privacy. Random value generated if omitted.
index
number | null
default: "null"
Position in merkle tree. Set when UTXO is committed on-chain.
// Create deposit UTXO
const utxo = new Utxo ({
amount: ethers . utils . parseEther ( '10' ),
keypair: myKeypair
})
// Create dummy UTXO (for padding)
const dummy = new Utxo ()
getCommitment getCommitment (): BigNumber
Calculates and caches the commitment hash. Returns: poseidonHash(amount, pubkey, blinding)const commitment = utxo . getCommitment ()
getNullifier getNullifier (): BigNumber
Calculates the nullifier hash. Requires index and keypair.privkey. Returns: poseidonHash(commitment, index, signature)Throws: Error if index or private key is missing for non-zero amount UTXOsutxo . index = 42 // Set from event
const nullifier = utxo . getNullifier ()
encrypt Encrypts the UTXO data (amount and blinding) using the keypair. Returns: Hex string with encrypted data (24 bytes nonce + 32 bytes ephemeral key + 62 bytes ciphertext)const encrypted = utxo . encrypt ()
// Store in ExtData.encryptedOutput1
decrypt (static) static decrypt ( keypair : Keypair , data : string , index : number ): Utxo
Decrypts a UTXO from encrypted data. Keypair with private key for decryption
Encrypted data hex string
UTXO index from NewCommitment event
Returns: Decrypted Utxo instanceThrows: Error if decryption fails (wrong keypair)// Listen for commitments
tornadoPool . on ( 'NewCommitment' , ( commitment , index , encrypted ) => {
try {
const utxo = Utxo . decrypt ( myKeypair , encrypted , index )
console . log ( `Received ${ utxo . amount } !` )
} catch ( e ) {
// Not for us
}
})
Type Aliases
type BigNumberish = BigNumber | string | number
type Address = string // Ethereum address (0x...)
type Bytes32 = string // 32-byte hex string (0x...)
type HexString = string // Hex string with 0x prefix
Usage Examples
Complete Transaction Types
Account Registration
UTXO Lifecycle
Type Validation
const { Keypair } = require ( './src/keypair' )
const Utxo = require ( './src/utxo' )
const { prepareTransaction } = require ( './src/index' )
const { ethers } = require ( 'hardhat' )
// Create keypair
const keypair = new Keypair ()
// Create UTXOs
const input = new Utxo ({
amount: ethers . utils . parseEther ( '10' ),
keypair ,
index: 42
})
const output = new Utxo ({
amount: ethers . utils . parseEther ( '8' ),
keypair: recipientKeypair
})
const change = new Utxo ({
amount: ethers . utils . parseEther ( '1.9' ),
keypair
})
// Prepare transaction
const { args , extData } = await prepareTransaction ({
tornadoPool ,
inputs: [ input ],
outputs: [ output , change ],
fee: ethers . utils . parseEther ( '0.1' ),
recipient: recipientAddress ,
relayer: relayerAddress
})
// args is Proof type
// extData is ExtData type
Dummy UTXOs: When creating dummy UTXOs for padding, use new Utxo() with no parameters. This creates a UTXO with amount 0 and random blinding/keypair.
Index Required: The UTXO index field must be set before calling getNullifier() for spending. Get the index from the NewCommitment event.
Type Safety: When using TypeScript, import type definitions:import type { ExtData , Proof , Account } from './types'