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