Initial commit

This commit is contained in:
Tornado Contrib 2024-07-22 13:02:53 -07:00
commit e9d78d3a7f
Signed by: tornadocontrib
GPG Key ID: 60B4DF1A076C64B1
8 changed files with 1316 additions and 0 deletions

27
.eslintrc.js Normal file

@ -0,0 +1,27 @@
module.exports = {
'env': {
'browser': false,
'node': true,
'commonjs': true,
'es2022': true
},
'extends': 'eslint:recommended',
'rules': {
'indent': [
'error',
2
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
],
}
};

5
.gitignore vendored Normal file

@ -0,0 +1,5 @@
node_modules
package-lock.json
yarn.lock
backup*
*.zip

24
README.md Normal file

@ -0,0 +1,24 @@
# Offline Tornado Cash Note Generation Tool
This tool allows you to create Tornado Cash deposit transaction data offline (So your note data is never exposed to online!)
### How to use?
```bash
# This will install all the necessary libraries
yarn
node ./tornadoNote.js <CHAIN_ID> <CURRENCY> <AMOUNT>
node ./tornadoNote.js --help
node ./tornadoNote.js 1 eth 0.1
node ./tornadoNote.js 1 dai 10000
node ./tornadoNote.js 56 bnb 100
```
Will also create backup similar with how Tornado UI or tornado-cli generates it
Use Transaction Data to submit ethereum transaction on https://myetherwallet.com, https://mycrypto.com, or https://remix.ethereum.org

228
abi/ERC20.abi.json Normal file

@ -0,0 +1,228 @@
[
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "_totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "who",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" },
{ "internalType": "uint256", "name": "deadline", "type": "uint256" },
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

237
abi/TornadoProxy.abi.json Normal file

@ -0,0 +1,237 @@
[
{
"inputs": [
{
"internalType": "bytes32",
"name": "_tornadoTrees",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_governance",
"type": "bytes32"
},
{
"internalType": "bytes32[]",
"name": "_instances",
"type": "bytes32[]"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes",
"name": "encryptedNote",
"type": "bytes"
}
],
"name": "EncryptedNote",
"type": "event"
},
{
"inputs": [
{
"internalType": "bytes32[]",
"name": "domains",
"type": "bytes32[]"
}
],
"name": "bulkResolve",
"outputs": [
{
"internalType": "address[]",
"name": "result",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "governance",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "",
"type": "address"
}
],
"name": "instances",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "resolve",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tornadoTrees",
"outputs": [
{
"internalType": "contract ITornadoTrees",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "_tornado",
"type": "address"
},
{
"internalType": "bytes32",
"name": "_commitment",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "_encryptedNote",
"type": "bytes"
}
],
"name": "deposit",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "_instance",
"type": "address"
},
{
"internalType": "bool",
"name": "_update",
"type": "bool"
}
],
"name": "updateInstance",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "_tornado",
"type": "address"
},
{
"internalType": "bytes",
"name": "_proof",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "_root",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_nullifierHash",
"type": "bytes32"
},
{
"internalType": "address payable",
"name": "_recipient",
"type": "address"
},
{
"internalType": "address payable",
"name": "_relayer",
"type": "address"
},
{
"internalType": "uint256",
"name": "_fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_refund",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IERC20",
"name": "_token",
"type": "address"
},
{
"internalType": "address payable",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_balance",
"type": "uint256"
}
],
"name": "rescueTokens",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

533
networkConfig.js Normal file

