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

2588
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 { Command } from 'commander';
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 = {
rpc?: string;
ethRpc?: string;
graph?: string;
ethGraph?: string;
disableGraph?: boolean;
disableTovarish?: boolean;
accountKey?: string;
relayer?: string;
walletWithdrawal?: boolean;
@ -40,13 +41,22 @@ export declare function getProgramRelayer({ options, fetchDataOptions, netId, }:
fetchDataOptions?: fetchDataOptions;
netId: NetIdType;
}): Promise<{
validRelayers?: RelayerInfo[];
invalidRelayers?: RelayerError[];
relayerClient?: RelayerClient;
validRelayers: RelayerInfo[];
invalidRelayers: RelayerError[];
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, }: {
signer: VoidSigner | Wallet;
options: commonProgramOptions;
populatedTransaction: TransactionLike;
}): Promise<void>;
}): Promise<import("ethers").TransactionResponse | undefined>;
export declare function tornadoProgram(): Command;

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

@ -14,7 +14,7 @@ export declare class NodeTornadoService extends BaseTornadoService {
nativeCurrency: string;
merkleTreeService?: MerkleTreeService;
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;
updateTransactionProgress({ 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 {
cacheDirectory: 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;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<EchoEvents>>;
@ -44,7 +44,7 @@ export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceCons
export declare class NodeEncryptedNotesService extends BaseEncryptedNotesService {
cacheDirectory: 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;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<EncryptedNotesEvents>>;
@ -58,7 +58,7 @@ export type NodeGovernanceServiceConstructor = BaseGovernanceServiceConstructor
export declare class NodeGovernanceService extends BaseGovernanceService {
cacheDirectory: 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;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]): void;
@ -73,7 +73,7 @@ export type NodeRegistryServiceConstructor = BaseRegistryServiceConstructor & {
export declare class NodeRegistryService extends BaseRegistryService {
cacheDirectory: 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;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<RegistersEvents>>;
@ -81,5 +81,5 @@ export declare class NodeRegistryService extends BaseRegistryService {
saveEvents({ events, lastBlock }: BaseEvents<RegistersEvents>): Promise<void>;
getRelayersFromDB(): 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",
"version": "1.0.10-alpha",
"version": "1.0.11-alpha",
"description": "Modern Toolsets for Privacy Pools on Ethereum",
"main": "./dist/cli.js",
"types": "./dist/cli.d.ts",
@ -50,12 +50,12 @@
"dependencies": {},
"optionalDependencies": {},
"devDependencies": {
"@colors/colors": "1.5.0",
"@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#c155649719d121fe50799420750f531eb50589ce",
"@colors/colors": "^1.6.0",
"@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#f73b9ecbff0b106854752388f94c2d0c30e54975",
"@typechain/ethers-v6": "^0.5.1",
"@types/figlet": "^1.5.8",
"@typescript-eslint/eslint-plugin": "^8.6.0",
"@typescript-eslint/parser": "^8.6.0",
"@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.7.0",
"bloomfilter.js": "^1.0.2",
"cli-table3": "^0.6.4",
"commander": "^12.0.0",
@ -76,7 +76,7 @@
"tsc": "^2.0.4",
"typechain": "^8.3.2",
"typescript": "^5.6.2",
"webpack": "^5.94.0",
"webpack": "^5.95.0",
"webpack-cli": "^5.1.4"
}
}

@ -68,6 +68,8 @@ import {
initGroth16,
getRelayerEnsSubdomains,
getActiveTokens,
TovarishClient,
TovarishInfo,
} from '@tornado/core';
import * as packageJson from '../package.json';
import {
@ -120,6 +122,7 @@ export type commonProgramOptions = {
graph?: string;
ethGraph?: string;
disableGraph?: boolean;
disableTovarish?: boolean;
accountKey?: string;
relayer?: string;
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),
ethGraph: options.ethGraph || (process.env.ETHGRAPH_URL ? parseUrl(process.env.ETHGRAPH_URL) : 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),
relayer: options.relayer || (process.env.RELAYER ? parseRelayer(process.env.RELAYER) : undefined),
walletWithdrawal:
@ -280,9 +284,9 @@ export async function getProgramRelayer({
fetchDataOptions?: fetchDataOptions;
netId: NetIdType;
}): Promise<{
validRelayers?: RelayerInfo[];
invalidRelayers?: RelayerError[];
relayerClient?: RelayerClient;
validRelayers: RelayerInfo[];
invalidRelayers: RelayerError[];
relayerClient: RelayerClient;
}> {
const { ethRpc, ethGraph, relayer, disableGraph } = options;
@ -333,11 +337,11 @@ export async function getProgramRelayer({
// If user provided relayer is a full working URL
if (relayer && relayer.startsWith('http')) {
const relayerStatus = await relayerClient.askRelayerStatus({
hostname: new URL(relayer).hostname,
url: relayer,
});
relayerClient.selectedRelayer = {
ensName: new URL(relayer).hostname,
ensName: new URL(relayerStatus.url).hostname,
relayerAddress: getAddress(relayerStatus.rewardAccount),
netId: relayerStatus.netId,
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({
signer,
options,
@ -446,9 +576,11 @@ export async function programSendTransaction({
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() {
@ -608,7 +740,7 @@ export function tornadoProgram() {
if (!isEth && denomination > tokenApprovals) {
// token approval
await programSendTransaction({
const resp = await programSendTransaction({
signer,
options,
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',
);
process.exit(0);
} else {
// Wait until the approval tx is confirmed
await resp?.wait();
}
}
@ -785,7 +920,7 @@ export function tornadoProgram() {
.action(
async (note: string, recipient: string, ethPurchase: number | undefined, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc, walletWithdrawal } = options;
const { rpc, walletWithdrawal, disableTovarish } = options;
// Prepare groth16 in advance
initGroth16();
@ -825,6 +960,24 @@ export function tornadoProgram() {
...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({
options,
provider,
@ -863,6 +1016,7 @@ export function tornadoProgram() {
currency,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
nativeCurrency,
@ -941,17 +1095,10 @@ export function tornadoProgram() {
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(KEY_PATH).then((b) => new Uint8Array(b).buffer),
depositTreePromise,
!walletWithdrawal
? getProgramRelayer({
options,
fetchDataOptions,
netId,
}).then(({ relayerClient }) => relayerClient)
: undefined,
tornadoFeeOracle.fetchL1OptimismFee(),
!isEth
? 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.gasPrice as bigint);
if (netId === NetId.BSC && gasPrice < parseUnits('3.3', 'gwei')) {
if (!walletWithdrawal && !tovarishClient && netId === NetId.BSC) {
gasPrice = parseUnits('3.3', 'gwei');
}
@ -1008,6 +1155,19 @@ export function tornadoProgram() {
isEth,
});
console.log({
gasPrice,
gasLimit,
l1Fee,
denomination,
ethRefund: refund,
tokenPriceInWei,
tokenDecimals: decimals,
relayerFeePercent: tornadoServiceFee,
isEth,
fee,
});
relayer = rewardAccount as string;
if (fee > denomination) {
@ -1064,7 +1224,7 @@ export function tornadoProgram() {
withdrawTable.push([{ colSpan: 2, content: 'Withdrawal Info', hAlign: 'center' }]);
// withdraw using relayer
if (!walletWithdrawal && relayerClient) {
if (!walletWithdrawal && relayerClient?.selectedRelayer) {
withdrawTable.push(
[{ colSpan: 2, content: 'Withdraw', hAlign: 'center' }],
['Withdrawal', `${amount} ${currency.toUpperCase()}`],
@ -1146,7 +1306,7 @@ export function tornadoProgram() {
.argument('<note>', 'Tornado Cash Deposit Note')
.action(async (note: string, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options;
const { rpc, disableTovarish } = options;
const deposit = await Deposit.parseNote(note);
const { netId, currency, amount, commitmentHex, nullifierHex } = deposit;
@ -1172,6 +1332,14 @@ export function tornadoProgram() {
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 TornadoServiceConstructor = {
@ -1184,6 +1352,7 @@ export function tornadoProgram() {
currency,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
nativeCurrency,
@ -1266,7 +1435,7 @@ export function tornadoProgram() {
.action(
async (netIdOpts: NetIdType | undefined, currencyOpts: string | undefined, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options;
const { rpc, disableTovarish } = options;
const networks = netIdOpts ? [netIdOpts] : enabledChains;
@ -1293,6 +1462,14 @@ export function tornadoProgram() {
});
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) {
const governanceService = new NodeGovernanceService({
netId,
@ -1302,6 +1479,7 @@ export function tornadoProgram() {
Governance: Governance__factory.connect(governanceContract, provider),
deployedBlock: GOVERNANCE_BLOCK,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
});
@ -1320,6 +1498,7 @@ export function tornadoProgram() {
relayerEnsSubdomains: getRelayerEnsSubdomains(),
deployedBlock: REGISTRY_BLOCK,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
});
@ -1335,6 +1514,7 @@ export function tornadoProgram() {
Echoer: Echoer__factory.connect(echoContract, provider),
deployedBlock: NOTE_ACCOUNT_BLOCK,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
});
@ -1349,6 +1529,7 @@ export function tornadoProgram() {
Router: TornadoRouter__factory.connect(routerContract, provider),
deployedBlock: ENCRYPTED_NOTES_BLOCK,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
});
@ -1376,6 +1557,7 @@ export function tornadoProgram() {
currency,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
nativeCurrency,
@ -1422,19 +1604,26 @@ export function tornadoProgram() {
.action(async (netIdOpts: NetIdType, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const allRelayers = await getProgramRelayer({
options,
fetchDataOptions,
netId: netIdOpts,
});
const allRelayers = options.disableTovarish
? await getProgramRelayer({
options,
fetchDataOptions,
netId: netIdOpts,
})
: await getTovarishRelayer({
options,
fetchDataOptions,
netId: netIdOpts,
});
const validRelayers = allRelayers.validRelayers as RelayerInfo[];
const invalidRelayers = allRelayers.invalidRelayers as RelayerError[];
const relayersTable = new Table();
const relayerName = options.disableTovarish ? 'Relayers' : 'Tovarish Relayers';
relayersTable.push(
[{ colSpan: 8, content: 'Relayers', hAlign: 'center' }],
[{ colSpan: 8, content: relayerName, hAlign: 'center' }],
[
'netId',
'url',
@ -1464,7 +1653,7 @@ export function tornadoProgram() {
const invalidRelayersTable = new Table();
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) })),
...invalidRelayers.map(({ hostname, relayerAddress, errorMessage }) => {
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)
.action(async (netId: NetIdType, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options;
const { rpc, disableTovarish } = options;
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
*/
@ -1535,6 +1732,7 @@ export function tornadoProgram() {
Echoer,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
});
@ -1605,7 +1803,7 @@ export function tornadoProgram() {
)
.action(async (netId: NetIdType, accountKey: string | undefined, cmdOptions: commonProgramOptions) => {
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
const { rpc } = options;
const { rpc, disableTovarish } = options;
if (!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 noteAccount = new NoteAccount({
@ -1648,6 +1854,7 @@ export function tornadoProgram() {
Router: TornadoRouter__factory.connect(routerContract, provider),
deployedBlock: ENCRYPTED_NOTES_BLOCK,
fetchDataOptions,
tovarishClient,
cacheDirectory: EVENTS_DIR,
userDirectory: SAVED_DIR,
});

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

@ -59,6 +59,7 @@ export class NodeTornadoService extends BaseTornadoService {
currency,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory,
userDirectory,
nativeCurrency,
@ -76,6 +77,7 @@ export class NodeTornadoService extends BaseTornadoService {
currency,
deployedBlock,
fetchDataOptions,
tovarishClient,
});
this.cacheDirectory = cacheDirectory;
@ -137,14 +139,13 @@ export class NodeTornadoService extends BaseTornadoService {
return {
events: [],
lastBlock: this.deployedBlock,
lastBlock: 0,
};
}
const savedEvents = await loadSavedEvents<DepositsEvents | WithdrawalsEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
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,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory,
userDirectory,
}: NodeEchoServiceConstructor) {
@ -280,6 +282,7 @@ export class NodeEchoService extends BaseEchoService {
Echoer,
deployedBlock,
fetchDataOptions,
tovarishClient,
});
this.cacheDirectory = cacheDirectory;
@ -320,14 +323,13 @@ export class NodeEchoService extends BaseEchoService {
return {
events: [],
lastBlock: this.deployedBlock,
lastBlock: 0,
};
}
const savedEvents = await loadSavedEvents<EchoEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`Updating events for ${this.netId} chain echo events\n`);
@ -415,6 +417,7 @@ export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
Router,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory,
userDirectory,
}: NodeEncryptedNotesServiceConstructor) {
@ -426,6 +429,7 @@ export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
Router,
deployedBlock,
fetchDataOptions,
tovarishClient,
});
this.cacheDirectory = cacheDirectory;
@ -466,14 +470,13 @@ export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
return {
events: [],
lastBlock: this.deployedBlock,
lastBlock: 0,
};
}
const savedEvents = await loadSavedEvents<EncryptedNotesEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`Updating events for ${this.netId} chain encrypted events\n`);
@ -573,6 +576,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
Governance,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory,
userDirectory,
}: NodeGovernanceServiceConstructor) {
@ -584,6 +588,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
Governance,
deployedBlock,
fetchDataOptions,
tovarishClient,
});
this.cacheDirectory = cacheDirectory;
@ -630,14 +635,13 @@ export class NodeGovernanceService extends BaseGovernanceService {
return {
events: [],
lastBlock: this.deployedBlock,
lastBlock: 0,
};
}
const savedEvents = await loadSavedEvents<AllGovernanceEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`Updating events for ${this.netId} chain governance events\n`);
@ -727,6 +731,7 @@ export class NodeRegistryService extends BaseRegistryService {
relayerEnsSubdomains,
deployedBlock,
fetchDataOptions,
tovarishClient,
cacheDirectory,
userDirectory,
}: NodeRegistryServiceConstructor) {
@ -740,6 +745,7 @@ export class NodeRegistryService extends BaseRegistryService {
relayerEnsSubdomains,
deployedBlock,
fetchDataOptions,
tovarishClient,
});
this.cacheDirectory = cacheDirectory;
@ -780,14 +786,13 @@ export class NodeRegistryService extends BaseRegistryService {
return {
events: [],
lastBlock: this.deployedBlock,
lastBlock: 0,
};
}
const savedEvents = await loadSavedEvents<RegistersEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
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))) {
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
};
}
try {
const { timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' }));
const { lastBlock, timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' }));
return {
lastBlock,
timestamp,
relayers,
};
@ -879,6 +886,7 @@ export class NodeRegistryService extends BaseRegistryService {
console.log(err);
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
};
@ -890,6 +898,7 @@ export class NodeRegistryService extends BaseRegistryService {
if (!this.cacheDirectory || !(await existsAsync(filePath))) {
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
fromCache: true,
@ -897,9 +906,10 @@ export class NodeRegistryService extends BaseRegistryService {
}
try {
const { timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' }));
const { lastBlock, timestamp, relayers } = JSON.parse(await readFile(filePath, { encoding: 'utf8' }));
return {
lastBlock,
timestamp,
relayers,
fromCache: true,
@ -909,6 +919,7 @@ export class NodeRegistryService extends BaseRegistryService {
console.log(err);
return {
lastBlock: 0,
timestamp: 0,
relayers: [],
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) {
await saveUserFile({
fileName: 'relayers.json',
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": [
{
"ensName": "tornadowithdraw.eth",
"relayerAddress": "0x40c3d1656a26C9266f4A10fed0D87EFf79F54E64",
"hostnames": {},
"tovarishHost": "tornadowithdraw.com",
"tovarishNetworks": [
1,
56,
137,
10,
42161,
100,
43114,
11155111
]
},
{
"ensName": "therelayer.eth",
"relayerAddress": "0xA0F0287683E820FF4211e67C03cf46a87431f4E1",
@ -190,7 +207,7 @@
"relayerAddress": "0xa0109274F53609f6Be97ec5f3052C659AB80f012",
"isRegistered": true,
"owner": "0xa0109274F53609f6Be97ec5f3052C659AB80f012",
"stakeBalance": "2378.32581329697947104",
"stakeBalance": "2075.677151387681021207",
"hostnames": {
"1": "torn.relayersdao.finance",
"56": "bsc.relayersdao.finance",
@ -202,7 +219,7 @@
"relayerAddress": "0x4750BCfcC340AA4B31be7e71fa072716d28c29C5",
"isRegistered": true,
"owner": "0x4750BCfcC340AA4B31be7e71fa072716d28c29C5",
"stakeBalance": "19959.537563477506888659",
"stakeBalance": "17078.896444585792890283",
"hostnames": {
"1": "eth.reltor.su",
"56": "binance.reltor.su",
@ -214,7 +231,7 @@
"relayerAddress": "0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe",
"isRegistered": true,
"owner": "0x000000Cd6521Ed1a65FAe0678eA15aF4EEAD74fe",
"stakeBalance": "6379.466188010824508431",
"stakeBalance": "9393.81613250700489948",
"hostnames": {
"1": "eth.t-relayer.com",
"56": "bsc.t-relayer.com"
@ -225,7 +242,7 @@
"relayerAddress": "0x5555555731006f71f121144534Ca7C8799F66AA3",
"isRegistered": true,
"owner": "0x5555555731006f71f121144534Ca7C8799F66AA3",
"stakeBalance": "5611.417537369167166026",
"stakeBalance": "12244.203047120053584442",
"hostnames": {
"1": "eth.default-relayer.com",
"56": "bsc.default-relayer.com"
@ -246,7 +263,7 @@
"relayerAddress": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c",
"isRegistered": true,
"owner": "0x94596B6A626392F5D972D6CC4D929a42c2f0008c",
"stakeBalance": "1979.261700994868938309",
"stakeBalance": "1649.22629774180421325",
"hostnames": {
"1": "main.gm777.xyz",
"56": "bsc.gm777.xyz",
@ -258,7 +275,7 @@
"relayerAddress": "0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c",
"isRegistered": true,
"owner": "0xC49415493eB3Ec64a0F13D8AA5056f1CfC4ce35c",
"stakeBalance": "2743.886989359646966334",
"stakeBalance": "2392.026618435010858225",
"hostnames": {
"1": "black-hardy.com",
"56": "bsc.black-hardy.com"
@ -279,7 +296,7 @@
"relayerAddress": "0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C",
"isRegistered": true,
"owner": "0x2Ee39Ff05643bC7cc9ed31B71e142429044A425C",
"stakeBalance": "1697.363238361952542846",
"stakeBalance": "4150.327987173762878437",
"hostnames": {
"1": "tornima.xyz",
"56": "binance.tornima.xyz"
@ -290,7 +307,7 @@
"relayerAddress": "0xd04e9f0945DEA8373D882C730e2c93a74B591796",
"isRegistered": true,
"owner": "0xd04e9f0945DEA8373D882C730e2c93a74B591796",
"stakeBalance": "6683.804986564144794348",
"stakeBalance": "6636.564618899564311691",
"hostnames": {
"1": "torn-city.com",
"56": "bsc.torn-city.com"
@ -345,13 +362,23 @@
"relayerAddress": "0x0e9D9a828247F5eed7f6D31D213A39805De52441",
"isRegistered": true,
"owner": "0x0e9D9a828247F5eed7f6D31D213A39805De52441",
"stakeBalance": "4086.387829537434113053",
"stakeBalance": "4668.726167448982243314",
"hostnames": {
"1": "okrelayer.xyz",
"56": "binance.okrelayer.xyz",
"137": "polygon.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"
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":
version "0.8.1"
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"
ethers "^6.4.0"
"@tornado/core@git+https://git.tornado.ws/tornadocontrib/tornado-core.git#c155649719d121fe50799420750f531eb50589ce":
version "1.0.15"
resolved "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.19"
resolved "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#f73b9ecbff0b106854752388f94c2d0c30e54975"
dependencies:
"@metamask/eth-sig-util" "^7.0.3"
"@tornado/contracts" "^1.0.1"
@ -900,62 +905,62 @@
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f"
integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==
"@typescript-eslint/eslint-plugin@^8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.6.0.tgz#20049754ff9f6d3a09bf240297f029ce04290999"
integrity sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==
"@typescript-eslint/eslint-plugin@^8.7.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz#b2b02a5447cdc885950eb256b3b8a97b92031bd3"
integrity sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==
dependencies:
"@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "8.6.0"
"@typescript-eslint/type-utils" "8.6.0"
"@typescript-eslint/utils" "8.6.0"
"@typescript-eslint/visitor-keys" "8.6.0"
"@typescript-eslint/scope-manager" "8.8.0"
"@typescript-eslint/type-utils" "8.8.0"
"@typescript-eslint/utils" "8.8.0"
"@typescript-eslint/visitor-keys" "8.8.0"
graphemer "^1.4.0"
ignore "^5.3.1"
natural-compare "^1.4.0"
ts-api-utils "^1.3.0"
"@typescript-eslint/parser@^8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.6.0.tgz#02e092b9dc8b4e319172af620d0d39b337d948f6"
integrity sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==
"@typescript-eslint/parser@^8.7.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.8.0.tgz#ee4397c70230c4eee030456924c0fba480072f5e"
integrity sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==
dependencies:
"@typescript-eslint/scope-manager" "8.6.0"
"@typescript-eslint/types" "8.6.0"
"@typescript-eslint/typescript-estree" "8.6.0"
"@typescript-eslint/visitor-keys" "8.6.0"
"@typescript-eslint/scope-manager" "8.8.0"
"@typescript-eslint/types" "8.8.0"
"@typescript-eslint/typescript-estree" "8.8.0"
"@typescript-eslint/visitor-keys" "8.8.0"
debug "^4.3.4"
"@typescript-eslint/scope-manager@8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.6.0.tgz#28cc2fc26a84b75addf45091a2c6283e29e2c982"
integrity sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==
"@typescript-eslint/scope-manager@8.8.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz#30b23a6ae5708bd7882e40675ef2f1b2beac741f"
integrity sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==
dependencies:
"@typescript-eslint/types" "8.6.0"
"@typescript-eslint/visitor-keys" "8.6.0"
"@typescript-eslint/types" "8.8.0"
"@typescript-eslint/visitor-keys" "8.8.0"
"@typescript-eslint/type-utils@8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.6.0.tgz#d4347e637478bef88cee1db691fcfa20ade9b8a0"
integrity sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==
"@typescript-eslint/type-utils@8.8.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.8.0.tgz#a0ca1c8a90d94b101176a169d7a0958187408d33"
integrity sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==
dependencies:
"@typescript-eslint/typescript-estree" "8.6.0"
"@typescript-eslint/utils" "8.6.0"
"@typescript-eslint/typescript-estree" "8.8.0"
"@typescript-eslint/utils" "8.8.0"
debug "^4.3.4"
ts-api-utils "^1.3.0"
"@typescript-eslint/types@8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.6.0.tgz#cdc3a16f83f2f0663d6723e9fd032331cdd9f51c"
integrity sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==
"@typescript-eslint/types@8.8.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.8.0.tgz#08ea5df6c01984d456056434641491fbf7a1bf43"
integrity sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==
"@typescript-eslint/typescript-estree@8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.6.0.tgz#f945506de42871f04868371cb5bf21e8f7266e01"
integrity sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==
"@typescript-eslint/typescript-estree@8.8.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz#072eaab97fdb63513fabfe1cf271812affe779e3"
integrity sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==
dependencies:
"@typescript-eslint/types" "8.6.0"
"@typescript-eslint/visitor-keys" "8.6.0"
"@typescript-eslint/types" "8.8.0"
"@typescript-eslint/visitor-keys" "8.8.0"
debug "^4.3.4"
fast-glob "^3.3.2"
is-glob "^4.0.3"
@ -963,22 +968,22 @@
semver "^7.6.0"
ts-api-utils "^1.3.0"
"@typescript-eslint/utils@8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.6.0.tgz#175fe893f32804bed1e72b3364ea6bbe1044181c"
integrity sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==
"@typescript-eslint/utils@8.8.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.8.0.tgz#bd8607e3a68c461b69169c7a5824637dc9e8b3f1"
integrity sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@typescript-eslint/scope-manager" "8.6.0"
"@typescript-eslint/types" "8.6.0"
"@typescript-eslint/typescript-estree" "8.6.0"
"@typescript-eslint/scope-manager" "8.8.0"
"@typescript-eslint/types" "8.8.0"
"@typescript-eslint/typescript-estree" "8.8.0"
"@typescript-eslint/visitor-keys@8.6.0":
version "8.6.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.6.0.tgz#5432af4a1753f376f35ab5b891fc9db237aaf76f"
integrity sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==
"@typescript-eslint/visitor-keys@8.8.0":
version "8.8.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz#f93965abd38c82a1a1f5574290a50d02daf1cd2e"
integrity sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==
dependencies:
"@typescript-eslint/types" "8.6.0"
"@typescript-eslint/types" "8.8.0"
eslint-visitor-keys "^3.4.3"
"@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"
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
webpack@^5.94.0:
version "5.94.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.94.0.tgz#77a6089c716e7ab90c1c67574a28da518a20970f"
integrity sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==
webpack@^5.95.0:
version "5.95.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.95.0.tgz#8fd8c454fa60dad186fbe36c400a55848307b4c0"
integrity sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==
dependencies:
"@types/estree" "^1.0.5"
"@webassemblyjs/ast" "^1.12.1"