Use Subgraph & Batched Events #2
@ -2,6 +2,12 @@
|
||||
|
||||
## Build Setup
|
||||
|
||||
If you use the latest Node.js version, you should modify your NODE_OPTIONS env
|
||||
|
||||
```bash
|
||||
export NODE_OPTIONS="--openssl-legacy-provider"
|
||||
```
|
||||
|
||||
```bash
|
||||
# install dependencies
|
||||
$ yarn install
|
||||
|
@ -1,18 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
import { AES, HmacSHA256, enc } from 'crypto-js'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { BigNumber, Contract } from 'ethers'
|
||||
import { poseidon } from '@tornado/circomlib'
|
||||
import { decrypt } from 'eth-sig-util'
|
||||
|
||||
const { AES, HmacSHA256, enc } = require('crypto-js')
|
||||
const { isEmpty } = require('lodash')
|
||||
const { BigNumber } = require('ethers')
|
||||
const { poseidon } = require('@tornado/circomlib')
|
||||
const { decrypt } = require('eth-sig-util')
|
||||
const { IndexedDB } = require('../services/idb')
|
||||
const { BatchEventsService } = require('../services/events/batch')
|
||||
const { sleep } = require('../utilities/helpers')
|
||||
const { workerEvents, numbers } = require('../constants/worker')
|
||||
const { ExtendedProvider } = require('../services/ether/ExtendedProvider')
|
||||
|
||||
const { POOL_CONTRACT, RPC_LIST, FALLBACK_RPC_LIST } = require('../constants/contracts')
|
||||
const { TornadoPool__factory: TornadoPoolFactory } = require('../_contracts')
|
||||
import { IndexedDB } from './services/idb'
|
||||
import { BatchEventsService } from './services/batch'
|
||||
import { getAllCommitments } from './services/graph'
|
||||
import { ExtendedProvider } from './services/provider'
|
||||
import { POOL_CONTRACT, RPC_LIST, FALLBACK_RPC_LIST, workerEvents, numbers } from './services/constants'
|
||||
import { sleep } from './services/utilities'
|
||||
import { poolAbi } from './services/pool'
|
||||
import { downloadEvents } from './services/downloadEvents'
|
||||
|
||||
const getProviderWithSigner = (chainId) => {
|
||||
return new ExtendedProvider(RPC_LIST[chainId], chainId, FALLBACK_RPC_LIST[chainId])
|
||||
@ -62,7 +61,7 @@ const initWorker = (chainId) => {
|
||||
setTornadoPool(chainId, provider)
|
||||
}
|
||||
const setTornadoPool = (chainId, provider) => {
|
||||
self.poolContract = TornadoPoolFactory.connect(POOL_CONTRACT[chainId], provider)
|
||||
self.poolContract = new Contract(POOL_CONTRACT[chainId], poolAbi, provider)
|
||||
|
||||
self.BatchEventsService = new BatchEventsService({
|
||||
provider,
|
||||
@ -71,21 +70,66 @@ const setTornadoPool = (chainId, provider) => {
|
||||
}
|
||||
|
||||
const getCommitmentBatch = async ({ blockFrom, blockTo, cachedEvents, withCache }) => {
|
||||
const events = await self.BatchEventsService.getBatchEvents({
|
||||
const events = []
|
||||
|
||||
let { events: graphEvents, lastSyncBlock } = await getAllCommitments({
|
||||
fromBlock: blockFrom,
|
||||
toBlock: blockTo,
|
||||
type: 'NewCommitment'
|
||||
chainId
|
||||
})
|
||||
|
||||
const commitmentEvents = events.map(({ blockNumber, transactionHash, args }) => ({
|
||||
blockNumber,
|
||||
transactionHash,
|
||||
index: Number(args.index),
|
||||
commitment: args.commitment,
|
||||
encryptedOutput: args.encryptedOutput,
|
||||
}))
|
||||
if (lastSyncBlock) {
|
||||
graphEvents = graphEvents
|
||||
.filter(({ blockNumber }) => {
|
||||
if (blockFrom && blockTo) {
|
||||
return Number(blockFrom) <= Number(blockNumber) && Number(blockNumber) <= Number(blockTo)
|
||||
} else if (blockTo) {
|
||||
return Number(blockNumber) <= Number(blockTo)
|
||||
}
|
||||
// does not filter by default
|
||||
return true
|
||||
})
|
||||
.map(({ blockNumber, transactionHash, index, commitment, encryptedOutput }) => ({
|
||||
blockNumber,
|
||||
transactionHash,
|
||||
index: Number(index),
|
||||
commitment,
|
||||
encryptedOutput,
|
||||
}))
|
||||
|
||||
return commitmentEvents.filter((el) => {
|
||||
console.log({
|
||||
graphEvents
|
||||
})
|
||||
|
||||
events.push(...graphEvents)
|
||||
blockFrom = lastSyncBlock
|
||||
}
|
||||
|
||||
if (!blockTo || blockTo > blockFrom) {
|
||||
let nodeEvents = await self.BatchEventsService.getBatchEvents({
|
||||
fromBlock: blockFrom,
|
||||
toBlock: blockTo,
|
||||
type: 'NewCommitment'
|
||||
})
|
||||
|
||||
if (nodeEvents && nodeEvents.length) {
|
||||
nodeEvents = nodeEvents.map(({ blockNumber, transactionHash, args }) => ({
|
||||
blockNumber,
|
||||
transactionHash,
|
||||
index: Number(args.index),
|
||||
commitment: args.commitment,
|
||||
encryptedOutput: args.encryptedOutput,
|
||||
}))
|
||||
|
||||
console.log({
|
||||
nodeEvents
|
||||
})
|
||||
|
||||
events.push(...nodeEvents)
|
||||
}
|
||||
}
|
||||
|
||||
return events.filter((el) => {
|
||||
if (!withCache && cachedEvents && cachedEvents.length) {
|
||||
return cachedEvents.find((cached) => {
|
||||
return el.transactionHash === cached.transactionHash && el.index === cached.index
|
||||
@ -122,6 +166,14 @@ const getCommitments = async ({ withCache, lastSyncBlock }) => {
|
||||
return { commitmentEvents: cachedEvents }
|
||||
}
|
||||
blockFrom = newBlockFrom > currentBlock ? currentBlock : newBlockFrom
|
||||
} else {
|
||||
const downloadedEvents = await downloadEvents(`commitments_${self.chainId}.json`, blockFrom)
|
||||
|
||||
if (downloadedEvents.events.length) {
|
||||
cachedEvents.push(...downloadedEvents.events)
|
||||
|
||||
blockFrom = downloadedEvents.lastBlock
|
||||
}
|
||||
}
|
||||
|
||||
const commitmentEvents = await getCommitmentBatch({ blockFrom, blockTo: currentBlock, cachedEvents, withCache })
|
||||
|
@ -1,15 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
const { isEmpty } = require('lodash')
|
||||
const { BigNumber } = require('ethers')
|
||||
import { isEmpty } from 'lodash'
|
||||
import { BigNumber, Contract } from 'ethers'
|
||||
|
||||
const { IndexedDB } = require('../services/idb')
|
||||
const { BatchEventsService } = require('../services/events/batch')
|
||||
const { sleep } = require('../utilities/helpers')
|
||||
const { workerEvents, numbers } = require('../constants/worker')
|
||||
const { ExtendedProvider } = require('../services/ether/ExtendedProvider')
|
||||
|
||||
const { POOL_CONTRACT, RPC_LIST, FALLBACK_RPC_LIST } = require('../constants/contracts')
|
||||
const { TornadoPool__factory: TornadoPoolFactory } = require('../_contracts')
|
||||
import { IndexedDB } from './services/idb'
|
||||
import { BatchEventsService } from './services/batch'
|
||||
import { getAllNullifiers } from './services/graph'
|
||||
import { ExtendedProvider } from './services/provider'
|
||||
import { POOL_CONTRACT, RPC_LIST, FALLBACK_RPC_LIST, workerEvents, numbers } from './services/constants'
|
||||
import { sleep } from './services/utilities'
|
||||
import { poolAbi } from './services/pool'
|
||||
import { downloadEvents } from './services/downloadEvents'
|
||||
|
||||
const getProviderWithSigner = (chainId) => {
|
||||
return new ExtendedProvider(RPC_LIST[chainId], chainId, FALLBACK_RPC_LIST[chainId])
|
||||
@ -48,7 +47,7 @@ const initWorker = (chainId) => {
|
||||
}
|
||||
|
||||
const setTornadoPool = (chainId, provider) => {
|
||||
self.poolContract = TornadoPoolFactory.connect(POOL_CONTRACT[chainId], provider)
|
||||
self.poolContract = new Contract(POOL_CONTRACT[chainId], poolAbi, provider)
|
||||
|
||||
self.BatchEventsService = new BatchEventsService({
|
||||
provider,
|
||||
@ -122,6 +121,14 @@ const getCachedEvents = async () => {
|
||||
return { blockFrom, cachedEvents }
|
||||
}
|
||||
blockFrom = newBlockFrom > currentBlock ? currentBlock : newBlockFrom
|
||||
} else {
|
||||
const downloadedEvents = await downloadEvents(`nullifiers_${self.chainId}.json`, blockFrom)
|
||||
|
||||
if (downloadedEvents.events.length) {
|
||||
cachedEvents.push(...downloadedEvents.events)
|
||||
|
||||
blockFrom = downloadedEvents.lastBlock
|
||||
}
|
||||
}
|
||||
|
||||
return { blockFrom, cachedEvents }
|
||||
@ -129,16 +136,39 @@ const getCachedEvents = async () => {
|
||||
|
||||
const getNullifiers = async (blockFrom) => {
|
||||
try {
|
||||
const events = await self.BatchEventsService.getBatchEvents({
|
||||
const events = []
|
||||
|
||||
let { events: graphEvents, lastSyncBlock } = await getAllNullifiers({ fromBlock: blockFrom, chainId })
|
||||
|
||||
if (lastSyncBlock) {
|
||||
console.log({
|
||||
graphEvents
|
||||
})
|
||||
|
||||
events.push(...graphEvents)
|
||||
blockFrom = lastSyncBlock
|
||||
}
|
||||
|
||||
let nodeEvents = await self.BatchEventsService.getBatchEvents({
|
||||
fromBlock: blockFrom,
|
||||
type: 'NewNullifier'
|
||||
})
|
||||
|
||||
if (nodeEvents && nodeEvents.length) {
|
||||
nodeEvents = nodeEvents.map(({ blockNumber, transactionHash, args }) => ({
|
||||
blockNumber,
|
||||
transactionHash,
|
||||
nullifier: args.nullifier,
|
||||
}))
|
||||
|
||||
return events.map(({ blockNumber, transactionHash, args }) => ({
|
||||
blockNumber,
|
||||
transactionHash,
|
||||
nullifier: args.nullifier,
|
||||
}))
|
||||
console.log({
|
||||
nodeEvents
|
||||
})
|
||||
|
||||
events.push(...nodeEvents)
|
||||
}
|
||||
|
||||
return events
|
||||
} catch (err) {
|
||||
console.error('getNullifiers', err.message)
|
||||
return []
|
||||
|
85
assets/services/batch.js
Normal file
85
assets/services/batch.js
Normal file
@ -0,0 +1,85 @@
|
||||
import { sleep, getBatches } from './utilities'
|
||||
|
||||
export class BatchEventsService {
|
||||
constructor({
|
||||
provider,
|
||||
contract,
|
||||
concurrencySize = 10,
|
||||
blocksPerRequest = 2000,
|
||||
shouldRetry = true,
|
||||
retryMax = 5,
|
||||
retryOn = 500,
|
||||
}) {
|
||||
this.provider = provider;
|
||||
this.contract = contract;
|
||||
this.concurrencySize = concurrencySize;
|
||||
this.blocksPerRequest = blocksPerRequest;
|
||||
this.shouldRetry = shouldRetry;
|
||||
this.retryMax = retryMax;
|
||||
this.retryOn = retryOn;
|
||||
}
|
||||
|
||||
async getPastEvents({ fromBlock, toBlock, type }) {
|
||||
let err;
|
||||
let retries = 0;
|
||||
|
||||
// eslint-disable-next-line no-unmodified-loop-condition
|
||||
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
|
||||
try {
|
||||
return (await this.contract.queryFilter(type, fromBlock, toBlock));
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (e) {
|
||||
err = e;
|
||||
retries++;
|
||||
|
||||
// If provider.getBlockNumber returned last block that isn't accepted (happened on Avalanche/Gnosis),
|
||||
// get events to last accepted block
|
||||
if (e.message.includes('after last accepted block')) {
|
||||
const acceptedBlock = parseInt(e.message.split('after last accepted block ')[1]);
|
||||
toBlock = acceptedBlock;
|
||||
}
|
||||
|
||||
// retry on 0.5 seconds
|
||||
await sleep(this.retryOn);
|
||||
}
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
createBatchRequest(batchArray) {
|
||||
return batchArray.map(async (event, index) => {
|
||||
await sleep(20 * index);
|
||||
|
||||
return this.getPastEvents(event);
|
||||
});
|
||||
}
|
||||
|
||||
async getBatchEvents({ fromBlock, toBlock, type = '*' }) {
|
||||
if (!toBlock) {
|
||||
toBlock = await this.provider.getBlockNumber();
|
||||
}
|
||||
|
||||
const eventsToSync = [];
|
||||
|
||||
for (let i = fromBlock; i < toBlock; i += this.blocksPerRequest) {
|
||||
const j = i + this.blocksPerRequest - 1 > toBlock ? toBlock : i + this.blocksPerRequest - 1;
|
||||
|
||||
eventsToSync.push({ fromBlock: i, toBlock: j, type });
|
||||
}
|
||||
|
||||
const events = [];
|
||||
const eventChunk = getBatches(eventsToSync, this.concurrencySize);
|
||||
|
||||
let chunkCount = 0;
|
||||
|
||||
for (const chunk of eventChunk) {
|
||||
chunkCount++;
|
||||
|
||||
const fetchedEvents = (await Promise.all(this.createBatchRequest(chunk))).flat();
|
||||
events.push(...fetchedEvents);
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
}
|
237
assets/services/bridgeHelper.js
Normal file
237
assets/services/bridgeHelper.js
Normal file
@ -0,0 +1,237 @@
|
||||
export const bridgeAbi = [
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "contract IOmnibridge",
|
||||
name: "_bridge",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "contract IWETH",
|
||||
name: "_weth",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_owner",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "nonpayable",
|
||||
type: "constructor",
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
internalType: "address",
|
||||
name: "owner",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: "bytes",
|
||||
name: "key",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
name: "PublicKey",
|
||||
type: "event",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "WETH",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "contract IWETH",
|
||||
name: "",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "bridge",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "contract IOmnibridge",
|
||||
name: "",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_token",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_to",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
name: "claimTokens",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_token",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_value",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "_data",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
name: "onTokenBridged",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "owner",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
components: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "owner",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "publicKey",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
internalType: "struct L1Helper.Account",
|
||||
name: "_account",
|
||||
type: "tuple",
|
||||
},
|
||||
],
|
||||
name: "register",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_newOwner",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
name: "transferOwnership",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "wrapAndRelayTokens",
|
||||
outputs: [],
|
||||
stateMutability: "payable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_receiver",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "_data",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
name: "wrapAndRelayTokens",
|
||||
outputs: [],
|
||||
stateMutability: "payable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_receiver",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "_data",
|
||||
type: "bytes",
|
||||
},
|
||||
{
|
||||
components: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "owner",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "publicKey",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
internalType: "struct L1Helper.Account",
|
||||
name: "_account",
|
||||
type: "tuple",
|
||||
},
|
||||
],
|
||||
name: "wrapAndRelayTokens",
|
||||
outputs: [],
|
||||
stateMutability: "payable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "address",
|
||||
name: "_receiver",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
name: "wrapAndRelayTokens",
|
||||
outputs: [],
|
||||
stateMutability: "payable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
stateMutability: "payable",
|
||||
type: "receive",
|
||||
},
|
||||
]
|
177
assets/services/constants.js
Normal file
177
assets/services/constants.js
Normal file
@ -0,0 +1,177 @@
|
||||
export const BSC_CHAIN_ID = 56
|
||||
export const XDAI_CHAIN_ID = 100
|
||||
export const MAINNET_CHAIN_ID = 1
|
||||
|
||||
export const ChainId = {
|
||||
BSC: BSC_CHAIN_ID,
|
||||
XDAI: XDAI_CHAIN_ID,
|
||||
MAINNET: MAINNET_CHAIN_ID,
|
||||
}
|
||||
|
||||
export const OFFCHAIN_ORACLE_CONTRACT = '0x07D91f5fb9Bf7798734C3f606dB065549F6893bb'
|
||||
|
||||
export const POOL_CONTRACT = {
|
||||
[ChainId.XDAI]: '0xD692Fd2D0b2Fbd2e52CFa5B5b9424bC981C30696', // ETH
|
||||
// [ChainId.XDAI]: '0x772F007F13604ac286312C85b9Cd9B2D691B353E', // BNB
|
||||
}
|
||||
export const REDGISTRY_CONTRACT = {
|
||||
[ChainId.MAINNET]: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2',
|
||||
}
|
||||
|
||||
export const AGGREGATOR_FACTORY = {
|
||||
[ChainId.MAINNET]: '0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49',
|
||||
}
|
||||
|
||||
export const WRAPPED_TOKEN = {
|
||||
[ChainId.MAINNET]: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH on mainnet
|
||||
[ChainId.XDAI]: '0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1', // WETH on xdai
|
||||
[ChainId.BSC]: '0xCa8d20f3e0144a72C6B5d576e9Bd3Fd8557E2B04', // WBNB on xdai
|
||||
}
|
||||
|
||||
export const RPC_LIST = {
|
||||
[ChainId.BSC]: 'https://tornadocash-rpc.com/bsc',
|
||||
[ChainId.MAINNET]: 'https://tornadocash-rpc.com/mainnet',
|
||||
[ChainId.XDAI]: 'https://tornadocash-rpc.com/gnosis',
|
||||
}
|
||||
|
||||
export const FALLBACK_RPC_LIST = {
|
||||
[ChainId.BSC]: [
|
||||
'https://binance.nodereal.io',
|
||||
// 'https://rpc.ankr.com/bsc/dbe08b852ba176a8aeac783cc1fa8becaf4f107235dfdae79241063fbf52ca4a',
|
||||
],
|
||||
[ChainId.MAINNET]: [
|
||||
'https://rpc.mevblocker.io',
|
||||
// 'https://rpc.ankr.com/eth/dbe08b852ba176a8aeac783cc1fa8becaf4f107235dfdae79241063fbf52ca4a',
|
||||
],
|
||||
[ChainId.XDAI]: [
|
||||
// 'https://rpc.ankr.com/gnosis/dbe08b852ba176a8aeac783cc1fa8becaf4f107235dfdae79241063fbf52ca4a',
|
||||
'https://tornadocash-rpc.com/gnosis',
|
||||
],
|
||||
}
|
||||
|
||||
export const RPC_WS_LIST = {
|
||||
[ChainId.MAINNET]: 'wss://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||
[ChainId.BSC]: 'wss://bsc-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||
[ChainId.XDAI]: 'wss://gnosis-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
|
||||
}
|
||||
|
||||
export const MULTICALL = {
|
||||
[ChainId.BSC]: '0xf072f255A3324198C7F653237B44E1C4e66f8C42',
|
||||
[ChainId.XDAI]: '0x8677b93D543d0217B32B8FDc20F2316E138D619B',
|
||||
[ChainId.MAINNET]: '0x1F98415757620B543A52E61c46B32eB19261F984',
|
||||
}
|
||||
|
||||
export const BRIDGE_PROXY = {
|
||||
[ChainId.BSC]: '0x05185872898b6f94AA600177EF41B9334B1FA48B',
|
||||
[ChainId.MAINNET]: '0x4c36d2919e407f0cc2ee3c993ccf8ac26d9ce64e',
|
||||
}
|
||||
|
||||
export const AMB_BRIDGE = {
|
||||
[ChainId.XDAI]: '0x75Df5AF045d91108662D8080fD1FEFAd6aA0bb59', // ETH
|
||||
// [ChainId.XDAI]: '0x162E898bD0aacB578C8D5F8d6ca588c13d2A383F', // BNB
|
||||
[ChainId.MAINNET]: '0x162E898bD0aacB578C8D5F8d6ca588c13d2A383F',
|
||||
}
|
||||
|
||||
export const BRIDGE_HELPER = {
|
||||
[ChainId.MAINNET]: '0xCa0840578f57fE71599D29375e16783424023357',
|
||||
[ChainId.BSC]: '0x8845F740F8B01bC7D9A4C82a6fD4A60320c07AF1',
|
||||
}
|
||||
|
||||
export const BRIDGE_FEE_MANAGER = {
|
||||
[ChainId.XDAI]: '0x5dbC897aEf6B18394D845A922BF107FA98E3AC55',
|
||||
}
|
||||
|
||||
export const FOREIGN_OMNIBRIDGE = {
|
||||
[ChainId.MAINNET]: '0x88ad09518695c6c3712AC10a214bE5109a655671',
|
||||
}
|
||||
|
||||
export const OMNIBRIDGE = {
|
||||
[ChainId.XDAI]: '0xf6A78083ca3e2a662D6dd1703c939c8aCE2e268d',
|
||||
}
|
||||
|
||||
export const SANCTION_LIST = {
|
||||
[ChainId.MAINNET]: '0x40C57923924B5c5c5455c48D93317139ADDaC8fb',
|
||||
}
|
||||
|
||||
export const CHAINS = {
|
||||
[ChainId.XDAI]: {
|
||||
symbol: 'XDAI',
|
||||
name: 'xdai',
|
||||
shortName: 'xdai',
|
||||
icon: 'ethereum',
|
||||
network: 'XDAI',
|
||||
blockDuration: 3000, // ms
|
||||
deployBlock: 19097755, // ETH
|
||||
// deployBlock: 20446605, // BNB
|
||||
blockGasLimit: 144000000, // rpc block gas limit
|
||||
hexChainId: '0x64',
|
||||
isEipSupported: false,
|
||||
ensSubdomainKey: 'gnosis-nova',
|
||||
blockExplorerUrl: 'https://gnosisscan.io'
|
||||
},
|
||||
[ChainId.MAINNET]: {
|
||||
symbol: 'ETH',
|
||||
name: 'ethereum',
|
||||
shortName: 'eth',
|
||||
icon: 'ethereum',
|
||||
network: 'Mainnet',
|
||||
deployBlock: 13494216,
|
||||
blockDuration: 15000,
|
||||
blockGasLimit: 144000000,
|
||||
hexChainId: '0x1',
|
||||
isEipSupported: true,
|
||||
ensSubdomainKey: 'mainnet-tornado',
|
||||
blockExplorerUrl: 'https://etherscan.io'
|
||||
},
|
||||
[ChainId.BSC]: {
|
||||
symbol: 'BNB',
|
||||
name: 'bsc',
|
||||
shortName: 'bsc',
|
||||
icon: 'binance',
|
||||
network: 'BSC',
|
||||
deployBlock: 14931075,
|
||||
blockDuration: 3000,
|
||||
blockGasLimit: 144000000,
|
||||
hexChainId: '0x38',
|
||||
isEipSupported: false,
|
||||
ensSubdomainKey: 'bsc-tornado',
|
||||
blockExplorerUrl: 'https://bscscan.com'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
export const workerEvents = {
|
||||
INIT_WORKER: 'initWorker',
|
||||
GET_COMMITMENT_EVENTS: 'get_commitment_events',
|
||||
// nullifier
|
||||
GET_UNSPENT_EVENTS: 'get_unspent_events',
|
||||
GET_NULLIFIER_EVENT: 'get_nullifier_event',
|
||||
GET_NULLIFIER_EVENTS_FROM_TX_HASH: 'get_nullifier_events_from_tx_hash',
|
||||
UPDATE_NULLIFIER_EVENTS: 'update_nullifier_events',
|
||||
// events
|
||||
GET_BATCH_EVENTS: 'get_batch_events',
|
||||
GET_BATCH_COMMITMENTS_EVENTS: 'get_batch_commitments_events',
|
||||
GET_EVENTS_FROM_TX_HASH: 'get_events_from_tx_hash',
|
||||
SAVE_EVENTS: 'save_events',
|
||||
GET_CACHED_EVENTS: 'get_cached_events',
|
||||
GET_CACHED_COMMITMENTS_EVENTS: 'get_cached_commitments_events',
|
||||
SAVE_LAST_SYNC_BLOCK: 'save_last_sync_block',
|
||||
}
|
||||
|
||||
export const numbers = {
|
||||
ZERO: 0,
|
||||
TWO: 2,
|
||||
ONE: 1,
|
||||
BYTES_31: 31,
|
||||
BYTES_62: 62,
|
||||
IS_SPENT_INDEX: 1,
|
||||
OX_LENGTH: 2,
|
||||
RECALL_DELAY: 500,
|
||||
NULLIFIER_LENGTH: 66,
|
||||
NONCE_BUF_LENGTH: 24,
|
||||
COMMITMENTS_CHAIN: 100,
|
||||
DEPLOYED_BLOCK: 19097755,
|
||||
DECRYPT_WORKERS_COUNT: 8,
|
||||
MIN_BLOCKS_INTERVAL_LINE: 200000,
|
||||
EPHEM_PUBLIC_KEY_BUF_LENGTH: 56,
|
||||
}
|
37
assets/services/downloadEvents.js
Normal file
37
assets/services/downloadEvents.js
Normal file
@ -0,0 +1,37 @@
|
||||
import { unzipAsync } from "./zip"
|
||||
|
||||
export async function downloadEvents(fileName, deployedBlock) {
|
||||
fileName = fileName.toLowerCase()
|
||||
|
||||
// @ts-ignore
|
||||
const prefix = __webpack_public_path__.slice(0, -7)
|
||||
|
||||
try {
|
||||
const resp = await fetch(`${prefix}/${fileName}.zip`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
})
|
||||
|
||||
const arrayBuffer = await resp.arrayBuffer()
|
||||
|
||||
const { [fileName]: content } = await unzipAsync(new Uint8Array(arrayBuffer))
|
||||
|
||||
const events = JSON.parse(new TextDecoder().decode(content))
|
||||
|
||||
const lastBlock = events && Array.isArray(events) && events[events.length - 1]
|
||||
? events[events.length - 1].blockNumber
|
||||
: deployedBlock
|
||||
|
||||
return {
|
||||
events,
|
||||
lastBlock
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: deployedBlock
|
||||
}
|
||||
}
|
||||
}
|
279
assets/services/graph/index.js
Normal file
279
assets/services/graph/index.js
Normal file
@ -0,0 +1,279 @@
|
||||
import { isEmpty } from 'lodash'
|
||||
import { ApolloClient, InMemoryCache, gql } from '@apollo/client/core'
|
||||
import { utils } from 'ethers'
|
||||
|
||||
import { GET_ACCOUNTS, GET_COMMITMENT, GET_NULLIFIER } from './queries'
|
||||
import { ChainId, numbers } from '../constants'
|
||||
|
||||
const { getAddress } = utils
|
||||
|
||||
const first = 1000
|
||||
const breakLength = 900
|
||||
|
||||
const CHAIN_GRAPH_URLS = {
|
||||
[ChainId.BSC]: 'https://api.thegraph.com/subgraphs/name/dan1kov/bsc-tornado-pool-subgraph',
|
||||
[ChainId.MAINNET]: 'https://tornadocash-rpc.com/subgraphs/name/tornadocash/mainnet-tornado-pool-subgraph',
|
||||
[ChainId.XDAI]: 'https://tornadocash-rpc.com/subgraphs/name/tornadocash/gnosis-tornado-nova-subgraph',
|
||||
}
|
||||
|
||||
const link = (operation) => {
|
||||
const { chainId } = operation.getContext()
|
||||
return CHAIN_GRAPH_URLS[chainId]
|
||||
}
|
||||
|
||||
const client = new ApolloClient({
|
||||
uri: link,
|
||||
cache: new InMemoryCache(),
|
||||
})
|
||||
|
||||
export async function getAccounts({ fromBlock, chainId }) {
|
||||
const { data } = await client.query({
|
||||
context: {
|
||||
chainId,
|
||||
},
|
||||
query: gql(GET_ACCOUNTS),
|
||||
variables: { first, fromBlock },
|
||||
})
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
results: [],
|
||||
lastSyncBlock: data._meta.block.number
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
results: data.accounts,
|
||||
lastSyncBlock: data._meta.block.number
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllAccounts({ fromBlock, toBlock, chainId }) {
|
||||
try {
|
||||
let accounts = []
|
||||
let lastSyncBlock
|
||||
|
||||
while (true) {
|
||||
let { results, lastSyncBlock: lastBlock } = await getAccounts({ fromBlock, chainId })
|
||||
|
||||
lastSyncBlock = lastBlock
|
||||
|
||||
if (isEmpty(results)) {
|
||||
break
|
||||
}
|
||||
|
||||
if (results.length < breakLength) {
|
||||
accounts = accounts.concat(results)
|
||||
break
|
||||
}
|
||||
|
||||
const [lastEvent] = results.slice(-numbers.ONE)
|
||||
|
||||
results = results.filter((e) => e.blockNumber !== lastEvent.blockNumber)
|
||||
fromBlock = Number(lastEvent.blockNumber)
|
||||
|
||||
accounts = accounts.concat(results)
|
||||
|
||||
if (toBlock && fromBlock >= Number(toBlock)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!accounts) {
|
||||
return {
|
||||
lastSyncBlock,
|
||||
events: [],
|
||||
}
|
||||
}
|
||||
|
||||
const data = accounts.map((e) => ({
|
||||
key: e.key,
|
||||
owner: getAddress(e.owner),
|
||||
blockNumber: Number(e.blockNumber),
|
||||
}))
|
||||
|
||||
const [lastEvent] = data.slice(-numbers.ONE)
|
||||
|
||||
return {
|
||||
events: data,
|
||||
lastSyncBlock: (lastEvent && lastEvent.blockNumber >= lastSyncBlock)
|
||||
? lastEvent.blockNumber + numbers.ONE
|
||||
: lastSyncBlock,
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error from getAllAccounts')
|
||||
console.log(err)
|
||||
return {
|
||||
lastSyncBlock: '',
|
||||
events: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCommitments({ fromBlock, chainId }) {
|
||||
const { data } = await client.query({
|
||||
context: {
|
||||
chainId,
|
||||
},
|
||||
query: gql(GET_COMMITMENT),
|
||||
variables: { first, fromBlock },
|
||||
})
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
results: [],
|
||||
lastSyncBlock: data._meta.block.number
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
results: data.commitments,
|
||||
lastSyncBlock: data._meta.block.number
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllCommitments({ fromBlock, toBlock, chainId }) {
|
||||
try {
|
||||
let commitments = []
|
||||
let lastSyncBlock
|
||||
|
||||
while (true) {
|
||||
let { results, lastSyncBlock: lastBlock } = await getCommitments({ fromBlock, chainId })
|
||||
|
||||
lastSyncBlock = lastBlock
|
||||
|
||||
if (isEmpty(results)) {
|
||||
break
|
||||
}
|
||||
|
||||
if (results.length < breakLength) {
|
||||
commitments = commitments.concat(results)
|
||||
break
|
||||
}
|
||||
|
||||
const [lastEvent] = results.slice(-numbers.ONE)
|
||||
|
||||
results = results.filter((e) => e.blockNumber !== lastEvent.blockNumber)
|
||||
fromBlock = Number(lastEvent.blockNumber)
|
||||
|
||||
commitments = commitments.concat(results)
|
||||
|
||||
if (toBlock && fromBlock >= Number(toBlock)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!commitments) {
|
||||
return {
|
||||
lastSyncBlock,
|
||||
events: [],
|
||||
}
|
||||
}
|
||||
|
||||
const data = commitments
|
||||
.map((e) => ({
|
||||
blockNumber: Number(e.blockNumber),
|
||||
transactionHash: e.transactionHash,
|
||||
index: Number(e.index),
|
||||
commitment: e.commitment,
|
||||
encryptedOutput: e.encryptedOutput
|
||||
}))
|
||||
.sort((a, b) => a.index - b.index)
|
||||
|
||||
const [lastEvent] = data.slice(-numbers.ONE)
|
||||
|
||||
return {
|
||||
events: data,
|
||||
lastSyncBlock: (lastEvent && lastEvent.blockNumber >= lastSyncBlock)
|
||||
? lastEvent.blockNumber + numbers.ONE
|
||||
: lastSyncBlock,
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error from getAllCommitments')
|
||||
console.log(err)
|
||||
return {
|
||||
lastSyncBlock: '',
|
||||
events: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function getNullifiers({ fromBlock, chainId }) {
|
||||
const { data } = await client.query({
|
||||
context: {
|
||||
chainId,
|
||||
},
|
||||
query: gql(GET_NULLIFIER),
|
||||
variables: { first, fromBlock },
|
||||
})
|
||||
|
||||
if (!data) {
|
||||
return {
|
||||
results: [],
|
||||
lastSyncBlock: data._meta.block.number
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
results: data.nullifiers,
|
||||
lastSyncBlock: data._meta.block.number
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllNullifiers({ fromBlock, chainId }) {
|
||||
try {
|
||||
let nullifiers = []
|
||||
let lastSyncBlock
|
||||
|
||||
while (true) {
|
||||
let { results, lastSyncBlock: lastBlock } = await getNullifiers({ fromBlock, chainId })
|
||||
|
||||
lastSyncBlock = lastBlock
|
||||
|
||||
if (isEmpty(results)) {
|
||||
break
|
||||
}
|
||||
|
||||
if (results.length < breakLength) {
|
||||
nullifiers = nullifiers.concat(results)
|
||||
break
|
||||
}
|
||||
|
||||
const [lastEvent] = results.slice(-numbers.ONE)
|
||||
|
||||
results = results.filter((e) => e.blockNumber !== lastEvent.blockNumber)
|
||||
fromBlock = Number(lastEvent.blockNumber)
|
||||
|
||||
nullifiers = nullifiers.concat(results)
|
||||
}
|
||||
|
||||
if (!nullifiers) {
|
||||
return {
|
||||
lastSyncBlock,
|
||||
events: [],
|
||||
}
|
||||
}
|
||||
|
||||
const data = nullifiers.map((e) => ({
|
||||
nullifier: e.nullifier,
|
||||
blockNumber: Number(e.blockNumber),
|
||||
transactionHash: e.transactionHash
|
||||
}))
|
||||
|
||||
const [lastEvent] = data.slice(-numbers.ONE)
|
||||
|
||||
return {
|
||||
events: data,
|
||||
lastSyncBlock: (lastEvent && lastEvent.blockNumber >= lastSyncBlock)
|
||||
? lastEvent.blockNumber + numbers.ONE
|
||||
: lastSyncBlock,
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Error from getAllNullifiers')
|
||||
console.log(err)
|
||||
return {
|
||||
lastSyncBlock: '',
|
||||
events: [],
|
||||
}
|
||||
}
|
||||
}
|
56
assets/services/graph/queries.js
Normal file
56
assets/services/graph/queries.js
Normal file
@ -0,0 +1,56 @@
|
||||
export const GET_ACCOUNTS = `
|
||||
query getAccounts($first: Int, $fromBlock: Int) {
|
||||
accounts(first: $first, orderBy: blockNumber, orderDirection: asc, where: {
|
||||
blockNumber_gte: $fromBlock
|
||||
}) {
|
||||
id
|
||||
key
|
||||
owner
|
||||
blockNumber
|
||||
}
|
||||
_meta {
|
||||
block {
|
||||
number
|
||||
}
|
||||
hasIndexingErrors
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GET_COMMITMENT = `
|
||||
query getCommitment($first: Int, $fromBlock: Int) {
|
||||
commitments(first: $first, orderBy: blockNumber, orderDirection: asc, where: {
|
||||
blockNumber_gte: $fromBlock
|
||||
}) {
|
||||
index
|
||||
commitment
|
||||
blockNumber
|
||||
encryptedOutput
|
||||
transactionHash
|
||||
}
|
||||
_meta {
|
||||
block {
|
||||
number
|
||||
}
|
||||
hasIndexingErrors
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const GET_NULLIFIER = `
|
||||
query getNullifier($first: Int, $fromBlock: Int) {
|
||||
nullifiers(first: $first, orderBy: blockNumber, orderDirection: asc, where: {
|
||||
blockNumber_gte: $fromBlock
|
||||
}) {
|
||||
nullifier
|
||||
blockNumber
|
||||
transactionHash
|
||||
}
|
||||
_meta {
|
||||
block {
|
||||
number
|
||||
}
|
||||
hasIndexingErrors
|
||||
}
|
||||
}
|
||||
`
|
222
assets/services/idb.js
Normal file
222
assets/services/idb.js
Normal file
@ -0,0 +1,222 @@
|
||||
import { deleteDB, openDB } from 'idb'
|
||||
|
||||
export const VERSION_ERROR = 'less than the existing version'
|
||||
export const INDEX_DB_ERROR = 'A mutation operation was attempted on a database that did not allow mutations.'
|
||||
|
||||
export const IDB_VERSION = 9
|
||||
|
||||
// TODO method for migration, remove indexed
|
||||
export class IndexedDB {
|
||||
constructor({ stores, dbName }) {
|
||||
this.dbExists = false
|
||||
this.isBlocked = false
|
||||
|
||||
this.options = {
|
||||
upgrade(db) {
|
||||
Object.values(db.objectStoreNames).forEach((value) => {
|
||||
db.deleteObjectStore(value)
|
||||
})
|
||||
|
||||
stores.forEach(({ name, keyPath, indexes }) => {
|
||||
const store = db.createObjectStore(name, {
|
||||
keyPath,
|
||||
autoIncrement: true,
|
||||
})
|
||||
|
||||
if (Array.isArray(indexes)) {
|
||||
indexes.forEach(({ name, unique = false }) => {
|
||||
store.createIndex(name, String(name), { unique })
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
this.dbName = dbName
|
||||
}
|
||||
|
||||
async initDB() {
|
||||
try {
|
||||
if (this.dbExists) {
|
||||
return
|
||||
}
|
||||
|
||||
this.db = await openDB(this.dbName, IDB_VERSION, this.options) // version (optional): Schema version, or undefined to open the current version.
|
||||
this.onEventHandler()
|
||||
|
||||
this.dbExists = true
|
||||
} catch (err) {
|
||||
// need for private mode firefox browser
|
||||
if (err.message.includes(INDEX_DB_ERROR)) {
|
||||
this.isBlocked = true
|
||||
return
|
||||
}
|
||||
|
||||
if (err.message.includes(VERSION_ERROR)) {
|
||||
await this.removeExist()
|
||||
}
|
||||
|
||||
console.error(`initDB has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async createTransactions({ storeName, data, mode = 'readwrite' }) {
|
||||
try {
|
||||
const tx = this.db.transaction(storeName, mode)
|
||||
const storedItem = tx.objectStore(storeName)
|
||||
|
||||
if (storedItem.add) {
|
||||
await storedItem.add(data)
|
||||
await tx.done
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Method createTransactions has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
createMultipleTransactions({
|
||||
storeName,
|
||||
data,
|
||||
index,
|
||||
mode = 'readwrite',
|
||||
}) {
|
||||
try {
|
||||
const tx = this.db.transaction(storeName, mode)
|
||||
|
||||
data.forEach((item) => {
|
||||
if (item && tx.store && tx.store.put) {
|
||||
tx.store.put({ ...item, ...index })
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
throw new Error(`Method createMultipleTransactions has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async getFromIndex(params) {
|
||||
if (this.isBlocked) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const item = await this.getFromIndexHandler(params)
|
||||
return item
|
||||
} catch (err) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async getItem({ storeName, key }) {
|
||||
try {
|
||||
if (this.isBlocked) {
|
||||
return
|
||||
}
|
||||
|
||||
const store = this.db.transaction(storeName).objectStore(storeName)
|
||||
|
||||
const value = await store.get(key)
|
||||
return value
|
||||
} catch (err) {
|
||||
throw new Error(`Method getItem has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async addItem({ storeName, data, key }) {
|
||||
try {
|
||||
const tx = this.db.transaction(storeName, 'readwrite')
|
||||
const isExist = await tx.objectStore(storeName).get(key)
|
||||
|
||||
if (!isExist) {
|
||||
await tx.objectStore(storeName).add(data)
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Method addItem has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async putItem({ storeName, data }) {
|
||||
try {
|
||||
if (this.isBlocked) {
|
||||
return
|
||||
}
|
||||
|
||||
const tx = this.db.transaction(storeName, 'readwrite')
|
||||
await tx.objectStore(storeName).put(data)
|
||||
} catch (err) {
|
||||
throw new Error(`Method putItem has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async getAll({ storeName }) {
|
||||
try {
|
||||
if (this.isBlocked || !this.dbExists) {
|
||||
return []
|
||||
}
|
||||
|
||||
const tx = this.db.transaction(storeName, 'readonly')
|
||||
const store = tx.objectStore(storeName)
|
||||
const data = await store.getAll()
|
||||
return data
|
||||
} catch (err) {
|
||||
throw new Error(`Method getAll has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async clearStore({ storeName, mode = 'readwrite' }) {
|
||||
try {
|
||||
const tx = this.db.transaction(storeName, mode)
|
||||
const storedItem = tx.objectStore(storeName)
|
||||
|
||||
if (storedItem.clear) {
|
||||
await storedItem.clear()
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Method clearStore has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async getAllFromIndex(params) {
|
||||
if (this.isBlocked) {
|
||||
return []
|
||||
}
|
||||
|
||||
try {
|
||||
const items = await this.getAllFromIndexHandler(params)
|
||||
return items
|
||||
} catch (err) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
onEventHandler() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
||||
this.db.addEventListener('onupgradeneeded', async () => {
|
||||
await this.removeExist()
|
||||
})
|
||||
}
|
||||
|
||||
async removeExist() {
|
||||
await deleteDB(this.dbName)
|
||||
this.dbExists = false
|
||||
|
||||
await this.initDB()
|
||||
}
|
||||
|
||||
async getFromIndexHandler({ storeName, indexName, key }) {
|
||||
try {
|
||||
const value = await this.db.getFromIndex(storeName, indexName, key)
|
||||
return value
|
||||
} catch (err) {
|
||||
throw new Error(`Method getFromIndexHandler has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async getAllFromIndexHandler({ storeName, indexName, key, count }) {
|
||||
try {
|
||||
const value = await this.db.getAllFromIndex(storeName, indexName, key, count)
|
||||
return value
|
||||
} catch (err) {
|
||||
throw new Error(`Method getAllFromIndex has error: ${err.message}`)
|
||||
}
|
||||
}
|
||||
}
|
1040
assets/services/pool.js
Normal file
1040
assets/services/pool.js
Normal file
File diff suppressed because it is too large
Load Diff
86
assets/services/provider.js
Normal file
86
assets/services/provider.js
Normal file
@ -0,0 +1,86 @@
|
||||
import { ethers } from 'ethers'
|
||||
import { fetchJson } from 'ethers/lib/utils'
|
||||
import { numbers } from './constants'
|
||||
|
||||
const defaultRetryAttempt = 0
|
||||
|
||||
export class ExtendedProvider extends ethers.providers.StaticJsonRpcProvider {
|
||||
constructor(url, network, fallbackRpcs) {
|
||||
super(url, network)
|
||||
this.fallbackRpcs = fallbackRpcs
|
||||
}
|
||||
|
||||
async send(method, params, retryAttempt = defaultRetryAttempt) {
|
||||
try {
|
||||
return await super.send(method, params)
|
||||
} catch (err) {
|
||||
if (!retryAttempt) {
|
||||
const TIME_OUT = 3000
|
||||
|
||||
await this.sleep(TIME_OUT)
|
||||
|
||||
if (this.fallbackRpcs) {
|
||||
return await this.fallbackSend(method, params, this.fallbackRpcs)
|
||||
}
|
||||
return this.send(method, params, ++retryAttempt)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
async fallbackSend(method, params, fallbackRpcs, retryAttempt = defaultRetryAttempt) {
|
||||
|
||||
function getResult(payload) {
|
||||
if (payload.error) {
|
||||
const error = new Error(payload.error.message)
|
||||
error.code = payload.error.code
|
||||
error.data = payload.error.data
|
||||
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
||||
throw error
|
||||
}
|
||||
return payload.result
|
||||
}
|
||||
|
||||
try {
|
||||
const request = {
|
||||
method: method,
|
||||
params: params,
|
||||
id: this._nextId + numbers.ONE,
|
||||
jsonrpc: '2.0',
|
||||
}
|
||||
|
||||
const result = fetchJson({ url: fallbackRpcs[retryAttempt] }, JSON.stringify(request), getResult).then(
|
||||
(result) => result,
|
||||
(error) => {
|
||||
throw error
|
||||
},
|
||||
)
|
||||
|
||||
return await result
|
||||
} catch (err) {
|
||||
retryAttempt += numbers.ONE
|
||||
if (!fallbackRpcs[retryAttempt]) {
|
||||
throw err
|
||||
} else {
|
||||
return await this.fallbackSend(method, params, fallbackRpcs, retryAttempt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async sleep(ms) {
|
||||
return await new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
// private checkRpcError(err: { data: string; code: string; message: string }) {
|
||||
// const code = String(err?.code)
|
||||
// const data = err.data?.toLowerCase()
|
||||
// const message = err.message?.toLowerCase()
|
||||
|
||||
// const ERROR_DATA = 'too many concurrent request'
|
||||
// const ERROR_MESSAGE = 'timeout'
|
||||
// const ERROR_CODE = '-32017'
|
||||
|
||||
// return (data?.includes(ERROR_DATA) || message?.includes(ERROR_MESSAGE)) && code === ERROR_CODE
|
||||
// }
|
||||
}
|
13
assets/services/utilities.js
Normal file
13
assets/services/utilities.js
Normal file
@ -0,0 +1,13 @@
|
||||
export const ZERO_ELEMENT = 0
|
||||
|
||||
export function getBatches(array, batchSize) {
|
||||
const batches = []
|
||||
while (array.length) {
|
||||
batches.push(array.splice(ZERO_ELEMENT, batchSize))
|
||||
}
|
||||
return batches
|
||||
}
|
||||
|
||||
export async function sleep(ms) {
|
||||
return await new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
25
assets/services/zip.js
Normal file
25
assets/services/zip.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { zip, unzip } from 'fflate'
|
||||
|
||||
export function zipAsync(file) {
|
||||
return new Promise((res, rej) => {
|
||||
zip(file, { mtime: new Date('1/1/1980') }, (err, data) => {
|
||||
if (err) {
|
||||
rej(err);
|
||||
return;
|
||||
}
|
||||
res(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function unzipAsync(data) {
|
||||
return new Promise((res, rej) => {
|
||||
unzip(data, {}, (err, data) => {
|
||||
if (err) {
|
||||
rej(err);
|
||||
return;
|
||||
}
|
||||
res(data);
|
||||
});
|
||||
});
|
||||
}
|
285
assets/syncEvents.js
Normal file
285
assets/syncEvents.js
Normal file
@ -0,0 +1,285 @@
|
||||
import path from 'path'
|
||||
import { stat, readFile, writeFile } from 'fs/promises'
|
||||
import { Contract, providers, utils } from 'ethers'
|
||||
|
||||
import { BatchEventsService } from './services/batch'
|
||||
import { getAllAccounts, getAllCommitments, getAllNullifiers } from './services/graph'
|
||||
import { POOL_CONTRACT, BRIDGE_HELPER, RPC_LIST, ChainId, CHAINS, numbers } from './services/constants'
|
||||
import { zipAsync, unzipAsync } from './services/zip'
|
||||
import { poolAbi } from './services/pool'
|
||||
import { bridgeAbi } from './services/bridgeHelper'
|
||||
|
||||
const { getAddress } = utils
|
||||
const { StaticJsonRpcProvider } = providers
|
||||
|
||||
const EVENT_PATH = './static'
|
||||
|
||||
async function existsAsync(fileOrDir) {
|
||||
try {
|
||||
await stat(fileOrDir);
|
||||
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const getProvider = (chainId) => {
|
||||
return new StaticJsonRpcProvider({ skipFetchSetup: true, url: RPC_LIST[chainId] }, chainId)
|
||||
}
|
||||
|
||||
const getTornadoPool = (chainId, provider) => {
|
||||
const TornadoPool = new Contract(POOL_CONTRACT[chainId], poolAbi, provider)
|
||||
|
||||
return {
|
||||
TornadoPool,
|
||||
BatchEventsService: new BatchEventsService({
|
||||
provider,
|
||||
contract: TornadoPool
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const getBridgeHelper = (chainId, provider) => {
|
||||
const BridgeHelper = new Contract(BRIDGE_HELPER[chainId], bridgeAbi, provider)
|
||||
|
||||
return {
|
||||
BridgeHelper,
|
||||
BridgeEventsService: new BatchEventsService({
|
||||
provider,
|
||||
contract: BridgeHelper
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const loadEvents = async (fileName, deployedBlock) => {
|
||||
fileName = fileName.toLowerCase()
|
||||
|
||||
const filePath = path.join(EVENT_PATH, fileName + '.zip')
|
||||
|
||||
if (!(await existsAsync(filePath))) {
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: deployedBlock
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await readFile(filePath)
|
||||
const { [fileName]: content } = await unzipAsync(data)
|
||||
|
||||
const events = JSON.parse(new TextDecoder().decode(content))
|
||||
|
||||
const lastBlock = events && Array.isArray(events) && events[events.length - 1]
|
||||
? events[events.length - 1].blockNumber
|
||||
: deployedBlock
|
||||
|
||||
return {
|
||||
events,
|
||||
lastBlock
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: deployedBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const saveEvents = async (fileName, events) => {
|
||||
fileName = fileName.toLowerCase()
|
||||
|
||||
const filePath = path.join(EVENT_PATH, fileName + '.zip')
|
||||
|
||||
const payload = await zipAsync({
|
||||
[fileName]: new TextEncoder().encode(JSON.stringify(events, null, 2) + '\n')
|
||||
})
|
||||
|
||||
await writeFile(filePath, payload)
|
||||
}
|
||||
|
||||
const syncAccounts = async (chainId, BatchEventsService) => {
|
||||
const fileName = `accounts_${chainId}.json`
|
||||
|
||||
console.log(`Syncing ${fileName}`)
|
||||
|
||||
const cachedEvents = await loadEvents(fileName, CHAINS[chainId].deployBlock)
|
||||
|
||||
const events = [...cachedEvents.events]
|
||||
let fromBlock = cachedEvents.lastBlock + numbers.ONE
|
||||
|
||||
console.log({
|
||||
cachedEvents: events.length,
|
||||
cachedBlock: fromBlock
|
||||
})
|
||||
|
||||
const { events: graphEvents, lastSyncBlock } = await getAllAccounts({
|
||||
fromBlock,
|
||||
chainId
|
||||
})
|
||||
|
||||
console.log({
|
||||
graphEvents: graphEvents.length,
|
||||
graphBlock: lastSyncBlock
|
||||
})
|
||||
|
||||
if (lastSyncBlock) {
|
||||
events.push(...graphEvents)
|
||||
fromBlock = lastSyncBlock
|
||||
}
|
||||
|
||||
let nodeEvents = await BatchEventsService.getBatchEvents({
|
||||
fromBlock,
|
||||
type: 'PublicKey'
|
||||
})
|
||||
|
||||
console.log({
|
||||
nodeEvents: nodeEvents.length,
|
||||
nodeBlock: nodeEvents && nodeEvents[nodeEvents.length - 1] ? nodeEvents[nodeEvents.length - 1].blockNumber : undefined
|
||||
})
|
||||
|
||||
if (nodeEvents && nodeEvents.length) {
|
||||
nodeEvents = nodeEvents.map(({ blockNumber, args }) => ({
|
||||
key: args.key,
|
||||
owner: getAddress(args.owner),
|
||||
blockNumber,
|
||||
}))
|
||||
|
||||
events.push(...nodeEvents)
|
||||
}
|
||||
|
||||
await saveEvents(fileName, events)
|
||||
}
|
||||
|
||||
const syncCommitments = async (chainId, BatchEventsService) => {
|
||||
const fileName = `commitments_${chainId}.json`
|
||||
|
||||
console.log(`Syncing ${fileName}`)
|
||||
|
||||
const cachedEvents = await loadEvents(fileName, CHAINS[chainId].deployBlock)
|
||||
|
||||
const events = [...cachedEvents.events]
|
||||
let fromBlock = cachedEvents.lastBlock + numbers.ONE
|
||||
|
||||
console.log({
|
||||
cachedEvents: events.length,
|
||||
cachedBlock: fromBlock
|
||||
})
|
||||
|
||||
const { events: graphEvents, lastSyncBlock } = await getAllCommitments({
|
||||
fromBlock,
|
||||
chainId
|
||||
})
|
||||
|
||||
console.log({
|
||||
graphEvents: graphEvents.length,
|
||||
graphBlock: lastSyncBlock
|
||||
})
|
||||
|
||||
if (lastSyncBlock) {
|
||||
events.push(...graphEvents)
|
||||
fromBlock = lastSyncBlock
|
||||
}
|
||||
|
||||
let nodeEvents = await BatchEventsService.getBatchEvents({
|
||||
fromBlock,
|
||||
type: 'NewCommitment'
|
||||
})
|
||||
|
||||
console.log({
|
||||
nodeEvents: nodeEvents.length,
|
||||
nodeBlock: nodeEvents && nodeEvents[nodeEvents.length - 1] ? nodeEvents[nodeEvents.length - 1].blockNumber : undefined
|
||||
})
|
||||
|
||||
if (nodeEvents && nodeEvents.length) {
|
||||
nodeEvents = nodeEvents.map(({ blockNumber, transactionHash, args }) => ({
|
||||
blockNumber,
|
||||
transactionHash,
|
||||
index: Number(args.index),
|
||||
commitment: args.commitment,
|
||||
encryptedOutput: args.encryptedOutput,
|
||||
}))
|
||||
|
||||
events.push(...nodeEvents)
|
||||
}
|
||||
|
||||
await saveEvents(fileName, events)
|
||||
}
|
||||
|
||||
const syncNullifiers = async (chainId, BatchEventsService) => {
|
||||
const fileName = `nullifiers_${chainId}.json`
|
||||
|
||||
console.log(`Syncing ${fileName}`)
|
||||
|
||||
const cachedEvents = await loadEvents(fileName, CHAINS[chainId].deployBlock)
|
||||
|
||||
const events = [...cachedEvents.events]
|
||||
let fromBlock = cachedEvents.lastBlock + numbers.ONE
|
||||
|
||||
console.log({
|
||||
cachedEvents: events.length,
|
||||
cachedBlock: fromBlock
|
||||
})
|
||||
|
||||
const { events: graphEvents, lastSyncBlock } = await getAllNullifiers({
|
||||
fromBlock,
|
||||
chainId
|
||||
})
|
||||
|
||||
console.log({
|
||||
graphEvents: graphEvents.length,
|
||||
graphBlock: lastSyncBlock
|
||||
})
|
||||
|
||||
if (lastSyncBlock) {
|
||||
events.push(...graphEvents)
|
||||
fromBlock = lastSyncBlock
|
||||
}
|
||||
|
||||
let nodeEvents = await BatchEventsService.getBatchEvents({
|
||||
fromBlock,
|
||||
type: 'NewNullifier'
|
||||
})
|
||||
|
||||
console.log({
|
||||
nodeEvents: nodeEvents.length,
|
||||
nodeBlock: nodeEvents && nodeEvents[nodeEvents.length - 1] ? nodeEvents[nodeEvents.length - 1].blockNumber : undefined
|
||||
})
|
||||
|
||||
if (nodeEvents && nodeEvents.length) {
|
||||
nodeEvents = nodeEvents.map(({ blockNumber, transactionHash, args }) => ({
|
||||
blockNumber,
|
||||
transactionHash,
|
||||
nullifier: args.nullifier,
|
||||
}))
|
||||
|
||||
events.push(...nodeEvents)
|
||||
}
|
||||
|
||||
await saveEvents(fileName, events)
|
||||
}
|
||||
|
||||
const main = async () => {
|
||||
const chainId = ChainId.XDAI
|
||||
|
||||
const ethChainId = ChainId.MAINNET
|
||||
|
||||
const provider = getProvider(chainId)
|
||||
|
||||
const ethProvider = getProvider(ethChainId)
|
||||
|
||||
const { BatchEventsService } = getTornadoPool(chainId, provider)
|
||||
|
||||
const { BridgeEventsService } = getBridgeHelper(ethChainId, ethProvider)
|
||||
|
||||
console.log(`Connected with ${chainId}: (block: ${await provider.getBlockNumber()})`)
|
||||
|
||||
console.log(`Connected with ${ethChainId}: (block: ${await ethProvider.getBlockNumber()})`)
|
||||
|
||||
await syncAccounts(ethChainId, BridgeEventsService)
|
||||
|
||||
await syncCommitments(chainId, BatchEventsService)
|
||||
|
||||
await syncNullifiers(chainId, BatchEventsService)
|
||||
}
|
||||
main()
|
@ -55,7 +55,6 @@ export default {
|
||||
'setProvider',
|
||||
'changeChain',
|
||||
'checkNetwork',
|
||||
'checkSanction',
|
||||
'setWalletParams',
|
||||
'getWalletBalance',
|
||||
]),
|
||||
@ -93,9 +92,6 @@ export default {
|
||||
const address = await provider.setupProvider()
|
||||
const network = await provider.checkNetworkVersion()
|
||||
|
||||
if (address) {
|
||||
await this.checkSanction(address)
|
||||
}
|
||||
await this.setProvider({ network, name: key })
|
||||
await this.setAccountData(address)
|
||||
|
||||
@ -126,8 +122,6 @@ export default {
|
||||
if (address) {
|
||||
const checksumAddress = toChecksumAddress(address)
|
||||
|
||||
await this.checkSanction(checksumAddress)
|
||||
|
||||
if (!this.isConnected) {
|
||||
return
|
||||
}
|
||||
|
@ -123,6 +123,7 @@ const config: NuxtConfig = {
|
||||
config.output.globalObject = 'this'
|
||||
}
|
||||
if (config?.module != null) {
|
||||
/**
|
||||
if (isClient) {
|
||||
config.module.rules.push({
|
||||
test: /\.worker\.js$/,
|
||||
@ -133,6 +134,7 @@ const config: NuxtConfig = {
|
||||
},
|
||||
})
|
||||
}
|
||||
**/
|
||||
|
||||
config.module.rules.push({
|
||||
test: /\.bin$/,
|
||||
|
@ -11,12 +11,11 @@
|
||||
"lint:fix": "eslint --ext .js,.ts --quiet --fix",
|
||||
"compile": "typechain --target ethers-v5 --out-dir ./_contracts './abi/*.json'",
|
||||
"copyFile": "node --loader ts-node/esm copyFile.ts",
|
||||
"generate": "nuxt generate && yarn copyFile dist/404.html dist/ipfs-404.html",
|
||||
"generate": "yarn worker:compile && nuxt generate && yarn copyFile dist/404.html dist/ipfs-404.html",
|
||||
"prepare": "husky install",
|
||||
"ipfs:upload": "node --loader ts-node/esm ipfsUpload.ts",
|
||||
"worker:compile": "yarn generate && yarn compile:events && yarn compile:nullifier",
|
||||
"compile:events": "babel dist/_nuxt/workers/events.worker.js --out-file static/events.worker.js",
|
||||
"compile:nullifier": "babel dist/_nuxt/workers/nullifier.worker.js --out-file static/nullifier.worker.js"
|
||||
"worker:compile": "webpack",
|
||||
"update:events": "webpack && node ./syncEvents.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.4.16",
|
||||
@ -75,6 +74,7 @@
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-vue": "^7.16.0",
|
||||
"fflate": "^0.8.2",
|
||||
"form-data": "^4.0.0",
|
||||
"husky": "^6.0.0",
|
||||
"lint-staged": "10.2.11",
|
||||
@ -87,6 +87,7 @@
|
||||
"typechain": "^5.1.0",
|
||||
"typescript": "^4.3.4",
|
||||
"vue-eslint-parser": "^7.6.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"worker-loader": "^3.0.8"
|
||||
},
|
||||
"husky": {
|
||||
|
@ -7,6 +7,7 @@ import { isEmpty, sleep, toChecksumAddress } from '@/utilities'
|
||||
import { getBridgeHelper, getBridgeProxy, getAmbBridge } from '@/contracts'
|
||||
|
||||
import { EventsClass, GetAffirmationParams, GetRelayedMessageParams, SaveEventsParams } from './@types'
|
||||
import { downloadEvents } from './load'
|
||||
|
||||
export * from './batch'
|
||||
|
||||
@ -33,19 +34,31 @@ class EventAggregator implements EventsClass {
|
||||
storeName: `${IndexDBStores.ACCOUNT_EVENTS}_${chainId}`,
|
||||
})
|
||||
|
||||
const newEvents = []
|
||||
|
||||
if (cachedEvents?.length) {
|
||||
const [latestEvent] = cachedEvents.slice(-numbers.ONE)
|
||||
blockFrom = Number(latestEvent.blockNumber) + numbers.ONE
|
||||
} else {
|
||||
const downloadedEvents = await downloadEvents(`accounts_${chainId}.json`, blockFrom)
|
||||
|
||||
if (downloadedEvents.events.length) {
|
||||
newEvents.push(...downloadedEvents.events)
|
||||
|
||||
blockFrom = downloadedEvents.lastBlock
|
||||
}
|
||||
}
|
||||
|
||||
const { events: graphEvents, lastSyncBlock } = await getAllAccounts({ fromBlock: blockFrom, chainId })
|
||||
|
||||
const [account] = graphEvents.filter((e: { key: string }) => e.key === publicKey)
|
||||
newEvents.push(...graphEvents)
|
||||
|
||||
const [account] = newEvents.filter((e: { key: string }) => e.key === publicKey)
|
||||
|
||||
if (account) {
|
||||
this.saveEvents({
|
||||
chainId,
|
||||
events: graphEvents,
|
||||
events: newEvents,
|
||||
storeName: IndexDBStores.ACCOUNT_EVENTS,
|
||||
})
|
||||
return account.owner
|
||||
@ -68,7 +81,7 @@ class EventAggregator implements EventsClass {
|
||||
}
|
||||
})
|
||||
|
||||
const newEvents = graphEvents.concat(accountEvents)
|
||||
newEvents.push(...accountEvents)
|
||||
|
||||
this.saveEvents({
|
||||
chainId,
|
||||
@ -76,7 +89,7 @@ class EventAggregator implements EventsClass {
|
||||
storeName: IndexDBStores.ACCOUNT_EVENTS,
|
||||
})
|
||||
|
||||
const events = cachedEvents.concat(newEvents).filter((e: { key: string }) => e.key === publicKey)
|
||||
const events = newEvents.filter((e: { key: string }) => e.key === publicKey)
|
||||
|
||||
if (isEmpty(events)) {
|
||||
return undefined
|
||||
@ -87,6 +100,7 @@ class EventAggregator implements EventsClass {
|
||||
|
||||
return event.owner
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
@ -113,19 +127,30 @@ class EventAggregator implements EventsClass {
|
||||
storeName: `${IndexDBStores.ACCOUNT_EVENTS}_${chainId}`,
|
||||
})
|
||||
|
||||
const newEvents = []
|
||||
|
||||
if (cachedEvents?.length) {
|
||||
const [latestEvent] = cachedEvents.slice(-numbers.ONE)
|
||||
blockFrom = Number(latestEvent.blockNumber) + numbers.ONE
|
||||
} else {
|
||||
const downloadedEvents = await downloadEvents(`accounts_${chainId}.json`, blockFrom)
|
||||
|
||||
if (downloadedEvents.events.length) {
|
||||
newEvents.push(...downloadedEvents.events)
|
||||
|
||||
blockFrom = downloadedEvents.lastBlock
|
||||
}
|
||||
}
|
||||
|
||||
const { events: graphEvents, lastSyncBlock } = await getAllAccounts({ fromBlock: blockFrom, chainId })
|
||||
newEvents.push(...graphEvents)
|
||||
|
||||
const [account] = graphEvents.filter((e: { owner: string }) => toChecksumAddress(e.owner) === toChecksumAddress(address))
|
||||
const [account] = newEvents.filter((e: { owner: string }) => toChecksumAddress(e.owner) === toChecksumAddress(address))
|
||||
|
||||
if (account) {
|
||||
this.saveEvents({
|
||||
chainId,
|
||||
events: graphEvents,
|
||||
events: newEvents,
|
||||
storeName: IndexDBStores.ACCOUNT_EVENTS,
|
||||
})
|
||||
return account.key
|
||||
@ -148,7 +173,7 @@ class EventAggregator implements EventsClass {
|
||||
}
|
||||
})
|
||||
|
||||
const newEvents = graphEvents.concat(accountEvents)
|
||||
newEvents.push(...accountEvents)
|
||||
|
||||
this.saveEvents({
|
||||
chainId,
|
||||
@ -169,6 +194,7 @@ class EventAggregator implements EventsClass {
|
||||
|
||||
return event.key
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
49
services/events/load.ts
Normal file
49
services/events/load.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { unzip } from 'fflate'
|
||||
|
||||
export function unzipAsync(data: Uint8Array) {
|
||||
return new Promise((res, rej) => {
|
||||
unzip(data, {}, (err, data) => {
|
||||
if (err) {
|
||||
rej(err);
|
||||
return;
|
||||
}
|
||||
res(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function downloadEvents(fileName: string, deployedBlock: number) {
|
||||
fileName = fileName.toLowerCase()
|
||||
|
||||
// @ts-ignore
|
||||
const prefix = __webpack_public_path__.slice(0, -7)
|
||||
|
||||
try {
|
||||
const resp = await fetch(`${prefix}/${fileName}.zip`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
}
|
||||
})
|
||||
|
||||
const arrayBuffer = await resp.arrayBuffer()
|
||||
|
||||
const { [fileName]: content } = (await unzipAsync(new Uint8Array(arrayBuffer))) as any
|
||||
|
||||
const events = JSON.parse(new TextDecoder().decode(content))
|
||||
|
||||
const lastBlock = events && Array.isArray(events) && events[events.length - 1]
|
||||
? events[events.length - 1].blockNumber
|
||||
: deployedBlock
|
||||
|
||||
return {
|
||||
events,
|
||||
lastBlock
|
||||
}
|
||||
} catch {
|
||||
return {
|
||||
events: [],
|
||||
lastBlock: deployedBlock
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,8 @@ const link = (operation: Operation) => {
|
||||
|
||||
const CHAIN_GRAPH_URLS: { [chainId in ChainId]: string } = {
|
||||
[ChainId.BSC]: 'https://api.thegraph.com/subgraphs/name/dan1kov/bsc-tornado-pool-subgraph',
|
||||
[ChainId.MAINNET]: 'https://api.thegraph.com/subgraphs/name/tornadocash/mainnet-tornado-pool-subgraph',
|
||||
[ChainId.MAINNET]: 'https://tornadocash-rpc.com/subgraphs/name/tornadocash/mainnet-tornado-pool-subgraph',
|
||||
[ChainId.XDAI]: 'https://tornadocash-rpc.com/subgraphs/name/tornadocash/gnosis-tornado-nova-subgraph',
|
||||
}
|
||||
|
||||
const client = new ApolloClient({
|
||||
@ -27,7 +28,7 @@ const client = new ApolloClient({
|
||||
})
|
||||
|
||||
const registryClient = new ApolloClient({
|
||||
uri: 'https://api.thegraph.com/subgraphs/name/tornadocash/tornado-relayer-registry',
|
||||
uri: 'https://tornadocash-rpc.com/subgraphs/name/tornadocash/tornado-relayer-registry',
|
||||
cache: new InMemoryCache(),
|
||||
})
|
||||
|
||||
@ -230,13 +231,15 @@ export async function getAllCommitments({ fromBlock, chainId }: Params) {
|
||||
}
|
||||
}
|
||||
|
||||
const data = commitments.map((e) => ({
|
||||
index: Number(e.index),
|
||||
commitment: e.commitment,
|
||||
blockNumber: Number(e.blockNumber),
|
||||
encryptedOutput: e.encryptedOutput,
|
||||
transactionHash: e.transactionHash
|
||||
}))
|
||||
const data = commitments
|
||||
.map((e) => ({
|
||||
index: Number(e.index),
|
||||
commitment: e.commitment,
|
||||
blockNumber: Number(e.blockNumber),
|
||||
encryptedOutput: e.encryptedOutput,
|
||||
transactionHash: e.transactionHash
|
||||
}))
|
||||
.sort((a, b) => a.index - b.index)
|
||||
|
||||
const [lastEvent] = data.slice(-numbers.ONE)
|
||||
|
||||
@ -272,7 +275,7 @@ export async function getNullifiers({ fromBlock, chainId }: Params): Promise<{
|
||||
}
|
||||
|
||||
return {
|
||||
results: data.commitments,
|
||||
results: data.nullifiers,
|
||||
lastSyncBlock: data._meta.block.number
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,6 @@ import { CommitmentEvents, NullifierEvents } from '@/services/events/@types'
|
||||
|
||||
import { EventsPayload, DecryptedEvents, GetEventsFromTxHashParams } from './@types'
|
||||
|
||||
import '@/assets/events.worker.js'
|
||||
import '@/assets/nullifier.worker.js'
|
||||
|
||||
// import NWorker from '@/assets/nullifier.worker.js'
|
||||
// import EWorker from '@/assets/events.worker.js'
|
||||
|
||||
export interface WorkerProvider {
|
||||
workerSetup: (chainId: ChainId) => void
|
||||
getCommitmentEvents: () => Promise<CommitmentEvents>
|
||||
@ -41,13 +35,8 @@ class Provider implements WorkerProvider {
|
||||
|
||||
const basePath = `${window.location.origin}${ipfsPathPrefix}`
|
||||
|
||||
this.nullifierWorkers = new Array(CORES).fill('').map(() => new Worker(`${basePath}/_nuxt/workers/nullifier.worker.js`))
|
||||
this.eventsWorkers = new Array(CORES).fill('').map(() => new Worker(`${basePath}/_nuxt/workers/events.worker.js`))
|
||||
|
||||
// // @ts-expect-error
|
||||
// this.nullifierWorkers = new Array(CORES).fill('').map(() => new NWorker())
|
||||
// // @ts-expect-error
|
||||
// this.eventsWorkers = new Array(CORES).fill('').map(() => new EWorker())
|
||||
this.nullifierWorkers = new Array(CORES).fill('').map(() => new Worker(`${basePath}/nullifier.worker.js`))
|
||||
this.eventsWorkers = new Array(CORES).fill('').map(() => new Worker(`${basePath}/events.worker.js`))
|
||||
}
|
||||
|
||||
public workerSetup = (chainId: ChainId) => {
|
||||
|
BIN
static/accounts_1.json.zip
Normal file
BIN
static/accounts_1.json.zip
Normal file
Binary file not shown.
BIN
static/commitments_100.json.zip
Normal file
BIN
static/commitments_100.json.zip
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
static/nullifiers_100.json.zip
Normal file
BIN
static/nullifiers_100.json.zip
Normal file
Binary file not shown.
@ -19,17 +19,6 @@ export const actions: ActionTree<WalletState, RootState> = {
|
||||
}
|
||||
},
|
||||
|
||||
async checkSanction({ getters }, address) {
|
||||
const contract = getSanctionList(getters.dependencies.l1ChainId)
|
||||
|
||||
const isSanctioned = await contract.callStatic.isSanctioned(address)
|
||||
if (isSanctioned) {
|
||||
window.onbeforeunload = null
|
||||
// ToDo add type
|
||||
// @ts-expect-error
|
||||
window.location = 'https://twitter.com/TornadoCash/status/1514904975037669386'
|
||||
}
|
||||
},
|
||||
checkAppNetwork({ commit }, network) {
|
||||
try {
|
||||
// TODO create a selector for active network
|
||||
|
64405
syncEvents.cjs
Normal file
64405
syncEvents.cjs
Normal file
File diff suppressed because one or more lines are too long
53
webpack.config.js
Normal file
53
webpack.config.js
Normal file
@ -0,0 +1,53 @@
|
||||
import path from 'path'
|
||||
import webpack from 'webpack'
|
||||
|
||||
export default [
|
||||
{
|
||||
mode: 'production',
|
||||
entry: './assets/events.worker.js',
|
||||
output: {
|
||||
path: path.resolve('static'),
|
||||
filename: 'events.worker.js',
|
||||
}
|
||||
},
|
||||
{
|
||||
mode: 'production',
|
||||
entry: './assets/nullifier.worker.js',
|
||||
output: {
|
||||
path: path.resolve('static'),
|
||||
filename: 'nullifier.worker.js',
|
||||
}
|
||||
},
|
||||
{
|
||||
mode: 'production',
|
||||
entry: './assets/syncEvents.js',
|
||||
output: {
|
||||
path: path.resolve('.'),
|
||||
filename: 'syncEvents.cjs',
|
||||
},
|
||||
target: 'node',
|
||||
plugins: [
|
||||
new webpack.BannerPlugin({
|
||||
banner: '#!/usr/bin/env node\n',
|
||||
raw: true
|
||||
})
|
||||
],
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.mjs$/,
|
||||
include: /node_modules/,
|
||||
type: 'javascript/auto'
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'fflate': 'fflate/esm'
|
||||
}
|
||||
},
|
||||
optimization: {
|
||||
minimize: false,
|
||||
}
|
||||
}
|
||||
]
|
148
yarn.lock
148
yarn.lock
@ -1320,7 +1320,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016"
|
||||
integrity "sha1-LLz4Ir83ZMlljE0uVovQwMt0gBY= sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw=="
|
||||
|
||||
"@discoveryjs/json-ext@0.5.7":
|
||||
"@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0":
|
||||
version "0.5.7"
|
||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
|
||||
integrity "sha1-HVcr+74Ut3BOC6Dzm3SBW4SHDXA= sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
|
||||
@ -3040,6 +3040,23 @@
|
||||
"@webassemblyjs/wast-parser" "1.9.0"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webpack-cli/configtest@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
|
||||
integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
|
||||
|
||||
"@webpack-cli/info@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
|
||||
integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
|
||||
dependencies:
|
||||
envinfo "^7.7.3"
|
||||
|
||||
"@webpack-cli/serve@^1.7.0":
|
||||
version "1.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
|
||||
integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
|
||||
|
||||
"@wry/context@^0.7.0":
|
||||
version "0.7.3"
|
||||
resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.3.tgz#240f6dfd4db5ef54f81f6597f6714e58d4f476a1"
|
||||
@ -4317,6 +4334,15 @@ cliui@^6.0.0:
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^6.2.0"
|
||||
|
||||
clone-deep@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
|
||||
integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
|
||||
dependencies:
|
||||
is-plain-object "^2.0.4"
|
||||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
clone-response@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3"
|
||||
@ -4371,7 +4397,7 @@ colord@^2.9.1:
|
||||
resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
|
||||
integrity "sha1-T4zpGd5Fbx1cHDaMMH/iDz5Z+0M= sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
|
||||
|
||||
colorette@^2.0.10:
|
||||
colorette@^2.0.10, colorette@^2.0.14:
|
||||
version "2.0.20"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
|
||||
integrity "sha1-nreT5oMwZ/cjWQL807CZF6AAqVo= sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
|
||||
@ -4407,7 +4433,7 @@ commander@^5.1.0:
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
integrity "sha1-Rqu9FlL44Fm92u+Zu9yyrZzxea4= sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="
|
||||
|
||||
commander@^7.2.0:
|
||||
commander@^7.0.0, commander@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||
integrity "sha1-o2y1fQtQHOEI5NIFWaFQo5HZerc= sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
|
||||
@ -5359,6 +5385,11 @@ entities@^4.2.0:
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity "sha1-XSaOpecRPsdMTQM7eepaNaSI+0g= sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
|
||||
|
||||
envinfo@^7.7.3:
|
||||
version "7.13.0"
|
||||
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31"
|
||||
integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==
|
||||
|
||||
errno@^0.1.3, errno@~0.1.7:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
||||
@ -6207,6 +6238,11 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
|
||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||
integrity "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
|
||||
|
||||
fastest-levenshtein@^1.0.12:
|
||||
version "1.0.16"
|
||||
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
|
||||
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
|
||||
|
||||
fastfile@0.0.18:
|
||||
version "0.0.18"
|
||||
resolved "https://registry.yarnpkg.com/fastfile/-/fastfile-0.0.18.tgz#2b69bbbfd2fcccc9bc8099c27de1379b89756a4b"
|
||||
@ -6286,6 +6322,11 @@ ffjavascript@^0.2.48:
|
||||
wasmcurves "0.2.2"
|
||||
web-worker "^1.2.0"
|
||||
|
||||
fflate@^0.8.2:
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
|
||||
integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
|
||||
|
||||
ffwasm@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/ffwasm/-/ffwasm-0.0.7.tgz#23bb9a3537ecc87c0f24fcfb3a9ddd0e86855fff"
|
||||
@ -6690,6 +6731,11 @@ function-bind@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0= sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
function.prototype.name@^1.1.5:
|
||||
version "1.1.5"
|
||||
resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
|
||||
@ -7109,6 +7155,13 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7:
|
||||
inherits "^2.0.3"
|
||||
minimalistic-assert "^1.0.1"
|
||||
|
||||
hasown@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
|
||||
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
he@1.2.0, he@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
@ -7314,6 +7367,14 @@ import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
|
||||
parent-module "^1.0.0"
|
||||
resolve-from "^4.0.0"
|
||||
|
||||
import-local@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
|
||||
integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
|
||||
dependencies:
|
||||
pkg-dir "^4.2.0"
|
||||
resolve-cwd "^3.0.0"
|
||||
|
||||
imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
@ -7404,6 +7465,11 @@ internal-slot@^1.0.5:
|
||||
has "^1.0.3"
|
||||
side-channel "^1.0.4"
|
||||
|
||||
interpret@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
|
||||
|
||||
invert-kv@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
|
||||
@ -7501,6 +7567,13 @@ is-core-module@^2.11.0:
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-core-module@^2.13.0:
|
||||
version "2.13.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
|
||||
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
|
||||
dependencies:
|
||||
hasown "^2.0.0"
|
||||
|
||||
is-data-descriptor@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
|
||||
@ -9481,7 +9554,7 @@ pkg-dir@^3.0.0:
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
pkg-dir@^4.1.0:
|
||||
pkg-dir@^4.1.0, pkg-dir@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
|
||||
integrity "sha1-8JkTPfft5CLoHR2ESCcO6z5CYfM= sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="
|
||||
@ -10589,6 +10662,13 @@ readline@^1.3.0:
|
||||
resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c"
|
||||
integrity sha512-k2d6ACCkiNYz222Fs/iNze30rRJ1iIicW7JuX/7/cozvih6YCkFZH+J6mAFDVgv0dRBaAyr4jDqC95R2y4IADg==
|
||||
|
||||
rechoir@^0.7.0:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
|
||||
integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
|
||||
dependencies:
|
||||
resolve "^1.9.0"
|
||||
|
||||
regenerate-unicode-properties@^10.1.0:
|
||||
version "10.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c"
|
||||
@ -10746,11 +10826,23 @@ resolve-alpn@^1.0.0, resolve-alpn@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
|
||||
integrity "sha1-t629rDVGqq7CC0Xn2CZZJwcnJvk= sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="
|
||||
|
||||
resolve-cwd@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
|
||||
integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
|
||||
dependencies:
|
||||
resolve-from "^5.0.0"
|
||||
|
||||
resolve-from@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||
integrity "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY= sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
|
||||
resolve-from@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
|
||||
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
||||
|
||||
resolve-url@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||
@ -10765,6 +10857,15 @@ resolve@^1.1.7, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.22.0, resolve@^1.22
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
resolve@^1.9.0:
|
||||
version "1.22.8"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
|
||||
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
|
||||
dependencies:
|
||||
is-core-module "^2.13.0"
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
response-iterator@^0.2.6:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da"
|
||||
@ -11137,6 +11238,13 @@ sha.js@^2.4.0, sha.js@^2.4.8:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
shallow-clone@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
|
||||
integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
@ -12936,6 +13044,24 @@ webpack-bundle-analyzer@^4.9.0:
|
||||
sirv "^1.0.7"
|
||||
ws "^7.3.1"
|
||||
|
||||
webpack-cli@^4.10.0:
|
||||
version "4.10.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31"
|
||||
integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==
|
||||
dependencies:
|
||||
"@discoveryjs/json-ext" "^0.5.0"
|
||||
"@webpack-cli/configtest" "^1.2.0"
|
||||
"@webpack-cli/info" "^1.5.0"
|
||||
"@webpack-cli/serve" "^1.7.0"
|
||||
colorette "^2.0.14"
|
||||
commander "^7.0.0"
|
||||
cross-spawn "^7.0.3"
|
||||
fastest-levenshtein "^1.0.12"
|
||||
import-local "^3.0.2"
|
||||
interpret "^2.2.0"
|
||||
rechoir "^0.7.0"
|
||||
webpack-merge "^5.7.3"
|
||||
|
||||
webpack-dev-middleware@^5.3.3:
|
||||
version "5.3.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f"
|
||||
@ -12956,6 +13082,15 @@ webpack-hot-middleware@^2.25.3:
|
||||
html-entities "^2.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
webpack-merge@^5.7.3:
|
||||
version "5.10.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177"
|
||||
integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==
|
||||
dependencies:
|
||||
clone-deep "^4.0.1"
|
||||
flat "^5.0.2"
|
||||
wildcard "^2.0.0"
|
||||
|
||||
webpack-node-externals@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz#1a3407c158d547a9feb4229a9e3385b7b60c9917"
|
||||
@ -13077,6 +13212,11 @@ widest-line@^3.1.0:
|
||||
dependencies:
|
||||
string-width "^4.0.0"
|
||||
|
||||
wildcard@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67"
|
||||
integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==
|
||||
|
||||
word-wrap@~1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
|
Loading…
x
Reference in New Issue
Block a user