@ -0,0 +1,533 @@
const blockSyncInterval = 10000;
const enabledChains = ['1', '10', '56', '100', '137', '42161', '43114', '11155111'];
const networkConfig = {
netId1: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 80,
fast: 50,
standard: 25,
low: 8
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: {
tx: 'https://etherscan.io/tx/',
address: 'https://etherscan.io/address/',
block: 'https://etherscan.io/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Ethereum Mainnet',
deployedBlock: 9116966,
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/mainnet'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
mevblockerRPC: {
name: 'MevblockerRPC',
url: 'https://rpc.mevblocker.io'
},
oneRPC: {
name: '1RPC',
url: 'https://1rpc.io/eth'
}
},
multicall: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
routerContract: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
registryContract: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2',
echoContractAccount: '0x9B27DD5Bb15d42DC224FCD0B7caEbBe16161Df42',
aggregatorContract: '0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49',
tokens: {
eth: {
instanceAddress: {
'0.1': '0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc',
'1': '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
'10': '0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF',
'100': '0xA160cdAB225685dA1d56aa342Ad8841c3b53f291'
},
symbol: 'ETH',
decimals: 18
},
dai: {
instanceAddress: {
'100': '0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3',
'1000': '0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144',
'10000': '0x07687e702b410Fa43f4cB4Af7FA097918ffD2730',
'100000': '0x23773E65ed146A459791799d01336DB287f25334'
},
tokenAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
symbol: 'DAI',
decimals: 18,
gasLimit: '55000'
},
cdai: {
instanceAddress: {
'5000': '0x22aaA7720ddd5388A3c0A3333430953C68f1849b',
'50000': '0x03893a7c7463AE47D46bc7f091665f1893656003',
'500000': '0x2717c5e28cf931547B621a5dddb772Ab6A35B701',
'5000000': '0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af'
},
tokenAddress: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643',
symbol: 'cDAI',
decimals: 8,
gasLimit: '425000'
},
/**
* Instances frozen due to sanctions
usdc: {
instanceAddress: {
'100': '0xd96f2B1c14Db8458374d9Aca76E26c3D18364307',
'1000': '0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D'
},
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
symbol: 'USDC',
decimals: 6,
gasLimit: '80000'
},
usdt: {
instanceAddress: {
'100': '0x169AD27A470D064DEDE56a2D3ff727986b15D52B',
'1000': '0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f'
},
tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
symbol: 'USDT',
decimals: 6,
gasLimit: '100000'
},
**/
wbtc: {
instanceAddress: {
'0.1': '0x178169B423a011fff22B9e3F3abeA13414dDD0F1',
'1': '0x610B717796ad172B316836AC95a2ffad065CeaB4',
'10': '0xbB93e510BbCD0B7beb5A853875f9eC60275CF498'
},
tokenAddress: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
symbol: 'WBTC',
decimals: 8,
gasLimit: '85000'
}
},
ensSubdomainKey: 'mainnet-tornado',
pollInterval: 15,
constants: {
GOVERNANCE_BLOCK: 11474695,
NOTE_ACCOUNT_BLOCK: 11842486,
ENCRYPTED_NOTES_BLOCK: 14248730,
MINING_BLOCK_TIME: 15
},
'torn.contract.tornadocash.eth': '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
'governance.contract.tornadocash.eth': '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
'tornado-router.contract.tornadocash.eth': '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
'staking-rewards.contract.tornadocash.eth': '0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29'
},
netId56: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 5,
fast: 5,
standard: 5,
low: 5
},
nativeCurrency: 'bnb',
currencyName: 'BNB',
explorerUrl: {
tx: 'https://bscscan.com/tx/',
address: 'https://bscscan.com/address/',
block: 'https://bscscan.com/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Binance Smart Chain',
deployedBlock: 8158799,
multicall: '0x41263cba59eb80dc200f3e2544eda4ed6a90e76c',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/bsc'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://bsc-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRPC: {
name: '1RPC',
url: 'https://1rpc.io/bnb'
}
},
tokens: {
bnb: {
instanceAddress: {
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD'
},
symbol: 'BNB',
decimals: 18
}
},
ensSubdomainKey: 'bsc-tornado',
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 8159269,
ENCRYPTED_NOTES_BLOCK: 8159269
},
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17'
},
netId137: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 100,
fast: 75,
standard: 50,
low: 30
},
nativeCurrency: 'matic',
currencyName: 'MATIC',
explorerUrl: {
tx: 'https://polygonscan.com/tx/',
address: 'https://polygonscan.com/address/',
block: 'https://polygonscan.com/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Polygon (Matic) Network',
deployedBlock: 16257962,
multicall: '0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://polygon-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/matic'
}
},
tokens: {
matic: {
instanceAddress: {
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
'1000': '0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178',
'10000': '0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040',
'100000': '0xa5C2254e4253490C54cef0a4347fddb8f75A4998'
},
symbol: 'MATIC',
decimals: 18
}
},
ensSubdomainKey: 'polygon-tornado',
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 16257996,
ENCRYPTED_NOTES_BLOCK: 16257996
},
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17'
},
netId10: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 0.001,
fast: 0.001,
standard: 0.001,
low: 0.001
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: {
tx: 'https://optimistic.etherscan.io/tx/',
address: 'https://optimistic.etherscan.io/address/',
block: 'https://optimistic.etherscan.io/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Optimism',
deployedBlock: 2243689,
multicall: '0x35A6Cdb2C9AD4a45112df4a04147EB07dFA01aB7',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/op'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/op'
}
},
tokens: {
eth: {
instanceAddress: {
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD'
},
symbol: 'ETH',
decimals: 18
}
},
ensSubdomainKey: 'optimism-tornado',
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 2243694,
ENCRYPTED_NOTES_BLOCK: 2243694
},
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17'
},
netId42161: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 4,
fast: 3,
standard: 2.52,
low: 2.29
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: {
tx: 'https://arbiscan.io/tx/',
address: 'https://arbiscan.io/address/',
block: 'https://arbiscan.io/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Arbitrum One',
deployedBlock: 3430648,
multicall: '0x842eC2c7D803033Edf55E478F461FC547Bc54EB2',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/arbitrum'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRpc: {
name: '1rpc',
url: 'https://1rpc.io/arb'
},
Arbitrum: {
name: 'Arbitrum RPC',
url: 'https://arb1.arbitrum.io/rpc'
}
},
tokens: {
eth: {
instanceAddress: {
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD'
},
symbol: 'ETH',
decimals: 18
}
},
ensSubdomainKey: 'arbitrum-tornado',
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605
},
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17'
},
netId100: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 6,
fast: 5,
standard: 4,
low: 1
},
nativeCurrency: 'xdai',
currencyName: 'xDAI',
explorerUrl: {
tx: 'https://blockscout.com/xdai/mainnet/tx/',
address: 'https://blockscout.com/xdai/mainnet/address/',
block: 'https://blockscout.com/xdai/mainnet/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Gnosis Chain',
deployedBlock: 17754561,
multicall: '0xb5b692a88bdfc81ca69dcb1d924f59f0413a602a',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/gnosis'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://gnosis-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
blockPi: {
name: 'BlockPi',
url: 'https://gnosis.blockpi.network/v1/rpc/public'
}
},
tokens: {
xdai: {
instanceAddress: {
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
'1000': '0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178',
'10000': '0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040',
'100000': '0xa5C2254e4253490C54cef0a4347fddb8f75A4998'
},
symbol: 'xDAI',
decimals: 18
}
},
ensSubdomainKey: 'gnosis-tornado',
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 17754564,
ENCRYPTED_NOTES_BLOCK: 17754564
},
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17'
},
netId43114: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 225,
fast: 35,
standard: 25,
low: 25
},
nativeCurrency: 'avax',
currencyName: 'AVAX',
explorerUrl: {
tx: 'https://snowtrace.io/tx/',
address: 'https://snowtrace.io/address/',
block: 'https://snowtrace.io/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Avalanche Mainnet',
deployedBlock: 4429818,
multicall: '0xe86e3989c74293Acc962156cd3F525c07b6a1B6e',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
publicRpc: {
name: 'Avalanche RPC',
url: 'https://api.avax.network/ext/bc/C/rpc'
},
meowRPC: {
name: 'Meow RPC',
url: 'https://avax.meowrpc.com'
},
oneRPC: {
name: 'OneRPC',
url: 'https://1rpc.io/avax/c'
}
},
tokens: {
avax: {
instanceAddress: {
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
'500': '0xaf8d1839c3c67cf571aa74B5c12398d4901147B3'
},
symbol: 'AVAX',
decimals: 18
}
},
ensSubdomainKey: 'avalanche-tornado',
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 4429813,
ENCRYPTED_NOTES_BLOCK: 4429813
},
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17'
},
netId11155111: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 2,
fast: 2,
standard: 2,
low: 2
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: {
tx: 'https://sepolia.etherscan.io/tx/',
address: 'https://sepolia.etherscan.io/address/',
block: 'https://sepolia.etherscan.io/block/'
},
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Ethereum Sepolia',
deployedBlock: 5594395,
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
echoContractAccount: '0xcDD1fc3F5ac2782D83449d3AbE80D6b7B273B0e5',
aggregatorContract: '0x4088712AC9fad39ea133cdb9130E465d235e9642',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/sepolia'
},
sepolia: {
name: 'Sepolia RPC',
url: 'https://rpc.sepolia.org'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://sepolia.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
}
},
tokens: {
eth: {
instanceAddress: {
'0.1': '0x8C4A04d872a6C1BE37964A21ba3a138525dFF50b',
'1': '0x8cc930096B4Df705A007c4A039BDFA1320Ed2508',
'10': '0x8D10d506D29Fc62ABb8A290B99F66dB27Fc43585',
'100': '0x44c5C92ed73dB43888210264f0C8b36Fd68D8379'
},
symbol: 'ETH',
decimals: 18
},
dai: {
instanceAddress: {
'100': '0x6921fd1a97441dd603a997ED6DDF388658daf754',
'1000': '0x50a637770F5d161999420F7d70d888DE47207145',
'10000': '0xecD649870407cD43923A816Cc6334a5bdf113621',
'100000': '0x73B4BD04bF83206B6e979BE2507098F92EDf4F90'
},
tokenAddress: '0xFF34B3d4Aee8ddCd6F9AFFFB6Fe49bD371b8a357',
symbol: 'DAI',
decimals: 18,
gasLimit: '55000'
}
},
ensSubdomainKey: 'sepolia-tornado',
pollInterval: 15,
constants: {
GOVERNANCE_BLOCK: 5594395,
NOTE_ACCOUNT_BLOCK: 5594395,
ENCRYPTED_NOTES_BLOCK: 5594395,
MINING_BLOCK_TIME: 15
},
'torn.contract.tornadocash.eth': '0x3AE6667167C0f44394106E197904519D808323cA',
'governance.contract.tornadocash.eth': '0xe5324cD7602eeb387418e594B87aCADee08aeCAD',
'tornado-router.contract.tornadocash.eth': '0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee'
}
};
module.exports = { blockSyncInterval, enabledChains, networkConfig };

