Tornado-CLI 1.0.11-alpha

support tovarish relayer
This commit is contained in:
Tornado Contrib 2024-10-02 20:54:28 +00:00
parent 29236fe7fd
commit 75bb730713
Signed by: tornadocontrib
GPG Key ID: 60B4DF1A076C64B1
10 changed files with 2400 additions and 715 deletions

2570
dist/cli.js vendored

File diff suppressed because one or more lines are too long

20
dist/program.d.ts vendored

@ -1,13 +1,14 @@
import 'dotenv/config'; import 'dotenv/config';
import { Command } from 'commander'; import { Command } from 'commander';
import { JsonRpcProvider, Provider, TransactionLike, Wallet, VoidSigner } from 'ethers'; import { JsonRpcProvider, Provider, TransactionLike, Wallet, VoidSigner } from 'ethers';
import { getProviderOptions, TornadoWallet, TornadoVoidSigner, RelayerInfo, RelayerError, RelayerClient, fetchDataOptions, NetIdType, Config } from '@tornado/core'; import { getProviderOptions, TornadoWallet, TornadoVoidSigner, RelayerInfo, RelayerError, RelayerClient, fetchDataOptions, NetIdType, Config, TovarishClient, TovarishInfo } from '@tornado/core';
export type commonProgramOptions = { export type commonProgramOptions = {
rpc?: string; rpc?: string;
ethRpc?: string; ethRpc?: string;
graph?: string; graph?: string;
ethGraph?: string; ethGraph?: string;
disableGraph?: boolean; disableGraph?: boolean;
disableTovarish?: boolean;
accountKey?: string; accountKey?: string;
relayer?: string; relayer?: string;
walletWithdrawal?: boolean; walletWithdrawal?: boolean;
@ -40,13 +41,22 @@ export declare function getProgramRelayer({ options, fetchDataOptions, netId, }:
fetchDataOptions?: fetchDataOptions; fetchDataOptions?: fetchDataOptions;
netId: NetIdType; netId: NetIdType;
}): Promise<{ }): Promise<{
validRelayers?: RelayerInfo[]; validRelayers: RelayerInfo[];
invalidRelayers?: RelayerError[]; invalidRelayers: RelayerError[];
relayerClient?: RelayerClient; relayerClient: RelayerClient;
}>;
export declare function getTovarishRelayer({ options, fetchDataOptions, netId, }: {
options: commonProgramOptions;
fetchDataOptions?: fetchDataOptions;
netId: NetIdType;
}): Promise<{
validRelayers: TovarishInfo[];
invalidRelayers: RelayerError[];
relayerClient: TovarishClient;
}>; }>;
export declare function programSendTransaction({ signer, options, populatedTransaction, }: { export declare function programSendTransaction({ signer, options, populatedTransaction, }: {
signer: VoidSigner | Wallet; signer: VoidSigner | Wallet;
options: commonProgramOptions; options: commonProgramOptions;
populatedTransaction: TransactionLike; populatedTransaction: TransactionLike;
}): Promise<void>; }): Promise<import("ethers").TransactionResponse | undefined>;
export declare function tornadoProgram(): Command; export declare function tornadoProgram(): Command;

