Add BaseMultiTornadoService

This commit is contained in:
Tornado Contrib 2024-11-19 05:04:13 +00:00
parent b65998fca5
commit 72c6b92b07
Signed by: tornadocontrib
GPG Key ID: 60B4DF1A076C64B1
14 changed files with 1111 additions and 51 deletions

37
dist/events/base.d.ts vendored

@ -1,5 +1,6 @@
import { BaseContract, Provider, EventLog } from 'ethers';
import { Tornado, TornadoRouter, TornadoProxyLight, Governance, RelayerRegistry, Echoer, Aggregator } from '@tornado/contracts';
import type { MerkleTree } from '@tornado/fixed-merkle-tree';
import { BatchEventsService, BatchBlockService, BatchTransactionService, BatchEventOnProgress, BatchBlockOnProgress } from '../batch';
import { fetchDataOptions } from '../providers';
import { type NetIdType, type SubdomainMap } from '../networkConfig';
@ -7,7 +8,8 @@ import { RelayerParams } from '../relayerClient';
import type { TovarishClient } from '../tovarishClient';
import type { ReverseRecords } from '../typechain';
import type { MerkleTreeService } from '../merkleTree';
import type { BaseEvents, CachedEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, GovernanceProposalCreatedEvents, GovernanceVotedEvents, EchoEvents, AllRelayerRegistryEvents, StakeBurnedEvents } from './types';
import type { DepositType } from '../deposits';
import type { BaseEvents, CachedEvents, MinimalEvents, DepositsEvents, WithdrawalsEvents, EncryptedNotesEvents, AllGovernanceEvents, GovernanceProposalCreatedEvents, GovernanceVotedEvents, EchoEvents, AllRelayerRegistryEvents, StakeBurnedEvents, MultiDepositsEvents, MultiWithdrawalsEvents } from './types';
export interface BaseEventsServiceConstructor {
netId: NetIdType;
provider: Provider;
@ -56,7 +58,9 @@ export declare class BaseEventsService<EventType extends MinimalEvents> {
/**
* Handle saving events
*/
saveEvents({ events, lastBlock }: BaseEvents<EventType>): Promise<void>;
saveEvents({ events, newEvents, lastBlock }: BaseEvents<EventType> & {
newEvents: EventType[];
}): Promise<void>;
/**
* Trigger saving and receiving latest events
*/
@ -90,6 +94,35 @@ export declare class BaseTornadoService extends BaseEventsService<DepositsEvents
fromBlock: number;
}): Promise<BaseEvents<DepositsEvents | WithdrawalsEvents>>;
}
export interface BaseMultiTornadoServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
instances: {
[key in string]: DepositType;
};
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
}
export declare class BaseMultiTornadoService extends BaseEventsService<MultiDepositsEvents | MultiWithdrawalsEvents> {
instances: {
[key in string]: DepositType;
};
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
batchTransactionService: BatchTransactionService;
batchBlockService: BatchBlockService;
constructor(serviceConstructor: BaseMultiTornadoServiceConstructor);
getInstanceName(): string;
getTovarishType(): string;
formatEvents(events: EventLog[]): Promise<(MultiDepositsEvents | MultiWithdrawalsEvents)[]>;
validateEvents<S>({ events, newEvents, }: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}): Promise<S>;
getEvents(instanceAddress: string): Promise<{
depositEvents: MultiDepositsEvents[];
withdrawalEvents: MultiWithdrawalsEvents[];
tree: MerkleTree | undefined;
lastBlock: number;
}>;
}
export interface BaseEchoServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Echoer: Echoer;
}