33
package.json Normal file

@ -0,0 +1,33 @@
{
"name": "tornado-offline-deposit",
"version": "1.0.0",
"description": "Command line tool to create Tornado Cash notes offline",
"main": "tornadoOffline.js",
"bin": {
"tornadoOffline": "tornadoOffline.js"
},
"scripts": {
"lint": "eslint *.js",
"start": "node tornadoOffline.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Tornado Cash Community",
"license": "MIT",
"files": [
"abi",
".eslintrc.js",
".gitignore",
"networkConfig.js",
"tornadoOffline.js",
"README.md"
],
"dependencies": {
"bn.js": "^5.2.1",
"circomlibjs": "0.1.7",
"commander": "^12.1.0",
"ethers": "^6.13.1"
},
"devDependencies": {
"eslint": "^8.57.0"
}
}

229
tornadoOffline.js Normal file

@ -0,0 +1,229 @@
#!/usr/bin/env node
const fs = require('fs');
const crypto = require('crypto').webcrypto;
const BN = require('bn.js');
const { Command } = require('commander');
const circomlibjs = require('circomlibjs');
const { MaxUint256, Interface } = require('ethers');
const ERC20ABI = require('./abi/ERC20.abi.json');
const RouterABI = require('./abi/TornadoProxy.abi.json');
const { enabledChains, networkConfig } = require('./networkConfig');
const { description, version } = require('./package.json');
const program = new Command();
const erc20Interface = new Interface(ERC20ABI);
const routerInterface = new Interface(RouterABI);
const bytesToHex = (bytes) => '0x' + Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
// Convert BE encoded bytes (Buffer | Uint8Array) array to BigInt
const bytesToBN = (bytes) => BigInt(bytesToHex(bytes));
// Convert LE encoded bytes (Buffer | Uint8Array) array to BigInt
// const leBuff2Int = (bytes) => new BN(bytes, 16, 'le');
// Convert BigInt to LE encoded Uint8Array type
const leInt2Buff = (bigint) => Uint8Array.from(new BN(bigint).toArray('le', 31));
const toFixedHex = (numberish, length = 32) => {
return '0x' + BigInt(numberish).toString(16).padStart(length * 2, '0');
};
const rBigInt = (nbytes = 31) => bytesToBN(crypto.getRandomValues(new Uint8Array(nbytes)));
// https://github.com/tornadocash/tornado-classic-ui/blob/master/services/pedersen.js
class Pedersen {
constructor() {
this.pedersenPromise = this.initPedersen();
}
async initPedersen() {
this.pedersenHash = await circomlibjs.buildPedersenHash();
this.babyJub = this.pedersenHash.babyJub;
}
async unpackPoint(buffer) {
await this.pedersenPromise;
return this.babyJub?.unpackPoint(this.pedersenHash?.hash(buffer));
}
toStringBuffer(bytes) {
return this.babyJub.F.toString(bytes);
}
}
const pedersen = new Pedersen();
const buffPedersenHash = async (bytes) => {
const [hash] = await pedersen.unpackPoint(bytes);
return pedersen.toStringBuffer(hash);
};
const createDeposit = async ({ nullifier, secret }) => {
const preimage = new Uint8Array([...leInt2Buff(nullifier), ...leInt2Buff(secret)]);
const noteHex = toFixedHex(bytesToBN(preimage), 62);
const commitment = BigInt(await buffPedersenHash(preimage));
const commitmentHex = toFixedHex(commitment);
const nullifierHash = BigInt(await buffPedersenHash(leInt2Buff(nullifier)));
const nullifierHex = toFixedHex(nullifierHash);
return {
preimage,
noteHex,
commitment,
commitmentHex,
nullifierHash,
nullifierHex,
};
};
/**
* Create Tornado Deposit Note and Invoice
*/
const createNote = async ({ netId, currency, amount }) => {
const nullifier = rBigInt(31);
const secret = rBigInt(31);
const depositObject = await createDeposit({
nullifier,
secret,
});
return {
currency,
amount,
netId,
note: `tornado-${currency}-${amount}-${netId}-${depositObject.noteHex}`,
invoice: `tornadoInvoice-${currency}-${amount}-${netId}-${depositObject.commitmentHex}`,
nullifier,
secret,
...depositObject,
};
};
const getInstanceInfo = ({ netId, currency, amount }) => {
const networkObject = networkConfig[`netId${netId}`];
const routerAddress = networkObject['tornado-router.contract.tornadocash.eth']
|| networkObject['tornado-proxy-light.contract.tornadocash.eth']
|| networkObject['tornado-proxy.contract.tornadocash.eth'];
const instanceAddress = networkObject.tokens[currency]?.instanceAddress[amount];
const instanceDecimals = Number(networkObject.tokens[currency]?.decimals);
const tokenAddress = networkObject.tokens[currency]?.tokenAddress;
const isNativeCurrency = networkObject.nativeCurrency === currency;
if (!instanceAddress) {
const errMsg = `Could not find a tornado instance ${netId} ${currency} ${amount}`;
throw new Error(errMsg);
}
const denomination = BigInt(amount * 10 ** instanceDecimals);
return {
routerAddress,
instanceAddress,
instanceDecimals,
tokenAddress,
isNativeCurrency,
denomination,
};
};
const newDeposit = async ({ netId, currency, amount }) => {
currency = String(currency).toLowerCase();
amount = Number(amount);
netId = Number(netId);
if (!enabledChains.includes(netId.toString())) {
const errMsg = `Unsupported chain ${netId}`;
throw new Error(errMsg);
}
const {
routerAddress,
instanceAddress,
tokenAddress,
isNativeCurrency,
denomination,
} = getInstanceInfo({ netId, currency, amount });
const {
note,
noteHex,
invoice,
commitmentHex: commitment,
nullifierHex: nullifierHash
} = await createNote({ netId, currency, amount });
const depositData = routerInterface.encodeFunctionData('deposit', [instanceAddress, commitment, '0x']);
console.log(
`New deposit: ${JSON.stringify({
note,
invoice,
commitment,
nullifierHash
}, null, 2)}\n`
);
// Backup locally
fs.writeFileSync(`./backup-tornado-${currency}-${amount}-${netId}-${noteHex.slice(0, 10)}.txt`, note, { encoding: 'utf8' });
if (isNativeCurrency) {
console.log(
`Transaction Data: ${JSON.stringify({
to: routerAddress,
value: denomination.toString(),
data: depositData
}, null, 2)}`
);
return;
}
const approveData = erc20Interface.encodeFunctionData('approve', [routerAddress, MaxUint256]);
console.log(
`Approve Data: ${JSON.stringify({
to: tokenAddress,
data: approveData
}, null, 2)}]\n`
);
console.log(
`Transaction Data: ${JSON.stringify({
to: routerAddress,
data: depositData
}, null, 2)}`
);
};
program
.name('tornadoOffline')
.description(description)
.version(version)
.argument('<netId>', 'netId of the supported network (Ethereum Mainnet: 1, Goerli Testnet: 5, BSC: 56)')
.argument('<currency>', 'Native Currency or Token supported by Tornado Cash')
.argument('<amount>', 'Amount to deposit (Check the UI for supported amount)')
.action((netId, currency, amount) => {
console.log('Creating offline Tornado Cash Note\n');
newDeposit({ netId, currency, amount });
});
program
.command('list')
.description('List tornado cash backup notes on local')
.action(() => {
const backups = fs.readdirSync('.').filter(f => f.includes('backup'));
const context = backups.map(b => fs.readFileSync(b, { encoding: 'utf8' })).join('\n');
console.log(context);
});
program.parse();