Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

10 changed files with 23 additions and 196 deletions

View File

@ -1,5 +1,2 @@
PINATA_API_KEY= PINATA_API_KEY=
PINATA_SECRET_API_KEY= PINATA_SECRET_API_KEY=
MAINNET_SUBGRAPH=
MAINNET_RPC=

View File

@ -1,46 +0,0 @@
# Dockefile from https://notes.ethereum.org/@GW1ZUbNKR5iRjjKYx6_dJQ/Bk8zsJ9xj
# FROM node:22.17.0-bullseye-slim
FROM node@sha256:98663a445a21da13827b841d8df7b4d8743d5133e0d7a4e28ec0852140aa1abe
# install wget, git and necessary certificates so we can install IPFS below
RUN apt update && apt install --yes --no-install-recommends wget git apt-transport-https ca-certificates && rm -rf /var/lib/apt/lists/*
# install IPFS
WORKDIR /home/root
RUN wget -qO - https://dist.ipfs.tech/kubo/v0.35.0/kubo_v0.35.0_linux-amd64.tar.gz | tar -xvzf - \
&& cd kubo \
&& ./install.sh \
&& cd .. \
&& rm -rf kubo
RUN ipfs init
ENV GIT_REPOSITORY=https://codeberg.org/tornadocash/relayers-network-ui.git
# From development branch, double check with tornado.ws
ENV GIT_COMMIT_HASH=e5f1b6f91c372fc58a7a6eb463e21a62a059db3b
# clone the repository
RUN mkdir /app/
WORKDIR /app
# Simple hack to fetch only commit and nothing more (no need to download 1GB sized repo, only 100MB would be enough)
RUN git init && \
git remote add origin $GIT_REPOSITORY && \
git fetch --depth 1 origin $GIT_COMMIT_HASH && \
git checkout $GIT_COMMIT_HASH
# install, build and prep for deployment
RUN yarn install --frozen-lockfile --ignore-scripts
RUN yarn build
RUN yarn generate
# add the build output to IPFS and write the hash to a file
RUN ipfs add --cid-version 1 --quieter --only-hash --recursive ./dist > ipfs_hash.txt
# print the hash for good measure in case someone is looking at the build logs
RUN cat ipfs_hash.txt
# this entrypoint file will execute `ipfs add` of the build output to the docker host's IPFS API endpoint, so we can easily extract the IPFS build out of the docker image
RUN printf '#!/bin/sh\nipfs --api /ip4/`getent ahostsv4 host.docker.internal | grep STREAM | head -n 1 | cut -d \ -f 1`/tcp/5001 add --cid-version 1 -r ./dist' >> entrypoint.sh
RUN chmod u+x entrypoint.sh
ENTRYPOINT [ "./entrypoint.sh" ]

View File

@ -1,10 +0,0 @@
import { ChainId } from '@/types'
export const GRAPHQL_LIMIT = 1000
/**
* todo: add support for subgraph on thegraph & API keys
*/
export const RELAYER_SUBGRAPH_LIST: Record<number, string> = {
[ChainId.MAINNET]: process.env.MAINNET_SUBGRAPH ?? 'https://tornadocash-rpc.com/subgraphs/name/tornadocash/tornado-governance',
}

View File

@ -3,7 +3,6 @@ export * from './link'
export * from './enums' export * from './enums'
export * from './steps' export * from './steps'
export * from './errors' export * from './errors'
export * from './graph'
export * from './relayer' export * from './relayer'
export * from './variables' export * from './variables'
export * from './contracts' export * from './contracts'

View File

@ -1,5 +1,5 @@
import { ChainId } from '@/types' import { ChainId } from '@/types'
export const RPC_LIST: Record<number, string> = { export const RPC_LIST: { [chainId in ChainId]: string } = {
[ChainId.MAINNET]: process.env.MAINNET_RPC ?? 'https://rpc.mevblocker.io', [ChainId.MAINNET]: 'https://tornadocash-rpc.com',
} }

View File