@ -83,6 +83,15 @@ export interface WithdrawalsEvents extends MinimalEvents {
fee: string;
timestamp: number;
}
export interface BaseMultiTornadoEvents {
event: string;
instanceAddress: string;
}
export interface MultiDepositsEvents extends BaseMultiTornadoEvents, DepositsEvents {
}
export interface MultiWithdrawalsEvents extends BaseMultiTornadoEvents, WithdrawalsEvents {
relayerAddress: string;
}
export interface EchoEvents extends MinimalEvents {
address: string;
encryptedAccount: string;

230
dist/index.js vendored

@ -1426,7 +1426,7 @@ function getActiveTokenInstances(config) {
}
function getInstanceByAddress(config, address) {
const { tokens, disabledTokens } = config;
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
for (const [currency, { instanceAddress, tokenAddress, symbol, decimals }] of Object.entries(tokens)) {
if (disabledTokens?.includes(currency)) {
continue;
}
@ -1434,7 +1434,10 @@ function getInstanceByAddress(config, address) {
if (instance === address) {
return {
amount,
currency
currency,
symbol,
decimals,
tokenAddress
};
}
}
@ -1447,6 +1450,21 @@ function getRelayerEnsSubdomains() {
return acc;
}, {});
}
function getMultiInstances(netId, config) {
return Object.entries(config.tokens).reduce(
(acc, [currency, { instanceAddress }]) => {
Object.entries(instanceAddress).forEach(([amount, contractAddress]) => {
acc[contractAddress] = {
currency,
amount,
netId
};
});
return acc;
},
{}
);
}
const ajv = new Ajv({ allErrors: true });
ajv.addKeyword({
@ -1684,6 +1702,61 @@ const withdrawalsEventsSchema = {
additionalProperties: false
}
};
const tornadoEventsSchema = {
type: "array",
items: {
anyOf: [
// depositsEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
instanceAddress: { type: "string" },
commitment: bytes32SchemaType,
leafIndex: { type: "number" },
timestamp: { type: "number" },
from: addressSchemaType
},
required: [
...baseEventsSchemaRequired,
"event",
"instanceAddress",
"commitment",
"leafIndex",
"timestamp",
"from"
],
additionalProperties: false
},
// withdrawalEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
instanceAddress: { type: "string" },
nullifierHash: bytes32SchemaType,
to: addressSchemaType,
relayerAddress: addressSchemaType,
fee: bnSchemaType,
timestamp: { type: "number" }
},
required: [
...baseEventsSchemaRequired,
"event",
"instanceAddress",
"nullifierHash",
"to",
"relayerAddress",
"fee",
"timestamp"
],
additionalProperties: false
}
]
}
};
const echoEventsSchema = {
type: "array",
items: {
@ -1710,6 +1783,9 @@ const encryptedNotesSchema = {
}
};
function getEventsSchemaValidator(type) {
if (type === "tornado") {
return ajv.compile(tornadoEventsSchema);
}
if (type === "deposit") {
return ajv.compile(depositsEventsSchema);
}
@ -2226,7 +2302,7 @@ class BaseEventsService {
* Handle saving events
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveEvents({ events, lastBlock }) {
async saveEvents({ events, newEvents, lastBlock }) {
}
/**
* Trigger saving and receiving latest events
@ -2257,7 +2333,7 @@ class BaseEventsService {
lastBlock
});
if (savedEvents.fromCache || newEvents.events.length) {
await this.saveEvents({ events: allEvents, lastBlock });
await this.saveEvents({ events: allEvents, newEvents: newEvents.events, lastBlock });
}
return {
events: allEvents,
@ -2368,6 +2444,149 @@ class BaseTornadoService extends BaseEventsService {
});
}
}
class BaseMultiTornadoService extends BaseEventsService {
instances;
optionalTree;
merkleTreeService;
batchTransactionService;
batchBlockService;
constructor(serviceConstructor) {
const { instances, provider, optionalTree, merkleTreeService } = serviceConstructor;
const contract = merkleTreeService?.Tornado || contracts.Tornado__factory.connect(Object.keys(instances)[0], provider);
super({
...serviceConstructor,
contract,
type: "*"
});
this.batchEventsService = new BatchEventsService({
provider,
contract,
address: Object.keys(instances),
onProgress: this.updateEventProgress
});
this.instances = instances;
this.optionalTree = optionalTree;
this.merkleTreeService = merkleTreeService;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress
});
this.batchBlockService = new BatchBlockService({
provider,
onProgress: this.updateBlockProgress
});
}
getInstanceName() {
return `tornado_${this.netId}`;
}
getTovarishType() {
return "tornado";
}
async formatEvents(events) {
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(
events.filter(({ eventName }) => eventName === "Deposit").map(({ transactionHash }) => transactionHash)
)
]);
const blocks = await this.batchBlockService.getBatchBlocks([
...new Set(
events.filter(({ eventName }) => eventName === "Withdrawal").map(({ blockNumber }) => blockNumber)
)
]);
return events.map(
({
address: instanceAddress,
blockNumber,
index: logIndex,
transactionHash,
args,
eventName: event
}) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
event,
instanceAddress
};
if (event === "Deposit") {
const { commitment, leafIndex, timestamp } = args;
return {
...eventObjects,
commitment,
leafIndex: Number(leafIndex),
timestamp: Number(timestamp),
from: txs.find(({ hash }) => hash === transactionHash)?.from || ""
};
}
if (event === "Withdrawal") {
const { nullifierHash, to, relayer: relayerAddress, fee } = args;
return {
...eventObjects,
logIndex,
transactionHash,
nullifierHash: String(nullifierHash),
to,
relayerAddress,
fee: String(fee),
timestamp: blocks.find(({ number }) => number === blockNumber)?.timestamp || 0
};
}
}
).filter((e) => e);
}
async validateEvents({
events,
newEvents
}) {
const instancesWithNewEvents = [
...new Set(
newEvents.filter(({ event }) => event === "Deposit").map(({ instanceAddress }) => instanceAddress)
)
];
let tree;
const requiredTree = this.merkleTreeService?.Tornado?.target;
if (requiredTree && !instancesWithNewEvents.includes(requiredTree)) {
instancesWithNewEvents.push(requiredTree);
}
for (const instance of instancesWithNewEvents) {
const depositEvents = events.filter(
({ instanceAddress, event }) => instanceAddress === instance && event === "Deposit"
);
const lastEvent = depositEvents[depositEvents.length - 1];
if (lastEvent.leafIndex !== depositEvents.length - 1) {
const errMsg = `Invalid deposit events for ${instance} wants ${depositEvents.length - 1} leafIndex have ${lastEvent.leafIndex}`;
throw new Error(errMsg);
}
if (requiredTree === instance && !this.optionalTree) {
tree = await this.merkleTreeService?.verifyTree(depositEvents);
}
}
return tree;
}
async getEvents(instanceAddress) {
const { events, validateResult: tree, lastBlock } = await this.updateEvents();
const { depositEvents, withdrawalEvents } = events.reduce(
(acc, curr) => {
if (curr.instanceAddress === instanceAddress) {
if (curr.event === "Deposit") {
acc.depositEvents.push(curr);
} else if (curr.event === "Withdrawal") {
acc.withdrawalEvents.push(curr);
}
}
return acc;
},
{}
);
return {
depositEvents,
withdrawalEvents,
tree,
lastBlock
};
}
}
class BaseEchoService extends BaseEventsService {
constructor(serviceConstructor) {
super({
@ -10411,6 +10630,7 @@ exports.BaseEchoService = BaseEchoService;
exports.BaseEncryptedNotesService = BaseEncryptedNotesService;
exports.BaseEventsService = BaseEventsService;
exports.BaseGovernanceService = BaseGovernanceService;
exports.BaseMultiTornadoService = BaseMultiTornadoService;
exports.BaseRegistryService = BaseRegistryService;
exports.BaseRevenueService = BaseRevenueService;
exports.BaseTornadoService = BaseTornadoService;
@ -10502,6 +10722,7 @@ exports.getEventsSchemaValidator = getEventsSchemaValidator;
exports.getHttpAgent = getHttpAgent;
exports.getIndexedDB = getIndexedDB;
exports.getInstanceByAddress = getInstanceByAddress;
exports.getMultiInstances = getMultiInstances;
exports.getNetworkConfig = getNetworkConfig;
exports.getPermit2CommitmentsSignature = getPermit2CommitmentsSignature;
exports.getPermit2Signature = getPermit2Signature;
@ -10553,6 +10774,7 @@ exports.substring = substring;
exports.toContentHash = toContentHash;
exports.toFixedHex = toFixedHex;
exports.toFixedLength = toFixedLength;
exports.tornadoEventsSchema = tornadoEventsSchema;
exports.unpackEncryptedMessage = unpackEncryptedMessage;
exports.unzipAsync = unzipAsync;
exports.validateUrl = validateUrl;

229
dist/index.mjs vendored

@ -1404,7 +1404,7 @@ function getActiveTokenInstances(config) {
}
function getInstanceByAddress(config, address) {
const { tokens, disabledTokens } = config;
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
for (const [currency, { instanceAddress, tokenAddress, symbol, decimals }] of Object.entries(tokens)) {
if (disabledTokens?.includes(currency)) {
continue;
}
@ -1412,7 +1412,10 @@ function getInstanceByAddress(config, address) {
if (instance === address) {
return {
amount,
currency
currency,
symbol,
decimals,
tokenAddress
};
}
}
@ -1425,6 +1428,21 @@ function getRelayerEnsSubdomains() {
return acc;
}, {});
}
function getMultiInstances(netId, config) {
return Object.entries(config.tokens).reduce(
(acc, [currency, { instanceAddress }]) => {
Object.entries(instanceAddress).forEach(([amount, contractAddress]) => {
acc[contractAddress] = {
currency,
amount,
netId
};
});
return acc;
},
{}
);
}
const ajv = new Ajv({ allErrors: true });
ajv.addKeyword({
@ -1662,6 +1680,61 @@ const withdrawalsEventsSchema = {
additionalProperties: false
}
};
const tornadoEventsSchema = {
type: "array",
items: {
anyOf: [
// depositsEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
instanceAddress: { type: "string" },
commitment: bytes32SchemaType,
leafIndex: { type: "number" },
timestamp: { type: "number" },
from: addressSchemaType
},
required: [
...baseEventsSchemaRequired,
"event",
"instanceAddress",
"commitment",
"leafIndex",
"timestamp",
"from"
],
additionalProperties: false
},
// withdrawalEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
instanceAddress: { type: "string" },
nullifierHash: bytes32SchemaType,
to: addressSchemaType,
relayerAddress: addressSchemaType,
fee: bnSchemaType,
timestamp: { type: "number" }
},
required: [
...baseEventsSchemaRequired,
"event",
"instanceAddress",
"nullifierHash",
"to",
"relayerAddress",
"fee",
"timestamp"
],
additionalProperties: false
}
]
}
};
const echoEventsSchema = {
type: "array",
items: {
@ -1688,6 +1761,9 @@ const encryptedNotesSchema = {
}
};
function getEventsSchemaValidator(type) {
if (type === "tornado") {
return ajv.compile(tornadoEventsSchema);
}
if (type === "deposit") {
return ajv.compile(depositsEventsSchema);
}
@ -2204,7 +2280,7 @@ class BaseEventsService {
* Handle saving events
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveEvents({ events, lastBlock }) {
async saveEvents({ events, newEvents, lastBlock }) {
}
/**
* Trigger saving and receiving latest events
@ -2235,7 +2311,7 @@ class BaseEventsService {
lastBlock
});
if (savedEvents.fromCache || newEvents.events.length) {
await this.saveEvents({ events: allEvents, lastBlock });
await this.saveEvents({ events: allEvents, newEvents: newEvents.events, lastBlock });
}
return {
events: allEvents,
@ -2346,6 +2422,149 @@ class BaseTornadoService extends BaseEventsService {
});
}
}
class BaseMultiTornadoService extends BaseEventsService {
instances;
optionalTree;
merkleTreeService;
batchTransactionService;
batchBlockService;
constructor(serviceConstructor) {
const { instances, provider, optionalTree, merkleTreeService } = serviceConstructor;
const contract = merkleTreeService?.Tornado || Tornado__factory.connect(Object.keys(instances)[0], provider);
super({
...serviceConstructor,
contract,
type: "*"
});
this.batchEventsService = new BatchEventsService({
provider,
contract,
address: Object.keys(instances),
onProgress: this.updateEventProgress
});
this.instances = instances;
this.optionalTree = optionalTree;
this.merkleTreeService = merkleTreeService;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress
});
this.batchBlockService = new BatchBlockService({
provider,
onProgress: this.updateBlockProgress
});
}
getInstanceName() {
return `tornado_${this.netId}`;
}
getTovarishType() {
return "tornado";
}
async formatEvents(events) {
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(
events.filter(({ eventName }) => eventName === "Deposit").map(({ transactionHash }) => transactionHash)
)
]);
const blocks = await this.batchBlockService.getBatchBlocks([
...new Set(
events.filter(({ eventName }) => eventName === "Withdrawal").map(({ blockNumber }) => blockNumber)
)
]);
return events.map(
({
address: instanceAddress,
blockNumber,
index: logIndex,
transactionHash,
args,
eventName: event
}) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
event,
instanceAddress
};
if (event === "Deposit") {
const { commitment, leafIndex, timestamp } = args;
return {
...eventObjects,
commitment,
leafIndex: Number(leafIndex),
timestamp: Number(timestamp),
from: txs.find(({ hash }) => hash === transactionHash)?.from || ""
};
}
if (event === "Withdrawal") {
const { nullifierHash, to, relayer: relayerAddress, fee } = args;
return {
...eventObjects,
logIndex,
transactionHash,
nullifierHash: String(nullifierHash),
to,
relayerAddress,
fee: String(fee),
timestamp: blocks.find(({ number }) => number === blockNumber)?.timestamp || 0
};
}
}
).filter((e) => e);
}
async validateEvents({
events,
newEvents
}) {
const instancesWithNewEvents = [
...new Set(
newEvents.filter(({ event }) => event === "Deposit").map(({ instanceAddress }) => instanceAddress)
)
];
let tree;
const requiredTree = this.merkleTreeService?.Tornado?.target;
if (requiredTree && !instancesWithNewEvents.includes(requiredTree)) {
instancesWithNewEvents.push(requiredTree);
}
for (const instance of instancesWithNewEvents) {
const depositEvents = events.filter(
({ instanceAddress, event }) => instanceAddress === instance && event === "Deposit"
);
const lastEvent = depositEvents[depositEvents.length - 1];
if (lastEvent.leafIndex !== depositEvents.length - 1) {
const errMsg = `Invalid deposit events for ${instance} wants ${depositEvents.length - 1} leafIndex have ${lastEvent.leafIndex}`;
throw new Error(errMsg);
}
if (requiredTree === instance && !this.optionalTree) {
tree = await this.merkleTreeService?.verifyTree(depositEvents);
}
}
return tree;
}
async getEvents(instanceAddress) {
const { events, validateResult: tree, lastBlock } = await this.updateEvents();
const { depositEvents, withdrawalEvents } = events.reduce(
(acc, curr) => {
if (curr.instanceAddress === instanceAddress) {
if (curr.event === "Deposit") {
acc.depositEvents.push(curr);
} else if (curr.event === "Withdrawal") {
acc.withdrawalEvents.push(curr);
}
}
return acc;
},
{}
);
return {
depositEvents,
withdrawalEvents,
tree,
lastBlock
};
}
}
class BaseEchoService extends BaseEventsService {
constructor(serviceConstructor) {
super({
@ -10385,4 +10604,4 @@ async function calculateSnarkProof(input, circuit, provingKey) {
return { proof, args };
}
export { BaseEchoService, BaseEncryptedNotesService, BaseEventsService, BaseGovernanceService, BaseRegistryService, BaseRevenueService, BaseTornadoService, BatchBlockService, BatchEventsService, BatchTransactionService, DBEchoService, DBEncryptedNotesService, DBGovernanceService, DBRegistryService, DBRevenueService, DBTornadoService, Deposit, ENSNameWrapper__factory, ENSRegistry__factory, ENSResolver__factory, ENSUtils, ENS__factory, ERC20__factory, EnsContracts, INDEX_DB_ERROR, IndexedDB, Invoice, MAX_FEE, MAX_TOVARISH_EVENTS, MIN_FEE, MIN_STAKE_BALANCE, MerkleTreeService, Mimc, Multicall__factory, NetId, NoteAccount, OffchainOracle__factory, OvmGasPriceOracle__factory, Pedersen, RelayerClient, ReverseRecords__factory, TokenPriceOracle, TornadoBrowserProvider, TornadoFeeOracle, TornadoRpcSigner, TornadoVoidSigner, TornadoWallet, TovarishClient, addNetwork, addressSchemaType, ajv, base64ToBytes, bigIntReplacer, bnSchemaType, bnToBytes, buffPedersenHash, bufferToBytes, bytes32BNSchemaType, bytes32SchemaType, bytesToBN, bytesToBase64, bytesToHex, calculateScore, calculateSnarkProof, chunk, concatBytes, convertETHToTokenAmount, createDeposit, crypto, customConfig, defaultConfig, defaultUserAgent, deployHasher, depositsEventsSchema, digest, downloadZip, echoEventsSchema, enabledChains, encodedLabelToLabelhash, encryptedNotesSchema, index as factories, fetchData, fetchGetUrlFunc, fetchIp, fromContentHash, gasZipID, gasZipInbounds, gasZipInput, gasZipMinMax, getActiveTokenInstances, getActiveTokens, getConfig, getEventsSchemaValidator, getHttpAgent, getIndexedDB, getInstanceByAddress, getNetworkConfig, getPermit2CommitmentsSignature, getPermit2Signature, getPermitCommitmentsSignature, getPermitSignature, getProvider, getProviderWithNetId, getRelayerEnsSubdomains, getStatusSchema, getSubInfo, getSupportedInstances, getTokenBalances, getTovarishNetworks, getWeightRandom, governanceEventsSchema, hasherBytecode, hexToBytes, initGroth16, isHex, isNode, jobRequestSchema, jobsSchema, labelhash, leBuff2Int, leInt2Buff, loadDBEvents, loadRemoteEvents, makeLabelNodeAndParent, mimc, multiQueryFilter, multicall, numberFormatter, packEncryptedMessage, parseInvoice, parseNote, pedersen, permit2Address, pickWeightedRandomRelayer, populateTransaction, proofSchemaType, proposalState, rBigInt, rHex, relayerRegistryEventsSchema, saveDBEvents, sleep, stakeBurnedEventsSchema, substring, toContentHash, toFixedHex, toFixedLength, unpackEncryptedMessage, unzipAsync, validateUrl, withdrawalsEventsSchema, zipAsync };
export { BaseEchoService, BaseEncryptedNotesService, BaseEventsService, BaseGovernanceService, BaseMultiTornadoService, BaseRegistryService, BaseRevenueService, BaseTornadoService, BatchBlockService, BatchEventsService, BatchTransactionService, DBEchoService, DBEncryptedNotesService, DBGovernanceService, DBRegistryService, DBRevenueService, DBTornadoService, Deposit, ENSNameWrapper__factory, ENSRegistry__factory, ENSResolver__factory, ENSUtils, ENS__factory, ERC20__factory, EnsContracts, INDEX_DB_ERROR, IndexedDB, Invoice, MAX_FEE, MAX_TOVARISH_EVENTS, MIN_FEE, MIN_STAKE_BALANCE, MerkleTreeService, Mimc, Multicall__factory, NetId, NoteAccount, OffchainOracle__factory, OvmGasPriceOracle__factory, Pedersen, RelayerClient, ReverseRecords__factory, TokenPriceOracle, TornadoBrowserProvider, TornadoFeeOracle, TornadoRpcSigner, TornadoVoidSigner, TornadoWallet, TovarishClient, addNetwork, addressSchemaType, ajv, base64ToBytes, bigIntReplacer, bnSchemaType, bnToBytes, buffPedersenHash, bufferToBytes, bytes32BNSchemaType, bytes32SchemaType, bytesToBN, bytesToBase64, bytesToHex, calculateScore, calculateSnarkProof, chunk, concatBytes, convertETHToTokenAmount, createDeposit, crypto, customConfig, defaultConfig, defaultUserAgent, deployHasher, depositsEventsSchema, digest, downloadZip, echoEventsSchema, enabledChains, encodedLabelToLabelhash, encryptedNotesSchema, index as factories, fetchData, fetchGetUrlFunc, fetchIp, fromContentHash, gasZipID, gasZipInbounds, gasZipInput, gasZipMinMax, getActiveTokenInstances, getActiveTokens, getConfig, getEventsSchemaValidator, getHttpAgent, getIndexedDB, getInstanceByAddress, getMultiInstances, getNetworkConfig, getPermit2CommitmentsSignature, getPermit2Signature, getPermitCommitmentsSignature, getPermitSignature, getProvider, getProviderWithNetId, getRelayerEnsSubdomains, getStatusSchema, getSubInfo, getSupportedInstances, getTokenBalances, getTovarishNetworks, getWeightRandom, governanceEventsSchema, hasherBytecode, hexToBytes, initGroth16, isHex, isNode, jobRequestSchema, jobsSchema, labelhash, leBuff2Int, leInt2Buff, loadDBEvents, loadRemoteEvents, makeLabelNodeAndParent, mimc, multiQueryFilter, multicall, numberFormatter, packEncryptedMessage, parseInvoice, parseNote, pedersen, permit2Address, pickWeightedRandomRelayer, populateTransaction, proofSchemaType, proposalState, rBigInt, rHex, relayerRegistryEventsSchema, saveDBEvents, sleep, stakeBurnedEventsSchema, substring, toContentHash, toFixedHex, toFixedLength, tornadoEventsSchema, unpackEncryptedMessage, unzipAsync, validateUrl, withdrawalsEventsSchema, zipAsync };

@ -1,3 +1,4 @@
import type { DepositType } from './deposits';
/**
* Type of default supported networks
*/
@ -114,5 +115,11 @@ export declare function getActiveTokenInstances(config: Config): TokenInstances;
export declare function getInstanceByAddress(config: Config, address: string): {
amount: string;
currency: string;
symbol: string;
decimals: number;
tokenAddress: string | undefined;
} | undefined;
export declare function getRelayerEnsSubdomains(): SubdomainMap;
export declare function getMultiInstances(netId: NetIdType, config: Config): {
[key in string]: DepositType;
};

@ -352,6 +352,92 @@ export declare const withdrawalsEventsSchema: {
readonly additionalProperties: false;
};
};
export declare const tornadoEventsSchema: {
readonly type: "array";
readonly items: {
readonly anyOf: readonly [{
readonly type: "object";
readonly properties: {
readonly event: {
readonly type: "string";
};
readonly instanceAddress: {
readonly type: "string";
};
readonly commitment: {
readonly type: "string";
readonly pattern: "^0x[a-fA-F0-9]{64}$";
};
readonly leafIndex: {
readonly type: "number";
};
readonly timestamp: {
readonly type: "number";
};
readonly from: {
readonly type: "string";
readonly pattern: "^0x[a-fA-F0-9]{40}$";
readonly isAddress: true;
};
readonly blockNumber: {
readonly type: "number";
};
readonly logIndex: {
readonly type: "number";
};
readonly transactionHash: {
readonly type: "string";
readonly pattern: "^0x[a-fA-F0-9]{64}$";
};
};
readonly required: readonly [...string[], "event", "instanceAddress", "commitment", "leafIndex", "timestamp", "from"];
readonly additionalProperties: false;
}, {
readonly type: "object";
readonly properties: {
readonly event: {
readonly type: "string";
};
readonly instanceAddress: {
readonly type: "string";
};
readonly nullifierHash: {
readonly type: "string";
readonly pattern: "^0x[a-fA-F0-9]{64}$";
};
readonly to: {
readonly type: "string";
readonly pattern: "^0x[a-fA-F0-9]{40}$";
readonly isAddress: true;
};
readonly relayerAddress: {
readonly type: "string";
readonly pattern: "^0x[a-fA-F0-9]{40}$";
readonly isAddress: true;
};
readonly fee: {
readonly type: "string";
readonly BN: true;
};
readonly timestamp: {
readonly type: "number";
};
readonly blockNumber: {
readonly type: "number";
};
readonly logIndex: {
readonly type: "number";
};
readonly transactionHash: {
readonly type: "string";
readonly pattern: "^0x[a-fA-F0-9]{64}$";
};
};
readonly required: readonly [...string[], "event", "instanceAddress", "nullifierHash", "to", "relayerAddress", "fee", "timestamp"];
readonly additionalProperties: false;
}];
};
};
export declare const echoEventsSchema: {
readonly type: "array";
readonly items: {

233
dist/tornado.umd.js vendored

@ -60854,6 +60854,7 @@ __webpack_require__.d(__webpack_exports__, {
O_: () => (/* binding */ BaseEncryptedNotesService),
uw: () => (/* binding */ BaseEventsService),
JJ: () => (/* binding */ BaseGovernanceService),
lG: () => (/* binding */ BaseMultiTornadoService),
cE: () => (/* binding */ BaseRegistryService),
Do: () => (/* binding */ BaseRevenueService),
e0: () => (/* binding */ BaseTornadoService),
@ -90513,7 +90514,7 @@ class BaseEventsService {
* Handle saving events
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveEvents({ events, lastBlock }) {
async saveEvents({ events, newEvents, lastBlock }) {
}
/**
* Trigger saving and receiving latest events
@ -90544,7 +90545,7 @@ class BaseEventsService {
lastBlock
});
if (savedEvents.fromCache || newEvents.events.length) {
await this.saveEvents({ events: allEvents, lastBlock });
await this.saveEvents({ events: allEvents, newEvents: newEvents.events, lastBlock });
}
return {
events: allEvents,
@ -90655,6 +90656,149 @@ class BaseTornadoService extends BaseEventsService {
});
}
}
class BaseMultiTornadoService extends BaseEventsService {
instances;
optionalTree;
merkleTreeService;
batchTransactionService;
batchBlockService;
constructor(serviceConstructor) {
const { instances, provider, optionalTree, merkleTreeService } = serviceConstructor;
const contract = merkleTreeService?.Tornado || Tornado__factory.connect(Object.keys(instances)[0], provider);
super({
...serviceConstructor,
contract,
type: "*"
});
this.batchEventsService = new batch/* BatchEventsService */.JY({
provider,
contract,
address: Object.keys(instances),
onProgress: this.updateEventProgress
});
this.instances = instances;
this.optionalTree = optionalTree;
this.merkleTreeService = merkleTreeService;
this.batchTransactionService = new batch/* BatchTransactionService */.AF({
provider,
onProgress: this.updateTransactionProgress
});
this.batchBlockService = new batch/* BatchBlockService */.B3({
provider,
onProgress: this.updateBlockProgress
});
}
getInstanceName() {
return `tornado_${this.netId}`;
}
getTovarishType() {
return "tornado";
}
async formatEvents(events) {
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(
events.filter(({ eventName }) => eventName === "Deposit").map(({ transactionHash }) => transactionHash)
)
]);
const blocks = await this.batchBlockService.getBatchBlocks([
...new Set(
events.filter(({ eventName }) => eventName === "Withdrawal").map(({ blockNumber }) => blockNumber)
)
]);
return events.map(
({
address: instanceAddress,
blockNumber,
index: logIndex,
transactionHash,
args,
eventName: event
}) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
event,
instanceAddress
};
if (event === "Deposit") {
const { commitment, leafIndex, timestamp } = args;
return {
...eventObjects,
commitment,
leafIndex: Number(leafIndex),
timestamp: Number(timestamp),
from: txs.find(({ hash }) => hash === transactionHash)?.from || ""
};
}
if (event === "Withdrawal") {
const { nullifierHash, to, relayer: relayerAddress, fee } = args;
return {
...eventObjects,
logIndex,
transactionHash,
nullifierHash: String(nullifierHash),
to,
relayerAddress,
fee: String(fee),
timestamp: blocks.find(({ number }) => number === blockNumber)?.timestamp || 0
};
}
}
).filter((e) => e);
}
async validateEvents({
events,
newEvents
}) {
const instancesWithNewEvents = [
...new Set(
newEvents.filter(({ event }) => event === "Deposit").map(({ instanceAddress }) => instanceAddress)
)
];
let tree;
const requiredTree = this.merkleTreeService?.Tornado?.target;
if (requiredTree && !instancesWithNewEvents.includes(requiredTree)) {
instancesWithNewEvents.push(requiredTree);
}
for (const instance of instancesWithNewEvents) {
const depositEvents = events.filter(
({ instanceAddress, event }) => instanceAddress === instance && event === "Deposit"
);
const lastEvent = depositEvents[depositEvents.length - 1];
if (lastEvent.leafIndex !== depositEvents.length - 1) {
const errMsg = `Invalid deposit events for ${instance} wants ${depositEvents.length - 1} leafIndex have ${lastEvent.leafIndex}`;
throw new Error(errMsg);
}
if (requiredTree === instance && !this.optionalTree) {
tree = await this.merkleTreeService?.verifyTree(depositEvents);
}
}
return tree;
}
async getEvents(instanceAddress) {
const { events, validateResult: tree, lastBlock } = await this.updateEvents();
const { depositEvents, withdrawalEvents } = events.reduce(
(acc, curr) => {
if (curr.instanceAddress === instanceAddress) {
if (curr.event === "Deposit") {
acc.depositEvents.push(curr);
} else if (curr.event === "Withdrawal") {
acc.withdrawalEvents.push(curr);
}
}
return acc;
},
{}
);
return {
depositEvents,
withdrawalEvents,
tree,
lastBlock
};
}
}
class BaseEchoService extends BaseEventsService {
constructor(serviceConstructor) {
super({
@ -91662,6 +91806,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ BaseEncryptedNotesService: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.O_),
/* harmony export */ BaseEventsService: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.uw),
/* harmony export */ BaseGovernanceService: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.JJ),
/* harmony export */ BaseMultiTornadoService: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.lG),
/* harmony export */ BaseRegistryService: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.cE),
/* harmony export */ BaseRevenueService: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.Do),
/* harmony export */ BaseTornadoService: () => (/* reexport safe */ _base__WEBPACK_IMPORTED_MODULE_1__.e0),
@ -92866,6 +93011,7 @@ async function multicall(Multicall2, calls) {
/* harmony export */ h9: () => (/* binding */ getActiveTokens),
/* harmony export */ o2: () => (/* binding */ getRelayerEnsSubdomains),
/* harmony export */ oY: () => (/* binding */ getActiveTokenInstances),
/* harmony export */ sX: () => (/* binding */ getMultiInstances),
/* harmony export */ sb: () => (/* binding */ defaultConfig),
/* harmony export */ zj: () => (/* binding */ getConfig),
/* harmony export */ zr: () => (/* binding */ NetId)
@ -93468,7 +93614,7 @@ function getActiveTokenInstances(config) {
}
function getInstanceByAddress(config, address) {
const { tokens, disabledTokens } = config;
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
for (const [currency, { instanceAddress, tokenAddress, symbol, decimals }] of Object.entries(tokens)) {
if (disabledTokens?.includes(currency)) {
continue;
}
@ -93476,7 +93622,10 @@ function getInstanceByAddress(config, address) {
if (instance === address) {
return {
amount,
currency
currency,
symbol,
decimals,
tokenAddress
};
}
}
@ -93489,6 +93638,21 @@ function getRelayerEnsSubdomains() {
return acc;
}, {});
}
function getMultiInstances(netId, config) {
return Object.entries(config.tokens).reduce(
(acc, [currency, { instanceAddress }]) => {
Object.entries(instanceAddress).forEach(([amount, contractAddress]) => {
acc[contractAddress] = {
currency,
amount,
netId
};
});
return acc;
},
{}
);
}
/***/ }),
@ -101493,6 +101657,7 @@ __webpack_require__.d(__webpack_exports__, {
Y6: () => (/* reexport */ proofSchemaType),
cl: () => (/* reexport */ relayerRegistryEventsSchema),
Fz: () => (/* reexport */ stakeBurnedEventsSchema),
U7: () => (/* reexport */ tornadoEventsSchema),
$j: () => (/* reexport */ withdrawalsEventsSchema)
});
@ -101747,6 +101912,61 @@ const withdrawalsEventsSchema = {
additionalProperties: false
}
};
const tornadoEventsSchema = {
type: "array",
items: {
anyOf: [
// depositsEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
instanceAddress: { type: "string" },
commitment: bytes32SchemaType,
leafIndex: { type: "number" },
timestamp: { type: "number" },
from: addressSchemaType
},
required: [
...baseEventsSchemaRequired,
"event",
"instanceAddress",
"commitment",
"leafIndex",
"timestamp",
"from"
],
additionalProperties: false
},
// withdrawalEvents
{
type: "object",
properties: {
...baseEventsSchemaProperty,
event: { type: "string" },
instanceAddress: { type: "string" },
nullifierHash: bytes32SchemaType,
to: addressSchemaType,
relayerAddress: addressSchemaType,
fee: bnSchemaType,
timestamp: { type: "number" }
},
required: [
...baseEventsSchemaRequired,
"event",
"instanceAddress",
"nullifierHash",
"to",
"relayerAddress",
"fee",
"timestamp"
],
additionalProperties: false
}
]
}
};
const echoEventsSchema = {
type: "array",
items: {
@ -101773,6 +101993,9 @@ const encryptedNotesSchema = {
}
};
function getEventsSchemaValidator(type) {
if (type === "tornado") {
return ajv_ajv.compile(tornadoEventsSchema);
}
if (type === "deposit") {
return ajv_ajv.compile(depositsEventsSchema);
}
@ -217571,6 +217794,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ getHttpAgent: () => (/* reexport safe */ _providers__WEBPACK_IMPORTED_MODULE_19__.WU),
/* harmony export */ getIndexedDB: () => (/* reexport safe */ _idb__WEBPACK_IMPORTED_MODULE_10__.W7),
/* harmony export */ getInstanceByAddress: () => (/* reexport safe */ _networkConfig__WEBPACK_IMPORTED_MODULE_15__.Zh),
/* harmony export */ getMultiInstances: () => (/* reexport safe */ _networkConfig__WEBPACK_IMPORTED_MODULE_15__.sX),
/* harmony export */ getNetworkConfig: () => (/* reexport safe */ _networkConfig__WEBPACK_IMPORTED_MODULE_15__.RY),
/* harmony export */ getPermit2CommitmentsSignature: () => (/* reexport safe */ _permit__WEBPACK_IMPORTED_MODULE_17__.Sl),
/* harmony export */ getPermit2Signature: () => (/* reexport safe */ _permit__WEBPACK_IMPORTED_MODULE_17__.KM),
@ -217617,6 +217841,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export */ toContentHash: () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_23__.vd),
/* harmony export */ toFixedHex: () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_23__.$W),
/* harmony export */ toFixedLength: () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_23__.sY),
/* harmony export */ tornadoEventsSchema: () => (/* reexport safe */ _schemas__WEBPACK_IMPORTED_MODULE_1__.U7),
/* harmony export */ unpackEncryptedMessage: () => (/* reexport safe */ _encryptedNotes__WEBPACK_IMPORTED_MODULE_5__.ol),
/* harmony export */ unzipAsync: () => (/* reexport safe */ _zip__WEBPACK_IMPORTED_MODULE_25__.fY),
/* harmony export */ validateUrl: () => (/* reexport safe */ _utils__WEBPACK_IMPORTED_MODULE_23__.wv),

