Compare commits

...

12 Commits

Author SHA1 Message Date
e132275d33 Update RPC endpoints, update deps ( 1.0.23 ) 2025-04-16 05:15:47 +00:00
bf432161fb Simplify config and use latest aggregator contract 2025-01-27 17:51:14 +00:00
a93fa4a8be Update Dockerfile 2025-01-21 18:25:31 +00:00
8d5658ee7a 🖕
They are only public IP echo provider with CORS fuck
2025-01-21 18:24:20 +00:00
d688f8a2b5 Update Dockerfile 2025-01-21 17:34:31 +00:00
0aa9f00383 Remove cross-fetch deps 2025-01-21 17:34:04 +00:00
e19f5a1373 Update Dockerfile 2024-12-23 20:52:21 +00:00
5bfef2a2ae Fixed merkleTreeWorker 2024-12-23 20:52:05 +00:00
53cb3ac414 Update Dockerfile 2024-12-23 19:09:40 +00:00
f4382f4e78 Rename package name 2024-12-23 19:09:02 +00:00
20bb8c25a1 Update Dockerfile 2024-12-23 02:56:15 +00:00
d143bc2923 1.0.20
* Use latest dependencies
* Rename package to tornado-scripts
* Added fallback RPCs
* Latest eslint support
2024-12-23 02:55:45 +00:00
61 changed files with 246744 additions and 254153 deletions

View File

@@ -1,44 +0,0 @@
module.exports = {
env: {
es2021: true,
node: true,
},
extends: [
'prettier',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:prettier/recommended',
],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'prettier'],
rules: {
'prettier/prettier': [
'error',
{
tabWidth: 4,
printWidth: 120,
singleQuote: true,
},
],
'import/order': ['error'],
'@typescript-eslint/no-unused-vars': ['warn'],
'@typescript-eslint/no-unused-expressions': ['off'],
},
};

2
.gitattributes vendored
View File

@@ -1 +1 @@
dist/* linguist-vendored
dist/**/* linguist-vendored

1
.npmrc
View File

@@ -1 +0,0 @@
@tornado:registry=https://git.tornado.ws/api/packages/tornado-packages/npm/

View File

@@ -1,13 +1,13 @@
# Dockefile from https://notes.ethereum.org/@GW1ZUbNKR5iRjjKYx6_dJQ/Bk8zsJ9xj
# FROM node:20.18.0-bullseye-slim
FROM node@sha256:9b558df8f10198fcd1f48cf344c55c4442c3446b8a9a69487523b3d890a4a59e
# FROM node:22.12.0-bullseye-slim
FROM node@sha256:9f385b101f66ecdf9ed9218d000cd5a35600722f0aab8112632371765109c065
# 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
ENV GIT_REPOSITORY=https://github.com/tornadocontrib/tornado-scripts.git
# From main branch, double check with git.tornado.ws and codeberg.org
ENV GIT_COMMIT_HASH=8d5658ee7aedbaa2413868c4c9e0fb453a92ae64
# clone the repository
RUN mkdir /app/

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Tornado Cash
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -2,9 +2,7 @@
<img src="./logo2.png">
# 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
# Tornado Scripts (tornado-scripts)
🛠 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)
@@ -12,9 +10,9 @@
</div>
### About Tornado Core
### About Tornado Scripts
Tornado Core is a modern building block for Privacy Pools to build anything from custom UI or CLI tools
Tornado Scripts is a modern building block for Privacy Pools to build anything from custom UI or CLI tools
+ Written in [TypeScript](https://www.typescriptlang.org/)

2
dist/contracts.d.ts vendored
View File

@@ -1,2 +1,2 @@
export * from '@tornado/contracts';
export * from 'tornado-contracts';
export { Multicall, Multicall__factory, OffchainOracle, OffchainOracle__factory, OvmGasPriceOracle, OvmGasPriceOracle__factory, ReverseRecords, ReverseRecords__factory, ENSNameWrapper, ENSNameWrapper__factory, ENSRegistry, ENSRegistry__factory, ENSResolver, ENSResolver__factory, } from './typechain';

12
dist/ens.d.ts vendored
View File

@@ -8,13 +8,11 @@ export declare function makeLabelNodeAndParent(name: string): {
labelhash: string;
parentNode: string;
};
export declare const EnsContracts: {
[key: NetIdType]: {
ensRegistry: string;
ensPublicResolver: string;
ensNameWrapper: string;
};
};
export declare const EnsContracts: Record<NetIdType, {
ensRegistry: string;
ensPublicResolver: string;
ensNameWrapper: string;
}>;
/**
* ENSUtils to manage on-chain registered relayers
*/

60
dist/events/base.d.ts vendored
View File

@@ -1,9 +1,9 @@
import { BaseContract, Provider, EventLog } from 'ethers';
import { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry, Echoer, Aggregator } from '@tornado/contracts';
import { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry, Echoer, TovarishAggregator } from 'tornado-contracts';
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 { TornadoConfig, type NetIdType, type SubdomainMap } from '../networkConfig';
import { RelayerParams } from '../relayerClient';
import type { TovarishClient } from '../tovarishClient';
import type { ERC20, ReverseRecords } from '../typechain';
@@ -98,16 +98,12 @@ export declare class BaseTornadoService extends BaseEventsService<DepositsEvents
}): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>;
}
export interface BaseMultiTornadoServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
instances: {
[key in string]: DepositType;
};
instances: Record<string, DepositType>;
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
}
export declare class BaseMultiTornadoService extends BaseEventsService<MultiDepositsEvents | MultiWithdrawalsEvents> {
instances: {
[key in string]: DepositType;
};
instances: Record<string, DepositType>;
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
batchTransactionService: BatchTransactionService;
@@ -143,9 +139,7 @@ export declare class BaseEncryptedNotesService extends BaseEventsService<Encrypt
getTovarishType(): string;
formatEvents(events: EventLog[]): Promise<EncryptedNotesEvents[]>;
}
export declare const proposalState: {
[key: string]: string;
};
export declare const proposalState: Record<string, string>;
export interface GovernanceProposals extends GovernanceProposalCreatedEvents {
title: string;
proposerName?: string;
@@ -164,12 +158,12 @@ export interface GovernanceVotes extends GovernanceVotedEvents {
}
export interface BaseGovernanceServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Governance: Governance;
Aggregator: Aggregator;
Aggregator: TovarishAggregator;
ReverseRecords: ReverseRecords;
}
export declare class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
Governance: Governance;
Aggregator: Aggregator;
Aggregator: TovarishAggregator;
ReverseRecords: ReverseRecords;
batchTransactionService: BatchTransactionService;
constructor(serviceConstructor: BaseGovernanceServiceConstructor);
@@ -182,14 +176,11 @@ export declare class BaseGovernanceService extends BaseEventsService<AllGovernan
delegatedAccs: string[];
undelegatedAccs: string[];
uniq: string[];
uniqNames: {
[key: string]: string;
};
uniqNames: Record<string, string>;
balances: bigint[];
balance: bigint;
}>;
}
export declare function getTovarishNetworks(registryService: BaseRegistryService, relayers: CachedRelayerInfo[]): Promise<void>;
/**
* Essential params:
* ensName, relayerAddress, hostnames
@@ -197,49 +188,26 @@ export declare function getTovarishNetworks(registryService: BaseRegistryService
*/
export interface CachedRelayerInfo extends RelayerParams {
isRegistered?: boolean;
registeredAddress?: string;
isPrior?: boolean;
stakeBalance?: string;
hostnames: SubdomainMap;
tovarishHost?: string;
tovarishNetworks?: number[];
}
export interface CachedRelayers {
lastBlock: number;
timestamp: number;
relayers: CachedRelayerInfo[];
fromCache?: boolean;
}
export interface BaseRegistryServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
tornadoConfig: TornadoConfig;
RelayerRegistry: RelayerRegistry;
Aggregator: Aggregator;
relayerEnsSubdomains: SubdomainMap;
Aggregator: TovarishAggregator;
}
export declare class BaseRegistryService extends BaseEventsService<AllRelayerRegistryEvents> {
Aggregator: Aggregator;
relayerEnsSubdomains: SubdomainMap;
tornadoConfig: TornadoConfig;
Aggregator: TovarishAggregator;
updateInterval: number;
constructor(serviceConstructor: BaseRegistryServiceConstructor);
getInstanceName(): string;
getTovarishType(): string;
formatEvents(events: EventLog[]): Promise<AllRelayerRegistryEvents[]>;
/**
* Get saved or cached relayers
*/
getRelayersFromDB(): Promise<CachedRelayers>;
/**
* Relayers from remote cache (Either from local cache, CDN, or from IPFS)
*/
getRelayersFromCache(): Promise<CachedRelayers>;
getSavedRelayers(): Promise<CachedRelayers>;
getLatestRelayers(): Promise<CachedRelayers>;
/**
* Handle saving relayers
*/
saveRelayers({ lastBlock, timestamp, relayers }: CachedRelayers): Promise<void>;
/**
* Get cached or latest relayer and save to local
*/
updateRelayers(): Promise<CachedRelayers>;
getLatestRelayers(knownRelayers?: string[]): Promise<CachedRelayerInfo[]>;
}
export interface BaseRevenueServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
RelayerRegistry: RelayerRegistry;

6
dist/events/db.d.ts vendored
View File