@ -4,17 +4,14 @@
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
"nuxt": "cross-env NODE_OPTIONS=\"--max_old_space_size=8192 --openssl-legacy-provider\" nuxt", "dev": "nuxt",
"dev": "yarn nuxt", "build": "nuxt build",
"build": "yarn nuxt build", "start": "nuxt start",
"start": "yarn nuxt start",
"lint": "eslint --ext .js,.ts", "lint": "eslint --ext .js,.ts",
"lint:fix": "eslint --ext .js,.ts --quiet --fix", "lint:fix": "eslint --ext .js,.ts --quiet --fix",
"compile": "typechain --target ethers-v5 --out-dir ./_contracts './abi/*.json'", "compile": "typechain --target ethers-v5 --out-dir ./_contracts './abi/*.json'",
"generate": "yarn nuxt generate && cp dist/404.html dist/ipfs-404.html", "generate": "nuxt generate && cp dist/404.html dist/ipfs-404.html",
"prepare": "husky install", "prepare": "husky install",
"docker:build": "docker build -t relayers-network-ui .",
"docker:hash": "docker container run --rm -it --entrypoint cat relayers-network-ui /app/ipfs_hash.txt",
"ipfs:upload": "node --loader ts-node/esm ipfsUpload.ts" "ipfs:upload": "node --loader ts-node/esm ipfsUpload.ts"
}, },
"dependencies": { "dependencies": {
@ -59,7 +56,6 @@
"@types/node": "^16.10.9", "@types/node": "^16.10.9",
"@typescript-eslint/eslint-plugin": "^4.28.0", "@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0", "@typescript-eslint/parser": "^4.28.0",
"cross-env": "^7.0.3",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"eslint": "^7.29.0", "eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",

View File

@ -7,16 +7,15 @@ async function getEnsOwner(ensName: string, chainId: ChainId) {
const { provider } = getProvider(chainId) const { provider } = getProvider(chainId)
const ownerAddress = await provider.resolveName(ensName) const ownerAddress = await provider.resolveName(ensName)
return ownerAddress ?? undefined return ownerAddress || undefined
} catch (err) { } catch (err) {
return undefined return undefined
} }
} }
/**
async function getNameFromHash(ensHash: string) { async function getNameFromHash(ensHash: string) {
try { try {
const response = await fetch('https://api.thegraph.com/subgraphs/name/ensdomains/ens', { const response = await fetch('https://tornadocash-rpc.com/subgraphs/name/graphprotocol/ens', {
body: JSON.stringify({ body: JSON.stringify({
query: `{ query: `{
domain(id: "${ensHash}") { domain(id: "${ensHash}") {
@ -34,6 +33,4 @@ async function getNameFromHash(ensHash: string) {
throw new Error(err.message) throw new Error(err.message)
} }
} }
**/ export { getEnsOwner, getNameFromHash }
export { getEnsOwner }

View File

@ -1,8 +1,9 @@
import { checkSubdomains, subdomains } from './ensSubdomains' import { checkSubdomains, subdomains } from './ensSubdomains'
import { getEnsOwner } from './ens' import { getEnsOwner, getNameFromHash } from './ens'
export const ensService = { export const ensService = {
subdomains, subdomains,
getEnsOwner, getEnsOwner,
checkSubdomains, checkSubdomains,
getNameFromHash,
} }

View File

