Merge pull request 'Community build #003' (#10) from staging into master

Reviewed-on: https://development.tornadocash.community/tornadocash/classic-ui/pulls/10
This commit is contained in:
gozzy 2022-11-24 10:55:39 +00:00
commit 90ef7362bf
262 changed files with 403 additions and 674 deletions

View file

@ -27,7 +27,7 @@ For detailed explanation on how things work, checkout [Nuxt.js docs](https://nux
## Audit
[TornadoCash_Classic_dApp_audit_Decurity.pdf](https://tornado.cash/audits/TornadoCash_Classic_dApp_audit_Decurity.pdf)
[TornadoCash_Classic_dApp_audit_Decurity.pdf](https://ipfs.io/ipfs/QmXzmwfsb4GwzmPD7W9VDNHh7ttyYKgXCY74973QMZqBDA)
## Update cached files

View file

@ -293,6 +293,10 @@
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M297.216 243.2c0 15.616-11.52 28.416-26.112 28.416-14.336 0-26.112-12.8-26.112-28.416s11.52-28.416 26.112-28.416c14.592 0 26.112 12.8 26.112 28.416zm-119.552-28.416c-14.592 0-26.112 12.8-26.112 28.416s11.776 28.416 26.112 28.416c14.592 0 26.112-12.8 26.112-28.416.256-15.616-11.52-28.416-26.112-28.416zM448 52.736V512c-64.494-56.994-43.868-38.128-118.784-107.776l13.568 47.36H52.48C23.552 451.584 0 428.032 0 398.848V52.736C0 23.552 23.552 0 52.48 0h343.04C424.448 0 448 23.552 448 52.736zm-72.96 242.688c0-82.432-36.864-149.248-36.864-149.248-36.864-27.648-71.936-26.88-71.936-26.88l-3.584 4.096c43.52 13.312 63.744 32.512 63.744 32.512-60.811-33.329-132.244-33.335-191.232-7.424-9.472 4.352-15.104 7.424-15.104 7.424s21.248-20.224 67.328-33.536l-2.56-3.072s-35.072-.768-71.936 26.88c0 0-36.864 66.816-36.864 149.248 0 0 21.504 37.12 78.08 38.912 0 0 9.472-11.52 17.152-21.248-32.512-9.728-44.8-30.208-44.8-30.208 3.766 2.636 9.976 6.053 10.496 6.4 43.21 24.198 104.588 32.126 159.744 8.96 8.96-3.328 18.944-8.192 29.44-15.104 0 0-12.8 20.992-46.336 30.464 7.68 9.728 16.896 20.736 16.896 20.736 56.576-1.792 78.336-38.912 78.336-38.912z'%3E%3C/path%3E%3C/svg%3E");
}
&-git {
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='92pt' height='92pt' viewBox='0 0 92 92'%3E%3Cpath style='stroke:none;fill-rule:nonzero;fill:%6b6b6b;fill-opacity:1' d='M90.156 41.965 50.036 1.848a5.918 5.918 0 0 0-8.372 0l-8.328 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.034 7.034 0 0 1 1.669 7.277l10.187 10.184a7.028 7.028 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.05 7.05 0 0 1-9.965 0 7.044 7.044 0 0 1-1.528-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.06 7.06 0 0 1 2.304-1.539V33.926a7.049 7.049 0 0 1-3.82-9.234L29.242 14.272 1.73 41.777a5.925 5.925 0 0 0 0 8.371L41.852 90.27a5.925 5.925 0 0 0 8.37 0l39.934-39.934a5.925 5.925 0 0 0 0-8.371'/%3E%3C/svg%3E");
}
&-discourse {
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512'%3E%3Cpath d='M225.9 32C103.3 32 0 130.5 0 252.1 0 256 .1 480 .1 480l225.8-.2c122.7 0 222.1-102.3 222.1-223.9C448 134.3 348.6 32 225.9 32zM224 384c-19.4 0-37.9-4.3-54.4-12.1L88.5 392l22.9-75c-9.8-18.1-15.4-38.9-15.4-61 0-70.7 57.3-128 128-128s128 57.3 128 128-57.3 128-128 128z'%3E%3C/path%3E%3C/svg%3E");
}

View file

@ -37,47 +37,15 @@
<b-button
tag="a"
type="is-icon"
href="https://forums.tornadocash.community"
href="https://development.tornadocash.community/tornadocash/classic-ui"
target="_blank"
rel="noopener noreferrer"
icon-right="discourse"
icon-right="git"
></b-button>
<b-button
tag="a"
type="is-icon"
href="https://matrix.tornadocash.community"
target="_blank"
rel="noopener noreferrer"
icon-right="matrix"
></b-button>
<b-button
tag="a"
type="is-icon"
href="https://tornado-cash.medium.com"
target="_blank"
rel="noopener noreferrer"
icon-right="medium"
></b-button>
<b-button
tag="a"
type="is-icon"
href="https://twitter.com/TornadoCashOrg"
target="_blank"
rel="noopener noreferrer"
icon-right="twitter"
></b-button>
<b-button
tag="a"
type="is-icon"
href="https://t.me/TornadoCashOrg"
target="_blank"
rel="noopener noreferrer"
icon-right="telegram"
></b-button>
<b-button
tag="a"
type="is-icon"
href="https://development.tornadocash.community"
href="https://github.com/tornadocash"
target="_blank"
rel="noopener noreferrer"
icon-right="github"

View file

@ -3,15 +3,14 @@
<div class="loading-container">
<div class="loading-tornado" data-test="tornado_loader"></div>
<div class="loading-message">{{ message }}...</div>
<div v-if="progress >= 0" class="loading-message">{{ progress }}%</div>
<approve-loader v-if="isApprove" />
</div>
</b-loading>
</template>
<script>
import { mapState, mapGetters } from 'vuex'
import ApproveLoader from './ApproveLoader'
export default {
components: {
ApproveLoader
@ -19,7 +18,7 @@ export default {
computed: {
...mapGetters('metamask', ['isWalletConnect']),
...mapState('metamask', ['providerName']),
...mapState('loading', ['enabled', 'message', 'type']),
...mapState('loading', ['enabled', 'message', 'progress', 'type']),
isApprove() {
return this.type === 'approve'
}

View file

@ -93,9 +93,7 @@ export default {
}
},
mounted() {
if (!this.timer) {
this.updateEvents()
}
this.updateEvents()
},
beforeDestroy() {
clearTimeout(this.timer)
@ -103,10 +101,6 @@ export default {
methods: {
updateEvents() {
this.$store.dispatch('application/updateSelectEvents')
this.timer = setTimeout(() => {
this.updateEvents()
}, 60 * 1000)
}
}
}

View file

@ -51,8 +51,7 @@
data-test="enter_note_field"
></b-input>
<div v-if="hasErrorNote" class="help" :class="hasErrorNote.type">
<!-- eslint-disable vue/no-v-html -->
<p v-html="hasErrorNote.msg"></p>
<p>{{ hasErrorNote.msg }}</p>
</div>
</div>
<div v-if="!hasErrorNote && depositTxHash" class="field field-withdraw">
@ -341,6 +340,7 @@ export default {
if (currency !== this.nativeCurrency) {
this.$store.dispatch('application/setDefaultEthToReceive', { currency })
}
this.$store.dispatch('loading/updateProgress', { progress: -1 })
this.depositsPast = Number(depositsPast) <= 0 ? 0 : depositsPast
this.depositTxHash = txHash
this.depositTimestamp = timestamp

View file

@ -60,6 +60,12 @@ export const cachedEventsLength = {
}
}
export const httpConfig = {
// buffer for tor connections
timeout: 30000,
keepAlive: true
}
export const PROVIDERS = {
walletConnect: {
name: 'WalletConnect',

View file

@ -1,6 +1,6 @@
const { EIP712Signer } = require('@ticket721/e712')
import { concatSig } from 'eth-sig-util'
const Permit = [
const PermitType = [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
@ -8,36 +8,53 @@ const Permit = [
{ name: 'deadline', type: 'uint256' }
]
class PermitSigner extends EIP712Signer {
const EIP712DomainType = [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' },
{ name: 'chainId', type: 'uint256' },
{ name: 'verifyingContract', type: 'address' }
]
export default class PermitSigner {
constructor(_domain, _permitArgs) {
super(_domain, ['Permit', Permit])
this.permitArgs = _permitArgs
this.domain = _domain
}
// Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)
setPermitInfo(_permitArgs) {
this.permitArgs = _permitArgs
}
getPayload() {
return this.generatePayload(this.permitArgs, 'Permit')
}
async getSignature(privateKey) {
const payload = this.getPayload()
const { hex, v, r, s } = await this.sign(privateKey, payload)
getReqPayload() {
return {
hex,
v,
r: '0x' + r,
s: '0x' + s
domain: this.domain,
primaryType: 'Permit',
types: {
Permit: PermitType,
EIP712Domain: EIP712DomainType
},
message: this.permitArgs
}
}
getSignerAddress(permitArgs, signature) {
const originalPayload = this.generatePayload(permitArgs, 'Permit')
return this.verify(originalPayload, signature)
getSignature(response) {
response = response.substring(2)
const r = '0x' + response.substring(0, 64)
const s = '0x' + response.substring(64, 128)
let v = parseInt(response.substring(128, 130), 16)
// fix ledger sign
if (v === 0 || v === 1) {
v = v + 27
}
return {
hex: concatSig(v, r, s),
v,
r,
s
}
}
}
module.exports = { PermitSigner }

View file

@ -278,8 +278,8 @@ export default {
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
Arbitrum: {
name: 'Arbitrum Public RPC',
url: 'https://arb1.arbitrum.io/rpc'
name: '1RPC',
url: 'https://1rpc.io/arb'
}
},
tokens: {

View file

@ -21,10 +21,8 @@
},
"dependencies": {
"@apollo/client": "^3.3.20",
"@ensdomains/ensjs": "^2.1.0",
"@metamask/onboarding": "^1.0.0",
"@nuxtjs/moment": "^1.6.0",
"@ticket721/e712": "^0.4.1",
"@walletconnect/web3-provider": "1.7.8",
"ajv": "^6.10.2",
"arraybuffer-loader": "^1.0.8",
@ -43,7 +41,6 @@
"graphql": "^15.5.1",
"idb": "^6.0.0",
"jspdf": "^1.5.3",
"jszip": "^3.5.0",
"lodash": "^4.17.21",
"node-fetch": "^2.6.1",
"numbro": "^2.3.2",

View file

@ -83,31 +83,6 @@
</i18n>
</b-notification>
<b-notification
v-if="isEthLink"
:active="isActiveNotification.ethLink"
class="main-notification"
type="is-warning"
icon-pack="icon"
has-icon
:aria-close-label="$t('closeNotification')"
@close="disableNotification({ key: 'ethLink' })"
>
<i18n path="ethLinkBanner.notification">
<template v-slot:issue>
<a
href="https://discuss.ens.domains/t/eth-link-expiry/13899"
target="_blank"
rel="noopener noreferrer"
>{{ $t('ethLinkBanner.issue') }}</a
>
</template>
<template v-slot:alternative>
<a href="https://tornado.cash/">{{ $t('ethLinkBanner.alternative') }}</a>
</template>
</i18n>
</b-notification>
<div class="columns">
<div class="column is-half">
<b-tabs v-model="activeTab" class="is-tornado" :animated="false" @input="tabChanged">
@ -140,8 +115,7 @@ export default {
data() {
return {
activeTab: 0,
isActive: false,
isEthLink: window.location.host === 'tornadocash.eth.link'
isActive: false
}
},
computed: {
@ -184,8 +158,19 @@ export default {
}
}
} else {
const { currency, amount } = this.selectedInstance
this.$store.dispatch('application/setAndUpdateStatistic', { currency, amount })
const userSelection = this.selectedInstance
const stateSelection = this.selectedStatistic
if (
!stateSelection ||
userSelection.amount !== stateSelection.amount ||
userSelection.currency !== stateSelection.currency
) {
this.$store.dispatch('application/setAndUpdateStatistic', {
currency: userSelection.currency,
amount: userSelection.amount
})
}
}
}
}

View file

@ -3,13 +3,28 @@ export default ({ store, isHMR, app }, inject) => {
inject('isLoadedFromIPFS', main)
}
function main() {
const whiteListedDomains = ['localhost:3000', 'tornadocash.eth.link', 'tornadocash.eth.limo']
const whiteListedDomains = [
'tornadocash.3th.li',
'tornadocash.3th.ws',
'tornadocash.eth.link',
'tornadocash.eth.limo',
'tornadocashcommunity.3th.li',
'tornadocashcommunity.3th.ws',
'tornadocashcommunity.eth.link',
'tornadocashcommunity.eth.limo'
]
const NETLIFY_REGEXP = /deploy-preview-(\d+)--tornadocash\.netlify\.app/
const IPFS_GATEWAY_REGEXP = /.ipfs./
const IPFS_LOCAL_REGEXP = /.ipfs.localhost:/
const IPFS_SOP_GATEWAY_REGEXP = /\/ipfs\//
if (NETLIFY_REGEXP.test(window.location.host)) {
if (IPFS_LOCAL_REGEXP.test(window.location.host)) {
return false
} else if (!whiteListedDomains.includes(window.location.host)) {
} else if (
IPFS_GATEWAY_REGEXP.test(window.location.host) ||
IPFS_SOP_GATEWAY_REGEXP.test(window.location.host) ||
whiteListedDomains.includes(window.location.host)
) {
console.warn('The page has been loaded from ipfs.io. LocalStorage is disabled')
return true
}

View file

@ -277,7 +277,7 @@ export default async (ctx, inject) => {
},
{
name: `withdrawals_${token}_${amount}`,
keyPath: 'transactionHash',
keyPath: 'blockNumber',
indexes: WITHDRAWAL_INDEXES
},
{

View file

@ -1,24 +1,14 @@
import fs from 'fs'
import Jszip from 'jszip'
import zlib from 'zlib'
import Web3 from 'web3'
import networkConfig from '../../networkConfig'
const jszip = new Jszip()
export async function download({ name, directory, contentType }) {
const path = `${directory}${name}.zip`.toLowerCase()
const path = `${directory}${name}.gz`.toLowerCase()
const data = fs.readFileSync(path)
const zip = await jszip.loadAsync(data)
const file = zip.file(
path
.replace(directory, '')
.slice(0, -4)
.toLowerCase()
)
const content = await file.async(contentType)
const content = zlib.inflateSync(data)
return content
}

View file

@ -1,33 +1,19 @@
import fs from 'fs'
import Jszip from 'jszip'
const compressionConfig = {
type: "nodebuffer",
compression: "DEFLATE",
compressionOptions: {
level: 9
}
}
const fileConfig = {
binary: true,
compression: "DEFLATE"
}
import zlib from 'zlib'
export async function save(filePath) {
const jszip = new Jszip()
const directories = filePath.split('/')
const fileName = directories[directories.length - 1]
try {
const data = fs.readFileSync(`${filePath}`)
await jszip.file(`${fileName}`, data, fileConfig)
await jszip.generateNodeStream({
...compressionConfig,
streamFiles: true
const payload = await zlib.deflateSync(data, {
level: zlib.constants.Z_BEST_COMPRESSION,
strategy: zlib.constants.Z_FILTERED
})
.pipe(fs.createWriteStream(`${filePath}.zip`))
fs.writeFileSync(`${filePath}.gz`, payload)
return true
} catch (err) {

View file

@ -9,7 +9,7 @@ import { loadCachedEvents, getPastEvents } from './helpers'
const EVENTS_PATH = './static/events/'
const EVENTS = ['Deposit', 'Withdrawal']
const enabledChains = ['1', '56', '5', '100', '137' ]
const enabledChains = ['1', '56', '100', '137' ]
async function main(type, netId) {
const { tokens, nativeCurrency, deployedBlock } = networkConfig[`netId${netId}`]

View file

@ -45,7 +45,7 @@ function createTreeZip(netId) {
treesFolder.forEach((fileName) => {
fileName = `${TREES_PATH}${fileName}`
const isInstanceFile = !fileName.includes('.zip') && fileName.includes(baseFilename)
const isInstanceFile = !fileName.includes('.gz') && fileName.includes(baseFilename)
if (isInstanceFile) {
save(fileName)

View file

@ -4,11 +4,18 @@ import graph from '@/services/graph'
import { download } from '@/store/snark'
import networkConfig from '@/networkConfig'
import InstanceABI from '@/abis/Instance.abi.json'
import { CONTRACT_INSTANCES, eventsType } from '@/constants'
import { sleep, formatEvents, capitalizeFirstLetter } from '@/utils'
import { CONTRACT_INSTANCES, eventsType, httpConfig } from '@/constants'
import { sleep, flattenNArray, formatEvents, capitalizeFirstLetter } from '@/utils'
const supportedCaches = ['1', '56', '100', '137']
let store
if (process.browser) {
window.onNuxtReady(({ $store }) => {
store = $store
})
}
class EventService {
constructor({ netId, amount, currency, factoryMethods }) {
this.idb = window.$nuxt.$indexedDB(netId)
@ -31,6 +38,15 @@ class EventService {
return `${type}s_${this.currency}_${this.amount}`
}
updateEventProgress(percentage, type) {
if (store) {
store.dispatch('loading/updateProgress', {
message: `Fetching past ${type} events`,
progress: Math.ceil(percentage * 100)
})
}
}
async getEvents(type) {
let cachedEvents = await this.getEventsFromDB(type)
@ -40,6 +56,7 @@ class EventService {
return cachedEvents
}
async updateEvents(type, cachedEvents) {
const { deployedBlock } = networkConfig[`netId${this.netId}`]
@ -116,7 +133,7 @@ class EventService {
const module = await download({
contentType: 'string',
name: `events/${instanceName}.json.zip`
name: `events/${instanceName}.json.gz`
})
if (module) {
@ -146,14 +163,6 @@ class EventService {
return undefined
}
// IndexedDB scrambles assortment
savedEvents.sort((a, b) => {
if (a.leafIndex && b.leafIndex) {
return a.leafIndex - b.leafIndex
}
return a.blockNumber - b.blockNumber
})
return {
events: savedEvents,
lastBlock: savedEvents[savedEvents.length - 1].blockNumber
@ -238,7 +247,22 @@ class EventService {
}
}
async getEventsPartFromRpc({ fromBlock, toBlock, type }) {
getPastEvents({ fromBlock, toBlock, type }) {
return new Promise((resolve, reject) => {
const repsonse = this.contract.getPastEvents(capitalizeFirstLetter(type), {
fromBlock,
toBlock
})
if (repsonse) {
resolve(repsonse)
} else {
reject(new Error())
}
})
}
async getEventsPartFromRpc({ fromBlock, toBlock, type }, shouldRetry = false, i = 0) {
try {
const { currentBlockNumber } = await this.getBlocksDiff({ fromBlock })
@ -249,10 +273,27 @@ class EventService {
}
}
const events = await this.contract.getPastEvents(capitalizeFirstLetter(type), {
fromBlock,
toBlock
})
let events = []
try {
events = await this.getPastEvents({ fromBlock, toBlock, type })
} catch (e) {
if (shouldRetry) {
i = i + 1
// maximum 10 second buffer for rate-limiting
await sleep(2000 * i)
events = await this.getEventsPartFromRpc(
{
fromBlock,
toBlock,
type
},
i !== 5,
i
)
}
}
if (!events?.length) {
return {
@ -269,41 +310,60 @@ class EventService {
}
}
createBatchRequest({ batchIndex, batchSize, batchBlocks, blockDenom, type }) {
return new Array(batchSize).fill('').map(
(_, i) =>
new Promise(async (resolve) => {
const toBlock = batchBlocks[batchIndex * batchSize + i]
const fromBlock = toBlock - blockDenom
const batchEvents = await this.getEventsPartFromRpc(
{
fromBlock,
toBlock,
type
},
true
)
resolve(batchEvents.events)
})
)
}
async getBatchEventsFromRpc({ fromBlock, type }) {
try {
const batchSize = 10
const blockRange = 10000
const { blockDifference, currentBlockNumber } = await this.getBlocksDiff({ fromBlock })
let numberParts = blockDifference === 0 ? 1 : Math.ceil(blockDifference / blockRange)
const part = Math.ceil(blockDifference / numberParts)
const batchDigest = blockDifference === 0 ? 1 : Math.ceil(blockDifference / blockRange)
const blockDenom = Math.ceil(blockDifference / batchDigest)
const batchCount = Math.ceil(batchDigest / batchSize)
const blocks = new Array(batchCount * batchSize).fill('')
const batchBlocks = blocks.map((_, i) => (i + 1) * blockDenom + fromBlock)
let events = []
let toBlock = fromBlock + part
if (fromBlock < currentBlockNumber) {
if (toBlock >= currentBlockNumber) {
toBlock = 'latest'
numberParts = 1
this.updateEventProgress(0, type)
for (let batchIndex = 0; batchIndex < batchCount; batchIndex++) {
const batch = await Promise.all(
this.createBatchRequest({ batchIndex, batchBlocks, blockDenom, batchSize, type })
)
this.updateEventProgress(batchIndex / batchCount, type)
events = events.concat(batch)
await sleep(200)
}
for (let i = 0; i < numberParts; i++) {
try {
await sleep(200)
const partOfEvents = await this.getEventsPartFromRpc({ fromBlock, toBlock, type })
if (partOfEvents) {
events = events.concat(partOfEvents.events)
}
fromBlock = toBlock
toBlock += part
} catch {
numberParts = numberParts + 1
}
}
if (events.length) {
return {
events,
lastBlock: toBlock === 'latest' ? currentBlockNumber : toBlock
}
events = flattenNArray(events)
return {
lastBlock: events[events.length - 1].blockNumber,
events
}
}
return undefined
@ -315,9 +375,11 @@ class EventService {
async getEventsFromRpc({ fromBlock, type }) {
try {
const { blockDifference } = await this.getBlocksDiff({ fromBlock })
const blockRange = 10000
let events
if (blockDifference < 10000) {
if (blockDifference < blockRange) {
const rpcEvents = await this.getEventsPartFromRpc({ fromBlock, toBlock: 'latest', type })
events = rpcEvents?.events || []
} else {
@ -380,7 +442,9 @@ class EventsFactory {
instances = new Map()
constructor(rpcUrl) {
this.provider = new Web3(rpcUrl).eth
const httpProvider = new Web3.providers.HttpProvider(rpcUrl, httpConfig)
this.provider = new Web3(httpProvider).eth
}
getBlockNumber = () => {

View file

@ -39,6 +39,7 @@ const defaultOptions = {
const client = new ApolloClient({
uri: link,
credentials: 'omit',
cache: new InMemoryCache(),
defaultOptions
})
@ -46,6 +47,7 @@ const client = new ApolloClient({
const registryClient = new ApolloClient({
uri: 'https://api.thegraph.com/subgraphs/name/tornadocash/tornado-relayer-registry',
cache: new InMemoryCache(),
credentials: 'omit',
defaultOptions
})

View file

@ -5,6 +5,8 @@ import { download } from '@/store/snark'
import networkConfig from '@/networkConfig'
import { mimc, bloomService } from '@/services'
const supportedCaches = ['1', '56', '100', '137']
class MerkleTreeService {
constructor({ netId, amount, currency, commitment, instanceName }) {
this.netId = netId
@ -20,12 +22,12 @@ class MerkleTreeService {
commitment,
instanceName,
fileFolder: 'trees',
fileName: `deposits_${currency}_${amount}_bloom.json.zip`
fileName: `deposits_${currency}_${amount}_bloom.json.gz`
})
}
getFileName(partNumber = trees.PARTS_COUNT) {
return `trees/deposits_${this.currency}_${this.amount}_slice${partNumber}.json.zip`
return `trees/deposits_${this.currency}_${this.amount}_slice${partNumber}.json.gz`
}
createTree({ events }) {
@ -153,8 +155,7 @@ class MerkleTreeService {
}
async getTree() {
const { nativeCurrency } = networkConfig[`netId${this.netId}`]
const hasCache = nativeCurrency === this.currency && Number(this.netId) === 1
const hasCache = supportedCaches.includes(this.netId.toString())
let cachedTree = await this.getTreeFromDB()

View file

@ -1,10 +1,11 @@
import Web3 from 'web3'
import namehash from 'eth-ens-namehash'
import { BigNumber as BN } from 'bignumber.js'
import { toChecksumAddress } from 'web3-utils'
import { toChecksumAddress, isAddress } from 'web3-utils'
import { graph } from '@/services'
import networkConfig from '@/networkConfig'
import { REGISTRY_DEPLOYED_BLOCK } from '@/constants'
import { sleep, flattenNArray } from '@/utils'
import AggregatorABI from '@/abis/Aggregator.abi.json'
import RelayerRegistryABI from '@/abis/RelayerRegistry.abi.json'
@ -24,28 +25,59 @@ class RelayerRegister {
this.relayerRegistry = new this.provider.Contract(RelayerRegistryABI, registryContract)
}
fetchEvents = async (fromBlock, toBlock) => {
if (fromBlock <= toBlock) {
try {
const registeredEventsPart = await this.relayerRegistry.getPastEvents('RelayerRegistered', {
fromBlock,
toBlock
})
fetchEvents = ({ fromBlock, toBlock }, shouldRetry = false) => {
return new Promise((resolve, reject) => {
if (fromBlock <= toBlock) {
try {
const registeredEventsPart = this.relayerRegistry.getPastEvents('RelayerRegistered', {
fromBlock,
toBlock
})
return registeredEventsPart
} catch (error) {
const midBlock = (fromBlock + toBlock) >> 1
resolve(registeredEventsPart)
} catch (error) {
if (shouldRetry) {
sleep(1000)
if (midBlock - fromBlock < 2) {
throw new Error(`error fetching events: ${error.message}`)
const events = this.fetchEvents({ fromBlock, toBlock })
resolve(events)
} else {
reject(new Error(error))
}
}
const arr1 = await this.fetchEvents(fromBlock, midBlock)
const arr2 = await this.fetchEvents(midBlock + 1, toBlock)
return [...arr1, ...arr2]
} else {
resolve([])
}
}
return []
})
}
batchFetchEvents = async ({ fromBlock, toBlock }) => {
const blockRange = 10000
const blockDifference = toBlock - fromBlock
const chunkCount = Math.ceil(blockDifference / blockRange)
const blockDenom = Math.ceil(blockDifference / chunkCount)
const promises = new Array(chunkCount).fill('').map(
(_, i) =>
new Promise((resolve) => {
sleep(300)
const batch = this.fetchEvents(
{
fromBlock: i * blockDenom + fromBlock,
toBlock: (i + 1) * blockDenom + fromBlock
},
true
)
resolve(batch)
})
)
const batchEvents = flattenNArray(await Promise.all(promises))
const events = batchEvents.map((e) => ({ ...e.returnValues }))
return events
}
saveEvents = async ({ events, lastSyncBlock, storeName }) => {
@ -96,51 +128,69 @@ class RelayerRegister {
}
}
getENSAddress = async (ensName) => {
const { url } = Object.values(networkConfig.netId1.rpcUrls)[0]
const provider = new Web3(url)
const ensAddress = await provider.eth.ens.getAddress(ensName)
return ensAddress
}
fetchRelayers = async () => {
const blockRange = 10000
// eslint-disable-next-line prefer-const
let { blockFrom, blockTo, cachedEvents } = await this.getCachedData()
let { blockTo, cachedEvents } = await this.getCachedData()
let allRelayers = cachedEvents
if (blockFrom !== blockTo) {
const registeredRelayersEvents = await graph.getAllRegisters(blockFrom)
const currentBlockNumber = await this.provider.getBlockNumber()
const fromBlock = cachedEvents.length === 0 ? REGISTRY_DEPLOYED_BLOCK[1] : blockTo
const blockDifference = currentBlockNumber - fromBlock
let relayers = {
lastSyncBlock: registeredRelayersEvents.lastSyncBlock,
events: registeredRelayersEvents.events.map((el) => ({
ensName: el.ensName,
relayerAddress: toChecksumAddress(el.address)
}))
try {
let toBlock
let registerRelayerEvents
let lastSyncBlock = blockTo
if (cachedEvents.length > 0 || blockDifference === 0) {
return cachedEvents
} else if (blockDifference >= blockRange) {
toBlock = currentBlockNumber
registerRelayerEvents = await this.batchFetchEvents({ fromBlock, toBlock })
lastSyncBlock = toBlock
} else {
toBlock = fromBlock + blockRange
registerRelayerEvents = await this.fetchEvents({ fromBlock, toBlock }, true)
lastSyncBlock = toBlock
}
const isGraphLate = relayers.lastSyncBlock && blockTo > Number(relayers.lastSyncBlock)
const relayerEvents = cachedEvents.concat(registerRelayerEvents || [])
const events = []
if (isGraphLate) {
blockFrom = relayers.lastSyncBlock
}
if (!relayers.events.length || isGraphLate) {
const multicallEvents = await this.fetchEvents(blockFrom, blockTo)
const eventsRelayers = multicallEvents.map(({ returnValues }) => ({
ensName: returnValues.ensName,
relayerAddress: returnValues.relayerAddress
}))
relayers = {
lastSyncBlock: blockTo,
events: relayers.events.concat(eventsRelayers)
for (let x = 0; x < relayerEvents.length; x++) {
const { ensName, relayerAddress } = relayerEvents[x]
let ensAddress
if (!isAddress(relayerAddress)) {
ensAddress = await this.getENSAddress(ensName)
ensAddress = toChecksumAddress(ensAddress)
} else {
ensAddress = relayerAddress
}
events.push({ ensName, relayerAddress: ensAddress })
}
await this.saveEvents({ storeName: 'register_events', ...relayers })
allRelayers = allRelayers.concat(relayers.events)
}
await this.saveEvents({ storeName: 'register_events', lastSyncBlock, events })
allRelayers = allRelayers.concat(events)
} catch (err) {
console.log(err)
}
return allRelayers
}
filterRelayer = (acc, curr, ensSubdomainKey, relayer) => {
const subdomainIndex = subdomains.indexOf(ensSubdomainKey)
const mainnetSubdomain = curr.records[0]
const hostname = curr.records[subdomainIndex]
const isHostWithProtocol = hostname.includes('http')
@ -191,7 +241,6 @@ class RelayerRegister {
getRelayers = async (ensSubdomainKey) => {
const relayers = await this.fetchRelayers()
const validRelayers = await this.getValidRelayers(relayers, ensSubdomainKey)
return validRelayers

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
static/tornado.json.gz Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more