Compare commits

..

2 Commits

Author SHA1 Message Date
2826a246ac
Use 4 tab size 2024-10-26 20:27:26 +00:00
42e6b1713b
Improved Note Encryption 2024-10-26 20:22:22 +00:00
16 changed files with 9528 additions and 4746 deletions

View File

@ -1,65 +1,43 @@
module.exports = { module.exports = {
"env": { env: {
"es2021": true, es2021: true,
"node": true node: true,
}, },
"extends": [ extends: [
"eslint:recommended", 'prettier',
"plugin:@typescript-eslint/recommended", 'eslint:recommended',
"plugin:import/recommended", 'plugin:@typescript-eslint/recommended',
"plugin:import/typescript", 'plugin:import/recommended',
"prettier", 'plugin:import/typescript',
"plugin:prettier/recommended", 'plugin:prettier/recommended',
],
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"prettier"
],
"rules": {
"prettier/prettier": [
"error",
{
singleQuote: true,
printWidth: 120
}
], ],
"import/order": ["error"], overrides: [
/** {
"indent": [ env: {
"error", node: true,
2 },
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
], ],
**/ parser: '@typescript-eslint/parser',
"linebreak-style": [ parserOptions: {
"error", ecmaVersion: 'latest',
"unix" sourceType: 'module',
], },
"quotes": [ plugins: ['@typescript-eslint', 'prettier'],
"error", rules: {
"single" 'prettier/prettier': [
], 'error',
"semi": [ {
"error", tabWidth: 4,
"always" singleQuote: true,
], },
"@typescript-eslint/no-unused-vars": ["warn"] ],
} 'import/order': ['error'],
} '@typescript-eslint/no-unused-vars': ['warn'],
'@typescript-eslint/no-unused-expressions': ['off'],
},
};

7154
dist/cli.js vendored

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,7 @@ export declare function loadSavedEvents<T extends MinimalEvents>({ name, userDir
name: string; name: string;
userDirectory: string; userDirectory: string;
}): Promise<BaseEvents<T>>; }): Promise<BaseEvents<T>>;
export declare function download({ name, cacheDirectory }: { export declare function download({ name, cacheDirectory, }: {
name: string; name: string;
cacheDirectory: string; cacheDirectory: string;
}): Promise<string>; }): Promise<string>;

View File