@@ -1,5 +1,5 @@
import { IndexedDB } from '../idb';
import { BaseTornadoService, BaseTornadoServiceConstructor, BaseEchoService, BaseEchoServiceConstructor, BaseEncryptedNotesService, BaseEncryptedNotesServiceConstructor, BaseGovernanceService, BaseGovernanceServiceConstructor, BaseRegistryService, BaseRegistryServiceConstructor, BaseRevenueService, BaseRevenueServiceConstructor, CachedRelayers, BaseMultiTornadoService, BaseMultiTornadoServiceConstructor } from './base';
import { BaseTornadoService, BaseTornadoServiceConstructor, BaseEchoService, BaseEchoServiceConstructor, BaseEncryptedNotesService, BaseEncryptedNotesServiceConstructor, BaseGovernanceService, BaseGovernanceServiceConstructor, BaseRegistryService, BaseRegistryServiceConstructor, BaseRevenueService, BaseRevenueServiceConstructor, 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;
@@ -100,16 +100,12 @@ export declare class DBRegistryService extends BaseRegistryService {
staticUrl: string;
idb: IndexedDB;
zipDigest?: string;
relayerJsonDigest?: string;
constructor(params: DBRegistryServiceConstructor);
getEventsFromDB(): Promise<BaseEvents<AllRelayerRegistryEvents>>;
getEventsFromCache(): Promise<CachedEvents<AllRelayerRegistryEvents>>;
saveEvents({ newEvents, lastBlock, }: BaseEvents<AllRelayerRegistryEvents> & {
newEvents: AllRelayerRegistryEvents[];
}): Promise<void>;
getRelayersFromDB(): Promise<CachedRelayers>;
getRelayersFromCache(): Promise<CachedRelayers>;
saveRelayers(cachedRelayers: CachedRelayers): Promise<void>;
}
export interface DBRevenueServiceConstructor extends BaseRevenueServiceConstructor {
staticUrl: string;

8
dist/gaszip.d.ts vendored
View File

@@ -1,10 +1,6 @@
import { NetIdType } from './networkConfig';
export declare const gasZipInbounds: {
[key in NetIdType]: string;
};
export declare const gasZipID: {
[key in NetIdType]: number;
};
export declare const gasZipInbounds: Record<NetIdType, string>;
export declare const gasZipID: Record<NetIdType, number>;
export declare function gasZipInput(to: string, shorts: number[]): string | null;
export declare function gasZipMinMax(ethUsd: number): {
min: number;

View File

@@ -12,9 +12,7 @@ export interface queryGraphParams {
graphApi: string;
subgraphName: string;
query: string;
variables?: {
[key: string]: string | number;
};
variables?: Record<string, string | number>;
fetchDataOptions?: fetchDataOptions;
}
export declare function queryGraph<T>({ graphApi, subgraphName, query, variables, fetchDataOptions, }: queryGraphParams): Promise<T>;

18
dist/hashes.json vendored
View File

@@ -1,11 +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"
"dist/index.js": "sha384-ILYwPMkfXtfslWBdxqmKYYq4yDnJf3tKKgW5Am0P4NnpLW9z5m/wlLBuITa51727",
"dist/index.mjs": "sha384-bgbAMKtG/iKiZKdITr1E+i14xCVp17/Yj8/O5bCE8BMnqlwTY/XvmLe8+EWmvB/s",
"dist/merkleTreeWorker.js": "sha384-CJID2MqR6z42NYw9ZXhHAZ05+7YrQg0YJDwJLB/qhOhIJYt3am2wVZdR/W7njnuZ",
"dist/merkleTreeWorker.umd.js": "sha384-6uVyasRLcNVbdsuJSbgzxfSQ/0yRURWhP6oULhW6jffTNJzpMWay6/ie9pQVUj78",
"dist/merkleTreeWorker.umd.min.js": "sha384-LUERJw4t5HapI287NVtz3VtnqjXXrC7yBdxMQHpkTX8ZGOjp5fyEqYb1KENzNY9R",
"dist/tornado.umd.js": "sha384-IXBljLi80UVwQj0arsdDbOLEQlHfXFAKbAencbqDgqyUaRs7mjsYJDFe+wPgB/ie",
"dist/tornado.umd.min.js": "sha384-FwrFV3WOdr2uBRdFyPIKJU+gCybthzvw6+D9GDTRAYflL+Kh8C3hUyk7qsJJGGvq",
"dist/tornadoContracts.umd.js": "sha384-wOj+yVI78CBtmwvEn3l/ko3CEbtaqHXbx69pKraBOLOkz98HlJSSTcKs/spf0Ods",
"dist/tornadoContracts.umd.min.js": "sha384-LMcw1ogbZefmFD6HUNEOnhIYizZEWtRW2FqUIglSxk5i1qoDq1+iZolSaGheQfx9"
}

12
dist/idb.d.ts vendored
View File

@@ -1,5 +1,11 @@
import { OpenDBCallbacks, IDBPDatabase } from 'idb';
import { NetIdType } from './networkConfig';
import type * as idb from 'idb';
import type { OpenDBCallbacks, IDBPDatabase } from 'idb';
import type { NetIdType, TornadoConfig } from './networkConfig';
declare global {
interface Window {
idb: typeof idb;
}
}
export declare const INDEX_DB_ERROR = "A mutation operation was attempted on a database that did not allow mutations.";
export interface IDBIndex {
name: string;
@@ -81,4 +87,4 @@ export declare class IndexedDB {
/**
* Should check if DB is initialized well
*/
export declare function getIndexedDB(netId?: NetIdType): Promise<IndexedDB>;
export declare function getIndexedDB(netId?: NetIdType, tornadoConfig?: TornadoConfig): Promise<IndexedDB>;

2100
dist/index.js vendored

File diff suppressed because it is too large Load Diff

2077
dist/index.mjs vendored

File diff suppressed because it is too large Load Diff

16
dist/ip.d.ts vendored
View File

@@ -1,6 +1,20 @@
import { fetchDataOptions } from './providers';
export interface IPResult {
ip: string;
iso?: string;
country?: string;
country_iso?: string;
tor?: boolean;
}
export declare function fetchIp(ipEcho: string): Promise<IPResult>;
export declare function fetchIp(ipEcho: string, fetchOptions?: fetchDataOptions): Promise<IPResult>;
export interface IPResultFuck {
YourFuckingIPAddress: string;
YourFuckingLocation: string;
YourFuckingHostname: string;
YourFuckingISP: string;
YourFuckingTorExit: boolean;
YourFuckingCity?: string;
YourFuckingCountry: string;
YourFuckingCountryCode: string;
}
export declare function fetchFuckingIp(ipFuck?: string, fetchOptions?: fetchDataOptions): Promise<IPResultFuck>;

View File

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

7508
dist/merkleTreeWorker.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -15,12 +15,3 @@
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/**
* [js-sha3]{@link https://github.com/emn178/js-sha3}
*
* @version 0.8.0
* @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2015-2018
* @license MIT
*/

View File

@@ -1,4 +1,12 @@
import type { DepositType } from './deposits';
export declare const MERKLE_TREE_HEIGHT = 20;
export declare const EMPTY_ELEMENT = "21663839004416932945382355908790599225266501822907911457504978515578255421292";
export declare const MULTICALL_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
export declare const ONEINCH_ORACLE_ADDRESS = "0x00000000000D6FFc74A8feb35aF5827bf57f6786";
export declare const TORNADO_PROXY_LIGHT_ADDRESS = "0x0D5550d52428E7e3175bfc9550207e4ad3859b17";
export declare const ECHOER_ADDRESS = "0xa75BF2815618872f155b7C4B0C81bF990f5245E4";
export declare const TOVARISH_REGISTRY_ADDRESS = "0xc9D5C487c10bC755d34029b1135FA1c190d80f9b";
export declare const TOVARISH_AGGREGATOR_ADDRESS = "0x7A51f64A277d3597475Ea28283d0423764613231";
/**
* Type of default supported networks
*/
@@ -15,52 +23,76 @@ export declare enum NetId {
SEPOLIA = 11155111
}
export type NetIdType = NetId | number;
export interface RpcUrl {
name: string;
url: string;
}
export interface RpcUrls {
[key: string]: RpcUrl;
}
export interface SubgraphUrl {
name: string;
url: string;
}
export interface SubgraphUrls {
[key: string]: SubgraphUrl;
}
export interface TornadoInstance {
instanceAddress: {
[key: string]: string;
};
export interface TornadoInstances {
instanceAddress: Record<string, string>;
instanceApproval?: boolean;
optionalInstances?: string[];
isOptional?: boolean;
isDisabled?: boolean;
tokenAddress?: string;
tokenGasLimit?: number;
symbol: string;
decimals: number;
gasLimit?: number;
}
export interface TokenInstances {
[key: string]: TornadoInstance;
export interface TornadoSingleInstance {
netId: NetId;
instanceAddress: string;
instanceApproval?: boolean;
isOptional?: boolean;
isDisabled?: boolean;
tokenAddress?: string;
tokenGasLimit?: number;
currency: string;
amount: string;
decimals: number;
gasLimit?: number;
}
export interface Config {
rpcCallRetryAttempt?: number;
gasPrices: {
instant: number;
fast?: number;
standard?: number;
low?: number;
maxPriorityFeePerGas?: number;
};
nativeCurrency: string;
currencyName: string;
explorerUrl: string;
merkleTreeHeight: number;
emptyElement: string;
export type TokenInstances = Record<string, TornadoInstances>;
export type SubdomainMap = Record<NetIdType, string>;
export interface ConfigParams {
netId: NetIdType;
networkName: string;
currencyName: string;
nativeCurrency?: string;
explorerUrl: string;
homepageUrl: string;
blockTime: number;
deployedBlock: number;
rpcUrls: RpcUrls;
merkleTreeHeight?: number;
emptyElement?: string;
stablecoin: string;
multicallContract?: string;
routerContract?: string;
echoContract?: string;
offchainOracleContract?: string;
tornContract?: string;
governanceContract?: string;
stakingRewardsContract?: string;
registryContract?: string;
tovarishRegistryContract?: string;
aggregatorContract?: string;
reverseRecordsContract?: string;
ovmGasPriceOracleContract?: string;
relayerEnsSubdomain: string;
tornadoSubgraph?: string;
registrySubgraph?: string;
governanceSubgraph?: string;
subgraphs?: string[];
rpcUrls: string[];
tokens: TokenInstances;
}
export declare class Config {
netId: NetIdType;
networkName: string;
currencyName: string;
nativeCurrency: string;
explorerUrl: string;
homepageUrl: string;
blockTime: number;
deployedBlock: number;
merkleTreeHeight?: number;
emptyElement?: string;
stablecoin: string;
multicallContract: string;
routerContract: string;
@@ -70,59 +102,36 @@ export interface Config {
governanceContract?: string;
stakingRewardsContract?: string;
registryContract?: string;
tovarishRegistryContract?: string;
aggregatorContract?: string;
reverseRecordsContract?: string;
ovmGasPriceOracleContract?: string;
tornadoSubgraph: string;
relayerEnsSubdomain: string;
tornadoSubgraph?: string;
registrySubgraph?: string;
governanceSubgraph?: string;
subgraphs: SubgraphUrls;
subgraphs?: string[];
rpcUrls: string[];
tokens: TokenInstances;
optionalTokens?: string[];
disabledTokens?: string[];
relayerEnsSubdomain: string;
pollInterval: number;
constants: {
GOVERNANCE_BLOCK?: number;
NOTE_ACCOUNT_BLOCK?: number;
ENCRYPTED_NOTES_BLOCK?: number;
REGISTRY_BLOCK?: number;
MINING_BLOCK_TIME?: number;
};
constructor(configParams: ConfigParams);
toJSON(): ConfigParams;
get allTokens(): string[];
get allSymbols(): string[];
getInstance(currency: string, amount: string): TornadoSingleInstance;
getInstanceByAddress(instanceAddress: string): TornadoSingleInstance;
get depositTypes(): Record<string, DepositType>;
}
export interface networkConfig {
[key: NetIdType]: Config;
export interface TornadoConfigParams {
configs?: Record<NetIdType, ConfigParams>;
governanceNetwork?: NetIdType;
relayerNetwork?: NetIdType;
}
export interface SubdomainMap {
[key: NetIdType]: string;
export declare class TornadoConfig {
configs: Record<NetIdType, Config>;
governanceNetwork: NetIdType;
relayerNetwork: NetIdType;
constructor(configParams?: TornadoConfigParams);
get chains(): NetIdType[];
getConfig(netId: NetIdType): Config;
}
export declare const defaultConfig: networkConfig;
export declare const enabledChains: NetIdType[];
/**
* Custom config object to extend default config
*
* Inspired by getUrlFunc from ethers.js
* https://github.com/ethers-io/ethers.js/blob/v6/src.ts/utils/fetch.ts#L59
*/
export declare let customConfig: networkConfig;
/**
* Add or override existing network config object
*
* Could be also called on the UI hook so that the UI could allow people to use custom privacy pools
*/
export declare function addNetwork(newConfig: networkConfig): void;
export declare function getNetworkConfig(): networkConfig;
export declare function getConfig(netId: NetIdType): Config;
export declare function getActiveTokens(config: Config): string[];
export declare function getActiveTokenInstances(config: Config): TokenInstances;
export declare function getInstanceByAddress(config: Config, address: string): {
amount: string;
currency: string;
symbol: string;
decimals: number;
tokenAddress: string | undefined;
} | undefined;
export declare function getRelayerEnsSubdomains(): SubdomainMap;
export declare function getMultiInstances(netId: NetIdType, config: Config): {
[key in string]: DepositType;
};
export declare const defaultConfig: Record<NetIdType, ConfigParams>;

6
dist/permit.d.ts vendored
View File

@@ -1,4 +1,4 @@
import { ERC20Permit, ERC20Mock, TORN } from '@tornado/contracts';
import { ERC20Permit, ERC20Mock, TORN } from 'tornado-contracts';
import { Signature, Signer, TypedDataField } from 'ethers';
export interface PermitValue {
spender: string;
@@ -18,9 +18,7 @@ export declare const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA
*/
export interface Witness {
witnessTypeName: string;
witnessType: {
[key: string]: TypedDataField[];
};
witnessType: Record<string, TypedDataField[]>;
witness: any;
}
export declare function getPermitSignature({ Token, signer, spender, value, nonce, deadline, }: PermitValue & {

56
dist/providers.d.ts vendored
View File

@@ -1,7 +1,6 @@
import type { EventEmitter } from 'stream';
import type { RequestOptions } from 'http';
import { JsonRpcApiProvider, JsonRpcProvider, Wallet, FetchGetUrlFunc, Provider, SigningKey, TransactionRequest, JsonRpcSigner, BrowserProvider, Networkish, Eip1193Provider, VoidSigner, FetchCancelSignal } from 'ethers';
import type { RequestInfo, RequestInit, Response, HeadersInit } from 'node-fetch';
import { JsonRpcApiProvider, JsonRpcProvider, Wallet, FetchGetUrlFunc, Provider, SigningKey, TransactionRequest, JsonRpcSigner, BrowserProvider, Networkish, Eip1193Provider, VoidSigner, FetchCancelSignal, TransactionLike } from 'ethers';
import type { Dispatcher, RequestInit } from 'undici-types';
import type { Config, NetIdType } from './networkConfig';
declare global {
interface Window {
@@ -9,35 +8,54 @@ declare global {
}
}
export declare const defaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0";
export type nodeFetch = (url: RequestInfo, init?: RequestInit) => Promise<Response>;
export type fetchDataOptions = RequestInit & {
export type DispatcherFunc = (retry?: number) => Dispatcher;
export interface fetchDataOptions extends Omit<RequestInit, 'headers'> {
/**
* Overriding RequestInit params
*/
headers?: HeadersInit | any;
/**
* Expanding RequestInit params
*/
maxRetry?: number;
retryOn?: number;
userAgent?: string;
timeout?: number;
proxy?: string;
torPort?: number;
debug?: Function;
returnResponse?: boolean;
cancelSignal?: FetchCancelSignal;
};
export type NodeAgent = RequestOptions['agent'] | ((parsedUrl: URL) => RequestOptions['agent']);
export declare function getHttpAgent({ fetchUrl, proxyUrl, torPort, retry, }: {
fetchUrl: string;
proxyUrl?: string;
torPort?: number;
retry: number;
}): NodeAgent | undefined;
export declare function fetchData(url: string, options?: fetchDataOptions): Promise<any>;
dispatcherFunc?: DispatcherFunc;
}
export declare function fetchData<T>(url: string, options?: fetchDataOptions): Promise<T>;
export declare const fetchGetUrlFunc: (options?: fetchDataOptions) => FetchGetUrlFunc;
export type getProviderOptions = fetchDataOptions & {
netId?: NetIdType;
pollingInterval?: number;
};
export declare const FeeDataNetworkPluginName: string;
export declare function getProvider(rpcUrl: string, fetchOptions?: getProviderOptions): Promise<JsonRpcProvider>;
export declare function getProviderWithNetId(netId: NetIdType, rpcUrl: string, config: Config, fetchOptions?: getProviderOptions): JsonRpcProvider;
export declare const populateTransaction: (signer: TornadoWallet | TornadoVoidSigner | TornadoRpcSigner, tx: TransactionRequest) => Promise<TransactionRequest>;
export declare const populateTransaction: (signer: TornadoWallet | TornadoVoidSigner | TornadoRpcSigner, tx: TransactionRequest) => Promise<{
type?: null | number | undefined;
to?: string | import("ethers").Addressable | null | undefined;
from?: string | import("ethers").Addressable | null | undefined;
nonce?: null | number | undefined;
gasLimit?: string | number | bigint | null | undefined;
gasPrice?: string | number | bigint | null | undefined;
maxPriorityFeePerGas?: string | number | bigint | null | undefined;
maxFeePerGas?: string | number | bigint | null | undefined;
data?: null | string | undefined;
value?: string | number | bigint | null | undefined;
chainId?: string | number | bigint | null | undefined;
accessList?: import("ethers").AccessList | [string, string[]][] | Record<string, string[]> | null | undefined;
customData?: any;
blockTag?: string | number | bigint | undefined;
enableCcipRead?: boolean | undefined;
blobVersionedHashes?: (null | Array<string>) | undefined;
maxFeePerBlobGas?: string | number | bigint | null | undefined;
blobs?: (null | Array<import("ethers").BlobLike>) | undefined;
kzg?: (null | import("ethers").KzgLibrary) | undefined;
}>;
export interface TornadoWalletOptions {
gasPriceBump?: number;
gasLimitBump?: number;
@@ -53,7 +71,7 @@ export declare class TornadoWallet extends Wallet {
bumpNonce: boolean;
constructor(key: string | SigningKey, provider?: Provider, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce }?: TornadoWalletOptions);
static fromMnemonic(mneomnic: string, provider: Provider, index?: number, options?: TornadoWalletOptions): TornadoWallet;
populateTransaction(tx: TransactionRequest): Promise<import("ethers").TransactionLike<string>>;
populateTransaction(tx: TransactionRequest): Promise<TransactionLike<string>>;
}
export declare class TornadoVoidSigner extends VoidSigner {
nonce?: number;
@@ -62,7 +80,7 @@ export declare class TornadoVoidSigner extends VoidSigner {
gasFailover: boolean;
bumpNonce: boolean;
constructor(address: string, provider?: Provider, { gasPriceBump, gasLimitBump, gasFailover, bumpNonce }?: TornadoWalletOptions);
populateTransaction(tx: TransactionRequest): Promise<import("ethers").TransactionLike<string>>;
populateTransaction(tx: TransactionRequest): Promise<TransactionLike<string>>;
}
export declare class TornadoRpcSigner extends JsonRpcSigner {
nonce?: number;

View File

@@ -1,4 +1,4 @@
import { NetIdType, Config } from './networkConfig';
import type { NetIdType, TornadoConfig } from './networkConfig';
import { fetchDataOptions } from './providers';
import type { snarkProofs } from './websnark';
import type { CachedRelayerInfo } from './events';
@@ -20,9 +20,7 @@ export interface RelayerInfo extends RelayerParams {
instances: string[];
stakeBalance?: string;
gasPrice?: number;
ethPrices?: {
[key in string]: string;
};
ethPrices?: Record<string, string>;
currentQueue: number;
tornadoServiceFee: number;
}
@@ -35,24 +33,18 @@ export interface RelayerError {
export interface RelayerStatus {
url: string;
rewardAccount: string;
instances: {
[key in string]: {
instanceAddress: {
[key in string]: string;
};
tokenAddress?: string;
symbol: string;
decimals: number;
};
};
instances: Record<string, {
instanceAddress: Record<string, string>;
tokenAddress?: string;
symbol: string;
decimals: number;
}>;
gasPrices?: {
fast: number;
additionalProperties?: number;
};
netId: NetIdType;
ethPrices?: {
[key in string]: string;
};
ethPrices?: Record<string, string>;
tornadoServiceFee: number;
latestBlock?: number;
version: string;
@@ -111,34 +103,28 @@ export function isRelayerUpdated(relayerVersion: string, netId: NetIdType) {
**/
export declare function calculateScore({ stakeBalance, tornadoServiceFee }: RelayerInfo): bigint;
export declare function getWeightRandom(weightsScores: bigint[], random: bigint): number;
export interface RelayerInstanceList {
[key: string]: {
instanceAddress: {
[key: string]: string;
};
};
}
export type RelayerInstanceList = Record<string, {
instanceAddress: Record<string, string>;
}>;
export declare function getSupportedInstances(instanceList: RelayerInstanceList): string[];
export declare function pickWeightedRandomRelayer(relayers: RelayerInfo[]): RelayerInfo;
export interface RelayerClientConstructor {
netId: NetIdType;
config: Config;
tornadoConfig: TornadoConfig;
fetchDataOptions?: fetchDataOptions;
}
export declare class RelayerClient {
netId: NetIdType;
config: Config;
tornadoConfig: TornadoConfig;
selectedRelayer?: RelayerInfo;
fetchDataOptions?: fetchDataOptions;
tovarish: boolean;
constructor({ netId, config, fetchDataOptions }: RelayerClientConstructor);
askRelayerStatus({ hostname, url, relayerAddress, }: {
constructor({ tornadoConfig, fetchDataOptions }: RelayerClientConstructor);
askRelayerStatus({ netId, hostname, url, }: {
netId: NetIdType;
hostname?: string;
url?: string;
relayerAddress?: string;
}): Promise<RelayerStatus>;
filterRelayer(relayer: CachedRelayerInfo): Promise<RelayerInfo | RelayerError | undefined>;
getValidRelayers(relayers: CachedRelayerInfo[]): Promise<{
filterRelayer(netId: NetIdType, relayer: CachedRelayerInfo): Promise<RelayerInfo | RelayerError | undefined>;
getValidRelayers(netId: NetIdType, relayers: CachedRelayerInfo[]): Promise<{
validRelayers: RelayerInfo[];
invalidRelayers: RelayerError[];
}>;

View File

@@ -1,18 +1,16 @@
import { Config, NetIdType } from '../networkConfig';
import { Config } from '../networkConfig';
import { addressSchemaType, bnSchemaType } from '.';
export interface statusInstanceType {
type: string;
properties: {
instanceAddress: {
type: string;
properties: {
[key in string]: typeof addressSchemaType;
};
properties: Record<string, typeof addressSchemaType>;
required: string[];
};
tokenAddress?: typeof addressSchemaType;
symbol?: {
enum: string[];
type: string;
};
decimals: {
enum: number[];
@@ -22,16 +20,12 @@ export interface statusInstanceType {
}
export interface statusInstancesType {
type: string;
properties: {
[key in string]: statusInstanceType;
};
properties: Record<string, statusInstanceType>;
required: string[];
}
export interface statusEthPricesType {
type: string;
properties: {
[key in string]: typeof bnSchemaType;
};
properties: Record<string, typeof bnSchemaType>;
required?: string[];
}
export interface statusSchema {
@@ -41,11 +35,9 @@ export interface statusSchema {
instances?: statusInstancesType;
gasPrices: {
type: string;
properties: {
[key in string]: {
type: string;
};
};
properties: Record<string, {
type: string;
}>;
required: string[];
};
netId: {
@@ -103,4 +95,4 @@ export interface statusSchema {
};
required: string[];
}
export declare function getStatusSchema(netId: NetIdType, config: Config, tovarish: boolean): statusSchema;
export declare function getStatusSchema(config: Config, tovarish: boolean): statusSchema;

334812
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

View File

@@ -23,12 +23,3 @@
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
/*! scure-base - MIT License (c) 2022 Paul Miller (paulmillr.com) */
/**
* [js-sha3]{@link https://github.com/emn178/js-sha3}
*
* @version 0.8.0
* @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2015-2018
* @license MIT
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,6 @@
import { RelayerClient, RelayerClientConstructor, RelayerError, RelayerInfo, RelayerStatus } from './relayerClient';
import { CachedRelayerInfo, MinimalEvents } from './events';
import { NetIdType } from './networkConfig';
export declare const MAX_TOVARISH_EVENTS = 5000;
export interface EventsStatus {
events: number;
@@ -45,7 +46,8 @@ export interface BaseTovarishEvents<T> {
export declare class TovarishClient extends RelayerClient {
selectedRelayer?: TovarishInfo;
constructor(clientConstructor: RelayerClientConstructor);
askRelayerStatus({ hostname, url, relayerAddress, }: {
askRelayerStatus({ netId, hostname, url, }: {
netId: NetIdType;
hostname?: string;
url?: string;
relayerAddress?: string;
@@ -58,8 +60,8 @@ export declare class TovarishClient extends RelayerClient {
url?: string;
relayerAddress?: string;
}): Promise<TovarishStatus[]>;
filterRelayer(relayer: CachedRelayerInfo): Promise<TovarishInfo | RelayerError | undefined>;
getValidRelayers(relayers: CachedRelayerInfo[]): Promise<{
filterRelayer(netId: NetIdType, relayer: CachedRelayerInfo): Promise<TovarishInfo | RelayerError | undefined>;
getValidRelayers(netId: NetIdType, relayers: CachedRelayerInfo[]): Promise<{
validRelayers: TovarishInfo[];
invalidRelayers: RelayerError[];
}>;

12
dist/utils.d.ts vendored
View File

@@ -8,22 +8,22 @@ export declare const chunk: <T>(arr: T[], size: number) => T[][];
export declare function sleep(ms: number): Promise<unknown>;
export declare function validateUrl(url: string, protocols?: string[]): boolean;
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
export declare function bufferToBytes(b: Buffer): Uint8Array;
export declare function bufferToBytes(b: Buffer): Uint8Array<ArrayBufferLike>;
export declare function bytesToBase64(bytes: Uint8Array): string;
export declare function base64ToBytes(base64: string): Uint8Array;
export declare function base64ToBytes(base64: string): Uint8Array<ArrayBuffer>;
export declare function bytesToHex(bytes: Uint8Array): string;
export declare function hexToBytes(hexString: string): Uint8Array;
export declare function hexToBytes(hexString: string): Uint8Array<ArrayBuffer>;
export declare function bytesToBN(bytes: Uint8Array): bigint;
export declare function bnToBytes(bigint: bigint | string): Uint8Array;
export declare function bnToBytes(bigint: bigint | string): Uint8Array<ArrayBuffer>;
export declare function leBuff2Int(bytes: Uint8Array): BN;
export declare function leInt2Buff(bigint: bnInput | bigint): Uint8Array;
export declare function leInt2Buff(bigint: bnInput | bigint): Uint8Array<ArrayBuffer>;
export declare function toFixedHex(numberish: BigNumberish, length?: number): string;
export declare function toFixedLength(string: string, length?: number): string;
export declare function rBigInt(nbytes?: number): bigint;
export declare function rHex(nbytes?: number): string;
export declare function bigIntReplacer(key: any, value: any): any;
export declare function substring(str: string, length?: number): string;
export declare function digest(bytes: Uint8Array, algo?: string): Promise<Uint8Array>;
export declare function digest(bytes: Uint8Array, algo?: string): Promise<Uint8Array<ArrayBuffer>>;
export declare function numberFormatter(num: string | number | bigint, digits?: number): string;
export declare function isHex(value: string): boolean;
export declare function toContentHash(ipfsUrl: string): any;

8
dist/zip.d.ts vendored
View File

@@ -1,9 +1,13 @@
import { AsyncZippable, Unzipped, ZipAttributes } from 'fflate';
import { AsyncZippable, Unzipped, ZipAttributes, AsyncZlibOptions, AsyncUnzlibOptions } from 'fflate';
import { fetchDataOptions } from './providers';
export declare function zipAsync(file: AsyncZippable, options?: ZipAttributes): Promise<Uint8Array>;
export declare function unzipAsync(data: Uint8Array): Promise<Unzipped>;
export declare function downloadZip<T>({ staticUrl, zipName, zipDigest, parseJson, }: {
export declare function zlibAsync(data: Uint8Array, options?: AsyncZlibOptions): Promise<Uint8Array>;
export declare function unzlibAsync(data: Uint8Array, options?: AsyncUnzlibOptions): Promise<Uint8Array>;
export declare function downloadZip<T>({ staticUrl, zipName, zipDigest, parseJson, fetchOptions, }: {
staticUrl?: string;
zipName: string;
zipDigest?: string;
parseJson?: boolean;
fetchOptions?: fetchDataOptions;
}): Promise<T>;

55
eslint.config.mjs Normal file
View File

@@ -0,0 +1,55 @@
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import importPlugin from 'eslint-plugin-import';
import prettierRecommendedConfig from 'eslint-plugin-prettier/recommended';
export default tseslint.config(
{
files: ['**/*.js', '**/*.mjs', '**/*.ts', '**/*.tsx'],
extends: [eslint.configs.recommended, prettierRecommendedConfig],
languageOptions: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
},
rules: {
'prettier/prettier': [
'error',
{
tabWidth: 4,
printWidth: 120,
singleQuote: true,
},
],
},
},
{
files: ['**/*.ts', '**/*.tsx'],
extends: [
...tseslint.configs.recommended,
...tseslint.configs.stylistic,
importPlugin.flatConfigs.recommended,
importPlugin.flatConfigs.typescript,
],
rules: {
'import/order': ['error'],
'@typescript-eslint/no-unused-vars': ['warn'],
'@typescript-eslint/no-unused-expressions': ['off'],
'@typescript-eslint/no-empty-function': ['off'],
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
},
},
languageOptions: {
parserOptions: {
projectService: true,
},
},
},
);

View File

@@ -1,6 +1,6 @@
{
"name": "@tornado/core",
"version": "1.0.19",
"name": "tornado-scripts",
"version": "1.0.23",
"description": "An SDK for building applications on top of Privacy Pools",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
@@ -10,98 +10,99 @@
"scripts": {
"typechain": "typechain --target ethers-v6 --out-dir src/typechain src/abi/*.json",
"types": "tsc --declaration --emitDeclarationOnly -p tsconfig.build.json",
"lint": "eslint src/**/*.ts test/**/*.ts --ext .ts --ignore-pattern src/typechain",
"lint": "eslint src/**/*.ts test/**/*.ts --ignore-pattern src/typechain",
"build:node": "rollup -c",
"build:web": "webpack",
"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'"
"docker:build": "docker build -t tornado-scripts .",
"docker:hash": "docker container run --rm -it --entrypoint cat tornado-scripts /app/dist/hashes.json",
"test": "nyc mocha --require ts-node/register --require source-map-support/register --timeout 300000 --recursive test/**/*.ts"
},
"author": "",
"author": "Tornado Contrib",
"license": "MIT",
"files": [
"dist",
"scripts",
"src",
".eslintrc.js",
"test",
".gitattributes",
".gitignore",
".npmrc",
"Dockerfile",
"eslint.config.mjs",
"hardhat.config.ts",
"LICENSE",
"logo.png",
"logo2.png",
"rollup.config.mjs",
"tsconfig.build.json",
"tsconfig.json",
"webpack.config.js",
"yarn.lock"
],
"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#093ae2210e1f1b016b756b4db200c4a1b3308408",
"@metamask/eth-sig-util": "^8.2.0",
"ajv": "^8.17.1",
"bn.js": "^5.2.1",
"circomlibjs": "0.1.7",
"cross-fetch": "^4.0.0",
"ethers": "^6.13.4",
"ffjavascript": "0.2.48",
"circomlibjs": "git+https://github.com/tornadocontrib/circomlibjs.git#2aef7aade8e2b8d103250e4b24c7f1526cf1dd8d",
"ethers": "^6.13.5",
"ffjavascript": "git+https://github.com/tornadocontrib/ffjavascript.git#fc766f09818d46967d1329c0fc8e361d8b349109",
"fflate": "^0.8.2",
"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"
"tornado-contracts": "git+https://github.com/tornadocontrib/tornado-contracts.git#a1c8fbd2919996a642a7de1abec86548ff64449b",
"websnark": "git+https://github.com/tornadocontrib/websnark.git#e5a79cca905d1ffb61a69739492be58d438c9f17"
},
"devDependencies": {
"@nomicfoundation/hardhat-chai-matchers": "^2.0.7",
"@nomicfoundation/hardhat-chai-matchers": "^2.0.8",
"@nomicfoundation/hardhat-ethers": "^3.0.8",
"@nomicfoundation/hardhat-ignition": "^0.15.5",
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.5",
"@nomicfoundation/hardhat-network-helpers": "^1.0.11",
"@nomicfoundation/hardhat-ignition": "^0.15.11",
"@nomicfoundation/hardhat-ignition-ethers": "^0.15.11",
"@nomicfoundation/hardhat-network-helpers": "^1.0.12",
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
"@nomicfoundation/hardhat-verify": "^2.0.10",
"@nomicfoundation/ignition-core": "^0.15.5",
"@rollup/plugin-commonjs": "^28.0.1",
"@nomicfoundation/hardhat-verify": "^2.0.13",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-node-resolve": "^16.0.1",
"@typechain/ethers-v6": "^0.5.1",
"@typechain/hardhat": "^9.1.0",
"@types/bn.js": "^5.1.6",
"@types/chai": "^4.2.0",
"@types/chai": "^5.2.1",
"@types/circomlibjs": "^0.1.6",
"@types/mocha": "^10.0.9",
"@types/node": "^22.8.0",
"@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",
"eslint-import-resolver-typescript": "^3.6.3",
"@types/mocha": "^10.0.10",
"@types/node": "^22.14.1",
"chai": "^4.5.0",
"esbuild-loader": "^4.3.0",
"eslint": "^9.24.0",
"eslint-config-prettier": "^10.1.2",
"eslint-import-resolver-typescript": "^4.3.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.1",
"fetch-mock": "^12.0.2",
"hardhat": "^2.22.10",
"hardhat-gas-reporter": "^2.2.1",
"mocha": "^10.7.3",
"node-polyfill-webpack-plugin": "^4.0.0",
"eslint-plugin-prettier": "^5.2.6",
"fetch-mock": "^12.5.2",
"hardhat": "^2.23.0",
"hardhat-gas-reporter": "^2.2.3",
"idb": "^8.0.2",
"mocha": "^11.1.0",
"node-polyfill-webpack-plugin": "^4.1.0",
"nyc": "^17.1.0",
"prettier": "^3.3.3",
"rollup": "^4.24.0",
"rollup-plugin-esbuild": "^6.1.1",
"solidity-coverage": "^0.8.13",
"prettier": "^3.5.3",
"rollup": "^4.40.0",
"rollup-plugin-esbuild": "^6.2.1",
"solidity-coverage": "^0.8.15",
"ts-node": "^10.9.2",
"tsc": "^2.0.4",
"typechain": "^8.3.2",
"typescript": "^5.6.3",
"webpack": "^5.95.0",
"webpack-cli": "^5.1.4"
"typescript": "^5.8.3",
"typescript-eslint": "^8.30.1",
"webpack": "^5.99.5",
"webpack-cli": "^6.0.1"
},
"resolutions": {
"strip-ansi": "6.0.1",
"@adraffy/ens-normalize": "1.10.1",
"@noble/curves": "1.2.0",
"@noble/hashes": "1.3.2",
"big-integer": "1.6.52",
"ffjavascript": "0.2.48"
"ffjavascript": "git+https://github.com/tornadocontrib/ffjavascript.git#fc766f09818d46967d1329c0fc8e361d8b349109"
}
}

View File

@@ -42,7 +42,7 @@ export async function getSubInfo(
tag: string;
topics: TopicFilter;
}> {
let topics: Array<null | string | Array<string>>;
let topics: (null | string | string[])[];
let fragment: null | EventFragment = null;
// Convert named events to topicHash and get the fragment for
@@ -242,7 +242,6 @@ export class BatchBlockService {
let retries = 0;
let err;
// eslint-disable-next-line no-unmodified-loop-condition
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
try {
return await Promise.all(blocks.map((b) => this.getBlock(b)));
@@ -346,7 +345,6 @@ export class BatchTransactionService {
let retries = 0;
let err;
// eslint-disable-next-line no-unmodified-loop-condition
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
try {
if (!receipt) {
@@ -491,7 +489,6 @@ export class BatchEventsService {
let err;
let retries = 0;
// eslint-disable-next-line no-unmodified-loop-condition
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
try {
if (this.address) {

View File

@@ -1,4 +1,4 @@
export * from '@tornado/contracts';
export * from 'tornado-contracts';
export {
Multicall,
Multicall__factory,

View File

@@ -40,13 +40,14 @@ export function makeLabelNodeAndParent(name: string) {
}
// https://github.com/ensdomains/ensjs/blob/main/packages/ensjs/src/contracts/consts.ts
export const EnsContracts: {
[key: NetIdType]: {
export const EnsContracts: Record<
NetIdType,
{
ensRegistry: string;
ensPublicResolver: string;
ensNameWrapper: string;
};
} = {
}
> = {
[NetId.MAINNET]: {
ensRegistry: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
ensPublicResolver: '0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63',
@@ -75,7 +76,8 @@ export class ENSUtils {
async getContracts() {
const { chainId } = await this.provider.getNetwork();
const { ensRegistry, ensPublicResolver, ensNameWrapper } = EnsContracts[Number(chainId)];
const { ensRegistry, ensPublicResolver, ensNameWrapper } =
EnsContracts[Number(chainId)] || EnsContracts[NetId.MAINNET];
this.ENSRegistry = ENSRegistry__factory.connect(ensRegistry, this.provider);
this.ENSResolver = ENSResolver__factory.connect(ensPublicResolver, this.provider);

View File

@@ -4,7 +4,6 @@ import {
EventLog,
TransactionResponse,
getAddress,
namehash,
formatEther,
AbiCoder,
dataLength,
@@ -18,9 +17,9 @@ import {
Governance,
RelayerRegistry,
Echoer,
Aggregator,
Tornado__factory,
} from '@tornado/contracts';
TovarishAggregator,
} from 'tornado-contracts';
import type { MerkleTree } from 'fixed-merkle-tree';
import {
@@ -31,8 +30,8 @@ import {
BatchBlockOnProgress,
} from '../batch';
import { fetchData, fetchDataOptions } from '../providers';
import { enabledChains, type NetIdType, type SubdomainMap } from '../networkConfig';
import { fetchDataOptions } from '../providers';
import { TornadoConfig, type NetIdType, type SubdomainMap } from '../networkConfig';
import { RelayerParams, MIN_STAKE_BALANCE } from '../relayerClient';
import type { TovarishClient } from '../tovarishClient';
@@ -131,7 +130,6 @@ export class BaseEventsService<EventType extends MinimalEvents> {
/* eslint-enable @typescript-eslint/no-unused-vars */
async formatEvents(events: EventLog[]): Promise<EventType[]> {
// eslint-disable-next-line no-return-await
return await new Promise((resolve) => resolve(events as unknown as EventType[]));
}
@@ -440,17 +438,13 @@ export class BaseTornadoService extends BaseEventsService<DepositsEvents | Withd
}
export interface BaseMultiTornadoServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
instances: {
[key in string]: DepositType;
};
instances: Record<string, DepositType>;
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
}
export class BaseMultiTornadoService extends BaseEventsService<MultiDepositsEvents | MultiWithdrawalsEvents> {
instances: {
[key in string]: DepositType;
};
instances: Record<string, DepositType>;
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
@@ -717,7 +711,7 @@ export class BaseEncryptedNotesService extends BaseEventsService<EncryptedNotesE
const abiCoder = AbiCoder.defaultAbiCoder();
export const proposalState: { [key: string]: string } = {
export const proposalState: Record<string, string> = {
0: 'Pending',
1: 'Active',
2: 'Defeated',
@@ -821,13 +815,13 @@ export interface GovernanceVotes extends GovernanceVotedEvents {
export interface BaseGovernanceServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Governance: Governance;
Aggregator: Aggregator;
Aggregator: TovarishAggregator;
ReverseRecords: ReverseRecords;
}
export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
Governance: Governance;
Aggregator: Aggregator;
Aggregator: TovarishAggregator;
ReverseRecords: ReverseRecords;
batchTransactionService: BatchTransactionService;
@@ -955,7 +949,7 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
const [QUORUM_VOTES, proposalStatus, proposerNameRecords] = await Promise.all([
this.Governance.QUORUM_VOTES(),
this.Aggregator.getAllProposals(this.Governance.target),
this.Aggregator.getAllProposals(),
this.ReverseRecords.getNames(allProposers),
]);
@@ -966,7 +960,7 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
}
return acc;
},
{} as { [key: string]: string },
{} as Record<string, string>,
);
return proposalEvents.map((event, index) => {
@@ -1018,7 +1012,7 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
}
return acc;
},
{} as { [key: string]: string },
{} as Record<string, string>,
);
const votes = votedEvents.map((event) => {
@@ -1066,7 +1060,7 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
});
const [balances, uniqNameRecords] = await Promise.all([
this.Aggregator.getGovernanceBalances(this.Governance.target, uniq),
this.Aggregator.getGovernanceBalances(uniq),
this.ReverseRecords.getNames(uniq),
]);
@@ -1077,7 +1071,7 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
}
return acc;
},
{} as { [key: string]: string },
{} as Record<string, string>,
);
return {
@@ -1091,28 +1085,6 @@ export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents
}
}
export async function getTovarishNetworks(registryService: BaseRegistryService, relayers: CachedRelayerInfo[]) {
await Promise.all(
relayers
.filter((r) => r.tovarishHost)
.map(async (relayer) => {
try {
relayer.tovarishNetworks = await fetchData(relayer.tovarishHost as string, {
...registryService.fetchDataOptions,
headers: {
'Content-Type': 'application/json',
},
timeout: 30000,
maxRetry: registryService.fetchDataOptions?.torPort ? 2 : 0,
});
} catch {
// Ignore error and disable relayer
relayer.tovarishNetworks = [];
}
}),
);
}
/**
* Essential params:
* ensName, relayerAddress, hostnames
@@ -1120,55 +1092,26 @@ export async function getTovarishNetworks(registryService: BaseRegistryService,
*/
export interface CachedRelayerInfo extends RelayerParams {
isRegistered?: boolean;
registeredAddress?: string;
isPrior?: boolean;
stakeBalance?: string;
hostnames: SubdomainMap;
tovarishHost?: string;
tovarishNetworks?: number[];
}
/**
* Static relayer provided by tornadowithdraw.eth
* This relayer isn't compatible with the current UI (tornadocash.eth) and only works as experimental mode
* Once DAO approves changes to UI to support new Tovarish Relayer software register relayer and remove static list
*/
const staticRelayers: CachedRelayerInfo[] = [
{
ensName: 'tornadowithdraw.eth',
relayerAddress: '0x40c3d1656a26C9266f4A10fed0D87EFf79F54E64',
hostnames: {},
tovarishHost: 'tornadowithdraw.com',
tovarishNetworks: enabledChains,
},
{
ensName: 'rpc.tornadowithdraw.eth',
relayerAddress: '0xFF787B7A5cd8a88508361E3B7bcE791Aa2796526',
hostnames: {},
tovarishHost: 'tornadocash-rpc.com',
tovarishNetworks: enabledChains,
},
];
export interface CachedRelayers {
lastBlock: number;
timestamp: number;
relayers: CachedRelayerInfo[];
fromCache?: boolean;
}
export interface BaseRegistryServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
tornadoConfig: TornadoConfig;
RelayerRegistry: RelayerRegistry;
Aggregator: Aggregator;
relayerEnsSubdomains: SubdomainMap;
Aggregator: TovarishAggregator;
}
export class BaseRegistryService extends BaseEventsService<AllRelayerRegistryEvents> {
Aggregator: Aggregator;
relayerEnsSubdomains: SubdomainMap;
tornadoConfig: TornadoConfig;
Aggregator: TovarishAggregator;
updateInterval: number;
constructor(serviceConstructor: BaseRegistryServiceConstructor) {
const { RelayerRegistry: contract, Aggregator, relayerEnsSubdomains } = serviceConstructor;
const { RelayerRegistry: contract, tornadoConfig, Aggregator } = serviceConstructor;
super({
...serviceConstructor,
@@ -1176,8 +1119,8 @@ export class BaseRegistryService extends BaseEventsService<AllRelayerRegistryEve
type: '*',
});
this.tornadoConfig = tornadoConfig;
this.Aggregator = Aggregator;
this.relayerEnsSubdomains = relayerEnsSubdomains;
this.updateInterval = 86400;
}
@@ -1254,141 +1197,100 @@ export class BaseRegistryService extends BaseEventsService<AllRelayerRegistryEve
];
}
/**
* Get saved or cached relayers
*/
async getRelayersFromDB(): Promise<CachedRelayers> {
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
};
}
async getLatestRelayers(knownRelayers?: string[]): Promise<CachedRelayerInfo[]> {
const newRelayers: string[] = [];
/**
* Relayers from remote cache (Either from local cache, CDN, or from IPFS)
*/
async getRelayersFromCache(): Promise<CachedRelayers> {
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
fromCache: true,
};
}
if (knownRelayers?.length) {
const { events: allEvents } = await this.updateEvents();
async getSavedRelayers(): Promise<CachedRelayers> {
let cachedRelayers = await this.getRelayersFromDB();
const events = allEvents.filter((e) => e.event === 'RelayerRegistered') as RelayerRegisteredEvents[];
if (!cachedRelayers || !cachedRelayers.relayers.length) {
cachedRelayers = await this.getRelayersFromCache();
for (const { ensName } of events) {
if (!newRelayers.includes(ensName) && !knownRelayers?.includes(ensName)) {
newRelayers.push(ensName);
}
}
}
return cachedRelayers;
}
async getLatestRelayers(): Promise<CachedRelayers> {
const { events: allEvents, lastBlock } = await this.updateEvents();
const events = allEvents.filter((e) => e.event === 'RelayerRegistered') as RelayerRegisteredEvents[];
const subdomains = Object.values(this.relayerEnsSubdomains);
const registerSet = new Set();
const uniqueRegisters = events.filter(({ ensName }) => {
if (!registerSet.has(ensName)) {
registerSet.add(ensName);
return true;
}
return false;
});
const relayerNameHashes = uniqueRegisters.map((r) => namehash(r.ensName));
const [relayersData, timestamp] = await Promise.all([
this.Aggregator.relayersData.staticCall(relayerNameHashes, subdomains.concat('tovarish-relayer')),
this.provider.getBlock(lastBlock).then((b) => Number(b?.timestamp)),
const [chains, relayersData] = await Promise.all([
this.Aggregator.getChainIds.staticCall(),
this.Aggregator.relayersData.staticCall(newRelayers),
]);
const relayers = relayersData
.map(({ owner, balance: stakeBalance, records, isRegistered }, index) => {
const { ensName, relayerAddress } = uniqueRegisters[index];
.map(
({
ensName,
owner,
balance: stakeBalance,
isRegistered,
isPrior,
tovarishHost,
tovarishChains,
records,
}) => {
const hostnames = records.reduce((acc, record, recordIndex) => {
if (record) {
// tovarish-relayer.relayer.eth
if (recordIndex === records.length - 1) {
tovarishHost = record;
return acc;
}
let tovarishHost = undefined;
const hostnames = records.reduce((acc, record, recordIndex) => {
if (record) {
// tovarish-relayer.relayer.eth
if (recordIndex === records.length - 1) {
tovarishHost = record;
return acc;
acc[Number(chains[recordIndex])] = record;
}
return acc;
}, {} as SubdomainMap);
acc[Number(Object.keys(this.relayerEnsSubdomains)[recordIndex])] = record;
const hasMinBalance = stakeBalance >= MIN_STAKE_BALANCE;
const tovarishNetworks = [
...new Set(
tovarishChains
.split(',')
.map((c) => Number(c))
.filter((c) => c && this.tornadoConfig.chains.includes(c)),
),
];
const preCondition =
(isRegistered && hasMinBalance && Object.keys(hostnames).length) ||
(tovarishHost.length && tovarishNetworks.length);
if (preCondition) {
return {
ensName,
relayerAddress: owner,
isPrior,
isRegistered,
stakeBalance: formatEther(stakeBalance),
hostnames,
tovarishHost,
tovarishNetworks,
} as CachedRelayerInfo;
}
return acc;
}, {} as SubdomainMap);
const hasMinBalance = stakeBalance >= MIN_STAKE_BALANCE;
const preCondition = Object.keys(hostnames).length && isRegistered && hasMinBalance;
if (preCondition) {
return {
ensName,
relayerAddress: owner,
registeredAddress: owner !== relayerAddress ? relayerAddress : undefined,
isRegistered,
stakeBalance: formatEther(stakeBalance),
hostnames,
tovarishHost,
} as CachedRelayerInfo;
}
})
},
)
.filter((r) => r) as CachedRelayerInfo[];
await getTovarishNetworks(this, relayers);
const sortedRelayers = relayers.sort((a, b) => {
// Scoring => isTovarishRelayer => hasMoreStakedBalance
// When it is tovarish relayer, it will compare with staked balance as well
const getPriorityScore = (i: CachedRelayerInfo) => (i.tovarishHost?.length || 0) + (i.isPrior ? 1 : 0);
const allRelayers = [...staticRelayers, ...relayers];
const tovarishRelayers = allRelayers.filter((r) => r.tovarishHost);
const classicRelayers = allRelayers.filter((r) => !r.tovarishHost);
const [aScore, bScore] = [getPriorityScore(a), getPriorityScore(b)];
return {
lastBlock,
timestamp,
relayers: [...tovarishRelayers, ...classicRelayers],
};
}
if (aScore === bScore) {
// Sort by staked balance
const [aBalance, bBalance] = [Number(a.stakeBalance || 0), Number(b.stakeBalance || 0)];
/**
* Handle saving relayers
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveRelayers({ lastBlock, timestamp, relayers }: CachedRelayers) {}
return bBalance - aBalance;
}
/**
* Get cached or latest relayer and save to local
*/
async updateRelayers(): Promise<CachedRelayers> {
// eslint-disable-next-line prefer-const
let { lastBlock, timestamp, relayers, fromCache } = await this.getSavedRelayers();
return bScore - aScore;
});
let shouldSave = fromCache ?? false;
if (!relayers.length || timestamp + this.updateInterval < Math.floor(Date.now() / 1000)) {
console.log('\nUpdating relayers from registry\n');
({ lastBlock, timestamp, relayers } = await this.getLatestRelayers());
shouldSave = true;
}
if (shouldSave) {
await this.saveRelayers({ lastBlock, timestamp, relayers });
}
return { lastBlock, timestamp, relayers };
return sortedRelayers;
}
}

View File

@@ -1,8 +1,6 @@
import { downloadZip } from '../zip';
import { IndexedDB } from '../idb';
import { bytesToBase64, digest } from '../utils';
import { fetchData } from '../providers';
import {
BaseTornadoService,
BaseTornadoServiceConstructor,
@@ -16,7 +14,6 @@ import {
BaseRegistryServiceConstructor,
BaseRevenueService,
BaseRevenueServiceConstructor,
CachedRelayers,
BaseMultiTornadoService,
BaseMultiTornadoServiceConstructor,
} from './base';
@@ -403,7 +400,6 @@ export class DBRegistryService extends BaseRegistryService {
idb: IndexedDB;
zipDigest?: string;
relayerJsonDigest?: string;
constructor(params: DBRegistryServiceConstructor) {
super(params);
@@ -439,78 +435,6 @@ export class DBRegistryService extends BaseRegistryService {
lastBlock,
});
}
async getRelayersFromDB(): Promise<CachedRelayers> {
try {
const allCachedRelayers = await this.idb.getAll<CachedRelayers[]>({
storeName: `relayers_${this.netId}`,
});
if (!allCachedRelayers?.length) {
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
};
}
return allCachedRelayers.slice(-1)[0];
} catch (err) {
console.log('Method getRelayersFromDB has error');
console.log(err);
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
};
}
}
async getRelayersFromCache(): Promise<CachedRelayers> {
const url = `${this.staticUrl}/relayers.json`;
try {
const resp = await fetchData(url, {
method: 'GET',
returnResponse: true,
});
const data = new Uint8Array(await resp.arrayBuffer());
if (this.relayerJsonDigest) {
const hash = 'sha384-' + bytesToBase64(await digest(data));
if (hash !== this.relayerJsonDigest) {
const errMsg = `Invalid digest hash for ${url}, wants ${this.relayerJsonDigest} has ${hash}`;
throw new Error(errMsg);
}
}
return JSON.parse(new TextDecoder().decode(data)) as CachedRelayers;
} catch (err) {
console.log('Method getRelayersFromCache has error');
console.log(err);
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
};
}
}
async saveRelayers(cachedRelayers: CachedRelayers): Promise<void> {
try {
await this.idb.putItem({
data: cachedRelayers,
storeName: `relayers_${this.netId}`,
});
} catch (err) {
console.log('Method saveRelayers has error');
console.log(err);
}
}
}
export interface DBRevenueServiceConstructor extends BaseRevenueServiceConstructor {

View File

@@ -17,7 +17,7 @@ const DUMMY_WITHDRAW_DATA =
export function convertETHToTokenAmount(
amountInWei: BigNumberish,
tokenPriceInWei: BigNumberish,
tokenDecimals: number = 18,
tokenDecimals = 18,
): bigint {
const tokenDecimalsMultiplier = BigInt(10 ** Number(tokenDecimals));
return (BigInt(amountInWei) * tokenDecimalsMultiplier) / BigInt(tokenPriceInWei);

View File

@@ -2,7 +2,7 @@ import { isAddress } from 'ethers';
import { NetId, NetIdType } from './networkConfig';
// https://dev.gas.zip/gas/chain-support/inbound
export const gasZipInbounds: { [key in NetIdType]: string } = {
export const gasZipInbounds: Record<NetIdType, string> = {
[NetId.MAINNET]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.BSC]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
[NetId.POLYGON]: '0x391E7C679d29bD940d63be94AD22A25d25b5A604',
@@ -15,7 +15,7 @@ export const gasZipInbounds: { [key in NetIdType]: string } = {
};
// https://dev.gas.zip/gas/chain-support/outbound
export const gasZipID: { [key in NetIdType]: number } = {
export const gasZipID: Record<NetIdType, number> = {
[NetId.MAINNET]: 255,
[NetId.BSC]: 14,
[NetId.POLYGON]: 17,

View File

@@ -49,9 +49,7 @@ export interface queryGraphParams {
graphApi: string;
subgraphName: string;
query: string;
variables?: {
[key: string]: string | number;
};
variables?: Record<string, string | number>;
fetchDataOptions?: fetchDataOptions;
}
@@ -64,7 +62,8 @@ export async function queryGraph<T>({
}: queryGraphParams): Promise<T> {
const graphUrl = `${graphApi}/subgraphs/name/${subgraphName}`;
const { data, errors } = await fetchData(graphUrl, {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { data, errors } = await fetchData<{ data: T & { _meta: any }; errors: any }>(graphUrl, {
...fetchDataOptions,
method: 'POST',
headers: {
@@ -270,7 +269,6 @@ export async function getAllRegisters({
const events = [];
let lastSyncBlock = fromBlock;
// eslint-disable-next-line no-constant-condition
while (true) {
let {
relayers: result,
@@ -406,7 +404,6 @@ export async function getAllDeposits({
const events = [];
let lastSyncBlock = fromBlock;
// eslint-disable-next-line no-constant-condition
while (true) {
let {
deposits: result,
@@ -552,7 +549,6 @@ export async function getAllWithdrawals({
const events = [];
let lastSyncBlock = fromBlock;
// eslint-disable-next-line no-constant-condition
while (true) {
let {
withdrawals: result,
@@ -756,7 +752,6 @@ export async function getAllGraphEchoEvents({
const events = [];
let lastSyncBlock = fromBlock;
// eslint-disable-next-line no-constant-condition
while (true) {
let {
noteAccounts: result,
@@ -888,7 +883,6 @@ export async function getAllEncryptedNotes({
const events = [];
let lastSyncBlock = fromBlock;
// eslint-disable-next-line no-constant-condition
while (true) {
let {
encryptedNotes: result,
@@ -1046,7 +1040,6 @@ export async function getAllGovernanceEvents({
let lastSyncBlock = fromBlock;
// eslint-disable-next-line no-constant-condition
while (true) {
const {
proposals,

View File

@@ -1,6 +1,13 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { openDB, deleteDB, OpenDBCallbacks, IDBPDatabase } from 'idb';
import { getConfig, NetIdType } from './networkConfig';
/* eslint-disable @typescript-eslint/no-explicit-any, import/no-duplicates */
import type * as idb from 'idb';
import type { OpenDBCallbacks, IDBPDatabase } from 'idb';
import type { NetIdType, TornadoConfig } from './networkConfig';
declare global {
interface Window {
idb: typeof idb;
}
}
export const INDEX_DB_ERROR = 'A mutation operation was attempted on a database that did not allow mutations.';
@@ -64,7 +71,7 @@ export class IndexedDB {
return;
}
this.db = await openDB(this.dbName, this.dbVersion, this.options);
this.db = await window?.idb?.openDB(this.dbName, this.dbVersion, this.options);
this.db.addEventListener('onupgradeneeded', async () => {
await this._removeExist();
});
@@ -89,7 +96,7 @@ export class IndexedDB {
}
async _removeExist() {
await deleteDB(this.dbName);
await window?.idb?.deleteDB(this.dbName);
this.dbExists = false;
await this.initDB();
@@ -314,10 +321,10 @@ export class IndexedDB {
/**
* Should check if DB is initialized well
*/
export async function getIndexedDB(netId?: NetIdType) {
export async function getIndexedDB(netId?: NetIdType, tornadoConfig?: TornadoConfig) {
// key-value db for settings
if (!netId) {
const idb = new IndexedDB({ dbName: 'tornado-core' });
if (!netId || !tornadoConfig) {
const idb = new IndexedDB({ dbName: 'tornado-scripts' });
await idb.initDB();
return idb;
}
@@ -363,7 +370,7 @@ export async function getIndexedDB(netId?: NetIdType) {
},
];
const { tokens, nativeCurrency, registryContract, governanceContract } = getConfig(netId);
const { tokens, nativeCurrency, registryContract, governanceContract } = tornadoConfig.getConfig(netId);
const stores = [...defaultState];
@@ -380,17 +387,6 @@ export async function getIndexedDB(netId?: NetIdType) {
],
});
stores.push({
name: `relayers_${netId}`,
keyPath: 'timestamp',
indexes: [
{
name: 'timestamp',
unique: true,
},
],
});
stores.push({
name: `revenue_${netId}`,
keyPath: 'timestamp',

View File

@@ -1,14 +1,37 @@
import { fetchData } from './providers';
import { fetchData, fetchDataOptions } from './providers';
export interface IPResult {
ip: string;
iso?: string;
country?: string;
country_iso?: string;
tor?: boolean;
}
export async function fetchIp(ipEcho: string) {
return (await fetchData(ipEcho, {
export function fetchIp(ipEcho: string, fetchOptions?: fetchDataOptions) {
return fetchData<IPResult>(ipEcho, {
...(fetchOptions || {}),
method: 'GET',
timeout: 30000,
})) as IPResult;
});
}
// 🖕
export interface IPResultFuck {
YourFuckingIPAddress: string;
YourFuckingLocation: string;
YourFuckingHostname: string;
YourFuckingISP: string;
YourFuckingTorExit: boolean;
YourFuckingCity?: string;
YourFuckingCountry: string;
YourFuckingCountryCode: string;
}
export function fetchFuckingIp(ipFuck = 'https://myip.wtf/json', fetchOptions?: fetchDataOptions) {
return fetchData<IPResultFuck>(ipFuck, {
...(fetchOptions || {}),
method: 'GET',
timeout: 30000,
});
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
import { ERC20Permit, ERC20Mock, TORN } from '@tornado/contracts';
import { ERC20Permit, ERC20Mock, TORN } from 'tornado-contracts';
import { MaxUint256, Provider, Signature, Signer, TypedDataField } from 'ethers';
export interface PermitValue {
@@ -22,9 +22,7 @@ export const permit2Address = '0x000000000022D473030F116dDEE9F6B43aC78BA3';
*/
export interface Witness {
witnessTypeName: string;
witnessType: {
[key: string]: TypedDataField[];
};
witnessType: Record<string, TypedDataField[]>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
witness: any;
}

View File

@@ -1,6 +1,4 @@
import type { EventEmitter } from 'stream';
import type { RequestOptions } from 'http';
import crossFetch from 'cross-fetch';
import {
FetchRequest,
JsonRpcApiProvider,
@@ -20,10 +18,13 @@ import {
EnsPlugin,
GasCostPlugin,
FetchCancelSignal,
resolveProperties,
TransactionLike,
FetchUrlFeeDataNetworkPlugin,
FeeData,
} from 'ethers';
import type { RequestInfo, RequestInit, Response, HeadersInit } from 'node-fetch';
// Temporary workaround until @types/node-fetch is compatible with @types/node
import type { AbortSignal as FetchAbortSignal } from 'node-fetch/externals';
import type { Dispatcher, RequestInit, fetch as undiciFetch } from 'undici-types';
import { isNode, sleep } from './utils';
import type { Config, NetIdType } from './networkConfig';
@@ -36,73 +37,34 @@ declare global {
// Update this for every Tor Browser release
export const defaultUserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0';
export type nodeFetch = (url: RequestInfo, init?: RequestInit) => Promise<Response>;
export type DispatcherFunc = (retry?: number) => Dispatcher;
export type fetchDataOptions = RequestInit & {
export interface fetchDataOptions extends Omit<RequestInit, 'headers'> {
/**
* Overriding RequestInit params
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
headers?: HeadersInit | any;
/**
* Expanding RequestInit params
*/
maxRetry?: number;
retryOn?: number;
userAgent?: string;
timeout?: number;
proxy?: string;
torPort?: number;
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
debug?: Function;
returnResponse?: boolean;
cancelSignal?: FetchCancelSignal;
};
export type NodeAgent = RequestOptions['agent'] | ((parsedUrl: URL) => RequestOptions['agent']);
export function getHttpAgent({
fetchUrl,
proxyUrl,
torPort,
retry,
}: {
fetchUrl: string;
proxyUrl?: string;
torPort?: number;
retry: number;
}): NodeAgent | undefined {
/* eslint-disable @typescript-eslint/no-require-imports */
const { HttpProxyAgent } = require('http-proxy-agent');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { SocksProxyAgent } = require('socks-proxy-agent');
/* eslint-enable @typescript-eslint/no-require-imports */
if (torPort) {
return new SocksProxyAgent(`socks5h://tor${retry}@127.0.0.1:${torPort}`);
}
if (!proxyUrl) {
return;
}
const isHttps = fetchUrl.includes('https://');
if (proxyUrl.includes('socks://') || proxyUrl.includes('socks4://') || proxyUrl.includes('socks5://')) {
return new SocksProxyAgent(proxyUrl);
}
if (proxyUrl.includes('http://') || proxyUrl.includes('https://')) {
if (isHttps) {
return new HttpsProxyAgent(proxyUrl);
}
return new HttpProxyAgent(proxyUrl);
}
dispatcherFunc?: DispatcherFunc;
}
export async function fetchData(url: string, options: fetchDataOptions = {}) {
export async function fetchData<T>(url: string, options: fetchDataOptions = {}): Promise<T> {
const MAX_RETRY = options.maxRetry ?? 3;
const RETRY_ON = options.retryOn ?? 500;
const userAgent = options.userAgent ?? defaultUserAgent;
const fetch = ((globalThis as unknown as { useGlobalFetch?: boolean }).useGlobalFetch
? globalThis.fetch
: crossFetch) as unknown as nodeFetch;
let retry = 0;
let errorObject;
@@ -122,14 +84,17 @@ export async function fetchData(url: string, options: fetchDataOptions = {}) {
options.headers['User-Agent'] = userAgent;
}
if (typeof globalThis.fetch !== 'function') {
throw new Error('Fetch API is not available, use latest browser or nodejs installation!');
}
while (retry < MAX_RETRY + 1) {
let timeout;
if (!options.signal && options.timeout) {
const controller = new AbortController();
// Temporary workaround until @types/node-fetch is compatible with @types/node
options.signal = controller.signal as FetchAbortSignal;
options.signal = controller.signal;
// Define timeout in seconds
timeout = setTimeout(() => {
@@ -149,16 +114,7 @@ export async function fetchData(url: string, options: fetchDataOptions = {}) {
}
}
if (!options.agent && isNode && (options.proxy || options.torPort)) {
options.agent = getHttpAgent({
fetchUrl: url,
proxyUrl: options.proxy,
torPort: options.torPort,
retry,
});
}
if (options.debug && typeof options.debug === 'function') {
if (typeof options.debug === 'function') {
options.debug('request', {
url,
retry,
@@ -168,13 +124,11 @@ export async function fetchData(url: string, options: fetchDataOptions = {}) {
}
try {
const resp = await fetch(url, {
method: options.method,
headers: options.headers,
body: options.body,
redirect: options.redirect,
signal: options.signal,
agent: options.agent,
const dispatcher = options.dispatcherFunc ? options.dispatcherFunc(retry) : options.dispatcher;
const resp = await (globalThis.fetch as unknown as typeof undiciFetch)(url, {
...options,
dispatcher,
});
if (options.debug && typeof options.debug === 'function') {
@@ -187,23 +141,23 @@ export async function fetchData(url: string, options: fetchDataOptions = {}) {
}
if (options.returnResponse) {
return resp;
return resp as T;
}
const contentType = resp.headers.get('content-type');
// If server returns JSON object, parse it and return as an object
if (contentType?.includes('application/json')) {
return await resp.json();
return (await resp.json()) as T;
}
// Else if the server returns text parse it as a string
if (contentType?.includes('text')) {
return await resp.text();
return (await resp.text()) as T;
}
// Return as a response object https://developer.mozilla.org/en-US/docs/Web/API/Response
return resp;
return resp as T;
} catch (error) {
if (timeout) {
clearTimeout(timeout);
@@ -242,9 +196,9 @@ export const fetchGetUrlFunc =
returnResponse: true,
};
const resp = await fetchData(req.url, init);
const resp = await fetchData<Response>(req.url, init);
const headers = {} as { [key in string]: any };
const headers = {} as Record<string, any>;
resp.headers.forEach((value: any, key: string) => {
headers[key.toLowerCase()] = value;
});
@@ -267,20 +221,36 @@ export type getProviderOptions = fetchDataOptions & {
pollingInterval?: number;
};
export const FeeDataNetworkPluginName = new FetchUrlFeeDataNetworkPlugin(
'',
() => new Promise((resolve) => resolve(new FeeData())),
).name;
export async function getProvider(rpcUrl: string, fetchOptions?: getProviderOptions): Promise<JsonRpcProvider> {
// Use our own fetchGetUrlFunc to support proxies and retries
const fetchReq = new FetchRequest(rpcUrl);
fetchReq.getUrlFunc = fetchGetUrlFunc(fetchOptions);
const staticNetwork = await new JsonRpcProvider(fetchReq).getNetwork();
const fetchedNetwork = await new JsonRpcProvider(fetchReq).getNetwork();
const chainId = Number(staticNetwork.chainId);
// Audit if we are connected to right network
const chainId = Number(fetchedNetwork.chainId);
if (fetchOptions?.netId && fetchOptions.netId !== chainId) {
const errMsg = `Wrong network for ${rpcUrl}, wants ${fetchOptions.netId} got ${chainId}`;
throw new Error(errMsg);
}
// Clone to new network to exclude polygon gas station plugin
const staticNetwork = new Network(fetchedNetwork.name, fetchedNetwork.chainId);
fetchedNetwork.plugins.forEach((plugin) => {
if (plugin.name !== FeeDataNetworkPluginName) {
staticNetwork.attachPlugin(plugin.clone());
}
});
return new JsonRpcProvider(fetchReq, staticNetwork, {
staticNetwork,
pollingInterval: fetchOptions?.pollingInterval || 1000,
@@ -293,7 +263,7 @@ export function getProviderWithNetId(
config: Config,
fetchOptions?: getProviderOptions,
): JsonRpcProvider {
const { networkName, reverseRecordsContract, pollInterval } = config;
const { networkName, reverseRecordsContract, blockTime } = config;
const hasEns = Boolean(reverseRecordsContract);
const fetchReq = new FetchRequest(rpcUrl);
@@ -306,7 +276,7 @@ export function getProviderWithNetId(
const provider = new JsonRpcProvider(fetchReq, staticNetwork, {
staticNetwork,
pollingInterval: fetchOptions?.pollingInterval || pollInterval * 1000,
pollingInterval: fetchOptions?.pollingInterval || blockTime * 1000,
});
return provider;
@@ -325,11 +295,16 @@ export const populateTransaction = async (
throw new Error(errMsg);
}
const [feeData, nonce] = await Promise.all([
const [chainId, feeData, nonce] = await Promise.all([
tx.chainId || tx.chainId === 0n ? undefined : provider.getNetwork().then((n) => Number(n.chainId)),
tx.maxFeePerGas || tx.gasPrice ? undefined : provider.getFeeData(),
tx.nonce ? undefined : provider.getTransactionCount(signer.address, 'pending'),
tx.nonce || tx.nonce === 0 ? undefined : provider.getTransactionCount(signer.address, 'pending'),
]);
if (chainId) {
tx.chainId = chainId;
}
if (feeData) {
// EIP-1559
if (feeData.maxFeePerGas) {
@@ -350,7 +325,7 @@ export const populateTransaction = async (
}
}
if (nonce) {
if (nonce || nonce === 0) {
tx.nonce = nonce;
}
@@ -373,7 +348,7 @@ export const populateTransaction = async (
}
}
return tx;
return resolveProperties(tx);
};
export interface TornadoWalletOptions {
@@ -414,8 +389,7 @@ export class TornadoWallet extends Wallet {
async populateTransaction(tx: TransactionRequest) {
const txObject = await populateTransaction(this, tx);
this.nonce = Number(txObject.nonce);
return super.populateTransaction(txObject);
return txObject as Promise<TransactionLike<string>>;
}
}
@@ -443,8 +417,7 @@ export class TornadoVoidSigner extends VoidSigner {
async populateTransaction(tx: TransactionRequest) {
const txObject = await populateTransaction(this, tx);
this.nonce = Number(txObject.nonce);
return super.populateTransaction(txObject);
return txObject as Promise<TransactionLike<string>>;
}
}

View File

@@ -1,6 +1,6 @@
import { getAddress, parseEther } from 'ethers';
import { sleep } from './utils';
import { NetId, NetIdType, Config } from './networkConfig';
import type { NetIdType, TornadoConfig } from './networkConfig';
import { fetchData, fetchDataOptions } from './providers';
import { ajv, jobsSchema, jobRequestSchema, getStatusSchema } from './schemas';
import type { snarkProofs } from './websnark';
@@ -28,9 +28,7 @@ export interface RelayerInfo extends RelayerParams {
instances: string[];
stakeBalance?: string;
gasPrice?: number;
ethPrices?: {
[key in string]: string;
};
ethPrices?: Record<string, string>;
currentQueue: number;
tornadoServiceFee: number;
}
@@ -45,24 +43,21 @@ export interface RelayerError {
export interface RelayerStatus {
url: string;
rewardAccount: string;
instances: {
[key in string]: {
instanceAddress: {
[key in string]: string;
};
instances: Record<
string,
{
instanceAddress: Record<string, string>;
tokenAddress?: string;
symbol: string;
decimals: number;
};
};
}
>;
gasPrices?: {
fast: number;
additionalProperties?: number;
};
netId: NetIdType;
ethPrices?: {
[key in string]: string;
};
ethPrices?: Record<string, string>;
tornadoServiceFee: number;
latestBlock?: number;
version: string;
@@ -149,13 +144,12 @@ export function getWeightRandom(weightsScores: bigint[], random: bigint) {
return Math.floor(Math.random() * weightsScores.length);
}
export interface RelayerInstanceList {
[key: string]: {
instanceAddress: {
[key: string]: string;
};
};
}
export type RelayerInstanceList = Record<
string,
{
instanceAddress: Record<string, string>;
}
>;
export function getSupportedInstances(instanceList: RelayerInstanceList) {
const rawList = Object.values(instanceList)
@@ -180,35 +174,31 @@ export function pickWeightedRandomRelayer(relayers: RelayerInfo[]) {
}
export interface RelayerClientConstructor {
netId: NetIdType;
config: Config;
tornadoConfig: TornadoConfig;
fetchDataOptions?: fetchDataOptions;
}
export class RelayerClient {
netId: NetIdType;
config: Config;
tornadoConfig: TornadoConfig;
selectedRelayer?: RelayerInfo;
fetchDataOptions?: fetchDataOptions;
tovarish: boolean;
constructor({ netId, config, fetchDataOptions }: RelayerClientConstructor) {
this.netId = netId;
this.config = config;
constructor({ tornadoConfig, fetchDataOptions }: RelayerClientConstructor) {
this.tornadoConfig = tornadoConfig;
this.fetchDataOptions = fetchDataOptions;
this.tovarish = false;
}
async askRelayerStatus({
netId,
hostname,
url,
relayerAddress,
}: {
netId: NetIdType;
hostname?: string;
// optional url if entered manually
url?: string;
// relayerAddress from registry contract to prevent cheating
relayerAddress?: string;
}): Promise<RelayerStatus> {
if (!url && hostname) {
url = `https://${!hostname.endsWith('/') ? hostname + '/' : hostname}`;
@@ -218,16 +208,18 @@ export class RelayerClient {
url = '';
}
const rawStatus = (await fetchData(`${url}status`, {
const rawStatus = await fetchData<RelayerStatus>(`${url}status`, {
...this.fetchDataOptions,
headers: {
'Content-Type': 'application/json, application/x-www-form-urlencoded',
},
timeout: 30000,
maxRetry: this.fetchDataOptions?.torPort ? 2 : 0,
})) as object;
maxRetry: this.fetchDataOptions?.dispatcher ? 2 : 0,
});
const statusValidator = ajv.compile(getStatusSchema(this.netId, this.config, this.tovarish));
const config = this.tornadoConfig.getConfig(netId);
const statusValidator = ajv.compile(getStatusSchema(config, this.tovarish));
if (!statusValidator(rawStatus)) {
throw new Error('Invalid status schema');
@@ -242,19 +234,21 @@ export class RelayerClient {
throw new Error('Withdrawal queue is overloaded');
}
if (status.netId !== this.netId) {
if (status.netId !== netId) {
throw new Error('This relayer serves a different network');
}
/**
if (relayerAddress && this.netId === NetId.MAINNET && status.rewardAccount !== relayerAddress) {
throw new Error('The Relayer reward address must match registered address');
}
**/
return status;
}
async filterRelayer(relayer: CachedRelayerInfo): Promise<RelayerInfo | RelayerError | undefined> {
const hostname = relayer.hostnames[this.netId];
async filterRelayer(netId: NetIdType, relayer: CachedRelayerInfo): Promise<RelayerInfo | RelayerError | undefined> {
const hostname = relayer.hostnames[netId];
const { ensName, relayerAddress } = relayer;
if (!hostname) {
@@ -263,8 +257,8 @@ export class RelayerClient {
try {
const status = await this.askRelayerStatus({
netId,
hostname,
relayerAddress,
});
return {
@@ -292,13 +286,16 @@ export class RelayerClient {
}
}
async getValidRelayers(relayers: CachedRelayerInfo[]): Promise<{
async getValidRelayers(
netId: NetIdType,
relayers: CachedRelayerInfo[],
): Promise<{
validRelayers: RelayerInfo[];
invalidRelayers: RelayerError[];
}> {
const invalidRelayers: RelayerError[] = [];
const validRelayers = (await Promise.all(relayers.map((relayer) => this.filterRelayer(relayer)))).filter(
const validRelayers = (await Promise.all(relayers.map((relayer) => this.filterRelayer(netId, relayer)))).filter(
(r) => {
if (!r) {
return false;
@@ -331,7 +328,7 @@ export class RelayerClient {
* Request new job
*/
const withdrawResponse = (await fetchData(`${url}v1/tornadoWithdraw`, {
const withdrawResponse = await fetchData<RelayerTornadoWithdraw>(`${url}v1/tornadoWithdraw`, {
...this.fetchDataOptions,
method: 'POST',
headers: {
@@ -342,7 +339,7 @@ export class RelayerClient {
proof,
args,
}),
})) as RelayerTornadoWithdraw;
});
const { id, error } = withdrawResponse;
@@ -372,7 +369,7 @@ export class RelayerClient {
console.log(`Job submitted: ${jobUrl}\n`);
while (!relayerStatus || !['FAILED', 'CONFIRMED'].includes(relayerStatus)) {
const jobResponse = await fetchData(jobUrl, {
const jobResponse = await fetchData<RelayerTornadoJobs>(jobUrl, {
...this.fetchDataOptions,
method: 'GET',
headers: {
@@ -391,7 +388,7 @@ export class RelayerClient {
throw new Error(errMsg);
}
const { status, txHash, confirmations, failedReason } = jobResponse as unknown as RelayerTornadoJobs;
const { status, txHash, confirmations, failedReason } = jobResponse;
if (relayerStatus !== status) {
if (status === 'FAILED') {

View File

@@ -1,4 +1,4 @@
import { Config, NetId, NetIdType } from '../networkConfig';
import { Config } from '../networkConfig';
import { addressSchemaType, bnSchemaType } from '.';
export interface statusInstanceType {
@@ -6,13 +6,12 @@ export interface statusInstanceType {
properties: {
instanceAddress: {
type: string;
properties: {
[key in string]: typeof addressSchemaType;
};
properties: Record<string, typeof addressSchemaType>;
required: string[];
};
tokenAddress?: typeof addressSchemaType;
symbol?: { enum: string[] };
//symbol?: { enum: string[] };
symbol?: { type: string };
decimals: { enum: number[] };
};
required: string[];
@@ -20,17 +19,13 @@ export interface statusInstanceType {
export interface statusInstancesType {
type: string;
properties: {
[key in string]: statusInstanceType;
};
properties: Record<string, statusInstanceType>;
required: string[];
}
export interface statusEthPricesType {
type: string;
properties: {
[key in string]: typeof bnSchemaType;
};
properties: Record<string, typeof bnSchemaType>;
required?: string[];
}
@@ -41,11 +36,12 @@ export interface statusSchema {
instances?: statusInstancesType;
gasPrices: {
type: string;
properties: {
[key in string]: {
properties: Record<
string,
{
type: string;
};
};
}
>;
required: string[];
};
netId: {
@@ -132,15 +128,23 @@ const statusSchema: statusSchema = {
required: ['rewardAccount', 'instances', 'netId', 'tornadoServiceFee', 'version', 'health', 'currentQueue'],
};
export function getStatusSchema(netId: NetIdType, config: Config, tovarish: boolean) {
const { tokens, optionalTokens, disabledTokens, nativeCurrency } = config;
export function getStatusSchema(config: Config, tovarish: boolean) {
const { tokens, nativeCurrency } = config;
// deep copy schema
const schema = JSON.parse(JSON.stringify(statusSchema)) as statusSchema;
const instances = Object.keys(tokens).reduce(
(acc: statusInstancesType, token) => {
const { instanceAddress, tokenAddress, symbol, decimals, optionalInstances = [] } = tokens[token];
const {
isOptional,
isDisabled,
instanceAddress,
tokenAddress,
symbol,
decimals,
optionalInstances = [],
} = tokens[token];
const amounts = Object.keys(instanceAddress);
const instanceProperties: statusInstanceType = {
@@ -148,18 +152,10 @@ export function getStatusSchema(netId: NetIdType, config: Config, tovarish: bool
properties: {
instanceAddress: {
type: 'object',
properties: amounts.reduce(
(
acc: {
[key in string]: typeof addressSchemaType;
},
cur,
) => {
acc[cur] = addressSchemaType;
return acc;
},
{},
),
properties: amounts.reduce((acc: Record<string, typeof addressSchemaType>, cur) => {
acc[cur] = addressSchemaType;
return acc;
}, {}),
required: amounts.filter((amount) => !optionalInstances.includes(amount)),
},
decimals: { enum: [decimals] },
@@ -173,12 +169,14 @@ export function getStatusSchema(netId: NetIdType, config: Config, tovarish: bool
if (tokenAddress) {
instanceProperties.properties.tokenAddress = addressSchemaType;
}
if (symbol) {
instanceProperties.properties.symbol = { enum: [symbol] };
// instanceProperties.properties.symbol = { enum: [symbol] };
instanceProperties.properties.symbol = { type: 'string' };
}
acc.properties[token] = instanceProperties;
if (!optionalTokens?.includes(token) && !disabledTokens?.includes(token)) {
if (!isOptional && !isDisabled) {
acc.required.push(token);
}
return acc;
@@ -192,18 +190,12 @@ export function getStatusSchema(netId: NetIdType, config: Config, tovarish: bool
schema.properties.instances = instances;
const _tokens = Object.keys(tokens).filter(
(t) => t !== nativeCurrency && !config.optionalTokens?.includes(t) && !config.disabledTokens?.includes(t),
);
if (netId === NetId.MAINNET) {
_tokens.push('torn');
}
const _tokens = instances.required.filter((t) => t !== nativeCurrency);
if (_tokens.length) {
const ethPrices: statusEthPricesType = {
type: 'object',
properties: _tokens.reduce((acc: { [key in string]: typeof bnSchemaType }, token: string) => {
properties: _tokens.reduce((acc: Record<string, typeof bnSchemaType>, token: string) => {
acc[token] = bnSchemaType;
return acc;
}, {}),

View File

@@ -11,7 +11,7 @@ import {
import { fetchData } from './providers';
import { CachedRelayerInfo, MinimalEvents } from './events';
import { ajv, getEventsSchemaValidator, getStatusSchema } from './schemas';
import { enabledChains, getConfig, NetId, NetIdType } from './networkConfig';
import { NetId, NetIdType } from './networkConfig';
// Return no more than 5K events per query
export const MAX_TOVARISH_EVENTS = 5000;
@@ -76,10 +76,11 @@ export class TovarishClient extends RelayerClient {
}
async askRelayerStatus({
netId,
hostname,
url,
relayerAddress,
}: {
netId: NetIdType;
hostname?: string;
// optional url if entered manually
url?: string;
@@ -87,9 +88,9 @@ export class TovarishClient extends RelayerClient {
relayerAddress?: string;
}): Promise<TovarishStatus> {
const status = (await super.askRelayerStatus({
netId,
hostname,
url,
relayerAddress,
})) as TovarishStatus;
if (!status.version.includes('tovarish')) {
@@ -121,14 +122,14 @@ export class TovarishClient extends RelayerClient {
url = '';
}
const statusArray = (await fetchData(`${url}status`, {
const statusArray = await fetchData<TovarishStatus[]>(`${url}status`, {
...this.fetchDataOptions,
headers: {
'Content-Type': 'application/json, application/x-www-form-urlencoded',
},
timeout: 30000,
maxRetry: this.fetchDataOptions?.torPort ? 2 : 0,
})) as object;
maxRetry: this.fetchDataOptions?.dispatcher ? 2 : 0,
});
if (!Array.isArray(statusArray)) {
return [];
@@ -137,33 +138,25 @@ export class TovarishClient extends RelayerClient {
const tovarishStatus: TovarishStatus[] = [];
for (const rawStatus of statusArray) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const netId = (rawStatus as any).netId as NetIdType;
const config = getConfig(netId);
const netId = rawStatus?.netId as NetIdType;
const config = this.tornadoConfig.getConfig(netId);
const statusValidator = ajv.compile(
getStatusSchema(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(rawStatus as any).netId,
config,
this.tovarish,
),
);
const statusValidator = ajv.compile(getStatusSchema(config, this.tovarish));
if (!statusValidator) {
if (!statusValidator(rawStatus)) {
continue;
}
const status = {
...rawStatus,
url: `${url}${netId}/`,
} as TovarishStatus;
};
if (status.currentQueue > 5) {
throw new Error('Withdrawal queue is overloaded');
}
if (!enabledChains.includes(status.netId)) {
if (!this.tornadoConfig.chains.includes(status.netId)) {
throw new Error('This relayer serves a different network');
}
@@ -181,17 +174,21 @@ export class TovarishClient extends RelayerClient {
return tovarishStatus;
}
async filterRelayer(relayer: CachedRelayerInfo): Promise<TovarishInfo | RelayerError | undefined> {
async filterRelayer(
netId: NetIdType,
relayer: CachedRelayerInfo,
): Promise<TovarishInfo | RelayerError | undefined> {
const { ensName, relayerAddress, tovarishHost, tovarishNetworks } = relayer;
if (!tovarishHost || !tovarishNetworks?.includes(this.netId)) {
if (!tovarishHost || !tovarishNetworks?.includes(netId)) {
return;
}
const hostname = `${tovarishHost}/${this.netId}`;
const hostname = `${tovarishHost}/${netId}`;
try {
const status = await this.askRelayerStatus({
netId,
hostname,
relayerAddress,
});
@@ -228,13 +225,16 @@ export class TovarishClient extends RelayerClient {
}
}
async getValidRelayers(relayers: CachedRelayerInfo[]): Promise<{
async getValidRelayers(
netId: NetIdType,
relayers: CachedRelayerInfo[],
): Promise<{
validRelayers: TovarishInfo[];
invalidRelayers: RelayerError[];
}> {
const invalidRelayers: RelayerError[] = [];
const validRelayers = (await Promise.all(relayers.map((relayer) => this.filterRelayer(relayer)))).filter(
const validRelayers = (await Promise.all(relayers.map((relayer) => this.filterRelayer(netId, relayer)))).filter(
(r) => {
if (!r) {
return false;
@@ -327,7 +327,6 @@ export class TovarishClient extends RelayerClient {
const events = [];
let lastSyncBlock = fromBlock;
// eslint-disable-next-line no-constant-condition
while (true) {
// eslint-disable-next-line prefer-const
let { events: fetchedEvents, lastSyncBlock: currentBlock } = (await fetchData(url, {

View File

@@ -112,7 +112,7 @@ export function leInt2Buff(bigint: bnInput | bigint) {
return Uint8Array.from(new BN(bigint as bnInput).toArray('le', 31));
}
// Inherited from tornado-core and tornado-cli
// Inherited from tornado-scripts and tornado-cli
export function toFixedHex(numberish: BigNumberish, length = 32) {
return (
'0x' +
@@ -122,17 +122,17 @@ export function toFixedHex(numberish: BigNumberish, length = 32) {
);
}
export function toFixedLength(string: string, length: number = 32) {
export function toFixedLength(string: string, length = 32) {
string = string.replace('0x', '');
return '0x' + string.padStart(length * 2, '0');
}
// Random BigInt in a range of bytes
export function rBigInt(nbytes: number = 31) {
export function rBigInt(nbytes = 31) {
return bytesToBN(crypto.getRandomValues(new Uint8Array(nbytes)));
}
export function rHex(nbytes: number = 32) {
export function rHex(nbytes = 32) {
return bytesToHex(crypto.getRandomValues(new Uint8Array(nbytes)));
}
@@ -142,7 +142,7 @@ export function bigIntReplacer(key: any, value: any) {
return typeof value === 'bigint' ? value.toString() : value;
}
export function substring(str: string, length: number = 10) {
export function substring(str: string, length = 10) {
if (str.length < length * 2) {
return str;
}
@@ -150,11 +150,11 @@ export function substring(str: string, length: number = 10) {
return `${str.substring(0, length)}...${str.substring(str.length - length)}`;
}
export async function digest(bytes: Uint8Array, algo: string = 'SHA-384') {
export async function digest(bytes: Uint8Array, algo = 'SHA-384') {
return new Uint8Array(await crypto.subtle.digest(algo, bytes));
}
export function numberFormatter(num: string | number | bigint, digits: number = 3): string {
export function numberFormatter(num: string | number | bigint, digits = 3): string {
const lookup = [
{ value: 1, symbol: '' },
{ value: 1e3, symbol: 'K' },

View File

@@ -1,5 +1,15 @@
import { zip, unzip, AsyncZippable, Unzipped, ZipAttributes } from 'fflate';
import { fetchData } from './providers';
import {
zip,
unzip,
AsyncZippable,
Unzipped,
ZipAttributes,
zlib,
unzlib,
AsyncZlibOptions,
AsyncUnzlibOptions,
} from 'fflate';
import { fetchData, fetchDataOptions } from './providers';
import { bytesToBase64, digest } from './utils';
export function zipAsync(file: AsyncZippable, options?: ZipAttributes): Promise<Uint8Array> {
@@ -26,20 +36,47 @@ export function unzipAsync(data: Uint8Array): Promise<Unzipped> {
});
}
export function zlibAsync(data: Uint8Array, options?: AsyncZlibOptions): Promise<Uint8Array> {
return new Promise((res, rej) => {
zlib(data, { ...(options || {}) }, (err, data) => {
if (err) {
rej(err);
return;
}
res(data);
});
});
}
export function unzlibAsync(data: Uint8Array, options?: AsyncUnzlibOptions): Promise<Uint8Array> {
return new Promise((res, rej) => {
unzlib(data, { ...(options || {}) }, (err, data) => {
if (err) {
rej(err);
return;
}
res(data);
});
});
}
export async function downloadZip<T>({
staticUrl = '',
zipName,
zipDigest,
parseJson = true,
fetchOptions,
}: {
staticUrl?: string;
zipName: string;
zipDigest?: string;
parseJson?: boolean;
fetchOptions?: fetchDataOptions;
}): Promise<T> {
const url = `${staticUrl}/${zipName}.zip`;
const resp = (await fetchData(url, {
...(fetchOptions || {}),
method: 'GET',
returnResponse: true,
})) as Response;

View File

@@ -4,7 +4,7 @@ import { ethers } from 'hardhat';
import { expect } from 'chai';
import { formatEther } from 'ethers';
import { ETHTornado__factory, Verifier__factory } from '@tornado/contracts';
import { ETHTornado__factory, Verifier__factory } from 'tornado-contracts';
import { Deposit, deployHasher } from '../src';
const { getSigners } = ethers;

View File

@@ -3,5 +3,7 @@
"compilerOptions": {
"rootDir": "./src"
},
"include": ["./src/**/*"],
"exclude": ["./test/**/*"],
"files": []
}

View File

@@ -110,6 +110,9 @@
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["./src/**/*"],
"include": [
"./src/**/*",
"./test/**/*"
],
"files": ["./hardhat.config.ts"]
}

View File

@@ -1,15 +1,24 @@
const { BannerPlugin } = require('webpack');
const { BannerPlugin, ProvidePlugin } = require('webpack');
const path = require('path');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
const esbuildLoader = {
test: /\.ts?$/,
loader: 'esbuild-loader',
options: {
loader: 'ts',
target: 'es2022',
const commonRules = [
{
test: /\.ts?$/,
loader: 'esbuild-loader',
options: {
loader: 'ts',
target: 'es2022',
}
},
// Disable strict dependency resolution for ESM modules, so that polyfill plugin can handle the rest
{
test: /\.m?js$/,
resolve: {
fullySpecified: false
}
}
}
]
const commonAlias = {
fs: false,
@@ -26,7 +35,7 @@ module.exports = [
{
mode: 'production',
module: {
rules: [esbuildLoader]
rules: [...commonRules]
},
entry: './src/index.ts',
output: {
@@ -37,11 +46,18 @@ module.exports = [
},
plugins: [
new NodePolyfillPlugin(),
new ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
}),
],
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias: {
...commonAlias,
},
fallback: {
'process/browser': require.resolve('process/browser'),
}
},
optimization: {
@@ -51,7 +67,7 @@ module.exports = [
{
mode: 'production',
module: {
rules: [esbuildLoader]
rules: [...commonRules]
},
entry: './src/index.ts',
output: {
@@ -62,18 +78,25 @@ module.exports = [
},
plugins: [
new NodePolyfillPlugin(),
new ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
}),
],
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias: {
...commonAlias,
},
fallback: {
'process/browser': require.resolve('process/browser'),
}
},
},
{
mode: 'production',
module: {
rules: [esbuildLoader]
rules: [...commonRules]
},
entry: './src/merkleTreeWorker.ts',
output: {
@@ -83,6 +106,10 @@ module.exports = [
},
plugins: [
new NodePolyfillPlugin(),
new ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
}),
new BannerPlugin({
banner: 'globalThis.process = { browser: true, env: {}, };\n',
raw: true,
@@ -92,6 +119,9 @@ module.exports = [
extensions: ['.tsx', '.ts', '.js'],
alias: {
...commonAlias,
},
fallback: {
'process/browser': require.resolve('process/browser'),
}
},
optimization: {
@@ -101,7 +131,7 @@ module.exports = [
{
mode: 'production',
module: {
rules: [esbuildLoader]
rules: [...commonRules]
},
entry: './src/merkleTreeWorker.ts',
output: {
@@ -111,6 +141,10 @@ module.exports = [
},
plugins: [
new NodePolyfillPlugin(),
new ProvidePlugin({
process: 'process/browser',
Buffer: ['buffer', 'Buffer']
}),
new BannerPlugin({
banner: 'globalThis.process = { browser: true, env: {}, };',
raw: true,
@@ -120,13 +154,16 @@ module.exports = [
extensions: ['.tsx', '.ts', '.js'],
alias: {
...commonAlias,
},
fallback: {
'process/browser': require.resolve('process/browser'),
}
},
},
{
mode: 'production',
module: {
rules: [esbuildLoader]
rules: [...commonRules]
},
entry: './src/contracts.ts',
output: {
@@ -151,7 +188,7 @@ module.exports = [
{
mode: 'production',
module: {
rules: [esbuildLoader]
rules: [...commonRules]
},
entry: './src/contracts.ts',
output: {

3100
yarn.lock

File diff suppressed because it is too large Load Diff