Compare commits
18 Commits
ace7a19dfa
...
65b670d0dd
Author | SHA1 | Date | |
---|---|---|---|
65b670d0dd | |||
5dc2b230ba | |||
8a0bd4a0d6 | |||
a5b87ccf74 | |||
867a45717e | |||
b1d3155ebb | |||
7dcd41c2c3 | |||
0d8c3cc7a4 | |||
5eb3a310c5 | |||
8f656af0ac | |||
28508ef299 | |||
a2ea239ea8 | |||
183dc5ca60 | |||
3312f44a4d | |||
195da66ce2 | |||
3f55d5ca99 | |||
bf6f5f21ae | |||
f8567d895f |
@ -42,10 +42,12 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"import/order": ["error"],
|
"import/order": ["error"],
|
||||||
|
/**
|
||||||
"indent": [
|
"indent": [
|
||||||
"error",
|
"error",
|
||||||
2
|
2
|
||||||
],
|
],
|
||||||
|
**/
|
||||||
"linebreak-style": [
|
"linebreak-style": [
|
||||||
"error",
|
"error",
|
||||||
"unix"
|
"unix"
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -3,4 +3,5 @@ node_modules
|
|||||||
/events
|
/events
|
||||||
/trees
|
/trees
|
||||||
backup-tornado-*
|
backup-tornado-*
|
||||||
backup-tornadoInvoice-*
|
backup-tornadoInvoice-*
|
||||||
|
backup-note-account-*
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Modern Toolsets for [Privacy Pools](https://www.forbes.com/sites/tomerniv/2023/09/07/privacy-pools-bridging-the-gap-between-blockchain-and-regulatory-compliance) on Ethereum
|
Modern Toolsets for [Privacy Pools](https://www.forbes.com/sites/tomerniv/2023/09/07/privacy-pools-bridging-the-gap-between-blockchain-and-regulatory-compliance) on Ethereum
|
||||||
|
|
||||||
[](https://t.me/tornadocli) [](https://element.tornadocash.social) [](https://forum.tornado.ws/)
|
[](https://t.me/tornadoofficial) [](https://element.tornadocash.social) [](https://forum.tornado.ws/)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
304172
dist/cli.js
vendored
304172
dist/cli.js
vendored
File diff suppressed because one or more lines are too long
2649
dist/index.js
vendored
2649
dist/index.js
vendored
File diff suppressed because it is too large
Load Diff
2621
dist/index.mjs
vendored
2621
dist/index.mjs
vendored
File diff suppressed because it is too large
Load Diff
201496
dist/index.umd.js
vendored
Normal file
201496
dist/index.umd.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
dist/program.d.ts
vendored
9
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, BigNumberish } from 'ethers';
|
import { JsonRpcProvider, Provider, TransactionLike, Wallet, VoidSigner } from 'ethers';
|
||||||
import { getProviderOptions, TornadoWallet, TornadoVoidSigner, Relayer, RelayerInfo, RelayerError, RelayerClient, fetchDataOptions, Config } from './services';
|
import { getProviderOptions, TornadoWallet, TornadoVoidSigner, Relayer, RelayerInfo, RelayerError, RelayerClient, fetchDataOptions, NetIdType, Config } from './services';
|
||||||
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;
|
||||||
|
accountKey?: string;
|
||||||
relayer?: string;
|
relayer?: string;
|
||||||
walletWithdrawal?: boolean;
|
walletWithdrawal?: boolean;
|
||||||
torPort?: number;
|
torPort?: number;
|
||||||
@ -29,7 +30,7 @@ export declare function getProgramOptions(options: commonProgramOptions): Promis
|
|||||||
fetchDataOptions: fetchDataOptions;
|
fetchDataOptions: fetchDataOptions;
|
||||||
}>;
|
}>;
|
||||||
export declare function getProgramGraphAPI(options: commonProgramOptions, config: Config): string;
|
export declare function getProgramGraphAPI(options: commonProgramOptions, config: Config): string;
|
||||||
export declare function getProgramProvider(netId: BigNumberish, rpcUrl: string | undefined, config: Config, providerOptions?: getProviderOptions): JsonRpcProvider;
|
export declare function getProgramProvider(netId: NetIdType, rpcUrl: string | undefined, config: Config, providerOptions?: getProviderOptions): JsonRpcProvider;
|
||||||
export declare function getProgramSigner({ options, provider, }: {
|
export declare function getProgramSigner({ options, provider, }: {
|
||||||
options: commonProgramOptions;
|
options: commonProgramOptions;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
@ -37,7 +38,7 @@ export declare function getProgramSigner({ options, provider, }: {
|
|||||||
export declare function getProgramRelayer({ options, fetchDataOptions, netId, }: {
|
export declare function getProgramRelayer({ options, fetchDataOptions, netId, }: {
|
||||||
options: commonProgramOptions;
|
options: commonProgramOptions;
|
||||||
fetchDataOptions?: fetchDataOptions;
|
fetchDataOptions?: fetchDataOptions;
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
validRelayers?: RelayerInfo[] | Relayer[];
|
validRelayers?: RelayerInfo[] | Relayer[];
|
||||||
invalidRelayers?: RelayerError[];
|
invalidRelayers?: RelayerError[];
|
||||||
|
6
dist/services/data.d.ts
vendored
6
dist/services/data.d.ts
vendored
@ -3,10 +3,10 @@ import { BaseEvents, MinimalEvents } from './events';
|
|||||||
export declare function existsAsync(fileOrDir: string): Promise<boolean>;
|
export declare function existsAsync(fileOrDir: string): Promise<boolean>;
|
||||||
export declare function zipAsync(file: AsyncZippable): Promise<Uint8Array>;
|
export declare function zipAsync(file: AsyncZippable): Promise<Uint8Array>;
|
||||||
export declare function unzipAsync(data: Uint8Array): Promise<Unzipped>;
|
export declare function unzipAsync(data: Uint8Array): Promise<Unzipped>;
|
||||||
export declare function saveEvents<T extends MinimalEvents>({ name, userDirectory, events, }: {
|
export declare function saveUserFile({ fileName, userDirectory, dataString, }: {
|
||||||
name: string;
|
fileName: string;
|
||||||
userDirectory: string;
|
userDirectory: string;
|
||||||
events: T[];
|
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, deployedBlock, }: {
|
||||||
name: string;
|
name: string;
|
||||||
|
9
dist/services/deposits.d.ts
vendored
9
dist/services/deposits.d.ts
vendored
@ -1,7 +1,8 @@
|
|||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
export type DepositType = {
|
export type DepositType = {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: string | number;
|
netId: NetIdType;
|
||||||
};
|
};
|
||||||
export type createDepositParams = {
|
export type createDepositParams = {
|
||||||
nullifier: bigint;
|
nullifier: bigint;
|
||||||
@ -34,7 +35,7 @@ export declare function createDeposit({ nullifier, secret }: createDepositParams
|
|||||||
export interface DepositConstructor {
|
export interface DepositConstructor {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
nullifier: bigint;
|
nullifier: bigint;
|
||||||
secret: bigint;
|
secret: bigint;
|
||||||
note: string;
|
note: string;
|
||||||
@ -46,7 +47,7 @@ export interface DepositConstructor {
|
|||||||
export declare class Deposit {
|
export declare class Deposit {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
nullifier: bigint;
|
nullifier: bigint;
|
||||||
secret: bigint;
|
secret: bigint;
|
||||||
note: string;
|
note: string;
|
||||||
@ -65,7 +66,7 @@ export type parsedInvoiceExec = DepositType & {
|
|||||||
export declare class Invoice {
|
export declare class Invoice {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
commitment: string;
|
commitment: string;
|
||||||
invoice: string;
|
invoice: string;
|
||||||
constructor(invoiceString: string);
|
constructor(invoiceString: string);
|
||||||
|
48
dist/services/encryptedNotes.d.ts
vendored
Normal file
48
dist/services/encryptedNotes.d.ts
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { EthEncryptedData } from '@metamask/eth-sig-util';
|
||||||
|
import { Echoer } from '@tornado/contracts';
|
||||||
|
import { Wallet } from 'ethers';
|
||||||
|
import { EchoEvents, EncryptedNotesEvents } from './events';
|
||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
|
export interface NoteToEncrypt {
|
||||||
|
address: string;
|
||||||
|
noteHex: string;
|
||||||
|
}
|
||||||
|
export interface DecryptedNotes {
|
||||||
|
blockNumber: number;
|
||||||
|
address: string;
|
||||||
|
noteHex: string;
|
||||||
|
}
|
||||||
|
export declare function packEncryptedMessage({ nonce, ephemPublicKey, ciphertext }: EthEncryptedData): string;
|
||||||
|
export declare function unpackEncryptedMessage(encryptedMessage: string): EthEncryptedData & {
|
||||||
|
messageBuff: string;
|
||||||
|
};
|
||||||
|
export interface NoteAccountConstructor {
|
||||||
|
netId: NetIdType;
|
||||||
|
blockNumber?: number;
|
||||||
|
recoveryKey?: string;
|
||||||
|
Echoer: Echoer;
|
||||||
|
}
|
||||||
|
export declare class NoteAccount {
|
||||||
|
netId: NetIdType;
|
||||||
|
blockNumber?: number;
|
||||||
|
recoveryKey: string;
|
||||||
|
recoveryAddress: string;
|
||||||
|
recoveryPublicKey: string;
|
||||||
|
Echoer: Echoer;
|
||||||
|
constructor({ netId, blockNumber, recoveryKey, Echoer }: NoteAccountConstructor);
|
||||||
|
/**
|
||||||
|
* Intends to mock eth_getEncryptionPublicKey behavior from MetaMask
|
||||||
|
* In order to make the recoveryKey retrival from Echoer possible from the bare private key
|
||||||
|
*/
|
||||||
|
static getWalletPublicKey(wallet: Wallet): string;
|
||||||
|
getEncryptedAccount(walletPublicKey: string): {
|
||||||
|
encryptedData: EthEncryptedData;
|
||||||
|
data: string;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Decrypt Echoer backuped note encryption account with private keys
|
||||||
|
*/
|
||||||
|
decryptAccountsWithWallet(wallet: Wallet, events: EchoEvents[]): NoteAccount[];
|
||||||
|
decryptNotes(events: EncryptedNotesEvents[]): DecryptedNotes[];
|
||||||
|
encryptNote({ address, noteHex }: NoteToEncrypt): string;
|
||||||
|
}
|
43
dist/services/events/base.d.ts
vendored
43
dist/services/events/base.d.ts
vendored
@ -1,12 +1,13 @@
|
|||||||
import { BaseContract, Provider, EventLog, ContractEventName } from 'ethers';
|
import { BaseContract, Provider, EventLog, ContractEventName } from 'ethers';
|
||||||
import type { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry } from '@tornado/contracts';
|
import type { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry, Echoer } from '@tornado/contracts';
|
||||||
import { BatchEventsService, BatchBlockService, BatchTransactionService, BatchEventOnProgress, BatchBlockOnProgress } from '../batch';
|
import { BatchEventsService, BatchBlockService, BatchTransactionService, BatchEventOnProgress, BatchBlockOnProgress } from '../batch';
|
||||||
import { fetchDataOptions } from '../providers';
|
import { fetchDataOptions } from '../providers';
|
||||||
import type { BaseEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, GovernanceProposalCreatedEvents, GovernanceVotedEvents, GovernanceDelegatedEvents, GovernanceUndelegatedEvents, RegistersEvents } from './types';
|
import type { NetIdType } from '../networkConfig';
|
||||||
|
import type { BaseEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, RegistersEvents, EchoEvents } from './types';
|
||||||
export declare const DEPOSIT = "deposit";
|
export declare const DEPOSIT = "deposit";
|
||||||
export declare const WITHDRAWAL = "withdrawal";
|
export declare const WITHDRAWAL = "withdrawal";
|
||||||
export type BaseEventsServiceConstructor = {
|
export type BaseEventsServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -28,7 +29,7 @@ export type BaseGraphParams = {
|
|||||||
onProgress?: BatchGraphOnProgress;
|
onProgress?: BatchGraphOnProgress;
|
||||||
};
|
};
|
||||||
export declare class BaseEventsService<EventType extends MinimalEvents> {
|
export declare class BaseEventsService<EventType extends MinimalEvents> {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -81,7 +82,7 @@ export declare class BaseEventsService<EventType extends MinimalEvents> {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
export type BaseDepositsServiceConstructor = {
|
export type BaseDepositsServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -110,8 +111,27 @@ export declare class BaseDepositsService extends BaseEventsService<DepositsEvent
|
|||||||
events: (DepositsEvents | WithdrawalsEvents)[];
|
events: (DepositsEvents | WithdrawalsEvents)[];
|
||||||
}): void;
|
}): void;
|
||||||
}
|
}
|
||||||
|
export type BaseEchoServiceConstructor = {
|
||||||
|
netId: NetIdType;
|
||||||
|
provider: Provider;
|
||||||
|
graphApi?: string;
|
||||||
|
subgraphName?: string;
|
||||||
|
Echoer: Echoer;
|
||||||
|
deployedBlock?: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
};
|
||||||
|
export declare class BaseEchoService extends BaseEventsService<EchoEvents> {
|
||||||
|
constructor({ netId, provider, graphApi, subgraphName, Echoer, deployedBlock, fetchDataOptions, }: BaseEchoServiceConstructor);
|
||||||
|
getInstanceName(): string;
|
||||||
|
getType(): string;
|
||||||
|
getGraphMethod(): string;
|
||||||
|
formatEvents(events: EventLog[]): Promise<EchoEvents[]>;
|
||||||
|
getEventsFromGraph({ fromBlock }: {
|
||||||
|
fromBlock: number;
|
||||||
|
}): Promise<BaseEvents<EchoEvents>>;
|
||||||
|
}
|
||||||
export type BaseEncryptedNotesServiceConstructor = {
|
export type BaseEncryptedNotesServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -126,9 +146,8 @@ export declare class BaseEncryptedNotesService extends BaseEventsService<Encrypt
|
|||||||
getGraphMethod(): string;
|
getGraphMethod(): string;
|
||||||
formatEvents(events: EventLog[]): Promise<EncryptedNotesEvents[]>;
|
formatEvents(events: EventLog[]): Promise<EncryptedNotesEvents[]>;
|
||||||
}
|
}
|
||||||
export type BaseGovernanceEventTypes = GovernanceProposalCreatedEvents | GovernanceVotedEvents | GovernanceDelegatedEvents | GovernanceUndelegatedEvents;
|
|
||||||
export type BaseGovernanceServiceConstructor = {
|
export type BaseGovernanceServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -136,19 +155,19 @@ export type BaseGovernanceServiceConstructor = {
|
|||||||
deployedBlock?: number;
|
deployedBlock?: number;
|
||||||
fetchDataOptions?: fetchDataOptions;
|
fetchDataOptions?: fetchDataOptions;
|
||||||
};
|
};
|
||||||
export declare class BaseGovernanceService extends BaseEventsService<BaseGovernanceEventTypes> {
|
export declare class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
|
||||||
batchTransactionService: BatchTransactionService;
|
batchTransactionService: BatchTransactionService;
|
||||||
constructor({ netId, provider, graphApi, subgraphName, Governance, deployedBlock, fetchDataOptions, }: BaseGovernanceServiceConstructor);
|
constructor({ netId, provider, graphApi, subgraphName, Governance, deployedBlock, fetchDataOptions, }: BaseGovernanceServiceConstructor);
|
||||||
getInstanceName(): string;
|
getInstanceName(): string;
|
||||||
getType(): string;
|
getType(): string;
|
||||||
getGraphMethod(): string;
|
getGraphMethod(): string;
|
||||||
formatEvents(events: EventLog[]): Promise<BaseGovernanceEventTypes[]>;
|
formatEvents(events: EventLog[]): Promise<AllGovernanceEvents[]>;
|
||||||
getEventsFromGraph({ fromBlock }: {
|
getEventsFromGraph({ fromBlock }: {
|
||||||
fromBlock: number;
|
fromBlock: number;
|
||||||
}): Promise<BaseEvents<BaseGovernanceEventTypes>>;
|
}): Promise<BaseEvents<AllGovernanceEvents>>;
|
||||||
}
|
}
|
||||||
export type BaseRegistryServiceConstructor = {
|
export type BaseRegistryServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
|
24
dist/services/events/node.d.ts
vendored
24
dist/services/events/node.d.ts
vendored
@ -1,6 +1,6 @@
|
|||||||
import { BatchBlockOnProgress, BatchEventOnProgress } from '../batch';
|
import { BatchBlockOnProgress, BatchEventOnProgress } from '../batch';
|
||||||
import { BaseDepositsService, BaseEncryptedNotesService, BaseGovernanceService, BaseRegistryService, BaseDepositsServiceConstructor, BaseEncryptedNotesServiceConstructor, BaseGovernanceServiceConstructor, BaseRegistryServiceConstructor, BaseGovernanceEventTypes } from './base';
|
import { BaseDepositsService, BaseEncryptedNotesService, BaseGovernanceService, BaseRegistryService, BaseDepositsServiceConstructor, BaseEncryptedNotesServiceConstructor, BaseGovernanceServiceConstructor, BaseRegistryServiceConstructor, BaseEchoServiceConstructor, BaseEchoService } from './base';
|
||||||
import type { BaseEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, RegistersEvents } from './types';
|
import type { BaseEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, RegistersEvents, AllGovernanceEvents, EchoEvents } from './types';
|
||||||
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
|
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
|
||||||
cacheDirectory?: string;
|
cacheDirectory?: string;
|
||||||
userDirectory?: string;
|
userDirectory?: string;
|
||||||
@ -17,6 +17,20 @@ export declare class NodeDepositsService extends BaseDepositsService {
|
|||||||
getEventsFromCache(): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>;
|
getEventsFromCache(): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>;
|
||||||
saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>): Promise<void>;
|
saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>): Promise<void>;
|
||||||
}
|
}
|
||||||
|
export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & {
|
||||||
|
cacheDirectory?: string;
|
||||||
|
userDirectory?: string;
|
||||||
|
};
|
||||||
|
export declare class NodeEchoService extends BaseEchoService {
|
||||||
|
cacheDirectory?: string;
|
||||||
|
userDirectory?: string;
|
||||||
|
constructor({ netId, provider, graphApi, subgraphName, Echoer, deployedBlock, fetchDataOptions, 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>>;
|
||||||
|
getEventsFromCache(): Promise<BaseEvents<EchoEvents>>;
|
||||||
|
saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>): Promise<void>;
|
||||||
|
}
|
||||||
export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceConstructor & {
|
export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceConstructor & {
|
||||||
cacheDirectory?: string;
|
cacheDirectory?: string;
|
||||||
userDirectory?: string;
|
userDirectory?: string;
|
||||||
@ -42,9 +56,9 @@ export declare class NodeGovernanceService extends BaseGovernanceService {
|
|||||||
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<BaseGovernanceEventTypes>>;
|
getEventsFromDB(): Promise<BaseEvents<AllGovernanceEvents>>;
|
||||||
getEventsFromCache(): Promise<BaseEvents<BaseGovernanceEventTypes>>;
|
getEventsFromCache(): Promise<BaseEvents<AllGovernanceEvents>>;
|
||||||
saveEvents({ events, lastBlock }: BaseEvents<BaseGovernanceEventTypes>): Promise<void>;
|
saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>): Promise<void>;
|
||||||
}
|
}
|
||||||
export type NodeRegistryServiceConstructor = BaseRegistryServiceConstructor & {
|
export type NodeRegistryServiceConstructor = BaseRegistryServiceConstructor & {
|
||||||
cacheDirectory?: string;
|
cacheDirectory?: string;
|
||||||
|
5
dist/services/events/types.d.ts
vendored
5
dist/services/events/types.d.ts
vendored
@ -39,6 +39,7 @@ export type GovernanceUndelegatedEvents = GovernanceEvents & {
|
|||||||
account: string;
|
account: string;
|
||||||
delegateFrom: string;
|
delegateFrom: string;
|
||||||
};
|
};
|
||||||
|
export type AllGovernanceEvents = GovernanceProposalCreatedEvents | GovernanceVotedEvents | GovernanceDelegatedEvents | GovernanceUndelegatedEvents;
|
||||||
export type RegistersEvents = MinimalEvents & RelayerParams;
|
export type RegistersEvents = MinimalEvents & RelayerParams;
|
||||||
export type DepositsEvents = MinimalEvents & {
|
export type DepositsEvents = MinimalEvents & {
|
||||||
commitment: string;
|
commitment: string;
|
||||||
@ -52,6 +53,10 @@ export type WithdrawalsEvents = MinimalEvents & {
|
|||||||
fee: string;
|
fee: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
|
export type EchoEvents = MinimalEvents & {
|
||||||
|
address: string;
|
||||||
|
encryptedAccount: string;
|
||||||
|
};
|
||||||
export type EncryptedNotesEvents = MinimalEvents & {
|
export type EncryptedNotesEvents = MinimalEvents & {
|
||||||
encryptedNote: string;
|
encryptedNote: string;
|
||||||
};
|
};
|
||||||
|
78
dist/services/graphql/index.d.ts
vendored
78
dist/services/graphql/index.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
import { fetchDataOptions } from '../providers';
|
import { fetchDataOptions } from '../providers';
|
||||||
import type { BaseGraphEvents, RegistersEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, BatchGraphOnProgress } from '../events';
|
import type { BaseGraphEvents, RegistersEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, BatchGraphOnProgress, EchoEvents, AllGovernanceEvents } from '../events';
|
||||||
export * from './queries';
|
export * from './queries';
|
||||||
export type queryGraphParams = {
|
export type queryGraphParams = {
|
||||||
graphApi: string;
|
graphApi: string;
|
||||||
@ -165,6 +165,29 @@ export interface getNoteAccountsReturns {
|
|||||||
lastSyncBlock: null | number;
|
lastSyncBlock: null | number;
|
||||||
}
|
}
|
||||||
export declare function getNoteAccounts({ graphApi, subgraphName, address, fetchDataOptions, }: getNoteAccountsParams): Promise<getNoteAccountsReturns>;
|
export declare function getNoteAccounts({ graphApi, subgraphName, address, fetchDataOptions, }: getNoteAccountsParams): Promise<getNoteAccountsReturns>;
|
||||||
|
export interface GraphEchoEvents {
|
||||||
|
noteAccounts: {
|
||||||
|
id: string;
|
||||||
|
blockNumber: string;
|
||||||
|
address: string;
|
||||||
|
encryptedAccount: string;
|
||||||
|
}[];
|
||||||
|
_meta: {
|
||||||
|
block: {
|
||||||
|
number: number;
|
||||||
|
};
|
||||||
|
hasIndexingErrors: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface getGraphEchoEventsParams {
|
||||||
|
graphApi: string;
|
||||||
|
subgraphName: string;
|
||||||
|
fromBlock: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
onProgress?: BatchGraphOnProgress;
|
||||||
|
}
|
||||||
|
export declare function getGraphEchoEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions, }: getGraphEchoEventsParams): Promise<GraphEchoEvents>;
|
||||||
|
export declare function getAllGraphEchoEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions, onProgress, }: getGraphEchoEventsParams): Promise<BaseGraphEvents<EchoEvents>>;
|
||||||
export interface GraphEncryptedNotes {
|
export interface GraphEncryptedNotes {
|
||||||
encryptedNotes: {
|
encryptedNotes: {
|
||||||
blockNumber: string;
|
blockNumber: string;
|
||||||
@ -188,3 +211,56 @@ export interface getEncryptedNotesParams {
|
|||||||
}
|
}
|
||||||
export declare function getEncryptedNotes({ graphApi, subgraphName, fromBlock, fetchDataOptions, }: getEncryptedNotesParams): Promise<GraphEncryptedNotes>;
|
export declare function getEncryptedNotes({ graphApi, subgraphName, fromBlock, fetchDataOptions, }: getEncryptedNotesParams): Promise<GraphEncryptedNotes>;
|
||||||
export declare function getAllEncryptedNotes({ graphApi, subgraphName, fromBlock, fetchDataOptions, onProgress, }: getEncryptedNotesParams): Promise<BaseGraphEvents<EncryptedNotesEvents>>;
|
export declare function getAllEncryptedNotes({ graphApi, subgraphName, fromBlock, fetchDataOptions, onProgress, }: getEncryptedNotesParams): Promise<BaseGraphEvents<EncryptedNotesEvents>>;
|
||||||
|
export interface GraphGovernanceEvents {
|
||||||
|
proposals: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
proposalId: number;
|
||||||
|
proposer: string;
|
||||||
|
target: string;
|
||||||
|
startTime: number;
|
||||||
|
endTime: number;
|
||||||
|
description: string;
|
||||||
|
}[];
|
||||||
|
votes: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
proposalId: number;
|
||||||
|
voter: string;
|
||||||
|
support: boolean;
|
||||||
|
votes: string;
|
||||||
|
from: string;
|
||||||
|
input: string;
|
||||||
|
}[];
|
||||||
|
delegates: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
account: string;
|
||||||
|
delegateTo: string;
|
||||||
|
}[];
|
||||||
|
undelegates: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
account: string;
|
||||||
|
delegateFrom: string;
|
||||||
|
}[];
|
||||||
|
_meta: {
|
||||||
|
block: {
|
||||||
|
number: number;
|
||||||
|
};
|
||||||
|
hasIndexingErrors: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export interface getGovernanceEventsParams {
|
||||||
|
graphApi: string;
|
||||||
|
subgraphName: string;
|
||||||
|
fromBlock: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
onProgress?: BatchGraphOnProgress;
|
||||||
|
}
|
||||||
|
export declare function getGovernanceEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions, }: getGovernanceEventsParams): Promise<GraphGovernanceEvents>;
|
||||||
|
export declare function getAllGovernanceEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions, onProgress, }: getGovernanceEventsParams): Promise<BaseGraphEvents<AllGovernanceEvents>>;
|
||||||
|
3
dist/services/graphql/queries.d.ts
vendored
3
dist/services/graphql/queries.d.ts
vendored
@ -4,4 +4,7 @@ export declare const GET_REGISTERED = "\n query getRegistered($first: Int, $fro
|
|||||||
export declare const GET_DEPOSITS = "\n query getDeposits($currency: String!, $amount: String!, $first: Int, $fromBlock: Int) {\n deposits(first: $first, orderBy: index, orderDirection: asc, where: { \n amount: $amount,\n currency: $currency,\n blockNumber_gte: $fromBlock\n }) {\n id\n blockNumber\n commitment\n index\n timestamp\n from\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
export declare const GET_DEPOSITS = "\n query getDeposits($currency: String!, $amount: String!, $first: Int, $fromBlock: Int) {\n deposits(first: $first, orderBy: index, orderDirection: asc, where: { \n amount: $amount,\n currency: $currency,\n blockNumber_gte: $fromBlock\n }) {\n id\n blockNumber\n commitment\n index\n timestamp\n from\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
||||||
export declare const GET_WITHDRAWALS = "\n query getWithdrawals($currency: String!, $amount: String!, $first: Int, $fromBlock: Int!) {\n withdrawals(first: $first, orderBy: blockNumber, orderDirection: asc, where: { \n currency: $currency,\n amount: $amount,\n blockNumber_gte: $fromBlock\n }) {\n id\n blockNumber\n nullifier\n to\n fee\n timestamp\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
export declare const GET_WITHDRAWALS = "\n query getWithdrawals($currency: String!, $amount: String!, $first: Int, $fromBlock: Int!) {\n withdrawals(first: $first, orderBy: blockNumber, orderDirection: asc, where: { \n currency: $currency,\n amount: $amount,\n blockNumber_gte: $fromBlock\n }) {\n id\n blockNumber\n nullifier\n to\n fee\n timestamp\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
||||||
export declare const GET_NOTE_ACCOUNTS = "\n query getNoteAccount($address: String!) {\n noteAccounts(where: { address: $address }) {\n id\n index\n address\n encryptedAccount\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
export declare const GET_NOTE_ACCOUNTS = "\n query getNoteAccount($address: String!) {\n noteAccounts(where: { address: $address }) {\n id\n index\n address\n encryptedAccount\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
||||||
|
export declare const GET_ECHO_EVENTS = "\n query getNoteAccounts($first: Int, $fromBlock: Int) {\n noteAccounts(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {\n id\n blockNumber\n address\n encryptedAccount\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
||||||
export declare const GET_ENCRYPTED_NOTES = "\n query getEncryptedNotes($first: Int, $fromBlock: Int) {\n encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {\n blockNumber\n index\n transactionHash\n encryptedNote\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
export declare const GET_ENCRYPTED_NOTES = "\n query getEncryptedNotes($first: Int, $fromBlock: Int) {\n encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {\n blockNumber\n index\n transactionHash\n encryptedNote\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
||||||
|
export declare const GET_GOVERNANCE_EVENTS = "\n query getGovernanceEvents($first: Int, $fromBlock: Int) {\n proposals(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {\n blockNumber\n logIndex\n transactionHash\n proposalId\n proposer\n target\n startTime\n endTime\n description\n }\n votes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {\n blockNumber\n logIndex\n transactionHash\n proposalId\n voter\n support\n votes\n from\n input\n }\n delegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {\n blockNumber\n logIndex\n transactionHash\n account\n delegateTo\n }\n undelegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {\n blockNumber\n logIndex\n transactionHash\n account\n delegateFrom\n }\n _meta {\n block {\n number\n }\n hasIndexingErrors\n }\n }\n";
|
||||||
|
export declare const GET_GOVERNANCE_APY = "\n stakeDailyBurns(first: 30, orderBy: date, orderDirection: desc) {\n id\n date\n dailyAmountBurned\n }\n";
|
||||||
|
2
dist/services/index.d.ts
vendored
2
dist/services/index.d.ts
vendored
@ -4,6 +4,7 @@ export * from './schemas';
|
|||||||
export * from './batch';
|
export * from './batch';
|
||||||
export * from './data';
|
export * from './data';
|
||||||
export * from './deposits';
|
export * from './deposits';
|
||||||
|
export * from './encryptedNotes';
|
||||||
export * from './fees';
|
export * from './fees';
|
||||||
export * from './merkleTree';
|
export * from './merkleTree';
|
||||||
export * from './mimc';
|
export * from './mimc';
|
||||||
@ -15,5 +16,6 @@ export * from './prices';
|
|||||||
export * from './providers';
|
export * from './providers';
|
||||||
export * from './relayerClient';
|
export * from './relayerClient';
|
||||||
export * from './tokens';
|
export * from './tokens';
|
||||||
|
export * from './treeCache';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
export * from './websnark';
|
export * from './websnark';
|
||||||
|
23
dist/services/merkleTree.d.ts
vendored
23
dist/services/merkleTree.d.ts
vendored
@ -1,10 +1,11 @@
|
|||||||
import { MerkleTree, Element } from '@tornado/fixed-merkle-tree';
|
import { MerkleTree, PartialMerkleTree, Element, TreeEdge } from '@tornado/fixed-merkle-tree';
|
||||||
import type { Tornado } from '@tornado/contracts';
|
import type { Tornado } from '@tornado/contracts';
|
||||||
import type { DepositType } from './deposits';
|
import type { DepositType } from './deposits';
|
||||||
import type { DepositsEvents } from './events';
|
import type { DepositsEvents } from './events';
|
||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
export type MerkleTreeConstructor = DepositType & {
|
export type MerkleTreeConstructor = DepositType & {
|
||||||
Tornado: Tornado;
|
Tornado: Tornado;
|
||||||
commitment?: string;
|
commitmentHex?: string;
|
||||||
merkleTreeHeight?: number;
|
merkleTreeHeight?: number;
|
||||||
emptyElement?: string;
|
emptyElement?: string;
|
||||||
merkleWorkerPath?: string;
|
merkleWorkerPath?: string;
|
||||||
@ -12,18 +13,18 @@ export type MerkleTreeConstructor = DepositType & {
|
|||||||
export declare class MerkleTreeService {
|
export declare class MerkleTreeService {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
Tornado: Tornado;
|
Tornado: Tornado;
|
||||||
commitment?: string;
|
commitmentHex?: string;
|
||||||
instanceName: string;
|
instanceName: string;
|
||||||
merkleTreeHeight: number;
|
merkleTreeHeight: number;
|
||||||
emptyElement: string;
|
emptyElement: string;
|
||||||
merkleWorkerPath?: string;
|
merkleWorkerPath?: string;
|
||||||
constructor({ netId, amount, currency, Tornado, commitment, merkleTreeHeight, emptyElement, merkleWorkerPath, }: MerkleTreeConstructor);
|
constructor({ netId, amount, currency, Tornado, commitmentHex, merkleTreeHeight, emptyElement, merkleWorkerPath, }: MerkleTreeConstructor);
|
||||||
createTree({ events }: {
|
createTree(events: Element[]): Promise<MerkleTree>;
|
||||||
events: Element[];
|
createPartialTree({ edge, elements }: {
|
||||||
}): Promise<MerkleTree>;
|
edge: TreeEdge;
|
||||||
verifyTree({ events }: {
|
elements: Element[];
|
||||||
events: DepositsEvents[];
|
}): Promise<PartialMerkleTree>;
|
||||||
}): Promise<MerkleTree>;
|
verifyTree(events: DepositsEvents[]): Promise<MerkleTree>;
|
||||||
}
|
}
|
||||||
|
66
dist/services/networkConfig.d.ts
vendored
66
dist/services/networkConfig.d.ts
vendored
@ -1,3 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* Type of default supported networks
|
||||||
|
*/
|
||||||
|
export declare enum NetId {
|
||||||
|
MAINNET = 1,
|
||||||
|
BSC = 56,
|
||||||
|
POLYGON = 137,
|
||||||
|
OPTIMISM = 10,
|
||||||
|
ARBITRUM = 42161,
|
||||||
|
GNOSIS = 100,
|
||||||
|
AVALANCHE = 43114,
|
||||||
|
SEPOLIA = 11155111
|
||||||
|
}
|
||||||
|
export type NetIdType = NetId | number;
|
||||||
export interface RpcUrl {
|
export interface RpcUrl {
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
@ -37,20 +51,20 @@ export type Config = {
|
|||||||
};
|
};
|
||||||
nativeCurrency: string;
|
nativeCurrency: string;
|
||||||
currencyName: string;
|
currencyName: string;
|
||||||
explorerUrl: {
|
explorerUrl: string;
|
||||||
tx: string;
|
|
||||||
address: string;
|
|
||||||
block: string;
|
|
||||||
};
|
|
||||||
merkleTreeHeight: number;
|
merkleTreeHeight: number;
|
||||||
emptyElement: string;
|
emptyElement: string;
|
||||||
networkName: string;
|
networkName: string;
|
||||||
deployedBlock: number;
|
deployedBlock: number;
|
||||||
rpcUrls: RpcUrls;
|
rpcUrls: RpcUrls;
|
||||||
multicall: string;
|
multicallContract: string;
|
||||||
routerContract: string;
|
routerContract: string;
|
||||||
registryContract?: string;
|
|
||||||
echoContract: string;
|
echoContract: string;
|
||||||
|
offchainOracleContract?: string;
|
||||||
|
tornContract?: string;
|
||||||
|
governanceContract?: string;
|
||||||
|
stakingRewardsContract?: string;
|
||||||
|
registryContract?: string;
|
||||||
aggregatorContract?: string;
|
aggregatorContract?: string;
|
||||||
reverseRecordsContract?: string;
|
reverseRecordsContract?: string;
|
||||||
gasPriceOracleContract?: string;
|
gasPriceOracleContract?: string;
|
||||||
@ -58,6 +72,7 @@ export type Config = {
|
|||||||
ovmGasPriceOracleContract?: string;
|
ovmGasPriceOracleContract?: string;
|
||||||
tornadoSubgraph: string;
|
tornadoSubgraph: string;
|
||||||
registrySubgraph?: string;
|
registrySubgraph?: string;
|
||||||
|
governanceSubgraph?: string;
|
||||||
subgraphs: SubgraphUrls;
|
subgraphs: SubgraphUrls;
|
||||||
tokens: TokenInstances;
|
tokens: TokenInstances;
|
||||||
optionalTokens?: string[];
|
optionalTokens?: string[];
|
||||||
@ -70,17 +85,32 @@ export type Config = {
|
|||||||
REGISTRY_BLOCK?: number;
|
REGISTRY_BLOCK?: number;
|
||||||
MINING_BLOCK_TIME?: number;
|
MINING_BLOCK_TIME?: number;
|
||||||
};
|
};
|
||||||
'torn.contract.tornadocash.eth'?: string;
|
|
||||||
'governance.contract.tornadocash.eth'?: string;
|
|
||||||
'staking-rewards.contract.tornadocash.eth'?: string;
|
|
||||||
'tornado-router.contract.tornadocash.eth'?: string;
|
|
||||||
'tornado-proxy-light.contract.tornadocash.eth'?: string;
|
|
||||||
};
|
};
|
||||||
export type networkConfig = {
|
export type networkConfig = {
|
||||||
[key in string]: Config;
|
[key in NetIdType]: Config;
|
||||||
};
|
};
|
||||||
export declare const blockSyncInterval = 10000;
|
export declare const defaultConfig: networkConfig;
|
||||||
export declare const enabledChains: string[];
|
export declare const enabledChains: number[];
|
||||||
export declare const networkConfig: networkConfig;
|
/**
|
||||||
export declare const subdomains: string[];
|
* Custom config object to extend default config
|
||||||
export default networkConfig;
|
*
|
||||||
|
* Inspired by getUrlFunc from ethers.js
|
||||||
|
* https://github.com/ethers-io/ethers.js/blob/v6/src.ts/utils/fetch.ts#L59
|
||||||
|
*/
|
||||||
|
export declare let customConfig: networkConfig;
|
||||||
|
/**
|
||||||
|
* Add or override existing network config object
|
||||||
|
*
|
||||||
|
* Could be also called on the UI hook so that the UI could allow people to use custom privacy pools
|
||||||
|
*/
|
||||||
|
export declare function addNetwork(newConfig: networkConfig): void;
|
||||||
|
export declare function getNetworkConfig(): networkConfig;
|
||||||
|
export declare function getConfig(netId: NetIdType): Config;
|
||||||
|
export declare function getInstanceByAddress({ netId, address }: {
|
||||||
|
netId: NetIdType;
|
||||||
|
address: string;
|
||||||
|
}): {
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
} | undefined;
|
||||||
|
export declare function getSubdomains(): string[];
|
||||||
|
4
dist/services/parser.d.ts
vendored
4
dist/services/parser.d.ts
vendored
@ -4,3 +4,7 @@ export declare function parseRelayer(value?: string): string;
|
|||||||
export declare function parseAddress(value?: string): string;
|
export declare function parseAddress(value?: string): string;
|
||||||
export declare function parseMnemonic(value?: string): string;
|
export declare function parseMnemonic(value?: string): string;
|
||||||
export declare function parseKey(value?: string): string;
|
export declare function parseKey(value?: string): string;
|
||||||
|
/**
|
||||||
|
* Recovery key shouldn't have a 0x prefix (Also this is how the UI generates)
|
||||||
|
*/
|
||||||
|
export declare function parseRecoveryKey(value?: string): string;
|
||||||
|
8
dist/services/providers.d.ts
vendored
8
dist/services/providers.d.ts
vendored
@ -3,9 +3,9 @@
|
|||||||
/// <reference types="node" />
|
/// <reference types="node" />
|
||||||
import type { EventEmitter } from 'stream';
|
import type { EventEmitter } from 'stream';
|
||||||
import type { RequestOptions } from 'http';
|
import type { RequestOptions } from 'http';
|
||||||
import { JsonRpcApiProvider, JsonRpcProvider, Wallet, FetchGetUrlFunc, Provider, SigningKey, TransactionRequest, JsonRpcSigner, BrowserProvider, Networkish, Eip1193Provider, VoidSigner, FetchUrlFeeDataNetworkPlugin, BigNumberish } from 'ethers';
|
import { JsonRpcApiProvider, JsonRpcProvider, Wallet, FetchGetUrlFunc, Provider, SigningKey, TransactionRequest, JsonRpcSigner, BrowserProvider, Networkish, Eip1193Provider, VoidSigner, FetchUrlFeeDataNetworkPlugin } from 'ethers';
|
||||||
import type { RequestInfo, RequestInit, Response, HeadersInit } from 'node-fetch';
|
import type { RequestInfo, RequestInit, Response, HeadersInit } from 'node-fetch';
|
||||||
import type { Config } from './networkConfig';
|
import type { Config, NetIdType } from './networkConfig';
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
ethereum?: Eip1193Provider & EventEmitter;
|
ethereum?: Eip1193Provider & EventEmitter;
|
||||||
@ -41,7 +41,7 @@ export type getProviderOptions = fetchDataOptions & {
|
|||||||
};
|
};
|
||||||
export declare function getGasOraclePlugin(networkKey: string, fetchOptions?: getProviderOptions): FetchUrlFeeDataNetworkPlugin;
|
export declare function getGasOraclePlugin(networkKey: string, fetchOptions?: getProviderOptions): FetchUrlFeeDataNetworkPlugin;
|
||||||
export declare function getProvider(rpcUrl: string, fetchOptions?: getProviderOptions): Promise<JsonRpcProvider>;
|
export declare function getProvider(rpcUrl: string, fetchOptions?: getProviderOptions): Promise<JsonRpcProvider>;
|
||||||
export declare function getProviderWithNetId(netId: BigNumberish, rpcUrl: string, config: Config, fetchOptions?: getProviderOptions): JsonRpcProvider;
|
export declare function getProviderWithNetId(netId: NetIdType, rpcUrl: string, config: Config, fetchOptions?: getProviderOptions): JsonRpcProvider;
|
||||||
export declare const populateTransaction: (signer: TornadoWallet | TornadoVoidSigner | TornadoRpcSigner, tx: TransactionRequest) => Promise<TransactionRequest>;
|
export declare const populateTransaction: (signer: TornadoWallet | TornadoVoidSigner | TornadoRpcSigner, tx: TransactionRequest) => Promise<TransactionRequest>;
|
||||||
export type TornadoWalletOptions = {
|
export type TornadoWalletOptions = {
|
||||||
gasPriceBump?: number;
|
gasPriceBump?: number;
|
||||||
@ -80,7 +80,7 @@ export declare class TornadoRpcSigner extends JsonRpcSigner {
|
|||||||
export type connectWalletFunc = (...args: any[]) => Promise<void>;
|
export type connectWalletFunc = (...args: any[]) => Promise<void>;
|
||||||
export type handleWalletFunc = (...args: any[]) => void;
|
export type handleWalletFunc = (...args: any[]) => void;
|
||||||
export type TornadoBrowserProviderOptions = TornadoWalletOptions & {
|
export type TornadoBrowserProviderOptions = TornadoWalletOptions & {
|
||||||
webChainId?: BigNumberish;
|
webChainId?: NetIdType;
|
||||||
connectWallet?: connectWalletFunc;
|
connectWallet?: connectWalletFunc;
|
||||||
handleNetworkChanges?: handleWalletFunc;
|
handleNetworkChanges?: handleWalletFunc;
|
||||||
handleAccountChanges?: handleWalletFunc;
|
handleAccountChanges?: handleWalletFunc;
|
||||||
|
32
dist/services/relayerClient.d.ts
vendored
32
dist/services/relayerClient.d.ts
vendored
@ -1,6 +1,6 @@
|
|||||||
import type { Aggregator } from '@tornado/contracts';
|
import type { Aggregator } from '@tornado/contracts';
|
||||||
import type { RelayerStructOutput } from '@tornado/contracts/dist/contracts/Governance/Aggregator/Aggregator';
|
import type { RelayerStructOutput } from '@tornado/contracts/dist/contracts/Governance/Aggregator/Aggregator';
|
||||||
import type { Config } from './networkConfig';
|
import { NetIdType, Config } from './networkConfig';
|
||||||
import { fetchDataOptions } from './providers';
|
import { fetchDataOptions } from './providers';
|
||||||
import type { snarkProofs } from './websnark';
|
import type { snarkProofs } from './websnark';
|
||||||
export declare const MIN_STAKE_BALANCE: bigint;
|
export declare const MIN_STAKE_BALANCE: bigint;
|
||||||
@ -9,20 +9,22 @@ export interface RelayerParams {
|
|||||||
relayerAddress?: string;
|
relayerAddress?: string;
|
||||||
}
|
}
|
||||||
export interface Relayer {
|
export interface Relayer {
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
url: string;
|
url: string;
|
||||||
|
hostname: string;
|
||||||
rewardAccount: string;
|
rewardAccount: string;
|
||||||
|
instances: string[];
|
||||||
|
gasPrice?: number;
|
||||||
|
ethPrices?: {
|
||||||
|
[key in string]: string;
|
||||||
|
};
|
||||||
currentQueue: number;
|
currentQueue: number;
|
||||||
tornadoServiceFee: number;
|
tornadoServiceFee: number;
|
||||||
}
|
}
|
||||||
export type RelayerInfo = Relayer & {
|
export type RelayerInfo = Relayer & {
|
||||||
hostname: string;
|
|
||||||
ensName: string;
|
ensName: string;
|
||||||
stakeBalance: bigint;
|
stakeBalance: bigint;
|
||||||
relayerAddress: string;
|
relayerAddress: string;
|
||||||
ethPrices?: {
|
|
||||||
[key in string]: string;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
export type RelayerError = {
|
export type RelayerError = {
|
||||||
hostname: string;
|
hostname: string;
|
||||||
@ -46,7 +48,7 @@ export interface RelayerStatus {
|
|||||||
fast: number;
|
fast: number;
|
||||||
additionalProperties?: number;
|
additionalProperties?: number;
|
||||||
};
|
};
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
ethPrices?: {
|
ethPrices?: {
|
||||||
[key in string]: string;
|
[key in string]: string;
|
||||||
};
|
};
|
||||||
@ -84,12 +86,20 @@ export interface semanticVersion {
|
|||||||
buildmetadata?: string;
|
buildmetadata?: string;
|
||||||
}
|
}
|
||||||
export declare function parseSemanticVersion(version: string): semanticVersion;
|
export declare function parseSemanticVersion(version: string): semanticVersion;
|
||||||
export declare function isRelayerUpdated(relayerVersion: string, netId: number | string): boolean;
|
export declare function isRelayerUpdated(relayerVersion: string, netId: NetIdType): boolean;
|
||||||
export declare function calculateScore({ stakeBalance, tornadoServiceFee }: RelayerInfo, minFee?: number, maxFee?: number): bigint;
|
export declare function calculateScore({ stakeBalance, tornadoServiceFee }: RelayerInfo, minFee?: number, maxFee?: number): bigint;
|
||||||
export declare function getWeightRandom(weightsScores: bigint[], random: bigint): number;
|
export declare function getWeightRandom(weightsScores: bigint[], random: bigint): number;
|
||||||
export declare function pickWeightedRandomRelayer(relayers: RelayerInfo[], netId: string | number): RelayerInfo;
|
export type RelayerInstanceList = {
|
||||||
|
[key in string]: {
|
||||||
|
instanceAddress: {
|
||||||
|
[key in string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export declare function getSupportedInstances(instanceList: RelayerInstanceList): string[];
|
||||||
|
export declare function pickWeightedRandomRelayer(relayers: RelayerInfo[], netId: NetIdType): RelayerInfo;
|
||||||
export interface RelayerClientConstructor {
|
export interface RelayerClientConstructor {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
config: Config;
|
config: Config;
|
||||||
Aggregator: Aggregator;
|
Aggregator: Aggregator;
|
||||||
fetchDataOptions?: fetchDataOptions;
|
fetchDataOptions?: fetchDataOptions;
|
||||||
@ -98,7 +108,7 @@ export type RelayerClientWithdraw = snarkProofs & {
|
|||||||
contract: string;
|
contract: string;
|
||||||
};
|
};
|
||||||
export declare class RelayerClient {
|
export declare class RelayerClient {
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
config: Config;
|
config: Config;
|
||||||
Aggregator: Aggregator;
|
Aggregator: Aggregator;
|
||||||
selectedRelayer?: Relayer;
|
selectedRelayer?: Relayer;
|
||||||
|
4
dist/services/schemas/status.d.ts
vendored
4
dist/services/schemas/status.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
import type { Config } from '../networkConfig';
|
import { Config, NetIdType } from '../networkConfig';
|
||||||
export type statusInstanceType = {
|
export type statusInstanceType = {
|
||||||
type: string;
|
type: string;
|
||||||
properties: {
|
properties: {
|
||||||
@ -88,5 +88,5 @@ declare const bnType: {
|
|||||||
type: string;
|
type: string;
|
||||||
BN: boolean;
|
BN: boolean;
|
||||||
};
|
};
|
||||||
export declare function getStatusSchema(netId: number | string, config: Config): statusSchema;
|
export declare function getStatusSchema(netId: NetIdType, config: Config): statusSchema;
|
||||||
export {};
|
export {};
|
||||||
|
35
dist/services/treeCache.d.ts
vendored
Normal file
35
dist/services/treeCache.d.ts
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* Create tree cache file from node.js
|
||||||
|
*
|
||||||
|
* Only works for node.js, modified from https://github.com/tornadocash/tornado-classic-ui/blob/master/scripts/updateTree.js
|
||||||
|
*/
|
||||||
|
import { MerkleTree } from '@tornado/fixed-merkle-tree';
|
||||||
|
import { DepositsEvents } from './events';
|
||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
|
export interface TreeCacheConstructor {
|
||||||
|
netId: NetIdType;
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
userDirectory: string;
|
||||||
|
PARTS_COUNT?: number;
|
||||||
|
LEAVES?: number;
|
||||||
|
zeroElement?: string;
|
||||||
|
}
|
||||||
|
export interface treeMetadata {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
timestamp: number;
|
||||||
|
from: string;
|
||||||
|
leafIndex: number;
|
||||||
|
}
|
||||||
|
export declare class TreeCache {
|
||||||
|
netId: NetIdType;
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
userDirectory: string;
|
||||||
|
PARTS_COUNT: number;
|
||||||
|
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT }: TreeCacheConstructor);
|
||||||
|
getInstanceName(): string;
|
||||||
|
createTree(events: DepositsEvents[], tree: MerkleTree): Promise<void>;
|
||||||
|
}
|
5
dist/services/utils.d.ts
vendored
5
dist/services/utils.d.ts
vendored
@ -1,15 +1,20 @@
|
|||||||
/// <reference types="node" />
|
/// <reference types="node" />
|
||||||
|
/// <reference types="node" />
|
||||||
|
import { webcrypto } from 'crypto';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import type { BigNumberish } from 'ethers';
|
import type { BigNumberish } from 'ethers';
|
||||||
type bnInput = number | string | number[] | Uint8Array | Buffer | BN;
|
type bnInput = number | string | number[] | Uint8Array | Buffer | BN;
|
||||||
export declare const isNode: boolean;
|
export declare const isNode: boolean;
|
||||||
|
export declare const crypto: webcrypto.Crypto;
|
||||||
export declare const chunk: <T>(arr: T[], size: number) => T[][];
|
export declare const chunk: <T>(arr: T[], size: number) => T[][];
|
||||||
export declare function sleep(ms: number): Promise<unknown>;
|
export declare function sleep(ms: number): Promise<unknown>;
|
||||||
export declare function validateUrl(url: string, protocols?: string[]): boolean;
|
export declare function validateUrl(url: string, protocols?: string[]): boolean;
|
||||||
|
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
|
||||||
export declare function bufferToBytes(b: Buffer): Uint8Array;
|
export declare function bufferToBytes(b: Buffer): Uint8Array;
|
||||||
export declare function bytesToBase64(bytes: Uint8Array): string;
|
export declare function bytesToBase64(bytes: Uint8Array): string;
|
||||||
export declare function base64ToBytes(base64: string): Uint8Array;
|
export declare function base64ToBytes(base64: string): Uint8Array;
|
||||||
export declare function bytesToHex(bytes: Uint8Array): string;
|
export declare function bytesToHex(bytes: Uint8Array): string;
|
||||||
|
export declare function hexToBytes(hexString: string): Uint8Array;
|
||||||
export declare function bytesToBN(bytes: Uint8Array): bigint;
|
export declare function bytesToBN(bytes: Uint8Array): bigint;
|
||||||
export declare function bnToBytes(bigint: bigint | string): Uint8Array;
|
export declare function bnToBytes(bigint: bigint | string): Uint8Array;
|
||||||
export declare function leBuff2Int(bytes: Uint8Array): BN;
|
export declare function leBuff2Int(bytes: Uint8Array): BN;
|
||||||
|
36
package.json
36
package.json
@ -1,10 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "tornado-cli",
|
"name": "tornado-cli",
|
||||||
"version": "1.0.1-alpha",
|
"version": "1.0.3-alpha",
|
||||||
"description": "Modern Toolsets for Privacy Pools on Ethereum",
|
"description": "Modern Toolsets for Privacy Pools on Ethereum",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.mjs",
|
"module": "./dist/index.mjs",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
"unpkg": "./dist/index.umd.js",
|
||||||
|
"jsdelivr": "./dist/index.umd.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"tornado-cli": "./dist/cli.js"
|
"tornado-cli": "./dist/cli.js"
|
||||||
},
|
},
|
||||||
@ -12,7 +14,9 @@
|
|||||||
"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",
|
||||||
"build": "yarn types && rollup -c",
|
"build:node": "ts-node scripts/fflate.ts && rollup -c",
|
||||||
|
"build:web": "webpack",
|
||||||
|
"build": "yarn types && yarn build:node && yarn build:web",
|
||||||
"start": "ts-node src/cli.ts",
|
"start": "ts-node src/cli.ts",
|
||||||
"startHelp": "ts-node src/cli.ts help",
|
"startHelp": "ts-node src/cli.ts help",
|
||||||
"createDeposit": "ts-node src/cli.ts create",
|
"createDeposit": "ts-node src/cli.ts create",
|
||||||
@ -20,8 +24,10 @@
|
|||||||
"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",
|
||||||
"syncEvents": "ts-node src/cli.ts syncEvents",
|
"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",
|
||||||
|
"decryptNotes": "ts-node src/cli.ts decryptNotes",
|
||||||
"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",
|
||||||
@ -46,23 +52,24 @@
|
|||||||
"yarn.lock"
|
"yarn.lock"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@colors/colors": "1.5.0",
|
"@metamask/eth-sig-util": "^7.0.1",
|
||||||
"@tornado/contracts": "1.0.0",
|
"@tornado/contracts": "1.0.0",
|
||||||
"@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",
|
||||||
"ajv": "^8.12.0",
|
"ajv": "^8.12.0",
|
||||||
"bignumber.js": "^9.1.2",
|
"bloomfilter.js": "^1.0.2",
|
||||||
"bn.js": "^5.2.1",
|
"bn.js": "^5.2.1",
|
||||||
"circomlibjs": "0.1.7",
|
"circomlibjs": "0.1.7",
|
||||||
"cli-table3": "^0.6.4",
|
|
||||||
"commander": "^12.0.0",
|
|
||||||
"cross-fetch": "^4.0.0",
|
"cross-fetch": "^4.0.0",
|
||||||
"dotenv": "^16.4.5",
|
|
||||||
"ethers": "^6.12.0",
|
"ethers": "^6.12.0",
|
||||||
"ffjavascript": "0.2.48",
|
"ffjavascript": "0.2.48",
|
||||||
"fflate": "^0.8.2",
|
"fflate": "^0.8.2"
|
||||||
"figlet": "^1.7.0",
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@colors/colors": "1.5.0",
|
||||||
|
"cli-table3": "^0.6.4",
|
||||||
|
"commander": "^12.0.0",
|
||||||
"http-proxy-agent": "^7.0.2",
|
"http-proxy-agent": "^7.0.2",
|
||||||
"https-proxy-agent": "^7.0.4",
|
"https-proxy-agent": "^7.0.4",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
@ -72,7 +79,6 @@
|
|||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-json": "^6.1.0",
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@rollup/plugin-replace": "^5.0.5",
|
|
||||||
"@typechain/ethers-v6": "^0.5.1",
|
"@typechain/ethers-v6": "^0.5.1",
|
||||||
"@types/bn.js": "^5.1.5",
|
"@types/bn.js": "^5.1.5",
|
||||||
"@types/circomlibjs": "^0.1.6",
|
"@types/circomlibjs": "^0.1.6",
|
||||||
@ -81,18 +87,24 @@
|
|||||||
"@types/node-fetch": "^2.6.11",
|
"@types/node-fetch": "^2.6.11",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.6.0",
|
"@typescript-eslint/eslint-plugin": "^7.6.0",
|
||||||
"@typescript-eslint/parser": "^7.6.0",
|
"@typescript-eslint/parser": "^7.6.0",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
"esbuild": "^0.20.2",
|
"esbuild": "^0.20.2",
|
||||||
|
"esbuild-loader": "^4.1.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
|
"figlet": "^1.7.0",
|
||||||
|
"node-polyfill-webpack-plugin": "^3.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"rollup": "^4.14.1",
|
"rollup": "^4.14.1",
|
||||||
"rollup-plugin-esbuild": "^6.1.1",
|
"rollup-plugin-esbuild": "^6.1.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tsc": "^2.0.4",
|
"tsc": "^2.0.4",
|
||||||
"typechain": "^8.3.2",
|
"typechain": "^8.3.2",
|
||||||
"typescript": "^5.4.4"
|
"typescript": "^5.4.4",
|
||||||
|
"webpack": "^5.91.0",
|
||||||
|
"webpack-cli": "^5.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ import esbuild from 'rollup-plugin-esbuild';
|
|||||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
import json from '@rollup/plugin-json';
|
import json from '@rollup/plugin-json';
|
||||||
import replace from '@rollup/plugin-replace';
|
|
||||||
import pkgJson from './package.json' assert { type: 'json' };
|
import pkgJson from './package.json' assert { type: 'json' };
|
||||||
|
|
||||||
const external = Object.keys(pkgJson.dependencies).concat(...[
|
const external = Object.keys(pkgJson.dependencies).concat(
|
||||||
'@tornado/websnark/src/utils',
|
Object.keys(pkgJson.optionalDependencies),
|
||||||
'@tornado/websnark/src/groth16',
|
[
|
||||||
]);
|
'@tornado/websnark/src/utils',
|
||||||
|
'@tornado/websnark/src/groth16',
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const config = [
|
const config = [
|
||||||
{
|
{
|
||||||
@ -28,8 +30,8 @@ const config = [
|
|||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
target: 'es2016',
|
target: 'es2016',
|
||||||
}),
|
}),
|
||||||
nodeResolve(),
|
|
||||||
commonjs(),
|
commonjs(),
|
||||||
|
nodeResolve(),
|
||||||
json()
|
json()
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -53,28 +55,6 @@ const config = [
|
|||||||
json()
|
json()
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
input: 'src/cli.ts',
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
file: pkgJson.bin[pkgJson.name],
|
|
||||||
format: "cjs",
|
|
||||||
esModule: false,
|
|
||||||
banner: '#!/usr/bin/env node\n'
|
|
||||||
},
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
esbuild({
|
|
||||||
include: /\.[jt]sx?$/,
|
|
||||||
minify: false,
|
|
||||||
sourceMap: true,
|
|
||||||
target: 'es2016',
|
|
||||||
}),
|
|
||||||
nodeResolve(),
|
|
||||||
commonjs(),
|
|
||||||
json()
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
input: 'src/merkleTreeWorker.ts',
|
input: 'src/merkleTreeWorker.ts',
|
||||||
output: [
|
output: [
|
||||||
@ -92,36 +72,10 @@ const config = [
|
|||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
target: 'es2016',
|
target: 'es2016',
|
||||||
}),
|
}),
|
||||||
nodeResolve(),
|
|
||||||
commonjs(),
|
commonjs(),
|
||||||
|
nodeResolve(),
|
||||||
json()
|
json()
|
||||||
],
|
],
|
||||||
},
|
|
||||||
{
|
|
||||||
input: 'src/merkleTreeWorker.ts',
|
|
||||||
output: [
|
|
||||||
{
|
|
||||||
file: 'static/merkleTreeWorker.umd.js',
|
|
||||||
format: "umd",
|
|
||||||
esModule: false
|
|
||||||
},
|
|
||||||
],
|
|
||||||
treeshake: 'smallest',
|
|
||||||
external: ['web-worker'],
|
|
||||||
plugins: [
|
|
||||||
esbuild({
|
|
||||||
include: /\.[jt]sx?$/,
|
|
||||||
minify: false,
|
|
||||||
sourceMap: true,
|
|
||||||
target: 'es2016',
|
|
||||||
}),
|
|
||||||
nodeResolve(),
|
|
||||||
commonjs(),
|
|
||||||
json(),
|
|
||||||
replace({
|
|
||||||
'process.browser': 'true'
|
|
||||||
})
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
39
scripts/fflate.ts
Normal file
39
scripts/fflate.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Correct the resolve field of fflate as we don't use browser esm
|
||||||
|
*
|
||||||
|
* See issue https://github.com/101arrowz/fflate/issues/211
|
||||||
|
*/
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
const pkgJson = JSON.parse(fs.readFileSync('./node_modules/fflate/package.json', { encoding: 'utf8' }));
|
||||||
|
const backupJson = JSON.stringify(pkgJson, null, 2);
|
||||||
|
|
||||||
|
let changes = false
|
||||||
|
|
||||||
|
if (pkgJson.module.includes('browser')) {
|
||||||
|
pkgJson.module = './esm/index.mjs';
|
||||||
|
|
||||||
|
changes = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pkgJson.exports['.']?.import?.types && pkgJson.exports?.['.']?.import?.types.includes('browser')) {
|
||||||
|
pkgJson.exports['.'] = {
|
||||||
|
...pkgJson.exports['.'],
|
||||||
|
"import": {
|
||||||
|
"types": "./esm/index.d.mts",
|
||||||
|
"default": "./esm/index.mjs"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"types": "./lib/index.d.ts",
|
||||||
|
"default": "./lib/index.cjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changes = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (changes) {
|
||||||
|
fs.writeFileSync('./node_modules/fflate/package.backup.json', backupJson + '\n');
|
||||||
|
fs.writeFileSync('./node_modules/fflate/package.json', JSON.stringify(pkgJson, null, 2) + '\n');
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any, prettier/prettier */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import workerThreads from 'worker_threads';
|
import workerThreads from 'worker_threads';
|
||||||
import { MerkleTree, Element, TreeEdge, PartialMerkleTree } from '@tornado/fixed-merkle-tree';
|
import { MerkleTree, Element, TreeEdge, PartialMerkleTree } from '@tornado/fixed-merkle-tree';
|
||||||
import { mimc, isNode } from './services';
|
import { mimc, isNode } from './services';
|
||||||
@ -65,5 +65,5 @@ if (isNode && workerThreads) {
|
|||||||
postMessage(merkleTree.toString());
|
postMessage(merkleTree.toString());
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Error('This browser / environment doesn\'t support workers!');
|
throw new Error('This browser / environment does not support workers!');
|
||||||
}
|
}
|
||||||
|
542
src/program.ts
542
src/program.ts
@ -13,6 +13,7 @@ import {
|
|||||||
RelayerRegistry__factory,
|
RelayerRegistry__factory,
|
||||||
Aggregator__factory,
|
Aggregator__factory,
|
||||||
Governance__factory,
|
Governance__factory,
|
||||||
|
Echoer__factory,
|
||||||
} from '@tornado/contracts';
|
} from '@tornado/contracts';
|
||||||
import {
|
import {
|
||||||
JsonRpcProvider,
|
JsonRpcProvider,
|
||||||
@ -27,7 +28,7 @@ import {
|
|||||||
ZeroAddress,
|
ZeroAddress,
|
||||||
MaxUint256,
|
MaxUint256,
|
||||||
Transaction,
|
Transaction,
|
||||||
BigNumberish,
|
getAddress,
|
||||||
} from 'ethers';
|
} from 'ethers';
|
||||||
import type MerkleTree from '@tornado/fixed-merkle-tree';
|
import type MerkleTree from '@tornado/fixed-merkle-tree';
|
||||||
import * as packageJson from '../package.json';
|
import * as packageJson from '../package.json';
|
||||||
@ -62,6 +63,7 @@ import {
|
|||||||
TornadoFeeOracle,
|
TornadoFeeOracle,
|
||||||
TokenPriceOracle,
|
TokenPriceOracle,
|
||||||
calculateSnarkProof,
|
calculateSnarkProof,
|
||||||
|
NodeEchoService,
|
||||||
NodeEncryptedNotesService,
|
NodeEncryptedNotesService,
|
||||||
NodeGovernanceService,
|
NodeGovernanceService,
|
||||||
RelayerClient,
|
RelayerClient,
|
||||||
@ -70,27 +72,34 @@ import {
|
|||||||
Invoice,
|
Invoice,
|
||||||
fetchData,
|
fetchData,
|
||||||
fetchDataOptions,
|
fetchDataOptions,
|
||||||
networkConfig,
|
NetId,
|
||||||
subdomains,
|
NetIdType,
|
||||||
|
getInstanceByAddress,
|
||||||
|
getSubdomains,
|
||||||
|
getConfig,
|
||||||
Config,
|
Config,
|
||||||
enabledChains,
|
enabledChains,
|
||||||
substring,
|
substring,
|
||||||
|
NoteAccount,
|
||||||
|
parseRecoveryKey,
|
||||||
|
getSupportedInstances,
|
||||||
|
TreeCache,
|
||||||
} from './services';
|
} from './services';
|
||||||
|
|
||||||
const DEFAULT_GAS_LIMIT = 600_000;
|
const DEFAULT_GAS_LIMIT = Number(process.env.DEFAULT_GAS_LIMIT) || 600_000;
|
||||||
const RELAYER_NETWORK = 1;
|
|
||||||
const TOKEN_PRICE_ORACLE = '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8';
|
const RELAYER_NETWORK = Number(process.env.RELAYER_NETWORK) || NetId.MAINNET;
|
||||||
|
|
||||||
// Where cached events, trees, circuits, and key is saved
|
// Where cached events, trees, circuits, and key is saved
|
||||||
const STATIC_DIR = process.env.CACHE_DIR || path.join(__dirname, '../static');
|
const STATIC_DIR = process.env.CACHE_DIR || path.join(__dirname, '../static');
|
||||||
const EVENTS_DIR = path.join(STATIC_DIR, './events');
|
const EVENTS_DIR = path.join(STATIC_DIR, './events');
|
||||||
const TREES_DIR = path.join(STATIC_DIR, './trees');
|
|
||||||
const MERKLE_WORKER_PATH =
|
const MERKLE_WORKER_PATH =
|
||||||
process.env.DISABLE_MERKLE_WORKER === 'true' ? undefined : path.join(STATIC_DIR, './merkleTreeWorker.js');
|
process.env.DISABLE_MERKLE_WORKER === 'true' ? undefined : path.join(STATIC_DIR, './merkleTreeWorker.js');
|
||||||
|
|
||||||
// Where we should backup notes and save events
|
// Where we should backup notes and save events
|
||||||
const USER_DIR = process.env.USER_DIR || '.';
|
const USER_DIR = process.env.USER_DIR || '.';
|
||||||
const SAVED_DIR = path.join(USER_DIR, './events');
|
const SAVED_DIR = path.join(USER_DIR, './events');
|
||||||
|
const SAVED_TREE_DIR = path.join(USER_DIR, './trees');
|
||||||
|
|
||||||
const CIRCUIT_PATH = path.join(__dirname, '../static/tornado.json');
|
const CIRCUIT_PATH = path.join(__dirname, '../static/tornado.json');
|
||||||
const KEY_PATH = path.join(__dirname, '../static/tornadoProvingKey.bin');
|
const KEY_PATH = path.join(__dirname, '../static/tornadoProvingKey.bin');
|
||||||
@ -107,6 +116,7 @@ export type commonProgramOptions = {
|
|||||||
graph?: string;
|
graph?: string;
|
||||||
ethGraph?: string;
|
ethGraph?: string;
|
||||||
disableGraph?: boolean;
|
disableGraph?: boolean;
|
||||||
|
accountKey?: string;
|
||||||
relayer?: string;
|
relayer?: string;
|
||||||
walletWithdrawal?: boolean;
|
walletWithdrawal?: boolean;
|
||||||
torPort?: number;
|
torPort?: number;
|
||||||
@ -161,6 +171,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),
|
||||||
|
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:
|
||||||
Boolean(options.walletWithdrawal) || (process.env.WALLET_WITHDRAWAL === 'true' ? true : undefined),
|
Boolean(options.walletWithdrawal) || (process.env.WALLET_WITHDRAWAL === 'true' ? true : undefined),
|
||||||
@ -221,7 +232,7 @@ export function getProgramGraphAPI(options: commonProgramOptions, config: Config
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getProgramProvider(
|
export function getProgramProvider(
|
||||||
netId: BigNumberish,
|
netId: NetIdType,
|
||||||
rpcUrl: string = '',
|
rpcUrl: string = '',
|
||||||
config: Config,
|
config: Config,
|
||||||
providerOptions?: getProviderOptions,
|
providerOptions?: getProviderOptions,
|
||||||
@ -260,7 +271,7 @@ export async function getProgramRelayer({
|
|||||||
}: {
|
}: {
|
||||||
options: commonProgramOptions;
|
options: commonProgramOptions;
|
||||||
fetchDataOptions?: fetchDataOptions;
|
fetchDataOptions?: fetchDataOptions;
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
validRelayers?: RelayerInfo[] | Relayer[];
|
validRelayers?: RelayerInfo[] | Relayer[];
|
||||||
invalidRelayers?: RelayerError[];
|
invalidRelayers?: RelayerError[];
|
||||||
@ -268,9 +279,11 @@ export async function getProgramRelayer({
|
|||||||
}> {
|
}> {
|
||||||
const { ethRpc, ethGraph, relayer, disableGraph } = options;
|
const { ethRpc, ethGraph, relayer, disableGraph } = options;
|
||||||
|
|
||||||
const netConfig = networkConfig[`netId${netId}`];
|
const netConfig = getConfig(netId);
|
||||||
|
|
||||||
const ethConfig = networkConfig[`netId${RELAYER_NETWORK}`];
|
const ethConfig = getConfig(RELAYER_NETWORK);
|
||||||
|
|
||||||
|
const subdomains = getSubdomains();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
aggregatorContract,
|
aggregatorContract,
|
||||||
@ -279,7 +292,7 @@ export async function getProgramRelayer({
|
|||||||
constants: { REGISTRY_BLOCK },
|
constants: { REGISTRY_BLOCK },
|
||||||
} = ethConfig;
|
} = ethConfig;
|
||||||
|
|
||||||
const provider = getProgramProvider(1, ethRpc, ethConfig, {
|
const provider = getProgramProvider(RELAYER_NETWORK, ethRpc, ethConfig, {
|
||||||
...fetchDataOptions,
|
...fetchDataOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -320,7 +333,11 @@ export async function getProgramRelayer({
|
|||||||
relayerClient.selectedRelayer = {
|
relayerClient.selectedRelayer = {
|
||||||
netId: relayerStatus.netId,
|
netId: relayerStatus.netId,
|
||||||
url: relayerStatus.url,
|
url: relayerStatus.url,
|
||||||
rewardAccount: relayerStatus.rewardAccount,
|
hostname: new URL(relayerStatus.url).hostname,
|
||||||
|
rewardAccount: getAddress(relayerStatus.rewardAccount),
|
||||||
|
instances: getSupportedInstances(relayerStatus.instances),
|
||||||
|
gasPrice: relayerStatus.gasPrices?.fast,
|
||||||
|
ethPrices: relayerStatus.ethPrices,
|
||||||
currentQueue: relayerStatus.currentQueue,
|
currentQueue: relayerStatus.currentQueue,
|
||||||
tornadoServiceFee: relayerStatus.tornadoServiceFee,
|
tornadoServiceFee: relayerStatus.tornadoServiceFee,
|
||||||
};
|
};
|
||||||
@ -336,13 +353,7 @@ export async function getProgramRelayer({
|
|||||||
const relayerStatus = validRelayers[0];
|
const relayerStatus = validRelayers[0];
|
||||||
|
|
||||||
if (relayerStatus) {
|
if (relayerStatus) {
|
||||||
relayerClient.selectedRelayer = {
|
relayerClient.selectedRelayer = relayerStatus;
|
||||||
netId: relayerStatus.netId,
|
|
||||||
url: relayerStatus.url,
|
|
||||||
rewardAccount: relayerStatus.rewardAccount,
|
|
||||||
currentQueue: relayerStatus.currentQueue,
|
|
||||||
tornadoServiceFee: relayerStatus.tornadoServiceFee,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -363,13 +374,7 @@ export async function getProgramRelayer({
|
|||||||
const relayerStatus = relayerClient.pickWeightedRandomRelayer(validRelayers);
|
const relayerStatus = relayerClient.pickWeightedRandomRelayer(validRelayers);
|
||||||
|
|
||||||
if (relayerStatus) {
|
if (relayerStatus) {
|
||||||
relayerClient.selectedRelayer = {
|
relayerClient.selectedRelayer = relayerStatus;
|
||||||
netId: relayerStatus.netId,
|
|
||||||
url: relayerStatus.url,
|
|
||||||
rewardAccount: relayerStatus.rewardAccount,
|
|
||||||
currentQueue: relayerStatus.currentQueue,
|
|
||||||
tornadoServiceFee: relayerStatus.tornadoServiceFee,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -454,10 +459,10 @@ 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)
|
||||||
.argument('<currency>', 'Currency to deposit on Tornado Cash')
|
.argument('<currency>', 'Currency to deposit on Tornado Cash')
|
||||||
.argument('<amount>', 'Amount to deposit on Tornado Cash')
|
.argument('<amount>', 'Amount to deposit on Tornado Cash')
|
||||||
.action(async (netId: string | number, currency: string, amount: string) => {
|
.action(async (netId: NetIdType, currency: string, amount: string) => {
|
||||||
currency = currency.toLowerCase();
|
currency = currency.toLowerCase();
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
routerContract,
|
routerContract,
|
||||||
@ -514,16 +519,17 @@ 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)
|
||||||
.argument('<currency>', 'Currency to deposit on Tornado Cash')
|
.argument('<currency>', 'Currency to deposit on Tornado Cash')
|
||||||
.argument('<amount>', 'Amount to deposit on Tornado Cash')
|
.argument('<amount>', 'Amount to deposit on Tornado Cash')
|
||||||
.action(async (netId: string | number, currency: string, amount: string, cmdOptions: commonProgramOptions) => {
|
.action(async (netId: NetIdType, currency: string, amount: string, cmdOptions: commonProgramOptions) => {
|
||||||
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
||||||
currency = currency.toLowerCase();
|
currency = currency.toLowerCase();
|
||||||
const { rpc } = options;
|
const { rpc, accountKey } = options;
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
multicall: multicallAddress,
|
multicallContract,
|
||||||
routerContract,
|
routerContract,
|
||||||
|
echoContract,
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
tokens: { [currency]: currencyConfig },
|
tokens: { [currency]: currencyConfig },
|
||||||
} = config;
|
} = config;
|
||||||
@ -546,6 +552,14 @@ export function tornadoProgram() {
|
|||||||
provider,
|
provider,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const noteAccount = accountKey
|
||||||
|
? new NoteAccount({
|
||||||
|
netId,
|
||||||
|
recoveryKey: accountKey,
|
||||||
|
Echoer: Echoer__factory.connect(echoContract, provider),
|
||||||
|
})
|
||||||
|
: undefined;
|
||||||
|
|
||||||
if (!signer) {
|
if (!signer) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Signer not defined, make sure you have either viewOnly address, mnemonic, or private key configured',
|
'Signer not defined, make sure you have either viewOnly address, mnemonic, or private key configured',
|
||||||
@ -553,7 +567,7 @@ export function tornadoProgram() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TornadoProxy = TornadoRouter__factory.connect(routerContract, signer);
|
const TornadoProxy = TornadoRouter__factory.connect(routerContract, signer);
|
||||||
const Multicall = Multicall__factory.connect(multicallAddress, provider);
|
const Multicall = Multicall__factory.connect(multicallContract, provider);
|
||||||
const Token = tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined;
|
const Token = tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined;
|
||||||
|
|
||||||
const [ethBalance, tokenBalance, tokenApprovals] = await multicall(Multicall, [
|
const [ethBalance, tokenBalance, tokenApprovals] = await multicall(Multicall, [
|
||||||
@ -562,22 +576,20 @@ export function tornadoProgram() {
|
|||||||
name: 'getEthBalance',
|
name: 'getEthBalance',
|
||||||
params: [signer.address],
|
params: [signer.address],
|
||||||
},
|
},
|
||||||
/* eslint-disable prettier/prettier */
|
|
||||||
...(!isEth
|
...(!isEth
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
contract: Token as ERC20,
|
contract: Token as ERC20,
|
||||||
name: 'balanceOf',
|
name: 'balanceOf',
|
||||||
params: [signer.address],
|
params: [signer.address],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
contract: Token as ERC20,
|
contract: Token as ERC20,
|
||||||
name: 'allowance',
|
name: 'allowance',
|
||||||
params: [signer.address, routerContract],
|
params: [signer.address, routerContract],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
/* eslint-enable prettier/prettier */
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isEth && denomination > ethBalance) {
|
if (isEth && denomination > ethBalance) {
|
||||||
@ -609,18 +621,36 @@ export function tornadoProgram() {
|
|||||||
|
|
||||||
const { note, noteHex, commitmentHex } = deposit;
|
const { note, noteHex, commitmentHex } = deposit;
|
||||||
|
|
||||||
|
const encryptedNote = noteAccount
|
||||||
|
? noteAccount.encryptNote({
|
||||||
|
address: instanceAddress,
|
||||||
|
noteHex,
|
||||||
|
})
|
||||||
|
: '0x';
|
||||||
|
|
||||||
|
const backupFile = `./backup-tornado-${currency}-${amount}-${netId}-${noteHex.slice(0, 10)}.txt`;
|
||||||
|
|
||||||
console.log(`New deposit: ${deposit.toString()}\n`);
|
console.log(`New deposit: ${deposit.toString()}\n`);
|
||||||
|
|
||||||
await writeFile(`./backup-tornado-${currency}-${amount}-${netId}-${noteHex.slice(0, 10)}.txt`, note, {
|
console.log(`Writing note backup at ${backupFile}\n`);
|
||||||
encoding: 'utf8',
|
|
||||||
});
|
await writeFile(backupFile, note, { encoding: 'utf8' });
|
||||||
|
|
||||||
|
if (encryptedNote !== '0x') {
|
||||||
|
console.log(`Storing encrypted note on-chain for backup (Account key: ${accountKey})\n`);
|
||||||
|
}
|
||||||
|
|
||||||
await programSendTransaction({
|
await programSendTransaction({
|
||||||
signer,
|
signer,
|
||||||
options,
|
options,
|
||||||
populatedTransaction: await TornadoProxy.deposit.populateTransaction(instanceAddress, commitmentHex, '0x', {
|
populatedTransaction: await TornadoProxy.deposit.populateTransaction(
|
||||||
value: isEth ? denomination : BigInt(0),
|
instanceAddress,
|
||||||
}),
|
commitmentHex,
|
||||||
|
encryptedNote,
|
||||||
|
{
|
||||||
|
value: isEth ? denomination : BigInt(0),
|
||||||
|
},
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
@ -639,10 +669,10 @@ export function tornadoProgram() {
|
|||||||
|
|
||||||
const { currency, amount, netId, commitment } = new Invoice(invoiceString);
|
const { currency, amount, netId, commitment } = new Invoice(invoiceString);
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
multicall: multicallAddress,
|
multicallContract,
|
||||||
routerContract,
|
routerContract,
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
tokens: { [currency]: currencyConfig },
|
tokens: { [currency]: currencyConfig },
|
||||||
@ -673,7 +703,7 @@ export function tornadoProgram() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const TornadoProxy = TornadoRouter__factory.connect(routerContract, signer);
|
const TornadoProxy = TornadoRouter__factory.connect(routerContract, signer);
|
||||||
const Multicall = Multicall__factory.connect(multicallAddress, provider);
|
const Multicall = Multicall__factory.connect(multicallContract, provider);
|
||||||
const Token = tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined;
|
const Token = tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined;
|
||||||
|
|
||||||
const [ethBalance, tokenBalance, tokenApprovals] = await multicall(Multicall, [
|
const [ethBalance, tokenBalance, tokenApprovals] = await multicall(Multicall, [
|
||||||
@ -682,22 +712,20 @@ export function tornadoProgram() {
|
|||||||
name: 'getEthBalance',
|
name: 'getEthBalance',
|
||||||
params: [signer.address],
|
params: [signer.address],
|
||||||
},
|
},
|
||||||
/* eslint-disable prettier/prettier */
|
|
||||||
...(!isEth
|
...(!isEth
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
contract: Token as ERC20,
|
contract: Token as ERC20,
|
||||||
name: 'balanceOf',
|
name: 'balanceOf',
|
||||||
params: [signer.address],
|
params: [signer.address],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
contract: Token as ERC20,
|
contract: Token as ERC20,
|
||||||
name: 'allowance',
|
name: 'allowance',
|
||||||
params: [signer.address, routerContract],
|
params: [signer.address, routerContract],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
/* eslint-enable prettier/prettier */
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (isEth && denomination > ethBalance) {
|
if (isEth && denomination > ethBalance) {
|
||||||
@ -756,14 +784,15 @@ export function tornadoProgram() {
|
|||||||
|
|
||||||
const { netId, currency, amount, commitmentHex, nullifierHex, nullifier, secret } = deposit;
|
const { netId, currency, amount, commitmentHex, nullifierHex, nullifier, secret } = deposit;
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tornadoSubgraph,
|
tornadoSubgraph,
|
||||||
deployedBlock,
|
deployedBlock,
|
||||||
nativeCurrency,
|
nativeCurrency,
|
||||||
|
multicallContract,
|
||||||
routerContract,
|
routerContract,
|
||||||
multicall: multicallAddress,
|
offchainOracleContract,
|
||||||
ovmGasPriceOracleContract,
|
ovmGasPriceOracleContract,
|
||||||
tokens: { [currency]: currencyConfig },
|
tokens: { [currency]: currencyConfig },
|
||||||
} = config;
|
} = config;
|
||||||
@ -799,17 +828,18 @@ export function tornadoProgram() {
|
|||||||
|
|
||||||
const Tornado = Tornado__factory.connect(instanceAddress, provider);
|
const Tornado = Tornado__factory.connect(instanceAddress, provider);
|
||||||
const TornadoProxy = TornadoRouter__factory.connect(routerContract, !walletWithdrawal ? provider : signer);
|
const TornadoProxy = TornadoRouter__factory.connect(routerContract, !walletWithdrawal ? provider : signer);
|
||||||
const Multicall = Multicall__factory.connect(multicallAddress, provider);
|
const Multicall = Multicall__factory.connect(multicallContract, provider);
|
||||||
|
|
||||||
const tornadoFeeOracle = new TornadoFeeOracle(
|
const tornadoFeeOracle = new TornadoFeeOracle(
|
||||||
ovmGasPriceOracleContract
|
ovmGasPriceOracleContract
|
||||||
? OvmGasPriceOracle__factory.connect(ovmGasPriceOracleContract, provider)
|
? OvmGasPriceOracle__factory.connect(ovmGasPriceOracleContract, provider)
|
||||||
: undefined,
|
: undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const tokenPriceOracle = new TokenPriceOracle(
|
const tokenPriceOracle = new TokenPriceOracle(
|
||||||
provider,
|
provider,
|
||||||
Multicall,
|
Multicall,
|
||||||
OffchainOracle__factory.connect(TOKEN_PRICE_ORACLE, provider),
|
offchainOracleContract ? OffchainOracle__factory.connect(offchainOracleContract, provider) : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const depositsServiceConstructor = {
|
const depositsServiceConstructor = {
|
||||||
@ -849,9 +879,9 @@ export function tornadoProgram() {
|
|||||||
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
||||||
const depositTreeInitiator = await (async () => {
|
const depositTreeInitiator = await (async () => {
|
||||||
if (MERKLE_WORKER_PATH) {
|
if (MERKLE_WORKER_PATH) {
|
||||||
return () => merkleTreeService.verifyTree({ events: depositEvents }) as Promise<MerkleTree>;
|
return () => merkleTreeService.verifyTree(depositEvents) as Promise<MerkleTree>;
|
||||||
}
|
}
|
||||||
return (await merkleTreeService.verifyTree({ events: depositEvents })) as MerkleTree;
|
return (await merkleTreeService.verifyTree(depositEvents)) as MerkleTree;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
let depositTreePromise: Promise<MerkleTree> | MerkleTree;
|
let depositTreePromise: Promise<MerkleTree> | MerkleTree;
|
||||||
@ -916,15 +946,13 @@ export function tornadoProgram() {
|
|||||||
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,
|
||||||
/* eslint-disable prettier/prettier */
|
|
||||||
!walletWithdrawal
|
!walletWithdrawal
|
||||||
? getProgramRelayer({
|
? getProgramRelayer({
|
||||||
options,
|
options,
|
||||||
fetchDataOptions,
|
fetchDataOptions,
|
||||||
netId,
|
netId,
|
||||||
}).then(({ relayerClient }) => relayerClient)
|
}).then(({ relayerClient }) => relayerClient)
|
||||||
: undefined,
|
: undefined,
|
||||||
/* eslint-enable prettier/prettier */
|
|
||||||
tornadoFeeOracle.fetchL1OptimismFee(),
|
tornadoFeeOracle.fetchL1OptimismFee(),
|
||||||
!isEth ? tokenPriceOracle.fetchPrices([tokenAddress as string]).then((p) => p[0]) : BigInt(0),
|
!isEth ? tokenPriceOracle.fetchPrices([tokenAddress as string]).then((p) => p[0]) : BigInt(0),
|
||||||
provider.getFeeData(),
|
provider.getFeeData(),
|
||||||
@ -1118,7 +1146,7 @@ export function tornadoProgram() {
|
|||||||
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;
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
tornadoSubgraph,
|
tornadoSubgraph,
|
||||||
@ -1176,9 +1204,9 @@ export function tornadoProgram() {
|
|||||||
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
||||||
const depositTreePromise = await (async () => {
|
const depositTreePromise = await (async () => {
|
||||||
if (MERKLE_WORKER_PATH) {
|
if (MERKLE_WORKER_PATH) {
|
||||||
return () => merkleTreeService.verifyTree({ events: depositEvents }) as Promise<MerkleTree>;
|
return () => merkleTreeService.verifyTree(depositEvents) as Promise<MerkleTree>;
|
||||||
}
|
}
|
||||||
return (await merkleTreeService.verifyTree({ events: depositEvents })) as MerkleTree;
|
return (await merkleTreeService.verifyTree(depositEvents)) as MerkleTree;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const [withdrawalEvents] = await Promise.all([
|
const [withdrawalEvents] = await Promise.all([
|
||||||
@ -1235,32 +1263,31 @@ export function tornadoProgram() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('syncEvents')
|
.command('updateEvents')
|
||||||
.description('Sync the local cache file of tornado cash events.\n\n')
|
.description('Sync the local cache file of tornado cash events.\n\n')
|
||||||
.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)
|
||||||
.argument('[currency]', 'Currency to sync events')
|
.argument('[currency]', 'Currency to sync events')
|
||||||
.action(
|
.action(
|
||||||
async (
|
async (netIdOpts: NetIdType | undefined, currencyOpts: string | undefined, cmdOptions: commonProgramOptions) => {
|
||||||
netIdOpts: number | string | undefined,
|
|
||||||
currencyOpts: string | undefined,
|
|
||||||
cmdOptions: commonProgramOptions,
|
|
||||||
) => {
|
|
||||||
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
||||||
const { rpc } = options;
|
const { rpc } = options;
|
||||||
|
|
||||||
const networks = netIdOpts ? [netIdOpts] : enabledChains;
|
const networks = netIdOpts ? [netIdOpts] : enabledChains;
|
||||||
|
|
||||||
for (const netId of networks) {
|
for (const netId of networks) {
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
const {
|
const {
|
||||||
tornadoSubgraph,
|
tornadoSubgraph,
|
||||||
registrySubgraph,
|
registrySubgraph,
|
||||||
|
governanceSubgraph,
|
||||||
tokens,
|
tokens,
|
||||||
|
nativeCurrency,
|
||||||
routerContract,
|
routerContract,
|
||||||
|
echoContract,
|
||||||
registryContract,
|
registryContract,
|
||||||
['governance.contract.tornadocash.eth']: governanceContract,
|
governanceContract,
|
||||||
deployedBlock,
|
deployedBlock,
|
||||||
constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, ENCRYPTED_NOTES_BLOCK },
|
constants: { GOVERNANCE_BLOCK, REGISTRY_BLOCK, NOTE_ACCOUNT_BLOCK, ENCRYPTED_NOTES_BLOCK },
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
const provider = getProgramProvider(netId, rpc, config, {
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
@ -1272,9 +1299,8 @@ export function tornadoProgram() {
|
|||||||
const governanceService = new NodeGovernanceService({
|
const governanceService = new NodeGovernanceService({
|
||||||
netId,
|
netId,
|
||||||
provider,
|
provider,
|
||||||
// to-do connect governance with subgraph
|
graphApi,
|
||||||
graphApi: '',
|
subgraphName: governanceSubgraph,
|
||||||
subgraphName: '',
|
|
||||||
Governance: Governance__factory.connect(governanceContract, provider),
|
Governance: Governance__factory.connect(governanceContract, provider),
|
||||||
deployedBlock: GOVERNANCE_BLOCK,
|
deployedBlock: GOVERNANCE_BLOCK,
|
||||||
fetchDataOptions,
|
fetchDataOptions,
|
||||||
@ -1301,6 +1327,20 @@ export function tornadoProgram() {
|
|||||||
await registryService.updateEvents();
|
await registryService.updateEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const echoService = new NodeEchoService({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName: tornadoSubgraph,
|
||||||
|
Echoer: Echoer__factory.connect(echoContract, provider),
|
||||||
|
deployedBlock: NOTE_ACCOUNT_BLOCK,
|
||||||
|
fetchDataOptions,
|
||||||
|
cacheDirectory: EVENTS_DIR,
|
||||||
|
userDirectory: SAVED_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
await echoService.updateEvents();
|
||||||
|
|
||||||
const encryptedNotesService = new NodeEncryptedNotesService({
|
const encryptedNotesService = new NodeEncryptedNotesService({
|
||||||
netId,
|
netId,
|
||||||
provider,
|
provider,
|
||||||
@ -1359,23 +1399,31 @@ export function tornadoProgram() {
|
|||||||
merkleWorkerPath: MERKLE_WORKER_PATH,
|
merkleWorkerPath: MERKLE_WORKER_PATH,
|
||||||
});
|
});
|
||||||
|
|
||||||
const depositEvents = (await depositsService.updateEvents()).events;
|
const treeCache = new TreeCache({
|
||||||
|
netId,
|
||||||
|
amount,
|
||||||
|
currency,
|
||||||
|
userDirectory: SAVED_TREE_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
const depositEvents = (await depositsService.updateEvents()).events as DepositsEvents[];
|
||||||
|
|
||||||
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
// If we have MERKLE_WORKER_PATH run worker at background otherwise resolve it here
|
||||||
const depositTreePromise = await (async () => {
|
const depositTreePromise = await (async () => {
|
||||||
if (MERKLE_WORKER_PATH) {
|
if (MERKLE_WORKER_PATH) {
|
||||||
return () =>
|
return () => merkleTreeService.verifyTree(depositEvents) as Promise<MerkleTree>;
|
||||||
merkleTreeService.verifyTree({ events: depositEvents as DepositsEvents[] }) as Promise<MerkleTree>;
|
|
||||||
}
|
}
|
||||||
return (await merkleTreeService.verifyTree({
|
return (await merkleTreeService.verifyTree(depositEvents)) as MerkleTree;
|
||||||
events: depositEvents as DepositsEvents[],
|
|
||||||
})) as MerkleTree;
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
await Promise.all([
|
const [tree] = await Promise.all([
|
||||||
withdrawalsService.updateEvents(),
|
|
||||||
typeof depositTreePromise === 'function' ? depositTreePromise() : depositTreePromise,
|
typeof depositTreePromise === 'function' ? depositTreePromise() : depositTreePromise,
|
||||||
|
withdrawalsService.updateEvents(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if (nativeCurrency === currency) {
|
||||||
|
await treeCache.createTree(depositEvents, tree);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1388,7 +1436,7 @@ export function tornadoProgram() {
|
|||||||
.command('relayers')
|
.command('relayers')
|
||||||
.description('List all registered relayers from the tornado cash registry.\n\n')
|
.description('List all registered relayers from the tornado cash registry.\n\n')
|
||||||
.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 (netIdOpts: number | string, 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 = await getProgramRelayer({
|
||||||
@ -1446,6 +1494,199 @@ export function tornadoProgram() {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('createAccount')
|
||||||
|
.description(
|
||||||
|
'Creates and save on-chain account that would store encrypted notes. \n\n' +
|
||||||
|
'Would first lookup on on-chain records to see if the notes are stored. \n\n' +
|
||||||
|
'Requires a valid signable wallet (mnemonic or a private key) to work (Since they would encrypt or encrypted)',
|
||||||
|
)
|
||||||
|
.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 config = getConfig(netId);
|
||||||
|
|
||||||
|
const {
|
||||||
|
echoContract,
|
||||||
|
tornadoSubgraph,
|
||||||
|
constants: { ['NOTE_ACCOUNT_BLOCK']: deployedBlock },
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
|
...fetchDataOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const signer = getProgramSigner({
|
||||||
|
options,
|
||||||
|
provider,
|
||||||
|
});
|
||||||
|
|
||||||
|
const graphApi = getProgramGraphAPI(options, config);
|
||||||
|
|
||||||
|
if (!signer || signer instanceof VoidSigner) {
|
||||||
|
throw new Error(
|
||||||
|
'No wallet found, make your you have supplied a valid mnemonic or private key before using this command',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find for any existing note accounts
|
||||||
|
*/
|
||||||
|
const walletPublicKey = NoteAccount.getWalletPublicKey(signer);
|
||||||
|
|
||||||
|
const Echoer = Echoer__factory.connect(echoContract, provider);
|
||||||
|
|
||||||
|
const newAccount = new NoteAccount({
|
||||||
|
netId,
|
||||||
|
Echoer,
|
||||||
|
});
|
||||||
|
|
||||||
|
const echoService = new NodeEchoService({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName: tornadoSubgraph,
|
||||||
|
Echoer,
|
||||||
|
deployedBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
cacheDirectory: EVENTS_DIR,
|
||||||
|
userDirectory: SAVED_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Getting historic note accounts would take a while\n');
|
||||||
|
|
||||||
|
const echoEvents = (await echoService.updateEvents()).events;
|
||||||
|
|
||||||
|
const userEvents = echoEvents.filter(({ address }) => address === signer.address);
|
||||||
|
|
||||||
|
const existingAccounts = newAccount.decryptAccountsWithWallet(signer, userEvents);
|
||||||
|
|
||||||
|
const accountsTable = new Table();
|
||||||
|
|
||||||
|
if (existingAccounts.length) {
|
||||||
|
accountsTable.push(
|
||||||
|
[{ colSpan: 2, content: `Note Accounts (${netId})`, hAlign: 'center' }],
|
||||||
|
[{ colSpan: 2, content: `Backed up by: ${signer.address}`, hAlign: 'center' }],
|
||||||
|
['blockNumber', 'noteAccount'].map((content) => ({ content: colors.red.bold(content) })),
|
||||||
|
...existingAccounts.map(({ blockNumber, recoveryKey }) => {
|
||||||
|
return [blockNumber, recoveryKey];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(accountsTable.toString() + '\n');
|
||||||
|
} else {
|
||||||
|
accountsTable.push(
|
||||||
|
[{ colSpan: 1, content: `New Note Account (${netId})`, hAlign: 'center' }],
|
||||||
|
['noteAccount'].map((content) => ({ content: colors.red.bold(content) })),
|
||||||
|
[newAccount.recoveryKey],
|
||||||
|
[{ colSpan: 1, content: `Would be backed up by: ${signer.address}`, hAlign: 'center' }],
|
||||||
|
);
|
||||||
|
|
||||||
|
const fileName = `backup-note-account-key-0x${newAccount.recoveryKey.slice(0, 8)}.txt`;
|
||||||
|
|
||||||
|
console.log('\n' + accountsTable.toString() + '\n');
|
||||||
|
|
||||||
|
console.log(`Writing backup to ${fileName}\n`);
|
||||||
|
|
||||||
|
await writeFile(fileName, newAccount.recoveryKey + '\n');
|
||||||
|
|
||||||
|
console.log('Backup encrypted account on-chain to use on UI?\n');
|
||||||
|
|
||||||
|
await promptConfirmation(options.nonInteractive);
|
||||||
|
|
||||||
|
const { data } = newAccount.getEncryptedAccount(walletPublicKey);
|
||||||
|
|
||||||
|
console.log('Sending encrypted note account backup transaction through wallet\n');
|
||||||
|
|
||||||
|
await programSendTransaction({
|
||||||
|
signer: signer as TornadoVoidSigner | TornadoWallet,
|
||||||
|
options,
|
||||||
|
populatedTransaction: await Echoer.echo.populateTransaction(data),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('decryptNotes')
|
||||||
|
.description('Fetch notes from deposit events and decrypt them. \n\n' + 'Requires a valid account key to work')
|
||||||
|
.argument('<netId>', 'Network Chain ID to connect with (see https://chainlist.org for examples)', parseNumber)
|
||||||
|
.argument(
|
||||||
|
'[accountKey]',
|
||||||
|
'Account key generated from UI or the createAccount to store encrypted notes on-chain',
|
||||||
|
parseRecoveryKey,
|
||||||
|
)
|
||||||
|
.action(async (netId: NetIdType, accountKey: string | undefined, cmdOptions: commonProgramOptions) => {
|
||||||
|
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
||||||
|
const { rpc } = options;
|
||||||
|
if (!accountKey) {
|
||||||
|
accountKey = options.accountKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = getConfig(netId);
|
||||||
|
|
||||||
|
const {
|
||||||
|
routerContract,
|
||||||
|
echoContract,
|
||||||
|
tornadoSubgraph,
|
||||||
|
constants: { ENCRYPTED_NOTES_BLOCK },
|
||||||
|
} = config;
|
||||||
|
|
||||||
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
|
...fetchDataOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
const graphApi = getProgramGraphAPI(options, config);
|
||||||
|
|
||||||
|
if (!accountKey) {
|
||||||
|
throw new Error(
|
||||||
|
'No account key find! Please supply correct account key from either UI or find one with createAccount command',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Echoer = Echoer__factory.connect(echoContract, provider);
|
||||||
|
|
||||||
|
const noteAccount = new NoteAccount({
|
||||||
|
netId,
|
||||||
|
recoveryKey: accountKey,
|
||||||
|
Echoer,
|
||||||
|
});
|
||||||
|
|
||||||
|
const encryptedNotesService = new NodeEncryptedNotesService({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName: tornadoSubgraph,
|
||||||
|
Router: TornadoRouter__factory.connect(routerContract, provider),
|
||||||
|
deployedBlock: ENCRYPTED_NOTES_BLOCK,
|
||||||
|
fetchDataOptions,
|
||||||
|
cacheDirectory: EVENTS_DIR,
|
||||||
|
userDirectory: SAVED_DIR,
|
||||||
|
});
|
||||||
|
|
||||||
|
const encryptedNoteEvents = (await encryptedNotesService.updateEvents()).events;
|
||||||
|
|
||||||
|
const accountsTable = new Table();
|
||||||
|
|
||||||
|
accountsTable.push(
|
||||||
|
[{ colSpan: 2, content: `Note Accounts (${netId})`, hAlign: 'center' }],
|
||||||
|
[{ colSpan: 2, content: `Account key: ${accountKey}`, hAlign: 'center' }],
|
||||||
|
['blockNumber', 'note'].map((content) => ({ content: colors.red.bold(content) })),
|
||||||
|
...noteAccount.decryptNotes(encryptedNoteEvents).map(({ blockNumber, address, noteHex }) => {
|
||||||
|
const { amount, currency } = getInstanceByAddress({ netId, address }) as { amount: string; currency: string };
|
||||||
|
|
||||||
|
return [blockNumber, `tornado-${currency}-${amount}-${netId}-${noteHex}`];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('\n' + accountsTable.toString() + '\n');
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('send')
|
.command('send')
|
||||||
.description('Send ETH or ERC20 token to address.\n\n')
|
.description('Send ETH or ERC20 token to address.\n\n')
|
||||||
@ -1455,7 +1696,7 @@ export function tornadoProgram() {
|
|||||||
.argument('[token]', 'ERC20 Token Contract to check Token Balance', parseAddress)
|
.argument('[token]', 'ERC20 Token Contract to check Token Balance', parseAddress)
|
||||||
.action(
|
.action(
|
||||||
async (
|
async (
|
||||||
netId: string | number,
|
netId: NetIdType,
|
||||||
to: string,
|
to: string,
|
||||||
amountArgs: number | undefined,
|
amountArgs: number | undefined,
|
||||||
tokenArgs: string | undefined,
|
tokenArgs: string | undefined,
|
||||||
@ -1464,9 +1705,9 @@ export function tornadoProgram() {
|
|||||||
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
||||||
const { rpc, token: tokenOpts } = options;
|
const { rpc, token: tokenOpts } = options;
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const { currencyName, multicall: multicallAddress } = config;
|
const { currencyName, multicallContract } = config;
|
||||||
|
|
||||||
const provider = getProgramProvider(netId, rpc, config, {
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
...fetchDataOptions,
|
...fetchDataOptions,
|
||||||
@ -1482,7 +1723,7 @@ export function tornadoProgram() {
|
|||||||
|
|
||||||
const tokenAddress = tokenArgs ? parseAddress(tokenArgs) : tokenOpts;
|
const tokenAddress = tokenArgs ? parseAddress(tokenArgs) : tokenOpts;
|
||||||
|
|
||||||
const Multicall = Multicall__factory.connect(multicallAddress, provider);
|
const Multicall = Multicall__factory.connect(multicallContract, provider);
|
||||||
const Token = (tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined) as ERC20;
|
const Token = (tokenAddress ? ERC20__factory.connect(tokenAddress, signer) : undefined) as ERC20;
|
||||||
|
|
||||||
// Fetching feeData or nonce is unnecessary however we do this to estimate transfer amounts
|
// Fetching feeData or nonce is unnecessary however we do this to estimate transfer amounts
|
||||||
@ -1508,15 +1749,14 @@ export function tornadoProgram() {
|
|||||||
const txGasPrice = feeData.maxFeePerGas
|
const txGasPrice = feeData.maxFeePerGas
|
||||||
? feeData.maxFeePerGas + (feeData.maxPriorityFeePerGas || BigInt(0))
|
? feeData.maxFeePerGas + (feeData.maxPriorityFeePerGas || BigInt(0))
|
||||||
: feeData.gasPrice || BigInt(0);
|
: feeData.gasPrice || BigInt(0);
|
||||||
/* eslint-disable prettier/prettier */
|
|
||||||
const txFees = feeData.maxFeePerGas
|
const txFees = feeData.maxFeePerGas
|
||||||
? {
|
? {
|
||||||
maxFeePerGas: feeData.maxFeePerGas,
|
maxFeePerGas: feeData.maxFeePerGas,
|
||||||
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
|
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
gasPrice: feeData.gasPrice,
|
gasPrice: feeData.gasPrice,
|
||||||
};
|
};
|
||||||
|
|
||||||
let toSend: bigint;
|
let toSend: bigint;
|
||||||
|
|
||||||
@ -1543,6 +1783,11 @@ export function tornadoProgram() {
|
|||||||
const initCost = txGasPrice * BigInt('400000');
|
const initCost = txGasPrice * BigInt('400000');
|
||||||
toSend = ethBalance - initCost;
|
toSend = ethBalance - initCost;
|
||||||
|
|
||||||
|
if (ethBalance === BigInt(0) || ethBalance < initCost) {
|
||||||
|
const errMsg = `Invalid ${currencyName} balance, wants ${formatEther(initCost)} have ${formatEther(ethBalance)}`;
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
const estimatedGas = await provider.estimateGas({
|
const estimatedGas = await provider.estimateGas({
|
||||||
type: txType,
|
type: txType,
|
||||||
from: signer.address,
|
from: signer.address,
|
||||||
@ -1552,10 +1797,10 @@ export function tornadoProgram() {
|
|||||||
...txFees,
|
...txFees,
|
||||||
});
|
});
|
||||||
|
|
||||||
const bumpedGas = (estimatedGas !== BigInt(21000) && signer.gasLimitBump
|
const bumpedGas =
|
||||||
? (estimatedGas * (BigInt(10000) + BigInt(signer.gasLimitBump))) / BigInt(10000)
|
estimatedGas !== BigInt(21000) && signer.gasLimitBump
|
||||||
: estimatedGas
|
? (estimatedGas * (BigInt(10000) + BigInt(signer.gasLimitBump))) / BigInt(10000)
|
||||||
);
|
: estimatedGas;
|
||||||
|
|
||||||
toSend = ethBalance - txGasPrice * bumpedGas;
|
toSend = ethBalance - txGasPrice * bumpedGas;
|
||||||
}
|
}
|
||||||
@ -1567,15 +1812,14 @@ export function tornadoProgram() {
|
|||||||
populatedTransaction: tokenAddress
|
populatedTransaction: tokenAddress
|
||||||
? await Token.transfer.populateTransaction(to, toSend)
|
? await Token.transfer.populateTransaction(to, toSend)
|
||||||
: await signer.populateTransaction({
|
: await signer.populateTransaction({
|
||||||
type: txType,
|
type: txType,
|
||||||
from: signer.address,
|
from: signer.address,
|
||||||
to,
|
to,
|
||||||
value: toSend,
|
value: toSend,
|
||||||
nonce,
|
nonce,
|
||||||
...txFees,
|
...txFees,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
/* eslint-enable prettier/prettier */
|
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
},
|
},
|
||||||
@ -1589,7 +1833,7 @@ export function tornadoProgram() {
|
|||||||
.argument('[token]', 'ERC20 Token Contract to check Token Balance', parseAddress)
|
.argument('[token]', 'ERC20 Token Contract to check Token Balance', parseAddress)
|
||||||
.action(
|
.action(
|
||||||
async (
|
async (
|
||||||
netId: string | number,
|
netId: NetIdType,
|
||||||
addressArgs: string | undefined,
|
addressArgs: string | undefined,
|
||||||
tokenArgs: string | undefined,
|
tokenArgs: string | undefined,
|
||||||
cmdOptions: commonProgramOptions,
|
cmdOptions: commonProgramOptions,
|
||||||
@ -1597,14 +1841,9 @@ export function tornadoProgram() {
|
|||||||
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
const { options, fetchDataOptions } = await getProgramOptions(cmdOptions);
|
||||||
const { rpc, token: tokenOpts } = options;
|
const { rpc, token: tokenOpts } = options;
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const {
|
const { currencyName, multicallContract, tornContract, tokens } = config;
|
||||||
currencyName,
|
|
||||||
multicall: multicallAddress,
|
|
||||||
['torn.contract.tornadocash.eth']: tornTokenAddress,
|
|
||||||
tokens,
|
|
||||||
} = config;
|
|
||||||
|
|
||||||
const provider = getProgramProvider(netId, rpc, config, {
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
...fetchDataOptions,
|
...fetchDataOptions,
|
||||||
@ -1617,14 +1856,14 @@ export function tornadoProgram() {
|
|||||||
throw new Error('Address is required however no user address is supplied');
|
throw new Error('Address is required however no user address is supplied');
|
||||||
}
|
}
|
||||||
|
|
||||||
const Multicall = Multicall__factory.connect(multicallAddress, provider);
|
const Multicall = Multicall__factory.connect(multicallContract, provider);
|
||||||
|
|
||||||
const tokenAddresses = Object.values(tokens)
|
const tokenAddresses = Object.values(tokens)
|
||||||
.map(({ tokenAddress }) => tokenAddress)
|
.map(({ tokenAddress }) => tokenAddress)
|
||||||
.filter((t) => t) as string[];
|
.filter((t) => t) as string[];
|
||||||
|
|
||||||
if (tornTokenAddress) {
|
if (tornContract) {
|
||||||
tokenAddresses.push(tornTokenAddress);
|
tokenAddresses.push(tornContract);
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenBalances = await getTokenBalances({
|
const tokenBalances = await getTokenBalances({
|
||||||
@ -1662,7 +1901,7 @@ export function tornadoProgram() {
|
|||||||
|
|
||||||
const netId = Number(deserializedTx.chainId);
|
const netId = Number(deserializedTx.chainId);
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const provider = getProgramProvider(netId, rpc, config, {
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
...fetchDataOptions,
|
...fetchDataOptions,
|
||||||
@ -1679,7 +1918,6 @@ export function tornadoProgram() {
|
|||||||
options,
|
options,
|
||||||
populatedTransaction: deserializedTx,
|
populatedTransaction: deserializedTx,
|
||||||
});
|
});
|
||||||
/* eslint-enable prettier/prettier */
|
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
@ -1698,7 +1936,7 @@ export function tornadoProgram() {
|
|||||||
throw new Error('NetId for the transaction is invalid, this command only supports EIP-155 transactions');
|
throw new Error('NetId for the transaction is invalid, this command only supports EIP-155 transactions');
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = networkConfig[`netId${netId}`];
|
const config = getConfig(netId);
|
||||||
|
|
||||||
const provider = getProgramProvider(netId, rpc, config, {
|
const provider = getProgramProvider(netId, rpc, config, {
|
||||||
...fetchDataOptions,
|
...fetchDataOptions,
|
||||||
@ -1712,13 +1950,24 @@ export function tornadoProgram() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// common options
|
// common options
|
||||||
/* eslint-disable prettier/prettier */
|
|
||||||
program.commands.forEach((cmd) => {
|
program.commands.forEach((cmd) => {
|
||||||
cmd.option('-r, --rpc <RPC_URL>', 'The RPC that CLI should interact with', parseUrl);
|
cmd.option('-r, --rpc <RPC_URL>', 'The RPC that CLI should interact with', parseUrl);
|
||||||
cmd.option('-e, --eth-rpc <ETHRPC_URL>', 'The Ethereum Mainnet RPC that CLI should interact with', parseUrl);
|
cmd.option('-e, --eth-rpc <ETHRPC_URL>', 'The Ethereum Mainnet RPC that CLI should interact with', parseUrl);
|
||||||
cmd.option('-g, --graph <GRAPH_URL>', 'The Subgraph API that CLI should interact with', parseUrl);
|
cmd.option('-g, --graph <GRAPH_URL>', 'The Subgraph API that CLI should interact with', parseUrl);
|
||||||
cmd.option('-G, --eth-graph <ETHGRAPH_URL>', 'The Ethereum Mainnet Subgraph API that CLI should interact with', parseUrl);
|
cmd.option(
|
||||||
cmd.option('-d, --disable-graph', 'Disable Graph API - Does not enable Subgraph API and use only local RPC as an event source');
|
'-G, --eth-graph <ETHGRAPH_URL>',
|
||||||
|
'The Ethereum Mainnet Subgraph API that CLI should interact with',
|
||||||
|
parseUrl,
|
||||||
|
);
|
||||||
|
cmd.option(
|
||||||
|
'-d, --disable-graph',
|
||||||
|
'Disable Graph API - Does not enable Subgraph API and use only local RPC as an event source',
|
||||||
|
);
|
||||||
|
cmd.option(
|
||||||
|
'-a, --account-key <ACCOUNT_KEY>',
|
||||||
|
'Account key generated from UI or the createAccount to store encrypted notes on-chain',
|
||||||
|
parseRecoveryKey,
|
||||||
|
);
|
||||||
cmd.option('-R, --relayer <RELAYER>', 'Withdraw via relayer (Should be either .eth name or URL)', parseRelayer);
|
cmd.option('-R, --relayer <RELAYER>', 'Withdraw via relayer (Should be either .eth name or URL)', parseRelayer);
|
||||||
cmd.option('-w, --wallet-withdrawal', 'Withdrawal via wallet (Should not be linked with deposits)');
|
cmd.option('-w, --wallet-withdrawal', 'Withdrawal via wallet (Should not be linked with deposits)');
|
||||||
cmd.option('-T, --tor-port <TOR_PORT>', 'Optional tor port', parseNumber);
|
cmd.option('-T, --tor-port <TOR_PORT>', 'Optional tor port', parseNumber);
|
||||||
@ -1730,13 +1979,13 @@ export function tornadoProgram() {
|
|||||||
);
|
);
|
||||||
cmd.option(
|
cmd.option(
|
||||||
'-m, --mnemonic <MNEMONIC>',
|
'-m, --mnemonic <MNEMONIC>',
|
||||||
'Wallet BIP39 Mnemonic Phrase - If you didn\'t add it to .env file and it is needed for operation',
|
'Wallet BIP39 Mnemonic Phrase - If you did not add it to .env file and it is needed for operation',
|
||||||
parseMnemonic,
|
parseMnemonic,
|
||||||
);
|
);
|
||||||
cmd.option('-i, --mnemonic-index <MNEMONIC_INDEX>', 'Optional wallet mnemonic index', parseNumber);
|
cmd.option('-i, --mnemonic-index <MNEMONIC_INDEX>', 'Optional wallet mnemonic index', parseNumber);
|
||||||
cmd.option(
|
cmd.option(
|
||||||
'-p, --private-key <PRIVATE_KEY>',
|
'-p, --private-key <PRIVATE_KEY>',
|
||||||
'Wallet private key - If you didn\'t add it to .env file and it is needed for operation',
|
'Wallet private key - If you did not add it to .env file and it is needed for operation',
|
||||||
parseKey,
|
parseKey,
|
||||||
);
|
);
|
||||||
cmd.option(
|
cmd.option(
|
||||||
@ -1745,7 +1994,6 @@ export function tornadoProgram() {
|
|||||||
);
|
);
|
||||||
cmd.option('-l, --local-rpc', 'Local node mode - Does not submit signed transaction to the node');
|
cmd.option('-l, --local-rpc', 'Local node mode - Does not submit signed transaction to the node');
|
||||||
});
|
});
|
||||||
/* eslint-enable prettier/prettier */
|
|
||||||
|
|
||||||
return program;
|
return program;
|
||||||
}
|
}
|
||||||
|
@ -37,22 +37,21 @@ export function unzipAsync(data: Uint8Array): Promise<Unzipped> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveEvents<T extends MinimalEvents>({
|
export async function saveUserFile({
|
||||||
name,
|
fileName,
|
||||||
userDirectory,
|
userDirectory,
|
||||||
events,
|
dataString,
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
fileName: string;
|
||||||
userDirectory: string;
|
userDirectory: string;
|
||||||
events: T[];
|
dataString: string;
|
||||||
}) {
|
}) {
|
||||||
const fileName = `${name}.json`.toLowerCase();
|
fileName = fileName.toLowerCase();
|
||||||
|
|
||||||
const filePath = path.join(userDirectory, fileName);
|
const filePath = path.join(userDirectory, fileName);
|
||||||
|
|
||||||
const stringEvents = JSON.stringify(events, null, 2) + '\n';
|
|
||||||
|
|
||||||
const payload = await zipAsync({
|
const payload = await zipAsync({
|
||||||
[fileName]: new TextEncoder().encode(stringEvents),
|
[fileName]: new TextEncoder().encode(dataString),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!(await existsAsync(userDirectory))) {
|
if (!(await existsAsync(userDirectory))) {
|
||||||
@ -60,7 +59,7 @@ export async function saveEvents<T extends MinimalEvents>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
await writeFile(filePath + '.zip', payload);
|
await writeFile(filePath + '.zip', payload);
|
||||||
await writeFile(filePath, stringEvents);
|
await writeFile(filePath, dataString);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function loadSavedEvents<T extends MinimalEvents>({
|
export async function loadSavedEvents<T extends MinimalEvents>({
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { bnToBytes, bytesToBN, leBuff2Int, leInt2Buff, rBigInt, toFixedHex } from './utils';
|
import { bnToBytes, bytesToBN, leBuff2Int, leInt2Buff, rBigInt, toFixedHex } from './utils';
|
||||||
import { buffPedersenHash } from './pedersen';
|
import { buffPedersenHash } from './pedersen';
|
||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
|
|
||||||
export type DepositType = {
|
export type DepositType = {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: string | number;
|
netId: NetIdType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type createDepositParams = {
|
export type createDepositParams = {
|
||||||
@ -61,7 +62,7 @@ export async function createDeposit({ nullifier, secret }: createDepositParams):
|
|||||||
export interface DepositConstructor {
|
export interface DepositConstructor {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
nullifier: bigint;
|
nullifier: bigint;
|
||||||
secret: bigint;
|
secret: bigint;
|
||||||
note: string;
|
note: string;
|
||||||
@ -74,7 +75,7 @@ export interface DepositConstructor {
|
|||||||
export class Deposit {
|
export class Deposit {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
|
|
||||||
nullifier: bigint;
|
nullifier: bigint;
|
||||||
secret: bigint;
|
secret: bigint;
|
||||||
@ -148,7 +149,7 @@ export class Deposit {
|
|||||||
const newDeposit = new Deposit({
|
const newDeposit = new Deposit({
|
||||||
currency: currency.toLowerCase(),
|
currency: currency.toLowerCase(),
|
||||||
amount: amount,
|
amount: amount,
|
||||||
netId: Number(netId),
|
netId,
|
||||||
note: `tornado-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.noteHex}`,
|
note: `tornado-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.noteHex}`,
|
||||||
noteHex: depositObject.noteHex,
|
noteHex: depositObject.noteHex,
|
||||||
invoice: `tornadoInvoice-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.commitmentHex}`,
|
invoice: `tornadoInvoice-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.commitmentHex}`,
|
||||||
@ -182,14 +183,14 @@ export class Deposit {
|
|||||||
const invoice = `tornadoInvoice-${currency}-${amount}-${netId}-${depositObject.commitmentHex}`;
|
const invoice = `tornadoInvoice-${currency}-${amount}-${netId}-${depositObject.commitmentHex}`;
|
||||||
|
|
||||||
const newDeposit = new Deposit({
|
const newDeposit = new Deposit({
|
||||||
currency: currency,
|
currency,
|
||||||
amount: amount,
|
amount,
|
||||||
netId: netId,
|
netId,
|
||||||
note: noteString,
|
note: noteString,
|
||||||
noteHex: depositObject.noteHex,
|
noteHex: depositObject.noteHex,
|
||||||
invoice: invoice,
|
invoice,
|
||||||
nullifier: nullifier,
|
nullifier,
|
||||||
secret: secret,
|
secret,
|
||||||
commitmentHex: depositObject.commitmentHex,
|
commitmentHex: depositObject.commitmentHex,
|
||||||
nullifierHex: depositObject.nullifierHex,
|
nullifierHex: depositObject.nullifierHex,
|
||||||
});
|
});
|
||||||
@ -205,7 +206,7 @@ export type parsedInvoiceExec = DepositType & {
|
|||||||
export class Invoice {
|
export class Invoice {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
commitment: string;
|
commitment: string;
|
||||||
invoice: string;
|
invoice: string;
|
||||||
|
|
||||||
|
189
src/services/encryptedNotes.ts
Normal file
189
src/services/encryptedNotes.ts
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
import { getEncryptionPublicKey, encrypt, decrypt, EthEncryptedData } from '@metamask/eth-sig-util';
|
||||||
|
import { Echoer } from '@tornado/contracts';
|
||||||
|
import { Wallet, computeAddress, getAddress } from 'ethers';
|
||||||
|
import { crypto, base64ToBytes, bytesToBase64, bytesToHex, hexToBytes, toFixedHex, concatBytes } from './utils';
|
||||||
|
import { EchoEvents, EncryptedNotesEvents } from './events';
|
||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
|
|
||||||
|
export interface NoteToEncrypt {
|
||||||
|
address: string;
|
||||||
|
noteHex: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DecryptedNotes {
|
||||||
|
blockNumber: number;
|
||||||
|
address: string;
|
||||||
|
noteHex: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function packEncryptedMessage({ nonce, ephemPublicKey, ciphertext }: EthEncryptedData) {
|
||||||
|
const nonceBuf = toFixedHex(bytesToHex(base64ToBytes(nonce)), 24);
|
||||||
|
const ephemPublicKeyBuf = toFixedHex(bytesToHex(base64ToBytes(ephemPublicKey)), 32);
|
||||||
|
const ciphertextBuf = bytesToHex(base64ToBytes(ciphertext));
|
||||||
|
|
||||||
|
const messageBuff = concatBytes(hexToBytes(nonceBuf), hexToBytes(ephemPublicKeyBuf), hexToBytes(ciphertextBuf));
|
||||||
|
|
||||||
|
return bytesToHex(messageBuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unpackEncryptedMessage(encryptedMessage: string) {
|
||||||
|
const messageBuff = hexToBytes(encryptedMessage);
|
||||||
|
const nonceBuf = bytesToBase64(messageBuff.slice(0, 24));
|
||||||
|
const ephemPublicKeyBuf = bytesToBase64(messageBuff.slice(24, 56));
|
||||||
|
const ciphertextBuf = bytesToBase64(messageBuff.slice(56));
|
||||||
|
|
||||||
|
return {
|
||||||
|
messageBuff: bytesToHex(messageBuff),
|
||||||
|
version: 'x25519-xsalsa20-poly1305',
|
||||||
|
nonce: nonceBuf,
|
||||||
|
ephemPublicKey: ephemPublicKeyBuf,
|
||||||
|
ciphertext: ciphertextBuf,
|
||||||
|
} as EthEncryptedData & {
|
||||||
|
messageBuff: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NoteAccountConstructor {
|
||||||
|
netId: NetIdType;
|
||||||
|
blockNumber?: number;
|
||||||
|
// hex
|
||||||
|
recoveryKey?: string;
|
||||||
|
Echoer: Echoer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NoteAccount {
|
||||||
|
netId: NetIdType;
|
||||||
|
blockNumber?: number;
|
||||||
|
// Dedicated 32 bytes private key only used for note encryption, backed up to an Echoer and local for future derivation
|
||||||
|
// Note that unlike the private key it shouldn't have the 0x prefix
|
||||||
|
recoveryKey: string;
|
||||||
|
// Address derived from recoveryKey, only used for frontend UI
|
||||||
|
recoveryAddress: string;
|
||||||
|
// Note encryption public key derived from recoveryKey
|
||||||
|
recoveryPublicKey: string;
|
||||||
|
Echoer: Echoer;
|
||||||
|
|
||||||
|
constructor({ netId, blockNumber, recoveryKey, Echoer }: NoteAccountConstructor) {
|
||||||
|
if (!recoveryKey) {
|
||||||
|
recoveryKey = bytesToHex(crypto.getRandomValues(new Uint8Array(32))).slice(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.netId = Math.floor(Number(netId));
|
||||||
|
this.blockNumber = blockNumber;
|
||||||
|
this.recoveryKey = recoveryKey;
|
||||||
|
this.recoveryAddress = computeAddress('0x' + recoveryKey);
|
||||||
|
this.recoveryPublicKey = getEncryptionPublicKey(recoveryKey);
|
||||||
|
this.Echoer = Echoer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intends to mock eth_getEncryptionPublicKey behavior from MetaMask
|
||||||
|
* In order to make the recoveryKey retrival from Echoer possible from the bare private key
|
||||||
|
*/
|
||||||
|
static getWalletPublicKey(wallet: Wallet) {
|
||||||
|
let { privateKey } = wallet;
|
||||||
|
|
||||||
|
if (privateKey.startsWith('0x')) {
|
||||||
|
privateKey = privateKey.replace('0x', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should return base64 encoded public key
|
||||||
|
return getEncryptionPublicKey(privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function intends to provide an encrypted value of recoveryKey for an on-chain Echoer backup purpose
|
||||||
|
// Thus, the pubKey should be derived by a Wallet instance or from Web3 wallets
|
||||||
|
// pubKey: base64 encoded 32 bytes key from https://docs.metamask.io/wallet/reference/eth_getencryptionpublickey/
|
||||||
|
getEncryptedAccount(walletPublicKey: string) {
|
||||||
|
const encryptedData = encrypt({
|
||||||
|
publicKey: walletPublicKey,
|
||||||
|
data: this.recoveryKey,
|
||||||
|
version: 'x25519-xsalsa20-poly1305',
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = packEncryptedMessage(encryptedData);
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Use this later to save hexPrivateKey generated with
|
||||||
|
// Buffer.from(JSON.stringify(encryptedData)).toString('hex')
|
||||||
|
// As we don't use buffer with this library we should leave UI to do the rest
|
||||||
|
encryptedData,
|
||||||
|
// Data that could be used as an echo(data) params
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt Echoer backuped note encryption account with private keys
|
||||||
|
*/
|
||||||
|
decryptAccountsWithWallet(wallet: Wallet, events: EchoEvents[]): NoteAccount[] {
|
||||||
|
let { privateKey } = wallet;
|
||||||
|
|
||||||
|
if (privateKey.startsWith('0x')) {
|
||||||
|
privateKey = privateKey.replace('0x', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
const decryptedEvents = [];
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
try {
|
||||||
|
const unpackedMessage = unpackEncryptedMessage(event.encryptedAccount);
|
||||||
|
|
||||||
|
const recoveryKey = decrypt({
|
||||||
|
encryptedData: unpackedMessage,
|
||||||
|
privateKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
decryptedEvents.push(
|
||||||
|
new NoteAccount({
|
||||||
|
netId: this.netId,
|
||||||
|
blockNumber: event.blockNumber,
|
||||||
|
recoveryKey,
|
||||||
|
Echoer: this.Echoer,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// decryption may fail for invalid accounts
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decryptedEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
decryptNotes(events: EncryptedNotesEvents[]): DecryptedNotes[] {
|
||||||
|
const decryptedEvents = [];
|
||||||
|
|
||||||
|
for (const event of events) {
|
||||||
|
try {
|
||||||
|
const unpackedMessage = unpackEncryptedMessage(event.encryptedNote);
|
||||||
|
|
||||||
|
const [address, noteHex] = decrypt({
|
||||||
|
encryptedData: unpackedMessage,
|
||||||
|
privateKey: this.recoveryKey,
|
||||||
|
}).split('-');
|
||||||
|
|
||||||
|
decryptedEvents.push({
|
||||||
|
blockNumber: event.blockNumber,
|
||||||
|
address: getAddress(address),
|
||||||
|
noteHex,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
// decryption may fail for foreign notes
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return decryptedEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptNote({ address, noteHex }: NoteToEncrypt) {
|
||||||
|
const encryptedData = encrypt({
|
||||||
|
publicKey: this.recoveryPublicKey,
|
||||||
|
data: `${address}-${noteHex}`,
|
||||||
|
version: 'x25519-xsalsa20-poly1305',
|
||||||
|
});
|
||||||
|
|
||||||
|
return packEncryptedMessage(encryptedData);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,12 @@
|
|||||||
import { BaseContract, Provider, EventLog, TransactionResponse, getAddress, Block, ContractEventName } from 'ethers';
|
import { BaseContract, Provider, EventLog, TransactionResponse, getAddress, Block, ContractEventName } from 'ethers';
|
||||||
import type { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry } from '@tornado/contracts';
|
import type {
|
||||||
|
Tornado,
|
||||||
|
TornadoRouter,
|
||||||
|
TornadoProxyLight,
|
||||||
|
Governance,
|
||||||
|
RelayerRegistry,
|
||||||
|
Echoer,
|
||||||
|
} from '@tornado/contracts';
|
||||||
import * as graph from '../graphql';
|
import * as graph from '../graphql';
|
||||||
import {
|
import {
|
||||||
BatchEventsService,
|
BatchEventsService,
|
||||||
@ -9,25 +16,28 @@ import {
|
|||||||
BatchBlockOnProgress,
|
BatchBlockOnProgress,
|
||||||
} from '../batch';
|
} from '../batch';
|
||||||
import { fetchDataOptions } from '../providers';
|
import { fetchDataOptions } from '../providers';
|
||||||
|
import type { NetIdType } from '../networkConfig';
|
||||||
import type {
|
import type {
|
||||||
BaseEvents,
|
BaseEvents,
|
||||||
MinimalEvents,
|
MinimalEvents,
|
||||||
DepositsEvents,
|
DepositsEvents,
|
||||||
WithdrawalsEvents,
|
WithdrawalsEvents,
|
||||||
EncryptedNotesEvents,
|
EncryptedNotesEvents,
|
||||||
|
AllGovernanceEvents,
|
||||||
GovernanceProposalCreatedEvents,
|
GovernanceProposalCreatedEvents,
|
||||||
GovernanceVotedEvents,
|
GovernanceVotedEvents,
|
||||||
GovernanceDelegatedEvents,
|
GovernanceDelegatedEvents,
|
||||||
GovernanceUndelegatedEvents,
|
GovernanceUndelegatedEvents,
|
||||||
RegistersEvents,
|
RegistersEvents,
|
||||||
BaseGraphEvents,
|
BaseGraphEvents,
|
||||||
|
EchoEvents,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export const DEPOSIT = 'deposit';
|
export const DEPOSIT = 'deposit';
|
||||||
export const WITHDRAWAL = 'withdrawal';
|
export const WITHDRAWAL = 'withdrawal';
|
||||||
|
|
||||||
export type BaseEventsServiceConstructor = {
|
export type BaseEventsServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -57,7 +67,7 @@ export type BaseGraphParams = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class BaseEventsService<EventType extends MinimalEvents> {
|
export class BaseEventsService<EventType extends MinimalEvents> {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -315,7 +325,7 @@ export class BaseEventsService<EventType extends MinimalEvents> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type BaseDepositsServiceConstructor = {
|
export type BaseDepositsServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -454,8 +464,78 @@ export class BaseDepositsService extends BaseEventsService<DepositsEvents | With
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BaseEchoServiceConstructor = {
|
||||||
|
netId: NetIdType;
|
||||||
|
provider: Provider;
|
||||||
|
graphApi?: string;
|
||||||
|
subgraphName?: string;
|
||||||
|
Echoer: Echoer;
|
||||||
|
deployedBlock?: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class BaseEchoService extends BaseEventsService<EchoEvents> {
|
||||||
|
constructor({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
Echoer,
|
||||||
|
deployedBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
}: BaseEchoServiceConstructor) {
|
||||||
|
super({ netId, provider, graphApi, subgraphName, contract: Echoer, deployedBlock, fetchDataOptions });
|
||||||
|
}
|
||||||
|
|
||||||
|
getInstanceName(): string {
|
||||||
|
return `echo_${this.netId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getType(): string {
|
||||||
|
return 'Echo';
|
||||||
|
}
|
||||||
|
|
||||||
|
getGraphMethod(): string {
|
||||||
|
return 'getAllGraphEchoEvents';
|
||||||
|
}
|
||||||
|
|
||||||
|
async formatEvents(events: EventLog[]) {
|
||||||
|
return events
|
||||||
|
.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
|
||||||
|
const { who, data } = args;
|
||||||
|
|
||||||
|
if (who && data) {
|
||||||
|
const eventObjects = {
|
||||||
|
blockNumber,
|
||||||
|
logIndex,
|
||||||
|
transactionHash,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...eventObjects,
|
||||||
|
address: who,
|
||||||
|
encryptedAccount: data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter((e) => e) as EchoEvents[];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<EchoEvents>> {
|
||||||
|
// TheGraph doesn't support our batch sync due to missing blockNumber field
|
||||||
|
if (!this.graphApi || this.graphApi.includes('api.thegraph.com')) {
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastBlock: fromBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getEventsFromGraph({ fromBlock });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type BaseEncryptedNotesServiceConstructor = {
|
export type BaseEncryptedNotesServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -511,14 +591,8 @@ export class BaseEncryptedNotesService extends BaseEventsService<EncryptedNotesE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BaseGovernanceEventTypes =
|
|
||||||
| GovernanceProposalCreatedEvents
|
|
||||||
| GovernanceVotedEvents
|
|
||||||
| GovernanceDelegatedEvents
|
|
||||||
| GovernanceUndelegatedEvents;
|
|
||||||
|
|
||||||
export type BaseGovernanceServiceConstructor = {
|
export type BaseGovernanceServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
@ -527,7 +601,7 @@ export type BaseGovernanceServiceConstructor = {
|
|||||||
fetchDataOptions?: fetchDataOptions;
|
fetchDataOptions?: fetchDataOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEventTypes> {
|
export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
|
||||||
batchTransactionService: BatchTransactionService;
|
batchTransactionService: BatchTransactionService;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
@ -556,70 +630,71 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
getGraphMethod() {
|
getGraphMethod() {
|
||||||
return 'governanceEvents';
|
return 'getAllGovernanceEvents';
|
||||||
}
|
}
|
||||||
|
|
||||||
async formatEvents(events: EventLog[]): Promise<BaseGovernanceEventTypes[]> {
|
async formatEvents(events: EventLog[]): Promise<AllGovernanceEvents[]> {
|
||||||
const formattedEvents = events
|
const proposalEvents: GovernanceProposalCreatedEvents[] = [];
|
||||||
.map(({ blockNumber, index: logIndex, transactionHash, args, eventName: event }) => {
|
const votedEvents: GovernanceVotedEvents[] = [];
|
||||||
const eventObjects = {
|
const delegatedEvents: GovernanceDelegatedEvents[] = [];
|
||||||
blockNumber,
|
const undelegatedEvents: GovernanceUndelegatedEvents[] = [];
|
||||||
logIndex,
|
|
||||||
transactionHash,
|
|
||||||
event,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (event === 'ProposalCreated') {
|
events.forEach(({ blockNumber, index: logIndex, transactionHash, args, eventName: event }) => {
|
||||||
const { id, proposer, target, startTime, endTime, description } = args;
|
const eventObjects = {
|
||||||
return {
|
blockNumber,
|
||||||
...eventObjects,
|
logIndex,
|
||||||
id,
|
transactionHash,
|
||||||
proposer,
|
event,
|
||||||
target,
|
};
|
||||||
startTime,
|
|
||||||
endTime,
|
|
||||||
description,
|
|
||||||
} as GovernanceProposalCreatedEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event === 'Voted') {
|
if (event === 'ProposalCreated') {
|
||||||
const { proposalId, voter, support, votes } = args;
|
const { id, proposer, target, startTime, endTime, description } = args;
|
||||||
return {
|
|
||||||
...eventObjects,
|
|
||||||
proposalId,
|
|
||||||
voter,
|
|
||||||
support,
|
|
||||||
votes,
|
|
||||||
} as GovernanceVotedEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event === 'Delegated') {
|
proposalEvents.push({
|
||||||
const { account, to: delegateTo } = args;
|
...eventObjects,
|
||||||
return {
|
id: Number(id),
|
||||||
...eventObjects,
|
proposer,
|
||||||
account,
|
target,
|
||||||
delegateTo,
|
startTime: Number(startTime),
|
||||||
} as GovernanceDelegatedEvents;
|
endTime: Number(endTime),
|
||||||
}
|
description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (event === 'Undelegated') {
|
if (event === 'Voted') {
|
||||||
const { account, from: delegateFrom } = args;
|
const { proposalId, voter, support, votes } = args;
|
||||||
return {
|
|
||||||
...eventObjects,
|
|
||||||
account,
|
|
||||||
delegateFrom,
|
|
||||||
} as GovernanceUndelegatedEvents;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter((e) => e) as BaseGovernanceEventTypes[];
|
|
||||||
|
|
||||||
type GovernanceVotedEventsIndexed = GovernanceVotedEvents & {
|
votedEvents.push({
|
||||||
index: number;
|
...eventObjects,
|
||||||
};
|
proposalId: Number(proposalId),
|
||||||
|
voter,
|
||||||
|
support,
|
||||||
|
votes,
|
||||||
|
from: '',
|
||||||
|
input: '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const votedEvents = formattedEvents
|
if (event === 'Delegated') {
|
||||||
.map((event, index) => ({ ...event, index }))
|
const { account, to: delegateTo } = args;
|
||||||
.filter(({ event }) => event === 'Voted') as GovernanceVotedEventsIndexed[];
|
|
||||||
|
delegatedEvents.push({
|
||||||
|
...eventObjects,
|
||||||
|
account,
|
||||||
|
delegateTo,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event === 'Undelegated') {
|
||||||
|
const { account, from: delegateFrom } = args;
|
||||||
|
|
||||||
|
undelegatedEvents.push({
|
||||||
|
...eventObjects,
|
||||||
|
account,
|
||||||
|
delegateFrom,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (votedEvents.length) {
|
if (votedEvents.length) {
|
||||||
this.updateTransactionProgress({ percentage: 0 });
|
this.updateTransactionProgress({ percentage: 0 });
|
||||||
@ -628,7 +703,7 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
|||||||
...new Set(votedEvents.map(({ transactionHash }) => transactionHash)),
|
...new Set(votedEvents.map(({ transactionHash }) => transactionHash)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
votedEvents.forEach((event) => {
|
votedEvents.forEach((event, index) => {
|
||||||
// eslint-disable-next-line prefer-const
|
// eslint-disable-next-line prefer-const
|
||||||
let { data: input, from } = txs.find((t) => t.hash === event.transactionHash) as TransactionResponse;
|
let { data: input, from } = txs.find((t) => t.hash === event.transactionHash) as TransactionResponse;
|
||||||
|
|
||||||
@ -637,19 +712,17 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
|||||||
input = '';
|
input = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-expect-error check formattedEvents types later
|
votedEvents[index].from = from;
|
||||||
formattedEvents[event.index].from = from;
|
votedEvents[index].input = input;
|
||||||
// @ts-expect-error check formattedEvents types later
|
|
||||||
formattedEvents[event.index].input = input;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return formattedEvents;
|
return [...proposalEvents, ...votedEvents, ...delegatedEvents, ...undelegatedEvents];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<BaseGovernanceEventTypes>> {
|
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<AllGovernanceEvents>> {
|
||||||
// TheGraph doesn't support governance subgraphs
|
// TheGraph doesn't support governance subgraphs
|
||||||
if (!this.graphApi || this.graphApi.includes('api.thegraph.com')) {
|
if (!this.graphApi || !this.subgraphName || this.graphApi.includes('api.thegraph.com')) {
|
||||||
return {
|
return {
|
||||||
events: [],
|
events: [],
|
||||||
lastBlock: fromBlock,
|
lastBlock: fromBlock,
|
||||||
@ -661,7 +734,7 @@ export class BaseGovernanceService extends BaseEventsService<BaseGovernanceEvent
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type BaseRegistryServiceConstructor = {
|
export type BaseRegistryServiceConstructor = {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
provider: Provider;
|
provider: Provider;
|
||||||
graphApi?: string;
|
graphApi?: string;
|
||||||
subgraphName?: string;
|
subgraphName?: string;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Table from 'cli-table3';
|
import Table from 'cli-table3';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { BatchBlockOnProgress, BatchEventOnProgress } from '../batch';
|
import { BatchBlockOnProgress, BatchEventOnProgress } from '../batch';
|
||||||
import { saveEvents, loadSavedEvents, loadCachedEvents } from '../data';
|
import { saveUserFile, loadSavedEvents, loadCachedEvents } from '../data';
|
||||||
import {
|
import {
|
||||||
BaseDepositsService,
|
BaseDepositsService,
|
||||||
BaseEncryptedNotesService,
|
BaseEncryptedNotesService,
|
||||||
@ -11,9 +11,18 @@ import {
|
|||||||
BaseEncryptedNotesServiceConstructor,
|
BaseEncryptedNotesServiceConstructor,
|
||||||
BaseGovernanceServiceConstructor,
|
BaseGovernanceServiceConstructor,
|
||||||
BaseRegistryServiceConstructor,
|
BaseRegistryServiceConstructor,
|
||||||
BaseGovernanceEventTypes,
|
BaseEchoServiceConstructor,
|
||||||
|
BaseEchoService,
|
||||||
} from './base';
|
} from './base';
|
||||||
import type { BaseEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, RegistersEvents } from './types';
|
import type {
|
||||||
|
BaseEvents,
|
||||||
|
DepositsEvents,
|
||||||
|
WithdrawalsEvents,
|
||||||
|
EncryptedNotesEvents,
|
||||||
|
RegistersEvents,
|
||||||
|
AllGovernanceEvents,
|
||||||
|
EchoEvents,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
|
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
|
||||||
cacheDirectory?: string;
|
cacheDirectory?: string;
|
||||||
@ -175,10 +184,155 @@ export class NodeDepositsService extends BaseDepositsService {
|
|||||||
console.log(eventTable.toString() + '\n');
|
console.log(eventTable.toString() + '\n');
|
||||||
|
|
||||||
if (this.userDirectory) {
|
if (this.userDirectory) {
|
||||||
await saveEvents<DepositsEvents | WithdrawalsEvents>({
|
await saveUserFile({
|
||||||
name: instanceName,
|
fileName: instanceName + '.json',
|
||||||
userDirectory: this.userDirectory,
|
userDirectory: this.userDirectory,
|
||||||
events,
|
dataString: JSON.stringify(events, null, 2) + '\n',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & {
|
||||||
|
cacheDirectory?: string;
|
||||||
|
userDirectory?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class NodeEchoService extends BaseEchoService {
|
||||||
|
cacheDirectory?: string;
|
||||||
|
userDirectory?: string;
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
Echoer,
|
||||||
|
deployedBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
cacheDirectory,
|
||||||
|
userDirectory,
|
||||||
|
}: NodeEchoServiceConstructor) {
|
||||||
|
super({
|
||||||
|
netId,
|
||||||
|
provider,
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
Echoer,
|
||||||
|
deployedBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cacheDirectory = cacheDirectory;
|
||||||
|
this.userDirectory = userDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
|
||||||
|
if (toBlock) {
|
||||||
|
console.log(`fromBlock - ${fromBlock}`);
|
||||||
|
console.log(`toBlock - ${toBlock}`);
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
console.log(`downloaded ${type} events count - ${count}`);
|
||||||
|
console.log('____________________________________________');
|
||||||
|
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
|
||||||
|
if (toBlock) {
|
||||||
|
console.log(`fromBlock - ${fromBlock}`);
|
||||||
|
console.log(`toBlock - ${toBlock}`);
|
||||||
|
|
||||||
|
if (count) {
|
||||||
|
console.log(`downloaded ${type} events from graph node count - ${count}`);
|
||||||
|
console.log('____________________________________________');
|
||||||
|
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEventsFromDB() {
|
||||||
|
if (!this.userDirectory) {
|
||||||
|
console.log(`Updating events for ${this.netId} chain echo events\n`);
|
||||||
|
console.log(`savedEvents count - ${0}`);
|
||||||
|
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastBlock: this.deployedBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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`);
|
||||||
|
console.log(`savedEvents count - ${savedEvents.events.length}`);
|
||||||
|
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
|
||||||
|
|
||||||
|
return savedEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEventsFromCache() {
|
||||||
|
if (!this.cacheDirectory) {
|
||||||
|
console.log(`cachedEvents count - ${0}`);
|
||||||
|
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastBlock: this.deployedBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedEvents = await loadCachedEvents<EchoEvents>({
|
||||||
|
name: this.getInstanceName(),
|
||||||
|
cacheDirectory: this.cacheDirectory,
|
||||||
|
deployedBlock: this.deployedBlock,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
|
||||||
|
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
|
||||||
|
|
||||||
|
return cachedEvents;
|
||||||
|
}
|
||||||
|
|
||||||
|
async saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>) {
|
||||||
|
const instanceName = this.getInstanceName();
|
||||||
|
|
||||||
|
console.log('\ntotalEvents count - ', events.length);
|
||||||
|
console.log(
|
||||||
|
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const eventTable = new Table();
|
||||||
|
|
||||||
|
eventTable.push(
|
||||||
|
[{ colSpan: 2, content: 'Echo Accounts', hAlign: 'center' }],
|
||||||
|
['Network', `${this.netId} chain`],
|
||||||
|
['Events', `${events.length} events`],
|
||||||
|
[{ colSpan: 2, content: 'Latest events' }],
|
||||||
|
...events
|
||||||
|
.slice(events.length - 10)
|
||||||
|
.reverse()
|
||||||
|
.map(({ blockNumber }, index) => {
|
||||||
|
const eventIndex = events.length - index;
|
||||||
|
|
||||||
|
return [eventIndex, blockNumber];
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(eventTable.toString() + '\n');
|
||||||
|
|
||||||
|
if (this.userDirectory) {
|
||||||
|
await saveUserFile({
|
||||||
|
fileName: instanceName + '.json',
|
||||||
|
userDirectory: this.userDirectory,
|
||||||
|
dataString: JSON.stringify(events, null, 2) + '\n',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,10 +474,10 @@ export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
|
|||||||
console.log(eventTable.toString() + '\n');
|
console.log(eventTable.toString() + '\n');
|
||||||
|
|
||||||
if (this.userDirectory) {
|
if (this.userDirectory) {
|
||||||
await saveEvents<EncryptedNotesEvents>({
|
await saveUserFile({
|
||||||
name: instanceName,
|
fileName: instanceName + '.json',
|
||||||
userDirectory: this.userDirectory,
|
userDirectory: this.userDirectory,
|
||||||
events,
|
dataString: JSON.stringify(events, null, 2) + '\n',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -407,7 +561,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedEvents = await loadSavedEvents<BaseGovernanceEventTypes>({
|
const savedEvents = await loadSavedEvents<AllGovernanceEvents>({
|
||||||
name: this.getInstanceName(),
|
name: this.getInstanceName(),
|
||||||
userDirectory: this.userDirectory,
|
userDirectory: this.userDirectory,
|
||||||
deployedBlock: this.deployedBlock,
|
deployedBlock: this.deployedBlock,
|
||||||
@ -431,7 +585,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const cachedEvents = await loadCachedEvents<BaseGovernanceEventTypes>({
|
const cachedEvents = await loadCachedEvents<AllGovernanceEvents>({
|
||||||
name: this.getInstanceName(),
|
name: this.getInstanceName(),
|
||||||
cacheDirectory: this.cacheDirectory,
|
cacheDirectory: this.cacheDirectory,
|
||||||
deployedBlock: this.deployedBlock,
|
deployedBlock: this.deployedBlock,
|
||||||
@ -443,7 +597,7 @@ export class NodeGovernanceService extends BaseGovernanceService {
|
|||||||
return cachedEvents;
|
return cachedEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveEvents({ events, lastBlock }: BaseEvents<BaseGovernanceEventTypes>) {
|
async saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>) {
|
||||||
const instanceName = this.getInstanceName();
|
const instanceName = this.getInstanceName();
|
||||||
|
|
||||||
console.log('\ntotalEvents count - ', events.length);
|
console.log('\ntotalEvents count - ', events.length);
|
||||||
@ -471,10 +625,10 @@ export class NodeGovernanceService extends BaseGovernanceService {
|
|||||||
console.log(eventTable.toString() + '\n');
|
console.log(eventTable.toString() + '\n');
|
||||||
|
|
||||||
if (this.userDirectory) {
|
if (this.userDirectory) {
|
||||||
await saveEvents<BaseGovernanceEventTypes>({
|
await saveUserFile({
|
||||||
name: instanceName,
|
fileName: instanceName + '.json',
|
||||||
userDirectory: this.userDirectory,
|
userDirectory: this.userDirectory,
|
||||||
events,
|
dataString: JSON.stringify(events, null, 2) + '\n',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -616,10 +770,10 @@ export class NodeRegistryService extends BaseRegistryService {
|
|||||||
console.log(eventTable.toString() + '\n');
|
console.log(eventTable.toString() + '\n');
|
||||||
|
|
||||||
if (this.userDirectory) {
|
if (this.userDirectory) {
|
||||||
await saveEvents<RegistersEvents>({
|
await saveUserFile({
|
||||||
name: instanceName,
|
fileName: instanceName + '.json',
|
||||||
userDirectory: this.userDirectory,
|
userDirectory: this.userDirectory,
|
||||||
events,
|
dataString: JSON.stringify(events, null, 2) + '\n',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,12 @@ export type GovernanceUndelegatedEvents = GovernanceEvents & {
|
|||||||
delegateFrom: string;
|
delegateFrom: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AllGovernanceEvents =
|
||||||
|
| GovernanceProposalCreatedEvents
|
||||||
|
| GovernanceVotedEvents
|
||||||
|
| GovernanceDelegatedEvents
|
||||||
|
| GovernanceUndelegatedEvents;
|
||||||
|
|
||||||
export type RegistersEvents = MinimalEvents & RelayerParams;
|
export type RegistersEvents = MinimalEvents & RelayerParams;
|
||||||
|
|
||||||
export type DepositsEvents = MinimalEvents & {
|
export type DepositsEvents = MinimalEvents & {
|
||||||
@ -64,6 +70,11 @@ export type WithdrawalsEvents = MinimalEvents & {
|
|||||||
timestamp: number;
|
timestamp: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EchoEvents = MinimalEvents & {
|
||||||
|
address: string;
|
||||||
|
encryptedAccount: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type EncryptedNotesEvents = MinimalEvents & {
|
export type EncryptedNotesEvents = MinimalEvents & {
|
||||||
encryptedNote: string;
|
encryptedNote: string;
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,12 @@ import type {
|
|||||||
WithdrawalsEvents,
|
WithdrawalsEvents,
|
||||||
EncryptedNotesEvents,
|
EncryptedNotesEvents,
|
||||||
BatchGraphOnProgress,
|
BatchGraphOnProgress,
|
||||||
|
EchoEvents,
|
||||||
|
AllGovernanceEvents,
|
||||||
|
GovernanceProposalCreatedEvents,
|
||||||
|
GovernanceVotedEvents,
|
||||||
|
GovernanceDelegatedEvents,
|
||||||
|
GovernanceUndelegatedEvents,
|
||||||
} from '../events';
|
} from '../events';
|
||||||
import {
|
import {
|
||||||
_META,
|
_META,
|
||||||
@ -17,6 +23,8 @@ import {
|
|||||||
GET_WITHDRAWALS,
|
GET_WITHDRAWALS,
|
||||||
GET_NOTE_ACCOUNTS,
|
GET_NOTE_ACCOUNTS,
|
||||||
GET_ENCRYPTED_NOTES,
|
GET_ENCRYPTED_NOTES,
|
||||||
|
GET_ECHO_EVENTS,
|
||||||
|
GET_GOVERNANCE_EVENTS,
|
||||||
} from './queries';
|
} from './queries';
|
||||||
|
|
||||||
export * from './queries';
|
export * from './queries';
|
||||||
@ -643,7 +651,7 @@ export async function getNoteAccounts({
|
|||||||
subgraphName,
|
subgraphName,
|
||||||
query: GET_NOTE_ACCOUNTS,
|
query: GET_NOTE_ACCOUNTS,
|
||||||
variables: {
|
variables: {
|
||||||
address,
|
address: address.toLowerCase(),
|
||||||
},
|
},
|
||||||
fetchDataOptions,
|
fetchDataOptions,
|
||||||
});
|
});
|
||||||
@ -662,6 +670,132 @@ export async function getNoteAccounts({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GraphEchoEvents {
|
||||||
|
noteAccounts: {
|
||||||
|
id: string;
|
||||||
|
blockNumber: string;
|
||||||
|
address: string;
|
||||||
|
encryptedAccount: string;
|
||||||
|
}[];
|
||||||
|
_meta: {
|
||||||
|
block: {
|
||||||
|
number: number;
|
||||||
|
};
|
||||||
|
hasIndexingErrors: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface getGraphEchoEventsParams {
|
||||||
|
graphApi: string;
|
||||||
|
subgraphName: string;
|
||||||
|
fromBlock: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
onProgress?: BatchGraphOnProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGraphEchoEvents({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
fromBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
}: getGraphEchoEventsParams): Promise<GraphEchoEvents> {
|
||||||
|
return queryGraph<GraphEchoEvents>({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
query: GET_ECHO_EVENTS,
|
||||||
|
variables: {
|
||||||
|
first,
|
||||||
|
fromBlock,
|
||||||
|
},
|
||||||
|
fetchDataOptions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllGraphEchoEvents({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
fromBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
onProgress,
|
||||||
|
}: getGraphEchoEventsParams): Promise<BaseGraphEvents<EchoEvents>> {
|
||||||
|
try {
|
||||||
|
const events = [];
|
||||||
|
let lastSyncBlock = fromBlock;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
let {
|
||||||
|
noteAccounts: result,
|
||||||
|
_meta: {
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
block: { number: currentBlock },
|
||||||
|
},
|
||||||
|
} = await getGraphEchoEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions });
|
||||||
|
|
||||||
|
lastSyncBlock = currentBlock;
|
||||||
|
|
||||||
|
if (isEmptyArray(result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [firstEvent] = result;
|
||||||
|
const [lastEvent] = result.slice(-1);
|
||||||
|
|
||||||
|
if (typeof onProgress === 'function') {
|
||||||
|
onProgress({
|
||||||
|
type: 'EchoEvents',
|
||||||
|
fromBlock: Number(firstEvent.blockNumber),
|
||||||
|
toBlock: Number(lastEvent.blockNumber),
|
||||||
|
count: result.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.length < 900) {
|
||||||
|
events.push(...result);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.filter(({ blockNumber }) => blockNumber !== lastEvent.blockNumber);
|
||||||
|
fromBlock = Number(lastEvent.blockNumber);
|
||||||
|
|
||||||
|
events.push(...result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!events.length) {
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastSyncBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = events.map((e) => {
|
||||||
|
const [transactionHash, logIndex] = e.id.split('-');
|
||||||
|
|
||||||
|
return {
|
||||||
|
blockNumber: Number(e.blockNumber),
|
||||||
|
logIndex: Number(logIndex),
|
||||||
|
transactionHash: transactionHash,
|
||||||
|
address: getAddress(e.address),
|
||||||
|
encryptedAccount: e.encryptedAccount,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const [lastEvent] = result.slice(-1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: result,
|
||||||
|
lastSyncBlock: lastEvent && lastEvent.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Error from getAllGraphEchoEvents query');
|
||||||
|
console.log(err);
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastSyncBlock: fromBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface GraphEncryptedNotes {
|
export interface GraphEncryptedNotes {
|
||||||
encryptedNotes: {
|
encryptedNotes: {
|
||||||
blockNumber: string;
|
blockNumber: string;
|
||||||
@ -782,3 +916,223 @@ export async function getAllEncryptedNotes({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GraphGovernanceEvents {
|
||||||
|
proposals: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
proposalId: number;
|
||||||
|
proposer: string;
|
||||||
|
target: string;
|
||||||
|
startTime: number;
|
||||||
|
endTime: number;
|
||||||
|
description: string;
|
||||||
|
}[];
|
||||||
|
votes: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
proposalId: number;
|
||||||
|
voter: string;
|
||||||
|
support: boolean;
|
||||||
|
votes: string;
|
||||||
|
from: string;
|
||||||
|
input: string;
|
||||||
|
}[];
|
||||||
|
delegates: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
account: string;
|
||||||
|
delegateTo: string;
|
||||||
|
}[];
|
||||||
|
undelegates: {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
account: string;
|
||||||
|
delegateFrom: string;
|
||||||
|
}[];
|
||||||
|
_meta: {
|
||||||
|
block: {
|
||||||
|
number: number;
|
||||||
|
};
|
||||||
|
hasIndexingErrors: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface getGovernanceEventsParams {
|
||||||
|
graphApi: string;
|
||||||
|
subgraphName: string;
|
||||||
|
fromBlock: number;
|
||||||
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
onProgress?: BatchGraphOnProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getGovernanceEvents({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
fromBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
}: getGovernanceEventsParams): Promise<GraphGovernanceEvents> {
|
||||||
|
return queryGraph<GraphGovernanceEvents>({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
query: GET_GOVERNANCE_EVENTS,
|
||||||
|
variables: {
|
||||||
|
first,
|
||||||
|
fromBlock,
|
||||||
|
},
|
||||||
|
fetchDataOptions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllGovernanceEvents({
|
||||||
|
graphApi,
|
||||||
|
subgraphName,
|
||||||
|
fromBlock,
|
||||||
|
fetchDataOptions,
|
||||||
|
onProgress,
|
||||||
|
}: getGovernanceEventsParams): Promise<BaseGraphEvents<AllGovernanceEvents>> {
|
||||||
|
try {
|
||||||
|
const result = [];
|
||||||
|
|
||||||
|
let lastSyncBlock = fromBlock;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-constant-condition
|
||||||
|
while (true) {
|
||||||
|
const {
|
||||||
|
proposals,
|
||||||
|
votes,
|
||||||
|
delegates,
|
||||||
|
undelegates,
|
||||||
|
_meta: {
|
||||||
|
block: { number: currentBlock },
|
||||||
|
},
|
||||||
|
} = await getGovernanceEvents({ graphApi, subgraphName, fromBlock, fetchDataOptions });
|
||||||
|
|
||||||
|
lastSyncBlock = currentBlock;
|
||||||
|
|
||||||
|
const eventsLength = proposals.length + votes.length + delegates.length + undelegates.length;
|
||||||
|
|
||||||
|
if (eventsLength === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formattedProposals: GovernanceProposalCreatedEvents[] = proposals.map(
|
||||||
|
({ blockNumber, logIndex, transactionHash, proposalId, proposer, target, startTime, endTime, description }) => {
|
||||||
|
return {
|
||||||
|
blockNumber: Number(blockNumber),
|
||||||
|
logIndex: Number(logIndex),
|
||||||
|
transactionHash,
|
||||||
|
event: 'ProposalCreated',
|
||||||
|
id: Number(proposalId),
|
||||||
|
proposer: getAddress(proposer),
|
||||||
|
target: getAddress(target),
|
||||||
|
startTime: Number(startTime),
|
||||||
|
endTime: Number(endTime),
|
||||||
|
description,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const formattedVotes: GovernanceVotedEvents[] = votes.map(
|
||||||
|
({ blockNumber, logIndex, transactionHash, proposalId, voter, support, votes, from, input }) => {
|
||||||
|
// Filter spammy txs
|
||||||
|
if (!input || input.length > 2048) {
|
||||||
|
input = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
blockNumber: Number(blockNumber),
|
||||||
|
logIndex: Number(logIndex),
|
||||||
|
transactionHash,
|
||||||
|
event: 'Voted',
|
||||||
|
proposalId: Number(proposalId),
|
||||||
|
voter: getAddress(voter),
|
||||||
|
support,
|
||||||
|
votes,
|
||||||
|
from: getAddress(from),
|
||||||
|
input,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const formattedDelegates: GovernanceDelegatedEvents[] = delegates.map(
|
||||||
|
({ blockNumber, logIndex, transactionHash, account, delegateTo }) => {
|
||||||
|
return {
|
||||||
|
blockNumber: Number(blockNumber),
|
||||||
|
logIndex: Number(logIndex),
|
||||||
|
transactionHash,
|
||||||
|
event: 'Delegated',
|
||||||
|
account: getAddress(account),
|
||||||
|
delegateTo: getAddress(delegateTo),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const formattedUndelegates: GovernanceUndelegatedEvents[] = undelegates.map(
|
||||||
|
({ blockNumber, logIndex, transactionHash, account, delegateFrom }) => {
|
||||||
|
return {
|
||||||
|
blockNumber: Number(blockNumber),
|
||||||
|
logIndex: Number(logIndex),
|
||||||
|
transactionHash,
|
||||||
|
event: 'Undelegated',
|
||||||
|
account: getAddress(account),
|
||||||
|
delegateFrom: getAddress(delegateFrom),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let formattedEvents = [
|
||||||
|
...formattedProposals,
|
||||||
|
...formattedVotes,
|
||||||
|
...formattedDelegates,
|
||||||
|
...formattedUndelegates,
|
||||||
|
].sort((a, b) => {
|
||||||
|
if (a.blockNumber === b.blockNumber) {
|
||||||
|
return a.logIndex - b.logIndex;
|
||||||
|
}
|
||||||
|
return a.blockNumber - b.blockNumber;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (eventsLength < 900) {
|
||||||
|
result.push(...formattedEvents);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [firstEvent] = formattedEvents;
|
||||||
|
const [lastEvent] = formattedEvents.slice(-1);
|
||||||
|
|
||||||
|
if (typeof onProgress === 'function') {
|
||||||
|
onProgress({
|
||||||
|
type: 'Governance Events',
|
||||||
|
fromBlock: Number(firstEvent.blockNumber),
|
||||||
|
toBlock: Number(lastEvent.blockNumber),
|
||||||
|
count: eventsLength,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedEvents = formattedEvents.filter(({ blockNumber }) => blockNumber !== lastEvent.blockNumber);
|
||||||
|
|
||||||
|
fromBlock = Number(lastEvent.blockNumber);
|
||||||
|
|
||||||
|
result.push(...formattedEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [lastEvent] = result.slice(-1);
|
||||||
|
|
||||||
|
return {
|
||||||
|
events: result,
|
||||||
|
lastSyncBlock: lastEvent && lastEvent.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Error from getAllGovernance query');
|
||||||
|
console.log(err);
|
||||||
|
return {
|
||||||
|
events: [],
|
||||||
|
lastSyncBlock: fromBlock,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -107,6 +107,23 @@ export const GET_NOTE_ACCOUNTS = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GET_ECHO_EVENTS = `
|
||||||
|
query getNoteAccounts($first: Int, $fromBlock: Int) {
|
||||||
|
noteAccounts(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
|
id
|
||||||
|
blockNumber
|
||||||
|
address
|
||||||
|
encryptedAccount
|
||||||
|
}
|
||||||
|
_meta {
|
||||||
|
block {
|
||||||
|
number
|
||||||
|
}
|
||||||
|
hasIndexingErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const GET_ENCRYPTED_NOTES = `
|
export const GET_ENCRYPTED_NOTES = `
|
||||||
query getEncryptedNotes($first: Int, $fromBlock: Int) {
|
query getEncryptedNotes($first: Int, $fromBlock: Int) {
|
||||||
encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
@ -123,3 +140,58 @@ export const GET_ENCRYPTED_NOTES = `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const GET_GOVERNANCE_EVENTS = `
|
||||||
|
query getGovernanceEvents($first: Int, $fromBlock: Int) {
|
||||||
|
proposals(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
|
blockNumber
|
||||||
|
logIndex
|
||||||
|
transactionHash
|
||||||
|
proposalId
|
||||||
|
proposer
|
||||||
|
target
|
||||||
|
startTime
|
||||||
|
endTime
|
||||||
|
description
|
||||||
|
}
|
||||||
|
votes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
|
blockNumber
|
||||||
|
logIndex
|
||||||
|
transactionHash
|
||||||
|
proposalId
|
||||||
|
voter
|
||||||
|
support
|
||||||
|
votes
|
||||||
|
from
|
||||||
|
input
|
||||||
|
}
|
||||||
|
delegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
|
blockNumber
|
||||||
|
logIndex
|
||||||
|
transactionHash
|
||||||
|
account
|
||||||
|
delegateTo
|
||||||
|
}
|
||||||
|
undelegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
|
||||||
|
blockNumber
|
||||||
|
logIndex
|
||||||
|
transactionHash
|
||||||
|
account
|
||||||
|
delegateFrom
|
||||||
|
}
|
||||||
|
_meta {
|
||||||
|
block {
|
||||||
|
number
|
||||||
|
}
|
||||||
|
hasIndexingErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const GET_GOVERNANCE_APY = `
|
||||||
|
stakeDailyBurns(first: 30, orderBy: date, orderDirection: desc) {
|
||||||
|
id
|
||||||
|
date
|
||||||
|
dailyAmountBurned
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
@ -4,6 +4,7 @@ export * from './schemas';
|
|||||||
export * from './batch';
|
export * from './batch';
|
||||||
export * from './data';
|
export * from './data';
|
||||||
export * from './deposits';
|
export * from './deposits';
|
||||||
|
export * from './encryptedNotes';
|
||||||
export * from './fees';
|
export * from './fees';
|
||||||
export * from './merkleTree';
|
export * from './merkleTree';
|
||||||
export * from './mimc';
|
export * from './mimc';
|
||||||
@ -15,5 +16,6 @@ export * from './prices';
|
|||||||
export * from './providers';
|
export * from './providers';
|
||||||
export * from './relayerClient';
|
export * from './relayerClient';
|
||||||
export * from './tokens';
|
export * from './tokens';
|
||||||
|
export * from './treeCache';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
export * from './websnark';
|
export * from './websnark';
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import { Worker as NodeWorker } from 'worker_threads';
|
import { Worker as NodeWorker } from 'worker_threads';
|
||||||
import { MerkleTree, Element } from '@tornado/fixed-merkle-tree';
|
import { MerkleTree, PartialMerkleTree, Element, TreeEdge } from '@tornado/fixed-merkle-tree';
|
||||||
import type { Tornado } from '@tornado/contracts';
|
import type { Tornado } from '@tornado/contracts';
|
||||||
import { isNode, toFixedHex } from './utils';
|
import { isNode, toFixedHex } from './utils';
|
||||||
import { mimc } from './mimc';
|
import { mimc } from './mimc';
|
||||||
import type { DepositType } from './deposits';
|
import type { DepositType } from './deposits';
|
||||||
import type { DepositsEvents } from './events';
|
import type { DepositsEvents } from './events';
|
||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
|
|
||||||
export type MerkleTreeConstructor = DepositType & {
|
export type MerkleTreeConstructor = DepositType & {
|
||||||
Tornado: Tornado;
|
Tornado: Tornado;
|
||||||
commitment?: string;
|
commitmentHex?: string;
|
||||||
merkleTreeHeight?: number;
|
merkleTreeHeight?: number;
|
||||||
emptyElement?: string;
|
emptyElement?: string;
|
||||||
merkleWorkerPath?: string;
|
merkleWorkerPath?: string;
|
||||||
@ -17,9 +18,9 @@ export type MerkleTreeConstructor = DepositType & {
|
|||||||
export class MerkleTreeService {
|
export class MerkleTreeService {
|
||||||
currency: string;
|
currency: string;
|
||||||
amount: string;
|
amount: string;
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
Tornado: Tornado;
|
Tornado: Tornado;
|
||||||
commitment?: string;
|
commitmentHex?: string;
|
||||||
instanceName: string;
|
instanceName: string;
|
||||||
|
|
||||||
merkleTreeHeight: number;
|
merkleTreeHeight: number;
|
||||||
@ -32,7 +33,7 @@ export class MerkleTreeService {
|
|||||||
amount,
|
amount,
|
||||||
currency,
|
currency,
|
||||||
Tornado,
|
Tornado,
|
||||||
commitment,
|
commitmentHex,
|
||||||
merkleTreeHeight = 20,
|
merkleTreeHeight = 20,
|
||||||
emptyElement = '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement = '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
merkleWorkerPath,
|
merkleWorkerPath,
|
||||||
@ -45,14 +46,14 @@ export class MerkleTreeService {
|
|||||||
|
|
||||||
this.Tornado = Tornado;
|
this.Tornado = Tornado;
|
||||||
this.instanceName = instanceName;
|
this.instanceName = instanceName;
|
||||||
this.commitment = commitment;
|
this.commitmentHex = commitmentHex;
|
||||||
|
|
||||||
this.merkleTreeHeight = merkleTreeHeight;
|
this.merkleTreeHeight = merkleTreeHeight;
|
||||||
this.emptyElement = emptyElement;
|
this.emptyElement = emptyElement;
|
||||||
this.merkleWorkerPath = merkleWorkerPath;
|
this.merkleWorkerPath = merkleWorkerPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createTree({ events }: { events: Element[] }) {
|
async createTree(events: Element[]) {
|
||||||
const { hash: hashFunction } = await mimc.getHash();
|
const { hash: hashFunction } = await mimc.getHash();
|
||||||
|
|
||||||
if (this.merkleWorkerPath) {
|
if (this.merkleWorkerPath) {
|
||||||
@ -113,13 +114,76 @@ export class MerkleTreeService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyTree({ events }: { events: DepositsEvents[] }) {
|
async createPartialTree({ edge, elements }: { edge: TreeEdge; elements: Element[] }) {
|
||||||
|
const { hash: hashFunction } = await mimc.getHash();
|
||||||
|
|
||||||
|
if (this.merkleWorkerPath) {
|
||||||
|
console.log('Using merkleWorker\n');
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isNode) {
|
||||||
|
const merkleWorkerPromise = new Promise((resolve, reject) => {
|
||||||
|
const worker = new NodeWorker(this.merkleWorkerPath as string, {
|
||||||
|
workerData: {
|
||||||
|
merkleTreeHeight: this.merkleTreeHeight,
|
||||||
|
edge,
|
||||||
|
elements,
|
||||||
|
zeroElement: this.emptyElement,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
worker.on('message', resolve);
|
||||||
|
worker.on('error', reject);
|
||||||
|
worker.on('exit', (code) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
reject(new Error(`Worker stopped with exit code ${code}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}) as Promise<string>;
|
||||||
|
|
||||||
|
return PartialMerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
|
||||||
|
} else {
|
||||||
|
const merkleWorkerPromise = new Promise((resolve, reject) => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const worker = new (Worker as any)(this.merkleWorkerPath);
|
||||||
|
|
||||||
|
worker.onmessage = (e: { data: string }) => {
|
||||||
|
resolve(e.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
worker.onerror = (e: any) => {
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.postMessage({
|
||||||
|
merkleTreeHeight: this.merkleTreeHeight,
|
||||||
|
edge,
|
||||||
|
elements,
|
||||||
|
zeroElement: this.emptyElement,
|
||||||
|
});
|
||||||
|
}) as Promise<string>;
|
||||||
|
|
||||||
|
return PartialMerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log('merkleWorker failed, falling back to synchronous merkle tree');
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PartialMerkleTree(this.merkleTreeHeight, edge, elements, {
|
||||||
|
zeroElement: this.emptyElement,
|
||||||
|
hashFunction,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyTree(events: DepositsEvents[]) {
|
||||||
console.log(
|
console.log(
|
||||||
`\nCreating deposit tree for ${this.netId} ${this.amount} ${this.currency.toUpperCase()} would take a while\n`,
|
`\nCreating deposit tree for ${this.netId} ${this.amount} ${this.currency.toUpperCase()} would take a while\n`,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.time('Created tree in');
|
console.time('Created tree in');
|
||||||
const tree = await this.createTree({ events: events.map(({ commitment }) => BigInt(commitment).toString()) });
|
const tree = await this.createTree(events.map(({ commitment }) => commitment));
|
||||||
console.timeEnd('Created tree in');
|
console.timeEnd('Created tree in');
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Type of default supported networks
|
||||||
|
*/
|
||||||
|
export enum NetId {
|
||||||
|
MAINNET = 1,
|
||||||
|
BSC = 56,
|
||||||
|
POLYGON = 137,
|
||||||
|
OPTIMISM = 10,
|
||||||
|
ARBITRUM = 42161,
|
||||||
|
GNOSIS = 100,
|
||||||
|
AVALANCHE = 43114,
|
||||||
|
SEPOLIA = 11155111,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NetIdType = NetId | number;
|
||||||
|
|
||||||
export interface RpcUrl {
|
export interface RpcUrl {
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
@ -46,20 +62,20 @@ export type Config = {
|
|||||||
};
|
};
|
||||||
nativeCurrency: string;
|
nativeCurrency: string;
|
||||||
currencyName: string;
|
currencyName: string;
|
||||||
explorerUrl: {
|
explorerUrl: string;
|
||||||
tx: string;
|
|
||||||
address: string;
|
|
||||||
block: string;
|
|
||||||
};
|
|
||||||
merkleTreeHeight: number;
|
merkleTreeHeight: number;
|
||||||
emptyElement: string;
|
emptyElement: string;
|
||||||
networkName: string;
|
networkName: string;
|
||||||
deployedBlock: number;
|
deployedBlock: number;
|
||||||
rpcUrls: RpcUrls;
|
rpcUrls: RpcUrls;
|
||||||
multicall: string;
|
multicallContract: string;
|
||||||
routerContract: string;
|
routerContract: string;
|
||||||
registryContract?: string;
|
|
||||||
echoContract: string;
|
echoContract: string;
|
||||||
|
offchainOracleContract?: string;
|
||||||
|
tornContract?: string;
|
||||||
|
governanceContract?: string;
|
||||||
|
stakingRewardsContract?: string;
|
||||||
|
registryContract?: string;
|
||||||
aggregatorContract?: string;
|
aggregatorContract?: string;
|
||||||
reverseRecordsContract?: string;
|
reverseRecordsContract?: string;
|
||||||
gasPriceOracleContract?: string;
|
gasPriceOracleContract?: string;
|
||||||
@ -67,6 +83,7 @@ export type Config = {
|
|||||||
ovmGasPriceOracleContract?: string;
|
ovmGasPriceOracleContract?: string;
|
||||||
tornadoSubgraph: string;
|
tornadoSubgraph: string;
|
||||||
registrySubgraph?: string;
|
registrySubgraph?: string;
|
||||||
|
governanceSubgraph?: string;
|
||||||
subgraphs: SubgraphUrls;
|
subgraphs: SubgraphUrls;
|
||||||
tokens: TokenInstances;
|
tokens: TokenInstances;
|
||||||
optionalTokens?: string[];
|
optionalTokens?: string[];
|
||||||
@ -81,20 +98,12 @@ export type Config = {
|
|||||||
// Should be in seconds
|
// Should be in seconds
|
||||||
MINING_BLOCK_TIME?: number;
|
MINING_BLOCK_TIME?: number;
|
||||||
};
|
};
|
||||||
'torn.contract.tornadocash.eth'?: string;
|
|
||||||
'governance.contract.tornadocash.eth'?: string;
|
|
||||||
'staking-rewards.contract.tornadocash.eth'?: string;
|
|
||||||
'tornado-router.contract.tornadocash.eth'?: string;
|
|
||||||
'tornado-proxy-light.contract.tornadocash.eth'?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type networkConfig = {
|
export type networkConfig = {
|
||||||
[key in string]: Config;
|
[key in NetIdType]: Config;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const blockSyncInterval = 10000;
|
|
||||||
export const enabledChains = ['1', '10', '56', '100', '137', '42161', '43114', '11155111'];
|
|
||||||
|
|
||||||
const theGraph = {
|
const theGraph = {
|
||||||
name: 'Hosted Graph',
|
name: 'Hosted Graph',
|
||||||
url: 'https://api.thegraph.com',
|
url: 'https://api.thegraph.com',
|
||||||
@ -104,8 +113,8 @@ const tornado = {
|
|||||||
url: 'https://tornadocash-rpc.com',
|
url: 'https://tornadocash-rpc.com',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const networkConfig: networkConfig = {
|
export const defaultConfig: networkConfig = {
|
||||||
netId1: {
|
[NetId.MAINNET]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 80,
|
instant: 80,
|
||||||
@ -115,11 +124,7 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'eth',
|
nativeCurrency: 'eth',
|
||||||
currencyName: 'ETH',
|
currencyName: 'ETH',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://etherscan.io',
|
||||||
tx: 'https://etherscan.io/tx/',
|
|
||||||
address: 'https://etherscan.io/address/',
|
|
||||||
block: 'https://etherscan.io/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Ethereum Mainnet',
|
networkName: 'Ethereum Mainnet',
|
||||||
@ -130,7 +135,7 @@ export const networkConfig: networkConfig = {
|
|||||||
url: 'https://tornadocash-rpc.com',
|
url: 'https://tornadocash-rpc.com',
|
||||||
},
|
},
|
||||||
chainnodes: {
|
chainnodes: {
|
||||||
name: 'Tornado RPC',
|
name: 'Chainnodes RPC',
|
||||||
url: 'https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
url: 'https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||||
},
|
},
|
||||||
mevblockerRPC: {
|
mevblockerRPC: {
|
||||||
@ -158,14 +163,19 @@ export const networkConfig: networkConfig = {
|
|||||||
url: 'https://1rpc.io/eth',
|
url: 'https://1rpc.io/eth',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
routerContract: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
|
routerContract: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
|
||||||
registryContract: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2',
|
|
||||||
echoContract: '0x9B27DD5Bb15d42DC224FCD0B7caEbBe16161Df42',
|
echoContract: '0x9B27DD5Bb15d42DC224FCD0B7caEbBe16161Df42',
|
||||||
|
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
|
||||||
|
tornContract: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
||||||
|
governanceContract: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||||
|
stakingRewardsContract: '0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29',
|
||||||
|
registryContract: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2',
|
||||||
aggregatorContract: '0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49',
|
aggregatorContract: '0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49',
|
||||||
reverseRecordsContract: '0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C',
|
reverseRecordsContract: '0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C',
|
||||||
tornadoSubgraph: 'tornadocash/mainnet-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/mainnet-tornado-subgraph',
|
||||||
registrySubgraph: 'tornadocash/tornado-relayer-registry',
|
registrySubgraph: 'tornadocash/tornado-relayer-registry',
|
||||||
|
governanceSubgraph: 'tornadocash/tornado-governance',
|
||||||
subgraphs: {
|
subgraphs: {
|
||||||
tornado,
|
tornado,
|
||||||
theGraph,
|
theGraph,
|
||||||
@ -247,16 +257,12 @@ export const networkConfig: networkConfig = {
|
|||||||
constants: {
|
constants: {
|
||||||
GOVERNANCE_BLOCK: 11474695,
|
GOVERNANCE_BLOCK: 11474695,
|
||||||
NOTE_ACCOUNT_BLOCK: 11842486,
|
NOTE_ACCOUNT_BLOCK: 11842486,
|
||||||
ENCRYPTED_NOTES_BLOCK: 14248730,
|
ENCRYPTED_NOTES_BLOCK: 12143762,
|
||||||
REGISTRY_BLOCK: 14173129,
|
REGISTRY_BLOCK: 14173129,
|
||||||
MINING_BLOCK_TIME: 15,
|
MINING_BLOCK_TIME: 15,
|
||||||
},
|
},
|
||||||
'torn.contract.tornadocash.eth': '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
|
||||||
'governance.contract.tornadocash.eth': '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
|
||||||
'tornado-router.contract.tornadocash.eth': '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
|
|
||||||
'staking-rewards.contract.tornadocash.eth': '0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29',
|
|
||||||
},
|
},
|
||||||
netId56: {
|
[NetId.BSC]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 5,
|
instant: 5,
|
||||||
@ -266,18 +272,15 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'bnb',
|
nativeCurrency: 'bnb',
|
||||||
currencyName: 'BNB',
|
currencyName: 'BNB',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://bscscan.com',
|
||||||
tx: 'https://bscscan.com/tx/',
|
|
||||||
address: 'https://bscscan.com/address/',
|
|
||||||
block: 'https://bscscan.com/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Binance Smart Chain',
|
networkName: 'Binance Smart Chain',
|
||||||
deployedBlock: 8158799,
|
deployedBlock: 8158799,
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
|
||||||
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
||||||
|
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
||||||
|
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
|
||||||
tornadoSubgraph: 'tornadocash/bsc-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/bsc-tornado-subgraph',
|
||||||
subgraphs: {
|
subgraphs: {
|
||||||
tornado,
|
tornado,
|
||||||
@ -289,7 +292,7 @@ export const networkConfig: networkConfig = {
|
|||||||
url: 'https://tornadocash-rpc.com/bsc',
|
url: 'https://tornadocash-rpc.com/bsc',
|
||||||
},
|
},
|
||||||
chainnodes: {
|
chainnodes: {
|
||||||
name: 'Tornado RPC',
|
name: 'Chainnodes RPC',
|
||||||
url: 'https://bsc-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
url: 'https://bsc-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||||
},
|
},
|
||||||
stackup: {
|
stackup: {
|
||||||
@ -323,9 +326,8 @@ export const networkConfig: networkConfig = {
|
|||||||
NOTE_ACCOUNT_BLOCK: 8159269,
|
NOTE_ACCOUNT_BLOCK: 8159269,
|
||||||
ENCRYPTED_NOTES_BLOCK: 8159269,
|
ENCRYPTED_NOTES_BLOCK: 8159269,
|
||||||
},
|
},
|
||||||
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
|
||||||
},
|
},
|
||||||
netId137: {
|
[NetId.POLYGON]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 100,
|
instant: 100,
|
||||||
@ -335,18 +337,15 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'matic',
|
nativeCurrency: 'matic',
|
||||||
currencyName: 'MATIC',
|
currencyName: 'MATIC',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://polygonscan.com',
|
||||||
tx: 'https://polygonscan.com/tx/',
|
|
||||||
address: 'https://polygonscan.com/address/',
|
|
||||||
block: 'https://polygonscan.com/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Polygon (Matic) Network',
|
networkName: 'Polygon (Matic) Network',
|
||||||
deployedBlock: 16257962,
|
deployedBlock: 16257962,
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
|
||||||
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
||||||
|
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
||||||
|
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
|
||||||
gasPriceOracleContract: '0xF81A8D8D3581985D3969fe53bFA67074aDFa8F3C',
|
gasPriceOracleContract: '0xF81A8D8D3581985D3969fe53bFA67074aDFa8F3C',
|
||||||
tornadoSubgraph: 'tornadocash/matic-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/matic-tornado-subgraph',
|
||||||
subgraphs: {
|
subgraphs: {
|
||||||
@ -385,9 +384,8 @@ export const networkConfig: networkConfig = {
|
|||||||
NOTE_ACCOUNT_BLOCK: 16257996,
|
NOTE_ACCOUNT_BLOCK: 16257996,
|
||||||
ENCRYPTED_NOTES_BLOCK: 16257996,
|
ENCRYPTED_NOTES_BLOCK: 16257996,
|
||||||
},
|
},
|
||||||
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
|
||||||
},
|
},
|
||||||
netId10: {
|
[NetId.OPTIMISM]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 0.001,
|
instant: 0.001,
|
||||||
@ -397,18 +395,15 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'eth',
|
nativeCurrency: 'eth',
|
||||||
currencyName: 'ETH',
|
currencyName: 'ETH',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://optimistic.etherscan.io',
|
||||||
tx: 'https://optimistic.etherscan.io/tx/',
|
|
||||||
address: 'https://optimistic.etherscan.io/address/',
|
|
||||||
block: 'https://optimistic.etherscan.io/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Optimism',
|
networkName: 'Optimism',
|
||||||
deployedBlock: 2243689,
|
deployedBlock: 2243689,
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
|
||||||
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
||||||
|
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
||||||
|
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
|
||||||
ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F',
|
ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F',
|
||||||
tornadoSubgraph: 'tornadocash/optimism-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/optimism-tornado-subgraph',
|
||||||
subgraphs: {
|
subgraphs: {
|
||||||
@ -421,7 +416,7 @@ export const networkConfig: networkConfig = {
|
|||||||
url: 'https://tornadocash-rpc.com/op',
|
url: 'https://tornadocash-rpc.com/op',
|
||||||
},
|
},
|
||||||
chainnodes: {
|
chainnodes: {
|
||||||
name: 'Tornado RPC',
|
name: 'Chainnodes RPC',
|
||||||
url: 'https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
url: 'https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||||
},
|
},
|
||||||
optimism: {
|
optimism: {
|
||||||
@ -455,9 +450,8 @@ export const networkConfig: networkConfig = {
|
|||||||
NOTE_ACCOUNT_BLOCK: 2243694,
|
NOTE_ACCOUNT_BLOCK: 2243694,
|
||||||
ENCRYPTED_NOTES_BLOCK: 2243694,
|
ENCRYPTED_NOTES_BLOCK: 2243694,
|
||||||
},
|
},
|
||||||
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
|
||||||
},
|
},
|
||||||
netId42161: {
|
[NetId.ARBITRUM]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 4,
|
instant: 4,
|
||||||
@ -467,18 +461,15 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'eth',
|
nativeCurrency: 'eth',
|
||||||
currencyName: 'ETH',
|
currencyName: 'ETH',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://arbiscan.io',
|
||||||
tx: 'https://arbiscan.io/tx/',
|
|
||||||
address: 'https://arbiscan.io/address/',
|
|
||||||
block: 'https://arbiscan.io/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Arbitrum One',
|
networkName: 'Arbitrum One',
|
||||||
deployedBlock: 3430648,
|
deployedBlock: 3430648,
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
|
||||||
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
||||||
|
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
||||||
|
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
|
||||||
tornadoSubgraph: 'tornadocash/arbitrum-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/arbitrum-tornado-subgraph',
|
||||||
subgraphs: {
|
subgraphs: {
|
||||||
tornado,
|
tornado,
|
||||||
@ -490,7 +481,7 @@ export const networkConfig: networkConfig = {
|
|||||||
url: 'https://tornadocash-rpc.com/arbitrum',
|
url: 'https://tornadocash-rpc.com/arbitrum',
|
||||||
},
|
},
|
||||||
chainnodes: {
|
chainnodes: {
|
||||||
name: 'Tornado RPC',
|
name: 'Chainnodes RPC',
|
||||||
url: 'https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
url: 'https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||||
},
|
},
|
||||||
arbitrum: {
|
arbitrum: {
|
||||||
@ -524,9 +515,8 @@ export const networkConfig: networkConfig = {
|
|||||||
NOTE_ACCOUNT_BLOCK: 3430605,
|
NOTE_ACCOUNT_BLOCK: 3430605,
|
||||||
ENCRYPTED_NOTES_BLOCK: 3430605,
|
ENCRYPTED_NOTES_BLOCK: 3430605,
|
||||||
},
|
},
|
||||||
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
|
||||||
},
|
},
|
||||||
netId100: {
|
[NetId.GNOSIS]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 6,
|
instant: 6,
|
||||||
@ -536,18 +526,15 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'xdai',
|
nativeCurrency: 'xdai',
|
||||||
currencyName: 'xDAI',
|
currencyName: 'xDAI',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://gnosisscan.io',
|
||||||
tx: 'https://blockscout.com/xdai/mainnet/tx/',
|
|
||||||
address: 'https://blockscout.com/xdai/mainnet/address/',
|
|
||||||
block: 'https://blockscout.com/xdai/mainnet/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Gnosis Chain',
|
networkName: 'Gnosis Chain',
|
||||||
deployedBlock: 17754561,
|
deployedBlock: 17754561,
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
|
||||||
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
||||||
|
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
||||||
|
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
|
||||||
tornadoSubgraph: 'tornadocash/xdai-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/xdai-tornado-subgraph',
|
||||||
subgraphs: {
|
subgraphs: {
|
||||||
tornado,
|
tornado,
|
||||||
@ -559,7 +546,7 @@ export const networkConfig: networkConfig = {
|
|||||||
url: 'https://tornadocash-rpc.com/gnosis',
|
url: 'https://tornadocash-rpc.com/gnosis',
|
||||||
},
|
},
|
||||||
chainnodes: {
|
chainnodes: {
|
||||||
name: 'Tornado RPC',
|
name: 'Chainnodes RPC',
|
||||||
url: 'https://gnosis-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
url: 'https://gnosis-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||||
},
|
},
|
||||||
gnosis: {
|
gnosis: {
|
||||||
@ -593,9 +580,8 @@ export const networkConfig: networkConfig = {
|
|||||||
NOTE_ACCOUNT_BLOCK: 17754564,
|
NOTE_ACCOUNT_BLOCK: 17754564,
|
||||||
ENCRYPTED_NOTES_BLOCK: 17754564,
|
ENCRYPTED_NOTES_BLOCK: 17754564,
|
||||||
},
|
},
|
||||||
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
|
||||||
},
|
},
|
||||||
netId43114: {
|
[NetId.AVALANCHE]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 225,
|
instant: 225,
|
||||||
@ -605,18 +591,15 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'avax',
|
nativeCurrency: 'avax',
|
||||||
currencyName: 'AVAX',
|
currencyName: 'AVAX',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://snowtrace.io',
|
||||||
tx: 'https://snowtrace.io/tx/',
|
|
||||||
address: 'https://snowtrace.io/address/',
|
|
||||||
block: 'https://snowtrace.io/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Avalanche Mainnet',
|
networkName: 'Avalanche Mainnet',
|
||||||
deployedBlock: 4429818,
|
deployedBlock: 4429818,
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
|
||||||
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
||||||
|
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
||||||
|
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
|
||||||
tornadoSubgraph: 'tornadocash/avalanche-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/avalanche-tornado-subgraph',
|
||||||
subgraphs: {
|
subgraphs: {
|
||||||
theGraph,
|
theGraph,
|
||||||
@ -652,9 +635,8 @@ export const networkConfig: networkConfig = {
|
|||||||
NOTE_ACCOUNT_BLOCK: 4429813,
|
NOTE_ACCOUNT_BLOCK: 4429813,
|
||||||
ENCRYPTED_NOTES_BLOCK: 4429813,
|
ENCRYPTED_NOTES_BLOCK: 4429813,
|
||||||
},
|
},
|
||||||
'tornado-proxy-light.contract.tornadocash.eth': '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
|
|
||||||
},
|
},
|
||||||
netId11155111: {
|
[NetId.SEPOLIA]: {
|
||||||
rpcCallRetryAttempt: 15,
|
rpcCallRetryAttempt: 15,
|
||||||
gasPrices: {
|
gasPrices: {
|
||||||
instant: 2,
|
instant: 2,
|
||||||
@ -664,19 +646,18 @@ export const networkConfig: networkConfig = {
|
|||||||
},
|
},
|
||||||
nativeCurrency: 'eth',
|
nativeCurrency: 'eth',
|
||||||
currencyName: 'SepoliaETH',
|
currencyName: 'SepoliaETH',
|
||||||
explorerUrl: {
|
explorerUrl: 'https://sepolia.etherscan.io',
|
||||||
tx: 'https://sepolia.etherscan.io/tx/',
|
|
||||||
address: 'https://sepolia.etherscan.io/address/',
|
|
||||||
block: 'https://sepolia.etherscan.io/block/',
|
|
||||||
},
|
|
||||||
merkleTreeHeight: 20,
|
merkleTreeHeight: 20,
|
||||||
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
|
||||||
networkName: 'Ethereum Sepolia',
|
networkName: 'Ethereum Sepolia',
|
||||||
deployedBlock: 5594395,
|
deployedBlock: 5594395,
|
||||||
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
||||||
routerContract: '0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee',
|
routerContract: '0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee',
|
||||||
|
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
|
||||||
|
tornContract: '0x3AE6667167C0f44394106E197904519D808323cA',
|
||||||
|
governanceContract: '0xe5324cD7602eeb387418e594B87aCADee08aeCAD',
|
||||||
|
stakingRewardsContract: '0x6d0018890751Efd31feb8166711B16732E2b496b',
|
||||||
registryContract: '0x1428e5d2356b13778A13108b10c440C83011dfB8',
|
registryContract: '0x1428e5d2356b13778A13108b10c440C83011dfB8',
|
||||||
echoContract: '0xcDD1fc3F5ac2782D83449d3AbE80D6b7B273B0e5',
|
|
||||||
aggregatorContract: '0x4088712AC9fad39ea133cdb9130E465d235e9642',
|
aggregatorContract: '0x4088712AC9fad39ea133cdb9130E465d235e9642',
|
||||||
reverseRecordsContract: '0xEc29700C0283e5Be64AcdFe8077d6cC95dE23C23',
|
reverseRecordsContract: '0xEc29700C0283e5Be64AcdFe8077d6cC95dE23C23',
|
||||||
tornadoSubgraph: 'tornadocash/sepolia-tornado-subgraph',
|
tornadoSubgraph: 'tornadocash/sepolia-tornado-subgraph',
|
||||||
@ -730,12 +711,80 @@ export const networkConfig: networkConfig = {
|
|||||||
ENCRYPTED_NOTES_BLOCK: 5594395,
|
ENCRYPTED_NOTES_BLOCK: 5594395,
|
||||||
MINING_BLOCK_TIME: 15,
|
MINING_BLOCK_TIME: 15,
|
||||||
},
|
},
|
||||||
'torn.contract.tornadocash.eth': '0x3AE6667167C0f44394106E197904519D808323cA',
|
|
||||||
'governance.contract.tornadocash.eth': '0xe5324cD7602eeb387418e594B87aCADee08aeCAD',
|
|
||||||
'tornado-router.contract.tornadocash.eth': '0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee',
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const subdomains = enabledChains.map((chain) => networkConfig[`netId${chain}`].ensSubdomainKey);
|
export const enabledChains = Object.values(NetId).filter((n) => typeof n === 'number') as NetIdType[];
|
||||||
|
|
||||||
export default networkConfig;
|
/**
|
||||||
|
* Custom config object to extend default config
|
||||||
|
*
|
||||||
|
* Inspired by getUrlFunc from ethers.js
|
||||||
|
* https://github.com/ethers-io/ethers.js/blob/v6/src.ts/utils/fetch.ts#L59
|
||||||
|
*/
|
||||||
|
export let customConfig: networkConfig = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or override existing network config object
|
||||||
|
*
|
||||||
|
* Could be also called on the UI hook so that the UI could allow people to use custom privacy pools
|
||||||
|
*/
|
||||||
|
export function addNetwork(newConfig: networkConfig) {
|
||||||
|
enabledChains.push(
|
||||||
|
...Object.keys(newConfig)
|
||||||
|
.map((netId) => Number(netId))
|
||||||
|
.filter((netId) => !enabledChains.includes(netId)),
|
||||||
|
);
|
||||||
|
|
||||||
|
customConfig = {
|
||||||
|
...customConfig,
|
||||||
|
...newConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getNetworkConfig(): networkConfig {
|
||||||
|
// customConfig object
|
||||||
|
const allConfig = {
|
||||||
|
...defaultConfig,
|
||||||
|
...customConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
return enabledChains.reduce((acc, curr) => {
|
||||||
|
acc[curr] = allConfig[curr];
|
||||||
|
return acc;
|
||||||
|
}, {} as networkConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getConfig(netId: NetIdType) {
|
||||||
|
const allConfig = getNetworkConfig();
|
||||||
|
|
||||||
|
const chainConfig = allConfig[netId];
|
||||||
|
|
||||||
|
if (!chainConfig) {
|
||||||
|
const errMsg = `No config found for network ${netId}!`;
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chainConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInstanceByAddress({ netId, address }: { netId: NetIdType; address: string }) {
|
||||||
|
const { tokens } = getConfig(netId);
|
||||||
|
|
||||||
|
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
|
||||||
|
for (const [amount, instance] of Object.entries(instanceAddress)) {
|
||||||
|
if (instance === address) {
|
||||||
|
return {
|
||||||
|
amount,
|
||||||
|
currency,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSubdomains() {
|
||||||
|
const allConfig = getNetworkConfig();
|
||||||
|
|
||||||
|
return enabledChains.map((chain) => allConfig[chain].ensSubdomainKey);
|
||||||
|
}
|
||||||
|
@ -60,3 +60,18 @@ export function parseKey(value?: string): string {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recovery key shouldn't have a 0x prefix (Also this is how the UI generates)
|
||||||
|
*/
|
||||||
|
export function parseRecoveryKey(value?: string): string {
|
||||||
|
if (!value) {
|
||||||
|
throw new InvalidArgumentError('Invalid Recovery Key');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
computeAddress('0x' + value);
|
||||||
|
} catch {
|
||||||
|
throw new InvalidArgumentError('Invalid Recovery Key');
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
@ -22,7 +22,6 @@ import {
|
|||||||
Network,
|
Network,
|
||||||
parseUnits,
|
parseUnits,
|
||||||
FetchUrlFeeDataNetworkPlugin,
|
FetchUrlFeeDataNetworkPlugin,
|
||||||
BigNumberish,
|
|
||||||
FeeData,
|
FeeData,
|
||||||
EnsPlugin,
|
EnsPlugin,
|
||||||
GasCostPlugin,
|
GasCostPlugin,
|
||||||
@ -30,7 +29,7 @@ import {
|
|||||||
import type { RequestInfo, RequestInit, Response, HeadersInit } from 'node-fetch';
|
import type { RequestInfo, RequestInit, Response, HeadersInit } from 'node-fetch';
|
||||||
import { GasPriceOracle, GasPriceOracle__factory, Multicall, Multicall__factory } from '../typechain';
|
import { GasPriceOracle, GasPriceOracle__factory, Multicall, Multicall__factory } from '../typechain';
|
||||||
import { isNode, sleep } from './utils';
|
import { isNode, sleep } from './utils';
|
||||||
import type { Config } from './networkConfig';
|
import type { Config, NetIdType } from './networkConfig';
|
||||||
import { multicall } from './multicall';
|
import { multicall } from './multicall';
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
@ -213,47 +212,47 @@ export async function fetchData(url: string, options: fetchDataOptions = {}) {
|
|||||||
throw errorObject;
|
throw errorObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable prettier/prettier, @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
export const fetchGetUrlFunc =
|
export const fetchGetUrlFunc =
|
||||||
(options: fetchDataOptions = {}): FetchGetUrlFunc =>
|
(options: fetchDataOptions = {}): FetchGetUrlFunc =>
|
||||||
async (req, _signal) => {
|
async (req, _signal) => {
|
||||||
let signal;
|
let signal;
|
||||||
|
|
||||||
if (_signal) {
|
if (_signal) {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
signal = controller.signal;
|
signal = controller.signal;
|
||||||
_signal.addListener(() => {
|
_signal.addListener(() => {
|
||||||
controller.abort();
|
controller.abort();
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const init = {
|
|
||||||
...options,
|
|
||||||
method: req.method || 'POST',
|
|
||||||
headers: req.headers,
|
|
||||||
body: req.body || undefined,
|
|
||||||
signal,
|
|
||||||
returnResponse: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const resp = await fetchData(req.url, init);
|
|
||||||
|
|
||||||
const headers = {} as { [key in string]: any };
|
|
||||||
resp.headers.forEach((value: any, key: string) => {
|
|
||||||
headers[key.toLowerCase()] = value;
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const respBody = await resp.arrayBuffer();
|
const init = {
|
||||||
const body = respBody == null ? null : new Uint8Array(respBody);
|
...options,
|
||||||
|
method: req.method || 'POST',
|
||||||
return {
|
headers: req.headers,
|
||||||
statusCode: resp.status,
|
body: req.body || undefined,
|
||||||
statusMessage: resp.statusText,
|
signal,
|
||||||
headers,
|
returnResponse: true,
|
||||||
body,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
/* eslint-enable prettier/prettier, @typescript-eslint/no-explicit-any */
|
|
||||||
|
const resp = await fetchData(req.url, init);
|
||||||
|
|
||||||
|
const headers = {} as { [key in string]: any };
|
||||||
|
resp.headers.forEach((value: any, key: string) => {
|
||||||
|
headers[key.toLowerCase()] = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
const respBody = await resp.arrayBuffer();
|
||||||
|
const body = respBody == null ? null : new Uint8Array(respBody);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: resp.status,
|
||||||
|
statusMessage: resp.statusText,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
// caching to improve performance
|
// caching to improve performance
|
||||||
const oracleMapper = new Map();
|
const oracleMapper = new Map();
|
||||||
@ -366,7 +365,7 @@ export async function getProvider(rpcUrl: string, fetchOptions?: getProviderOpti
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getProviderWithNetId(
|
export function getProviderWithNetId(
|
||||||
netId: BigNumberish,
|
netId: NetIdType,
|
||||||
rpcUrl: string,
|
rpcUrl: string,
|
||||||
config: Config,
|
config: Config,
|
||||||
fetchOptions?: getProviderOptions,
|
fetchOptions?: getProviderOptions,
|
||||||
@ -604,7 +603,7 @@ export type handleWalletFunc = (...args: any[]) => void;
|
|||||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
export type TornadoBrowserProviderOptions = TornadoWalletOptions & {
|
export type TornadoBrowserProviderOptions = TornadoWalletOptions & {
|
||||||
webChainId?: BigNumberish;
|
webChainId?: NetIdType;
|
||||||
connectWallet?: connectWalletFunc;
|
connectWallet?: connectWalletFunc;
|
||||||
handleNetworkChanges?: handleWalletFunc;
|
handleNetworkChanges?: handleWalletFunc;
|
||||||
handleAccountChanges?: handleWalletFunc;
|
handleAccountChanges?: handleWalletFunc;
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { namehash, parseEther } from 'ethers';
|
import { getAddress, namehash, parseEther } from 'ethers';
|
||||||
import type { Aggregator } from '@tornado/contracts';
|
import type { Aggregator } from '@tornado/contracts';
|
||||||
import type { RelayerStructOutput } from '@tornado/contracts/dist/contracts/Governance/Aggregator/Aggregator';
|
import type { RelayerStructOutput } from '@tornado/contracts/dist/contracts/Governance/Aggregator/Aggregator';
|
||||||
import { sleep } from './utils';
|
import { sleep } from './utils';
|
||||||
import type { Config } from './networkConfig';
|
import { NetId, NetIdType, Config } from './networkConfig';
|
||||||
import { fetchData, fetchDataOptions } from './providers';
|
import { fetchData, fetchDataOptions } from './providers';
|
||||||
import { ajv, jobsSchema, getStatusSchema } from './schemas';
|
import { ajv, jobsSchema, getStatusSchema } from './schemas';
|
||||||
import type { snarkProofs } from './websnark';
|
import type { snarkProofs } from './websnark';
|
||||||
@ -15,21 +15,23 @@ export interface RelayerParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Relayer {
|
export interface Relayer {
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
url: string;
|
url: string;
|
||||||
|
hostname: string;
|
||||||
rewardAccount: string;
|
rewardAccount: string;
|
||||||
|
instances: string[];
|
||||||
|
gasPrice?: number;
|
||||||
|
ethPrices?: {
|
||||||
|
[key in string]: string;
|
||||||
|
};
|
||||||
currentQueue: number;
|
currentQueue: number;
|
||||||
tornadoServiceFee: number;
|
tornadoServiceFee: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RelayerInfo = Relayer & {
|
export type RelayerInfo = Relayer & {
|
||||||
hostname: string;
|
|
||||||
ensName: string;
|
ensName: string;
|
||||||
stakeBalance: bigint;
|
stakeBalance: bigint;
|
||||||
relayerAddress: string;
|
relayerAddress: string;
|
||||||
ethPrices?: {
|
|
||||||
[key in string]: string;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RelayerError = {
|
export type RelayerError = {
|
||||||
@ -55,7 +57,7 @@ export interface RelayerStatus {
|
|||||||
fast: number;
|
fast: number;
|
||||||
additionalProperties?: number;
|
additionalProperties?: number;
|
||||||
};
|
};
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
ethPrices?: {
|
ethPrices?: {
|
||||||
[key in string]: string;
|
[key in string]: string;
|
||||||
};
|
};
|
||||||
@ -105,14 +107,14 @@ export function parseSemanticVersion(version: string) {
|
|||||||
return groups as unknown as semanticVersion;
|
return groups as unknown as semanticVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isRelayerUpdated(relayerVersion: string, netId: number | string) {
|
export function isRelayerUpdated(relayerVersion: string, netId: NetIdType) {
|
||||||
const { major, patch, prerelease } = parseSemanticVersion(relayerVersion);
|
const { major, patch, prerelease } = parseSemanticVersion(relayerVersion);
|
||||||
// Save backwards compatibility with V4 relayers for Ethereum Mainnet
|
// Save backwards compatibility with V4 relayers for Ethereum Mainnet
|
||||||
const requiredMajor = netId === 1 ? '4' : '5';
|
const requiredMajor = netId === NetId.MAINNET ? '4' : '5';
|
||||||
const isUpdatedMajor = major === requiredMajor;
|
const isUpdatedMajor = major === requiredMajor;
|
||||||
|
|
||||||
if (prerelease) return false;
|
if (prerelease) return false;
|
||||||
return isUpdatedMajor && (Number(patch) >= 5 || Number(netId) !== 1); // Patch checking - also backwards compatibility for Mainnet
|
return isUpdatedMajor && (Number(patch) >= 5 || netId !== NetId.MAINNET); // Patch checking - also backwards compatibility for Mainnet
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calculateScore({ stakeBalance, tornadoServiceFee }: RelayerInfo, minFee = 0.33, maxFee = 0.53) {
|
export function calculateScore({ stakeBalance, tornadoServiceFee }: RelayerInfo, minFee = 0.33, maxFee = 0.53) {
|
||||||
@ -139,10 +141,28 @@ export function getWeightRandom(weightsScores: bigint[], random: bigint) {
|
|||||||
return Math.floor(Math.random() * weightsScores.length);
|
return Math.floor(Math.random() * weightsScores.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pickWeightedRandomRelayer(relayers: RelayerInfo[], netId: string | number) {
|
export type RelayerInstanceList = {
|
||||||
|
[key in string]: {
|
||||||
|
instanceAddress: {
|
||||||
|
[key in string]: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getSupportedInstances(instanceList: RelayerInstanceList) {
|
||||||
|
const rawList = Object.values(instanceList)
|
||||||
|
.map(({ instanceAddress }) => {
|
||||||
|
return Object.values(instanceAddress);
|
||||||
|
})
|
||||||
|
.flat();
|
||||||
|
|
||||||
|
return rawList.map((l) => getAddress(l));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pickWeightedRandomRelayer(relayers: RelayerInfo[], netId: NetIdType) {
|
||||||
let minFee: number, maxFee: number;
|
let minFee: number, maxFee: number;
|
||||||
|
|
||||||
if (Number(netId) !== 1) {
|
if (netId !== NetId.MAINNET) {
|
||||||
minFee = 0.01;
|
minFee = 0.01;
|
||||||
maxFee = 0.3;
|
maxFee = 0.3;
|
||||||
}
|
}
|
||||||
@ -159,7 +179,7 @@ export function pickWeightedRandomRelayer(relayers: RelayerInfo[], netId: string
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RelayerClientConstructor {
|
export interface RelayerClientConstructor {
|
||||||
netId: number | string;
|
netId: NetIdType;
|
||||||
config: Config;
|
config: Config;
|
||||||
Aggregator: Aggregator;
|
Aggregator: Aggregator;
|
||||||
fetchDataOptions?: fetchDataOptions;
|
fetchDataOptions?: fetchDataOptions;
|
||||||
@ -170,14 +190,14 @@ export type RelayerClientWithdraw = snarkProofs & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class RelayerClient {
|
export class RelayerClient {
|
||||||
netId: number;
|
netId: NetIdType;
|
||||||
config: Config;
|
config: Config;
|
||||||
Aggregator: Aggregator;
|
Aggregator: Aggregator;
|
||||||
selectedRelayer?: Relayer;
|
selectedRelayer?: Relayer;
|
||||||
fetchDataOptions?: fetchDataOptions;
|
fetchDataOptions?: fetchDataOptions;
|
||||||
|
|
||||||
constructor({ netId, config, Aggregator, fetchDataOptions }: RelayerClientConstructor) {
|
constructor({ netId, config, Aggregator, fetchDataOptions }: RelayerClientConstructor) {
|
||||||
this.netId = Number(netId);
|
this.netId = netId;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.Aggregator = Aggregator;
|
this.Aggregator = Aggregator;
|
||||||
this.fetchDataOptions = fetchDataOptions;
|
this.fetchDataOptions = fetchDataOptions;
|
||||||
@ -220,7 +240,7 @@ export class RelayerClient {
|
|||||||
throw new Error('This relayer serves a different network');
|
throw new Error('This relayer serves a different network');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (relayerAddress && this.netId === 1 && status.rewardAccount !== relayerAddress) {
|
if (relayerAddress && this.netId === NetId.MAINNET && status.rewardAccount !== relayerAddress) {
|
||||||
throw new Error('The Relayer reward address must match registered address');
|
throw new Error('The Relayer reward address must match registered address');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +283,9 @@ export class RelayerClient {
|
|||||||
ensName,
|
ensName,
|
||||||
stakeBalance,
|
stakeBalance,
|
||||||
relayerAddress,
|
relayerAddress,
|
||||||
rewardAccount: status.rewardAccount,
|
rewardAccount: getAddress(status.rewardAccount),
|
||||||
|
instances: getSupportedInstances(status.instances),
|
||||||
|
gasPrice: status.gasPrices?.fast,
|
||||||
ethPrices: status.ethPrices,
|
ethPrices: status.ethPrices,
|
||||||
currentQueue: status.currentQueue,
|
currentQueue: status.currentQueue,
|
||||||
tornadoServiceFee: status.tornadoServiceFee,
|
tornadoServiceFee: status.tornadoServiceFee,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Config } from '../networkConfig';
|
import { Config, NetId, NetIdType } from '../networkConfig';
|
||||||
|
|
||||||
export type statusInstanceType = {
|
export type statusInstanceType = {
|
||||||
type: string;
|
type: string;
|
||||||
@ -110,7 +110,7 @@ const statusSchema: statusSchema = {
|
|||||||
required: ['rewardAccount', 'instances', 'netId', 'tornadoServiceFee', 'version', 'health'],
|
required: ['rewardAccount', 'instances', 'netId', 'tornadoServiceFee', 'version', 'health'],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getStatusSchema(netId: number | string, config: Config) {
|
export function getStatusSchema(netId: NetIdType, config: Config) {
|
||||||
const { tokens, optionalTokens = [], nativeCurrency } = config;
|
const { tokens, optionalTokens = [], nativeCurrency } = config;
|
||||||
|
|
||||||
// deep copy schema
|
// deep copy schema
|
||||||
@ -162,7 +162,7 @@ export function getStatusSchema(netId: number | string, config: Config) {
|
|||||||
|
|
||||||
schema.properties.instances = instances;
|
schema.properties.instances = instances;
|
||||||
|
|
||||||
if (Number(netId) === 1) {
|
if (netId === NetId.MAINNET) {
|
||||||
const _tokens = Object.keys(tokens).filter((t) => t !== nativeCurrency);
|
const _tokens = Object.keys(tokens).filter((t) => t !== nativeCurrency);
|
||||||
|
|
||||||
const ethPrices: statusEthPricesType = {
|
const ethPrices: statusEthPricesType = {
|
||||||
|
113
src/services/treeCache.ts
Normal file
113
src/services/treeCache.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/**
|
||||||
|
* Create tree cache file from node.js
|
||||||
|
*
|
||||||
|
* Only works for node.js, modified from https://github.com/tornadocash/tornado-classic-ui/blob/master/scripts/updateTree.js
|
||||||
|
*/
|
||||||
|
import { MerkleTree } from '@tornado/fixed-merkle-tree';
|
||||||
|
import BloomFilter from 'bloomfilter.js';
|
||||||
|
import { saveUserFile } from './data';
|
||||||
|
import { DepositsEvents } from './events';
|
||||||
|
import type { NetIdType } from './networkConfig';
|
||||||
|
|
||||||
|
export interface TreeCacheConstructor {
|
||||||
|
netId: NetIdType;
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
userDirectory: string;
|
||||||
|
PARTS_COUNT?: number;
|
||||||
|
LEAVES?: number;
|
||||||
|
zeroElement?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface treeMetadata {
|
||||||
|
blockNumber: number;
|
||||||
|
logIndex: number;
|
||||||
|
transactionHash: string;
|
||||||
|
timestamp: number;
|
||||||
|
from: string;
|
||||||
|
leafIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TreeCache {
|
||||||
|
netId: NetIdType;
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
userDirectory: string;
|
||||||
|
|
||||||
|
PARTS_COUNT: number;
|
||||||
|
|
||||||
|
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT = 4 }: TreeCacheConstructor) {
|
||||||
|
this.netId = netId;
|
||||||
|
this.amount = amount;
|
||||||
|
this.currency = currency;
|
||||||
|
this.userDirectory = userDirectory;
|
||||||
|
|
||||||
|
this.PARTS_COUNT = PARTS_COUNT;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInstanceName(): string {
|
||||||
|
return `deposits_${this.netId}_${this.currency}_${this.amount}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createTree(events: DepositsEvents[], tree: MerkleTree) {
|
||||||
|
const bloom = new BloomFilter(events.length);
|
||||||
|
|
||||||
|
console.log(`Creating cached tree for ${this.getInstanceName()}\n`);
|
||||||
|
|
||||||
|
// events indexed by commitment
|
||||||
|
const eventsData = events.reduce(
|
||||||
|
(acc, { leafIndex, commitment, ...rest }, i) => {
|
||||||
|
if (leafIndex !== i) {
|
||||||
|
throw new Error(`leafIndex (${leafIndex}) !== i (${i})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[commitment] = { ...rest, leafIndex };
|
||||||
|
|
||||||
|
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,
|
||||||
|
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({
|
||||||
|
fileName,
|
||||||
|
userDirectory: this.userDirectory,
|
||||||
|
dataString,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { URL } from 'url';
|
import { webcrypto } from 'crypto';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import type { BigNumberish } from 'ethers';
|
import type { BigNumberish } from 'ethers';
|
||||||
|
|
||||||
@ -16,6 +16,8 @@ export const isNode =
|
|||||||
}
|
}
|
||||||
).browser && typeof globalThis.window === 'undefined';
|
).browser && typeof globalThis.window === 'undefined';
|
||||||
|
|
||||||
|
export const crypto = isNode ? webcrypto : (globalThis.crypto as typeof webcrypto);
|
||||||
|
|
||||||
export const chunk = <T>(arr: T[], size: number): T[][] =>
|
export const chunk = <T>(arr: T[], size: number): T[][] =>
|
||||||
[...Array(Math.ceil(arr.length / size))].map((_, i) => arr.slice(size * i, size + size * i));
|
[...Array(Math.ceil(arr.length / size))].map((_, i) => arr.slice(size * i, size + size * i));
|
||||||
|
|
||||||
@ -35,26 +37,28 @@ export function validateUrl(url: string, protocols?: string[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
|
||||||
|
const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
|
||||||
|
const merged = new Uint8Array(totalSize);
|
||||||
|
|
||||||
|
arrays.forEach((array, i, arrays) => {
|
||||||
|
const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
|
||||||
|
merged.set(array, offset);
|
||||||
|
});
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
export function bufferToBytes(b: Buffer) {
|
export function bufferToBytes(b: Buffer) {
|
||||||
return new Uint8Array(b.buffer);
|
return new Uint8Array(b.buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bytesToBase64(bytes: Uint8Array) {
|
export function bytesToBase64(bytes: Uint8Array) {
|
||||||
let binary = '';
|
return btoa(String.fromCharCode.apply(null, Array.from(bytes)));
|
||||||
const len = bytes.byteLength;
|
|
||||||
for (let i = 0; i < len; ++i) {
|
|
||||||
binary += String.fromCharCode(bytes[i]);
|
|
||||||
}
|
|
||||||
return btoa(binary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function base64ToBytes(base64: string) {
|
export function base64ToBytes(base64: string) {
|
||||||
const binaryString = atob(base64);
|
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
|
||||||
const bytes = new Uint8Array(binaryString.length);
|
|
||||||
for (let i = 0; i < binaryString.length; i++) {
|
|
||||||
bytes[i] = binaryString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function bytesToHex(bytes: Uint8Array) {
|
export function bytesToHex(bytes: Uint8Array) {
|
||||||
@ -66,6 +70,16 @@ export function bytesToHex(bytes: Uint8Array) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hexToBytes(hexString: string) {
|
||||||
|
if (hexString.slice(0, 2) === '0x') {
|
||||||
|
hexString = hexString.replace('0x', '');
|
||||||
|
}
|
||||||
|
if (hexString.length % 2 !== 0) {
|
||||||
|
hexString = '0' + hexString;
|
||||||
|
}
|
||||||
|
return Uint8Array.from((hexString.match(/.{1,2}/g) as string[]).map((byte) => parseInt(byte, 16)));
|
||||||
|
}
|
||||||
|
|
||||||
// Convert BE encoded bytes (Buffer | Uint8Array) array to BigInt
|
// Convert BE encoded bytes (Buffer | Uint8Array) array to BigInt
|
||||||
export function bytesToBN(bytes: Uint8Array) {
|
export function bytesToBN(bytes: Uint8Array) {
|
||||||
return BigInt(bytesToHex(bytes));
|
return BigInt(bytesToHex(bytes));
|
||||||
|
25
src/types/bloomfilter.js.d.ts
vendored
Normal file
25
src/types/bloomfilter.js.d.ts
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
declare module 'bloomfilter.js' {
|
||||||
|
export default class BloomFilter {
|
||||||
|
m: number;
|
||||||
|
k: number;
|
||||||
|
size: number;
|
||||||
|
bitview: any;
|
||||||
|
|
||||||
|
constructor(n: number, false_postive_tolerance?: number);
|
||||||
|
|
||||||
|
calculateHash(x: number, m: number, i: number): number;
|
||||||
|
|
||||||
|
test(data: any): boolean;
|
||||||
|
|
||||||
|
add(data: any): void;
|
||||||
|
|
||||||
|
bytelength(): number;
|
||||||
|
|
||||||
|
view(): Uint8Array;
|
||||||
|
|
||||||
|
serialize(): string;
|
||||||
|
|
||||||
|
deserialize(serialized: string): BloomFilter;
|
||||||
|
}
|
||||||
|
}
|
BIN
static/events/echo_1.json.zip
Normal file
BIN
static/events/echo_1.json.zip
Normal file
Binary file not shown.
BIN
static/events/echo_10.json.zip
Normal file
BIN
static/events/echo_10.json.zip
Normal file
Binary file not shown.
BIN
static/events/echo_100.json.zip
Normal file
BIN
static/events/echo_100.json.zip
Normal file
Binary file not shown.
BIN
static/events/echo_11155111.json.zip
Normal file
BIN
static/events/echo_11155111.json.zip
Normal file
Binary file not shown.
BIN
static/events/echo_137.json.zip
Normal file
BIN
static/events/echo_137.json.zip
Normal file
Binary file not shown.
BIN
static/events/echo_42161.json.zip
Normal file
BIN
static/events/echo_42161.json.zip
Normal file
Binary file not shown.
BIN
static/events/echo_43114.json.zip
Normal file
BIN
static/events/echo_43114.json.zip
Normal file
Binary file not shown.
BIN
static/events/echo_56.json.zip
Normal file
BIN
static/events/echo_56.json.zip
Normal file
Binary file not shown.
Binary file not shown.
2
static/merkleTreeWorker.js
vendored
2
static/merkleTreeWorker.js
vendored
@ -19824,5 +19824,5 @@ if (isNode && threads) {
|
|||||||
postMessage(merkleTree.toString());
|
postMessage(merkleTree.toString());
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
throw new Error("This browser / environment doesn't support workers!");
|
throw new Error("This browser / environment does not support workers!");
|
||||||
}
|
}
|
||||||
|
219526
static/merkleTreeWorker.umd.js
vendored
219526
static/merkleTreeWorker.umd.js
vendored
File diff suppressed because one or more lines are too long
@ -2,6 +2,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"./node_modules/@types",
|
"./node_modules/@types",
|
||||||
|
"./src/types",
|
||||||
],
|
],
|
||||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||||
|
|
||||||
|
100
webpack.config.js
Normal file
100
webpack.config.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
const esbuild = require('esbuild');
|
||||||
|
const path = require('path');
|
||||||
|
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
|
||||||
|
|
||||||
|
const esbuildLoader = {
|
||||||
|
test: /\.ts?$/,
|
||||||
|
loader: 'esbuild-loader',
|
||||||
|
options: {
|
||||||
|
loader: 'ts',
|
||||||
|
target: 'es2016',
|
||||||
|
implementation: esbuild
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonAlias = {
|
||||||
|
fs: false,
|
||||||
|
'fs/promises': false,
|
||||||
|
'path': false,
|
||||||
|
'url': false,
|
||||||
|
'worker_threads': false,
|
||||||
|
'fflate': 'fflate/browser',
|
||||||
|
'@colors/colors': false,
|
||||||
|
'cli-table3': false,
|
||||||
|
'commander': false,
|
||||||
|
'http-proxy-agent': false,
|
||||||
|
'https-proxy-agent': false,
|
||||||
|
'socks-proxy-agent': false,
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
mode: 'production',
|
||||||
|
module: {
|
||||||
|
rules: [esbuildLoader]
|
||||||
|
},
|
||||||
|
entry: './src/index.ts',
|
||||||
|
output: {
|
||||||
|
filename: 'index.umd.js',
|
||||||
|
path: path.resolve(__dirname, './dist'),
|
||||||
|
library: 'Tornado',
|
||||||
|
libraryTarget: 'umd'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NodePolyfillPlugin(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
alias: {
|
||||||
|
...commonAlias,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: 'production',
|
||||||
|
module: {
|
||||||
|
rules: [esbuildLoader]
|
||||||
|
},
|
||||||
|
entry: './src/cli.ts',
|
||||||
|
output: {
|
||||||
|
filename: 'cli.js',
|
||||||
|
path: path.resolve(__dirname, './dist'),
|
||||||
|
},
|
||||||
|
target: 'node',
|
||||||
|
plugins: [],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
alias: {}
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
mode: 'production',
|
||||||
|
module: {
|
||||||
|
rules: [esbuildLoader]
|
||||||
|
},
|
||||||
|
entry: './src/merkleTreeWorker.ts',
|
||||||
|
output: {
|
||||||
|
filename: 'merkleTreeWorker.umd.js',
|
||||||
|
path: path.resolve(__dirname, './static'),
|
||||||
|
libraryTarget: 'umd'
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new NodePolyfillPlugin(),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.tsx', '.ts', '.js'],
|
||||||
|
alias: {
|
||||||
|
...commonAlias,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
optimization: {
|
||||||
|
minimize: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
Loading…
x
Reference in New Issue
Block a user