Add BaseMultiTornadoService

This commit is contained in:
Tornado Contrib 2024-11-19 00:38:46 +00:00
parent b65998fca5
commit 49a5ba7a2d
Signed by: tornadocontrib
GPG Key ID: 60B4DF1A076C64B1
10 changed files with 789 additions and 10 deletions

34
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, MultiDepositEvents, MultiWithdrawalsEvents } from './types';
export interface BaseEventsServiceConstructor {
netId: NetIdType;
provider: Provider;
@ -90,6 +92,36 @@ 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<MultiDepositEvents | MultiWithdrawalsEvents> {
instances: {
[key in string]: DepositType;
};
optionalTree?: boolean;
merkleTreeService?: MerkleTreeService;
batchTransactionService: BatchTransactionService;
batchBlockService: BatchBlockService;
constructor(serviceConstructor: BaseMultiTornadoServiceConstructor);
getInstanceName(): string;
getTovarishType(): string;
updateEventProgress({ percentage, type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]): void;
formatEvents(events: EventLog[]): Promise<(MultiDepositEvents | MultiWithdrawalsEvents)[]>;
validateEvents<S>({ events, newEvents, }: BaseEvents<MultiDepositEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositEvents | MultiWithdrawalsEvents)[];
}): Promise<S>;
getEvents(instanceAddress: string): Promise<{
depositEvents: MultiDepositEvents[];
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 MultiDepositEvents extends BaseMultiTornadoEvents, DepositsEvents {
}
export interface MultiWithdrawalsEvents extends BaseMultiTornadoEvents, WithdrawalsEvents {
relayerAddress: string;
}
export interface EchoEvents extends MinimalEvents {
address: string;
encryptedAccount: string;

170
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({
@ -2368,6 +2386,152 @@ 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";
}
updateEventProgress({ percentage, type, fromBlock, toBlock, count }) {
console.log({ percentage, type, fromBlock, toBlock, count });
}
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 +10575,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 +10667,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;

170
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({
@ -2346,6 +2364,152 @@ 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";
}
updateEventProgress({ percentage, type, fromBlock, toBlock, count }) {
console.log({ percentage, type, fromBlock, toBlock, count });
}
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 +10549,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, 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;
};

172
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),
@ -90655,6 +90656,152 @@ 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";
}
updateEventProgress({ percentage, type, fromBlock, toBlock, count }) {
console.log({ percentage, type, fromBlock, toBlock, count });
}
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 +91809,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 +93014,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 +93617,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 +93625,10 @@ function getInstanceByAddress(config, address) {
if (instance === address) {
return {
amount,
currency
currency,
symbol,
decimals,
tokenAddress
};
}
}
@ -93489,6 +93641,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;
},
{}
);
}
/***/ }),
@ -217571,6 +217738,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),

File diff suppressed because one or more lines are too long

@ -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,
MultiDepositEvents,
MultiWithdrawalsEvents,
} from './types';
export interface BaseEventsServiceConstructor {
@ -429,6 +433,203 @@ 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<MultiDepositEvents | 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';
}
updateEventProgress({ percentage, type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
console.log({ percentage, type, fromBlock, toBlock, count });
}
async formatEvents(events: EventLog[]): Promise<(MultiDepositEvents | 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 MultiDepositEvents;
}
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 (MultiDepositEvents | MultiWithdrawalsEvents)[];
}
async validateEvents<S>({
events,
newEvents,
}: BaseEvents<MultiDepositEvents | MultiWithdrawalsEvents> & {
newEvents: (MultiDepositEvents | 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 MultiDepositEvents[];
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 MultiDepositEvents);
} else if (curr.event === 'Withdrawal') {
acc.withdrawalEvents.push(curr as MultiWithdrawalsEvents);
}
}
return acc;
},
{} as {
depositEvents: MultiDepositEvents[];
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 MultiDepositEvents 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 },
);
}