2023.04.18: Check HISTORY.md for more info

Signed-off-by: T-Hax <>
This commit is contained in:
T-Hax 2023-04-17 21:56:57 +00:00
parent 0a7c98abb9
commit d83dcd8112
21 changed files with 194720 additions and 519 deletions

1
.gitignore vendored

@ -8,3 +8,4 @@ cache
.env .env
scripts scripts
reference reference
.npm

@ -1,4 +1,3 @@
build build
node_modules node_modules
package-log.json package-log.json
types/sol

@ -1,5 +1,17 @@
# History # History
### 2023.04.18 (2023-04-18)
Did:
* Trying to test withdrawals. The issue is that the ganache provider seemingly can't provide data on events via queryFilter. Have manually inserted events, commitments seem to pass but the note is deemed invalid by the contract on forknet.
* Tests are more complicated right now, they rely on synchronized caches. Per default, all but that withdraw one should pass.
* The good news is, if we pass the withdraw test, then we almost have the hardest part done.
Next:
* Need to reorganize into monorepo.
### 2023.04.12 (2023-04-12) ### 2023.04.12 (2023-04-12)
Did: Did:

@ -10,7 +10,7 @@
"zk" "zk"
], ],
"private": false, "private": false,
"version": "2023.04.12", "version": "2023.04.18",
"engines": { "engines": {
"node": "^18" "node": "^18"
}, },
@ -29,7 +29,8 @@
"lint": "eslint --ext ts,js --fix src", "lint": "eslint --ext ts,js --fix src",
"format": "prettier src/lib/**/*.ts src/types/sdk/**/*.ts src/test/**/*.ts -w", "format": "prettier src/lib/**/*.ts src/types/sdk/**/*.ts src/test/**/*.ts -w",
"build-live": "tsc -w && tsc-alias -w", "build-live": "tsc -w && tsc-alias -w",
"typechain": "typechain --target \"ethers-v5\" --discriminate-types --glob \"./src/resources/abis/*.json\" --out-dir=\"./src/types/deth\"" "typechain": "typechain --target \"ethers-v5\" --discriminate-types --glob \"./src/resources/abis/*.json\" --out-dir=\"./src/types/deth\"",
"clean": "rm -rf --interactive=never cache/*"
}, },
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
@ -74,6 +75,6 @@
"tsc-alias": "^1.2.11", "tsc-alias": "^1.2.11",
"tsconfig-paths": "^4.1.2", "tsconfig-paths": "^4.1.2",
"typechain": "^8.1.1", "typechain": "^8.1.1",
"typescript": "^5.1.0-dev.20230310" "typescript": "^5"
} }
} }

@ -3,4 +3,4 @@ export * as Data from "lib/data"
export * as Network from "lib/chain" export * as Network from "lib/chain"
export * as Web from "lib/web" export * as Web from "lib/web"
export * as Utils from "lib/utils" export * as Utils from "lib/utils"
export * from "lib/main" export * from "lib/core"