@ -17,10 +17,9 @@ export declare function saveUserFile({ fileName, userDirectory, dataString, }: {
userDirectory: string; userDirectory: string;
dataString: string; dataString: string;
}): Promise<void>; }): Promise<void>;
export declare function loadSavedEvents<T extends MinimalEvents>({ name, userDirectory, deployedBlock, }: { export declare function loadSavedEvents<T extends MinimalEvents>({ name, userDirectory, }: {
name: string; name: string;
userDirectory: string; userDirectory: string;
deployedBlock: number;
}): Promise<BaseEvents<T>>; }): Promise<BaseEvents<T>>;
export declare function download({ name, cacheDirectory }: { export declare function download({ name, cacheDirectory }: {
name: string; name: string;

@ -14,7 +14,7 @@ export declare class NodeTornadoService extends BaseTornadoService {
nativeCurrency: string; nativeCurrency: string;
merkleTreeService?: MerkleTreeService; merkleTreeService?: MerkleTreeService;
treeCache?: TreeCache; treeCache?: TreeCache;
constructor({ netId, provider, graphApi, subgraphName, Tornado, type, amount, currency, deployedBlock, fetchDataOptions, cacheDirectory, userDirectory, nativeCurrency, merkleTreeService, treeCache, }: NodeTornadoServiceConstructor); constructor({ netId, provider, graphApi, subgraphName, Tornado, type, amount, currency, deployedBlock, fetchDataOptions, tovarishClient, cacheDirectory, userDirectory, nativeCurrency, merkleTreeService, treeCache, }: NodeTornadoServiceConstructor);
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void; updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void; updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
@ -30,7 +30,7 @@ export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & {
export declare class NodeEchoService extends BaseEchoService { export declare class NodeEchoService extends BaseEchoService {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor({ netId, provider, graphApi, subgraphName, Echoer, deployedBlock, fetchDataOptions, cacheDirectory, userDirectory, }: NodeEchoServiceConstructor); constructor({ netId, provider, graphApi, subgraphName, Echoer, deployedBlock, fetchDataOptions, tovarishClient, cacheDirectory, userDirectory, }: NodeEchoServiceConstructor);
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<EchoEvents>>; getEventsFromDB(): Promise<BaseEvents<EchoEvents>>;
@ -44,7 +44,7 @@ export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceCons
export declare class NodeEncryptedNotesService extends BaseEncryptedNotesService { export declare class NodeEncryptedNotesService extends BaseEncryptedNotesService {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor({ netId, provider, graphApi, subgraphName, Router, deployedBlock, fetchDataOptions, cacheDirectory, userDirectory, }: NodeEncryptedNotesServiceConstructor); constructor({ netId, provider, graphApi, subgraphName, Router, deployedBlock, fetchDataOptions, tovarishClient, cacheDirectory, userDirectory, }: NodeEncryptedNotesServiceConstructor);
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<EncryptedNotesEvents>>; getEventsFromDB(): Promise<BaseEvents<EncryptedNotesEvents>>;
@ -58,7 +58,7 @@ export type NodeGovernanceServiceConstructor = BaseGovernanceServiceConstructor
export declare class NodeGovernanceService extends BaseGovernanceService { export declare class NodeGovernanceService extends BaseGovernanceService {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor({ netId, provider, graphApi, subgraphName, Governance, deployedBlock, fetchDataOptions, cacheDirectory, userDirectory, }: NodeGovernanceServiceConstructor); constructor({ netId, provider, graphApi, subgraphName, Governance, deployedBlock, fetchDataOptions, tovarishClient, cacheDirectory, userDirectory, }: NodeGovernanceServiceConstructor);
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void; updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
@ -73,7 +73,7 @@ export type NodeRegistryServiceConstructor = BaseRegistryServiceConstructor & {
export declare class NodeRegistryService extends BaseRegistryService { export declare class NodeRegistryService extends BaseRegistryService {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor({ netId, provider, graphApi, subgraphName, RelayerRegistry, Aggregator, relayerEnsSubdomains, deployedBlock, fetchDataOptions, cacheDirectory, userDirectory, }: NodeRegistryServiceConstructor); constructor({ netId, provider, graphApi, subgraphName, RelayerRegistry, Aggregator, relayerEnsSubdomains, deployedBlock, fetchDataOptions, tovarishClient, cacheDirectory, userDirectory, }: NodeRegistryServiceConstructor);
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<RegistersEvents>>; getEventsFromDB(): Promise<BaseEvents<RegistersEvents>>;
@ -81,5 +81,5 @@ export declare class NodeRegistryService extends BaseRegistryService {
saveEvents({ events, lastBlock }: BaseEvents<RegistersEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<RegistersEvents>): Promise<void>;
getRelayersFromDB(): Promise<CachedRelayers>; getRelayersFromDB(): Promise<CachedRelayers>;
getRelayersFromCache(): Promise<CachedRelayers>; getRelayersFromCache(): Promise<CachedRelayers>;
saveRelayers({ timestamp, relayers }: CachedRelayers): Promise<void>; saveRelayers({ lastBlock, timestamp, relayers }: CachedRelayers): Promise<void>;
} }

@ -1,6 +1,6 @@
{ {
"name": "@tornado/cli", "name": "@tornado/cli",
"version": "1.0.10-alpha", "version": "1.0.11-alpha",
"description": "Modern Toolsets for Privacy Pools on Ethereum", "description": "Modern Toolsets for Privacy Pools on Ethereum",
"main": "./dist/cli.js", "main": "./dist/cli.js",
"types": "./dist/cli.d.ts", "types": "./dist/cli.d.ts",
@ -50,12 +50,12 @@
"dependencies": {}, "dependencies": {},
"optionalDependencies": {}, "optionalDependencies": {},
"devDependencies": { "devDependencies": {
"@colors/colors": "1.5.0", "@colors/colors": "^1.6.0",
"@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#c155649719d121fe50799420750f531eb50589ce", "@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#f73b9ecbff0b106854752388f94c2d0c30e54975",
"@typechain/ethers-v6": "^0.5.1", "@typechain/ethers-v6": "^0.5.1",
"@types/figlet": "^1.5.8", "@types/figlet": "^1.5.8",
"@typescript-eslint/eslint-plugin": "^8.6.0", "@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.6.0", "@typescript-eslint/parser": "^8.7.0",
"bloomfilter.js": "^1.0.2", "bloomfilter.js": "^1.0.2",
"cli-table3": "^0.6.4", "cli-table3": "^0.6.4",
"commander": "^12.0.0", "commander": "^12.0.0",
@ -76,7 +76,7 @@
"tsc": "^2.0.4", "tsc": "^2.0.4",
"typechain": "^8.3.2", "typechain": "^8.3.2",
"typescript": "^5.6.2", "typescript": "^5.6.2",
"webpack": "^5.94.0", "webpack": "^5.95.0",
"webpack-cli": "^5.1.4" "webpack-cli": "^5.1.4"
} }
} }

@ -68,6 +68,8 @@ import {
initGroth16, initGroth16,
getRelayerEnsSubdomains, getRelayerEnsSubdomains,
getActiveTokens, getActiveTokens,
TovarishClient,
TovarishInfo,
} from '@tornado/core'; } from '@tornado/core';
import * as packageJson from '../package.json'; import * as packageJson from '../package.json';
import { import {
@ -120,6 +122,7 @@ export type commonProgramOptions = {
graph?: string; graph?: string;
ethGraph?: string; ethGraph?: string;
disableGraph?: boolean; disableGraph?: boolean;
disableTovarish?: boolean;
accountKey?: string; accountKey?: string;
relayer?: string; relayer?: string;
walletWithdrawal?: boolean; walletWithdrawal?: boolean;
@ -175,6 +178,7 @@ export async function getProgramOptions(options: commonProgramOptions): Promise<
graph: options.graph || (process.env.GRAPH_URL ? parseUrl(process.env.GRAPH_URL) : undefined), graph: options.graph || (process.env.GRAPH_URL ? parseUrl(process.env.GRAPH_URL) : undefined),
ethGraph: options.ethGraph || (process.env.ETHGRAPH_URL ? parseUrl(process.env.ETHGRAPH_URL) : undefined), ethGraph: options.ethGraph || (process.env.ETHGRAPH_URL ? parseUrl(process.env.ETHGRAPH_URL) : undefined),
disableGraph: Boolean(options.disableGraph) || (process.env.DISABLE_GRAPH === 'true' ? true : undefined), disableGraph: Boolean(options.disableGraph) || (process.env.DISABLE_GRAPH === 'true' ? true : undefined),
disableTovarish: Boolean(options.disableTovarish) || (process.env.DISABLE_TOVARISH === 'true' ? true : undefined),
accountKey: options.accountKey || (process.env.ACCOUNT_KEY ? parseRecoveryKey(process.env.ACCOUNT_KEY) : undefined), accountKey: options.accountKey || (process.env.ACCOUNT_KEY ? parseRecoveryKey(process.env.ACCOUNT_KEY) : undefined),
relayer: options.relayer || (process.env.RELAYER ? parseRelayer(process.env.RELAYER) : undefined), relayer: options.relayer || (process.env.RELAYER ? parseRelayer(process.env.RELAYER) : undefined),
walletWithdrawal: walletWithdrawal:
@ -280,9 +284,9 @@ export async function getProgramRelayer({
fetchDataOptions?: fetchDataOptions; fetchDataOptions?: fetchDataOptions;
netId: NetIdType; netId: NetIdType;
}): Promise<{ }): Promise<{
validRelayers?: RelayerInfo[]; validRelayers: RelayerInfo[];
invalidRelayers?: RelayerError[]; invalidRelayers: RelayerError[];
relayerClient?: RelayerClient; relayerClient: RelayerClient;
}> { }> {
const { ethRpc, ethGraph, relayer, disableGraph } = options; const { ethRpc, ethGraph, relayer, disableGraph } = options;
@ -333,11 +337,11 @@ export async function getProgramRelayer({
// If user provided relayer is a full working URL // If user provided relayer is a full working URL
if (relayer && relayer.startsWith('http')) { if (relayer && relayer.startsWith('http')) {
const relayerStatus = await relayerClient.askRelayerStatus({ const relayerStatus = await relayerClient.askRelayerStatus({
hostname: new URL(relayer).hostname, url: relayer,
}); });
relayerClient.selectedRelayer = { relayerClient.selectedRelayer = {
ensName: new URL(relayer).hostname, ensName: new URL(relayerStatus.url).hostname,
relayerAddress: getAddress(relayerStatus.rewardAccount), relayerAddress: getAddress(relayerStatus.rewardAccount),
netId: relayerStatus.netId, netId: relayerStatus.netId,
url: relayerStatus.url, url: relayerStatus.url,
@ -391,6 +395,132 @@ export async function getProgramRelayer({
}; };
} }
export async function getTovarishRelayer({
options,
fetchDataOptions,
netId,
}: {
options: commonProgramOptions;
fetchDataOptions?: fetchDataOptions;
netId: NetIdType;
}): Promise<{
validRelayers: TovarishInfo[];
invalidRelayers: RelayerError[];
relayerClient: TovarishClient;
}> {
const { ethRpc, ethGraph, relayer, disableGraph } = options;
const netConfig = getConfig(netId);
const ethConfig = getConfig(RELAYER_NETWORK);
const {
aggregatorContract,
registryContract,
registrySubgraph,
constants: { REGISTRY_BLOCK },
} = ethConfig;
const provider = await getProgramProvider(ethRpc, {
netId: RELAYER_NETWORK,
...fetchDataOptions,
});
const graphApi = getProgramGraphAPI(
{
disableGraph,
graph: ethGraph,
},
ethConfig,
);
const registryService = new NodeRegistryService({
netId: RELAYER_NETWORK,
provider,
graphApi,
subgraphName: registrySubgraph,
RelayerRegistry: RelayerRegistry__factory.connect(registryContract as string, provider),
Aggregator: Aggregator__factory.connect(aggregatorContract as string, provider),
relayerEnsSubdomains: getRelayerEnsSubdomains(),
deployedBlock: REGISTRY_BLOCK,
fetchDataOptions,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
});
const relayerClient = new TovarishClient({
netId,
config: netConfig,
fetchDataOptions,
});
// If user provided relayer is a full working URL
if (relayer && relayer.startsWith('http')) {
const relayerStatus = await relayerClient.askRelayerStatus({
url: relayer,
});
relayerClient.selectedRelayer = {
ensName: new URL(relayerStatus.url).hostname,
relayerAddress: getAddress(relayerStatus.rewardAccount),
netId: relayerStatus.netId,
url: relayerStatus.url,
hostname: new URL(relayerStatus.url).hostname,
rewardAccount: getAddress(relayerStatus.rewardAccount),
instances: getSupportedInstances(relayerStatus.instances),
gasPrice: relayerStatus.gasPrices?.fast,
ethPrices: relayerStatus.ethPrices,
currentQueue: relayerStatus.currentQueue,
tornadoServiceFee: relayerStatus.tornadoServiceFee,
latestBlock: Number(relayerStatus.latestBlock),
latestBalance: relayerStatus.latestBalance,
version: relayerStatus.version,
events: relayerStatus.events,
syncStatus: relayerStatus.syncStatus,
};
return {
validRelayers: [relayerClient.selectedRelayer],
invalidRelayers: [],
relayerClient,
};
}
console.log('\nGetting valid tovarish relayers\n');
const { validRelayers, invalidRelayers } = await relayerClient.getValidRelayers(
(await registryService.getRelayersFromCache()).relayers,
);
// If user has provided either a hostname or ensname of known registered relayer
const foundRelayer = relayer
? validRelayers.find((r) => r.hostname.includes(relayer) || r.ensName.includes(relayer))
: undefined;
if (foundRelayer) {
relayerClient.selectedRelayer = foundRelayer;
return {
validRelayers: [foundRelayer],
invalidRelayers: [],
relayerClient,
};
}
if (validRelayers.length) {
relayerClient.selectedRelayer = validRelayers[0];
} else {
const errMsg = `Working Tovarish Relayer for net ${netId} not found, you can try with legacy relayer with DISABLE_TOVARISH='true' env and try again`;
throw new Error(errMsg);
}
return {
validRelayers,
invalidRelayers,
relayerClient,
};
}
export async function programSendTransaction({ export async function programSendTransaction({
signer, signer,
options, options,
@ -446,9 +576,11 @@ export async function programSendTransaction({
await promptConfirmation(options.nonInteractive); await promptConfirmation(options.nonInteractive);
const { hash } = await signer.sendTransaction(txObject); const resp = await signer.sendTransaction(txObject);
console.log(`Sent transaction ${hash}\n`); console.log(`Sent transaction ${resp.hash}\n`);
return resp;
} }
export function tornadoProgram() { export function tornadoProgram() {
@ -608,7 +740,7 @@ export function tornadoProgram() {
if (!isEth && denomination > tokenApprovals) { if (!isEth && denomination > tokenApprovals) {
// token approval // token approval
await programSendTransaction({ const resp = await programSendTransaction({
signer, signer,
options, options,
populatedTransaction: await (Token as ERC20).approve.populateTransaction(routerContract, MaxUint256), populatedTransaction: await (Token as ERC20).approve.populateTransaction(routerContract, MaxUint256),
@ -620,6 +752,9 @@ export function tornadoProgram() {
'Signer can not sign or broadcast transactions, please send the token approve transaction first and try again.\n', 'Signer can not sign or broadcast transactions, please send the token approve transaction first and try again.\n',
); );
process.exit(0); process.exit(0);
} else {
// Wait until the approval tx is confirmed
await resp?.wait();
} }
} }
@ -785,7 +920,7 @@ export function tornadoProgram() {
.action( .action(
async (note: string, recipient: string, ethPurchase: number | undefined, cmdOptions: commonProgramOptions) => { async (note: string, recipient: string, ethPurchase: number | undefined, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions); const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc, walletWithdrawal } = options; const { rpc, walletWithdrawal, disableTovarish } = options;
// Prepare groth16 in advance // Prepare groth16 in advance
initGroth16(); initGroth16();
@ -825,6 +960,24 @@ export function tornadoProgram() {
...fetchDataOptions, ...fetchDataOptions,
}); });
const { relayerClient } = disableTovarish
? await getProgramRelayer({
options,
fetchDataOptions,
netId,
})
: await getTovarishRelayer({
options,
fetchDataOptions,
netId,
});
const tovarishClient = relayerClient.tovarish ? (relayerClient as TovarishClient) : undefined;
if (tovarishClient) {
console.log(`\nConnected with Tovarish Relayer ${tovarishClient.selectedRelayer?.url}\n`);
}
const signer = getProgramSigner({ const signer = getProgramSigner({
options, options,
provider, provider,
@ -863,6 +1016,7 @@ export function tornadoProgram() {
currency, currency,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
nativeCurrency, nativeCurrency,
@ -941,17 +1095,10 @@ export function tornadoProgram() {
throw new Error('Note is already spent'); throw new Error('Note is already spent');
} }
const [circuit, provingKey, tree, relayerClient, l1Fee, tokenPriceInWei, feeData] = await Promise.all([ const [circuit, provingKey, tree, l1Fee, tokenPriceInWei, feeData] = await Promise.all([
readFile(CIRCUIT_PATH, { encoding: 'utf8' }).then((s) => JSON.parse(s)), readFile(CIRCUIT_PATH, { encoding: 'utf8' }).then((s) => JSON.parse(s)),
readFile(KEY_PATH).then((b) => new Uint8Array(b).buffer), readFile(KEY_PATH).then((b) => new Uint8Array(b).buffer),
depositTreePromise, depositTreePromise,
!walletWithdrawal
? getProgramRelayer({
options,
fetchDataOptions,
netId,
}).then(({ relayerClient }) => relayerClient)
: undefined,
tornadoFeeOracle.fetchL1OptimismFee(), tornadoFeeOracle.fetchL1OptimismFee(),
!isEth !isEth
? tokenPriceOracle.fetchPrices([{ tokenAddress: tokenAddress as string, decimals }]).then((p) => p[0]) ? tokenPriceOracle.fetchPrices([{ tokenAddress: tokenAddress as string, decimals }]).then((p) => p[0])
@ -971,7 +1118,7 @@ export function tornadoProgram() {
? feeData.maxFeePerGas + (feeData.maxPriorityFeePerGas || BigInt(0)) ? feeData.maxFeePerGas + (feeData.maxPriorityFeePerGas || BigInt(0))
: (feeData.gasPrice as bigint); : (feeData.gasPrice as bigint);
if (netId === NetId.BSC && gasPrice < parseUnits('3.3', 'gwei')) { if (!walletWithdrawal && !tovarishClient && netId === NetId.BSC) {
gasPrice = parseUnits('3.3', 'gwei'); gasPrice = parseUnits('3.3', 'gwei');
} }
@ -1008,6 +1155,19 @@ export function tornadoProgram() {
isEth, isEth,
}); });
console.log({
gasPrice,
gasLimit,
l1Fee,
denomination,
ethRefund: refund,
tokenPriceInWei,
tokenDecimals: decimals,
relayerFeePercent: tornadoServiceFee,
isEth,
fee,
});
relayer = rewardAccount as string; relayer = rewardAccount as string;
if (fee > denomination) { if (fee > denomination) {
@ -1064,7 +1224,7 @@ export function tornadoProgram() {
withdrawTable.push([{ colSpan: 2, content: 'Withdrawal Info', hAlign: 'center' }]); withdrawTable.push([{ colSpan: 2, content: 'Withdrawal Info', hAlign: 'center' }]);
// withdraw using relayer // withdraw using relayer
if (!walletWithdrawal && relayerClient) { if (!walletWithdrawal && relayerClient?.selectedRelayer) {
withdrawTable.push( withdrawTable.push(
[{ colSpan: 2, content: 'Withdraw', hAlign: 'center' }], [{ colSpan: 2, content: 'Withdraw', hAlign: 'center' }],
['Withdrawal', `${amount} ${currency.toUpperCase()}`], ['Withdrawal', `${amount} ${currency.toUpperCase()}`],
@ -1146,7 +1306,7 @@ export function tornadoProgram() {
.argument('<note>', 'Tornado Cash Deposit Note') .argument('<note>', 'Tornado Cash Deposit Note')
.action(async (note: string, cmdOptions: commonProgramOptions) => { .action(async (note: string, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions); const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options; const { rpc, disableTovarish } = options;
const deposit = await Deposit.parseNote(note); const deposit = await Deposit.parseNote(note);
const { netId, currency, amount, commitmentHex, nullifierHex } = deposit; const { netId, currency, amount, commitmentHex, nullifierHex } = deposit;
@ -1172,6 +1332,14 @@ export function tornadoProgram() {
const graphApi = getProgramGraphAPI(options, config); const graphApi = getProgramGraphAPI(options, config);
const tovarishClient = !disableTovarish
? (await getTovarishRelayer({ options, fetchDataOptions, netId })).relayerClient
: undefined;
if (tovarishClient) {
console.log(`\nConnected with Tovarish Relayer ${tovarishClient.selectedRelayer?.url}\n`);
}
const Tornado = Tornado__factory.connect(instanceAddress, provider); const Tornado = Tornado__factory.connect(instanceAddress, provider);
const TornadoServiceConstructor = { const TornadoServiceConstructor = {
@ -1184,6 +1352,7 @@ export function tornadoProgram() {
currency, currency,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
nativeCurrency, nativeCurrency,
@ -1266,7 +1435,7 @@ export function tornadoProgram() {
.action( .action(
async (netIdOpts: NetIdType | undefined, currencyOpts: string | undefined, cmdOptions: commonProgramOptions) => { async (netIdOpts: NetIdType | undefined, currencyOpts: string | undefined, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions); const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options; const { rpc, disableTovarish } = options;
const networks = netIdOpts ? [netIdOpts] : enabledChains; const networks = netIdOpts ? [netIdOpts] : enabledChains;
@ -1293,6 +1462,14 @@ export function tornadoProgram() {
}); });
const graphApi = getProgramGraphAPI(options, config); const graphApi = getProgramGraphAPI(options, config);
const tovarishClient = !disableTovarish
? (await getTovarishRelayer({ options, fetchDataOptions, netId })).relayerClient
: undefined;
if (tovarishClient) {
console.log(`\nConnected with Tovarish Relayer ${tovarishClient.selectedRelayer?.url}\n`);
}
if (governanceContract) { if (governanceContract) {
const governanceService = new NodeGovernanceService({ const governanceService = new NodeGovernanceService({
netId, netId,
@ -1302,6 +1479,7 @@ export function tornadoProgram() {
Governance: Governance__factory.connect(governanceContract, provider), Governance: Governance__factory.connect(governanceContract, provider),
deployedBlock: GOVERNANCE_BLOCK, deployedBlock: GOVERNANCE_BLOCK,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
}); });
@ -1320,6 +1498,7 @@ export function tornadoProgram() {
relayerEnsSubdomains: getRelayerEnsSubdomains(), relayerEnsSubdomains: getRelayerEnsSubdomains(),
deployedBlock: REGISTRY_BLOCK, deployedBlock: REGISTRY_BLOCK,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
}); });
@ -1335,6 +1514,7 @@ export function tornadoProgram() {
Echoer: Echoer__factory.connect(echoContract, provider), Echoer: Echoer__factory.connect(echoContract, provider),
deployedBlock: NOTE_ACCOUNT_BLOCK, deployedBlock: NOTE_ACCOUNT_BLOCK,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
}); });
@ -1349,6 +1529,7 @@ export function tornadoProgram() {
Router: TornadoRouter__factory.connect(routerContract, provider), Router: TornadoRouter__factory.connect(routerContract, provider),
deployedBlock: ENCRYPTED_NOTES_BLOCK, deployedBlock: ENCRYPTED_NOTES_BLOCK,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
}); });
@ -1376,6 +1557,7 @@ export function tornadoProgram() {
currency, currency,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
nativeCurrency, nativeCurrency,
@ -1422,7 +1604,13 @@ export function tornadoProgram() {
.action(async (netIdOpts: NetIdType, cmdOptions: commonProgramOptions) => { .action(async (netIdOpts: NetIdType, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions); const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const allRelayers = await getProgramRelayer({ const allRelayers = options.disableTovarish
? await getProgramRelayer({
options,
fetchDataOptions,
netId: netIdOpts,
})
: await getTovarishRelayer({
options, options,
fetchDataOptions, fetchDataOptions,
netId: netIdOpts, netId: netIdOpts,
@ -1432,9 +1620,10 @@ export function tornadoProgram() {
const invalidRelayers = allRelayers.invalidRelayers as RelayerError[]; const invalidRelayers = allRelayers.invalidRelayers as RelayerError[];
const relayersTable = new Table(); const relayersTable = new Table();
const relayerName = options.disableTovarish ? 'Relayers' : 'Tovarish Relayers';
relayersTable.push( relayersTable.push(
[{ colSpan: 8, content: 'Relayers', hAlign: 'center' }], [{ colSpan: 8, content: relayerName, hAlign: 'center' }],
[ [
'netId', 'netId',
'url', 'url',
@ -1464,7 +1653,7 @@ export function tornadoProgram() {
const invalidRelayersTable = new Table(); const invalidRelayersTable = new Table();
invalidRelayersTable.push( invalidRelayersTable.push(
[{ colSpan: 3, content: 'Invalid Relayers', hAlign: 'center' }], [{ colSpan: 3, content: `Invalid ${relayerName}`, hAlign: 'center' }],
['hostname', 'relayerAddress', 'errorMessage'].map((content) => ({ content: colors.red.bold(content) })), ['hostname', 'relayerAddress', 'errorMessage'].map((content) => ({ content: colors.red.bold(content) })),
...invalidRelayers.map(({ hostname, relayerAddress, errorMessage }) => { ...invalidRelayers.map(({ hostname, relayerAddress, errorMessage }) => {
return [hostname, relayerAddress, errorMessage ? substring(errorMessage, 40) : '']; return [hostname, relayerAddress, errorMessage ? substring(errorMessage, 40) : ''];
@ -1487,7 +1676,7 @@ export function tornadoProgram() {
.argument('<netId>', 'Network Chain ID to connect with (see https://chainlist.org for examples)', parseNumber) .argument('<netId>', 'Network Chain ID to connect with (see https://chainlist.org for examples)', parseNumber)
.action(async (netId: NetIdType, cmdOptions: commonProgramOptions) => { .action(async (netId: NetIdType, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions); const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options; const { rpc, disableTovarish } = options;
const config = getConfig(netId); const config = getConfig(netId);
@ -1515,6 +1704,14 @@ export function tornadoProgram() {
); );
} }
const tovarishClient = !disableTovarish
? (await getTovarishRelayer({ options, fetchDataOptions, netId })).relayerClient
: undefined;
if (tovarishClient) {
console.log(`\nConnected with Tovarish Relayer ${tovarishClient.selectedRelayer?.url}\n`);
}
/** /**
* Find for any existing note accounts * Find for any existing note accounts
*/ */
@ -1535,6 +1732,7 @@ export function tornadoProgram() {
Echoer, Echoer,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
}); });
@ -1605,7 +1803,7 @@ export function tornadoProgram() {
) )
.action(async (netId: NetIdType, accountKey: string | undefined, cmdOptions: commonProgramOptions) => { .action(async (netId: NetIdType, accountKey: string | undefined, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions); const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options; const { rpc, disableTovarish } = options;
if (!accountKey) { if (!accountKey) {
accountKey = options.accountKey; accountKey = options.accountKey;
} }
@ -1632,6 +1830,14 @@ export function tornadoProgram() {
); );
} }
const tovarishClient = !disableTovarish
? (await getTovarishRelayer({ options, fetchDataOptions, netId })).relayerClient
: undefined;
if (tovarishClient) {
console.log(`\nConnected with Tovarish Relayer ${tovarishClient.selectedRelayer?.url}\n`);
}
const Echoer = Echoer__factory.connect(echoContract, provider); const Echoer = Echoer__factory.connect(echoContract, provider);
const noteAccount = new NoteAccount({ const noteAccount = new NoteAccount({
@ -1648,6 +1854,7 @@ export function tornadoProgram() {
Router: TornadoRouter__factory.connect(routerContract, provider), Router: TornadoRouter__factory.connect(routerContract, provider),
deployedBlock: ENCRYPTED_NOTES_BLOCK, deployedBlock: ENCRYPTED_NOTES_BLOCK,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR, cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR, userDirectory: SAVED_DIR,
}); });

@ -110,18 +110,16 @@ export async function saveUserFile({
export async function loadSavedEvents<T extends MinimalEvents>({ export async function loadSavedEvents<T extends MinimalEvents>({
name, name,
userDirectory, userDirectory,
deployedBlock,
}: { }: {
name: string; name: string;
userDirectory: string; userDirectory: string;
deployedBlock: number;
}): Promise<BaseEvents<T>> { }): Promise<BaseEvents<T>> {
const filePath = path.join(userDirectory, `${name}.json`.toLowerCase()); const filePath = path.join(userDirectory, `${name}.json`.toLowerCase());
if (!(await existsAsync(filePath))) { if (!(await existsAsync(filePath))) {
return { return {
events: [] as T[], events: [] as T[],
lastBlock: deployedBlock, lastBlock: 0,
}; };
} }
@ -130,14 +128,14 @@ export async function loadSavedEvents<T extends MinimalEvents>({
return { return {
events, events,
lastBlock: events && events.length ? events[events.length - 1].blockNumber : deployedBlock, lastBlock: events[events.length - 1]?.blockNumber || 0,
}; };
} catch (err) { } catch (err) {
console.log('Method loadSavedEvents has error'); console.log('Method loadSavedEvents has error');
console.log(err); console.log(err);
return { return {
events: [], events: [],
lastBlock: deployedBlock, lastBlock: 0,
}; };
} }
} }

@ -59,6 +59,7 @@ export class NodeTornadoService extends BaseTornadoService {
currency, currency,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory, cacheDirectory,
userDirectory, userDirectory,
nativeCurrency, nativeCurrency,
@ -76,6 +77,7 @@ export class NodeTornadoService extends BaseTornadoService {
currency, currency,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
}); });
this.cacheDirectory = cacheDirectory; this.cacheDirectory = cacheDirectory;
@ -137,14 +139,13 @@ export class NodeTornadoService extends BaseTornadoService {
return { return {
events: [], events: [],
lastBlock: this.deployedBlock, lastBlock: 0,
}; };
} }
const savedEvents = await loadSavedEvents<DepositsEvents | WithdrawalsEvents>({ const savedEvents = await loadSavedEvents<DepositsEvents | WithdrawalsEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
}); });
console.log('Updating events for', this.amount, this.currency.toUpperCase(), `${this.getType().toLowerCase()}s\n`); console.log('Updating events for', this.amount, this.currency.toUpperCase(), `${this.getType().toLowerCase()}s\n`);
@ -269,6 +270,7 @@ export class NodeEchoService extends BaseEchoService {
Echoer, Echoer,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory, cacheDirectory,
userDirectory, userDirectory,
}: NodeEchoServiceConstructor) { }: NodeEchoServiceConstructor) {
@ -280,6 +282,7 @@ export class NodeEchoService extends BaseEchoService {
Echoer, Echoer,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
}); });
this.cacheDirectory = cacheDirectory; this.cacheDirectory = cacheDirectory;
@ -320,14 +323,13 @@ export class NodeEchoService extends BaseEchoService {
return { return {
events: [], events: [],
lastBlock: this.deployedBlock, lastBlock: 0,
}; };
} }
const savedEvents = await loadSavedEvents<EchoEvents>({ const savedEvents = await loadSavedEvents<EchoEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
}); });
console.log(`Updating events for ${this.netId} chain echo events\n`); console.log(`Updating events for ${this.netId} chain echo events\n`);
@ -415,6 +417,7 @@ export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
Router, Router,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory, cacheDirectory,
userDirectory, userDirectory,
}: NodeEncryptedNotesServiceConstructor) { }: NodeEncryptedNotesServiceConstructor) {
@ -426,6 +429,7 @@ export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
Router, Router,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
}); });
this.cacheDirectory = cacheDirectory; this.cacheDirectory = cacheDirectory;
@ -466,14 +470,13 @@ export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
return { return {
events: [], events: [],
lastBlock: this.deployedBlock, lastBlock: 0,
}; };
} }
const savedEvents = await loadSavedEvents<EncryptedNotesEvents>({ const savedEvents = await loadSavedEvents<EncryptedNotesEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
}); });
console.log(`Updating events for ${this.netId} chain encrypted events\n`); console.log(`Updating events for ${this.netId} chain encrypted events\n`);
@ -573,6 +576,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
Governance, Governance,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory, cacheDirectory,
userDirectory, userDirectory,
}: NodeGovernanceServiceConstructor) { }: NodeGovernanceServiceConstructor) {
@ -584,6 +588,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
Governance, Governance,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
}); });
this.cacheDirectory = cacheDirectory; this.cacheDirectory = cacheDirectory;
@ -630,14 +635,13 @@ export class NodeGovernanceService extends BaseGovernanceService {
return { return {
events: [], events: [],
lastBlock: this.deployedBlock, lastBlock: 0,
}; };
} }
const savedEvents = await loadSavedEvents<AllGovernanceEvents>({ const savedEvents = await loadSavedEvents<AllGovernanceEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
}); });
console.log(`Updating events for ${this.netId} chain governance events\n`); console.log(`Updating events for ${this.netId} chain governance events\n`);
@ -727,6 +731,7 @@ export class NodeRegistryService extends BaseRegistryService {
relayerEnsSubdomains, relayerEnsSubdomains,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
cacheDirectory, cacheDirectory,
userDirectory, userDirectory,
}: NodeRegistryServiceConstructor) { }: NodeRegistryServiceConstructor) {
@ -740,6 +745,7 @@ export class NodeRegistryService extends BaseRegistryService {
relayerEnsSubdomains, relayerEnsSubdomains,
deployedBlock, deployedBlock,
fetchDataOptions, fetchDataOptions,
tovarishClient,
}); });
this.cacheDirectory = cacheDirectory; this.cacheDirectory = cacheDirectory;
@ -780,14 +786,13 @@ export class NodeRegistryService extends BaseRegistryService {
return { return {
events: [], events: [],
lastBlock: this.deployedBlock, lastBlock: 0,
}; };
} }
const savedEvents = await loadSavedEvents<RegistersEvents>({ const savedEvents = await loadSavedEvents<RegistersEvents>({
name: this.getInstanceName(), name: this.getInstanceName(),
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
}); });
console.log(`Updating events for ${this.netId} chain registry events\n`); console.log(`Updating events for ${this.netId} chain registry events\n`);
@ -862,15 +867,17 @@ export class NodeRegistryService extends BaseRegistryService {
if (!this.userDirectory || !(await existsAsync(filePath))) { if (!this.userDirectory || !(await existsAsync(filePath))) {
return { return {
lastBlock: 0,
timestamp: 0, timestamp: 0,
relayers: [], relayers: [],
}; };
} }
try { try {
const { timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' })); const { lastBlock, timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' }));
return { return {
lastBlock,
timestamp, timestamp,
relayers, relayers,
}; };
@ -879,6 +886,7 @@ export class NodeRegistryService extends BaseRegistryService {
console.log(err); console.log(err);
return { return {
lastBlock: 0,
timestamp: 0, timestamp: 0,
relayers: [], relayers: [],
}; };
@ -890,6 +898,7 @@ export class NodeRegistryService extends BaseRegistryService {
if (!this.cacheDirectory || !(await existsAsync(filePath))) { if (!this.cacheDirectory || !(await existsAsync(filePath))) {
return { return {
lastBlock: 0,
timestamp: 0, timestamp: 0,
relayers: [], relayers: [],
fromCache: true, fromCache: true,
@ -897,9 +906,10 @@ export class NodeRegistryService extends BaseRegistryService {
} }
try { try {
const { timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' })); const { lastBlock, timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' }));
return { return {
lastBlock,
timestamp, timestamp,
relayers, relayers,
fromCache: true, fromCache: true,
@ -909,6 +919,7 @@ export class NodeRegistryService extends BaseRegistryService {
console.log(err); console.log(err);
return { return {
lastBlock: 0,
timestamp: 0, timestamp: 0,
relayers: [], relayers: [],
fromCache: true, fromCache: true,
@ -916,12 +927,12 @@ export class NodeRegistryService extends BaseRegistryService {
} }
} }
async saveRelayers({ timestamp, relayers }: CachedRelayers) { async saveRelayers({ lastBlock, timestamp, relayers }: CachedRelayers) {
if (this.userDirectory) { if (this.userDirectory) {
await saveUserFile({ await saveUserFile({
fileName: 'relayers.json', fileName: 'relayers.json',
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
dataString: JSON.stringify({ timestamp, relayers }, null, 2) + '\n', dataString: JSON.stringify({ lastBlock, timestamp, relayers }, null, 2) + '\n',
}); });
} }
} }

@ -1,6 +1,23 @@
{ {
"timestamp": 1726984895, "lastBlock": 20874000,
"timestamp": 1727826179,
"relayers": [ "relayers": [
{
"ensName": "tornadowithdraw.eth",
"relayerAddress": "0x40c3d1656a26C9266f4A10fed0D87EFf79F54E64",
"hostnames": {},
"tovarishHost": "tornadowithdraw.com",
"tovarishNetworks": [
1,
56,
137,
10,
42161,
100,
43114,
11155111
]
},
{ {
"ensName": "therelayer.eth", "ensName": "therelayer.eth",
"relayerAddress": "0xA0F0287683E820FF4211e67C03cf46a87431f4E1", "relayerAddress": "0xA0F0287683E820FF4211e67C03cf46a87431f4E1",
@ -190,7 +207,7 @@
"relayerAddress": "0xa0109274F53609f6Be97ec5f3052C659AB80f012", "relayerAddress": "0xa0109274F53609f6Be97ec5f3052C659AB80f012",
"isRegistered": true, "isRegistered": true,
"owner": "0xa0109274F53609f6Be97ec5f3052C659AB80f012", "owner": "0xa0109274F53609f6Be97ec5f3052C659AB80f012",
"stakeBalance": "2378.32581329697947104", "stakeBalance": "2075.677151387681021207",
"hostnames": { "hostnames": {
"1": "torn.relayersdao.finance", "1": "torn.relayersdao.finance",
"56": "bsc.relayersdao.finance", "56": "bsc.relayersdao.finance",
@ -202,7 +219,7 @@
"relayerAddress": "0x4750BCfcC340AA4B31be7e71fa072716d28c29C5", "relayerAddress": "0x4750BCfcC340AA4B31be7e71fa072716d28c29C5",
"isRegistered": true, "isRegistered": true,
"owner": "0x4750BCfcC340AA4B31be7e71fa072716d28c29C5", "owner": "0x4750BCfcC340AA4B31be7e71fa072716d28c29C5",
"stakeBalance": "19959.537563477506888659", "stakeBalance": "17078.896444585792890283",
"hostnames": { "hostnames": {
"1": "eth.reltor.su", "1": "eth.reltor.su",
"56": "binance.reltor.su", "56": "binance.reltor.su",
@ -214,7 +231,7 @@
"relayerAddress": "0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe", "relayerAddress": "0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe",
"isRegistered": true, "isRegistered": true,
"owner": "0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe", "owner": "0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe",
"stakeBalance": "6379.466188010824508431", "stakeBalance": "9393.81613250700489948",
"hostnames": { "hostnames": {
"1": "eth.t-relayer.com", "1": "eth.t-relayer.com",
"56": "bsc.t-relayer.com" "56": "bsc.t-relayer.com"
@ -225,7 +242,7 @@
"relayerAddress": "0x5555555731006f71f121144534Ca7C8799F66AA3", "relayerAddress": "0x5555555731006f71f121144534Ca7C8799F66AA3",
"isRegistered": true, "isRegistered": true,
"owner": "0x5555555731006f71f121144534Ca7C8799F66AA3", "owner": "0x5555555731006f71f121144534Ca7C8799F66AA3",
"stakeBalance": "5611.417537369167166026", "stakeBalance": "12244.203047120053584442",
"hostnames": { "hostnames": {
"1": "eth.default-relayer.com", "1": "eth.default-relayer.com",
"56": "bsc.default-relayer.com" "56": "bsc.default-relayer.com"
@ -246,7 +263,7 @@
"relayerAddress": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c", "relayerAddress": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c",
"isRegistered": true, "isRegistered": true,
"owner": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c", "owner": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c",
"stakeBalance": "1979.261700994868938309", "stakeBalance": "1649.22629774180421325",
"hostnames": { "hostnames": {
"1": "main.gm777.xyz", "1": "main.gm777.xyz",
"56": "bsc.gm777.xyz", "56": "bsc.gm777.xyz",
@ -258,7 +275,7 @@
"relayerAddress": "0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c", "relayerAddress": "0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c",
"isRegistered": true, "isRegistered": true,
"owner": "0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c", "owner": "0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c",
"stakeBalance": "2743.886989359646966334", "stakeBalance": "2392.026618435010858225",
"hostnames": { "hostnames": {
"1": "black-hardy.com", "1": "black-hardy.com",
"56": "bsc.black-hardy.com" "56": "bsc.black-hardy.com"
@ -279,7 +296,7 @@
"relayerAddress": "0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C", "relayerAddress": "0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C",
"isRegistered": true, "isRegistered": true,
"owner": "0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C", "owner": "0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C",
"stakeBalance": "1697.363238361952542846", "stakeBalance": "4150.327987173762878437",
"hostnames": { "hostnames": {
"1": "tornima.xyz", "1": "tornima.xyz",
"56": "binance.tornima.xyz" "56": "binance.tornima.xyz"
@ -290,7 +307,7 @@
"relayerAddress": "0xd04e9f0945DEA8373D882C730e2c93a74B591796", "relayerAddress": "0xd04e9f0945DEA8373D882C730e2c93a74B591796",
"isRegistered": true, "isRegistered": true,
"owner": "0xd04e9f0945DEA8373D882C730e2c93a74B591796", "owner": "0xd04e9f0945DEA8373D882C730e2c93a74B591796",
"stakeBalance": "6683.804986564144794348", "stakeBalance": "6636.564618899564311691",
"hostnames": { "hostnames": {
"1": "torn-city.com", "1": "torn-city.com",
"56": "bsc.torn-city.com" "56": "bsc.torn-city.com"
@ -345,13 +362,23 @@
"relayerAddress": "0x0e9D9a828247F5eed7f6D31D213A39805De52441", "relayerAddress": "0x0e9D9a828247F5eed7f6D31D213A39805De52441",
"isRegistered": true, "isRegistered": true,
"owner": "0x0e9D9a828247F5eed7f6D31D213A39805De52441", "owner": "0x0e9D9a828247F5eed7f6D31D213A39805De52441",
"stakeBalance": "4086.387829537434113053", "stakeBalance": "4668.726167448982243314",
"hostnames": { "hostnames": {
"1": "okrelayer.xyz", "1": "okrelayer.xyz",
"56": "binance.okrelayer.xyz", "56": "binance.okrelayer.xyz",
"137": "polygon.okrelayer.xyz", "137": "polygon.okrelayer.xyz",
"42161": "arb.okrelayer.xyz" "42161": "arb.okrelayer.xyz"
} }
},
{
"ensName": "s-relayer.eth",
"relayerAddress": "0xc6398b4e8B60720051ed33f5C5a692f9785f5580",
"isRegistered": true,
"owner": "0xc6398b4e8B60720051ed33f5C5a692f9785f5580",
"stakeBalance": "5000.0",
"hostnames": {
"1": "s-relayer.xyz"
}
} }
] ]
} }

119
yarn.lock

@ -35,6 +35,11 @@
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
"@colors/colors@^1.6.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0"
integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==
"@cspotcode/source-map-support@^0.8.0": "@cspotcode/source-map-support@^0.8.0":
version "0.8.1" version "0.8.1"
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
@ -781,9 +786,9 @@
"@openzeppelin/contracts-v3" "npm:@openzeppelin/contracts@3.2.0-rc.0" "@openzeppelin/contracts-v3" "npm:@openzeppelin/contracts@3.2.0-rc.0"
ethers "^6.4.0" ethers "^6.4.0"
"@tornado/core@git+https://git.tornado.ws/tornadocontrib/tornado-core.git#c155649719d121fe50799420750f531eb50589ce": "@tornado/core@git+https://git.tornado.ws/tornadocontrib/tornado-core.git#f73b9ecbff0b106854752388f94c2d0c30e54975":
version "1.0.15" version "1.0.19"
resolved "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#c155649719d121fe50799420750f531eb50589ce" resolved "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#f73b9ecbff0b106854752388f94c2d0c30e54975"
dependencies: dependencies:
"@metamask/eth-sig-util" "^7.0.3" "@metamask/eth-sig-util" "^7.0.3"
"@tornado/contracts" "^1.0.1" "@tornado/contracts" "^1.0.1"
@ -900,62 +905,62 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f"
integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==
"@typescript-eslint/eslint-plugin@^8.6.0": "@typescript-eslint/eslint-plugin@^8.7.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz#20049754ff9f6d3a09bf240297f029ce04290999" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz#b2b02a5447cdc885950eb256b3b8a97b92031bd3"
integrity sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg== integrity sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==
dependencies: dependencies:
"@eslint-community/regexpp" "^4.10.0" "@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "8.6.0" "@typescript-eslint/scope-manager" "8.8.0"
"@typescript-eslint/type-utils" "8.6.0" "@typescript-eslint/type-utils" "8.8.0"
"@typescript-eslint/utils" "8.6.0" "@typescript-eslint/utils" "8.8.0"
"@typescript-eslint/visitor-keys" "8.6.0" "@typescript-eslint/visitor-keys" "8.8.0"
graphemer "^1.4.0" graphemer "^1.4.0"
ignore "^5.3.1" ignore "^5.3.1"
natural-compare "^1.4.0" natural-compare "^1.4.0"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/parser@^8.6.0": "@typescript-eslint/parser@^8.7.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.6.0.tgz#02e092b9dc8b4e319172af620d0d39b337d948f6" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.8.0.tgz#ee4397c70230c4eee030456924c0fba480072f5e"
integrity sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow== integrity sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "8.6.0" "@typescript-eslint/scope-manager" "8.8.0"
"@typescript-eslint/types" "8.6.0" "@typescript-eslint/types" "8.8.0"
"@typescript-eslint/typescript-estree" "8.6.0" "@typescript-eslint/typescript-estree" "8.8.0"
"@typescript-eslint/visitor-keys" "8.6.0" "@typescript-eslint/visitor-keys" "8.8.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@8.6.0": "@typescript-eslint/scope-manager@8.8.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz#28cc2fc26a84b75addf45091a2c6283e29e2c982" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz#30b23a6ae5708bd7882e40675ef2f1b2beac741f"
integrity sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw== integrity sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==
dependencies: dependencies:
"@typescript-eslint/types" "8.6.0" "@typescript-eslint/types" "8.8.0"
"@typescript-eslint/visitor-keys" "8.6.0" "@typescript-eslint/visitor-keys" "8.8.0"
"@typescript-eslint/type-utils@8.6.0": "@typescript-eslint/type-utils@8.8.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz#d4347e637478bef88cee1db691fcfa20ade9b8a0" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.8.0.tgz#a0ca1c8a90d94b101176a169d7a0958187408d33"
integrity sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg== integrity sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==
dependencies: dependencies:
"@typescript-eslint/typescript-estree" "8.6.0" "@typescript-eslint/typescript-estree" "8.8.0"
"@typescript-eslint/utils" "8.6.0" "@typescript-eslint/utils" "8.8.0"
debug "^4.3.4" debug "^4.3.4"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/types@8.6.0": "@typescript-eslint/types@8.8.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.6.0.tgz#cdc3a16f83f2f0663d6723e9fd032331cdd9f51c" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.8.0.tgz#08ea5df6c01984d456056434641491fbf7a1bf43"
integrity sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw== integrity sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==
"@typescript-eslint/typescript-estree@8.6.0": "@typescript-eslint/typescript-estree@8.8.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz#f945506de42871f04868371cb5bf21e8f7266e01" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz#072eaab97fdb63513fabfe1cf271812affe779e3"
integrity sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g== integrity sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==
dependencies: dependencies:
"@typescript-eslint/types" "8.6.0" "@typescript-eslint/types" "8.8.0"
"@typescript-eslint/visitor-keys" "8.6.0" "@typescript-eslint/visitor-keys" "8.8.0"
debug "^4.3.4" debug "^4.3.4"
fast-glob "^3.3.2" fast-glob "^3.3.2"
is-glob "^4.0.3" is-glob "^4.0.3"
@ -963,22 +968,22 @@
semver "^7.6.0" semver "^7.6.0"
ts-api-utils "^1.3.0" ts-api-utils "^1.3.0"
"@typescript-eslint/utils@8.6.0": "@typescript-eslint/utils@8.8.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.6.0.tgz#175fe893f32804bed1e72b3364ea6bbe1044181c" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.8.0.tgz#bd8607e3a68c461b69169c7a5824637dc9e8b3f1"
integrity sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A== integrity sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.4.0" "@eslint-community/eslint-utils" "^4.4.0"
"@typescript-eslint/scope-manager" "8.6.0" "@typescript-eslint/scope-manager" "8.8.0"
"@typescript-eslint/types" "8.6.0" "@typescript-eslint/types" "8.8.0"
"@typescript-eslint/typescript-estree" "8.6.0" "@typescript-eslint/typescript-estree" "8.8.0"
"@typescript-eslint/visitor-keys@8.6.0": "@typescript-eslint/visitor-keys@8.8.0":
version "8.6.0" version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz#5432af4a1753f376f35ab5b891fc9db237aaf76f" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz#f93965abd38c82a1a1f5574290a50d02daf1cd2e"
integrity sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg== integrity sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==
dependencies: dependencies:
"@typescript-eslint/types" "8.6.0" "@typescript-eslint/types" "8.8.0"
eslint-visitor-keys "^3.4.3" eslint-visitor-keys "^3.4.3"
"@ungap/structured-clone@^1.2.0": "@ungap/structured-clone@^1.2.0":
@ -4572,10 +4577,10 @@ webpack-sources@^3.2.3:
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack@^5.94.0: webpack@^5.95.0:
version "5.94.0" version "5.95.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.95.0.tgz#8fd8c454fa60dad186fbe36c400a55848307b4c0"
integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg== integrity sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==
dependencies: dependencies:
"@types/estree" "^1.0.5" "@types/estree" "^1.0.5"
"@webassemblyjs/ast" "^1.12.1" "@webassemblyjs/ast" "^1.12.1"