Compare commits
10 Commits
main
...
mainnet-v5
Author | SHA1 | Date | |
---|---|---|---|
52473197ea | |||
cb1212d793 | |||
531567d8b2 | |||
67c0782794 | |||
40c55b3e7c | |||
fd72f09e11 | |||
fdbbb05733 | |||
eb908ff1f5 | |||
44f70bd41d | |||
16a17079eb |
@ -1,6 +1,6 @@
|
||||
NET_ID=1
|
||||
HTTP_RPC_URL=https://api.securerpc.com/v1
|
||||
# WS_RPC_URL=wss://mainnet.infura.io/ws/v3/
|
||||
WS_RPC_URL=wss://mainnet.infura.io/ws/v3/
|
||||
# ORACLE_RPC_URL should always point to the mainnet
|
||||
ORACLE_RPC_URL=https://api.securerpc.com/v1
|
||||
REDIS_URL=redis://127.0.0.1:6379
|
||||
@ -13,7 +13,7 @@ APP_PORT=8000
|
||||
# without 0x prefix
|
||||
PRIVATE_KEY=
|
||||
# 0.05 means 0.05%
|
||||
REGULAR_TORNADO_WITHDRAW_FEE=0.35
|
||||
RELAYER_FEE=0.4
|
||||
MINING_SERVICE_FEE=0.05
|
||||
REWARD_ACCOUNT=
|
||||
CONFIRMATIONS=4
|
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
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
|
68
README.md
68
README.md
@ -1,8 +1,8 @@
|
||||
# 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)
|
||||
|
||||
__*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 deloyed smart contracts. Please understand the laws where you live and take all necessary steps to protect and anonomize yourself.__
|
||||
__*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.__
|
||||
|
||||
__*It is recommended to run your Relayer on a VPS instnace ([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).__
|
||||
__*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).__
|
||||
|
||||
## Deploy with docker-compose (recommended)
|
||||
|
||||
@ -29,73 +29,47 @@ _* Warning: Failure to configure SSH as the first UFW rule, will lock you out of
|
||||
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. Finalise changes and enable firewall `ufw enable`
|
||||
4. Finalize changes and enable firewall `ufw enable`
|
||||
|
||||
__NETWORK DEPLOYMENT OPTIONS__
|
||||
|
||||
_Ethereum (eth), Binance (bnb), Gnosis (xdai), Polygon (matic), Optimisim (op), Arbitrum (arb) and Goerli (geth)_
|
||||
|
||||
__SINGLE NETWORK DEPLOYMENT__
|
||||
__DEPLOYMENT__
|
||||
1. Clone the repository and enter the directory
|
||||
- `git clone https://development.tornadocash.community/tornadocash/classic-relayer && cd classic-relayer`
|
||||
2. Clone the example enviroment file `.env.example` to configure for the perferred network
|
||||
- By default each network is preconfigured the naming of `.env.<NETWORK SYMBOL>`
|
||||
- `cp .env.example .env.eth`
|
||||
- `git clone https://git.tornado.ws/tornadocash/classic-relayer -b mainnet-v5 && cd classic-relayer`
|
||||
2. Clone the example environment file `.env.example` to configure for the preferred network - `cp .env.example .env` , then fill `.env` file.
|
||||
- Set `PRIVATE_KEY` for your relayer address (remove the 0x from your private key)
|
||||
- Set `VIRTUAL_HOST` and `LETSENCRYPT_HOST` to your domain address
|
||||
- add a A record DNS record with the value assigned to your instance IP address to configure the domain
|
||||
- Set `RELAYER_FEE` to what you would like to charge as your fee (remember 0.3% is deducted from your staked relayer balance)
|
||||
- Set `RPC_URL` to a non-censoring RPC endpoint (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. Uncomment the `env_file` lines (remove `# `) for the associated network services in `docker-compose.yml`
|
||||
5. Build and deploy the docker source by specifying the network through `--profile <NETWORK_SYMBOL>`
|
||||
- `docker-compose --profile eth up -d`
|
||||
5. Build and deploy the docker source by specifying the network through:
|
||||
|
||||
- `npm run build`
|
||||
- `docker-compose up -d`
|
||||
5. Visit your domain address and check the `/status` endpoint and ensure there is no errors in the `status` field
|
||||
|
||||
__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 configuraiton to include streams
|
||||
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 configruation
|
||||
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`
|
||||
|
||||
__MULTIPLE NETWORK DEPLOYMENT__
|
||||
1. Setup the instructions stated to setup an nginx reverse proxy
|
||||
2. Clone the example enviroment file `.env.example` for the networks of choice and configure
|
||||
- By default each network is preconfigured the naming of `.env.<NETWORK SYMBOL>`
|
||||
- `cp .env.example .env.eth`
|
||||
- `cp .env.example .env.bnb`
|
||||
- `cp .env.example .env.arb`
|
||||
- `cp .env.example .env.op`
|
||||
- `cp .env.example .env.xdai`
|
||||
- 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
|
||||
- ensure that the parent domain domain is specified first with the subdomain after sperated by a comma for SAN certificates
|
||||
- eg: `VIRTUAL_HOST=example.com,eth.example.com` and `LETSENCRYPT_HOST=example.com,eth.example.com`
|
||||
- add a A wildcard record DNS record with the value assigned to your instance IP address to configure submdomains
|
||||
- Set `RELAYER_FEE` to what you would like to charge as your fee (remember 0.3% is deducted from your staked relayer balance)
|
||||
- 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
|
||||
3. Uncomment the `env_file` lines (remove `# `) for the associated network services in `docker-compose.yml`
|
||||
4. Build and deploy the docker source for the configured neworks specified via `--profile <NETWORK_SYMBOL>`
|
||||
- `docker-compose --profile eth --profile bnb --profile arb --profile --profile op --profile xdai up -d`
|
||||
5. Visit your domain addresses and check each `/status` endpoint to ensure there is no errors in the `status` fields
|
||||
|
||||
## Run locally
|
||||
|
||||
1. `npm i`
|
||||
2. `cp .env.example .env`
|
||||
3. Modify `.env` as needed
|
||||
4. `npm run start`
|
||||
5. Go to `http://127.0.0.1:8000`
|
||||
6. In order to execute withdraw request, you can run following command
|
||||
1. `npm i -g yarn` (if yarn not installed in your system)
|
||||
2. `yarn`
|
||||
3. `cp .env.mainnet.example .env`
|
||||
4. Modify `.env` as needed
|
||||
5. `yarn start`
|
||||
6. Go to `http://127.0.0.1:8000`
|
||||
7. In order to execute withdraw request, you can run following command
|
||||
|
||||
```bash
|
||||
curl -X POST -H 'content-type:application/json' --data '<input data>' http://127.0.0.1:8000/relay
|
||||
|
@ -1,181 +0,0 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "contract MultiWrapper", "name": "_multiWrapper", "type": "address" },
|
||||
{ "internalType": "contract IOracle[]", "name": "existingOracles", "type": "address[]" },
|
||||
{ "internalType": "enum OffchainOracle.OracleType[]", "name": "oracleTypes", "type": "uint8[]" },
|
||||
{ "internalType": "contract IERC20[]", "name": "existingConnectors", "type": "address[]" },
|
||||
{ "internalType": "contract IERC20", "name": "wBase", "type": "address" }
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{ "indexed": false, "internalType": "contract IERC20", "name": "connector", "type": "address" }
|
||||
],
|
||||
"name": "ConnectorAdded",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{ "indexed": false, "internalType": "contract IERC20", "name": "connector", "type": "address" }
|
||||
],
|
||||
"name": "ConnectorRemoved",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{ "indexed": false, "internalType": "contract MultiWrapper", "name": "multiWrapper", "type": "address" }
|
||||
],
|
||||
"name": "MultiWrapperUpdated",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{ "indexed": false, "internalType": "contract IOracle", "name": "oracle", "type": "address" },
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "enum OffchainOracle.OracleType",
|
||||
"name": "oracleType",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "OracleAdded",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{ "indexed": false, "internalType": "contract IOracle", "name": "oracle", "type": "address" },
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "enum OffchainOracle.OracleType",
|
||||
"name": "oracleType",
|
||||
"type": "uint8"
|
||||
}
|
||||
],
|
||||
"name": "OracleRemoved",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "contract IERC20", "name": "connector", "type": "address" }],
|
||||
"name": "addConnector",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "contract IOracle", "name": "oracle", "type": "address" },
|
||||
{ "internalType": "enum OffchainOracle.OracleType", "name": "oracleKind", "type": "uint8" }
|
||||
],
|
||||
"name": "addOracle",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "connectors",
|
||||
"outputs": [{ "internalType": "contract IERC20[]", "name": "allConnectors", "type": "address[]" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "contract IERC20", "name": "srcToken", "type": "address" },
|
||||
{ "internalType": "contract IERC20", "name": "dstToken", "type": "address" },
|
||||
{ "internalType": "bool", "name": "useWrappers", "type": "bool" }
|
||||
],
|
||||
"name": "getRate",
|
||||
"outputs": [{ "internalType": "uint256", "name": "weightedRate", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "contract IERC20", "name": "srcToken", "type": "address" },
|
||||
{ "internalType": "bool", "name": "useSrcWrappers", "type": "bool" }
|
||||
],
|
||||
"name": "getRateToEth",
|
||||
"outputs": [{ "internalType": "uint256", "name": "weightedRate", "type": "uint256" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "multiWrapper",
|
||||
"outputs": [{ "internalType": "contract MultiWrapper", "name": "", "type": "address" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "oracles",
|
||||
"outputs": [
|
||||
{ "internalType": "contract IOracle[]", "name": "allOracles", "type": "address[]" },
|
||||
{ "internalType": "enum OffchainOracle.OracleType[]", "name": "oracleTypes", "type": "uint8[]" }
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "owner",
|
||||
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "contract IERC20", "name": "connector", "type": "address" }],
|
||||
"name": "removeConnector",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{ "internalType": "contract IOracle", "name": "oracle", "type": "address" },
|
||||
{ "internalType": "enum OffchainOracle.OracleType", "name": "oracleKind", "type": "uint8" }
|
||||
],
|
||||
"name": "removeOracle",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "renounceOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "contract MultiWrapper", "name": "_multiWrapper", "type": "address" }],
|
||||
"name": "setMultiWrapper",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }],
|
||||
"name": "transferOwnership",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
@ -1,13 +1,14 @@
|
||||
version: '2'
|
||||
|
||||
services:
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
restart: always
|
||||
command: [ redis-server, --appendonly, 'yes' ]
|
||||
command: [redis-server, --appendonly, 'yes']
|
||||
volumes:
|
||||
- redis:/data
|
||||
ports:
|
||||
- '127.0.0.1:6379:6379'
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
@ -49,7 +50,7 @@ services:
|
||||
eth-server:
|
||||
build: .
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'eth' ]
|
||||
profiles: ['eth']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.eth
|
||||
@ -57,51 +58,51 @@ services:
|
||||
NET_ID: 1
|
||||
REDIS_URL: redis://redis/0
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
eth-treeWatcher:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'eth' ]
|
||||
profiles: ['eth']
|
||||
restart: always
|
||||
command: treeWatcher
|
||||
env_file: .env.eth
|
||||
environment:
|
||||
NET_ID: 1
|
||||
REDIS_URL: redis://redis/0
|
||||
depends_on: [ redis, eth-server ]
|
||||
depends_on: [redis, eth-server]
|
||||
|
||||
eth-priceWatcher:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'eth' ]
|
||||
profiles: ['eth']
|
||||
restart: always
|
||||
command: priceWatcher
|
||||
env_file: .env.eth
|
||||
environment:
|
||||
NET_ID: 1
|
||||
REDIS_URL: redis://redis/0
|
||||
depends_on: [ redis, eth-server ]
|
||||
depends_on: [redis, eth-server]
|
||||
|
||||
eth-healthWatcher:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'eth' ]
|
||||
profiles: ['eth']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.eth
|
||||
environment:
|
||||
NET_ID: 1
|
||||
REDIS_URL: redis://redis/0
|
||||
depends_on: [ redis, eth-server ]
|
||||
depends_on: [redis, eth-server]
|
||||
|
||||
eth-worker1:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'eth' ]
|
||||
profiles: ['eth']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.eth
|
||||
environment:
|
||||
NET_ID: 1
|
||||
REDIS_URL: redis://redis/0
|
||||
depends_on: [ redis, eth-server ]
|
||||
depends_on: [redis, eth-server]
|
||||
|
||||
# # This is additional worker for ethereum mainnet
|
||||
# # So you can process transactions from multiple addresses, but before it you need to set up those addresses as workers
|
||||
@ -168,7 +169,7 @@ services:
|
||||
|
||||
bsc-server:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'bsc' ]
|
||||
profiles: ['bsc']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.bsc
|
||||
@ -176,29 +177,29 @@ services:
|
||||
NET_ID: 56
|
||||
REDIS_URL: redis://redis/1
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
bsc-healthWatcher:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'bsc' ]
|
||||
profiles: ['bsc']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.bsc
|
||||
environment:
|
||||
NET_ID: 56
|
||||
REDIS_URL: redis://redis/1
|
||||
depends_on: [ redis, bsc-server ]
|
||||
depends_on: [redis, bsc-server]
|
||||
|
||||
bsc-worker1:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'bsc' ]
|
||||
profiles: ['bsc']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.bsc
|
||||
environment:
|
||||
NET_ID: 56
|
||||
REDIS_URL: redis://redis/1
|
||||
depends_on: [ redis, bsc-server ]
|
||||
depends_on: [redis, bsc-server]
|
||||
|
||||
# -------------------------------------------------- #
|
||||
|
||||
@ -206,7 +207,7 @@ services:
|
||||
|
||||
polygon-server:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'polygon' ]
|
||||
profiles: ['polygon']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.polygon
|
||||
@ -214,29 +215,29 @@ services:
|
||||
NET_ID: 137
|
||||
REDIS_URL: redis://redis/2
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
polygon-healthWatcher:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'polygon' ]
|
||||
profiles: ['polygon']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.polygon
|
||||
environment:
|
||||
NET_ID: 137
|
||||
REDIS_URL: redis://redis/2
|
||||
depends_on: [ redis, polygon-server ]
|
||||
depends_on: [redis, polygon-server]
|
||||
|
||||
polygon-worker1:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'polygon' ]
|
||||
profiles: ['polygon']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.polygon
|
||||
environment:
|
||||
NET_ID: 137
|
||||
REDIS_URL: redis://redis/2
|
||||
depends_on: [ redis, polygon-server ]
|
||||
depends_on: [redis, polygon-server]
|
||||
|
||||
# -------------------------------------------------- #
|
||||
|
||||
@ -244,7 +245,7 @@ services:
|
||||
|
||||
gnosis-server:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'gnosis' ]
|
||||
profiles: ['gnosis']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.gnosis
|
||||
@ -252,29 +253,29 @@ services:
|
||||
NET_ID: 100
|
||||
REDIS_URL: redis://redis/3
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
gnosis-healthWatcher:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'gnosis' ]
|
||||
profiles: ['gnosis']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.gnosis
|
||||
environment:
|
||||
NET_ID: 100
|
||||
REDIS_URL: redis://redis/3
|
||||
depends_on: [ redis, gnosis-server ]
|
||||
depends_on: [redis, gnosis-server]
|
||||
|
||||
gnosis-worker1:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'gnosis' ]
|
||||
profiles: ['gnosis']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.gnosis
|
||||
environment:
|
||||
NET_ID: 100
|
||||
REDIS_URL: redis://redis/3
|
||||
depends_on: [ redis, gnosis-server ]
|
||||
depends_on: [redis, gnosis-server]
|
||||
|
||||
# -------------------------------------------------- #
|
||||
|
||||
@ -282,7 +283,7 @@ services:
|
||||
|
||||
avax-server:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'avax' ]
|
||||
profiles: ['avax']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.avax
|
||||
@ -290,29 +291,29 @@ services:
|
||||
NET_ID: 43114
|
||||
REDIS_URL: redis://redis/4
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
avax-healthWatcher:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'avax' ]
|
||||
profiles: ['avax']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.avax
|
||||
environment:
|
||||
NET_ID: 43114
|
||||
REDIS_URL: redis://redis/4
|
||||
depends_on: [ redis, avax-server ]
|
||||
depends_on: [redis, avax-server]
|
||||
|
||||
avax-worker1:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'avax' ]
|
||||
profiles: ['avax']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.avax
|
||||
environment:
|
||||
NET_ID: 43114
|
||||
REDIS_URL: redis://redis/4
|
||||
depends_on: [ redis, avax-server ]
|
||||
depends_on: [redis, avax-server]
|
||||
|
||||
# -------------------------------------------------- #
|
||||
|
||||
@ -320,7 +321,7 @@ services:
|
||||
|
||||
op-server:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'op' ]
|
||||
profiles: ['op']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.op
|
||||
@ -328,29 +329,29 @@ services:
|
||||
NET_ID: 10
|
||||
REDIS_URL: redis://redis/5
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
op-healthWatcher:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'op' ]
|
||||
profiles: ['op']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.op
|
||||
environment:
|
||||
NET_ID: 10
|
||||
REDIS_URL: redis://redis/5
|
||||
depends_on: [ redis, op-server ]
|
||||
depends_on: [redis, op-server]
|
||||
|
||||
op-worker1:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'op' ]
|
||||
profiles: ['op']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.op
|
||||
environment:
|
||||
NET_ID: 10
|
||||
REDIS_URL: redis://redis/5
|
||||
depends_on: [ redis, op-server ]
|
||||
depends_on: [redis, op-server]
|
||||
|
||||
# -------------------------------------------------- #
|
||||
|
||||
@ -358,7 +359,7 @@ services:
|
||||
|
||||
arb-server:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'arb' ]
|
||||
profiles: ['arb']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.arb
|
||||
@ -366,29 +367,29 @@ services:
|
||||
NET_ID: 42161
|
||||
REDIS_URL: redis://redis/6
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
arb-healthWatcher:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'arb' ]
|
||||
profiles: ['arb']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.arb
|
||||
environment:
|
||||
NET_ID: 42161
|
||||
REDIS_URL: redis://redis/6
|
||||
depends_on: [ redis, arb-server ]
|
||||
depends_on: [redis, arb-server]
|
||||
|
||||
arb-worker1:
|
||||
image: tornadorelayer:sidechain
|
||||
profiles: [ 'arb' ]
|
||||
profiles: ['arb']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.arb
|
||||
environment:
|
||||
NET_ID: 42161
|
||||
REDIS_URL: redis://redis/6
|
||||
depends_on: [ redis, arb-server ]
|
||||
depends_on: [redis, arb-server]
|
||||
|
||||
# -------------------------------------------------- #
|
||||
|
||||
@ -396,7 +397,7 @@ services:
|
||||
|
||||
goerli-server:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'geth' ]
|
||||
profiles: ['geth']
|
||||
restart: always
|
||||
command: server
|
||||
env_file: .env.goerli
|
||||
@ -404,51 +405,51 @@ services:
|
||||
NET_ID: 5
|
||||
REDIS_URL: redis://redis/7
|
||||
nginx_proxy_read_timeout: 600
|
||||
depends_on: [ redis ]
|
||||
depends_on: [redis]
|
||||
|
||||
goerli-treeWatcher:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'goerli' ]
|
||||
profiles: ['goerli']
|
||||
restart: always
|
||||
command: treeWatcher
|
||||
env_file: .env.goerli
|
||||
environment:
|
||||
NET_ID: 5
|
||||
REDIS_URL: redis://redis/7
|
||||
depends_on: [ redis, goerli-server ]
|
||||
depends_on: [redis, goerli-server]
|
||||
|
||||
goerli-priceWatcher:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'goerli' ]
|
||||
profiles: ['goerli']
|
||||
restart: always
|
||||
command: priceWatcher
|
||||
env_file: .env.goerli
|
||||
environment:
|
||||
NET_ID: 5
|
||||
REDIS_URL: redis://redis/7
|
||||
depends_on: [ redis, goerli-server ]
|
||||
depends_on: [redis, goerli-server]
|
||||
|
||||
goerli-healthWatcher:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'goerli' ]
|
||||
profiles: ['goerli']
|
||||
restart: always
|
||||
command: healthWatcher
|
||||
env_file: .env.goerli
|
||||
environment:
|
||||
NET_ID: 5
|
||||
REDIS_URL: redis://redis/7
|
||||
depends_on: [ redis, goerli-server ]
|
||||
depends_on: [redis, goerli-server]
|
||||
|
||||
goerli-worker1:
|
||||
image: tornadorelayer:mainnet
|
||||
profiles: [ 'goerli' ]
|
||||
profiles: ['goerli']
|
||||
restart: always
|
||||
command: worker
|
||||
env_file: .env.goerli
|
||||
environment:
|
||||
NET_ID: 5
|
||||
REDIS_URL: redis://redis/7
|
||||
depends_on: [ redis, goerli-server ]
|
||||
depends_on: [redis, goerli-server]
|
||||
# -------------------------------------------------- #
|
||||
|
||||
volumes:
|
||||
|
109
install.sh
109
install.sh
@ -1,109 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Script must be running from root
|
||||
if [ "$EUID" -ne 0 ];
|
||||
then echo "Please run as root";
|
||||
exit 1;
|
||||
fi;
|
||||
|
||||
relayer_soft_git_repo="https://git.tornado.ws/tornadocash/classic-relayer";
|
||||
|
||||
user_home_dir=$(eval echo ~$USER);
|
||||
relayer_folder="$user_home_dir/tornado-relayer";
|
||||
relayer_mainnet_soft_source_folder="$relayer_folder/mainnet-soft-source";
|
||||
relayer_sidechains_soft_source_folder="$relayer_folder/sidechains-soft-source";
|
||||
script_log_file="/tmp/tornado-classic-relayer-installation.log"
|
||||
if [ -f $script_log_file ]; then rm $script_log_file; fi;
|
||||
|
||||
function echo_log_err(){
|
||||
echo $1 1>&2;
|
||||
echo -e "$1\n" &>> $script_log_file;
|
||||
}
|
||||
|
||||
function echo_log_err_and_exit(){
|
||||
echo_log_err "$1";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
function is_package_installed(){
|
||||
if [ $(dpkg-query -W -f='${Status}' $1 2>/dev/null | grep -c "ok installed") -eq 0 ]; then return 1; else return 0; fi;
|
||||
}
|
||||
|
||||
function install_requred_packages(){
|
||||
apt update &>> $script_log_file;
|
||||
|
||||
requred_packages=("curl" "git-all" "ufw" "nginx");
|
||||
local package;
|
||||
for package in ${requred_packages[@]}; do
|
||||
if ! is_package_installed $package; then
|
||||
# Kill apache process, because Debian configuring nginx package right during installation
|
||||
if [ $package = "nginx" ]; then systemctl stop apache2; fi;
|
||||
apt install --yes --force-yes -o DPkg::Options::="--force-confold" $package &>> $script_log_file;
|
||||
if ! is_package_installed $package; then
|
||||
echo_log_err_and_exit "Error: cannot install \"$package\" package";
|
||||
fi;
|
||||
fi;
|
||||
done;
|
||||
|
||||
echo -e "\nAll required packages installed successfully";
|
||||
}
|
||||
|
||||
function install_repositories(){
|
||||
git clone $relayer_soft_git_repo -b mainnet $relayer_mainnet_soft_source_folder;
|
||||
git clone $relayer_soft_git_repo -b sidechain $relayer_sidechains_soft_source_folder;
|
||||
}
|
||||
|
||||
function install_docker_utilities(){
|
||||
local kernel_name=$(uname -s);
|
||||
local processor_type=$(uname -m);
|
||||
|
||||
curl -SL https://github.com/docker/compose/releases/download/v2.16.0/docker-compose-$kernel_name-$processor_type -o /usr/local/bin/docker-compose;
|
||||
chmod +x /usr/local/bin/docker-compose;
|
||||
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh;
|
||||
chmod +x get-docker.sh;
|
||||
./get-docker.sh;
|
||||
}
|
||||
|
||||
function configure_firewall(){
|
||||
ufw allow https/tcp;
|
||||
ufw allow http/tcp;
|
||||
ufw insert 1 allow OpenSSH;
|
||||
echo "y" | ufw enable;
|
||||
}
|
||||
|
||||
function configure_nginx_reverse_proxy(){
|
||||
systemctl stop apache2;
|
||||
|
||||
cp $relayer_mainnet_soft_source_folder/tornado.conf /etc/nginx/sites-available/default;
|
||||
echo "stream { map_hash_bucket_size 128; map_hash_max_size 128; include /etc/nginx/conf.d/streams/*.conf; }" >> /etc/nginx/nginx.conf;
|
||||
mkdir /etc/nginx/conf.d/streams;
|
||||
cp $relayer_mainnet_soft_source_folder/tornado-stream.conf /etc/nginx/conf.d/streams/tornado-stream.conf;
|
||||
|
||||
systemctl restart nginx;
|
||||
systemctl stop nginx;
|
||||
}
|
||||
|
||||
function build_relayer_docker_containers(){
|
||||
docker build -t tornadorelayer:mainnet $relayer_mainnet_soft_source_folder;
|
||||
docker build -t tornadorelayer:sidechain $relayer_sidechains_soft_source_folder;
|
||||
}
|
||||
|
||||
function prepare_environments(){
|
||||
cp $relayer_mainnet_soft_source_folder/.env.example $relayer_folder/.env.eth;
|
||||
tee $relayer_folder/.env.bsc $relayer_folder/.env.arb $relayer_folder/.env.goerli $relayer_folder/.env.polygon $relayer_folder/.env.op \
|
||||
$relayer_folder/.env.avax $relayer_folder/.env.gnosis < $relayer_sidechains_soft_source_folder/.env.example > /dev/null;
|
||||
cp $relayer_mainnet_soft_source_folder/docker-compose.yml $relayer_folder/docker-compose.yml;
|
||||
}
|
||||
|
||||
function main(){
|
||||
install_requred_packages;
|
||||
install_repositories;
|
||||
configure_firewall;
|
||||
configure_nginx_reverse_proxy;
|
||||
install_docker_utilities;
|
||||
build_relayer_docker_containers;
|
||||
prepare_environments;
|
||||
cd $relayer_folder;
|
||||
}
|
||||
|
||||
main;
|
22
package.json
22
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "relay",
|
||||
"version": "4.1.4",
|
||||
"description": "Relayer for Tornado.cash privacy solution. https://tornado.cash",
|
||||
"version": "5.2.0",
|
||||
"description": "Relayer for Tornado.cash privacy solution.",
|
||||
"scripts": {
|
||||
"server": "node src/server.js",
|
||||
"worker": "node src/worker",
|
||||
@ -13,25 +13,27 @@
|
||||
"prettier:fix": "npx prettier --write . --config .prettierrc",
|
||||
"lint": "yarn eslint && yarn prettier:check",
|
||||
"test": "mocha",
|
||||
"start": "yarn server & yarn priceWatcher & yarn treeWatcher & yarn worker & yarn healthWatcher"
|
||||
"build": "docker build -t tornadocash/relayer:mainnet-v5 .",
|
||||
"start": "docker-compose up -d redis && concurrently \"yarn server\" \"yarn priceWatcher\" \"yarn treeWatcher\" \"yarn worker\" \"yarn healthWatcher\""
|
||||
},
|
||||
"author": "tornado.cash",
|
||||
"author": "Tornado Cash team",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tornado/anonymity-mining": "^2.1.5",
|
||||
"@tornado/circomlib": "^0.0.21",
|
||||
"@tornado/fixed-merkle-tree": "0.4.0",
|
||||
"@tornado/tornado-config": "^1",
|
||||
"@tornado/tornado-oracles": "^3.3.0",
|
||||
"@tornado/tx-manager": "^0.4.9",
|
||||
"ajv": "^6.12.5",
|
||||
"async-mutex": "^0.2.4",
|
||||
"bull": "^3.12.1",
|
||||
"circomlib": "git+https://github.com/tornadocash/circomlib.git#3b492f9801573eebcfe1b6c584afe8a3beecf2b4",
|
||||
"concurrently": "^8.2.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"eth-ens-namehash": "^2.0.8",
|
||||
"express": "^4.17.1",
|
||||
"fixed-merkle-tree": "^0.4.0",
|
||||
"gas-price-oracle": "^0.4.7",
|
||||
"ioredis": "^4.14.1",
|
||||
"node-fetch": "^2.6.7",
|
||||
"torn-token": "1.0.6",
|
||||
"tornado-anonymity-mining": "^2.1.2",
|
||||
"tx-manager": "^0.4.8",
|
||||
"uuid": "^8.3.0",
|
||||
"web3": "^1.3.0",
|
||||
"web3-core-promievent": "^1.3.0",
|
||||
|
@ -1,28 +1,24 @@
|
||||
require('dotenv').config()
|
||||
|
||||
const { jobType } = require('./constants')
|
||||
const tornConfig = require('torn-token')
|
||||
const tornConfig = require('@tornado/tornado-config')
|
||||
module.exports = {
|
||||
netId: Number(process.env.NET_ID) || 1,
|
||||
redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
|
||||
httpRpcUrl: process.env.HTTP_RPC_URL,
|
||||
wsRpcUrl: process.env.WS_RPC_URL,
|
||||
oracleRpcUrl: process.env.ORACLE_RPC_URL || 'https://mainnet.infura.io/',
|
||||
offchainOracleAddress: '0x07D91f5fb9Bf7798734C3f606dB065549F6893bb',
|
||||
oracleRpcUrl: process.env.ORACLE_RPC_URL || 'https://api.securerpc.com/v1',
|
||||
aggregatorAddress: process.env.AGGREGATOR,
|
||||
minerMerkleTreeHeight: 20,
|
||||
privateKey: process.env.PRIVATE_KEY,
|
||||
instances: tornConfig.instances,
|
||||
torn: tornConfig,
|
||||
port: process.env.APP_PORT || 8000,
|
||||
tornadoServiceFee: Number(process.env.REGULAR_TORNADO_WITHDRAW_FEE),
|
||||
miningServiceFee: Number(process.env.MINING_SERVICE_FEE),
|
||||
tornadoServiceFee: Number(process.env.RELAYER_FEE),
|
||||
rewardAccount: process.env.REWARD_ACCOUNT,
|
||||
governanceAddress: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
|
||||
tornadoGoerliProxy: '0x454d870a72e29d5E5697f635128D18077BD04C60',
|
||||
gasLimits: {
|
||||
[jobType.TORNADO_WITHDRAW]: 390000,
|
||||
WITHDRAW_WITH_EXTRA: 700000,
|
||||
[jobType.MINING_REWARD]: 455000,
|
||||
[jobType.MINING_WITHDRAW]: 400000,
|
||||
},
|
||||
|
@ -12,7 +12,7 @@ async function status(req, res) {
|
||||
|
||||
res.json({
|
||||
rewardAccount,
|
||||
instances: instances[`netId${netId}`],
|
||||
instances: instances[netId],
|
||||
netId,
|
||||
ethPrices,
|
||||
tornadoServiceFee,
|
||||
|
@ -1,41 +1,17 @@
|
||||
const { offchainOracleAddress } = require('./config')
|
||||
const {
|
||||
getArgsForOracle,
|
||||
setSafeInterval,
|
||||
toChecksumAddress,
|
||||
toBN,
|
||||
RelayerError,
|
||||
logRelayerError,
|
||||
} = require('./utils')
|
||||
const { setSafeInterval, RelayerError, logRelayerError } = require('./utils')
|
||||
const { redis } = require('./modules/redis')
|
||||
const web3 = require('./modules/web3')('oracle')
|
||||
const { TokenPriceOracle } = require('@tornado/tornado-oracles')
|
||||
const { oracleRpcUrl } = require('./config')
|
||||
|
||||
const offchainOracleABI = require('../abis/OffchainOracle.abi.json')
|
||||
|
||||
const offchainOracle = new web3.eth.Contract(offchainOracleABI, offchainOracleAddress)
|
||||
const { tokenAddresses, oneUintAmount, currencyLookup } = getArgsForOracle()
|
||||
const priceOracle = new TokenPriceOracle(oracleRpcUrl)
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const ethPrices = {}
|
||||
for (let i = 0; i < tokenAddresses.length; i++) {
|
||||
try {
|
||||
const isWrap =
|
||||
toChecksumAddress(tokenAddresses[i]) ===
|
||||
toChecksumAddress('0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643')
|
||||
|
||||
const price = await offchainOracle.methods.getRateToEth(tokenAddresses[i], isWrap).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])
|
||||
}
|
||||
}
|
||||
const ethPrices = await priceOracle.fetchPrices()
|
||||
if (!Object.values(ethPrices).length) {
|
||||
throw new RelayerError('Can`t update prices', 1)
|
||||
}
|
||||
|
||||
await redis.hmset('prices', ethPrices)
|
||||
console.log('Wrote following prices to redis', ethPrices)
|
||||
} catch (e) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
const MerkleTree = require('fixed-merkle-tree')
|
||||
const MerkleTree = require('@tornado/fixed-merkle-tree')
|
||||
const { minerMerkleTreeHeight, torn, netId } = require('./config')
|
||||
const { poseidonHash2, toBN, logRelayerError } = require('./utils')
|
||||
const resolver = require('./modules/resolver')
|
||||
|
34
src/utils.js
34
src/utils.js
@ -1,17 +1,9 @@
|
||||
const { instances, netId } = require('./config')
|
||||
const { poseidon } = require('circomlib')
|
||||
const { toBN, toChecksumAddress, BN, fromWei, isAddress, toWei } = require('web3-utils')
|
||||
|
||||
const TOKENS = {
|
||||
torn: {
|
||||
tokenAddress: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
|
||||
symbol: 'TORN',
|
||||
decimals: 18,
|
||||
},
|
||||
}
|
||||
const { poseidon } = require('@tornado/circomlib')
|
||||
const { toBN, toChecksumAddress, BN, fromWei, isAddress, toWei, toHex } = require('web3-utils')
|
||||
|
||||
const addressMap = new Map()
|
||||
const instance = instances[`netId${netId}`]
|
||||
const instance = instances[netId]
|
||||
|
||||
for (const [currency, { instanceAddress, symbol, decimals }] of Object.entries(instance)) {
|
||||
Object.entries(instanceAddress).forEach(([amount, address]) =>
|
||||
@ -61,24 +53,6 @@ function when(source, event) {
|
||||
})
|
||||
}
|
||||
|
||||
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) {
|
||||
value = value.toString()
|
||||
let ether = value.toString()
|
||||
@ -155,12 +129,12 @@ module.exports = {
|
||||
poseidonHash2,
|
||||
sleep,
|
||||
when,
|
||||
getArgsForOracle,
|
||||
fromDecimals,
|
||||
toBN,
|
||||
toChecksumAddress,
|
||||
fromWei,
|
||||
toWei,
|
||||
toHex,
|
||||
BN,
|
||||
isAddress,
|
||||
RelayerError,
|
||||
|
109
src/worker.js
109
src/worker.js
@ -1,8 +1,7 @@
|
||||
const fs = require('fs')
|
||||
const MerkleTree = require('fixed-merkle-tree')
|
||||
const { GasPriceOracle } = require('gas-price-oracle')
|
||||
const { Utils, Controller } = require('tornado-anonymity-mining')
|
||||
|
||||
const MerkleTree = require('@tornado/fixed-merkle-tree')
|
||||
const { Utils, Controller } = require('@tornado/anonymity-mining')
|
||||
const { TornadoFeeOracleV5 } = require('@tornado/tornado-oracles')
|
||||
const swapABI = require('../abis/swap.abi.json')
|
||||
const miningABI = require('../abis/mining.abi.json')
|
||||
const tornadoABI = require('../abis/tornadoABI.json')
|
||||
@ -11,12 +10,11 @@ const { queue } = require('./queue')
|
||||
const {
|
||||
poseidonHash2,
|
||||
getInstance,
|
||||
fromDecimals,
|
||||
sleep,
|
||||
toBN,
|
||||
toWei,
|
||||
fromWei,
|
||||
toChecksumAddress,
|
||||
isAddress,
|
||||
RelayerError,
|
||||
logRelayerError,
|
||||
} = require('./utils')
|
||||
@ -34,7 +32,7 @@ const {
|
||||
tornadoGoerliProxy,
|
||||
} = require('./config')
|
||||
const resolver = require('./modules/resolver')
|
||||
const { TxManager } = require('tx-manager')
|
||||
const { TxManager } = require('@tornado/tx-manager')
|
||||
const { redis, redisSubscribe } = require('./modules/redis')
|
||||
const getWeb3 = require('./modules/web3')
|
||||
|
||||
@ -46,7 +44,7 @@ let txManager
|
||||
let controller
|
||||
let swap
|
||||
let minerContract
|
||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: oracleRpcUrl })
|
||||
const feeOracle = new TornadoFeeOracleV5(netId, oracleRpcUrl)
|
||||
|
||||
async function fetchTree() {
|
||||
const elements = await redis.get('tree:elements')
|
||||
@ -93,6 +91,7 @@ async function start() {
|
||||
BASE_FEE_RESERVE_PERCENTAGE: baseFeeReserve,
|
||||
},
|
||||
})
|
||||
|
||||
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)
|
||||
@ -111,58 +110,38 @@ async function start() {
|
||||
}
|
||||
}
|
||||
|
||||
function checkFee({ data }) {
|
||||
function checkFee({ data }, gasInfo) {
|
||||
if (data.type === jobType.TORNADO_WITHDRAW) {
|
||||
return checkTornadoFee(data)
|
||||
return checkTornadoFee(data, gasInfo)
|
||||
}
|
||||
return checkMiningFee(data)
|
||||
}
|
||||
|
||||
async function getGasPrice() {
|
||||
const block = await web3.eth.getBlock('latest')
|
||||
|
||||
if (block && block.baseFeePerGas) {
|
||||
return toBN(block.baseFeePerGas)
|
||||
}
|
||||
|
||||
const { fast } = await gasPriceOracle.gasPrices()
|
||||
return toBN(toWei(fast.toString(), 'gwei'))
|
||||
}
|
||||
|
||||
async function checkTornadoFee({ args, contract }) {
|
||||
async function checkTornadoFee({ args, contract }, tx) {
|
||||
const { currency, amount, decimals } = getInstance(contract)
|
||||
const [fee, refund] = [args[4], args[5]].map(toBN)
|
||||
const gasPrice = await getGasPrice()
|
||||
const [userProvidedFee, refund] = [args[4], args[5]].map(toBN)
|
||||
const { gasLimit, gasPrice } = tx
|
||||
|
||||
const ethPrice = await redis.hget('prices', currency)
|
||||
const expense = gasPrice.mul(toBN(gasLimits[jobType.TORNADO_WITHDRAW]))
|
||||
const totalWithdrawalFee = await feeOracle.calculateWithdrawalFeeViaRelayer({
|
||||
tx,
|
||||
txType: 'relayer_withdrawal',
|
||||
amount,
|
||||
currency,
|
||||
decimals,
|
||||
refundInEth: refund.toString(),
|
||||
predefinedGasLimit: gasLimit,
|
||||
predefinedGasPrice: gasPrice,
|
||||
tokenPriceInEth: ethPrice,
|
||||
relayerFeePercent: tornadoServiceFee,
|
||||
})
|
||||
|
||||
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(
|
||||
'sent fee, desired fee, feePercent',
|
||||
fromWei(fee.toString()),
|
||||
fromWei(desiredFee.toString()),
|
||||
fromWei(feePercent.toString()),
|
||||
'user-provided fee, desired fee',
|
||||
fromWei(userProvidedFee.toString()),
|
||||
fromWei(toBN(totalWithdrawalFee).toString()),
|
||||
)
|
||||
if (fee.lt(desiredFee)) {
|
||||
if (userProvidedFee.lt(toBN(totalWithdrawalFee))) {
|
||||
throw new RelayerError(
|
||||
'Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.',
|
||||
0,
|
||||
@ -171,12 +150,12 @@ async function checkTornadoFee({ args, contract }) {
|
||||
}
|
||||
|
||||
async function checkMiningFee({ args }) {
|
||||
const gasPrice = await getGasPrice()
|
||||
const gasPrice = await feeOracle.getGasPrice()
|
||||
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 = gasPrice.mul(toBN(gasLimits[currentJob.data.type]))
|
||||
const expense = toBN(gasPrice).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()
|
||||
@ -222,6 +201,18 @@ function checkOldProxy(address) {
|
||||
return toChecksumAddress(address) === toChecksumAddress(OLD_PROXY)
|
||||
}
|
||||
|
||||
async function checkRecipient({ data }) {
|
||||
// Checks only for default withdrawals
|
||||
if (data.type !== jobType.TORNADO_WITHDRAW) return
|
||||
|
||||
console.log(data.args)
|
||||
const recipient = data.args[2]
|
||||
if (!isAddress(recipient)) throw new Error('Recipient address is invalid')
|
||||
|
||||
const addressCode = await web3.eth.getCode(toChecksumAddress(recipient))
|
||||
if (addressCode !== '0x') throw new Error('Recipient cannot be a smart-contract, only EOA')
|
||||
}
|
||||
|
||||
async function getTxObject({ data }) {
|
||||
if (data.type === jobType.TORNADO_WITHDRAW) {
|
||||
let { contract, isOldProxy } = await getProxyContract()
|
||||
@ -233,12 +224,18 @@ async function getTxObject({ data }) {
|
||||
calldata = contract.methods.withdraw(data.proof, ...data.args).encodeABI()
|
||||
}
|
||||
|
||||
return {
|
||||
const incompleteTx = {
|
||||
value: data.args[5],
|
||||
from: txManager.address, // Required, because without it relayerRegistry.burn will fail, because msg.sender is not relayer
|
||||
to: contract._address,
|
||||
data: calldata,
|
||||
gasLimit: gasLimits['WITHDRAW_WITH_EXTRA'],
|
||||
}
|
||||
const { gasLimit, gasPrice } = await feeOracle.getGasParams({
|
||||
tx: incompleteTx,
|
||||
txType: 'relayer_withdrawal',
|
||||
})
|
||||
|
||||
return { ...incompleteTx, gasLimit, gasPrice }
|
||||
} else {
|
||||
const method = data.type === jobType.MINING_REWARD ? 'reward' : 'withdraw'
|
||||
const calldata = minerContract.methods[method](data.proof, data.args).encodeABI()
|
||||
@ -281,8 +278,10 @@ async function processJob(job) {
|
||||
}
|
||||
|
||||
async function submitTx(job, retry = 0) {
|
||||
await checkFee(job)
|
||||
currentTx = await txManager.createTx(await getTxObject(job))
|
||||
await checkRecipient(job)
|
||||
const rawTx = await getTxObject(job)
|
||||
await checkFee(job, rawTx)
|
||||
currentTx = await txManager.createTx(rawTx)
|
||||
|
||||
if (job.data.type !== jobType.TORNADO_WITHDRAW) {
|
||||
await fetchTree()
|
||||
|
Loading…
Reference in New Issue
Block a user