Add multiQueryFilter function
This commit is contained in:
parent
092989ebaa
commit
8644cd3c82
17
dist/batch.d.ts
vendored
17
dist/batch.d.ts
vendored
@ -1,4 +1,14 @@
|
||||
import type { Provider, BlockTag, Block, TransactionResponse, BaseContract, ContractEventName, EventLog, TransactionReceipt } from 'ethers';
|
||||
import { Provider, BlockTag, Block, TransactionResponse, BaseContract, ContractEventName, EventLog, TransactionReceipt, EventFragment, TopicFilter, Interface, Log } from 'ethers';
|
||||
/**
|
||||
* Copied from ethers.js as they don't export this function
|
||||
* https://github.com/ethers-io/ethers.js/blob/main/src.ts/contract/contract.ts#L464
|
||||
*/
|
||||
export declare function getSubInfo(abiInterface: Interface, event: ContractEventName): Promise<{
|
||||
fragment: null | EventFragment;
|
||||
tag: string;
|
||||
topics: TopicFilter;
|
||||
}>;
|
||||
export declare function multiQueryFilter(address: string | string[], contract: BaseContract, event: ContractEventName, fromBlock?: BlockTag, toBlock?: BlockTag): Promise<Log[]>;
|
||||
export interface BatchBlockServiceConstructor {
|
||||
provider: Provider;
|
||||
onProgress?: BatchBlockOnProgress;
|
||||
@ -65,6 +75,7 @@ export type BatchEventOnProgress = ({ percentage, type, fromBlock, toBlock, coun
|
||||
count?: number;
|
||||
}) => void;
|
||||
export interface EventInput {
|
||||
address?: string | string[];
|
||||
fromBlock: number;
|
||||
toBlock: number;
|
||||
type: ContractEventName;
|
||||
@ -82,7 +93,7 @@ export declare class BatchEventsService {
|
||||
retryMax: number;
|
||||
retryOn: number;
|
||||
constructor({ provider, contract, onProgress, concurrencySize, blocksPerRequest, shouldRetry, retryMax, retryOn, }: BatchEventServiceConstructor);
|
||||
getPastEvents({ fromBlock, toBlock, type }: EventInput): Promise<EventLog[]>;
|
||||
getPastEvents({ address, fromBlock, toBlock, type }: EventInput): Promise<EventLog[]>;
|
||||
createBatchRequest(batchArray: EventInput[]): Promise<EventLog[]>[];
|
||||
getBatchEvents({ fromBlock, toBlock, type }: EventInput): Promise<EventLog[]>;
|
||||
getBatchEvents({ address, fromBlock, toBlock, type }: EventInput): Promise<EventLog[]>;
|
||||
}
|
||||
|
118
dist/index.js
vendored
118
dist/index.js
vendored
@ -154,6 +154,107 @@ function fromContentHash(contentHash) {
|
||||
return contentHashUtils__namespace.decode(contentHash);
|
||||
}
|
||||
|
||||
function isDeferred(value) {
|
||||
return value && typeof value === "object" && "getTopicFilter" in value && typeof value.getTopicFilter === "function" && value.fragment;
|
||||
}
|
||||
async function getSubInfo(abiInterface, event) {
|
||||
let topics;
|
||||
let fragment = null;
|
||||
if (Array.isArray(event)) {
|
||||
const topicHashify = function(name) {
|
||||
if (ethers.isHexString(name, 32)) {
|
||||
return name;
|
||||
}
|
||||
const fragment2 = abiInterface.getEvent(name);
|
||||
ethers.assertArgument(fragment2, "unknown fragment", "name", name);
|
||||
return fragment2.topicHash;
|
||||
};
|
||||
topics = event.map((e) => {
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(e)) {
|
||||
return e.map(topicHashify);
|
||||
}
|
||||
return topicHashify(e);
|
||||
});
|
||||
} else if (event === "*") {
|
||||
topics = [null];
|
||||
} else if (typeof event === "string") {
|
||||
if (ethers.isHexString(event, 32)) {
|
||||
topics = [event];
|
||||
} else {
|
||||
fragment = abiInterface.getEvent(event);
|
||||
ethers.assertArgument(fragment, "unknown fragment", "event", event);
|
||||
topics = [fragment.topicHash];
|
||||
}
|
||||
} else if (isDeferred(event)) {
|
||||
topics = await event.getTopicFilter();
|
||||
} else if ("fragment" in event) {
|
||||
fragment = event.fragment;
|
||||
topics = [fragment.topicHash];
|
||||
} else {
|
||||
ethers.assertArgument(false, "unknown event name", "event", event);
|
||||
}
|
||||
topics = topics.map((t) => {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(t)) {
|
||||
const items = Array.from(new Set(t.map((t2) => t2.toLowerCase())).values());
|
||||
if (items.length === 1) {
|
||||
return items[0];
|
||||
}
|
||||
items.sort();
|
||||
return items;
|
||||
}
|
||||
return t.toLowerCase();
|
||||
});
|
||||
const tag = topics.map((t) => {
|
||||
if (t == null) {
|
||||
return "null";
|
||||
}
|
||||
if (Array.isArray(t)) {
|
||||
return t.join("|");
|
||||
}
|
||||
return t;
|
||||
}).join("&");
|
||||
return { fragment, tag, topics };
|
||||
}
|
||||
async function multiQueryFilter(address, contract, event, fromBlock, toBlock) {
|
||||
if (fromBlock == null) {
|
||||
fromBlock = 0;
|
||||
}
|
||||
if (toBlock == null) {
|
||||
toBlock = "latest";
|
||||
}
|
||||
const { fragment, topics } = await getSubInfo(contract.interface, event);
|
||||
const filter = {
|
||||
address: address === "*" ? void 0 : address,
|
||||
topics,
|
||||
fromBlock,
|
||||
toBlock
|
||||
};
|
||||
const provider = contract.runner;
|
||||
ethers.assert(provider, "contract runner does not have a provider", "UNSUPPORTED_OPERATION", { operation: "queryFilter" });
|
||||
return (await provider.getLogs(filter)).map((log) => {
|
||||
let foundFragment = fragment;
|
||||
if (foundFragment == null) {
|
||||
try {
|
||||
foundFragment = contract.interface.getEvent(log.topics[0]);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
if (foundFragment) {
|
||||
try {
|
||||
return new ethers.EventLog(log, contract.interface, foundFragment);
|
||||
} catch (error) {
|
||||
return new ethers.UndecodedEventLog(log, error);
|
||||
}
|
||||
}
|
||||
return new ethers.Log(log, provider);
|
||||
});
|
||||
}
|
||||
class BatchBlockService {
|
||||
provider;
|
||||
onProgress;
|
||||
@ -351,11 +452,14 @@ class BatchEventsService {
|
||||
this.retryMax = retryMax;
|
||||
this.retryOn = retryOn;
|
||||
}
|
||||
async getPastEvents({ fromBlock, toBlock, type }) {
|
||||
async getPastEvents({ address, fromBlock, toBlock, type }) {
|
||||
let err;
|
||||
let retries = 0;
|
||||
while (!this.shouldRetry && retries === 0 || this.shouldRetry && retries < this.retryMax) {
|
||||
try {
|
||||
if (address) {
|
||||
return await multiQueryFilter(address, this.contract, type, fromBlock, toBlock);
|
||||
}
|
||||
return await this.contract.queryFilter(type, fromBlock, toBlock);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
@ -375,14 +479,14 @@ class BatchEventsService {
|
||||
return this.getPastEvents(event);
|
||||
});
|
||||
}
|
||||
async getBatchEvents({ fromBlock, toBlock, type = "*" }) {
|
||||
async getBatchEvents({ address, fromBlock, toBlock, type = "*" }) {
|
||||
if (!toBlock) {
|
||||
toBlock = await this.provider.getBlockNumber();
|
||||
}
|
||||
const eventsToSync = [];
|
||||
for (let i = fromBlock; i < toBlock; i += this.blocksPerRequest) {
|
||||
const j = i + this.blocksPerRequest - 1 > toBlock ? toBlock : i + this.blocksPerRequest - 1;
|
||||
eventsToSync.push({ fromBlock: i, toBlock: j, type });
|
||||
eventsToSync.push({ address, fromBlock: i, toBlock: j, type });
|
||||
}
|
||||
const events = [];
|
||||
const eventChunk = chunk(eventsToSync, this.concurrencySize);
|
||||
@ -2086,7 +2190,7 @@ class BaseEventsService {
|
||||
}
|
||||
}
|
||||
async getLatestEvents({ fromBlock }) {
|
||||
if (this.tovarishClient?.selectedRelayer && !["Deposit", "Withdrawal"].includes(this.type)) {
|
||||
if (this.tovarishClient?.selectedRelayer) {
|
||||
const { events, lastSyncBlock: lastBlock } = await this.tovarishClient.getEvents({
|
||||
type: this.getTovarishType(),
|
||||
fromBlock
|
||||
@ -2250,7 +2354,9 @@ class BaseTornadoService extends BaseEventsService {
|
||||
lastBlock
|
||||
};
|
||||
}
|
||||
return super.getLatestEvents({ fromBlock });
|
||||
return await this.getEventsFromRpc({
|
||||
fromBlock
|
||||
});
|
||||
}
|
||||
}
|
||||
class BaseEchoService extends BaseEventsService {
|
||||
@ -10396,6 +10502,7 @@ exports.getProvider = getProvider;
|
||||
exports.getProviderWithNetId = getProviderWithNetId;
|
||||
exports.getRelayerEnsSubdomains = getRelayerEnsSubdomains;
|
||||
exports.getStatusSchema = getStatusSchema;
|
||||
exports.getSubInfo = getSubInfo;
|
||||
exports.getSupportedInstances = getSupportedInstances;
|
||||
exports.getTokenBalances = getTokenBalances;
|
||||
exports.getTovarishNetworks = getTovarishNetworks;
|
||||
@ -10415,6 +10522,7 @@ exports.loadDBEvents = loadDBEvents;
|
||||
exports.loadRemoteEvents = loadRemoteEvents;
|
||||
exports.makeLabelNodeAndParent = makeLabelNodeAndParent;
|
||||
exports.mimc = mimc;
|
||||
exports.multiQueryFilter = multiQueryFilter;
|
||||
exports.multicall = multicall;
|
||||
exports.numberFormatter = numberFormatter;
|
||||
exports.packEncryptedMessage = packEncryptedMessage;
|
||||
|
120
dist/index.mjs
vendored
120
dist/index.mjs
vendored
@ -1,4 +1,4 @@
|
||||
import { FetchRequest, JsonRpcProvider, Network, EnsPlugin, GasCostPlugin, Wallet, HDNodeWallet, VoidSigner, JsonRpcSigner, BrowserProvider, isAddress, parseEther, getAddress, AbiCoder, formatEther, namehash, dataSlice, dataLength, Interface, Contract, computeAddress, keccak256, EnsResolver, parseUnits, Transaction, Signature, MaxUint256, solidityPackedKeccak256, TypedDataEncoder, ZeroAddress } from 'ethers';
|
||||
import { isHexString, assertArgument, assert, EventLog, UndecodedEventLog, Log, FetchRequest, JsonRpcProvider, Network, EnsPlugin, GasCostPlugin, Wallet, HDNodeWallet, VoidSigner, JsonRpcSigner, BrowserProvider, isAddress, parseEther, getAddress, AbiCoder, formatEther, namehash, dataSlice, dataLength, Interface, Contract, computeAddress, keccak256, EnsResolver, parseUnits, Transaction, Signature, MaxUint256, solidityPackedKeccak256, TypedDataEncoder, ZeroAddress } from 'ethers';
|
||||
import { Tornado__factory } from '@tornado/contracts';
|
||||
import { webcrypto } from 'crypto';
|
||||
import BN from 'bn.js';
|
||||
@ -132,6 +132,107 @@ function fromContentHash(contentHash) {
|
||||
return contentHashUtils.decode(contentHash);
|
||||
}
|
||||
|
||||
function isDeferred(value) {
|
||||
return value && typeof value === "object" && "getTopicFilter" in value && typeof value.getTopicFilter === "function" && value.fragment;
|
||||
}
|
||||
async function getSubInfo(abiInterface, event) {
|
||||
let topics;
|
||||
let fragment = null;
|
||||
if (Array.isArray(event)) {
|
||||
const topicHashify = function(name) {
|
||||
if (isHexString(name, 32)) {
|
||||
return name;
|
||||
}
|
||||
const fragment2 = abiInterface.getEvent(name);
|
||||
assertArgument(fragment2, "unknown fragment", "name", name);
|
||||
return fragment2.topicHash;
|
||||
};
|
||||
topics = event.map((e) => {
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(e)) {
|
||||
return e.map(topicHashify);
|
||||
}
|
||||
return topicHashify(e);
|
||||
});
|
||||
} else if (event === "*") {
|
||||
topics = [null];
|
||||
} else if (typeof event === "string") {
|
||||
if (isHexString(event, 32)) {
|
||||
topics = [event];
|
||||
} else {
|
||||
fragment = abiInterface.getEvent(event);
|
||||
assertArgument(fragment, "unknown fragment", "event", event);
|
||||
topics = [fragment.topicHash];
|
||||
}
|
||||
} else if (isDeferred(event)) {
|
||||
topics = await event.getTopicFilter();
|
||||
} else if ("fragment" in event) {
|
||||
fragment = event.fragment;
|
||||
topics = [fragment.topicHash];
|
||||
} else {
|
||||
assertArgument(false, "unknown event name", "event", event);
|
||||
}
|
||||
topics = topics.map((t) => {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(t)) {
|
||||
const items = Array.from(new Set(t.map((t2) => t2.toLowerCase())).values());
|
||||
if (items.length === 1) {
|
||||
return items[0];
|
||||
}
|
||||
items.sort();
|
||||
return items;
|
||||
}
|
||||
return t.toLowerCase();
|
||||
});
|
||||
const tag = topics.map((t) => {
|
||||
if (t == null) {
|
||||
return "null";
|
||||
}
|
||||
if (Array.isArray(t)) {
|
||||
return t.join("|");
|
||||
}
|
||||
return t;
|
||||
}).join("&");
|
||||
return { fragment, tag, topics };
|
||||
}
|
||||
async function multiQueryFilter(address, contract, event, fromBlock, toBlock) {
|
||||
if (fromBlock == null) {
|
||||
fromBlock = 0;
|
||||
}
|
||||
if (toBlock == null) {
|
||||
toBlock = "latest";
|
||||
}
|
||||
const { fragment, topics } = await getSubInfo(contract.interface, event);
|
||||
const filter = {
|
||||
address: address === "*" ? void 0 : address,
|
||||
topics,
|
||||
fromBlock,
|
||||
toBlock
|
||||
};
|
||||
const provider = contract.runner;
|
||||
assert(provider, "contract runner does not have a provider", "UNSUPPORTED_OPERATION", { operation: "queryFilter" });
|
||||
return (await provider.getLogs(filter)).map((log) => {
|
||||
let foundFragment = fragment;
|
||||
if (foundFragment == null) {
|
||||
try {
|
||||
foundFragment = contract.interface.getEvent(log.topics[0]);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
if (foundFragment) {
|
||||
try {
|
||||
return new EventLog(log, contract.interface, foundFragment);
|
||||
} catch (error) {
|
||||
return new UndecodedEventLog(log, error);
|
||||
}
|
||||
}
|
||||
return new Log(log, provider);
|
||||
});
|
||||
}
|
||||
class BatchBlockService {
|
||||
provider;
|
||||
onProgress;
|
||||
@ -329,11 +430,14 @@ class BatchEventsService {
|
||||
this.retryMax = retryMax;
|
||||
this.retryOn = retryOn;
|
||||
}
|
||||
async getPastEvents({ fromBlock, toBlock, type }) {
|
||||
async getPastEvents({ address, fromBlock, toBlock, type }) {
|
||||
let err;
|
||||
let retries = 0;
|
||||
while (!this.shouldRetry && retries === 0 || this.shouldRetry && retries < this.retryMax) {
|
||||
try {
|
||||
if (address) {
|
||||
return await multiQueryFilter(address, this.contract, type, fromBlock, toBlock);
|
||||
}
|
||||
return await this.contract.queryFilter(type, fromBlock, toBlock);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
@ -353,14 +457,14 @@ class BatchEventsService {
|
||||
return this.getPastEvents(event);
|
||||
});
|
||||
}
|
||||
async getBatchEvents({ fromBlock, toBlock, type = "*" }) {
|
||||
async getBatchEvents({ address, fromBlock, toBlock, type = "*" }) {
|
||||
if (!toBlock) {
|
||||
toBlock = await this.provider.getBlockNumber();
|
||||
}
|
||||
const eventsToSync = [];
|
||||
for (let i = fromBlock; i < toBlock; i += this.blocksPerRequest) {
|
||||
const j = i + this.blocksPerRequest - 1 > toBlock ? toBlock : i + this.blocksPerRequest - 1;
|
||||
eventsToSync.push({ fromBlock: i, toBlock: j, type });
|
||||
eventsToSync.push({ address, fromBlock: i, toBlock: j, type });
|
||||
}
|
||||
const events = [];
|
||||
const eventChunk = chunk(eventsToSync, this.concurrencySize);
|
||||
@ -2064,7 +2168,7 @@ class BaseEventsService {
|
||||
}
|
||||
}
|
||||
async getLatestEvents({ fromBlock }) {
|
||||
if (this.tovarishClient?.selectedRelayer && !["Deposit", "Withdrawal"].includes(this.type)) {
|
||||
if (this.tovarishClient?.selectedRelayer) {
|
||||
const { events, lastSyncBlock: lastBlock } = await this.tovarishClient.getEvents({
|
||||
type: this.getTovarishType(),
|
||||
fromBlock
|
||||
@ -2228,7 +2332,9 @@ class BaseTornadoService extends BaseEventsService {
|
||||
lastBlock
|
||||
};
|
||||
}
|
||||
return super.getLatestEvents({ fromBlock });
|
||||
return await this.getEventsFromRpc({
|
||||
fromBlock
|
||||
});
|
||||
}
|
||||
}
|
||||
class BaseEchoService extends BaseEventsService {
|
||||
@ -10270,4 +10376,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, getSupportedInstances, getTokenBalances, getTovarishNetworks, getWeightRandom, governanceEventsSchema, hasherBytecode, hexToBytes, initGroth16, isHex, isNode, jobRequestSchema, jobsSchema, labelhash, leBuff2Int, leInt2Buff, loadDBEvents, loadRemoteEvents, makeLabelNodeAndParent, mimc, 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, 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 };
|
||||
|
679
dist/tornado.umd.js
vendored
679
dist/tornado.umd.js
vendored
File diff suppressed because it is too large
Load Diff
2
dist/tornado.umd.min.js
vendored
2
dist/tornado.umd.min.js
vendored
File diff suppressed because one or more lines are too long
174
src/batch.ts
174
src/batch.ts
@ -1,4 +1,4 @@
|
||||
import type {
|
||||
import {
|
||||
Provider,
|
||||
BlockTag,
|
||||
Block,
|
||||
@ -7,9 +7,171 @@ import type {
|
||||
ContractEventName,
|
||||
EventLog,
|
||||
TransactionReceipt,
|
||||
isHexString,
|
||||
assert,
|
||||
assertArgument,
|
||||
DeferredTopicFilter,
|
||||
EventFragment,
|
||||
TopicFilter,
|
||||
Interface,
|
||||
UndecodedEventLog,
|
||||
Log,
|
||||
} from 'ethers';
|
||||
import { chunk, sleep } from './utils';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function isDeferred(value: any): value is DeferredTopicFilter {
|
||||
return (
|
||||
value &&
|
||||
typeof value === 'object' &&
|
||||
'getTopicFilter' in value &&
|
||||
typeof value.getTopicFilter === 'function' &&
|
||||
value.fragment
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from ethers.js as they don't export this function
|
||||
* https://github.com/ethers-io/ethers.js/blob/main/src.ts/contract/contract.ts#L464
|
||||
*/
|
||||
export async function getSubInfo(
|
||||
abiInterface: Interface,
|
||||
event: ContractEventName,
|
||||
): Promise<{
|
||||
fragment: null | EventFragment;
|
||||
tag: string;
|
||||
topics: TopicFilter;
|
||||
}> {
|
||||
let topics: Array<null | string | Array<string>>;
|
||||
let fragment: null | EventFragment = null;
|
||||
|
||||
// Convert named events to topicHash and get the fragment for
|
||||
// events which need deconstructing.
|
||||
|
||||
if (Array.isArray(event)) {
|
||||
const topicHashify = function (name: string): string {
|
||||
if (isHexString(name, 32)) {
|
||||
return name;
|
||||
}
|
||||
const fragment = abiInterface.getEvent(name);
|
||||
assertArgument(fragment, 'unknown fragment', 'name', name);
|
||||
return fragment.topicHash;
|
||||
};
|
||||
|
||||
// Array of Topics and Names; e.g. `[ "0x1234...89ab", "Transfer(address)" ]`
|
||||
topics = event.map((e) => {
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(e)) {
|
||||
return e.map(topicHashify);
|
||||
}
|
||||
return topicHashify(e);
|
||||
});
|
||||
} else if (event === '*') {
|
||||
topics = [null];
|
||||
} else if (typeof event === 'string') {
|
||||
if (isHexString(event, 32)) {
|
||||
// Topic Hash
|
||||
topics = [event];
|
||||
} else {
|
||||
// Name or Signature; e.g. `"Transfer", `"Transfer(address)"`
|
||||
fragment = abiInterface.getEvent(event);
|
||||
assertArgument(fragment, 'unknown fragment', 'event', event);
|
||||
topics = [fragment.topicHash];
|
||||
}
|
||||
} else if (isDeferred(event)) {
|
||||
// Deferred Topic Filter; e.g. `contract.filter.Transfer(from)`
|
||||
topics = await event.getTopicFilter();
|
||||
} else if ('fragment' in event) {
|
||||
// ContractEvent; e.g. `contract.filter.Transfer`
|
||||
fragment = event.fragment;
|
||||
topics = [fragment.topicHash];
|
||||
} else {
|
||||
assertArgument(false, 'unknown event name', 'event', event);
|
||||
}
|
||||
|
||||
// Normalize topics and sort TopicSets
|
||||
topics = topics.map((t) => {
|
||||
if (t == null) {
|
||||
return null;
|
||||
}
|
||||
if (Array.isArray(t)) {
|
||||
const items = Array.from(new Set(t.map((t) => t.toLowerCase())).values());
|
||||
if (items.length === 1) {
|
||||
return items[0];
|
||||
}
|
||||
items.sort();
|
||||
return items;
|
||||
}
|
||||
return t.toLowerCase();
|
||||
});
|
||||
|
||||
const tag = topics
|
||||
.map((t) => {
|
||||
if (t == null) {
|
||||
return 'null';
|
||||
}
|
||||
if (Array.isArray(t)) {
|
||||
return t.join('|');
|
||||
}
|
||||
return t;
|
||||
})
|
||||
.join('&');
|
||||
|
||||
return { fragment, tag, topics };
|
||||
}
|
||||
|
||||
export async function multiQueryFilter(
|
||||
// Single address will scan for a single contract, array for multiple, and * for all contracts with event topic
|
||||
address: string | string[],
|
||||
contract: BaseContract,
|
||||
event: ContractEventName,
|
||||
fromBlock?: BlockTag,
|
||||
toBlock?: BlockTag,
|
||||
) {
|
||||
if (fromBlock == null) {
|
||||
fromBlock = 0;
|
||||
}
|
||||
if (toBlock == null) {
|
||||
toBlock = 'latest';
|
||||
}
|
||||
|
||||
const { fragment, topics } = await getSubInfo(contract.interface, event);
|
||||
|
||||
const filter = {
|
||||
address: address === '*' ? undefined : address,
|
||||
topics,
|
||||
fromBlock,
|
||||
toBlock,
|
||||
};
|
||||
|
||||
const provider = contract.runner as Provider | null;
|
||||
|
||||
assert(provider, 'contract runner does not have a provider', 'UNSUPPORTED_OPERATION', { operation: 'queryFilter' });
|
||||
|
||||
return (await provider.getLogs(filter)).map((log) => {
|
||||
let foundFragment = fragment;
|
||||
if (foundFragment == null) {
|
||||
try {
|
||||
foundFragment = contract.interface.getEvent(log.topics[0]);
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (foundFragment) {
|
||||
try {
|
||||
return new EventLog(log, contract.interface, foundFragment);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (error: any) {
|
||||
return new UndecodedEventLog(log, error);
|
||||
}
|
||||
}
|
||||
|
||||
return new Log(log, provider);
|
||||
});
|
||||
}
|
||||
|
||||
export interface BatchBlockServiceConstructor {
|
||||
provider: Provider;
|
||||
onProgress?: BatchBlockOnProgress;
|
||||
@ -284,6 +446,7 @@ export type BatchEventOnProgress = ({
|
||||
|
||||
// To enable iteration only numbers are accepted for fromBlock input
|
||||
export interface EventInput {
|
||||
address?: string | string[];
|
||||
fromBlock: number;
|
||||
toBlock: number;
|
||||
type: ContractEventName;
|
||||
@ -321,13 +484,16 @@ export class BatchEventsService {
|
||||
this.retryOn = retryOn;
|
||||
}
|
||||
|
||||
async getPastEvents({ fromBlock, toBlock, type }: EventInput): Promise<EventLog[]> {
|
||||
async getPastEvents({ address, fromBlock, toBlock, type }: EventInput): Promise<EventLog[]> {
|
||||
let err;
|
||||
let retries = 0;
|
||||
|
||||
// eslint-disable-next-line no-unmodified-loop-condition
|
||||
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
|
||||
try {
|
||||
if (address) {
|
||||
return (await multiQueryFilter(address, this.contract, type, fromBlock, toBlock)) as EventLog[];
|
||||
}
|
||||
return (await this.contract.queryFilter(type, fromBlock, toBlock)) as EventLog[];
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e: any) {
|
||||
@ -357,7 +523,7 @@ export class BatchEventsService {
|
||||
});
|
||||
}
|
||||
|
||||
async getBatchEvents({ fromBlock, toBlock, type = '*' }: EventInput): Promise<EventLog[]> {
|
||||
async getBatchEvents({ address, fromBlock, toBlock, type = '*' }: EventInput): Promise<EventLog[]> {
|
||||
if (!toBlock) {
|
||||
toBlock = await this.provider.getBlockNumber();
|
||||
}
|
||||
@ -367,7 +533,7 @@ export class BatchEventsService {
|
||||
for (let i = fromBlock; i < toBlock; i += this.blocksPerRequest) {
|
||||
const j = i + this.blocksPerRequest - 1 > toBlock ? toBlock : i + this.blocksPerRequest - 1;
|
||||
|
||||
eventsToSync.push({ fromBlock: i, toBlock: j, type });
|
||||
eventsToSync.push({ address, fromBlock: i, toBlock: j, type });
|
||||
}
|
||||
|
||||
const events = [];
|
||||
|
@ -205,7 +205,7 @@ export class BaseEventsService<EventType extends MinimalEvents> {
|
||||
}
|
||||
|
||||
async getLatestEvents({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<EventType>> {
|
||||
if (this.tovarishClient?.selectedRelayer && !['Deposit', 'Withdrawal'].includes(this.type)) {
|
||||
if (this.tovarishClient?.selectedRelayer) {
|
||||
const { events, lastSyncBlock: lastBlock } = await this.tovarishClient.getEvents<EventType>({
|
||||
type: this.getTovarishType(),
|
||||
fromBlock,
|
||||
@ -423,7 +423,9 @@ export class BaseTornadoService extends BaseEventsService<DepositsEvents | Withd
|
||||
};
|
||||
}
|
||||
|
||||
return super.getLatestEvents({ fromBlock });
|
||||
return await this.getEventsFromRpc({
|
||||
fromBlock,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user