mirror of
https://github.com/tornadocash/tornado-pool-relayer
synced 2024-02-02 15:04:09 +01:00
feat: init commit
This commit is contained in:
commit
0ce449620a
24
.eslintrc.js
Normal file
24
.eslintrc.js
Normal file
@ -0,0 +1,24 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
34
.gitignore
vendored
Normal file
34
.gitignore
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
}
|
73
README.md
Normal file
73
README.md
Normal file
@ -0,0 +1,73 @@
|
||||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo_text.svg" width="320" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
</p>
|
||||
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
|
||||
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
## Running the app
|
||||
|
||||
```bash
|
||||
# development
|
||||
$ npm run start
|
||||
|
||||
# watch mode
|
||||
$ npm run start:dev
|
||||
|
||||
# production mode
|
||||
$ npm run start:prod
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
# unit tests
|
||||
$ npm run test
|
||||
|
||||
# e2e tests
|
||||
$ npm run test:e2e
|
||||
|
||||
# test coverage
|
||||
$ npm run test:cov
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](LICENSE).
|
4
nest-cli.json
Normal file
4
nest-cli.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
}
|
88
package.json
Normal file
88
package.json
Normal file
@ -0,0 +1,88 @@
|
||||
{
|
||||
"name": "new-relayer",
|
||||
"version": "0.0.1",
|
||||
"description": "Relayer for Tornado.cash privacy solution. https://tornado.cash",
|
||||
"author": "tornado.cash",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"compile": "typechain --target ethers-v5 --out-dir ./src/artifacts './src/abi/*.json'",
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@flashbots/ethers-provider-bundle": "^0.3.2",
|
||||
"@nestjs/bull": "^0.4.0",
|
||||
"@nestjs/common": "^8.0.0",
|
||||
"@nestjs/config": "^1.0.0",
|
||||
"@nestjs/core": "^8.0.0",
|
||||
"@nestjs/platform-express": "^8.0.0",
|
||||
"ajv": "^8.6.1",
|
||||
"bull": "^3.22.11",
|
||||
"class-validator": "^0.13.1",
|
||||
"config": "^3.3.6",
|
||||
"ethers": "^5.4.1",
|
||||
"gas-price-oracle": "^0.3.3",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.2.0",
|
||||
"tx-manager": "^0.3.1",
|
||||
"uuid": "^8.3.2",
|
||||
"web3-utils": "^1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^8.0.0",
|
||||
"@nestjs/schematics": "^8.0.0",
|
||||
"@nestjs/testing": "^8.0.0",
|
||||
"@typechain/ethers-v5": "^7.0.1",
|
||||
"@types/bull": "^3.15.2",
|
||||
"@types/config": "^0.0.39",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.2",
|
||||
"@typescript-eslint/parser": "^4.28.2",
|
||||
"eslint": "^7.30.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"jest": "^27.0.6",
|
||||
"prettier": "^2.3.2",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
"ts-loader": "^9.2.3",
|
||||
"ts-node": "^10.0.0",
|
||||
"tsconfig-paths": "^3.10.1",
|
||||
"typechain": "^5.1.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
391
src/abi/TornadoPool.json
Normal file
391
src/abi/TornadoPool.json
Normal file
@ -0,0 +1,391 @@
|
||||
[
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "contract IVerifier",
|
||||
"name": "_verifier2",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "contract IVerifier",
|
||||
"name": "_verifier16",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_currentRoot",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bytes",
|
||||
"name": "account",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "EncryptedAccount",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bytes32",
|
||||
"name": "commitment",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "uint256",
|
||||
"name": "index",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bytes",
|
||||
"name": "encryptedOutput",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "NewCommitment",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bytes32",
|
||||
"name": "nullifier",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "NewNullifier",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"anonymous": false,
|
||||
"inputs": [
|
||||
{
|
||||
"indexed": true,
|
||||
"internalType": "address",
|
||||
"name": "owner",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"indexed": false,
|
||||
"internalType": "bytes",
|
||||
"name": "key",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "PublicKey",
|
||||
"type": "event"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "FIELD_SIZE",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "MAX_EXT_AMOUNT",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_extAmount",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "calculateExternalAmount",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "int256",
|
||||
"name": "",
|
||||
"type": "int256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "pure",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "currentCommitmentIndex",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "currentRoot",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_nullifierHash",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "isSpent",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "nullifierHashes",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "_pubKey",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "_account",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"name": "register",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "_proof",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_root",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_newRoot",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32[]",
|
||||
"name": "_inputNullifiers",
|
||||
"type": "bytes32[]"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32[2]",
|
||||
"name": "_outputCommitments",
|
||||
"type": "bytes32[2]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_outPathIndices",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_extAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_fee",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "address payable",
|
||||
"name": "recipient",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "address payable",
|
||||
"name": "relayer",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "encryptedOutput1",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "encryptedOutput2",
|
||||
"type": "bytes"
|
||||
}
|
||||
],
|
||||
"internalType": "struct TornadoPool.ExtData",
|
||||
"name": "_extData",
|
||||
"type": "tuple"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_extDataHash",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "transaction",
|
||||
"outputs": [],
|
||||
"stateMutability": "payable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "verifier16",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "contract IVerifier",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "verifier2",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "contract IVerifier",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "_proof",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_root",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_newRoot",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32[]",
|
||||
"name": "_inputNullifiers",
|
||||
"type": "bytes32[]"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32[2]",
|
||||
"name": "_outputCommitments",
|
||||
"type": "bytes32[2]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_outPathIndices",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_extAmount",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_fee",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "_extDataHash",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "verifyProof",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
5
src/abi/index.ts
Normal file
5
src/abi/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import TORNADO_POOL from './TornadoPool.json';
|
||||
|
||||
export const abi = {
|
||||
TORNADO_POOL,
|
||||
};
|
18
src/app.module.ts
Normal file
18
src/app.module.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { baseConfig } from './config';
|
||||
import { QueueModule } from './modules';
|
||||
import { CommunicationsModule } from './communication';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
load: [baseConfig],
|
||||
isGlobal: true,
|
||||
}),
|
||||
QueueModule,
|
||||
CommunicationsModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
554
src/artifacts/TornadoPool.d.ts
vendored
Normal file
554
src/artifacts/TornadoPool.d.ts
vendored
Normal file
@ -0,0 +1,554 @@
|
||||
/* Autogenerated file. Do not edit manually. */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import {
|
||||
ethers,
|
||||
EventFilter,
|
||||
Signer,
|
||||
BigNumber,
|
||||
BigNumberish,
|
||||
PopulatedTransaction,
|
||||
BaseContract,
|
||||
ContractTransaction,
|
||||
Overrides,
|
||||
PayableOverrides,
|
||||
CallOverrides,
|
||||
} from "ethers";
|
||||
import { BytesLike } from "@ethersproject/bytes";
|
||||
import { Listener, Provider } from "@ethersproject/providers";
|
||||
import { FunctionFragment, EventFragment, Result } from "@ethersproject/abi";
|
||||
import { TypedEventFilter, TypedEvent, TypedListener } from "./commons";
|
||||
|
||||
interface TornadoPoolInterface extends ethers.utils.Interface {
|
||||
functions: {
|
||||
"FIELD_SIZE()": FunctionFragment;
|
||||
"MAX_EXT_AMOUNT()": FunctionFragment;
|
||||
"calculateExternalAmount(uint256)": FunctionFragment;
|
||||
"currentCommitmentIndex()": FunctionFragment;
|
||||
"currentRoot()": FunctionFragment;
|
||||
"isSpent(bytes32)": FunctionFragment;
|
||||
"nullifierHashes(bytes32)": FunctionFragment;
|
||||
"register(bytes,bytes)": FunctionFragment;
|
||||
"transaction(bytes,bytes32,bytes32,bytes32[],bytes32[2],uint256,uint256,uint256,tuple,bytes32)": FunctionFragment;
|
||||
"verifier16()": FunctionFragment;
|
||||
"verifier2()": FunctionFragment;
|
||||
"verifyProof(bytes,bytes32,bytes32,bytes32[],bytes32[2],uint256,uint256,uint256,bytes32)": FunctionFragment;
|
||||
};
|
||||
|
||||
encodeFunctionData(
|
||||
functionFragment: "FIELD_SIZE",
|
||||
values?: undefined
|
||||
): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "MAX_EXT_AMOUNT",
|
||||
values?: undefined
|
||||
): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "calculateExternalAmount",
|
||||
values: [BigNumberish]
|
||||
): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "currentCommitmentIndex",
|
||||
values?: undefined
|
||||
): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "currentRoot",
|
||||
values?: undefined
|
||||
): string;
|
||||
encodeFunctionData(functionFragment: "isSpent", values: [BytesLike]): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "nullifierHashes",
|
||||
values: [BytesLike]
|
||||
): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "register",
|
||||
values: [BytesLike, BytesLike]
|
||||
): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "transaction",
|
||||
values: [
|
||||
BytesLike,
|
||||
BytesLike,
|
||||
BytesLike,
|
||||
BytesLike[],
|
||||
[BytesLike, BytesLike],
|
||||
BigNumberish,
|
||||
BigNumberish,
|
||||
BigNumberish,
|
||||
{
|
||||
recipient: string;
|
||||
relayer: string;
|
||||
encryptedOutput1: BytesLike;
|
||||
encryptedOutput2: BytesLike;
|
||||
},
|
||||
BytesLike
|
||||
]
|
||||
): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "verifier16",
|
||||
values?: undefined
|
||||
): string;
|
||||
encodeFunctionData(functionFragment: "verifier2", values?: undefined): string;
|
||||
encodeFunctionData(
|
||||
functionFragment: "verifyProof",
|
||||
values: [
|
||||
BytesLike,
|
||||
BytesLike,
|
||||
BytesLike,
|
||||
BytesLike[],
|
||||
[BytesLike, BytesLike],
|
||||
BigNumberish,
|
||||
BigNumberish,
|
||||
BigNumberish,
|
||||
BytesLike
|
||||
]
|
||||
): string;
|
||||
|
||||
decodeFunctionResult(functionFragment: "FIELD_SIZE", data: BytesLike): Result;
|
||||
decodeFunctionResult(
|
||||
functionFragment: "MAX_EXT_AMOUNT",
|
||||
data: BytesLike
|
||||
): Result;
|
||||
decodeFunctionResult(
|
||||
functionFragment: "calculateExternalAmount",
|
||||
data: BytesLike
|
||||
): Result;
|
||||
decodeFunctionResult(
|
||||
functionFragment: "currentCommitmentIndex",
|
||||
data: BytesLike
|
||||
): Result;
|
||||
decodeFunctionResult(
|
||||
functionFragment: "currentRoot",
|
||||
data: BytesLike
|
||||
): Result;
|
||||
decodeFunctionResult(functionFragment: "isSpent", data: BytesLike): Result;
|
||||
decodeFunctionResult(
|
||||
functionFragment: "nullifierHashes",
|
||||
data: BytesLike
|
||||
): Result;
|
||||
decodeFunctionResult(functionFragment: "register", data: BytesLike): Result;
|
||||
decodeFunctionResult(
|
||||
functionFragment: "transaction",
|
||||
data: BytesLike
|
||||
): Result;
|
||||
decodeFunctionResult(functionFragment: "verifier16", data: BytesLike): Result;
|
||||
decodeFunctionResult(functionFragment: "verifier2", data: BytesLike): Result;
|
||||
decodeFunctionResult(
|
||||
functionFragment: "verifyProof",
|
||||
data: BytesLike
|
||||
): Result;
|
||||
|
||||
events: {
|
||||
"EncryptedAccount(address,bytes)": EventFragment;
|
||||
"NewCommitment(bytes32,uint256,bytes)": EventFragment;
|
||||
"NewNullifier(bytes32)": EventFragment;
|
||||
"PublicKey(address,bytes)": EventFragment;
|
||||
};
|
||||
|
||||
getEvent(nameOrSignatureOrTopic: "EncryptedAccount"): EventFragment;
|
||||
getEvent(nameOrSignatureOrTopic: "NewCommitment"): EventFragment;
|
||||
getEvent(nameOrSignatureOrTopic: "NewNullifier"): EventFragment;
|
||||
getEvent(nameOrSignatureOrTopic: "PublicKey"): EventFragment;
|
||||
}
|
||||
|
||||
export class TornadoPool extends BaseContract {
|
||||
connect(signerOrProvider: Signer | Provider | string): this;
|
||||
attach(addressOrName: string): this;
|
||||
deployed(): Promise<this>;
|
||||
|
||||
listeners<EventArgsArray extends Array<any>, EventArgsObject>(
|
||||
eventFilter?: TypedEventFilter<EventArgsArray, EventArgsObject>
|
||||
): Array<TypedListener<EventArgsArray, EventArgsObject>>;
|
||||
off<EventArgsArray extends Array<any>, EventArgsObject>(
|
||||
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
|
||||
listener: TypedListener<EventArgsArray, EventArgsObject>
|
||||
): this;
|
||||
on<EventArgsArray extends Array<any>, EventArgsObject>(
|
||||
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
|
||||
listener: TypedListener<EventArgsArray, EventArgsObject>
|
||||
): this;
|
||||
once<EventArgsArray extends Array<any>, EventArgsObject>(
|
||||
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
|
||||
listener: TypedListener<EventArgsArray, EventArgsObject>
|
||||
): this;
|
||||
removeListener<EventArgsArray extends Array<any>, EventArgsObject>(
|
||||
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>,
|
||||
listener: TypedListener<EventArgsArray, EventArgsObject>
|
||||
): this;
|
||||
removeAllListeners<EventArgsArray extends Array<any>, EventArgsObject>(
|
||||
eventFilter: TypedEventFilter<EventArgsArray, EventArgsObject>
|
||||
): this;
|
||||
|
||||
listeners(eventName?: string): Array<Listener>;
|
||||
off(eventName: string, listener: Listener): this;
|
||||
on(eventName: string, listener: Listener): this;
|
||||
once(eventName: string, listener: Listener): this;
|
||||
removeListener(eventName: string, listener: Listener): this;
|
||||
removeAllListeners(eventName?: string): this;
|
||||
|
||||
queryFilter<EventArgsArray extends Array<any>, EventArgsObject>(
|
||||
event: TypedEventFilter<EventArgsArray, EventArgsObject>,
|
||||
fromBlockOrBlockhash?: string | number | undefined,
|
||||
toBlock?: string | number | undefined
|
||||
): Promise<Array<TypedEvent<EventArgsArray & EventArgsObject>>>;
|
||||
|
||||
interface: TornadoPoolInterface;
|
||||
|
||||
functions: {
|
||||
FIELD_SIZE(overrides?: CallOverrides): Promise<[BigNumber]>;
|
||||
|
||||
MAX_EXT_AMOUNT(overrides?: CallOverrides): Promise<[BigNumber]>;
|
||||
|
||||
calculateExternalAmount(
|
||||
_extAmount: BigNumberish,
|
||||
overrides?: CallOverrides
|
||||
): Promise<[BigNumber]>;
|
||||
|
||||
currentCommitmentIndex(overrides?: CallOverrides): Promise<[BigNumber]>;
|
||||
|
||||
currentRoot(overrides?: CallOverrides): Promise<[string]>;
|
||||
|
||||
isSpent(
|
||||
_nullifierHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<[boolean]>;
|
||||
|
||||
nullifierHashes(
|
||||
arg0: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<[boolean]>;
|
||||
|
||||
register(
|
||||
_pubKey: BytesLike,
|
||||
_account: BytesLike,
|
||||
overrides?: Overrides & { from?: string | Promise<string> }
|
||||
): Promise<ContractTransaction>;
|
||||
|
||||
transaction(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extData: {
|
||||
recipient: string;
|
||||
relayer: string;
|
||||
encryptedOutput1: BytesLike;
|
||||
encryptedOutput2: BytesLike;
|
||||
},
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: PayableOverrides & { from?: string | Promise<string> }
|
||||
): Promise<ContractTransaction>;
|
||||
|
||||
verifier16(overrides?: CallOverrides): Promise<[string]>;
|
||||
|
||||
verifier2(overrides?: CallOverrides): Promise<[string]>;
|
||||
|
||||
verifyProof(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<[boolean]>;
|
||||
};
|
||||
|
||||
FIELD_SIZE(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
MAX_EXT_AMOUNT(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
calculateExternalAmount(
|
||||
_extAmount: BigNumberish,
|
||||
overrides?: CallOverrides
|
||||
): Promise<BigNumber>;
|
||||
|
||||
currentCommitmentIndex(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
currentRoot(overrides?: CallOverrides): Promise<string>;
|
||||
|
||||
isSpent(
|
||||
_nullifierHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<boolean>;
|
||||
|
||||
nullifierHashes(arg0: BytesLike, overrides?: CallOverrides): Promise<boolean>;
|
||||
|
||||
register(
|
||||
_pubKey: BytesLike,
|
||||
_account: BytesLike,
|
||||
overrides?: Overrides & { from?: string | Promise<string> }
|
||||
): Promise<ContractTransaction>;
|
||||
|
||||
transaction(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extData: {
|
||||
recipient: string;
|
||||
relayer: string;
|
||||
encryptedOutput1: BytesLike;
|
||||
encryptedOutput2: BytesLike;
|
||||
},
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: PayableOverrides & { from?: string | Promise<string> }
|
||||
): Promise<ContractTransaction>;
|
||||
|
||||
verifier16(overrides?: CallOverrides): Promise<string>;
|
||||
|
||||
verifier2(overrides?: CallOverrides): Promise<string>;
|
||||
|
||||
verifyProof(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<boolean>;
|
||||
|
||||
callStatic: {
|
||||
FIELD_SIZE(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
MAX_EXT_AMOUNT(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
calculateExternalAmount(
|
||||
_extAmount: BigNumberish,
|
||||
overrides?: CallOverrides
|
||||
): Promise<BigNumber>;
|
||||
|
||||
currentCommitmentIndex(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
currentRoot(overrides?: CallOverrides): Promise<string>;
|
||||
|
||||
isSpent(
|
||||
_nullifierHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<boolean>;
|
||||
|
||||
nullifierHashes(
|
||||
arg0: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<boolean>;
|
||||
|
||||
register(
|
||||
_pubKey: BytesLike,
|
||||
_account: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<void>;
|
||||
|
||||
transaction(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extData: {
|
||||
recipient: string;
|
||||
relayer: string;
|
||||
encryptedOutput1: BytesLike;
|
||||
encryptedOutput2: BytesLike;
|
||||
},
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<void>;
|
||||
|
||||
verifier16(overrides?: CallOverrides): Promise<string>;
|
||||
|
||||
verifier2(overrides?: CallOverrides): Promise<string>;
|
||||
|
||||
verifyProof(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<boolean>;
|
||||
};
|
||||
|
||||
filters: {
|
||||
EncryptedAccount(
|
||||
owner?: string | null,
|
||||
account?: null
|
||||
): TypedEventFilter<[string, string], { owner: string; account: string }>;
|
||||
|
||||
NewCommitment(
|
||||
commitment?: null,
|
||||
index?: null,
|
||||
encryptedOutput?: null
|
||||
): TypedEventFilter<
|
||||
[string, BigNumber, string],
|
||||
{ commitment: string; index: BigNumber; encryptedOutput: string }
|
||||
>;
|
||||
|
||||
NewNullifier(
|
||||
nullifier?: null
|
||||
): TypedEventFilter<[string], { nullifier: string }>;
|
||||
|
||||
PublicKey(
|
||||
owner?: string | null,
|
||||
key?: null
|
||||
): TypedEventFilter<[string, string], { owner: string; key: string }>;
|
||||
};
|
||||
|
||||
estimateGas: {
|
||||
FIELD_SIZE(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
MAX_EXT_AMOUNT(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
calculateExternalAmount(
|
||||
_extAmount: BigNumberish,
|
||||
overrides?: CallOverrides
|
||||
): Promise<BigNumber>;
|
||||
|
||||
currentCommitmentIndex(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
currentRoot(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
isSpent(
|
||||
_nullifierHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<BigNumber>;
|
||||
|
||||
nullifierHashes(
|
||||
arg0: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<BigNumber>;
|
||||
|
||||
register(
|
||||
_pubKey: BytesLike,
|
||||
_account: BytesLike,
|
||||
overrides?: Overrides & { from?: string | Promise<string> }
|
||||
): Promise<BigNumber>;
|
||||
|
||||
transaction(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extData: {
|
||||
recipient: string;
|
||||
relayer: string;
|
||||
encryptedOutput1: BytesLike;
|
||||
encryptedOutput2: BytesLike;
|
||||
},
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: PayableOverrides & { from?: string | Promise<string> }
|
||||
): Promise<BigNumber>;
|
||||
|
||||
verifier16(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
verifier2(overrides?: CallOverrides): Promise<BigNumber>;
|
||||
|
||||
verifyProof(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<BigNumber>;
|
||||
};
|
||||
|
||||
populateTransaction: {
|
||||
FIELD_SIZE(overrides?: CallOverrides): Promise<PopulatedTransaction>;
|
||||
|
||||
MAX_EXT_AMOUNT(overrides?: CallOverrides): Promise<PopulatedTransaction>;
|
||||
|
||||
calculateExternalAmount(
|
||||
_extAmount: BigNumberish,
|
||||
overrides?: CallOverrides
|
||||
): Promise<PopulatedTransaction>;
|
||||
|
||||
currentCommitmentIndex(
|
||||
overrides?: CallOverrides
|
||||
): Promise<PopulatedTransaction>;
|
||||
|
||||
currentRoot(overrides?: CallOverrides): Promise<PopulatedTransaction>;
|
||||
|
||||
isSpent(
|
||||
_nullifierHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<PopulatedTransaction>;
|
||||
|
||||
nullifierHashes(
|
||||
arg0: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<PopulatedTransaction>;
|
||||
|
||||
register(
|
||||
_pubKey: BytesLike,
|
||||
_account: BytesLike,
|
||||
overrides?: Overrides & { from?: string | Promise<string> }
|
||||
): Promise<PopulatedTransaction>;
|
||||
|
||||
transaction(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extData: {
|
||||
recipient: string;
|
||||
relayer: string;
|
||||
encryptedOutput1: BytesLike;
|
||||
encryptedOutput2: BytesLike;
|
||||
},
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: PayableOverrides & { from?: string | Promise<string> }
|
||||
): Promise<PopulatedTransaction>;
|
||||
|
||||
verifier16(overrides?: CallOverrides): Promise<PopulatedTransaction>;
|
||||
|
||||
verifier2(overrides?: CallOverrides): Promise<PopulatedTransaction>;
|
||||
|
||||
verifyProof(
|
||||
_proof: BytesLike,
|
||||
_root: BytesLike,
|
||||
_newRoot: BytesLike,
|
||||
_inputNullifiers: BytesLike[],
|
||||
_outputCommitments: [BytesLike, BytesLike],
|
||||
_outPathIndices: BigNumberish,
|
||||
_extAmount: BigNumberish,
|
||||
_fee: BigNumberish,
|
||||
_extDataHash: BytesLike,
|
||||
overrides?: CallOverrides
|
||||
): Promise<PopulatedTransaction>;
|
||||
};
|
||||
}
|
36
src/artifacts/commons.ts
Normal file
36
src/artifacts/commons.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/* Autogenerated file. Do not edit manually. */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import { EventFilter, Event } from "ethers";
|
||||
import { Result } from "@ethersproject/abi";
|
||||
|
||||
export interface TypedEventFilter<_EventArgsArray, _EventArgsObject>
|
||||
extends EventFilter {}
|
||||
|
||||
export interface TypedEvent<EventArgs extends Result> extends Event {
|
||||
args: EventArgs;
|
||||
}
|
||||
|
||||
export type TypedListener<
|
||||
EventArgsArray extends Array<any>,
|
||||
EventArgsObject
|
||||
> = (
|
||||
...listenerArg: [
|
||||
...EventArgsArray,
|
||||
TypedEvent<EventArgsArray & EventArgsObject>
|
||||
]
|
||||
) => void;
|
||||
|
||||
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;
|
412
src/artifacts/factories/TornadoPool__factory.ts
Normal file
412
src/artifacts/factories/TornadoPool__factory.ts
Normal file
@ -0,0 +1,412 @@
|
||||
/* Autogenerated file. Do not edit manually. */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import { Contract, Signer, utils } from "ethers";
|
||||
import { Provider } from "@ethersproject/providers";
|
||||
import type { TornadoPool, TornadoPoolInterface } from "../TornadoPool";
|
||||
|
||||
const _abi = [
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "contract IVerifier",
|
||||
name: "_verifier2",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "contract IVerifier",
|
||||
name: "_verifier16",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_currentRoot",
|
||||
type: "bytes32",
|
||||
},
|
||||
],
|
||||
stateMutability: "nonpayable",
|
||||
type: "constructor",
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
internalType: "address",
|
||||
name: "owner",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: "bytes",
|
||||
name: "account",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
name: "EncryptedAccount",
|
||||
type: "event",
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
internalType: "bytes32",
|
||||
name: "commitment",
|
||||
type: "bytes32",
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: "uint256",
|
||||
name: "index",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: "bytes",
|
||||
name: "encryptedOutput",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
name: "NewCommitment",
|
||||
type: "event",
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
internalType: "bytes32",
|
||||
name: "nullifier",
|
||||
type: "bytes32",
|
||||
},
|
||||
],
|
||||
name: "NewNullifier",
|
||||
type: "event",
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
internalType: "address",
|
||||
name: "owner",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
internalType: "bytes",
|
||||
name: "key",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
name: "PublicKey",
|
||||
type: "event",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "FIELD_SIZE",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "",
|
||||
type: "uint256",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "MAX_EXT_AMOUNT",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "",
|
||||
type: "uint256",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_extAmount",
|
||||
type: "uint256",
|
||||
},
|
||||
],
|
||||
name: "calculateExternalAmount",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "int256",
|
||||
name: "",
|
||||
type: "int256",
|
||||
},
|
||||
],
|
||||
stateMutability: "pure",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "currentCommitmentIndex",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "",
|
||||
type: "uint256",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "currentRoot",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "",
|
||||
type: "bytes32",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_nullifierHash",
|
||||
type: "bytes32",
|
||||
},
|
||||
],
|
||||
name: "isSpent",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "bool",
|
||||
name: "",
|
||||
type: "bool",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "",
|
||||
type: "bytes32",
|
||||
},
|
||||
],
|
||||
name: "nullifierHashes",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "bool",
|
||||
name: "",
|
||||
type: "bool",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "_pubKey",
|
||||
type: "bytes",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "_account",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
name: "register",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "_proof",
|
||||
type: "bytes",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_root",
|
||||
type: "bytes32",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_newRoot",
|
||||
type: "bytes32",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32[]",
|
||||
name: "_inputNullifiers",
|
||||
type: "bytes32[]",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32[2]",
|
||||
name: "_outputCommitments",
|
||||
type: "bytes32[2]",
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_outPathIndices",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_extAmount",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_fee",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
components: [
|
||||
{
|
||||
internalType: "address payable",
|
||||
name: "recipient",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "address payable",
|
||||
name: "relayer",
|
||||
type: "address",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "encryptedOutput1",
|
||||
type: "bytes",
|
||||
},
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "encryptedOutput2",
|
||||
type: "bytes",
|
||||
},
|
||||
],
|
||||
internalType: "struct TornadoPool.ExtData",
|
||||
name: "_extData",
|
||||
type: "tuple",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_extDataHash",
|
||||
type: "bytes32",
|
||||
},
|
||||
],
|
||||
name: "transaction",
|
||||
outputs: [],
|
||||
stateMutability: "payable",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "verifier16",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "contract IVerifier",
|
||||
name: "",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [],
|
||||
name: "verifier2",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "contract IVerifier",
|
||||
name: "",
|
||||
type: "address",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
{
|
||||
inputs: [
|
||||
{
|
||||
internalType: "bytes",
|
||||
name: "_proof",
|
||||
type: "bytes",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_root",
|
||||
type: "bytes32",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_newRoot",
|
||||
type: "bytes32",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32[]",
|
||||
name: "_inputNullifiers",
|
||||
type: "bytes32[]",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32[2]",
|
||||
name: "_outputCommitments",
|
||||
type: "bytes32[2]",
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_outPathIndices",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_extAmount",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
internalType: "uint256",
|
||||
name: "_fee",
|
||||
type: "uint256",
|
||||
},
|
||||
{
|
||||
internalType: "bytes32",
|
||||
name: "_extDataHash",
|
||||
type: "bytes32",
|
||||
},
|
||||
],
|
||||
name: "verifyProof",
|
||||
outputs: [
|
||||
{
|
||||
internalType: "bool",
|
||||
name: "",
|
||||
type: "bool",
|
||||
},
|
||||
],
|
||||
stateMutability: "view",
|
||||
type: "function",
|
||||
},
|
||||
];
|
||||
|
||||
export class TornadoPool__factory {
|
||||
static readonly abi = _abi;
|
||||
static createInterface(): TornadoPoolInterface {
|
||||
return new utils.Interface(_abi) as TornadoPoolInterface;
|
||||
}
|
||||
static connect(
|
||||
address: string,
|
||||
signerOrProvider: Signer | Provider
|
||||
): TornadoPool {
|
||||
return new Contract(address, _abi, signerOrProvider) as TornadoPool;
|
||||
}
|
||||
}
|
6
src/artifacts/index.ts
Normal file
6
src/artifacts/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/* Autogenerated file. Do not edit manually. */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export type { TornadoPool } from "./TornadoPool";
|
||||
|
||||
export { TornadoPool__factory } from "./factories/TornadoPool__factory";
|
18
src/config/configuration.ts
Normal file
18
src/config/configuration.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export const baseConfig = () => ({
|
||||
port: parseInt(process.env.PORT, 10) || 8080,
|
||||
bull: {
|
||||
redis: {
|
||||
host: 'localhost',
|
||||
port: 6379,
|
||||
},
|
||||
settings: {
|
||||
lockDuration: 300000,
|
||||
lockRenewTime: 30000,
|
||||
stalledInterval: 30000,
|
||||
maxStalledCount: 3,
|
||||
guardInterval: 5000,
|
||||
retryProcessDelay: 5000,
|
||||
drainDelay: 5,
|
||||
},
|
||||
},
|
||||
});
|
1
src/config/index.ts
Normal file
1
src/config/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './configuration';
|
16
src/constants/contracts.ts
Normal file
16
src/constants/contracts.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { ChainId } from '@/types';
|
||||
|
||||
export const CONTRACT_NETWORKS: { [chainId in ChainId]: string } = {
|
||||
[ChainId.MAINNET]: '0x8Bfac9EF3d73cE08C7CEC339C0fE3B2e57814c1E',
|
||||
[ChainId.GOERLI]: '0x20a2D506cf52453D681F9E8E814A3437c6242B9e',
|
||||
[ChainId.OPTIMISM]: '0xc436071dE853A4421c57ddD0CDDC116C735aa8b5',
|
||||
};
|
||||
|
||||
export const RPC_LIST: { [chainId in ChainId]: string } = {
|
||||
[ChainId.MAINNET]:
|
||||
'https://mainnet.infura.io/v3/eb6a84e726614079948e0b1efce5baa5',
|
||||
[ChainId.GOERLI]:
|
||||
'https://eth-goerli.alchemyapi.io/v2/hlSj0EqPUuLGyyTExs6UqFKnXDrc_eOh',
|
||||
[ChainId.OPTIMISM]:
|
||||
'https://optimism-kovan.infura.io/v3/8f786b96d16046b78e0287fa61c6fcf8',
|
||||
};
|
2
src/constants/index.ts
Normal file
2
src/constants/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './variables';
|
||||
export * from './contracts';
|
19
src/constants/variables.ts
Normal file
19
src/constants/variables.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { BigNumber } from 'ethers';
|
||||
|
||||
const numbers = {
|
||||
ZERO: 0,
|
||||
ONE: 1,
|
||||
TWO: 2,
|
||||
SECOND: 1000,
|
||||
ETH_DECIMALS: 18,
|
||||
MERKLE_TREE_HEIGHT: 32,
|
||||
};
|
||||
|
||||
const BG_ZERO = BigNumber.from(numbers.ZERO);
|
||||
const FIELD_SIZE = BigNumber.from(
|
||||
'21888242871839275222246405745257275088548364400416034343698204186575808495617',
|
||||
);
|
||||
|
||||
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
export { numbers, FIELD_SIZE, BG_ZERO, ZERO_ADDRESS };
|
10
src/contracts/index.ts
Normal file
10
src/contracts/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { ChainId } from '@/types';
|
||||
import { CONTRACT_NETWORKS } from '@/constants';
|
||||
import { getProviderWithSigner } from '@/services';
|
||||
|
||||
import { TornadoPool__factory as TornadoPoolFactory } from '@/artifacts';
|
||||
|
||||
export function getTornadoPool(chainId: ChainId) {
|
||||
const provider = getProviderWithSigner(chainId);
|
||||
return TornadoPoolFactory.connect(CONTRACT_NETWORKS[chainId], provider);
|
||||
}
|
15
src/main.ts
Normal file
15
src/main.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
||||
const configService = app.get(ConfigService);
|
||||
await app.listen(configService.get('port'));
|
||||
}
|
||||
|
||||
bootstrap()
|
||||
.then((result) => console.log('result', result))
|
||||
.catch((e) => console.log('error', e.message));
|
1
src/modules/index.ts
Normal file
1
src/modules/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './queue';
|
73
src/modules/queue/base.processor.ts
Normal file
73
src/modules/queue/base.processor.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import {
|
||||
Processor,
|
||||
OnQueueActive,
|
||||
OnQueueFailed,
|
||||
OnQueueRemoved,
|
||||
OnQueueResumed,
|
||||
OnQueueStalled,
|
||||
OnQueueProgress,
|
||||
OnQueueCompleted,
|
||||
} from '@nestjs/bull';
|
||||
import { Injectable, OnModuleDestroy } from '@nestjs/common';
|
||||
import { Job, Queue } from 'bull';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
@Injectable()
|
||||
@Processor()
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export class BaseProcessor<T = object> implements OnModuleDestroy {
|
||||
public queueName: string;
|
||||
public queue: Queue<T>;
|
||||
|
||||
@OnQueueActive()
|
||||
async onQueueActive(job: Job<T>) {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
@OnQueueFailed()
|
||||
async onQueueFailed(job: Job<T>) {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
@OnQueueCompleted()
|
||||
async onQueueCompleted(job: Job<T>) {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
@OnQueueProgress()
|
||||
async onQueueProgress(job: Job<T>) {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
@OnQueueRemoved()
|
||||
async onQueueRemoved(job: Job<T>) {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
@OnQueueResumed()
|
||||
async onQueueResumed(job: Job<T>) {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
@OnQueueStalled()
|
||||
async onQueueStalled(job: Job<T>) {
|
||||
return this.updateTask(job);
|
||||
}
|
||||
|
||||
async updateTask(job: Job<T>) {
|
||||
const currentJob = await this.queue.getJob(job.id);
|
||||
await currentJob.update(job.data);
|
||||
}
|
||||
|
||||
private async createTask({ request }) {
|
||||
const id = uuid();
|
||||
await this.queue.add({ ...request, id });
|
||||
return id;
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
if (this.queue) {
|
||||
await this.queue.close();
|
||||
}
|
||||
}
|
||||
}
|
1
src/modules/queue/index.ts
Normal file
1
src/modules/queue/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './queue.module';
|
24
src/modules/queue/queue.module.ts
Normal file
24
src/modules/queue/queue.module.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { BullModule } from '@nestjs/bull';
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import config from 'config';
|
||||
import { AdvancedSettings } from 'bull';
|
||||
|
||||
import { RedisOptions } from 'ioredis';
|
||||
import { WithdrawalProcessor } from './withdrawal.processor';
|
||||
|
||||
const redis = config.get<RedisOptions>('bull.redis');
|
||||
const settings = config.get<AdvancedSettings>('bull.settings');
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
BullModule.registerQueue({
|
||||
redis,
|
||||
settings,
|
||||
name: 'withdrawal',
|
||||
}),
|
||||
],
|
||||
providers: [WithdrawalProcessor],
|
||||
exports: [BullModule],
|
||||
})
|
||||
export class QueueModule {}
|
132
src/modules/queue/withdrawal.processor.ts
Normal file
132
src/modules/queue/withdrawal.processor.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import { InjectQueue, Process, Processor } from '@nestjs/bull';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Job, Queue } from 'bull';
|
||||
import { BigNumber } from 'ethers';
|
||||
import { TxManager } from 'tx-manager';
|
||||
|
||||
import { getGasPrice } from '@/services';
|
||||
import { toChecksumAddress, toWei } from '@/utilities';
|
||||
|
||||
import { BaseProcessor } from './base.processor';
|
||||
|
||||
export interface Withdrawal {
|
||||
args: string[];
|
||||
txHash: string;
|
||||
status: string;
|
||||
contract: string;
|
||||
confirmations: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@Processor('job')
|
||||
export class WithdrawalProcessor extends BaseProcessor<Withdrawal> {
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
@InjectQueue('withdrawal') public withdrawalQueue: Queue,
|
||||
) {
|
||||
super();
|
||||
this.queueName = 'withdrawal';
|
||||
this.queue = withdrawalQueue;
|
||||
}
|
||||
|
||||
@Process()
|
||||
async processWithdrawals(job: Job<Withdrawal>) {
|
||||
try {
|
||||
await job.isActive();
|
||||
|
||||
const { args, contract } = job.data;
|
||||
|
||||
await this.checkFee({ contract, fee: args[4] });
|
||||
} catch (err) {
|
||||
await job.moveToFailed(err, true);
|
||||
}
|
||||
}
|
||||
|
||||
async submitTx(job: Job<Withdrawal>) {
|
||||
const txManager = new TxManager({
|
||||
privateKey: '',
|
||||
rpcUrl: '',
|
||||
config: { CONFIRMATIONS: '', MAX_GAS_PRICE: '', THROW_ON_REVERT: false },
|
||||
});
|
||||
|
||||
const tx = await txManager.createTx(await getTxObject(job));
|
||||
|
||||
try {
|
||||
const receipt = await tx
|
||||
.send()
|
||||
.on('transactionHash', async (txHash: string) => {
|
||||
job.data.txHash = txHash;
|
||||
job.data.status = 'SENT';
|
||||
|
||||
await job.update(job.data);
|
||||
})
|
||||
.on('mined', async () => {
|
||||
job.data.status = 'MINED';
|
||||
|
||||
await job.update(job.data);
|
||||
})
|
||||
.on('confirmations', async (confirmations) => {
|
||||
job.data.confirmations = confirmations;
|
||||
|
||||
await job.update(job.data);
|
||||
});
|
||||
|
||||
if (receipt.status === 1) {
|
||||
await job.isCompleted();
|
||||
|
||||
job.data.status = 'SENT';
|
||||
|
||||
await job.update(job.data);
|
||||
} else {
|
||||
throw new Error('Submitted transaction failed');
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error(`Revert by smart contract ${e.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
getInstance(address) {
|
||||
const id = this.configService.get('network.id');
|
||||
const instances = this.configService.get(`instances.${id}`);
|
||||
|
||||
for (const currency of Object.keys(instances)) {
|
||||
const { instanceAddress, decimals } = instances[currency];
|
||||
|
||||
for (const amount of Object.keys(instanceAddress)) {
|
||||
const contract = instances[currency].instanceAddress[amount];
|
||||
|
||||
if (toChecksumAddress(contract) === toChecksumAddress(address)) {
|
||||
return { currency, amount, decimals };
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async checkFee({ fee, contract }) {
|
||||
const { amount } = this.getInstance(contract);
|
||||
|
||||
const gasLimit = this.configService.get<number>('gasLimits');
|
||||
|
||||
const { fast } = await getGasPrice(1);
|
||||
|
||||
const expense = BigNumber.from(toWei(fast.toString(), 'gwei')).mul(
|
||||
gasLimit,
|
||||
);
|
||||
|
||||
const serviceFee = this.configService.get<number>('fee');
|
||||
|
||||
const feePercent = BigNumber.from(toWei(amount))
|
||||
.mul(toWei(serviceFee.toString()))
|
||||
.div(100);
|
||||
|
||||
const desiredFee = expense.add(feePercent);
|
||||
|
||||
if (fee.lt(desiredFee)) {
|
||||
throw new Error(
|
||||
'Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
4
src/modules/status/dto/create-subscribe.dto.ts
Normal file
4
src/modules/status/dto/create-subscribe.dto.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export class CreateStatusDto {
|
||||
error: boolean;
|
||||
status: string;
|
||||
}
|
1
src/modules/status/dto/index.ts
Normal file
1
src/modules/status/dto/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './create-subscribe.dto'
|
1
src/modules/status/index.ts
Normal file
1
src/modules/status/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { StatusModule } from './stat.module';
|
18
src/modules/status/stat.controller.ts
Normal file
18
src/modules/status/stat.controller.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
import { StatusService } from './stat.service';
|
||||
|
||||
@Controller()
|
||||
export class StatusController {
|
||||
constructor(private readonly service: StatusService) {}
|
||||
|
||||
@Get('/status')
|
||||
async status(): Promise<Health> {
|
||||
return await this.service.status();
|
||||
}
|
||||
|
||||
@Get('/')
|
||||
async main(): Promise<string> {
|
||||
return this.service.main();
|
||||
}
|
||||
}
|
12
src/modules/status/stat.module.ts
Normal file
12
src/modules/status/stat.module.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { StatusService } from './stat.service';
|
||||
import { StatusController } from './stat.controller';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule],
|
||||
providers: [StatusService],
|
||||
controllers: [StatusController],
|
||||
})
|
||||
export class StatusModule {}
|
26
src/modules/status/stat.service.ts
Normal file
26
src/modules/status/stat.service.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { Queue } from 'bull';
|
||||
|
||||
import { InjectQueue } from '@nestjs/bull';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
class StatusService {
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
@InjectQueue('withdrawal') private withdrawalQueue: Queue,
|
||||
) {}
|
||||
|
||||
async status(): Promise<Health> {
|
||||
return {
|
||||
status: '',
|
||||
error: false,
|
||||
};
|
||||
}
|
||||
|
||||
main(): string {
|
||||
return `This is <a href=https://tornado.cash>tornado.cash</a> Relayer service. Check the <a href=/status>/status</a> for settings`;
|
||||
}
|
||||
}
|
||||
|
||||
export { StatusService };
|
89
src/modules/status/stat.validator.ts
Normal file
89
src/modules/status/stat.validator.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import Ajv, { ValidateFunction } from 'ajv';
|
||||
import { isAddress, toChecksumAddress } from '@/utilities';
|
||||
|
||||
const ajv = new Ajv();
|
||||
|
||||
ajv.addKeyword({
|
||||
keyword: 'isAddress',
|
||||
validate: (schema: any, address: string) => {
|
||||
return isAddress(address);
|
||||
},
|
||||
errors: true,
|
||||
});
|
||||
|
||||
ajv.addKeyword({
|
||||
keyword: 'isKnownContract',
|
||||
validate: (schema: any, address: string) => {
|
||||
try {
|
||||
return address !== null;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
errors: true,
|
||||
});
|
||||
|
||||
ajv.addKeyword({
|
||||
keyword: 'isFeeRecipient',
|
||||
validate: (schema: any, address: string) => {
|
||||
try {
|
||||
return toChecksumAddress('') === toChecksumAddress(address);
|
||||
} 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 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 validateTornadoWithdraw = ajv.compile(tornadoWithdrawSchema);
|
||||
|
||||
function getInputError(
|
||||
validator: ValidateFunction,
|
||||
data: typeof tornadoWithdrawSchema,
|
||||
) {
|
||||
validator(data);
|
||||
if (validator.errors) {
|
||||
const [error] = validator.errors;
|
||||
return error.message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function validateWithdrawRequest(data: typeof tornadoWithdrawSchema) {
|
||||
return getInputError(validateTornadoWithdraw, data);
|
||||
}
|
||||
|
||||
export { validateWithdrawRequest };
|
4
src/modules/status/types/index.ts
Normal file
4
src/modules/status/types/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
type Health = {
|
||||
status: string;
|
||||
error: boolean;
|
||||
};
|
26
src/services/ether.ts
Normal file
26
src/services/ether.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { ethers } from 'ethers';
|
||||
|
||||
import { ChainId } from '@/types';
|
||||
import { RPC_LIST } from '@/constants';
|
||||
|
||||
interface Options {
|
||||
url: string;
|
||||
}
|
||||
|
||||
export class Provider {
|
||||
public provider: ethers.providers.JsonRpcProvider;
|
||||
|
||||
constructor(options: Options) {
|
||||
this.provider = new ethers.providers.JsonRpcProvider(options.url);
|
||||
}
|
||||
}
|
||||
|
||||
export function getProvider(chainId: ChainId): Provider {
|
||||
return new Provider({ url: RPC_LIST[chainId] });
|
||||
}
|
||||
|
||||
export function getProviderWithSigner(
|
||||
chainId: ChainId,
|
||||
): ethers.providers.BaseProvider {
|
||||
return ethers.providers.getDefaultProvider(RPC_LIST[chainId]);
|
||||
}
|
91
src/services/flashbot.ts
Normal file
91
src/services/flashbot.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { Wallet, PopulatedTransaction } from 'ethers';
|
||||
import {
|
||||
FlashbotsBundleProvider,
|
||||
FlashbotsBundleResolution,
|
||||
} from '@flashbots/ethers-provider-bundle';
|
||||
|
||||
import { ChainId } from '@/types';
|
||||
import { numbers } from '@/constants';
|
||||
import { getProviderWithSigner } from '@/services';
|
||||
|
||||
const authSigner = Wallet.createRandom();
|
||||
|
||||
const FLASH_BOT_RPC: { [key in ChainId]: { name: string; url: string } } = {
|
||||
[ChainId.GOERLI]: {
|
||||
url: 'https://relay-goerli.flashbots.net/',
|
||||
name: 'goerli',
|
||||
},
|
||||
[ChainId.MAINNET]: {
|
||||
url: '',
|
||||
name: '',
|
||||
},
|
||||
};
|
||||
|
||||
async function sendFlashBotTransaction(
|
||||
transaction: PopulatedTransaction,
|
||||
chainId: ChainId,
|
||||
) {
|
||||
const provider = getProviderWithSigner(chainId);
|
||||
|
||||
const { url, name } = FLASH_BOT_RPC[chainId];
|
||||
|
||||
const flashBotsProvider = await FlashbotsBundleProvider.create(
|
||||
provider,
|
||||
authSigner,
|
||||
url,
|
||||
name,
|
||||
);
|
||||
|
||||
const nonce = await provider.getTransactionCount(authSigner.address);
|
||||
|
||||
const mergedTx = {
|
||||
...transaction,
|
||||
nonce,
|
||||
from: authSigner.address,
|
||||
};
|
||||
|
||||
const signedTransaction = await authSigner.signTransaction(mergedTx);
|
||||
|
||||
const TIME_10_BLOCK = 130;
|
||||
|
||||
const blockNumber = await provider.getBlockNumber();
|
||||
const minTimestamp = (await provider.getBlock(blockNumber)).timestamp;
|
||||
|
||||
const maxTimestamp = minTimestamp + TIME_10_BLOCK;
|
||||
const targetBlockNumber = blockNumber + numbers.TWO;
|
||||
|
||||
const simulation = await flashBotsProvider.simulate(
|
||||
[signedTransaction],
|
||||
targetBlockNumber,
|
||||
);
|
||||
|
||||
if ('error' in simulation) {
|
||||
console.log(`Simulation Error: ${simulation.error.message}`);
|
||||
} else {
|
||||
console.log(
|
||||
`Simulation Success: ${JSON.stringify(simulation, null, numbers.TWO)}`,
|
||||
);
|
||||
}
|
||||
|
||||
const bundleSubmission = await flashBotsProvider.sendBundle(
|
||||
[{ signedTransaction }],
|
||||
targetBlockNumber,
|
||||
{
|
||||
minTimestamp,
|
||||
maxTimestamp,
|
||||
},
|
||||
);
|
||||
|
||||
if ('error' in bundleSubmission) {
|
||||
throw new Error(bundleSubmission.error.message);
|
||||
}
|
||||
|
||||
const waitResponse = await bundleSubmission.wait();
|
||||
const bundleSubmissionSimulation = await bundleSubmission.simulate();
|
||||
console.log({
|
||||
bundleSubmissionSimulation,
|
||||
waitResponse: FlashbotsBundleResolution[waitResponse],
|
||||
});
|
||||
}
|
||||
|
||||
export { sendFlashBotTransaction };
|
3
src/services/index.ts
Normal file
3
src/services/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './ether';
|
||||
export * from './oracle';
|
||||
export * from './flashbot';
|
29
src/services/oracle.ts
Normal file
29
src/services/oracle.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { GasPriceOracle } from 'gas-price-oracle';
|
||||
import { GasPrice } from 'gas-price-oracle/lib/types';
|
||||
|
||||
import { ChainId } from '@/types';
|
||||
import { RPC_LIST, numbers } from '@/constants';
|
||||
|
||||
const SECONDS = 10;
|
||||
const TEN_SECOND = SECONDS * numbers.SECOND;
|
||||
|
||||
const OPTIMISM_GAS_PRICE = {
|
||||
fast: 0.015,
|
||||
low: 0.015,
|
||||
instant: 0.015,
|
||||
standard: 0.015,
|
||||
};
|
||||
|
||||
const getGasPrice = async (chainId: ChainId): Promise<GasPrice> => {
|
||||
if (chainId === ChainId.OPTIMISM) {
|
||||
return OPTIMISM_GAS_PRICE;
|
||||
}
|
||||
|
||||
const instance = new GasPriceOracle({
|
||||
timeout: TEN_SECOND,
|
||||
defaultRpc: RPC_LIST[ChainId.MAINNET],
|
||||
});
|
||||
return await instance.gasPrices();
|
||||
};
|
||||
|
||||
export { getGasPrice };
|
9
src/types/index.ts
Normal file
9
src/types/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
const MAINNET_CHAIN_ID = 1
|
||||
const GOERLI_CHAIN_ID = 5
|
||||
const OPTIMISM_CHAIN_ID = 69
|
||||
|
||||
export enum ChainId {
|
||||
MAINNET = MAINNET_CHAIN_ID,
|
||||
GOERLI = GOERLI_CHAIN_ID,
|
||||
OPTIMISM = OPTIMISM_CHAIN_ID,
|
||||
}
|
37
src/utilities/crypto.ts
Normal file
37
src/utilities/crypto.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { BigNumber, utils, BigNumberish } from 'ethers';
|
||||
import {
|
||||
toChecksumAddress as checksumAddress,
|
||||
isAddress as checkAddress,
|
||||
} from 'web3-utils';
|
||||
|
||||
import { numbers } from '@/constants';
|
||||
|
||||
// eslint-disable-next-line
|
||||
export function isAddress(value: any): boolean {
|
||||
try {
|
||||
return checkAddress(value);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line
|
||||
export function toChecksumAddress(value: any): string {
|
||||
return checksumAddress(value);
|
||||
}
|
||||
|
||||
export function toWei(value: string, uintName = 'wei') {
|
||||
return utils.parseUnits(value, uintName);
|
||||
}
|
||||
|
||||
export function hexToNumber(hex: string) {
|
||||
return BigNumber.from(hex).toNumber();
|
||||
}
|
||||
|
||||
export function numberToHex(value: number) {
|
||||
return utils.hexlify(value);
|
||||
}
|
||||
|
||||
export function fromWei(balance: BigNumberish) {
|
||||
return utils.formatUnits(balance, numbers.ETH_DECIMALS);
|
||||
}
|
2
src/utilities/index.ts
Normal file
2
src/utilities/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './crypto'
|
||||
|
24
test/app.e2e-spec.ts
Normal file
24
test/app.e2e-spec.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
9
test/jest-e2e.json
Normal file
9
test/jest-e2e.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": ".",
|
||||
"testEnvironment": "node",
|
||||
"testRegex": ".e2e-spec.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
}
|
||||
}
|
4
tsconfig.build.json
Normal file
4
tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"skipLibCheck": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user