WIP fix relayers UI #2
@ -1,2 +1,5 @@
|
||||
PINATA_API_KEY=
|
||||
PINATA_SECRET_API_KEY=
|
||||
|
||||
MAINNET_SUBGRAPH=
|
||||
MAINNET_RPC=
|
||||
46
Dockerfile
Normal file
46
Dockerfile
Normal file
@ -0,0 +1,46 @@
|
||||
# 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" ]
|
||||
10
constants/graph.ts
Normal file
10
constants/graph.ts
Normal file
@ -0,0 +1,10 @@
|
||||
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',
|
||||
}
|
||||
@ -3,6 +3,7 @@ export * from './link'
|
||||
export * from './enums'
|
||||
export * from './steps'
|
||||
export * from './errors'
|
||||
export * from './graph'
|
||||
export * from './relayer'
|
||||
export * from './variables'
|
||||
export * from './contracts'
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ChainId } from '@/types'
|
||||
|
||||
export const RPC_LIST: { [chainId in ChainId]: string } = {
|
||||
[ChainId.MAINNET]: 'https://tornadocash-rpc.com',
|
||||
export const RPC_LIST: Record<number, string> = {
|
||||
[ChainId.MAINNET]: process.env.MAINNET_RPC ?? 'https://rpc.mevblocker.io',
|
||||
}
|
||||
|
||||
12
package.json
12
package.json
@ -4,14 +4,17 @@
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start",
|
||||
"nuxt": "cross-env NODE_OPTIONS=\"--max_old_space_size=8192 --openssl-legacy-provider\" nuxt",
|
||||
"dev": "yarn nuxt",
|
||||
"build": "yarn nuxt build",
|
||||
"start": "yarn nuxt start",
|
||||
"lint": "eslint --ext .js,.ts",
|
||||
"lint:fix": "eslint --ext .js,.ts --quiet --fix",
|
||||
"compile": "typechain --target ethers-v5 --out-dir ./_contracts './abi/*.json'",
|
||||
"generate": "nuxt generate && cp dist/404.html dist/ipfs-404.html",
|
||||
"generate": "yarn nuxt generate && cp dist/404.html dist/ipfs-404.html",
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -56,6 +59,7 @@
|
||||
"@types/node": "^16.10.9",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
||||
"@typescript-eslint/parser": "^4.28.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dotenv": "^10.0.0",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
|
||||
@ -7,15 +7,16 @@ async function getEnsOwner(ensName: string, chainId: ChainId) {
|
||||
const { provider } = getProvider(chainId)
|
||||
const ownerAddress = await provider.resolveName(ensName)
|
||||
|
||||
return ownerAddress || undefined
|
||||
return ownerAddress ?? undefined
|
||||
} catch (err) {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
async function getNameFromHash(ensHash: string) {
|
||||
try {
|
||||
const response = await fetch('https://tornadocash-rpc.com/subgraphs/name/graphprotocol/ens', {
|
||||
const response = await fetch('https://api.thegraph.com/subgraphs/name/ensdomains/ens', {
|
||||
body: JSON.stringify({
|
||||
query: `{
|
||||
domain(id: "${ensHash}") {
|
||||
@ -33,4 +34,6 @@ async function getNameFromHash(ensHash: string) {
|
||||
throw new Error(err.message)
|
||||
}
|
||||
}
|
||||
export { getEnsOwner, getNameFromHash }
|
||||
**/
|
||||
|
||||
export { getEnsOwner }
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { checkSubdomains, subdomains } from './ensSubdomains'
|
||||
import { getEnsOwner, getNameFromHash } from './ens'
|
||||
import { getEnsOwner } from './ens'
|
||||
|
||||
export const ensService = {
|
||||
subdomains,
|
||||
getEnsOwner,
|
||||
checkSubdomains,
|
||||
getNameFromHash,
|
||||
}
|
||||
|
||||
115
store/relayer.ts
115
store/relayer.ts
@ -5,9 +5,26 @@ import { AddStakeParams, AddStakePermitParams, ChainId, RootState } from '@/type
|
||||
import { RelayerMutation, RelayerState } from '@/types/store/relayer'
|
||||
|
||||
import { getRelayerRegistry } from '@/contracts'
|
||||
import { DEPLOYED_BLOCK, errors, numbers } from '@/constants'
|
||||
import { DEPLOYED_BLOCK, GRAPHQL_LIMIT, RELAYER_SUBGRAPH_LIST, errors, numbers } from '@/constants'
|
||||
import { ensService, tornadoRelayerService } from '@/services'
|
||||
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> = {
|
||||
async checkIsRelayerRegistered({ getters, commit }) {
|
||||
@ -24,6 +41,7 @@ export const actions: ActionTree<RelayerState, RootState> = {
|
||||
}
|
||||
},
|
||||
|
||||
// Unused because block range is too broad
|
||||
async geRelayerWorkers({ getters, commit }) {
|
||||
try {
|
||||
const { walletAddress, chainId } = getters.dependencies
|
||||
@ -51,11 +69,81 @@ export const actions: ActionTree<RelayerState, RootState> = {
|
||||
}
|
||||
},
|
||||
|
||||
async getRelayerENSData({ getters, commit, dispatch }, ensHash) {
|
||||
async getRelayersFromGraph({ getters }) {
|
||||
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 {
|
||||
const { chainId } = getters.dependencies
|
||||
|
||||
const ensName = await ensService.getNameFromHash(ensHash)
|
||||
const subdomains = await ensService.checkSubdomains(ensName, chainId)
|
||||
|
||||
const mainnetSubdomain = subdomains.find((el) => el.chainId === ChainId.MAINNET)
|
||||
@ -74,17 +162,22 @@ export const actions: ActionTree<RelayerState, RootState> = {
|
||||
}
|
||||
},
|
||||
|
||||
async getRelayers({ getters, commit }) {
|
||||
async getRelayers({ getters, commit, dispatch }) {
|
||||
try {
|
||||
const { walletAddress, chainId } = getters.dependencies
|
||||
const { walletAddress } = getters.dependencies
|
||||
|
||||
const registryContract = getRelayerRegistry(chainId)
|
||||
const relayers = (await dispatch('getRelayersFromGraph')) as GraphRelayerFormatted[]
|
||||
|
||||
const { balance, ensHash } = await registryContract.callStatic.relayers(walletAddress)
|
||||
const relayer = relayers.find((r) => r.address === walletAddress)
|
||||
|
||||
commit(RelayerMutation.SET_BALANCE, balance)
|
||||
if (!relayer) {
|
||||
throw new Error(`No relayer found for ${walletAddress}`)
|
||||
}
|
||||
|
||||
return ensHash
|
||||
commit(RelayerMutation.SET_WORKERS, relayer.workers)
|
||||
commit(RelayerMutation.SET_BALANCE, relayer.stakeBalance)
|
||||
|
||||
await dispatch('getRelayerENSData', relayer.ensName)
|
||||
} catch (err) {
|
||||
throw new Error(err.message)
|
||||
}
|
||||
@ -98,9 +191,7 @@ export const actions: ActionTree<RelayerState, RootState> = {
|
||||
throw new Error(errors.relayer.NOT_REGISTERED)
|
||||
}
|
||||
|
||||
await dispatch('geRelayerWorkers')
|
||||
const ensHash = await dispatch('getRelayers')
|
||||
await dispatch('getRelayerENSData', ensHash)
|
||||
await dispatch('getRelayers')
|
||||
} catch (err) {
|
||||
const errorText = errorParser(err.message, errors.validation.NO_RESPONSE)
|
||||
|
||||
|
||||
16
yarn.lock
16
yarn.lock
@ -3923,6 +3923,13 @@ create-require@^1.1.0, create-require@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
||||
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:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||
@ -3932,6 +3939,15 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
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:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user