Reviewed-on: https://development.tornadocash.community/tornadocash/classic-ui/pulls/10
This commit is contained in:
commit
90ef7362bf
262 changed files with 403 additions and 674 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -60,6 +60,12 @@ export const cachedEventsLength = {
|
|||
}
|
||||
}
|
||||
|
||||
export const httpConfig = {
|
||||
// buffer for tor connections
|
||||
timeout: 30000,
|
||||
keepAlive: true
|
||||
}
|
||||
|
||||
export const PROVIDERS = {
|
||||
walletConnect: {
|
||||
name: 'WalletConnect',
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -277,7 +277,7 @@ export default async (ctx, inject) => {
|
|||
},
|
||||
{
|
||||
name: `withdrawals_${token}_${amount}`,
|
||||
keyPath: 'transactionHash',
|
||||
keyPath: 'blockNumber',
|
||||
indexes: WITHDRAWAL_INDEXES
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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}`]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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
|
||||
})
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
static/events/deposits_bnb_0.1.json.gz
Normal file
BIN
static/events/deposits_bnb_0.1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_bnb_1.json.gz
Normal file
BIN
static/events/deposits_bnb_1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_bnb_10.json.gz
Normal file
BIN
static/events/deposits_bnb_10.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_bnb_100.json.gz
Normal file
BIN
static/events/deposits_bnb_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_eth_0.1.json.gz
Normal file
BIN
static/events/deposits_eth_0.1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_eth_1.json.gz
Normal file
BIN
static/events/deposits_eth_1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_eth_10.json.gz
Normal file
BIN
static/events/deposits_eth_10.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_eth_100.json.gz
Normal file
BIN
static/events/deposits_eth_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_matic_100.json.gz
Normal file
BIN
static/events/deposits_matic_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_matic_1000.json.gz
Normal file
BIN
static/events/deposits_matic_1000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_matic_10000.json.gz
Normal file
BIN
static/events/deposits_matic_10000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_matic_100000.json.gz
Normal file
BIN
static/events/deposits_matic_100000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_xdai_100.json.gz
Normal file
BIN
static/events/deposits_xdai_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_xdai_1000.json.gz
Normal file
BIN
static/events/deposits_xdai_1000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_xdai_10000.json.gz
Normal file
BIN
static/events/deposits_xdai_10000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/deposits_xdai_100000.json.gz
Normal file
BIN
static/events/deposits_xdai_100000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/encrypted_notes_1.json.gz
Normal file
BIN
static/events/encrypted_notes_1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/encrypted_notes_100.json.gz
Normal file
BIN
static/events/encrypted_notes_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/encrypted_notes_137.json.gz
Normal file
BIN
static/events/encrypted_notes_137.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/encrypted_notes_5.json.gz
Normal file
BIN
static/events/encrypted_notes_5.json.gz
Normal file
Binary file not shown.
BIN
static/events/encrypted_notes_56.json.gz
Normal file
BIN
static/events/encrypted_notes_56.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_bnb_0.1.json.gz
Normal file
BIN
static/events/withdrawals_bnb_0.1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_bnb_1.json.gz
Normal file
BIN
static/events/withdrawals_bnb_1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_bnb_10.json.gz
Normal file
BIN
static/events/withdrawals_bnb_10.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_bnb_100.json.gz
Normal file
BIN
static/events/withdrawals_bnb_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_eth_0.1.json.gz
Normal file
BIN
static/events/withdrawals_eth_0.1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_eth_1.json.gz
Normal file
BIN
static/events/withdrawals_eth_1.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_eth_10.json.gz
Normal file
BIN
static/events/withdrawals_eth_10.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_eth_100.json.gz
Normal file
BIN
static/events/withdrawals_eth_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_matic_100.json.gz
Normal file
BIN
static/events/withdrawals_matic_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_matic_1000.json.gz
Normal file
BIN
static/events/withdrawals_matic_1000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_matic_10000.json.gz
Normal file
BIN
static/events/withdrawals_matic_10000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_matic_100000.json.gz
Normal file
BIN
static/events/withdrawals_matic_100000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_xdai_100.json.gz
Normal file
BIN
static/events/withdrawals_xdai_100.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_xdai_1000.json.gz
Normal file
BIN
static/events/withdrawals_xdai_1000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_xdai_10000.json.gz
Normal file
BIN
static/events/withdrawals_xdai_10000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/events/withdrawals_xdai_100000.json.gz
Normal file
BIN
static/events/withdrawals_xdai_100000.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/tornado.json.gz
Normal file
BIN
static/tornado.json.gz
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
static/trees/deposits_bnb_0.1_bloom.json.gz
Normal file
BIN
static/trees/deposits_bnb_0.1_bloom.json.gz
Normal file
Binary file not shown.
Binary file not shown.
BIN
static/trees/deposits_bnb_0.1_slice1.json.gz
Normal file
BIN
static/trees/deposits_bnb_0.1_slice1.json.gz
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue