72 Commits

Author SHA1 Message Date
8e8346e651 Bump node version to newest (20) and update corresponding dependencies 2023-07-25 08:55:01 -07:00
098a4687cf Prepare installation script, config, environment and docker-compose for deployment to v6 version 2023-07-24 03:13:19 -07:00
14c231fae7 Bump version to 6.0.0 2023-07-21 21:20:16 -07:00
85ca668396 Correct gas estimation to v5 version 2023-07-21 21:19:08 -07:00
d69a17073a Fix relayer balance checking: need to get relayer locked balance in relayer registry contract, not TORN token balance on relayer wallet 2023-07-10 13:51:35 -07:00
26fdb54fb0 Use BigNumber for balance calculation to avoid overflows or incorrect stringification 2023-07-10 07:09:55 -07:00
1f549c799c Update ABI generation and add missing Optimism L1 gas oracle contract ABI 2023-07-10 06:26:27 -07:00
0fe61d572f Add concurrently for correct local deployment & bump tsyringe for types 2023-07-09 19:54:06 -07:00
0c33fcd54d Fix software versions comparison, now, if version higher than latest on git, no error thrown 2023-07-09 19:40:08 -07:00
8527be370c Change RPC to non-censoring 2023-07-09 19:38:28 -07:00
fb80cd1904 Change minimum staked TORN balance for relayer to 500 TORN corresponding to Proposal #16 results 2023-07-09 13:54:51 -07:00
fd59706ea1 Change minimum relayer balances: specify unique value for each chain 2023-07-09 13:51:15 -07:00
smart_ex
9231f3f8ec check version fallback 2022-08-05 16:34:24 +10:00
smart_ex
72f0e8399d add tests, update deps 2022-08-05 15:32:23 +10:00
smart_ex
1b4f25cb9f fix types, checkUpdate interval 2022-08-05 15:29:51 +10:00
smart_ex
ed0a389df2 update Dockerfile 2022-08-02 15:46:14 +10:00
smart_ex
5a8b6247a9 update start command, cleanup 2022-08-02 14:42:51 +10:00
smart_ex
833b89c617 update rpc-node link https://github.com/tornadocash/rpc-nodes 2022-08-01 16:32:33 +10:00
smart_ex
21aac61bf6 update workflow 2022-07-29 18:48:55 +10:00
smart_ex
340df8bdd1 cleanup 2022-07-29 18:42:55 +10:00
smart_ex
32b7714371 update readme 2022-07-28 18:12:34 +10:00
smart_ex
0ce5dcbf50 update dockerfile and start commands 2022-07-28 17:29:03 +10:00
smart_ex
74dbd94de8 update docker image, compose file 2022-07-26 18:09:26 +10:00
smart_ex
09429eb276 check update job 2022-07-26 17:49:54 +10:00
Danil Kovtonyuk
cf3ed4e25e update deps 2022-07-15 17:26:13 +10:00
smart_ex
761900d187 set resubmitted job status 2022-07-08 18:14:19 +10:00
smart_ex
15d5529876 yarn start 2022-07-08 10:36:09 +10:00
smart_ex
d32ff07fa8 cleanup and formatting code 2022-07-08 10:20:17 +10:00
smart_ex
591c2d94f3 relayer tx job attempts 2022-07-07 18:36:04 +10:00
smart_ex
185b18b33a tornado classic fee estimate compatibility 2022-07-07 17:00:16 +10:00
smart_ex
be969ca8c0 Fix gasLimits config, check l1Fee for optimism 2022-07-07 16:47:50 +10:00
smart_ex
de279a9e18 add OVM gas oracle, typechain 2022-07-07 16:41:39 +10:00
smart_ex
4f63a335ae update deps 2022-07-06 16:06:12 +10:00
smart_ex
7557158835 error handling 2022-07-05 22:19:38 +10:00
smart_ex
29fa454428 update env config, send alerts for SEND_ERROR code 2022-07-05 20:35:20 +10:00
smart_ex
0e1a9043be update config and worker 2022-07-04 23:09:06 +10:00
smart_ex
4edf32a76e update dockerfile 2022-07-04 21:56:05 +10:00
smart_ex
5de52850b5 workflow debug 2022-07-04 20:27:46 +10:00
smart_ex
02d64f67de workflow debug 2022-07-04 20:26:58 +10:00
smart_ex
e8e1384b65 server deps major updates 2 2022-07-04 20:23:41 +10:00
smart_ex
9a0e3952ce server deps major updates 2022-07-04 18:25:28 +10:00
smart_ex
a4fdadafd7 tx-manager conf from env 2022-07-04 17:21:19 +10:00
smart_ex
8649a5d258 update conf 2022-07-01 19:36:22 +10:00
smart_ex
cff6427e33 add name prefix to notify 2022-07-01 12:58:35 +10:00
smart_ex
8c027dbfcf fix pub/sub 2022-07-01 01:04:04 +10:00
smart_ex
5aebdab767 yml lint 2022-06-30 19:26:37 +10:00
smart_ex
1532bfc4db updates and fixes 2 2022-06-30 19:25:36 +10:00
smart_ex
b48a44ae4e updates and fixes 2022-06-30 12:10:43 +10:00
smart_ex
e997d4d07e lint 2022-06-29 20:03:25 +10:00
smart_ex
bd0f8d2a2e update libs, docker file, lint 2022-06-29 20:02:30 +10:00
smart_ex
362605702e job link 2022-06-22 20:13:05 +10:00
smart_ex
eef6bb2581 add MockTelegram 2022-06-22 19:43:00 +10:00
smart_ex
66dc7efa60 use simple pup/sub chanel 2022-06-22 14:34:27 +10:00
smart_ex
2c20febcab send alert for tx errors, update docker 2022-06-14 20:06:42 +10:00
smart_ex
978c70de1e WIP health service, error handling 2022-06-10 14:08:47 +10:00
smart_ex
ec2f20bfaf WIP clear redis store on init. Notifier state 2022-06-09 14:12:46 +10:00
smart_ex
03f9bfac45 WIP monitoring and alerts 2022-06-08 14:42:54 +10:00
smart_ex
3a74ebf90e dependencies upgrade 2022-06-06 18:08:34 +10:00
smart_ex
a35207eabc TG notifier 2022-05-26 20:08:59 +10:00
smart_ex
bee7d46f6e ERC20 abis 2022-05-26 17:48:13 +10:00
smart_ex
3279eeaf08 ERC20 abi Torn token 2022-05-26 17:48:01 +10:00
smart_ex
fecac18687 Fallback gas prices for light mode 2022-05-26 15:43:07 +10:00
smart_ex
1a375d1cd9 wip tx service update with DI 2022-05-23 20:29:28 +10:00
smart_ex
1828228a9d wip tx queue worker 2022-05-23 20:28:40 +10:00
smart_ex
82d3cc63e4 wip DI implemented, Queue helpers 2022-05-23 17:17:39 +10:00
smart_ex
ae61e3ec96 clean up 2022-05-18 18:20:06 +10:00
smart_ex
8bc5b7be9e wip. relayer job flow. server api 2022-05-18 18:18:10 +10:00
smart_ex
8e3f20f76c wip 2022-05-13 17:05:38 +10:00
smart_ex
9f17840034 set relayer mode on init() 2022-05-12 13:04:08 +10:00
smart_ex
6f66b34288 remove mining related staff 2022-05-12 13:03:29 +10:00
smart_ex
8d2bad2a41 update contracts abis 2022-05-12 13:00:59 +10:00
smart_ex
4595085d61 wip 2022-05-10 18:52:07 +10:00
96 changed files with 13767 additions and 744175 deletions

View File

@@ -1,3 +1,6 @@
node_modules
.env
.git
.git
build
test
.nyc_output

View File

@@ -1,6 +1,5 @@
NET_ID=1
HTTP_RPC_URL=https://api.securerpc.com/v1
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
@@ -12,13 +11,15 @@ APP_PORT=8000
# without 0x prefix
PRIVATE_KEY=
# 0.05 means 0.05%
RELAYER_FEE=0.4
MINING_SERVICE_FEE=0.05
REWARD_ACCOUNT=
# 0.1 means 0.1%, we recommend 0.1 for sidechains and 0.4 for mainnet
RELAYER_FEE=0.1
REWARD_ACCOUNT=0x...
CONFIRMATIONS=4
# in GWEI
MAX_GAS_PRICE=1000
BASE_FEE_RESERVE_PERCENTAGE=25
AGGREGATOR=0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49
GAS_BUMP_PERCENTAGE=5
AGGREGATOR=0x8cb1436F64a3c33aD17bb42F94e255c4c0E871b2
# Telegram bot alerts
TELEGRAM_NOTIFIER_BOT_TOKEN=
TELEGRAM_NOTIFIER_CHAT_ID=

View File

@@ -1,45 +1,21 @@
{
"env": {
"node": true,
"browser": true,
"es6": true,
"mocha": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
"commonjs": true,
"es2020": true
},
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2018
"ecmaVersion": 11
},
"plugins": ["@typescript-eslint"],
"rules": {
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"linebreak-style": ["error", "unix"],
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
],
"semi": ["error", "never"],
"object-curly-spacing": ["error", "always"],
"require-await": "error",
"comma-dangle": ["error", "only-multiline"],
"space-before-function-paren": [
"error",
{
"anonymous": "always",
"named": "never",
"asyncArrow": "always"
}
]
"quotes": ["error", "single"],
"semi": ["error", "always"],
"no-useless-catch": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/explicit-module-boundary-types": "off"
}
}

View File

