2022-04-21 23:05:56 -04:00
|
|
|
import { ApolloClient, InMemoryCache, gql } from '@apollo/client/core'
|
|
|
|
|
|
|
|
import {
|
|
|
|
_META,
|
|
|
|
GET_DEPOSITS,
|
|
|
|
GET_STATISTIC,
|
|
|
|
GET_REGISTERED,
|
|
|
|
GET_WITHDRAWALS,
|
|
|
|
GET_NOTE_ACCOUNTS,
|
|
|
|
GET_ENCRYPTED_NOTES
|
|
|
|
} from './queries'
|
|
|
|
|
|
|
|
const isEmptyArray = (arr) => !Array.isArray(arr) || !arr.length
|
|
|
|
|
|
|
|
const first = 1000
|
|
|
|
|
|
|
|
const link = ({ getContext }) => {
|
|
|
|
const { chainId } = getContext()
|
|
|
|
return CHAIN_GRAPH_URLS[chainId]
|
|
|
|
}
|
|
|
|
|
|
|
|
const CHAIN_GRAPH_URLS = {
|
|
|
|
1: 'https://api.thegraph.com/subgraphs/name/tornadocash/mainnet-tornado-subgraph',
|
|
|
|
5: 'https://api.thegraph.com/subgraphs/name/tornadocash/goerli-tornado-subgraph',
|
|
|
|
10: 'https://api.thegraph.com/subgraphs/name/tornadocash/optimism-tornado-subgraph',
|
|
|
|
56: 'https://api.thegraph.com/subgraphs/name/tornadocash/bsc-tornado-subgraph',
|
|
|
|
100: 'https://api.thegraph.com/subgraphs/name/tornadocash/xdai-tornado-subgraph',
|
|
|
|
137: 'https://api.thegraph.com/subgraphs/name/tornadocash/matic-tornado-subgraph',
|
|
|
|
42161: 'https://api.thegraph.com/subgraphs/name/tornadocash/arbitrum-tornado-subgraph',
|
|
|
|
43114: 'https://api.thegraph.com/subgraphs/name/tornadocash/avalanche-tornado-subgraph'
|
|
|
|
}
|
|
|
|
|
|
|
|
const defaultOptions = {
|
|
|
|
query: {
|
|
|
|
fetchPolicy: 'no-cache',
|
|
|
|
errorPolicy: 'all'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const client = new ApolloClient({
|
|
|
|
uri: link,
|
|
|
|
cache: new InMemoryCache(),
|
|
|
|
defaultOptions
|
|
|
|
})
|
|
|
|
|
|
|
|
const registryClient = new ApolloClient({
|
|
|
|
uri: 'https://api.thegraph.com/subgraphs/name/tornadocash/tornado-relayer-registry',
|
|
|
|
cache: new InMemoryCache(),
|
|
|
|
defaultOptions
|
|
|
|
})
|
|
|
|
|
|
|
|
async function getStatistic({ currency, amount, netId }) {
|
|
|
|
try {
|
|
|
|
const { data } = await client.query({
|
|
|
|
context: {
|
|
|
|
chainId: netId
|
|
|
|
},
|
|
|
|
query: gql(GET_STATISTIC),
|
|
|
|
variables: {
|
|
|
|
currency,
|
|
|
|
first: 10,
|
|
|
|
orderBy: 'index',
|
|
|
|
orderDirection: 'desc',
|
|
|
|
amount: String(amount)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const { deposits } = data
|
|
|
|
|
|
|
|
const lastSyncBlock = await getMeta({ netId })
|
|
|
|
|
|
|
|
const events = deposits
|
|
|
|
.map((e) => ({
|
|
|
|
timestamp: e.timestamp,
|
|
|
|
leafIndex: Number(e.index),
|
|
|
|
blockNumber: Number(e.blockNumber)
|
|
|
|
}))
|
|
|
|
.reverse()
|
|
|
|
|
|
|
|
const [lastEvent] = events.slice(-1)
|
|
|
|
|
|
|
|
return {
|
|
|
|
lastSyncBlock: lastEvent?.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock,
|
|
|
|
events
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getAllRegisters(fromBlock) {
|
|
|
|
try {
|
|
|
|
const relayers = await getRegisters(fromBlock)
|
|
|
|
|
|
|
|
if (!relayers) {
|
|
|
|
return { lastSyncBlock: '', events: [] }
|
|
|
|
}
|
|
|
|
|
|
|
|
const lastSyncBlock = await getRegisteredMeta()
|
|
|
|
|
|
|
|
return { lastSyncBlock, events: relayers }
|
|
|
|
} catch {
|
|
|
|
return { lastSyncBlock: '', events: [] }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
async function getAllDeposits({ currency, amount, fromBlock, netId }) {
|
|
|
|
try {
|
|
|
|
let deposits = []
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
let result = await getDeposits({ currency, amount, fromBlock, netId })
|
|
|
|
|
|
|
|
if (isEmptyArray(result)) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result.length < 900) {
|
|
|
|
deposits = deposits.concat(result)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
const [lastEvent] = result.slice(-1)
|
|
|
|
|
|
|
|
result = result.filter((e) => e.blockNumber !== lastEvent.blockNumber)
|
|
|
|
fromBlock = Number(lastEvent.blockNumber)
|
|
|
|
|
|
|
|
deposits = deposits.concat(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!deposits) {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const lastSyncBlock = await getMeta({ netId })
|
|
|
|
|
|
|
|
const data = deposits.map((e) => ({
|
2022-06-14 04:56:36 -04:00
|
|
|
timestamp: e.timestamp,
|
|
|
|
commitment: e.commitment,
|
2022-04-21 23:05:56 -04:00
|
|
|
leafIndex: Number(e.index),
|
2022-06-14 04:56:36 -04:00
|
|
|
blockNumber: Number(e.blockNumber),
|
|
|
|
transactionHash: e.transactionHash
|
2022-04-21 23:05:56 -04:00
|
|
|
}))
|
|
|
|
|
|
|
|
const [lastEvent] = data.slice(-1)
|
|
|
|
|
|
|
|
return {
|
|
|
|
events: data,
|
|
|
|
lastSyncBlock: lastEvent?.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getMeta({ netId }) {
|
|
|
|
try {
|
|
|
|
const { data } = await client.query({
|
|
|
|
context: {
|
|
|
|
chainId: netId
|
|
|
|
},
|
|
|
|
query: gql(_META)
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return data._meta.block.number
|
|
|
|
} catch {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getRegisteredMeta() {
|
|
|
|
try {
|
|
|
|
const { data } = await registryClient.query({
|
|
|
|
context: {
|
|
|
|
chainId: 1
|
|
|
|
},
|
|
|
|
query: gql(_META)
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
|
|
|
|
return data._meta.block.number
|
|
|
|
} catch {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getRegisters(fromBlock) {
|
|
|
|
const { data } = await registryClient.query({
|
|
|
|
context: {
|
|
|
|
chainId: 1
|
|
|
|
},
|
|
|
|
query: gql(GET_REGISTERED),
|
|
|
|
variables: { first, fromBlock }
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.relayers
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getDeposits({ currency, amount, fromBlock, netId }) {
|
|
|
|
const { data } = await client.query({
|
|
|
|
context: {
|
|
|
|
chainId: netId
|
|
|
|
},
|
|
|
|
query: gql(GET_DEPOSITS),
|
|
|
|
variables: { currency, amount: String(amount), first, fromBlock }
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.deposits
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getAllWithdrawals({ currency, amount, fromBlock, netId }) {
|
|
|
|
try {
|
|
|
|
let withdrawals = []
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
let result = await getWithdrawals({ currency, amount, fromBlock, netId })
|
|
|
|
|
|
|
|
if (isEmptyArray(result)) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result.length < 900) {
|
|
|
|
withdrawals = withdrawals.concat(result)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
const [lastEvent] = result.slice(-1)
|
|
|
|
|
|
|
|
result = result.filter((e) => e.blockNumber !== lastEvent.blockNumber)
|
|
|
|
fromBlock = Number(lastEvent.blockNumber)
|
|
|
|
|
|
|
|
withdrawals = withdrawals.concat(result)
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!withdrawals) {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const lastSyncBlock = await getMeta({ netId })
|
|
|
|
|
|
|
|
const data = withdrawals.map((e) => ({
|
2022-06-14 04:56:36 -04:00
|
|
|
to: e.to,
|
|
|
|
fee: e.fee,
|
|
|
|
timestamp: e.timestamp,
|
2022-04-21 23:05:56 -04:00
|
|
|
nullifierHash: e.nullifier,
|
2022-06-14 04:56:36 -04:00
|
|
|
blockNumber: Number(e.blockNumber),
|
|
|
|
transactionHash: e.transactionHash
|
2022-04-21 23:05:56 -04:00
|
|
|
}))
|
|
|
|
|
|
|
|
const [lastEvent] = data.slice(-1)
|
|
|
|
|
|
|
|
return {
|
|
|
|
events: data,
|
|
|
|
lastSyncBlock: lastEvent?.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getWithdrawals({ currency, amount, fromBlock, netId }) {
|
|
|
|
const { data } = await client.query({
|
|
|
|
context: {
|
|
|
|
chainId: netId
|
|
|
|
},
|
|
|
|
query: gql(GET_WITHDRAWALS),
|
|
|
|
variables: { currency, amount: String(amount), fromBlock, first }
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.withdrawals
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getNoteAccounts({ address, netId }) {
|
|
|
|
try {
|
|
|
|
const { data } = await client.query({
|
|
|
|
context: {
|
|
|
|
chainId: netId
|
|
|
|
},
|
|
|
|
query: gql(GET_NOTE_ACCOUNTS),
|
|
|
|
variables: { address }
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const lastSyncBlock = await getMeta({ netId })
|
|
|
|
|
|
|
|
return {
|
|
|
|
lastSyncBlock,
|
|
|
|
events: data.noteAccounts
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getAllEncryptedNotes({ fromBlock, netId }) {
|
|
|
|
try {
|
|
|
|
let encryptedNotes = []
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
let result = await getEncryptedNotes({ fromBlock, netId })
|
|
|
|
|
|
|
|
if (isEmptyArray(result)) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result.length < 900) {
|
|
|
|
encryptedNotes = encryptedNotes.concat(result)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
const [lastEvent] = result.slice(-1)
|
|
|
|
|
|
|
|
result = result.filter((e) => e.blockNumber !== lastEvent.blockNumber)
|
|
|
|
fromBlock = Number(lastEvent.blockNumber)
|
|
|
|
|
|
|
|
encryptedNotes = encryptedNotes.concat(result)
|
|
|
|
|
|
|
|
if (isEmptyArray(result)) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!encryptedNotes) {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const lastSyncBlock = await getMeta({ netId })
|
|
|
|
|
|
|
|
const data = encryptedNotes.map((e) => ({
|
|
|
|
txHash: e.transactionHash,
|
|
|
|
encryptedNote: e.encryptedNote,
|
|
|
|
transactionHash: e.transactionHash,
|
|
|
|
blockNumber: Number(e.blockNumber)
|
|
|
|
}))
|
|
|
|
|
|
|
|
const [lastEvent] = data.slice(-1)
|
|
|
|
|
|
|
|
return {
|
|
|
|
events: data,
|
|
|
|
lastSyncBlock: lastEvent?.blockNumber >= lastSyncBlock ? lastEvent.blockNumber + 1 : lastSyncBlock
|
|
|
|
}
|
|
|
|
} catch {
|
|
|
|
return {
|
|
|
|
lastSyncBlock: '',
|
|
|
|
events: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getEncryptedNotes({ fromBlock, netId }) {
|
|
|
|
const { data } = await client.query({
|
|
|
|
context: {
|
|
|
|
chainId: netId
|
|
|
|
},
|
|
|
|
query: gql(GET_ENCRYPTED_NOTES),
|
|
|
|
variables: { fromBlock, first }
|
|
|
|
})
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
|
|
|
return data.encryptedNotes
|
|
|
|
}
|
|
|
|
|
|
|
|
export default {
|
|
|
|
getDeposits,
|
|
|
|
getStatistic,
|
|
|
|
getAllDeposits,
|
|
|
|
getWithdrawals,
|
|
|
|
getNoteAccounts,
|
|
|
|
getAllRegisters,
|
|
|
|
getAllWithdrawals,
|
|
|
|
getAllEncryptedNotes
|
|
|
|
}
|