Fetch events from API and remove external fee oracles #37

Open
tornadocontrib wants to merge 1 commits from tornadocontrib/classic-ui:development into development
30 changed files with 480 additions and 189 deletions

View File

@ -1,7 +1,7 @@
PINATA_API_KEY=
PINATA_SECRET_API_KEY=
CHAINNODES_KEY=
DEFAULT_RPC=
WC_BRIDGE=

View File

@ -32,7 +32,6 @@ jobs:
- name: Build
run: yarn generate
env:
CHAINNODES_KEY: ${{ secrets.CHAINNODES_KEY }}
WC_BRIDGE: ${{ secrets.WC_BRIDGE }}
OLD_STORE_NAME: ${{ secrets.OLD_STORE_NAME }}
STORE_NAME: ${{ secrets.STORE_NAME }}

View File

@ -18,9 +18,9 @@
</template>
<template v-slot:description>{{ notice.description }}</template>
</i18n>
<a v-if="notice.nova" href="https://nova.tornado.ws/" target="_blank" rel="noopener noreferrer">
<!-- a v-if="notice.nova" href="https://nova.tornado.ws/" target="_blank" rel="noopener noreferrer">
Tornado Cash Nova
</a>
</a -->
<a
v-if="notice.txHash"
:href="txExplorerUrl(notice.txHash)"

View File