@ -15,24 +15,24 @@ export declare class NodeTornadoService extends BaseTornadoService {
nativeCurrency: string; nativeCurrency: string;
treeCache?: TreeCache; treeCache?: TreeCache;
constructor(serviceConstructor: NodeTornadoServiceConstructor); constructor(serviceConstructor: 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;
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void; updateGraphProgress({ type, fromBlock, toBlock, count, }: Parameters<BatchEventOnProgress>[0]): void;
getEventsFromDB(): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>; getEventsFromDB(): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<DepositsEvents | WithdrawalsEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<DepositsEvents | WithdrawalsEvents>>;
validateEvents<S>({ events, lastBlock, hasNewEvents, }: BaseEvents<DepositsEvents | WithdrawalsEvents> & { validateEvents<S>({ events, lastBlock, hasNewEvents, }: BaseEvents<DepositsEvents | WithdrawalsEvents> & {
hasNewEvents?: boolean; hasNewEvents?: boolean;
}): Promise<S>; }): Promise<S>;
saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>): Promise<void>; saveEvents({ events, lastBlock, }: BaseEvents<DepositsEvents | WithdrawalsEvents>): Promise<void>;
} }
export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & NodeServiceConstructor; export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & NodeServiceConstructor;
export declare class NodeEchoService extends BaseEchoService { export declare class NodeEchoService extends BaseEchoService {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor(serviceConstructor: NodeEchoServiceConstructor); constructor(serviceConstructor: 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>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<EchoEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<EchoEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>): Promise<void>;
@ -42,8 +42,8 @@ export declare class NodeEncryptedNotesService extends BaseEncryptedNotesService
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor(serviceConstructor: NodeEncryptedNotesServiceConstructor); constructor(serviceConstructor: 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>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<EncryptedNotesEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<EncryptedNotesEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<EncryptedNotesEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<EncryptedNotesEvents>): Promise<void>;
@ -53,9 +53,9 @@ export declare class NodeGovernanceService extends BaseGovernanceService {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor(serviceConstructor: NodeGovernanceServiceConstructor); constructor(serviceConstructor: 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;
getEventsFromDB(): Promise<BaseEvents<AllGovernanceEvents>>; getEventsFromDB(): Promise<BaseEvents<AllGovernanceEvents>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<AllGovernanceEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<AllGovernanceEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>): Promise<void>;
@ -65,8 +65,8 @@ export declare class NodeRegistryService extends BaseRegistryService {
cacheDirectory: string; cacheDirectory: string;
userDirectory: string; userDirectory: string;
constructor(serviceConstructor: NodeRegistryServiceConstructor); constructor(serviceConstructor: 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>>;
getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<RegistersEvents>>; getEventsFromCache(): Promise<import("@tornado/core").CachedEvents<RegistersEvents>>;
saveEvents({ events, lastBlock }: BaseEvents<RegistersEvents>): Promise<void>; saveEvents({ events, lastBlock }: BaseEvents<RegistersEvents>): Promise<void>;

View File

@ -29,7 +29,7 @@ export declare class TreeCache {
currency: string; currency: string;
userDirectory: string; userDirectory: string;
PARTS_COUNT: number; PARTS_COUNT: number;
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT }: TreeCacheConstructor); constructor({ netId, amount, currency, userDirectory, PARTS_COUNT, }: TreeCacheConstructor);
getInstanceName(): string; getInstanceName(): string;
createTree(events: DepositsEvents[], tree: MerkleTree): Promise<void>; createTree(events: DepositsEvents[], tree: MerkleTree): Promise<void>;
} }

View File

@ -1,82 +1,84 @@
{ {
"name": "@tornado/cli", "name": "@tornado/cli",
"version": "1.0.11-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",
"bin": { "bin": {
"tornado-cli": "./dist/cli.js" "tornado-cli": "./dist/cli.js"
}, },
"scripts": { "scripts": {
"typechain": "typechain --target ethers-v6 --out-dir src/typechain src/abi/*.json", "typechain": "typechain --target ethers-v6 --out-dir src/typechain src/abi/*.json",
"types": "tsc --declaration --emitDeclarationOnly", "types": "tsc --declaration --emitDeclarationOnly",
"lint": "eslint src/**/*.ts --ext .ts --ignore-pattern src/typechain", "lint": "eslint src/**/*.ts --ext .ts --ignore-pattern src/typechain",
"copy:worker": "ts-node scripts/copyFile.ts node_modules/@tornado/core/dist/merkleTreeWorker.js static/merkleTreeWorker.js", "copy:worker": "ts-node scripts/copyFile.ts node_modules/@tornado/core/dist/merkleTreeWorker.js static/merkleTreeWorker.js",
"build:node": "webpack", "build:node": "webpack",
"build": "yarn types && yarn build:node", "build": "yarn types && yarn build:node",
"start": "ts-node src/cli.ts", "start": "ts-node src/cli.ts",
"help": "ts-node src/cli.ts help", "help": "ts-node src/cli.ts help",
"create": "ts-node src/cli.ts create", "create": "ts-node src/cli.ts create",
"deposit": "ts-node src/cli.ts deposit", "deposit": "ts-node src/cli.ts deposit",
"depositInvoice": "ts-node src/cli.ts depositInvoice", "depositInvoice": "ts-node src/cli.ts depositInvoice",
"withdraw": "ts-node src/cli.ts withdraw", "withdraw": "ts-node src/cli.ts withdraw",
"compliance": "ts-node src/cli.ts compliance", "compliance": "ts-node src/cli.ts compliance",
"updateEvents": "ts-node src/cli.ts updateEvents", "updateEvents": "ts-node src/cli.ts updateEvents",
"relayers": "ts-node src/cli.ts relayers", "relayers": "ts-node src/cli.ts relayers",
"createAccount": "ts-node src/cli.ts createAccount", "createAccount": "ts-node src/cli.ts createAccount",
"decryptNotes": "ts-node src/cli.ts decryptNotes", "decrypt": "ts-node src/cli.ts decrypt",
"send": "ts-node src/cli.ts send", "send": "ts-node src/cli.ts send",
"balance": "ts-node src/cli.ts balance", "balance": "ts-node src/cli.ts balance",
"sign": "ts-node src/cli.ts sign", "sign": "ts-node src/cli.ts sign",
"broadcast": "ts-node src/cli.ts broadcast" "broadcast": "ts-node src/cli.ts broadcast",
}, "proposals": "ts-node src/cli.ts proposals",
"author": "", "delegates": "ts-node src/cli.ts delegates"
"license": "MIT", },
"files": [ "author": "",
"dist", "license": "MIT",
"scripts", "files": [
"src", "dist",
"static", "scripts",
".env.example", "src",
".eslintrc.js", "static",
".gitattributes", ".env.example",
".gitignore", ".eslintrc.js",
".npmrc", ".gitattributes",
"logo.png", ".gitignore",
"logo2.png", ".npmrc",
"tsconfig.json", "logo.png",
"yarn.lock" "logo2.png",
], "tsconfig.json",
"dependencies": {}, "yarn.lock"
"optionalDependencies": {}, ],
"devDependencies": { "dependencies": {},
"@colors/colors": "^1.6.0", "optionalDependencies": {},
"@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#9f4044d11033e99e0a863989864d1578a93de531", "devDependencies": {
"@typechain/ethers-v6": "^0.5.1", "@colors/colors": "^1.6.0",
"@types/figlet": "^1.7.0", "@tornado/core": "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#8041bd7f7801fd97a87d8c1945c0251b49032ec3",
"@typescript-eslint/eslint-plugin": "^8.9.0", "@typechain/ethers-v6": "^0.5.1",
"@typescript-eslint/parser": "^8.9.0", "@types/figlet": "^1.7.0",
"bloomfilter.js": "^1.0.2", "@typescript-eslint/eslint-plugin": "^8.11.0",
"cli-table3": "^0.6.4", "@typescript-eslint/parser": "^8.11.0",
"commander": "^12.0.0", "bloomfilter.js": "^1.0.2",
"dotenv": "^16.4.5", "cli-table3": "^0.6.4",
"esbuild-loader": "^4.2.2", "commander": "^12.0.0",
"eslint": "8.57.0", "dotenv": "^16.4.5",
"eslint-config-prettier": "^9.1.0", "esbuild-loader": "^4.2.2",
"eslint-import-resolver-typescript": "^3.6.3", "eslint": "8.57.0",
"eslint-plugin-import": "^2.31.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1", "eslint-import-resolver-typescript": "^3.6.3",
"figlet": "^1.8.0", "eslint-plugin-import": "^2.31.0",
"http-proxy-agent": "^7.0.2", "eslint-plugin-prettier": "^5.2.1",
"https-proxy-agent": "^7.0.4", "figlet": "^1.8.0",
"moment": "^2.30.1", "http-proxy-agent": "^7.0.2",
"prettier": "^3.2.5", "https-proxy-agent": "^7.0.4",
"socks-proxy-agent": "^8.0.3", "moment": "^2.30.1",
"ts-node": "^10.9.2", "prettier": "^3.2.5",
"tsc": "^2.0.4", "socks-proxy-agent": "^8.0.3",
"typechain": "^8.3.2", "ts-node": "^10.9.2",
"typescript": "^5.6.3", "tsc": "^2.0.4",
"webpack": "^5.95.0", "typechain": "^8.3.2",
"webpack-cli": "^5.1.4" "typescript": "^5.6.3",
} "webpack": "^5.95.0",
"webpack-cli": "^5.1.4"
}
} }

View File

@ -2,14 +2,14 @@ import { argv } from 'process';
import { copyFile } from 'fs'; import { copyFile } from 'fs';
function copyFiles() { function copyFiles() {
const [, , inFile, outFile] = argv; const [, , inFile, outFile] = argv;
copyFile(inFile, outFile, function(err) { copyFile(inFile, outFile, function(err) {
if (err) { if (err) {
throw err; throw err;
} }
console.log(`Copied ${inFile} to ${outFile}`) console.log(`Copied ${inFile} to ${outFile}`)
}) })
} }
copyFiles() copyFiles()

File diff suppressed because it is too large Load Diff

View File

@ -1,167 +1,184 @@
import path from 'path'; import path from 'path';
import { deflate, constants } from 'zlib'; import { deflate, constants } from 'zlib';
import { stat, mkdir, readFile, writeFile } from 'fs/promises'; import { stat, mkdir, readFile, writeFile } from 'fs/promises';
import { BaseEvents, CachedEvents, MinimalEvents, zipAsync, unzipAsync } from '@tornado/core'; import {
BaseEvents,
CachedEvents,
MinimalEvents,
zipAsync,
unzipAsync,
} from '@tornado/core';
export async function existsAsync(fileOrDir: string): Promise<boolean> { export async function existsAsync(fileOrDir: string): Promise<boolean> {
try { try {
await stat(fileOrDir); await stat(fileOrDir);
return true; return true;
} catch { } catch {
return false; return false;
} }
} }
/** /**
* Supports legacy gz format for legacy UI * Supports legacy gz format for legacy UI
*/ */
export function deflateAsync(data: Uint8Array): Promise<Buffer> { export function deflateAsync(data: Uint8Array): Promise<Buffer> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
deflate( deflate(
data, data,
{ {
level: constants.Z_BEST_COMPRESSION, level: constants.Z_BEST_COMPRESSION,
strategy: constants.Z_FILTERED, strategy: constants.Z_FILTERED,
}, },
(err, buffer) => { (err, buffer) => {
if (!err) { if (!err) {
resolve(buffer); resolve(buffer);
} else { } else {
reject(err); reject(err);
} }
}, },
); );
}); });
} }
export async function saveLegacyFile({ export async function saveLegacyFile({
fileName, fileName,
userDirectory, userDirectory,
dataString, dataString,
}: { }: {
fileName: string; fileName: string;
userDirectory: string; userDirectory: string;
dataString: string; dataString: string;
}) { }) {
fileName = fileName.toLowerCase(); fileName = fileName.toLowerCase();
const filePath = path.join(userDirectory, fileName); const filePath = path.join(userDirectory, fileName);
const payload = await deflateAsync(new TextEncoder().encode(dataString)); const payload = await deflateAsync(new TextEncoder().encode(dataString));
if (!(await existsAsync(userDirectory))) { if (!(await existsAsync(userDirectory))) {
await mkdir(userDirectory, { recursive: true }); await mkdir(userDirectory, { recursive: true });
} }
await writeFile(filePath + '.gz', payload); await writeFile(filePath + '.gz', payload);
} }
export async function saveUserFile({ export async function saveUserFile({
fileName, fileName,
userDirectory, userDirectory,
dataString, dataString,
}: { }: {
fileName: string; fileName: string;
userDirectory: string; userDirectory: string;
dataString: string; dataString: string;
}) { }) {
fileName = fileName.toLowerCase(); fileName = fileName.toLowerCase();
const filePath = path.join(userDirectory, fileName); const filePath = path.join(userDirectory, fileName);
const payload = await zipAsync({ const payload = await zipAsync({
[fileName]: new TextEncoder().encode(dataString), [fileName]: new TextEncoder().encode(dataString),
}); });
if (!(await existsAsync(userDirectory))) { if (!(await existsAsync(userDirectory))) {
await mkdir(userDirectory, { recursive: true }); await mkdir(userDirectory, { recursive: true });
} }
await writeFile(filePath + '.zip', payload); await writeFile(filePath + '.zip', payload);
await writeFile(filePath, dataString); await writeFile(filePath, dataString);
} }
export async function loadSavedEvents<T extends MinimalEvents>({ export async function loadSavedEvents<T extends MinimalEvents>({
name, name,
userDirectory, userDirectory,
}: { }: {
name: string; name: string;
userDirectory: string; userDirectory: string;
}): 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: 0, lastBlock: 0,
}; };
} }
try { try {
const events = JSON.parse(await readFile(filePath, { encoding: 'utf8' })) as T[]; const events = JSON.parse(
await readFile(filePath, { encoding: 'utf8' }),
) as T[];
return { return {
events, events,
lastBlock: events[events.length - 1]?.blockNumber || 0, 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: 0, lastBlock: 0,
}; };
} }
} }
export async function download({ name, cacheDirectory }: { name: string; cacheDirectory: string }) { export async function download({
const fileName = `${name}.json`.toLowerCase(); name,
const zipName = `${fileName}.zip`; cacheDirectory,
const zipPath = path.join(cacheDirectory, zipName); }: {
name: string;
cacheDirectory: string;
}) {
const fileName = `${name}.json`.toLowerCase();
const zipName = `${fileName}.zip`;
const zipPath = path.join(cacheDirectory, zipName);
const data = await readFile(zipPath); const data = await readFile(zipPath);
const { [fileName]: content } = await unzipAsync(data); const { [fileName]: content } = await unzipAsync(data);
return new TextDecoder().decode(content); return new TextDecoder().decode(content);
} }
export async function loadCachedEvents<T extends MinimalEvents>({ export async function loadCachedEvents<T extends MinimalEvents>({
name, name,
cacheDirectory, cacheDirectory,
deployedBlock, deployedBlock,
}: { }: {
name: string; name: string;
cacheDirectory: string; cacheDirectory: string;
deployedBlock: number; deployedBlock: number;
}): Promise<CachedEvents<T>> { }): Promise<CachedEvents<T>> {
try { try {
const module = await download({ cacheDirectory, name }); const module = await download({ cacheDirectory, name });
if (module) { if (module) {
const events = JSON.parse(module); const events = JSON.parse(module);
const lastBlock = events && events.length ? events[events.length - 1].blockNumber : deployedBlock; const lastBlock =
events && events.length
? events[events.length - 1].blockNumber
: deployedBlock;
return { return {
events, events,
lastBlock, lastBlock,
fromCache: true, fromCache: true,
}; };
}
return {
events: [],
lastBlock: deployedBlock,
fromCache: true,
};
} catch (err) {
console.log('Method loadCachedEvents has error');
console.log(err);
return {
events: [],
lastBlock: deployedBlock,
fromCache: true,
};
} }
return {
events: [],
lastBlock: deployedBlock,
fromCache: true,
};
} catch (err) {
console.log('Method loadCachedEvents has error');
console.log(err);
return {
events: [],
lastBlock: deployedBlock,
fromCache: true,
};
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,75 +3,78 @@ import { computeAddress, getAddress, Mnemonic } from 'ethers';
import { validateUrl } from '@tornado/core'; import { validateUrl } from '@tornado/core';
export function parseNumber(value?: string | number): number { export function parseNumber(value?: string | number): number {
if (!value || isNaN(Number(value))) { if (!value || isNaN(Number(value))) {
throw new InvalidArgumentError('Invalid Number'); throw new InvalidArgumentError('Invalid Number');
} }
return Number(value); return Number(value);
} }
export function parseUrl(value?: string): string { export function parseUrl(value?: string): string {
if (!value || !validateUrl(value, ['http:', 'https:'])) { if (!value || !validateUrl(value, ['http:', 'https:'])) {
throw new InvalidArgumentError('Invalid URL'); throw new InvalidArgumentError('Invalid URL');
} }
return value; return value;
} }
export function parseRelayer(value?: string): string { export function parseRelayer(value?: string): string {
if (!value || !(value.endsWith('.eth') || validateUrl(value, ['http:', 'https:']))) { if (
throw new InvalidArgumentError('Invalid Relayer ETH address or URL'); !value ||
} !(value.endsWith('.eth') || validateUrl(value, ['http:', 'https:']))
return value; ) {
throw new InvalidArgumentError('Invalid Relayer ETH address or URL');
}
return value;
} }
export function parseAddress(value?: string): string { export function parseAddress(value?: string): string {
if (!value) { if (!value) {
throw new InvalidArgumentError('Invalid Address'); throw new InvalidArgumentError('Invalid Address');
} }
try { try {
return getAddress(value); return getAddress(value);
} catch { } catch {
throw new InvalidArgumentError('Invalid Address'); throw new InvalidArgumentError('Invalid Address');
} }
} }
export function parseMnemonic(value?: string): string { export function parseMnemonic(value?: string): string {
if (!value) { if (!value) {
throw new InvalidArgumentError('Invalid Mnemonic'); throw new InvalidArgumentError('Invalid Mnemonic');
} }
try { try {
Mnemonic.fromPhrase(value); Mnemonic.fromPhrase(value);
} catch { } catch {
throw new InvalidArgumentError('Invalid Mnemonic'); throw new InvalidArgumentError('Invalid Mnemonic');
} }
return value; return value;
} }
export function parseKey(value?: string): string { export function parseKey(value?: string): string {
if (!value) { if (!value) {
throw new InvalidArgumentError('Invalid Private Key'); throw new InvalidArgumentError('Invalid Private Key');
} }
if (value.length === 64) { if (value.length === 64) {
value = '0x' + value; value = '0x' + value;
} }
try { try {
computeAddress(value); computeAddress(value);
} catch { } catch {
throw new InvalidArgumentError('Invalid Private Key'); throw new InvalidArgumentError('Invalid Private Key');
} }
return value; return value;
} }
/** /**
* Recovery key shouldn't have a 0x prefix (Also this is how the UI generates) * Recovery key shouldn't have a 0x prefix (Also this is how the UI generates)
*/ */
export function parseRecoveryKey(value?: string): string { export function parseRecoveryKey(value?: string): string {
if (!value) { if (!value) {
throw new InvalidArgumentError('Invalid Recovery Key'); throw new InvalidArgumentError('Invalid Recovery Key');
} }
try { try {
computeAddress('0x' + value); computeAddress('0x' + value);
} catch { } catch {
throw new InvalidArgumentError('Invalid Recovery Key'); throw new InvalidArgumentError('Invalid Recovery Key');
} }
return value; return value;
} }

View File

@ -10,104 +10,110 @@ import type { NetIdType } from '@tornado/core';
import { saveUserFile } from './data'; import { saveUserFile } from './data';
export interface TreeCacheConstructor { export interface TreeCacheConstructor {
netId: NetIdType; netId: NetIdType;
amount: string; amount: string;
currency: string; currency: string;
userDirectory: string; userDirectory: string;
PARTS_COUNT?: number; PARTS_COUNT?: number;
LEAVES?: number; LEAVES?: number;
zeroElement?: string; zeroElement?: string;
} }
export interface treeMetadata { export interface treeMetadata {
blockNumber: number; blockNumber: number;
logIndex: number; logIndex: number;
transactionHash: string; transactionHash: string;
timestamp: number; timestamp: number;
from: string; from: string;
leafIndex: number; leafIndex: number;
} }
export class TreeCache { export class TreeCache {
netId: NetIdType; netId: NetIdType;
amount: string; amount: string;
currency: string; currency: string;
userDirectory: string; userDirectory: string;
PARTS_COUNT: number; PARTS_COUNT: number;
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT = 4 }: TreeCacheConstructor) { constructor({
this.netId = netId; netId,
this.amount = amount; amount,
this.currency = currency; currency,
this.userDirectory = userDirectory; userDirectory,
PARTS_COUNT = 4,
}: TreeCacheConstructor) {
this.netId = netId;
this.amount = amount;
this.currency = currency;
this.userDirectory = userDirectory;
this.PARTS_COUNT = PARTS_COUNT; this.PARTS_COUNT = PARTS_COUNT;
} }
getInstanceName(): string { getInstanceName(): string {
return `deposits_${this.netId}_${this.currency}_${this.amount}`; return `deposits_${this.netId}_${this.currency}_${this.amount}`;
} }
async createTree(events: DepositsEvents[], tree: MerkleTree) { async createTree(events: DepositsEvents[], tree: MerkleTree) {
const bloom = new BloomFilter(events.length); const bloom = new BloomFilter(events.length);
console.log(`Creating cached tree for ${this.getInstanceName()}\n`); console.log(`Creating cached tree for ${this.getInstanceName()}\n`);
// events indexed by commitment // events indexed by commitment
const eventsData = events.reduce( const eventsData = events.reduce(
(acc, { leafIndex, commitment, ...rest }, i) => { (acc, { leafIndex, commitment, ...rest }, i) => {
if (leafIndex !== i) { if (leafIndex !== i) {
throw new Error(`leafIndex (${leafIndex}) !== i (${i})`); throw new Error(`leafIndex (${leafIndex}) !== i (${i})`);
} }
acc[commitment] = { ...rest, leafIndex }; acc[commitment] = { ...rest, leafIndex };
return acc; return acc;
},
{} as { [key in string]: treeMetadata },
);
const slices = tree.getTreeSlices(this.PARTS_COUNT);
await Promise.all(
slices.map(async (slice, index) => {
const metadata = slice.elements.reduce((acc, curr) => {
if (index < this.PARTS_COUNT - 1) {
bloom.add(curr);
}
acc.push(eventsData[curr]);
return acc;
}, [] as treeMetadata[]);
const dataString =
JSON.stringify(
{
...slice,
metadata,
}, },
null, {} as { [key in string]: treeMetadata },
2, );
) + '\n';
const fileName = `${this.getInstanceName()}_slice${index + 1}.json`; const slices = tree.getTreeSlices(this.PARTS_COUNT);
await Promise.all(
slices.map(async (slice, index) => {
const metadata = slice.elements.reduce((acc, curr) => {
if (index < this.PARTS_COUNT - 1) {
bloom.add(curr);
}
acc.push(eventsData[curr]);
return acc;
}, [] as treeMetadata[]);
const dataString =
JSON.stringify(
{
...slice,
metadata,
},
null,
2,
) + '\n';
const fileName = `${this.getInstanceName()}_slice${index + 1}.json`;
await saveUserFile({
fileName,
userDirectory: this.userDirectory,
dataString,
});
}),
);
const dataString = bloom.serialize() + '\n';
const fileName = `${this.getInstanceName()}_bloom.json`;
await saveUserFile({ await saveUserFile({
fileName, fileName,
userDirectory: this.userDirectory, userDirectory: this.userDirectory,
dataString, dataString,
}); });
}), }
);
const dataString = bloom.serialize() + '\n';
const fileName = `${this.getInstanceName()}_bloom.json`;
await saveUserFile({
fileName,
userDirectory: this.userDirectory,
dataString,
});
}
} }

View File

@ -1,25 +1,25 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
declare module 'bloomfilter.js' { declare module 'bloomfilter.js' {
export default class BloomFilter { export default class BloomFilter {
m: number; m: number;
k: number; k: number;
size: number; size: number;
bitview: any; bitview: any;
constructor(n: number, false_postive_tolerance?: number); constructor(n: number, false_postive_tolerance?: number);
calculateHash(x: number, m: number, i: number): number; calculateHash(x: number, m: number, i: number): number;
test(data: any): boolean; test(data: any): boolean;
add(data: any): void; add(data: any): void;
bytelength(): number; bytelength(): number;
view(): Uint8Array; view(): Uint8Array;
serialize(): string; serialize(): string;
deserialize(serialized: string): BloomFilter; deserialize(serialized: string): BloomFilter;
} }
} }

Binary file not shown.

View File

@ -2,44 +2,44 @@ const path = require('path');
const { BannerPlugin } = require('webpack'); const { BannerPlugin } = require('webpack');
const esbuildLoader = { const esbuildLoader = {
test: /\.ts?$/, test: /\.ts?$/,
loader: 'esbuild-loader', loader: 'esbuild-loader',
options: { options: {
loader: 'ts', loader: 'ts',
target: 'es2022', target: 'es2022',
} }
} }
module.exports = [ module.exports = [
{ {
mode: 'production', mode: 'production',
module: { module: {
rules: [esbuildLoader] rules: [esbuildLoader]
}, },
entry: './src/cli.ts', entry: './src/cli.ts',
output: { output: {
filename: 'cli.js', filename: 'cli.js',
path: path.resolve(__dirname, './dist'), path: path.resolve(__dirname, './dist'),
}, },
target: 'node', target: 'node',
plugins: [ plugins: [
new BannerPlugin({ new BannerPlugin({
banner: '#!/usr/bin/env node\n', banner: '#!/usr/bin/env node\n',
raw: true raw: true
}) })
], ],
resolve: { resolve: {
extensions: ['.tsx', '.ts', '.js'], extensions: ['.tsx', '.ts', '.js'],
alias: { alias: {
'fflate': 'fflate/node' 'fflate': 'fflate/node'
}, },
fallback: { fallback: {
'@tornado/websnark/src/utils': '@tornado/websnark/src/utils.js', '@tornado/websnark/src/utils': '@tornado/websnark/src/utils.js',
'@tornado/websnark/src/groth16': '@tornado/websnark/src/groth16.js', '@tornado/websnark/src/groth16': '@tornado/websnark/src/groth16.js',
} }
}, },
optimization: { optimization: {
minimize: false, minimize: false,
}
} }
}
]; ];

120
yarn.lock
View File

@ -648,10 +648,10 @@
"@metamask/superstruct" "^3.1.0" "@metamask/superstruct" "^3.1.0"
"@metamask/utils" "^9.0.0" "@metamask/utils" "^9.0.0"
"@metamask/eth-sig-util@^7.0.3": "@metamask/eth-sig-util@^8.0.0":
version "7.0.3" version "8.0.0"
resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-7.0.3.tgz#be9e444fe0b8474c04e2ff42fd983173767f6ac0" resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-8.0.0.tgz#6310d93cd1101cab3cc6bc2a1ff526290ed2695b"
integrity sha512-PAtGnOkYvh90k2lEZldq/FK7GTLF6WxE+2bV85PoA3pqlJnmJCAY62tuvxHSwnVngSKlc4mcNvjnUg2eYO6JGg== integrity sha512-IwE6aoxUL39IhmsAgE4nk+OZbNo+ThFZRNsUjE1pjdEa4MFpWzm1Rue4zJ5DMy1oUyZBi/aiCLMhdMnjl2bh2Q==
dependencies: dependencies:
"@ethereumjs/util" "^8.1.0" "@ethereumjs/util" "^8.1.0"
"@metamask/abi-utils" "^2.0.4" "@metamask/abi-utils" "^2.0.4"
@ -777,20 +777,20 @@
"@noble/hashes" "~1.4.0" "@noble/hashes" "~1.4.0"
"@scure/base" "~1.1.6" "@scure/base" "~1.1.6"
"@tornado/contracts@git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#ece511f424dc811c3aec149a4bf0e3731c0598a4": "@tornado/contracts@git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#1b1d707878c16a3dc60d295299d4f0e7ce6ba831":
version "1.0.2" version "1.0.2"
resolved "git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#ece511f424dc811c3aec149a4bf0e3731c0598a4" resolved "git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#1b1d707878c16a3dc60d295299d4f0e7ce6ba831"
dependencies: dependencies:
"@openzeppelin/contracts" "5.0.2" "@openzeppelin/contracts" "5.0.2"
"@openzeppelin/contracts-v3" "npm:@openzeppelin/contracts@3.2.0-rc.0" "@openzeppelin/contracts-v3" "npm:@openzeppelin/contracts@3.2.0-rc.0"
ethers "^6.13.4" ethers "^6.13.4"
"@tornado/core@git+https://git.tornado.ws/tornadocontrib/tornado-core.git#9f4044d11033e99e0a863989864d1578a93de531": "@tornado/core@git+https://git.tornado.ws/tornadocontrib/tornado-core.git#8041bd7f7801fd97a87d8c1945c0251b49032ec3":
version "1.0.19" version "1.0.19"
resolved "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#9f4044d11033e99e0a863989864d1578a93de531" resolved "git+https://git.tornado.ws/tornadocontrib/tornado-core.git#8041bd7f7801fd97a87d8c1945c0251b49032ec3"
dependencies: dependencies:
"@metamask/eth-sig-util" "^7.0.3" "@metamask/eth-sig-util" "^8.0.0"
"@tornado/contracts" "git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#ece511f424dc811c3aec149a4bf0e3731c0598a4" "@tornado/contracts" "git+https://git.tornado.ws/tornadocontrib/tornado-contracts.git#1b1d707878c16a3dc60d295299d4f0e7ce6ba831"
"@tornado/fixed-merkle-tree" "^0.7.3" "@tornado/fixed-merkle-tree" "^0.7.3"
"@tornado/snarkjs" "^0.1.20" "@tornado/snarkjs" "^0.1.20"
"@tornado/websnark" "^0.0.4" "@tornado/websnark" "^0.0.4"
@ -907,62 +907,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.9.0": "@typescript-eslint/eslint-plugin@^8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.9.0.tgz#bf0b25305b0bf014b4b194a6919103d7ac2a7907" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.11.0.tgz#c3f087d20715fa94310b30666c08b3349e0ab084"
integrity sha512-Y1n621OCy4m7/vTXNlCbMVp87zSd7NH0L9cXD8aIpOaNlzeWxIK4+Q19A68gSmTNRZn92UjocVUWDthGxtqHFg== integrity sha512-KhGn2LjW1PJT2A/GfDpiyOfS4a8xHQv2myUagTM5+zsormOmBlYsnQ6pobJ8XxJmh6hnHwa2Mbe3fPrDJoDhbA==
dependencies: dependencies:
"@eslint-community/regexpp" "^4.10.0" "@eslint-community/regexpp" "^4.10.0"
"@typescript-eslint/scope-manager" "8.9.0" "@typescript-eslint/scope-manager" "8.11.0"
"@typescript-eslint/type-utils" "8.9.0" "@typescript-eslint/type-utils" "8.11.0"
"@typescript-eslint/utils" "8.9.0" "@typescript-eslint/utils" "8.11.0"
"@typescript-eslint/visitor-keys" "8.9.0" "@typescript-eslint/visitor-keys" "8.11.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.9.0": "@typescript-eslint/parser@^8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.9.0.tgz#0cecda6def8aef95d7c7098359c0fda5a362d6ad" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.11.0.tgz#2ad1481388dc1c937f50b2d138c9ca57cc6c5cce"
integrity sha512-U+BLn2rqTTHnc4FL3FJjxaXptTxmf9sNftJK62XLz4+GxG3hLHm/SUNaaXP5Y4uTiuYoL5YLy4JBCJe3+t8awQ== integrity sha512-lmt73NeHdy1Q/2ul295Qy3uninSqi6wQI18XwSpm8w0ZbQXUpjCAWP1Vlv/obudoBiIjJVjlztjQ+d/Md98Yxg==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "8.9.0" "@typescript-eslint/scope-manager" "8.11.0"
"@typescript-eslint/types" "8.9.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/typescript-estree" "8.9.0" "@typescript-eslint/typescript-estree" "8.11.0"
"@typescript-eslint/visitor-keys" "8.9.0" "@typescript-eslint/visitor-keys" "8.11.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@8.9.0": "@typescript-eslint/scope-manager@8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz#c98fef0c4a82a484e6a1eb610a55b154d14d46f3" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.11.0.tgz#9d399ce624118966732824878bc9a83593a30405"
integrity sha512-bZu9bUud9ym1cabmOYH9S6TnbWRzpklVmwqICeOulTCZ9ue2/pczWzQvt/cGj2r2o1RdKoZbuEMalJJSYw3pHQ== integrity sha512-Uholz7tWhXmA4r6epo+vaeV7yjdKy5QFCERMjs1kMVsLRKIrSdM6o21W2He9ftp5PP6aWOVpD5zvrvuHZC0bMQ==
dependencies: dependencies:
"@typescript-eslint/types" "8.9.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/visitor-keys" "8.9.0" "@typescript-eslint/visitor-keys" "8.11.0"
"@typescript-eslint/type-utils@8.9.0": "@typescript-eslint/type-utils@8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.9.0.tgz#aa86da3e4555fe7c8b42ab75e13561c4b5a8dfeb" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.11.0.tgz#b7f9e6120c1ddee8a1a07615646642ad85fc91b5"
integrity sha512-JD+/pCqlKqAk5961vxCluK+clkppHY07IbV3vett97KOV+8C6l+CPEPwpUuiMwgbOz/qrN3Ke4zzjqbT+ls+1Q== integrity sha512-ItiMfJS6pQU0NIKAaybBKkuVzo6IdnAhPFZA/2Mba/uBjuPQPet/8+zh5GtLHwmuFRShZx+8lhIs7/QeDHflOg==
dependencies: dependencies:
"@typescript-eslint/typescript-estree" "8.9.0" "@typescript-eslint/typescript-estree" "8.11.0"
"@typescript-eslint/utils" "8.9.0" "@typescript-eslint/utils" "8.11.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.9.0": "@typescript-eslint/types@8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.9.0.tgz#b733af07fb340b32e962c6c63b1062aec2dc0fe6" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.11.0.tgz#7c766250502097f49bbc2e651132e6bf489e20b8"
integrity sha512-SjgkvdYyt1FAPhU9c6FiYCXrldwYYlIQLkuc+LfAhCna6ggp96ACncdtlbn8FmnG72tUkXclrDExOpEYf1nfJQ== integrity sha512-tn6sNMHf6EBAYMvmPUaKaVeYvhUsrE6x+bXQTxjQRp360h1giATU0WvgeEys1spbvb5R+VpNOZ+XJmjD8wOUHw==
"@typescript-eslint/typescript-estree@8.9.0": "@typescript-eslint/typescript-estree@8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz#1714f167e9063062dc0df49c1d25afcbc7a96199" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.11.0.tgz#35fe5d3636fc5727c52429393415412e552e222b"
integrity sha512-9iJYTgKLDG6+iqegehc5+EqE6sqaee7kb8vWpmHZ86EqwDjmlqNNHeqDVqb9duh+BY6WCNHfIGvuVU3Tf9Db0g== integrity sha512-yHC3s1z1RCHoCz5t06gf7jH24rr3vns08XXhfEqzYpd6Hll3z/3g23JRi0jM8A47UFKNc3u/y5KIMx8Ynbjohg==
dependencies: dependencies:
"@typescript-eslint/types" "8.9.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/visitor-keys" "8.9.0" "@typescript-eslint/visitor-keys" "8.11.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"
@ -970,22 +970,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.9.0": "@typescript-eslint/utils@8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.9.0.tgz#748bbe3ea5bee526d9786d9405cf1b0df081c299" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.11.0.tgz#4480d1e9f2bb18ea3510c79f870a1aefc118103d"
integrity sha512-PKgMmaSo/Yg/F7kIZvrgrWa1+Vwn036CdNUvYFEkYbPwOH4i8xvkaRlu148W3vtheWK9ckKRIz7PBP5oUlkrvQ== integrity sha512-CYiX6WZcbXNJV7UNB4PLDIBtSdRmRI/nb0FMyqHPTQD1rMjA0foPLaPUV39C/MxkTd/QKSeX+Gb34PPsDVC35g==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.4.0" "@eslint-community/eslint-utils" "^4.4.0"
"@typescript-eslint/scope-manager" "8.9.0" "@typescript-eslint/scope-manager" "8.11.0"
"@typescript-eslint/types" "8.9.0" "@typescript-eslint/types" "8.11.0"
"@typescript-eslint/typescript-estree" "8.9.0" "@typescript-eslint/typescript-estree" "8.11.0"
"@typescript-eslint/visitor-keys@8.9.0": "@typescript-eslint/visitor-keys@8.11.0":
version "8.9.0" version "8.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz#5f11f4d9db913f37da42776893ffe0dd1ae78f78" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.11.0.tgz#273de1cbffe63d9f9cd7dfc20b5a5af66310cb92"
integrity sha512-Ht4y38ubk4L5/U8xKUBfKNYGmvKvA1CANoxiTRMM+tOLk3lbF3DvzZCxJCRSE+2GdCMSh6zq9VZJc3asc1XuAA== integrity sha512-EaewX6lxSjRJnc+99+dqzTeoDZUfyrA52d2/HRrkI830kgovWsmIiTfmr0NZorzqic7ga+1bS60lRBUgR3n/Bw==
dependencies: dependencies:
"@typescript-eslint/types" "8.9.0" "@typescript-eslint/types" "8.11.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":