@ -1,12 +1,7 @@
// Tooling for interaction with the blockchain. // Types
import { MarkOptional } from 'ts-essentials'
import * as Types from 'types/sdk/chain' import * as Types from 'types/sdk/chain'
// External imports
import { TransactionRequest } from '@ethersproject/abstract-provider'
import { BaseContract, BigNumber, ContractTransaction, providers, Signer, VoidSigner } from 'ethers'
import { randomBytes } from 'crypto'
// Our local types // Our local types
import { import {
ERC20Tornado__factory, ERC20Tornado__factory,
@ -21,19 +16,19 @@ import {
} from 'types/deth' } from 'types/deth'
import { Multicall3 } from 'types/deth/Multicall3Contract' import { Multicall3 } from 'types/deth/Multicall3Contract'
// External imports
import { TransactionRequest } from '@ethersproject/abstract-provider'
import { BaseContract, BigNumber, ContractTransaction, providers, Signer, VoidSigner } from 'ethers'
import { randomBytes } from 'crypto'
// Local modules // Local modules
import { OnchainData } from 'lib/data' import { OnchainData } from 'lib/data'
import { ErrorUtils } from './utils' import { ErrorUtils, HexUtils } from 'lib/utils'
import { MarkOptional } from 'ts-essentials'
// We use a vanilla provider here, but in reality we will probably // We use a vanilla provider here, but in reality we will probably
// add a censorship-checking custom derivative of it // add a censorship-checking custom derivative of it
type Provider = providers.Provider type Provider = providers.Provider
export function prepareAddress(address: string): string {
return (address.slice(0, 2) == '0x' ? address.slice(2) : address).toLowerCase().padStart(64, '0')
}
/** /**
* The Chain class stores Tornado-agnostic chain data and also * The Chain class stores Tornado-agnostic chain data and also
* handles such interactions. * handles such interactions.
@ -43,6 +38,7 @@ export class Chain {
public provider: Provider public provider: Provider
private _emptySigner: VoidSigner private _emptySigner: VoidSigner
public chainId?: number public chainId?: number
public symbol?: string
constructor(provider: Provider, signer?: Signer) { constructor(provider: Provider, signer?: Signer) {
this.provider = provider this.provider = provider
@ -55,14 +51,27 @@ export class Chain {
return this.chainId return this.chainId
} }
async latestBlockNum(): Promise<number> { async getChainSymbol(): Promise<string> {
if (!this.symbol) this.symbol = await OnchainData.getNetworkSymbol(String(await this.getChainId()))
return this.symbol
}
latestBlockNum(): Promise<number> {
return this.provider.getBlockNumber() return this.provider.getBlockNumber()
} }
async getAccountBalance(account: string): Promise<BigNumber> { getAccountBalance(account: string): Promise<BigNumber> {
return this.provider.getBalance(account) return this.provider.getBalance(account)
} }
getGasPrice(): Promise<BigNumber> {
return this.provider.getGasPrice()
}
getTokenContract(tokenAddress: string): ERC20 {
return Contracts.getToken(tokenAddress, this.signer ?? this.provider)
}
async getTokenDecimals(token: string): Promise<BigNumber> { async getTokenDecimals(token: string): Promise<BigNumber> {
let treq = { let treq = {
to: token, to: token,
@ -74,16 +83,12 @@ export class Chain {
async getTokenBalance(account: string, token: string, normalized: boolean = false): Promise<BigNumber> { async getTokenBalance(account: string, token: string, normalized: boolean = false): Promise<BigNumber> {
let treq = { let treq = {
to: token, to: token,
data: '0x70a08231000000000000000000000000' + prepareAddress(account) data: '0x70a08231000000000000000000000000' + HexUtils.prepareAddress(account)
} }
let divisor = normalized ? BigNumber.from(10).pow(await this.getTokenDecimals(token)) : 1 let divisor = normalized ? BigNumber.from(10).pow(await this.getTokenDecimals(token)) : 1
return BigNumber.from(await this._emptySigner.call(treq)).div(divisor) return BigNumber.from(await this._emptySigner.call(treq)).div(divisor)
} }
getTokenContract(tokenAddress: string): ERC20 {
return ERC20__factory.connect(tokenAddress, this.provider)
}
async populateBatchCall( async populateBatchCall(
callStruct: Array<MarkOptional<Multicall3.Call3ValueStruct, 'value'>> callStruct: Array<MarkOptional<Multicall3.Call3ValueStruct, 'value'>>
): Promise<TransactionRequest> { ): Promise<TransactionRequest> {
@ -181,7 +186,13 @@ export namespace Contracts {
return contractMap.get(key) as TornadoInstance return contractMap.get(key) as TornadoInstance
} }
export async function getTornToken(signerOrProvider: Signer | Provider): Promise<ERC20> { export function getToken(tokenAddress: string, signerOrProvider: Signer | Provider): ERC20 {
if (!contractMap.has(tokenAddress))
contractMap.set(tokenAddress, _getContract<ERC20>('ERC20', tokenAddress, signerOrProvider))
return contractMap.get(tokenAddress) as ERC20
}
export function getTornToken(signerOrProvider: Signer | Provider): ERC20 {
const key = '$TORN' const key = '$TORN'
if (!contractMap.has(key)) { if (!contractMap.has(key)) {
contractMap.set( contractMap.set(

@ -1,37 +1,41 @@
// ts-essentials // ts-essentials
import { DeepRequired } from 'ts-essentials' import { DeepRequired, MarkOptional, MarkRequired } from 'ts-essentials'
// Local types // Local types
import { RelayerProperties } from 'types/sdk/data' import { RelayerProperties as RelayerDataProperties } from 'types/sdk/data'
import { Options, Transactions } from 'types/sdk/main' import { Options, Transactions } from 'types/sdk/core'
import { ZKDepositData, InputFor } from 'types/sdk/crypto' import { ZKDepositData, InputFor } from 'types/sdk/crypto'
import { TornadoInstance, TornadoProxy } from 'types/deth' import { TornadoInstance, TornadoProxy } from 'types/deth'
// External imports // External imports
import { BigNumber, EventFilter, providers } from 'ethers' import { BigNumber, EventFilter, providers } from 'ethers'
import { parseUnits } from 'ethers/lib/utils'
import { bigInt } from 'snarkjs'
// @ts-ignore // @ts-ignore
import { parseIndexableString } from 'pouchdb-collate' import { parseIndexableString } from 'pouchdb-collate'
// Important local // Local imports
import { Docs, Cache, Types as DataTypes, Json, Constants } from 'lib/data' import { Docs, Cache, Types as DataTypes, Json, Constants, OnchainData } from 'lib/data'
import { Primitives } from 'lib/crypto' import { Primitives } from 'lib/crypto'
import { Contracts } from 'lib/chain' import { Contracts, Chain } from 'lib/chain'
import { ErrorUtils, ObjectUtils } from 'lib/utils'
// Other local imports
import { OnchainData } from 'lib/data'
import { ErrorUtils } from 'lib/utils'
import { Chain } from 'lib/chain'
import { parseUnits } from 'ethers/lib/utils'
type Provider = providers.Provider type Provider = providers.Provider
type BackupDepositDoc = { type BackupDepositDoc = {
pathstring: string network: string
denomination: string
token: string
invoice?: string invoice?: string
note?: string note?: string
} }
type RelayerProperties = MarkOptional<
Pick<RelayerDataProperties, 'address' | 'serviceFeePercent' | 'prices'>,
'serviceFeePercent' | 'prices'
>
export class Core { export class Core {
chain: Chain chain: Chain
caches: Map<string, Cache.Base<Docs.Base>> caches: Map<string, Cache.Base<Docs.Base>>
@ -39,7 +43,7 @@ export class Core {
constructor(provider: providers.Provider) { constructor(provider: providers.Provider) {
this.chain = new Chain(provider) this.chain = new Chain(provider)
this.caches = new Map<string, Cache.Base<Docs.Base>>() this.caches = new Map<string, Cache.Syncable<Docs.Base>>()
this.instances = new Map<string, TornadoInstance>() this.instances = new Map<string, TornadoInstance>()
} }
@ -65,7 +69,7 @@ export class Core {
async getProxy(): Promise<TornadoProxy> { async getProxy(): Promise<TornadoProxy> {
const chainId = await this.chain.getChainId() const chainId = await this.chain.getChainId()
return Contracts.getProxy(String(chainId), this.chain.signer ?? this.chain.provider) return Contracts.getProxy(String(chainId), this.chain.provider)
} }
async buildDepositProof( async buildDepositProof(
@ -74,7 +78,7 @@ export class Core {
recipientAddress: string, recipientAddress: string,
zkDepositsData: ZKDepositData, zkDepositsData: ZKDepositData,
options?: Options.Core.BuildDepositProof options?: Options.Core.BuildDepositProof
): Promise<any> { ): Promise<Array<string>> {
return ( return (
await this.buildDepositProofs( await this.buildDepositProofs(
instance, instance,
@ -88,10 +92,10 @@ export class Core {
/** /**
* @param instance This is the Tornado Instance which will be withdrawn from. * @param instance This is the Tornado Instance which will be withdrawn from.
* @param relayerProperties The properties of the relayer that is going to be used for the withdrawals. These properties are included in the ZK proof. * @param relayerProperties The properties of the relayer that is going to be used for the withdrawals. If the service fee is 0, it is assumed that there is no relayer, but that a manual wallet withdraw is being made. These properties are included in the ZK proof.
* @param recipientAddresses The recipient addresses which should receive the withdrawals, in order. * @param recipientAddresses The recipient addresses which should receive the withdrawals, in order.
* @param zkDepositsData These represent the public and private values, reconstructed from the deposit note, generated during the building of deposit transactions, used for building the proof of knowledge statement for withdrawal, for each withdrawal (in this context). * @param zkDepositsData These represent the public and private values, reconstructed from the deposit note, generated during the building of deposit transactions, used for building the proof of knowledge statement for withdrawal, for each withdrawal (in this context).
* @param options Additional options which allow the user to skip checking whether the notes are spent or changing the target merkle tree height. * @param options Numerous options which most importantly allow a user to specify whether he is buying ETH, whether to check proof data validity and finally to modulate the gas prices which will be used to calculate the gas fees paid to the relayer.
* @returns The proofs for which the user should then decide whether to use a relayer (recommended, but decide carefully which one) or use his own wallet (if needed). * @returns The proofs for which the user should then decide whether to use a relayer (recommended, but decide carefully which one) or use his own wallet (if needed).
*/ */
async buildDepositProofs( async buildDepositProofs(
@ -100,16 +104,19 @@ export class Core {
recipientAddresses: Array<string>, recipientAddresses: Array<string>,
zkDepositsData: Array<ZKDepositData>, zkDepositsData: Array<ZKDepositData>,
options?: Options.Core.BuildDepositProof options?: Options.Core.BuildDepositProof
): Promise<Array<any>> { ): Promise<Array<Array<string>>> {
// Extract commitments and nullifier hashes // Extract commitments and nullifier hashes
const hexCommitments: string[] = [] const hexCommitments: string[] = []
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.'
) )
if (zkDepositsData.length !== purchaseAmounts.length) if (zkDepositsData.length !== purchaseAmounts.length)
throw ErrorUtils.getError( throw ErrorUtils.getError(
'Core.buildDepositProofs: if purchase amounts is specified, it must equal the length of zkDepositsData.' 'Core.buildDepositProofs: if purchase amounts is specified, it must equal the length of zkDepositsData.'
@ -120,20 +127,26 @@ 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 this.getInstanceLookupKeys(instance.address)
const name = 'Deposit' + (lookupKeys.network + lookupKeys.token + lookupKeys.denomination).toUpperCase() const name = 'Deposits' + (lookupKeys.network + lookupKeys.token + lookupKeys.denomination).toUpperCase()
// Find all leaf indices by reading from cache console.log('\nLeaves and indices.\n')
const leafIndices = await this._findLeafIndices(name, hexCommitments)
// Find all leaves & indices by reading from cache
const [leaves, leafIndices] = await this._findLeavesAndIndices(name, hexCommitments)
const invalidCommitments: string[] = [] const invalidCommitments: string[] = []
// Determine whether we will be checking whether notes are spent // Determine whether we will be checking whether notes are spent
const spentNotes: string[] = []
const checkSpent = options?.checkNotesSpent !== false const checkSpent = options?.checkNotesSpent !== false
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) : null const checkSpentArray = checkSpent ? await instance.isSpentArray(hexNullifierHashes) : undefined
// Check whether a commitment has not been found in all deposits, meaning that it is invalid // Check whether a commitment has not been found in all deposits, meaning that it is invalid
// Also add the invalid commitments. We can do leafIndices[i] because the matched one are concatenated // Also add the invalid commitments. We can do leafIndices[i] because the matched one are concatenated
@ -148,37 +161,65 @@ 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.buildWithdrawalTxs: ` + `Core.buildDepositProofs: ` +
(commitmentsAreInvalid (commitmentsAreInvalid
? `following commitments are invalid:\n\n${invalidCommitments.join('\n')}\n\n` ? `following commitments are invalid:\n\n${invalidCommitments.join('\n')}\n\n`
: '') + : '') +
(notesAreSpent (notesAreSpent
? `${commitmentsAreInvalid ? 'and ' : ''}following notes are already spent:\n\n${spentNotes.join( ? `${
'\n' commitmentsAreInvalid ? 'and ' : ''
)}\n\n` }following notes are already spent or invalid:\n\n${spentNotes.join('\n')}\n\n`
: '') : '')
) )
// Otherwise, build the merkle tree from the leaf indices console.log('\nMerkle tree.\n')
// We have to slice to get the leaf indices in order
// 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,
leaves: leafIndices.slice(zkDepositsData.length).map((leafIndex) => String(leafIndex)) leaves: leaves
}) })
const root: string = merkleTree.root() const root: string = merkleTree.root()
const checkKnownRoot: boolean = options?.checkKnownRoot ?? true
// Check whether the root is valid // Check whether the root is valid
if (!(await instance.isKnownRoot(root))) if (checkKnownRoot && !(await instance.isKnownRoot(root)))
throw ErrorUtils.getError( throw ErrorUtils.getError(
'Core.buildWithdrawalTxs: 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.'
) )
// Compute proofs console.log('\nProof data invariant.\n')
const inputsForProofs: InputFor.ZKProof[] = []
// Rest of note invariant arguments
const inputsForProofs: InputFor.ZKProof[] = []
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 serviceFee = relayerProperties.serviceFeePercent ?? 0
const tokenPrice = relayerProperties.prices?.get(lookupKeys.token)
const decimals =
// @ts-expect-error
bigInt(10).pow(
options?.tokenDecimals ?? (await OnchainData.getTokenDecimals(lookupKeys.network, lookupKeys.token))
)
const toWithdraw = BigNumber.from(lookupKeys.denomination).mul(decimals)
// TODO: Decide if necessary
if (!tokenPrice && lookupKeys.token !== (await this.chain.getChainSymbol()))
throw ErrorUtils.getError(
'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++) { for (let i = 0, len = zkDepositsData.length; i < len; i++) {
inputsForProofs.push({ inputsForProofs.push({
public: { public: {
@ -188,8 +229,17 @@ export class Core {
hexNullifierHash: zkDepositsData[i].hexNullifierHash, hexNullifierHash: zkDepositsData[i].hexNullifierHash,
recipientAddress: recipientAddresses[i], recipientAddress: recipientAddresses[i],
relayerAddress: relayerProperties.address, relayerAddress: relayerProperties.address,
fee: 5, // TODO: placeholder fee: this._calcWithdrawalFee(
refund: purchaseAmounts[i] ?? 0 toWithdraw,
decimals,
gasPrice,
gasPriceCushion,
serviceFee,
purchaseAmounts[i],
tokenPrice
),
// @ts-expect-error
refund: bigInt(purchaseAmounts[i].toString()) ?? bigInt(0)
}, },
private: { private: {
nullifier: zkDepositsData[i].nullifier, nullifier: zkDepositsData[i].nullifier,
@ -198,9 +248,68 @@ export class Core {
}) })
} }
console.log('\nCalc and return.\n')
return await Primitives.calcDepositProofs(inputsForProofs) return await Primitives.calcDepositProofs(inputsForProofs)
} }
private _calcWithdrawalFee(
toWithdraw: BigNumber,
decimals: BigNumber,
gasPrice: BigNumber,
gasPriceCushion: BigNumber,
relayerServiceFee: number,
ethBought?: BigNumber,
tokenPriceInEth?: BigNumber
): typeof bigInt {
const factor = BigNumber.from(10).pow(String(relayerServiceFee).length)
const baseRelayerFee = toWithdraw.mul(BigNumber.from(relayerServiceFee).mul(factor)).div(factor)
const txCost = gasPrice.add(gasPriceCushion).mul(5e5)
if (ethBought && tokenPriceInEth) {
// @ts-expect-error
return bigInt(txCost.add(ethBought).mul(decimals).div(tokenPriceInEth).add(baseRelayerFee).toString())
}
// @ts-expect-error
else return bigInt(txCost.add(baseRelayerFee).toString())
}
async loadNotes(
indexes?: Array<number>,
keys?: Partial<DataTypes.Keys.InstanceLookup>
): Promise<Array<ZKDepositData>> {
const rows = await Cache.loadContents<Docs.Note>('DepositNotes')
let docs: Array<Docs.Note | undefined> = []
let notes: Array<string> = []
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)
}
else docs = rows.map((row) => row.doc)
if (keys)
docs.forEach((doc) => {
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)
if (lastlyDenominationMatches && doc?.note) notes.push(doc.note)
})
else notes = docs.filter((doc) => ObjectUtils.exists(doc?.note)).map((doc) => doc!.note)
return this.parseNotes(notes)
}
parseNotes(notes: Array<string>): Array<ZKDepositData> {
return notes.map((note) => Primitives.parseNote(note))
}
parseNote(note: string): ZKDepositData {
return this.parseNotes([note])[0]
}
async createInvoice( async createInvoice(
instance: TornadoInstance, instance: TornadoInstance,
options?: Omit<Options.Core.Invoice, 'depositsPerInstance'> options?: Omit<Options.Core.Invoice, 'depositsPerInstance'>
@ -261,9 +370,21 @@ export class Core {
const deposit = Primitives.createDeposit() const deposit = Primitives.createDeposit()
const note = Primitives.createNote(deposit.preimage) const note = Primitives.createNote(deposit.preimage)
if (backupNotes) notesToBackup.push({ pathstring: pathstring, note: note }) if (backupNotes)
notesToBackup.push({
network: lookupKeys.network,
denomination: lookupKeys.denomination,
token: lookupKeys.token,
note: note
})
if (backupInvoices) invoicesToBackup.push({ pathstring: pathstring, invoice: deposit.hexCommitment }) if (backupInvoices)
invoicesToBackup.push({
network: lookupKeys.network,
denomination: lookupKeys.denomination,
token: lookupKeys.token,
invoice: deposit.hexCommitment
})
if (!doNotPopulate) { if (!doNotPopulate) {
txs.push({ txs.push({
@ -305,8 +426,10 @@ export class Core {
await cache.db await cache.db
.bulkDocs( .bulkDocs(
backupData.map((entry) => { backupData.map((entry) => {
if (entry.note) return new Docs.Note(++id, entry.pathstring, entry.note) if (entry.note)
else if (entry.invoice) return new Docs.Invoice(++id, entry.pathstring, entry.invoice) 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)
}) as Array<T> }) as Array<T>
) )
.catch((err) => { .catch((err) => {
@ -318,6 +441,13 @@ export class Core {
}) })
} }
loadCache<T extends Docs.Base, C extends Cache.Base<T>>(cacheName: string): C {
if (!this.caches.has(cacheName)) {
this.caches.set(cacheName, new Cache.Base<T>(cacheName))
}
return this.caches.get(cacheName) as C
}
async syncMultiple(instances: Array<TornadoInstance>, syncOptions?: Options.Core.Sync): Promise<void> { async syncMultiple(instances: Array<TornadoInstance>, syncOptions?: Options.Core.Sync): Promise<void> {
for (const instance of instances) { for (const instance of instances) {
await this.sync(instance, syncOptions) await this.sync(instance, syncOptions)
@ -350,21 +480,41 @@ export class Core {
}`, }`,
pathstring = name.substring(action.length).toLowerCase() pathstring = name.substring(action.length).toLowerCase()
let cache: Cache.Syncable<Docs.Base>, toDoc: (_: any) => Docs.Base, filter: EventFilter let cache: Cache.Syncable<Docs.Base>,
toDoc: (_: any) => Docs.Base,
filter: EventFilter,
numEntries: number
if (action == 'Deposit') { if (action == 'Deposit') {
toDoc = (resp: any) => new Docs.Deposit(resp) toDoc = (resp: any) => new Docs.Deposit(resp)
cache = new Cache.Deposit(name, syncOptions.cache) 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) filter = instance.filters.Deposit(null, null, null)
} else { } else {
toDoc = (resp: any) => new Docs.Withdrawal(resp) toDoc = (resp: any) => new Docs.Withdrawal(resp)
cache = new Cache.Withdrawal(name, syncOptions.cache) 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) filter = instance.filters.Withdrawal(null, null, null, null)
} }
// Assign pooler // Assign pooler
cache.sync.pooler = await cache.sync.initializePooler(cache.getCallbacks(instance)) cache.sync.pooler = await cache.sync.initializePooler(cache.getCallbacks(instance))
// Decide whether we have a latest block
numEntries = (await cache.db.info()).doc_count
// Check for synced blocks
if (0 < numEntries) {
const [lastSyncedBlock, ,] = parseIndexableString(
(await cache.db.allDocs({ descending: true, limit: 1 })).rows[0].id
)
syncOptions.blocks.startBlock =
lastSyncedBlock < syncOptions.blocks.startBlock ? syncOptions.blocks.startBlock : lastSyncedBlock
syncOptions.blocks.blockDelta = this._getBlockDelta(syncOptions)
}
// Start synchronizing // Start synchronizing
let dbPromises = [] let dbPromises = []
@ -373,14 +523,14 @@ export class Core {
blockDelta = syncOptions.blocks.blockDelta, blockDelta = syncOptions.blocks.blockDelta,
targetBlock = syncOptions.blocks.targetBlock, targetBlock = syncOptions.blocks.targetBlock,
concurrencyLimit = syncOptions.cache.sync.concurrencyLimit; concurrencyLimit = syncOptions.cache.sync.concurrencyLimit;
currentBlock < targetBlock + blockDelta; currentBlock < targetBlock;
currentBlock += blockDelta currentBlock += blockDelta
) { ) {
if (cache.sync.pooler.pending < concurrencyLimit) { if (cache.sync.pooler.pending < concurrencyLimit) {
if (currentBlock < targetBlock) { const sum = currentBlock + blockDelta
await cache.sync.pooler.pool(currentBlock, currentBlock + blockDelta) if (currentBlock + blockDelta < targetBlock) {
await cache.sync.pooler.pool(currentBlock, sum)
} else { } else {
let sum = currentBlock + blockDelta
await cache.sync.pooler.pool(currentBlock, sum - (sum % targetBlock)) await cache.sync.pooler.pool(currentBlock, sum - (sum % targetBlock))
} }
} else { } else {
@ -452,8 +602,7 @@ export class Core {
syncOptions.blocks.targetBlock = syncOptions.blocks.targetBlock ?? (await this.chain.latestBlockNum()) syncOptions.blocks.targetBlock = syncOptions.blocks.targetBlock ?? (await this.chain.latestBlockNum())
syncOptions.blocks.blockDelta = syncOptions.blocks.blockDelta = this._getBlockDelta(syncOptions)
syncOptions.blocks.blockDelta ?? (syncOptions.blocks.targetBlock - syncOptions.blocks.startBlock) / 20
// cache // cache
// db // db
@ -467,14 +616,24 @@ export class Core {
return syncOptions as DeepRequired<Options.Core.Sync> return syncOptions as DeepRequired<Options.Core.Sync>
} }
private _getBlockDelta(syncOptions?: Options.Core.Sync): number {
return Math.floor(
syncOptions?.blocks?.blockDelta ??
(syncOptions!.blocks!.targetBlock! - syncOptions!.blocks!.startBlock!) / 20
)
}
/** /**
* @param instanceName The name of the instance as created in `_sync` function. * @param instanceName The name of the instance as created in `_sync` function.
* @param commitments The commitments for which the leaf index values are to be noted down extra. * @param commitments The commitments for which the leaf index values are to be noted down extra.
* @returns The result of concatenating the array of leaf indices found by matching them with the provided commitment values, followed by the array of all leaf indices, including all of the formerly mentioned values given that they are valid. Values which have not been matched, meaning probably invalid values, will be `0`. * @returns The result of concatenating the array of leaf indices found by matching them with the provided commitment values, followed by the array of all leaf indices, including all of the formerly mentioned values given that they are valid. Values which have not been matched, meaning probably invalid values, will be `0`.
*/ */
private async _findLeafIndices(instanceName: string, commitments: Array<string>): Promise<Array<number>> { private async _findLeavesAndIndices(
const matchedLeafIndices = new Array<number>(commitments.length).fill(0) instanceName: string,
const leafIndices: Array<number> = [] commitments: Array<string>
): Promise<[Array<string>, Array<number>]> {
const indices = new Array<number>(commitments.length).fill(0)
const leaves: Array<string> = []
// Either load all deposit events from memory or from cache // Either load all deposit events from memory or from cache
let cache: Cache.Base<Docs.Deposit> let cache: Cache.Base<Docs.Deposit>
@ -493,24 +652,23 @@ export class Core {
) )
} }
// Otherwise start looking for commitment leaf indices and also pick up // Otherwise start looking for commitment leaf indices and also pick up all other leafs on the way
// all other leafs on the way
for (const row of docs.rows) { for (const row of docs.rows) {
const [, leafIndex, loadedCommitment] = parseIndexableString(row.id) const [, leafIndex, loadedCommitment] = parseIndexableString(row.id)
const index = commitments.findIndex((commitment) => commitment === loadedCommitment) const index = commitments.findIndex((commitment) => commitment === loadedCommitment)
// If some commitment is found then add the leaf index and remove that commitment // If some commitment is found then add the leaf index and remove that commitment
if (index !== -1) { if (index !== -1) {
matchedLeafIndices[index] = leafIndex indices[index] = leafIndex
commitments.splice(index, 1) commitments.splice(index, 1)
} }
// In any case push every leaf // In any case push every leaf
leafIndices.push(leafIndex) leaves.push(BigNumber.from(loadedCommitment).toString())
} }
// Concat matched and all leaf indices // Concat matched and all leaf indices
return matchedLeafIndices.concat(leafIndices) return [leaves, indices]
} }
async getInstanceLookupKeys(instanceAddress: string): Promise<DataTypes.Keys.InstanceLookup> { async getInstanceLookupKeys(instanceAddress: string): Promise<DataTypes.Keys.InstanceLookup> {
@ -521,7 +679,6 @@ export class Core {
const pathstring: string = Object.entries(lookupObj).find((el) => el[1] === instanceAddress)![0] const pathstring: string = Object.entries(lookupObj).find((el) => el[1] === instanceAddress)![0]
// I like JS/TS
const network = pathstring.match('[0-9]+')![0], const network = pathstring.match('[0-9]+')![0],
token = pathstring.substring(network.length).match('[a-z]+')![0], token = pathstring.substring(network.length).match('[a-z]+')![0],
denomination = pathstring.substring(network.length + token.length) denomination = pathstring.substring(network.length + token.length)

@ -50,7 +50,8 @@ export namespace Primitives {
} }
export function parseNote(hexNote: string): Types.ZKDepositData { export function parseNote(hexNote: string): Types.ZKDepositData {
const buffer = Buffer.from(hexNote, 'hex') const _hexNote = hexNote.split('_')[1] ?? hexNote
const buffer = Buffer.from(_hexNote, 'hex')
return createDeposit({ return createDeposit({
// @ts-expect-error // @ts-expect-error
nullifier: bigInt.leBuff2int(buffer.subarray(0, 31)), nullifier: bigInt.leBuff2int(buffer.subarray(0, 31)),
@ -66,11 +67,11 @@ export namespace Primitives {
secret: NumberUtils.randomBigInteger(31) secret: NumberUtils.randomBigInteger(31)
} }
// @ts-expect-error // @ts-expect-error
let preimage = Buffer.concat([depositData.nullifier.leInt2Buff(31), depositData.secret.leInt2Buff(31)]) let preimage = Buffer.concat([input.nullifier.leInt2Buff(31), input.secret.leInt2Buff(31)])
let commitment = calcPedersenHash({ msg: preimage }) let commitment = calcPedersenHash({ msg: preimage })
let commitmentHex = HexUtils.bigIntToHex(commitment) let commitmentHex = HexUtils.bigIntToHex(commitment)
// @ts-expect-error // @ts-expect-error
let nullifierHash = calcPedersenHash({ msg: depositData.nullifier.leInt2Buff(31) }) let nullifierHash = calcPedersenHash({ msg: input.nullifier.leInt2Buff(31) })
let nullifierHex = HexUtils.bigIntToHex(nullifierHash) let nullifierHex = HexUtils.bigIntToHex(nullifierHash)
return { return {
nullifier: input.nullifier!, nullifier: input.nullifier!,
@ -87,10 +88,10 @@ export namespace Primitives {
return new MerkleTree(inputs.height, inputs.leaves) return new MerkleTree(inputs.height, inputs.leaves)
} }
export async function calcDepositProofs(inputs: Array<Types.InputFor.ZKProof>): Promise<Array<any>> { export async function calcDepositProofs(
inputs: Array<Types.InputFor.ZKProof>
): Promise<Array<Array<string>>> {
const proofs: string[][] = [] const proofs: string[][] = []
const args: any[][] = []
const groth16 = await Setup.getGroth16() const groth16 = await Setup.getGroth16()
const circuit = await Setup.getTornadoCircuit() const circuit = await Setup.getTornadoCircuit()
const provingKey = await Setup.getProvingKey() const provingKey = await Setup.getProvingKey()
@ -101,7 +102,6 @@ export namespace Primitives {
// Compute Merkle Proof // Compute Merkle Proof
const { pathElements, pathIndex } = input.public.tree.path(input.public.leafIndex) const { pathElements, pathIndex } = input.public.tree.path(input.public.leafIndex)
args.push([])
proofs.push([]) proofs.push([])
const proofData = await genWitnessAndProve( const proofData = await genWitnessAndProve(
@ -109,16 +109,16 @@ export namespace Primitives {
{ {
// Public inputs // Public inputs
root: input.public.root, root: input.public.root,
// @ts-ignore // @ts-expect-error
nullifierHash: bigInt(input.public.hexNullifierHash), nullifierHash: bigInt(input.public.hexNullifierHash),
// @ts-ignore // @ts-expect-error
fee: bigInt(input.public.fee),
// @ts-ignore
refund: bigInt(input.public.refund),
// @ts-ignore
relayer: bigInt(input.public.relayerAddress),
// @ts-ignore
recipient: bigInt(input.public.recipientAddress), recipient: bigInt(input.public.recipientAddress),
// @ts-expect-error
relayer: bigInt(input.public.relayerAddress),
// @ts-expect-error
fee: bigInt(input.public.fee),
//
refund: input.public.refund,
// Private inputs // Private inputs
nullifier: input.private.nullifier, nullifier: input.private.nullifier,
@ -132,21 +132,20 @@ export namespace Primitives {
proofs[i].push(toSolidityInput(proofData).proof) proofs[i].push(toSolidityInput(proofData).proof)
args[i].push([ proofs[i].push(
input.public.root, input.public.root,
input.public.hexNullifierHash, input.public.hexNullifierHash,
HexUtils.prepareAddress(input.public.recipientAddress, 20), '0x' + HexUtils.prepareAddress(input.public.recipientAddress, 20),
// @ts-ignore '0x' + HexUtils.prepareAddress(input.public.relayerAddress, 20),
HexUtils.prepareAddress(input.public.relayerAddress, 20), HexUtils.bigIntToHex(input.public.fee),
HexUtils.numberToHex(input.public.fee), HexUtils.bigIntToHex(input.public.refund)
HexUtils.numberToHex(input.public.refund) )
])
} }
// Done. 🤷‍♀️ // Done. 🤷‍♀️
groth16.terminate() groth16.terminate()
return proofs.concat(args) return proofs
} }
} }

@ -2,11 +2,7 @@
import { TornadoInstance } from 'types/deth' import { TornadoInstance } from 'types/deth'
import * as Types from 'types/sdk/data' import * as Types from 'types/sdk/data'
import { RelayerProperties } from 'types/sdk/data' import { RelayerProperties } from 'types/sdk/data'
import { Options } from 'types/sdk/main' import { Options } from 'types/sdk/core'
// Local logic
import { NumberUtils, ErrorUtils } from 'lib/utils'
import { AsyncUtils } from 'lib/utils'
// Big modules // Big modules
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
@ -14,13 +10,15 @@ import { existsSync, mkdirSync } from 'fs'
import { opendir, readFile, rm } from 'fs/promises' import { opendir, readFile, rm } from 'fs/promises'
import { createInterface } from 'readline' import { createInterface } from 'readline'
// Local logic
import { AsyncUtils, NumberUtils, ErrorUtils } from 'lib/utils'
// PouchDB // PouchDB
import PouchDB from 'pouchdb' import PouchDB from 'pouchdb'
import * as PouchDBAdapterMemory from 'pouchdb-adapter-memory' import * as PouchDBAdapterMemory from 'pouchdb-adapter-memory'
// @ts-ignore // @ts-ignore
import { toIndexableString } from 'pouchdb-collate' import { toIndexableString } from 'pouchdb-collate'
import { timeStamp } from 'console'
// Register plugins // Register plugins
PouchDB.plugin(PouchDBAdapterMemory) PouchDB.plugin(PouchDBAdapterMemory)
@ -146,20 +144,24 @@ export namespace OnchainData {
export async function getInstanceAttrSet<T>( export async function getInstanceAttrSet<T>(
key: string, key: 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('onchain/quickLookup.json')
return await Promise.all( return await Promise.all(
paths.map((path) => Json.getValue(obj, [key, `${path.network}${path.token}${path.denomination}`])) paths.map((path) =>
Json.getValue(obj, [key, `${path.network ?? '\0'}${path.token ?? '\0'}${path.denomination ?? '\0'}`])
)
) )
} }
// TODO: Add field key for classic or nova export async function getNetworkSymbol(networkId: string): Promise<string> {
return (await getInstanceAttrSet<string>('networkSymbols', [{ network: networkId }]))[0]
}
export async function getInstanceAddresses( export function getInstanceAddresses(
paths: Array<{ paths: Array<{
network: string network: string
token: string token: string
@ -177,7 +179,7 @@ export namespace OnchainData {
return (await getInstanceAddresses([{ network: network, token: token, denomination: denomination }]))[0] return (await getInstanceAddresses([{ network: network, token: token, denomination: denomination }]))[0]
} }
export async function getInstanceDeployBlockNums( export function getInstanceDeployBlockNums(
paths: Array<{ paths: Array<{
network: string network: string
token: string token: string
@ -217,6 +219,10 @@ export namespace OnchainData {
address: data['address'] address: data['address']
} }
} }
export async function getTokenDecimals(network: string, token: string): Promise<number> {
return (await getTokenData(network, token)).decimals
}
} }
export namespace OffchainData { export namespace OffchainData {
@ -307,23 +313,31 @@ export namespace Docs {
} }
export class Note extends Base { export class Note extends Base {
pathstring: string network: string
token: string
denomination: string
note: string note: string
constructor(index: number, pathstring: string, note: string) { constructor(index: number, network: string, token: string, denomination: string, note: string) {
super(toIndexableString([index, pathstring])) super(toIndexableString([index, network, denomination, token]))
this.pathstring = pathstring this.network = network
this.token = token
this.denomination = denomination
this.note = note this.note = note
} }
} }
export class Invoice extends Base { export class Invoice extends Base {
pathstring: string network: string
token: string
denomination: string
invoice: string invoice: string
constructor(index: number, pathstring: string, invoice: string) { constructor(index: number, network: string, token: string, denomination: string, invoice: string) {
super(toIndexableString([index, pathstring])) super(toIndexableString([index, network, denomination, token]))
this.pathstring = pathstring this.network = network
this.token = token
this.denomination = denomination
this.invoice = invoice this.invoice = invoice
} }
} }
@ -335,6 +349,7 @@ export namespace Docs {
miningFeePercent: number miningFeePercent: number
status: string status: string
chainId: number chainId: number
prices: Map<string, BigNumber>
constructor(url: string, properties: RelayerProperties) { constructor(url: string, properties: RelayerProperties) {
super(toIndexableString([url])) super(toIndexableString([url]))
@ -344,6 +359,7 @@ export namespace Docs {
this.miningFeePercent = properties.miningFeePercent this.miningFeePercent = properties.miningFeePercent
this.status = properties.status this.status = properties.status
this.chainId = properties.chainId this.chainId = properties.chainId
this.prices = properties.prices
} }
} }
} }
@ -354,10 +370,10 @@ export namespace Cache {
constructor(name: string, options?: Options.Cache.Database) { constructor(name: string, options?: Options.Cache.Database) {
if (options?.persistent === false && options?.adapter !== 'memory' && options?.adapter !== null) if (options?.persistent === false && options?.adapter !== 'memory' && options?.adapter !== null)
throw ErrorUtils.getError('if not persistent, cache must use memory adapter.') throw ErrorUtils.getError('Cache.new: if not persistent, cache must use memory adapter.')
if (options?.adapter === 'memory' && options?.persistent === true) if (options?.adapter === 'memory' && options?.persistent === true)
throw ErrorUtils.getError("can't specify memory adapter if persistent.") throw ErrorUtils.getError("Cache.new: can't specify memory adapter if persistent.")
const dbAdapter = options?.adapter ?? (options?.persistent === false ? 'memory' : 'leveldb') const dbAdapter = options?.adapter ?? (options?.persistent === false ? 'memory' : 'leveldb')
@ -392,13 +408,13 @@ export namespace Cache {
async close(): Promise<void> { async close(): Promise<void> {
if (this.sync.pooler!.pending) if (this.sync.pooler!.pending)
throw ErrorUtils.getError("can't clear while pooler still has pending promises.") throw ErrorUtils.getError("Syncable.close: can't clear while pooler still has pending promises.")
await super.close() await super.close()
} }
async clear(): Promise<void> { async clear(): Promise<void> {
if (this.sync.pooler!.pending) if (this.sync.pooler!.pending)
throw ErrorUtils.getError("can't clear while pooler still has pending promises.") throw ErrorUtils.getError("Syncable.clear: can't clear while pooler still has pending promises.")
await super.clear() await super.clear()
} }
} }
@ -440,13 +456,16 @@ export namespace Cache {
`Core.loadCacheContents: there is no cache entry for ${nameOfContent}` `Core.loadCacheContents: there is no cache entry for ${nameOfContent}`
) )
): Promise<DocsArray<T>> { ): Promise<DocsArray<T>> {
const cache = new Cache.Base<T>(Files.getCachePath(nameOfContent)) const cache = new Cache.Base<T>(nameOfContent)
const docs = await cache.db.allDocs({ include_docs: full }).catch((err) => { const docs = await cache.db.allDocs({ include_docs: full }).catch((err) => {
throw ErrorUtils.ensureError(err) throw ErrorUtils.ensureError(err)
}) })
if (docs.total_rows === 0) throw emptyError if (docs.total_rows === 0) {
await cache.clear()
throw emptyError
}
return docs.rows as DocsArray<T> return docs.rows as DocsArray<T>
} }

@ -1,6 +1,6 @@
// Local types // Local types
import * as Crypto from 'types/sdk/crypto' import * as Crypto from 'types/sdk/crypto'
import { Options } from 'types/sdk/main' import { Options } from 'types/sdk/core'
// Needed // Needed
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
@ -8,8 +8,8 @@ import { bigInt } from 'snarkjs'
import { randomBytes } from 'crypto' import { randomBytes } from 'crypto'
export namespace ErrorUtils { export namespace ErrorUtils {
export function ensureError(value: unknown): Error { export function ensureError<T extends Error>(value: unknown): T {
if (value instanceof Error) return value if (value instanceof Error) return value as T
let stringified = '[Unable to stringify the thrown value]' let stringified = '[Unable to stringify the thrown value]'
try { try {
@ -17,7 +17,7 @@ export namespace ErrorUtils {
} catch {} } catch {}
const error = getError(`This value was thrown as is, not through an Error: ${stringified}`) const error = getError(`This value was thrown as is, not through an Error: ${stringified}`)
return error return error as T
} }
export function getError(message: string): Error { export function getError(message: string): Error {
@ -177,13 +177,9 @@ export namespace NumberUtils {
isInteger: number = 1 isInteger: number = 1
): number { ): number {
isInteger = 0 < isInteger ? 1 : 0 isInteger = 0 < isInteger ? 1 : 0
return ( return (isInteger ? Math.floor : (x: number) => x)(
isInteger Math.random() * (upperInclusive + isInteger - lowerInclusive) + lowerInclusive
? Math.floor )
: (x: number) => {
return x
}
)(Math.random() * (upperInclusive + isInteger - lowerInclusive) + lowerInclusive)
} }
} }
@ -240,4 +236,8 @@ export namespace ObjectUtils {
right: left right: left
} }
} }
export function exists(obj: any): boolean {
return obj !== undefined && obj !== null
}
} }

@ -1,34 +1,46 @@
import axios from 'axios' // Types
import { Relayer as Types, RelayerOptions } from 'types/sdk/web'
import { AxiosInstance } from 'axios'
import { SocksProxyAgent } from 'socks-proxy-agent'
import { Web3Provider, Networkish } from '@ethersproject/providers'
import { RelayerOptions } from 'types/sdk/web'
import { BigNumber } from 'ethers'
import { ErrorUtils } from './utils'
import { Cache, Docs } from './data'
import { RelayerProperties } from 'types/sdk/data' import { RelayerProperties } from 'types/sdk/data'
// HTTP and proxy
import axios from 'axios'
import { AxiosInstance, AxiosError, AxiosResponse } from 'axios'
import { SocksProxyAgent } from 'socks-proxy-agent'
// Ethers
import { BigNumber } from 'ethers'
import { Web3Provider, Networkish } from '@ethersproject/providers'
// Local
import { ErrorUtils } from 'lib/utils'
import { Cache, Docs } from 'lib/data'
// It seems that the default HttpProvider offered by the normal web3 package // It seems that the default HttpProvider offered by the normal web3 package
// has some logic which either ignores the SocksProxyAgent or which falls back to // has some logic which either ignores the SocksProxyAgent or which falls back to
// using no proxy for some reason. In any case, the Tornado-modified one, originally // using no proxy for some reason. In any case, the Tornado-modified one, originally
// modified by the Tornado Team or who else, seems to properly error out when Tor // modified by the Tornado Team or whoever else conributed, seems to properly error
// is not running. // out when Tor is not running.
const HttpProvider = require('web3-providers-http') const HttpProvider = require('web3-providers-http')
export interface TorOptions { export interface ObfuscationOptions {
port?: number port?: number
headers?: { name: string; value: string }[] headers?: { name: string; value: string }[]
rv?: string
} }
/** /**
* You can also set up a SOCKS5 I2P tunnel on some port and then use that instead. Meaning that this should be compatible with I2P. * You can also set up a SOCKS5 I2P tunnel on some port and then use that instead. Meaning that this should be compatible with I2P.
*/ */
export class TorProvider extends Web3Provider { export class TorProvider extends Web3Provider {
constructor(url: string, torOpts: TorOptions, network?: Networkish) { constructor(url: string, torOpts?: ObfuscationOptions, network?: Networkish) {
torOpts = torOpts ?? {}
torOpts.rv = torOpts.rv ?? '102.0'
const torPort = torOpts.port ?? 9050, const torPort = torOpts.port ?? 9050,
headers = torOpts.headers ?? [ headers = torOpts.headers ?? [
{ name: 'User-Agent', value: 'Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0' } {
name: 'User-Agent',
value: `Mozilla/5.0 (Windows NT 10.0; rv:${torOpts.rv}) Gecko/20100101 Firefox/${torOpts.rv}`
}
] ]
super( super(
@ -79,8 +91,9 @@ export class Relayer {
private _miningFee?: number private _miningFee?: number
private _status?: string private _status?: string
private _chainId?: number private _chainId?: number
private _prices?: Map<string, BigNumber>
constructor(options: RelayerOptions, properties?: RelayerProperties) { constructor(options: Types.Options, properties?: RelayerProperties) {
this.url = options.url this.url = options.url
this.httpClient = options.httpClient this.httpClient = options.httpClient
this._fetched = false this._fetched = false
@ -92,6 +105,7 @@ export class Relayer {
this._serviceFee = properties.serviceFeePercent this._serviceFee = properties.serviceFeePercent
this._miningFee = properties.miningFeePercent this._miningFee = properties.miningFeePercent
this._status = properties.status this._status = properties.status
this._prices = properties.prices
this._fetched = true this._fetched = true
} }
} }
@ -121,6 +135,10 @@ 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(
(map, entry) => map.set(entry[0], BigNumber.from(entry[1])),
new Map<string, BigNumber>()
)
this._fetched = true this._fetched = true
return { return {
@ -129,7 +147,8 @@ export class Relayer {
chainId: this._chainId!, chainId: this._chainId!,
serviceFeePercent: this._serviceFee!, serviceFeePercent: this._serviceFee!,
miningFeePercent: this._miningFee!, miningFeePercent: this._miningFee!,
status: this._status! status: this._status!,
prices: this._prices!
} }
} }
@ -166,6 +185,10 @@ export class Relayer {
this._propertiesFetched('chainId') this._propertiesFetched('chainId')
return this._chainId! return this._chainId!
} }
get prices(): Map<string, BigNumber> {
this._propertiesFetched('prices')
return this._prices!
}
async getETHPurchasePrice(token: string): Promise<BigNumber> { async getETHPurchasePrice(token: string): Promise<BigNumber> {
return BigNumber.from( return BigNumber.from(
@ -178,13 +201,60 @@ export class Relayer {
) )
} }
// TODO: Relaying stuff and related async handleWithdrawal(
instanceAddress: string,
proof: Array<string>
): Promise<Types.WithdrawalRequestResult> {
const response = (await this.httpClient
.post(this.url + '/v1/tornadoWithdraw', {
contract: instanceAddress,
proof: proof[0],
args: proof.slice(1)
})
.catch(this._handleHTTPError)) as AxiosResponse
async relay(): Promise<any> {} const { id } = response.data
async calcWithdrawalFee(token: string, denomination: number): Promise<BigNumber> { let result: Types.WithdrawalRequestResult = { success: false },
//placeholder finished = false
return BigNumber.from(0)
while (!finished) {
const statusResponse = (await this.httpClient
.get(this.url + '/v1/jobs/' + id)
.catch((err) => this._handleHTTPError(err, false))) as AxiosResponse
if (statusResponse.status === 200) {
const { txHash, status, _, failedReason } = statusResponse.data
if (status === 'FAILED') {
console.error(`\nRelayer.handleWithdrawal: withdrawal failed with reason: ${failedReason}\n`)
finished = true
}
if (status == 'CONFIRMED') {
result.success = true
result.txHash = txHash
finished = true
}
}
await new Promise((resolve) => setTimeout(resolve, 3000))
}
return result
}
private _handleHTTPError(err: AxiosError, httpThrow: boolean = true): void {
err = ErrorUtils.ensureError(err)
if (err.response) {
if (httpThrow)
// @ts-expect-error
throw ErrorUtils.ensureError(err.response.data.error)
// @ts-expect-error
else console.error(err.response.data.error)
} else if (err.request) {
if (httpThrow) throw ErrorUtils.ensureError(err.request)
else console.error(err.request)
} else throw err
} }
// Cache // Cache
@ -217,7 +287,8 @@ export class Relayer {
chainId: this._chainId!, chainId: this._chainId!,
serviceFeePercent: this._serviceFee!, serviceFeePercent: this._serviceFee!,
miningFeePercent: this._miningFee!, miningFeePercent: this._miningFee!,
status: this._status! status: this._status!,
prices: this._prices!
}) })
await cache.db.put(doc).catch((err) => { await cache.db.put(doc).catch((err) => {

@ -1,78 +1,142 @@
{ {
"instanceAddresses": { "instanceAddresses": {
"1eth0.1": "0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc", "1eth0.1": "0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc",
"1eth1": "0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936", "1eth1": "0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936",
"1eth10": "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF", "1eth10": "0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF",
"1eth100": "0xA160cdAB225685dA1d56aa342Ad8841c3b53f291", "1eth100": "0xA160cdAB225685dA1d56aa342Ad8841c3b53f291",
"1dai100": "0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3", "1dai100": "0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3",
"1dai1000": "0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144", "1dai1000": "0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144",
"1dai10000": "0x07687e702b410Fa43f4cB4Af7FA097918ffD2730", "1dai10000": "0x07687e702b410Fa43f4cB4Af7FA097918ffD2730",
"1dai100000": "0x23773E65ed146A459791799d01336DB287f25334", "1dai100000": "0x23773E65ed146A459791799d01336DB287f25334",
"1cdai5000": "0x22aaA7720ddd5388A3c0A3333430953C68f1849b", "1cdai5000": "0x22aaA7720ddd5388A3c0A3333430953C68f1849b",
"1cdai50000": "0x03893a7c7463AE47D46bc7f091665f1893656003", "1cdai50000": "0x03893a7c7463AE47D46bc7f091665f1893656003",
"1cdai500000": "0x2717c5e28cf931547B621a5dddb772Ab6A35B701", "1cdai500000": "0x2717c5e28cf931547B621a5dddb772Ab6A35B701",
"1cdai5000000": "0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af", "1cdai5000000": "0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af",
"1usdc100": "0xd96f2B1c14Db8458374d9Aca76E26c3D18364307", "1usdc100": "0xd96f2B1c14Db8458374d9Aca76E26c3D18364307",
"1usdc1000": "0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D", "1usdc1000": "0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D",
"1usdc10000": "", "1usdc10000": null,
"1usdc100000": "", "1usdc100000": null,
"1usdt100": "0x169AD27A470D064DEDE56a2D3ff727986b15D52B", "1usdt100": "0x169AD27A470D064DEDE56a2D3ff727986b15D52B",
"1usdt1000": "0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f", "1usdt1000": "0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f",
"1usdt10000": "", "1usdt10000": null,
"1usdt100000": "", "1usdt100000": null,
"1wbtc0.1": "0x178169B423a011fff22B9e3F3abeA13414dDD0F1", "1wbtc0.1": "0x178169B423a011fff22B9e3F3abeA13414dDD0F1",
"1wbtc1": "0x610B717796ad172B316836AC95a2ffad065CeaB4", "1wbtc1": "0x610B717796ad172B316836AC95a2ffad065CeaB4",
"1wbtc10": "0xbB93e510BbCD0B7beb5A853875f9eC60275CF498", "1wbtc10": "0xbB93e510BbCD0B7beb5A853875f9eC60275CF498",
"1wbtc100": "", "1wbtc100": null,
"5eth0.1": "0x6Bf694a291DF3FeC1f7e69701E3ab6c592435Ae7", "5eth0.1": "0x6Bf694a291DF3FeC1f7e69701E3ab6c592435Ae7",
"5eth1": "0x3aac1cC67c2ec5Db4eA850957b967Ba153aD6279", "5eth1": "0x3aac1cC67c2ec5Db4eA850957b967Ba153aD6279",
"5eth10": "0x723B78e67497E85279CB204544566F4dC5d2acA0", "5eth10": "0x723B78e67497E85279CB204544566F4dC5d2acA0",
"5eth100": "0x0E3A09dDA6B20aFbB34aC7cD4A6881493f3E7bf7", "5eth100": "0x0E3A09dDA6B20aFbB34aC7cD4A6881493f3E7bf7",
"5dai100": "0x76D85B4C0Fc497EeCc38902397aC608000A06607", "5dai100": "0x76D85B4C0Fc497EeCc38902397aC608000A06607",
"5dai1000": "0xCC84179FFD19A1627E79F8648d09e095252Bc418", "5dai1000": "0xCC84179FFD19A1627E79F8648d09e095252Bc418",
"5dai10000": "0xD5d6f8D9e784d0e26222ad3834500801a68D027D", "5dai10000": "0xD5d6f8D9e784d0e26222ad3834500801a68D027D",
"5dai100000": "0x407CcEeaA7c95d2FE2250Bf9F2c105aA7AAFB512", "5dai100000": "0x407CcEeaA7c95d2FE2250Bf9F2c105aA7AAFB512",
"5cdai5000": "0x833481186f16Cece3f1Eeea1a694c42034c3a0dB", "5cdai5000": "0x833481186f16Cece3f1Eeea1a694c42034c3a0dB",
"5cdai50000": "0xd8D7DE3349ccaA0Fde6298fe6D7b7d0d34586193", "5cdai50000": "0xd8D7DE3349ccaA0Fde6298fe6D7b7d0d34586193",
"5cdai500000": "0x8281Aa6795aDE17C8973e1aedcA380258Bc124F9", "5cdai500000": "0x8281Aa6795aDE17C8973e1aedcA380258Bc124F9",
"5cdai5000000": "0x57b2B8c82F065de8Ef5573f9730fC1449B403C9f", "5cdai5000000": "0x57b2B8c82F065de8Ef5573f9730fC1449B403C9f",
"5usdc100": "0x05E0b5B40B7b66098C2161A5EE11C5740A3A7C45", "5usdc100": "0x05E0b5B40B7b66098C2161A5EE11C5740A3A7C45",
"5usdc1000": "0x23173fE8b96A4Ad8d2E17fB83EA5dcccdCa1Ae52", "5usdc1000": "0x23173fE8b96A4Ad8d2E17fB83EA5dcccdCa1Ae52",
"5usdc10000": "", "5usdc10000": null,
"5usdc100000": "", "5usdc100000": null,
"5usdt100": "0x538Ab61E8A9fc1b2f93b3dd9011d662d89bE6FE6", "5usdt100": "0x538Ab61E8A9fc1b2f93b3dd9011d662d89bE6FE6",
"5usdt1000": "0x94Be88213a387E992Dd87DE56950a9aef34b9448", "5usdt1000": "0x94Be88213a387E992Dd87DE56950a9aef34b9448",
"5usdt10000": "", "5usdt10000": null,
"5usdt100000": "", "5usdt100000": null,
"5wbtc0.1": "0x242654336ca2205714071898f67E254EB49ACdCe", "5wbtc0.1": "0x242654336ca2205714071898f67E254EB49ACdCe",
"5wbtc1": "0x776198CCF446DFa168347089d7338879273172cF", "5wbtc1": "0x776198CCF446DFa168347089d7338879273172cF",
"5wbtc10": "0xeDC5d01286f99A066559F60a585406f3878a033e", "5wbtc10": "0xeDC5d01286f99A066559F60a585406f3878a033e",
"5wbtc100": "" "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": { "deployedBlockNumber": {
"1eth0.1": 9116966, "1eth0.1": 9116966,
"1eth1": 9117609, "1eth1": 9117609,
"1eth10": 9117720, "1eth10": 9117720,
"1eth100": 9161895, "1eth100": 9161895,
"1dai100": 9117612, "1dai100": 9117612,
"1dai1000": 9161917, "1dai1000": 9161917,
"1dai10000": 12066007, "1dai10000": 12066007,
"1dai100000": 12066048, "1dai100000": 12066048,
"1cdai5000": 9161938, "1cdai5000": 9161938,
"1cdai50000": 12069037, "1cdai50000": 12069037,
"1cdai500000": 12067606, "1cdai500000": 12067606,
"1cdai5000000": 12066053, "1cdai5000000": 12066053,
"1usdc100": 9161958, "1usdc100": 9161958,
"1usdc1000": 9161965, "1usdc1000": 9161965,
"1usdc10000": "", "1usdc10000": null,
"1usdc100000": "", "1usdc100000": null,
"1usdt100": 9162005, "1usdt100": 9162005,
"1usdt1000": 9162012, "1usdt1000": 9162012,
"1usdt10000": "", "1usdt10000": null,
"1usdt100000": "", "1usdt100000": null,
"1wbtc0.1": 12067529, "1wbtc0.1": 12067529,
"1wbtc1": 12066652, "1wbtc1": 12066652,
"1wbtc10": 12067591, "1wbtc10": 12067591,
"1wbtc100": "" "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"
} }
} }

@ -5,19 +5,26 @@ import { solidity } from 'ethereum-waffle'
import { providers } from 'ethers' import { providers } from 'ethers'
import { parseUnits } from 'ethers/lib/utils' import { parseUnits } from 'ethers/lib/utils'
import { ERC20 } from 'types/deth' import { ERC20, TornadoInstance } from 'types/deth'
import { Json } from 'types/sdk/data' import { Json } from 'types/sdk/data'
import { Core } from 'lib/main' import { Core } from 'lib/core'
import { Chain, Contracts } from 'lib/chain' import { Chain, Contracts } from 'lib/chain'
import { Files, OnchainData } from 'lib/data' import { Docs, Files, OnchainData, Cache } from 'lib/data'
import { ErrorUtils } from 'lib/utils' import { ErrorUtils } from 'lib/utils'
import { TorProvider } from 'lib/web' import { TorProvider } from 'lib/web'
// Data
// @ts-expect-error
import { parseIndexableString } from 'pouchdb-collate'
import compareDeposits from './resources/deposits_eth_0.1.json'
import { Primitives } from 'lib/crypto'
import { ZKDepositData } from 'types/sdk/crypto'
chai.use(solidity) chai.use(solidity)
const expect = chai.expect const expect = chai.expect
describe('main', () => { describe.only('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,19 +41,19 @@ describe('main', () => {
? 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 = new providers.Web3Provider( const _ganacheProvider = ganache.provider({
chain: { chainId: 1 },
// @ts-ignore // @ts-ignore
ganache.provider({ fork: { url: process.env.ETH_MAINNET_TEST_RPC },
chain: { chainId: 1 }, logging: { quiet: true },
// @ts-ignore wallet: {
fork: { url: process.env.ETH_MAINNET_TEST_RPC }, totalAccounts: 20,
logging: { quiet: true }, unlockedAccounts: [daiWhale]
wallet: { }
totalAccounts: 20, })
unlockedAccounts: [daiWhale]
} // @ts-expect-error
}) const ganacheProvider = new providers.Web3Provider(_ganacheProvider)
)
const chain = new Chain(ganacheProvider) const chain = new Chain(ganacheProvider)
let snapshotId: any let snapshotId: any
@ -57,7 +64,7 @@ describe('main', () => {
}) })
describe('namespace Tornado', () => { describe('namespace Tornado', () => {
describe('namespace Contracts', () => { describe.skip('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')
@ -66,17 +73,17 @@ describe('main', () => {
}) })
describe('class Classic', () => { describe('class Classic', () => {
it('sync: should be able to fetch a couple events', async () => { it.skip('sync: Should be able to fetch deposit events', async () => {
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 targetBlock = 16928712
const startBlock = targetBlock - 7200 //const startBlock = targetBlock - 7200
await core.sync(instance, { await core.sync(instance, {
//deposit: true, //deposit: true,
//withdrawal: false, //withdrawal: false,
blocks: { blocks: {
startBlock: startBlock, //startBlock: startBlock,
targetBlock: targetBlock //targetBlock: targetBlock
}, },
cache: { cache: {
sync: { sync: {
@ -84,6 +91,21 @@ describe('main', () => {
} }
} }
}) })
const cache = core.caches.get('Deposits1ETH0.1')
const rows = (await cache!.db.allDocs()).rows
const valid = Object.values(compareDeposits)
expect(rows.length).to.be.gte(valid.length)
for (let i = 0, len = valid.length; i < len; i++) {
const id = rows[i].id
const [bn, leafIndex, commitment] = parseIndexableString(id)
const validDoc = valid[i]
expect(bn).to.equal(validDoc['blockNumber'])
expect(leafIndex).to.equal(validDoc['leafIndex'])
expect(commitment).to.equal(validDoc['commitment'])
}
}).timeout(0) }).timeout(0)
describe('ganache fork', async () => { describe('ganache fork', async () => {
@ -93,6 +115,8 @@ describe('main', () => {
let needsMoneyAddress: string let needsMoneyAddress: string
let dai: ERC20 let dai: ERC20
let smallestEth: TornadoInstance
let note: ZKDepositData, noteObj: any
before(async function () { before(async function () {
this.timeout(0) this.timeout(0)
@ -102,6 +126,7 @@ describe('main', () => {
needsMoneyAddress = await needsMoney.getAddress() needsMoneyAddress = await needsMoney.getAddress()
daiData = await OnchainData.getTokenData('1', 'dai') daiData = await OnchainData.getTokenData('1', 'dai')
dai = chain.getTokenContract(daiData.address).connect(whale) dai = chain.getTokenContract(daiData.address).connect(whale)
smallestEth = await core.getInstance('eth', 0.1)
}) })
after(async function () { after(async function () {
this.timeout(0) this.timeout(0)
@ -112,20 +137,72 @@ describe('main', () => {
dai = dai.connect(whale) dai = dai.connect(whale)
}) })
it('buildDepositTx: build a single eth deposit tx and succeed', async () => { it.only('buildDepositTx: 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(await core.getInstance('eth', 100)) const tx = await core.buildDepositTx(smallestEth)
await signer.sendTransaction(tx.request) const response = await signer.sendTransaction(tx.request)
const receipt = await response.wait()
noteObj = {
blockNumber: receipt.blockNumber,
transactionHash: receipt.transactionHash,
args: {
commitment: '',
leafIndex: 0,
timestamp: response.timestamp
}
}
console.log(receipt, '\n')
note = Primitives.parseNote(tx.note!)
const endBal = await signer.getBalance() const endBal = await signer.getBalance()
expect(initBal).to.equal(parseUnits('1000')) expect(initBal).to.equal(parseUnits('1000'))
expect(endBal).to.be.lte(parseUnits('900')) expect(endBal).to.be.lte(parseUnits('999.9'))
}).timeout(0) }).timeout(0)
it('buildDepositTx: build a single token deposit tx and succeed', async () => { it.only('buildDepositProofs: it should be able to build', async () => {
try {
const instance = await core.getInstance('eth', 0.1)
const signer = ganacheProvider.getSigner()
const withdrawer = ganacheProvider.getSigner(2)
const cache = core.loadCache('Deposits1ETH0.1') as Cache.Base<Docs.Deposit>
noteObj['args'] = {
commitment: note.hexCommitment,
leafIndex: (await cache!.db.allDocs({ descending: true, limit: 1, include_docs: true }))
?.rows[0].doc?.leafIndex,
timestamp: noteObj['args']['timestamp']
}
console.log(noteObj, '\n')
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
)
console.log(proof)
} catch (err) {
console.log(ErrorUtils.ensureError(err).message)
throw err
}
}).timeout(0)
it.skip('buildDepositTx: 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')
@ -142,7 +219,7 @@ describe('main', () => {
expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0)
}).timeout(0) }).timeout(0)
it('buildDepositTxs: multiple eth deposits', async () => { it.skip('buildDepositTxs: 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 }
@ -159,7 +236,7 @@ describe('main', () => {
expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0)
}).timeout(0) }).timeout(0)
it('buildDepositTxs: multiple token deposits', async () => { it.skip('buildDepositTxs: 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 }
@ -185,7 +262,7 @@ describe('main', () => {
expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0) expect(await dai.balanceOf(needsMoneyAddress)).to.equal(0)
}).timeout(0) }).timeout(0)
it('createInvoice: should be able to create an invoice', async () => { it.skip('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)

File diff suppressed because it is too large Load Diff

@ -18,9 +18,10 @@ describe('web', () => {
const torProvider = new TorProvider(process.env.ETH_MAINNET_TEST_RPC, { port: +process.env.TOR_PORT }) const torProvider = new TorProvider(process.env.ETH_MAINNET_TEST_RPC, { port: +process.env.TOR_PORT })
const httpClient = new TorHttpClient({ port: +process.env.TOR_PORT }) const httpClient = new TorHttpClient({ port: +process.env.TOR_PORT })
console.log( if (process.env.TORIFY === 'true')
'\nSome Tor tips: Support non-profit exit node operators, host your own nodes, avoid spy nodes by configuring torrc.\n' console.log(
) '\nSome Tor tips: Support non-profit exit node operators, host your own nodes, avoid spy nodes by configuring torrc.\n'
)
function torErrorThrow(err: Error) { function torErrorThrow(err: Error) {
err.message = err.message =
@ -41,7 +42,7 @@ describe('web', () => {
} }
}).timeout(0) }).timeout(0)
it.only('TorProvider: Should be able to fetch some basic blockchain data over Tor', async () => { it('TorProvider: Should be able to fetch some basic blockchain data over Tor', async () => {
try { try {
console.log('\nBlock Number: ' + (await torProvider.getBlockNumber())) console.log('\nBlock Number: ' + (await torProvider.getBlockNumber()))
console.log('Gas Price: ' + (await torProvider.getGasPrice()).div(1000000000) + ' gwei') console.log('Gas Price: ' + (await torProvider.getGasPrice()).div(1000000000) + ' gwei')
@ -55,7 +56,7 @@ describe('web', () => {
} }
}).timeout(0) }).timeout(0)
it.skip('DISCONNECTED: Should not be able to request over Tor', async function () { it('DISCONNECTED: Should not be able to request over Tor', async function () {
try { try {
await torProvider.getBlockNumber() await torProvider.getBlockNumber()
throw ErrorUtils.getError('should not have succeeded.') throw ErrorUtils.getError('should not have succeeded.')

@ -7,7 +7,6 @@
import { TransactionRequest } from '@ethersproject/abstract-provider' import { TransactionRequest } from '@ethersproject/abstract-provider'
import { BigNumber } from 'ethers' import { BigNumber } from 'ethers'
import { RelayerProperties as RelayerDataProperties } from 'types/sdk/data'
export namespace Options { export namespace Options {
export namespace Cache { export namespace Cache {
@ -48,9 +47,13 @@ export namespace Options {
export type Invoice = Deposit export type Invoice = Deposit
export interface BuildDepositProof { export interface BuildDepositProof {
gasPrice?: BigNumber
gasPriceCushion?: BigNumber
tokenDecimals?: number
ethPurchaseAmounts?: Array<BigNumber> ethPurchaseAmounts?: Array<BigNumber>
merkleTreeHeight?: number
checkNotesSpent?: boolean checkNotesSpent?: boolean
checkKnownRoot?: boolean
merkleTreeHeight?: number
} }
} }
} }

@ -34,9 +34,9 @@ export namespace OutputOf {
} }
export interface Groth16Proof { export interface Groth16Proof {
pi_a: Array<string> pi_a: string
pi_b: Array<string> pi_b: string
pi_c: Array<string> pi_c: string
} }
} }
@ -50,6 +50,7 @@ type __OutputAliasDelimiter = null
export type MerkleProof = OutputOf.MerkleProof export type MerkleProof = OutputOf.MerkleProof
export type ZKProof = OutputOf.Groth16Proof export type ZKProof = OutputOf.Groth16Proof
export type DepositProof = OutputOf.Groth16Proof
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INPUTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INPUTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/** /**
@ -82,8 +83,8 @@ export namespace InputFor {
hexNullifierHash: string hexNullifierHash: string
recipientAddress: string recipientAddress: string
relayerAddress: string relayerAddress: string
fee: number fee: bigInt
refund: number refund: bigInt
} }
private: { private: {
nullifier: bigInt nullifier: bigInt
@ -104,7 +105,7 @@ export namespace InputFor {
nullifier: bigInt nullifier: bigInt
secret: bigInt secret: bigInt
pathIndices: number[] pathIndices: number[]
pathElements: any[] pathElements: string[]
} }
export type Groth16 = PublicGroth16 & PrivateGroth16 export type Groth16 = PublicGroth16 & PrivateGroth16

@ -1,49 +1,4 @@
import { isAddress } from 'ethers/lib/utils' import { BigNumber } from 'ethers'
import { ErrorUtils } from 'lib/utils'
// TODO: Decide whether to really use below, possibly from external interface
type NetworkId = string | number
type TokenId = string
type ContractId = string
type Denomination = number
class DataKey<T extends TokenId | NetworkId | ContractId | Denomination> {
value: string
isAddress?: boolean
isSymbol?: boolean
isName?: boolean
constructor(id: T) {
this.value = this._validate(id)
}
private _validate(id: T): string {
let type = typeof id
switch (type) {
case 'string':
let val = id as string
if (isAddress(val)) {
this.isAddress = true
return val
} else {
this.isSymbol = true
this.isName = true
}
return val.toLowerCase()
case 'number':
return String(id)
default:
throw ErrorUtils.getError(`unsupported key type ${type}.`)
}
}
}
type NetworkKey = DataKey<NetworkId>
type TokenKey = DataKey<TokenId>
type ContractKey = DataKey<ContractId>
type DenominationKey = DataKey<Denomination>
export namespace Json { export namespace Json {
export interface TornadoInstance { export interface TornadoInstance {
@ -81,4 +36,5 @@ export interface RelayerProperties {
miningFeePercent: number miningFeePercent: number
status: string status: string
chainId: number chainId: number
prices: Map<string, BigNumber>
} }

@ -1,7 +1,16 @@
import { AxiosInstance } from 'axios' import { AxiosInstance } from 'axios'
export interface RelayerOptions { export namespace Relayer {
url: string export interface Options {
address?: string url: string
httpClient: AxiosInstance address?: string
httpClient: AxiosInstance
}
export interface WithdrawalRequestResult {
success: boolean
txHash?: string
}
} }
export type RelayerOptions = Relayer.Options

@ -6,20 +6,23 @@
"require": ["tsconfig-paths/register"] "require": ["tsconfig-paths/register"]
}, },
"compilerOptions": { "compilerOptions": {
"target": "es2017", // ~~~~~~~~~~~~~~~~~~~~~~~~~NODE 18 STANDARD~~~~~~~~~~~~~~~~~~~~~~~
"target": "es2022",
"module": "commonjs", "module": "commonjs",
"lib": ["es2020"], "lib": ["es2022"],
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node",
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"outDir": "./build", "outDir": "./build",
"resolveJsonModule": true,
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"sourceMap": true, "sourceMap": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"strict": true,
"checkJs": true, "checkJs": true,
"allowJs": true, "allowJs": true,
"skipLibCheck": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"test/*": ["src/test/*"], "test/*": ["src/test/*"],

364
yarn.lock

@ -30,14 +30,26 @@
dependencies: dependencies:
"@jridgewell/trace-mapping" "0.3.9" "@jridgewell/trace-mapping" "0.3.9"
"@eslint/eslintrc@^2.0.0": "@eslint-community/eslint-utils@^4.2.0":
version "2.0.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.0.tgz#943309d8697c52fc82c076e90c1c74fbbe69dbff" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
integrity sha512-fluIaaV+GyV24CCu/ggiHdV+j4RNh85yQnAYS/G2mZODZgGmmlrgCydjUcV3YvxCm9x8nMAfThsqTni4KiXT4A== integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
dependencies:
eslint-visitor-keys "^3.3.0"
"@eslint-community/regexpp@^4.4.0":
version "4.5.0"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724"
integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ==
"@eslint/eslintrc@^2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02"
integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ==
dependencies: dependencies:
ajv "^6.12.4" ajv "^6.12.4"
debug "^4.3.2" debug "^4.3.2"
espree "^9.4.0" espree "^9.5.1"
globals "^13.19.0" globals "^13.19.0"
ignore "^5.2.0" ignore "^5.2.0"
import-fresh "^3.2.1" import-fresh "^3.2.1"
@ -45,10 +57,10 @@
minimatch "^3.1.2" minimatch "^3.1.2"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
"@eslint/js@8.35.0": "@eslint/js@8.38.0":
version "8.35.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.35.0.tgz#b7569632b0b788a0ca0e438235154e45d42813a7" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892"
integrity sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw== integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g==
"@ethereum-waffle/chai@4.0.10": "@ethereum-waffle/chai@4.0.10":
version "4.0.10" version "4.0.10"
@ -622,14 +634,14 @@
integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==
"@jridgewell/resolve-uri@^3.0.3": "@jridgewell/resolve-uri@^3.0.3":
version "3.1.0" version "3.1.1"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
"@jridgewell/sourcemap-codec@^1.4.10": "@jridgewell/sourcemap-codec@^1.4.10":
version "1.4.14" version "1.4.15"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
"@jridgewell/trace-mapping@0.3.9": "@jridgewell/trace-mapping@0.3.9":
version "0.3.9" version "0.3.9"
@ -738,6 +750,16 @@
dependencies: dependencies:
node-gyp-build "4.3.0" node-gyp-build "4.3.0"
"@trufflesuite/uws-js-unofficial@20.10.0-unofficial.2":
version "20.10.0-unofficial.2"
resolved "https://registry.yarnpkg.com/@trufflesuite/uws-js-unofficial/-/uws-js-unofficial-20.10.0-unofficial.2.tgz#7ed613ce3260cd5d1773a4d5787a2a106acd1a91"
integrity sha512-oQQlnS3oNeGsgS4K3KCSSavJgSb0W9D5ktZs4FacX9VbM7b+NlhjH96d6/G4fMrz+bc5MXRyco419on0X0dvRA==
dependencies:
ws "8.2.3"
optionalDependencies:
bufferutil "4.0.5"
utf-8-validate "5.0.7"
"@tsconfig/node10@^1.0.7": "@tsconfig/node10@^1.0.7":
version "1.0.9" version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
@ -888,14 +910,14 @@
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/node-fetch@^2.6.1": "@types/node-fetch@^2.6.1":
version "2.6.2" version "2.6.3"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.3.tgz#175d977f5e24d93ad0f57602693c435c57ad7e80"
integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== integrity sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
form-data "^3.0.0" form-data "^3.0.0"
"@types/node@*": "@types/node@*", "@types/node@^18.15.0":
version "18.15.11" version "18.15.11"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f"
integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==
@ -910,11 +932,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240"
integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==
"@types/node@^18.15.0":
version "18.15.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.0.tgz#286a65e3fdffd691e170541e6ecb0410b16a38be"
integrity sha512-z6nr0TTEOBGkzLGmbypWOGnpSpSIBorEhC4L+4HeQ2iezKCi4f77kyslRwvHeNitymGQ+oFyIWGP96l/DPSV9w==
"@types/normalize-package-data@^2.4.0": "@types/normalize-package-data@^2.4.0":
version "2.4.1" version "2.4.1"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@ -1103,87 +1120,87 @@
integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
"@typescript-eslint/eslint-plugin@^5.54.1": "@typescript-eslint/eslint-plugin@^5.54.1":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz#0c5091289ce28372e38ab8d28e861d2dbe1ab29e" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz#b1d4b0ad20243269d020ef9bbb036a40b0849829"
integrity sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew== integrity sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.54.1" "@eslint-community/regexpp" "^4.4.0"
"@typescript-eslint/type-utils" "5.54.1" "@typescript-eslint/scope-manager" "5.58.0"
"@typescript-eslint/utils" "5.54.1" "@typescript-eslint/type-utils" "5.58.0"
"@typescript-eslint/utils" "5.58.0"
debug "^4.3.4" debug "^4.3.4"
grapheme-splitter "^1.0.4" grapheme-splitter "^1.0.4"
ignore "^5.2.0" ignore "^5.2.0"
natural-compare-lite "^1.4.0" natural-compare-lite "^1.4.0"
regexpp "^3.2.0"
semver "^7.3.7" semver "^7.3.7"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/parser@^5.54.1": "@typescript-eslint/parser@^5.54.1":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.54.1.tgz#05761d7f777ef1c37c971d3af6631715099b084c" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.58.0.tgz#2ac4464cf48bef2e3234cb178ede5af352dddbc6"
integrity sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg== integrity sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==
dependencies: dependencies:
"@typescript-eslint/scope-manager" "5.54.1" "@typescript-eslint/scope-manager" "5.58.0"
"@typescript-eslint/types" "5.54.1" "@typescript-eslint/types" "5.58.0"
"@typescript-eslint/typescript-estree" "5.54.1" "@typescript-eslint/typescript-estree" "5.58.0"
debug "^4.3.4" debug "^4.3.4"
"@typescript-eslint/scope-manager@5.54.1": "@typescript-eslint/scope-manager@5.58.0":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz#6d864b4915741c608a58ce9912edf5a02bb58735" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz#5e023a48352afc6a87be6ce3c8e763bc9e2f0bc8"
integrity sha512-zWKuGliXxvuxyM71UA/EcPxaviw39dB2504LqAmFDjmkpO8qNLHcmzlh6pbHs1h/7YQ9bnsO8CCcYCSA8sykUg== integrity sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==
dependencies: dependencies:
"@typescript-eslint/types" "5.54.1" "@typescript-eslint/types" "5.58.0"
"@typescript-eslint/visitor-keys" "5.54.1" "@typescript-eslint/visitor-keys" "5.58.0"
"@typescript-eslint/type-utils@5.54.1": "@typescript-eslint/type-utils@5.58.0":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz#4825918ec27e55da8bb99cd07ec2a8e5f50ab748" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.58.0.tgz#f7d5b3971483d4015a470d8a9e5b8a7d10066e52"
integrity sha512-WREHsTz0GqVYLIbzIZYbmUUr95DKEKIXZNH57W3s+4bVnuF1TKe2jH8ZNH8rO1CeMY3U4j4UQeqPNkHMiGem3g== integrity sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==
dependencies: dependencies:
"@typescript-eslint/typescript-estree" "5.54.1" "@typescript-eslint/typescript-estree" "5.58.0"
"@typescript-eslint/utils" "5.54.1" "@typescript-eslint/utils" "5.58.0"
debug "^4.3.4" debug "^4.3.4"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/types@5.54.1": "@typescript-eslint/types@5.58.0":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.54.1.tgz#29fbac29a716d0f08c62fe5de70c9b6735de215c" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.58.0.tgz#54c490b8522c18986004df7674c644ffe2ed77d8"
integrity sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw== integrity sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==
"@typescript-eslint/typescript-estree@5.54.1": "@typescript-eslint/typescript-estree@5.58.0":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz#df7b6ae05fd8fef724a87afa7e2f57fa4a599be1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz#4966e6ff57eaf6e0fce2586497edc097e2ab3e61"
integrity sha512-bjK5t+S6ffHnVwA0qRPTZrxKSaFYocwFIkZx5k7pvWfsB1I57pO/0M0Skatzzw1sCkjJ83AfGTL0oFIFiDX3bg== integrity sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==
dependencies: dependencies:
"@typescript-eslint/types" "5.54.1" "@typescript-eslint/types" "5.58.0"
"@typescript-eslint/visitor-keys" "5.54.1" "@typescript-eslint/visitor-keys" "5.58.0"
debug "^4.3.4" debug "^4.3.4"
globby "^11.1.0" globby "^11.1.0"
is-glob "^4.0.3" is-glob "^4.0.3"
semver "^7.3.7" semver "^7.3.7"
tsutils "^3.21.0" tsutils "^3.21.0"
"@typescript-eslint/utils@5.54.1": "@typescript-eslint/utils@5.58.0":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.54.1.tgz#7a3ee47409285387b9d4609ea7e1020d1797ec34" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.58.0.tgz#430d7c95f23ec457b05be5520c1700a0dfd559d5"
integrity sha512-IY5dyQM8XD1zfDe5X8jegX6r2EVU5o/WJnLu/znLPWCBF7KNGC+adacXnt5jEYS9JixDcoccI6CvE4RCjHMzCQ== integrity sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==
dependencies: dependencies:
"@eslint-community/eslint-utils" "^4.2.0"
"@types/json-schema" "^7.0.9" "@types/json-schema" "^7.0.9"
"@types/semver" "^7.3.12" "@types/semver" "^7.3.12"
"@typescript-eslint/scope-manager" "5.54.1" "@typescript-eslint/scope-manager" "5.58.0"
"@typescript-eslint/types" "5.54.1" "@typescript-eslint/types" "5.58.0"
"@typescript-eslint/typescript-estree" "5.54.1" "@typescript-eslint/typescript-estree" "5.58.0"
eslint-scope "^5.1.1" eslint-scope "^5.1.1"
eslint-utils "^3.0.0"
semver "^7.3.7" semver "^7.3.7"
"@typescript-eslint/visitor-keys@5.54.1": "@typescript-eslint/visitor-keys@5.58.0":
version "5.54.1" version "5.58.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz#d7a8a0f7181d6ac748f4d47b2306e0513b98bf8b" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz#eb9de3a61d2331829e6761ce7fd13061781168b4"
integrity sha512-q8iSoHTgwCfgcRJ2l2x+xCbu8nBlRAlsQ33k24Adj8eoVBE0f8dUeI+bAa8F84Mv05UGbAx57g2zrRsYIooqQg== integrity sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==
dependencies: dependencies:
"@typescript-eslint/types" "5.54.1" "@typescript-eslint/types" "5.58.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@uniswap/default-token-list@^9.3.0": "@uniswap/default-token-list@^9.3.0":
@ -2241,9 +2258,9 @@ cookiejar@^2.1.1:
integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw== integrity sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==
core-js-pure@^3.0.1: core-js-pure@^3.0.1:
version "3.29.1" version "3.30.1"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.29.1.tgz#1be6ca2b8772f6b4df7fc4621743286e676c6162" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.30.1.tgz#7d93dc89e7d47b8ef05d7e79f507b0e99ea77eec"
integrity sha512-4En6zYVi0i0XlXHVz/bi6l1XDjCqkKRq765NXuX+SnaIatlE96Odt5lMLjdxUiNI1v9OXI5DSLWYPlmTfkTktg== integrity sha512-nXBEVpmUnNRhz83cHd9JRQC52cTMcuXAmR56+9dSMpRdpeA4I1PX6yjmhd71Eyc/wXNsdBdUDIj1QTIeZpU5Tg==
core-util-is@1.0.2: core-util-is@1.0.2:
version "1.0.2" version "1.0.2"
@ -2656,9 +2673,9 @@ eslint-scope@^5.1.1:
estraverse "^4.1.1" estraverse "^4.1.1"
eslint-scope@^7.1.1: eslint-scope@^7.1.1:
version "7.1.1" version "7.2.0"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b"
integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==
dependencies: dependencies:
esrecurse "^4.3.0" esrecurse "^4.3.0"
estraverse "^5.2.0" estraverse "^5.2.0"
@ -2670,27 +2687,15 @@ eslint-utils@^1.3.1:
dependencies: dependencies:
eslint-visitor-keys "^1.1.0" eslint-visitor-keys "^1.1.0"
eslint-utils@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
dependencies:
eslint-visitor-keys "^2.0.0"
eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
eslint-visitor-keys@^2.0.0: eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0:
version "2.1.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ==
eslint-visitor-keys@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
eslint@^5.16.0: eslint@^5.16.0:
version "5.16.0" version "5.16.0"
@ -2735,12 +2740,14 @@ eslint@^5.16.0:
text-table "^0.2.0" text-table "^0.2.0"
eslint@^8.35.0: eslint@^8.35.0:
version "8.35.0" version "8.38.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.35.0.tgz#fffad7c7e326bae606f0e8f436a6158566d42323" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a"
integrity sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw== integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg==
dependencies: dependencies:
"@eslint/eslintrc" "^2.0.0" "@eslint-community/eslint-utils" "^4.2.0"
"@eslint/js" "8.35.0" "@eslint-community/regexpp" "^4.4.0"
"@eslint/eslintrc" "^2.0.2"
"@eslint/js" "8.38.0"
"@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/config-array" "^0.11.8"
"@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/module-importer" "^1.0.1"
"@nodelib/fs.walk" "^1.2.8" "@nodelib/fs.walk" "^1.2.8"
@ -2751,9 +2758,8 @@ eslint@^8.35.0:
doctrine "^3.0.0" doctrine "^3.0.0"
escape-string-regexp "^4.0.0" escape-string-regexp "^4.0.0"
eslint-scope "^7.1.1" eslint-scope "^7.1.1"
eslint-utils "^3.0.0" eslint-visitor-keys "^3.4.0"
eslint-visitor-keys "^3.3.0" espree "^9.5.1"
espree "^9.4.0"
esquery "^1.4.2" esquery "^1.4.2"
esutils "^2.0.2" esutils "^2.0.2"
fast-deep-equal "^3.1.3" fast-deep-equal "^3.1.3"
@ -2775,7 +2781,6 @@ eslint@^8.35.0:
minimatch "^3.1.2" minimatch "^3.1.2"
natural-compare "^1.4.0" natural-compare "^1.4.0"
optionator "^0.9.1" optionator "^0.9.1"
regexpp "^3.2.0"
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
strip-json-comments "^3.1.0" strip-json-comments "^3.1.0"
text-table "^0.2.0" text-table "^0.2.0"
@ -2789,14 +2794,14 @@ espree@^5.0.1:
acorn-jsx "^5.0.0" acorn-jsx "^5.0.0"
eslint-visitor-keys "^1.0.0" eslint-visitor-keys "^1.0.0"
espree@^9.4.0: espree@^9.5.1:
version "9.4.1" version "9.5.1"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4"
integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg==
dependencies: dependencies:
acorn "^8.8.0" acorn "^8.8.0"
acorn-jsx "^5.3.2" acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.4.0"
esprima@^4.0.0: esprima@^4.0.0:
version "4.0.1" version "4.0.1"
@ -3328,9 +3333,9 @@ fresh@0.5.2:
integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
fs-extra@^11.1.0: fs-extra@^11.1.0:
version "11.1.0" version "11.1.1"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d"
integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==
dependencies: dependencies:
graceful-fs "^4.2.0" graceful-fs "^4.2.0"
jsonfile "^6.0.1" jsonfile "^6.0.1"
@ -3399,11 +3404,12 @@ ganache@7.4.3:
utf-8-validate "5.0.7" utf-8-validate "5.0.7"
ganache@^7.7.7: ganache@^7.7.7:
version "7.7.7" version "7.8.0"
resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.7.7.tgz#19939a86799f0bcb7df02e88082944466394b913" resolved "https://registry.yarnpkg.com/ganache/-/ganache-7.8.0.tgz#02154384f246b66e98974cbcbb18e8372df3c2e0"
integrity sha512-kZUuOcgDQBtbxzs4iB3chg1iAc28s2ffdOdzyTTzo4vr9sb843w4PbWd5v1hsIqtcNjurcpLaW8XRp/cw2u++g== integrity sha512-IrUYvsaE/m2/NaVIZ7D/gCnsmyU/buechnH6MhUipzG1qJcZIwIp/DoP/LZUcHyhy0Bv0NKZD2pGOjpRhn7l7A==
dependencies: dependencies:
"@trufflesuite/bigint-buffer" "1.1.10" "@trufflesuite/bigint-buffer" "1.1.10"
"@trufflesuite/uws-js-unofficial" "20.10.0-unofficial.2"
"@types/bn.js" "^5.1.0" "@types/bn.js" "^5.1.0"
"@types/lru-cache" "5.1.1" "@types/lru-cache" "5.1.1"
"@types/seedrandom" "3.0.1" "@types/seedrandom" "3.0.1"
@ -3563,12 +3569,12 @@ glob@^7.1.2, glob@^7.1.3:
path-is-absolute "^1.0.0" path-is-absolute "^1.0.0"
glob@^9.2.0: glob@^9.2.0:
version "9.2.1" version "9.3.5"
resolved "https://registry.yarnpkg.com/glob/-/glob-9.2.1.tgz#f47e34e1119e7d4f93a546e75851ba1f1e68de50" resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.5.tgz#ca2ed8ca452781a3009685607fdf025a899dfe21"
integrity sha512-Pxxgq3W0HyA3XUvSXcFhRSs+43Jsx0ddxcFrbjxNGkL2Ak5BAUBxLqI5G6ADDeCHLfzzXFhe0b1yYcctGmytMA== integrity sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==
dependencies: dependencies:
fs.realpath "^1.0.0" fs.realpath "^1.0.0"
minimatch "^7.4.1" minimatch "^8.0.2"
minipass "^4.2.4" minipass "^4.2.4"
path-scurry "^1.6.1" path-scurry "^1.6.1"
@ -3647,16 +3653,11 @@ got@^11.8.5:
p-cancelable "^2.0.0" p-cancelable "^2.0.0"
responselike "^2.0.0" responselike "^2.0.0"
graceful-fs@^4.1.2, graceful-fs@^4.1.6: graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.11" version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
graceful-fs@^4.2.0:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
grapheme-splitter@^1.0.4: grapheme-splitter@^1.0.4:
version "1.0.4" version "1.0.4"
resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
@ -3949,10 +3950,10 @@ is-callable@^1.1.3:
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
is-core-module@^2.5.0, is-core-module@^2.9.0: is-core-module@^2.12.0, is-core-module@^2.5.0:
version "2.11.0" version "2.12.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4"
integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==
dependencies: dependencies:
has "^1.0.3" has "^1.0.3"
@ -4086,9 +4087,9 @@ isstream@~0.1.2:
integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
js-sdsl@^4.1.4: js-sdsl@^4.1.4:
version "4.3.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430"
integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==
js-sha3@0.8.0, js-sha3@^0.8.0: js-sha3@0.8.0, js-sha3@^0.8.0:
version "0.8.0" version "0.8.0"
@ -4518,10 +4519,10 @@ lru-cache@^6.0.0:
dependencies: dependencies:
yallist "^4.0.0" yallist "^4.0.0"
lru-cache@^7.14.1: lru-cache@^9.0.0:
version "7.18.3" version "9.0.3"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.3.tgz#8a04f282df5320227bb7215c55df2660d3e4e25b"
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== integrity sha512-cyjNRew29d4kbgnz1sjDqxg7qg8NW4s+HQzCGjeon7DV5T2yDije16W9HaUFV1dhVEMh+SjrOcK0TomBmf3Egg==
ltgt@2.2.1, ltgt@^2.1.2, ltgt@~2.2.0: ltgt@2.2.1, ltgt@^2.1.2, ltgt@~2.2.0:
version "2.2.1" version "2.2.1"
@ -4740,10 +4741,10 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies: dependencies:
brace-expansion "^1.1.7" brace-expansion "^1.1.7"
minimatch@^7.4.1: minimatch@^8.0.2:
version "7.4.2" version "8.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.4.tgz#847c1b25c014d4e9a7f68aaf63dedd668a626229"
integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== integrity sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==
dependencies: dependencies:
brace-expansion "^2.0.1" brace-expansion "^2.0.1"
@ -4769,10 +4770,15 @@ minipass@^2.6.0, minipass@^2.9.0:
safe-buffer "^5.1.2" safe-buffer "^5.1.2"
yallist "^3.0.0" yallist "^3.0.0"
minipass@^4.0.2, minipass@^4.2.4: minipass@^4.2.4:
version "4.2.4" version "4.2.8"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.4.tgz#7d0d97434b6a19f59c5c3221698b48bbf3b2cd06" resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a"
integrity sha512-lwycX3cBMTvcejsHITUgYj6Gy6A7Nh4Q6h9NP4sTHY1ccJlC7yKzDmiShEHsJ16Jf1nKGDEaiHxiltsJEvk0nQ== integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==
minipass@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
minizlib@^1.3.3: minizlib@^1.3.3:
version "1.3.3" version "1.3.3"
@ -5297,12 +5303,12 @@ path-parse@^1.0.7:
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
path-scurry@^1.6.1: path-scurry@^1.6.1:
version "1.6.1" version "1.7.0"
resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.7.0.tgz#99c741a2cfbce782294a39994d63748b5a24f6db"
integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== integrity sha512-UkZUeDjczjYRE495+9thsgcVgsaCPkaw80slmfVFgllxY+IO8ubTsOpFVjDPROBqJdHfVPUFRHPBV/WciOVfWg==
dependencies: dependencies:
lru-cache "^7.14.1" lru-cache "^9.0.0"
minipass "^4.0.2" minipass "^5.0.0"
path-to-regexp@0.1.7: path-to-regexp@0.1.7:
version "0.1.7" version "0.1.7"
@ -5542,9 +5548,9 @@ prettier-linter-helpers@^1.0.0:
fast-diff "^1.1.2" fast-diff "^1.1.2"
prettier@^2.3.0, prettier@^2.3.1: prettier@^2.3.0, prettier@^2.3.1:
version "2.8.4" version "2.8.7"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450"
integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==
process-nextick-args@~2.0.0: process-nextick-args@~2.0.0:
version "2.0.1" version "2.0.1"
@ -5793,11 +5799,6 @@ regexpp@^2.0.1:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
regexpp@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
request@^2.79.0, request@^2.85.0: request@^2.79.0, request@^2.85.0:
version "2.88.2" version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
@ -5850,11 +5851,11 @@ resolve-from@^4.0.0:
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve@^1.10.0: resolve@^1.10.0:
version "1.22.1" version "1.22.3"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw==
dependencies: dependencies:
is-core-module "^2.9.0" is-core-module "^2.12.0"
path-parse "^1.0.7" path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0" supports-preserve-symlinks-flag "^1.0.0"
@ -5893,9 +5894,9 @@ rimraf@^3.0.2:
glob "^7.1.3" glob "^7.1.3"
rimraf@^4.4.0: rimraf@^4.4.0:
version "4.4.0" version "4.4.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.0.tgz#c7a9f45bb2ec058d2e60ef9aca5167974313d605" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755"
integrity sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ== integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==
dependencies: dependencies:
glob "^9.2.0" glob "^9.2.0"
@ -5995,9 +5996,9 @@ semver@^6.0.0:
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.4, semver@^7.3.7: semver@^7.3.4, semver@^7.3.7:
version "7.3.8" version "7.4.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" resolved "https://registry.yarnpkg.com/semver/-/semver-7.4.0.tgz#8481c92feffc531ab1e012a8ffc15bdd3a0f4318"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== integrity sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==
dependencies: dependencies:
lru-cache "^6.0.0" lru-cache "^6.0.0"
@ -6582,9 +6583,9 @@ trim-newlines@^3.0.0:
integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==
ts-command-line-args@^2.2.0: ts-command-line-args@^2.2.0:
version "2.4.2" version "2.5.0"
resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.4.2.tgz#b4815b23c35f8a0159d4e69e01012d95690bc448" resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.0.tgz#7eeed3a6937b2612ea08a0794cf9d43fbbea89c4"
integrity sha512-mJLQQBOdyD4XI/ZWQY44PIdYde47JhV2xl380O7twPkTQ+Y5vFDHsk8LOeXKuz7dVY5aDCfAzRarNfSqtKOkQQ== integrity sha512-Ff7Xt04WWCjj/cmPO9eWTJX3qpBZWuPWyQYG1vnxJao+alWWYjwJBc5aYz3h5p5dE08A6AnpkgiCtP/0KXXBYw==
dependencies: dependencies:
"@morgan-stanley/ts-mocking-bird" "^0.6.2" "@morgan-stanley/ts-mocking-bird" "^0.6.2"
chalk "^4.1.0" chalk "^4.1.0"
@ -6622,9 +6623,9 @@ ts-node@^10.9.1:
yn "3.1.1" yn "3.1.1"
tsc-alias@^1.2.11: tsc-alias@^1.2.11:
version "1.8.3" version "1.8.5"
resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.8.3.tgz#fe0c8331edd69b94160b2b936752b114f8063cb5" resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.8.5.tgz#6b74e938230573354c9118deb58fd341d06d6253"
integrity sha512-/9JARcmXBrEqSuLjdSOqxY7/xI/AnvmBi4CU9/Ba2oX6Oq8vnd0OGSQTk+PIwqWJ5ZxskV0X/x15yzxCNTHU+g== integrity sha512-Y3ka0olwSRdbHPyX5kXhYY2aoBKuT53DFdeY+PpQUR4hg5M/b8eIRmC8dL4FBdd0wT366iWc6iDUUGe6QwI7mg==
dependencies: dependencies:
chokidar "^3.5.3" chokidar "^3.5.3"
commander "^9.0.0" commander "^9.0.0"
@ -6634,9 +6635,9 @@ tsc-alias@^1.2.11:
plimit-lit "^1.2.6" plimit-lit "^1.2.6"
tsconfig-paths@^4.1.2: tsconfig-paths@^4.1.2:
version "4.1.2" version "4.2.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz#4819f861eef82e6da52fb4af1e8c930a39ed979a" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c"
integrity sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw== integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==
dependencies: dependencies:
json5 "^2.2.2" json5 "^2.2.2"
minimist "^1.2.6" minimist "^1.2.6"
@ -6746,10 +6747,10 @@ typedarray-to-buffer@^3.1.5:
dependencies: dependencies:
is-typedarray "^1.0.0" is-typedarray "^1.0.0"
typescript@^5.1.0-dev.20230310: typescript@^5:
version "5.1.0-dev.20230316" version "5.0.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.0-dev.20230316.tgz#d57f1bde040419beaacf92ecb8adc5e29ecabdd5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
integrity sha512-XQP4u67mBfrcRRfdyT3yvOAlCYlC0fERQOt9wwinZVALcj0BNO+7d7U1SVxzbb6RhXGEQ+xUgSIXxKXuHBZ0xw== integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
typical@^4.0.0: typical@^4.0.0:
version "4.0.0" version "4.0.0"
@ -7318,6 +7319,11 @@ ws@7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
ws@8.2.3:
version "8.2.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
ws@^3.0.0: ws@^3.0.0:
version "3.3.3" version "3.3.3"
resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2"