@ -4,10 +4,7 @@
<div class="box-modal-title">{{ $t('withdrawalSettings') }}</div>
<button type="button" class="delete" @click="$parent.cancel('escape')" />
</header>
<b-tabs v-if="isRelayersAvailable" v-model="withdrawType" :animated="false" class="is-modal">
<RelayerTab />
</b-tabs>
<b-tabs v-else v-model="withdrawType" :animated="false" class="is-modal">
<b-tabs v-model="withdrawType" :animated="false" class="is-modal">
<RelayerTab />
<WalletTab />
</b-tabs>
@ -49,11 +46,7 @@ export default {
computed: {
...mapState('application', {
defaultWithdrawType: 'withdrawType'
}),
...mapState('relayer', ['isLoadingRelayers', 'validRelayers']),
isRelayersAvailable() {
return !this.isLoadingRelayers && this.validRelayers.length > 0
}
})
},
created() {
this.withdrawType = this.defaultWithdrawType

View File

@ -200,7 +200,7 @@ export default {
return false
},
shouldSettingsShow() {
return !this.isLoading && !this.error.type && !this.hasErrorNote
return !this.hasErrorNote && !this.error.message
},
hasErrorNote() {
const note = this.withdrawNote.split('-')[4]

View File

@ -64,7 +64,7 @@ export default {
},
created() {
this.checkRecoveryKey()
this.newNotify()
// this.newNotify()
this.$store.dispatch('fees/setDefaultGasPrice')
},
mounted() {
@ -125,6 +125,7 @@ export default {
width: 440
})
},
/**
newNotify() {
const hasNotify = window.localStorage.getItem('hasNotify')
@ -144,6 +145,7 @@ export default {
window.localStorage.setItem('hasNotify', true)
}
},
**/
handleOpenModal() {
const recoveryKey = this.$sessionStorage.getItem(this.accounts.encrypt)
if (recoveryKey) {

View File

@ -53,7 +53,7 @@ export async function _encryptFormatTx({ dispatch, getters, rootGetters }, { eve
return getDeposit({ event, netId, service, instance })
})
const proceedDeposits = await Promise.all(depositPromises)
const proceedDeposits = (await Promise.all(depositPromises)).filter((d) => d)
console.log({ proceedDeposits })
dispatch(

View File

@ -1,4 +1,4 @@
import { graph } from '@/services'
import { fetchEvents } from '@/services'
import networkConfig from '@/networkConfig'
function createMutation({ commit, rootState }, { type, payload }) {
@ -16,7 +16,13 @@ function clearState({ dispatch }, { key }) {
async function getEventsFromBlockPart({ echoContract, address, currentBlockNumber, netId }) {
try {
const { events: graphEvents, lastSyncBlock } = await graph.getNoteAccounts({ address, netId })
// const { events: graphEvents, lastSyncBlock } = await graph.getNoteAccounts({ address, netId })
const { events: allEvents, lastSyncBlock } = await fetchEvents({
type: 'echo',
netId
})
const graphEvents = allEvents.filter((el) => address === el.address)
if (graphEvents.length) {
return graphEvents

View File

@ -25,10 +25,6 @@ export default {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/mainnet'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
mevblockerRPC: {
name: 'MevblockerRPC',
url: 'https://rpc.mevblocker.io'
@ -38,7 +34,7 @@ export default {
url: 'https://1rpc.io/eth'
}
},
multicall: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
registryContract: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2',
echoContractAccount: '0x9B27DD5Bb15d42DC224FCD0B7caEbBe16161Df42',
@ -145,17 +141,13 @@ export default {
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Binance Smart Chain',
deployedBlock: 8158799,
multicall: '0x41263cba59eb80dc200f3e2544eda4ed6a90e76c',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/bsc'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://bsc-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRPC: {
name: '1RPC',
url: 'https://1rpc.io/bnb'
@ -171,6 +163,32 @@ export default {
},
symbol: 'BNB',
decimals: 18
},
usdt: {
instanceAddress: {
'10': '0x261fB4f84bb0BdEe7E035B6a8a08e5c35AdacdDD',
'100': '0x3957861d4897d883C9b944C0b4E22bBd0DDE6e21',
'1000': '0x6D180403AdFb39F70983eB51A033C5e52eb9BB69',
'10000': '0x3722662D8AaB07B216B14C02eF0ee940d14A4200'
},
instanceApproval: true,
tokenAddress: '0x55d398326f99059fF775485246999027B3197955',
symbol: 'USDT',
decimals: 18,
gasLimit: '700000'
},
btcb: {
instanceAddress: {
'0.0001': '0x736dABbFc8101Ae75287104eCcf67e45D7369Ae1',
'0.001': '0x82c7Ce6f1F158cEC5536d591a2BC19864b3CA823',
'0.01': '0x8284c96679037d8081E498d8F767cA5a140BFAAf',
'0.1': '0x2bcD128Ce23ee30Ee945E613ff129c4DE1102C79'
},
instanceApproval: true,
tokenAddress: '0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c',
symbol: 'BTCB',
decimals: 18,
gasLimit: '700000'
}
},
ensSubdomainKey: 'bsc-tornado',
@ -200,13 +218,9 @@ export default {
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Polygon (Matic) Network',
deployedBlock: 16257962,
multicall: '0x11ce4B23bD875D7F5C6a31084f55fDe1e9A87507',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://polygon-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/matic'
@ -251,18 +265,10 @@ export default {
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Optimism',
deployedBlock: 2243689,
multicall: '0x35A6Cdb2C9AD4a45112df4a04147EB07dFA01aB7',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/op'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/op'
@ -307,17 +313,13 @@ export default {
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Arbitrum One',
deployedBlock: 3430648,
multicall: '0x842eC2c7D803033Edf55E478F461FC547Bc54EB2',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/arbitrum'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
oneRpc: {
name: '1rpc',
url: 'https://1rpc.io/arb'
@ -366,17 +368,13 @@ export default {
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Gnosis Chain',
deployedBlock: 17754561,
multicall: '0xb5b692a88bdfc81ca69dcb1d924f59f0413a602a',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
tornadoRPC: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/gnosis'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://gnosis-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
},
blockPi: {
name: 'BlockPi',
url: 'https://gnosis.blockpi.network/v1/rpc/public'
@ -421,7 +419,7 @@ export default {
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Avalanche Mainnet',
deployedBlock: 4429818,
multicall: '0xe86e3989c74293Acc962156cd3F525c07b6a1B6e',
multicall: '0xcA11bde05977b3631167028862bE2a173976CA11',
echoContractAccount: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
rpcUrls: {
publicRpc: {
@ -486,10 +484,6 @@ export default {
sepolia: {
name: 'Sepolia RPC',
url: 'https://rpc.sepolia.org'
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://sepolia.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607'
}
},
tokens: {

View File

@ -187,7 +187,7 @@ export default {
new webpack.IgnorePlugin(/worker_threads/),
new webpack.DefinePlugin({
'process.env': JSON.stringify({
CHAINNODES_KEY: process.env.CHAINNODES_KEY,
DEFAULT_RPC: process.env.DEFAULT_RPC,
WC_BRIDGE: process.env.WC_BRIDGE,
OLD_STORE_NAME: process.env.OLD_STORE_NAME,
STORE_NAME: process.env.STORE_NAME,
@ -225,7 +225,7 @@ export default {
},
provider: {
rpcUrl: `https://mainnet.chainnodes.org/${process.env.CHAINNODES_KEY}`
rpcUrl: process.env.DEFAULT_RPC || 'https://tornadocash-rpc.com'
},
// todo make custom loading page

View File

@ -30,7 +30,7 @@
"@nuxtjs/moment": "^1.6.0",
"@tornado/fixed-merkle-tree": "0.7",
"@tornado/snarkjs": "0.1.20",
"@tornado/tornado-oracles": "^2.1.0",
"@tornado/tornado-oracles": "git+https://git.tornado.ws/tornadocontrib/tornado-oracles.git#c9f43ff29266c48dc030a3b66e818201ff4cfc8d",
"@tornado/websnark": "0.0.4",
"@walletconnect/web3-provider": "1.7.8",
"ajv": "^6.10.2",

View File

@ -314,7 +314,7 @@ export default {
const { timestamp, txHash, isSpent } = event
const receipt = await this.getTransactionReceipt(txHash)
const from = event.from || (await this.getTransactionReceipt(txHash)).from
const { nullifierHex, commitmentHex } = parseNote(withdrawNote)
@ -324,19 +324,23 @@ export default {
isSpent,
txHash,
timestamp,
from: receipt.from,
from,
commitment: commitmentHex
}
if (isSpent) {
const { withdrawalBlock, txHash, to, fee, amount } = await this.$store.dispatch(
'application/loadWithdrawalData',
{
withdrawNote
}
)
const {
withdrawalBlock,
txHash,
to,
fee,
amount,
timestamp: eventTimestamp
} = await this.$store.dispatch('application/loadWithdrawalData', {
withdrawNote
})
const { timestamp } = await this.getBlock(withdrawalBlock)
const timestamp = eventTimestamp || (await this.getBlock(withdrawalBlock)).timestamp
this.txWithdrawalInfo = {
amount,

28
relayers.json Normal file
View File

@ -0,0 +1,28 @@
[
{
"ensName": "tornadowithdraw.eth",
"hostnames": {
"bsc-tornado": "tornadowithdraw.com/56",
"polygon-tornado": "tornadowithdraw.com/137",
"optimism-tornado": "tornadowithdraw.com/10",
"arbitrum-tornado": "tornadowithdraw.com/42161",
"gnosis-tornado": "tornadowithdraw.com/100",
"avalanche-tornado": "tornadowithdraw.com/43114",
"sepolia-tornado": "tornadowithdraw.com/11155111"
},
"relayerAddress": "0x40c3d1656a26C9266f4A10fed0D87EFf79F54E64"
},
{
"ensName": "rpc.tornadowithdraw.eth",
"hostnames": {
"bsc-tornado": "tornadocash-rpc.com/56",
"polygon-tornado": "tornadocash-rpc.com/137",
"optimism-tornado": "tornadocash-rpc.com/10",
"arbitrum-tornado": "tornadocash-rpc.com/42161",
"gnosis-tornado": "tornadocash-rpc.com/100",
"avalanche-tornado": "tornadocash-rpc.com/43114",
"sepolia-tornado": "tornadocash-rpc.com/11155111"
},
"relayerAddress": "0xFF787B7A5cd8a88508361E3B7bcE791Aa2796526"
}
]

View File

@ -37,7 +37,7 @@ export function loadCachedEvents({ name, directory, deployedBlock }) {
export async function getPastEvents({ type, fromBlock, netId, events, contractAttrs }) {
let downloadedEvents = events
let [{ url: rpcUrl }] = Object.values(networkConfig[`netId${netId}`].rpcUrls)
const [{ url: rpcUrl }] = Object.values(networkConfig[`netId${netId}`].rpcUrls)
const provider = new Web3.providers.HttpProvider(rpcUrl)
const web3 = new Web3(provider)
@ -49,7 +49,7 @@ export async function getPastEvents({ type, fromBlock, netId, events, contractAt
const blockDifference = Math.ceil(blockNumberBuffer - fromBlock)
// eth_logs and eth_filter are restricted > 10,000 block queries
const blockRange = blockSyncInterval ? blockSyncInterval : 10_000
const blockRange = blockSyncInterval || 10_000
let chunksCount = blockDifference === 0 ? 1 : Math.ceil(blockDifference / blockRange)
const chunkSize = Math.ceil(blockDifference / chunksCount)

View File

@ -86,7 +86,7 @@ async function main(netId, chosenToken, chosenEvent) {
let events = await getPastEvents({
type: eventName,
fromBlock: cachedEvents.lastBlock + 1,
netId: netId,
netId,
events: [],
contractAttrs: [ABI, address]
})

View File

@ -1,6 +1,6 @@
/**
* Manually patch vuex to support Node.js >= 18.x
*
*
* See issue https://github.com/vuejs/vuex/issues/2160
* https://github.com/vuejs/vuex/commit/397e9fba45c8b4ec0c4a33d2578e34829bd348d7
*/
@ -17,7 +17,6 @@ if (!pkgJson.exports['./*']) {
changes = true
}
if (changes) {
fs.writeFileSync('./node_modules/vuex/package.backup.json', backupJson + '\n')
fs.writeFileSync('./node_modules/vuex/package.json', JSON.stringify(pkgJson, null, 2) + '\n')

82
services/eventApi.js Normal file
View File

@ -0,0 +1,82 @@
// Maximum of 5K events can be returned from the single query (to prevent DDOS)
const first = 5000
const EVENT_API_ROOT = 'https://tornadocash-rpc.com'
export async function fetchEvents({ netId = 1, type, currency, amount, fromBlock = 0, recent }) {
try {
const url = `${EVENT_API_ROOT}/${netId}/events`
const events = []
let lastSyncBlock = fromBlock
// Iterate if we have more than 4.9K events
while (true) {
const resp = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
type,
currency,
amount,
fromBlock,
recent
})
})
if (!resp.ok) {
const errMsg = `${url} failed with error ${resp.status}: ${await resp.text()}`
throw new Error(errMsg)
}
// eslint-disable-next-line prefer-const
let { events: fetchedEvents, lastSyncBlock: currentBlock } = await resp.json()
if (recent) {
return {
events: fetchedEvents,
lastSyncBlock: currentBlock
}
}
lastSyncBlock = currentBlock
if (!Array.isArray(fetchedEvents) || !fetchedEvents.length) {
break
}
fetchedEvents = fetchedEvents.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex
}
return a.blockNumber - b.blockNumber
})
const [lastEvent] = fetchedEvents.slice(-1)
if (fetchedEvents.length < first - 100) {
events.push(...fetchedEvents)
break
}
fetchedEvents = fetchedEvents.filter((e) => e.blockNumber !== lastEvent.blockNumber)
fromBlock = Number(lastEvent.blockNumber)
events.push(...fetchedEvents)
}
return {
events,
lastSyncBlock
}
} catch (err) {
console.log('Error from events api')
console.log(err)
return {
events: [],
lastSyncBlock: fromBlock
}
}
}

View File

@ -1,6 +1,7 @@
import Web3 from 'web3'
import graph from '@/services/graph'
// import graph from '@/services/graph'
import { fetchEvents } from '@/services'
import { download } from '@/store/snark'
import networkConfig, { enabledChains, blockSyncInterval } from '@/networkConfig'
import InstanceABI from '@/abis/Instance.abi.json'
@ -220,12 +221,27 @@ class EventService {
async getEventsFromGraph({ fromBlock, methodName }) {
try {
/**
const { events, lastSyncBlock } = await graph[methodName]({
fromBlock,
netId: this.netId,
amount: this.amount,
currency: this.currency
})
**/
methodName = methodName.substr(6)
methodName = methodName.substr(0, methodName.length - 1).toLowerCase()
const { events, lastSyncBlock } = await fetchEvents({
fromBlock,
netId: this.netId,
amount: this.amount,
currency: this.currency,
type: methodName
})
return {
events,
lastBlock: lastSyncBlock

View File

@ -10,6 +10,7 @@ export { default as graph } from './graph'
export { default as schema } from './schema'
export { default as walletConnectConnector } from './walletConnect'
export * from './lookupAddress'
export * from './eventApi'
// eslint-disable-next-line no-undef
window.graph = graph

View File

@ -3,13 +3,14 @@ import namehash from 'eth-ens-namehash'
import { BigNumber as BN } from 'bignumber.js'
import { toChecksumAddress, isAddress } from 'web3-utils'
import { graph } from '@/services'
import { fetchEvents } 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'
import STATIC_RELAYER from '@/relayers.json'
const MIN_STAKE_BALANCE = '0X1B1AE4D6E2EF500000' // 500 TORN
@ -174,13 +175,20 @@ class RelayerRegister {
let allRelayers = cachedEvents
if (!cachedEvents || !cachedEvents.length) {
const { lastSyncBlock, events } = await graph.getAllRegisters(blockFrom)
// const { lastSyncBlock, events } = await graph.getAllRegisters(blockFrom)
const { events: apiEvents, lastSyncBlock } = await fetchEvents({
type: 'registry',
netId: 1,
fromBlock: blockFrom
})
const events = apiEvents.filter(({ event }) => event === 'RelayerRegistered')
if (events.length) {
blockTo = lastSyncBlock + 1
cachedEvents = events.map((el) => ({
ensName: el.ensName,
relayerAddress: toChecksumAddress(el.address)
relayerAddress: toChecksumAddress(el.relayerAddress)
}))
}
}
@ -240,6 +248,7 @@ class RelayerRegister {
const isOwner = relayer.relayerAddress === curr.owner
const hasMinBalance = new BN(curr.balance).gte(MIN_STAKE_BALANCE)
const hasRelayer = acc.find(({ ensName }) => ensName === relayer.ensName)
if (
hostname &&
@ -247,7 +256,8 @@ class RelayerRegister {
mainnetSubdomain &&
curr.isRegistered &&
hasMinBalance &&
!isHostWithProtocol
!isHostWithProtocol &&
!hasRelayer
) {
acc.push({
hostname,
@ -279,7 +289,18 @@ class RelayerRegister {
[]
)
return validRelayers
const staticRelayers = STATIC_RELAYER.reduce((acc, relayer) => {
if (relayer.hostnames[ensSubdomainKey]) {
acc.push({
...relayer,
hostname: relayer.hostnames[ensSubdomainKey],
stakeBalance: MIN_STAKE_BALANCE
})
}
return acc
}, [])
return [...staticRelayers, ...validRelayers]
}
getRelayers = async (ensSubdomainKey) => {

View File

@ -32,6 +32,9 @@ function getRelayerValidateFunction(netId) {
case 42161:
return ajv.getSchema('l2Relayer')
case 11155111:
return ajv.getSchema('sepoliaRelayer')
default:
return ajv.getSchema('defaultRelayer')
}

View File

@ -5,11 +5,15 @@ import { statusSchema as defaultRelayer } from './default'
import { statusSchema as polygonRelayer } from './polygon'
import { statusSchema as avalancheRelayer } from './avalanche'
const sepoliaRelayer = JSON.parse(JSON.stringify(defaultRelayer))
sepoliaRelayer.properties.ethPrices.required = ['dai']
export default {
l2Relayer,
bscRelayer,
xdaiRelayer,
defaultRelayer,
polygonRelayer,
avalancheRelayer
avalancheRelayer,
sepoliaRelayer
}

View File

@ -9,7 +9,7 @@ import MulticallABI from '@/abis/Multicall.json'
import InstanceABI from '@/abis/Instance.abi.json'
import TornadoProxyABI from '@/abis/TornadoProxy.abi.json'
import { graph, treesInterface, EventsFactory } from '@/services'
import { treesInterface, EventsFactory, fetchEvents } from '@/services'
import {
randomBN,
@ -233,8 +233,15 @@ const actions = {
const netId = rootGetters['metamask/netId']
const { currency, amount } = state.selectedStatistic
const eventService = getters.eventsInterface.getService({ netId, amount, currency })
const graphEvents = await eventService.getEventsFromGraph({ methodName: 'getStatistic' })
// const eventService = getters.eventsInterface.getService({ netId, amount, currency })
// const graphEvents = await eventService.getEventsFromGraph({ methodName: 'getStatistic' })
const graphEvents = await fetchEvents({
netId,
type: 'deposit',
currency,
amount,
recent: true
})
let statistic = graphEvents?.events
@ -435,10 +442,17 @@ const actions = {
let events = []
/**
const { events: graphEvents, lastSyncBlock } = await graph.getAllEncryptedNotes({
netId,
fromBlock: deployedBlock
})
**/
const { events: graphEvents, lastSyncBlock } = await fetchEvents({
netId,
type: 'encrypted_notes',
fromBlock: deployedBlock
})
if (lastSyncBlock) {
deployedBlock = lastSyncBlock
@ -869,7 +883,8 @@ const actions = {
timestamp: lastEvent.timestamp,
leafIndex: lastEvent.leafIndex,
txHash: lastEvent.transactionHash,
depositBlock: lastEvent.blockNumber
depositBlock: lastEvent.blockNumber,
from: lastEvent.from
}
}
} catch (err) {
@ -893,7 +908,8 @@ const actions = {
to: lastEvent.to,
fee: lastEvent.fee,
txHash: lastEvent.transactionHash,
blockNumber: lastEvent.blockNumber
blockNumber: lastEvent.blockNumber,
timestamp: lastEvent.timestamp
}
}
} catch (err) {

View File

@ -1,6 +1,6 @@
/* eslint-disable no-console */
import { toWei, fromWei, toBN } from 'web3-utils'
import { TornadoFeeOracleV4, TornadoFeeOracleV5 } from '@tornado/tornado-oracles'
import { TornadoFeeOracleV6 } from '@tornado/tornado-oracles'
export const state = () => {
return {
@ -14,12 +14,12 @@ export const getters = {
oracle: (state, getters, rootState, rootGetters) => {
const netId = Number(rootGetters['metamask/netId'])
const { url: rpcUrl } = rootState.settings[`netId${netId}`].rpc
const { gasPrices } = rootGetters['metamask/networkConfig']
// Compatibility with old BSC relayers
const overrideGasPrice = netId === 56 ? { gasPrice: toWei('3.3', 'gwei').toString() } : undefined
// Return old oracle for backwards compatibility, if chain is ETH Mainnet
return netId === 1
? new TornadoFeeOracleV4(netId, rpcUrl, gasPrices)
: new TornadoFeeOracleV5(netId, rpcUrl, gasPrices)
return new TornadoFeeOracleV6(netId, rpcUrl, overrideGasPrice)
},
getGasPriceParams: (state) => {
return state.gasPriceParams
@ -77,16 +77,16 @@ export const actions = {
if (currency !== nativeCurrency)
await dispatch('application/setDefaultEthToReceive', { currency }, { root: true })
const withdrawalFee = await getters.oracle.calculateWithdrawalFeeViaRelayer(
'user_withdrawal',
const withdrawalFee = await getters.oracle.calculateWithdrawalFeeViaRelayer({
tx,
feePercent,
currency.toLowerCase(),
txType: 'user_withdrawal',
relayerFeePercent: feePercent,
currency: currency.toLowerCase(),
amount,
decimals,
rootState.application.ethToReceive || 0,
rootState.price.prices[currency.toLowerCase()]
)
refundInEth: rootState.application.ethToReceive || 0,
tokenPriceInEth: rootState.price.prices[currency.toLowerCase()]
})
commit('SAVE_WITHDRAWAL_FEE_VIA_RELAYER', toBN(withdrawalFee))
}

View File

@ -12,6 +12,8 @@ import AggregatorABI from '@/abis/Aggregator.abi.json'
import { httpConfig } from '@/constants'
import { fetchEvents } from '@/services'
const { numberToHex, toWei, fromWei, toBN, hexToNumber, hexToNumberString } = require('web3-utils')
const state = () => {
@ -682,10 +684,26 @@ const actions = {
}
const [events, statuses] = await Promise.all([
govInstance.getPastEvents('ProposalCreated', {
fromBlock: config.constants.GOVERNANCE_BLOCK,
toBlock: 'latest'
}),
(async () => {
try {
const { events } = await fetchEvents({
netId,
type: 'governance',
fromBlock: config.constants.GOVERNANCE_BLOCK
})
if (!events?.length) {
throw new Error('0 length')
}
return events.filter((e) => e.event === 'ProposalCreated')
} catch {
return govInstance.getPastEvents('ProposalCreated', {
fromBlock: config.constants.GOVERNANCE_BLOCK,
toBlock: 'latest'
})
}
})(),
aggregatorContract.methods.getAllProposals(govInstance._address).call()
])
@ -735,17 +753,41 @@ const actions = {
}
proposals = events
.map(({ returnValues, blockNumber }, index) => {
const id = Number(returnValues.id)
.map((event, index) => {
const { returnValues, blockNumber } = event
if (returnValues) {
const id = Number(returnValues.id)
const { state, startTime, endTime, forVotes, againstVotes } = statuses[index]
const { title, description } = parseDescription({ id, text: returnValues.description })
return {
id,
title,
description,
target: returnValues.target,
proposer: returnValues.proposer,
endTime: Number(endTime),
startTime: Number(startTime),
status: ProposalState[Number(state)],
blockNumber,
results: {
for: fromWei(forVotes),
against: fromWei(againstVotes)
}
}
}
const id = event.id
const { state, startTime, endTime, forVotes, againstVotes } = statuses[index]
const { title, description } = parseDescription({ id, text: returnValues.description })
const { title, description } = parseDescription({ id, text: event.description })
return {
id,
title,
description,
target: returnValues.target,
proposer: returnValues.proposer,
target: event.target,
proposer: event.proposer,
endTime: Number(endTime),
startTime: Number(startTime),
status: ProposalState[Number(state)],
@ -844,22 +886,44 @@ const actions = {
const aggregatorContract = getters.aggregatorContract
const govInstance = getters.govContract({ netId })
let delegatedAccs = await govInstance.getPastEvents('Delegated', {
filter: {
to: ethAccount
},
fromBlock: config.constants.GOVERNANCE_BLOCK,
toBlock: 'latest'
})
let undelegatedAccs = await govInstance.getPastEvents('Undelegated', {
filter: {
from: ethAccount
},
fromBlock: config.constants.GOVERNANCE_BLOCK,
toBlock: 'latest'
})
delegatedAccs = delegatedAccs.map((acc) => acc.returnValues.account)
undelegatedAccs = undelegatedAccs.map((acc) => acc.returnValues.account)
let delegatedAccs, undelegatedAccs
try {
const { events } = await fetchEvents({
netId,
type: 'governance',
fromBlock: config.constants.GOVERNANCE_BLOCK
})
if (!events?.length) {
throw new Error('0 length')
}
delegatedAccs = events
.filter((e) => e.event === 'Delegated' && e.delegateTo === ethAccount)
.map((e) => e.account)
undelegatedAccs = events
.filter((e) => e.event === 'Undelegated' && e.delegateFrom === ethAccount)
.map((e) => e.account)
} catch {
delegatedAccs = await govInstance.getPastEvents('Delegated', {
filter: {
to: ethAccount
},
fromBlock: config.constants.GOVERNANCE_BLOCK,
toBlock: 'latest'
})
undelegatedAccs = await govInstance.getPastEvents('Undelegated', {
filter: {
from: ethAccount
},
fromBlock: config.constants.GOVERNANCE_BLOCK,
toBlock: 'latest'
})
delegatedAccs = delegatedAccs.map((acc) => acc.returnValues.account)
undelegatedAccs = undelegatedAccs.map((acc) => acc.returnValues.account)
}
const uniq = delegatedAccs.filter((obj, index, self) => {
const indexUndelegated = undelegatedAccs.indexOf(obj)
if (indexUndelegated !== -1) {

View File

@ -5,7 +5,7 @@ import { utils } from 'ethers'
import uniqBy from 'lodash/uniqBy'
import chunk from 'lodash/chunk'
import { lookupAddresses, createBatchRequestCallback } from '@/services'
import { lookupAddresses, createBatchRequestCallback, fetchEvents } from '@/services'
import { CHUNK_COUNT_PER_BATCH_REQUEST } from '@/constants'
const { toWei, fromWei, toBN } = require('web3-utils')
@ -36,21 +36,50 @@ const parseComment = (calldata, govInstance) => {
const createProposalComment = (resultAll, votedEvent) => {
const { transactionHash, returnValues, blockNumber } = votedEvent
const { voter } = returnValues
if (returnValues) {
const { voter } = returnValues
const comment = parseComment()
const percentage =
toBN(votedEvent.returnValues.votes)
.mul(toBN(10000))
.divRound(resultAll)
.toNumber() / 100
return {
id: `${transactionHash}-${voter}`,
percentage,
...returnValues,
votes: fromWei(returnValues.votes),
transactionHash,
blockNumber,
...comment,
ens: {
delegator: null,
voter: null
},
delegator: null,
timestamp: null
}
}
const comment = parseComment()
const percentage =
toBN(votedEvent.returnValues.votes)
toBN(votedEvent.votes)
.mul(toBN(10000))
.divRound(resultAll)
.toNumber() / 100
return {
id: `${transactionHash}-${voter}`,
id: `${transactionHash}-${votedEvent.voter}`,
percentage,
...returnValues,
votes: fromWei(returnValues.votes),
...votedEvent,
votes: fromWei(votedEvent.votes),
transactionHash,
blockNumber,
@ -186,21 +215,45 @@ const actions = {
}
try {
let votedEvents = await govInstance.getPastEvents('Voted', {
filter: {
// support: [false],
proposalId: proposal.id
},
fromBlock,
toBlock: 'latest'
})
let votedEvents
console.log('fetchVotedEvents', votedEvents.length)
try {
const { events } = await fetchEvents({
netId,
type: 'governance'
})
votedEvents = votedEvents.sort((a, b) => b.blockNumber - a.blockNumber)
votedEvents = uniqBy(votedEvents, 'returnValues.voter')
if (!events?.length) {
throw new Error('0 length')
}
console.log('fetchVotedEvents uniq', votedEvents.length)
votedEvents = events.filter(
({ event, proposalId }) => event === 'Voted' && proposalId === proposal.id
)
console.log('fetchVotedEvents', votedEvents.length)
votedEvents = votedEvents.sort((a, b) => b.blockNumber - a.blockNumber)
votedEvents = uniqBy(votedEvents, 'voter')
console.log('fetchVotedEvents uniq', votedEvents.length)
} catch {
votedEvents = await govInstance.getPastEvents('Voted', {
filter: {
// support: [false],
proposalId: proposal.id
},
fromBlock,
toBlock: 'latest'
})
console.log('fetchVotedEvents', votedEvents.length)
votedEvents = votedEvents.sort((a, b) => b.blockNumber - a.blockNumber)
votedEvents = uniqBy(votedEvents, 'returnValues.voter')
console.log('fetchVotedEvents uniq', votedEvents.length)
}
const resultAll = toBN(toWei(proposal.results.for)).add(toBN(toWei(proposal.results.against)))
let newComments = votedEvents.map((votedEvent) => createProposalComment(resultAll, votedEvent))

View File

@ -19,7 +19,7 @@ export const getters = {
const netId = Number(rootGetters['metamask/netId'])
const { url: rpcUrl } = rootState.settings[`netId${netId}`].rpc
return new TokenPriceOracle(rpcUrl)
return new TokenPriceOracle(rpcUrl, undefined, undefined, netId)
},
tokenRate: (state, getters, rootState) => {
return state.prices[rootState.application.selectedStatistic.currency]
@ -44,13 +44,34 @@ export const mutations = {
}
export const actions = {
async fetchTokenPrice({ getters, commit, dispatch, rootState }) {
async fetchTokenPrice({ rootGetters, getters, commit, dispatch, rootState }) {
if (getters.isPriceWatcherDisabled) {
return
}
const config = rootGetters['metamask/networkConfig']
const tokens = Object.entries(config.tokens).reduce((acc, [symbol, { tokenAddress, decimals }]) => {
if (tokenAddress) {
acc.push({
tokenAddress,
symbol,
decimals
})
}
return acc
}, [])
if (config['torn.contract.tornadocash.eth']) {
tokens.push({
tokenAddress: config['torn.contract.tornadocash.eth'],
symbol: 'torn',
decimals: 18
})
}
try {
const prices = await getters.priceOracle.fetchPrices()
const prices = await getters.priceOracle.fetchPrices(tokens)
console.log('prices', prices)
commit('SAVE_TOKEN_PRICES', prices)

View File

@ -5,7 +5,7 @@ import namehash from 'eth-ens-namehash'
import { httpConfig } from '@/constants'
import { schema, relayerRegisterService } from '@/services'
import { createChainIdState, parseNote, parseSemanticVersion } from '@/utils'
import { createChainIdState, parseNote } from '@/utils'
import ENSABI from '@/abis/ENS.abi.json'
import networkConfig from '@/networkConfig'
@ -43,7 +43,6 @@ const pickWeightedRandomRelayer = (items, netId) => {
if (netId !== 1) {
minFee = 0.01
maxFee = 0.3
}
const weightsScores = items.map((el) => calculateScore(el, minFee, maxFee))
@ -196,7 +195,7 @@ export const mutations = {
export const actions = {
async askRelayerStatus(
{ rootState, dispatch, rootGetters },
{ hostname, relayerAddress, stakeBalance, ensName }
{ url, hostname, relayerAddress, stakeBalance, ensName }
) {
try {
const axios = await getAxios()
@ -205,7 +204,10 @@ export const actions = {
hostname += '/'
}
const url = `${window.location.protocol}//${hostname}`
if (!url) {
url = `${window.location.protocol}//${hostname}`
}
const reqConfig = {
headers: {
'Content-Type': 'application/json, application/x-www-form-urlencoded'
@ -240,6 +242,7 @@ export const actions = {
throw new Error(this.app.i18n.t('canNotFetchStatusFromTheRelayer'))
}
/**
const isRelayerUpdated = () => {
const relayerVersion = response.data.version
@ -255,6 +258,7 @@ export const actions = {
if (!isRelayerUpdated()) {
throw new Error('Outdated version.')
}
**/
return {
isValid,
@ -335,10 +339,11 @@ export const actions = {
async getKnownRelayerData({ rootGetters, getters }, { relayerAddress, name }) {
const { ensSubdomainKey } = rootGetters['metamask/networkConfig']
const [validRelayer] = await relayerRegisterService(getters.ethProvider).getValidRelayers(
const validRelayers = await relayerRegisterService(getters.ethProvider).getValidRelayers(
[{ relayerAddress, ensName: name.replace(`${ensSubdomainKey}.`, '') }],
ensSubdomainKey
)
const validRelayer = validRelayers.find((r) => r.relayerAddress === relayerAddress)
console.warn('validRelayer', validRelayer)
return validRelayer
},
@ -378,7 +383,7 @@ export const actions = {
const hostname = urlParser.host
return { hostname, ensName, stakeBalance: 0 }
return { url, hostname, ensName, stakeBalance: 0 }
},
async getRelayerData({ state, dispatch }, { url, name }) {
const knownRelayer = state.validRelayers.find((el) => el.name === name)

View File

@ -149,8 +149,12 @@ export const actions = {
async approve({ rootState, getters, dispatch, rootGetters, state }) {
try {
const netId = rootGetters['metamask/netId']
const { currency } = rootState.application.selectedInstance
const { decimals } = rootGetters['metamask/networkConfig'].tokens[currency]
const { currency, amount } = rootState.application.selectedInstance
const {
decimals,
instanceAddress: { [amount]: instanceAddress },
instanceApproval
} = rootGetters['metamask/networkConfig'].tokens[currency]
const tokenInstance = getters.tokenContract({ currency, netId })
const tornadoProxy = rootGetters['application/tornadoProxyContract']({ netId })
@ -160,10 +164,10 @@ export const actions = {
? toBN('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
: toBN(getters.fromDecimals(state.approvalAmount, decimals))
const data = tokenInstance.methods
.approve(tornadoProxy._address, amountToApprove.toString())
.approve(instanceApproval ? instanceAddress : tornadoProxy._address, amountToApprove.toString())
.encodeABI()
const gas = await tokenInstance.methods
.approve(tornadoProxy._address, amountToApprove.toString())
.approve(instanceApproval ? instanceAddress : tornadoProxy._address, amountToApprove.toString())
.estimateGas({
from: ethAccount
})
@ -190,14 +194,20 @@ export const actions = {
},
async fetchTokenAllowance({ getters, rootGetters, commit, rootState }) {
const netId = rootGetters['metamask/netId']
const { currency } = rootState.application.selectedInstance
const { currency, amount } = rootState.application.selectedInstance
const { ethAccount } = rootState.metamask
try {
const {
instanceAddress: { [amount]: instanceAddress },
instanceApproval
} = rootGetters['metamask/networkConfig'].tokens[currency]
const tornadoInstance = rootGetters['application/tornadoProxyContract']({ netId })
const nativeCurrency = rootGetters['metamask/nativeCurrency']
if (currency !== nativeCurrency && ethAccount) {
const tokenInstance = getters.tokenContract({ currency, netId })
const allowance = await tokenInstance.methods.allowance(ethAccount, tornadoInstance._address).call()
const allowance = await tokenInstance.methods
.allowance(ethAccount, instanceApproval ? instanceAddress : tornadoInstance._address)
.call()
commit('SAVE_ALLOWANCE', { allowance })
}
} catch (e) {

View File

@ -2456,15 +2456,6 @@
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Ffixed-merkle-tree/-/0.7.3/fixed-merkle-tree-0.7.3.tgz#6636ce9d334553c5f17e5a564fd22f2e9ec04472"
integrity sha512-8UWvIzz0/rMGBkzXACwmCv/5I1VJmnshAKc4C+nkTfOdmnX8Pf1bBa0GlxUIZ25ZFGiU/h2IKEHYckmHovwzEA==
"@tornado/gas-price-oracle@^0.5.3":
version "0.5.3"
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Fgas-price-oracle/-/0.5.3/gas-price-oracle-0.5.3.tgz#fb5423dddee2f52edbc16174c5ddce90bea5413d"
integrity sha512-LpVfPiPIz3FOmJdiqJf/yoeO5n9/Pd5jgtdY+6hB9lNW0AiWhylhpScojICViS+3OL9QC8CoTlgr+kbfGeO9pQ==
dependencies:
axios "^0.21.2"
bignumber.js "^9.0.0"
node-cache "^5.1.2"
"@tornado/snarkjs@0.1.20", "@tornado/snarkjs@^0.1.20":
version "0.1.20"
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Fsnarkjs/-/0.1.20/snarkjs-0.1.20.tgz#d7610cd3c8dc10598da7dc3e40e5d7470c3aa8c7"
@ -2482,12 +2473,10 @@
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Ftornado-config/-/2.0.0/tornado-config-2.0.0.tgz#52bbc179ecb2385f71b4d56e060b68e7dd6fb8b4"
integrity sha512-7EkpWNfEm34VEOrbLnPpvd/aUJYnA1L+6/qx2fZ/AfmuJFkjSZ18Z4jvVGNY7ktKIhTu3/Tbze+9l3eNueCNIA==
"@tornado/tornado-oracles@^2.1.0":
version "2.1.0"
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Ftornado-oracles/-/2.1.0/tornado-oracles-2.1.0.tgz#2aa0d8c9288992e6d194d4bb28acb37c2035c453"
integrity sha512-Y6FPAGnCvHLWzUnNYgGoOv+X7KY3CF02rRSawataYaLyl+v2ivh7RYZZZ3G/B5hXf+pD3IFeCdm4PDnTNyNe1g==
"@tornado/tornado-oracles@git+https://git.tornado.ws/tornadocontrib/tornado-oracles.git#c9f43ff29266c48dc030a3b66e818201ff4cfc8d":
version "3.4.0"
resolved "git+https://git.tornado.ws/tornadocontrib/tornado-oracles.git#c9f43ff29266c48dc030a3b66e818201ff4cfc8d"
dependencies:
"@tornado/gas-price-oracle" "^0.5.3"
"@tornado/tornado-config" "^2.0.0"
"@types/node" "^20.5.1"
bignumber.js "^9.1.1"
@ -3682,13 +3671,6 @@ axios@^0.19.0:
follow-redirects "1.5.10"
is-buffer "^2.0.2"
axios@^0.21.2:
version "0.21.4"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.4.tgz#c67b90dc0568e5c1cf2b0b858c43ba28e2eda575"
integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==
dependencies:
follow-redirects "^1.14.0"
b4a@^1.0.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.3.1.tgz#5ead1402bd4a2dcfea35cc83928815d53315ff32"
@ -8037,11 +8019,6 @@ follow-redirects@1.5.10:
dependencies:
debug "=3.1.0"
follow-redirects@^1.14.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
for-in@^1.0.1, for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@ -11448,13 +11425,6 @@ node-cache@^4.1.1:
clone "2.x"
lodash "^4.17.15"
node-cache@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d"
integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==
dependencies:
clone "2.x"
node-fetch@2.6.1, node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"