@@ -14,9 +14,8 @@ jobs:
uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
node-version: 16
- run: yarn install
- run: yarn test
- run: yarn lint
- name: Telegram Failure Notification
uses: appleboy/telegram-action@master
@@ -26,7 +25,6 @@ jobs:
format: markdown
to: ${{ secrets.TELEGRAM_CHAT_ID }}
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
publish:
runs-on: ubuntu-latest
needs: build
@@ -51,7 +49,7 @@ jobs:
dockerfile: Dockerfile
repository: tornadocash/relayer
tag_with_ref: true
tags: mining,candidate
tags: candidate
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
@@ -70,10 +68,9 @@ jobs:
to: ${{ secrets.TELEGRAM_RELAYER_CHAT_ID }}
token: ${{ secrets.TELEGRAM_BOT_TOKEN }}
message: |
🚀 Published a new version of the relayer node service for mainnet to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:mining`.
🚀 Published a new version of the relayer node service to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer`.
Please update your mainnet nodes ❗️
DO NOT TOUCH SIDECHAINS AND NOVA RELAYERS.
Please update your nodes ❗️
debug: true
format: markdown
@@ -84,10 +81,9 @@ jobs:
uses: Ilshidur/action-discord@master
with:
args: |
🚀 Published a new version of the relayer node service for mainnet to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer:mining`.
🚀 Published a new version of the relayer node service to docker hub: `tornadocash/relayer:v${{ steps.vars.outputs.version }}` and `tornadocash/relayer`.
Please update your mainnet nodes ❗️
DO NOT TOUCH SIDECHAINS AND NOVA RELAYERS.
Please update your nodes ❗️
- name: Telegram Failure Notification
uses: appleboy/telegram-action@master

2
.gitignore vendored
View File

@@ -6,3 +6,5 @@ node_modules/
kovan.*
dump.rdb
.idea
build
.nyc_output

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
v20.3.0

View File

@@ -1 +0,0 @@
keys/TreeUpdate.json

View File

@@ -1,7 +1,7 @@
{
"semi": false,
"arrowParens": "avoid",
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 110,
"trailingComma": "all"
"printWidth": 130,
"tabWidth": 2
}

View File

@@ -1,9 +1,28 @@
FROM node:16
FROM node:20-alpine as dev
ENV NODE_ENV=development
WORKDIR /usr/app
COPY yarn.lock .
COPY package.json .
RUN apk update && apk add --no-cache g++ make python3 && rm -rf /var/cache/apk/*
RUN yarn install
COPY . ./
RUN yarn build
FROM node:20-alpine as prod
ENV NODE_ENV=production
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn && yarn cache clean --force
COPY . .
RUN apk update && apk add --no-cache g++ make python3 && rm -rf /var/cache/apk/*
COPY --from=dev /usr/app/ /app
COPY --from=dev /usr/app/package.json /app/
COPY --from=dev /usr/app/yarn.lock /app/
RUN yarn install && yarn cache clean -f
EXPOSE 8000
ENTRYPOINT ["yarn"]

181
README.md
View File

@@ -1,100 +1,137 @@
# 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)
# 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-6.0.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 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 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).__
__*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).__
## Deploy with docker-compose (recommended)
## Deploy with script and docker-compose
*The following instructions are for Ubuntu 22.10, other operating systems may vary. These instructions include automated SSL configuration with LetsEncrypt.*
*The following instructions are for Ubuntu 22.10, other operating systems may vary.*
__PREREQUISITES__
1. Update core dependencies
- `sudo apt-get update`
2. Install docker-compose
- `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`
3. Install Docker
- `curl -fsSL https://get.docker.com -o get-docker.sh && chmod +x get-docker.sh && ./get-docker.sh`
4. Install git
- `sudo apt-get install git-all`
5. Install nginx
- `sudo apt install nginx`
6. Stop apache2 instance (enabled by default)
- `sudo systemctl stop apache2`
#### Installation:
__FIREWALL CONFIGURATION__
Just run in terminal:
_* Warning: Failure to configure SSH as the first UFW rule, will lock you out of the instance_
```bash
curl -s https://git.tornado.ws/tornadocash/classic-relayer/raw/branch/v6/install.sh | bash
```
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`
#### Configuring environments:
1. Go to `tornado-relayer` folder on the server home directory
2. Check environment files:
By default each network is preconfigured the naming of `.env.<NETWORK>`
- `.env.eth` for Ethereum Mainnet
- `.env.goerli` for Goerli testnet
- `.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
__DEPLOYMENT__
1. Clone the repository and enter the directory
- `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 `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. 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:
- Set `REWARD_ACCOUNT` - eth address that is used to collect fees
- `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
- Set `TELEGRAM_NOTIFIER_BOT_TOKEN` and `TELEGRAM_NOTIFIER_CHAT_ID` if your want get notify to telegram
__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`
- 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
- Update `GAS_BUMP_PERCENTAGE` if needed - how much in % will the network gas for transaction additionally increased
**NB!** Don't update these values if you not sure what you doing.
#### Deployment:
1. Build and deploy the docker source for the configured neworks specified via `--profile <NETWORK_SYMBOL>`, for example (if you run relayer only for Ethereum Mainnet, Binance Smart Chain and Arbitrum):
- `docker-compose --profile eth --profile bsc --profile arb up -d`
2. Visit your domain addresses and check each `/status` endpoint to ensure there is no errors in the `status` fields
## Run locally
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
1. `yarn`
2. `cp .env.example .env`
3. Modify `.env` as needed
4. `yarn start`
5. Go to `http://127.0.0.1:8000`
6. 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
curl -X POST -H 'content-type:application/json' --data '<input data>' http://127.0.0.1:8000/v1/tornadoWithdraw
```
Relayer should return a transaction hash.
Relayer should return a job id in uuid v4 format.
_Note._ If you want to change contracts' addresses go to [config.js](./config.js) file.
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
## Run geth node
It is strongly recommended that you use your own RPC node. Instruction on how to run full node with `geth` can be
found [here](https://github.com/tornadocash/rpc-nodes).
## Monitoring
### Basic
For basic monitoring setup telegram bot and fill variables in .env file
Alerts about:
- Main relayer currency balance
- Torn staked balance in relayer contract
- Withdraw transactions send errors
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
### Advanced
You can find the guide on how to install the Zabbix server in the [/monitoring/README.md](/monitoring/README.md).
## Compatible networks
- Ethereum Mainnet (1)
- Binance Smart Chain (56)
- Polygon (Matic) Network (137)
- Optimism (10)
- Arbitrum One (42161)
- Gnosis Chain (100)
- Avalanche Mainnet (43114)
- Ethereum Goerli (5)
## Input data example
```json
{
"proof": "0x0f8cb4c2ca9cbb23a5f21475773e19e39d3470436d7296f25c8730d19d88fcef2986ec694ad094f4c5fff79a4e5043bd553df20b23108bc023ec3670718143c20cc49c6d9798e1ae831fd32a878b96ff8897728f9b7963f0d5a4b5574426ac6203b2456d360b8e825d8f5731970bf1fc1b95b9713e3b24203667ecdd5939c2e40dec48f9e51d9cc8dc2f7f3916f0e9e31519c7df2bea8c51a195eb0f57beea4924cb846deaa78cdcbe361a6c310638af6f6157317bc27d74746bfaa2e1f8d2e9088fd10fa62100740874cdffdd6feb15c95c5a303f6bc226d5e51619c5b825471a17ddfeb05b250c0802261f7d05cf29a39a72c13e200e5bc721b0e4c50d55e6",
"args": [
"0x1579d41e5290ab5bcec9a7df16705e49b5c0b869095299196c19c5e14462c9e3",
"0x0cf7f49c5b35c48b9e1d43713e0b46a75977e3d10521e9ac1e4c3cd5e3da1c5d",
"0x03ebd0748aa4d1457cf479cce56309641e0a98f5",
"0xbd4369dc854c5d5b79fe25492e3a3cfcb5d02da5",
"0x000000000000000000000000000000000000000000000000058d15e176280000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"contract": "0xA27E34Ad97F171846bAf21399c370c9CE6129e0D"
}
```
Disclaimer:

View File

@@ -2,107 +2,198 @@
{
"inputs": [
{
"internalType": "bytes32[]",
"name": "domains",
"type": "bytes32[]"
}
],
"name": "bulkResolve",
"outputs": [
"internalType": "contract MultiWrapper",
"name": "_multiWrapper",
"type": "address"
},
{
"internalType": "address[]",
"name": "result",
"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 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",
"internalType": "contract IOracle",
"name": "oracle",
"type": "address"
},
{
"internalType": "address[]",
"name": "accs",
"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[]"
}
],
"name": "getGovernanceBalances",
"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": "amounts",
"type": "uint256[]"
"internalType": "uint256",
"name": "weightedRate",
"type": "uint256"
}
],
"stateMutability": "view",
@@ -111,104 +202,61 @@
{
"inputs": [
{
"internalType": "address[]",
"name": "fromTokens",
"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": "uint256[]",
"name": "oneUnitAmounts",
"type": "uint256[]"
}
],
"name": "getPricesInETH",
"outputs": [
{
"internalType": "uint256[]",
"name": "prices",
"type": "uint256[]"
"internalType": "enum OffchainOracle.OracleType[]",
"name": "oracleTypes",
"type": "uint8[]"
}
],
"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",
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
@@ -222,98 +270,65 @@
{
"inputs": [
{
"internalType": "contract RewardSwap",
"name": "swap",
"internalType": "contract IERC20",
"name": "connector",
"type": "address"
}
],
"name": "swapState",
"outputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "poolWeight",
"type": "uint256"
}
],
"stateMutability": "view",
"name": "removeConnector",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract Miner",
"name": "miner",
"internalType": "contract IOracle",
"name": "oracle",
"type": "address"
},
{
"internalType": "address[]",
"name": "instances",
"type": "address[]"
},
{
"internalType": "contract RewardSwap",
"name": "swap",
"type": "address"
"internalType": "enum OffchainOracle.OracleType",
"name": "oracleKind",
"type": "uint8"
}
],
"name": "miningData",
"outputs": [
{
"internalType": "uint256[]",
"name": "_rates",
"type": "uint256[]"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "poolWeight",
"type": "uint256"
}
],
"stateMutability": "view",
"name": "removeOracle",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "fromTokens",
"type": "address[]"
},
{
"internalType": "uint256[]",
"name": "oneUnitAmounts",
"type": "uint256[]"
},
{
"internalType": "contract RewardSwap",
"name": "swap",
"internalType": "contract MultiWrapper",
"name": "_multiWrapper",
"type": "address"
}
],
"name": "marketData",
"outputs": [
"name": "setMultiWrapper",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256[]",
"name": "prices",
"type": "uint256[]"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"stateMutability": "view",
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

View File

@@ -1,41 +1,98 @@
[
{
"inputs": [
{
"internalType": "bytes32",
"name": "_torn",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_miner",
"type": "bytes32"
},
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "_miningCap",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_initialLiquidity",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "_totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "who",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newWeight",
"name": "value",
"type": "uint256"
}
],
"name": "PoolWeightUpdated",
"name": "Approval",
"type": "event"
},
{
@@ -44,188 +101,105 @@
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "pTORN",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "TORN",
"name": "value",
"type": "uint256"
}
],
"name": "Swap",
"name": "Transfer",
"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"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "recipient",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"name": "value",
"type": "uint256"
}
],
"name": "swap",
"name": "transferFrom",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "getExpectedReturn",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tornVirtualBalance",
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
@@ -238,13 +212,43 @@
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "newWeight",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "setPoolWeight",
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"

38
abis/Multicall.abi.json Normal file
View File

@@ -0,0 +1,38 @@
[
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"internalType": "struct MultiCall.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "multicall",
"outputs": [
{
"internalType": "bytes[]",
"name": "results",
"type": "bytes[]"
},
{
"internalType": "bool[]",
"name": "success",
"type": "bool[]"
}
],
"stateMutability": "view",
"type": "function"
}
]

View File

@@ -0,0 +1,292 @@
[
{
"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"
}
]

View File

@@ -0,0 +1,145 @@
[
{
"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/ProxyLight.abi.json Normal file
View 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"
}
]

View File

@@ -0,0 +1,297 @@
[
{
"inputs": [
{ "internalType": "address", "name": "_torn", "type": "address" },
{ "internalType": "address", "name": "_governance", "type": "address" },
{ "internalType": "address", "name": "_ens", "type": "address" },
{ "internalType": "address", "name": "_staking", "type": "address" },
{ "internalType": "address", "name": "_feeManager", "type": "address" }
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "minStakeAmount", "type": "uint256" }],
"name": "MinimumStakeAmount",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "address", "name": "relayer", "type": "address" }],
"name": "RelayerBalanceNullified",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "bytes32", "name": "relayer", "type": "bytes32" },
{ "indexed": false, "internalType": "string", "name": "ensName", "type": "string" },
{ "indexed": false, "internalType": "address", "name": "relayerAddress", "type": "address" },
{ "indexed": false, "internalType": "uint256", "name": "stakedAmount", "type": "uint256" }
],
"name": "RelayerRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "address", "name": "tornadoRouter", "type": "address" }],
"name": "RouterRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "address", "name": "relayer", "type": "address" },
{ "indexed": false, "internalType": "uint256", "name": "amountStakeAdded", "type": "uint256" }
],
"name": "StakeAddedToRelayer",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "address", "name": "relayer", "type": "address" },
{ "indexed": false, "internalType": "uint256", "name": "amountBurned", "type": "uint256" }
],
"name": "StakeBurned",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "address", "name": "relayer", "type": "address" },
{ "indexed": false, "internalType": "address", "name": "worker", "type": "address" }
],
"name": "WorkerRegistered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": false, "internalType": "address", "name": "relayer", "type": "address" },
{ "indexed": false, "internalType": "address", "name": "worker", "type": "address" }
],
"name": "WorkerUnregistered",
"type": "event"
},
{
"inputs": [{ "internalType": "bytes32[]", "name": "domains", "type": "bytes32[]" }],
"name": "bulkResolve",
"outputs": [{ "internalType": "address[]", "name": "result", "type": "address[]" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "sender", "type": "address" },
{ "internalType": "address", "name": "relayer", "type": "address" },
{ "internalType": "contract ITornadoInstance", "name": "pool", "type": "address" }
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "ens",
"outputs": [{ "internalType": "contract IENS", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeManager",
"outputs": [{ "internalType": "contract IFeeManager", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "relayer", "type": "address" }],
"name": "getRelayerBalance",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "relayer", "type": "address" }],
"name": "getRelayerEnsHash",
"outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "governance",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "bytes32", "name": "_tornadoRouter", "type": "bytes32" }],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "toResolve", "type": "address" }],
"name": "isRelayer",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "relayer", "type": "address" },
{ "internalType": "address", "name": "toResolve", "type": "address" }
],
"name": "isRelayerRegistered",
"outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "minStakeAmount",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "relayer", "type": "address" }],
"name": "nullifyBalance",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "string", "name": "ensName", "type": "string" },
{ "internalType": "uint256", "name": "stake", "type": "uint256" },
{ "internalType": "address[]", "name": "workersToRegister", "type": "address[]" }
],
"name": "register",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "string", "name": "ensName", "type": "string" },
{ "internalType": "uint256", "name": "stake", "type": "uint256" },
{ "internalType": "address[]", "name": "workersToRegister", "type": "address[]" },
{ "internalType": "address", "name": "relayer", "type": "address" },
{ "internalType": "uint256", "name": "deadline", "type": "uint256" },
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"name": "registerPermit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "relayer", "type": "address" },
{ "internalType": "address", "name": "worker", "type": "address" }
],
"name": "registerWorker",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "", "type": "address" }],
"name": "relayers",
"outputs": [
{ "internalType": "uint256", "name": "balance", "type": "uint256" },
{ "internalType": "bytes32", "name": "ensHash", "type": "bytes32" }
],
"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": "uint256", "name": "minAmount", "type": "uint256" }],
"name": "setMinStakeAmount",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "tornadoRouterAddress", "type": "address" }],
"name": "setTornadoRouter",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "relayer", "type": "address" },
{ "internalType": "uint256", "name": "stake", "type": "uint256" }
],
"name": "stakeToRelayer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "relayer", "type": "address" },
{ "internalType": "uint256", "name": "stake", "type": "uint256" },
{ "internalType": "address", "name": "staker", "type": "address" },
{ "internalType": "uint256", "name": "deadline", "type": "uint256" },
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"name": "stakeToRelayerPermit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "staking",
"outputs": [{ "internalType": "contract TornadoStakingRewards", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "torn",
"outputs": [{ "internalType": "contract TORN", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tornadoRouter",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "worker", "type": "address" }],
"name": "unregisterWorker",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "", "type": "address" }],
"name": "workers",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
}
]

File diff suppressed because it is too large Load Diff

1
app.js
View File

@@ -1 +0,0 @@
module.exports = require('./src/server')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -48,8 +48,7 @@ services:
# ---------------------- ETH Mainnet ----------------------- #
eth-server:
build: .
image: tornadorelayer:mainnet
image: tornadocash/relayer:v6
profiles: ['eth']
restart: always
command: server
@@ -60,57 +59,35 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
eth-treeWatcher:
image: tornadorelayer:mainnet
eth-healthWorker:
image: tornadocash/relayer:v6
profiles: ['eth']
restart: always
command: treeWatcher
command: healthWorker
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
depends_on: [redis, eth-server]
eth-priceWatcher:
image: tornadorelayer:mainnet
eth-txWorker1:
image: tornadocash/relayer:v6
profiles: ['eth']
restart: always
command: priceWatcher
command: txWorker
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
depends_on: [redis, eth-server]
eth-healthWatcher:
image: tornadorelayer:mainnet
profiles: ['eth']
restart: always
command: healthWatcher
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
depends_on: [redis, eth-server]
eth-worker1:
image: tornadorelayer:mainnet
profiles: ['eth']
restart: always
command: worker
env_file: .env.eth
environment:
NET_ID: 1
REDIS_URL: redis://redis/0
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
# eth-worker2:
# image: tornadorelayer:mainnet
# # This is additional txWorker for ethereum mainnet
# # So you can process transactions from multiple addresses, but before it you need to set up those addresses as txWorkers
# eth-txWorker2:
# image: tornadocash/relayer:v6
# profiles: [ 'eth' ]
# restart: always
# command: worker
# command: txWorker
# env_file: .env2.eth
# environment:
# REDIS_URL: redis://redis/0
@@ -168,7 +145,7 @@ services:
# ---------------------- BSC (Binance Smart Chain) ----------------------- #
bsc-server:
image: tornadorelayer:sidechain
image: tornadocash/relayer:v6
profiles: ['bsc']
restart: always
command: server
@@ -179,22 +156,22 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
bsc-healthWatcher:
image: tornadorelayer:sidechain
bsc-healthWorker:
image: tornadocash/relayer:v6
profiles: ['bsc']
restart: always
command: healthWatcher
command: healthWorker
env_file: .env.bsc
environment:
NET_ID: 56
REDIS_URL: redis://redis/1
depends_on: [redis, bsc-server]
bsc-worker1:
image: tornadorelayer:sidechain
bsc-txWorker1:
image: tornadocash/relayer:v6
profiles: ['bsc']
restart: always
command: worker
command: txWorker
env_file: .env.bsc
environment:
NET_ID: 56
@@ -206,7 +183,7 @@ services:
# ---------------------- Polygon (MATIC) --------------------- #
polygon-server:
image: tornadorelayer:sidechain
image: tornadocash/relayer:v6
profiles: ['polygon']
restart: always
command: server
@@ -217,22 +194,22 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
polygon-healthWatcher:
image: tornadorelayer:sidechain
polygon-healthWorker:
image: tornadocash/relayer:v6
profiles: ['polygon']
restart: always
command: healthWatcher
command: healthWorker
env_file: .env.polygon
environment:
NET_ID: 137
REDIS_URL: redis://redis/2
depends_on: [redis, polygon-server]
polygon-worker1:
image: tornadorelayer:sidechain
polygon-txWorker1:
image: tornadocash/relayer:v6
profiles: ['polygon']
restart: always
command: worker
command: txWorker
env_file: .env.polygon
environment:
NET_ID: 137
@@ -244,7 +221,7 @@ services:
# ---------------------- Gnosis (XDAI) ---------------------- #
gnosis-server:
image: tornadorelayer:sidechain
image: tornadocash/relayer:v6
profiles: ['gnosis']
restart: always
command: server
@@ -255,22 +232,22 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
gnosis-healthWatcher:
image: tornadorelayer:sidechain
gnosis-healthWorker:
image: tornadocash/relayer:v6
profiles: ['gnosis']
restart: always
command: healthWatcher
command: healthWorker
env_file: .env.gnosis
environment:
NET_ID: 100
REDIS_URL: redis://redis/3
depends_on: [redis, gnosis-server]
gnosis-worker1:
image: tornadorelayer:sidechain
gnosis-txWorker1:
image: tornadocash/relayer:v6
profiles: ['gnosis']
restart: always
command: worker
command: txWorker
env_file: .env.gnosis
environment:
NET_ID: 100
@@ -282,7 +259,7 @@ services:
# ---------------------- AVAX ---------------------- #
avax-server:
image: tornadorelayer:sidechain
image: tornadocash/relayer:v6
profiles: ['avax']
restart: always
command: server
@@ -293,22 +270,22 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
avax-healthWatcher:
image: tornadorelayer:sidechain
avax-healthWorker:
image: tornadocash/relayer:v6
profiles: ['avax']
restart: always
command: healthWatcher
command: healthWorker
env_file: .env.avax
environment:
NET_ID: 43114
REDIS_URL: redis://redis/4
depends_on: [redis, avax-server]
avax-worker1:
image: tornadorelayer:sidechain
avax-txWorker1:
image: tornadocash/relayer:v6
profiles: ['avax']
restart: always
command: worker
command: txWorker
env_file: .env.avax
environment:
NET_ID: 43114
@@ -320,7 +297,7 @@ services:
# ---------------------- OP ------------------------ #
op-server:
image: tornadorelayer:sidechain
image: tornadocash/relayer:v6
profiles: ['op']
restart: always
command: server
@@ -331,22 +308,22 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
op-healthWatcher:
image: tornadorelayer:sidechain
op-healthWorker:
image: tornadocash/relayer:v6
profiles: ['op']
restart: always
command: healthWatcher
command: healthWorker
env_file: .env.op
environment:
NET_ID: 10
REDIS_URL: redis://redis/5
depends_on: [redis, op-server]
op-worker1:
image: tornadorelayer:sidechain
op-txWorker1:
image: tornadocash/relayer:v6
profiles: ['op']
restart: always
command: worker
command: txWorker
env_file: .env.op
environment:
NET_ID: 10
@@ -358,7 +335,7 @@ services:
# ---------------------- Arbitrum ----------------------- #
arb-server:
image: tornadorelayer:sidechain
image: tornadocash/relayer:v6
profiles: ['arb']
restart: always
command: server
@@ -369,22 +346,22 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
arb-healthWatcher:
image: tornadorelayer:sidechain
arb-healthWorker:
image: tornadocash/relayer:v6
profiles: ['arb']
restart: always
command: healthWatcher
command: healthWorker
env_file: .env.arb
environment:
NET_ID: 42161
REDIS_URL: redis://redis/6
depends_on: [redis, arb-server]
arb-worker1:
image: tornadorelayer:sidechain
arb-txWorker1:
image: tornadocash/relayer:v6
profiles: ['arb']
restart: always
command: worker
command: txWorker
env_file: .env.arb
environment:
NET_ID: 42161
@@ -396,7 +373,7 @@ services:
# ---------------------- Goerli (Ethereum Testnet) ---------------------- #
goerli-server:
image: tornadorelayer:mainnet
image: tornadocash/relayer:v6
profiles: ['geth']
restart: always
command: server
@@ -407,44 +384,22 @@ services:
nginx_proxy_read_timeout: 600
depends_on: [redis]
goerli-treeWatcher:
image: tornadorelayer:mainnet
goerli-healthWorker:
image: tornadocash/relayer:v6
profiles: ['goerli']
restart: always
command: treeWatcher
command: healthWorker
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
depends_on: [redis, goerli-server]
goerli-priceWatcher:
image: tornadorelayer:mainnet
goerli-txWorker1:
image: tornadocash/relayer:v6
profiles: ['goerli']
restart: always
command: priceWatcher
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
depends_on: [redis, goerli-server]
goerli-healthWatcher:
image: tornadorelayer:mainnet
profiles: ['goerli']
restart: always
command: healthWatcher
env_file: .env.goerli
environment:
NET_ID: 5
REDIS_URL: redis://redis/7
depends_on: [redis, goerli-server]
goerli-worker1:
image: tornadorelayer:mainnet
profiles: ['goerli']
restart: always
command: worker
command: txWorker
env_file: .env.goerli
environment:
NET_ID: 5

110
install.sh Normal file
View File

@@ -0,0 +1,110 @@
#!/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";
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_node(){
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.3/install.sh | bash;
. ~/.nvm/nvm.sh;
. ~/.profile;
. ~/.bashrc;
nvm install 20.3.0;
}
function install_relayer_repo(){
git clone $relayer_soft_git_repo -b v6 $relayer_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 -s https://get.docker.com | bash;
}
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_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_folder/tornado-stream.conf /etc/nginx/conf.d/streams/tornado-stream.conf;
systemctl restart nginx;
systemctl stop nginx;
}
function build_relayer_docker_container(){
cd $relayer_folder && npm run build:docker;
}
function prepare_environments(){
tee $relayer_folder/.env.bsc $relayer_folder/.env.arb $relayer_folder/.env.goerli $relayer_folder/.env.eth $relayer_folder/.env.polygon $relayer_folder/.env.op \
$relayer_folder/.env.avax $relayer_folder/.env.gnosis < $relayer_folder/.env.example > /dev/null;
}
function main(){
install_requred_packages;
install_node;
install_relayer_repo;
configure_firewall;
configure_nginx_reverse_proxy;
install_docker_utilities;
build_relayer_docker_container;
prepare_environments;
cd $relayer_folder;
}
main;

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -1,50 +1,67 @@
{
"name": "relay",
"version": "5.2.0",
"description": "Relayer for Tornado.cash privacy solution.",
"name": "classic-relay",
"version": "6.0.0",
"description": "Relayer for Tornado.cash privacy solution. https://tornado.cash",
"scripts": {
"server": "node src/server.js",
"worker": "node src/worker",
"treeWatcher": "node src/treeWatcher",
"priceWatcher": "node src/priceWatcher",
"healthWatcher": "node src/healthWatcher",
"eslint": "eslint --ext .js --ignore-path .gitignore .",
"prettier:check": "npx prettier --check . --config .prettierrc",
"prettier:fix": "npx prettier --write . --config .prettierrc",
"lint": "yarn eslint && yarn prettier:check",
"test": "mocha",
"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\""
"start": "yarn build && docker-compose up -d redis && concurrently \"yarn server\" \"yarn txWorker\" \"yarn healthWorker\"",
"server": "node build/src/app/index.js",
"txWorker": "node build/src/txWorker.js",
"healthWorker": "node build/src/healthWorker.js",
"dev": "docker-compose up -d redis && concurrently \"yarn dev:server\" \"yarn dev:txWorker\" \"yarn dev:healthWorker\"",
"dev:server": "nodemon --watch './src/**/*.ts' --exec ts-node src/app/index.ts",
"dev:healthWorker": "nodemon --watch './src/**/*.ts' --exec ts-node src/healthWorker.ts",
"dev:txWorker": "nodemon --watch './src/**/*.ts' --exec ts-node src/txWorker.ts",
"build": "tsc",
"build:abi": "yarn typechain --target ethers-v5 --out-dir src/contracts ./abis/*.abi.json",
"build:docker": "docker build -t tornadocash/relayer:v6 .",
"test": "tap --node-arg=--require=ts-node/register --no-check-coverage",
"eslint": "eslint --ext .ts --ignore-path .gitignore .",
"prettier:check": "npx prettier --check . --config .prettierrc --ignore-path .gitignore",
"prettier:fix": "npx prettier --write . --config .prettierrc --ignore-path .gitignore",
"lint": "yarn eslint && yarn prettier:check"
},
"author": "Tornado Cash team",
"author": "tornado.cash",
"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",
"@fastify/cors": "^8.0.0",
"@fastify/helmet": "^9.1.0",
"@fastify/sensible": "^5.1.0",
"@tornado/gas-price-oracle": "^0.5.3",
"ajv": "^8.11.0",
"bullmq": "^1.80.6",
"compare-versions": "^4.1.3",
"concurrently": "^8.2.0",
"dotenv": "^8.2.0",
"eth-ens-namehash": "^2.0.8",
"express": "^4.17.1",
"ioredis": "^4.14.1",
"node-fetch": "^2.6.7",
"uuid": "^8.3.0",
"web3": "^1.3.0",
"web3-core-promievent": "^1.3.0",
"web3-utils": "^1.2.2"
"ethers": "^5.6.4",
"fastify": "^4.2.0",
"ioredis": "^5.0.6",
"json-schema-to-ts": "^2.2.0",
"node-fetch": "2",
"reflect-metadata": "^0.1.13",
"telegraf": "^4.12.3-canary.1",
"torn-token": "^1.0.8",
"tsyringe": "^4.8.0",
"tx-manager": "^0.4.9",
"uuid": "^8.3.0"
},
"devDependencies": {
"@typechain/ethers-v5": "^11.0.0",
"@types/ioredis": "^4.28.10",
"@types/node": "^20.4.1",
"@types/tap": "^15.0.7",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.20.0",
"@typescript-eslint/parser": "^5.20.0",
"chai": "^4.2.0",
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.12.0",
"eslint-plugin-prettier": "^3.1.4",
"mocha": "^8.1.3",
"prettier": "^2.1.2"
"eslint": "^8.19.0",
"eslint-config-prettier": "^8.5.0",
"mocha": "^10.0.0",
"nodemon": "^2.0.19",
"pino-pretty": "^8.1.0",
"prettier": "^2.7.1",
"tap": "^16.3.0",
"ts-node": "^10.7.0",
"typechain": "^8.2.0",
"typescript": "^4.7.4"
}
}

24
src/app/index.ts Normal file
View File

@@ -0,0 +1,24 @@
import 'reflect-metadata';
import createServer from './server';
import { utils } from 'ethers';
import { port, relayerVersion, rewardAccount } from '../config';
import { configService, getJobService, getNotifierService } from '../services';
if (!utils.isAddress(rewardAccount)) {
throw new Error('No REWARD_ACCOUNT specified');
}
const server = createServer();
server.listen({ port, host: '0.0.0.0' }, async (err, address) => {
if (err) throw err;
await configService.init();
await configService.clearRedisState();
await getJobService().setupRepeatableJobs();
await getNotifierService().subscribe();
console.log(`Relayer ${relayerVersion} started on port ${address}`);
});
process.on('uncaughtException', (e) => {
console.log('uncaughtException', e);
process.exit(1);
});

View File

@@ -0,0 +1,52 @@
import Ajv from 'ajv';
import fp from 'fastify-plugin';
import { rewardAccount } from '../../config';
import { getAddress, isAddress } from 'ethers/lib/utils';
import { configService } from '../../services';
import addFormats from 'ajv-formats';
export default fp(async (server) => {
const ajv = new Ajv();
addFormats(ajv);
ajv.addKeyword({
keyword: 'isAddress',
validate: (schema, data) => {
try {
return isAddress(data);
} catch (e) {
return false;
}
},
errors: true,
});
ajv.addKeyword({
keyword: 'isKnownContract',
validate: (schema, data) => {
try {
return !!configService.getInstance(data);
} catch (e) {
return false;
}
},
errors: true,
});
ajv.addKeyword({
keyword: 'isFeeRecipient',
validate: (schema, data) => {
try {
return getAddress(rewardAccount) === getAddress(data);
} catch (e) {
return false;
}
},
errors: true,
});
server.setValidatorCompiler(({ schema }) => {
return ajv.compile(schema);
});
console.log('validator plugin registered');
return Promise.resolve();
});

55
src/app/routes.ts Normal file
View File

@@ -0,0 +1,55 @@
import { FastifyInstance } from 'fastify';
import { jobsSchema, statusSchema, withdrawSchema } from './schema';
import { relayerVersion, rewardAccount, tornadoServiceFee } from '../config';
import { configService, getHealthService, getJobService, getPriceService } from '../services';
import { RelayerJobType } from '../types';
import { WithdrawalData } from '../services/tx.service';
export function mainHandler(server: FastifyInstance, options, next) {
const jobService = getJobService();
const priceService = getPriceService();
const healthService = getHealthService();
server.get('/', async (req, res) => {
res
.type('text/html')
.send(
'<h1>This is <a href=https://tornado.cash>tornado.cash</a> Relayer service.' +
' Check the <a href=/v1/status>/status</a> for settings</h1>',
);
});
server.get('/status', { schema: statusSchema }, async (req, res) => {
const ethPrices = await priceService.getPrices();
const currentQueue = await jobService.getQueueCount();
const health = await healthService.getStatus();
const version = relayerVersion;
res.send({
rewardAccount,
instances: configService.instances,
netId: configService.netId,
ethPrices,
tornadoServiceFee,
version,
health,
currentQueue,
});
});
next();
}
export function relayerHandler(server: FastifyInstance, options, next) {
const jobService = getJobService();
server.get<{ Params: { id: string } }>('/jobs/:id', { schema: jobsSchema }, async (req, res) => {
const job = await jobService.getJob(req.params.id);
if (!job) return server.httpErrors.notFound();
res.send({ ...job.data, failedReason: job.failedReason });
});
server.post<{ Body: WithdrawalData }>('/tornadoWithdraw', { schema: withdrawSchema }, async (req, res) => {
server.log.info(`Withdrawal request: ${JSON.stringify(req.body)}`);
const id = await jobService.postJob(RelayerJobType.TORNADO_WITHDRAW, req.body);
res.send({ id });
});
next();
}

80
src/app/schema.ts Normal file
View File

@@ -0,0 +1,80 @@
const addressType = {
type: 'string',
pattern: '^0x[a-fA-F0-9]{40}$',
isAddress: true,
} as const;
const proofType = { type: 'string', pattern: '^0x[a-fA-F0-9]{512}$' } as const;
// const encryptedAccountType = { type: 'string', pattern: '^0x[a-fA-F0-9]{392}$' } as const;
const bytes32Type = { type: 'string', pattern: '^0x[a-fA-F0-9]{64}$' } as const;
const instanceType = { ...addressType, isKnownContract: true } as const;
const relayerType = { ...addressType, isFeeRecipient: true } as const;
export const idParamsSchema = {
type: 'object',
properties: {
id: { type: 'string', format: 'uuid' },
},
required: ['id'],
additionalProperties: false,
} as const;
export const withdrawBodySchema = {
type: 'object',
properties: {
proof: proofType,
contract: instanceType,
args: {
type: 'array',
maxItems: 6,
minItems: 6,
items: [bytes32Type, bytes32Type, addressType, relayerType, bytes32Type, bytes32Type],
},
},
additionalProperties: false,
required: ['proof', 'contract', 'args'],
} as const;
export const jobsResponseSchema = {
...withdrawBodySchema,
properties: {
id: { type: 'string' },
status: { type: 'string' },
type: { type: 'string' },
confirmations: { type: 'integer' },
txHash: { type: 'string' },
...withdrawBodySchema.properties,
failedReason: { type: 'string' },
},
} as const;
export const jobsSchema = {
params: idParamsSchema,
response: {
200: jobsResponseSchema,
},
};
export const withdrawSchema = {
body: withdrawBodySchema,
response: {
200: idParamsSchema,
},
};
const statusResponseSchema = {
type: 'object',
properties: {
rewardAccount: addressType,
netId: { type: 'integer' },
version: { type: 'string' },
tornadoServiceFee: { type: 'number' },
currentQueue: { type: 'number' },
ethPrices: { type: 'object', additionalProperties: true },
instances: { type: 'object', additionalProperties: true },
health: { type: 'object', additionalProperties: true },
},
} as const;
export const statusSchema = {
response: {
200: statusResponseSchema,
},
};

35
src/app/server.ts Normal file
View File

@@ -0,0 +1,35 @@
import fastify from 'fastify';
import cors from '@fastify/cors';
import fastifySensible from '@fastify/sensible';
import helmet from '@fastify/helmet';
import validator from './plugins/validator';
import { mainHandler, relayerHandler } from './routes';
function createServer() {
const server = fastify({
logger: true,
trustProxy: true,
pluginTimeout: 5000,
bodyLimit: 1048576, // 1 mb
});
server.register(cors);
server.register(validator);
server.register(helmet, { contentSecurityPolicy: false, frameguard: true });
server.register(fastifySensible);
server.register(mainHandler);
server.register(mainHandler, { prefix: '/v1' });
server.register(relayerHandler, { prefix: '/v1' });
server.setErrorHandler((error, req, res) => {
req.log.error(error.toString());
let statusCode = 500;
if ('validation' in error) {
statusCode = 400;
}
res.code(statusCode).send({ error: error.toString() });
});
return server;
}
export default createServer;

View File

@@ -1,27 +0,0 @@
require('dotenv').config()
const { jobType } = require('./constants')
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://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.RELAYER_FEE),
rewardAccount: process.env.REWARD_ACCOUNT,
governanceAddress: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
tornadoGoerliProxy: '0x454d870a72e29d5E5697f635128D18077BD04C60',
gasLimits: {
[jobType.MINING_REWARD]: 455000,
[jobType.MINING_WITHDRAW]: 400000,
},
minimumBalance: '500000000000000000',
baseFeeReserve: Number(process.env.BASE_FEE_RESERVE_PERCENTAGE),
}

112
src/config.ts Normal file
View File

@@ -0,0 +1,112 @@
import { RelayerJobType } from './types';
import tornConfig, { availableIds } from 'torn-token';
import { config } from 'dotenv';
import { version } from '../package.json';
import { BigNumber } from 'ethers';
config();
const isProduction = process.env.NODE_ENV === 'production';
export const relayerVersion = version;
export const netId = <availableIds>Number(process.env.NET_ID ?? 1);
export const redisUrl = process.env.REDIS_URL || 'redis://127.0.0.1:6379';
export const rpcUrl = process.env.HTTP_RPC_URL;
export const CONFIRMATIONS = Number(process.env.CONFIRMATIONS ?? 4);
export const MAX_GAS_PRICE = Number(process.env.MAX_GAS_PRICE ?? 1000);
export const GAS_BUMP_PERCENTAGE = Number(process.env.GAS_BUMP_PERCENTAGE ?? 5);
export const mainnetRpcUrl = process.env.MAINNET_RPC_URL || process.env.ORACLE_RPC_URL || 'https://api.securerpc.com/v1';
export const oracleRpcUrl = process.env.ORACLE_RPC_URL || 'https://api.securerpc.com/v1';
export const offchainOracleAddress = '0x07D91f5fb9Bf7798734C3f606dB065549F6893bb';
export const multiCallAddress = '0xda3c19c6fe954576707fa24695efb830d9cca1ca';
export const privateKey = process.env.PRIVATE_KEY;
export const instances = tornConfig.instances;
export const torn = tornConfig;
export const port = Number(process.env.APP_PORT) || 8000;
export const host = isProduction ? 'https://' + process.env.VIRTUAL_HOST : `http://localhost:${port}`;
export const tornadoServiceFee = Number(process.env.RELAYER_FEE);
export const rewardAccount = process.env.REWARD_ACCOUNT;
export const tornadoGoerliProxy = '0x454d870a72e29d5E5697f635128D18077BD04C60';
export const ovmGasPriceOracleContract = '0x420000000000000000000000000000000000000F';
export const txJobAttempts = 3;
export const gasLimits = {
[RelayerJobType.TORNADO_WITHDRAW]: 390000,
[RelayerJobType.WITHDRAW_WITH_EXTRA]: 700000,
[RelayerJobType.OP_TORNADO_WITHDRAW]: 440000,
[RelayerJobType.ARB_TORNADO_WITHDRAW]: 1900000,
};
const minimumBalances: { [availableId in availableIds]: number } = {
1: 0.5, // 0.5 ETH on Ethereum mainnet
5: 10, // 10 ETH on Goerli testnet (because of high gas spikes)
10: 0.1, // 0.1 ETH on Optimism
56: 0.1, // 0.1 BNB on Binance Smart Chain
100: 100, // 100 XDAI on Gnosis (~ 100$)
137: 10, // 10 MATIC on Polygon
42161: 0.1, // 0.1 ETH on Arbitrum
43114: 10, // 10 AVAX on Avalanche C-Chain
};
const decimals = BigNumber.from(10).pow(18);
export const minimumBalance = BigNumber.from(minimumBalances[netId] * 10) // BN don't accept float values
.mul(decimals.div(10))
.toString();
export const minimumTornBalance = BigNumber.from(500).mul(decimals).toString();
export const tornToken = {
tokenAddress: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
symbol: 'TORN',
decimals: 18,
};
export 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',
},
};

