Compare commits
53 Commits
main
...
sidechain-
Author | SHA1 | Date | |
---|---|---|---|
1705dd1ce2 | |||
1e99a136cc | |||
03c71f28bb | |||
d04480a62f | |||
5191207cc2 | |||
bb76455d6a | |||
c33a7f9ef2 | |||
0a7e97e62e | |||
fb2fbb89f3 | |||
bc6778bda2 | |||
64ccb5d4d0 | |||
b2ae3d3399 | |||
9b14e3ed89 | |||
4c1500bd96 | |||
|
a4916d1cb4 | ||
|
7350591ebd | ||
|
a453444318 | ||
|
bca85764bc | ||
|
38fd1e6a4c | ||
|
ecdcb545fc | ||
|
ed9f003d50 | ||
|
526f1889f9 | ||
|
e5dd4b465f | ||
|
92bc3bb0a8 | ||
|
7d8a6dc30f | ||
|
5b2815e6a0 | ||
|
220a71fe09 | ||
|
692ab64d49 | ||
|
6eb3d702a4 | ||
|
06848352f1 | ||
|
87d3528346 | ||
|
1d6c2575f9 | ||
|
96b6e722d8 | ||
|
4a28d4646e | ||
|
3d952f1906 | ||
|
07b091ff46 | ||
|
10b2fed839 | ||
|
c0320285b9 | ||
|
7ea0b9abd6 | ||
|
aa7d389766 | ||
|
e538236904 | ||
|
787c9663fe | ||
|
71b638e640 | ||
|
3d7a9b5f93 | ||
|
96d937ee0d | ||
|
9b49cab775 | ||
|
9747c1a397 | ||
|
3cf642fe68 | ||
|
5cfe527be8 | ||
|
e24af4239e | ||
|
a875bc77fa | ||
|
7286aeaff0 | ||
|
803a104a93 |
13
.env.example
13
.env.example
@ -1,8 +1,5 @@
|
|||||||
NET_ID=1
|
NET_ID=56
|
||||||
HTTP_RPC_URL=https://mainnet.infura.io
|
HTTP_RPC_URL=https://bsc-dataseed1.ninicoin.io/
|
||||||
WS_RPC_URL=wss://mainnet.infura.io/ws/v3/
|
|
||||||
# ORACLE_RPC_URL should always point to the mainnet
|
|
||||||
ORACLE_RPC_URL=https://mainnet.infura.io
|
|
||||||
REDIS_URL=redis://127.0.0.1:6379
|
REDIS_URL=redis://127.0.0.1:6379
|
||||||
|
|
||||||
# DNS settings
|
# DNS settings
|
||||||
@ -12,12 +9,10 @@ APP_PORT=8000
|
|||||||
|
|
||||||
# without 0x prefix
|
# without 0x prefix
|
||||||
PRIVATE_KEY=
|
PRIVATE_KEY=
|
||||||
# 0.05 means 0.05%
|
# 0.1 means 0.1%
|
||||||
REGULAR_TORNADO_WITHDRAW_FEE=0.05
|
RELAYER_FEE=0.1
|
||||||
MINING_SERVICE_FEE=0.05
|
|
||||||
REWARD_ACCOUNT=
|
REWARD_ACCOUNT=
|
||||||
CONFIRMATIONS=4
|
CONFIRMATIONS=4
|
||||||
|
|
||||||
# in GWEI
|
# in GWEI
|
||||||
MAX_GAS_PRICE=1000
|
MAX_GAS_PRICE=1000
|
||||||
AGGREGATOR=0x8cb1436F64a3c33aD17bb42F94e255c4c0E871b2
|
|
||||||
|
38
.github/workflows/build.yml
vendored
38
.github/workflows/build.yml
vendored
@ -3,7 +3,7 @@ name: build
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['*']
|
branches: ['*']
|
||||||
tags: ['v[0-9]+.[0-9]+.[0-9]+']
|
tags: ['v[0-9]+.[0-9]+.[0-9]+*']
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -14,15 +14,15 @@ jobs:
|
|||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: 12
|
node-version: 14
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn test
|
- run: yarn test
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
- name: Telegram Failure Notification
|
- name: Telegram Failure Notification
|
||||||
uses: appleboy/telegram-action@0.0.7
|
uses: appleboy/telegram-action@master
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
message: ❗ Build failed for [${{ github.repository }}](https://github.com/${{ github.repository }}/actions) because of ${{ github.actor }}
|
message: ❗ Build failed for [${{ github.repository }}](https://github.com/${{ github.repository }}/actions) for sidechains because of ${{ github.actor }}
|
||||||
format: markdown
|
format: markdown
|
||||||
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
@ -43,7 +43,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Check package.json version vs tag
|
- name: Check package.json version vs tag
|
||||||
run: |
|
run: |
|
||||||
[ ${{ steps.vars.outputs.version }} = $(grep '"version":' package.json | grep -o "[0-9.]*") ] || (echo "Git tag doesn't match version in package.json" && false)
|
[ ${{ steps.vars.outputs.version }} = $(sed -nE 's/^\s*"version": "([0-9]+.[0-9]+.[0-9]+(-beta.[0-9]+)?)",$/\1/p' package.json) ] || (echo "Git tag doesn't match version in package.json" && false)
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v1.1.0
|
uses: docker/build-push-action@v1.1.0
|
||||||
@ -51,36 +51,48 @@ jobs:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
repository: tornadocash/relayer
|
repository: tornadocash/relayer
|
||||||
tag_with_ref: true
|
tag_with_ref: true
|
||||||
tags: mining,candidate
|
tags: light
|
||||||
username: ${{ secrets.DOCKER_USERNAME }}
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.DOCKER_TOKEN }}
|
password: ${{ secrets.DOCKER_TOKEN }}
|
||||||
|
|
||||||
- name: Telegram Message Notify
|
- name: Telegram Message Notify
|
||||||
uses: appleboy/telegram-action@0.0.7
|
uses: appleboy/telegram-action@master
|
||||||
with:
|
with:
|
||||||
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
message: 🚀 Published a [${{ steps.vars.outputs.repo_name }}](https://github.com/${{ github.repository }}) version ${{ steps.vars.outputs.version }} to docker hub
|
message: 🚀 Published a [${{ steps.vars.outputs.repo_name }}](https://github.com/${{ github.repository }}) for sidechains version ${{ steps.vars.outputs.version }} to docker hub
|
||||||
debug: true
|
debug: true
|
||||||
format: markdown
|
format: markdown
|
||||||
|
|
||||||
- name: Telegram Relayer Channel Notification
|
- name: Telegram Relayer Channel Notification
|
||||||
uses: appleboy/telegram-action@0.0.7
|
uses: appleboy/telegram-action@master
|
||||||
with:
|
with:
|
||||||
to: ${{ secrets.TELEGRAM_RELAYER_CHAT_ID }}
|
to: ${{ secrets.TELEGRAM_RELAYER_CHAT_ID }}
|
||||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
message: |
|
message: |
|
||||||
🚀 Published a new version of the relayer node service to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:mining`.
|
🚀 Published a new version of the sidechains relayer node service to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:light`.
|
||||||
|
|
||||||
❗️Please update your nodes ❗️
|
❗️Please update your sidechains nodes ❗️
|
||||||
|
DO NOT TOUCH MAINNET AND NOVA RELAYERS.
|
||||||
debug: true
|
debug: true
|
||||||
format: markdown
|
format: markdown
|
||||||
|
|
||||||
|
- name: Discord Relayer Channel Notification
|
||||||
|
env:
|
||||||
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELAYER_WEBHOOK }}
|
||||||
|
uses: Ilshidur/action-discord@master
|
||||||
|
with:
|
||||||
|
args: |
|
||||||
|
🚀 Published a new version of the sidechains relayer node service to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:light`.
|
||||||
|
|
||||||
|
❗️Please update your sidechains nodes ❗️
|
||||||
|
DO NOT TOUCH MAINNET AND NOVA RELAYERS.
|
||||||
|
|
||||||
- name: Telegram Failure Notification
|
- name: Telegram Failure Notification
|
||||||
uses: appleboy/telegram-action@0.0.7
|
uses: appleboy/telegram-action@master
|
||||||
if: failure()
|
if: failure()
|
||||||
with:
|
with:
|
||||||
message: ❗ Failed to publish [${{ steps.vars.outputs.repo_name }}](https://github.com/${{ github.repository }}/actions) because of ${{ github.actor }}
|
message: ❗ Failed to publish [${{ steps.vars.outputs.repo_name }}](https://github.com/${{ github.repository }}/actions) for sidechains because of ${{ github.actor }}
|
||||||
format: markdown
|
format: markdown
|
||||||
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
to: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||||
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,8 +1,9 @@
|
|||||||
.vscode
|
.vscode
|
||||||
node_modules/
|
node_modules/
|
||||||
.env
|
.env
|
||||||
.env.mainnet
|
.env.*
|
||||||
.env.kovan
|
!.env.*.example
|
||||||
|
!.env.example
|
||||||
kovan.*
|
kovan.*
|
||||||
dump.rdb
|
dump.rdb
|
||||||
.idea
|
.idea
|
||||||
|
1
.npmrc
Normal file
1
.npmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
@tornado:registry=https://git.tornado.ws/api/packages/tornado-packages/npm/
|
@ -1,4 +1,4 @@
|
|||||||
FROM node:12
|
FROM node:16
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json yarn.lock ./
|
COPY package.json yarn.lock ./
|
||||||
|
148
README.md
148
README.md
@ -1,62 +1,116 @@
|
|||||||
# Relayer for Tornado Cash [![Build Status](https://github.com/tornadocash/relayer/workflows/build/badge.svg)](https://github.com/tornadocash/relayer/actions) [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/tornadocash/relayer?logo=docker&logoColor=%23FFFFFF&sort=semver)](https://hub.docker.com/repository/docker/tornadocash/relayer)
|
# Relayer for Tornado Cash [![Build Status](https://github.com/tornadocash/relayer/workflows/build/badge.svg)](https://github.com/tornadocash/relayer/actions) ![Static Badge](https://img.shields.io/badge/version-5.1.0-blue?logo=docker)
|
||||||
|
|
||||||
## Getting listed on app.tornado.cash
|
***Tornado Cash was sanctioned by the US Treasury on 08/08/2022, this makes it illegal for US citizens to interact with Tornado Cash and all of it's associated deployed smart contracts. Please understand the laws where you live and take all necessary steps to protect and anonymize yourself.**
|
||||||
|
|
||||||
If you would like to be listed in tornado.cash UI relayer's dropdown option, please do the following:
|
***It is recommended to run your Relayer on a VPS instance ([Virtual Private Server](https://njal.la/)). Ensure SSH configuration is enabled for security, you can find information about SSH keygen and management [here](https://www.ssh.com/academy/ssh/keygen).**
|
||||||
|
|
||||||
1. Setup tornado.cash relayer node(see below for docker-compose.yml example)
|
|
||||||
2. Setup ENS subdomain(`goerli-v2.xxx.eth`, `mainnet-v2.xxx.eth`) with TEXT record and URL key that points to your DNS or IP address.
|
|
||||||
3. Test your relayer setup on Goerli testnet at https://app.tornado.cash by choosing custom relayer's option on withdraw tab. Enter your ens name and initiate a withdrawal.
|
|
||||||
4. Open new Github issue in https://github.com/tornadocash/tornado-relayer/issues and specify the following:
|
|
||||||
|
|
||||||
- your goerli ens url
|
|
||||||
- your mainnet ens url
|
|
||||||
- your telegram handle
|
|
||||||
- withdrawal tx on goerli
|
|
||||||
- withdrawal tx on mainnet
|
|
||||||
|
|
||||||
Please choose your testnet relayer's fee wisely.
|
|
||||||
|
|
||||||
Disclaimer: Please consult with legal and tax advisors regarding the compliance of running a relayer service in your jurisdiction. The authors of this project bear no responsibility.
|
|
||||||
|
|
||||||
USE AT YOUR OWN RISK.
|
|
||||||
|
|
||||||
## Deploy with docker-compose
|
## Deploy with docker-compose
|
||||||
|
|
||||||
docker-compose.yml contains a stack that will automatically provision SSL certificates for your domain name and will add a https redirect to port 80.
|
__PREREQUISITES__
|
||||||
|
|
||||||
1. Download [docker-compose.yml](/docker-compose.yml) and [.env.example](/.env.example)
|
1. Update core dependencies
|
||||||
|
|
||||||
```
|
- `sudo apt-get update`
|
||||||
wget https://raw.githubusercontent.com/tornadocash/tornado-relayer/master/docker-compose.yml
|
|
||||||
wget https://raw.githubusercontent.com/tornadocash/tornado-relayer/master/.env.example -O .env
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Setup environment variables
|
2. Install docker-compose
|
||||||
|
|
||||||
- set `NET_ID` (1 for mainnet, 5 for Goerli)
|
- `curl -SL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose && sudo chmod +x /usr/local/bin/docker-compose`
|
||||||
- set `HTTP_RPC_URL` rpc url for your ethereum node
|
|
||||||
- set `WS_RPC_URL` websocket url
|
|
||||||
- set `ORACLE_RPC_URL` - rpc url for mainnet node for fetching prices(always have to be on mainnet)
|
|
||||||
- set `PRIVATE_KEY` for your relayer address (without 0x prefix)
|
|
||||||
- set `VIRTUAL_HOST` and `LETSENCRYPT_HOST` to your domain and add DNS record pointing to your relayer ip address
|
|
||||||
- set `REGULAR_TORNADO_WITHDRAW_FEE` - fee in % that is used for tornado pool withdrawals
|
|
||||||
- set `MINING_SERVICE_FEE` - fee in % that is used for mining AP withdrawals
|
|
||||||
- set `REWARD_ACCOUNT` - eth address that is used to collect fees
|
|
||||||
- update `AGGREGATOR` if needed - Contract address of aggregator instance.
|
|
||||||
- update `CONFIRMATIONS` if needed - how many block confirmations to wait before processing an event. Not recommended to set less than 3
|
|
||||||
- update `MAX_GAS_PRICE` if needed - maximum value of gwei value for relayer's transaction
|
|
||||||
|
|
||||||
If you want to use more than 1 eth address for relaying transactions, please add as many `workers` as you want. For example, you can comment out `worker2` in docker-compose.yml file, but please use a different `PRIVATE_KEY` for each worker.
|
3. Install Docker
|
||||||
|
|
||||||
3. Run `docker-compose up -d`
|
- `curl -fsSL https://get.docker.com -o get-docker.sh && chmod +x get-docker.sh && ./get-docker.sh`
|
||||||
|
|
||||||
## Run locally
|
4. Install git
|
||||||
|
|
||||||
1. `npm i`
|
- `sudo apt-get install git-all`
|
||||||
|
|
||||||
|
5. Install nginx
|
||||||
|
|
||||||
|
- `sudo apt install nginx`
|
||||||
|
|
||||||
|
6. Stop apache2 instance (enabled by default)
|
||||||
|
|
||||||
|
- `sudo systemctl stop apache2`
|
||||||
|
|
||||||
|
__FIREWALL CONFIGURATION__
|
||||||
|
|
||||||
|
_* Warning: Failure to configure SSH as the first UFW rule, will lock you out of the instance_
|
||||||
|
|
||||||
|
1. Make sure UFW is installed by running `apt update` and `apt install ufw`
|
||||||
|
2. Allow SSH in the first position in UFW by running `ufw insert 1 allow ssh`*
|
||||||
|
3. Allow HTTP, and HTTPS by running `ufw allow https/tcp/http`
|
||||||
|
4. Finalize changes and enable firewall `ufw enable`
|
||||||
|
|
||||||
|
__NGINX REVERSE PROXY__
|
||||||
|
|
||||||
|
1. Copy the pre-modified nginx policy as your default policy
|
||||||
|
|
||||||
|
- `cp tornado.conf /etc/nginx/sites-available/default`
|
||||||
|
|
||||||
|
2. Append the default nginx configuration to include streams
|
||||||
|
|
||||||
|
- `echo "stream { map_hash_bucket_size 128; map_hash_max_size 128; include /etc/nginx/conf.d/streams/*.conf; }" >> /etc/nginx/nginx.conf`
|
||||||
|
|
||||||
|
3. Create the stream configuration
|
||||||
|
|
||||||
|
- `mkdir /etc/nginx/conf.d/streams && cp tornado-stream.conf /etc/nginx/conf.d/streams/tornado-stream.conf`
|
||||||
|
|
||||||
|
4. Start nginx to make sure the configuration is correct
|
||||||
|
|
||||||
|
- `sudo systemctl restart nginx`
|
||||||
|
|
||||||
|
5. Stop nginx
|
||||||
|
|
||||||
|
- `sudo systemctl stop nginx`
|
||||||
|
|
||||||
|
__DEPLOYMENT__
|
||||||
|
|
||||||
|
1. Clone the repository and enter the directory
|
||||||
|
|
||||||
|
- `git clone https://git.tornado.ws/tornadocash/classic-relayer -b sidechain-v5 && cd classic-relayer`
|
||||||
|
|
||||||
|
2. Check environment files:
|
||||||
|
|
||||||
|
By default each network is preconfigured the naming of `.env.<NETWORK>`
|
||||||
|
|
||||||
|
- `.env.bsc` for Binance Smart Chain
|
||||||
|
|
||||||
|
- `.env.arb` for Arbitrum
|
||||||
|
|
||||||
|
- `.env.op` for Optimism
|
||||||
|
|
||||||
|
- `.env.gnosis` for Gnosis (xdai)
|
||||||
|
|
||||||
|
- `.env.polygon` for Polygon (matic)
|
||||||
|
|
||||||
|
- `.env.avax` for Avalanche C-Chain
|
||||||
|
|
||||||
|
3. Configure (fill) environment files for those networks on which the relayer will be deployed:
|
||||||
|
|
||||||
|
- Set `PRIVATE_KEY` to your relayer address (remove the 0x from your private key) to each environment file (*It is recommended not to reuse the same private keys for each network as a security measure*)
|
||||||
|
|
||||||
|
- Set `VIRTUAL_HOST` and `LETSENCRYPT_HOST` a unique subndomain for every network to each environment file, eg: `mainnet.example.com` for Ethereum, `binance.example.com` for Binance etc
|
||||||
|
- Add a A wildcard record DNS record with the value assigned to your instance IP address to configure subdomains
|
||||||
|
- Set `RELAYER_FEE` to what you would like to charge as your fee
|
||||||
|
- Set `RPC_URL` to a non-censoring RPC (You can [run your own](https://github.com/feshchenkod/rpc-nodes), or use a [free option](https://chainnodes.org/))
|
||||||
|
- Set `ORACLE_RPC_URL` to an Ethereum native RPC endpoint
|
||||||
|
|
||||||
|
4. Build docker image for sidechain with simple `npm run build` command
|
||||||
|
|
||||||
|
5. Uncomment the `env_file` lines (remove `# `) for the associated network services in `docker-compose.yml` for chosen chains (networks)
|
||||||
|
|
||||||
|
6. Run docker-compose for the configured networks specified via `--profile <NETWORK_SYMBOL>`, for example (if you run relayer only Binance Smart Chain and Arbitrum):
|
||||||
|
|
||||||
|
- `docker-compose --profile bsc --profile arb up -d`
|
||||||
|
|
||||||
|
7. Visit your domain addresses and check each `/status` endpoint to ensure there is no errors in the `status` fields
|
||||||
|
|
||||||
|
## Run locally for one chain
|
||||||
|
|
||||||
|
1. `yarn`
|
||||||
2. `cp .env.example .env`
|
2. `cp .env.example .env`
|
||||||
3. Modify `.env` as needed
|
3. Modify `.env` as needed (described above)
|
||||||
4. `npm run start`
|
4. `yarn start`
|
||||||
5. Go to `http://127.0.0.1:8000`
|
5. Go to `http://127.0.0.1:8000`
|
||||||
6. In order to execute withdraw request, you can run following command
|
6. In order to execute withdraw request, you can run following command
|
||||||
|
|
||||||
@ -69,6 +123,10 @@ Relayer should return a transaction hash
|
|||||||
In that case you will need to add https termination yourself because browsers with default settings will prevent https
|
In that case you will need to add https termination yourself because browsers with default settings will prevent https
|
||||||
tornado.cash UI from submitting your request over http connection
|
tornado.cash UI from submitting your request over http connection
|
||||||
|
|
||||||
|
## Run own RPC nodes
|
||||||
|
|
||||||
|
It is strongly recommended that you use your own RPC nodes. Instruction on how to run full nodes can be found [here](https://github.com/feshchenkod/rpc-nodes).
|
||||||
|
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
1. TreeWatcher module keeps track of Account Tree changes and automatically caches the actual state in Redis and emits `treeUpdate` event to redis pub/sub channel
|
1. TreeWatcher module keeps track of Account Tree changes and automatically caches the actual state in Redis and emits `treeUpdate` event to redis pub/sub channel
|
||||||
|
@ -1,319 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32[]",
|
|
||||||
"name": "domains",
|
|
||||||
"type": "bytes32[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "bulkResolve",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address[]",
|
|
||||||
"name": "result",
|
|
||||||
"type": "address[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract Governance",
|
|
||||||
"name": "governance",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "getAllProposals",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"components": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "proposer",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "target",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "startTime",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "endTime",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "forVotes",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "againstVotes",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "executed",
|
|
||||||
"type": "bool"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "extended",
|
|
||||||
"type": "bool"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "enum Governance.ProposalState",
|
|
||||||
"name": "state",
|
|
||||||
"type": "uint8"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"internalType": "struct GovernanceAggregator.Proposal[]",
|
|
||||||
"name": "proposals",
|
|
||||||
"type": "tuple[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract Governance",
|
|
||||||
"name": "governance",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address[]",
|
|
||||||
"name": "accs",
|
|
||||||
"type": "address[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "getGovernanceBalances",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256[]",
|
|
||||||
"name": "amounts",
|
|
||||||
"type": "uint256[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address[]",
|
|
||||||
"name": "fromTokens",
|
|
||||||
"type": "address[]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256[]",
|
|
||||||
"name": "oneUnitAmounts",
|
|
||||||
"type": "uint256[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "getPricesInETH",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256[]",
|
|
||||||
"name": "prices",
|
|
||||||
"type": "uint256[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract Governance",
|
|
||||||
"name": "governance",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "account",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "getUserData",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "balance",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "latestProposalId",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "latestProposalIdState",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "timelock",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "delegatee",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract Miner",
|
|
||||||
"name": "miner",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address[]",
|
|
||||||
"name": "instances",
|
|
||||||
"type": "address[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "minerRates",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256[]",
|
|
||||||
"name": "_rates",
|
|
||||||
"type": "uint256[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "node",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "resolve",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract RewardSwap",
|
|
||||||
"name": "swap",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "swapState",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "balance",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "poolWeight",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract Miner",
|
|
||||||
"name": "miner",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address[]",
|
|
||||||
"name": "instances",
|
|
||||||
"type": "address[]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "contract RewardSwap",
|
|
||||||
"name": "swap",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "miningData",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256[]",
|
|
||||||
"name": "_rates",
|
|
||||||
"type": "uint256[]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "balance",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "poolWeight",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address[]",
|
|
||||||
"name": "fromTokens",
|
|
||||||
"type": "address[]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256[]",
|
|
||||||
"name": "oneUnitAmounts",
|
|
||||||
"type": "uint256[]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "contract RewardSwap",
|
|
||||||
"name": "swap",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "marketData",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256[]",
|
|
||||||
"name": "prices",
|
|
||||||
"type": "uint256[]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "balance",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,12 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{ "internalType": "contract IERC20", "name": "srcToken", "type": "address" },
|
|
||||||
{ "internalType": "contract IERC20", "name": "dstToken", "type": "address" }
|
|
||||||
],
|
|
||||||
"name": "getRate",
|
|
||||||
"outputs": [{ "internalType": "uint256", "name": "weightedRate", "type": "uint256" }],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]
|
|
1077
abis/mining.abi.json
1077
abis/mining.abi.json
File diff suppressed because it is too large
Load Diff
151
abis/ovmGasPriceOracleABI.json
Normal file
151
abis/ovmGasPriceOracleABI.json
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "address", "name": "_owner", "type": "address" }],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "constructor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"name": "DecimalsUpdated",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"name": "GasPriceUpdated",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"name": "L1BaseFeeUpdated",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"name": "OverheadUpdated",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{ "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" },
|
||||||
|
{ "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" }
|
||||||
|
],
|
||||||
|
"name": "OwnershipTransferred",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"name": "ScalarUpdated",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "decimals",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "gasPrice",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "bytes", "name": "_data", "type": "bytes" }],
|
||||||
|
"name": "getL1Fee",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "bytes", "name": "_data", "type": "bytes" }],
|
||||||
|
"name": "getL1GasUsed",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "l1BaseFee",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "overhead",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "owner",
|
||||||
|
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "renounceOwnership",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "scalar",
|
||||||
|
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "uint256", "name": "_decimals", "type": "uint256" }],
|
||||||
|
"name": "setDecimals",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "uint256", "name": "_gasPrice", "type": "uint256" }],
|
||||||
|
"name": "setGasPrice",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "uint256", "name": "_baseFee", "type": "uint256" }],
|
||||||
|
"name": "setL1BaseFee",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "uint256", "name": "_overhead", "type": "uint256" }],
|
||||||
|
"name": "setOverhead",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "uint256", "name": "_scalar", "type": "uint256" }],
|
||||||
|
"name": "setScalar",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }],
|
||||||
|
"name": "transferOwnership",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]
|
105
abis/proxyLightABI.json
Normal file
105
abis/proxyLightABI.json
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"anonymous": false,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"indexed": true,
|
||||||
|
"internalType": "address",
|
||||||
|
"name": "sender",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"indexed": false,
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "encryptedNote",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "EncryptedNote",
|
||||||
|
"type": "event"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "bytes[]",
|
||||||
|
"name": "_encryptedNotes",
|
||||||
|
"type": "bytes[]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "backupNotes",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "contract ITornadoInstance",
|
||||||
|
"name": "_tornado",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes32",
|
||||||
|
"name": "_commitment",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "_encryptedNote",
|
||||||
|
"type": "bytes"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "deposit",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "payable",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"internalType": "contract ITornadoInstance",
|
||||||
|
"name": "_tornado",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes",
|
||||||
|
"name": "_proof",
|
||||||
|
"type": "bytes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes32",
|
||||||
|
"name": "_root",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "bytes32",
|
||||||
|
"name": "_nullifierHash",
|
||||||
|
"type": "bytes32"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "address payable",
|
||||||
|
"name": "_recipient",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "address payable",
|
||||||
|
"name": "_relayer",
|
||||||
|
"type": "address"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "_fee",
|
||||||
|
"type": "uint256"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"internalType": "uint256",
|
||||||
|
"name": "_refund",
|
||||||
|
"type": "uint256"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "withdraw",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "payable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]
|
@ -1,252 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_torn",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_miner",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "_miningCap",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "_initialLiquidity",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "constructor"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"anonymous": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "newWeight",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "PoolWeightUpdated",
|
|
||||||
"type": "event"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"anonymous": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"indexed": true,
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "recipient",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "pTORN",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "TORN",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "Swap",
|
|
||||||
"type": "event"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "DURATION",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "initialLiquidity",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "liquidity",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "miner",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "poolWeight",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "node",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "resolve",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "startTimestamp",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "tokensSold",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "torn",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract IERC20",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "recipient",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "amount",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "swap",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "amount",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "getExpectedReturn",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "tornVirtualBalance",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "newWeight",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "setPoolWeight",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,498 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"constant": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "_newOperator",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "changeOperator",
|
|
||||||
"outputs": [],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "nullifierHashes",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "",
|
|
||||||
"type": "bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes",
|
|
||||||
"name": "_proof",
|
|
||||||
"type": "bytes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_root",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_nullifierHash",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address payable",
|
|
||||||
"name": "_recipient",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address payable",
|
|
||||||
"name": "_relayer",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "_fee",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "_refund",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "withdraw",
|
|
||||||
"outputs": [],
|
|
||||||
"payable": true,
|
|
||||||
"stateMutability": "payable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "verifier",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract IVerifier",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_left",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_right",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "hashLeftRight",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "pure",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "FIELD_SIZE",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "levels",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint32",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "operator",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_root",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "isKnownRoot",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "",
|
|
||||||
"type": "bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "commitments",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "",
|
|
||||||
"type": "bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "denomination",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "currentRootIndex",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint32",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "_newVerifier",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "updateVerifier",
|
|
||||||
"outputs": [],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_commitment",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "deposit",
|
|
||||||
"outputs": [],
|
|
||||||
"payable": true,
|
|
||||||
"stateMutability": "payable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "getLastRoot",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "roots",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "ROOT_HISTORY_SIZE",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint32",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_nullifierHash",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "isSpent",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "",
|
|
||||||
"type": "bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "zeros",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "ZERO_VALUE",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "filledSubtrees",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"constant": true,
|
|
||||||
"inputs": [],
|
|
||||||
"name": "nextIndex",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "uint32",
|
|
||||||
"name": "",
|
|
||||||
"type": "uint32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract IVerifier",
|
|
||||||
"name": "_verifier",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "_denomination",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint32",
|
|
||||||
"name": "_merkleTreeHeight",
|
|
||||||
"type": "uint32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "_operator",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"payable": false,
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "constructor"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"anonymous": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"indexed": true,
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "commitment",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "uint32",
|
|
||||||
"name": "leafIndex",
|
|
||||||
"type": "uint32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "timestamp",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "Deposit",
|
|
||||||
"type": "event"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"anonymous": false,
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "to",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "nullifierHash",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": true,
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "relayer",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"indexed": false,
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "fee",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "Withdrawal",
|
|
||||||
"type": "event"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,171 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_tornadoTrees",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "_governance",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "contract ITornado[]",
|
|
||||||
"name": "_instances",
|
|
||||||
"type": "address[]"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "constructor"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "governance",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract ITornado",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "instances",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "",
|
|
||||||
"type": "bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "node",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "resolve",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "address",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [],
|
|
||||||
"name": "tornadoTrees",
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract ITornadoTrees",
|
|
||||||
"name": "",
|
|
||||||
"type": "address"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"stateMutability": "view",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract ITornado",
|
|
||||||
"name": "tornado",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "commitment",
|
|
||||||
"type": "bytes32"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "deposit",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "payable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract ITornado",
|
|
||||||
"name": "instance",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bool",
|
|
||||||
"name": "update",
|
|
||||||
"type": "bool"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "updateInstances",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "nonpayable",
|
|
||||||
"type": "function"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"inputs": [
|
|
||||||
{
|
|
||||||
"internalType": "contract ITornado",
|
|
||||||
"name": "tornado",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes",
|
|
||||||
"name": "proof",
|
|
||||||
"type": "bytes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "root",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "bytes32",
|
|
||||||
"name": "nullifierHash",
|
|
||||||
"type": "bytes32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address payable",
|
|
||||||
"name": "recipient",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "address payable",
|
|
||||||
"name": "relayer",
|
|
||||||
"type": "address"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "fee",
|
|
||||||
"type": "uint256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"internalType": "uint256",
|
|
||||||
"name": "refund",
|
|
||||||
"type": "uint256"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"name": "withdraw",
|
|
||||||
"outputs": [],
|
|
||||||
"stateMutability": "payable",
|
|
||||||
"type": "function"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,62 +0,0 @@
|
|||||||
version: '2'
|
|
||||||
|
|
||||||
# ssh-agent && ssh-add -K ~/.ssh/id_rsa
|
|
||||||
# DOCKER_BUILDKIT=1 docker build --ssh default -t tornadocash/relayer .
|
|
||||||
services:
|
|
||||||
server:
|
|
||||||
image: tornadocash/relayer
|
|
||||||
restart: always
|
|
||||||
command: server
|
|
||||||
env_file: .env
|
|
||||||
ports:
|
|
||||||
- 8000:8000
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
nginx_proxy_read_timeout: 600
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
treeWatcher:
|
|
||||||
image: tornadocash/relayer
|
|
||||||
restart: always
|
|
||||||
command: treeWatcher
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
priceWatcher:
|
|
||||||
image: tornadocash/relayer
|
|
||||||
restart: always
|
|
||||||
command: priceWatcher
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
worker1:
|
|
||||||
image: tornadocash/relayer
|
|
||||||
restart: always
|
|
||||||
command: worker
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
# worker2:
|
|
||||||
# image: tornadocash/relayer
|
|
||||||
# restart: always
|
|
||||||
# command: worker
|
|
||||||
# env_file: .env
|
|
||||||
# environment:
|
|
||||||
# PRIVATE_KEY: qwe
|
|
||||||
# REDIS_URL: redis://redis/0
|
|
||||||
|
|
||||||
redis:
|
|
||||||
image: redis
|
|
||||||
restart: always
|
|
||||||
command: [redis-server, --appendonly, 'yes']
|
|
||||||
volumes:
|
|
||||||
- redis:/data
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
redis:
|
|
@ -1,115 +1,14 @@
|
|||||||
version: '2'
|
version: '2'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
server:
|
|
||||||
image: tornadocash/relayer:mining
|
|
||||||
restart: always
|
|
||||||
command: server
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
nginx_proxy_read_timeout: 600
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
treeWatcher:
|
|
||||||
image: tornadocash/relayer:mining
|
|
||||||
restart: always
|
|
||||||
command: treeWatcher
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
priceWatcher:
|
|
||||||
image: tornadocash/relayer:mining
|
|
||||||
restart: always
|
|
||||||
command: priceWatcher
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
healthWatcher:
|
|
||||||
image: tornadocash/relayer:mining
|
|
||||||
restart: always
|
|
||||||
command: healthWatcher
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
worker1:
|
|
||||||
image: tornadocash/relayer:mining
|
|
||||||
restart: always
|
|
||||||
command: worker
|
|
||||||
env_file: .env
|
|
||||||
environment:
|
|
||||||
REDIS_URL: redis://redis/0
|
|
||||||
depends_on: [redis]
|
|
||||||
|
|
||||||
# worker2:
|
|
||||||
# image: tornadocash/relayer:mining
|
|
||||||
# restart: always
|
|
||||||
# command: worker
|
|
||||||
# env_file: .env
|
|
||||||
# environment:
|
|
||||||
# PRIVATE_KEY: qwe
|
|
||||||
# REDIS_URL: redis://redis/0
|
|
||||||
|
|
||||||
# # this container will proxy *.onion domain to the server container
|
|
||||||
# # if you want to run *only* as .onion service, you don't need `nginx`, `letsencrypt`, `dockergen` containers
|
|
||||||
# tor:
|
|
||||||
# image: strm/tor
|
|
||||||
# restart: always
|
|
||||||
# depends_on: [server]
|
|
||||||
# environment:
|
|
||||||
# LISTEN_PORT: 80
|
|
||||||
# REDIRECT: server:8000
|
|
||||||
# # Generate a new key with
|
|
||||||
# # docker run --rm --entrypoint shallot strm/tor-hiddenservice-nginx ^foo
|
|
||||||
# PRIVATE_KEY: |
|
|
||||||
# -----BEGIN RSA PRIVATE KEY-----
|
|
||||||
# ...
|
|
||||||
# -----END RSA PRIVATE KEY-----
|
|
||||||
|
|
||||||
# # auto update docker containers when new image is pushed to docker hub (be careful with that)
|
|
||||||
# watchtower:
|
|
||||||
# image: v2tec/watchtower
|
|
||||||
# restart: always
|
|
||||||
# volumes:
|
|
||||||
# - /var/run/docker.sock:/var/run/docker.sock
|
|
||||||
|
|
||||||
# # this container will send Telegram notifications when other containers are stopped/restarted
|
|
||||||
# # it's best to run this container on some other instance, otherwise it can't notify if the whole instance goes down
|
|
||||||
# notifier:
|
|
||||||
# image: poma/docker-telegram-notifier
|
|
||||||
# restart: always
|
|
||||||
# volumes:
|
|
||||||
# - /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
# environment:
|
|
||||||
# # How to create bot: https://core.telegram.org/bots#3-how-do-i-create-a-bot
|
|
||||||
# # How to get chat id: https://stackoverflow.com/questions/32423837/telegram-bot-how-to-get-a-group-chat-id/32572159#32572159
|
|
||||||
# TELEGRAM_NOTIFIER_BOT_TOKEN: ...
|
|
||||||
# TELEGRAM_NOTIFIER_CHAT_ID: ...
|
|
||||||
|
|
||||||
# # this container will send Telegram notifications if specified address doesn't have enough funds
|
|
||||||
# monitor_mainnet:
|
|
||||||
# image: peppersec/monitor_eth
|
|
||||||
# restart: always
|
|
||||||
# environment:
|
|
||||||
# TELEGRAM_NOTIFIER_BOT_TOKEN: ...
|
|
||||||
# TELEGRAM_NOTIFIER_CHAT_ID: ...
|
|
||||||
# ADDRESS: '0x0000000000000000000000000000000000000000'
|
|
||||||
# THRESHOLD: 0.5 # ETH
|
|
||||||
# RPC_URL: https://mainnet.infura.io
|
|
||||||
# BLOCK_EXPLORER: etherscan.io
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis
|
image: redis
|
||||||
restart: always
|
restart: always
|
||||||
command: [redis-server, --appendonly, 'yes']
|
command: [redis-server, --appendonly, 'yes']
|
||||||
volumes:
|
volumes:
|
||||||
- redis:/data
|
- redis:/data
|
||||||
|
ports:
|
||||||
|
- '127.0.0.1:6379:6379'
|
||||||
|
|
||||||
nginx:
|
nginx:
|
||||||
image: nginx:alpine
|
image: nginx:alpine
|
||||||
@ -146,6 +45,234 @@ services:
|
|||||||
- nginx
|
- nginx
|
||||||
- dockergen
|
- dockergen
|
||||||
|
|
||||||
|
# ---------------------- BSC (Binance Smart Chain) ----------------------- #
|
||||||
|
|
||||||
|
bsc-server:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['bsc']
|
||||||
|
restart: always
|
||||||
|
command: server
|
||||||
|
env_file: .env.bsc
|
||||||
|
environment:
|
||||||
|
NET_ID: 56
|
||||||
|
REDIS_URL: redis://redis/1
|
||||||
|
nginx_proxy_read_timeout: 600
|
||||||
|
depends_on: [redis]
|
||||||
|
|
||||||
|
bsc-healthWatcher:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['bsc']
|
||||||
|
restart: always
|
||||||
|
command: healthWatcher
|
||||||
|
env_file: .env.bsc
|
||||||
|
environment:
|
||||||
|
NET_ID: 56
|
||||||
|
REDIS_URL: redis://redis/1
|
||||||
|
depends_on: [redis, bsc-server]
|
||||||
|
|
||||||
|
bsc-worker1:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['bsc']
|
||||||
|
restart: always
|
||||||
|
command: worker
|
||||||
|
env_file: .env.bsc
|
||||||
|
environment:
|
||||||
|
NET_ID: 56
|
||||||
|
REDIS_URL: redis://redis/1
|
||||||
|
depends_on: [redis, bsc-server]
|
||||||
|
|
||||||
|
# -------------------------------------------------- #
|
||||||
|
|
||||||
|
# ---------------------- Polygon (MATIC) --------------------- #
|
||||||
|
|
||||||
|
polygon-server:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['polygon']
|
||||||
|
restart: always
|
||||||
|
command: server
|
||||||
|
env_file: .env.polygon
|
||||||
|
environment:
|
||||||
|
NET_ID: 137
|
||||||
|
REDIS_URL: redis://redis/2
|
||||||
|
nginx_proxy_read_timeout: 600
|
||||||
|
depends_on: [redis]
|
||||||
|
|
||||||
|
polygon-healthWatcher:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['polygon']
|
||||||
|
restart: always
|
||||||
|
command: healthWatcher
|
||||||
|
env_file: .env.polygon
|
||||||
|
environment:
|
||||||
|
NET_ID: 137
|
||||||
|
REDIS_URL: redis://redis/2
|
||||||
|
depends_on: [redis, polygon-server]
|
||||||
|
|
||||||
|
polygon-worker1:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['polygon']
|
||||||
|
restart: always
|
||||||
|
command: worker
|
||||||
|
env_file: .env.polygon
|
||||||
|
environment:
|
||||||
|
NET_ID: 137
|
||||||
|
REDIS_URL: redis://redis/2
|
||||||
|
depends_on: [redis, polygon-server]
|
||||||
|
|
||||||
|
# -------------------------------------------------- #
|
||||||
|
|
||||||
|
# ---------------------- Gnosis (XDAI) ---------------------- #
|
||||||
|
|
||||||
|
gnosis-server:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['gnosis']
|
||||||
|
restart: always
|
||||||
|
command: server
|
||||||
|
env_file: .env.gnosis
|
||||||
|
environment:
|
||||||
|
NET_ID: 100
|
||||||
|
REDIS_URL: redis://redis/3
|
||||||
|
nginx_proxy_read_timeout: 600
|
||||||
|
depends_on: [redis]
|
||||||
|
|
||||||
|
gnosis-healthWatcher:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['gnosis']
|
||||||
|
restart: always
|
||||||
|
command: healthWatcher
|
||||||
|
env_file: .env.gnosis
|
||||||
|
environment:
|
||||||
|
NET_ID: 100
|
||||||
|
REDIS_URL: redis://redis/3
|
||||||
|
depends_on: [redis, gnosis-server]
|
||||||
|
|
||||||
|
gnosis-worker1:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['gnosis']
|
||||||
|
restart: always
|
||||||
|
command: worker
|
||||||
|
env_file: .env.gnosis
|
||||||
|
environment:
|
||||||
|
NET_ID: 100
|
||||||
|
REDIS_URL: redis://redis/3
|
||||||
|
depends_on: [redis, gnosis-server]
|
||||||
|
|
||||||
|
# -------------------------------------------------- #
|
||||||
|
|
||||||
|
# ---------------------- AVAX ---------------------- #
|
||||||
|
|
||||||
|
avax-server:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['avax']
|
||||||
|
restart: always
|
||||||
|
command: server
|
||||||
|
env_file: .env.avax
|
||||||
|
environment:
|
||||||
|
NET_ID: 43114
|
||||||
|
REDIS_URL: redis://redis/4
|
||||||
|
nginx_proxy_read_timeout: 600
|
||||||
|
depends_on: [redis]
|
||||||
|
|
||||||
|
avax-healthWatcher:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['avax']
|
||||||
|
restart: always
|
||||||
|
command: healthWatcher
|
||||||
|
env_file: .env.avax
|
||||||
|
environment:
|
||||||
|
NET_ID: 43114
|
||||||
|
REDIS_URL: redis://redis/4
|
||||||
|
depends_on: [redis, avax-server]
|
||||||
|
|
||||||
|
avax-worker1:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['avax']
|
||||||
|
restart: always
|
||||||
|
command: worker
|
||||||
|
env_file: .env.avax
|
||||||
|
environment:
|
||||||
|
NET_ID: 43114
|
||||||
|
REDIS_URL: redis://redis/4
|
||||||
|
depends_on: [redis, avax-server]
|
||||||
|
|
||||||
|
# -------------------------------------------------- #
|
||||||
|
|
||||||
|
# ---------------------- OP ------------------------ #
|
||||||
|
|
||||||
|
op-server:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['op']
|
||||||
|
restart: always
|
||||||
|
command: server
|
||||||
|
env_file: .env.op
|
||||||
|
environment:
|
||||||
|
NET_ID: 10
|
||||||
|
REDIS_URL: redis://redis/5
|
||||||
|
nginx_proxy_read_timeout: 600
|
||||||
|
depends_on: [redis]
|
||||||
|
|
||||||
|
op-healthWatcher:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['op']
|
||||||
|
restart: always
|
||||||
|
command: healthWatcher
|
||||||
|
env_file: .env.op
|
||||||
|
environment:
|
||||||
|
NET_ID: 10
|
||||||
|
REDIS_URL: redis://redis/5
|
||||||
|
depends_on: [redis, op-server]
|
||||||
|
|
||||||
|
op-worker1:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['op']
|
||||||
|
restart: always
|
||||||
|
command: worker
|
||||||
|
env_file: .env.op
|
||||||
|
environment:
|
||||||
|
NET_ID: 10
|
||||||
|
REDIS_URL: redis://redis/5
|
||||||
|
depends_on: [redis, op-server]
|
||||||
|
|
||||||
|
# -------------------------------------------------- #
|
||||||
|
|
||||||
|
# ---------------------- Arbitrum ----------------------- #
|
||||||
|
|
||||||
|
arb-server:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['arb']
|
||||||
|
restart: always
|
||||||
|
command: server
|
||||||
|
env_file: .env.arb
|
||||||
|
environment:
|
||||||
|
NET_ID: 42161
|
||||||
|
REDIS_URL: redis://redis/6
|
||||||
|
nginx_proxy_read_timeout: 600
|
||||||
|
depends_on: [redis]
|
||||||
|
|
||||||
|
arb-healthWatcher:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['arb']
|
||||||
|
restart: always
|
||||||
|
command: healthWatcher
|
||||||
|
env_file: .env.arb
|
||||||
|
environment:
|
||||||
|
NET_ID: 42161
|
||||||
|
REDIS_URL: redis://redis/6
|
||||||
|
depends_on: [redis, arb-server]
|
||||||
|
|
||||||
|
arb-worker1:
|
||||||
|
image: tornadocash/relayer:sidechain-v5
|
||||||
|
profiles: ['arb']
|
||||||
|
restart: always
|
||||||
|
command: worker
|
||||||
|
env_file: .env.arb
|
||||||
|
environment:
|
||||||
|
NET_ID: 42161
|
||||||
|
REDIS_URL: redis://redis/6
|
||||||
|
depends_on: [redis, arb-server]
|
||||||
|
|
||||||
|
# -------------------------------------------------- #
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
conf:
|
conf:
|
||||||
vhost:
|
vhost:
|
||||||
|
647134
keys/TreeUpdate.json
647134
keys/TreeUpdate.json
File diff suppressed because one or more lines are too long
Binary file not shown.
21
package.json
21
package.json
@ -1,40 +1,33 @@
|
|||||||
{
|
{
|
||||||
"name": "relay",
|
"name": "relay",
|
||||||
"version": "4.0.15",
|
"version": "5.2.2",
|
||||||
"description": "Relayer for Tornado.cash privacy solution. https://tornado.cash",
|
"description": "Relayer for Tornado.cash privacy solution. https://tornado.cash",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"server": "node src/server.js",
|
"server": "node src/server.js",
|
||||||
"worker": "node src/worker",
|
"worker": "node src/worker",
|
||||||
"treeWatcher": "node src/treeWatcher",
|
|
||||||
"priceWatcher": "node src/priceWatcher",
|
|
||||||
"healthWatcher": "node src/healthWatcher",
|
"healthWatcher": "node src/healthWatcher",
|
||||||
"eslint": "eslint --ext .js --ignore-path .gitignore .",
|
"eslint": "eslint --ext .js --ignore-path .gitignore .",
|
||||||
"prettier:check": "npx prettier --check . --config .prettierrc",
|
"prettier:check": "npx prettier --check . --config .prettierrc",
|
||||||
"prettier:fix": "npx prettier --write . --config .prettierrc",
|
"prettier:fix": "npx prettier --write . --config .prettierrc",
|
||||||
"lint": "yarn eslint && yarn prettier:check",
|
"lint": "yarn eslint && yarn prettier:check",
|
||||||
"test": "mocha",
|
"test": "mocha",
|
||||||
"start": "yarn server & yarn priceWatcher & yarn treeWatcher & yarn worker & yarn healthWatcher"
|
"build": "docker build -t tornadocash/relayer:sidechain-v5 .",
|
||||||
|
"start": "docker-compose up -d redis && concurrently \"yarn server\" \"yarn worker\" \"yarn healthWatcher\""
|
||||||
},
|
},
|
||||||
"author": "tornado.cash",
|
"author": "tornado.cash",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tornado/tornado-config": "^1.0.8",
|
||||||
|
"@tornado/tornado-oracles": "^3.3.0",
|
||||||
|
"@tornado/tx-manager": "^0.4.9",
|
||||||
"ajv": "^6.12.5",
|
"ajv": "^6.12.5",
|
||||||
"async-mutex": "^0.2.4",
|
|
||||||
"bull": "^3.12.1",
|
"bull": "^3.12.1",
|
||||||
"circomlib": "git+https://github.com/tornadocash/circomlib.git#3b492f9801573eebcfe1b6c584afe8a3beecf2b4",
|
"concurrently": "^8.2.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"eth-ens-namehash": "^2.0.8",
|
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"fixed-merkle-tree": "^0.4.0",
|
|
||||||
"gas-price-oracle": "^0.2.2",
|
|
||||||
"ioredis": "^4.14.1",
|
"ioredis": "^4.14.1",
|
||||||
"node-fetch": "^2.6.0",
|
|
||||||
"torn-token": "1.0.4",
|
|
||||||
"tornado-anonymity-mining": "^2.1.2",
|
|
||||||
"tx-manager": "^0.2.9",
|
|
||||||
"uuid": "^8.3.0",
|
"uuid": "^8.3.0",
|
||||||
"web3": "^1.3.0",
|
"web3": "^1.3.0",
|
||||||
"web3-core-promievent": "^1.3.0",
|
|
||||||
"web3-utils": "^1.2.2"
|
"web3-utils": "^1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,29 +1,27 @@
|
|||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
|
|
||||||
const { jobType } = require('./constants')
|
const tornConfig = require('@tornado/tornado-config')
|
||||||
const tornConfig = require('torn-token')
|
|
||||||
|
const { networkConfig } = require('./constants')
|
||||||
|
|
||||||
|
const netId = Number(process.env.NET_ID) || 56
|
||||||
|
const instances = tornConfig.instances[netId]
|
||||||
|
const proxyLight = tornConfig.tornadoProxyLight.address
|
||||||
|
const { gasPrices, nativeCurrency } = networkConfig[`netId${netId}`]
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
netId: Number(process.env.NET_ID) || 1,
|
netId,
|
||||||
redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
|
redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
|
||||||
httpRpcUrl: process.env.HTTP_RPC_URL,
|
httpRpcUrl: process.env.HTTP_RPC_URL,
|
||||||
wsRpcUrl: process.env.WS_RPC_URL,
|
oracleRpcUrl: process.env.ORACLE_RPC_URL || 'https://rpc.payload.de',
|
||||||
oracleRpcUrl: process.env.ORACLE_RPC_URL || 'https://mainnet.infura.io/',
|
|
||||||
offchainOracleAddress: '0x080AB73787A8B13EC7F40bd7d00d6CC07F9b24d0',
|
|
||||||
aggregatorAddress: process.env.AGGREGATOR,
|
|
||||||
minerMerkleTreeHeight: 20,
|
minerMerkleTreeHeight: 20,
|
||||||
privateKey: process.env.PRIVATE_KEY,
|
privateKey: process.env.PRIVATE_KEY,
|
||||||
instances: tornConfig.instances,
|
instances,
|
||||||
torn: tornConfig,
|
|
||||||
port: process.env.APP_PORT || 8000,
|
port: process.env.APP_PORT || 8000,
|
||||||
tornadoServiceFee: Number(process.env.REGULAR_TORNADO_WITHDRAW_FEE),
|
tornadoServiceFee: Number(process.env.RELAYER_FEE),
|
||||||
miningServiceFee: Number(process.env.MINING_SERVICE_FEE),
|
|
||||||
rewardAccount: process.env.REWARD_ACCOUNT,
|
rewardAccount: process.env.REWARD_ACCOUNT,
|
||||||
tornadoGoerliProxy: '0x454d870a72e29d5E5697f635128D18077BD04C60',
|
gasPrices,
|
||||||
gasLimits: {
|
proxyLight,
|
||||||
[jobType.TORNADO_WITHDRAW]: 390000,
|
nativeCurrency,
|
||||||
WITHDRAW_WITH_EXTRA: 480000,
|
minimumBalance: netId === 137 || netId === 43114 ? '10000000000000000000' : '100000000000000000', // 10 or 0.1
|
||||||
[jobType.MINING_REWARD]: 455000,
|
|
||||||
[jobType.MINING_WITHDRAW]: 400000,
|
|
||||||
},
|
|
||||||
minimumBalance: '1000000000000000000',
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const jobType = Object.freeze({
|
const jobType = Object.freeze({
|
||||||
TORNADO_WITHDRAW: 'TORNADO_WITHDRAW',
|
TORNADO_WITHDRAW: 'TORNADO_WITHDRAW',
|
||||||
MINING_REWARD: 'MINING_REWARD',
|
OP_TORNADO_WITHDRAW: 'OP_TORNADO_WITHDRAW',
|
||||||
MINING_WITHDRAW: 'MINING_WITHDRAW',
|
ARB_TORNADO_WITHDRAW: 'ARB_TORNADO_WITHDRAW',
|
||||||
})
|
})
|
||||||
|
|
||||||
const status = Object.freeze({
|
const status = Object.freeze({
|
||||||
@ -14,7 +14,65 @@ const status = Object.freeze({
|
|||||||
FAILED: 'FAILED',
|
FAILED: 'FAILED',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const networkConfig = {
|
||||||
|
netId56: {
|
||||||
|
gasPrices: {
|
||||||
|
instant: 5,
|
||||||
|
fast: 5,
|
||||||
|
standard: 5,
|
||||||
|
low: 5,
|
||||||
|
},
|
||||||
|
nativeCurrency: 'bnb',
|
||||||
|
},
|
||||||
|
netId10: {
|
||||||
|
gasPrices: {
|
||||||
|
instant: 0.001,
|
||||||
|
fast: 0.001,
|
||||||
|
standard: 0.001,
|
||||||
|
low: 0.001,
|
||||||
|
},
|
||||||
|
nativeCurrency: 'eth',
|
||||||
|
},
|
||||||
|
netId100: {
|
||||||
|
gasPrices: {
|
||||||
|
instant: 6,
|
||||||
|
fast: 5,
|
||||||
|
standard: 4,
|
||||||
|
low: 1,
|
||||||
|
},
|
||||||
|
nativeCurrency: 'xdai',
|
||||||
|
},
|
||||||
|
netId137: {
|
||||||
|
gasPrices: {
|
||||||
|
instant: 100,
|
||||||
|
fast: 75,
|
||||||
|
standard: 50,
|
||||||
|
low: 30,
|
||||||
|
},
|
||||||
|
nativeCurrency: 'matic',
|
||||||
|
},
|
||||||
|
netId42161: {
|
||||||
|
gasPrices: {
|
||||||
|
instant: 4,
|
||||||
|
fast: 3,
|
||||||
|
standard: 2.52,
|
||||||
|
low: 2.29,
|
||||||
|
},
|
||||||
|
nativeCurrency: 'eth',
|
||||||
|
},
|
||||||
|
netId43114: {
|
||||||
|
gasPrices: {
|
||||||
|
instant: 225,
|
||||||
|
fast: 35,
|
||||||
|
standard: 25,
|
||||||
|
low: 25,
|
||||||
|
},
|
||||||
|
nativeCurrency: 'avax',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
jobType,
|
jobType,
|
||||||
status,
|
status,
|
||||||
|
networkConfig,
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
const {
|
const { getTornadoWithdrawInputError } = require('./modules/validator')
|
||||||
getTornadoWithdrawInputError,
|
|
||||||
getMiningRewardInputError,
|
|
||||||
getMiningWithdrawInputError,
|
|
||||||
} = require('./validator')
|
|
||||||
const { postJob } = require('./queue')
|
const { postJob } = require('./queue')
|
||||||
const { jobType } = require('./constants')
|
const { jobType } = require('./constants')
|
||||||
|
|
||||||
@ -20,36 +16,6 @@ async function tornadoWithdraw(req, res) {
|
|||||||
return res.json({ id })
|
return res.json({ id })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function miningReward(req, res) {
|
|
||||||
const inputError = getMiningRewardInputError(req.body)
|
|
||||||
if (inputError) {
|
|
||||||
console.log('Invalid input:', inputError)
|
|
||||||
return res.status(400).json({ error: inputError })
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = await postJob({
|
|
||||||
type: jobType.MINING_REWARD,
|
|
||||||
request: req.body,
|
|
||||||
})
|
|
||||||
return res.json({ id })
|
|
||||||
}
|
|
||||||
|
|
||||||
async function miningWithdraw(req, res) {
|
|
||||||
const inputError = getMiningWithdrawInputError(req.body)
|
|
||||||
if (inputError) {
|
|
||||||
console.log('Invalid input:', inputError)
|
|
||||||
return res.status(400).json({ error: inputError })
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = await postJob({
|
|
||||||
type: jobType.MINING_WITHDRAW,
|
|
||||||
request: req.body,
|
|
||||||
})
|
|
||||||
return res.json({ id })
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
tornadoWithdraw,
|
tornadoWithdraw,
|
||||||
miningReward,
|
|
||||||
miningWithdraw,
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const Redis = require('ioredis')
|
|
||||||
const { toBN, fromWei } = require('web3-utils')
|
const { toBN, fromWei } = require('web3-utils')
|
||||||
|
const { setSafeInterval, logRelayerError } = require('./utils')
|
||||||
const { setSafeInterval } = require('./utils')
|
const { httpRpcUrl, privateKey, minimumBalance, nativeCurrency, instances } = require('./config')
|
||||||
const { redisUrl, httpRpcUrl, privateKey, minimumBalance } = require('./config')
|
|
||||||
|
|
||||||
const web3 = new Web3(httpRpcUrl)
|
const web3 = new Web3(httpRpcUrl)
|
||||||
const redis = new Redis(redisUrl)
|
const { redis } = require('./modules/redis')
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
@ -14,12 +12,14 @@ async function main() {
|
|||||||
const balance = await web3.eth.getBalance(address)
|
const balance = await web3.eth.getBalance(address)
|
||||||
|
|
||||||
if (toBN(balance).lt(toBN(minimumBalance))) {
|
if (toBN(balance).lt(toBN(minimumBalance))) {
|
||||||
throw new Error(`Not enough balance, less than ${fromWei(minimumBalance)} ETH`)
|
const currency = instances[nativeCurrency].symbol
|
||||||
|
throw new Error(`Not enough balance, less than ${fromWei(minimumBalance)} ${currency}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await redis.hset('health', { status: true, error: '' })
|
await redis.hset('health', { status: true, error: '' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('healthWatcher', e.message)
|
console.error('healthWatcher', e.message)
|
||||||
|
await logRelayerError(redis, e)
|
||||||
await redis.hset('health', { status: false, error: e.message })
|
await redis.hset('health', { status: false, error: e.message })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
src/modules/redis.js
Normal file
9
src/modules/redis.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const { createClient } = require('ioredis')
|
||||||
|
const { redisUrl } = require('../config')
|
||||||
|
|
||||||
|
const redis = createClient(redisUrl)
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
redis,
|
||||||
|
redisUrl,
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
const { isAddress, toChecksumAddress } = require('web3-utils')
|
const { isAddress, toChecksumAddress } = require('web3-utils')
|
||||||
const { getInstance } = require('./utils')
|
const { getInstance } = require('../utils')
|
||||||
const { rewardAccount } = require('./config')
|
const { rewardAccount } = require('../config')
|
||||||
|
|
||||||
const Ajv = require('ajv')
|
const Ajv = require('ajv')
|
||||||
const ajv = new Ajv({ format: 'fast' })
|
const ajv = new Ajv({ format: 'fast' })
|
||||||
@ -19,7 +19,7 @@ ajv.addKeyword('isAddress', {
|
|||||||
ajv.addKeyword('isKnownContract', {
|
ajv.addKeyword('isKnownContract', {
|
||||||
validate: (schema, data) => {
|
validate: (schema, data) => {
|
||||||
try {
|
try {
|
||||||
return getInstance(data) !== null
|
return !!getInstance(data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
@ -1,44 +0,0 @@
|
|||||||
const Redis = require('ioredis')
|
|
||||||
const { redisUrl, offchainOracleAddress, oracleRpcUrl } = require('./config')
|
|
||||||
const { getArgsForOracle, setSafeInterval } = require('./utils')
|
|
||||||
const redis = new Redis(redisUrl)
|
|
||||||
const Web3 = require('web3')
|
|
||||||
const web3 = new Web3(
|
|
||||||
new Web3.providers.HttpProvider(oracleRpcUrl, {
|
|
||||||
timeout: 200000, // ms
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
const offchainOracleABI = require('../abis/OffchainOracle.abi.json')
|
|
||||||
|
|
||||||
const offchainOracle = new web3.eth.Contract(offchainOracleABI, offchainOracleAddress)
|
|
||||||
const { tokenAddresses, oneUintAmount, currencyLookup } = getArgsForOracle()
|
|
||||||
|
|
||||||
const { toBN } = require('web3-utils')
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
try {
|
|
||||||
const ethPrices = {}
|
|
||||||
for (let i = 0; i < tokenAddresses.length; i++) {
|
|
||||||
try {
|
|
||||||
const price = await offchainOracle.methods
|
|
||||||
.getRate(tokenAddresses[i], '0x0000000000000000000000000000000000000000')
|
|
||||||
.call()
|
|
||||||
const numerator = toBN(oneUintAmount[i])
|
|
||||||
const denominator = toBN(10).pow(toBN(18)) // eth decimals
|
|
||||||
const priceFormatted = toBN(price).mul(numerator).div(denominator)
|
|
||||||
|
|
||||||
ethPrices[currencyLookup[tokenAddresses[i]]] = priceFormatted.toString()
|
|
||||||
} catch (e) {
|
|
||||||
console.error('cant get price of ', tokenAddresses[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await redis.hmset('prices', ethPrices)
|
|
||||||
console.log('Wrote following prices to redis', ethPrices)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('priceWatcher error', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSafeInterval(main, 30 * 1000)
|
|
10
src/queue.js
10
src/queue.js
@ -1,11 +1,11 @@
|
|||||||
const { v4: uuid } = require('uuid')
|
const { v4: uuid } = require('uuid')
|
||||||
const Queue = require('bull')
|
const Queue = require('bull')
|
||||||
const Redis = require('ioredis')
|
|
||||||
const { redisUrl } = require('./config')
|
|
||||||
const { status } = require('./constants')
|
|
||||||
const redis = new Redis(redisUrl)
|
|
||||||
|
|
||||||
const queue = new Queue('proofs', redisUrl, {
|
const { netId } = require('./config')
|
||||||
|
const { status } = require('./constants')
|
||||||
|
const { redis, redisUrl } = require('./modules/redis')
|
||||||
|
|
||||||
|
const queue = new Queue(`proofs_${netId}`, redisUrl, {
|
||||||
lockDuration: 300000, // Key expiration time for job locks.
|
lockDuration: 300000, // Key expiration time for job locks.
|
||||||
lockRenewTime: 30000, // Interval on which to acquire the job lock
|
lockRenewTime: 30000, // Interval on which to acquire the job lock
|
||||||
stalledInterval: 30000, // How often check for stalled jobs (use 0 for never checking).
|
stalledInterval: 30000, // How often check for stalled jobs (use 0 for never checking).
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
const { httpRpcUrl, aggregatorAddress } = require('./config')
|
|
||||||
const Web3 = require('web3')
|
|
||||||
const web3 = new Web3(httpRpcUrl)
|
|
||||||
const aggregator = new web3.eth.Contract(require('../abis/Aggregator.abi.json'), aggregatorAddress)
|
|
||||||
const ens = require('eth-ens-namehash')
|
|
||||||
|
|
||||||
class ENSResolver {
|
|
||||||
constructor() {
|
|
||||||
this.addresses = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
async resolve(domains) {
|
|
||||||
if (!Array.isArray(domains)) {
|
|
||||||
domains = [domains]
|
|
||||||
}
|
|
||||||
|
|
||||||
const unresolved = domains.filter(d => !this.addresses[d])
|
|
||||||
if (unresolved.length) {
|
|
||||||
const resolved = await aggregator.methods.bulkResolve(unresolved.map(ens.hash)).call()
|
|
||||||
for (let i = 0; i < resolved.length; i++) {
|
|
||||||
this.addresses[domains[i]] = resolved[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const addresses = domains.map(domain => this.addresses[domain])
|
|
||||||
return addresses.length === 1 ? addresses[0] : addresses
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ENSResolver
|
|
29
src/router.js
Normal file
29
src/router.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
const controller = require('./controller')
|
||||||
|
const status = require('./status')
|
||||||
|
const router = require('express').Router()
|
||||||
|
|
||||||
|
// Add CORS headers
|
||||||
|
router.use((req, res, next) => {
|
||||||
|
res.header('X-Frame-Options', 'DENY')
|
||||||
|
res.header('Access-Control-Allow-Origin', '*')
|
||||||
|
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Log error to console but don't send it to the client to avoid leaking data
|
||||||
|
router.use((err, req, res, next) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err)
|
||||||
|
return res.sendStatus(500)
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
router.get('/', status.index)
|
||||||
|
router.get('/v1/status', status.status)
|
||||||
|
router.get('/v1/jobs/:id', status.getJob)
|
||||||
|
router.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
|
||||||
|
router.get('/status', status.status)
|
||||||
|
router.post('/relay', controller.tornadoWithdraw)
|
||||||
|
|
||||||
|
module.exports = router
|
@ -1,41 +1,16 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const status = require('./status')
|
const router = require('./router')
|
||||||
const controller = require('./controller')
|
|
||||||
const { port, rewardAccount } = require('./config')
|
const { port, rewardAccount } = require('./config')
|
||||||
const { version } = require('../package.json')
|
const { version } = require('../package.json')
|
||||||
const { isAddress } = require('web3-utils')
|
const { isAddress } = require('web3-utils')
|
||||||
|
|
||||||
const app = express()
|
|
||||||
app.use(express.json())
|
|
||||||
|
|
||||||
// Add CORS headers
|
|
||||||
app.use((req, res, next) => {
|
|
||||||
res.header('Access-Control-Allow-Origin', '*')
|
|
||||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
// Log error to console but don't send it to the client to avoid leaking data
|
|
||||||
app.use((err, req, res, next) => {
|
|
||||||
if (err) {
|
|
||||||
console.error(err)
|
|
||||||
return res.sendStatus(500)
|
|
||||||
}
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
app.get('/', status.index)
|
|
||||||
app.get('/v1/status', status.status)
|
|
||||||
app.get('/v1/jobs/:id', status.getJob)
|
|
||||||
app.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
|
|
||||||
app.get('/status', status.status)
|
|
||||||
app.post('/relay', controller.tornadoWithdraw)
|
|
||||||
app.post('/v1/miningReward', controller.miningReward)
|
|
||||||
app.post('/v1/miningWithdraw', controller.miningWithdraw)
|
|
||||||
|
|
||||||
if (!isAddress(rewardAccount)) {
|
if (!isAddress(rewardAccount)) {
|
||||||
throw new Error('No REWARD_ACCOUNT specified')
|
throw new Error('No REWARD_ACCOUNT specified')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const app = express()
|
||||||
|
app.use(express.json())
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
app.listen(port)
|
app.listen(port)
|
||||||
console.log(`Relayer ${version} started on port ${port}`)
|
console.log(`Relayer ${version} started on port ${port}`)
|
||||||
|
@ -1,22 +1,19 @@
|
|||||||
const queue = require('./queue')
|
const queue = require('./queue')
|
||||||
const { netId, tornadoServiceFee, miningServiceFee, instances, redisUrl, rewardAccount } = require('./config')
|
const { netId, tornadoServiceFee, instances, rewardAccount } = require('./config')
|
||||||
const { version } = require('../package.json')
|
const { version } = require('../package.json')
|
||||||
const Redis = require('ioredis')
|
const { readRelayerErrors } = require('./utils')
|
||||||
const redis = new Redis(redisUrl)
|
const { redis } = require('./modules/redis')
|
||||||
|
|
||||||
async function status(req, res) {
|
async function status(req, res) {
|
||||||
const ethPrices = await redis.hgetall('prices')
|
|
||||||
const health = await redis.hgetall('health')
|
const health = await redis.hgetall('health')
|
||||||
|
health.errorsLog = await readRelayerErrors(redis)
|
||||||
const { waiting: currentQueue } = await queue.queue.getJobCounts()
|
const { waiting: currentQueue } = await queue.queue.getJobCounts()
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
rewardAccount,
|
rewardAccount,
|
||||||
instances: instances[`netId${netId}`],
|
instances,
|
||||||
netId,
|
netId,
|
||||||
ethPrices,
|
|
||||||
tornadoServiceFee,
|
tornadoServiceFee,
|
||||||
miningServiceFee,
|
|
||||||
version,
|
version,
|
||||||
health,
|
health,
|
||||||
currentQueue,
|
currentQueue,
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
const MerkleTree = require('fixed-merkle-tree')
|
|
||||||
const { redisUrl, wsRpcUrl, minerMerkleTreeHeight, torn } = require('./config')
|
|
||||||
const { poseidonHash2 } = require('./utils')
|
|
||||||
const { toBN } = require('web3-utils')
|
|
||||||
const Redis = require('ioredis')
|
|
||||||
const redis = new Redis(redisUrl)
|
|
||||||
const ENSResolver = require('./resolver')
|
|
||||||
const resolver = new ENSResolver()
|
|
||||||
const Web3 = require('web3')
|
|
||||||
const web3 = new Web3(
|
|
||||||
new Web3.providers.WebsocketProvider(wsRpcUrl, {
|
|
||||||
clientConfig: {
|
|
||||||
maxReceivedFrameSize: 100000000,
|
|
||||||
maxReceivedMessageSize: 100000000,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
const MinerABI = require('../abis/mining.abi.json')
|
|
||||||
let contract
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-unused-vars
|
|
||||||
let tree, eventSubscription, blockSubscription
|
|
||||||
|
|
||||||
// todo handle the situation when we have two rewards in one block
|
|
||||||
async function fetchEvents(from = 0, to = 'latest') {
|
|
||||||
try {
|
|
||||||
const events = await contract.getPastEvents('NewAccount', {
|
|
||||||
fromBlock: from,
|
|
||||||
toBlock: to,
|
|
||||||
})
|
|
||||||
return events
|
|
||||||
.sort((a, b) => a.returnValues.index - b.returnValues.index)
|
|
||||||
.map(e => toBN(e.returnValues.commitment))
|
|
||||||
} catch (e) {
|
|
||||||
console.error('error fetching events', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processNewEvent(err, event) {
|
|
||||||
if (err) {
|
|
||||||
throw new Error(`Event handler error: ${err}`)
|
|
||||||
// console.error(err)
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`New account event
|
|
||||||
Index: ${event.returnValues.index}
|
|
||||||
Commitment: ${event.returnValues.commitment}
|
|
||||||
Nullifier: ${event.returnValues.nullifier}
|
|
||||||
EncAcc: ${event.returnValues.encryptedAccount}`,
|
|
||||||
)
|
|
||||||
const { commitment, index } = event.returnValues
|
|
||||||
if (tree.elements().length === Number(index)) {
|
|
||||||
tree.insert(toBN(commitment))
|
|
||||||
await updateRedis()
|
|
||||||
} else if (tree.elements().length === Number(index) + 1) {
|
|
||||||
console.log('Replacing element', index)
|
|
||||||
tree.update(index, toBN(commitment))
|
|
||||||
await updateRedis()
|
|
||||||
} else {
|
|
||||||
console.log(`Invalid element index ${index}, rebuilding tree`)
|
|
||||||
rebuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function processNewBlock(err) {
|
|
||||||
if (err) {
|
|
||||||
throw new Error(`Event handler error: ${err}`)
|
|
||||||
// console.error(err)
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
// what if updateRedis takes more than 15 sec?
|
|
||||||
await updateRedis()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateRedis() {
|
|
||||||
const rootOnContract = await contract.methods.getLastAccountRoot().call()
|
|
||||||
if (!tree.root().eq(toBN(rootOnContract))) {
|
|
||||||
console.log(`Invalid tree root: ${tree.root()} != ${toBN(rootOnContract)}, rebuilding tree`)
|
|
||||||
rebuild()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const rootInRedis = await redis.get('tree:root')
|
|
||||||
if (!rootInRedis || !tree.root().eq(toBN(rootInRedis))) {
|
|
||||||
const serializedTree = JSON.stringify(tree.serialize())
|
|
||||||
await redis.set('tree:elements', serializedTree)
|
|
||||||
await redis.set('tree:root', tree.root().toString())
|
|
||||||
await redis.publish('treeUpdate', tree.root().toString())
|
|
||||||
console.log('Updated tree in redis, new root:', tree.root().toString())
|
|
||||||
} else {
|
|
||||||
console.log('Tree in redis is up to date, skipping update')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function rebuild() {
|
|
||||||
process.exit(1)
|
|
||||||
// await eventSubscription.unsubscribe()
|
|
||||||
// await blockSubscription.unsubscribe()
|
|
||||||
// setTimeout(init, 3000)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
try {
|
|
||||||
console.log('Initializing')
|
|
||||||
const miner = await resolver.resolve(torn.miningV2.address)
|
|
||||||
contract = new web3.eth.Contract(MinerABI, miner)
|
|
||||||
const block = await web3.eth.getBlockNumber()
|
|
||||||
const events = await fetchEvents(0, block)
|
|
||||||
tree = new MerkleTree(minerMerkleTreeHeight, events, { hashFunction: poseidonHash2 })
|
|
||||||
await updateRedis()
|
|
||||||
console.log(`Rebuilt tree with ${events.length} elements, root: ${tree.root()}`)
|
|
||||||
eventSubscription = contract.events.NewAccount({ fromBlock: block + 1 }, processNewEvent)
|
|
||||||
blockSubscription = web3.eth.subscribe('newBlockHeaders', processNewBlock)
|
|
||||||
} catch (e) {
|
|
||||||
console.error('error on init treeWatcher', e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init()
|
|
||||||
|
|
||||||
process.on('unhandledRejection', error => {
|
|
||||||
console.error('Unhandled promise rejection', error)
|
|
||||||
process.exit(1)
|
|
||||||
})
|
|
96
src/utils.js
96
src/utils.js
@ -1,33 +1,28 @@
|
|||||||
const { instances, netId } = require('./config')
|
const { instances } = require('./config')
|
||||||
const { poseidon } = require('circomlib')
|
const { toChecksumAddress, BN } = require('web3-utils')
|
||||||
const { toBN, toChecksumAddress, BN } = require('web3-utils')
|
|
||||||
|
|
||||||
const TOKENS = {
|
const addressMap = new Map()
|
||||||
torn: {
|
for (const [currency, { instanceAddress, symbol, decimals }] of Object.entries(instances)) {
|
||||||
tokenAddress: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
Object.entries(instanceAddress).forEach(([amount, address]) =>
|
||||||
symbol: 'TORN',
|
addressMap.set(address, {
|
||||||
decimals: 18,
|
currency,
|
||||||
},
|
amount,
|
||||||
|
symbol,
|
||||||
|
decimals,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sleep = ms => new Promise(res => setTimeout(res, ms))
|
|
||||||
|
|
||||||
function getInstance(address) {
|
function getInstance(address) {
|
||||||
address = toChecksumAddress(address)
|
address = toChecksumAddress(address)
|
||||||
const inst = instances[`netId${netId}`]
|
const key = toChecksumAddress(address)
|
||||||
for (const currency of Object.keys(inst)) {
|
if (addressMap.has(key)) {
|
||||||
for (const amount of Object.keys(inst[currency].instanceAddress)) {
|
return addressMap.get(key)
|
||||||
if (inst[currency].instanceAddress[amount] === address) {
|
} else {
|
||||||
return { currency, amount }
|
throw new Error('Unknown contact address')
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const poseidonHash = items => toBN(poseidon(items).toString())
|
|
||||||
const poseidonHash2 = (a, b) => poseidonHash([a, b])
|
|
||||||
|
|
||||||
function setSafeInterval(func, interval) {
|
function setSafeInterval(func, interval) {
|
||||||
func()
|
func()
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
@ -36,39 +31,6 @@ function setSafeInterval(func, interval) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A promise that resolves when the source emits specified event
|
|
||||||
*/
|
|
||||||
function when(source, event) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
source
|
|
||||||
.once(event, payload => {
|
|
||||||
resolve(payload)
|
|
||||||
})
|
|
||||||
.on('error', error => {
|
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getArgsForOracle() {
|
|
||||||
const tokens = {
|
|
||||||
...instances.netId1,
|
|
||||||
...TOKENS,
|
|
||||||
}
|
|
||||||
const tokenAddresses = []
|
|
||||||
const oneUintAmount = []
|
|
||||||
const currencyLookup = {}
|
|
||||||
Object.entries(tokens).map(([currency, data]) => {
|
|
||||||
if (currency !== 'eth') {
|
|
||||||
tokenAddresses.push(data.tokenAddress)
|
|
||||||
oneUintAmount.push(toBN('10').pow(toBN(data.decimals.toString())).toString())
|
|
||||||
currencyLookup[data.tokenAddress] = currency
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return { tokenAddresses, oneUintAmount, currencyLookup }
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromDecimals(value, decimals) {
|
function fromDecimals(value, decimals) {
|
||||||
value = value.toString()
|
value = value.toString()
|
||||||
let ether = value.toString()
|
let ether = value.toString()
|
||||||
@ -118,12 +80,28 @@ function fromDecimals(value, decimals) {
|
|||||||
return new BN(wei.toString(10), 10)
|
return new BN(wei.toString(10), 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logRelayerError = async (redis, e) => {
|
||||||
|
await redis.zadd('errors', 'INCR', 1, e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
const readRelayerErrors = async redis => {
|
||||||
|
const set = await redis.zrevrange('errors', 0, -1, 'WITHSCORES')
|
||||||
|
const errors = []
|
||||||
|
while (set.length) {
|
||||||
|
const [message, score] = set.splice(0, 2)
|
||||||
|
errors.push({ message, score })
|
||||||
|
}
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearRelayerErrors = redis => {
|
||||||
|
redis.del('errors')
|
||||||
|
}
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getInstance,
|
getInstance,
|
||||||
setSafeInterval,
|
setSafeInterval,
|
||||||
poseidonHash2,
|
|
||||||
sleep,
|
|
||||||
when,
|
|
||||||
getArgsForOracle,
|
|
||||||
fromDecimals,
|
fromDecimals,
|
||||||
|
logRelayerError,
|
||||||
|
readRelayerErrors,
|
||||||
|
clearRelayerErrors,
|
||||||
}
|
}
|
||||||
|
298
src/worker.js
298
src/worker.js
@ -1,241 +1,98 @@
|
|||||||
const fs = require('fs')
|
|
||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const { toBN, toWei, fromWei, toChecksumAddress } = require('web3-utils')
|
const { toBN, fromWei, isAddress, toChecksumAddress } = require('web3-utils')
|
||||||
const MerkleTree = require('fixed-merkle-tree')
|
const { redis } = require('./modules/redis')
|
||||||
const Redis = require('ioredis')
|
const proxyLightABI = require('../abis/proxyLightABI.json')
|
||||||
const { GasPriceOracle } = require('gas-price-oracle')
|
|
||||||
const { Utils, Controller } = require('tornado-anonymity-mining')
|
|
||||||
|
|
||||||
const swapABI = require('../abis/swap.abi.json')
|
|
||||||
const miningABI = require('../abis/mining.abi.json')
|
|
||||||
const tornadoABI = require('../abis/tornadoABI.json')
|
|
||||||
const tornadoProxyABI = require('../abis/tornadoProxyABI.json')
|
|
||||||
const { queue } = require('./queue')
|
const { queue } = require('./queue')
|
||||||
const { poseidonHash2, getInstance, fromDecimals, sleep } = require('./utils')
|
const { getInstance, logRelayerError, clearRelayerErrors } = require('./utils')
|
||||||
const { jobType, status } = require('./constants')
|
const { jobType, status } = require('./constants')
|
||||||
const {
|
const { netId, gasPrices, privateKey, proxyLight, httpRpcUrl, tornadoServiceFee } = require('./config')
|
||||||
torn,
|
const { TxManager } = require('@tornado/tx-manager')
|
||||||
netId,
|
const { TornadoFeeOracleV5 } = require('@tornado/tornado-oracles')
|
||||||
redisUrl,
|
|
||||||
gasLimits,
|
|
||||||
instances,
|
|
||||||
privateKey,
|
|
||||||
httpRpcUrl,
|
|
||||||
oracleRpcUrl,
|
|
||||||
miningServiceFee,
|
|
||||||
tornadoServiceFee,
|
|
||||||
tornadoGoerliProxy,
|
|
||||||
} = require('./config')
|
|
||||||
const ENSResolver = require('./resolver')
|
|
||||||
const resolver = new ENSResolver()
|
|
||||||
const { TxManager } = require('tx-manager')
|
|
||||||
|
|
||||||
let web3
|
let web3
|
||||||
let currentTx
|
let currentTx
|
||||||
let currentJob
|
let currentJob
|
||||||
let tree
|
|
||||||
let txManager
|
let txManager
|
||||||
let controller
|
let tornadoProxyInstance
|
||||||
let swap
|
const feeOracle = new TornadoFeeOracleV5(netId, httpRpcUrl, gasPrices)
|
||||||
let minerContract
|
|
||||||
const redis = new Redis(redisUrl)
|
|
||||||
const redisSubscribe = new Redis(redisUrl)
|
|
||||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: oracleRpcUrl })
|
|
||||||
|
|
||||||
async function fetchTree() {
|
function start() {
|
||||||
const elements = await redis.get('tree:elements')
|
|
||||||
const convert = (_, val) => (typeof val === 'string' ? toBN(val) : val)
|
|
||||||
tree = MerkleTree.deserialize(JSON.parse(elements, convert), poseidonHash2)
|
|
||||||
|
|
||||||
if (currentTx && currentJob && ['MINING_REWARD', 'MINING_WITHDRAW'].includes(currentJob.data.type)) {
|
|
||||||
const { proof, args } = currentJob.data
|
|
||||||
if (toBN(args.account.inputRoot).eq(toBN(tree.root()))) {
|
|
||||||
console.log('Account root is up to date. Skipping Root Update operation...')
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
console.log('Account root is outdated. Starting Root Update operation...')
|
|
||||||
}
|
|
||||||
|
|
||||||
const update = await controller.treeUpdate(args.account.outputCommitment, tree)
|
|
||||||
|
|
||||||
const minerAddress = await resolver.resolve(torn.miningV2.address)
|
|
||||||
const instance = new web3.eth.Contract(miningABI, minerAddress)
|
|
||||||
const data =
|
|
||||||
currentJob.data.type === 'MINING_REWARD'
|
|
||||||
? instance.methods.reward(proof, args, update.proof, update.args).encodeABI()
|
|
||||||
: instance.methods.withdraw(proof, args, update.proof, update.args).encodeABI()
|
|
||||||
await currentTx.replace({
|
|
||||||
to: minerAddress,
|
|
||||||
data,
|
|
||||||
})
|
|
||||||
console.log('replaced pending tx')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function start() {
|
|
||||||
try {
|
try {
|
||||||
web3 = new Web3(httpRpcUrl)
|
web3 = new Web3(httpRpcUrl)
|
||||||
|
tornadoProxyInstance = new web3.eth.Contract(proxyLightABI, proxyLight)
|
||||||
|
clearRelayerErrors(redis)
|
||||||
const { CONFIRMATIONS, MAX_GAS_PRICE } = process.env
|
const { CONFIRMATIONS, MAX_GAS_PRICE } = process.env
|
||||||
|
|
||||||
|
const gasPriceOracleConfig = {
|
||||||
|
chainId: netId,
|
||||||
|
defaultRpc: httpRpcUrl,
|
||||||
|
defaultFallbackGasPrices: gasPrices,
|
||||||
|
minPriority: 0.05,
|
||||||
|
percentile: 5,
|
||||||
|
blocksCount: 20,
|
||||||
|
}
|
||||||
txManager = new TxManager({
|
txManager = new TxManager({
|
||||||
privateKey,
|
privateKey,
|
||||||
rpcUrl: httpRpcUrl,
|
rpcUrl: httpRpcUrl,
|
||||||
config: { CONFIRMATIONS, MAX_GAS_PRICE, THROW_ON_REVERT: false },
|
config: { CONFIRMATIONS, MAX_GAS_PRICE, THROW_ON_REVERT: false },
|
||||||
|
gasPriceOracleConfig,
|
||||||
})
|
})
|
||||||
swap = new web3.eth.Contract(swapABI, await resolver.resolve(torn.rewardSwap.address))
|
|
||||||
minerContract = new web3.eth.Contract(miningABI, await resolver.resolve(torn.miningV2.address))
|
|
||||||
redisSubscribe.subscribe('treeUpdate', fetchTree)
|
|
||||||
await fetchTree()
|
|
||||||
const provingKeys = {
|
|
||||||
treeUpdateCircuit: require('../keys/TreeUpdate.json'),
|
|
||||||
treeUpdateProvingKey: fs.readFileSync('./keys/TreeUpdate_proving_key.bin').buffer,
|
|
||||||
}
|
|
||||||
controller = new Controller({ provingKeys })
|
|
||||||
await controller.init()
|
|
||||||
queue.process(processJob)
|
queue.process(processJob)
|
||||||
console.log('Worker started')
|
console.log('Worker started')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logRelayerError(redis, e)
|
||||||
console.error('error on start worker', e.message)
|
console.error('error on start worker', e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkFee({ data }) {
|
async function checkTornadoFee({ data }, tx) {
|
||||||
if (data.type === jobType.TORNADO_WITHDRAW) {
|
const userProvidedFee = toBN(data.args[4])
|
||||||
return checkTornadoFee(data)
|
const { amount, decimals, currency } = getInstance(data.contract)
|
||||||
}
|
|
||||||
return checkMiningFee(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkTornadoFee({ args, contract }) {
|
const relayerDesiredFee = await feeOracle.calculateWithdrawalFeeViaRelayer({
|
||||||
const { currency, amount } = getInstance(contract)
|
tx,
|
||||||
const { decimals } = instances[`netId${netId}`][currency]
|
txType: 'relayer_withdrawal',
|
||||||
const [fee, refund] = [args[4], args[5]].map(toBN)
|
relayerFeePercent: tornadoServiceFee,
|
||||||
const { fast } = await gasPriceOracle.gasPrices()
|
currency,
|
||||||
|
amount,
|
||||||
|
decimals,
|
||||||
|
gasLimit: tx.gasLimit,
|
||||||
|
gasPrice: tx.gasPrice,
|
||||||
|
})
|
||||||
|
|
||||||
const ethPrice = await redis.hget('prices', currency)
|
|
||||||
const expense = toBN(toWei(fast.toString(), 'gwei')).mul(toBN(gasLimits[jobType.TORNADO_WITHDRAW]))
|
|
||||||
const feePercent = toBN(fromDecimals(amount, decimals))
|
|
||||||
.mul(toBN(parseInt(tornadoServiceFee * 1e10)))
|
|
||||||
.div(toBN(1e10 * 100))
|
|
||||||
let desiredFee
|
|
||||||
switch (currency) {
|
|
||||||
case 'eth': {
|
|
||||||
desiredFee = expense.add(feePercent)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
desiredFee = expense
|
|
||||||
.add(refund)
|
|
||||||
.mul(toBN(10 ** decimals))
|
|
||||||
.div(toBN(ethPrice))
|
|
||||||
desiredFee = desiredFee.add(feePercent)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(
|
console.log(
|
||||||
'sent fee, desired fee, feePercent',
|
'user-provided fee, desired fee',
|
||||||
fromWei(fee.toString()),
|
fromWei(userProvidedFee.toString()),
|
||||||
fromWei(desiredFee.toString()),
|
fromWei(toBN(relayerDesiredFee).toString()),
|
||||||
fromWei(feePercent.toString()),
|
|
||||||
)
|
)
|
||||||
if (fee.lt(desiredFee)) {
|
if (userProvidedFee.lt(toBN(relayerDesiredFee))) {
|
||||||
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
|
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkMiningFee({ args }) {
|
|
||||||
const { fast } = await gasPriceOracle.gasPrices()
|
|
||||||
const ethPrice = await redis.hget('prices', 'torn')
|
|
||||||
const isMiningReward = currentJob.data.type === jobType.MINING_REWARD
|
|
||||||
const providedFee = isMiningReward ? toBN(args.fee) : toBN(args.extData.fee)
|
|
||||||
|
|
||||||
const expense = toBN(toWei(fast.toString(), 'gwei')).mul(toBN(gasLimits[currentJob.data.type]))
|
|
||||||
const expenseInTorn = expense.mul(toBN(1e18)).div(toBN(ethPrice))
|
|
||||||
// todo make aggregator for ethPrices and rewardSwap data
|
|
||||||
const balance = await swap.methods.tornVirtualBalance().call()
|
|
||||||
const poolWeight = await swap.methods.poolWeight().call()
|
|
||||||
const expenseInPoints = Utils.reverseTornadoFormula({ balance, tokens: expenseInTorn, poolWeight })
|
|
||||||
/* eslint-disable */
|
|
||||||
const serviceFeePercent = isMiningReward
|
|
||||||
? toBN(0)
|
|
||||||
: toBN(args.amount)
|
|
||||||
.sub(providedFee) // args.amount includes fee
|
|
||||||
.mul(toBN(parseInt(miningServiceFee * 1e10)))
|
|
||||||
.div(toBN(1e10 * 100))
|
|
||||||
/* eslint-enable */
|
|
||||||
const desiredFee = expenseInPoints.add(serviceFeePercent) // in points
|
|
||||||
console.log(
|
|
||||||
'user provided fee, desired fee, serviceFeePercent',
|
|
||||||
providedFee.toString(),
|
|
||||||
desiredFee.toString(),
|
|
||||||
serviceFeePercent.toString(),
|
|
||||||
)
|
|
||||||
if (toBN(providedFee).lt(desiredFee)) {
|
|
||||||
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getProxyContract() {
|
|
||||||
let proxyAddress
|
|
||||||
|
|
||||||
if (netId === 5) {
|
|
||||||
proxyAddress = tornadoGoerliProxy
|
|
||||||
} else {
|
|
||||||
proxyAddress = await resolver.resolve(torn.tornadoProxy.address)
|
|
||||||
}
|
|
||||||
|
|
||||||
const contract = new web3.eth.Contract(tornadoProxyABI, proxyAddress)
|
|
||||||
|
|
||||||
return {
|
|
||||||
contract,
|
|
||||||
isOldProxy: checkOldProxy(proxyAddress),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkOldProxy(address) {
|
|
||||||
const OLD_PROXY = '0x905b63Fff465B9fFBF41DeA908CEb12478ec7601'
|
|
||||||
return toChecksumAddress(address) === toChecksumAddress(OLD_PROXY)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getTxObject({ data }) {
|
async function getTxObject({ data }) {
|
||||||
if (data.type === jobType.TORNADO_WITHDRAW) {
|
const calldata = tornadoProxyInstance.methods.withdraw(data.contract, data.proof, ...data.args).encodeABI()
|
||||||
let { contract, isOldProxy } = await getProxyContract()
|
|
||||||
|
|
||||||
let calldata = contract.methods.withdraw(data.contract, data.proof, ...data.args).encodeABI()
|
const incompleteTx = {
|
||||||
|
value: data.args[5],
|
||||||
if (isOldProxy && getInstance(data.contract).currency !== 'eth') {
|
to: tornadoProxyInstance._address,
|
||||||
contract = new web3.eth.Contract(tornadoABI, data.contract)
|
data: calldata,
|
||||||
calldata = contract.methods.withdraw(data.proof, ...data.args).encodeABI()
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
value: data.args[5],
|
|
||||||
to: contract._address,
|
|
||||||
data: calldata,
|
|
||||||
gasLimit: gasLimits['WITHDRAW_WITH_EXTRA'],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const method = data.type === jobType.MINING_REWARD ? 'reward' : 'withdraw'
|
|
||||||
const calldata = minerContract.methods[method](data.proof, data.args).encodeABI()
|
|
||||||
return {
|
|
||||||
to: minerContract._address,
|
|
||||||
data: calldata,
|
|
||||||
gasLimit: gasLimits[data.type],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const { gasLimit, gasPrice } = await feeOracle.getGasParams({
|
||||||
|
tx: incompleteTx,
|
||||||
|
txType: 'relayer_withdrawal',
|
||||||
|
})
|
||||||
|
|
||||||
|
return { ...incompleteTx, gasLimit, gasPrice }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function isOutdatedTreeRevert(receipt, currentTx) {
|
async function checkRecipient({ data }) {
|
||||||
try {
|
const recipient = data.args[2]
|
||||||
await web3.eth.call(currentTx.tx, receipt.blockNumber)
|
if (!isAddress(recipient)) throw new Error('Recipient address is invalid')
|
||||||
console.log('Simulated call successful')
|
|
||||||
return false
|
const addressCode = await web3.eth.getCode(toChecksumAddress(recipient))
|
||||||
} catch (e) {
|
if (addressCode !== '0x') throw new Error('Recipient cannot be a smart-contract, only EOA')
|
||||||
console.log('Decoded revert reason:', e.message)
|
|
||||||
return (
|
|
||||||
e.message.indexOf('Outdated account merkle root') !== -1 ||
|
|
||||||
e.message.indexOf('Outdated tree update merkle root') !== -1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processJob(job) {
|
async function processJob(job) {
|
||||||
@ -254,13 +111,11 @@ async function processJob(job) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitTx(job, retry = 0) {
|
async function submitTx(job) {
|
||||||
await checkFee(job)
|
await checkRecipient(job)
|
||||||
currentTx = await txManager.createTx(await getTxObject(job))
|
const tx = await getTxObject(job)
|
||||||
|
await checkTornadoFee(job, tx)
|
||||||
if (job.data.type !== jobType.TORNADO_WITHDRAW) {
|
currentTx = await txManager.createTx(tx)
|
||||||
await fetchTree()
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const receipt = await currentTx
|
const receipt = await currentTx
|
||||||
@ -278,35 +133,12 @@ async function submitTx(job, retry = 0) {
|
|||||||
if (receipt.status === 1) {
|
if (receipt.status === 1) {
|
||||||
await updateStatus(status.CONFIRMED)
|
await updateStatus(status.CONFIRMED)
|
||||||
} else {
|
} else {
|
||||||
if (job.data.type !== jobType.TORNADO_WITHDRAW && (await isOutdatedTreeRevert(receipt, currentTx))) {
|
throw new Error('Submitted transaction failed')
|
||||||
if (retry < 3) {
|
|
||||||
await updateStatus(status.RESUBMITTED)
|
|
||||||
await submitTx(job, retry + 1)
|
|
||||||
} else {
|
|
||||||
throw new Error('Tree update retry limit exceeded')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error('Submitted transaction failed')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// todo this could result in duplicated error logs
|
// todo this could result in duplicated error logs
|
||||||
// todo handle a case where account tree is still not up to date (wait and retry)?
|
// todo handle a case where account tree is still not up to date (wait and retry)?
|
||||||
if (
|
throw new Error(`Revert by smart contract ${e.message}`)
|
||||||
job.data.type !== jobType.TORNADO_WITHDRAW &&
|
|
||||||
(e.message.indexOf('Outdated account merkle root') !== -1 ||
|
|
||||||
e.message.indexOf('Outdated tree update merkle root') !== -1)
|
|
||||||
) {
|
|
||||||
if (retry < 5) {
|
|
||||||
await sleep(3000)
|
|
||||||
console.log('Tree is still not up to date, resubmitting')
|
|
||||||
await submitTx(job, retry + 1)
|
|
||||||
} else {
|
|
||||||
throw new Error('Tree update retry limit exceeded')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(`Revert by smart contract ${e.message}`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
require('chai').should()
|
require('chai').should()
|
||||||
|
|
||||||
const {
|
const { getTornadoWithdrawInputError } = require('../src/modules/validator')
|
||||||
getTornadoWithdrawInputError,
|
|
||||||
getMiningRewardInputError,
|
|
||||||
getMiningWithdrawInputError,
|
|
||||||
} = require('../src/validator')
|
|
||||||
|
|
||||||
describe('Validator', () => {
|
describe('Validator', () => {
|
||||||
describe('#getTornadoWithdrawInputError', () => {
|
describe('#getTornadoWithdrawInputError', () => {
|
||||||
@ -19,7 +15,13 @@ describe('Validator', () => {
|
|||||||
'.proof should match pattern "^0x[a-fA-F0-9]{512}$"',
|
'.proof should match pattern "^0x[a-fA-F0-9]{512}$"',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
it('should throw if unknown contract', () => {
|
||||||
|
const malformedData = { ...withdrawData }
|
||||||
|
malformedData.contract = '0xf17f52151ebef6c7334fad080c5704d77216b732'
|
||||||
|
getTornadoWithdrawInputError(malformedData).should.be.equal(
|
||||||
|
'.contract should pass "isKnownContract" keyword validation',
|
||||||
|
)
|
||||||
|
})
|
||||||
it('should throw something is missing', () => {
|
it('should throw something is missing', () => {
|
||||||
const malformedData = { ...withdrawData }
|
const malformedData = { ...withdrawData }
|
||||||
delete malformedData.proof
|
delete malformedData.proof
|
||||||
@ -35,56 +37,6 @@ describe('Validator', () => {
|
|||||||
malformedData.contract = withdrawData.contract
|
malformedData.contract = withdrawData.contract
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#getMiningRewardInputError', () => {
|
|
||||||
it('should work', () => {
|
|
||||||
getMiningRewardInputError(rewardData)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should throw for incorrect proof', () => {
|
|
||||||
const malformedData = { ...rewardData }
|
|
||||||
malformedData.proof = '0xbeef'
|
|
||||||
getMiningRewardInputError(malformedData).should.be.equal(
|
|
||||||
'.proof should match pattern "^0x[a-fA-F0-9]{512}$"',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should throw something is missing', () => {
|
|
||||||
const malformedData = { ...rewardData }
|
|
||||||
delete malformedData.proof
|
|
||||||
getMiningRewardInputError(malformedData).should.be.equal(" should have required property 'proof'")
|
|
||||||
malformedData.proof = rewardData.proof
|
|
||||||
|
|
||||||
delete malformedData.args
|
|
||||||
getMiningRewardInputError(malformedData).should.be.equal(" should have required property 'args'")
|
|
||||||
malformedData.args = rewardData.args
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('#getMiningWithdrawInputError', () => {
|
|
||||||
it('should work', () => {
|
|
||||||
getMiningWithdrawInputError(miningWithdrawData)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should throw for incorrect proof', () => {
|
|
||||||
const malformedData = { ...miningWithdrawData }
|
|
||||||
malformedData.proof = '0xbeef'
|
|
||||||
getMiningWithdrawInputError(malformedData).should.be.equal(
|
|
||||||
'.proof should match pattern "^0x[a-fA-F0-9]{512}$"',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should throw something is missing', () => {
|
|
||||||
const malformedData = { ...miningWithdrawData }
|
|
||||||
delete malformedData.proof
|
|
||||||
getMiningWithdrawInputError(malformedData).should.be.equal(" should have required property 'proof'")
|
|
||||||
malformedData.proof = miningWithdrawData.proof
|
|
||||||
|
|
||||||
delete malformedData.args
|
|
||||||
getMiningWithdrawInputError(malformedData).should.be.equal(" should have required property 'args'")
|
|
||||||
malformedData.args = miningWithdrawData.args
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const withdrawData = {
|
const withdrawData = {
|
||||||
@ -98,54 +50,5 @@ const withdrawData = {
|
|||||||
'0x000000000000000000000000000000000000000000000000058d15e176280000',
|
'0x000000000000000000000000000000000000000000000000058d15e176280000',
|
||||||
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
'0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||||
],
|
],
|
||||||
contract: '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
|
contract: '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
|
||||||
}
|
|
||||||
|
|
||||||
const rewardData = {
|
|
||||||
proof:
|
|
||||||
'0x2e0f4c76b35ce3275bf57492cbe12ddc76fae4eabdbeaacdcc7cd5255d0abb2325bd80b2a867f9c1bab854de5d7c443a18eb9ad796943dd53c30c04e8f0a37ae164916c932776b3c28dd49808a5d5e1648d8bc9006b2386096b88757644ce8f102f7e2f1505bb66385a1d53a101922a17d8ab653694dedd7d150ec71d543202e0f0a67e5d59904d75af1c52bef4dfac0a302c2beb2ca3bb29b6bbbe1038368702e5ba8d6d829d74968a94e321cc91cccbc0654f5df6460a0a6ad73b06c42b7d1289ff36655fc7106b5538bd2c6617dd0c313919331e63bcb4de9c9b45dc2207b098a5729efbecf79a4cab39ade3c99e5772bfbe5ae75d932facbf9e0910a34ae',
|
|
||||||
args: {
|
|
||||||
rate: '0x000000000000000000000000000000000000000000000000000000000000000a',
|
|
||||||
fee: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
instance: '0x8b3f5393bA08c24cc7ff5A66a832562aAB7bC95f',
|
|
||||||
rewardNullifier: '0x08fdc416b85c76d246925994ae0c0df539789fd1669c45b57104907c7ef8b0b5',
|
|
||||||
extDataHash: '0x006c5f12c20933beab10cfffab31ea0c9d736cf9aa868ee29eed3047d4ea4c2e',
|
|
||||||
depositRoot: '0x0405962838a47fb25ffd75d80d53b268654a06bc1bdde7e5ad94c675c2f2f0ff',
|
|
||||||
withdrawalRoot: '0x1cd83f5df5dbc826fecbf6be87f05db9c9dc617a3f1b1f3a421b1335c1ff7dbf',
|
|
||||||
extData: {
|
|
||||||
relayer: '0x0000000000000000000000000000000000000000',
|
|
||||||
encryptedAccount:
|
|
||||||
'0x6a8494fca4c433ef323d03f0db3fede90c3d2c6f216d73345ffc77ceec79622f327a83c4254063a3027620c262835e335fa32c33600a70547a53b2aa311d3ff35cf943e8f9e8f321f60d4266f680e0606a5837d78deb4d74c8b4fa3e9b67414513c71b73e38995cd8d57fd08aa9e135b342cecaf4128d4cfbb26148022e7a87da8b2423440b62034be202a6a48b45baa9736def6455771b442baaf2358fc52aa6c1d14a9a452b064d280fafd69f2a3ba416c10c1d8276f1c3810c664b24e0f1eefc75d63',
|
|
||||||
},
|
|
||||||
account: {
|
|
||||||
inputRoot: '0x22e875e5e54d8569fb40d0c568984e87b4c97da6383d8d8a334a79e22b48fd54',
|
|
||||||
inputNullifierHash: '0x24be972a00e3938a58f44ea6f8ead271ecdd6ab2cab42d1910fb7190b5816188',
|
|
||||||
outputRoot: '0x04a3cd1e37487dcee5da51cbce4245742903262a5824aef77fb7aff84a3cb053',
|
|
||||||
outputPathIndices: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
outputCommitment: '0x0ae58c1605312bd42fffdfc41d5e0f9a364ad458717c522bf9338068ab258601',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const miningWithdrawData = {
|
|
||||||
proof:
|
|
||||||
'0x087c02cdc5946b44f295e1adb8b65341708fe43854e44f05f205da6e46e2e4c4248b2dd5ee30236e7be2ea657265765b4e43dae263d67ff43190bb806faaafc10dd0a771f9d589b5061ddf0a713f27fc0b496d1b136dc4e98838b88f60efb072087c3018fa5c25b1f78b4bb968291b9afa3966d976e961d0a86719a8e07d771209dad29620f3bc2fc21c00510749a19e7ff369ade6b9fd1a7f05b74e70faee771fd839c710bd983927c9d3d5f39bb5e839a2ece19e899c4d50a91b29d5ac3f1a0e8faf7eeb2f6f672561bfba39bcb1d851f6c97d5c14b7fce6661cf315af3468119855a426fc4df511e848011bcdb704369deba20541a7651ab4d5813a60c056',
|
|
||||||
args: {
|
|
||||||
amount: '0x000000000000000000000000000000000000000000000000000000000000000f',
|
|
||||||
fee: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
||||||
extDataHash: '0x00d95a201b89061613b5bc539bcf8fdee63a400ea80f1f5e813d6aacfee3ec67',
|
|
||||||
extData: {
|
|
||||||
recipient: '0xf17f52151ebef6c7334fad080c5704d77216b732',
|
|
||||||
relayer: '0x0000000000000000000000000000000000000000',
|
|
||||||
encryptedAccount:
|
|
||||||
'0x4bd7f84edab796b390181d8b1dd850c418c8b3fe41d63b9677b7b99a2fadc505dcc70df336a42847dc00fa39175d16ddfec0d80dc166282e024b5371f561467651ed94e71524fa2e365a8330b053d5cff7c3bcc3564b335fb9e74fb805a3a6e760b811db60e5d6b4e154376196c3cb61457bac6d5ea804f63208a389555cde72f40ab1b94705e728f692e699fc441504b9df34390b3992a1a1eac160dcf0df0b5c5a9ec9cd6c0c8f5f8aa11627fdf2b3bedece5836e9ca38b09d70ff7ba06702971d245d',
|
|
||||||
},
|
|
||||||
account: {
|
|
||||||
inputRoot: '0x1a756aeee7f7d05f276b20c8ca83150e110e1a436c2d959e501ab306420ab536',
|
|
||||||
inputNullifierHash: '0x0dc8ea0330171a1f868ef5f3f9f92e919d7be754846f6145c5e7819e87738e65',
|
|
||||||
outputRoot: '0x0d9d85371bd8c941400ae54815491799e98d1f335a9d263e41f0b81f22b55aa8',
|
|
||||||
outputPathIndices: '0x0000000000000000000000000000000000000000000000000000000000000001',
|
|
||||||
outputCommitment: '0x1ebd38a8bc53f47386687386397c8b5cefd33d55341b62a2a576b39d9bcec57c',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
15
tornado-stream.conf
Normal file
15
tornado-stream.conf
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
map $ssl_preread_server_name $name {
|
||||||
|
yourdomain.com tornado_mainnet;
|
||||||
|
|
||||||
|
default tornado_mainnet;
|
||||||
|
}
|
||||||
|
|
||||||
|
upstream tornado_mainnet {
|
||||||
|
server 127.0.0.1:4380;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 0.0.0.0:443;
|
||||||
|
proxy_pass $name;
|
||||||
|
ssl_preread on;
|
||||||
|
}
|
87
tornado.conf
Normal file
87
tornado.conf
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
|
||||||
|
# scheme used to connect to this server
|
||||||
|
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
|
||||||
|
default $http_x_forwarded_proto;
|
||||||
|
'' $scheme;
|
||||||
|
}
|
||||||
|
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
|
||||||
|
# server port the client connected to
|
||||||
|
map $http_x_forwarded_port $proxy_x_forwarded_port {
|
||||||
|
default $http_x_forwarded_port;
|
||||||
|
'' $server_port;
|
||||||
|
}
|
||||||
|
# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any
|
||||||
|
# Connection header that may have been passed to this server
|
||||||
|
map $http_upgrade $proxy_connection {
|
||||||
|
default upgrade;
|
||||||
|
'' close;
|
||||||
|
}
|
||||||
|
# Apply fix for very long server names
|
||||||
|
server_names_hash_bucket_size 128;
|
||||||
|
# Default dhparam
|
||||||
|
# Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto
|
||||||
|
map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
|
||||||
|
default off;
|
||||||
|
https on;
|
||||||
|
}
|
||||||
|
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
||||||
|
log_format vhost '$host $remote_addr - $remote_user [$time_local] '
|
||||||
|
'"$request" $status $body_bytes_sent '
|
||||||
|
'"$http_referer" "$http_user_agent" '
|
||||||
|
'"$upstream_addr"';
|
||||||
|
# HTTP 1.1 support
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection $proxy_connection;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
|
||||||
|
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
|
||||||
|
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
|
||||||
|
proxy_set_header X-Original-URI $request_uri;
|
||||||
|
# Mitigate httpoxy attack (see README for details)
|
||||||
|
proxy_set_header Proxy "";
|
||||||
|
|
||||||
|
# Request rate limiting per second, 2Mb zone @ 5 requests per second
|
||||||
|
limit_req_zone $binary_remote_addr zone=one:2m rate=5r/s;
|
||||||
|
# Connections per IP limited to 2
|
||||||
|
limit_conn_zone $binary_remote_addr zone=two:2m;
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name _; # This is just an invalid value which will never trigger on a real hostname.
|
||||||
|
server_tokens off;
|
||||||
|
listen 80;
|
||||||
|
access_log /var/log/nginx/access.log vhost;
|
||||||
|
return 503;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name yourdomain.com;
|
||||||
|
|
||||||
|
# Connection timeouts
|
||||||
|
client_body_timeout 10s;
|
||||||
|
client_header_timeout 10s;
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
access_log /var/log/nginx/access.log vhost;
|
||||||
|
|
||||||
|
# Do not HTTPS redirect LetsEncrypt ACME challenge
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
limit_req zone=one;
|
||||||
|
limit_conn two 1;
|
||||||
|
|
||||||
|
proxy_pass http://127.0.0.1:8000;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
limit_req zone=one;
|
||||||
|
limit_conn two 1;
|
||||||
|
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user