diff --git a/package.json b/package.json index d8308e4..ea68351 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "depositInvoice": "ts-node src/cli.ts depositInvoice", "withdraw": "ts-node src/cli.ts withdraw", "compliance": "ts-node src/cli.ts compliance", - "syncEvents": "ts-node src/cli.ts syncEvents", + "updateEvents": "ts-node src/cli.ts updateEvents", "relayers": "ts-node src/cli.ts relayers", "createAccount": "ts-node src/cli.ts createAccount", "decryptNotes": "ts-node src/cli.ts decryptNotes", diff --git a/src/program.ts b/src/program.ts index b46eb27..a50887f 100644 --- a/src/program.ts +++ b/src/program.ts @@ -29,6 +29,7 @@ import { MaxUint256, Transaction, BigNumberish, + getAddress, } from 'ethers'; import type MerkleTree from '@tornado/fixed-merkle-tree'; import * as packageJson from '../package.json'; @@ -80,6 +81,7 @@ import { substring, NoteAccount, parseRecoveryKey, + getSupportedInstances, } from './services'; const DEFAULT_GAS_LIMIT = 600_000; @@ -327,7 +329,11 @@ export async function getProgramRelayer({ relayerClient.selectedRelayer = { netId: relayerStatus.netId, url: relayerStatus.url, - rewardAccount: relayerStatus.rewardAccount, + hostname: new URL(relayerStatus.url).hostname, + rewardAccount: getAddress(relayerStatus.rewardAccount), + instances: getSupportedInstances(relayerStatus.instances), + gasPrice: relayerStatus.gasPrices?.fast, + ethPrices: relayerStatus.ethPrices, currentQueue: relayerStatus.currentQueue, tornadoServiceFee: relayerStatus.tornadoServiceFee, }; @@ -343,13 +349,7 @@ export async function getProgramRelayer({ const relayerStatus = validRelayers[0]; if (relayerStatus) { - relayerClient.selectedRelayer = { - netId: relayerStatus.netId, - url: relayerStatus.url, - rewardAccount: relayerStatus.rewardAccount, - currentQueue: relayerStatus.currentQueue, - tornadoServiceFee: relayerStatus.tornadoServiceFee, - }; + relayerClient.selectedRelayer = relayerStatus; } return { @@ -370,13 +370,7 @@ export async function getProgramRelayer({ const relayerStatus = relayerClient.pickWeightedRandomRelayer(validRelayers); if (relayerStatus) { - relayerClient.selectedRelayer = { - netId: relayerStatus.netId, - url: relayerStatus.url, - rewardAccount: relayerStatus.rewardAccount, - currentQueue: relayerStatus.currentQueue, - tornadoServiceFee: relayerStatus.tornadoServiceFee, - }; + relayerClient.selectedRelayer = relayerStatus; } return { @@ -529,7 +523,7 @@ export function tornadoProgram() { const config = networkConfig[`netId${netId}`]; const { - multicall: multicallAddress, + multicallContract, routerContract, echoContract, nativeCurrency, @@ -569,7 +563,7 @@ export function tornadoProgram() { } const TornadoProxy = TornadoRouter__factory.connect(routerContract, signer); - const Multicall = Multicall__factory.connect(multicallAddress, provider); + const Multicall = Multicall__factory.connect(multicallContract, provider); const Token = tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined; const [ethBalance, tokenBalance, tokenApprovals] = await multicall(Multicall, [ @@ -674,7 +668,7 @@ export function tornadoProgram() { const config = networkConfig[`netId${netId}`]; const { - multicall: multicallAddress, + multicallContract, routerContract, nativeCurrency, tokens: { [currency]: currencyConfig }, @@ -705,7 +699,7 @@ export function tornadoProgram() { } const TornadoProxy = TornadoRouter__factory.connect(routerContract, signer); - const Multicall = Multicall__factory.connect(multicallAddress, provider); + const Multicall = Multicall__factory.connect(multicallContract, provider); const Token = tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined; const [ethBalance, tokenBalance, tokenApprovals] = await multicall(Multicall, [ @@ -792,8 +786,8 @@ export function tornadoProgram() { tornadoSubgraph, deployedBlock, nativeCurrency, + multicallContract, routerContract, - multicall: multicallAddress, ovmGasPriceOracleContract, tokens: { [currency]: currencyConfig }, } = config; @@ -829,7 +823,7 @@ export function tornadoProgram() { const Tornado = Tornado__factory.connect(instanceAddress, provider); const TornadoProxy = TornadoRouter__factory.connect(routerContract, !walletWithdrawal ? provider : signer); - const Multicall = Multicall__factory.connect(multicallAddress, provider); + const Multicall = Multicall__factory.connect(multicallContract, provider); const tornadoFeeOracle = new TornadoFeeOracle( ovmGasPriceOracleContract @@ -1263,7 +1257,7 @@ export function tornadoProgram() { }); program - .command('syncEvents') + .command('updateEvents') .description('Sync the local cache file of tornado cash events.\n\n') .argument('[netId]', 'Network Chain ID to connect with (see https://chainlist.org for examples)', parseNumber) .argument('[currency]', 'Currency to sync events') @@ -1287,7 +1281,7 @@ export function tornadoProgram() { routerContract, echoContract, registryContract, - ['governance.contract.tornadocash.eth']: governanceContract, + governanceContract, deployedBlock, constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK }, } = config; @@ -1440,6 +1434,8 @@ export function tornadoProgram() { const validRelayers = allRelayers.validRelayers as RelayerInfo[]; const invalidRelayers = allRelayers.invalidRelayers as RelayerError[]; + console.log(validRelayers); + const relayersTable = new Table(); relayersTable.push( @@ -1699,7 +1695,7 @@ export function tornadoProgram() { const config = networkConfig[`netId${netId}`]; - const { currencyName, multicall: multicallAddress } = config; + const { currencyName, multicallContract } = config; const provider = getProgramProvider(netId, rpc, config, { ...fetchDataOptions, @@ -1715,7 +1711,7 @@ export function tornadoProgram() { const tokenAddress = tokenArgs ? parseAddress(tokenArgs) : tokenOpts; - const Multicall = Multicall__factory.connect(multicallAddress, provider); + const Multicall = Multicall__factory.connect(multicallContract, provider); const Token = (tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined) as ERC20; // Fetching feeData or nonce is unnecessary however we do this to estimate transfer amounts @@ -1775,6 +1771,11 @@ export function tornadoProgram() { const initCost = txGasPrice * BigInt('400000'); toSend = ethBalance - initCost; + if (ethBalance === BigInt(0) || ethBalance < initCost) { + const errMsg = `Invalid ${currencyName} balance, wants ${formatEther(initCost)} have ${formatEther(ethBalance)}`; + throw new Error(errMsg); + } + const estimatedGas = await provider.estimateGas({ type: txType, from: signer.address, @@ -1830,12 +1831,7 @@ export function tornadoProgram() { const config = networkConfig[`netId${netId}`]; - const { - currencyName, - multicall: multicallAddress, - ['torn.contract.tornadocash.eth']: tornTokenAddress, - tokens, - } = config; + const { currencyName, multicallContract, tornContract, tokens } = config; const provider = getProgramProvider(netId, rpc, config, { ...fetchDataOptions, @@ -1848,14 +1844,14 @@ export function tornadoProgram() { throw new Error('Address is required however no user address is supplied'); } - const Multicall = Multicall__factory.connect(multicallAddress, provider); + const Multicall = Multicall__factory.connect(multicallContract, provider); const tokenAddresses = Object.values(tokens) .map(({ tokenAddress }) => tokenAddress) .filter((t) => t) as string[]; - if (tornTokenAddress) { - tokenAddresses.push(tornTokenAddress); + if (tornContract) { + tokenAddresses.push(tornContract); } const tokenBalances = await getTokenBalances({ diff --git a/src/services/encryptedNotes.ts b/src/services/encryptedNotes.ts index 6bbe7c8..f71a4d6 100644 --- a/src/services/encryptedNotes.ts +++ b/src/services/encryptedNotes.ts @@ -1,6 +1,6 @@ import { getEncryptionPublicKey, encrypt, decrypt, EthEncryptedData } from '@metamask/eth-sig-util'; import { Echoer } from '@tornado/contracts'; -import { Wallet, computeAddress } from 'ethers'; +import { Wallet, computeAddress, getAddress } from 'ethers'; import { crypto, base64ToBytes, bytesToBase64, bytesToHex, hexToBytes, toFixedHex, concatBytes } from './utils'; import { EchoEvents, EncryptedNotesEvents } from './events'; @@ -164,7 +164,7 @@ export class NoteAccount { decryptedEvents.push({ blockNumber: event.blockNumber, - address, + address: getAddress(address), noteHex, }); } catch { diff --git a/src/services/graphql/index.ts b/src/services/graphql/index.ts index 212ead5..9c59a2d 100644 --- a/src/services/graphql/index.ts +++ b/src/services/graphql/index.ts @@ -645,7 +645,7 @@ export async function getNoteAccounts({ subgraphName, query: GET_NOTE_ACCOUNTS, variables: { - address, + address: address.toLowerCase(), }, fetchDataOptions, }); diff --git a/src/services/merkleTree.ts b/src/services/merkleTree.ts index 166e31a..7432b74 100644 --- a/src/services/merkleTree.ts +++ b/src/services/merkleTree.ts @@ -119,7 +119,7 @@ export class MerkleTreeService { ); console.time('Created tree in'); - const tree = await this.createTree(events.map(({ commitment }) => BigInt(commitment).toString())); + const tree = await this.createTree(events.map(({ commitment }) => commitment)); console.timeEnd('Created tree in'); console.log(''); diff --git a/src/services/networkConfig.ts b/src/services/networkConfig.ts index 06cdc2b..f2197d6 100644 --- a/src/services/networkConfig.ts +++ b/src/services/networkConfig.ts @@ -56,8 +56,11 @@ export type Config = { networkName: string; deployedBlock: number; rpcUrls: RpcUrls; - multicall: string; + multicallContract: string; routerContract: string; + tornContract?: string; + governanceContract?: string; + stakingRewardsContract?: string; registryContract?: string; echoContract: string; aggregatorContract?: string; @@ -81,11 +84,6 @@ export type Config = { // Should be in seconds MINING_BLOCK_TIME?: number; }; - 'torn.contract.tornadocash.eth'?: string; - 'governance.contract.tornadocash.eth'?: string; - 'staking-rewards.contract.tornadocash.eth'?: string; - 'tornado-router.contract.tornadocash.eth'?: string; - 'tornado-proxy-light.contract.tornadocash.eth'?: string; }; export type networkConfig = { @@ -145,7 +143,7 @@ export const networkConfig: networkConfig = { url: 'https://tornadocash-rpc.com', }, chainnodes: { - name: 'Tornado RPC', + name: 'Chainnodes RPC', url: 'https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607', }, mevblockerRPC: { @@ -173,8 +171,11 @@ export const networkConfig: networkConfig = { url: 'https://1rpc.io/eth', }, }, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', routerContract: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b', + tornContract: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C', + governanceContract: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce', + stakingRewardsContract: '0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29', registryContract: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2', echoContract: '0x9B27DD5Bb15d42DC224FCD0B7caEbBe16161Df42', aggregatorContract: '0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49', @@ -262,14 +263,10 @@ export const networkConfig: networkConfig = { constants: { GOVERNANCE_BLOCK: 11474695, NOTE_ACCOUNT_BLOCK: 11842486, - ENCRYPTED_NOTES_BLOCK: 14248730, + ENCRYPTED_NOTES_BLOCK: 12143762, REGISTRY_BLOCK: 14173129, 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, @@ -290,7 +287,7 @@ export const networkConfig: networkConfig = { emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292', networkName: 'Binance Smart Chain', deployedBlock: 8158799, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4', routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', tornadoSubgraph: 'tornadocash/bsc-tornado-subgraph', @@ -304,7 +301,7 @@ export const networkConfig: networkConfig = { url: 'https://tornadocash-rpc.com/bsc', }, chainnodes: { - name: 'Tornado RPC', + name: 'Chainnodes RPC', url: 'https://bsc-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607', }, stackup: { @@ -338,7 +335,6 @@ export const networkConfig: networkConfig = { NOTE_ACCOUNT_BLOCK: 8159269, ENCRYPTED_NOTES_BLOCK: 8159269, }, - 'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', }, netId137: { rpcCallRetryAttempt: 15, @@ -359,7 +355,7 @@ export const networkConfig: networkConfig = { emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292', networkName: 'Polygon (Matic) Network', deployedBlock: 16257962, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4', routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', gasPriceOracleContract: '0xF81A8D8D3581985D3969fe53bFA67074aDFa8F3C', @@ -400,7 +396,6 @@ export const networkConfig: networkConfig = { NOTE_ACCOUNT_BLOCK: 16257996, ENCRYPTED_NOTES_BLOCK: 16257996, }, - 'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', }, netId10: { rpcCallRetryAttempt: 15, @@ -421,7 +416,7 @@ export const networkConfig: networkConfig = { emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292', networkName: 'Optimism', deployedBlock: 2243689, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4', routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F', @@ -436,7 +431,7 @@ export const networkConfig: networkConfig = { url: 'https://tornadocash-rpc.com/op', }, chainnodes: { - name: 'Tornado RPC', + name: 'Chainnodes RPC', url: 'https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607', }, optimism: { @@ -470,7 +465,6 @@ export const networkConfig: networkConfig = { NOTE_ACCOUNT_BLOCK: 2243694, ENCRYPTED_NOTES_BLOCK: 2243694, }, - 'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', }, netId42161: { rpcCallRetryAttempt: 15, @@ -491,7 +485,7 @@ export const networkConfig: networkConfig = { emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292', networkName: 'Arbitrum One', deployedBlock: 3430648, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4', routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', tornadoSubgraph: 'tornadocash/arbitrum-tornado-subgraph', @@ -505,7 +499,7 @@ export const networkConfig: networkConfig = { url: 'https://tornadocash-rpc.com/arbitrum', }, chainnodes: { - name: 'Tornado RPC', + name: 'Chainnodes RPC', url: 'https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607', }, arbitrum: { @@ -539,7 +533,6 @@ export const networkConfig: networkConfig = { NOTE_ACCOUNT_BLOCK: 3430605, ENCRYPTED_NOTES_BLOCK: 3430605, }, - 'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', }, netId100: { rpcCallRetryAttempt: 15, @@ -560,7 +553,7 @@ export const networkConfig: networkConfig = { emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292', networkName: 'Gnosis Chain', deployedBlock: 17754561, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4', routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', tornadoSubgraph: 'tornadocash/xdai-tornado-subgraph', @@ -574,7 +567,7 @@ export const networkConfig: networkConfig = { url: 'https://tornadocash-rpc.com/gnosis', }, chainnodes: { - name: 'Tornado RPC', + name: 'Chainnodes RPC', url: 'https://gnosis-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607', }, gnosis: { @@ -608,7 +601,6 @@ export const networkConfig: networkConfig = { NOTE_ACCOUNT_BLOCK: 17754564, ENCRYPTED_NOTES_BLOCK: 17754564, }, - 'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', }, netId43114: { rpcCallRetryAttempt: 15, @@ -629,7 +621,7 @@ export const networkConfig: networkConfig = { emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292', networkName: 'Avalanche Mainnet', deployedBlock: 4429818, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4', routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', tornadoSubgraph: 'tornadocash/avalanche-tornado-subgraph', @@ -667,7 +659,6 @@ export const networkConfig: networkConfig = { NOTE_ACCOUNT_BLOCK: 4429813, ENCRYPTED_NOTES_BLOCK: 4429813, }, - 'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17', }, netId11155111: { rpcCallRetryAttempt: 15, @@ -688,8 +679,11 @@ export const networkConfig: networkConfig = { emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292', networkName: 'Ethereum Sepolia', deployedBlock: 5594395, - multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11', routerContract: '0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee', + tornContract: '0x3AE6667167C0f44394106E197904519D808323cA', + governanceContract: '0xe5324cD7602eeb387418e594B87aCADee08aeCAD', + stakingRewardsContract: '0x6d0018890751Efd31feb8166711B16732E2b496b', registryContract: '0x1428e5d2356b13778A13108b10c440C83011dfB8', echoContract: '0xcDD1fc3F5ac2782D83449d3AbE80D6b7B273B0e5', aggregatorContract: '0x4088712AC9fad39ea133cdb9130E465d235e9642', @@ -745,9 +739,6 @@ export const networkConfig: networkConfig = { ENCRYPTED_NOTES_BLOCK: 5594395, MINING_BLOCK_TIME: 15, }, - 'torn.contract.tornadocash.eth': '0x3AE6667167C0f44394106E197904519D808323cA', - 'governance.contract.tornadocash.eth': '0xe5324cD7602eeb387418e594B87aCADee08aeCAD', - 'tornado-router.contract.tornadocash.eth': '0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee', }, }; diff --git a/src/services/relayerClient.ts b/src/services/relayerClient.ts index 2ad6ef1..546cf67 100644 --- a/src/services/relayerClient.ts +++ b/src/services/relayerClient.ts @@ -1,4 +1,4 @@ -import { namehash, parseEther } from 'ethers'; +import { getAddress, namehash, parseEther } from 'ethers'; import type { Aggregator } from '@tornado/contracts'; import type { RelayerStructOutput } from '@tornado/contracts/dist/contracts/Governance/Aggregator/Aggregator'; import { sleep } from './utils'; @@ -17,19 +17,21 @@ export interface RelayerParams { export interface Relayer { netId: number; url: string; + hostname: string; rewardAccount: string; + instances: string[]; + gasPrice?: number; + ethPrices?: { + [key in string]: string; + }; currentQueue: number; tornadoServiceFee: number; } export type RelayerInfo = Relayer & { - hostname: string; ensName: string; stakeBalance: bigint; relayerAddress: string; - ethPrices?: { - [key in string]: string; - }; }; export type RelayerError = { @@ -139,6 +141,24 @@ export function getWeightRandom(weightsScores: bigint[], random: bigint) { return Math.floor(Math.random() * weightsScores.length); } +export type RelayerInstanceList = { + [key in string]: { + instanceAddress: { + [key in string]: string; + }; + }; +}; + +export function getSupportedInstances(instanceList: RelayerInstanceList) { + const rawList = Object.values(instanceList) + .map(({ instanceAddress }) => { + return Object.values(instanceAddress); + }) + .flat(); + + return rawList.map((l) => getAddress(l)); +} + export function pickWeightedRandomRelayer(relayers: RelayerInfo[], netId: string | number) { let minFee: number, maxFee: number; @@ -263,7 +283,9 @@ export class RelayerClient { ensName, stakeBalance, relayerAddress, - rewardAccount: status.rewardAccount, + rewardAccount: getAddress(status.rewardAccount), + instances: getSupportedInstances(status.instances), + gasPrice: status.gasPrices?.fast, ethPrices: status.ethPrices, currentQueue: status.currentQueue, tornadoServiceFee: status.tornadoServiceFee, diff --git a/src/services/utils.ts b/src/services/utils.ts index e77199a..5c550d1 100644 --- a/src/services/utils.ts +++ b/src/services/utils.ts @@ -1,4 +1,3 @@ -import { URL } from 'url'; import { webcrypto } from 'crypto'; import BN from 'bn.js'; import type { BigNumberish } from 'ethers';