2023.04.20: Check HISTORY.md for more info

Signed-off-by: T-Hax <>
This commit is contained in:
T-Hax 2023-04-19 17:01:40 +00:00
parent d83dcd8112
commit 4b661dd3e6
15 changed files with 459 additions and 327 deletions

@ -1,5 +1,16 @@
# History # 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) ### 2023.04.18 (2023-04-18)
Did: Did:

@ -10,7 +10,7 @@
"zk" "zk"
], ],
"private": false, "private": false,
"version": "2023.04.18", "version": "2023.04.20",
"engines": { "engines": {
"node": "^18" "node": "^18"
}, },

@ -22,7 +22,7 @@ import { BaseContract, BigNumber, ContractTransaction, providers, Signer, VoidSi
import { randomBytes } from 'crypto' import { randomBytes } from 'crypto'
// Local modules // Local modules
import { OnchainData } from 'lib/data' import { Onchain } from 'lib/data'
import { ErrorUtils, HexUtils } from 'lib/utils' import { ErrorUtils, HexUtils } from 'lib/utils'
// We use a vanilla provider here, but in reality we will probably // We use a vanilla provider here, but in reality we will probably
@ -52,7 +52,7 @@ export class Chain {
} }
async getChainSymbol(): Promise<string> { async getChainSymbol(): Promise<string> {
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 return this.symbol
} }
@ -94,12 +94,12 @@ export class Chain {
): Promise<TransactionRequest> { ): Promise<TransactionRequest> {
if (callStruct[0].value) if (callStruct[0].value)
return await Multicall3Contract__factory.connect( return await Multicall3Contract__factory.connect(
await OnchainData.getMulticall3Address(String(this.chainId)), await Onchain.getMulticall3Address(String(this.chainId)),
this.provider this.provider
).populateTransaction.aggregate3Value(callStruct as Array<Multicall3.Call3ValueStruct>) ).populateTransaction.aggregate3Value(callStruct as Array<Multicall3.Call3ValueStruct>)
return await Multicall3Contract__factory.connect( return await Multicall3Contract__factory.connect(
await OnchainData.getMulticall3Address(String(this.chainId)), await Onchain.getMulticall3Address(String(this.chainId)),
this.provider this.provider
).populateTransaction.aggregate3(callStruct) ).populateTransaction.aggregate3(callStruct)
} }
@ -110,12 +110,12 @@ export class Chain {
if (this.signer) if (this.signer)
if (callStruct[0].value) if (callStruct[0].value)
return await Multicall3Contract__factory.connect( return await Multicall3Contract__factory.connect(
await OnchainData.getMulticall3Address(String(this.chainId)), await Onchain.getMulticall3Address(String(this.chainId)),
this.signer this.signer
).aggregate3Value(callStruct as Array<Multicall3.Call3ValueStruct>) ).aggregate3Value(callStruct as Array<Multicall3.Call3ValueStruct>)
else { else {
return await Multicall3Contract__factory.connect( return await Multicall3Contract__factory.connect(
await OnchainData.getMulticall3Address(String(this.chainId)), await Onchain.getMulticall3Address(String(this.chainId)),
this.provider this.provider
).aggregate3(callStruct) ).aggregate3(callStruct)
} }
@ -156,11 +156,7 @@ export namespace Contracts {
if (!contractMap.has(key)) { if (!contractMap.has(key)) {
contractMap.set( contractMap.set(
key, key,
_getContract<TornadoProxy>( _getContract<TornadoProxy>('TornadoProxy', await Onchain.getProxyAddress(network), signerOrProvider)
'TornadoProxy',
await OnchainData.getProxyAddress(network),
signerOrProvider
)
) )
} }
return contractMap.get(`TornadoProxy${network}`) as TornadoProxy return contractMap.get(`TornadoProxy${network}`) as TornadoProxy
@ -178,7 +174,7 @@ export namespace Contracts {
key, key,
_getContract<TornadoInstance>( _getContract<TornadoInstance>(
'TornadoInstance', 'TornadoInstance',
await OnchainData.getInstanceAddress(network, token, denomination), await Onchain.getInstanceAddress(network, token, denomination),
signerOrProvider signerOrProvider
) )
) )

@ -1,5 +1,5 @@
// ts-essentials // ts-essentials
import { DeepRequired, MarkOptional, MarkRequired } from 'ts-essentials' import { DeepRequired, MarkOptional } from 'ts-essentials'
// Local types // Local types
import { RelayerProperties as RelayerDataProperties } from 'types/sdk/data' 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' import { TornadoInstance, TornadoProxy } from 'types/deth'
// External imports // External imports
import { EventEmitter } from 'stream'
import { BigNumber, EventFilter, providers } from 'ethers' import { BigNumber, EventFilter, providers } from 'ethers'
import { parseUnits } from 'ethers/lib/utils' import { parseUnits } from 'ethers/lib/utils'
import { bigInt } from 'snarkjs' import { bigInt } from 'snarkjs'
@ -16,10 +17,10 @@ import { bigInt } from 'snarkjs'
import { parseIndexableString } from 'pouchdb-collate' import { parseIndexableString } from 'pouchdb-collate'
// Local imports // 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 { Primitives } from 'lib/crypto'
import { Contracts, Chain } from 'lib/chain' import { Contracts, Chain } from 'lib/chain'
import { ErrorUtils, ObjectUtils } from 'lib/utils' import { ErrorUtils, ObjectUtils, AsyncUtils } from 'lib/utils'
type Provider = providers.Provider type Provider = providers.Provider
@ -36,12 +37,13 @@ type RelayerProperties = MarkOptional<
'serviceFeePercent' | 'prices' 'serviceFeePercent' | 'prices'
> >
export class Core { export class Core extends EventEmitter {
chain: Chain chain: Chain
caches: Map<string, Cache.Base<Docs.Base>> caches: Map<string, Cache.Base<Docs.Base>>
instances: Map<string, TornadoInstance> instances: Map<string, TornadoInstance>
constructor(provider: providers.Provider) { constructor(provider: providers.Provider) {
super()
this.chain = new Chain(provider) this.chain = new Chain(provider)
this.caches = new Map<string, Cache.Syncable<Docs.Base>>() this.caches = new Map<string, Cache.Syncable<Docs.Base>>()
this.instances = new Map<string, TornadoInstance>() this.instances = new Map<string, TornadoInstance>()
@ -110,8 +112,6 @@ export class Core {
const hexNullifierHashes: string[] = [] const hexNullifierHashes: string[] = []
const purchaseAmounts = options?.ethPurchaseAmounts ?? new Array(zkDepositsData.length) const purchaseAmounts = options?.ethPurchaseAmounts ?? new Array(zkDepositsData.length)
console.log('\nChecking inputs.\n')
if (zkDepositsData.length !== recipientAddresses.length) if (zkDepositsData.length !== recipientAddresses.length)
throw ErrorUtils.getError( throw ErrorUtils.getError(
'Core.buildDepositProofs: the number of recipients must equal the length of zkDepositsData.' 'Core.buildDepositProofs: the number of recipients must equal the length of zkDepositsData.'
@ -127,14 +127,10 @@ export class Core {
hexNullifierHashes.push(deposit.hexNullifierHash) hexNullifierHashes.push(deposit.hexNullifierHash)
}) })
console.log('\nGetting lookup keys.\n')
// Determine cache name // 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() const name = 'Deposits' + (lookupKeys.network + lookupKeys.token + lookupKeys.denomination).toUpperCase()
console.log('\nLeaves and indices.\n')
// Find all leaves & indices by reading from cache // Find all leaves & indices by reading from cache
const [leaves, leafIndices] = await this._findLeavesAndIndices(name, hexCommitments) const [leaves, leafIndices] = await this._findLeavesAndIndices(name, hexCommitments)
const invalidCommitments: string[] = [] const invalidCommitments: string[] = []
@ -143,8 +139,6 @@ export class Core {
const checkSpent = options?.checkNotesSpent !== false const checkSpent = options?.checkNotesSpent !== false
const spentNotes: string[] = [] const spentNotes: string[] = []
console.log('\nNote checking.\n')
// If yes, immediately check it with the supplied Tornado Instance // If yes, immediately check it with the supplied Tornado Instance
const checkSpentArray = checkSpent ? await instance.isSpentArray(hexNullifierHashes) : undefined const checkSpentArray = checkSpent ? await instance.isSpentArray(hexNullifierHashes) : undefined
@ -161,8 +155,6 @@ export class Core {
const commitmentsAreInvalid = invalidCommitments.length !== 0 const commitmentsAreInvalid = invalidCommitments.length !== 0
const notesAreSpent = spentNotes.length !== 0 const notesAreSpent = spentNotes.length !== 0
console.log('\nErrors.\n')
if (commitmentsAreInvalid || notesAreSpent) if (commitmentsAreInvalid || notesAreSpent)
throw ErrorUtils.getError( throw ErrorUtils.getError(
`Core.buildDepositProofs: ` + `Core.buildDepositProofs: ` +
@ -176,8 +168,6 @@ export class Core {
: '') : '')
) )
console.log('\nMerkle tree.\n')
// Otherwise, build the merkle tree from the leaves // Otherwise, build the merkle tree from the leaves
const merkleTree = Primitives.buildMerkleTree({ const merkleTree = Primitives.buildMerkleTree({
height: options?.merkleTreeHeight ?? Constants.MERKLE_TREE_HEIGHT, 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.' '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 // Rest of note invariant arguments
const inputsForProofs: InputFor.ZKProof[] = [] const inputsForProofs: InputFor.ZKProof[] = []
const gasPrice = options?.gasPrice ?? (await this.chain.getGasPrice()) const gasPrice = options?.gasPrice ?? (await this.chain.getGasPrice())
@ -207,7 +195,7 @@ export class Core {
const decimals = const decimals =
// @ts-expect-error // @ts-expect-error
bigInt(10).pow( 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) 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.' 'Core.buildDepositProofs: a token price MUST be supplied if the token withdrawn is not native.'
) )
console.log('\nConstruct.\n')
// Compute proofs // Compute proofs
for (let i = 0, len = zkDepositsData.length; i < len; i++) { for (let i = 0, len = zkDepositsData.length; i < len; i++) {
inputsForProofs.push({ inputsForProofs.push({
@ -248,8 +234,6 @@ export class Core {
}) })
} }
console.log('\nCalc and return.\n')
return await Primitives.calcDepositProofs(inputsForProofs) return await Primitives.calcDepositProofs(inputsForProofs)
} }
@ -285,13 +269,13 @@ export class Core {
if (indexes) if (indexes)
for (let i = 0, len = rows.length; i < len; i++) { for (let i = 0, len = rows.length; i < len; i++) {
const id = parseIndexableString(rows[i].id)[0] 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) else docs = rows.map((row) => row.doc)
if (keys) if (keys)
docs.forEach((doc) => { 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 andTokenSymbolMatches = idNetworkMatches && (keys.token ? keys.token === doc?.token : true)
const lastlyDenominationMatches = const lastlyDenominationMatches =
andTokenSymbolMatches && (keys.denomination ? keys.denomination === doc?.denomination : true) andTokenSymbolMatches && (keys.denomination ? keys.denomination === doc?.denomination : true)
@ -328,19 +312,19 @@ export class Core {
options.backup.invoices = options.backup.invoices ?? true options.backup.invoices = options.backup.invoices ?? true
options.backup.notes = options.backup.notes ?? true options.backup.notes = options.backup.notes ?? true
options.doNotPopulate = options.doNotPopulate ?? true options.doNotPopulate = options.doNotPopulate ?? true
return this.buildDepositTxs(instances, options) return this.buildDepositTransactions(instances, options)
} }
async buildDepositTx( async buildDepositTransaction(
instance: TornadoInstance, instance: TornadoInstance,
options?: Options.Core.Deposit options?: Options.Core.Deposit
): Promise<Transactions.Deposit> { ): Promise<Transactions.Deposit> {
let opts: Options.Core.Deposit = options ?? {} let opts: Options.Core.Deposit = options ?? {}
opts.depositsPerInstance = [1] opts.depositsPerInstance = [1]
return (await this.buildDepositTxs([instance], opts))[0] return (await this.buildDepositTransactions([instance], opts))[0]
} }
async buildDepositTxs( async buildDepositTransactions(
instances: Array<TornadoInstance>, instances: Array<TornadoInstance>,
options?: Options.Core.Deposit options?: Options.Core.Deposit
): Promise<Array<Transactions.Deposit>> { ): Promise<Array<Transactions.Deposit>> {
@ -363,7 +347,7 @@ export class Core {
const proxy: TornadoProxy = await Contracts.getProxy(String(chainId), this.chain.provider) const proxy: TornadoProxy = await Contracts.getProxy(String(chainId), this.chain.provider)
for (let i = 0, nInstances = instances.length; i < nInstances; i++) { 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 const pathstring = lookupKeys.network + lookupKeys.token + lookupKeys.denomination
for (let d = 0, nDeposits = depositsPerInstance[i]; d < nDeposits; d++) { for (let d = 0, nDeposits = depositsPerInstance[i]; d < nDeposits; d++) {
@ -441,11 +425,25 @@ export class Core {
}) })
} }
loadCache<T extends Docs.Base, C extends Cache.Base<T>>(cacheName: string): C { loadWithdrawalCache(name: string, options?: Options.Core.Cache): Cache.Withdrawal {
if (!this.caches.has(cacheName)) { if (!this.caches.has(name)) {
this.caches.set(cacheName, new Cache.Base<T>(cacheName)) 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<T extends Docs.Base, C extends Cache.Base<T>>(name: string, options?: Options.Cache.Database): C {
if (!this.caches.has(name)) {
this.caches.set(name, new Cache.Base<T>(name, options))
}
return this.caches.get(name) as C
} }
async syncMultiple(instances: Array<TornadoInstance>, syncOptions?: Options.Core.Sync): Promise<void> { async syncMultiple(instances: Array<TornadoInstance>, syncOptions?: Options.Core.Sync): Promise<void> {
@ -456,7 +454,7 @@ export class Core {
async sync(instance: TornadoInstance, syncOptions?: Options.Core.Sync): Promise<void> { async sync(instance: TornadoInstance, syncOptions?: Options.Core.Sync): Promise<void> {
// Get some data // Get some data
const lookupKeys = await this.getInstanceLookupKeys(instance.address) const lookupKeys = await Onchain.getInstanceLookupKeys(instance.address)
const populatedSyncOpts = await this._populateSyncOpts(lookupKeys, syncOptions) const populatedSyncOpts = await this._populateSyncOpts(lookupKeys, syncOptions)
@ -464,46 +462,41 @@ export class Core {
// Synchronize // Synchronize
for (let i = 0, bound = actions.length; i < bound; i++) { for (let i = 0, bound = actions.length; i < bound; i++) {
let action = actions[i][0].charAt(0).toUpperCase() + actions[i][0].slice(1) const action = actions[i][0].charAt(0).toUpperCase() + actions[i][0].slice(1)
await this._sync(action, lookupKeys, instance, populatedSyncOpts) 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( private async _sync(
action: string, pathstring: string,
lookupKeys: DataTypes.Keys.InstanceLookup, cache: Cache.Syncable<Docs.Base>,
filter: EventFilter,
instance: TornadoInstance, instance: TornadoInstance,
syncOptions: DeepRequired<Options.Core.Sync> syncOptions: DeepRequired<Options.Core.Sync>
): Promise<void> { ): Promise<void> {
const name = `${action + 's'}${lookupKeys.network}${lookupKeys.token.toUpperCase()}${
lookupKeys.denomination
}`,
pathstring = name.substring(action.length).toLowerCase()
let cache: Cache.Syncable<Docs.Base>,
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 // 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 // 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 // Check for synced blocks
if (0 < numEntries) { if (0 < numEntries) {
@ -518,6 +511,15 @@ export class Core {
// Start synchronizing // Start synchronizing
let dbPromises = [] let dbPromises = []
this.emit(
'debug',
syncOptions.blocks.startBlock,
syncOptions.blocks.targetBlock,
syncOptions.blocks.blockDelta
)
this.emit('sync', 'syncing')
for ( for (
let currentBlock = syncOptions.blocks.startBlock, let currentBlock = syncOptions.blocks.startBlock,
blockDelta = syncOptions.blocks.blockDelta, blockDelta = syncOptions.blocks.blockDelta,
@ -526,37 +528,46 @@ export class Core {
currentBlock < targetBlock; currentBlock < targetBlock;
currentBlock += blockDelta currentBlock += blockDelta
) { ) {
if (cache.sync.pooler.pending < concurrencyLimit) { if (cache.sync.pooler!.pending < concurrencyLimit) {
const sum = currentBlock + blockDelta const sum = currentBlock + blockDelta
await AsyncUtils.timeout(syncOptions.msTimeout)
if (currentBlock + blockDelta < targetBlock) { if (currentBlock + blockDelta < targetBlock) {
await cache.sync.pooler.pool(currentBlock, sum) await cache.sync.pooler!.pool(currentBlock, sum)
} else { } 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 { } else {
let res: Array<any> = await cache.sync.pooler.race() let res: Array<any> = await cache.sync.pooler!.race()
if (res.length != 0) if (res.length != 0)
dbPromises.push( 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) throw ErrorUtils.ensureError(err)
}) })
) )
currentBlock -= blockDelta currentBlock -= blockDelta
} }
} }
this.emit('sync', 'synced')
// Immediately start listening if we're doing this // Immediately start listening if we're doing this
if (syncOptions.cache.sync.listen) if (syncOptions.cache.sync.listen)
instance = instance.on(filter, (...eventArgs) => { 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 // 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 // Then transform them, we know the shape in forward
results = results.reduce((res: any[], response: any[]) => { 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 return res
}, []) }, [])
@ -570,7 +581,7 @@ export class Core {
// Finally, store the objects // Finally, store the objects
if (!this.instances.has(pathstring)) this.instances.set(pathstring, instance) 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( private async _populateSyncOpts(
@ -594,16 +605,16 @@ export class Core {
// blocks // blocks
syncOptions.blocks.startBlock = syncOptions.blocks.startBlock =
syncOptions.blocks.startBlock ?? syncOptions.blocks.startBlock ??
(await OnchainData.getInstanceDeployBlockNum( (await Onchain.getInstanceDeployBlockNum(lookupKeys.network, lookupKeys.token, lookupKeys.denomination))
lookupKeys.network,
lookupKeys.token,
lookupKeys.denomination
))
syncOptions.blocks.targetBlock = syncOptions.blocks.targetBlock ?? (await this.chain.latestBlockNum()) syncOptions.blocks.targetBlock = syncOptions.blocks.targetBlock ?? (await this.chain.latestBlockNum())
syncOptions.blocks.deltaDivisor = syncOptions.blocks.deltaDivisor ?? 100
syncOptions.blocks.blockDelta = this._getBlockDelta(syncOptions) syncOptions.blocks.blockDelta = this._getBlockDelta(syncOptions)
syncOptions.msTimeout = syncOptions.msTimeout ?? 200 // 5 requests per second
// cache // cache
// db // db
syncOptions.cache.db.persistent = syncOptions.cache.db.persistent ?? true syncOptions.cache.db.persistent = syncOptions.cache.db.persistent ?? true
@ -618,8 +629,8 @@ export class Core {
private _getBlockDelta(syncOptions?: Options.Core.Sync): number { private _getBlockDelta(syncOptions?: Options.Core.Sync): number {
return Math.floor( return Math.floor(
syncOptions?.blocks?.blockDelta ?? (syncOptions!.blocks!.targetBlock! - syncOptions!.blocks!.startBlock!) /
(syncOptions!.blocks!.targetBlock! - syncOptions!.blocks!.startBlock!) / 20 syncOptions!.blocks!.deltaDivisor!
) )
} }
@ -670,25 +681,6 @@ export class Core {
// Concat matched and all leaf indices // Concat matched and all leaf indices
return [leaves, indices] return [leaves, indices]
} }
async getInstanceLookupKeys(instanceAddress: string): Promise<DataTypes.Keys.InstanceLookup> {
// 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 } export { Transactions, Options }

@ -26,12 +26,15 @@ PouchDB.plugin(PouchDBAdapterMemory)
export namespace Files { export namespace Files {
export type PathGetter = (relative: string) => string export type PathGetter = (relative: string) => string
export const getModulesPath = (relative: string): string => __dirname + '/../../node_modules/' + relative export const getModulesPath = (relative: string, prefix?: string): string =>
export const getResourcePath = (relative: string): string => __dirname + '/../resources/' + relative (prefix ?? __dirname + '/../../node_modules/') + relative
export const getCachePath = (relative: string): string => __dirname + '/../../cache/' + 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 cacheDirExists = (prefix?: string): boolean => existsSync(getCachePath('', prefix))
export const makeCacheDir = (): void => mkdirSync(getCachePath('')) export const makeCacheDir = (prefix?: string): void => mkdirSync(getCachePath('', prefix))
export const loadRaw = (relative: string): Promise<Buffer> => readFile(getResourcePath(relative)) export const loadRaw = (relative: string): Promise<Buffer> => 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 // 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( export async function getClassicInstanceData(
network: string, network: string,
token: string, token: string,
@ -141,24 +144,43 @@ export namespace OnchainData {
} }
} }
export async function getInstanceAttrSet<T>( export async function getInstanceLookupKeys(instanceAddress: string): Promise<Types.Keys.InstanceLookup> {
key: string, // 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<T>(
filepath: string,
paths: Array<{ paths: Array<{
network?: string network?: string
token?: string token?: string
denomination?: string denomination?: string
}> }>
): Promise<Array<T>> { ): Promise<Array<T>> {
const obj = await Json.load('onchain/quickLookup.json') const obj = await Json.load(filepath)
return await Promise.all( return await Promise.all(
paths.map((path) => 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<string> { export async function getNetworkSymbol(networkId: string): Promise<string> {
return (await getInstanceAttrSet<string>('networkSymbols', [{ network: networkId }]))[0] return (
await getPathstringBasedContent<string>('onchain/networkSymbols.json', [{ network: networkId }])
)[0]
} }
export function getInstanceAddresses( export function getInstanceAddresses(
@ -168,7 +190,7 @@ export namespace OnchainData {
denomination: string denomination: string
}> }>
): Promise<Array<string>> { ): Promise<Array<string>> {
return getInstanceAttrSet<string>('instanceAddresses', paths) return getPathstringBasedContent<string>('onchain/instanceAddresses.json', paths)
} }
export async function getInstanceAddress( export async function getInstanceAddress(
@ -186,7 +208,7 @@ export namespace OnchainData {
denomination: string denomination: string
}> }>
): Promise<Array<number>> { ): Promise<Array<number>> {
return getInstanceAttrSet<number>('deployedBlockNumber', paths) return getPathstringBasedContent<number>('onchain/deployedBlockNumbers.json', paths)
} }
export async function getInstanceDeployBlockNum( export async function getInstanceDeployBlockNum(
@ -220,12 +242,22 @@ export namespace OnchainData {
} }
} }
export async function getTokenAddress(network: string, token: string): Promise<string> {
return (
await getPathstringBasedContent<string>('onchain/tokenAddresses.json', [
{ network: network, token: token }
])
)[0]
}
export async function getTokenDecimals(network: string, token: string): Promise<number> { export async function getTokenDecimals(network: string, token: string): Promise<number> {
return (await getTokenData(network, token)).decimals return (
await getPathstringBasedContent<number>('onchain/decimals.json', [{ network: network, token: token }])
)[0]
} }
} }
export namespace OffchainData { export namespace Offchain {
export async function getUncensoredRpcURL(network: string, name: string = ''): Promise<string> { export async function getUncensoredRpcURL(network: string, name: string = ''): Promise<string> {
const rpcs = Json.toMap<string>( const rpcs = Json.toMap<string>(
Json.getValue(await Json.load('offchain/infrastructure.json'), ['jrpc-uncensored', network]) Json.getValue(await Json.load('offchain/infrastructure.json'), ['jrpc-uncensored', network])
@ -366,9 +398,12 @@ export namespace Docs {
export namespace Cache { export namespace Cache {
export class Base<T extends Docs.Base> { export class Base<T extends Docs.Base> {
name: string
db: PouchDB.Database<T> db: PouchDB.Database<T>
constructor(name: string, options?: Options.Cache.Database) { constructor(name: string, options?: Options.Cache.Database) {
this.name = name
if (options?.persistent === false && options?.adapter !== 'memory' && options?.adapter !== null) if (options?.persistent === false && options?.adapter !== 'memory' && options?.adapter !== null)
throw ErrorUtils.getError('Cache.new: if not persistent, cache must use memory adapter.') 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) this.sync = new AsyncUtils.Sync(options?.sync)
} }
abstract buildDoc(response: any): Docs.Base
abstract getCallbacks(...args: Array<any>): Array<AsyncUtils.Callback> abstract getCallbacks(...args: Array<any>): Array<AsyncUtils.Callback>
abstract getErrorHandlers(...args: Array<any>): Array<AsyncUtils.ErrorHandler>
async close(): Promise<void> { async close(): Promise<void> {
if (this.sync.pooler!.pending) if (this.sync.pooler!.pending)
throw ErrorUtils.getError("Syncable.close: can't clear while pooler still has pending promises.") 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<Error>(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<Docs.Deposit> { export class Deposit extends Syncable<Docs.Deposit> {
buildDoc(response: any): Docs.Deposit {
return new Docs.Deposit(response)
}
getErrorHandlers(): Array<AsyncUtils.ErrorHandler> {
return [tornadoSyncErrorHandler]
}
getCallbacks(instance: TornadoInstance): Array<AsyncUtils.Callback> { getCallbacks(instance: TornadoInstance): Array<AsyncUtils.Callback> {
return [ return [
(fromBlock: number, toBlock: number) => { (fromBlock: number, toBlock: number) => {
@ -430,6 +502,14 @@ export namespace Cache {
} }
export class Withdrawal extends Syncable<Docs.Withdrawal> { export class Withdrawal extends Syncable<Docs.Withdrawal> {
buildDoc(response: any): Docs.Withdrawal {
return new Docs.Withdrawal(response)
}
getErrorHandlers(): Array<AsyncUtils.ErrorHandler> {
return [tornadoSyncErrorHandler]
}
getCallbacks(instance: TornadoInstance): Array<AsyncUtils.Callback> { getCallbacks(instance: TornadoInstance): Array<AsyncUtils.Callback> {
return [ return [
(fromBlock: number, toBlock: number) => { (fromBlock: number, toBlock: number) => {

@ -2,7 +2,8 @@
import * as Crypto from 'types/sdk/crypto' import * as Crypto from 'types/sdk/crypto'
import { Options } from 'types/sdk/core' import { Options } from 'types/sdk/core'
// Needed // External imports
import assert from 'assert'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { bigInt } from 'snarkjs' import { bigInt } from 'snarkjs'
import { randomBytes } from 'crypto' import { randomBytes } from 'crypto'
@ -22,7 +23,7 @@ export namespace ErrorUtils {
export function getError(message: string): Error { export function getError(message: string): Error {
let error = new Error(message) let error = new Error(message)
error.name = 'Error (tornado-sdk)' error.name = '\nError (tornado-sdk)'
return error return error
} }
} }
@ -34,25 +35,34 @@ export namespace AsyncUtils {
export type Callback = (...values: any[]) => Promise<any> export type Callback = (...values: any[]) => Promise<any>
export type ErrorHandler = (
err: Error,
numResolvedPromises: number,
callbackIndex: number,
orderIndex: number,
...args: any[]
) => void
export class PromisePooler { export class PromisePooler {
concurrencyLimit: number concurrencyLimit: number
private _totalAdded: number private _totalAdded: number
_results: Array<any> _results: Array<any>
_callbacks: Array<Callback> _callbacks: Array<Callback>
_errorHandlers: Array<ErrorHandler>
private _promises: Array<PoolPromise<any>> private _promises: Array<PoolPromise<any>>
constructor(callbacks: Array<Callback>, concurrencyLimit: number) { constructor(callbacks: Array<Callback>, errorHandlers: Array<ErrorHandler>, concurrencyLimit: number) {
if (callbacks.length == 0) throw ErrorUtils.getError('PromisePooler: callbacks are empty.') if (callbacks.length == 0) throw ErrorUtils.getError('PromisePooler: callbacks are empty.')
if (concurrencyLimit <= 0) if (concurrencyLimit <= 0)
throw ErrorUtils.getError("PromisePooler: concurrencyLimit can't be 0 or less.") throw ErrorUtils.getError("PromisePooler: concurrencyLimit can't be 0 or less.")
this.concurrencyLimit = concurrencyLimit this.concurrencyLimit = concurrencyLimit
this._totalAdded = 0 this._totalAdded = 0
this._results = [] this._results = []
this._promises = [] this._promises = []
this._callbacks = callbacks this._callbacks = callbacks
this._errorHandlers = errorHandlers
} }
get pending(): number { get pending(): number {
@ -98,10 +108,21 @@ export namespace AsyncUtils {
} }
} }
// TODO: Immediately set new callbacks and error handlers
async reset(): Promise<Array<any>> { async reset(): Promise<Array<any>> {
let results = await this.all() 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._totalAdded = 0
this._callbacks = []
this._errorHandlers = []
return results return results
} }
@ -113,29 +134,42 @@ export namespace AsyncUtils {
return this._pool(callbackIndex + 1, orderIndex, ...results) return this._pool(callbackIndex + 1, orderIndex, ...results)
} else { } else {
let result = results.length == 1 ? results[0] : results let result = results.length == 1 ? results[0] : results
this._promises.splice( this._promises.splice(this._getPromiseIndex(orderIndex), 1)
this._promises.findIndex((_promise) => _promise.orderIndex == orderIndex),
1
)
this._results.push(result) this._results.push(result)
return result return result
} }
}, },
(err) => { async (err: Error) => {
throw ErrorUtils.ensureError(err) let resolved = this._totalAdded - this.concurrencyLimit
resolved = resolved < 0 ? 0 : resolved
// Throw inside to abort
this._errorHandlers[callbackIndex](
ErrorUtils.ensureError<Error>(err),
resolved,
callbackIndex,
orderIndex,
...args
)
return this._pool(callbackIndex, orderIndex, ...args)
} }
) )
promise.orderIndex = orderIndex promise.orderIndex = orderIndex
if (callbackIndex == 0) { const promiseIndex = this._getPromiseIndex(orderIndex)
if (promiseIndex < 0) {
this._promises.push(promise) this._promises.push(promise)
} else { } else {
this._promises[this._promises.findIndex((_promise) => _promise.orderIndex == orderIndex)] = promise this._promises[promiseIndex] = promise
} }
return promise return promise
} }
private _getPromiseIndex(orderIndex: number): number {
return this._promises.findIndex((_promise) => _promise.orderIndex == orderIndex)
}
private async _waitIfFull(): Promise<void> { private async _waitIfFull(): Promise<void> {
if (this.concurrencyLimit <= this._promises.length) { if (this.concurrencyLimit <= this._promises.length) {
await Promise.race(this._promises) await Promise.race(this._promises)
@ -153,15 +187,14 @@ export namespace AsyncUtils {
this.listen = options?.listen ?? false this.listen = options?.listen ?? false
} }
async initializePooler(callbacks: Array<Callback>): Promise<PromisePooler> { initializePooler(callbacks: Array<Callback>, errorHandlers: Array<ErrorHandler>): void {
return new PromisePooler(callbacks, this.concurrencyLimit) if (this.pooler) this.pooler.reset()
this.pooler = new PromisePooler(callbacks, errorHandlers, this.concurrencyLimit)
}
} }
async newCallbacks(callbacks: Array<Callback>): Promise<Array<any>> { export function timeout(msTimeout: number): Promise<any> {
let results = this.pooler!.reset() return new Promise((resolve) => setTimeout(resolve, msTimeout))
this.pooler!._callbacks = callbacks
return results
}
} }
} }

@ -49,7 +49,9 @@ export class TorProvider extends Web3Provider {
agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) },
// The XHR2 XMLHttpRequest assigns a Tor Browser header by itself. // The XHR2 XMLHttpRequest assigns a Tor Browser header by itself.
// But if in Browser we assign just in case. // 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 network
) )
@ -135,10 +137,12 @@ export class Relayer {
this._serviceFee = properties['tornadoServiceFee'] this._serviceFee = properties['tornadoServiceFee']
this._miningFee = properties['miningFee'] this._miningFee = properties['miningFee']
this._status = properties['health']['status'] this._status = properties['health']['status']
this._prices = Object.entries(properties['ethPrices']).reduce( this._prices = Object.entries(properties['ethPrices']).reduce(
(map, entry) => map.set(entry[0], BigNumber.from(entry[1])), (map, entry) => map.set(entry[0], BigNumber.from(entry[1])),
new Map<string, BigNumber>() new Map<string, BigNumber>()
) )
this._fetched = true this._fetched = true
return { return {

@ -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}

@ -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
}

@ -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"
}

@ -0,0 +1,8 @@
{
"1": "eth",
"5": "eth",
"56": "bnb",
"100": "xdai",
"137": "matic",
"42161": "eth"
}

@ -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"
}
}

@ -0,0 +1 @@
{"1dai":"0x6B175474E89094C44Da98b954EedeAC495271d0F","1cdai":"0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643","1usdc":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","1usdt":"0xdAC17F958D2ee523a2206206994597C13D831ec7","1wbtc":"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599","5dai":"0xdc31Ee1784292379Fbb2964b3B9C4124D8F89C60","5cdai":"0x822397d9a55d0fefd20F5c4bCaB33C5F65bd28Eb","5usdc":"0xD87Ba7A50B2E7E660f678A895E4B72E7CB4CCd9C","5usdt":"0xb7FC2023D96AEa94Ba0254AA5Aeb93141e4aad66","5wbtc":"0xC04B0d3107736C32e19F1c62b2aF67BE61d63a05"}

@ -9,7 +9,7 @@ import { ERC20, TornadoInstance } from 'types/deth'
import { Json } from 'types/sdk/data' import { Json } from 'types/sdk/data'
import { Core } from 'lib/core' import { Core } from 'lib/core'
import { Chain, Contracts } from 'lib/chain' 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 { ErrorUtils } from 'lib/utils'
import { TorProvider } from 'lib/web' import { TorProvider } from 'lib/web'
@ -24,7 +24,7 @@ chai.use(solidity)
const expect = chai.expect const expect = chai.expect
describe.only('main', () => { describe('main', () => {
const torify = process.env.TORIFY === 'true' const torify = process.env.TORIFY === 'true'
if (!process.env.ETH_MAINNET_TEST_RPC) throw ErrorUtils.getError('need a mainnet rpc endpoint.') 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. 🐧' '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 daiWhale = '0x5777d92f208679db4b9778590fa3cab3ac9e2168' // Uniswap V3 Something/Dai Pool
const mainnetProvider = torify const mainnetProvider: providers.Provider = torify
? new TorProvider(process.env.ETH_MAINNET_TEST_RPC, { port: +process.env.TOR_PORT! }) ? new TorProvider(process.env.ETH_MAINNET_TEST_RPC!, { port: +process.env.TOR_PORT! })
: new providers.JsonRpcProvider(process.env.ETH_MAINNET_TEST_RPC) : new providers.JsonRpcProvider(process.env.ETH_MAINNET_TEST_RPC)
const _ganacheProvider = ganache.provider({ const _ganacheProvider = ganache.provider({
@ -54,6 +54,7 @@ describe.only('main', () => {
// @ts-expect-error // @ts-expect-error
const ganacheProvider = new providers.Web3Provider(_ganacheProvider) const ganacheProvider = new providers.Web3Provider(_ganacheProvider)
const chain = new Chain(ganacheProvider) const chain = new Chain(ganacheProvider)
let snapshotId: any let snapshotId: any
@ -64,7 +65,7 @@ describe.only('main', () => {
}) })
describe('namespace Tornado', () => { describe('namespace Tornado', () => {
describe.skip('namespace Contracts', () => { describe('namespace Contracts', () => {
it('getClassicInstance: should be able to get a tornado instance', async () => { it('getClassicInstance: should be able to get a tornado instance', async () => {
let instance = await Contracts.getInstance(String(1), 'eth', String(1), mainnetProvider) let instance = await Contracts.getInstance(String(1), 'eth', String(1), mainnetProvider)
expect(instance.address).to.equal('0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936') expect(instance.address).to.equal('0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936')
@ -73,21 +74,30 @@ describe.only('main', () => {
}) })
describe('class Classic', () => { 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 core = new Core(mainnetProvider)
const instance = await Contracts.getInstance(String(1), 'eth', String(0.1), 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, { await core.sync(instance, {
//deposit: true,
//withdrawal: false,
blocks: { blocks: {
//startBlock: startBlock, deltaDivisor: 50
//targetBlock: targetBlock
}, },
cache: { cache: {
sync: { sync: {
concurrencyLimit: 10 concurrencyLimit: 20
} }
} }
}) })
@ -124,8 +134,8 @@ describe.only('main', () => {
await ganacheProvider.send('evm_setAccountBalance', [daiWhale, parseUnits('10').toHexString()]) await ganacheProvider.send('evm_setAccountBalance', [daiWhale, parseUnits('10').toHexString()])
needsMoneyAddress = await needsMoney.getAddress() needsMoneyAddress = await needsMoney.getAddress()
daiData = await OnchainData.getTokenData('1', 'dai') daiAddress = await Onchain.getTokenAddress('1', 'dai')
dai = chain.getTokenContract(daiData.address).connect(whale) dai = chain.getTokenContract(daiAddress).connect(whale)
smallestEth = await core.getInstance('eth', 0.1) smallestEth = await core.getInstance('eth', 0.1)
}) })
after(async function () { after(async function () {
@ -137,11 +147,11 @@ describe.only('main', () => {
dai = dai.connect(whale) 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 signer = ganacheProvider.getSigner()
const initBal = await signer.getBalance() 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 response = await signer.sendTransaction(tx.request)
const receipt = await response.wait() const receipt = await response.wait()
@ -165,7 +175,7 @@ describe.only('main', () => {
expect(endBal).to.be.lte(parseUnits('999.9')) expect(endBal).to.be.lte(parseUnits('999.9'))
}).timeout(0) }).timeout(0)
it.only('buildDepositProofs: it should be able to build', async () => { it('buildDepositProofs: it should be able to build', async () => {
try { try {
const instance = await core.getInstance('eth', 0.1) const instance = await core.getInstance('eth', 0.1)
const signer = ganacheProvider.getSigner() const signer = ganacheProvider.getSigner()
@ -175,8 +185,9 @@ describe.only('main', () => {
noteObj['args'] = { noteObj['args'] = {
commitment: note.hexCommitment, commitment: note.hexCommitment,
leafIndex: (await cache!.db.allDocs({ descending: true, limit: 1, include_docs: true })) leafIndex:
?.rows[0].doc?.leafIndex, (await cache!.db.allDocs({ descending: true, limit: 1, include_docs: true }))?.rows[0].doc
?.leafIndex! + 1,
timestamp: noteObj['args']['timestamp'] timestamp: noteObj['args']['timestamp']
} }
@ -184,15 +195,16 @@ describe.only('main', () => {
await cache!.db.put(new Docs.Deposit(noteObj)) await cache!.db.put(new Docs.Deposit(noteObj))
console.log(`\nBuilding proof from note:\n\n${note}\n\n`)
const proof = await core.buildDepositProof( const proof = await core.buildDepositProof(
instance, instance,
{ {
address: await withdrawer.getAddress() address: await withdrawer.getAddress()
}, },
await signer.getAddress(), await signer.getAddress(),
note note,
{
checkNotesSpent: false
}
) )
console.log(proof) console.log(proof)
@ -202,7 +214,7 @@ describe.only('main', () => {
} }
}).timeout(0) }).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 dai100K = await core.getInstance('dai', 100000)
const proxy = await core.getProxy() const proxy = await core.getProxy()
const depositAmount = parseUnits('100000') const depositAmount = parseUnits('100000')
@ -210,7 +222,7 @@ describe.only('main', () => {
await dai.transfer(needsMoneyAddress, depositAmount) await dai.transfer(needsMoneyAddress, depositAmount)
dai = dai.connect(needsMoney) dai = dai.connect(needsMoney)
const tx = await core.buildDepositTx(dai100K) const tx = await core.buildDepositTransaction(dai100K)
await dai.approve(proxy.address, depositAmount) await dai.approve(proxy.address, depositAmount)
@ -219,13 +231,13 @@ describe.only('main', () => {
expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0)
}).timeout(0) }).timeout(0)
it.skip('buildDepositTxs: multiple eth deposits', async () => { it('buildDepositTransactions: multiple eth deposits', async () => {
const instances = await core.getInstances( const instances = await core.getInstances(
[0.1, 1, 10, 100].map((el) => { [0.1, 1, 10, 100].map((el) => {
return { token: 'eth', denomination: el } return { token: 'eth', denomination: el }
}) })
) )
const txs = await core.buildDepositTxs(instances, { const txs = await core.buildDepositTransactions(instances, {
depositsPerInstance: [1, 2, 3, 4] depositsPerInstance: [1, 2, 3, 4]
}) })
@ -236,7 +248,7 @@ describe.only('main', () => {
expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0)
}).timeout(0) }).timeout(0)
it.skip('buildDepositTxs: multiple token deposits', async () => { it('buildDepositTransactions: multiple token deposits', async () => {
const instances = await core.getInstances( const instances = await core.getInstances(
[100, 1000, 10000, 100000].map((el) => { [100, 1000, 10000, 100000].map((el) => {
return { token: 'dai', denomination: el } return { token: 'dai', denomination: el }
@ -249,7 +261,7 @@ describe.only('main', () => {
await dai.transfer(needsMoneyAddress, parseUnits('432100')) await dai.transfer(needsMoneyAddress, parseUnits('432100'))
dai = dai.connect(needsMoney) dai = dai.connect(needsMoney)
const txs = await core.buildDepositTxs(instances, { const txs = await core.buildDepositTransactions(instances, {
depositsPerInstance: [1, 2, 3, 4] depositsPerInstance: [1, 2, 3, 4]
}) })
@ -262,7 +274,7 @@ describe.only('main', () => {
expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0)
}).timeout(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 instance = await core.getInstance('dai', '1000')
const invoice = await core.createInvoice(instance) const invoice = await core.createInvoice(instance)
console.log(invoice) console.log(invoice)

@ -21,18 +21,22 @@ export namespace Options {
} }
export namespace Core { export namespace Core {
export interface Cache {
sync?: Cache.Sync
db?: Cache.Database
}
export interface Sync { export interface Sync {
deposit?: boolean deposit?: boolean
withdrawal?: boolean withdrawal?: boolean
msTimeout?: number
blocks?: { blocks?: {
startBlock?: number startBlock?: number
targetBlock?: number targetBlock?: number
blockDelta?: number blockDelta?: number
deltaDivisor?: number
} }
cache?: { cache?: Cache
sync?: Cache.Sync
db?: Cache.Database
}
} }
export interface Deposit { export interface Deposit {