classic-ui-dev/services/graph.js
2022-06-16 16:04:45 +10:00

428 lines
8.8 KiB
JavaScript

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) => ({
timestamp: e.timestamp,
commitment: e.commitment,
leafIndex: Number(e.index),
blockNumber: Number(e.blockNumber),
transactionHash: e.transactionHash
}))
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) => ({
to: e.to,
fee: e.fee,
timestamp: e.timestamp,
nullifierHash: e.nullifier,
blockNumber: Number(e.blockNumber),
transactionHash: e.transactionHash
}))
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
}