File diff suppressed because one or more lines are too long

@ -5,27 +5,13 @@ export interface EventsStatus {
events: number;
lastBlock: number;
}
export interface InstanceEventsStatus {
[index: string]: {
deposits: EventsStatus;
withdrawals: EventsStatus;
};
}
export interface CurrencyEventsStatus {
[index: string]: InstanceEventsStatus;
}
export interface TovarishEventsStatus {
governance?: EventsStatus;
registered?: {
lastBlock: number;
timestamp: number;
relayers: number;
};
registry?: EventsStatus;
revenue?: EventsStatus;
echo: EventsStatus;
encrypted_notes: EventsStatus;
instances: CurrencyEventsStatus;
tornado: EventsStatus;
}
export interface TovarishSyncStatus {
events: boolean;

@ -22,6 +22,7 @@ import {
Tornado__factory,
} from '@tornado/contracts';
import type { MerkleTree } from '@tornado/fixed-merkle-tree';
import {
BatchEventsService,
BatchBlockService,
@ -37,6 +38,7 @@ import type { TovarishClient } from '../tovarishClient';
import type { ReverseRecords } from '../typechain';
import type { MerkleTreeService } from '../merkleTree';
import type { DepositType } from '../deposits';
import type {
BaseEvents,
CachedEvents,
@ -56,6 +58,8 @@ import type {
WorkerUnregisteredEvents,
AllRelayerRegistryEvents,
StakeBurnedEvents,
MultiDepositsEvents,
MultiWithdrawalsEvents,
} from './types';
export interface BaseEventsServiceConstructor {
@ -237,7 +241,7 @@ export class BaseEventsService<EventType extends MinimalEvents> {
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveEvents({ events, lastBlock }: BaseEvents<EventType>) {}
async saveEvents({ events, newEvents, lastBlock }: BaseEvents<EventType> & { newEvents: EventType[] }) {}
/**
* Trigger saving and receiving latest events
@ -280,7 +284,7 @@ export class BaseEventsService<EventType extends MinimalEvents> {
// If the events are loaded from cache or we have found new events, save them
if ((savedEvents as CachedEvents<EventType>).fromCache || newEvents.events.length) {
await this.saveEvents({ events: allEvents, lastBlock });
await this.saveEvents({ events: allEvents, newEvents: newEvents.events, lastBlock });
}
return {
@ -429,6 +433,199 @@ export class BaseTornadoService extends BaseEventsService<DepositsEvents | Withd
}
}
export interface BaseMultiTornadoServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
instances: {
[key in string]: DepositType;
};
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
}
export class BaseMultiTornadoService extends BaseEventsService<MultiDepositsEvents | MultiWithdrawalsEvents> {
instances: {
[key in string]: DepositType;
};
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
batchTransactionService: BatchTransactionService;
batchBlockService: BatchBlockService;
constructor(serviceConstructor: BaseMultiTornadoServiceConstructor) {
const { instances, provider, optionalTree, merkleTreeService } = serviceConstructor;
const contract =
merkleTreeService?.Tornado || Tornado__factory.connect(Object.keys(instances)[0] as string, provider);
super({
...serviceConstructor,
contract,
type: '*',
});
this.batchEventsService = new BatchEventsService({
provider,
contract,
address: Object.keys(instances),
onProgress: this.updateEventProgress,
});
this.instances = instances;
this.optionalTree = optionalTree;
this.merkleTreeService = merkleTreeService;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress,
});
this.batchBlockService = new BatchBlockService({
provider,
onProgress: this.updateBlockProgress,
});
}
getInstanceName(): string {
return `tornado_${this.netId}`;
}
getTovarishType(): string {
return 'tornado';
}
async formatEvents(events: EventLog[]): Promise<(MultiDepositsEvents | MultiWithdrawalsEvents)[]> {
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(
events.filter(({ eventName }) => eventName === 'Deposit').map(({ transactionHash }) => transactionHash),
),
]);
const blocks = await this.batchBlockService.getBatchBlocks([
...new Set(
events.filter(({ eventName }) => eventName === 'Withdrawal').map(({ blockNumber }) => blockNumber),
),
]);
return events
.map(
({
address: instanceAddress,
blockNumber,
index: logIndex,
transactionHash,
args,
eventName: event,
}) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
event,
instanceAddress,
};
if (event === 'Deposit') {
const { commitment, leafIndex, timestamp } = args;
return {
...eventObjects,
commitment: commitment as string,
leafIndex: Number(leafIndex),
timestamp: Number(timestamp),
from: txs.find(({ hash }) => hash === transactionHash)?.from || '',
} as MultiDepositsEvents;
}
if (event === 'Withdrawal') {
const { nullifierHash, to, relayer: relayerAddress, fee } = args;
return {
...eventObjects,
logIndex,
transactionHash,
nullifierHash: String(nullifierHash),
to,
relayerAddress,
fee: String(fee),
timestamp: blocks.find(({ number }) => number === blockNumber)?.timestamp || 0,
} as MultiWithdrawalsEvents;
}
},
)
.filter((e) => e) as (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}
async validateEvents<S>({
events,
newEvents,
}: BaseEvents<MultiDepositsEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositsEvents | MultiWithdrawalsEvents)[];
}) {
const instancesWithNewEvents = [
...new Set(
newEvents.filter(({ event }) => event === 'Deposit').map(({ instanceAddress }) => instanceAddress),
),
];
let tree: S | undefined;
const requiredTree = this.merkleTreeService?.Tornado?.target as string | undefined;
// Audit and create deposit tree
if (requiredTree && !instancesWithNewEvents.includes(requiredTree)) {
instancesWithNewEvents.push(requiredTree);
}
for (const instance of instancesWithNewEvents) {
const depositEvents = events.filter(
({ instanceAddress, event }) => instanceAddress === instance && event === 'Deposit',
) as MultiDepositsEvents[];
const lastEvent = depositEvents[depositEvents.length - 1];
if (lastEvent.leafIndex !== depositEvents.length - 1) {
const errMsg = `Invalid deposit events for ${instance} wants ${depositEvents.length - 1} leafIndex have ${lastEvent.leafIndex}`;
throw new Error(errMsg);
}
if (requiredTree === instance && !this.optionalTree) {
tree = (await this.merkleTreeService?.verifyTree(depositEvents)) as S;
}
}
return tree as S;
}
async getEvents(instanceAddress: string) {
const { events, validateResult: tree, lastBlock } = await this.updateEvents<MerkleTree | undefined>();
const { depositEvents, withdrawalEvents } = events.reduce(
(acc, curr) => {
if (curr.instanceAddress === instanceAddress) {
if (curr.event === 'Deposit') {
acc.depositEvents.push(curr as MultiDepositsEvents);
} else if (curr.event === 'Withdrawal') {
acc.withdrawalEvents.push(curr as MultiWithdrawalsEvents);
}
}
return acc;
},
{} as {
depositEvents: MultiDepositsEvents[];
withdrawalEvents: MultiWithdrawalsEvents[];
},
);
return {
depositEvents,
withdrawalEvents,
tree,
lastBlock,
};
}
}
export interface BaseEchoServiceConstructor extends Omit<BaseEventsServiceConstructor, 'contract' | 'type'> {
Echoer: Echoer;
}

@ -113,6 +113,17 @@ export interface WithdrawalsEvents extends MinimalEvents {
timestamp: number;
}
export interface BaseMultiTornadoEvents {
event: string;
instanceAddress: string;
}
export interface MultiDepositsEvents extends BaseMultiTornadoEvents, DepositsEvents {}
export interface MultiWithdrawalsEvents extends BaseMultiTornadoEvents, WithdrawalsEvents {
relayerAddress: string;
}
export interface EchoEvents extends MinimalEvents {
address: string;
encryptedAccount: string;

@ -1,3 +1,5 @@
import type { DepositType } from './deposits';
/**
* Type of default supported networks
*/
@ -725,7 +727,7 @@ export function getActiveTokenInstances(config: Config): TokenInstances {
export function getInstanceByAddress(config: Config, address: string) {
const { tokens, disabledTokens } = config;
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
for (const [currency, { instanceAddress, tokenAddress, symbol, decimals }] of Object.entries(tokens)) {
if (disabledTokens?.includes(currency)) {
continue;
}
@ -734,6 +736,9 @@ export function getInstanceByAddress(config: Config, address: string) {
return {
amount,
currency,
symbol,
decimals,
tokenAddress,
};
}
}
@ -748,3 +753,19 @@ export function getRelayerEnsSubdomains() {
return acc;
}, {} as SubdomainMap);
}
export function getMultiInstances(netId: NetIdType, config: Config): { [key in string]: DepositType } {
return Object.entries(config.tokens).reduce(
(acc, [currency, { instanceAddress }]) => {
Object.entries(instanceAddress).forEach(([amount, contractAddress]) => {
acc[contractAddress] = {
currency,
amount,
netId,
};
});
return acc;
},
{} as { [key in string]: DepositType },
);
}

@ -201,6 +201,62 @@ export const withdrawalsEventsSchema = {
},
} as const;
export const tornadoEventsSchema = {
type: 'array',
items: {
anyOf: [
// depositsEvents
{
type: 'object',
properties: {
...baseEventsSchemaProperty,
event: { type: 'string' },
instanceAddress: { type: 'string' },
commitment: bytes32SchemaType,
leafIndex: { type: 'number' },
timestamp: { type: 'number' },
from: addressSchemaType,
},
required: [
...baseEventsSchemaRequired,
'event',
'instanceAddress',
'commitment',
'leafIndex',
'timestamp',
'from',
],
additionalProperties: false,
},
// withdrawalEvents
{
type: 'object',
properties: {
...baseEventsSchemaProperty,
event: { type: 'string' },
instanceAddress: { type: 'string' },
nullifierHash: bytes32SchemaType,
to: addressSchemaType,
relayerAddress: addressSchemaType,
fee: bnSchemaType,
timestamp: { type: 'number' },
},
required: [
...baseEventsSchemaRequired,
'event',
'instanceAddress',
'nullifierHash',
'to',
'relayerAddress',
'fee',
'timestamp',
],
additionalProperties: false,
},
],
},
} as const;
export const echoEventsSchema = {
type: 'array',
items: {
@ -229,6 +285,10 @@ export const encryptedNotesSchema = {
} as const;
export function getEventsSchemaValidator(type: string) {
if (type === 'tornado') {
return ajv.compile(tornadoEventsSchema);
}
if (type === 'deposit') {
return ajv.compile(depositsEventsSchema);
}

@ -21,29 +21,13 @@ export interface EventsStatus {
lastBlock: number;
}
export interface InstanceEventsStatus {
[index: string]: {
deposits: EventsStatus;
withdrawals: EventsStatus;
};
}
export interface CurrencyEventsStatus {
[index: string]: InstanceEventsStatus;
}
export interface TovarishEventsStatus {
governance?: EventsStatus;
registered?: {
lastBlock: number;
timestamp: number;
relayers: number;
};
registry?: EventsStatus;
revenue?: EventsStatus;
echo: EventsStatus;
encrypted_notes: EventsStatus;
instances: CurrencyEventsStatus;
tornado: EventsStatus;
}
export interface TovarishSyncStatus {