before dangerous ops
Signed-off-by: T-Hax <>
This commit is contained in:
parent
4065d89909
commit
704648ed2c
41 changed files with 31021 additions and 460 deletions
|
@ -1,13 +0,0 @@
|
|||
# RPC URLs
|
||||
ETH_MAINNET_TEST_RPC=
|
||||
|
||||
# debug (debug events are logged to console)
|
||||
DEBUG=
|
||||
|
||||
# use tor (torify tests)
|
||||
TORIFY=
|
||||
# tor port (regular = 9050, browser = 9150)
|
||||
TOR_PORT=
|
||||
|
||||
# relayer DOMAIN (the example.xyz in https://example.xyz) for testing
|
||||
TEST_RELAYER_DOMAIN=
|
1
@tornado/sdk-core/docs/.nojekyll
Normal file
1
@tornado/sdk-core/docs/.nojekyll
Normal file
|
@ -0,0 +1 @@
|
|||
TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
|
|
@ -49,13 +49,14 @@
|
|||
"ganache": "^7.7.7",
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^2.3.0",
|
||||
"rimraf": "^4.4.0",
|
||||
"rimraf": "^5.0.0",
|
||||
"source-map-support": "^0.5.19",
|
||||
"ts-essentials": "^9.3.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"tsc-alias": "^1.2.11",
|
||||
"tsconfig-paths": "^4.1.2",
|
||||
"typechain": "^8.1.1",
|
||||
"typedoc": "^0.24.6",
|
||||
"typescript": "^5.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -67,7 +68,7 @@
|
|||
"postversion": "git push --follow-tags && npm publish",
|
||||
"lint": "eslint --ext ts,js --fix src",
|
||||
"build-live": "tsc -w && tsc-alias -w",
|
||||
"clean": "rm -rf --interactive=never cache/*",
|
||||
"clean": "rm -rf --interactive=never docs/*",
|
||||
"format": "prettier src/**/*.ts -w"
|
||||
},
|
||||
"files": [
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ZKDepositData, InputFor } from '@tornado/sdk-crypto'
|
|||
|
||||
// External imports
|
||||
import { TransactionRequest } from '@ethersproject/abstract-provider'
|
||||
import { BigNumber, providers } from 'ethers'
|
||||
import { BigNumber, EventFilter, providers } from 'ethers'
|
||||
import { parseUnits } from 'ethers/lib/utils'
|
||||
import { bigInt } from 'snarkjs'
|
||||
|
||||
|
@ -30,10 +30,6 @@ export namespace Options {
|
|||
export interface Deposit {
|
||||
depositsPerInstance?: Array<number>
|
||||
doNotPopulate?: boolean
|
||||
backup?: {
|
||||
invoices?: boolean
|
||||
notes?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type Invoice = Deposit
|
||||
|
@ -128,58 +124,52 @@ export class WithdrawalCache extends Cache.Syncable<Docs.Withdrawal> {
|
|||
|
||||
type Provider = providers.Provider
|
||||
|
||||
type BackupDepositDoc = {
|
||||
network: string
|
||||
denomination: string
|
||||
token: string
|
||||
invoice?: string
|
||||
note?: string
|
||||
}
|
||||
|
||||
type RelayerProperties = MarkOptional<
|
||||
Pick<RelayerDataProperties, 'address' | 'serviceFeePercent' | 'prices'>,
|
||||
'serviceFeePercent' | 'prices'
|
||||
>
|
||||
|
||||
export class Core extends Synchronizer {
|
||||
chain: Chain
|
||||
private _mutex: AsyncUtils.SimpleMutex
|
||||
|
||||
caches: Map<string, Cache.Base<Docs.Base>>
|
||||
instances: Map<string, TornadoInstance>
|
||||
chain?: Chain
|
||||
|
||||
constructor(provider: providers.Provider) {
|
||||
constructor() {
|
||||
super()
|
||||
this.chain = new Chain(provider)
|
||||
this.caches = new Map<string, Cache.Syncable<Docs.Base>>()
|
||||
this.instances = new Map<string, TornadoInstance>()
|
||||
this._mutex = new AsyncUtils.SimpleMutex()
|
||||
}
|
||||
|
||||
connect(provider: Provider): void {
|
||||
this.chain.provider = provider
|
||||
private _checkProvider(parentCallName: string): void {
|
||||
try {
|
||||
this.chain?.id
|
||||
} catch (err) {
|
||||
throw ErrorUtils.getError('Core.' + parentCallName + ': you must first connect a provider!')
|
||||
}
|
||||
}
|
||||
|
||||
async getInstances(
|
||||
keys: Array<{ token: string; denomination: number | string }>
|
||||
): Promise<Array<TornadoInstance>> {
|
||||
const chainId = await this.chain.getChainId()
|
||||
return Promise.all(
|
||||
keys.map((key) =>
|
||||
Contracts.getInstance(String(chainId), key.token, String(key.denomination), this.chain.provider)
|
||||
)
|
||||
async connect(provider: Provider): Promise<void> {
|
||||
if (!this.chain) this.chain = new Chain(provider)
|
||||
else this.chain.provider = provider
|
||||
await this.chain.fetchChainData()
|
||||
}
|
||||
|
||||
getInstances(keys: Array<{ token: string; denomination: number | string }>): Array<TornadoInstance> {
|
||||
this._checkProvider('getInstances')
|
||||
return keys.map((key) =>
|
||||
Contracts.getInstance(String(this.chain!.id), key.token, String(key.denomination), this.chain!.provider)
|
||||
)
|
||||
}
|
||||
|
||||
async getInstance(token: string, denomination: number | string): Promise<TornadoInstance> {
|
||||
const chainId = String(await this.chain.getChainId())
|
||||
token = token.toLowerCase()
|
||||
denomination = String(denomination)
|
||||
if (this.instances.has(chainId + token + denomination))
|
||||
return this.instances.get(chainId + token + denomination)!
|
||||
else return Contracts.getInstance(chainId, token, denomination, this.chain.provider)
|
||||
getInstance(token: string, denomination: number | string): TornadoInstance {
|
||||
this._checkProvider('getInstance')
|
||||
return this.loadInstance(this.chain!.id, token, denomination)
|
||||
}
|
||||
|
||||
async getProxy(): Promise<TornadoProxy> {
|
||||
const chainId = await this.chain.getChainId()
|
||||
return Contracts.getProxy(String(chainId), this.chain.provider)
|
||||
getProxy(): TornadoProxy {
|
||||
this._checkProvider('getProxy')
|
||||
return Contracts.getProxy(String(this.chain!.id), this.chain!.provider)
|
||||
}
|
||||
|
||||
async buildDepositProof(
|
||||
|
@ -215,6 +205,8 @@ export class Core extends Synchronizer {
|
|||
zkDepositsData: Array<ZKDepositData>,
|
||||
options?: Options.Core.BuildDepositProof
|
||||
): Promise<Array<Array<string>>> {
|
||||
this._checkProvider('buildDepositProofs')
|
||||
|
||||
// Extract commitments and nullifier hashes
|
||||
const hexCommitments: string[] = []
|
||||
const hexNullifierHashes: string[] = []
|
||||
|
@ -237,8 +229,8 @@ export class Core extends Synchronizer {
|
|||
})
|
||||
|
||||
// Determine cache name
|
||||
const lookupKeys = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
const name = 'Deposits' + (lookupKeys.network + lookupKeys.token + lookupKeys.denomination).toUpperCase()
|
||||
const { network, token, denomination } = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
const name = 'Deposits' + (network + token + denomination).toUpperCase()
|
||||
|
||||
// Find all leaves & indices by reading from cache
|
||||
const [leaves, leafIndices] = await this._findLeavesAndIndices(name, hexCommitments)
|
||||
|
@ -305,20 +297,23 @@ export class Core extends Synchronizer {
|
|||
|
||||
// Rest of note invariant arguments
|
||||
const inputsForProofs: InputFor.ZKProof[] = []
|
||||
const gasPrice = options?.gasPrice ?? (await this.chain.getGasPrice())
|
||||
const gasPrice = options?.gasPrice ?? (await this.chain!.getGasPrice())
|
||||
const gasPriceCushion = options?.gasPrice ?? gasPrice.mul(10).div(100)
|
||||
|
||||
// In reality, if a manual withdraw is made, we don't differentiate it from a relayer withdraw
|
||||
// Since it is only serviceFee 0 AND without a token price, the function will not buy more tokens
|
||||
const serviceFeePercent = relayerProperties.serviceFeePercent ?? 0
|
||||
const tokenPrice = relayerProperties.prices?.get(lookupKeys.token)
|
||||
const tokenPrice = relayerProperties.prices?.get(token)
|
||||
|
||||
const decimals = BigNumber.from(10).pow(
|
||||
options?.tokenDecimals ?? (await Onchain.getTokenDecimals(lookupKeys.network, lookupKeys.token))
|
||||
options?.tokenDecimals ?? (await Onchain.getTokenDecimals(network, token))
|
||||
)
|
||||
const toWithdraw = BigNumber.from(+lookupKeys.denomination * 10 ** lookupKeys.denomination.length)
|
||||
|
||||
const toWithdraw = BigNumber.from(+denomination * 10 ** denomination.length)
|
||||
.mul(decimals)
|
||||
.div(10 ** lookupKeys.denomination.length)
|
||||
const native = lookupKeys.token == (await this.chain.getChainSymbol())
|
||||
.div(10 ** denomination.length)
|
||||
|
||||
const native = token == this.chain!.symbol
|
||||
|
||||
if (!tokenPrice && !native)
|
||||
throw ErrorUtils.getError(
|
||||
|
@ -403,6 +398,9 @@ export class Core extends Synchronizer {
|
|||
const leaves: Array<string> = []
|
||||
|
||||
const cache = this.loadCache<Cache.Base<Docs.Deposit>>(instanceName)
|
||||
|
||||
// Shallow copy so we can find indexes again for commitments
|
||||
const commitmentsCopy = [...commitments]
|
||||
const docs = await cache.db.allDocs()
|
||||
|
||||
// If no docs in cache throw and stop
|
||||
|
@ -415,13 +413,25 @@ export class Core extends Synchronizer {
|
|||
|
||||
// Otherwise start looking for commitment leaf indices and also pick up all other leafs on the way
|
||||
for (const row of docs.rows) {
|
||||
const [, leafIndex, loadedCommitment] = parseIndexableString(row.id)
|
||||
const index = commitments.findIndex((commitment) => commitment === loadedCommitment)
|
||||
let index = -1
|
||||
|
||||
// If some commitment is found then add the leaf index and remove that commitment
|
||||
const [, leafIndex, loadedCommitment] = parseIndexableString(row.id)
|
||||
|
||||
// Search only if there is some left
|
||||
if (commitments.length !== 0)
|
||||
index = commitments.findIndex((commitment) => commitment === loadedCommitment)
|
||||
|
||||
// If some commitment is found then add the leaf index
|
||||
if (index !== -1) {
|
||||
indices[index] = leafIndex
|
||||
// Add it there where we intended for it to be originally
|
||||
indices[commitmentsCopy.findIndex((commitment) => commitment === loadedCommitment)] = leafIndex
|
||||
commitments.splice(index, 1)
|
||||
this.emit(
|
||||
'debug',
|
||||
`\nMatched commitment ${loadedCommitment} @ leaf index ${leafIndex}, leftover commitments:\n\n${commitments.join(
|
||||
'\n'
|
||||
)}\n`
|
||||
)
|
||||
}
|
||||
|
||||
// In any case push every leaf
|
||||
|
@ -443,8 +453,7 @@ export class Core extends Synchronizer {
|
|||
|
||||
if (indexes)
|
||||
for (let i = 0, len = rows.length; i < len; i++) {
|
||||
const [index, , ,] = parseIndexableString(rows[i].id)[0]
|
||||
if (0 < indexes.findIndex(index)) docs.push(rows[i].doc)
|
||||
docs.push(rows[indexes[i]].doc)
|
||||
}
|
||||
else docs = rows.map((row) => row.doc)
|
||||
|
||||
|
@ -469,82 +478,141 @@ export class Core extends Synchronizer {
|
|||
return this.parseNotes([note])[0]
|
||||
}
|
||||
|
||||
async createInvoice(
|
||||
instance: TornadoInstance,
|
||||
options?: Omit<Options.Core.Invoice, 'depositsPerInstance'>
|
||||
): Promise<Transactions.Invoice> {
|
||||
let opts: Options.Core.Invoice = options ?? {}
|
||||
opts.depositsPerInstance = [1]
|
||||
return (await this.createInvoices([instance], options))[0]
|
||||
clearListener(
|
||||
instance: TornadoInstance | string,
|
||||
event: Function | number = 0,
|
||||
listenerIndex: number = 0
|
||||
): void {
|
||||
const _instance = this._resolveInstance(instance)
|
||||
const filter = this._resolveInstanceEvent(_instance, event)
|
||||
this.clearListenerByIndex(
|
||||
_instance,
|
||||
this._instanceEventToFilter(filter, _instance.filters.Deposit),
|
||||
listenerIndex
|
||||
)
|
||||
}
|
||||
|
||||
async createInvoices(
|
||||
instances: Array<TornadoInstance>,
|
||||
options?: Options.Core.Invoice
|
||||
): Promise<Array<Transactions.Invoice>> {
|
||||
if (!options) options = {}
|
||||
if (!options.backup) options.backup = {}
|
||||
options.backup.invoices = options.backup.invoices ?? true
|
||||
options.backup.notes = options.backup.notes ?? true
|
||||
options.doNotPopulate = options.doNotPopulate ?? true
|
||||
return this.buildDepositTransactions(instances, options)
|
||||
clearListeners(instance: TornadoInstance | string): void {
|
||||
this._resolveInstance(instance).removeAllListeners()
|
||||
}
|
||||
|
||||
async buildDepositTransaction(
|
||||
instance: TornadoInstance,
|
||||
options?: Options.Core.Deposit
|
||||
): Promise<Transactions.Deposit> {
|
||||
listenForDeposits(instance: TornadoInstance | string): void {
|
||||
this.listenForInstanceEvents(instance, 0)
|
||||
}
|
||||
|
||||
listenForWithdrawals(instance: TornadoInstance | string): void {
|
||||
this.listenForInstanceEvents(instance, 1)
|
||||
}
|
||||
|
||||
listenForInstanceEvents(instance: TornadoInstance | string, event: Function | number = 0): void {
|
||||
let _instance: TornadoInstance
|
||||
let key: string
|
||||
|
||||
if (typeof instance !== 'string') {
|
||||
const { network, token, denomination } = Onchain.getInstanceLookupKeysSync(instance.address)
|
||||
_instance = this.loadInstance(network, token, denomination)
|
||||
key = network + token + denomination
|
||||
} else {
|
||||
key = instance.toLowerCase()
|
||||
_instance = this._resolveInstance(key)
|
||||
}
|
||||
|
||||
const filter = this._resolveInstanceEvent(_instance!, event)
|
||||
|
||||
const isDeposit = filter == _instance.filters.Deposit
|
||||
|
||||
const cache = isDeposit
|
||||
? this.loadDepositCache('Deposits' + key.toUpperCase())
|
||||
: this.loadWithdrawalCache('Withdrawals' + key.toUpperCase())
|
||||
|
||||
this.listenForEvents(
|
||||
isDeposit ? 'deposit' : 'withdrawal',
|
||||
_instance!,
|
||||
this._instanceEventToFilter(filter, _instance.filters.Deposit),
|
||||
cache
|
||||
)
|
||||
}
|
||||
|
||||
private _instanceEventToFilter(event: Function, depositEvent: Function): EventFilter {
|
||||
return event == depositEvent ? event(null, null, null) : event(null, null, null, null)
|
||||
}
|
||||
|
||||
private _resolveInstanceEvent(instance: TornadoInstance, event: Function | number = 0): Function {
|
||||
let filter: Function
|
||||
|
||||
if (typeof event === 'number') {
|
||||
filter = event === 0 ? instance.filters.Deposit : instance.filters.Withdrawal
|
||||
} else filter = event
|
||||
|
||||
return filter
|
||||
}
|
||||
|
||||
private _resolveInstance(instance: TornadoInstance | string): TornadoInstance {
|
||||
let _instance: TornadoInstance
|
||||
|
||||
if (typeof instance === 'string') {
|
||||
instance = instance.toLowerCase()
|
||||
|
||||
const regexp = /([0-9]+)([a-z]+)([0-9.]+)/
|
||||
const matches = instance.match(regexp)?.slice(1)
|
||||
|
||||
if (!matches || matches.length === 0)
|
||||
throw ErrorUtils.getError('Core._resolveInstance: instance string key invalid.')
|
||||
|
||||
_instance = this.loadInstance(matches[0], matches[1], matches[2])
|
||||
} else _instance = instance
|
||||
|
||||
return _instance
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main function to build a single Tornado Cash Classic deposit. An address need not be supplied because the returned note proves a deposit.
|
||||
* @param instance The TornadoInstance for which to build transactions.
|
||||
* @param options Whether or not to populate the transactions (only in the sense of encoding transaction data), and whether to backup notes and invoices. Defaults: `depositsPerInstance = [1], doNotPopulate = false, backup { notes = true, invoices = false }` Deposits per instance are hardcoded to 1, since we're doing a single transaction.
|
||||
* @returns A promise which resolves to the created transaction.
|
||||
*/
|
||||
buildDepositTransaction(instance: TornadoInstance, options?: Options.Core.Deposit): Transactions.Deposit {
|
||||
let opts: Options.Core.Deposit = options ?? {}
|
||||
opts.depositsPerInstance = [1]
|
||||
return (await this.buildDepositTransactions([instance], opts))[0]
|
||||
return this.buildDepositTransactions([instance], opts)[0]
|
||||
}
|
||||
|
||||
async buildDepositTransactions(
|
||||
/**
|
||||
* This is the main function which is used to build Tornado Cash Classic deposit transactions. An address need not be supplied because the returned note proves a deposit.
|
||||
* @param instances The TornadoInstance instances for which to build transactions.
|
||||
* @param options The number of deposits per instance, whether or not to populate the transactions (only in the sense of encoding transaction data), and whether to backup notes and invoices. Defaults: `depositsPerInstance = [1]*instance_num, doNotPopulate = false, backup { notes = true, invoices = false }`
|
||||
* @returns A promise which resolves to the created transactions.
|
||||
* @todo TODO: Maybe this should be sync and deposit backups should be async somewhere else
|
||||
*/
|
||||
buildDepositTransactions(
|
||||
instances: Array<TornadoInstance>,
|
||||
options?: Options.Core.Deposit
|
||||
): Promise<Array<Transactions.Deposit>> {
|
||||
): Array<Transactions.Deposit> {
|
||||
this._checkProvider('buildDepositTransactions')
|
||||
|
||||
const depositsPerInstance = options?.depositsPerInstance ?? new Array<number>(instances.length).fill(1)
|
||||
|
||||
const doNotPopulate = options?.doNotPopulate ?? false
|
||||
const backupNotes = options?.backup?.notes ?? true
|
||||
const backupInvoices = options?.backup?.invoices ?? false
|
||||
|
||||
if (depositsPerInstance.length != instances.length)
|
||||
throw ErrorUtils.getError(
|
||||
'Core.buildDepositTx: number of deposit amount elements must equal the number of instances!'
|
||||
)
|
||||
|
||||
const notesToBackup: Array<BackupDepositDoc> = []
|
||||
const invoicesToBackup: Array<BackupDepositDoc> = []
|
||||
const txs: Array<Transactions.Deposit> = []
|
||||
const chainId = await this.chain.getChainId()
|
||||
const chainId = this.chain!.id
|
||||
|
||||
const proxy: TornadoProxy = await Contracts.getProxy(String(chainId), this.chain.provider)
|
||||
const proxy: TornadoProxy = Contracts.getProxy(String(chainId), this.chain!.provider)
|
||||
|
||||
const txs: Array<Transactions.Deposit> = []
|
||||
|
||||
for (let i = 0, nInstances = instances.length; i < nInstances; i++) {
|
||||
const lookupKeys = await Onchain.getInstanceLookupKeys(instances[i].address)
|
||||
const pathstring = lookupKeys.network + lookupKeys.token + lookupKeys.denomination
|
||||
const { network, token, denomination } = Onchain.getInstanceLookupKeysSync(instances[i].address)
|
||||
const pathstring = network + token + denomination
|
||||
|
||||
for (let d = 0, nDeposits = depositsPerInstance[i]; d < nDeposits; d++) {
|
||||
const deposit = Primitives.createDeposit()
|
||||
const note = Primitives.createNote(deposit.preimage)
|
||||
|
||||
if (backupNotes)
|
||||
notesToBackup.push({
|
||||
network: lookupKeys.network,
|
||||
denomination: lookupKeys.denomination,
|
||||
token: lookupKeys.token,
|
||||
note: note
|
||||
})
|
||||
|
||||
if (backupInvoices)
|
||||
invoicesToBackup.push({
|
||||
network: lookupKeys.network,
|
||||
denomination: lookupKeys.denomination,
|
||||
token: lookupKeys.token,
|
||||
invoice: deposit.hexCommitment
|
||||
})
|
||||
|
||||
if (!doNotPopulate) {
|
||||
txs.push({
|
||||
request: {
|
||||
|
@ -554,7 +622,7 @@ export class Core extends Synchronizer {
|
|||
deposit.hexCommitment,
|
||||
[]
|
||||
]),
|
||||
value: lookupKeys.token == 'eth' ? parseUnits(lookupKeys.denomination) : BigNumber.from(0)
|
||||
value: token == 'eth' ? parseUnits(denomination) : BigNumber.from(0)
|
||||
},
|
||||
note: pathstring + '_' + note,
|
||||
invoice: pathstring + '_' + deposit.hexCommitment
|
||||
|
@ -568,41 +636,67 @@ export class Core extends Synchronizer {
|
|||
}
|
||||
}
|
||||
|
||||
if (backupNotes)
|
||||
await this._backupDepositData(this.loadCache<Cache.Base<Docs.Note>>('DepositNotes'), notesToBackup)
|
||||
|
||||
if (backupInvoices)
|
||||
await this._backupDepositData(
|
||||
this.loadCache<Cache.Base<Docs.Invoice>>('DepositInvoices'),
|
||||
invoicesToBackup
|
||||
)
|
||||
|
||||
return txs
|
||||
}
|
||||
|
||||
async backupNote(instance: TornadoInstance, transaction: Transactions.Deposit): Promise<void> {
|
||||
await this.backupNotes(instance, [transaction])
|
||||
}
|
||||
|
||||
async backupInvoice(instance: TornadoInstance, transaction: Transactions.Deposit): Promise<void> {
|
||||
await this.backupInvoices(instance, [transaction])
|
||||
}
|
||||
|
||||
async backupNotes(instance: TornadoInstance, transactions: Array<Transactions.Deposit>): Promise<void> {
|
||||
const { network, token, denomination } = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
await this._backupDepositData(
|
||||
network,
|
||||
token,
|
||||
denomination,
|
||||
transactions,
|
||||
this.loadCache<Cache.Base<Docs.Note>>('DepositNotes')
|
||||
)
|
||||
}
|
||||
|
||||
async backupInvoices(instance: TornadoInstance, transactions: Array<Transactions.Deposit>): Promise<void> {
|
||||
const { network, token, denomination } = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
await this._backupDepositData(
|
||||
network,
|
||||
token,
|
||||
denomination,
|
||||
transactions,
|
||||
this.loadCache<Cache.Base<Docs.Invoice>>('DepositInvoices')
|
||||
)
|
||||
}
|
||||
|
||||
private async _backupDepositData<T extends Docs.Note | Docs.Invoice>(
|
||||
cache: Cache.Base<T>,
|
||||
backupData: Array<BackupDepositDoc>
|
||||
network: string,
|
||||
token: string,
|
||||
denomination: string,
|
||||
transactions: Array<Transactions.Deposit>,
|
||||
cache: Cache.Base<T>
|
||||
): Promise<void> {
|
||||
const notes = cache.name.length === 12 ? true : false
|
||||
const name = notes ? 'notes' : 'invoices'
|
||||
|
||||
// We need a mutex here
|
||||
const release = await this._mutex.acquire(name)
|
||||
|
||||
let id = +(await cache.db.info()).update_seq
|
||||
|
||||
await cache.db
|
||||
.bulkDocs(
|
||||
backupData.map((entry) => {
|
||||
if (entry.note)
|
||||
return new Docs.Note(++id, entry.network, entry.token, entry.denomination, entry.note)
|
||||
else if (entry.invoice)
|
||||
return new Docs.Invoice(++id, entry.network, entry.token, entry.denomination, entry.invoice)
|
||||
transactions.map((transaction) => {
|
||||
if (notes) return new Docs.Note(++id, network, token, denomination, transaction.note!)
|
||||
else return new Docs.Invoice(++id, network, token, denomination, transaction.invoice!)
|
||||
}) as Array<T>
|
||||
)
|
||||
.catch((err) => {
|
||||
throw ErrorUtils.ensureError(err)
|
||||
})
|
||||
|
||||
// TODO: Decide whether to close caches by default or not
|
||||
//await cache.close().catch((err) => {
|
||||
// throw ErrorUtils.ensureError(err)
|
||||
//})
|
||||
// Release
|
||||
release()
|
||||
}
|
||||
|
||||
loadDepositCache(name: string, options?: Options.Sync): DepositCache {
|
||||
|
@ -644,50 +738,57 @@ export class Core extends Synchronizer {
|
|||
return this.caches.get(name) as C
|
||||
}
|
||||
|
||||
loadInstance(chainId: number | string, token: string, denomination: number | string): TornadoInstance {
|
||||
token = token.toLowerCase()
|
||||
return Contracts.getInstance('' + chainId, token, '' + denomination, this.chain!.provider)
|
||||
}
|
||||
|
||||
async syncDeposits(instance: TornadoInstance, options?: Options.Sync): Promise<void> {
|
||||
const lookupKeys = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
const pathstring = lookupKeys.network + lookupKeys.token + lookupKeys.denomination
|
||||
this._checkProvider('syncDeposits')
|
||||
|
||||
const { network, token, denomination } = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
|
||||
const pathstring = network + token + denomination
|
||||
|
||||
options = options ?? {}
|
||||
options.startBlock = await Onchain.getInstanceDeployBlockNum(
|
||||
lookupKeys.network,
|
||||
lookupKeys.token,
|
||||
lookupKeys.denomination
|
||||
)
|
||||
options.startBlock = await Onchain.getInstanceDeployBlockNum(network, token, denomination)
|
||||
|
||||
const populatedOptions = await this._populateSyncOptions(options)
|
||||
const cache = this.loadDepositCache('Deposits' + pathstring.toUpperCase(), populatedOptions)
|
||||
|
||||
await this.sync(instance.filters.Deposit(null, null, null), instance, cache, populatedOptions)
|
||||
await this.sync('deposit', instance.filters.Deposit(null, null, null), instance, cache, populatedOptions)
|
||||
|
||||
if (!this.instances.has(pathstring)) this.instances.set(pathstring, instance)
|
||||
if (!this.caches.has(cache.name)) this.caches.set(cache.name, cache)
|
||||
}
|
||||
|
||||
async syncWithdrawals(instance: TornadoInstance, options?: Options.Sync): Promise<void> {
|
||||
const lookupKeys = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
const pathstring = lookupKeys.network + lookupKeys.token + lookupKeys.denomination
|
||||
this._checkProvider('syncWithdrawals')
|
||||
|
||||
const { network, token, denomination } = await Onchain.getInstanceLookupKeys(instance.address)
|
||||
|
||||
const pathstring = network + token + denomination
|
||||
|
||||
options = options ?? {}
|
||||
options.startBlock = await Onchain.getInstanceDeployBlockNum(
|
||||
lookupKeys.network,
|
||||
lookupKeys.token,
|
||||
lookupKeys.denomination
|
||||
)
|
||||
options.startBlock = await Onchain.getInstanceDeployBlockNum(network, token, denomination)
|
||||
|
||||
const populatedOptions = await this._populateSyncOptions(options)
|
||||
const cache = this.loadWithdrawalCache('Withdrawals' + pathstring.toUpperCase(), populatedOptions)
|
||||
|
||||
await this.sync(instance.filters.Withdrawal(null, null, null), instance, cache, populatedOptions)
|
||||
await this.sync(
|
||||
'withdrawal',
|
||||
instance.filters.Withdrawal(null, null, null),
|
||||
instance,
|
||||
cache,
|
||||
populatedOptions
|
||||
)
|
||||
|
||||
if (!this.instances.has(pathstring)) this.instances.set(pathstring, instance)
|
||||
if (!this.caches.has(cache.name)) this.caches.set(cache.name, cache)
|
||||
}
|
||||
|
||||
protected async _populateSyncOptions(options: Options.Sync): Promise<DeepRequired<Options.Sync>> {
|
||||
if (!options.startBlock) throw ErrorUtils.getError('Core._populateSyncOptions: startBlock not set.')
|
||||
|
||||
options.targetBlock = options.targetBlock ?? (await this.chain.latestBlockNum())
|
||||
options.targetBlock = options.targetBlock ?? (await this.chain!.latestBlockNum())
|
||||
|
||||
options.blockDivisor = options.blockDivisor ?? 40
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue