Compare commits

...

10 Commits

Author SHA1 Message Date
722c426fcb
Update README.md 2024-12-22 22:16:43 +00:00
63be1b2d14
Update Dockerfile 2024-12-06 08:39:01 +00:00
f16bb2ed12
Switched to working git reference 2024-12-06 08:38:21 +00:00
960321e9bd
Update Dockerfile 2024-12-04 18:50:35 +00:00
309a53600c
Added missing ovm l1 fee contract 2024-12-04 17:27:38 +00:00
6d5f2681f4
Add readonlyProvider for RpcSigner 2024-12-04 15:50:21 +00:00
93a2049879
Add dockerfile for deterministic build 2024-12-04 03:45:19 +00:00
8b6887108f
Use simplified websnark & snarkjs
* Use simplified websnark & snarkjs
* Added Base & Blast pools
* Added additional pools for optimism & arbitrum
* Added token transfer events
2024-12-04 03:30:35 +00:00
b35e53ebdb
Exclude new instances from schema checking 2024-11-23 16:24:17 +00:00
2337892195
Added ERC20Tornado instances on BSC 2024-11-23 16:00:02 +00:00
39 changed files with 66415 additions and 40001 deletions

25
Dockerfile Normal file

@ -0,0 +1,25 @@
# Dockefile from https://notes.ethereum.org/@GW1ZUbNKR5iRjjKYx6_dJQ/Bk8zsJ9xj
# FROM node:20.18.0-bullseye-slim
FROM node@sha256:9b558df8f10198fcd1f48cf344c55c4442c3446b8a9a69487523b3d890a4a59e
# install wget, git and necessary certificates
RUN apt update && apt install --yes --no-install-recommends wget git apt-transport-https ca-certificates && rm -rf /var/lib/apt/lists/*
ENV GIT_REPOSITORY=https://git.tornado.ws/tornadocontrib/tornado-core.git
# From development branch, double check with tornado.ws
ENV GIT_COMMIT_HASH=f16bb2ed12464dce4f31aa5a237bb1643989e02d
# clone the repository
RUN mkdir /app/
WORKDIR /app
# Simple hack to fetch only commit and nothing more (no need to download 1GB sized repo, only 100MB would be enough)
RUN git init && \
git remote add origin $GIT_REPOSITORY && \
git fetch --depth 1 origin $GIT_COMMIT_HASH && \
git checkout $GIT_COMMIT_HASH
# install, build and prep for deployment
RUN yarn install --frozen-lockfile --ignore-scripts
RUN yarn build

@ -4,6 +4,8 @@
# Tornado Core (@tornado/core)
### Notice: Repository migrated to [tornado-scripts](https://github.com/tornadocontrib/tornado-scripts) to avoid naming conflict with legacy tornado-core repository
🛠 An SDK for building applications on top of [Privacy Pools](https://www.forbes.com/sites/tomerniv/2023/09/07/privacy-pools-bridging-the-gap-between-blockchain-and-regulatory-compliance)
[![Telegram Badge](https://img.shields.io/badge/Join%20Group-telegram?style=flat&logo=telegram&color=blue&link=https%3A%2F%2Ft.me%2Ftornadoofficial)](https://t.me/tornadoofficial) [![Element Badge](https://img.shields.io/badge/Join%20Element%20Chat-Element?style=flat&logo=element&color=green&link=https%3A%2F%2Felement.tornadocash.social%2F)](https://element.tornadocash.social) [![Discourse Badge](https://img.shields.io/badge/Discourse-Discourse?style=flat&logo=Discourse&color=black&link=https%3A%2F%2Fforum.tornado.ws%2F)](https://forum.tornado.ws/)

16
dist/events/base.d.ts vendored

@ -1,15 +1,15 @@
import { BaseContract, Provider, EventLog } from 'ethers';
import { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry, Echoer, Aggregator } from '@tornado/contracts';
import type { MerkleTree } from '@tornado/fixed-merkle-tree';
import type { MerkleTree } from 'fixed-merkle-tree';
import { BatchEventsService, BatchBlockService, BatchTransactionService, BatchEventOnProgress, BatchBlockOnProgress } from '../batch';
import { fetchDataOptions } from '../providers';
import { type NetIdType, type SubdomainMap } from '../networkConfig';
import { RelayerParams } from '../relayerClient';
import type { TovarishClient } from '../tovarishClient';
import type { ReverseRecords } from '../typechain';
import type { ERC20, ReverseRecords } from '../typechain';
import type { MerkleTreeService } from '../merkleTree';
import type { DepositType } from '../deposits';
import type { BaseEvents, CachedEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, GovernanceProposalCreatedEvents, GovernanceVotedEvents, EchoEvents, AllRelayerRegistryEvents, StakeBurnedEvents, MultiDepositsEvents, MultiWithdrawalsEvents } from './types';
import type { BaseEvents, CachedEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, GovernanceProposalCreatedEvents, GovernanceVotedEvents, EchoEvents, AllRelayerRegistryEvents, StakeBurnedEvents, MultiDepositsEvents, MultiWithdrawalsEvents, TransferEvents } from './types';
export interface BaseEventsServiceConstructor {
netId: NetIdType;
provider: Provider;
@ -44,6 +44,9 @@ export declare class BaseEventsService<EventType extends MinimalEvents> {
* Events from remote cache (Either from local cache, CDN, or from IPFS)
*/
getEventsFromCache(): Promise<CachedEvents<EventType>>;
/**
* This may not return in sorted events when called from browser, make sure to sort it again when directly called
*/
getSavedEvents(): Promise<BaseEvents<EventType> | CachedEvents<EventType>>;
getEventsFromRpc({ fromBlock, toBlock, }: {
fromBlock: number;
@ -252,3 +255,10 @@ export declare class BaseRevenueService extends BaseEventsService<StakeBurnedEve
getTovarishType(): string;
formatEvents(events: EventLog[]): Promise<StakeBurnedEvents[]>;
}
export interface BaseTransferServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Token: ERC20;
}
export declare class BaseTransferService extends BaseEventsService<TransferEvents> {
constructor(serviceConstructor: BaseTransferServiceConstructor);
formatEvents(events: EventLog[]): Promise<TransferEvents[]>;
}

47
dist/events/db.d.ts vendored