@ -5,26 +5,9 @@ import { AddStakeParams, AddStakePermitParams, ChainId, RootState } from '@/type
import { RelayerMutation, RelayerState } from '@/types/store/relayer' import { RelayerMutation, RelayerState } from '@/types/store/relayer'
import { getRelayerRegistry } from '@/contracts' import { getRelayerRegistry } from '@/contracts'
import { DEPLOYED_BLOCK, GRAPHQL_LIMIT, RELAYER_SUBGRAPH_LIST, errors, numbers } from '@/constants' import { DEPLOYED_BLOCK, errors, numbers } from '@/constants'
import { ensService, tornadoRelayerService } from '@/services' import { ensService, tornadoRelayerService } from '@/services'
import { errorParser, fromWei, toDecimalsPlaces } from '@/utilities' import { errorParser, fromWei, toDecimalsPlaces } from '@/utilities'
import { getAddress, parseEther } from 'ethers/lib/utils'
import { BigNumber } from 'ethers'
interface GraphRelayer {
address: string
ensName: string
ensHash: string
workers: string[]
stakeBalance: string
blockRegistration: string
}
interface GraphRelayerFormatted extends Omit<GraphRelayer, 'stakeBalance' | 'blockRegistration'> {
stakeBalance: BigNumber
registerBlock: number
}
export const actions: ActionTree<RelayerState, RootState> = { export const actions: ActionTree<RelayerState, RootState> = {
async checkIsRelayerRegistered({ getters, commit }) { async checkIsRelayerRegistered({ getters, commit }) {
@ -41,7 +24,6 @@ export const actions: ActionTree<RelayerState, RootState> = {
} }
}, },
// Unused because block range is too broad
async geRelayerWorkers({ getters, commit }) { async geRelayerWorkers({ getters, commit }) {
try { try {
const { walletAddress, chainId } = getters.dependencies const { walletAddress, chainId } = getters.dependencies
@ -69,81 +51,11 @@ export const actions: ActionTree<RelayerState, RootState> = {
} }
}, },
async getRelayersFromGraph({ getters }) { async getRelayerENSData({ getters, commit, dispatch }, ensHash) {
try {
const { chainId } = getters.dependencies
const graphUrl = RELAYER_SUBGRAPH_LIST[chainId as ChainId]
const res = await fetch(graphUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
query getRelayers($first: Int) {
relayers(first: $first, orderBy: blockRegistration, orderDirection: asc) {
address
ensName
ensHash
workers
stakeBalance
blockRegistration
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`,
variables: {
first: GRAPHQL_LIMIT,
},
}),
})
if (!res.ok) {
throw new Error(`Invalid response from ${graphUrl} ${res.statusText}`)
}
const { data, errors } = await res.json()
if (errors) {
throw new Error(`Error from graph: ${JSON.stringify(errors)}`)
}
if (data?._meta?.hasIndexingErrors) {
throw new Error('Subgraph has indexing errors')
}
return (data.relayers as GraphRelayer[]).map(({ address, ensName, ensHash, workers, stakeBalance, blockRegistration }) => {
if (!workers.includes(address)) {
workers.push(getAddress(address))
}
return {
address: getAddress(address),
ensName,
ensHash,
workers,
stakeBalance: parseEther(stakeBalance),
registerBlock: Number(blockRegistration),
}
})
} catch (err) {
console.log(err)
throw err
}
},
async getRelayerENSData({ getters, commit }, ensName) {
try { try {
const { chainId } = getters.dependencies const { chainId } = getters.dependencies
const ensName = await ensService.getNameFromHash(ensHash)
const subdomains = await ensService.checkSubdomains(ensName, chainId) const subdomains = await ensService.checkSubdomains(ensName, chainId)
const mainnetSubdomain = subdomains.find((el) => el.chainId === ChainId.MAINNET) const mainnetSubdomain = subdomains.find((el) => el.chainId === ChainId.MAINNET)
@ -162,22 +74,17 @@ export const actions: ActionTree<RelayerState, RootState> = {
} }
}, },
async getRelayers({ getters, commit, dispatch }) { async getRelayers({ getters, commit }) {
try { try {
const { walletAddress } = getters.dependencies const { walletAddress, chainId } = getters.dependencies
const relayers = (await dispatch('getRelayersFromGraph')) as GraphRelayerFormatted[] const registryContract = getRelayerRegistry(chainId)
const relayer = relayers.find((r) => r.address === walletAddress) const { balance, ensHash } = await registryContract.callStatic.relayers(walletAddress)
if (!relayer) { commit(RelayerMutation.SET_BALANCE, balance)
throw new Error(`No relayer found for ${walletAddress}`)
}
commit(RelayerMutation.SET_WORKERS, relayer.workers) return ensHash
commit(RelayerMutation.SET_BALANCE, relayer.stakeBalance)
await dispatch('getRelayerENSData', relayer.ensName)
} catch (err) { } catch (err) {
throw new Error(err.message) throw new Error(err.message)
} }
@ -191,7 +98,9 @@ export const actions: ActionTree<RelayerState, RootState> = {
throw new Error(errors.relayer.NOT_REGISTERED) throw new Error(errors.relayer.NOT_REGISTERED)
} }
await dispatch('getRelayers') await dispatch('geRelayerWorkers')
const ensHash = await dispatch('getRelayers')
await dispatch('getRelayerENSData', ensHash)
} catch (err) { } catch (err) {
const errorText = errorParser(err.message, errors.validation.NO_RESPONSE) const errorText = errorParser(err.message, errors.validation.NO_RESPONSE)

View File

@ -3923,13 +3923,6 @@ create-require@^1.1.0, create-require@^1.1.1:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
dependencies:
cross-spawn "^7.0.1"
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3" version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@ -3939,15 +3932,6 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
cross-spawn@^7.0.1:
version "7.0.6"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
crypto-browserify@^3.11.0: crypto-browserify@^3.11.0:
version "3.12.0" version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"