diff --git a/HISTORY.md b/HISTORY.md index 07b9307..2cae1d6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,16 @@ # History +### 2023.04.20 (2023-04-20) + +Did: + +* Syncing is now stable. +* Reorganized some stuff including resources. + +Next: + +* Still testing withdraws, will do some relayer and then monorepo :] (finally excited about something again) + ### 2023.04.18 (2023-04-18) Did: diff --git a/package.json b/package.json index 359b35e..74349f9 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "zk" ], "private": false, - "version": "2023.04.18", + "version": "2023.04.20", "engines": { "node": "^18" }, diff --git a/src/lib/chain.ts b/src/lib/chain.ts index 48bcdfd..6cb4a6c 100644 --- a/src/lib/chain.ts +++ b/src/lib/chain.ts @@ -22,7 +22,7 @@ import { BaseContract, BigNumber, ContractTransaction, providers, Signer, VoidSi import { randomBytes } from 'crypto' // Local modules -import { OnchainData } from 'lib/data' +import { Onchain } from 'lib/data' import { ErrorUtils, HexUtils } from 'lib/utils' // We use a vanilla provider here, but in reality we will probably @@ -52,7 +52,7 @@ export class Chain { } async getChainSymbol(): Promise { - if (!this.symbol) this.symbol = await OnchainData.getNetworkSymbol(String(await this.getChainId())) + if (!this.symbol) this.symbol = await Onchain.getNetworkSymbol(String(await this.getChainId())) return this.symbol } @@ -94,12 +94,12 @@ export class Chain { ): Promise { if (callStruct[0].value) return await Multicall3Contract__factory.connect( - await OnchainData.getMulticall3Address(String(this.chainId)), + await Onchain.getMulticall3Address(String(this.chainId)), this.provider ).populateTransaction.aggregate3Value(callStruct as Array) return await Multicall3Contract__factory.connect( - await OnchainData.getMulticall3Address(String(this.chainId)), + await Onchain.getMulticall3Address(String(this.chainId)), this.provider ).populateTransaction.aggregate3(callStruct) } @@ -110,12 +110,12 @@ export class Chain { if (this.signer) if (callStruct[0].value) return await Multicall3Contract__factory.connect( - await OnchainData.getMulticall3Address(String(this.chainId)), + await Onchain.getMulticall3Address(String(this.chainId)), this.signer ).aggregate3Value(callStruct as Array) else { return await Multicall3Contract__factory.connect( - await OnchainData.getMulticall3Address(String(this.chainId)), + await Onchain.getMulticall3Address(String(this.chainId)), this.provider ).aggregate3(callStruct) } @@ -156,11 +156,7 @@ export namespace Contracts { if (!contractMap.has(key)) { contractMap.set( key, - _getContract( - 'TornadoProxy', - await OnchainData.getProxyAddress(network), - signerOrProvider - ) + _getContract('TornadoProxy', await Onchain.getProxyAddress(network), signerOrProvider) ) } return contractMap.get(`TornadoProxy${network}`) as TornadoProxy @@ -178,7 +174,7 @@ export namespace Contracts { key, _getContract( 'TornadoInstance', - await OnchainData.getInstanceAddress(network, token, denomination), + await Onchain.getInstanceAddress(network, token, denomination), signerOrProvider ) ) diff --git a/src/lib/core.ts b/src/lib/core.ts index 73d73fb..cab71b7 100644 --- a/src/lib/core.ts +++ b/src/lib/core.ts @@ -1,5 +1,5 @@ // ts-essentials -import { DeepRequired, MarkOptional, MarkRequired } from 'ts-essentials' +import { DeepRequired, MarkOptional } from 'ts-essentials' // Local types import { RelayerProperties as RelayerDataProperties } from 'types/sdk/data' @@ -8,6 +8,7 @@ import { ZKDepositData, InputFor } from 'types/sdk/crypto' import { TornadoInstance, TornadoProxy } from 'types/deth' // External imports +import { EventEmitter } from 'stream' import { BigNumber, EventFilter, providers } from 'ethers' import { parseUnits } from 'ethers/lib/utils' import { bigInt } from 'snarkjs' @@ -16,10 +17,10 @@ import { bigInt } from 'snarkjs' import { parseIndexableString } from 'pouchdb-collate' // Local imports -import { Docs, Cache, Types as DataTypes, Json, Constants, OnchainData } from 'lib/data' +import { Docs, Cache, Types as DataTypes, Json, Constants, Onchain } from 'lib/data' import { Primitives } from 'lib/crypto' import { Contracts, Chain } from 'lib/chain' -import { ErrorUtils, ObjectUtils } from 'lib/utils' +import { ErrorUtils, ObjectUtils, AsyncUtils } from 'lib/utils' type Provider = providers.Provider @@ -36,12 +37,13 @@ type RelayerProperties = MarkOptional< 'serviceFeePercent' | 'prices' > -export class Core { +export class Core extends EventEmitter { chain: Chain caches: Map> instances: Map constructor(provider: providers.Provider) { + super() this.chain = new Chain(provider) this.caches = new Map>() this.instances = new Map() @@ -110,8 +112,6 @@ export class Core { const hexNullifierHashes: string[] = [] const purchaseAmounts = options?.ethPurchaseAmounts ?? new Array(zkDepositsData.length) - console.log('\nChecking inputs.\n') - if (zkDepositsData.length !== recipientAddresses.length) throw ErrorUtils.getError( 'Core.buildDepositProofs: the number of recipients must equal the length of zkDepositsData.' @@ -127,14 +127,10 @@ export class Core { hexNullifierHashes.push(deposit.hexNullifierHash) }) - console.log('\nGetting lookup keys.\n') - // Determine cache name - const lookupKeys = await this.getInstanceLookupKeys(instance.address) + const lookupKeys = await Onchain.getInstanceLookupKeys(instance.address) const name = 'Deposits' + (lookupKeys.network + lookupKeys.token + lookupKeys.denomination).toUpperCase() - console.log('\nLeaves and indices.\n') - // Find all leaves & indices by reading from cache const [leaves, leafIndices] = await this._findLeavesAndIndices(name, hexCommitments) const invalidCommitments: string[] = [] @@ -143,8 +139,6 @@ export class Core { const checkSpent = options?.checkNotesSpent !== false const spentNotes: string[] = [] - console.log('\nNote checking.\n') - // If yes, immediately check it with the supplied Tornado Instance const checkSpentArray = checkSpent ? await instance.isSpentArray(hexNullifierHashes) : undefined @@ -161,8 +155,6 @@ export class Core { const commitmentsAreInvalid = invalidCommitments.length !== 0 const notesAreSpent = spentNotes.length !== 0 - console.log('\nErrors.\n') - if (commitmentsAreInvalid || notesAreSpent) throw ErrorUtils.getError( `Core.buildDepositProofs: ` + @@ -176,8 +168,6 @@ export class Core { : '') ) - console.log('\nMerkle tree.\n') - // Otherwise, build the merkle tree from the leaves const merkleTree = Primitives.buildMerkleTree({ height: options?.merkleTreeHeight ?? Constants.MERKLE_TREE_HEIGHT, @@ -193,8 +183,6 @@ export class Core { 'Core.buildDepositProofs: the merkle tree created is not valid, something went wrong with syncing.' ) - console.log('\nProof data invariant.\n') - // Rest of note invariant arguments const inputsForProofs: InputFor.ZKProof[] = [] const gasPrice = options?.gasPrice ?? (await this.chain.getGasPrice()) @@ -207,7 +195,7 @@ export class Core { const decimals = // @ts-expect-error bigInt(10).pow( - options?.tokenDecimals ?? (await OnchainData.getTokenDecimals(lookupKeys.network, lookupKeys.token)) + options?.tokenDecimals ?? (await Onchain.getTokenDecimals(lookupKeys.network, lookupKeys.token)) ) const toWithdraw = BigNumber.from(lookupKeys.denomination).mul(decimals) @@ -217,8 +205,6 @@ export class Core { 'Core.buildDepositProofs: a token price MUST be supplied if the token withdrawn is not native.' ) - console.log('\nConstruct.\n') - // Compute proofs for (let i = 0, len = zkDepositsData.length; i < len; i++) { inputsForProofs.push({ @@ -248,8 +234,6 @@ export class Core { }) } - console.log('\nCalc and return.\n') - return await Primitives.calcDepositProofs(inputsForProofs) } @@ -285,13 +269,13 @@ export class Core { if (indexes) for (let i = 0, len = rows.length; i < len; i++) { const id = parseIndexableString(rows[i].id)[0] - if (id === indexes[i]) docs.push(rows[i].doc) + if (0 < indexes.findIndex(id)) docs.push(rows[i].doc) } else docs = rows.map((row) => row.doc) if (keys) docs.forEach((doc) => { - const idNetworkMatches = doc && keys.network ? keys.network === doc?.network : true + const idNetworkMatches = doc && (keys.network ? keys.network === doc?.network : true) const andTokenSymbolMatches = idNetworkMatches && (keys.token ? keys.token === doc?.token : true) const lastlyDenominationMatches = andTokenSymbolMatches && (keys.denomination ? keys.denomination === doc?.denomination : true) @@ -328,19 +312,19 @@ export class Core { options.backup.invoices = options.backup.invoices ?? true options.backup.notes = options.backup.notes ?? true options.doNotPopulate = options.doNotPopulate ?? true - return this.buildDepositTxs(instances, options) + return this.buildDepositTransactions(instances, options) } - async buildDepositTx( + async buildDepositTransaction( instance: TornadoInstance, options?: Options.Core.Deposit ): Promise { let opts: Options.Core.Deposit = options ?? {} opts.depositsPerInstance = [1] - return (await this.buildDepositTxs([instance], opts))[0] + return (await this.buildDepositTransactions([instance], opts))[0] } - async buildDepositTxs( + async buildDepositTransactions( instances: Array, options?: Options.Core.Deposit ): Promise> { @@ -363,7 +347,7 @@ export class Core { const proxy: TornadoProxy = await Contracts.getProxy(String(chainId), this.chain.provider) for (let i = 0, nInstances = instances.length; i < nInstances; i++) { - const lookupKeys = await this.getInstanceLookupKeys(instances[i].address) + const lookupKeys = await Onchain.getInstanceLookupKeys(instances[i].address) const pathstring = lookupKeys.network + lookupKeys.token + lookupKeys.denomination for (let d = 0, nDeposits = depositsPerInstance[i]; d < nDeposits; d++) { @@ -441,11 +425,25 @@ export class Core { }) } - loadCache>(cacheName: string): C { - if (!this.caches.has(cacheName)) { - this.caches.set(cacheName, new Cache.Base(cacheName)) + loadWithdrawalCache(name: string, options?: Options.Core.Cache): Cache.Withdrawal { + if (!this.caches.has(name)) { + this.caches.set(name, new Cache.Withdrawal(name, options)) } - return this.caches.get(cacheName) as C + return this.caches.get(name) as Cache.Withdrawal + } + + loadDepositCache(name: string, options?: Options.Core.Cache): Cache.Deposit { + if (!this.caches.has(name)) { + this.caches.set(name, new Cache.Deposit(name, options)) + } + return this.caches.get(name) as Cache.Deposit + } + + loadCache>(name: string, options?: Options.Cache.Database): C { + if (!this.caches.has(name)) { + this.caches.set(name, new Cache.Base(name, options)) + } + return this.caches.get(name) as C } async syncMultiple(instances: Array, syncOptions?: Options.Core.Sync): Promise { @@ -456,7 +454,7 @@ export class Core { async sync(instance: TornadoInstance, syncOptions?: Options.Core.Sync): Promise { // Get some data - const lookupKeys = await this.getInstanceLookupKeys(instance.address) + const lookupKeys = await Onchain.getInstanceLookupKeys(instance.address) const populatedSyncOpts = await this._populateSyncOpts(lookupKeys, syncOptions) @@ -464,46 +462,41 @@ export class Core { // Synchronize for (let i = 0, bound = actions.length; i < bound; i++) { - let action = actions[i][0].charAt(0).toUpperCase() + actions[i][0].slice(1) - await this._sync(action, lookupKeys, instance, populatedSyncOpts) + const action = actions[i][0].charAt(0).toUpperCase() + actions[i][0].slice(1) + const pathstring = lookupKeys.network + lookupKeys.token + lookupKeys.denomination + const name = action + 's' + pathstring.toUpperCase() + + if (action == 'Deposit') + await this._sync( + pathstring, + this.loadDepositCache(name, syncOptions?.cache), + instance.filters.Deposit(null, null, null), + instance, + populatedSyncOpts + ) + else if (action == 'Withdrawal') + await this._sync( + pathstring, + this.loadWithdrawalCache(name, syncOptions?.cache), + instance.filters.Withdrawal(null, null, null, null), + instance, + populatedSyncOpts + ) } } private async _sync( - action: string, - lookupKeys: DataTypes.Keys.InstanceLookup, + pathstring: string, + cache: Cache.Syncable, + filter: EventFilter, instance: TornadoInstance, syncOptions: DeepRequired ): Promise { - const name = `${action + 's'}${lookupKeys.network}${lookupKeys.token.toUpperCase()}${ - lookupKeys.denomination - }`, - pathstring = name.substring(action.length).toLowerCase() - - let cache: Cache.Syncable, - toDoc: (_: any) => Docs.Base, - filter: EventFilter, - numEntries: number - - if (action == 'Deposit') { - toDoc = (resp: any) => new Docs.Deposit(resp) - cache = this.caches.has(name) - ? (this.caches.get(name)! as Cache.Deposit) - : new Cache.Deposit(name, syncOptions.cache) - filter = instance.filters.Deposit(null, null, null) - } else { - toDoc = (resp: any) => new Docs.Withdrawal(resp) - cache = this.caches.has(name) - ? (this.caches.get(name)! as Cache.Withdrawal) - : new Cache.Withdrawal(name, syncOptions.cache) - filter = instance.filters.Withdrawal(null, null, null, null) - } - // Assign pooler - cache.sync.pooler = await cache.sync.initializePooler(cache.getCallbacks(instance)) + cache.sync.initializePooler(cache.getCallbacks(instance), cache.getErrorHandlers()) // Decide whether we have a latest block - numEntries = (await cache.db.info()).doc_count + const numEntries = (await cache.db.info()).doc_count // Check for synced blocks if (0 < numEntries) { @@ -518,6 +511,15 @@ export class Core { // Start synchronizing let dbPromises = [] + this.emit( + 'debug', + syncOptions.blocks.startBlock, + syncOptions.blocks.targetBlock, + syncOptions.blocks.blockDelta + ) + + this.emit('sync', 'syncing') + for ( let currentBlock = syncOptions.blocks.startBlock, blockDelta = syncOptions.blocks.blockDelta, @@ -526,37 +528,46 @@ export class Core { currentBlock < targetBlock; currentBlock += blockDelta ) { - if (cache.sync.pooler.pending < concurrencyLimit) { + if (cache.sync.pooler!.pending < concurrencyLimit) { const sum = currentBlock + blockDelta + + await AsyncUtils.timeout(syncOptions.msTimeout) + if (currentBlock + blockDelta < targetBlock) { - await cache.sync.pooler.pool(currentBlock, sum) + await cache.sync.pooler!.pool(currentBlock, sum) } else { - await cache.sync.pooler.pool(currentBlock, sum - (sum % targetBlock)) + await cache.sync.pooler!.pool(currentBlock, sum - (sum % targetBlock)) } + + this.emit('debug', currentBlock++, sum) } else { - let res: Array = await cache.sync.pooler.race() + let res: Array = await cache.sync.pooler!.race() + if (res.length != 0) dbPromises.push( - cache.db.bulkDocs(res.map((el) => toDoc(el))).catch((err) => { + cache.db.bulkDocs(res.map((el) => cache.buildDoc(el))).catch((err) => { throw ErrorUtils.ensureError(err) }) ) + currentBlock -= blockDelta } } + this.emit('sync', 'synced') + // Immediately start listening if we're doing this if (syncOptions.cache.sync.listen) instance = instance.on(filter, (...eventArgs) => { - cache.db.put(toDoc(eventArgs[eventArgs.length - 1])) + cache.db.put(cache.buildDoc(eventArgs[eventArgs.length - 1])) }) // Then wait for all pooler requests to resolve - let results = await cache.sync.pooler.all() + let results = await cache.sync.pooler!.all() // Then transform them, we know the shape in forward results = results.reduce((res: any[], response: any[]) => { - if (response[0]) response.forEach((el: any) => res.push(toDoc(el))) + if (response[0]) response.forEach((el: any) => res.push(cache.buildDoc(el))) return res }, []) @@ -570,7 +581,7 @@ export class Core { // Finally, store the objects if (!this.instances.has(pathstring)) this.instances.set(pathstring, instance) - if (!this.caches.has(name)) this.caches.set(name, cache) + if (!this.caches.has(cache.name)) this.caches.set(cache.name, cache) } private async _populateSyncOpts( @@ -594,16 +605,16 @@ export class Core { // blocks syncOptions.blocks.startBlock = syncOptions.blocks.startBlock ?? - (await OnchainData.getInstanceDeployBlockNum( - lookupKeys.network, - lookupKeys.token, - lookupKeys.denomination - )) + (await Onchain.getInstanceDeployBlockNum(lookupKeys.network, lookupKeys.token, lookupKeys.denomination)) syncOptions.blocks.targetBlock = syncOptions.blocks.targetBlock ?? (await this.chain.latestBlockNum()) + syncOptions.blocks.deltaDivisor = syncOptions.blocks.deltaDivisor ?? 100 + syncOptions.blocks.blockDelta = this._getBlockDelta(syncOptions) + syncOptions.msTimeout = syncOptions.msTimeout ?? 200 // 5 requests per second + // cache // db syncOptions.cache.db.persistent = syncOptions.cache.db.persistent ?? true @@ -618,8 +629,8 @@ export class Core { private _getBlockDelta(syncOptions?: Options.Core.Sync): number { return Math.floor( - syncOptions?.blocks?.blockDelta ?? - (syncOptions!.blocks!.targetBlock! - syncOptions!.blocks!.startBlock!) / 20 + (syncOptions!.blocks!.targetBlock! - syncOptions!.blocks!.startBlock!) / + syncOptions!.blocks!.deltaDivisor! ) } @@ -670,25 +681,6 @@ export class Core { // Concat matched and all leaf indices return [leaves, indices] } - - async getInstanceLookupKeys(instanceAddress: string): Promise { - // lookup some stuff first - const lookupObj: { [key: string]: string } = Json.getValue(await Json.load('onchain/quickLookup.json'), [ - 'instanceAddresses' - ]) - - const pathstring: string = Object.entries(lookupObj).find((el) => el[1] === instanceAddress)![0] - - const network = pathstring.match('[0-9]+')![0], - token = pathstring.substring(network.length).match('[a-z]+')![0], - denomination = pathstring.substring(network.length + token.length) - - return { - network: network, - token: token, - denomination: denomination - } - } } export { Transactions, Options } diff --git a/src/lib/data.ts b/src/lib/data.ts index c33b314..c9345d3 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -26,12 +26,15 @@ PouchDB.plugin(PouchDBAdapterMemory) export namespace Files { export type PathGetter = (relative: string) => string - export const getModulesPath = (relative: string): string => __dirname + '/../../node_modules/' + relative - export const getResourcePath = (relative: string): string => __dirname + '/../resources/' + relative - export const getCachePath = (relative: string): string => __dirname + '/../../cache/' + relative + export const getModulesPath = (relative: string, prefix?: string): string => + (prefix ?? __dirname + '/../../node_modules/') + relative + export const getResourcePath = (relative: string, prefix?: string): string => + (prefix ?? __dirname + '/../resources/') + relative + export const getCachePath = (relative: string, prefix?: string): string => + (prefix ?? __dirname + '/../../cache/') + relative - export const cacheDirExists = (): boolean => existsSync(getCachePath('')) - export const makeCacheDir = (): void => mkdirSync(getCachePath('')) + export const cacheDirExists = (prefix?: string): boolean => existsSync(getCachePath('', prefix)) + export const makeCacheDir = (prefix?: string): void => mkdirSync(getCachePath('', prefix)) export const loadRaw = (relative: string): Promise => readFile(getResourcePath(relative)) @@ -123,7 +126,7 @@ export namespace Json { // TODO: Decide whether to also cache the data instead of just loading it for the function call -export namespace OnchainData { +export namespace Onchain { export async function getClassicInstanceData( network: string, token: string, @@ -141,24 +144,43 @@ export namespace OnchainData { } } - export async function getInstanceAttrSet( - key: string, + export async function getInstanceLookupKeys(instanceAddress: string): Promise { + // lookup some stuff first + const lookupObj: { [key: string]: string } = await Json.load('onchain/instanceAddresses.json') + + const pathstring: string = Object.entries(lookupObj).find((el) => el[1] === instanceAddress)![0] + + const network = pathstring.match('[0-9]+')![0], + token = pathstring.substring(network.length).match('[a-z]+')![0], + denomination = pathstring.substring(network.length + token.length) + + return { + network: network, + token: token, + denomination: denomination + } + } + + export async function getPathstringBasedContent( + filepath: string, paths: Array<{ network?: string token?: string denomination?: string }> ): Promise> { - const obj = await Json.load('onchain/quickLookup.json') + const obj = await Json.load(filepath) return await Promise.all( paths.map((path) => - Json.getValue(obj, [key, `${path.network ?? '\0'}${path.token ?? '\0'}${path.denomination ?? '\0'}`]) + Json.getValue(obj, [`${path.network ?? '\0'}${path.token ?? '\0'}${path.denomination ?? '\0'}`]) ) ) } export async function getNetworkSymbol(networkId: string): Promise { - return (await getInstanceAttrSet('networkSymbols', [{ network: networkId }]))[0] + return ( + await getPathstringBasedContent('onchain/networkSymbols.json', [{ network: networkId }]) + )[0] } export function getInstanceAddresses( @@ -168,7 +190,7 @@ export namespace OnchainData { denomination: string }> ): Promise> { - return getInstanceAttrSet('instanceAddresses', paths) + return getPathstringBasedContent('onchain/instanceAddresses.json', paths) } export async function getInstanceAddress( @@ -186,7 +208,7 @@ export namespace OnchainData { denomination: string }> ): Promise> { - return getInstanceAttrSet('deployedBlockNumber', paths) + return getPathstringBasedContent('onchain/deployedBlockNumbers.json', paths) } export async function getInstanceDeployBlockNum( @@ -220,12 +242,22 @@ export namespace OnchainData { } } + export async function getTokenAddress(network: string, token: string): Promise { + return ( + await getPathstringBasedContent('onchain/tokenAddresses.json', [ + { network: network, token: token } + ]) + )[0] + } + export async function getTokenDecimals(network: string, token: string): Promise { - return (await getTokenData(network, token)).decimals + return ( + await getPathstringBasedContent('onchain/decimals.json', [{ network: network, token: token }]) + )[0] } } -export namespace OffchainData { +export namespace Offchain { export async function getUncensoredRpcURL(network: string, name: string = ''): Promise { const rpcs = Json.toMap( Json.getValue(await Json.load('offchain/infrastructure.json'), ['jrpc-uncensored', network]) @@ -366,9 +398,12 @@ export namespace Docs { export namespace Cache { export class Base { + name: string db: PouchDB.Database constructor(name: string, options?: Options.Cache.Database) { + this.name = name + if (options?.persistent === false && options?.adapter !== 'memory' && options?.adapter !== null) throw ErrorUtils.getError('Cache.new: if not persistent, cache must use memory adapter.') @@ -404,8 +439,12 @@ export namespace Cache { this.sync = new AsyncUtils.Sync(options?.sync) } + abstract buildDoc(response: any): Docs.Base + abstract getCallbacks(...args: Array): Array + abstract getErrorHandlers(...args: Array): Array + async close(): Promise { if (this.sync.pooler!.pending) throw ErrorUtils.getError("Syncable.close: can't clear while pooler still has pending promises.") @@ -419,7 +458,40 @@ export namespace Cache { } } + function tornadoSyncErrorHandler( + err: Error, + numResolvedPromises: number, + callbackIndex: number, + orderIndex: number, + ...args: any[] + ): void { + err = ErrorUtils.ensureError(err) + + if (err.message.match('context deadline exceeded')) + console.error( + ErrorUtils.getError( + `Context deadline exceeded, stop if more promises do not resolve. Resolved: ${numResolvedPromises}` + ) + ) + else if (err.message.match('Invalid JSON RPC')) + console.error( + ErrorUtils.getError(`Endpoint returned invalid value (we might be rate limited), retrying.`) + ) + else { + err.message += `\nCallback args supplied: [${args.join(', ')}]\n` + throw err + } + } + export class Deposit extends Syncable { + buildDoc(response: any): Docs.Deposit { + return new Docs.Deposit(response) + } + + getErrorHandlers(): Array { + return [tornadoSyncErrorHandler] + } + getCallbacks(instance: TornadoInstance): Array { return [ (fromBlock: number, toBlock: number) => { @@ -430,6 +502,14 @@ export namespace Cache { } export class Withdrawal extends Syncable { + buildDoc(response: any): Docs.Withdrawal { + return new Docs.Withdrawal(response) + } + + getErrorHandlers(): Array { + return [tornadoSyncErrorHandler] + } + getCallbacks(instance: TornadoInstance): Array { return [ (fromBlock: number, toBlock: number) => { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index e632f2d..62ee4bd 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,7 +2,8 @@ import * as Crypto from 'types/sdk/crypto' import { Options } from 'types/sdk/core' -// Needed +// External imports +import assert from 'assert' import { BigNumber } from 'ethers' import { bigInt } from 'snarkjs' import { randomBytes } from 'crypto' @@ -22,7 +23,7 @@ export namespace ErrorUtils { export function getError(message: string): Error { let error = new Error(message) - error.name = 'Error (tornado-sdk)' + error.name = '\nError (tornado-sdk)' return error } } @@ -34,25 +35,34 @@ export namespace AsyncUtils { export type Callback = (...values: any[]) => Promise + export type ErrorHandler = ( + err: Error, + numResolvedPromises: number, + callbackIndex: number, + orderIndex: number, + ...args: any[] + ) => void + export class PromisePooler { concurrencyLimit: number private _totalAdded: number _results: Array _callbacks: Array + _errorHandlers: Array private _promises: Array> - constructor(callbacks: Array, concurrencyLimit: number) { + constructor(callbacks: Array, errorHandlers: Array, concurrencyLimit: number) { if (callbacks.length == 0) throw ErrorUtils.getError('PromisePooler: callbacks are empty.') if (concurrencyLimit <= 0) throw ErrorUtils.getError("PromisePooler: concurrencyLimit can't be 0 or less.") this.concurrencyLimit = concurrencyLimit - this._totalAdded = 0 this._results = [] this._promises = [] this._callbacks = callbacks + this._errorHandlers = errorHandlers } get pending(): number { @@ -98,10 +108,21 @@ export namespace AsyncUtils { } } + // TODO: Immediately set new callbacks and error handlers async reset(): Promise> { let results = await this.all() - this._callbacks = [] + + assert( + this._promises.length === 0, + ErrorUtils.getError('PromisePooler.reset: Resetting should have allowed all promises to resolve.') + ) + + this._results = [] this._totalAdded = 0 + + this._callbacks = [] + this._errorHandlers = [] + return results } @@ -113,29 +134,42 @@ export namespace AsyncUtils { return this._pool(callbackIndex + 1, orderIndex, ...results) } else { let result = results.length == 1 ? results[0] : results - this._promises.splice( - this._promises.findIndex((_promise) => _promise.orderIndex == orderIndex), - 1 - ) + this._promises.splice(this._getPromiseIndex(orderIndex), 1) this._results.push(result) return result } }, - (err) => { - throw ErrorUtils.ensureError(err) + async (err: Error) => { + let resolved = this._totalAdded - this.concurrencyLimit + resolved = resolved < 0 ? 0 : resolved + // Throw inside to abort + this._errorHandlers[callbackIndex]( + ErrorUtils.ensureError(err), + resolved, + callbackIndex, + orderIndex, + ...args + ) + return this._pool(callbackIndex, orderIndex, ...args) } ) promise.orderIndex = orderIndex - if (callbackIndex == 0) { + const promiseIndex = this._getPromiseIndex(orderIndex) + + if (promiseIndex < 0) { this._promises.push(promise) } else { - this._promises[this._promises.findIndex((_promise) => _promise.orderIndex == orderIndex)] = promise + this._promises[promiseIndex] = promise } return promise } + private _getPromiseIndex(orderIndex: number): number { + return this._promises.findIndex((_promise) => _promise.orderIndex == orderIndex) + } + private async _waitIfFull(): Promise { if (this.concurrencyLimit <= this._promises.length) { await Promise.race(this._promises) @@ -153,15 +187,14 @@ export namespace AsyncUtils { this.listen = options?.listen ?? false } - async initializePooler(callbacks: Array): Promise { - return new PromisePooler(callbacks, this.concurrencyLimit) + initializePooler(callbacks: Array, errorHandlers: Array): void { + if (this.pooler) this.pooler.reset() + this.pooler = new PromisePooler(callbacks, errorHandlers, this.concurrencyLimit) } + } - async newCallbacks(callbacks: Array): Promise> { - let results = this.pooler!.reset() - this.pooler!._callbacks = callbacks - return results - } + export function timeout(msTimeout: number): Promise { + return new Promise((resolve) => setTimeout(resolve, msTimeout)) } } diff --git a/src/lib/web.ts b/src/lib/web.ts index e502505..fae5d12 100644 --- a/src/lib/web.ts +++ b/src/lib/web.ts @@ -49,7 +49,9 @@ export class TorProvider extends Web3Provider { agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, // The XHR2 XMLHttpRequest assigns a Tor Browser header by itself. // But if in Browser we assign just in case. - headers: typeof window !== 'undefined' ? headers : undefined + headers: typeof window !== 'undefined' ? headers : undefined, + // 1 minute timeout, although not sure if this is behaving properly + timeout: 60000 }), network ) @@ -135,10 +137,12 @@ export class Relayer { this._serviceFee = properties['tornadoServiceFee'] this._miningFee = properties['miningFee'] this._status = properties['health']['status'] + this._prices = Object.entries(properties['ethPrices']).reduce( (map, entry) => map.set(entry[0], BigNumber.from(entry[1])), new Map() ) + this._fetched = true return { diff --git a/src/resources/onchain/decimals.json b/src/resources/onchain/decimals.json new file mode 100644 index 0000000..73a0a9e --- /dev/null +++ b/src/resources/onchain/decimals.json @@ -0,0 +1 @@ +{"1eth":18,"1dai":18,"1cdai":8,"1usdc":6,"1usdt":6,"1wbtc":8,"5eth":18,"5dai":18,"5cdai":8,"5usdc":6,"5usdt":6,"5wbtc":8,"10eth":18,"56bnb":18,"100xdai":18,"137matic":18,"42161eth":18,"43114avax":18} \ No newline at end of file diff --git a/src/resources/onchain/deployedBlockNumbers.json b/src/resources/onchain/deployedBlockNumbers.json new file mode 100644 index 0000000..219954a --- /dev/null +++ b/src/resources/onchain/deployedBlockNumbers.json @@ -0,0 +1,66 @@ +{ + "1eth0.1": 9116966, + "1eth1": 9117609, + "1eth10": 9117720, + "1eth100": 9161895, + "1dai100": 9117612, + "1dai1000": 9161917, + "1dai10000": 12066007, + "1dai100000": 12066048, + "1cdai5000": 9161938, + "1cdai50000": 12069037, + "1cdai500000": 12067606, + "1cdai5000000": 12066053, + "1usdc100": 9161958, + "1usdc1000": 9161965, + "1usdc10000": null, + "1usdc100000": null, + "1usdt100": 9162005, + "1usdt1000": 9162012, + "1usdt10000": null, + "1usdt100000": null, + "1wbtc0.1": 12067529, + "1wbtc1": 12066652, + "1wbtc10": 12067591, + "1wbtc100": null, + "5eth0.1": 3782581, + "5eth1": 3782590, + "5eth10": 3782593, + "5eth100": 3782596, + "5dai100": 4339088, + "5dai1000": 4367659, + "5dai10000": 4441492, + "5dai100000": 4441488, + "5cdai5000": 4441443, + "5cdai50000": 4441489, + "5cdai500000": 4441493, + "5cdai5000000": 4441489, + "5usdc100": 4441426, + "5usdc1000": 4441492, + "5usdc10000": null, + "5usdc100000": null, + "5usdt100": 4441490, + "5usdt1000": 4441492, + "5usdt10000": null, + "5usdt100000": null, + "5wbtc0.1": 4441488, + "5wbtc1": 4441490, + "5wbtc10": 4441490, + "5wbtc100": null, + "56bnb0.1": 8159279, + "56bnb1": 8159286, + "56bnb10": 8159290, + "56bnb100": 8159296, + "100xdai100": 17754566, + "100xdai1000": 17754568, + "100xdai10000": 17754572, + "100xdai100000": 17754574, + "137matic100": 16258013, + "137matic1000": 16258032, + "137matic10000": 16258046, + "137matic100000": 16258053, + "42161eth0.1": 3300000, + "42161eth1": 3300000, + "42161eth10": 3300000, + "42161eth100": 3300000 +} \ No newline at end of file diff --git a/src/resources/onchain/instanceAddresses.json b/src/resources/onchain/instanceAddresses.json new file mode 100644 index 0000000..300e103 --- /dev/null +++ b/src/resources/onchain/instanceAddresses.json @@ -0,0 +1,66 @@ +{ + "1eth0.1": "0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc", + "1eth1": "0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936", + "1eth10": "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF", + "1eth100": "0xA160cdAB225685dA1d56aa342Ad8841c3b53f291", + "1dai100": "0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3", + "1dai1000": "0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144", + "1dai10000": "0x07687e702b410Fa43f4cB4Af7FA097918ffD2730", + "1dai100000": "0x23773E65ed146A459791799d01336DB287f25334", + "1cdai5000": "0x22aaA7720ddd5388A3c0A3333430953C68f1849b", + "1cdai50000": "0x03893a7c7463AE47D46bc7f091665f1893656003", + "1cdai500000": "0x2717c5e28cf931547B621a5dddb772Ab6A35B701", + "1cdai5000000": "0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af", + "1usdc100": "0xd96f2B1c14Db8458374d9Aca76E26c3D18364307", + "1usdc1000": "0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D", + "1usdc10000": null, + "1usdc100000": null, + "1usdt100": "0x169AD27A470D064DEDE56a2D3ff727986b15D52B", + "1usdt1000": "0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f", + "1usdt10000": null, + "1usdt100000": null, + "1wbtc0.1": "0x178169B423a011fff22B9e3F3abeA13414dDD0F1", + "1wbtc1": "0x610B717796ad172B316836AC95a2ffad065CeaB4", + "1wbtc10": "0xbB93e510BbCD0B7beb5A853875f9eC60275CF498", + "1wbtc100": null, + "5eth0.1": "0x6Bf694a291DF3FeC1f7e69701E3ab6c592435Ae7", + "5eth1": "0x3aac1cC67c2ec5Db4eA850957b967Ba153aD6279", + "5eth10": "0x723B78e67497E85279CB204544566F4dC5d2acA0", + "5eth100": "0x0E3A09dDA6B20aFbB34aC7cD4A6881493f3E7bf7", + "5dai100": "0x76D85B4C0Fc497EeCc38902397aC608000A06607", + "5dai1000": "0xCC84179FFD19A1627E79F8648d09e095252Bc418", + "5dai10000": "0xD5d6f8D9e784d0e26222ad3834500801a68D027D", + "5dai100000": "0x407CcEeaA7c95d2FE2250Bf9F2c105aA7AAFB512", + "5cdai5000": "0x833481186f16Cece3f1Eeea1a694c42034c3a0dB", + "5cdai50000": "0xd8D7DE3349ccaA0Fde6298fe6D7b7d0d34586193", + "5cdai500000": "0x8281Aa6795aDE17C8973e1aedcA380258Bc124F9", + "5cdai5000000": "0x57b2B8c82F065de8Ef5573f9730fC1449B403C9f", + "5usdc100": "0x05E0b5B40B7b66098C2161A5EE11C5740A3A7C45", + "5usdc1000": "0x23173fE8b96A4Ad8d2E17fB83EA5dcccdCa1Ae52", + "5usdc10000": null, + "5usdc100000": null, + "5usdt100": "0x538Ab61E8A9fc1b2f93b3dd9011d662d89bE6FE6", + "5usdt1000": "0x94Be88213a387E992Dd87DE56950a9aef34b9448", + "5usdt10000": null, + "5usdt100000": null, + "5wbtc0.1": "0x242654336ca2205714071898f67E254EB49ACdCe", + "5wbtc1": "0x776198CCF446DFa168347089d7338879273172cF", + "5wbtc10": "0xeDC5d01286f99A066559F60a585406f3878a033e", + "5wbtc100": null, + "56bnb0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F", + "56bnb1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3", + "56bnb10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a", + "56bnb100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD", + "100xdai100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD", + "100xdai1000": "0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178", + "100xdai10000": "0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040", + "100xdai100000": "0xa5C2254e4253490C54cef0a4347fddb8f75A4998", + "137matic100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD", + "137matic1000": "0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178", + "137matic10000": "0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040", + "137matic100000": "0xa5C2254e4253490C54cef0a4347fddb8f75A4998", + "42161eth0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F", + "42161eth1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3", + "42161eth10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a", + "42161eth100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD" +} \ No newline at end of file diff --git a/src/resources/onchain/networkSymbols.json b/src/resources/onchain/networkSymbols.json new file mode 100644 index 0000000..eaa8ce6 --- /dev/null +++ b/src/resources/onchain/networkSymbols.json @@ -0,0 +1,8 @@ +{ + "1": "eth", + "5": "eth", + "56": "bnb", + "100": "xdai", + "137": "matic", + "42161": "eth" +} \ No newline at end of file diff --git a/src/resources/onchain/quickLookup.json b/src/resources/onchain/quickLookup.json deleted file mode 100644 index 1ef4887..0000000 --- a/src/resources/onchain/quickLookup.json +++ /dev/null @@ -1,142 +0,0 @@ -{ - "instanceAddresses": { - "1eth0.1": "0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc", - "1eth1": "0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936", - "1eth10": "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF", - "1eth100": "0xA160cdAB225685dA1d56aa342Ad8841c3b53f291", - "1dai100": "0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3", - "1dai1000": "0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144", - "1dai10000": "0x07687e702b410Fa43f4cB4Af7FA097918ffD2730", - "1dai100000": "0x23773E65ed146A459791799d01336DB287f25334", - "1cdai5000": "0x22aaA7720ddd5388A3c0A3333430953C68f1849b", - "1cdai50000": "0x03893a7c7463AE47D46bc7f091665f1893656003", - "1cdai500000": "0x2717c5e28cf931547B621a5dddb772Ab6A35B701", - "1cdai5000000": "0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af", - "1usdc100": "0xd96f2B1c14Db8458374d9Aca76E26c3D18364307", - "1usdc1000": "0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D", - "1usdc10000": null, - "1usdc100000": null, - "1usdt100": "0x169AD27A470D064DEDE56a2D3ff727986b15D52B", - "1usdt1000": "0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f", - "1usdt10000": null, - "1usdt100000": null, - "1wbtc0.1": "0x178169B423a011fff22B9e3F3abeA13414dDD0F1", - "1wbtc1": "0x610B717796ad172B316836AC95a2ffad065CeaB4", - "1wbtc10": "0xbB93e510BbCD0B7beb5A853875f9eC60275CF498", - "1wbtc100": null, - "5eth0.1": "0x6Bf694a291DF3FeC1f7e69701E3ab6c592435Ae7", - "5eth1": "0x3aac1cC67c2ec5Db4eA850957b967Ba153aD6279", - "5eth10": "0x723B78e67497E85279CB204544566F4dC5d2acA0", - "5eth100": "0x0E3A09dDA6B20aFbB34aC7cD4A6881493f3E7bf7", - "5dai100": "0x76D85B4C0Fc497EeCc38902397aC608000A06607", - "5dai1000": "0xCC84179FFD19A1627E79F8648d09e095252Bc418", - "5dai10000": "0xD5d6f8D9e784d0e26222ad3834500801a68D027D", - "5dai100000": "0x407CcEeaA7c95d2FE2250Bf9F2c105aA7AAFB512", - "5cdai5000": "0x833481186f16Cece3f1Eeea1a694c42034c3a0dB", - "5cdai50000": "0xd8D7DE3349ccaA0Fde6298fe6D7b7d0d34586193", - "5cdai500000": "0x8281Aa6795aDE17C8973e1aedcA380258Bc124F9", - "5cdai5000000": "0x57b2B8c82F065de8Ef5573f9730fC1449B403C9f", - "5usdc100": "0x05E0b5B40B7b66098C2161A5EE11C5740A3A7C45", - "5usdc1000": "0x23173fE8b96A4Ad8d2E17fB83EA5dcccdCa1Ae52", - "5usdc10000": null, - "5usdc100000": null, - "5usdt100": "0x538Ab61E8A9fc1b2f93b3dd9011d662d89bE6FE6", - "5usdt1000": "0x94Be88213a387E992Dd87DE56950a9aef34b9448", - "5usdt10000": null, - "5usdt100000": null, - "5wbtc0.1": "0x242654336ca2205714071898f67E254EB49ACdCe", - "5wbtc1": "0x776198CCF446DFa168347089d7338879273172cF", - "5wbtc10": "0xeDC5d01286f99A066559F60a585406f3878a033e", - "5wbtc100": null, - "56bnb0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F", - "56bnb1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3", - "56bnb10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a", - "56bnb100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD", - "100xdai100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD", - "100xdai1000": "0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178", - "100xdai10000": "0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040", - "100xdai100000": "0xa5C2254e4253490C54cef0a4347fddb8f75A4998", - "137matic100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD", - "137matic1000": "0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178", - "137matic10000": "0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040", - "137matic100000": "0xa5C2254e4253490C54cef0a4347fddb8f75A4998", - "42161eth0.1": "0x84443CFd09A48AF6eF360C6976C5392aC5023a1F", - "42161eth1": "0xd47438C816c9E7f2E2888E060936a499Af9582b3", - "42161eth10": "0x330bdFADE01eE9bF63C209Ee33102DD334618e0a", - "42161eth100": "0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD" - }, - "deployedBlockNumber": { - "1eth0.1": 9116966, - "1eth1": 9117609, - "1eth10": 9117720, - "1eth100": 9161895, - "1dai100": 9117612, - "1dai1000": 9161917, - "1dai10000": 12066007, - "1dai100000": 12066048, - "1cdai5000": 9161938, - "1cdai50000": 12069037, - "1cdai500000": 12067606, - "1cdai5000000": 12066053, - "1usdc100": 9161958, - "1usdc1000": 9161965, - "1usdc10000": null, - "1usdc100000": null, - "1usdt100": 9162005, - "1usdt1000": 9162012, - "1usdt10000": null, - "1usdt100000": null, - "1wbtc0.1": 12067529, - "1wbtc1": 12066652, - "1wbtc10": 12067591, - "1wbtc100": null, - "5eth0.1": 3782581, - "5eth1": 3782590, - "5eth10": 3782593, - "5eth100": 3782596, - "5dai100": 4339088, - "5dai1000": 4367659, - "5dai10000": 4441492, - "5dai100000": 4441488, - "5cdai5000": 4441443, - "5cdai50000": 4441489, - "5cdai500000": 4441493, - "5cdai5000000": 4441489, - "5usdc100": 4441426, - "5usdc1000": 4441492, - "5usdc10000": null, - "5usdc100000": null, - "5usdt100": 4441490, - "5usdt1000": 4441492, - "5usdt10000": null, - "5usdt100000": null, - "5wbtc0.1": 4441488, - "5wbtc1": 4441490, - "5wbtc10": 4441490, - "5wbtc100": null, - "56bnb0.1": 8159279, - "56bnb1": 8159286, - "56bnb10": 8159290, - "56bnb100": 8159296, - "100xdai100": 17754566, - "100xdai1000": 17754568, - "100xdai10000": 17754572, - "100xdai100000": 17754574, - "137matic100": 16258013, - "137matic1000": 16258032, - "137matic10000": 16258046, - "137matic100000": 16258053, - "42161eth0.1": 3300000, - "42161eth1": 3300000, - "42161eth10": 3300000, - "42161eth100": 3300000 - }, - "networkSymbols": { - "1": "eth", - "5": "eth", - "56": "bnb", - "100": "xdai", - "137": "matic", - "42161": "eth" - } -} \ No newline at end of file diff --git a/src/resources/onchain/tokenAddresses.json b/src/resources/onchain/tokenAddresses.json new file mode 100644 index 0000000..cb075c5 --- /dev/null +++ b/src/resources/onchain/tokenAddresses.json @@ -0,0 +1 @@ +{"1dai":"0x6B175474E89094C44Da98b954EedeAC495271d0F","1cdai":"0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643","1usdc":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","1usdt":"0xdAC17F958D2ee523a2206206994597C13D831ec7","1wbtc":"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599","5dai":"0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60","5cdai":"0x822397d9a55d0fefd20F5c4bCaB33C5F65bd28Eb","5usdc":"0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C","5usdt":"0xb7FC2023D96AEa94Ba0254AA5Aeb93141e4aad66","5wbtc":"0xC04B0d3107736C32e19F1c62b2aF67BE61d63a05"} \ No newline at end of file diff --git a/src/test/main.test.ts b/src/test/main.test.ts index 55d2588..6e342fc 100644 --- a/src/test/main.test.ts +++ b/src/test/main.test.ts @@ -9,7 +9,7 @@ import { ERC20, TornadoInstance } from 'types/deth' import { Json } from 'types/sdk/data' import { Core } from 'lib/core' import { Chain, Contracts } from 'lib/chain' -import { Docs, Files, OnchainData, Cache } from 'lib/data' +import { Docs, Files, Onchain, Cache } from 'lib/data' import { ErrorUtils } from 'lib/utils' import { TorProvider } from 'lib/web' @@ -24,7 +24,7 @@ chai.use(solidity) const expect = chai.expect -describe.only('main', () => { +describe('main', () => { const torify = process.env.TORIFY === 'true' if (!process.env.ETH_MAINNET_TEST_RPC) throw ErrorUtils.getError('need a mainnet rpc endpoint.') @@ -34,11 +34,11 @@ describe.only('main', () => { 'Also, we are using ganache because we just need a forked blockchain and not an entire environment. 🐧' ) - let daiData: Json.TokenData + let daiAddress: string const daiWhale = '0x5777d92f208679db4b9778590fa3cab3ac9e2168' // Uniswap V3 Something/Dai Pool - const mainnetProvider = torify - ? new TorProvider(process.env.ETH_MAINNET_TEST_RPC, { port: +process.env.TOR_PORT! }) + const mainnetProvider: providers.Provider = torify + ? new TorProvider(process.env.ETH_MAINNET_TEST_RPC!, { port: +process.env.TOR_PORT! }) : new providers.JsonRpcProvider(process.env.ETH_MAINNET_TEST_RPC) const _ganacheProvider = ganache.provider({ @@ -54,6 +54,7 @@ describe.only('main', () => { // @ts-expect-error const ganacheProvider = new providers.Web3Provider(_ganacheProvider) + const chain = new Chain(ganacheProvider) let snapshotId: any @@ -64,7 +65,7 @@ describe.only('main', () => { }) describe('namespace Tornado', () => { - describe.skip('namespace Contracts', () => { + describe('namespace Contracts', () => { it('getClassicInstance: should be able to get a tornado instance', async () => { let instance = await Contracts.getInstance(String(1), 'eth', String(1), mainnetProvider) expect(instance.address).to.equal('0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936') @@ -73,21 +74,30 @@ describe.only('main', () => { }) describe('class Classic', () => { - it.skip('sync: Should be able to fetch deposit events', async () => { + it.only('sync: Should be able to fetch deposit events', async function () { const core = new Core(mainnetProvider) const instance = await Contracts.getInstance(String(1), 'eth', String(0.1), mainnetProvider) - //const targetBlock = 16928712 - //const startBlock = targetBlock - 7200 + + // For safety + expect(torify).to.be.true + + core.on('debug', function (...args) { + if (args.length === 3) { + console.debug(`\nSync will be started with SB: ${args[0]}, TB: ${args[1]}, BD: ${args[2]}\n`) + } else if (args.length == 2) { + console.debug(`Syncing from block ${args[0]} to ${args[1]}`) + } + }) + + // This is going to try syncing the entire range + await core.sync(instance, { - //deposit: true, - //withdrawal: false, blocks: { - //startBlock: startBlock, - //targetBlock: targetBlock + deltaDivisor: 50 }, cache: { sync: { - concurrencyLimit: 10 + concurrencyLimit: 20 } } }) @@ -124,8 +134,8 @@ describe.only('main', () => { await ganacheProvider.send('evm_setAccountBalance', [daiWhale, parseUnits('10').toHexString()]) needsMoneyAddress = await needsMoney.getAddress() - daiData = await OnchainData.getTokenData('1', 'dai') - dai = chain.getTokenContract(daiData.address).connect(whale) + daiAddress = await Onchain.getTokenAddress('1', 'dai') + dai = chain.getTokenContract(daiAddress).connect(whale) smallestEth = await core.getInstance('eth', 0.1) }) after(async function () { @@ -137,11 +147,11 @@ describe.only('main', () => { dai = dai.connect(whale) }) - it.only('buildDepositTx: build a single eth deposit tx and succeed', async () => { + it('buildDepositTransaction: build a single eth deposit tx and succeed', async () => { const signer = ganacheProvider.getSigner() const initBal = await signer.getBalance() - const tx = await core.buildDepositTx(smallestEth) + const tx = await core.buildDepositTransaction(smallestEth) const response = await signer.sendTransaction(tx.request) const receipt = await response.wait() @@ -165,7 +175,7 @@ describe.only('main', () => { expect(endBal).to.be.lte(parseUnits('999.9')) }).timeout(0) - it.only('buildDepositProofs: it should be able to build', async () => { + it('buildDepositProofs: it should be able to build', async () => { try { const instance = await core.getInstance('eth', 0.1) const signer = ganacheProvider.getSigner() @@ -175,8 +185,9 @@ describe.only('main', () => { noteObj['args'] = { commitment: note.hexCommitment, - leafIndex: (await cache!.db.allDocs({ descending: true, limit: 1, include_docs: true })) - ?.rows[0].doc?.leafIndex, + leafIndex: + (await cache!.db.allDocs({ descending: true, limit: 1, include_docs: true }))?.rows[0].doc + ?.leafIndex! + 1, timestamp: noteObj['args']['timestamp'] } @@ -184,15 +195,16 @@ describe.only('main', () => { await cache!.db.put(new Docs.Deposit(noteObj)) - console.log(`\nBuilding proof from note:\n\n${note}\n\n`) - const proof = await core.buildDepositProof( instance, { address: await withdrawer.getAddress() }, await signer.getAddress(), - note + note, + { + checkNotesSpent: false + } ) console.log(proof) @@ -202,7 +214,7 @@ describe.only('main', () => { } }).timeout(0) - it.skip('buildDepositTx: build a single token deposit tx and succeed', async () => { + it('buildDepositTransaction: build a single token deposit tx and succeed', async () => { const dai100K = await core.getInstance('dai', 100000) const proxy = await core.getProxy() const depositAmount = parseUnits('100000') @@ -210,7 +222,7 @@ describe.only('main', () => { await dai.transfer(needsMoneyAddress, depositAmount) dai = dai.connect(needsMoney) - const tx = await core.buildDepositTx(dai100K) + const tx = await core.buildDepositTransaction(dai100K) await dai.approve(proxy.address, depositAmount) @@ -219,13 +231,13 @@ describe.only('main', () => { expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) }).timeout(0) - it.skip('buildDepositTxs: multiple eth deposits', async () => { + it('buildDepositTransactions: multiple eth deposits', async () => { const instances = await core.getInstances( [0.1, 1, 10, 100].map((el) => { return { token: 'eth', denomination: el } }) ) - const txs = await core.buildDepositTxs(instances, { + const txs = await core.buildDepositTransactions(instances, { depositsPerInstance: [1, 2, 3, 4] }) @@ -236,7 +248,7 @@ describe.only('main', () => { expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) }).timeout(0) - it.skip('buildDepositTxs: multiple token deposits', async () => { + it('buildDepositTransactions: multiple token deposits', async () => { const instances = await core.getInstances( [100, 1000, 10000, 100000].map((el) => { return { token: 'dai', denomination: el } @@ -249,7 +261,7 @@ describe.only('main', () => { await dai.transfer(needsMoneyAddress, parseUnits('432100')) dai = dai.connect(needsMoney) - const txs = await core.buildDepositTxs(instances, { + const txs = await core.buildDepositTransactions(instances, { depositsPerInstance: [1, 2, 3, 4] }) @@ -262,7 +274,7 @@ describe.only('main', () => { expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) }).timeout(0) - it.skip('createInvoice: should be able to create an invoice', async () => { + it('createInvoice: should be able to create an invoice', async () => { const instance = await core.getInstance('dai', '1000') const invoice = await core.createInvoice(instance) console.log(invoice) diff --git a/src/types/sdk/core.ts b/src/types/sdk/core.ts index 4b37557..4c0f310 100644 --- a/src/types/sdk/core.ts +++ b/src/types/sdk/core.ts @@ -21,18 +21,22 @@ export namespace Options { } export namespace Core { + export interface Cache { + sync?: Cache.Sync + db?: Cache.Database + } + export interface Sync { deposit?: boolean withdrawal?: boolean + msTimeout?: number blocks?: { startBlock?: number targetBlock?: number blockDelta?: number + deltaDivisor?: number } - cache?: { - sync?: Cache.Sync - db?: Cache.Database - } + cache?: Cache } export interface Deposit {