View File

@@ -1,20 +0,0 @@
const jobType = Object.freeze({
TORNADO_WITHDRAW: 'TORNADO_WITHDRAW',
MINING_REWARD: 'MINING_REWARD',
MINING_WITHDRAW: 'MINING_WITHDRAW',
})
const status = Object.freeze({
QUEUED: 'QUEUED',
ACCEPTED: 'ACCEPTED',
SENT: 'SENT',
MINED: 'MINED',
RESUBMITTED: 'RESUBMITTED',
CONFIRMED: 'CONFIRMED',
FAILED: 'FAILED',
})
module.exports = {
jobType,
status,
}

View File

@@ -1,55 +0,0 @@
const {
getTornadoWithdrawInputError,
getMiningRewardInputError,
getMiningWithdrawInputError,
} = require('../modules/validator')
const { postJob } = require('../queue')
const { jobType } = require('../constants')
async function tornadoWithdraw(req, res) {
const inputError = getTornadoWithdrawInputError(req.body)
if (inputError) {
console.log('Invalid input:', inputError)
return res.status(400).json({ error: inputError })
}
const id = await postJob({
type: jobType.TORNADO_WITHDRAW,
request: req.body,
})
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 = {
tornadoWithdraw,
miningReward,
miningWithdraw,
}

View File

@@ -1,4 +0,0 @@
module.exports = {
controller: require('./controller'),
status: require('./status'),
}

View File

@@ -1,41 +0,0 @@
const queue = require('../queue')
const { netId, tornadoServiceFee, miningServiceFee, instances, rewardAccount } = require('../config')
const { version } = require('../../package.json')
const { redis } = require('../modules/redis')
const { readRelayerErrors } = require('../utils')
async function status(req, res) {
const ethPrices = await redis.hgetall('prices')
const health = await redis.hgetall('health')
health.errorsLog = await readRelayerErrors(redis)
const { waiting: currentQueue } = await queue.queue.getJobCounts()
res.json({
rewardAccount,
instances: instances[netId],
netId,
ethPrices,
tornadoServiceFee,
miningServiceFee,
version,
health,
currentQueue,
})
}
function index(req, res) {
res.send(
'This is <a href=https://tornado.ws>Tornado Cash</a> Relayer service. Check the <a href=/v1/status>/status</a> for settings',
)
}
async function getJob(req, res) {
const status = await queue.getJobStatus(req.params.id)
return status ? res.json(status) : res.status(400).json({ error: "The job doesn't exist" })
}
module.exports = {
status,
index,
getJob,
}

View File

@@ -0,0 +1,592 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type {
FunctionFragment,
Result,
EventFragment,
} from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export interface AggregatorAbiInterface extends utils.Interface {
functions: {
"addConnector(address)": FunctionFragment;
"addOracle(address,uint8)": FunctionFragment;
"connectors()": FunctionFragment;
"getRate(address,address,bool)": FunctionFragment;
"getRateToEth(address,bool)": FunctionFragment;
"multiWrapper()": FunctionFragment;
"oracles()": FunctionFragment;
"owner()": FunctionFragment;
"removeConnector(address)": FunctionFragment;
"removeOracle(address,uint8)": FunctionFragment;
"renounceOwnership()": FunctionFragment;
"setMultiWrapper(address)": FunctionFragment;
"transferOwnership(address)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "addConnector"
| "addOracle"
| "connectors"
| "getRate"
| "getRateToEth"
| "multiWrapper"
| "oracles"
| "owner"
| "removeConnector"
| "removeOracle"
| "renounceOwnership"
| "setMultiWrapper"
| "transferOwnership"
): FunctionFragment;
encodeFunctionData(
functionFragment: "addConnector",
values: [string]
): string;
encodeFunctionData(
functionFragment: "addOracle",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "connectors",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getRate",
values: [string, string, boolean]
): string;
encodeFunctionData(
functionFragment: "getRateToEth",
values: [string, boolean]
): string;
encodeFunctionData(
functionFragment: "multiWrapper",
values?: undefined
): string;
encodeFunctionData(functionFragment: "oracles", values?: undefined): string;
encodeFunctionData(functionFragment: "owner", values?: undefined): string;
encodeFunctionData(
functionFragment: "removeConnector",
values: [string]
): string;
encodeFunctionData(
functionFragment: "removeOracle",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "renounceOwnership",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "setMultiWrapper",
values: [string]
): string;
encodeFunctionData(
functionFragment: "transferOwnership",
values: [string]
): string;
decodeFunctionResult(
functionFragment: "addConnector",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "addOracle", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "connectors", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "getRate", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getRateToEth",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "multiWrapper",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "oracles", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "removeConnector",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "removeOracle",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "renounceOwnership",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setMultiWrapper",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "transferOwnership",
data: BytesLike
): Result;
events: {
"ConnectorAdded(address)": EventFragment;
"ConnectorRemoved(address)": EventFragment;
"MultiWrapperUpdated(address)": EventFragment;
"OracleAdded(address,uint8)": EventFragment;
"OracleRemoved(address,uint8)": EventFragment;
"OwnershipTransferred(address,address)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "ConnectorAdded"): EventFragment;
getEvent(nameOrSignatureOrTopic: "ConnectorRemoved"): EventFragment;
getEvent(nameOrSignatureOrTopic: "MultiWrapperUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OracleAdded"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OracleRemoved"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment;
}
export interface ConnectorAddedEventObject {
connector: string;
}
export type ConnectorAddedEvent = TypedEvent<
[string],
ConnectorAddedEventObject
>;
export type ConnectorAddedEventFilter = TypedEventFilter<ConnectorAddedEvent>;
export interface ConnectorRemovedEventObject {
connector: string;
}
export type ConnectorRemovedEvent = TypedEvent<
[string],
ConnectorRemovedEventObject
>;
export type ConnectorRemovedEventFilter =
TypedEventFilter<ConnectorRemovedEvent>;
export interface MultiWrapperUpdatedEventObject {
multiWrapper: string;
}
export type MultiWrapperUpdatedEvent = TypedEvent<
[string],
MultiWrapperUpdatedEventObject
>;
export type MultiWrapperUpdatedEventFilter =
TypedEventFilter<MultiWrapperUpdatedEvent>;
export interface OracleAddedEventObject {
oracle: string;
oracleType: number;
}
export type OracleAddedEvent = TypedEvent<
[string, number],
OracleAddedEventObject
>;
export type OracleAddedEventFilter = TypedEventFilter<OracleAddedEvent>;
export interface OracleRemovedEventObject {
oracle: string;
oracleType: number;
}
export type OracleRemovedEvent = TypedEvent<
[string, number],
OracleRemovedEventObject
>;
export type OracleRemovedEventFilter = TypedEventFilter<OracleRemovedEvent>;
export interface OwnershipTransferredEventObject {
previousOwner: string;
newOwner: string;
}
export type OwnershipTransferredEvent = TypedEvent<
[string, string],
OwnershipTransferredEventObject
>;
export type OwnershipTransferredEventFilter =
TypedEventFilter<OwnershipTransferredEvent>;
export interface AggregatorAbi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: AggregatorAbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
connectors(
overrides?: CallOverrides
): Promise<[string[]] & { allConnectors: string[] }>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<[BigNumber] & { weightedRate: BigNumber }>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<[BigNumber] & { weightedRate: BigNumber }>;
multiWrapper(overrides?: CallOverrides): Promise<[string]>;
oracles(
overrides?: CallOverrides
): Promise<
[string[], number[]] & { allOracles: string[]; oracleTypes: number[] }
>;
owner(overrides?: CallOverrides): Promise<[string]>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
};
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
connectors(overrides?: CallOverrides): Promise<string[]>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
multiWrapper(overrides?: CallOverrides): Promise<string>;
oracles(
overrides?: CallOverrides
): Promise<
[string[], number[]] & { allOracles: string[]; oracleTypes: number[] }
>;
owner(overrides?: CallOverrides): Promise<string>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
callStatic: {
addConnector(connector: string, overrides?: CallOverrides): Promise<void>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
connectors(overrides?: CallOverrides): Promise<string[]>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
multiWrapper(overrides?: CallOverrides): Promise<string>;
oracles(
overrides?: CallOverrides
): Promise<
[string[], number[]] & { allOracles: string[]; oracleTypes: number[] }
>;
owner(overrides?: CallOverrides): Promise<string>;
removeConnector(
connector: string,
overrides?: CallOverrides
): Promise<void>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
renounceOwnership(overrides?: CallOverrides): Promise<void>;
setMultiWrapper(
_multiWrapper: string,
overrides?: CallOverrides
): Promise<void>;
transferOwnership(
newOwner: string,
overrides?: CallOverrides
): Promise<void>;
};
filters: {
"ConnectorAdded(address)"(connector?: null): ConnectorAddedEventFilter;
ConnectorAdded(connector?: null): ConnectorAddedEventFilter;
"ConnectorRemoved(address)"(connector?: null): ConnectorRemovedEventFilter;
ConnectorRemoved(connector?: null): ConnectorRemovedEventFilter;
"MultiWrapperUpdated(address)"(
multiWrapper?: null
): MultiWrapperUpdatedEventFilter;
MultiWrapperUpdated(multiWrapper?: null): MultiWrapperUpdatedEventFilter;
"OracleAdded(address,uint8)"(
oracle?: null,
oracleType?: null
): OracleAddedEventFilter;
OracleAdded(oracle?: null, oracleType?: null): OracleAddedEventFilter;
"OracleRemoved(address,uint8)"(
oracle?: null,
oracleType?: null
): OracleRemovedEventFilter;
OracleRemoved(oracle?: null, oracleType?: null): OracleRemovedEventFilter;
"OwnershipTransferred(address,address)"(
previousOwner?: string | null,
newOwner?: string | null
): OwnershipTransferredEventFilter;
OwnershipTransferred(
previousOwner?: string | null,
newOwner?: string | null
): OwnershipTransferredEventFilter;
};
estimateGas: {
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
connectors(overrides?: CallOverrides): Promise<BigNumber>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
multiWrapper(overrides?: CallOverrides): Promise<BigNumber>;
oracles(overrides?: CallOverrides): Promise<BigNumber>;
owner(overrides?: CallOverrides): Promise<BigNumber>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
};
populateTransaction: {
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
connectors(overrides?: CallOverrides): Promise<PopulatedTransaction>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
multiWrapper(overrides?: CallOverrides): Promise<PopulatedTransaction>;
oracles(overrides?: CallOverrides): Promise<PopulatedTransaction>;
owner(overrides?: CallOverrides): Promise<PopulatedTransaction>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
};
}

429
src/contracts/ERC20Abi.ts Normal file
View File

@@ -0,0 +1,429 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type {
FunctionFragment,
Result,
EventFragment,
} from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export interface ERC20AbiInterface extends utils.Interface {
functions: {
"totalSupply()": FunctionFragment;
"_totalSupply()": FunctionFragment;
"balanceOf(address)": FunctionFragment;
"transfer(address,uint256)": FunctionFragment;
"allowance(address,address)": FunctionFragment;
"transferFrom(address,address,uint256)": FunctionFragment;
"approve(address,uint256)": FunctionFragment;
"nonces(address)": FunctionFragment;
"permit(address,address,uint256,uint256,uint8,bytes32,bytes32)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "totalSupply"
| "_totalSupply"
| "balanceOf"
| "transfer"
| "allowance"
| "transferFrom"
| "approve"
| "nonces"
| "permit"
): FunctionFragment;
encodeFunctionData(
functionFragment: "totalSupply",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "_totalSupply",
values?: undefined
): string;
encodeFunctionData(functionFragment: "balanceOf", values: [string]): string;
encodeFunctionData(
functionFragment: "transfer",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "allowance",
values: [string, string]
): string;
encodeFunctionData(
functionFragment: "transferFrom",
values: [string, string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "approve",
values: [string, BigNumberish]
): string;
encodeFunctionData(functionFragment: "nonces", values: [string]): string;
encodeFunctionData(
functionFragment: "permit",
values: [
string,
string,
BigNumberish,
BigNumberish,
BigNumberish,
BytesLike,
BytesLike
]
): string;
decodeFunctionResult(
functionFragment: "totalSupply",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "_totalSupply",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "allowance", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "transferFrom",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "approve", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "nonces", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "permit", data: BytesLike): Result;
events: {
"Approval(address,address,uint256)": EventFragment;
"Transfer(address,address,uint256)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "Approval"): EventFragment;
getEvent(nameOrSignatureOrTopic: "Transfer"): EventFragment;
}
export interface ApprovalEventObject {
owner: string;
spender: string;
value: BigNumber;
}
export type ApprovalEvent = TypedEvent<
[string, string, BigNumber],
ApprovalEventObject
>;
export type ApprovalEventFilter = TypedEventFilter<ApprovalEvent>;
export interface TransferEventObject {
from: string;
to: string;
value: BigNumber;
}
export type TransferEvent = TypedEvent<
[string, string, BigNumber],
TransferEventObject
>;
export type TransferEventFilter = TypedEventFilter<TransferEvent>;
export interface ERC20Abi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: ERC20AbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
totalSupply(overrides?: CallOverrides): Promise<[BigNumber]>;
_totalSupply(overrides?: CallOverrides): Promise<[BigNumber]>;
balanceOf(who: string, overrides?: CallOverrides): Promise<[BigNumber]>;
transfer(
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<[BigNumber]>;
transferFrom(
from: string,
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
approve(
spender: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
nonces(owner: string, overrides?: CallOverrides): Promise<[BigNumber]>;
permit(
owner: string,
spender: string,
amount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: BytesLike,
s: BytesLike,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
};
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
_totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
balanceOf(who: string, overrides?: CallOverrides): Promise<BigNumber>;
transfer(
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
transferFrom(
from: string,
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
approve(
spender: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
nonces(owner: string, overrides?: CallOverrides): Promise<BigNumber>;
permit(
owner: string,
spender: string,
amount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: BytesLike,
s: BytesLike,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
callStatic: {
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
_totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
balanceOf(who: string, overrides?: CallOverrides): Promise<BigNumber>;
transfer(
to: string,
value: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
transferFrom(
from: string,
to: string,
value: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
approve(
spender: string,
value: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
nonces(owner: string, overrides?: CallOverrides): Promise<BigNumber>;
permit(
owner: string,
spender: string,
amount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: BytesLike,
s: BytesLike,
overrides?: CallOverrides
): Promise<void>;
};
filters: {
"Approval(address,address,uint256)"(
owner?: string | null,
spender?: string | null,
value?: null
): ApprovalEventFilter;
Approval(
owner?: string | null,
spender?: string | null,
value?: null
): ApprovalEventFilter;
"Transfer(address,address,uint256)"(
from?: string | null,
to?: string | null,
value?: null
): TransferEventFilter;
Transfer(
from?: string | null,
to?: string | null,
value?: null
): TransferEventFilter;
};
estimateGas: {
totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
_totalSupply(overrides?: CallOverrides): Promise<BigNumber>;
balanceOf(who: string, overrides?: CallOverrides): Promise<BigNumber>;
transfer(
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<BigNumber>;
transferFrom(
from: string,
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
approve(
spender: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
nonces(owner: string, overrides?: CallOverrides): Promise<BigNumber>;
permit(
owner: string,
spender: string,
amount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: BytesLike,
s: BytesLike,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
};
populateTransaction: {
totalSupply(overrides?: CallOverrides): Promise<PopulatedTransaction>;
_totalSupply(overrides?: CallOverrides): Promise<PopulatedTransaction>;
balanceOf(
who: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
transfer(
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
allowance(
owner: string,
spender: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
transferFrom(
from: string,
to: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
approve(
spender: string,
value: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
nonces(
owner: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
permit(
owner: string,
spender: string,
amount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: BytesLike,
s: BytesLike,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
};
}

View File

@@ -0,0 +1,112 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BytesLike,
CallOverrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type { FunctionFragment, Result } from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export declare namespace MultiCall {
export type CallStruct = { to: string; data: BytesLike };
export type CallStructOutput = [string, string] & {
to: string;
data: string;
};
}
export interface MulticallAbiInterface extends utils.Interface {
functions: {
"multicall((address,bytes)[])": FunctionFragment;
};
getFunction(nameOrSignatureOrTopic: "multicall"): FunctionFragment;
encodeFunctionData(
functionFragment: "multicall",
values: [MultiCall.CallStruct[]]
): string;
decodeFunctionResult(functionFragment: "multicall", data: BytesLike): Result;
events: {};
}
export interface MulticallAbi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: MulticallAbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
multicall(
calls: MultiCall.CallStruct[],
overrides?: CallOverrides
): Promise<
[string[], boolean[]] & { results: string[]; success: boolean[] }
>;
};
multicall(
calls: MultiCall.CallStruct[],
overrides?: CallOverrides
): Promise<[string[], boolean[]] & { results: string[]; success: boolean[] }>;
callStatic: {
multicall(
calls: MultiCall.CallStruct[],
overrides?: CallOverrides
): Promise<
[string[], boolean[]] & { results: string[]; success: boolean[] }
>;
};
filters: {};
estimateGas: {
multicall(
calls: MultiCall.CallStruct[],
overrides?: CallOverrides
): Promise<BigNumber>;
};
populateTransaction: {
multicall(
calls: MultiCall.CallStruct[],
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
};
}

View File

@@ -0,0 +1,592 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type {
FunctionFragment,
Result,
EventFragment,
} from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export interface OffchainOracleAbiInterface extends utils.Interface {
functions: {
"addConnector(address)": FunctionFragment;
"addOracle(address,uint8)": FunctionFragment;
"connectors()": FunctionFragment;
"getRate(address,address,bool)": FunctionFragment;
"getRateToEth(address,bool)": FunctionFragment;
"multiWrapper()": FunctionFragment;
"oracles()": FunctionFragment;
"owner()": FunctionFragment;
"removeConnector(address)": FunctionFragment;
"removeOracle(address,uint8)": FunctionFragment;
"renounceOwnership()": FunctionFragment;
"setMultiWrapper(address)": FunctionFragment;
"transferOwnership(address)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "addConnector"
| "addOracle"
| "connectors"
| "getRate"
| "getRateToEth"
| "multiWrapper"
| "oracles"
| "owner"
| "removeConnector"
| "removeOracle"
| "renounceOwnership"
| "setMultiWrapper"
| "transferOwnership"
): FunctionFragment;
encodeFunctionData(
functionFragment: "addConnector",
values: [string]
): string;
encodeFunctionData(
functionFragment: "addOracle",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "connectors",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getRate",
values: [string, string, boolean]
): string;
encodeFunctionData(
functionFragment: "getRateToEth",
values: [string, boolean]
): string;
encodeFunctionData(
functionFragment: "multiWrapper",
values?: undefined
): string;
encodeFunctionData(functionFragment: "oracles", values?: undefined): string;
encodeFunctionData(functionFragment: "owner", values?: undefined): string;
encodeFunctionData(
functionFragment: "removeConnector",
values: [string]
): string;
encodeFunctionData(
functionFragment: "removeOracle",
values: [string, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "renounceOwnership",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "setMultiWrapper",
values: [string]
): string;
encodeFunctionData(
functionFragment: "transferOwnership",
values: [string]
): string;
decodeFunctionResult(
functionFragment: "addConnector",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "addOracle", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "connectors", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "getRate", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getRateToEth",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "multiWrapper",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "oracles", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "removeConnector",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "removeOracle",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "renounceOwnership",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setMultiWrapper",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "transferOwnership",
data: BytesLike
): Result;
events: {
"ConnectorAdded(address)": EventFragment;
"ConnectorRemoved(address)": EventFragment;
"MultiWrapperUpdated(address)": EventFragment;
"OracleAdded(address,uint8)": EventFragment;
"OracleRemoved(address,uint8)": EventFragment;
"OwnershipTransferred(address,address)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "ConnectorAdded"): EventFragment;
getEvent(nameOrSignatureOrTopic: "ConnectorRemoved"): EventFragment;
getEvent(nameOrSignatureOrTopic: "MultiWrapperUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OracleAdded"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OracleRemoved"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment;
}
export interface ConnectorAddedEventObject {
connector: string;
}
export type ConnectorAddedEvent = TypedEvent<
[string],
ConnectorAddedEventObject
>;
export type ConnectorAddedEventFilter = TypedEventFilter<ConnectorAddedEvent>;
export interface ConnectorRemovedEventObject {
connector: string;
}
export type ConnectorRemovedEvent = TypedEvent<
[string],
ConnectorRemovedEventObject
>;
export type ConnectorRemovedEventFilter =
TypedEventFilter<ConnectorRemovedEvent>;
export interface MultiWrapperUpdatedEventObject {
multiWrapper: string;
}
export type MultiWrapperUpdatedEvent = TypedEvent<
[string],
MultiWrapperUpdatedEventObject
>;
export type MultiWrapperUpdatedEventFilter =
TypedEventFilter<MultiWrapperUpdatedEvent>;
export interface OracleAddedEventObject {
oracle: string;
oracleType: number;
}
export type OracleAddedEvent = TypedEvent<
[string, number],
OracleAddedEventObject
>;
export type OracleAddedEventFilter = TypedEventFilter<OracleAddedEvent>;
export interface OracleRemovedEventObject {
oracle: string;
oracleType: number;
}
export type OracleRemovedEvent = TypedEvent<
[string, number],
OracleRemovedEventObject
>;
export type OracleRemovedEventFilter = TypedEventFilter<OracleRemovedEvent>;
export interface OwnershipTransferredEventObject {
previousOwner: string;
newOwner: string;
}
export type OwnershipTransferredEvent = TypedEvent<
[string, string],
OwnershipTransferredEventObject
>;
export type OwnershipTransferredEventFilter =
TypedEventFilter<OwnershipTransferredEvent>;
export interface OffchainOracleAbi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: OffchainOracleAbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
connectors(
overrides?: CallOverrides
): Promise<[string[]] & { allConnectors: string[] }>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<[BigNumber] & { weightedRate: BigNumber }>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<[BigNumber] & { weightedRate: BigNumber }>;
multiWrapper(overrides?: CallOverrides): Promise<[string]>;
oracles(
overrides?: CallOverrides
): Promise<
[string[], number[]] & { allOracles: string[]; oracleTypes: number[] }
>;
owner(overrides?: CallOverrides): Promise<[string]>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
};
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
connectors(overrides?: CallOverrides): Promise<string[]>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
multiWrapper(overrides?: CallOverrides): Promise<string>;
oracles(
overrides?: CallOverrides
): Promise<
[string[], number[]] & { allOracles: string[]; oracleTypes: number[] }
>;
owner(overrides?: CallOverrides): Promise<string>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
callStatic: {
addConnector(connector: string, overrides?: CallOverrides): Promise<void>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
connectors(overrides?: CallOverrides): Promise<string[]>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
multiWrapper(overrides?: CallOverrides): Promise<string>;
oracles(
overrides?: CallOverrides
): Promise<
[string[], number[]] & { allOracles: string[]; oracleTypes: number[] }
>;
owner(overrides?: CallOverrides): Promise<string>;
removeConnector(
connector: string,
overrides?: CallOverrides
): Promise<void>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
renounceOwnership(overrides?: CallOverrides): Promise<void>;
setMultiWrapper(
_multiWrapper: string,
overrides?: CallOverrides
): Promise<void>;
transferOwnership(
newOwner: string,
overrides?: CallOverrides
): Promise<void>;
};
filters: {
"ConnectorAdded(address)"(connector?: null): ConnectorAddedEventFilter;
ConnectorAdded(connector?: null): ConnectorAddedEventFilter;
"ConnectorRemoved(address)"(connector?: null): ConnectorRemovedEventFilter;
ConnectorRemoved(connector?: null): ConnectorRemovedEventFilter;
"MultiWrapperUpdated(address)"(
multiWrapper?: null
): MultiWrapperUpdatedEventFilter;
MultiWrapperUpdated(multiWrapper?: null): MultiWrapperUpdatedEventFilter;
"OracleAdded(address,uint8)"(
oracle?: null,
oracleType?: null
): OracleAddedEventFilter;
OracleAdded(oracle?: null, oracleType?: null): OracleAddedEventFilter;
"OracleRemoved(address,uint8)"(
oracle?: null,
oracleType?: null
): OracleRemovedEventFilter;
OracleRemoved(oracle?: null, oracleType?: null): OracleRemovedEventFilter;
"OwnershipTransferred(address,address)"(
previousOwner?: string | null,
newOwner?: string | null
): OwnershipTransferredEventFilter;
OwnershipTransferred(
previousOwner?: string | null,
newOwner?: string | null
): OwnershipTransferredEventFilter;
};
estimateGas: {
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
connectors(overrides?: CallOverrides): Promise<BigNumber>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<BigNumber>;
multiWrapper(overrides?: CallOverrides): Promise<BigNumber>;
oracles(overrides?: CallOverrides): Promise<BigNumber>;
owner(overrides?: CallOverrides): Promise<BigNumber>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
};
populateTransaction: {
addConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
addOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
connectors(overrides?: CallOverrides): Promise<PopulatedTransaction>;
getRate(
srcToken: string,
dstToken: string,
useWrappers: boolean,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
getRateToEth(
srcToken: string,
useSrcWrappers: boolean,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
multiWrapper(overrides?: CallOverrides): Promise<PopulatedTransaction>;
oracles(overrides?: CallOverrides): Promise<PopulatedTransaction>;
owner(overrides?: CallOverrides): Promise<PopulatedTransaction>;
removeConnector(
connector: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
removeOracle(
oracle: string,
oracleKind: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
setMultiWrapper(
_multiWrapper: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
};
}

View File

@@ -0,0 +1,543 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type {
FunctionFragment,
Result,
EventFragment,
} from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export interface OvmGasPriceOracleAbiInterface extends utils.Interface {
functions: {
"decimals()": FunctionFragment;
"gasPrice()": FunctionFragment;
"getL1Fee(bytes)": FunctionFragment;
"getL1GasUsed(bytes)": FunctionFragment;
"l1BaseFee()": FunctionFragment;
"overhead()": FunctionFragment;
"owner()": FunctionFragment;
"renounceOwnership()": FunctionFragment;
"scalar()": FunctionFragment;
"setDecimals(uint256)": FunctionFragment;
"setGasPrice(uint256)": FunctionFragment;
"setL1BaseFee(uint256)": FunctionFragment;
"setOverhead(uint256)": FunctionFragment;
"setScalar(uint256)": FunctionFragment;
"transferOwnership(address)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "decimals"
| "gasPrice"
| "getL1Fee"
| "getL1GasUsed"
| "l1BaseFee"
| "overhead"
| "owner"
| "renounceOwnership"
| "scalar"
| "setDecimals"
| "setGasPrice"
| "setL1BaseFee"
| "setOverhead"
| "setScalar"
| "transferOwnership"
): FunctionFragment;
encodeFunctionData(functionFragment: "decimals", values?: undefined): string;
encodeFunctionData(functionFragment: "gasPrice", values?: undefined): string;
encodeFunctionData(functionFragment: "getL1Fee", values: [BytesLike]): string;
encodeFunctionData(
functionFragment: "getL1GasUsed",
values: [BytesLike]
): string;
encodeFunctionData(functionFragment: "l1BaseFee", values?: undefined): string;
encodeFunctionData(functionFragment: "overhead", values?: undefined): string;
encodeFunctionData(functionFragment: "owner", values?: undefined): string;
encodeFunctionData(
functionFragment: "renounceOwnership",
values?: undefined
): string;
encodeFunctionData(functionFragment: "scalar", values?: undefined): string;
encodeFunctionData(
functionFragment: "setDecimals",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setGasPrice",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setL1BaseFee",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setOverhead",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setScalar",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "transferOwnership",
values: [string]
): string;
decodeFunctionResult(functionFragment: "decimals", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "gasPrice", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "getL1Fee", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getL1GasUsed",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "l1BaseFee", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "overhead", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "renounceOwnership",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "scalar", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "setDecimals",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setGasPrice",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setL1BaseFee",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setOverhead",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "setScalar", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "transferOwnership",
data: BytesLike
): Result;
events: {
"DecimalsUpdated(uint256)": EventFragment;
"GasPriceUpdated(uint256)": EventFragment;
"L1BaseFeeUpdated(uint256)": EventFragment;
"OverheadUpdated(uint256)": EventFragment;
"OwnershipTransferred(address,address)": EventFragment;
"ScalarUpdated(uint256)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "DecimalsUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "GasPriceUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "L1BaseFeeUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OverheadUpdated"): EventFragment;
getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment;
getEvent(nameOrSignatureOrTopic: "ScalarUpdated"): EventFragment;
}
export interface DecimalsUpdatedEventObject {
arg0: BigNumber;
}
export type DecimalsUpdatedEvent = TypedEvent<
[BigNumber],
DecimalsUpdatedEventObject
>;
export type DecimalsUpdatedEventFilter = TypedEventFilter<DecimalsUpdatedEvent>;
export interface GasPriceUpdatedEventObject {
arg0: BigNumber;
}
export type GasPriceUpdatedEvent = TypedEvent<
[BigNumber],
GasPriceUpdatedEventObject
>;
export type GasPriceUpdatedEventFilter = TypedEventFilter<GasPriceUpdatedEvent>;
export interface L1BaseFeeUpdatedEventObject {
arg0: BigNumber;
}
export type L1BaseFeeUpdatedEvent = TypedEvent<
[BigNumber],
L1BaseFeeUpdatedEventObject
>;
export type L1BaseFeeUpdatedEventFilter =
TypedEventFilter<L1BaseFeeUpdatedEvent>;
export interface OverheadUpdatedEventObject {
arg0: BigNumber;
}
export type OverheadUpdatedEvent = TypedEvent<
[BigNumber],
OverheadUpdatedEventObject
>;
export type OverheadUpdatedEventFilter = TypedEventFilter<OverheadUpdatedEvent>;
export interface OwnershipTransferredEventObject {
previousOwner: string;
newOwner: string;
}
export type OwnershipTransferredEvent = TypedEvent<
[string, string],
OwnershipTransferredEventObject
>;
export type OwnershipTransferredEventFilter =
TypedEventFilter<OwnershipTransferredEvent>;
export interface ScalarUpdatedEventObject {
arg0: BigNumber;
}
export type ScalarUpdatedEvent = TypedEvent<
[BigNumber],
ScalarUpdatedEventObject
>;
export type ScalarUpdatedEventFilter = TypedEventFilter<ScalarUpdatedEvent>;
export interface OvmGasPriceOracleAbi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: OvmGasPriceOracleAbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
decimals(overrides?: CallOverrides): Promise<[BigNumber]>;
gasPrice(overrides?: CallOverrides): Promise<[BigNumber]>;
getL1Fee(_data: BytesLike, overrides?: CallOverrides): Promise<[BigNumber]>;
getL1GasUsed(
_data: BytesLike,
overrides?: CallOverrides
): Promise<[BigNumber]>;
l1BaseFee(overrides?: CallOverrides): Promise<[BigNumber]>;
overhead(overrides?: CallOverrides): Promise<[BigNumber]>;
owner(overrides?: CallOverrides): Promise<[string]>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
scalar(overrides?: CallOverrides): Promise<[BigNumber]>;
setDecimals(
_decimals: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setGasPrice(
_gasPrice: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setL1BaseFee(
_baseFee: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setOverhead(
_overhead: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setScalar(
_scalar: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
};
decimals(overrides?: CallOverrides): Promise<BigNumber>;
gasPrice(overrides?: CallOverrides): Promise<BigNumber>;
getL1Fee(_data: BytesLike, overrides?: CallOverrides): Promise<BigNumber>;
getL1GasUsed(_data: BytesLike, overrides?: CallOverrides): Promise<BigNumber>;
l1BaseFee(overrides?: CallOverrides): Promise<BigNumber>;
overhead(overrides?: CallOverrides): Promise<BigNumber>;
owner(overrides?: CallOverrides): Promise<string>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
scalar(overrides?: CallOverrides): Promise<BigNumber>;
setDecimals(
_decimals: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setGasPrice(
_gasPrice: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setL1BaseFee(
_baseFee: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setOverhead(
_overhead: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
setScalar(
_scalar: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
callStatic: {
decimals(overrides?: CallOverrides): Promise<BigNumber>;
gasPrice(overrides?: CallOverrides): Promise<BigNumber>;
getL1Fee(_data: BytesLike, overrides?: CallOverrides): Promise<BigNumber>;
getL1GasUsed(
_data: BytesLike,
overrides?: CallOverrides
): Promise<BigNumber>;
l1BaseFee(overrides?: CallOverrides): Promise<BigNumber>;
overhead(overrides?: CallOverrides): Promise<BigNumber>;
owner(overrides?: CallOverrides): Promise<string>;
renounceOwnership(overrides?: CallOverrides): Promise<void>;
scalar(overrides?: CallOverrides): Promise<BigNumber>;
setDecimals(
_decimals: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
setGasPrice(
_gasPrice: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
setL1BaseFee(
_baseFee: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
setOverhead(
_overhead: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
setScalar(_scalar: BigNumberish, overrides?: CallOverrides): Promise<void>;
transferOwnership(
newOwner: string,
overrides?: CallOverrides
): Promise<void>;
};
filters: {
"DecimalsUpdated(uint256)"(arg0?: null): DecimalsUpdatedEventFilter;
DecimalsUpdated(arg0?: null): DecimalsUpdatedEventFilter;
"GasPriceUpdated(uint256)"(arg0?: null): GasPriceUpdatedEventFilter;
GasPriceUpdated(arg0?: null): GasPriceUpdatedEventFilter;
"L1BaseFeeUpdated(uint256)"(arg0?: null): L1BaseFeeUpdatedEventFilter;
L1BaseFeeUpdated(arg0?: null): L1BaseFeeUpdatedEventFilter;
"OverheadUpdated(uint256)"(arg0?: null): OverheadUpdatedEventFilter;
OverheadUpdated(arg0?: null): OverheadUpdatedEventFilter;
"OwnershipTransferred(address,address)"(
previousOwner?: string | null,
newOwner?: string | null
): OwnershipTransferredEventFilter;
OwnershipTransferred(
previousOwner?: string | null,
newOwner?: string | null
): OwnershipTransferredEventFilter;
"ScalarUpdated(uint256)"(arg0?: null): ScalarUpdatedEventFilter;
ScalarUpdated(arg0?: null): ScalarUpdatedEventFilter;
};
estimateGas: {
decimals(overrides?: CallOverrides): Promise<BigNumber>;
gasPrice(overrides?: CallOverrides): Promise<BigNumber>;
getL1Fee(_data: BytesLike, overrides?: CallOverrides): Promise<BigNumber>;
getL1GasUsed(
_data: BytesLike,
overrides?: CallOverrides
): Promise<BigNumber>;
l1BaseFee(overrides?: CallOverrides): Promise<BigNumber>;
overhead(overrides?: CallOverrides): Promise<BigNumber>;
owner(overrides?: CallOverrides): Promise<BigNumber>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
scalar(overrides?: CallOverrides): Promise<BigNumber>;
setDecimals(
_decimals: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
setGasPrice(
_gasPrice: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
setL1BaseFee(
_baseFee: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
setOverhead(
_overhead: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
setScalar(
_scalar: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
};
populateTransaction: {
decimals(overrides?: CallOverrides): Promise<PopulatedTransaction>;
gasPrice(overrides?: CallOverrides): Promise<PopulatedTransaction>;
getL1Fee(
_data: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
getL1GasUsed(
_data: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
l1BaseFee(overrides?: CallOverrides): Promise<PopulatedTransaction>;
overhead(overrides?: CallOverrides): Promise<PopulatedTransaction>;
owner(overrides?: CallOverrides): Promise<PopulatedTransaction>;
renounceOwnership(
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
scalar(overrides?: CallOverrides): Promise<PopulatedTransaction>;
setDecimals(
_decimals: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
setGasPrice(
_gasPrice: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
setL1BaseFee(
_baseFee: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
setOverhead(
_overhead: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
setScalar(
_scalar: BigNumberish,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
transferOwnership(
newOwner: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
};
}

View File

@@ -0,0 +1,252 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PayableOverrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type {
FunctionFragment,
Result,
EventFragment,
} from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export interface ProxyLightAbiInterface extends utils.Interface {
functions: {
"backupNotes(bytes[])": FunctionFragment;
"deposit(address,bytes32,bytes)": FunctionFragment;
"withdraw(address,bytes,bytes32,bytes32,address,address,uint256,uint256)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic: "backupNotes" | "deposit" | "withdraw"
): FunctionFragment;
encodeFunctionData(
functionFragment: "backupNotes",
values: [BytesLike[]]
): string;
encodeFunctionData(
functionFragment: "deposit",
values: [string, BytesLike, BytesLike]
): string;
encodeFunctionData(
functionFragment: "withdraw",
values: [
string,
BytesLike,
BytesLike,
BytesLike,
string,
string,
BigNumberish,
BigNumberish
]
): string;
decodeFunctionResult(
functionFragment: "backupNotes",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "deposit", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "withdraw", data: BytesLike): Result;
events: {
"EncryptedNote(address,bytes)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "EncryptedNote"): EventFragment;
}
export interface EncryptedNoteEventObject {
sender: string;
encryptedNote: string;
}
export type EncryptedNoteEvent = TypedEvent<
[string, string],
EncryptedNoteEventObject
>;
export type EncryptedNoteEventFilter = TypedEventFilter<EncryptedNoteEvent>;
export interface ProxyLightAbi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: ProxyLightAbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
backupNotes(
_encryptedNotes: BytesLike[],
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
deposit(
_tornado: string,
_commitment: BytesLike,
_encryptedNote: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
withdraw(
_tornado: string,
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
};
backupNotes(
_encryptedNotes: BytesLike[],
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
deposit(
_tornado: string,
_commitment: BytesLike,
_encryptedNote: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
withdraw(
_tornado: string,
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
callStatic: {
backupNotes(
_encryptedNotes: BytesLike[],
overrides?: CallOverrides
): Promise<void>;
deposit(
_tornado: string,
_commitment: BytesLike,
_encryptedNote: BytesLike,
overrides?: CallOverrides
): Promise<void>;
withdraw(
_tornado: string,
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
};
filters: {
"EncryptedNote(address,bytes)"(
sender?: string | null,
encryptedNote?: null
): EncryptedNoteEventFilter;
EncryptedNote(
sender?: string | null,
encryptedNote?: null
): EncryptedNoteEventFilter;
};
estimateGas: {
backupNotes(
_encryptedNotes: BytesLike[],
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
deposit(
_tornado: string,
_commitment: BytesLike,
_encryptedNote: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<BigNumber>;
withdraw(
_tornado: string,
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<BigNumber>;
};
populateTransaction: {
backupNotes(
_encryptedNotes: BytesLike[],
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
deposit(
_tornado: string,
_commitment: BytesLike,
_encryptedNote: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<PopulatedTransaction>;
withdraw(
_tornado: string,
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<PopulatedTransaction>;
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,691 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PayableOverrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type {
FunctionFragment,
Result,
EventFragment,
} from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export interface TornadoInstanceAbiInterface extends utils.Interface {
functions: {
"changeOperator(address)": FunctionFragment;
"nullifierHashes(bytes32)": FunctionFragment;
"withdraw(bytes,bytes32,bytes32,address,address,uint256,uint256)": FunctionFragment;
"verifier()": FunctionFragment;
"hashLeftRight(bytes32,bytes32)": FunctionFragment;
"FIELD_SIZE()": FunctionFragment;
"levels()": FunctionFragment;
"operator()": FunctionFragment;
"isKnownRoot(bytes32)": FunctionFragment;
"commitments(bytes32)": FunctionFragment;
"denomination()": FunctionFragment;
"currentRootIndex()": FunctionFragment;
"updateVerifier(address)": FunctionFragment;
"deposit(bytes32)": FunctionFragment;
"getLastRoot()": FunctionFragment;
"roots(uint256)": FunctionFragment;
"ROOT_HISTORY_SIZE()": FunctionFragment;
"isSpent(bytes32)": FunctionFragment;
"zeros(uint256)": FunctionFragment;
"ZERO_VALUE()": FunctionFragment;
"filledSubtrees(uint256)": FunctionFragment;
"nextIndex()": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "changeOperator"
| "nullifierHashes"
| "withdraw"
| "verifier"
| "hashLeftRight"
| "FIELD_SIZE"
| "levels"
| "operator"
| "isKnownRoot"
| "commitments"
| "denomination"
| "currentRootIndex"
| "updateVerifier"
| "deposit"
| "getLastRoot"
| "roots"
| "ROOT_HISTORY_SIZE"
| "isSpent"
| "zeros"
| "ZERO_VALUE"
| "filledSubtrees"
| "nextIndex"
): FunctionFragment;
encodeFunctionData(
functionFragment: "changeOperator",
values: [string]
): string;
encodeFunctionData(
functionFragment: "nullifierHashes",
values: [BytesLike]
): string;
encodeFunctionData(
functionFragment: "withdraw",
values: [
BytesLike,
BytesLike,
BytesLike,
string,
string,
BigNumberish,
BigNumberish
]
): string;
encodeFunctionData(functionFragment: "verifier", values?: undefined): string;
encodeFunctionData(
functionFragment: "hashLeftRight",
values: [BytesLike, BytesLike]
): string;
encodeFunctionData(
functionFragment: "FIELD_SIZE",
values?: undefined
): string;
encodeFunctionData(functionFragment: "levels", values?: undefined): string;
encodeFunctionData(functionFragment: "operator", values?: undefined): string;
encodeFunctionData(
functionFragment: "isKnownRoot",
values: [BytesLike]
): string;
encodeFunctionData(
functionFragment: "commitments",
values: [BytesLike]
): string;
encodeFunctionData(
functionFragment: "denomination",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "currentRootIndex",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "updateVerifier",
values: [string]
): string;
encodeFunctionData(functionFragment: "deposit", values: [BytesLike]): string;
encodeFunctionData(
functionFragment: "getLastRoot",
values?: undefined
): string;
encodeFunctionData(functionFragment: "roots", values: [BigNumberish]): string;
encodeFunctionData(
functionFragment: "ROOT_HISTORY_SIZE",
values?: undefined
): string;
encodeFunctionData(functionFragment: "isSpent", values: [BytesLike]): string;
encodeFunctionData(functionFragment: "zeros", values: [BigNumberish]): string;
encodeFunctionData(
functionFragment: "ZERO_VALUE",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "filledSubtrees",
values: [BigNumberish]
): string;
encodeFunctionData(functionFragment: "nextIndex", values?: undefined): string;
decodeFunctionResult(
functionFragment: "changeOperator",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "nullifierHashes",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "withdraw", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "verifier", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "hashLeftRight",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "FIELD_SIZE", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "levels", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "operator", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "isKnownRoot",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "commitments",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "denomination",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "currentRootIndex",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "updateVerifier",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "deposit", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getLastRoot",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "roots", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "ROOT_HISTORY_SIZE",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "isSpent", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "zeros", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "ZERO_VALUE", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "filledSubtrees",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "nextIndex", data: BytesLike): Result;
events: {
"Deposit(bytes32,uint32,uint256)": EventFragment;
"Withdrawal(address,bytes32,address,uint256)": EventFragment;
};
getEvent(nameOrSignatureOrTopic: "Deposit"): EventFragment;
getEvent(nameOrSignatureOrTopic: "Withdrawal"): EventFragment;
}
export interface DepositEventObject {
commitment: string;
leafIndex: number;
timestamp: BigNumber;
}
export type DepositEvent = TypedEvent<
[string, number, BigNumber],
DepositEventObject
>;
export type DepositEventFilter = TypedEventFilter<DepositEvent>;
export interface WithdrawalEventObject {
to: string;
nullifierHash: string;
relayer: string;
fee: BigNumber;
}
export type WithdrawalEvent = TypedEvent<
[string, string, string, BigNumber],
WithdrawalEventObject
>;
export type WithdrawalEventFilter = TypedEventFilter<WithdrawalEvent>;
export interface TornadoInstanceAbi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: TornadoInstanceAbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
changeOperator(
_newOperator: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
nullifierHashes(
arg0: BytesLike,
overrides?: CallOverrides
): Promise<[boolean]>;
withdraw(
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
verifier(overrides?: CallOverrides): Promise<[string]>;
hashLeftRight(
_left: BytesLike,
_right: BytesLike,
overrides?: CallOverrides
): Promise<[string]>;
FIELD_SIZE(overrides?: CallOverrides): Promise<[BigNumber]>;
levels(overrides?: CallOverrides): Promise<[number]>;
operator(overrides?: CallOverrides): Promise<[string]>;
isKnownRoot(
_root: BytesLike,
overrides?: CallOverrides
): Promise<[boolean]>;
commitments(arg0: BytesLike, overrides?: CallOverrides): Promise<[boolean]>;
denomination(overrides?: CallOverrides): Promise<[BigNumber]>;
currentRootIndex(overrides?: CallOverrides): Promise<[number]>;
updateVerifier(
_newVerifier: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
deposit(
_commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
getLastRoot(overrides?: CallOverrides): Promise<[string]>;
roots(arg0: BigNumberish, overrides?: CallOverrides): Promise<[string]>;
ROOT_HISTORY_SIZE(overrides?: CallOverrides): Promise<[number]>;
isSpent(
_nullifierHash: BytesLike,
overrides?: CallOverrides
): Promise<[boolean]>;
zeros(arg0: BigNumberish, overrides?: CallOverrides): Promise<[string]>;
ZERO_VALUE(overrides?: CallOverrides): Promise<[BigNumber]>;
filledSubtrees(
arg0: BigNumberish,
overrides?: CallOverrides
): Promise<[string]>;
nextIndex(overrides?: CallOverrides): Promise<[number]>;
};
changeOperator(
_newOperator: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
nullifierHashes(arg0: BytesLike, overrides?: CallOverrides): Promise<boolean>;
withdraw(
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
verifier(overrides?: CallOverrides): Promise<string>;
hashLeftRight(
_left: BytesLike,
_right: BytesLike,
overrides?: CallOverrides
): Promise<string>;
FIELD_SIZE(overrides?: CallOverrides): Promise<BigNumber>;
levels(overrides?: CallOverrides): Promise<number>;
operator(overrides?: CallOverrides): Promise<string>;
isKnownRoot(_root: BytesLike, overrides?: CallOverrides): Promise<boolean>;
commitments(arg0: BytesLike, overrides?: CallOverrides): Promise<boolean>;
denomination(overrides?: CallOverrides): Promise<BigNumber>;
currentRootIndex(overrides?: CallOverrides): Promise<number>;
updateVerifier(
_newVerifier: string,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
deposit(
_commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
getLastRoot(overrides?: CallOverrides): Promise<string>;
roots(arg0: BigNumberish, overrides?: CallOverrides): Promise<string>;
ROOT_HISTORY_SIZE(overrides?: CallOverrides): Promise<number>;
isSpent(
_nullifierHash: BytesLike,
overrides?: CallOverrides
): Promise<boolean>;
zeros(arg0: BigNumberish, overrides?: CallOverrides): Promise<string>;
ZERO_VALUE(overrides?: CallOverrides): Promise<BigNumber>;
filledSubtrees(
arg0: BigNumberish,
overrides?: CallOverrides
): Promise<string>;
nextIndex(overrides?: CallOverrides): Promise<number>;
callStatic: {
changeOperator(
_newOperator: string,
overrides?: CallOverrides
): Promise<void>;
nullifierHashes(
arg0: BytesLike,
overrides?: CallOverrides
): Promise<boolean>;
withdraw(
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
verifier(overrides?: CallOverrides): Promise<string>;
hashLeftRight(
_left: BytesLike,
_right: BytesLike,
overrides?: CallOverrides
): Promise<string>;
FIELD_SIZE(overrides?: CallOverrides): Promise<BigNumber>;
levels(overrides?: CallOverrides): Promise<number>;
operator(overrides?: CallOverrides): Promise<string>;
isKnownRoot(_root: BytesLike, overrides?: CallOverrides): Promise<boolean>;
commitments(arg0: BytesLike, overrides?: CallOverrides): Promise<boolean>;
denomination(overrides?: CallOverrides): Promise<BigNumber>;
currentRootIndex(overrides?: CallOverrides): Promise<number>;
updateVerifier(
_newVerifier: string,
overrides?: CallOverrides
): Promise<void>;
deposit(_commitment: BytesLike, overrides?: CallOverrides): Promise<void>;
getLastRoot(overrides?: CallOverrides): Promise<string>;
roots(arg0: BigNumberish, overrides?: CallOverrides): Promise<string>;
ROOT_HISTORY_SIZE(overrides?: CallOverrides): Promise<number>;
isSpent(
_nullifierHash: BytesLike,
overrides?: CallOverrides
): Promise<boolean>;
zeros(arg0: BigNumberish, overrides?: CallOverrides): Promise<string>;
ZERO_VALUE(overrides?: CallOverrides): Promise<BigNumber>;
filledSubtrees(
arg0: BigNumberish,
overrides?: CallOverrides
): Promise<string>;
nextIndex(overrides?: CallOverrides): Promise<number>;
};
filters: {
"Deposit(bytes32,uint32,uint256)"(
commitment?: BytesLike | null,
leafIndex?: null,
timestamp?: null
): DepositEventFilter;
Deposit(
commitment?: BytesLike | null,
leafIndex?: null,
timestamp?: null
): DepositEventFilter;
"Withdrawal(address,bytes32,address,uint256)"(
to?: null,
nullifierHash?: null,
relayer?: string | null,
fee?: null
): WithdrawalEventFilter;
Withdrawal(
to?: null,
nullifierHash?: null,
relayer?: string | null,
fee?: null
): WithdrawalEventFilter;
};
estimateGas: {
changeOperator(
_newOperator: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
nullifierHashes(
arg0: BytesLike,
overrides?: CallOverrides
): Promise<BigNumber>;
withdraw(
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<BigNumber>;
verifier(overrides?: CallOverrides): Promise<BigNumber>;
hashLeftRight(
_left: BytesLike,
_right: BytesLike,
overrides?: CallOverrides
): Promise<BigNumber>;
FIELD_SIZE(overrides?: CallOverrides): Promise<BigNumber>;
levels(overrides?: CallOverrides): Promise<BigNumber>;
operator(overrides?: CallOverrides): Promise<BigNumber>;
isKnownRoot(
_root: BytesLike,
overrides?: CallOverrides
): Promise<BigNumber>;
commitments(arg0: BytesLike, overrides?: CallOverrides): Promise<BigNumber>;
denomination(overrides?: CallOverrides): Promise<BigNumber>;
currentRootIndex(overrides?: CallOverrides): Promise<BigNumber>;
updateVerifier(
_newVerifier: string,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
deposit(
_commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<BigNumber>;
getLastRoot(overrides?: CallOverrides): Promise<BigNumber>;
roots(arg0: BigNumberish, overrides?: CallOverrides): Promise<BigNumber>;
ROOT_HISTORY_SIZE(overrides?: CallOverrides): Promise<BigNumber>;
isSpent(
_nullifierHash: BytesLike,
overrides?: CallOverrides
): Promise<BigNumber>;
zeros(arg0: BigNumberish, overrides?: CallOverrides): Promise<BigNumber>;
ZERO_VALUE(overrides?: CallOverrides): Promise<BigNumber>;
filledSubtrees(
arg0: BigNumberish,
overrides?: CallOverrides
): Promise<BigNumber>;
nextIndex(overrides?: CallOverrides): Promise<BigNumber>;
};
populateTransaction: {
changeOperator(
_newOperator: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
nullifierHashes(
arg0: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
withdraw(
_proof: BytesLike,
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: string,
_relayer: string,
_fee: BigNumberish,
_refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<PopulatedTransaction>;
verifier(overrides?: CallOverrides): Promise<PopulatedTransaction>;
hashLeftRight(
_left: BytesLike,
_right: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
FIELD_SIZE(overrides?: CallOverrides): Promise<PopulatedTransaction>;
levels(overrides?: CallOverrides): Promise<PopulatedTransaction>;
operator(overrides?: CallOverrides): Promise<PopulatedTransaction>;
isKnownRoot(
_root: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
commitments(
arg0: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
denomination(overrides?: CallOverrides): Promise<PopulatedTransaction>;
currentRootIndex(overrides?: CallOverrides): Promise<PopulatedTransaction>;
updateVerifier(
_newVerifier: string,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
deposit(
_commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<PopulatedTransaction>;
getLastRoot(overrides?: CallOverrides): Promise<PopulatedTransaction>;
roots(
arg0: BigNumberish,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
ROOT_HISTORY_SIZE(overrides?: CallOverrides): Promise<PopulatedTransaction>;
isSpent(
_nullifierHash: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
zeros(
arg0: BigNumberish,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
ZERO_VALUE(overrides?: CallOverrides): Promise<PopulatedTransaction>;
filledSubtrees(
arg0: BigNumberish,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
nextIndex(overrides?: CallOverrides): Promise<PopulatedTransaction>;
};
}

View File

@@ -0,0 +1,298 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumber,
BigNumberish,
BytesLike,
CallOverrides,
ContractTransaction,
Overrides,
PayableOverrides,
PopulatedTransaction,
Signer,
utils,
} from "ethers";
import type { FunctionFragment, Result } from "@ethersproject/abi";
import type { Listener, Provider } from "@ethersproject/providers";
import type {
TypedEventFilter,
TypedEvent,
TypedListener,
OnEvent,
} from "./common";
export interface TornadoProxyAbiInterface extends utils.Interface {
functions: {
"governance()": FunctionFragment;
"instances(address)": FunctionFragment;
"resolve(bytes32)": FunctionFragment;
"tornadoTrees()": FunctionFragment;
"deposit(address,bytes32)": FunctionFragment;
"updateInstances(address,bool)": FunctionFragment;
"withdraw(address,bytes,bytes32,bytes32,address,address,uint256,uint256)": FunctionFragment;
};
getFunction(
nameOrSignatureOrTopic:
| "governance"
| "instances"
| "resolve"
| "tornadoTrees"
| "deposit"
| "updateInstances"
| "withdraw"
): FunctionFragment;
encodeFunctionData(
functionFragment: "governance",
values?: undefined
): string;
encodeFunctionData(functionFragment: "instances", values: [string]): string;
encodeFunctionData(functionFragment: "resolve", values: [BytesLike]): string;
encodeFunctionData(
functionFragment: "tornadoTrees",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "deposit",
values: [string, BytesLike]
): string;
encodeFunctionData(
functionFragment: "updateInstances",
values: [string, boolean]
): string;
encodeFunctionData(
functionFragment: "withdraw",
values: [
string,
BytesLike,
BytesLike,
BytesLike,
string,
string,
BigNumberish,
BigNumberish
]
): string;
decodeFunctionResult(functionFragment: "governance", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "instances", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "resolve", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "tornadoTrees",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "deposit", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "updateInstances",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "withdraw", data: BytesLike): Result;
events: {};
}
export interface TornadoProxyAbi extends BaseContract {
connect(signerOrProvider: Signer | Provider | string): this;
attach(addressOrName: string): this;
deployed(): Promise<this>;
interface: TornadoProxyAbiInterface;
queryFilter<TEvent extends TypedEvent>(
event: TypedEventFilter<TEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TEvent>>;
listeners<TEvent extends TypedEvent>(
eventFilter?: TypedEventFilter<TEvent>
): Array<TypedListener<TEvent>>;
listeners(eventName?: string): Array<Listener>;
removeAllListeners<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>
): this;
removeAllListeners(eventName?: string): this;
off: OnEvent<this>;
on: OnEvent<this>;
once: OnEvent<this>;
removeListener: OnEvent<this>;
functions: {
governance(overrides?: CallOverrides): Promise<[string]>;
instances(arg0: string, overrides?: CallOverrides): Promise<[boolean]>;
resolve(node: BytesLike, overrides?: CallOverrides): Promise<[string]>;
tornadoTrees(overrides?: CallOverrides): Promise<[string]>;
deposit(
tornado: string,
commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
updateInstances(
instance: string,
update: boolean,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
withdraw(
tornado: string,
proof: BytesLike,
root: BytesLike,
nullifierHash: BytesLike,
recipient: string,
relayer: string,
fee: BigNumberish,
refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
};
governance(overrides?: CallOverrides): Promise<string>;
instances(arg0: string, overrides?: CallOverrides): Promise<boolean>;
resolve(node: BytesLike, overrides?: CallOverrides): Promise<string>;
tornadoTrees(overrides?: CallOverrides): Promise<string>;
deposit(
tornado: string,
commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
updateInstances(
instance: string,
update: boolean,
overrides?: Overrides & { from?: string }
): Promise<ContractTransaction>;
withdraw(
tornado: string,
proof: BytesLike,
root: BytesLike,
nullifierHash: BytesLike,
recipient: string,
relayer: string,
fee: BigNumberish,
refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<ContractTransaction>;
callStatic: {
governance(overrides?: CallOverrides): Promise<string>;
instances(arg0: string, overrides?: CallOverrides): Promise<boolean>;
resolve(node: BytesLike, overrides?: CallOverrides): Promise<string>;
tornadoTrees(overrides?: CallOverrides): Promise<string>;
deposit(
tornado: string,
commitment: BytesLike,
overrides?: CallOverrides
): Promise<void>;
updateInstances(
instance: string,
update: boolean,
overrides?: CallOverrides
): Promise<void>;
withdraw(
tornado: string,
proof: BytesLike,
root: BytesLike,
nullifierHash: BytesLike,
recipient: string,
relayer: string,
fee: BigNumberish,
refund: BigNumberish,
overrides?: CallOverrides
): Promise<void>;
};
filters: {};
estimateGas: {
governance(overrides?: CallOverrides): Promise<BigNumber>;
instances(arg0: string, overrides?: CallOverrides): Promise<BigNumber>;
resolve(node: BytesLike, overrides?: CallOverrides): Promise<BigNumber>;
tornadoTrees(overrides?: CallOverrides): Promise<BigNumber>;
deposit(
tornado: string,
commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<BigNumber>;
updateInstances(
instance: string,
update: boolean,
overrides?: Overrides & { from?: string }
): Promise<BigNumber>;
withdraw(
tornado: string,
proof: BytesLike,
root: BytesLike,
nullifierHash: BytesLike,
recipient: string,
relayer: string,
fee: BigNumberish,
refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<BigNumber>;
};
populateTransaction: {
governance(overrides?: CallOverrides): Promise<PopulatedTransaction>;
instances(
arg0: string,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
resolve(
node: BytesLike,
overrides?: CallOverrides
): Promise<PopulatedTransaction>;
tornadoTrees(overrides?: CallOverrides): Promise<PopulatedTransaction>;
deposit(
tornado: string,
commitment: BytesLike,
overrides?: PayableOverrides & { from?: string }
): Promise<PopulatedTransaction>;
updateInstances(
instance: string,
update: boolean,
overrides?: Overrides & { from?: string }
): Promise<PopulatedTransaction>;
withdraw(
tornado: string,
proof: BytesLike,
root: BytesLike,
nullifierHash: BytesLike,
recipient: string,
relayer: string,
fee: BigNumberish,
refund: BigNumberish,
overrides?: PayableOverrides & { from?: string }
): Promise<PopulatedTransaction>;
};
}

44
src/contracts/common.ts Normal file
View File

@@ -0,0 +1,44 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type { Listener } from "@ethersproject/providers";
import type { Event, EventFilter } from "ethers";
export interface TypedEvent<
TArgsArray extends Array<any> = any,
TArgsObject = any
> extends Event {
args: TArgsArray & TArgsObject;
}
export interface TypedEventFilter<_TEvent extends TypedEvent>
extends EventFilter {}
export interface TypedListener<TEvent extends TypedEvent> {
(...listenerArg: [...__TypechainArgsArray<TEvent>, TEvent]): void;
}
type __TypechainArgsArray<T> = T extends TypedEvent<infer U> ? U : never;
export interface OnEvent<TRes> {
<TEvent extends TypedEvent>(
eventFilter: TypedEventFilter<TEvent>,
listener: TypedListener<TEvent>
): TRes;
(eventName: string, listener: Listener): TRes;
}
export type MinEthersFactory<C, ARGS> = {
deploy(...a: ARGS[]): Promise<C>;
};
export type GetContractTypeFromFactory<F> = F extends MinEthersFactory<
infer C,
any
>
? C
: never;
export type GetARGsTypeFromFactory<F> = F extends MinEthersFactory<any, any>
? Parameters<F["deploy"]>
: never;

View File

@@ -0,0 +1,355 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type { AggregatorAbi, AggregatorAbiInterface } from "../AggregatorAbi";
const _abi = [
{
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",
},
] as const;
export class AggregatorAbi__factory {
static readonly abi = _abi;
static createInterface(): AggregatorAbiInterface {
return new utils.Interface(_abi) as AggregatorAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): AggregatorAbi {
return new Contract(address, _abi, signerOrProvider) as AggregatorAbi;
}
}

View File

@@ -0,0 +1,277 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type { ERC20Abi, ERC20AbiInterface } from "../ERC20Abi";
const _abi = [
{
constant: true,
inputs: [],
name: "totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: true,
inputs: [],
name: "_totalSupply",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "who",
type: "address",
},
],
name: "balanceOf",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "to",
type: "address",
},
{
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "transfer",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "owner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "spender",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Approval",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "from",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "to",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "Transfer",
type: "event",
},
{
constant: true,
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
{
internalType: "address",
name: "spender",
type: "address",
},
],
name: "allowance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "from",
type: "address",
},
{
internalType: "address",
name: "to",
type: "address",
},
{
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "transferFrom",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "address",
name: "spender",
type: "address",
},
{
internalType: "uint256",
name: "value",
type: "uint256",
},
],
name: "approve",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
],
name: "nonces",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "owner",
type: "address",
},
{
internalType: "address",
name: "spender",
type: "address",
},
{
internalType: "uint256",
name: "amount",
type: "uint256",
},
{
internalType: "uint256",
name: "deadline",
type: "uint256",
},
{
internalType: "uint8",
name: "v",
type: "uint8",
},
{
internalType: "bytes32",
name: "r",
type: "bytes32",
},
{
internalType: "bytes32",
name: "s",
type: "bytes32",
},
],
name: "permit",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as const;
export class ERC20Abi__factory {
static readonly abi = _abi;
static createInterface(): ERC20AbiInterface {
return new utils.Interface(_abi) as ERC20AbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): ERC20Abi {
return new Contract(address, _abi, signerOrProvider) as ERC20Abi;
}
}

View File

@@ -0,0 +1,59 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type { MulticallAbi, MulticallAbiInterface } from "../MulticallAbi";
const _abi = [
{
inputs: [
{
components: [
{
internalType: "address",
name: "to",
type: "address",
},
{
internalType: "bytes",
name: "data",
type: "bytes",
},
],
internalType: "struct MultiCall.Call[]",
name: "calls",
type: "tuple[]",
},
],
name: "multicall",
outputs: [
{
internalType: "bytes[]",
name: "results",
type: "bytes[]",
},
{
internalType: "bool[]",
name: "success",
type: "bool[]",
},
],
stateMutability: "view",
type: "function",
},
] as const;
export class MulticallAbi__factory {
static readonly abi = _abi;
static createInterface(): MulticallAbiInterface {
return new utils.Interface(_abi) as MulticallAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): MulticallAbi {
return new Contract(address, _abi, signerOrProvider) as MulticallAbi;
}
}

View File

@@ -0,0 +1,358 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type {
OffchainOracleAbi,
OffchainOracleAbiInterface,
} from "../OffchainOracleAbi";
const _abi = [
{
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",
},
] as const;
export class OffchainOracleAbi__factory {
static readonly abi = _abi;
static createInterface(): OffchainOracleAbiInterface {
return new utils.Interface(_abi) as OffchainOracleAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): OffchainOracleAbi {
return new Contract(address, _abi, signerOrProvider) as OffchainOracleAbi;
}
}

View File

@@ -0,0 +1,326 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type {
OvmGasPriceOracleAbi,
OvmGasPriceOracleAbiInterface,
} from "../OvmGasPriceOracleAbi";
const _abi = [
{
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",
},
] as const;
export class OvmGasPriceOracleAbi__factory {
static readonly abi = _abi;
static createInterface(): OvmGasPriceOracleAbiInterface {
return new utils.Interface(_abi) as OvmGasPriceOracleAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): OvmGasPriceOracleAbi {
return new Contract(
address,
_abi,
signerOrProvider
) as OvmGasPriceOracleAbi;
}
}

View File

@@ -0,0 +1,126 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type { ProxyLightAbi, ProxyLightAbiInterface } from "../ProxyLightAbi";
const _abi = [
{
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",
},
] as const;
export class ProxyLightAbi__factory {
static readonly abi = _abi;
static createInterface(): ProxyLightAbiInterface {
return new utils.Interface(_abi) as ProxyLightAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): ProxyLightAbi {
return new Contract(address, _abi, signerOrProvider) as ProxyLightAbi;
}
}

View File

@@ -0,0 +1,694 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type {
RelayerRegistryAbi,
RelayerRegistryAbiInterface,
} from "../RelayerRegistryAbi";
const _abi = [
{
inputs: [
{
internalType: "address",
name: "_torn",
type: "address",
},
{
internalType: "address",
name: "_governance",
type: "address",
},
{
internalType: "address",
name: "_ens",
type: "address",
},
{
internalType: "address",
name: "_staking",
type: "address",
},
{
internalType: "address",
name: "_feeManager",
type: "address",
},
],
stateMutability: "nonpayable",
type: "constructor",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "minStakeAmount",
type: "uint256",
},
],
name: "MinimumStakeAmount",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "relayer",
type: "address",
},
],
name: "RelayerBalanceNullified",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "bytes32",
name: "relayer",
type: "bytes32",
},
{
indexed: false,
internalType: "string",
name: "ensName",
type: "string",
},
{
indexed: false,
internalType: "address",
name: "relayerAddress",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "stakedAmount",
type: "uint256",
},
],
name: "RelayerRegistered",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "tornadoRouter",
type: "address",
},
],
name: "RouterRegistered",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "relayer",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "amountStakeAdded",
type: "uint256",
},
],
name: "StakeAddedToRelayer",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "relayer",
type: "address",
},
{
indexed: false,
internalType: "uint256",
name: "amountBurned",
type: "uint256",
},
],
name: "StakeBurned",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "relayer",
type: "address",
},
{
indexed: false,
internalType: "address",
name: "worker",
type: "address",
},
],
name: "WorkerRegistered",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "address",
name: "relayer",
type: "address",
},
{
indexed: false,
internalType: "address",
name: "worker",
type: "address",
},
],
name: "WorkerUnregistered",
type: "event",
},
{
inputs: [
{
internalType: "bytes32[]",
name: "domains",
type: "bytes32[]",
},
],
name: "bulkResolve",
outputs: [
{
internalType: "address[]",
name: "result",
type: "address[]",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "sender",
type: "address",
},
{
internalType: "address",
name: "relayer",
type: "address",
},
{
internalType: "contract ITornadoInstance",
name: "pool",
type: "address",
},
],
name: "burn",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "ens",
outputs: [
{
internalType: "contract IENS",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "feeManager",
outputs: [
{
internalType: "contract IFeeManager",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "relayer",
type: "address",
},
],
name: "getRelayerBalance",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "relayer",
type: "address",
},
],
name: "getRelayerEnsHash",
outputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "governance",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "bytes32",
name: "_tornadoRouter",
type: "bytes32",
},
],
name: "initialize",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "toResolve",
type: "address",
},
],
name: "isRelayer",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "relayer",
type: "address",
},
{
internalType: "address",
name: "toResolve",
type: "address",
},
],
name: "isRelayerRegistered",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "minStakeAmount",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "relayer",
type: "address",
},
],
name: "nullifyBalance",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "string",
name: "ensName",
type: "string",
},
{
internalType: "uint256",
name: "stake",
type: "uint256",
},
{
internalType: "address[]",
name: "workersToRegister",
type: "address[]",
},
],
name: "register",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "string",
name: "ensName",
type: "string",
},
{
internalType: "uint256",
name: "stake",
type: "uint256",
},
{
internalType: "address[]",
name: "workersToRegister",
type: "address[]",
},
{
internalType: "address",
name: "relayer",
type: "address",
},
{
internalType: "uint256",
name: "deadline",
type: "uint256",
},
{
internalType: "uint8",
name: "v",
type: "uint8",
},
{
internalType: "bytes32",
name: "r",
type: "bytes32",
},
{
internalType: "bytes32",
name: "s",
type: "bytes32",
},
],
name: "registerPermit",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "relayer",
type: "address",
},
{
internalType: "address",
name: "worker",
type: "address",
},
],
name: "registerWorker",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
name: "relayers",
outputs: [
{
internalType: "uint256",
name: "balance",
type: "uint256",
},
{
internalType: "bytes32",
name: "ensHash",
type: "bytes32",
},
],
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: "uint256",
name: "minAmount",
type: "uint256",
},
],
name: "setMinStakeAmount",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "tornadoRouterAddress",
type: "address",
},
],
name: "setTornadoRouter",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "relayer",
type: "address",
},
{
internalType: "uint256",
name: "stake",
type: "uint256",
},
],
name: "stakeToRelayer",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "relayer",
type: "address",
},
{
internalType: "uint256",
name: "stake",
type: "uint256",
},
{
internalType: "address",
name: "staker",
type: "address",
},
{
internalType: "uint256",
name: "deadline",
type: "uint256",
},
{
internalType: "uint8",
name: "v",
type: "uint8",
},
{
internalType: "bytes32",
name: "r",
type: "bytes32",
},
{
internalType: "bytes32",
name: "s",
type: "bytes32",
},
],
name: "stakeToRelayerPermit",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "staking",
outputs: [
{
internalType: "contract TornadoStakingRewards",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "torn",
outputs: [
{
internalType: "contract TORN",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "tornadoRouter",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "worker",
type: "address",
},
],
name: "unregisterWorker",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
name: "workers",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
] as const;
export class RelayerRegistryAbi__factory {
static readonly abi = _abi;
static createInterface(): RelayerRegistryAbiInterface {
return new utils.Interface(_abi) as RelayerRegistryAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): RelayerRegistryAbi {
return new Contract(address, _abi, signerOrProvider) as RelayerRegistryAbi;
}
}

View File

@@ -0,0 +1,522 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type {
TornadoInstanceAbi,
TornadoInstanceAbiInterface,
} from "../TornadoInstanceAbi";
const _abi = [
{
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",
},
] as const;
export class TornadoInstanceAbi__factory {
static readonly abi = _abi;
static createInterface(): TornadoInstanceAbiInterface {
return new utils.Interface(_abi) as TornadoInstanceAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): TornadoInstanceAbi {
return new Contract(address, _abi, signerOrProvider) as TornadoInstanceAbi;
}
}

View File

@@ -0,0 +1,195 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from "ethers";
import type { Provider } from "@ethersproject/providers";
import type {
TornadoProxyAbi,
TornadoProxyAbiInterface,
} from "../TornadoProxyAbi";
const _abi = [
{
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",
},
] as const;
export class TornadoProxyAbi__factory {
static readonly abi = _abi;
static createInterface(): TornadoProxyAbiInterface {
return new utils.Interface(_abi) as TornadoProxyAbiInterface;
}
static connect(
address: string,
signerOrProvider: Signer | Provider
): TornadoProxyAbi {
return new Contract(address, _abi, signerOrProvider) as TornadoProxyAbi;
}
}

View File

@@ -0,0 +1,12 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export { AggregatorAbi__factory } from "./AggregatorAbi__factory";
export { ERC20Abi__factory } from "./ERC20Abi__factory";
export { MulticallAbi__factory } from "./MulticallAbi__factory";
export { OffchainOracleAbi__factory } from "./OffchainOracleAbi__factory";
export { OvmGasPriceOracleAbi__factory } from "./OvmGasPriceOracleAbi__factory";
export { ProxyLightAbi__factory } from "./ProxyLightAbi__factory";
export { RelayerRegistryAbi__factory } from "./RelayerRegistryAbi__factory";
export { TornadoInstanceAbi__factory } from "./TornadoInstanceAbi__factory";
export { TornadoProxyAbi__factory } from "./TornadoProxyAbi__factory";

22
src/contracts/index.ts Normal file
View File

@@ -0,0 +1,22 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export type { AggregatorAbi } from "./AggregatorAbi";
export type { ERC20Abi } from "./ERC20Abi";
export type { MulticallAbi } from "./MulticallAbi";
export type { OffchainOracleAbi } from "./OffchainOracleAbi";
export type { OvmGasPriceOracleAbi } from "./OvmGasPriceOracleAbi";
export type { ProxyLightAbi } from "./ProxyLightAbi";
export type { RelayerRegistryAbi } from "./RelayerRegistryAbi";
export type { TornadoInstanceAbi } from "./TornadoInstanceAbi";
export type { TornadoProxyAbi } from "./TornadoProxyAbi";
export * as factories from "./factories";
export { AggregatorAbi__factory } from "./factories/AggregatorAbi__factory";
export { ERC20Abi__factory } from "./factories/ERC20Abi__factory";
export { MulticallAbi__factory } from "./factories/MulticallAbi__factory";
export { OffchainOracleAbi__factory } from "./factories/OffchainOracleAbi__factory";
export { OvmGasPriceOracleAbi__factory } from "./factories/OvmGasPriceOracleAbi__factory";
export { ProxyLightAbi__factory } from "./factories/ProxyLightAbi__factory";
export { RelayerRegistryAbi__factory } from "./factories/RelayerRegistryAbi__factory";
export { TornadoInstanceAbi__factory } from "./factories/TornadoInstanceAbi__factory";
export { TornadoProxyAbi__factory } from "./factories/TornadoProxyAbi__factory";

View File

@@ -1,21 +0,0 @@
const { setSafeInterval, toBN, fromWei, RelayerError } = require('./utils')
const { privateKey, minimumBalance } = require('./config')
const { redis } = require('./modules/redis')
const web3 = require('./modules/web3')()
async function main() {
try {
const { address } = web3.eth.accounts.privateKeyToAccount(privateKey)
const balance = await web3.eth.getBalance(address)
if (toBN(balance).lt(toBN(minimumBalance))) {
throw new RelayerError(`Not enough balance, less than ${fromWei(minimumBalance)} ETH`, 1)
}
await redis.hset('health', { status: true, error: '' })
} catch (e) {
console.error('healthWatcher', e.message)
await redis.hset('health', { status: false, error: e.message })
}
}
setSafeInterval(main, 30 * 1000)

4
src/healthWorker.ts Normal file
View File

@@ -0,0 +1,4 @@
import { healthWorker, priceWorker } from './queue/worker';
priceWorker();
healthWorker();

52
src/modules/contracts.ts Normal file
View File

@@ -0,0 +1,52 @@
import {
ERC20Abi__factory,
MulticallAbi__factory,
OffchainOracleAbi__factory,
OvmGasPriceOracleAbi__factory,
ProxyLightAbi__factory,
RelayerRegistryAbi__factory,
TornadoProxyAbi__factory,
} from '../contracts';
import { providers } from 'ethers';
import {
mainnetRpcUrl,
multiCallAddress,
netId,
offchainOracleAddress,
oracleRpcUrl,
ovmGasPriceOracleContract,
rpcUrl,
} from '../config';
export function getProvider(isStatic = true, customRpcUrl?: string, chainId = netId) {
const url = customRpcUrl || rpcUrl;
if (isStatic) return new providers.StaticJsonRpcProvider(url, chainId);
else return new providers.JsonRpcProvider(url, chainId);
}
export const getTornadoProxyContract = (proxyAddress: string) => {
return TornadoProxyAbi__factory.connect(proxyAddress, getProvider());
};
export const getTornadoProxyLightContract = (proxyAddress: string) => {
return ProxyLightAbi__factory.connect(proxyAddress, getProvider());
};
export const getOffchainOracleContract = () => {
return OffchainOracleAbi__factory.connect(offchainOracleAddress, getProvider(true, oracleRpcUrl));
};
export const getMultiCallContract = () => {
return MulticallAbi__factory.connect(multiCallAddress, getProvider(true, mainnetRpcUrl));
};
export const getTornTokenContract = (tokenAddress: string) => {
return ERC20Abi__factory.connect(tokenAddress, getProvider(true, mainnetRpcUrl));
};
export const getOvmGasPriceOracle = () => {
return OvmGasPriceOracleAbi__factory.connect(ovmGasPriceOracleContract, getProvider());
};
export const getRelayerRegistryContract = (relayerRegistryAddress: string) => {
return RelayerRegistryAbi__factory.connect(relayerRegistryAddress, getProvider(true, mainnetRpcUrl));
};

View File

@@ -0,0 +1,21 @@
import { getProvider } from './contracts';
import { mainnetRpcUrl } from '../config';
import { ChainIds } from '../types';
const addresses = new Map<string, string>();
async function resolve(domain: string) {
try {
const provider = getProvider(true, mainnetRpcUrl, ChainIds.ethereum);
if (!addresses.has(domain)) {
const resolved = await provider.resolveName(domain);
addresses.set(domain, resolved);
}
return addresses.get(domain);
} catch (e) {
console.log(e);
return null;
}
}
export { resolve };

2
src/modules/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export { default as redisStore } from './redis';
export { resolve } from './ensResolver';

View File

@@ -1,11 +0,0 @@
const { createClient } = require('ioredis')
const { redisUrl } = require('../config')
const redis = createClient(redisUrl)
const redisSubscribe = createClient(redisUrl)
module.exports = {
redis,
redisSubscribe,
redisUrl,
}

35
src/modules/redis.ts Normal file
View File

@@ -0,0 +1,35 @@
import IORedis, { Redis } from 'ioredis';
import { redisUrl } from '../config';
import { container, singleton } from 'tsyringe';
const getNewInstance: () => Redis = () => new IORedis(redisUrl, { maxRetriesPerRequest: null });
@singleton()
export class RedisStore {
get publisher(): Redis {
if (!this._publisher) {
this._publisher = getNewInstance();
}
return this._publisher;
}
get client() {
if (!this._client) {
this._client = getNewInstance();
}
return this._client;
}
get subscriber() {
if (!this._subscriber) {
this._subscriber = getNewInstance();
}
return this._subscriber;
}
private _subscriber: Redis;
private _publisher: Redis;
private _client: Redis;
}
export default () => container.resolve(RedisStore);

View File

@@ -1,29 +0,0 @@
const { aggregatorAddress } = require('../config')
const web3 = require('./web3')()
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 = new ENSResolver()

18
src/modules/utils.ts Normal file
View File

@@ -0,0 +1,18 @@
import { BigNumber, BigNumberish } from 'ethers';
export const parseJSON = (str: string) => {
let parsed = null;
try {
parsed = JSON.parse(str);
if (typeof parsed === 'string') parsed = parseJSON(parsed);
return parsed;
} catch (e) {
return parsed;
}
};
export const bump = (value: BigNumberish, percent: number | BigNumber): BigNumber => {
const hundredPercents = BigNumber.from(100);
return BigNumber.from(value).mul(hundredPercents.add(percent)).div(hundredPercents);
};

View File

@@ -1,200 +0,0 @@
const { isAddress, toChecksumAddress } = require('web3-utils')
const { getInstance } = require('../utils')
const { rewardAccount } = require('../config')
const Ajv = require('ajv')
const ajv = new Ajv({ format: 'fast' })
ajv.addKeyword('isAddress', {
validate: (schema, data) => {
try {
return isAddress(data)
} catch (e) {
return false
}
},
errors: true,
})
ajv.addKeyword('isKnownContract', {
validate: (schema, data) => {
try {
return !!getInstance(data)
} catch (e) {
return false
}
},
errors: true,
})
ajv.addKeyword('isFeeRecipient', {
validate: (schema, data) => {
try {
return toChecksumAddress(rewardAccount) === toChecksumAddress(data)
} catch (e) {
return false
}
},
errors: true,
})
const addressType = { type: 'string', pattern: '^0x[a-fA-F0-9]{40}$', isAddress: true }
const proofType = { type: 'string', pattern: '^0x[a-fA-F0-9]{512}$' }
const encryptedAccountType = { type: 'string', pattern: '^0x[a-fA-F0-9]{392}$' }
const bytes32Type = { type: 'string', pattern: '^0x[a-fA-F0-9]{64}$' }
const instanceType = { ...addressType, isKnownContract: true }
const relayerType = { ...addressType, isFeeRecipient: true }
const tornadoWithdrawSchema = {
type: 'object',
properties: {
proof: proofType,
contract: instanceType,
args: {
type: 'array',
maxItems: 6,
minItems: 6,
items: [bytes32Type, bytes32Type, addressType, relayerType, bytes32Type, bytes32Type],
},
},
additionalProperties: false,
required: ['proof', 'contract', 'args'],
}
const miningRewardSchema = {
type: 'object',
properties: {
proof: proofType,
args: {
type: 'object',
properties: {
rate: bytes32Type,
fee: bytes32Type,
instance: instanceType,
rewardNullifier: bytes32Type,
extDataHash: bytes32Type,
depositRoot: bytes32Type,
withdrawalRoot: bytes32Type,
extData: {
type: 'object',
properties: {
relayer: relayerType,
encryptedAccount: encryptedAccountType,
},
additionalProperties: false,
required: ['relayer', 'encryptedAccount'],
},
account: {
type: 'object',
properties: {
inputRoot: bytes32Type,
inputNullifierHash: bytes32Type,
outputRoot: bytes32Type,
outputPathIndices: bytes32Type,
outputCommitment: bytes32Type,
},
additionalProperties: false,
required: [
'inputRoot',
'inputNullifierHash',
'outputRoot',
'outputPathIndices',
'outputCommitment',
],
},
},
additionalProperties: false,
required: [
'rate',
'fee',
'instance',
'rewardNullifier',
'extDataHash',
'depositRoot',
'withdrawalRoot',
'extData',
'account',
],
},
},
additionalProperties: false,
required: ['proof', 'args'],
}
const miningWithdrawSchema = {
type: 'object',
properties: {
proof: proofType,
args: {
type: 'object',
properties: {
amount: bytes32Type,
extDataHash: bytes32Type,
extData: {
type: 'object',
properties: {
fee: bytes32Type,
recipient: addressType,
relayer: relayerType,
encryptedAccount: encryptedAccountType,
},
additionalProperties: false,
required: ['fee', 'relayer', 'encryptedAccount', 'recipient'],
},
account: {
type: 'object',
properties: {
inputRoot: bytes32Type,
inputNullifierHash: bytes32Type,
outputRoot: bytes32Type,
outputPathIndices: bytes32Type,
outputCommitment: bytes32Type,
},
additionalProperties: false,
required: [
'inputRoot',
'inputNullifierHash',
'outputRoot',
'outputPathIndices',
'outputCommitment',
],
},
},
additionalProperties: false,
required: ['amount', 'extDataHash', 'extData', 'account'],
},
},
additionalProperties: false,
required: ['proof', 'args'],
}
const validateTornadoWithdraw = ajv.compile(tornadoWithdrawSchema)
const validateMiningReward = ajv.compile(miningRewardSchema)
const validateMiningWithdraw = ajv.compile(miningWithdrawSchema)
function getInputError(validator, data) {
validator(data)
if (validator.errors) {
const error = validator.errors[0]
return `${error.dataPath} ${error.message}`
}
return null
}
function getTornadoWithdrawInputError(data) {
return getInputError(validateTornadoWithdraw, data)
}
function getMiningRewardInputError(data) {
return getInputError(validateMiningReward, data)
}
function getMiningWithdrawInputError(data) {
return getInputError(validateMiningWithdraw, data)
}
module.exports = {
getTornadoWithdrawInputError,
getMiningRewardInputError,
getMiningWithdrawInputError,
}

View File

@@ -1,30 +0,0 @@
const Web3 = require('web3')
const { oracleRpcUrl, httpRpcUrl, wsRpcUrl } = require('../config')
const getWeb3 = (type = 'http') => {
let url
switch (type) {
case 'oracle':
url = oracleRpcUrl
break
case 'ws':
url = wsRpcUrl
return new Web3(
new Web3.providers.WebsocketProvider(wsRpcUrl, {
clientConfig: {
maxReceivedFrameSize: 100000000,
maxReceivedMessageSize: 100000000,
},
}),
)
case 'http':
default:
url = httpRpcUrl
break
}
return new Web3(
new Web3.providers.HttpProvider(url, {
timeout: 200000, // ms
}),
)
}
module.exports = getWeb3

View File

@@ -1,23 +0,0 @@
const { setSafeInterval, RelayerError, logRelayerError } = require('./utils')
const { redis } = require('./modules/redis')
const { TokenPriceOracle } = require('@tornado/tornado-oracles')
const { oracleRpcUrl } = require('./config')
const priceOracle = new TokenPriceOracle(oracleRpcUrl)
async function main() {
try {
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) {
await logRelayerError(redis, e)
console.error('priceWatcher error', e)
}
}
setSafeInterval(main, 30 * 1000)

View File

@@ -1,57 +0,0 @@
const { v4: uuid } = require('uuid')
const Queue = require('bull')
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.
lockRenewTime: 30000, // Interval on which to acquire the job lock
stalledInterval: 30000, // How often check for stalled jobs (use 0 for never checking).
maxStalledCount: 3, // Max amount of times a stalled job will be re-processed.
guardInterval: 5000, // Poll interval for delayed jobs and added jobs.
retryProcessDelay: 5000, // delay before processing next job in case of internal error.
drainDelay: 5, // A timeout for when the queue is in drained state (empty waiting for jobs).
})
async function postJob({ type, request }) {
const id = uuid()
const job = await queue.add(
{
id,
type,
status: status.QUEUED,
...request, // proof, args, ?contract
},
{
//removeOnComplete: true
},
)
await redis.set(`job:${id}`, job.id)
return id
}
async function getJob(uuid) {
const id = await redis.get(`job:${uuid}`)
return queue.getJobFromId(id)
}
async function getJobStatus(uuid) {
const job = await getJob(uuid)
if (!job) {
return null
}
return {
...job.data,
failedReason: job.failedReason,
}
}
module.exports = {
postJob,
getJob,
getJobStatus,
queue,
}

View File

@@ -0,0 +1,24 @@
import { getHealthService } from '../services';
import { HealthProcessor } from './index';
export const healthProcessor: HealthProcessor = async (job) => {
const healthService = getHealthService();
try {
switch (job.name) {
case 'checkHealth': {
const status = await healthService.check();
await healthService.clearErrorCodes();
await healthService.setStatus({ status: true, error: '' });
return status;
}
case 'checkUpdate': {
const status = await healthService.checkUpdate();
return status;
}
}
} catch (e) {
await healthService.saveError(e);
await healthService.setStatus({ status: false, error: e.message });
return { error: e.message };
}
};

182
src/queue/index.ts Normal file
View File

@@ -0,0 +1,182 @@
import { Processor, Queue, QueueScheduler, Worker } from 'bullmq';
import { TransactionReceipt } from '@ethersproject/abstract-provider';
import { JobStatus, RelayerJobType, Token } from '../types';
import { WithdrawalData } from '../services/tx.service';
import { priceProcessor } from './price.processor';
import { autoInjectable } from 'tsyringe';
import { RedisStore } from '../modules/redis';
import { ConfigService } from '../services/config.service';
import { relayerProcessor } from './relayer.processor';
import { healthProcessor } from './health.processor';
import { txJobAttempts } from '../config';
type PriceJobData = Token[];
type PriceJobReturn = number;
type HealthStatusInfo = { level: 'OK' | 'CRITICAL' | 'WARN'; time: number; type: string; message: string };
type HealthJobReturn =
| { tornStatus: HealthStatusInfo; mainStatus: HealthStatusInfo }
| { isUpToDate: boolean; lastVersion: string }
| { error: string };
type HealthJobData = null;
type HealthJobType = 'checkHealth' | 'checkUpdate';
export type HealthProcessor = Processor<HealthJobData, HealthJobReturn, HealthJobType>;
export type RelayerJobData = WithdrawalData & {
id: string;
status: JobStatus;
type: RelayerJobType;
txHash?: string;
confirmations?: number;
};
export type RelayerJobReturn = TransactionReceipt;
export type RelayerProcessor = Processor<RelayerJobData, RelayerJobReturn, RelayerJobType>;
export type PriceProcessor = Processor<PriceJobData, PriceJobReturn, 'updatePrice'>;
@autoInjectable()
export class PriceQueueHelper {
_queue: Queue<PriceJobData, PriceJobReturn, 'updatePrice'>;
_worker: Worker<PriceJobData, PriceJobReturn, 'updatePrice'>;
_scheduler: QueueScheduler;
interval = 30000;
constructor(private store?: RedisStore) {}
get queue() {
if (!this._queue) {
this._queue = new Queue<Token[], PriceJobReturn, 'updatePrice'>('price', {
connection: this.store.client,
defaultJobOptions: {
removeOnFail: 10,
removeOnComplete: 10,
},
});
}
return this._queue;
}
get worker() {
if (!this._worker) {
this._worker = new Worker<PriceJobData, PriceJobReturn, 'updatePrice'>('price', priceProcessor, {
connection: this.store.client,
concurrency: 1,
});
}
return this._worker;
}
get scheduler() {
if (!this._scheduler) {
this._scheduler = new QueueScheduler('price', {
connection: this.store.client,
});
}
return this._scheduler;
}
async addRepeatable(tokens: PriceJobData) {
await this.queue.add('updatePrice', tokens, {
repeat: {
every: this.interval,
immediately: true,
},
});
}
}
@autoInjectable()
export class RelayerQueueHelper {
private _queue: Queue<RelayerJobData, RelayerJobReturn, RelayerJobType>;
private _worker: Worker<RelayerJobData, RelayerJobReturn, RelayerJobType>;
private _scheduler: QueueScheduler;
constructor(private store?: RedisStore, private config?: ConfigService) {}
get queue() {
if (!this._queue) {
this._queue = new Queue<RelayerJobData, RelayerJobReturn, RelayerJobType>(this.config.queueName, {
connection: this.store.client,
defaultJobOptions: {
stackTraceLimit: 100,
attempts: txJobAttempts,
backoff: 1000,
},
});
}
return this._queue;
}
get worker() {
if (!this._worker) {
this._worker = new Worker<RelayerJobData, RelayerJobReturn, RelayerJobType>(this.config.queueName, relayerProcessor, {
connection: this.store.client,
concurrency: 1,
});
}
return this._worker;
}
get scheduler() {
if (!this._scheduler) {
this._scheduler = new QueueScheduler(this.config.queueName, {
connection: this.store.client,
});
}
return this._scheduler;
}
}
@autoInjectable()
export class HealthQueueHelper {
private _queue: Queue<HealthJobData, HealthJobReturn, HealthJobType>;
private _worker: Worker<HealthJobData, HealthJobReturn, HealthJobType>;
private _scheduler: QueueScheduler;
interval = 30000;
constructor(private store?: RedisStore) {}
get scheduler(): QueueScheduler {
if (!this._scheduler) {
this._scheduler = new QueueScheduler('health', {
connection: this.store.client,
});
}
return this._scheduler;
}
get worker() {
if (!this._worker) {
this._worker = new Worker<HealthJobData, HealthJobReturn, HealthJobType>('health', healthProcessor, {
connection: this.store.client,
concurrency: 1,
});
}
return this._worker;
}
get queue() {
if (!this._queue) {
this._queue = new Queue<HealthJobData, HealthJobReturn, HealthJobType>('health', {
connection: this.store.client,
defaultJobOptions: { stackTraceLimit: 100 },
});
}
return this._queue;
}
async addRepeatable() {
await this.queue.add('checkHealth', null, {
repeat: {
every: this.interval,
immediately: true,
},
});
await this.queue.add('checkUpdate', null, {
repeat: {
every: this.interval * 2 * 60, // once per hour
immediately: true,
},
});
}
}

View File

@@ -0,0 +1,9 @@
import { getPriceService } from '../services';
import { PriceProcessor } from './index';
export const priceProcessor: PriceProcessor = async (job) => {
const priceService = getPriceService();
const result = await priceService.fetchPrices(job.data);
if (result) return await priceService.savePrices(result);
return null;
};

View File

@@ -0,0 +1,37 @@
import { RelayerProcessor } from './index';
import { getTxService } from '../services';
import { JobStatus } from '../types';
import { UnrecoverableError } from 'bullmq';
import { ExecutionError } from '../services/tx.service';
import { txJobAttempts } from '../config';
class RevertError extends UnrecoverableError {
code: string;
constructor(message: string, code: string) {
super(message);
this.name = this.constructor.name;
this.code = code;
Object.setPrototypeOf(this, new.target.prototype);
}
}
export const relayerProcessor: RelayerProcessor = async (job) => {
try {
await job.update({ ...job.data, status: JobStatus.ACCEPTED });
console.log(`Start processing a new ${job.data.type} job ${job.id}`);
const txService = getTxService();
txService.currentJob = job;
const withdrawalData = job.data;
const txData = await txService.prepareTxData(withdrawalData);
await txService.checkTornadoFee(withdrawalData, txData);
return await txService.sendTx(txData);
} catch (e) {
if ((e instanceof ExecutionError && e.code === 'REVERTED') || job.attemptsMade === txJobAttempts) {
await job.update({ ...job.data, status: JobStatus.FAILED });
throw new RevertError(e.message, e.code);
}
await job.update({ ...job.data, status: JobStatus.RESUBMITTED });
throw e;
}
};

49
src/queue/worker.ts Normal file
View File

@@ -0,0 +1,49 @@
import 'reflect-metadata';
import { HealthQueueHelper, PriceQueueHelper, RelayerQueueHelper } from './';
import { configService, getHealthService } from '../services';
export const priceWorker = async () => {
await configService.init();
if (configService.isLightMode) return;
const healthService = getHealthService();
const price = new PriceQueueHelper();
console.log(price.queue.name, 'worker started');
price.scheduler.on('stalled', (jobId, prev) => console.log({ jobId, prev }));
price.worker.on('active', () => console.log('worker active'));
price.worker.on('completed', async (job, result) => {
console.log(`Job ${job.name} completed with result: `, result);
});
price.worker.on('failed', (job, error) => healthService.saveError(error));
};
export const relayerWorker = async () => {
await configService.init();
const relayer = new RelayerQueueHelper();
const healthService = getHealthService();
console.log(relayer.queue.name, 'worker started');
relayer.worker.on('completed', (job, result) => {
console.log(`Job ${job.id} completed with result: `, result);
});
relayer.worker.on('failed', (job, error) => {
healthService.saveError(error, job.id);
console.log(`Failed job ${job.id}: `, error);
});
relayer.scheduler.on('stalled', (jobId, prev) => console.log({ jobId, prev }));
};
export const healthWorker = async () => {
await configService.init();
const health = new HealthQueueHelper();
console.log(health.queue.name, 'worker started');
health.scheduler.on('stalled', (jobId, prev) => console.log({ jobId, prev }));
health.worker.on('completed', (job, result) => {
console.log(`Job ${job.name} completed with result: `, result);
});
health.worker.on('failed', (job, error) => {
console.log(`Failed job ${job.id}: `, error);
});
};

View File

@@ -1,30 +0,0 @@
const { controller, status } = require('./contollers')
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)
router.post('/v1/miningReward', controller.miningReward)
router.post('/v1/miningWithdraw', controller.miningWithdraw)
module.exports = router

View File

@@ -1,14 +0,0 @@
const express = require('express')
const { port, rewardAccount } = require('./config')
const { version } = require('../package.json')
const { isAddress } = require('./utils')
const router = require('./router')
if (!isAddress(rewardAccount)) {
throw new Error('No REWARD_ACCOUNT specified')
}
const app = express()
app.use(express.json())
app.use(router)
app.listen(port)
console.log(`Relayer ${version} started on port ${port}`)

View File

@@ -0,0 +1,192 @@
import {
host,
instances,
mainnetRpcUrl,
minimumBalance,
minimumTornBalance,
netId,
networkConfig,
privateKey,
relayerVersion,
rpcUrl,
torn,
tornadoGoerliProxy,
tornToken,
} from '../config';
import { ChainIds, Token } from '../types';
import {
getProvider,
getRelayerRegistryContract,
getTornadoProxyContract,
getTornadoProxyLightContract,
getTornTokenContract,
} from '../modules/contracts';
import { resolve } from '../modules';
import { ERC20Abi, ProxyLightAbi, RelayerRegistryAbi, TornadoProxyAbi } from '../contracts';
import { availableIds, netIds, NetInstances } from 'torn-token';
import { getAddress } from 'ethers/lib/utils';
import { BigNumber, providers, Wallet } from 'ethers';
import { container, singleton } from 'tsyringe';
import { FallbackGasPrices } from '@tornado/gas-price-oracle';
import { RedisStore } from '../modules/redis';
type relayerQueueName = `relayer_${availableIds}`;
@singleton()
export class ConfigService {
netIdKey: netIds;
queueName: relayerQueueName;
tokens: Token[];
private _proxyAddress: string;
private _proxyContract: TornadoProxyAbi | ProxyLightAbi;
addressMap = new Map<string, InstanceProps>();
isLightMode: boolean;
instances: NetInstances;
provider: providers.JsonRpcProvider;
mainnentProvider: providers.JsonRpcProvider;
wallet: Wallet;
public readonly netId: availableIds = netId;
public readonly privateKey = privateKey;
public readonly rpcUrl = rpcUrl;
isInit: boolean;
nativeCurrency: string;
fallbackGasPrices: FallbackGasPrices;
private _tokenAddress: string;
private _tokenContract: ERC20Abi;
private _relayerRegistryAddress: string;
private _relayerRegistryContract: RelayerRegistryAbi;
balances: {
MAIN: { warn: string; critical: string };
TORN: { warn: string; critical: string };
};
host: string;
version: string;
constructor(private store: RedisStore) {
this.netIdKey = `netId${this.netId}`;
this.queueName = `relayer_${this.netId}`;
this.version = relayerVersion;
this.isLightMode = ![ChainIds.ethereum, ChainIds.goerli].includes(netId);
this.host = host;
this.instances = instances[this.netIdKey];
this.provider = getProvider(false);
this.mainnentProvider = getProvider(false, mainnetRpcUrl, 1);
this.wallet = new Wallet(this.privateKey, this.provider);
this.balances = {
MAIN: {
warn: BigNumber.from(minimumBalance).mul(150).div(100).toString(),
critical: minimumBalance,
},
TORN: {
warn: BigNumber.from(minimumTornBalance).mul(2).toString(),
critical: minimumTornBalance,
},
};
this._fillInstanceMap();
}
get proxyContract(): TornadoProxyAbi | ProxyLightAbi {
return this._proxyContract;
}
get tokenContract(): ERC20Abi {
return this._tokenContract;
}
get relayerRegistryContract(): RelayerRegistryAbi {
return this._relayerRegistryContract;
}
private _fillInstanceMap() {
if (!this.instances) throw new Error('config mismatch, check your environment variables');
for (const [currency, { instanceAddress, symbol, decimals }] of Object.entries(this.instances)) {
for (const [amount, address] of Object.entries(instanceAddress)) {
if (address)
this.addressMap.set(getAddress(address), {
currency,
amount,
symbol,
decimals,
});
}
}
}
async checkNetwork() {
console.log('Checking network...');
await this.provider.getNetwork();
if (this.isLightMode) {
await this.mainnentProvider.getNetwork();
}
}
async init() {
try {
if (this.isInit) return;
await this.checkNetwork();
console.log('Initializing...');
this._tokenAddress = await resolve(torn.torn.address);
this._tokenContract = getTornTokenContract(this._tokenAddress);
this._relayerRegistryAddress = await resolve(torn.relayerRegistry.address);
this._relayerRegistryContract = getRelayerRegistryContract(this._relayerRegistryAddress);
if (this.isLightMode) {
this._proxyAddress = torn.tornadoProxyLight.address;
this._proxyContract = getTornadoProxyLightContract(this._proxyAddress);
const { gasPrices, nativeCurrency } = networkConfig[this.netIdKey];
this.nativeCurrency = nativeCurrency;
this.fallbackGasPrices = gasPrices;
} else {
this._proxyAddress = tornadoGoerliProxy;
this.nativeCurrency = 'eth';
if (this.netId === ChainIds.ethereum) {
this._proxyAddress = await resolve(torn.tornadoRouter.address);
}
this._proxyContract = getTornadoProxyContract(this._proxyAddress);
this.tokens = [tornToken, ...Object.values(torn.instances['netId1'])]
.map<Token>(
(el) =>
el.tokenAddress && {
address: getAddress(el.tokenAddress),
decimals: el.decimals,
symbol: el.symbol,
},
)
.filter(Boolean);
}
console.log(
'Configuration completed\n',
`-- netId: ${this.netId}\n`,
`-- rpcUrl: ${this.rpcUrl}\n`,
`-- relayer Address: ${this.wallet.address}\n`,
);
this.isInit = true;
} catch (e) {
console.error(`${this.constructor.name} Error:`, e.message);
process.exit(1);
}
}
async clearRedisState() {
const queueKeys = (await this.store.client.keys('bull:*')).filter((s) => s.indexOf('relayer') === -1);
const errorKeys = await this.store.client.keys('errors:*');
const alertKeys = await this.store.client.keys('alerts:*');
const keys = [...queueKeys, ...errorKeys, ...alertKeys];
if (keys.length) await this.store.client.del(keys);
}
getInstance(address: string) {
return this.addressMap.get(getAddress(address));
}
}
type InstanceProps = {
currency: string;
amount: string;
symbol: string;
decimals: number;
};
export default container.resolve(ConfigService);

View File

@@ -0,0 +1,164 @@
import { autoInjectable, container } from 'tsyringe';
import compareVersions from 'compare-versions';
import fetch from 'node-fetch';
import { ConfigService } from './config.service';
import { RedisStore } from '../modules/redis';
import { formatEther } from 'ethers/lib/utils';
import { Levels } from './notifier.service';
class RelayerError extends Error {
constructor(message: string, code: string) {
super(message);
this.code = code;
}
code: string;
}
@autoInjectable()
export class HealthService {
constructor(private config: ConfigService, private store: RedisStore) {}
async clearErrorCodes() {
await this.store.client.del('errors:code');
}
private async _getErrors(): Promise<{
errorsLog: { message: string; score: number }[];
errorsCode: Record<string, number>;
}> {
const logSet = await this.store.client.zrevrange('errors:log', 0, -1, 'WITHSCORES');
const codeSet = await this.store.client.zrevrange('errors:code', 0, -1, 'WITHSCORES');
return {
errorsLog: HealthService._parseSet(logSet),
errorsCode: HealthService._parseSet(codeSet, 'object'),
};
}
private async _getStatus() {
const status = await this.store.client.hgetall('health:status');
if (Object.keys(status).length === 0) {
return { health: 'false', error: 'Service is not running' };
}
return status;
}
private static _parseSet(log, to = 'array', keys = ['message', 'score']) {
let out;
if (to === 'array') {
out = [];
while (log.length) {
const [a, b] = log.splice(0, 2);
out.push({ [keys[0]]: a, [keys[1]]: b });
}
} else {
out = {};
while (log.length) {
const [a, b] = log.splice(0, 2);
out[a] = Number(b);
}
}
return out;
}
async setStatus(status: { status: boolean; error: string }) {
await this.store.client.hset('health:status', status);
}
async getStatus() {
const { errorsLog, errorsCode } = await this._getErrors();
if (errorsCode['NETWORK_ERROR'] > 10) {
await this.setStatus({ status: false, error: 'Network error' });
}
const heathStatus = await this._getStatus();
return {
...heathStatus,
errorsLog,
errorsCode,
};
}
async saveError(e, jobId?: string) {
await this.store.client.zadd('errors:code', 'INCR', 1, e.code || 'RUNTIME_ERROR');
await this.store.client.zadd('errors:log', 'INCR', 1, e.message);
if (e.code === 'REVERTED' || e.code === 'SEND_ERROR') {
const jobUrl = `${this.config.host}/v1/jobs/${jobId}`;
await this.pushAlert({
message: `${e.message} \n ${jobUrl}`,
type: 'REVERTED',
level: 'WARN',
time: new Date().getTime(),
});
}
}
async pushAlert(alert: Alert) {
const channel = `${this.config.netId}/user-notify`;
await this.store.publisher.publish(channel, JSON.stringify(alert));
}
private async _checkBalance(value, currency: 'MAIN' | 'TORN') {
let level: Levels = 'OK';
const type = 'BALANCE';
const time = new Date().getTime();
if (value.lt(this.config.balances[currency].critical)) {
level = 'CRITICAL';
} else if (value.lt(this.config.balances[currency].warn)) {
level = 'WARN';
}
const msg = { WARN: 'Please refill your balance', CRITICAL: 'Insufficient balance', OK: 'ok' };
const alert = {
type: `${type}_${currency}_${level}`,
message: `${msg[level]} ${formatEther(value)} ${currency === 'MAIN' ? this.config.nativeCurrency : 'torn'}`,
level,
time,
};
await this.pushAlert(alert);
return alert;
}
async checkUpdate() {
console.log('Checking version...');
const lastVersion =
(await fetch('https://api.github.com/repos/tornadocash/tornado-relayer/releases').then((res) => res.json()))[0]?.tag_name ||
this.config.version;
const isUpToDate = compareVersions(this.config.version, lastVersion) >= 0;
if (!isUpToDate) {
await this.pushAlert({
type: 'VERSION_UPDATE_WARN',
message: `New version available: ${lastVersion}`,
level: 'WARN',
time: new Date().getTime(),
});
}
return { isUpToDate, lastVersion };
}
async check() {
await this.config.checkNetwork();
const mainBalance = await this.config.wallet.getBalance();
const tornBalance = await this.config.relayerRegistryContract.getRelayerBalance(this.config.wallet.address);
const mainStatus = await this._checkBalance(mainBalance, 'MAIN');
const tornStatus = await this._checkBalance(tornBalance, 'TORN');
if (mainStatus.level === 'CRITICAL') {
throw new RelayerError(mainStatus.message, 'INSUFFICIENT_MAIN_BALANCE');
}
if (tornStatus.level === 'CRITICAL') {
throw new RelayerError(tornStatus.message, 'INSUFFICIENT_TORN_BALANCE');
}
return { mainStatus, tornStatus };
}
}
type Alert = {
type: string;
message: string;
level: Levels;
time?: number;
};
export default () => container.resolve(HealthService);

6
src/services/index.ts Normal file
View File

@@ -0,0 +1,6 @@
export { default as configService } from './config.service';
export { default as getPriceService } from './price.service';
export { default as getJobService } from './job.service';
export { default as getTxService } from './tx.service';
export { default as getNotifierService } from './notifier.service';
export { default as getHealthService } from './health.service';

View File

@@ -0,0 +1,49 @@
import { v4 } from 'uuid';
import { JobStatus, RelayerJobType } from '../types';
import { HealthQueueHelper, PriceQueueHelper, RelayerQueueHelper } from '../queue';
import { WithdrawalData } from './tx.service';
import { container, injectable } from 'tsyringe';
import { ConfigService } from './config.service';
@injectable()
export class JobService {
constructor(
private price?: PriceQueueHelper,
private relayer?: RelayerQueueHelper,
private health?: HealthQueueHelper,
public config?: ConfigService,
) {}
async postJob(type: RelayerJobType, data: WithdrawalData) {
const id = v4();
const job = await this.relayer.queue.add(
type,
{
id,
type,
status: JobStatus.QUEUED,
...data,
},
{ jobId: id },
);
return job.id;
}
async getJob(jobId: string) {
return await this.relayer.queue.getJob(jobId);
}
async getQueueCount() {
return await this.relayer.queue.getJobCountByTypes('active', 'waiting', 'delayed');
}
async setupRepeatableJobs() {
if (!this.config.isLightMode) {
await this.price.addRepeatable(this.config.tokens);
}
await this.health.addRepeatable();
}
}
export default () => container.resolve(JobService);

View File

@@ -0,0 +1,88 @@
import { Telegram } from 'telegraf';
import { autoInjectable, container } from 'tsyringe';
import { RedisStore } from '../modules/redis';
import { ExtraReplyMessage } from 'telegraf/typings/telegram-types';
import { netId } from '../config';
import { ConfigService } from './config.service';
export type Levels = keyof typeof AlertLevel;
export enum AlertLevel {
'INFO' = '',
'WARN' = '⚠️',
'CRITICAL' = '‼️',
'ERROR' = '💩',
'OK' = '✅',
}
export enum AlertType {
'INSUFFICIENT_BALANCE',
'INSUFFICIENT_TORN_BALANCE',
'RPC',
}
class MockTelegram {
async sendMessage(chatId, text: string, extra?: ExtraReplyMessage) {
console.log(text, extra);
}
async getMe() {
return {
id: 1,
first_name: 'test',
is_bot: true,
};
}
}
@autoInjectable()
export class NotifierService {
private telegram: Telegram | MockTelegram;
private readonly token: string;
private readonly chatId: string;
host: string;
constructor(private store: RedisStore, private config: ConfigService) {
this.token = process.env.TELEGRAM_NOTIFIER_BOT_TOKEN;
this.chatId = process.env.TELEGRAM_NOTIFIER_CHAT_ID;
this.telegram = this.token ? new Telegram(this.token) : new MockTelegram();
this.host = this.config.host;
}
async processAlert(message: string) {
const alert = JSON.parse(message);
const [a, b, c] = alert.type.split('_');
const isSent = await this.store.client.sismember('alerts:sent', `${a}_${b}_${c}`);
if (!isSent) {
if (alert.level === 'OK') {
this.store.client.srem('alerts:sent', ...['WARN', 'CRITICAL'].map((c) => `${a}_${b}_${c}`));
} else {
await this.send(alert.message, alert.level);
this.store.client.sadd('alerts:sent', alert.type);
}
}
}
async subscribe() {
const channel = `${netId}/user-notify`;
this.store.subscriber.subscribe(channel);
this.store.subscriber.on('message', async (channel, message) => {
await this.processAlert(<string>message);
});
}
send(message: string, level: Levels) {
const text = `${AlertLevel[level]} ${this.host}: ${message}`;
return this.telegram.sendMessage(this.chatId, text, { parse_mode: 'HTML' });
}
sendError(e: Error) {
return this.telegram.sendMessage(this.chatId, `Error: ${e}`);
}
check() {
return this.telegram.getMe();
}
}
export default () => container.resolve(NotifierService);

View File

@@ -0,0 +1,66 @@
import { getMultiCallContract, getOffchainOracleContract } from '../modules/contracts';
import { MulticallAbi, OffchainOracleAbi } from '../contracts';
import { MultiCall } from '../contracts/MulticallAbi';
import { BigNumber } from 'ethers';
import { defaultAbiCoder } from 'ethers/lib/utils';
import { Token } from '../types';
import { container, injectable } from 'tsyringe';
import { RedisStore } from '../modules/redis';
@injectable()
export class PriceService {
private oracle: OffchainOracleAbi;
private multiCall: MulticallAbi;
constructor(private store: RedisStore) {
this.oracle = getOffchainOracleContract();
this.multiCall = getMultiCallContract();
}
prepareCallData(tokens: Token[]): MultiCall.CallStruct[] {
return tokens.map((token) => ({
to: this.oracle.address,
data: this.oracle.interface.encodeFunctionData('getRateToEth', [token.address, true]),
}));
}
async fetchPrices(tokens: Token[]) {
try {
if (!tokens?.length) return;
const names = tokens.reduce((p, c) => {
p[c.address] = c.symbol.toLowerCase();
return p;
}, {});
const callData = this.prepareCallData(tokens);
const { results, success } = await this.multiCall.multicall(callData);
const prices: Record<string, string> = {};
for (let i = 0; i < results.length; i++) {
if (!success[i]) {
continue;
}
const decodedRate = defaultAbiCoder.decode(['uint256'], results[i]).toString();
const numerator = BigNumber.from(10).pow(tokens[i].decimals);
const denominator = BigNumber.from(10).pow(18); // eth decimals
const price = BigNumber.from(decodedRate).mul(numerator).div(denominator);
prices[names[tokens[i].address]] = price.toString();
}
return prices;
} catch (e) {
console.log(e);
}
}
async getPrice(currency: string) {
return this.store.client.hget('prices', currency);
}
async getPrices() {
return this.store.client.hgetall('prices');
}
async savePrices(prices: Record<string, string>) {
return this.store.client.hset('prices', prices);
}
}
export default () => container.resolve(PriceService);

236
src/services/tx.service.ts Normal file
View File

@@ -0,0 +1,236 @@
import { TransactionData, TxManager } from 'tx-manager';
import { GasPriceOracle } from '@tornado/gas-price-oracle';
import { Provider } from '@ethersproject/providers';
import { serialize } from '@ethersproject/transactions';
import { formatEther, parseUnits } from 'ethers/lib/utils';
import { BigNumber, BigNumberish, BytesLike } from 'ethers';
import { ProxyLightAbi, TornadoProxyAbi } from '../contracts';
import { GAS_BUMP_PERCENTAGE, CONFIRMATIONS, gasLimits, MAX_GAS_PRICE, netId, tornadoServiceFee } from '../config';
import { ChainIds, JobStatus, RelayerJobType } from '../types';
import { PriceService } from './price.service';
import { Job } from 'bullmq';
import { RelayerJobData } from '../queue';
import { ConfigService } from './config.service';
import { container, injectable } from 'tsyringe';
import { parseJSON, bump } from '../modules/utils';
import { getOvmGasPriceOracle } from '../modules/contracts';
export type WithdrawalData = {
contract: string;
proof: BytesLike;
args: [BytesLike, BytesLike, string, string, BigNumberish, BigNumberish];
};
export class ExecutionError extends Error {
constructor(message: string, code?: string) {
super(message);
this.code = code;
}
code: string;
}
@injectable()
export class TxService {
set currentJob(value: Job) {
this._currentJob = value;
}
gasLimit: number;
txManager: TxManager;
tornadoProxy: TornadoProxyAbi | ProxyLightAbi;
oracle: GasPriceOracle;
provider: Provider;
private _currentJob: Job;
constructor(private config: ConfigService, private priceService: PriceService) {
const { privateKey, rpcUrl, netId } = this.config;
this.tornadoProxy = this.config.proxyContract;
this.provider = this.tornadoProxy.provider;
const gasPriceOracleConfig = {
defaultRpc: rpcUrl,
chainId: netId,
minPriority: netId === ChainIds.ethereum || ChainIds.goerli ? 2 : 0.05,
percentile: 5,
blocksCount: 20,
fallbackGasPrices: this.config?.fallbackGasPrices,
};
this.txManager = new TxManager({
privateKey,
rpcUrl,
config: { THROW_ON_REVERT: true, CONFIRMATIONS, MAX_GAS_PRICE, GAS_BUMP_PERCENTAGE },
gasPriceOracleConfig,
provider: this.provider,
});
this.oracle = new GasPriceOracle(gasPriceOracleConfig);
switch (netId) {
case ChainIds.ethereum:
case ChainIds.goerli:
this.gasLimit = gasLimits[RelayerJobType.WITHDRAW_WITH_EXTRA];
break;
case ChainIds.optimism:
this.gasLimit = gasLimits[RelayerJobType.OP_TORNADO_WITHDRAW];
break;
case ChainIds.arbitrum:
this.gasLimit = gasLimits[RelayerJobType.ARB_TORNADO_WITHDRAW];
break;
default:
this.gasLimit = gasLimits[RelayerJobType.TORNADO_WITHDRAW];
}
}
async updateJobData(data: Partial<RelayerJobData>) {
const updatedData = { ...this._currentJob.data, ...data };
console.log({ updatedData });
await this._currentJob.update(updatedData);
}
async sendTx(tx: TransactionData) {
try {
const currentTx = this.txManager.createTx(tx);
const receipt = await currentTx
.send()
.on('transactionHash', async (txHash) => {
console.log('Transaction sent, txHash: ', txHash);
await this.updateJobData({ txHash, status: JobStatus.SENT });
})
.on('mined', async (receipt) => {
console.log('Transaction mined in block', receipt.blockNumber);
await this.updateJobData({ status: JobStatus.MINED });
})
.on('confirmations', async (confirmations) => {
console.log('Transaction confirmations: ', confirmations);
await this.updateJobData({ confirmations });
});
if (receipt.status === 1) {
await this.updateJobData({ status: JobStatus.CONFIRMED });
} else {
throw new ExecutionError('Submitted transaction failed', 'REVERTED');
}
return receipt;
} catch (e) {
const regex = /body=("\{.*}}")/;
if (regex.test(e.message)) {
const { error } = parseJSON(regex.exec(e.message)[1]);
throw new ExecutionError(error.message, 'REVERTED');
} else throw new ExecutionError(e.message, 'SEND_ERROR');
}
}
async getGasPrice(): Promise<BigNumber> {
let bumpPercent: number;
switch (netId) {
case ChainIds.goerli:
bumpPercent = 50;
break;
case ChainIds.polygon:
case ChainIds.avalanche:
case ChainIds.xdai:
bumpPercent = 30;
break;
default:
bumpPercent = 10;
}
try {
const gasParams = await this.oracle.getTxGasParams({
legacySpeed: 'fast',
bumpPercent,
});
console.log(BigNumber.from(gasParams['maxFeePerGas']).toString());
return BigNumber.from(gasParams['maxFeePerGas'] || gasParams['gasPrice']);
} catch (e) {
const feeData = await this.provider.getFeeData();
return bump(feeData.maxFeePerGas, bumpPercent);
}
}
async estimateGasLimit(txData: TransactionData): Promise<BigNumber> {
try {
const fetchedGasLimit = await this.provider.estimateGas(txData);
const bumped = bump(fetchedGasLimit, 10);
console.log('Gas limit: ', bumped.toString());
return bumped;
} catch (e) {
console.log('Estimation error: ', e);
return BigNumber.from(this.gasLimit);
}
}
async prepareTxData(data: WithdrawalData): Promise<TransactionData> {
const { contract, proof, args } = data;
const calldata = this.tornadoProxy.interface.encodeFunctionData('withdraw', [contract, proof, ...args]);
const gasPrice = await this.getGasPrice();
const incompleteTxData: TransactionData = {
value: args[5],
to: this.tornadoProxy.address,
from: this.txManager.address, // Required to estimate relayerRegistry.burn for Ethereum Mainnet withdrawals
data: calldata,
gasLimit: this.gasLimit,
gasPrice: gasPrice,
};
const gasLimit = await this.estimateGasLimit(incompleteTxData);
return Object.assign(incompleteTxData, { gasLimit });
}
async getL1Fee(data: WithdrawalData, gasPrice: BigNumber) {
const { contract, proof, args } = data;
const ovmOracle = getOvmGasPriceOracle();
const calldata = this.tornadoProxy.interface.encodeFunctionData('withdraw', [contract, proof, ...args]);
const nonce = await this.config.wallet.getTransactionCount();
const tx = serialize({
nonce,
type: 0,
data: calldata,
chainId: netId,
value: data.args[5],
to: this.tornadoProxy.address,
gasLimit: this.gasLimit,
gasPrice: BigNumber.from(gasPrice),
});
return await ovmOracle.getL1Fee(tx);
}
async checkTornadoFee(data: WithdrawalData, txData: TransactionData) {
const { contract, args } = data;
const instance = this.config.getInstance(contract);
if (!instance) throw new Error('Instance not found');
const { currency, amount, decimals } = instance;
const [fee, refund] = [args[4], args[5]].map(BigNumber.from);
const gasPrice = BigNumber.from(txData.gasPrice);
let operationCost = gasPrice.mul(txData.gasLimit);
if (netId === ChainIds.optimism) {
const l1Fee = await this.getL1Fee(data, gasPrice);
operationCost = operationCost.add(l1Fee);
}
const serviceFee = parseUnits(amount, decimals)
.mul(`${tornadoServiceFee * 1e10}`)
.div(`${100 * 1e10}`);
let desiredFee = operationCost.add(serviceFee);
if (!this.config.isLightMode && currency !== 'eth') {
const ethPrice = await this.priceService.getPrice(currency);
const numerator = BigNumber.from(10).pow(decimals);
desiredFee = operationCost.add(refund).mul(numerator).div(ethPrice).add(serviceFee);
}
console.log({
sentFee: formatEther(fee),
desiredFee: formatEther(desiredFee),
serviceFee: formatEther(serviceFee),
});
if (fee.lt(desiredFee)) {
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.');
}
}
}
export default () => container.resolve(TxService);

View File

@@ -1,136 +0,0 @@
const MerkleTree = require('@tornado/fixed-merkle-tree')
const { minerMerkleTreeHeight, torn, netId } = require('./config')
const { poseidonHash2, toBN, logRelayerError } = require('./utils')
const resolver = require('./modules/resolver')
const web3 = require('./modules/web3')('ws')
const MinerABI = require('../abis/mining.abi.json')
const { redis } = require('./modules/redis')
let contract
// eslint-disable-next-line no-unused-vars
let tree, eventSubscription, blockSubscription
async function fetchEvents(fromBlock, toBlock) {
if (fromBlock <= toBlock) {
try {
return await contract.getPastEvents('NewAccount', {
fromBlock,
toBlock,
})
} catch (error) {
const midBlock = (fromBlock + toBlock) >> 1
if (midBlock - fromBlock < 2) {
throw new Error(`error fetching events: ${error.message}`)
}
const arr1 = await fetchEvents(fromBlock, midBlock)
const arr2 = await fetchEvents(midBlock + 1, toBlock)
return [...arr1, ...arr2]
}
}
return []
}
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 cachedEvents = require(`../cache/accounts_farmer_${netId}.json`)
const cachedCommitments = cachedEvents.map(e => toBN(e.commitment))
const toBlock = await web3.eth.getBlockNumber()
const [{ blockNumber: fromBlock }] = cachedEvents.slice(-1)
const newEvents = await fetchEvents(fromBlock + 1, toBlock)
const newCommitments = newEvents
.sort((a, b) => a.returnValues.index - b.returnValues.index)
.map(e => toBN(e.returnValues.commitment))
.filter((item, index, arr) => !index || item !== arr[index - 1])
const commitments = cachedCommitments.concat(newCommitments)
tree = new MerkleTree(minerMerkleTreeHeight, commitments, { hashFunction: poseidonHash2 })
await updateRedis()
console.log(`Rebuilt tree with ${commitments.length} elements, root: ${tree.root()}`)
eventSubscription = contract.events.NewAccount({ fromBlock: toBlock + 1 }, processNewEvent)
blockSubscription = web3.eth.subscribe('newBlockHeaders', processNewBlock)
} catch (e) {
await logRelayerError(redis, e)
console.error('error on init treeWatcher', e.message)
}
}
init()
process.on('unhandledRejection', error => {
console.error('Unhandled promise rejection', error)
process.exit(1)
})

3
src/txWorker.ts Normal file
View File

@@ -0,0 +1,3 @@
import { relayerWorker } from './queue/worker';
relayerWorker();

80
src/types.ts Normal file
View File

@@ -0,0 +1,80 @@
export enum RelayerJobType {
TORNADO_WITHDRAW = 'TORNADO_WITHDRAW',
WITHDRAW_WITH_EXTRA = 'WITHDRAW_WITH_EXTRA',
OP_TORNADO_WITHDRAW = 'OP_TORNADO_WITHDRAW',
ARB_TORNADO_WITHDRAW = 'ARB_TORNADO_WITHDRAW',
}
export enum JobStatus {
QUEUED = 'QUEUED',
ACCEPTED = 'ACCEPTED',
SENT = 'SENT',
MINED = 'MINED',
RESUBMITTED = 'RESUBMITTED',
CONFIRMED = 'CONFIRMED',
FAILED = 'FAILED',
}
export type Token = { address: string; decimals: number; symbol?: string };
export enum ChainIds {
'kardia' = 0,
'ethereum' = 1,
'goerli' = 5,
'ubiq' = 8,
'optimism' = 10,
'songbird' = 19,
'elastos' = 20,
'cronos' = 25,
'rsk' = 30,
'telos' = 40,
'csc' = 52,
'zyx' = 55,
'binance' = 56,
'syscoin' = 57,
'gochain' = 60,
'ethclassic' = 61,
'okexchain' = 66,
'hoo' = 70,
'meter' = 82,
'tomochain' = 88,
'xdai' = 100,
'velas' = 106,
'thundercore' = 108,
'fuse' = 122,
'heco' = 128,
'polygon' = 137,
'xdaiarb' = 200,
'energyweb' = 246,
'fantom' = 250,
'hpb' = 269,
'boba' = 288,
'kucoin' = 321,
'shiden' = 336,
'theta' = 361,
'candle' = 534,
'astar' = 592,
'callisto' = 820,
'wanchain' = 888,
'metis' = 1088,
'moonbeam' = 1284,
'moonriver' = 1285,
'ronin' = 2020,
'ezchain' = 2612,
'iotex' = 4689,
'xlc' = 5050,
'nahmii' = 5551,
'klaytn' = 8217,
'smartbch' = 10000,
'fusion' = 32659,
'arbitrum' = 42161,
'celo' = 42220,
'oasis' = 42262,
'avalanche' = 43114,
'godwoken' = 71394,
'polis' = 333999,
'aurora' = 1313161554,
'harmony' = 1666600000,
'palm' = 11297108109,
'curio' = 836542336838601,
}

View File

@@ -1,143 +0,0 @@
const { instances, netId } = require('./config')
const { poseidon } = require('@tornado/circomlib')
const { toBN, toChecksumAddress, BN, fromWei, isAddress, toWei, toHex } = require('web3-utils')
const addressMap = new Map()
const instance = instances[netId]
for (const [currency, { instanceAddress, symbol, decimals }] of Object.entries(instance)) {
Object.entries(instanceAddress).forEach(([amount, address]) =>
addressMap.set(`${netId}_${address}`, {
currency,
amount,
symbol,
decimals,
}),
)
}
const sleep = ms => new Promise(res => setTimeout(res, ms))
function getInstance(address) {
const key = `${netId}_${toChecksumAddress(address)}`
if (addressMap.has(key)) {
return addressMap.get(key)
} else {
throw new Error('Unknown contact address')
}
}
const poseidonHash = items => toBN(poseidon(items).toString())
const poseidonHash2 = (a, b) => poseidonHash([a, b])
function setSafeInterval(func, interval) {
func()
.catch(console.error)
.finally(() => {
setTimeout(() => setSafeInterval(func, interval), 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 fromDecimals(value, decimals) {
value = value.toString()
let ether = value.toString()
const base = new BN('10').pow(new BN(decimals))
const baseLength = base.toString(10).length - 1 || 1
const negative = ether.substring(0, 1) === '-'
if (negative) {
ether = ether.substring(1)
}
if (ether === '.') {
throw new Error('[ethjs-unit] while converting number ' + value + ' to wei, invalid value')
}
// Split it into a whole and fractional part
const comps = ether.split('.')
if (comps.length > 2) {
throw new Error('[ethjs-unit] while converting number ' + value + ' to wei, too many decimal points')
}
let whole = comps[0]
let fraction = comps[1]
if (!whole) {
whole = '0'
}
if (!fraction) {
fraction = '0'
}
if (fraction.length > baseLength) {
throw new Error('[ethjs-unit] while converting number ' + value + ' to wei, too many decimal places')
}
while (fraction.length < baseLength) {
fraction += '0'
}
whole = new BN(whole)
fraction = new BN(fraction)
let wei = whole.mul(base).add(fraction)
if (negative) {
wei = wei.mul(negative)
}
return new BN(wei.toString(10), 10)
}
class RelayerError extends Error {
constructor(message, score = 0) {
super(message)
this.score = score
}
}
const logRelayerError = async (redis, e) => {
await redis.zadd('errors', 'INCR', e.score || 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
}
module.exports = {
getInstance,
setSafeInterval,
poseidonHash2,
sleep,
when,
fromDecimals,
toBN,
toChecksumAddress,
fromWei,
toWei,
toHex,
BN,
isAddress,
RelayerError,
logRelayerError,
readRelayerErrors,
}

View File

@@ -1,361 +0,0 @@
const fs = require('fs')
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')
const tornadoProxyABI = require('../abis/tornadoProxyABI.json')
const { queue } = require('./queue')
const {
poseidonHash2,
getInstance,
sleep,
toBN,
fromWei,
toChecksumAddress,
isAddress,
RelayerError,
logRelayerError,
} = require('./utils')
const { jobType, status } = require('./constants')
const {
torn,
netId,
gasLimits,
privateKey,
httpRpcUrl,
oracleRpcUrl,
baseFeeReserve,
miningServiceFee,
tornadoServiceFee,
tornadoGoerliProxy,
} = require('./config')
const resolver = require('./modules/resolver')
const { TxManager } = require('@tornado/tx-manager')
const { redis, redisSubscribe } = require('./modules/redis')
const getWeb3 = require('./modules/web3')
let web3
let currentTx
let currentJob
let tree
let txManager
let controller
let swap
let minerContract
const feeOracle = new TornadoFeeOracleV5(netId, oracleRpcUrl)
async function fetchTree() {
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 {
await clearErrors()
web3 = getWeb3()
const { CONFIRMATIONS, MAX_GAS_PRICE } = process.env
txManager = new TxManager({
privateKey,
rpcUrl: httpRpcUrl,
config: {
CONFIRMATIONS,
MAX_GAS_PRICE,
THROW_ON_REVERT: false,
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)
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)
console.log('Worker started')
} catch (e) {
await logRelayerError(redis, e)
console.error('error on start worker', e.message)
}
}
function checkFee({ data }, gasInfo) {
if (data.type === jobType.TORNADO_WITHDRAW) {
return checkTornadoFee(data, gasInfo)
}
return checkMiningFee(data)
}
async function checkTornadoFee({ args, contract }, tx) {
const { currency, amount, decimals } = getInstance(contract)
const [userProvidedFee, refund] = [args[4], args[5]].map(toBN)
const { gasLimit, gasPrice } = tx
const ethPrice = await redis.hget('prices', currency)
const totalWithdrawalFee = await feeOracle.calculateWithdrawalFeeViaRelayer({
tx,
txType: 'relayer_withdrawal',
amount,
currency,
decimals,
refundInEth: refund.toString(),
predefinedGasLimit: gasLimit,
predefinedGasPrice: gasPrice,
tokenPriceInEth: ethPrice,
relayerFeePercent: tornadoServiceFee,
})
console.log(
'user-provided fee, desired fee',
fromWei(userProvidedFee.toString()),
fromWei(toBN(totalWithdrawalFee).toString()),
)
if (userProvidedFee.lt(toBN(totalWithdrawalFee))) {
throw new RelayerError(
'Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.',
0,
)
}
}
async function checkMiningFee({ args }) {
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 = 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()
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 RelayerError('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.tornadoRouter.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 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()
let calldata = contract.methods.withdraw(data.contract, data.proof, ...data.args).encodeABI()
if (isOldProxy && getInstance(data.contract).currency !== 'eth') {
contract = new web3.eth.Contract(tornadoABI, data.contract)
calldata = contract.methods.withdraw(data.proof, ...data.args).encodeABI()
}
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,
}
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()
return {
to: minerContract._address,
data: calldata,
gasLimit: gasLimits[data.type],
}
}
}
async function isOutdatedTreeRevert(receipt, currentTx) {
try {
await web3.eth.call(currentTx.tx, receipt.blockNumber)
console.log('Simulated call successful')
return false
} catch (e) {
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) {
try {
if (!jobType[job.data.type]) {
throw new RelayerError(`Unknown job type: ${job.data.type}`)
}
currentJob = job
await updateStatus(status.ACCEPTED)
console.log(`Start processing a new ${job.data.type} job #${job.id}`)
await submitTx(job)
} catch (e) {
console.error('processJob', e.message)
await updateStatus(status.FAILED)
throw new RelayerError(e.message)
}
}
async function submitTx(job, retry = 0) {
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()
}
try {
const receipt = await currentTx
.send()
.on('transactionHash', txHash => {
updateTxHash(txHash)
updateStatus(status.SENT)
})
.on('mined', receipt => {
console.log('Mined in block', receipt.blockNumber)
updateStatus(status.MINED)
})
.on('confirmations', updateConfirmations)
if (receipt.status === 1) {
await updateStatus(status.CONFIRMED)
} else {
if (job.data.type !== jobType.TORNADO_WITHDRAW && (await isOutdatedTreeRevert(receipt, currentTx))) {
if (retry < 3) {
await updateStatus(status.RESUBMITTED)
await submitTx(job, retry + 1)
} else {
throw new RelayerError('Tree update retry limit exceeded')
}
} else {
throw new RelayerError('Submitted transaction failed')
}
}
} catch (e) {
// todo this could result in duplicated error logs
// todo handle a case where account tree is still not up to date (wait and retry)?
if (
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 RelayerError('Tree update retry limit exceeded')
}
} else {
throw new RelayerError(`Revert by smart contract ${e.message}`)
}
}
}
async function updateTxHash(txHash) {
console.log(`A new successfully sent tx ${txHash}`)
currentJob.data.txHash = txHash
await currentJob.update(currentJob.data)
}
async function updateConfirmations(confirmations) {
console.log(`Confirmations count ${confirmations}`)
currentJob.data.confirmations = confirmations
await currentJob.update(currentJob.data)
}
async function updateStatus(status) {
console.log(`Job status updated ${status}`)
currentJob.data.status = status
await currentJob.update(currentJob.data)
}
async function clearErrors() {
console.log('Errors list cleared')
await redis.del('errors')
}
start()

48
test/app.test.ts Normal file
View File

@@ -0,0 +1,48 @@
import 'reflect-metadata';
import { test } from 'tap';
import createServer from '../src/app/server';
const withdrawData = {
proof:
'0x0f8cb4c2ca9cbb23a5f21475773e19e39d3470436d7296f25c8730d19d88fcef2986ec694ad094f4c5fff79a4e5043bd553df20b23108bc023ec3670718143c20cc49c6d9798e1ae831fd32a878b96ff8897728f9b7963f0d5a4b5574426ac6203b2456d360b8e825d8f5731970bf1fc1b95b9713e3b24203667ecdd5939c2e40dec48f9e51d9cc8dc2f7f3916f0e9e31519c7df2bea8c51a195eb0f57beea4924cb846deaa78cdcbe361a6c310638af6f6157317bc27d74746bfaa2e1f8d2e9088fd10fa62100740874cdffdd6feb15c95c5a303f6bc226d5e51619c5b825471a17ddfeb05b250c0802261f7d05cf29a39a72c13e200e5bc721b0e4c50d55e6',
args: [
'0x1579d41e5290ab5bcec9a7df16705e49b5c0b869095299196c19c5e14462c9e3',
'0x0cf7f49c5b35c48b9e1d43713e0b46a75977e3d10521e9ac1e4c3cd5e3da1c5d',
'0xbd4369dc854c5d5b79fe25492e3a3cfcb5d02da5',
'0x090D03d9f18e9336416A515EfC1E47dB68bC270E',
'0x000000000000000000000000000000000000000000000000058d15e176280000',
'0x0000000000000000000000000000000000000000000000000000000000000000',
],
contract: '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
};
(async () => {
await test('requests the "/v1/status" route', async (t) => {
const fastify = await createServer();
t.teardown(() => fastify.close());
await fastify.ready();
const response = await fastify.inject({
method: 'GET',
url: '/v1/status',
});
t.equal(response.statusCode, 200, 'returns a status code of 200');
t.equal(response.headers['content-type'], 'application/json; charset=utf-8');
t.same(response.json().health.status, 'false');
t.end();
});
await test('requests the "/v1/tornadoWithdraw" route', async (t) => {
const fastify = await createServer();
t.teardown(() => fastify.close());
await fastify.ready();
const response = await fastify.inject({
method: 'POST',
url: '/v1/tornadoWithdraw',
headers: { 'Content-Type': 'application/json' },
payload: withdrawData,
});
t.equal(response.statusCode, 200, 'returns a status code of 200');
t.equal(response.headers['content-type'], 'application/json; charset=utf-8');
t.end();
});
process.exit(0);
})();

View File

@@ -1,157 +0,0 @@
require('chai').should()
const {
getTornadoWithdrawInputError,
getMiningRewardInputError,
getMiningWithdrawInputError,
} = require('../src/modules/validator')
describe('Validator', () => {
describe('#getTornadoWithdrawInputError', () => {
it('should work', () => {
getTornadoWithdrawInputError(withdrawData)
})
it('should throw for incorrect proof', () => {
const malformedData = { ...withdrawData }
malformedData.proof = '0xbeef'
getTornadoWithdrawInputError(malformedData).should.be.equal(
'.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', () => {
const malformedData = { ...withdrawData }
delete malformedData.proof
getTornadoWithdrawInputError(malformedData).should.be.equal(" should have required property 'proof'")
malformedData.proof = withdrawData.proof
delete malformedData.args
getTornadoWithdrawInputError(malformedData).should.be.equal(" should have required property 'args'")
malformedData.args = withdrawData.args
delete malformedData.contract
getTornadoWithdrawInputError(malformedData).should.be.equal(" should have required property '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 = {
proof:
'0x0f8cb4c2ca9cbb23a5f21475773e19e39d3470436d7296f25c8730d19d88fcef2986ec694ad094f4c5fff79a4e5043bd553df20b23108bc023ec3670718143c20cc49c6d9798e1ae831fd32a878b96ff8897728f9b7963f0d5a4b5574426ac6203b2456d360b8e825d8f5731970bf1fc1b95b9713e3b24203667ecdd5939c2e40dec48f9e51d9cc8dc2f7f3916f0e9e31519c7df2bea8c51a195eb0f57beea4924cb846deaa78cdcbe361a6c310638af6f6157317bc27d74746bfaa2e1f8d2e9088fd10fa62100740874cdffdd6feb15c95c5a303f6bc226d5e51619c5b825471a17ddfeb05b250c0802261f7d05cf29a39a72c13e200e5bc721b0e4c50d55e6',
args: [
'0x1579d41e5290ab5bcec9a7df16705e49b5c0b869095299196c19c5e14462c9e3',
'0x0cf7f49c5b35c48b9e1d43713e0b46a75977e3d10521e9ac1e4c3cd5e3da1c5d',
'0xbd4369dc854c5d5b79fe25492e3a3cfcb5d02da5',
'0x0000000000000000000000000000000000000000',
'0x000000000000000000000000000000000000000000000000058d15e176280000',
'0x0000000000000000000000000000000000000000000000000000000000000000',
],
contract: '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
}
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
tsconfig.json Normal file
View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"lib": ["es6", "es2020"],
"target": "es2020",
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./build",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"resolveJsonModule": true
},
"include": ["src/**/*"]
}

4886
yarn.lock

File diff suppressed because it is too large Load Diff