@ -1,10 +1,10 @@
import { IndexedDB } from '../idb';
import { BaseTornadoService, BaseTornadoServiceConstructor, BaseEchoService, BaseEchoServiceConstructor, BaseEncryptedNotesService, BaseEncryptedNotesServiceConstructor, BaseGovernanceService, BaseGovernanceServiceConstructor, BaseRegistryService, BaseRegistryServiceConstructor, BaseRevenueService, BaseRevenueServiceConstructor, CachedRelayers } from './base';
import { BaseEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, CachedEvents, EchoEvents, EncryptedNotesEvents, AllGovernanceEvents, AllRelayerRegistryEvents, StakeBurnedEvents } from './types';
export declare function saveDBEvents<T extends MinimalEvents>({ idb, instanceName, events, lastBlock, }: {
import { BaseTornadoService, BaseTornadoServiceConstructor, BaseEchoService, BaseEchoServiceConstructor, BaseEncryptedNotesService, BaseEncryptedNotesServiceConstructor, BaseGovernanceService, BaseGovernanceServiceConstructor, BaseRegistryService, BaseRegistryServiceConstructor, BaseRevenueService, BaseRevenueServiceConstructor, CachedRelayers, BaseMultiTornadoService, BaseMultiTornadoServiceConstructor } from './base';
import { BaseEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, CachedEvents, EchoEvents, EncryptedNotesEvents, AllGovernanceEvents, AllRelayerRegistryEvents, StakeBurnedEvents, MultiDepositsEvents, MultiWithdrawalsEvents } from './types';
export declare function saveDBEvents<T extends MinimalEvents>({ idb, instanceName, newEvents, lastBlock, }: {
idb: IndexedDB;
instanceName: string;
events: T[];
newEvents: T[];
lastBlock: number;
}): Promise<void>;
export declare function loadDBEvents<T extends MinimalEvents>({ idb, instanceName, }: {
@ -28,7 +28,24 @@ export declare class DBTornadoService extends BaseTornadoService {
constructor(params: DBTornadoServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>;
getEventsFromCache(): Promise<CachedEvents<DepositsEvents | WithdrawalsEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>): Promise<void>;
saveEvents({ newEvents, lastBlock, }: BaseEvents<DepositsEvents | WithdrawalsEvents> & {
newEvents: (DepositsEvents | WithdrawalsEvents)[];
}): Promise<void>;
}
export interface DBMultiTornadoServiceConstructor extends BaseMultiTornadoServiceConstructor {
staticUrl: string;
idb: IndexedDB;
}
export declare class DBMultiTornadoService extends BaseMultiTornadoService {
staticUrl: string;
idb: IndexedDB;
zipDigest?: string;
constructor(params: DBMultiTornadoServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents>>;
getEventsFromCache(): Promise<CachedEvents<MultiDepositsEvents | MultiWithdrawalsEvents>>;
saveEvents({ newEvents, lastBlock, }: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}): Promise<void>;
}
export interface DBEchoServiceConstructor extends BaseEchoServiceConstructor {
staticUrl: string;
@ -41,7 +58,9 @@ export declare class DBEchoService extends BaseEchoService {
constructor(params: DBEchoServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<EchoEvents>>;
getEventsFromCache(): Promise<CachedEvents<EchoEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>): Promise<void>;
saveEvents({ newEvents, lastBlock }: BaseEvents<EchoEvents> & {
newEvents: EchoEvents[];
}): Promise<void>;
}
export interface DBEncryptedNotesServiceConstructor extends BaseEncryptedNotesServiceConstructor {
staticUrl: string;
@ -54,7 +73,9 @@ export declare class DBEncryptedNotesService extends BaseEncryptedNotesService {
constructor(params: DBEncryptedNotesServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<EncryptedNotesEvents>>;
getEventsFromCache(): Promise<CachedEvents<EncryptedNotesEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<EncryptedNotesEvents>): Promise<void>;
saveEvents({ newEvents, lastBlock, }: BaseEvents<EncryptedNotesEvents> & {
newEvents: EncryptedNotesEvents[];
}): Promise<void>;
}
export interface DBGovernanceServiceConstructor extends BaseGovernanceServiceConstructor {
staticUrl: string;
@ -67,7 +88,9 @@ export declare class DBGovernanceService extends BaseGovernanceService {
constructor(params: DBGovernanceServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<AllGovernanceEvents>>;
getEventsFromCache(): Promise<CachedEvents<AllGovernanceEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>): Promise<void>;
saveEvents({ newEvents, lastBlock }: BaseEvents<AllGovernanceEvents> & {
newEvents: AllGovernanceEvents[];
}): Promise<void>;
}
export interface DBRegistryServiceConstructor extends BaseRegistryServiceConstructor {
staticUrl: string;
@ -81,7 +104,9 @@ export declare class DBRegistryService extends BaseRegistryService {
constructor(params: DBRegistryServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<AllRelayerRegistryEvents>>;
getEventsFromCache(): Promise<CachedEvents<AllRelayerRegistryEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<AllRelayerRegistryEvents>): Promise<void>;
saveEvents({ newEvents, lastBlock, }: BaseEvents<AllRelayerRegistryEvents> & {
newEvents: AllRelayerRegistryEvents[];
}): Promise<void>;
getRelayersFromDB(): Promise<CachedRelayers>;
getRelayersFromCache(): Promise<CachedRelayers>;
saveRelayers(cachedRelayers: CachedRelayers): Promise<void>;
@ -98,5 +123,7 @@ export declare class DBRevenueService extends BaseRevenueService {
constructor(params: DBRevenueServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<StakeBurnedEvents>>;
getEventsFromCache(): Promise<CachedEvents<StakeBurnedEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<StakeBurnedEvents>): Promise<void>;
saveEvents({ newEvents, lastBlock }: BaseEvents<StakeBurnedEvents> & {
newEvents: StakeBurnedEvents[];
}): Promise<void>;
}

@ -99,3 +99,8 @@ export interface EchoEvents extends MinimalEvents {
export interface EncryptedNotesEvents extends MinimalEvents {
encryptedNote: string;
}
export interface TransferEvents extends MinimalEvents {
from: string;
to: string;
value: bigint;
}

2
dist/fees.d.ts vendored

@ -29,7 +29,7 @@ export declare class TornadoFeeOracle {
* (A single block can bump 12.5% of fees, see the methodology https://hackmd.io/@tvanepps/1559-wallets)
* (Still it is recommended to use 100% premium for sending transactions to prevent stucking it)
*/
gasPrice(): Promise<bigint>;
gasPrice(premium?: number): Promise<bigint>;
/**
* Calculate L1 fee for op-stack chains
*

11
dist/hashes.json vendored Normal file

@ -0,0 +1,11 @@
{
"dist/index.js": "sha384-txpjgDLxNbeXsLgVCyNAwpaJRS0SmFN/ITfadSo8+xzSvLqwa5bQ9GoxQ1Su8QEr",
"dist/index.mjs": "sha384-CrEHmX4zKIv/68CKWElNzvKk6FWRMMkKV8JmqL1KqYCxO73LPEAjDqxTYpxUuDBy",
"dist/merkleTreeWorker.js": "sha384-XVv9HRGDZlaF0LeEZNQygle31UY6yvIwsZ4GWCTZUX7O/hZ7N5Zwpi3GqnHSBYzW",
"dist/merkleTreeWorker.umd.js": "sha384-e5B65ZZ4IxKVOU1o02K6IL2tjJof20QCcEtD+vKe1cPmyfW2BPCHWDPIbPksIpvJ",
"dist/merkleTreeWorker.umd.min.js": "sha384-AxQv1da+lSi3gMYkdGQuR1WN1+4HB8IT3cPFa17HBj14+ZzdZN6SS1fCw5Izc2rr",
"dist/tornado.umd.js": "sha384-xTUkkUTm3aBsnHCUnVGxRgieAu3z4wCwrKT1DFj8VyCk/eZZO9nq+yP4/McLRfB4",
"dist/tornado.umd.min.js": "sha384-ubqr6m6jEOPf7LQz0pXKmEo7tCCbNUAE+iSZWK6X/f7i4wt14rYW7pUcSzw5glth",
"dist/tornadoContracts.umd.js": "sha384-bFq/cemb0bP0JbIq8p75n0UR8smQ/2Yh69pjegzSuu0upawO3sKOjehY8SjX85Xh",
"dist/tornadoContracts.umd.min.js": "sha384-4ay7cmK0auAgZH1xfAsqyN6GtnHBNNgh9C22qy0rHxKicDzNtTsnNZDbz0qmd9GI"
}

531
dist/index.js vendored

@ -12,9 +12,9 @@ var circomlibjs = require('circomlibjs');
var ethSigUtil = require('@metamask/eth-sig-util');
var idb = require('idb');
var worker_threads = require('worker_threads');
var fixedMerkleTree = require('@tornado/fixed-merkle-tree');
var websnarkUtils = require('@tornado/websnark/src/utils');
var websnarkGroth = require('@tornado/websnark/src/groth16');
var fixedMerkleTree = require('fixed-merkle-tree');
var websnarkUtils = require('websnark/src/utils');
var websnarkGroth = require('websnark/src/groth16');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
@ -699,7 +699,7 @@ function getProviderWithNetId(netId, rpcUrl, config, fetchOptions) {
return provider;
}
const populateTransaction = async (signer, tx) => {
const provider = signer.provider;
const provider = signer.readonlyProvider || signer.provider;
if (!tx.from) {
tx.from = signer.address;
} else if (tx.from !== signer.address) {
@ -794,12 +794,14 @@ class TornadoRpcSigner extends ethers.JsonRpcSigner {
gasLimitBump;
gasFailover;
bumpNonce;
constructor(provider, address, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce } = {}) {
readonlyProvider;
constructor(provider, address, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce, readonlyProvider } = {}) {
super(provider, address);
this.gasPriceBump = gasPriceBump ?? 0;
this.gasLimitBump = gasLimitBump ?? 3e3;
this.gasFailover = gasFailover ?? false;
this.bumpNonce = bumpNonce ?? false;
this.readonlyProvider = readonlyProvider;
}
async sendUncheckedTransaction(tx) {
return super.sendUncheckedTransaction(await populateTransaction(this, tx));
@ -835,6 +837,8 @@ var NetId = /* @__PURE__ */ ((NetId2) => {
NetId2[NetId2["POLYGON"] = 137] = "POLYGON";
NetId2[NetId2["OPTIMISM"] = 10] = "OPTIMISM";
NetId2[NetId2["ARBITRUM"] = 42161] = "ARBITRUM";
NetId2[NetId2["BASE"] = 8453] = "BASE";
NetId2[NetId2["BLAST"] = 81457] = "BLAST";
NetId2[NetId2["GNOSIS"] = 100] = "GNOSIS";
NetId2[NetId2["AVALANCHE"] = 43114] = "AVALANCHE";
NetId2[NetId2["SEPOLIA"] = 11155111] = "SEPOLIA";
@ -861,6 +865,10 @@ const defaultConfig = {
name: "MEV Blocker",
url: "https://rpc.mevblocker.io"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com"
},
keydonix: {
name: "Horswap ( Keydonix )",
url: "https://ethereum.keydonix.com/v1/mainnet"
@ -980,10 +988,10 @@ const defaultConfig = {
[56 /* BSC */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 5,
fast: 5,
standard: 5,
low: 5
instant: 3,
fast: 1,
standard: 1,
low: 1
},
nativeCurrency: "bnb",
currencyName: "BNB",
@ -1008,6 +1016,10 @@ const defaultConfig = {
name: "BNB Chain 2",
url: "https://bsc-dataseed1.ninicoin.io"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com/bsc"
},
nodereal: {
name: "NodeReal",
url: "https://binance.nodereal.io"
@ -1031,10 +1043,39 @@ const defaultConfig = {
},
symbol: "BNB",
decimals: 18
},
usdt: {
instanceAddress: {
"10": "0x261fB4f84bb0BdEe7E035B6a8a08e5c35AdacdDD",
"100": "0x3957861d4897d883C9b944C0b4E22bBd0DDE6e21",
"1000": "0x6D180403AdFb39F70983eB51A033C5e52eb9BB69",
"10000": "0x3722662D8AaB07B216B14C02eF0ee940d14A4200"
},
instanceApproval: true,
tokenAddress: "0x55d398326f99059fF775485246999027B3197955",
tokenGasLimit: 7e4,
symbol: "USDT",
decimals: 18,
gasLimit: 7e5
},
btcb: {
instanceAddress: {
"0.0001": "0x736dABbFc8101Ae75287104eCcf67e45D7369Ae1",
"0.001": "0x82c7Ce6f1F158cEC5536d591a2BC19864b3CA823",
"0.01": "0x8284c96679037d8081E498d8F767cA5a140BFAAf",
"0.1": "0x2bcD128Ce23ee30Ee945E613ff129c4DE1102C79"
},
instanceApproval: true,
tokenAddress: "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c",
tokenGasLimit: 7e4,
symbol: "BTCB",
decimals: 18,
gasLimit: 7e5
}
},
optionalTokens: ["usdt", "btcb"],
relayerEnsSubdomain: "bsc-tornado",
pollInterval: 10,
pollInterval: 3,
constants: {
NOTE_ACCOUNT_BLOCK: 8159269,
ENCRYPTED_NOTES_BLOCK: 8159269
@ -1043,9 +1084,9 @@ const defaultConfig = {
[137 /* POLYGON */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 100,
fast: 75,
standard: 50,
instant: 60,
fast: 30,
standard: 30,
low: 30
},
nativeCurrency: "matic",
@ -1085,7 +1126,7 @@ const defaultConfig = {
}
},
relayerEnsSubdomain: "polygon-tornado",
pollInterval: 10,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 16257996,
ENCRYPTED_NOTES_BLOCK: 16257996
@ -1127,17 +1168,20 @@ const defaultConfig = {
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
optionalInstances: ["0.001", "0.01"],
symbol: "ETH",
decimals: 18
}
},
relayerEnsSubdomain: "optimism-tornado",
pollInterval: 15,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 2243694,
ENCRYPTED_NOTES_BLOCK: 2243694
@ -1146,10 +1190,10 @@ const defaultConfig = {
[42161 /* ARBITRUM */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 4,
fast: 3,
standard: 2.52,
low: 2.29
instant: 0.02,
fast: 0.02,
standard: 0.02,
low: 0.02
},
nativeCurrency: "eth",
currencyName: "ETH",
@ -1170,6 +1214,10 @@ const defaultConfig = {
name: "Arbitrum",
url: "https://arb1.arbitrum.io/rpc"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com/arbitrum"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/arbitrum-one"
@ -1182,6 +1230,150 @@ const defaultConfig = {
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
optionalInstances: ["0.001", "0.01"],
symbol: "ETH",
decimals: 18
}
},
relayerEnsSubdomain: "arbitrum-tornado",
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605
}
},
[8453 /* BASE */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 0.1,
fast: 0.06,
standard: 0.05,
low: 0.02
},
nativeCurrency: "eth",
currencyName: "ETH",
explorerUrl: "https://basescan.org",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Base",
deployedBlock: 23149794,
stablecoin: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
ovmGasPriceOracleContract: "0x420000000000000000000000000000000000000F",
tornadoSubgraph: "tornadocash/base-tornado-subgraph",
subgraphs: {},
rpcUrls: {
Base: {
name: "Base",
url: "https://mainnet.base.org"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/base-mainnet"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/base"
}
},
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
symbol: "ETH",
decimals: 18
},
dai: {
instanceAddress: {
"10": "0x70CC374aE7D1549a4666b7172B78dDCF672B74f7",
"100": "0xD063894588177B8362Dda6C0A7EF09BF6fDF851c",
"1000": "0xa7513fdfF61fc83a9C5c08CE31266e6dd400C54E",
"10000": "0x8f05eDE57098D843F30bE74AC25c292F87b7f775",
"100000": "0xeB7fc86c32e9a5E9DD2a0a78C091b8b625cbee24"
},
instanceApproval: true,
tokenAddress: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
tokenGasLimit: 7e4,
symbol: "DAI",
decimals: 18,
gasLimit: 7e5
},
tbtc: {
instanceAddress: {
"0.0001": "0x5465800D7Be34dAe2c1572d2227De94dE93B4432",
"0.001": "0xf2d3404c03C8cC0b120bd6E8edD6F69226F03c6d",
"0.01": "0x4261d5209A285410DEa8173B6FE1A0e7BCf20f7c",
"0.1": "0x9FB147F49bFE17D19789547187EAE2406590b217",
"1": "0x2A8515F39716B0C160a3eB32D24E4cbeB76932d2"
},
instanceApproval: true,
tokenAddress: "0x236aa50979D5f3De3Bd1Eeb40E81137F22ab794b",
tokenGasLimit: 7e4,
symbol: "tBTC",
decimals: 18,
gasLimit: 7e5
}
},
relayerEnsSubdomain: "base-tornado",
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 23149794,
ENCRYPTED_NOTES_BLOCK: 23149794
}
},
[81457 /* BLAST */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 1e-3,
fast: 1e-3,
standard: 1e-3,
low: 1e-3
},
nativeCurrency: "eth",
currencyName: "ETH",
explorerUrl: "https://blastscan.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Blast",
deployedBlock: 12144065,
stablecoin: "0x4300000000000000000000000000000000000003",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
ovmGasPriceOracleContract: "0x420000000000000000000000000000000000000F",
tornadoSubgraph: "tornadocash/blast-tornado-subgraph",
subgraphs: {},
rpcUrls: {
Blast: {
name: "Blast",
url: "https://rpc.blast.io"
},
blastApi: {
name: "BlastApi",
url: "https://blastl2-mainnet.public.blastapi.io"
}
},
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
@ -1191,11 +1383,11 @@ const defaultConfig = {
decimals: 18
}
},
relayerEnsSubdomain: "arbitrum-tornado",
pollInterval: 15,
relayerEnsSubdomain: "blast-tornado",
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605
NOTE_ACCOUNT_BLOCK: 12144065,
ENCRYPTED_NOTES_BLOCK: 12144065
}
},
[100 /* GNOSIS */]: {
@ -1243,7 +1435,7 @@ const defaultConfig = {
}
},
relayerEnsSubdomain: "gnosis-tornado",
pollInterval: 15,
pollInterval: 5,
constants: {
NOTE_ACCOUNT_BLOCK: 17754564,
ENCRYPTED_NOTES_BLOCK: 17754564
@ -1293,7 +1485,7 @@ const defaultConfig = {
}
},
relayerEnsSubdomain: "avalanche-tornado",
pollInterval: 10,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 4429813,
ENCRYPTED_NOTES_BLOCK: 4429813
@ -1328,6 +1520,14 @@ const defaultConfig = {
tornadoSubgraph: "tornadocash/sepolia-tornado-subgraph",
subgraphs: {},
rpcUrls: {
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/sepolia"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com/sepolia"
},
sepolia: {
name: "Sepolia RPC",
url: "https://rpc.sepolia.org"
@ -1336,10 +1536,6 @@ const defaultConfig = {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/ethereum-sepolia"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/sepolia"
},
ethpandaops: {
name: "ethpandaops",
url: "https://rpc.sepolia.ethpandaops.io"
@ -2233,6 +2429,9 @@ class BaseEventsService {
fromCache: true
};
}
/**
* This may not return in sorted events when called from browser, make sure to sort it again when directly called
*/
async getSavedEvents() {
let dbEvents = await this.getEventsFromDB();
if (!dbEvents.lastBlock) {
@ -2578,7 +2777,10 @@ class BaseMultiTornadoService extends BaseEventsService {
}
return acc;
},
{}
{
depositEvents: [],
withdrawalEvents: []
}
);
return {
depositEvents,
@ -2856,7 +3058,12 @@ class BaseGovernanceService extends BaseEventsService {
});
}
async getVotes(proposalId) {
const { events } = await this.getSavedEvents();
const events = (await this.getSavedEvents()).events.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
});
const votedEvents = events.filter(
(e) => e.event === "Voted" && e.proposalId === proposalId
);
@ -2885,7 +3092,12 @@ class BaseGovernanceService extends BaseEventsService {
return votes;
}
async getDelegatedBalance(ethAccount) {
const { events } = await this.getSavedEvents();
const events = (await this.getSavedEvents()).events.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
});
const delegatedAccs = events.filter((e) => e.event === "Delegated" && e.delegateTo === ethAccount).map((e) => e.account);
const undelegatedAccs = events.filter((e) => e.event === "Undelegated" && e.delegateFrom === ethAccount).map((e) => e.account);
const undel = [...undelegatedAccs];
@ -2945,6 +3157,13 @@ const staticRelayers = [
hostnames: {},
tovarishHost: "tornadowithdraw.com",
tovarishNetworks: enabledChains
},
{
ensName: "rpc.tornadowithdraw.eth",
relayerAddress: "0xFF787B7A5cd8a88508361E3B7bcE791Aa2796526",
hostnames: {},
tovarishHost: "tornadocash-rpc.com",
tovarishNetworks: enabledChains
}
];
class BaseRegistryService extends BaseEventsService {
@ -3204,6 +3423,31 @@ RevenueService: Mismatch on withdrawal logs (${withdrawalLogs.length} ) and even
});
}
}
class BaseTransferService extends BaseEventsService {
constructor(serviceConstructor) {
super({
...serviceConstructor,
contract: serviceConstructor.Token,
type: "Transfer"
});
}
async formatEvents(events) {
return events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { from, to, value } = args;
const eventObjects = {
blockNumber,
logIndex,
transactionHash
};
return {
...eventObjects,
from,
to,
value
};
}).filter((e) => e);
}
}
function zipAsync(file, options) {
return new Promise((res, rej) => {
@ -3257,11 +3501,11 @@ async function downloadZip({
async function saveDBEvents({
idb,
instanceName,
events,
newEvents,
lastBlock
}) {
try {
const formattedEvents = events.map((e) => {
const formattedEvents = newEvents.map((e) => {
return {
eid: `${e.transactionHash}_${e.logIndex}`,
...e
@ -3372,11 +3616,49 @@ class DBTornadoService extends BaseTornadoService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
}
class DBMultiTornadoService extends BaseMultiTornadoService {
staticUrl;
idb;
zipDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
newEvents,
lastBlock
});
}
@ -3404,11 +3686,11 @@ class DBEchoService extends BaseEchoService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({ newEvents, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3436,11 +3718,14 @@ class DBEncryptedNotesService extends BaseEncryptedNotesService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3468,11 +3753,11 @@ class DBGovernanceService extends BaseGovernanceService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({ newEvents, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3501,11 +3786,14 @@ class DBRegistryService extends BaseRegistryService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3594,11 +3882,11 @@ class DBRevenueService extends BaseRevenueService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({ newEvents, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -9341,7 +9629,7 @@ class TornadoFeeOracle {
* (A single block can bump 12.5% of fees, see the methodology https://hackmd.io/@tvanepps/1559-wallets)
* (Still it is recommended to use 100% premium for sending transactions to prevent stucking it)
*/
async gasPrice() {
async gasPrice(premium) {
const [block, getGasPrice, getPriorityFee] = await Promise.all([
this.provider.getBlock("latest"),
(async () => {
@ -9359,14 +9647,14 @@ class TornadoFeeOracle {
}
})()
]);
return block?.baseFeePerGas ? block.baseFeePerGas * BigInt(15) / BigInt(10) + getPriorityFee : getGasPrice;
return block?.baseFeePerGas ? block.baseFeePerGas * BigInt(1e4 * (100 + (premium || 50))) / BigInt(1e4 * 100) + getPriorityFee : getGasPrice;
}
/**
* Calculate L1 fee for op-stack chains
*
* This is required since relayers would pay the full transaction fees for users
*/
fetchL1OptimismFee(tx) {
async fetchL1OptimismFee(tx) {
if (!this.ovmGasPriceOracle) {
return new Promise((resolve) => resolve(BigInt(0)));
}
@ -9380,7 +9668,7 @@ class TornadoFeeOracle {
to: DUMMY_ADDRESS
};
}
return this.ovmGasPriceOracle.getL1Fee.staticCall(ethers.Transaction.from(tx).unsignedSerialized);
return await this.ovmGasPriceOracle.getL1Fee.staticCall(ethers.Transaction.from(tx).unsignedSerialized) * 12n / 10n;
}
/**
* We don't need to distinguish default refunds by tokens since most users interact with other defi protocols after withdrawal
@ -9428,6 +9716,8 @@ const gasZipInbounds = {
[NetId.POLYGON]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.OPTIMISM]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.ARBITRUM]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.BASE]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.BLAST]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.GNOSIS]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.AVALANCHE]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604"
};
@ -9437,6 +9727,8 @@ const gasZipID = {
[NetId.POLYGON]: 17,
[NetId.OPTIMISM]: 55,
[NetId.ARBITRUM]: 57,
[NetId.BASE]: 54,
[NetId.BLAST]: 96,
[NetId.GNOSIS]: 16,
[NetId.AVALANCHE]: 15,
[NetId.SEPOLIA]: 102
@ -9502,7 +9794,7 @@ class IndexedDB {
}
};
this.dbName = dbName;
this.dbVersion = 35;
this.dbVersion = 36;
}
async initDB() {
try {
@ -9698,15 +9990,16 @@ async function getIndexedDB(netId) {
}
const minimalIndexes = [
{
name: "blockNumber",
unique: false
},
{
name: "transactionHash",
unique: false
name: "eid",
unique: true
}
];
const defaultState = [
{
name: `tornado_${netId}`,
keyPath: "eid",
indexes: [...minimalIndexes]
},
{
name: `echo_${netId}`,
keyPath: "eid",
@ -9734,8 +10027,7 @@ async function getIndexedDB(netId) {
]
}
];
const config = getConfig(netId);
const { tokens, nativeCurrency, registryContract, governanceContract } = config;
const { tokens, nativeCurrency, registryContract, governanceContract } = getConfig(netId);
const stores = [...defaultState];
if (registryContract) {
stores.push({
@ -10079,122 +10371,6 @@ async function getPermitSignature({
})
);
}
async function getPermitCommitmentsSignature({
PermitTornado: PermitTornado2,
Token,
signer,
denomination,
commitments,
nonce
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = ethers.solidityPackedKeccak256(["bytes32[]"], [commitments]);
return await getPermitSignature({
Token,
signer,
spender: PermitTornado2.target,
value,
nonce,
deadline: BigInt(commitmentsHash)
});
}
async function getPermit2Signature({
Token,
signer,
spender,
value: amount,
nonce,
deadline,
witness
}) {
const sigSigner = signer || Token.runner;
const provider = sigSigner.provider;
const domain = {
name: "Permit2",
chainId: (await provider.getNetwork()).chainId,
verifyingContract: permit2Address
};
const types = !witness ? {
PermitTransferFrom: [
{ name: "permitted", type: "TokenPermissions" },
{ name: "spender", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" }
],
TokenPermissions: [
{ name: "token", type: "address" },
{ name: "amount", type: "uint256" }
]
} : {
PermitWitnessTransferFrom: [
{ name: "permitted", type: "TokenPermissions" },
{ name: "spender", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
{ name: "witness", type: witness.witnessTypeName }
],
TokenPermissions: [
{ name: "token", type: "address" },
{ name: "amount", type: "uint256" }
],
...witness.witnessType
};
const values = {
permitted: {
token: Token.target,
amount
},
spender,
// Sorted nonce are not required for Permit2
nonce: nonce || rBigInt(16),
deadline: deadline || ethers.MaxUint256
};
if (witness) {
values.witness = witness.witness;
}
const hash = new ethers.TypedDataEncoder(types).hash(values);
const signature = ethers.Signature.from(await sigSigner.signTypedData(domain, types, values));
return {
domain,
types,
values,
hash,
signature
};
}
async function getPermit2CommitmentsSignature({
PermitTornado: PermitTornado2,
Token,
signer,
denomination,
commitments,
nonce,
deadline
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = ethers.solidityPackedKeccak256(["bytes32[]"], [commitments]);
return await getPermit2Signature({
Token,
signer,
spender: PermitTornado2.target,
value,
nonce,
deadline,
witness: {
witnessTypeName: "PermitCommitments",
witnessType: {
PermitCommitments: [
{ name: "instance", type: "address" },
{ name: "commitmentsHash", type: "bytes32" }
]
},
witness: {
instance: PermitTornado2.target,
commitmentsHash
}
}
});
}
class TokenPriceOracle {
oracle;
@ -10635,12 +10811,14 @@ exports.BaseMultiTornadoService = BaseMultiTornadoService;
exports.BaseRegistryService = BaseRegistryService;
exports.BaseRevenueService = BaseRevenueService;
exports.BaseTornadoService = BaseTornadoService;
exports.BaseTransferService = BaseTransferService;
exports.BatchBlockService = BatchBlockService;
exports.BatchEventsService = BatchEventsService;
exports.BatchTransactionService = BatchTransactionService;
exports.DBEchoService = DBEchoService;
exports.DBEncryptedNotesService = DBEncryptedNotesService;
exports.DBGovernanceService = DBGovernanceService;
exports.DBMultiTornadoService = DBMultiTornadoService;
exports.DBRegistryService = DBRegistryService;
exports.DBRevenueService = DBRevenueService;
exports.DBTornadoService = DBTornadoService;
@ -10725,9 +10903,6 @@ exports.getIndexedDB = getIndexedDB;
exports.getInstanceByAddress = getInstanceByAddress;
exports.getMultiInstances = getMultiInstances;
exports.getNetworkConfig = getNetworkConfig;
exports.getPermit2CommitmentsSignature = getPermit2CommitmentsSignature;
exports.getPermit2Signature = getPermit2Signature;
exports.getPermitCommitmentsSignature = getPermitCommitmentsSignature;
exports.getPermitSignature = getPermitSignature;
exports.getProvider = getProvider;
exports.getProviderWithNetId = getProviderWithNetId;

530
dist/index.mjs vendored

@ -1,4 +1,4 @@
import { isHexString, assertArgument, assert, EventLog, UndecodedEventLog, Log, FetchRequest, JsonRpcProvider, Network, EnsPlugin, GasCostPlugin, Wallet, HDNodeWallet, VoidSigner, JsonRpcSigner, BrowserProvider, isAddress, parseEther, getAddress, AbiCoder, formatEther, namehash, dataSlice, dataLength, Interface, Contract, computeAddress, keccak256, EnsResolver, parseUnits, Transaction, Signature, MaxUint256, solidityPackedKeccak256, TypedDataEncoder, ZeroAddress } from 'ethers';
import { isHexString, assertArgument, assert, EventLog, UndecodedEventLog, Log, FetchRequest, JsonRpcProvider, Network, EnsPlugin, GasCostPlugin, Wallet, HDNodeWallet, VoidSigner, JsonRpcSigner, BrowserProvider, isAddress, parseEther, getAddress, AbiCoder, formatEther, namehash, dataSlice, dataLength, Interface, Contract, computeAddress, keccak256, EnsResolver, parseUnits, Transaction, Signature, MaxUint256, ZeroAddress } from 'ethers';
import { Tornado__factory } from '@tornado/contracts';
import { webcrypto } from 'crypto';
import BN from 'bn.js';
@ -10,9 +10,9 @@ import { buildPedersenHash, buildMimcSponge } from 'circomlibjs';
import { getEncryptionPublicKey, encrypt, decrypt } from '@metamask/eth-sig-util';
import { openDB, deleteDB } from 'idb';
import { Worker as Worker$1 } from 'worker_threads';
import { MerkleTree, PartialMerkleTree } from '@tornado/fixed-merkle-tree';
import * as websnarkUtils from '@tornado/websnark/src/utils';
import websnarkGroth from '@tornado/websnark/src/groth16';
import { MerkleTree, PartialMerkleTree } from 'fixed-merkle-tree';
import * as websnarkUtils from 'websnark/src/utils';
import websnarkGroth from 'websnark/src/groth16';
BigInt.prototype.toJSON = function() {
return this.toString();
@ -677,7 +677,7 @@ function getProviderWithNetId(netId, rpcUrl, config, fetchOptions) {
return provider;
}
const populateTransaction = async (signer, tx) => {
const provider = signer.provider;
const provider = signer.readonlyProvider || signer.provider;
if (!tx.from) {
tx.from = signer.address;
} else if (tx.from !== signer.address) {
@ -772,12 +772,14 @@ class TornadoRpcSigner extends JsonRpcSigner {
gasLimitBump;
gasFailover;
bumpNonce;
constructor(provider, address, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce } = {}) {
readonlyProvider;
constructor(provider, address, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce, readonlyProvider } = {}) {
super(provider, address);
this.gasPriceBump = gasPriceBump ?? 0;
this.gasLimitBump = gasLimitBump ?? 3e3;
this.gasFailover = gasFailover ?? false;
this.bumpNonce = bumpNonce ?? false;
this.readonlyProvider = readonlyProvider;
}
async sendUncheckedTransaction(tx) {
return super.sendUncheckedTransaction(await populateTransaction(this, tx));
@ -813,6 +815,8 @@ var NetId = /* @__PURE__ */ ((NetId2) => {
NetId2[NetId2["POLYGON"] = 137] = "POLYGON";
NetId2[NetId2["OPTIMISM"] = 10] = "OPTIMISM";
NetId2[NetId2["ARBITRUM"] = 42161] = "ARBITRUM";
NetId2[NetId2["BASE"] = 8453] = "BASE";
NetId2[NetId2["BLAST"] = 81457] = "BLAST";
NetId2[NetId2["GNOSIS"] = 100] = "GNOSIS";
NetId2[NetId2["AVALANCHE"] = 43114] = "AVALANCHE";
NetId2[NetId2["SEPOLIA"] = 11155111] = "SEPOLIA";
@ -839,6 +843,10 @@ const defaultConfig = {
name: "MEV Blocker",
url: "https://rpc.mevblocker.io"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com"
},
keydonix: {
name: "Horswap ( Keydonix )",
url: "https://ethereum.keydonix.com/v1/mainnet"
@ -958,10 +966,10 @@ const defaultConfig = {
[56 /* BSC */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 5,
fast: 5,
standard: 5,
low: 5
instant: 3,
fast: 1,
standard: 1,
low: 1
},
nativeCurrency: "bnb",
currencyName: "BNB",
@ -986,6 +994,10 @@ const defaultConfig = {
name: "BNB Chain 2",
url: "https://bsc-dataseed1.ninicoin.io"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com/bsc"
},
nodereal: {
name: "NodeReal",
url: "https://binance.nodereal.io"
@ -1009,10 +1021,39 @@ const defaultConfig = {
},
symbol: "BNB",
decimals: 18
},
usdt: {
instanceAddress: {
"10": "0x261fB4f84bb0BdEe7E035B6a8a08e5c35AdacdDD",
"100": "0x3957861d4897d883C9b944C0b4E22bBd0DDE6e21",
"1000": "0x6D180403AdFb39F70983eB51A033C5e52eb9BB69",
"10000": "0x3722662D8AaB07B216B14C02eF0ee940d14A4200"
},
instanceApproval: true,
tokenAddress: "0x55d398326f99059fF775485246999027B3197955",
tokenGasLimit: 7e4,
symbol: "USDT",
decimals: 18,
gasLimit: 7e5
},
btcb: {
instanceAddress: {
"0.0001": "0x736dABbFc8101Ae75287104eCcf67e45D7369Ae1",
"0.001": "0x82c7Ce6f1F158cEC5536d591a2BC19864b3CA823",
"0.01": "0x8284c96679037d8081E498d8F767cA5a140BFAAf",
"0.1": "0x2bcD128Ce23ee30Ee945E613ff129c4DE1102C79"
},
instanceApproval: true,
tokenAddress: "0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c",
tokenGasLimit: 7e4,
symbol: "BTCB",
decimals: 18,
gasLimit: 7e5
}
},
optionalTokens: ["usdt", "btcb"],
relayerEnsSubdomain: "bsc-tornado",
pollInterval: 10,
pollInterval: 3,
constants: {
NOTE_ACCOUNT_BLOCK: 8159269,
ENCRYPTED_NOTES_BLOCK: 8159269
@ -1021,9 +1062,9 @@ const defaultConfig = {
[137 /* POLYGON */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 100,
fast: 75,
standard: 50,
instant: 60,
fast: 30,
standard: 30,
low: 30
},
nativeCurrency: "matic",
@ -1063,7 +1104,7 @@ const defaultConfig = {
}
},
relayerEnsSubdomain: "polygon-tornado",
pollInterval: 10,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 16257996,
ENCRYPTED_NOTES_BLOCK: 16257996
@ -1105,17 +1146,20 @@ const defaultConfig = {
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
optionalInstances: ["0.001", "0.01"],
symbol: "ETH",
decimals: 18
}
},
relayerEnsSubdomain: "optimism-tornado",
pollInterval: 15,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 2243694,
ENCRYPTED_NOTES_BLOCK: 2243694
@ -1124,10 +1168,10 @@ const defaultConfig = {
[42161 /* ARBITRUM */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 4,
fast: 3,
standard: 2.52,
low: 2.29
instant: 0.02,
fast: 0.02,
standard: 0.02,
low: 0.02
},
nativeCurrency: "eth",
currencyName: "ETH",
@ -1148,6 +1192,10 @@ const defaultConfig = {
name: "Arbitrum",
url: "https://arb1.arbitrum.io/rpc"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com/arbitrum"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/arbitrum-one"
@ -1160,6 +1208,150 @@ const defaultConfig = {
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
optionalInstances: ["0.001", "0.01"],
symbol: "ETH",
decimals: 18
}
},
relayerEnsSubdomain: "arbitrum-tornado",
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605
}
},
[8453 /* BASE */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 0.1,
fast: 0.06,
standard: 0.05,
low: 0.02
},
nativeCurrency: "eth",
currencyName: "ETH",
explorerUrl: "https://basescan.org",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Base",
deployedBlock: 23149794,
stablecoin: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
offchainOracleContract: "0x00000000000D6FFc74A8feb35aF5827bf57f6786",
ovmGasPriceOracleContract: "0x420000000000000000000000000000000000000F",
tornadoSubgraph: "tornadocash/base-tornado-subgraph",
subgraphs: {},
rpcUrls: {
Base: {
name: "Base",
url: "https://mainnet.base.org"
},
stackup: {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/base-mainnet"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/base"
}
},
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
"100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD"
},
symbol: "ETH",
decimals: 18
},
dai: {
instanceAddress: {
"10": "0x70CC374aE7D1549a4666b7172B78dDCF672B74f7",
"100": "0xD063894588177B8362Dda6C0A7EF09BF6fDF851c",
"1000": "0xa7513fdfF61fc83a9C5c08CE31266e6dd400C54E",
"10000": "0x8f05eDE57098D843F30bE74AC25c292F87b7f775",
"100000": "0xeB7fc86c32e9a5E9DD2a0a78C091b8b625cbee24"
},
instanceApproval: true,
tokenAddress: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
tokenGasLimit: 7e4,
symbol: "DAI",
decimals: 18,
gasLimit: 7e5
},
tbtc: {
instanceAddress: {
"0.0001": "0x5465800D7Be34dAe2c1572d2227De94dE93B4432",
"0.001": "0xf2d3404c03C8cC0b120bd6E8edD6F69226F03c6d",
"0.01": "0x4261d5209A285410DEa8173B6FE1A0e7BCf20f7c",
"0.1": "0x9FB147F49bFE17D19789547187EAE2406590b217",
"1": "0x2A8515F39716B0C160a3eB32D24E4cbeB76932d2"
},
instanceApproval: true,
tokenAddress: "0x236aa50979D5f3De3Bd1Eeb40E81137F22ab794b",
tokenGasLimit: 7e4,
symbol: "tBTC",
decimals: 18,
gasLimit: 7e5
}
},
relayerEnsSubdomain: "base-tornado",
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 23149794,
ENCRYPTED_NOTES_BLOCK: 23149794
}
},
[81457 /* BLAST */]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 1e-3,
fast: 1e-3,
standard: 1e-3,
low: 1e-3
},
nativeCurrency: "eth",
currencyName: "ETH",
explorerUrl: "https://blastscan.io",
merkleTreeHeight: 20,
emptyElement: "21663839004416932945382355908790599225266501822907911457504978515578255421292",
networkName: "Blast",
deployedBlock: 12144065,
stablecoin: "0x4300000000000000000000000000000000000003",
multicallContract: "0xcA11bde05977b3631167028862bE2a173976CA11",
routerContract: "0x0D5550d52428E7e3175bfc9550207e4ad3859b17",
echoContract: "0xa75BF2815618872f155b7C4B0C81bF990f5245E4",
ovmGasPriceOracleContract: "0x420000000000000000000000000000000000000F",
tornadoSubgraph: "tornadocash/blast-tornado-subgraph",
subgraphs: {},
rpcUrls: {
Blast: {
name: "Blast",
url: "https://rpc.blast.io"
},
blastApi: {
name: "BlastApi",
url: "https://blastl2-mainnet.public.blastapi.io"
}
},
tokens: {
eth: {
instanceAddress: {
"0.001": "0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76",
"0.01": "0xA287c40411685438750a247Ca67488DEBe56EE32",
"0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F",
"1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3",
"10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a",
@ -1169,11 +1361,11 @@ const defaultConfig = {
decimals: 18
}
},
relayerEnsSubdomain: "arbitrum-tornado",
pollInterval: 15,
relayerEnsSubdomain: "blast-tornado",
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605
NOTE_ACCOUNT_BLOCK: 12144065,
ENCRYPTED_NOTES_BLOCK: 12144065
}
},
[100 /* GNOSIS */]: {
@ -1221,7 +1413,7 @@ const defaultConfig = {
}
},
relayerEnsSubdomain: "gnosis-tornado",
pollInterval: 15,
pollInterval: 5,
constants: {
NOTE_ACCOUNT_BLOCK: 17754564,
ENCRYPTED_NOTES_BLOCK: 17754564
@ -1271,7 +1463,7 @@ const defaultConfig = {
}
},
relayerEnsSubdomain: "avalanche-tornado",
pollInterval: 10,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 4429813,
ENCRYPTED_NOTES_BLOCK: 4429813
@ -1306,6 +1498,14 @@ const defaultConfig = {
tornadoSubgraph: "tornadocash/sepolia-tornado-subgraph",
subgraphs: {},
rpcUrls: {
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/sepolia"
},
tornadoRpc: {
name: "Tornado RPC",
url: "https://tornadocash-rpc.com/sepolia"
},
sepolia: {
name: "Sepolia RPC",
url: "https://rpc.sepolia.org"
@ -1314,10 +1514,6 @@ const defaultConfig = {
name: "Stackup",
url: "https://public.stackup.sh/api/v1/node/ethereum-sepolia"
},
oneRpc: {
name: "1RPC",
url: "https://1rpc.io/sepolia"
},
ethpandaops: {
name: "ethpandaops",
url: "https://rpc.sepolia.ethpandaops.io"
@ -2211,6 +2407,9 @@ class BaseEventsService {
fromCache: true
};
}
/**
* This may not return in sorted events when called from browser, make sure to sort it again when directly called
*/
async getSavedEvents() {
let dbEvents = await this.getEventsFromDB();
if (!dbEvents.lastBlock) {
@ -2556,7 +2755,10 @@ class BaseMultiTornadoService extends BaseEventsService {
}
return acc;
},
{}
{
depositEvents: [],
withdrawalEvents: []
}
);
return {
depositEvents,
@ -2834,7 +3036,12 @@ class BaseGovernanceService extends BaseEventsService {
});
}
async getVotes(proposalId) {
const { events } = await this.getSavedEvents();
const events = (await this.getSavedEvents()).events.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
});
const votedEvents = events.filter(
(e) => e.event === "Voted" && e.proposalId === proposalId
);
@ -2863,7 +3070,12 @@ class BaseGovernanceService extends BaseEventsService {
return votes;
}
async getDelegatedBalance(ethAccount) {
const { events } = await this.getSavedEvents();
const events = (await this.getSavedEvents()).events.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
});
const delegatedAccs = events.filter((e) => e.event === "Delegated" && e.delegateTo === ethAccount).map((e) => e.account);
const undelegatedAccs = events.filter((e) => e.event === "Undelegated" && e.delegateFrom === ethAccount).map((e) => e.account);
const undel = [...undelegatedAccs];
@ -2923,6 +3135,13 @@ const staticRelayers = [
hostnames: {},
tovarishHost: "tornadowithdraw.com",
tovarishNetworks: enabledChains
},
{
ensName: "rpc.tornadowithdraw.eth",
relayerAddress: "0xFF787B7A5cd8a88508361E3B7bcE791Aa2796526",
hostnames: {},
tovarishHost: "tornadocash-rpc.com",
tovarishNetworks: enabledChains
}
];
class BaseRegistryService extends BaseEventsService {
@ -3182,6 +3401,31 @@ RevenueService: Mismatch on withdrawal logs (${withdrawalLogs.length} ) and even
});
}
}
class BaseTransferService extends BaseEventsService {
constructor(serviceConstructor) {
super({
...serviceConstructor,
contract: serviceConstructor.Token,
type: "Transfer"
});
}
async formatEvents(events) {
return events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { from, to, value } = args;
const eventObjects = {
blockNumber,
logIndex,
transactionHash
};
return {
...eventObjects,
from,
to,
value
};
}).filter((e) => e);
}
}
function zipAsync(file, options) {
return new Promise((res, rej) => {
@ -3235,11 +3479,11 @@ async function downloadZip({
async function saveDBEvents({
idb,
instanceName,
events,
newEvents,
lastBlock
}) {
try {
const formattedEvents = events.map((e) => {
const formattedEvents = newEvents.map((e) => {
return {
eid: `${e.transactionHash}_${e.logIndex}`,
...e
@ -3350,11 +3594,49 @@ class DBTornadoService extends BaseTornadoService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
}
class DBMultiTornadoService extends BaseMultiTornadoService {
staticUrl;
idb;
zipDigest;
constructor(params) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents({
idb: this.idb,
instanceName: this.getInstanceName()
});
}
async getEventsFromCache() {
return await loadRemoteEvents({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest
});
}
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
newEvents,
lastBlock
});
}
@ -3382,11 +3664,11 @@ class DBEchoService extends BaseEchoService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({ newEvents, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3414,11 +3696,14 @@ class DBEncryptedNotesService extends BaseEncryptedNotesService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3446,11 +3731,11 @@ class DBGovernanceService extends BaseGovernanceService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({ newEvents, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3479,11 +3764,14 @@ class DBRegistryService extends BaseRegistryService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({
newEvents,
lastBlock
}) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -3572,11 +3860,11 @@ class DBRevenueService extends BaseRevenueService {
zipDigest: this.zipDigest
});
}
async saveEvents({ events, lastBlock }) {
async saveEvents({ newEvents, lastBlock }) {
await saveDBEvents({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock
});
}
@ -9319,7 +9607,7 @@ class TornadoFeeOracle {
* (A single block can bump 12.5% of fees, see the methodology https://hackmd.io/@tvanepps/1559-wallets)
* (Still it is recommended to use 100% premium for sending transactions to prevent stucking it)
*/
async gasPrice() {
async gasPrice(premium) {
const [block, getGasPrice, getPriorityFee] = await Promise.all([
this.provider.getBlock("latest"),
(async () => {
@ -9337,14 +9625,14 @@ class TornadoFeeOracle {
}
})()
]);
return block?.baseFeePerGas ? block.baseFeePerGas * BigInt(15) / BigInt(10) + getPriorityFee : getGasPrice;
return block?.baseFeePerGas ? block.baseFeePerGas * BigInt(1e4 * (100 + (premium || 50))) / BigInt(1e4 * 100) + getPriorityFee : getGasPrice;
}
/**
* Calculate L1 fee for op-stack chains
*
* This is required since relayers would pay the full transaction fees for users
*/
fetchL1OptimismFee(tx) {
async fetchL1OptimismFee(tx) {
if (!this.ovmGasPriceOracle) {
return new Promise((resolve) => resolve(BigInt(0)));
}
@ -9358,7 +9646,7 @@ class TornadoFeeOracle {
to: DUMMY_ADDRESS
};
}
return this.ovmGasPriceOracle.getL1Fee.staticCall(Transaction.from(tx).unsignedSerialized);
return await this.ovmGasPriceOracle.getL1Fee.staticCall(Transaction.from(tx).unsignedSerialized) * 12n / 10n;
}
/**
* We don't need to distinguish default refunds by tokens since most users interact with other defi protocols after withdrawal
@ -9406,6 +9694,8 @@ const gasZipInbounds = {
[NetId.POLYGON]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.OPTIMISM]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.ARBITRUM]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.BASE]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.BLAST]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.GNOSIS]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604",
[NetId.AVALANCHE]: "0x391E7C679d29bD940d63be94AD22A25d25b5A604"
};
@ -9415,6 +9705,8 @@ const gasZipID = {
[NetId.POLYGON]: 17,
[NetId.OPTIMISM]: 55,
[NetId.ARBITRUM]: 57,
[NetId.BASE]: 54,
[NetId.BLAST]: 96,
[NetId.GNOSIS]: 16,
[NetId.AVALANCHE]: 15,
[NetId.SEPOLIA]: 102
@ -9480,7 +9772,7 @@ class IndexedDB {
}
};
this.dbName = dbName;
this.dbVersion = 35;
this.dbVersion = 36;
}
async initDB() {
try {
@ -9676,15 +9968,16 @@ async function getIndexedDB(netId) {
}
const minimalIndexes = [
{
name: "blockNumber",
unique: false
},
{
name: "transactionHash",
unique: false
name: "eid",
unique: true
}
];
const defaultState = [
{
name: `tornado_${netId}`,
keyPath: "eid",
indexes: [...minimalIndexes]
},
{
name: `echo_${netId}`,
keyPath: "eid",
@ -9712,8 +10005,7 @@ async function getIndexedDB(netId) {
]
}
];
const config = getConfig(netId);
const { tokens, nativeCurrency, registryContract, governanceContract } = config;
const { tokens, nativeCurrency, registryContract, governanceContract } = getConfig(netId);
const stores = [...defaultState];
if (registryContract) {
stores.push({
@ -10057,122 +10349,6 @@ async function getPermitSignature({
})
);
}
async function getPermitCommitmentsSignature({
PermitTornado: PermitTornado2,
Token,
signer,
denomination,
commitments,
nonce
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = solidityPackedKeccak256(["bytes32[]"], [commitments]);
return await getPermitSignature({
Token,
signer,
spender: PermitTornado2.target,
value,
nonce,
deadline: BigInt(commitmentsHash)
});
}
async function getPermit2Signature({
Token,
signer,
spender,
value: amount,
nonce,
deadline,
witness
}) {
const sigSigner = signer || Token.runner;
const provider = sigSigner.provider;
const domain = {
name: "Permit2",
chainId: (await provider.getNetwork()).chainId,
verifyingContract: permit2Address
};
const types = !witness ? {
PermitTransferFrom: [
{ name: "permitted", type: "TokenPermissions" },
{ name: "spender", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" }
],
TokenPermissions: [
{ name: "token", type: "address" },
{ name: "amount", type: "uint256" }
]
} : {
PermitWitnessTransferFrom: [
{ name: "permitted", type: "TokenPermissions" },
{ name: "spender", type: "address" },
{ name: "nonce", type: "uint256" },
{ name: "deadline", type: "uint256" },
{ name: "witness", type: witness.witnessTypeName }
],
TokenPermissions: [
{ name: "token", type: "address" },
{ name: "amount", type: "uint256" }
],
...witness.witnessType
};
const values = {
permitted: {
token: Token.target,
amount
},
spender,
// Sorted nonce are not required for Permit2
nonce: nonce || rBigInt(16),
deadline: deadline || MaxUint256
};
if (witness) {
values.witness = witness.witness;
}
const hash = new TypedDataEncoder(types).hash(values);
const signature = Signature.from(await sigSigner.signTypedData(domain, types, values));
return {
domain,
types,
values,
hash,
signature
};
}
async function getPermit2CommitmentsSignature({
PermitTornado: PermitTornado2,
Token,
signer,
denomination,
commitments,
nonce,
deadline
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = solidityPackedKeccak256(["bytes32[]"], [commitments]);
return await getPermit2Signature({
Token,
signer,
spender: PermitTornado2.target,
value,
nonce,
deadline,
witness: {
witnessTypeName: "PermitCommitments",
witnessType: {
PermitCommitments: [
{ name: "instance", type: "address" },
{ name: "commitmentsHash", type: "bytes32" }
]
},
witness: {
instance: PermitTornado2.target,
commitmentsHash
}
}
});
}
class TokenPriceOracle {
oracle;
@ -10605,4 +10781,4 @@ async function calculateSnarkProof(input, circuit, provingKey) {
return { proof, args };
}
export { BaseEchoService, BaseEncryptedNotesService, BaseEventsService, BaseGovernanceService, BaseMultiTornadoService, BaseRegistryService, BaseRevenueService, BaseTornadoService, BatchBlockService, BatchEventsService, BatchTransactionService, DBEchoService, DBEncryptedNotesService, DBGovernanceService, DBRegistryService, DBRevenueService, DBTornadoService, Deposit, ENSNameWrapper__factory, ENSRegistry__factory, ENSResolver__factory, ENSUtils, ENS__factory, ERC20__factory, EnsContracts, INDEX_DB_ERROR, IndexedDB, Invoice, MAX_FEE, MAX_TOVARISH_EVENTS, MIN_FEE, MIN_STAKE_BALANCE, MerkleTreeService, Mimc, Multicall__factory, NetId, NoteAccount, OffchainOracle__factory, OvmGasPriceOracle__factory, Pedersen, RelayerClient, ReverseRecords__factory, TokenPriceOracle, TornadoBrowserProvider, TornadoFeeOracle, TornadoRpcSigner, TornadoVoidSigner, TornadoWallet, TovarishClient, addNetwork, addressSchemaType, ajv, base64ToBytes, bigIntReplacer, bnSchemaType, bnToBytes, buffPedersenHash, bufferToBytes, bytes32BNSchemaType, bytes32SchemaType, bytesToBN, bytesToBase64, bytesToHex, calculateScore, calculateSnarkProof, chunk, concatBytes, convertETHToTokenAmount, createDeposit, crypto, customConfig, defaultConfig, defaultUserAgent, deployHasher, depositsEventsSchema, digest, downloadZip, echoEventsSchema, enabledChains, encodedLabelToLabelhash, encryptedNotesSchema, index as factories, fetchData, fetchGetUrlFunc, fetchIp, fromContentHash, gasZipID, gasZipInbounds, gasZipInput, gasZipMinMax, getActiveTokenInstances, getActiveTokens, getConfig, getEventsSchemaValidator, getHttpAgent, getIndexedDB, getInstanceByAddress, getMultiInstances, getNetworkConfig, getPermit2CommitmentsSignature, getPermit2Signature, getPermitCommitmentsSignature, getPermitSignature, getProvider, getProviderWithNetId, getRelayerEnsSubdomains, getStatusSchema, getSubInfo, getSupportedInstances, getTokenBalances, getTovarishNetworks, getWeightRandom, governanceEventsSchema, hasherBytecode, hexToBytes, initGroth16, isHex, isNode, jobRequestSchema, jobsSchema, labelhash, leBuff2Int, leInt2Buff, loadDBEvents, loadRemoteEvents, makeLabelNodeAndParent, mimc, multiQueryFilter, multicall, numberFormatter, packEncryptedMessage, parseInvoice, parseNote, pedersen, permit2Address, pickWeightedRandomRelayer, populateTransaction, proofSchemaType, proposalState, rBigInt, rHex, relayerRegistryEventsSchema, saveDBEvents, sleep, stakeBurnedEventsSchema, substring, toContentHash, toFixedHex, toFixedLength, tornadoEventsSchema, unpackEncryptedMessage, unzipAsync, validateUrl, withdrawalsEventsSchema, zipAsync };
export { BaseEchoService, BaseEncryptedNotesService, BaseEventsService, BaseGovernanceService, BaseMultiTornadoService, BaseRegistryService, BaseRevenueService, BaseTornadoService, BaseTransferService, BatchBlockService, BatchEventsService, BatchTransactionService, DBEchoService, DBEncryptedNotesService, DBGovernanceService, DBMultiTornadoService, DBRegistryService, DBRevenueService, DBTornadoService, Deposit, ENSNameWrapper__factory, ENSRegistry__factory, ENSResolver__factory, ENSUtils, ENS__factory, ERC20__factory, EnsContracts, INDEX_DB_ERROR, IndexedDB, Invoice, MAX_FEE, MAX_TOVARISH_EVENTS, MIN_FEE, MIN_STAKE_BALANCE, MerkleTreeService, Mimc, Multicall__factory, NetId, NoteAccount, OffchainOracle__factory, OvmGasPriceOracle__factory, Pedersen, RelayerClient, ReverseRecords__factory, TokenPriceOracle, TornadoBrowserProvider, TornadoFeeOracle, TornadoRpcSigner, TornadoVoidSigner, TornadoWallet, TovarishClient, addNetwork, addressSchemaType, ajv, base64ToBytes, bigIntReplacer, bnSchemaType, bnToBytes, buffPedersenHash, bufferToBytes, bytes32BNSchemaType, bytes32SchemaType, bytesToBN, bytesToBase64, bytesToHex, calculateScore, calculateSnarkProof, chunk, concatBytes, convertETHToTokenAmount, createDeposit, crypto, customConfig, defaultConfig, defaultUserAgent, deployHasher, depositsEventsSchema, digest, downloadZip, echoEventsSchema, enabledChains, encodedLabelToLabelhash, encryptedNotesSchema, index as factories, fetchData, fetchGetUrlFunc, fetchIp, fromContentHash, gasZipID, gasZipInbounds, gasZipInput, gasZipMinMax, getActiveTokenInstances, getActiveTokens, getConfig, getEventsSchemaValidator, getHttpAgent, getIndexedDB, getInstanceByAddress, getMultiInstances, getNetworkConfig, getPermitSignature, getProvider, getProviderWithNetId, getRelayerEnsSubdomains, getStatusSchema, getSubInfo, getSupportedInstances, getTokenBalances, getTovarishNetworks, getWeightRandom, governanceEventsSchema, hasherBytecode, hexToBytes, initGroth16, isHex, isNode, jobRequestSchema, jobsSchema, labelhash, leBuff2Int, leInt2Buff, loadDBEvents, loadRemoteEvents, makeLabelNodeAndParent, mimc, multiQueryFilter, multicall, numberFormatter, packEncryptedMessage, parseInvoice, parseNote, pedersen, permit2Address, pickWeightedRandomRelayer, populateTransaction, proofSchemaType, proposalState, rBigInt, rHex, relayerRegistryEventsSchema, saveDBEvents, sleep, stakeBurnedEventsSchema, substring, toContentHash, toFixedHex, toFixedLength, tornadoEventsSchema, unpackEncryptedMessage, unzipAsync, validateUrl, withdrawalsEventsSchema, zipAsync };

@ -1,4 +1,4 @@
import { MerkleTree, PartialMerkleTree, Element, TreeEdge } from '@tornado/fixed-merkle-tree';
import { MerkleTree, PartialMerkleTree, Element, TreeEdge } from 'fixed-merkle-tree';
import type { Tornado } from '@tornado/contracts';
import type { DepositType } from './deposits';
import type { DepositsEvents } from './events';

19033
dist/merkleTreeWorker.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

2
dist/mimc.d.ts vendored

@ -1,5 +1,5 @@
import { MimcSponge } from 'circomlibjs';
import type { Element, HashFunction } from '@tornado/fixed-merkle-tree';
import type { Element, HashFunction } from 'fixed-merkle-tree';
export declare class Mimc {
sponge?: MimcSponge;
hash?: HashFunction<Element>;

@ -8,6 +8,8 @@ export declare enum NetId {
POLYGON = 137,
OPTIMISM = 10,
ARBITRUM = 42161,
BASE = 8453,
BLAST = 81457,
GNOSIS = 100,
AVALANCHE = 43114,
SEPOLIA = 11155111
@ -31,6 +33,7 @@ export interface TornadoInstance {
instanceAddress: {
[key: string]: string;
};
instanceApproval?: boolean;
optionalInstances?: string[];
tokenAddress?: string;
tokenGasLimit?: number;

176
dist/permit.d.ts vendored

@ -1,5 +1,5 @@
import { ERC20Permit, ERC20Mock, TORN, PermitTornado } from '@tornado/contracts';
import { BaseContract, Signature, Signer, TypedDataField } from 'ethers';
import { ERC20Permit, ERC20Mock, TORN } from '@tornado/contracts';
import { Signature, Signer, TypedDataField } from 'ethers';
export interface PermitValue {
spender: string;
value: bigint;
@ -27,25 +27,85 @@ export declare function getPermitSignature({ Token, signer, spender, value, nonc
Token: ERC20Permit | ERC20Mock | TORN;
signer?: Signer;
}): Promise<Signature>;
export declare function getPermitCommitmentsSignature({ PermitTornado, Token, signer, denomination, commitments, nonce, }: PermitCommitments & {
/**
export async function getPermitCommitmentsSignature({
PermitTornado,
Token,
signer,
denomination,
commitments,
nonce,
}: PermitCommitments & {
PermitTornado: PermitTornado;
Token: ERC20Permit | ERC20Mock | TORN;
signer?: Signer;
}): Promise<Signature>;
export declare function getPermit2Signature({ Token, signer, spender, value: amount, nonce, deadline, witness, }: PermitValue & {
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = solidityPackedKeccak256(['bytes32[]'], [commitments]);
return await getPermitSignature({
Token,
signer,
spender: PermitTornado.target as string,
value,
nonce,
deadline: BigInt(commitmentsHash),
});
}
export async function getPermit2Signature({
Token,
signer,
spender,
value: amount,
nonce,
deadline,
witness,
}: PermitValue & {
Token: BaseContract;
signer?: Signer;
witness?: Witness;
}): Promise<{
domain: {
name: string;
chainId: bigint;
verifyingContract: string;
}) {
const sigSigner = (signer || Token.runner) as Signer & { address: string };
const provider = sigSigner.provider as Provider;
const domain = {
name: 'Permit2',
chainId: (await provider.getNetwork()).chainId,
verifyingContract: permit2Address,
};
types: {
const types: {
[key: string]: TypedDataField[];
};
values: {
} = !witness
? {
PermitTransferFrom: [
{ name: 'permitted', type: 'TokenPermissions' },
{ name: 'spender', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
TokenPermissions: [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
}
: {
PermitWitnessTransferFrom: [
{ name: 'permitted', type: 'TokenPermissions' },
{ name: 'spender', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
{ name: 'witness', type: witness.witnessTypeName },
],
TokenPermissions: [
{ name: 'token', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
...witness.witnessType,
};
const values: {
permitted: {
token: string;
amount: bigint;
@ -53,34 +113,72 @@ export declare function getPermit2Signature({ Token, signer, spender, value: amo
spender: string;
nonce: bigint;
deadline: bigint;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
witness?: any;
} = {
permitted: {
token: Token.target as string,
amount,
},
spender,
// Sorted nonce are not required for Permit2
nonce: nonce || rBigInt(16),
deadline: deadline || MaxUint256,
};
hash: string;
signature: Signature;
}>;
export declare function getPermit2CommitmentsSignature({ PermitTornado, Token, signer, denomination, commitments, nonce, deadline, }: PermitCommitments & {
if (witness) {
values.witness = witness.witness;
}
const hash = new TypedDataEncoder(types).hash(values);
const signature = Signature.from(await sigSigner.signTypedData(domain, types, values));
return {
domain,
types,
values,
hash,
signature,
};
}
export async function getPermit2CommitmentsSignature({
PermitTornado,
Token,
signer,
denomination,
commitments,
nonce,
deadline,
}: PermitCommitments & {
PermitTornado: PermitTornado;
Token: BaseContract;
signer?: Signer;
}): Promise<{
domain: {
name: string;
chainId: bigint;
verifyingContract: string;
};
types: {
[key: string]: TypedDataField[];
};
values: {
permitted: {
token: string;
amount: bigint;
};
spender: string;
nonce: bigint;
deadline: bigint;
witness?: any;
};
hash: string;
signature: Signature;
}>;
}) {
const value = BigInt(commitments.length) * denomination;
const commitmentsHash = solidityPackedKeccak256(['bytes32[]'], [commitments]);
return await getPermit2Signature({
Token,
signer,
spender: PermitTornado.target as string,
value,
nonce,
deadline,
witness: {
witnessTypeName: 'PermitCommitments',
witnessType: {
PermitCommitments: [
{ name: 'instance', type: 'address' },
{ name: 'commitmentsHash', type: 'bytes32' },
],
},
witness: {
instance: PermitTornado.target,
commitmentsHash,
},
},
});
}
**/

4
dist/providers.d.ts vendored

@ -43,6 +43,7 @@ export interface TornadoWalletOptions {
gasLimitBump?: number;
gasFailover?: boolean;
bumpNonce?: boolean;
readonlyProvider?: Provider;
}
export declare class TornadoWallet extends Wallet {
nonce?: number;
@ -69,7 +70,8 @@ export declare class TornadoRpcSigner extends JsonRpcSigner {
gasLimitBump: number;
gasFailover: boolean;
bumpNonce: boolean;
constructor(provider: JsonRpcApiProvider, address: string, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce }?: TornadoWalletOptions);
readonlyProvider?: Provider;
constructor(provider: JsonRpcApiProvider, address: string, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce, readonlyProvider }?: TornadoWalletOptions);
sendUncheckedTransaction(tx: TransactionRequest): Promise<string>;
}
export type connectWalletFunc = (...args: any[]) => Promise<void>;

45136
dist/tornado.umd.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
dist/websnark.d.ts vendored

@ -1,4 +1,4 @@
import type { Element } from '@tornado/fixed-merkle-tree';
import type { Element } from 'fixed-merkle-tree';
export interface snarkInputs {
root: Element;
nullifierHex: string;

@ -13,7 +13,10 @@
"lint": "eslint src/**/*.ts test/**/*.ts --ext .ts --ignore-pattern src/typechain",
"build:node": "rollup -c",
"build:web": "webpack",
"build": "yarn types && yarn build:node && yarn build:web",
"build:hash": "ts-node scripts/hash.ts",
"build": "yarn types && yarn build:node && yarn build:web && yarn build:hash",
"ipfs:build": "docker build -t tornado-core .",
"ipfs:hash": "docker container run --rm -it --entrypoint cat tornado-core /app/dist/hashes.json",
"test": "nyc mocha --require ts-node/register --require source-map-support/register --recursive 'test/**/*.ts' --timeout '300000'"
},
"author": "",
@ -34,10 +37,7 @@
"dependencies": {
"@ensdomains/content-hash": "2.5.7",
"@metamask/eth-sig-util": "^8.0.0",
"@tornado/contracts": "git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#1b1d707878c16a3dc60d295299d4f0e7ce6ba831",
"@tornado/fixed-merkle-tree": "^0.7.3",
"@tornado/snarkjs": "^0.1.20",
"@tornado/websnark": "^0.0.4",
"@tornado/contracts": "git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#093ae2210e1f1b016b756b4db200c4a1b3308408",
"ajv": "^8.17.1",
"bn.js": "^5.2.1",
"circomlibjs": "0.1.7",
@ -45,7 +45,10 @@
"ethers": "^6.13.4",
"ffjavascript": "0.2.48",
"fflate": "^0.8.2",
"idb": "^8.0.0"
"fixed-merkle-tree": "0.7.3",
"idb": "^8.0.0",
"snarkjs": "git+https://github.com/tornadocontrib/snarkjs.git#2c964b3fe6019e057acab04cc17705d1f7fdaf9a",
"websnark": "git+https://github.com/tornadocontrib/websnark.git#f0ddbf34b3045cac9e6d3e4d977bf3b439869fae"
},
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^2.0.7",
@ -69,6 +72,7 @@
"@types/node-fetch": "^2.6.11",
"@typescript-eslint/eslint-plugin": "^8.11.0",
"@typescript-eslint/parser": "^8.11.0",
"chai": "4.5.0",
"esbuild-loader": "^4.2.2",
"eslint": "8.57.0",
"eslint-config-prettier": "^9.1.0",
@ -96,6 +100,8 @@
"strip-ansi": "6.0.1",
"@adraffy/ens-normalize": "1.10.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.2"
"@noble/hashes": "1.3.2",
"big-integer": "1.6.52",
"ffjavascript": "0.2.48"
}
}

@ -12,8 +12,8 @@ const external = Object.keys(pkgJson.dependencies).concat(
'http-proxy-agent',
'https-proxy-agent',
'socks-proxy-agent',
'@tornado/websnark/src/utils',
'@tornado/websnark/src/groth16',
'websnark/src/utils',
'websnark/src/groth16',
]
);

36
scripts/hash.ts Normal file

@ -0,0 +1,36 @@
import path from 'path'
import { readFile, readdir, writeFile } from 'fs/promises';
import { bytesToBase64, digest } from '../src';
async function content(file: string) {
const content = new Uint8Array(await readFile(file));
const hash = 'sha384-' + bytesToBase64(await digest(content));
return hash;
}
async function hash() {
const staticFiles = await readdir('dist');
const hashes = {} as {
[key: string]: string;
};
for (const filePath of staticFiles) {
const file = path.join('dist', filePath).replaceAll(path.sep, path.posix.sep);
if (!['.js', '.mjs'].includes(path.extname(file))) {
continue;
}
const hash = await content(file);
hashes[file] = hash;
}
await writeFile('dist/hashes.json', JSON.stringify(hashes, null, 2));
console.log('hashes', hashes);
}
hash();

@ -22,7 +22,7 @@ import {
Tornado__factory,
} from '@tornado/contracts';
import type { MerkleTree } from '@tornado/fixed-merkle-tree';
import type { MerkleTree } from 'fixed-merkle-tree';
import {
BatchEventsService,
BatchBlockService,
@ -36,7 +36,7 @@ import { enabledChains, type NetIdType, type SubdomainMap } from '../networkConf
import { RelayerParams, MIN_STAKE_BALANCE } from '../relayerClient';
import type { TovarishClient } from '../tovarishClient';
import type { ReverseRecords } from '../typechain';
import type { ERC20, ReverseRecords } from '../typechain';
import type { MerkleTreeService } from '../merkleTree';
import type { DepositType } from '../deposits';
import type {
@ -60,6 +60,7 @@ import type {
StakeBurnedEvents,
MultiDepositsEvents,
MultiWithdrawalsEvents,
TransferEvents,
} from './types';
export interface BaseEventsServiceConstructor {
@ -156,6 +157,9 @@ export class BaseEventsService<EventType extends MinimalEvents> {
};
}
/**
* This may not return in sorted events when called from browser, make sure to sort it again when directly called
*/
async getSavedEvents(): Promise<BaseEvents<EventType> | CachedEvents<EventType>> {
let dbEvents = await this.getEventsFromDB();
@ -613,9 +617,9 @@ export class BaseMultiTornadoService extends BaseEventsService<MultiDepositsEven
}
return acc;
},
{} as {
depositEvents: MultiDepositsEvents[];
withdrawalEvents: MultiWithdrawalsEvents[];
{
depositEvents: [] as MultiDepositsEvents[],
withdrawalEvents: [] as MultiWithdrawalsEvents[],
},
);
@ -992,7 +996,12 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
}
async getVotes(proposalId: number): Promise<GovernanceVotes[]> {
const { events } = await this.getSavedEvents();
const events = (await this.getSavedEvents()).events.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
});
const votedEvents = events.filter(
(e) => e.event === 'Voted' && (e as GovernanceVotedEvents).proposalId === proposalId,
@ -1030,7 +1039,12 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
}
async getDelegatedBalance(ethAccount: string) {
const { events } = await this.getSavedEvents();
const events = (await this.getSavedEvents()).events.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
});
const delegatedAccs = events
.filter((e) => e.event === 'Delegated' && (e as GovernanceDelegatedEvents).delegateTo === ethAccount)
@ -1126,6 +1140,13 @@ const staticRelayers: CachedRelayerInfo[] = [
tovarishHost: 'tornadowithdraw.com',
tovarishNetworks: enabledChains,
},
{
ensName: 'rpc.tornadowithdraw.eth',
relayerAddress: '0xFF787B7A5cd8a88508361E3B7bcE791Aa2796526',
hostnames: {},
tovarishHost: 'tornadocash-rpc.com',
tovarishNetworks: enabledChains,
},
];
export interface CachedRelayers {
@ -1484,3 +1505,38 @@ export class BaseRevenueService extends BaseEventsService<StakeBurnedEvents> {
});
}
}
export interface BaseTransferServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Token: ERC20;
}
export class BaseTransferService extends BaseEventsService<TransferEvents> {
constructor(serviceConstructor: BaseTransferServiceConstructor) {
super({
...serviceConstructor,
contract: serviceConstructor.Token,
type: 'Transfer',
});
}
async formatEvents(events: EventLog[]) {
return events
.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { from, to, value } = args;
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
};
return {
...eventObjects,
from,
to,
value,
};
})
.filter((e) => e) as TransferEvents[];
}
}

@ -17,6 +17,8 @@ import {
BaseRevenueService,
BaseRevenueServiceConstructor,
CachedRelayers,
BaseMultiTornadoService,
BaseMultiTornadoServiceConstructor,
} from './base';
import {
@ -30,21 +32,23 @@ import {
AllGovernanceEvents,
AllRelayerRegistryEvents,
StakeBurnedEvents,
MultiDepositsEvents,
MultiWithdrawalsEvents,
} from './types';
export async function saveDBEvents<T extends MinimalEvents>({
idb,
instanceName,
events,
newEvents,
lastBlock,
}: {
idb: IndexedDB;
instanceName: string;
events: T[];
newEvents: T[];
lastBlock: number;
}) {
try {
const formattedEvents = events.map((e) => {
const formattedEvents = newEvents.map((e) => {
return {
eid: `${e.transactionHash}_${e.logIndex}`,
...e,
@ -192,11 +196,63 @@ export class DBTornadoService extends BaseTornadoService {
});
}
async saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>) {
async saveEvents({
newEvents,
lastBlock,
}: BaseEvents<DepositsEvents | WithdrawalsEvents> & { newEvents: (DepositsEvents | WithdrawalsEvents)[] }) {
await saveDBEvents<DepositsEvents | WithdrawalsEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock,
});
}
}
export interface DBMultiTornadoServiceConstructor extends BaseMultiTornadoServiceConstructor {
staticUrl: string;
idb: IndexedDB;
}
export class DBMultiTornadoService extends BaseMultiTornadoService {
staticUrl: string;
idb: IndexedDB;
zipDigest?: string;
constructor(params: DBMultiTornadoServiceConstructor) {
super(params);
this.staticUrl = params.staticUrl;
this.idb = params.idb;
}
async getEventsFromDB() {
return await loadDBEvents<MultiDepositsEvents | MultiWithdrawalsEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
});
}
async getEventsFromCache() {
return await loadRemoteEvents<MultiDepositsEvents | MultiWithdrawalsEvents>({
staticUrl: this.staticUrl,
instanceName: this.getInstanceName(),
deployedBlock: this.deployedBlock,
zipDigest: this.zipDigest,
});
}
async saveEvents({
newEvents,
lastBlock,
}: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}) {
await saveDBEvents<MultiDepositsEvents | MultiWithdrawalsEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
newEvents,
lastBlock,
});
}
@ -236,11 +292,11 @@ export class DBEchoService extends BaseEchoService {
});
}
async saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>) {
async saveEvents({ newEvents, lastBlock }: BaseEvents<EchoEvents> & { newEvents: EchoEvents[] }) {
await saveDBEvents<EchoEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock,
});
}
@ -280,11 +336,14 @@ export class DBEncryptedNotesService extends BaseEncryptedNotesService {
});
}
async saveEvents({ events, lastBlock }: BaseEvents<EncryptedNotesEvents>) {
async saveEvents({
newEvents,
lastBlock,
}: BaseEvents<EncryptedNotesEvents> & { newEvents: EncryptedNotesEvents[] }) {
await saveDBEvents<EncryptedNotesEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock,
});
}
@ -324,11 +383,11 @@ export class DBGovernanceService extends BaseGovernanceService {
});
}
async saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>) {
async saveEvents({ newEvents, lastBlock }: BaseEvents<AllGovernanceEvents> & { newEvents: AllGovernanceEvents[] }) {
await saveDBEvents<AllGovernanceEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock,
});
}
@ -369,11 +428,14 @@ export class DBRegistryService extends BaseRegistryService {
});
}
async saveEvents({ events, lastBlock }: BaseEvents<AllRelayerRegistryEvents>) {
async saveEvents({
newEvents,
lastBlock,
}: BaseEvents<AllRelayerRegistryEvents> & { newEvents: AllRelayerRegistryEvents[] }) {
await saveDBEvents<AllRelayerRegistryEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock,
});
}
@ -486,11 +548,11 @@ export class DBRevenueService extends BaseRevenueService {
});
}
async saveEvents({ events, lastBlock }: BaseEvents<StakeBurnedEvents>) {
async saveEvents({ newEvents, lastBlock }: BaseEvents<StakeBurnedEvents> & { newEvents: StakeBurnedEvents[] }) {
await saveDBEvents<StakeBurnedEvents>({
idb: this.idb,
instanceName: this.getInstanceName(),
events,
newEvents,
lastBlock,
});
}

@ -132,3 +132,9 @@ export interface EchoEvents extends MinimalEvents {
export interface EncryptedNotesEvents extends MinimalEvents {
encryptedNote: string;
}
export interface TransferEvents extends MinimalEvents {
from: string;
to: string;
value: bigint;
}

@ -55,7 +55,7 @@ export class TornadoFeeOracle {
* (A single block can bump 12.5% of fees, see the methodology https://hackmd.io/@tvanepps/1559-wallets)
* (Still it is recommended to use 100% premium for sending transactions to prevent stucking it)
*/
async gasPrice() {
async gasPrice(premium?: number) {
const [block, getGasPrice, getPriorityFee] = await Promise.all([
this.provider.getBlock('latest'),
(async () => {
@ -74,7 +74,9 @@ export class TornadoFeeOracle {
})(),
]);
return block?.baseFeePerGas ? (block.baseFeePerGas * BigInt(15)) / BigInt(10) + getPriorityFee : getGasPrice;
return block?.baseFeePerGas
? (block.baseFeePerGas * BigInt(10000 * (100 + (premium || 50)))) / BigInt(10000 * 100) + getPriorityFee
: getGasPrice;
}
/**
@ -82,7 +84,7 @@ export class TornadoFeeOracle {
*
* This is required since relayers would pay the full transaction fees for users
*/
fetchL1OptimismFee(tx?: TransactionLike): Promise<bigint> {
async fetchL1OptimismFee(tx?: TransactionLike): Promise<bigint> {
if (!this.ovmGasPriceOracle) {
return new Promise((resolve) => resolve(BigInt(0)));
}
@ -100,7 +102,9 @@ export class TornadoFeeOracle {
};
}
return this.ovmGasPriceOracle.getL1Fee.staticCall(Transaction.from(tx).unsignedSerialized);
return (
((await this.ovmGasPriceOracle.getL1Fee.staticCall(Transaction.from(tx).unsignedSerialized)) * 12n) / 10n
);
}
/**

@ -8,6 +8,8 @@ export const gasZipInbounds: { [key in NetIdType]: string } = {
[NetId.POLYGON]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.OPTIMISM]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.ARBITRUM]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.BASE]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.BLAST]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.GNOSIS]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.AVALANCHE]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
};
@ -19,6 +21,8 @@ export const gasZipID: { [key in NetIdType]: number } = {
[NetId.POLYGON]: 17,
[NetId.OPTIMISM]: 55,
[NetId.ARBITRUM]: 57,
[NetId.BASE]: 54,
[NetId.BLAST]: 96,
[NetId.GNOSIS]: 16,
[NetId.AVALANCHE]: 15,
[NetId.SEPOLIA]: 102,

@ -55,7 +55,7 @@ export class IndexedDB {
};
this.dbName = dbName;
this.dbVersion = 35;
this.dbVersion = 36;
}
async initDB() {
@ -324,16 +324,17 @@ export async function getIndexedDB(netId?: NetIdType) {
const minimalIndexes = [
{
name: 'blockNumber',
unique: false,
},
{
name: 'transactionHash',
unique: false,
name: 'eid',
unique: true,
},
];
const defaultState = [
{
name: `tornado_${netId}`,
keyPath: 'eid',
indexes: [...minimalIndexes],
},
{
name: `echo_${netId}`,
keyPath: 'eid',
@ -362,9 +363,7 @@ export async function getIndexedDB(netId?: NetIdType) {
},
];
const config = getConfig(netId);
const { tokens, nativeCurrency, registryContract, governanceContract } = config;
const { tokens, nativeCurrency, registryContract, governanceContract } = getConfig(netId);
const stores = [...defaultState];

@ -1,5 +1,5 @@
import { Worker as NodeWorker } from 'worker_threads';
import { MerkleTree, PartialMerkleTree, Element, TreeEdge } from '@tornado/fixed-merkle-tree';
import { MerkleTree, PartialMerkleTree, Element, TreeEdge } from 'fixed-merkle-tree';
import type { Tornado } from '@tornado/contracts';
import { isNode, toFixedHex } from './utils';
import { mimc } from './mimc';

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import workerThreads from 'worker_threads';
import { MerkleTree, Element, TreeEdge, PartialMerkleTree } from '@tornado/fixed-merkle-tree';
import { MerkleTree, Element, TreeEdge, PartialMerkleTree } from 'fixed-merkle-tree';
import { mimc } from './mimc';
import { isNode } from './utils';

@ -1,5 +1,5 @@
import { MimcSponge, buildMimcSponge } from 'circomlibjs';
import type { Element, HashFunction } from '@tornado/fixed-merkle-tree';
import type { Element, HashFunction } from 'fixed-merkle-tree';
export class Mimc {
sponge?: MimcSponge;

@ -9,6 +9,8 @@ export enum NetId {
POLYGON = 137,
OPTIMISM = 10,
ARBITRUM = 42161,
BASE = 8453,
BLAST = 81457,
GNOSIS = 100,
AVALANCHE = 43114,
SEPOLIA = 11155111,
@ -38,6 +40,7 @@ export interface TornadoInstance {
instanceAddress: {
[key: string]: string;
};
instanceApproval?: boolean;
optionalInstances?: string[];
tokenAddress?: string;
tokenGasLimit?: number;
@ -132,6 +135,10 @@ export const defaultConfig: networkConfig = {
name: 'MEV Blocker',
url: 'https://rpc.mevblocker.io',
},
tornadoRpc: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com',
},
keydonix: {
name: 'Horswap ( Keydonix )',
url: 'https://ethereum.keydonix.com/v1/mainnet',
@ -251,10 +258,10 @@ export const defaultConfig: networkConfig = {
[NetId.BSC]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 5,
fast: 5,
standard: 5,
low: 5,
instant: 3,
fast: 1,
standard: 1,
low: 1,
},
nativeCurrency: 'bnb',
currencyName: 'BNB',
@ -279,6 +286,10 @@ export const defaultConfig: networkConfig = {
name: 'BNB Chain 2',
url: 'https://bsc-dataseed1.ninicoin.io',
},
tornadoRpc: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/bsc',
},
nodereal: {
name: 'NodeReal',
url: 'https://binance.nodereal.io',
@ -303,9 +314,38 @@ export const defaultConfig: networkConfig = {
symbol: 'BNB',
decimals: 18,
},
usdt: {
instanceAddress: {
'10': '0x261fB4f84bb0BdEe7E035B6a8a08e5c35AdacdDD',
'100': '0x3957861d4897d883C9b944C0b4E22bBd0DDE6e21',
'1000': '0x6D180403AdFb39F70983eB51A033C5e52eb9BB69',
'10000': '0x3722662D8AaB07B216B14C02eF0ee940d14A4200',
},
instanceApproval: true,
tokenAddress: '0x55d398326f99059fF775485246999027B3197955',
tokenGasLimit: 70_000,
symbol: 'USDT',
decimals: 18,
gasLimit: 700_000,
},
btcb: {
instanceAddress: {
'0.0001': '0x736dABbFc8101Ae75287104eCcf67e45D7369Ae1',
'0.001': '0x82c7Ce6f1F158cEC5536d591a2BC19864b3CA823',
'0.01': '0x8284c96679037d8081E498d8F767cA5a140BFAAf',
'0.1': '0x2bcD128Ce23ee30Ee945E613ff129c4DE1102C79',
},
instanceApproval: true,
tokenAddress: '0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c',
tokenGasLimit: 70_000,
symbol: 'BTCB',
decimals: 18,
gasLimit: 700_000,
},
},
optionalTokens: ['usdt', 'btcb'],
relayerEnsSubdomain: 'bsc-tornado',
pollInterval: 10,
pollInterval: 3,
constants: {
NOTE_ACCOUNT_BLOCK: 8159269,
ENCRYPTED_NOTES_BLOCK: 8159269,
@ -314,9 +354,9 @@ export const defaultConfig: networkConfig = {
[NetId.POLYGON]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 100,
fast: 75,
standard: 50,
instant: 60,
fast: 30,
standard: 30,
low: 30,
},
nativeCurrency: 'matic',
@ -356,7 +396,7 @@ export const defaultConfig: networkConfig = {
},
},
relayerEnsSubdomain: 'polygon-tornado',
pollInterval: 10,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 16257996,
ENCRYPTED_NOTES_BLOCK: 16257996,
@ -398,17 +438,20 @@ export const defaultConfig: networkConfig = {
tokens: {
eth: {
instanceAddress: {
'0.001': '0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76',
'0.01': '0xA287c40411685438750a247Ca67488DEBe56EE32',
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
},
optionalInstances: ['0.001', '0.01'],
symbol: 'ETH',
decimals: 18,
},
},
relayerEnsSubdomain: 'optimism-tornado',
pollInterval: 15,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 2243694,
ENCRYPTED_NOTES_BLOCK: 2243694,
@ -417,10 +460,10 @@ export const defaultConfig: networkConfig = {
[NetId.ARBITRUM]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 4,
fast: 3,
standard: 2.52,
low: 2.29,
instant: 0.02,
fast: 0.02,
standard: 0.02,
low: 0.02,
},
nativeCurrency: 'eth',
currencyName: 'ETH',
@ -441,6 +484,10 @@ export const defaultConfig: networkConfig = {
name: 'Arbitrum',
url: 'https://arb1.arbitrum.io/rpc',
},
tornadoRpc: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/arbitrum',
},
stackup: {
name: 'Stackup',
url: 'https://public.stackup.sh/api/v1/node/arbitrum-one',
@ -453,6 +500,150 @@ export const defaultConfig: networkConfig = {
tokens: {
eth: {
instanceAddress: {
'0.001': '0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76',
'0.01': '0xA287c40411685438750a247Ca67488DEBe56EE32',
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
},
optionalInstances: ['0.001', '0.01'],
symbol: 'ETH',
decimals: 18,
},
},
relayerEnsSubdomain: 'arbitrum-tornado',
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605,
},
},
[NetId.BASE]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 0.1,
fast: 0.06,
standard: 0.05,
low: 0.02,
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: 'https://basescan.org',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Base',
deployedBlock: 23149794,
stablecoin: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
offchainOracleContract: '0x00000000000D6FFc74A8feb35aF5827bf57f6786',
ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F',
tornadoSubgraph: 'tornadocash/base-tornado-subgraph',
subgraphs: {},
rpcUrls: {
Base: {
name: 'Base',
url: 'https://mainnet.base.org',
},
stackup: {
name: 'Stackup',
url: 'https://public.stackup.sh/api/v1/node/base-mainnet',
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/base',
},
},
tokens: {
eth: {
instanceAddress: {
'0.001': '0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76',
'0.01': '0xA287c40411685438750a247Ca67488DEBe56EE32',
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
},
symbol: 'ETH',
decimals: 18,
},
dai: {
instanceAddress: {
'10': '0x70CC374aE7D1549a4666b7172B78dDCF672B74f7',
'100': '0xD063894588177B8362Dda6C0A7EF09BF6fDF851c',
'1000': '0xa7513fdfF61fc83a9C5c08CE31266e6dd400C54E',
'10000': '0x8f05eDE57098D843F30bE74AC25c292F87b7f775',
'100000': '0xeB7fc86c32e9a5E9DD2a0a78C091b8b625cbee24',
},
instanceApproval: true,
tokenAddress: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
tokenGasLimit: 70_000,
symbol: 'DAI',
decimals: 18,
gasLimit: 700_000,
},
tbtc: {
instanceAddress: {
'0.0001': '0x5465800D7Be34dAe2c1572d2227De94dE93B4432',
'0.001': '0xf2d3404c03C8cC0b120bd6E8edD6F69226F03c6d',
'0.01': '0x4261d5209A285410DEa8173B6FE1A0e7BCf20f7c',
'0.1': '0x9FB147F49bFE17D19789547187EAE2406590b217',
'1': '0x2A8515F39716B0C160a3eB32D24E4cbeB76932d2',
},
instanceApproval: true,
tokenAddress: '0x236aa50979D5f3De3Bd1Eeb40E81137F22ab794b',
tokenGasLimit: 70_000,
symbol: 'tBTC',
decimals: 18,
gasLimit: 700_000,
},
},
relayerEnsSubdomain: 'base-tornado',
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 23149794,
ENCRYPTED_NOTES_BLOCK: 23149794,
},
},
[NetId.BLAST]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 0.001,
fast: 0.001,
standard: 0.001,
low: 0.001,
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: 'https://blastscan.io',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Blast',
deployedBlock: 12144065,
stablecoin: '0x4300000000000000000000000000000000000003',
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F',
tornadoSubgraph: 'tornadocash/blast-tornado-subgraph',
subgraphs: {},
rpcUrls: {
Blast: {
name: 'Blast',
url: 'https://rpc.blast.io',
},
blastApi: {
name: 'BlastApi',
url: 'https://blastl2-mainnet.public.blastapi.io',
},
},
tokens: {
eth: {
instanceAddress: {
'0.001': '0x82859DC3697062c16422E9b5e8Ba1B6a6EC72c76',
'0.01': '0xA287c40411685438750a247Ca67488DEBe56EE32',
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
@ -462,11 +653,11 @@ export const defaultConfig: networkConfig = {
decimals: 18,
},
},
relayerEnsSubdomain: 'arbitrum-tornado',
pollInterval: 15,
relayerEnsSubdomain: 'blast-tornado',
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605,
NOTE_ACCOUNT_BLOCK: 12144065,
ENCRYPTED_NOTES_BLOCK: 12144065,
},
},
[NetId.GNOSIS]: {
@ -514,7 +705,7 @@ export const defaultConfig: networkConfig = {
},
},
relayerEnsSubdomain: 'gnosis-tornado',
pollInterval: 15,
pollInterval: 5,
constants: {
NOTE_ACCOUNT_BLOCK: 17754564,
ENCRYPTED_NOTES_BLOCK: 17754564,
@ -564,7 +755,7 @@ export const defaultConfig: networkConfig = {
},
},
relayerEnsSubdomain: 'avalanche-tornado',
pollInterval: 10,
pollInterval: 2,
constants: {
NOTE_ACCOUNT_BLOCK: 4429813,
ENCRYPTED_NOTES_BLOCK: 4429813,
@ -599,6 +790,14 @@ export const defaultConfig: networkConfig = {
tornadoSubgraph: 'tornadocash/sepolia-tornado-subgraph',
subgraphs: {},
rpcUrls: {
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/sepolia',
},
tornadoRpc: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/sepolia',
},
sepolia: {
name: 'Sepolia RPC',
url: 'https://rpc.sepolia.org',
@ -607,10 +806,6 @@ export const defaultConfig: networkConfig = {
name: 'Stackup',
url: 'https://public.stackup.sh/api/v1/node/ethereum-sepolia',
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/sepolia',
},
ethpandaops: {
name: 'ethpandaops',
url: 'https://rpc.sepolia.ethpandaops.io',

@ -1,15 +1,5 @@
import { ERC20Permit, ERC20Mock, TORN, PermitTornado } from '@tornado/contracts';
import {
BaseContract,
MaxUint256,
Provider,
Signature,
Signer,
solidityPackedKeccak256,
TypedDataEncoder,
TypedDataField,
} from 'ethers';
import { rBigInt } from './utils';
import { ERC20Permit, ERC20Mock, TORN } from '@tornado/contracts';
import { MaxUint256, Provider, Signature, Signer, TypedDataField } from 'ethers';
export interface PermitValue {
spender: string;
@ -87,6 +77,7 @@ export async function getPermitSignature({
);
}
/**
export async function getPermitCommitmentsSignature({
PermitTornado,
Token,
@ -240,3 +231,4 @@ export async function getPermit2CommitmentsSignature({
},
});
}
**/

@ -316,7 +316,7 @@ export const populateTransaction = async (
signer: TornadoWallet | TornadoVoidSigner | TornadoRpcSigner,
tx: TransactionRequest,
) => {
const provider = signer.provider as Provider;
const provider = ((signer as TornadoRpcSigner).readonlyProvider || signer.provider) as Provider;
if (!tx.from) {
tx.from = signer.address;
@ -381,6 +381,7 @@ export interface TornadoWalletOptions {
gasLimitBump?: number;
gasFailover?: boolean;
bumpNonce?: boolean;
readonlyProvider?: Provider;
}
export class TornadoWallet extends Wallet {
@ -453,10 +454,11 @@ export class TornadoRpcSigner extends JsonRpcSigner {
gasLimitBump: number;
gasFailover: boolean;
bumpNonce: boolean;
readonlyProvider?: Provider;
constructor(
provider: JsonRpcApiProvider,
address: string,
{ gasPriceBump, gasLimitBump, gasFailover, bumpNonce }: TornadoWalletOptions = {},
{ gasPriceBump, gasLimitBump, gasFailover, bumpNonce, readonlyProvider }: TornadoWalletOptions = {},
) {
super(provider, address);
// 10% bump from the recommended fee
@ -466,6 +468,7 @@ export class TornadoRpcSigner extends JsonRpcSigner {
this.gasFailover = gasFailover ?? false;
// turn off bumpNonce feature for browser wallet
this.bumpNonce = bumpNonce ?? false;
this.readonlyProvider = readonlyProvider;
}
async sendUncheckedTransaction(tx: TransactionRequest) {

@ -1,8 +1,8 @@
// @ts-expect-error no-websnark-types
import * as websnarkUtils from '@tornado/websnark/src/utils';
import * as websnarkUtils from 'websnark/src/utils';
// @ts-expect-error no-websnark-types
import websnarkGroth from '@tornado/websnark/src/groth16';
import type { Element } from '@tornado/fixed-merkle-tree';
import websnarkGroth from 'websnark/src/groth16';
import type { Element } from 'fixed-merkle-tree';
import { toFixedHex } from './utils';
export interface snarkInputs {

732
yarn.lock

File diff suppressed because it is too large Load Diff