Initial commit

Exported from tornado-cli 1.0.3
This commit is contained in:
Tornado Contrib 2024-04-29 15:54:32 +00:00
commit a9035f1579
No known key found for this signature in database
GPG Key ID: 60B4DF1A076C64B1
66 changed files with 21095 additions and 0 deletions

65
.eslintrc.js Normal file
View File

@ -0,0 +1,65 @@
module.exports = {
"env": {
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"prettier",
"plugin:prettier/recommended",
],
"overrides": [
{
"env": {
"node": true
},
"files": [
".eslintrc.{js,cjs}"
],
"parserOptions": {
"sourceType": "script"
}
}
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"prettier"
],
"rules": {
"prettier/prettier": [
"error",
{
singleQuote: true,
printWidth: 120
}
],
"import/order": ["error"],
/**
"indent": [
"error",
2
],
**/
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
],
"@typescript-eslint/no-unused-vars": ["warn"]
}
}

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
dist/* linguist-vendored

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
node_modules
.env
/events
/trees
backup-tornado-*
backup-tornadoInvoice-*
backup-note-account-*

1
.npmrc Normal file
View File

@ -0,0 +1 @@
@tornado:registry=https://git.tornado.ws/api/packages/tornado-packages/npm/

25
README.md Normal file
View File

@ -0,0 +1,25 @@
<div class="hero" align="center">
<img src="./logo2.png">
# Tornado Core
🛠 An SDK for building applications on top of [Privacy Pools](https://www.forbes.com/sites/tomerniv/2023/09/07/privacy-pools-bridging-the-gap-between-blockchain-and-regulatory-compliance)
[![Telegram Badge](https://img.shields.io/badge/Join%20Group-telegram?style=flat&logo=telegram&color=blue&link=https%3A%2F%2Ft.me%2Ftornadoofficial)](https://t.me/tornadoofficial) [![Element Badge](https://img.shields.io/badge/Join%20Element%20Chat-Element?style=flat&logo=element&color=green&link=https%3A%2F%2Felement.tornadocash.social%2F)](https://element.tornadocash.social) [![Discourse Badge](https://img.shields.io/badge/Discourse-Discourse?style=flat&logo=Discourse&color=black&link=https%3A%2F%2Fforum.tornado.ws%2F)](https://forum.tornado.ws/)
</div>
### About Tornado Core
Tornado Core is a modern building block for Privacy Pools to build anything from custom UI or CLI tools
+ Written in [TypeScript](https://www.typescriptlang.org/)
+ Built on top of modern tech stacks like [Ethers.js V6](https://docs.ethers.org/v6/) or [TypeChain](https://github.com/dethcrypto/TypeChain)
+ Creates resource heavy Merkle Trees on a separate thread using Web Workers / Worker Threads
+ Resilient API requests made by [cross-fetch](https://www.npmjs.com/package/cross-fetch) and retries, especially for Tor Users
+ Modular design

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
logo2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

85
package.json Normal file
View File

@ -0,0 +1,85 @@
{
"name": "@tornado/core",
"version": "1.0.0",
"description": "An SDK for building applications on top of Privacy Pools",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"unpkg": "./dist/index.umd.js",
"jsdelivr": "./dist/index.umd.js",
"scripts": {
"typechain": "typechain --target ethers-v6 --out-dir src/typechain src/abi/*.json",
"types": "tsc --declaration --emitDeclarationOnly",
"lint": "eslint src/**/*.ts --ext .ts --ignore-pattern src/typechain",
"build:node": "rollup -c",
"build:web": "webpack",
"build": "yarn types && yarn build:node && yarn build:web"
},
"author": "",
"license": "MIT",
"files": [
"dist",
"src",
".eslintrc.js",
".gitattributes",
".gitignore",
".npmrc",
"logo.png",
"logo2.png",
"rollup.config.mjs",
"tsconfig.json",
"yarn.lock"
],
"dependencies": {
"@metamask/eth-sig-util": "^7.0.1",
"@tornado/contracts": "^1.0.0",
"@tornado/fixed-merkle-tree": "^0.7.3",
"@tornado/snarkjs": "^0.1.20",
"@tornado/websnark": "^0.0.4",
"ajv": "^8.12.0",
"bloomfilter.js": "^1.0.2",
"bn.js": "^5.2.1",
"circomlibjs": "0.1.7",
"cross-fetch": "^4.0.0",
"ethers": "^6.4.0",
"ffjavascript": "0.2.48",
"fflate": "^0.8.2"
},
"optionalDependencies": {
"@colors/colors": "1.5.0",
"cli-table3": "^0.6.4",
"commander": "^12.0.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.4",
"moment": "^2.30.1",
"socks-proxy-agent": "^8.0.3"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@typechain/ethers-v6": "^0.5.1",
"@types/bn.js": "^5.1.5",
"@types/circomlibjs": "^0.1.6",
"@types/node": "^20.12.5",
"@types/node-fetch": "^2.6.11",
"@typescript-eslint/eslint-plugin": "^7.6.0",
"@typescript-eslint/parser": "^7.6.0",
"esbuild": "^0.20.2",
"esbuild-loader": "^4.1.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^5.1.3",
"node-polyfill-webpack-plugin": "^3.0.0",
"prettier": "^3.2.5",
"rollup": "^4.14.1",
"rollup-plugin-esbuild": "^6.1.1",
"tsc": "^2.0.4",
"typechain": "^8.3.2",
"typescript": "^5.4.4",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4"
}
}

82
rollup.config.mjs Normal file
View File

@ -0,0 +1,82 @@
import esbuild from 'rollup-plugin-esbuild';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import pkgJson from './package.json' assert { type: 'json' };
const external = Object.keys(pkgJson.dependencies).concat(
Object.keys(pkgJson.optionalDependencies),
[
'@tornado/websnark/src/utils',
'@tornado/websnark/src/groth16',
]
);
const config = [
{
input: 'src/index.ts',
output: [
{
file: pkgJson.main,
format: "cjs",
esModule: false,
},
],
external,
plugins: [
esbuild({
include: /\.[jt]sx?$/,
minify: false,
sourceMap: true,
target: 'es2016',
}),
commonjs(),
nodeResolve(),
json()
],
},
{
input: 'src/index.ts',
output: [
{
file: pkgJson.module,
format: "esm",
},
],
external,
plugins: [
esbuild({
include: /\.[jt]sx?$/,
minify: false,
sourceMap: true,
target: 'es2016',
}),
nodeResolve(),
json()
],
},
{
input: 'src/merkleTreeWorker.ts',
output: [
{
file: 'dist/merkleTreeWorker.js',
format: "cjs",
esModule: false,
},
],
treeshake: 'smallest',
plugins: [
esbuild({
include: /\.[jt]sx?$/,
minify: false,
sourceMap: true,
target: 'es2016',
}),
commonjs(),
nodeResolve(),
json()
],
}
]
export default config;

681
src/abi/ENS.json Normal file
View File

@ -0,0 +1,681 @@
[
{
"constant": true,
"inputs": [
{
"internalType": "bytes4",
"name": "interfaceID",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "string",
"name": "key",
"type": "string"
},
{
"internalType": "string",
"name": "value",
"type": "string"
}
],
"name": "setText",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "bytes4",
"name": "interfaceID",
"type": "bytes4"
}
],
"name": "interfaceImplementer",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "contentTypes",
"type": "uint256"
}
],
"name": "ABI",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "x",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "y",
"type": "bytes32"
}
],
"name": "setPubkey",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "hash",
"type": "bytes"
}
],
"name": "setContenthash",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "addr",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bool",
"name": "isAuthorised",
"type": "bool"
}
],
"name": "setAuthorisation",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "string",
"name": "key",
"type": "string"
}
],
"name": "text",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "contentType",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "setABI",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "string",
"name": "name",
"type": "string"
}
],
"name": "setName",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "coinType",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "a",
"type": "bytes"
}
],
"name": "setAddr",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "contenthash",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "pubkey",
"outputs": [
{
"internalType": "bytes32",
"name": "x",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "y",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "address",
"name": "a",
"type": "address"
}
],
"name": "setAddr",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "bytes4",
"name": "interfaceID",
"type": "bytes4"
},
{
"internalType": "address",
"name": "implementer",
"type": "address"
}
],
"name": "setInterface",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "coinType",
"type": "uint256"
}
],
"name": "addr",
"outputs": [
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "authorisations",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ENS",
"name": "_ens",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "target",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "isAuthorised",
"type": "bool"
}
],
"name": "AuthorisationChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "string",
"name": "indexedKey",
"type": "string"
},
{
"indexed": false,
"internalType": "string",
"name": "key",
"type": "string"
}
],
"name": "TextChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "x",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "y",
"type": "bytes32"
}
],
"name": "PubkeyChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "string",
"name": "name",
"type": "string"
}
],
"name": "NameChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes4",
"name": "interfaceID",
"type": "bytes4"
},
{
"indexed": false,
"internalType": "address",
"name": "implementer",
"type": "address"
}
],
"name": "InterfaceChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes",
"name": "hash",
"type": "bytes"
}
],
"name": "ContenthashChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "address",
"name": "a",
"type": "address"
}
],
"name": "AddrChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "coinType",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes",
"name": "newAddress",
"type": "bytes"
}
],
"name": "AddressChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "uint256",
"name": "contentType",
"type": "uint256"
}
],
"name": "ABIChanged",
"type": "event"
}
]

273
src/abi/ERC20.json Normal file
View File

@ -0,0 +1,273 @@
[
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "_totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "who",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{ "internalType": "address", "name": "owner", "type": "address" },
{ "internalType": "address", "name": "spender", "type": "address" },
{ "internalType": "uint256", "name": "amount", "type": "uint256" },
{ "internalType": "uint256", "name": "deadline", "type": "uint256" },
{ "internalType": "uint8", "name": "v", "type": "uint8" },
{ "internalType": "bytes32", "name": "r", "type": "bytes32" },
{ "internalType": "bytes32", "name": "s", "type": "bytes32" }
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

189
src/abi/GasPriceOracle.json Normal file
View File

@ -0,0 +1,189 @@
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "GAS_UNIT",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "_derivationThresold",
"type": "uint32"
}
],
"name": "changeDerivationThresold",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "_gasUnit",
"type": "uint32"
}
],
"name": "changeGasUnit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "_heartbeat",
"type": "uint32"
}
],
"name": "changeHeartbeat",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"name": "changeOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "derivationThresold",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "gasPrice",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "heartbeat",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxFeePerGas",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "maxPriorityFeePerGas",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pastGasPrice",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint32",
"name": "_gasPrice",
"type": "uint32"
}
],
"name": "setGasPrice",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "timestamp",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
}
]

440
src/abi/Multicall.json Normal file
View File

@ -0,0 +1,440 @@
[
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes[]",
"name": "returnData",
"type": "bytes[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bool",
"name": "allowFailure",
"type": "bool"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call3[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate3",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bool",
"name": "allowFailure",
"type": "bool"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call3Value[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "aggregate3Value",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "blockAndAggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "getBasefee",
"outputs": [
{
"internalType": "uint256",
"name": "basefee",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"name": "getBlockHash",
"outputs": [
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getChainId",
"outputs": [
{
"internalType": "uint256",
"name": "chainid",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockCoinbase",
"outputs": [
{
"internalType": "address",
"name": "coinbase",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockDifficulty",
"outputs": [
{
"internalType": "uint256",
"name": "difficulty",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockGasLimit",
"outputs": [
{
"internalType": "uint256",
"name": "gaslimit",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getCurrentBlockTimestamp",
"outputs": [
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "addr",
"type": "address"
}
],
"name": "getEthBalance",
"outputs": [
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastBlockHash",
"outputs": [
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "requireSuccess",
"type": "bool"
},
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryAggregate",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bool",
"name": "requireSuccess",
"type": "bool"
},
{
"components": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Call[]",
"name": "calls",
"type": "tuple[]"
}
],
"name": "tryBlockAndAggregate",
"outputs": [
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "blockHash",
"type": "bytes32"
},
{
"components": [
{
"internalType": "bool",
"name": "success",
"type": "bool"
},
{
"internalType": "bytes",
"name": "returnData",
"type": "bytes"
}
],
"internalType": "struct Multicall3.Result[]",
"name": "returnData",
"type": "tuple[]"
}
],
"stateMutability": "payable",
"type": "function"
}
]

555
src/abi/OffchainOracle.json Normal file
View File

@ -0,0 +1,555 @@
[
{
"inputs":[
{
"internalType":"contract MultiWrapper",
"name":"_multiWrapper",
"type":"address"
},
{
"internalType":"contract IOracle[]",
"name":"existingOracles",
"type":"address[]"
},
{
"internalType":"enum OffchainOracle.OracleType[]",
"name":"oracleTypes",
"type":"uint8[]"
},
{
"internalType":"contract IERC20[]",
"name":"existingConnectors",
"type":"address[]"
},
{
"internalType":"contract IERC20",
"name":"wBase",
"type":"address"
},
{
"internalType":"address",
"name":"owner",
"type":"address"
}
],
"stateMutability":"nonpayable",
"type":"constructor"
},
{
"inputs":[
],
"name":"ArraysLengthMismatch",
"type":"error"
},
{
"inputs":[
],
"name":"ConnectorAlreadyAdded",
"type":"error"
},
{
"inputs":[
],
"name":"InvalidOracleTokenKind",
"type":"error"
},
{
"inputs":[
],
"name":"OracleAlreadyAdded",
"type":"error"
},
{
"inputs":[
],
"name":"SameTokens",
"type":"error"
},
{
"inputs":[
],
"name":"TooBigThreshold",
"type":"error"
},
{
"inputs":[
],
"name":"UnknownConnector",
"type":"error"
},
{
"inputs":[
],
"name":"UnknownOracle",
"type":"error"
},
{
"anonymous":false,
"inputs":[
{
"indexed":false,
"internalType":"contract IERC20",
"name":"connector",
"type":"address"
}
],
"name":"ConnectorAdded",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":false,
"internalType":"contract IERC20",
"name":"connector",
"type":"address"
}
],
"name":"ConnectorRemoved",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":false,
"internalType":"contract MultiWrapper",
"name":"multiWrapper",
"type":"address"
}
],
"name":"MultiWrapperUpdated",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":false,
"internalType":"contract IOracle",
"name":"oracle",
"type":"address"
},
{
"indexed":false,
"internalType":"enum OffchainOracle.OracleType",
"name":"oracleType",
"type":"uint8"
}
],
"name":"OracleAdded",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":false,
"internalType":"contract IOracle",
"name":"oracle",
"type":"address"
},
{
"indexed":false,
"internalType":"enum OffchainOracle.OracleType",
"name":"oracleType",
"type":"uint8"
}
],
"name":"OracleRemoved",
"type":"event"
},
{
"anonymous":false,
"inputs":[
{
"indexed":true,
"internalType":"address",
"name":"previousOwner",
"type":"address"
},
{
"indexed":true,
"internalType":"address",
"name":"newOwner",
"type":"address"
}
],
"name":"OwnershipTransferred",
"type":"event"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"connector",
"type":"address"
}
],
"name":"addConnector",
"outputs":[
],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IOracle",
"name":"oracle",
"type":"address"
},
{
"internalType":"enum OffchainOracle.OracleType",
"name":"oracleKind",
"type":"uint8"
}
],
"name":"addOracle",
"outputs":[
],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[
],
"name":"connectors",
"outputs":[
{
"internalType":"contract IERC20[]",
"name":"allConnectors",
"type":"address[]"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"srcToken",
"type":"address"
},
{
"internalType":"contract IERC20",
"name":"dstToken",
"type":"address"
},
{
"internalType":"bool",
"name":"useWrappers",
"type":"bool"
}
],
"name":"getRate",
"outputs":[
{
"internalType":"uint256",
"name":"weightedRate",
"type":"uint256"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"srcToken",
"type":"address"
},
{
"internalType":"bool",
"name":"useSrcWrappers",
"type":"bool"
}
],
"name":"getRateToEth",
"outputs":[
{
"internalType":"uint256",
"name":"weightedRate",
"type":"uint256"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"srcToken",
"type":"address"
},
{
"internalType":"bool",
"name":"useSrcWrappers",
"type":"bool"
},
{
"internalType":"contract IERC20[]",
"name":"customConnectors",
"type":"address[]"
},
{
"internalType":"uint256",
"name":"thresholdFilter",
"type":"uint256"
}
],
"name":"getRateToEthWithCustomConnectors",
"outputs":[
{
"internalType":"uint256",
"name":"weightedRate",
"type":"uint256"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"srcToken",
"type":"address"
},
{
"internalType":"bool",
"name":"useSrcWrappers",
"type":"bool"
},
{
"internalType":"uint256",
"name":"thresholdFilter",
"type":"uint256"
}
],
"name":"getRateToEthWithThreshold",
"outputs":[
{
"internalType":"uint256",
"name":"weightedRate",
"type":"uint256"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"srcToken",
"type":"address"
},
{
"internalType":"contract IERC20",
"name":"dstToken",
"type":"address"
},
{
"internalType":"bool",
"name":"useWrappers",
"type":"bool"
},
{
"internalType":"contract IERC20[]",
"name":"customConnectors",
"type":"address[]"
},
{
"internalType":"uint256",
"name":"thresholdFilter",
"type":"uint256"
}
],
"name":"getRateWithCustomConnectors",
"outputs":[
{
"internalType":"uint256",
"name":"weightedRate",
"type":"uint256"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"srcToken",
"type":"address"
},
{
"internalType":"contract IERC20",
"name":"dstToken",
"type":"address"
},
{
"internalType":"bool",
"name":"useWrappers",
"type":"bool"
},
{
"internalType":"uint256",
"name":"thresholdFilter",
"type":"uint256"
}
],
"name":"getRateWithThreshold",
"outputs":[
{
"internalType":"uint256",
"name":"weightedRate",
"type":"uint256"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
],
"name":"multiWrapper",
"outputs":[
{
"internalType":"contract MultiWrapper",
"name":"",
"type":"address"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
],
"name":"oracles",
"outputs":[
{
"internalType":"contract IOracle[]",
"name":"allOracles",
"type":"address[]"
},
{
"internalType":"enum OffchainOracle.OracleType[]",
"name":"oracleTypes",
"type":"uint8[]"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
],
"name":"owner",
"outputs":[
{
"internalType":"address",
"name":"",
"type":"address"
}
],
"stateMutability":"view",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IERC20",
"name":"connector",
"type":"address"
}
],
"name":"removeConnector",
"outputs":[
],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract IOracle",
"name":"oracle",
"type":"address"
},
{
"internalType":"enum OffchainOracle.OracleType",
"name":"oracleKind",
"type":"uint8"
}
],
"name":"removeOracle",
"outputs":[
],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[
],
"name":"renounceOwnership",
"outputs":[
],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[
{
"internalType":"contract MultiWrapper",
"name":"_multiWrapper",
"type":"address"
}
],
"name":"setMultiWrapper",
"outputs":[
],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[
{
"internalType":"address",
"name":"newOwner",
"type":"address"
}
],
"name":"transferOwnership",
"outputs":[
],
"stateMutability":"nonpayable",
"type":"function"
}
]

View File

@ -0,0 +1,152 @@
[
{
"inputs": [{ "internalType": "address", "name": "_owner", "type": "address" }],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
"name": "DecimalsUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
"name": "GasPriceUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
"name": "L1BaseFeeUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
"name": "OverheadUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{ "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" },
{ "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" }
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [{ "indexed": false, "internalType": "uint256", "name": "", "type": "uint256" }],
"name": "ScalarUpdated",
"type": "event"
},
{
"inputs": [],
"name": "decimals",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "gasPrice",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "bytes", "name": "_data", "type": "bytes" }],
"name": "getL1Fee",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "bytes", "name": "_data", "type": "bytes" }],
"name": "getL1GasUsed",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "l1BaseFee",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "overhead",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [{ "internalType": "address", "name": "", "type": "address" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "scalar",
"outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "_decimals", "type": "uint256" }],
"name": "setDecimals",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "_gasPrice", "type": "uint256" }],
"name": "setGasPrice",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "_baseFee", "type": "uint256" }],
"name": "setL1BaseFee",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "_overhead", "type": "uint256" }],
"name": "setOverhead",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "uint256", "name": "_scalar", "type": "uint256" }],
"name": "setScalar",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

View File

@ -0,0 +1,32 @@
[
{
"inputs": [
{
"internalType": "contract ENS",
"name": "_ens",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "addresses",
"type": "address[]"
}
],
"name": "getNames",
"outputs": [
{
"internalType": "string[]",
"name": "r",
"type": "string[]"
}
],
"stateMutability": "view",
"type": "function"
}
]

339
src/batch.ts Normal file
View File

@ -0,0 +1,339 @@
import type { Provider, BlockTag, Block, TransactionResponse, BaseContract, ContractEventName, EventLog } from 'ethers';
import { chunk, sleep } from './utils';
export interface BatchBlockServiceConstructor {
provider: Provider;
onProgress?: BatchBlockOnProgress;
concurrencySize?: number;
batchSize?: number;
shouldRetry?: boolean;
retryMax?: number;
retryOn?: number;
}
export type BatchBlockOnProgress = ({
percentage,
currentIndex,
totalIndex,
}: {
percentage: number;
currentIndex?: number;
totalIndex?: number;
}) => void;
/**
* Fetch blocks from web3 provider on batches
*/
export class BatchBlockService {
provider: Provider;
onProgress?: BatchBlockOnProgress;
concurrencySize: number;
batchSize: number;
shouldRetry: boolean;
retryMax: number;
retryOn: number;
constructor({
provider,
onProgress,
concurrencySize = 10,
batchSize = 10,
shouldRetry = true,
retryMax = 5,
retryOn = 500,
}: BatchBlockServiceConstructor) {
this.provider = provider;
this.onProgress = onProgress;
this.concurrencySize = concurrencySize;
this.batchSize = batchSize;
this.shouldRetry = shouldRetry;
this.retryMax = retryMax;
this.retryOn = retryOn;
}
async getBlock(blockTag: BlockTag): Promise<Block> {
const blockObject = await this.provider.getBlock(blockTag);
// if the provider returns null (which they have corrupted block data for one of their nodes) throw and retry
if (!blockObject) {
const errMsg = `No block for ${blockTag}`;
throw new Error(errMsg);
}
return blockObject;
}
createBatchRequest(batchArray: BlockTag[][]): Promise<Block[]>[] {
return batchArray.map(async (blocks: BlockTag[], index: number) => {
// send batch requests on milliseconds to avoid including them on a single batch request
await sleep(20 * index);
return (async () => {
let retries = 0;
let err;
// eslint-disable-next-line no-unmodified-loop-condition
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
try {
return await Promise.all(blocks.map((b) => this.getBlock(b)));
} catch (e) {
retries++;
err = e;
// retry on 0.5 seconds
await sleep(this.retryOn);
}
}
throw err;
})();
});
}
async getBatchBlocks(blocks: BlockTag[]): Promise<Block[]> {
let blockCount = 0;
const results: Block[] = [];
for (const chunks of chunk(blocks, this.concurrencySize * this.batchSize)) {
const chunksResult = (await Promise.all(this.createBatchRequest(chunk(chunks, this.batchSize)))).flat();
results.push(...chunksResult);
blockCount += chunks.length;
if (typeof this.onProgress === 'function') {
this.onProgress({
percentage: blockCount / blocks.length,
currentIndex: blockCount,
totalIndex: blocks.length,
});
}
}
return results;
}
}
/**
* Fetch transactions from web3 provider on batches
*/
export class BatchTransactionService {
provider: Provider;
onProgress?: BatchBlockOnProgress;
concurrencySize: number;
batchSize: number;
shouldRetry: boolean;
retryMax: number;
retryOn: number;
constructor({
provider,
onProgress,
concurrencySize = 10,
batchSize = 10,
shouldRetry = true,
retryMax = 5,
retryOn = 500,
}: BatchBlockServiceConstructor) {
this.provider = provider;
this.onProgress = onProgress;
this.concurrencySize = concurrencySize;
this.batchSize = batchSize;
this.shouldRetry = shouldRetry;
this.retryMax = retryMax;
this.retryOn = retryOn;
}
async getTransaction(txHash: string): Promise<TransactionResponse> {
const txObject = await this.provider.getTransaction(txHash);
if (!txObject) {
const errMsg = `No transaction for ${txHash}`;
throw new Error(errMsg);
}
return txObject;
}
createBatchRequest(batchArray: string[][]): Promise<TransactionResponse[]>[] {
return batchArray.map(async (txs: string[], index: number) => {
await sleep(20 * index);
return (async () => {
let retries = 0;
let err;
// eslint-disable-next-line no-unmodified-loop-condition
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
try {
return await Promise.all(txs.map((tx) => this.getTransaction(tx)));
} catch (e) {
retries++;
err = e;
// retry on 0.5 seconds
await sleep(this.retryOn);
}
}
throw err;
})();
});
}
async getBatchTransactions(txs: string[]): Promise<TransactionResponse[]> {
let txCount = 0;
const results = [];
for (const chunks of chunk(txs, this.concurrencySize * this.batchSize)) {
const chunksResult = (await Promise.all(this.createBatchRequest(chunk(chunks, this.batchSize)))).flat();
results.push(...chunksResult);
txCount += chunks.length;
if (typeof this.onProgress === 'function') {
this.onProgress({ percentage: txCount / txs.length, currentIndex: txCount, totalIndex: txs.length });
}
}
return results;
}
}
export interface BatchEventServiceConstructor {
provider: Provider;
contract: BaseContract;
onProgress?: BatchEventOnProgress;
concurrencySize?: number;
blocksPerRequest?: number;
shouldRetry?: boolean;
retryMax?: number;
retryOn?: number;
}
export type BatchEventOnProgress = ({
percentage,
type,
fromBlock,
toBlock,
count,
}: {
percentage: number;
type?: ContractEventName;
fromBlock?: number;
toBlock?: number;
count?: number;
}) => void;
// To enable iteration only numbers are accepted for fromBlock input
export type EventInput = {
fromBlock: number;
toBlock: number;
type: ContractEventName;
};
/**
* Fetch events from web3 provider on bulk
*/
export class BatchEventsService {
provider: Provider;
contract: BaseContract;
onProgress?: BatchEventOnProgress;
concurrencySize: number;
blocksPerRequest: number;
shouldRetry: boolean;
retryMax: number;
retryOn: number;
constructor({
provider,
contract,
onProgress,
concurrencySize = 10,
blocksPerRequest = 2000,
shouldRetry = true,
retryMax = 5,
retryOn = 500,
}: BatchEventServiceConstructor) {
this.provider = provider;
this.contract = contract;
this.onProgress = onProgress;
this.concurrencySize = concurrencySize;
this.blocksPerRequest = blocksPerRequest;
this.shouldRetry = shouldRetry;
this.retryMax = retryMax;
this.retryOn = retryOn;
}
async getPastEvents({ fromBlock, toBlock, type }: EventInput): Promise<EventLog[]> {
let err;
let retries = 0;
// eslint-disable-next-line no-unmodified-loop-condition
while ((!this.shouldRetry && retries === 0) || (this.shouldRetry && retries < this.retryMax)) {
try {
return (await this.contract.queryFilter(type, fromBlock, toBlock)) as EventLog[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (e: any) {
err = e;
retries++;
// If provider.getBlockNumber returned last block that isn't accepted (happened on Avalanche/Gnosis),
// get events to last accepted block
if (e.message.includes('after last accepted block')) {
const acceptedBlock = parseInt(e.message.split('after last accepted block ')[1]);
toBlock = acceptedBlock;
}
// retry on 0.5 seconds
await sleep(this.retryOn);
}
}
throw err;
}
createBatchRequest(batchArray: EventInput[]): Promise<EventLog[]>[] {
return batchArray.map(async (event: EventInput, index: number) => {
await sleep(20 * index);
return this.getPastEvents(event);
});
}
async getBatchEvents({ fromBlock, toBlock, type = '*' }: EventInput): Promise<EventLog[]> {
if (!toBlock) {
toBlock = await this.provider.getBlockNumber();
}
const eventsToSync = [];
for (let i = fromBlock; i < toBlock; i += this.blocksPerRequest) {
const j = i + this.blocksPerRequest - 1 > toBlock ? toBlock : i + this.blocksPerRequest - 1;
eventsToSync.push({ fromBlock: i, toBlock: j, type });
}
const events = [];
const eventChunk = chunk(eventsToSync, this.concurrencySize);
let chunkCount = 0;
for (const chunk of eventChunk) {
chunkCount++;
const fetchedEvents = (await Promise.all(this.createBatchRequest(chunk))).flat();
events.push(...fetchedEvents);
if (typeof this.onProgress === 'function') {
this.onProgress({
percentage: chunkCount / eventChunk.length,
type,
fromBlock: chunk[0].fromBlock,
toBlock: chunk[chunk.length - 1].toBlock,
count: fetchedEvents.length,
});
}
}
return events;
}
}

146
src/data.ts Normal file
View File

@ -0,0 +1,146 @@
import path from 'path';
import { stat, mkdir, readFile, writeFile } from 'fs/promises';
import { zip, unzip, AsyncZippable, Unzipped } from 'fflate';
import { BaseEvents, MinimalEvents } from './events';
export async function existsAsync(fileOrDir: string): Promise<boolean> {
try {
await stat(fileOrDir);
return true;
} catch {
return false;
}
}
export function zipAsync(file: AsyncZippable): Promise<Uint8Array> {
return new Promise((res, rej) => {
zip(file, { mtime: new Date('1/1/1980') }, (err, data) => {
if (err) {
rej(err);
return;
}
res(data);
});
});
}
export function unzipAsync(data: Uint8Array): Promise<Unzipped> {
return new Promise((res, rej) => {
unzip(data, {}, (err, data) => {
if (err) {
rej(err);
return;
}
res(data);
});
});
}
export async function saveUserFile({
fileName,
userDirectory,
dataString,
}: {
fileName: string;
userDirectory: string;
dataString: string;
}) {
fileName = fileName.toLowerCase();
const filePath = path.join(userDirectory, fileName);
const payload = await zipAsync({
[fileName]: new TextEncoder().encode(dataString),
});
if (!(await existsAsync(userDirectory))) {
await mkdir(userDirectory, { recursive: true });
}
await writeFile(filePath + '.zip', payload);
await writeFile(filePath, dataString);
}
export async function loadSavedEvents<T extends MinimalEvents>({
name,
userDirectory,
deployedBlock,
}: {
name: string;
userDirectory: string;
deployedBlock: number;
}): Promise<BaseEvents<T>> {
const filePath = path.join(userDirectory, `${name}.json`.toLowerCase());
if (!(await existsAsync(filePath))) {
return {
events: [] as T[],
lastBlock: null,
};
}
try {
const events = JSON.parse(await readFile(filePath, { encoding: 'utf8' })) as T[];
return {
events,
lastBlock: events && events.length ? events[events.length - 1].blockNumber : deployedBlock,
};
} catch (err) {
console.log('Method loadSavedEvents has error');
console.log(err);
return {
events: [],
lastBlock: deployedBlock,
};
}
}
export async function download({ name, cacheDirectory }: { name: string; cacheDirectory: string }) {
const fileName = `${name}.json`.toLowerCase();
const zipName = `${fileName}.zip`;
const zipPath = path.join(cacheDirectory, zipName);
const data = await readFile(zipPath);
const { [fileName]: content } = await unzipAsync(data);
return new TextDecoder().decode(content);
}
export async function loadCachedEvents<T extends MinimalEvents>({
name,
cacheDirectory,
deployedBlock,
}: {
name: string;
cacheDirectory: string;
deployedBlock: number;
}): Promise<BaseEvents<T>> {
try {
const module = await download({ cacheDirectory, name });
if (module) {
const events = JSON.parse(module);
const lastBlock = events && events.length ? events[events.length - 1].blockNumber : deployedBlock;
return {
events,
lastBlock,
};
}
return {
events: [],
lastBlock: deployedBlock,
};
} catch (err) {
console.log('Method loadCachedEvents has error');
console.log(err);
return {
events: [],
lastBlock: deployedBlock,
};
}
}

247
src/deposits.ts Normal file
View File

@ -0,0 +1,247 @@
import { bnToBytes, bytesToBN, leBuff2Int, leInt2Buff, rBigInt, toFixedHex } from './utils';
import { buffPedersenHash } from './pedersen';
import type { NetIdType } from './networkConfig';
export type DepositType = {
currency: string;
amount: string;
netId: NetIdType;
};
export type createDepositParams = {
nullifier: bigint;
secret: bigint;
};
export type createDepositObject = {
preimage: Uint8Array;
noteHex: string;
commitment: bigint;
commitmentHex: string;
nullifierHash: bigint;
nullifierHex: string;
};
export type createNoteParams = DepositType & {
nullifier?: bigint;
secret?: bigint;
};
export type parsedNoteExec = DepositType & {
note: string;
};
export type depositTx = {
from: string;
transactionHash: string;
};
export type withdrawalTx = {
to: string;
transactionHash: string;
};
export async function createDeposit({ nullifier, secret }: createDepositParams): Promise<createDepositObject> {
const preimage = new Uint8Array([...leInt2Buff(nullifier), ...leInt2Buff(secret)]);
const noteHex = toFixedHex(bytesToBN(preimage), 62);
const commitment = BigInt(await buffPedersenHash(preimage));
const commitmentHex = toFixedHex(commitment);
const nullifierHash = BigInt(await buffPedersenHash(leInt2Buff(nullifier)));
const nullifierHex = toFixedHex(nullifierHash);
return {
preimage,
noteHex,
commitment,
commitmentHex,
nullifierHash,
nullifierHex,
};
}
export interface DepositConstructor {
currency: string;
amount: string;
netId: NetIdType;
nullifier: bigint;
secret: bigint;
note: string;
noteHex: string;
invoice: string;
commitmentHex: string;
nullifierHex: string;
}
export class Deposit {
currency: string;
amount: string;
netId: NetIdType;
nullifier: bigint;
secret: bigint;
note: string;
noteHex: string;
invoice: string;
commitmentHex: string;
nullifierHex: string;
constructor({
currency,
amount,
netId,
nullifier,
secret,
note,
noteHex,
invoice,
commitmentHex,
nullifierHex,
}: DepositConstructor) {
this.currency = currency;
this.amount = amount;
this.netId = netId;
this.nullifier = nullifier;
this.secret = secret;
this.note = note;
this.noteHex = noteHex;
this.invoice = invoice;
this.commitmentHex = commitmentHex;
this.nullifierHex = nullifierHex;
}
toString() {
return JSON.stringify(
{
currency: this.currency,
amount: this.amount,
netId: this.netId,
nullifier: this.nullifier,
secret: this.secret,
note: this.note,
noteHex: this.noteHex,
invoice: this.invoice,
commitmentHex: this.commitmentHex,
nullifierHex: this.nullifierHex,
},
null,
2,
);
}
static async createNote({ currency, amount, netId, nullifier, secret }: createNoteParams): Promise<Deposit> {
if (!nullifier) {
nullifier = rBigInt(31);
}
if (!secret) {
secret = rBigInt(31);
}
const depositObject = await createDeposit({
nullifier,
secret,
});
const newDeposit = new Deposit({
currency: currency.toLowerCase(),
amount: amount,
netId,
note: `tornado-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.noteHex}`,
noteHex: depositObject.noteHex,
invoice: `tornadoInvoice-${currency.toLowerCase()}-${amount}-${netId}-${depositObject.commitmentHex}`,
nullifier: nullifier,
secret: secret,
commitmentHex: depositObject.commitmentHex,
nullifierHex: depositObject.nullifierHex,
});
return newDeposit;
}
static async parseNote(noteString: string): Promise<Deposit> {
const noteRegex = /tornado-(?<currency>\w+)-(?<amount>[\d.]+)-(?<netId>\d+)-0x(?<note>[0-9a-fA-F]{124})/g;
const match = noteRegex.exec(noteString);
if (!match) {
throw new Error('The note has invalid format');
}
const matchGroup = match?.groups as unknown as parsedNoteExec;
const currency = matchGroup.currency.toLowerCase();
const amount = matchGroup.amount;
const netId = Number(matchGroup.netId);
const bytes = bnToBytes('0x' + matchGroup.note);
const nullifier = BigInt(leBuff2Int(bytes.slice(0, 31)).toString());
const secret = BigInt(leBuff2Int(bytes.slice(31, 62)).toString());
const depositObject = await createDeposit({ nullifier, secret });
const invoice = `tornadoInvoice-${currency}-${amount}-${netId}-${depositObject.commitmentHex}`;
const newDeposit = new Deposit({
currency,
amount,
netId,
note: noteString,
noteHex: depositObject.noteHex,
invoice,
nullifier,
secret,
commitmentHex: depositObject.commitmentHex,
nullifierHex: depositObject.nullifierHex,
});
return newDeposit;
}
}
export type parsedInvoiceExec = DepositType & {
commitment: string;
};
export class Invoice {
currency: string;
amount: string;
netId: NetIdType;
commitment: string;
invoice: string;
constructor(invoiceString: string) {
const invoiceRegex =
/tornadoInvoice-(?<currency>\w+)-(?<amount>[\d.]+)-(?<netId>\d+)-0x(?<commitment>[0-9a-fA-F]{64})/g;
const match = invoiceRegex.exec(invoiceString);
if (!match) {
throw new Error('The note has invalid format');
}
const matchGroup = match?.groups as unknown as parsedInvoiceExec;
const currency = matchGroup.currency.toLowerCase();
const amount = matchGroup.amount;
const netId = Number(matchGroup.netId);
this.currency = currency;
this.amount = amount;
this.netId = netId;
this.commitment = '0x' + matchGroup.commitment;
this.invoice = invoiceString;
}
toString() {
return JSON.stringify(
{
currency: this.currency,
amount: this.amount,
netId: this.netId,
commitment: this.commitment,
invoice: this.invoice,
},
null,
2,
);
}
}

189
src/encryptedNotes.ts Normal file
View File

@ -0,0 +1,189 @@
import { getEncryptionPublicKey, encrypt, decrypt, EthEncryptedData } from '@metamask/eth-sig-util';
import { Echoer } from '@tornado/contracts';
import { Wallet, computeAddress, getAddress } from 'ethers';
import { crypto, base64ToBytes, bytesToBase64, bytesToHex, hexToBytes, toFixedHex, concatBytes } from './utils';
import { EchoEvents, EncryptedNotesEvents } from './events';
import type { NetIdType } from './networkConfig';
export interface NoteToEncrypt {
address: string;
noteHex: string;
}
export interface DecryptedNotes {
blockNumber: number;
address: string;
noteHex: string;
}
export function packEncryptedMessage({ nonce, ephemPublicKey, ciphertext }: EthEncryptedData) {
const nonceBuf = toFixedHex(bytesToHex(base64ToBytes(nonce)), 24);
const ephemPublicKeyBuf = toFixedHex(bytesToHex(base64ToBytes(ephemPublicKey)), 32);
const ciphertextBuf = bytesToHex(base64ToBytes(ciphertext));
const messageBuff = concatBytes(hexToBytes(nonceBuf), hexToBytes(ephemPublicKeyBuf), hexToBytes(ciphertextBuf));
return bytesToHex(messageBuff);
}
export function unpackEncryptedMessage(encryptedMessage: string) {
const messageBuff = hexToBytes(encryptedMessage);
const nonceBuf = bytesToBase64(messageBuff.slice(0, 24));
const ephemPublicKeyBuf = bytesToBase64(messageBuff.slice(24, 56));
const ciphertextBuf = bytesToBase64(messageBuff.slice(56));
return {
messageBuff: bytesToHex(messageBuff),
version: 'x25519-xsalsa20-poly1305',
nonce: nonceBuf,
ephemPublicKey: ephemPublicKeyBuf,
ciphertext: ciphertextBuf,
} as EthEncryptedData & {
messageBuff: string;
};
}
export interface NoteAccountConstructor {
netId: NetIdType;
blockNumber?: number;
// hex
recoveryKey?: string;
Echoer: Echoer;
}
export class NoteAccount {
netId: NetIdType;
blockNumber?: number;
// Dedicated 32 bytes private key only used for note encryption, backed up to an Echoer and local for future derivation
// Note that unlike the private key it shouldn't have the 0x prefix
recoveryKey: string;
// Address derived from recoveryKey, only used for frontend UI
recoveryAddress: string;
// Note encryption public key derived from recoveryKey
recoveryPublicKey: string;
Echoer: Echoer;
constructor({ netId, blockNumber, recoveryKey, Echoer }: NoteAccountConstructor) {
if (!recoveryKey) {
recoveryKey = bytesToHex(crypto.getRandomValues(new Uint8Array(32))).slice(2);
}
this.netId = Math.floor(Number(netId));
this.blockNumber = blockNumber;
this.recoveryKey = recoveryKey;
this.recoveryAddress = computeAddress('0x' + recoveryKey);
this.recoveryPublicKey = getEncryptionPublicKey(recoveryKey);
this.Echoer = Echoer;
}
/**
* Intends to mock eth_getEncryptionPublicKey behavior from MetaMask
* In order to make the recoveryKey retrival from Echoer possible from the bare private key
*/
static getWalletPublicKey(wallet: Wallet) {
let { privateKey } = wallet;
if (privateKey.startsWith('0x')) {
privateKey = privateKey.replace('0x', '');
}
// Should return base64 encoded public key
return getEncryptionPublicKey(privateKey);
}
// This function intends to provide an encrypted value of recoveryKey for an on-chain Echoer backup purpose
// Thus, the pubKey should be derived by a Wallet instance or from Web3 wallets
// pubKey: base64 encoded 32 bytes key from https://docs.metamask.io/wallet/reference/eth_getencryptionpublickey/
getEncryptedAccount(walletPublicKey: string) {
const encryptedData = encrypt({
publicKey: walletPublicKey,
data: this.recoveryKey,
version: 'x25519-xsalsa20-poly1305',
});
const data = packEncryptedMessage(encryptedData);
return {
// Use this later to save hexPrivateKey generated with
// Buffer.from(JSON.stringify(encryptedData)).toString('hex')
// As we don't use buffer with this library we should leave UI to do the rest
encryptedData,
// Data that could be used as an echo(data) params
data,
};
}
/**
* Decrypt Echoer backuped note encryption account with private keys
*/
decryptAccountsWithWallet(wallet: Wallet, events: EchoEvents[]): NoteAccount[] {
let { privateKey } = wallet;
if (privateKey.startsWith('0x')) {
privateKey = privateKey.replace('0x', '');
}
const decryptedEvents = [];
for (const event of events) {
try {
const unpackedMessage = unpackEncryptedMessage(event.encryptedAccount);
const recoveryKey = decrypt({
encryptedData: unpackedMessage,
privateKey,
});
decryptedEvents.push(
new NoteAccount({
netId: this.netId,
blockNumber: event.blockNumber,
recoveryKey,
Echoer: this.Echoer,
}),
);
} catch {
// decryption may fail for invalid accounts
continue;
}
}
return decryptedEvents;
}
decryptNotes(events: EncryptedNotesEvents[]): DecryptedNotes[] {
const decryptedEvents = [];
for (const event of events) {
try {
const unpackedMessage = unpackEncryptedMessage(event.encryptedNote);
const [address, noteHex] = decrypt({
encryptedData: unpackedMessage,
privateKey: this.recoveryKey,
}).split('-');
decryptedEvents.push({
blockNumber: event.blockNumber,
address: getAddress(address),
noteHex,
});
} catch {
// decryption may fail for foreign notes
continue;
}
}
return decryptedEvents;
}
encryptNote({ address, noteHex }: NoteToEncrypt) {
const encryptedData = encrypt({
publicKey: this.recoveryPublicKey,
data: `${address}-${noteHex}`,
version: 'x25519-xsalsa20-poly1305',
});
return packEncryptedMessage(encryptedData);
}
}

792
src/events/base.ts Normal file
View File

@ -0,0 +1,792 @@
import { BaseContract, Provider, EventLog, TransactionResponse, getAddress, Block, ContractEventName } from 'ethers';
import type {
Tornado,
TornadoRouter,
TornadoProxyLight,
Governance,
RelayerRegistry,
Echoer,
} from '@tornado/contracts';
import * as graph from '../graphql';
import {
BatchEventsService,
BatchBlockService,
BatchTransactionService,
BatchEventOnProgress,
BatchBlockOnProgress,
} from '../batch';
import { fetchDataOptions } from '../providers';
import type { NetIdType } from '../networkConfig';
import type {
BaseEvents,
MinimalEvents,
DepositsEvents,
WithdrawalsEvents,
EncryptedNotesEvents,
AllGovernanceEvents,
GovernanceProposalCreatedEvents,
GovernanceVotedEvents,
GovernanceDelegatedEvents,
GovernanceUndelegatedEvents,
RegistersEvents,
BaseGraphEvents,
EchoEvents,
} from './types';
export const DEPOSIT = 'deposit';
export const WITHDRAWAL = 'withdrawal';
export type BaseEventsServiceConstructor = {
netId: NetIdType;
provider: Provider;
graphApi?: string;
subgraphName?: string;
contract: BaseContract;
type?: string;
deployedBlock?: number;
fetchDataOptions?: fetchDataOptions;
};
export type BatchGraphOnProgress = ({
type,
fromBlock,
toBlock,
count,
}: {
type?: ContractEventName;
fromBlock?: number;
toBlock?: number;
count?: number;
}) => void;
export type BaseGraphParams = {
graphApi: string;
subgraphName: string;
fetchDataOptions?: fetchDataOptions;
onProgress?: BatchGraphOnProgress;
};
export class BaseEventsService<EventType extends MinimalEvents> {
netId: NetIdType;
provider: Provider;
graphApi?: string;
subgraphName?: string;
contract: BaseContract;
type: string;
deployedBlock: number;
batchEventsService: BatchEventsService;
fetchDataOptions?: fetchDataOptions;
constructor({
netId,
provider,
graphApi,
subgraphName,
contract,
type = '',
deployedBlock = 0,
fetchDataOptions,
}: BaseEventsServiceConstructor) {
this.netId = netId;
this.provider = provider;
this.graphApi = graphApi;
this.subgraphName = subgraphName;
this.fetchDataOptions = fetchDataOptions;
this.contract = contract;
this.type = type;
this.deployedBlock = deployedBlock;
this.batchEventsService = new BatchEventsService({
provider,
contract,
onProgress: this.updateEventProgress,
});
}
getInstanceName(): string {
return '';
}
getType(): string {
return this.type || '';
}
getGraphMethod(): string {
return '';
}
getGraphParams(): BaseGraphParams {
return {
graphApi: this.graphApi || '',
subgraphName: this.subgraphName || '',
fetchDataOptions: this.fetchDataOptions,
onProgress: this.updateGraphProgress,
};
}
/* eslint-disable @typescript-eslint/no-unused-vars */
updateEventProgress({ percentage, type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {}
updateBlockProgress({ percentage, currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {}
updateTransactionProgress({ percentage, currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {}
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchGraphOnProgress>[0]) {}
/* eslint-enable @typescript-eslint/no-unused-vars */
async formatEvents(events: EventLog[]): Promise<EventType[]> {
// eslint-disable-next-line no-return-await
return await new Promise((resolve) => resolve(events as unknown as EventType[]));
}
/**
* Get saved or cached events
*/
async getEventsFromDB(): Promise<BaseEvents<EventType>> {
return {
events: [],
lastBlock: null,
};
}
async getEventsFromCache(): Promise<BaseEvents<EventType>> {
return {
events: [],
lastBlock: null,
};
}
async getSavedEvents(): Promise<BaseEvents<EventType>> {
let cachedEvents = await this.getEventsFromDB();
if (!cachedEvents || !cachedEvents.events.length) {
cachedEvents = await this.getEventsFromCache();
}
return cachedEvents;
}
/**
* Get latest events
*/
async getEventsFromGraph({
fromBlock,
methodName = '',
}: {
fromBlock: number;
methodName?: string;
}): Promise<BaseEvents<EventType>> {
if (!this.graphApi || !this.subgraphName) {
return {
events: [],
lastBlock: fromBlock,
};
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { events, lastSyncBlock } = (await (graph as any)[methodName || this.getGraphMethod()]({
fromBlock,
...this.getGraphParams(),
})) as BaseGraphEvents<EventType>;
return {
events,
lastBlock: lastSyncBlock,
};
}
async getEventsFromRpc({
fromBlock,
toBlock,
}: {
fromBlock: number;
toBlock?: number;
}): Promise<BaseEvents<EventType>> {
try {
if (!toBlock) {
toBlock = await this.provider.getBlockNumber();
}
if (fromBlock >= toBlock) {
return {
events: [],
lastBlock: toBlock,
};
}
this.updateEventProgress({ percentage: 0, type: this.getType() });
const events = await this.formatEvents(
await this.batchEventsService.getBatchEvents({ fromBlock, toBlock, type: this.getType() }),
);
if (!events.length) {
return {
events,
lastBlock: toBlock,
};
}
return {
events,
lastBlock: toBlock,
};
} catch (err) {
console.log(err);
return {
events: [],
lastBlock: fromBlock,
};
}
}
async getLatestEvents({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<EventType>> {
const allEvents = [];
const graphEvents = await this.getEventsFromGraph({ fromBlock });
const lastSyncBlock =
graphEvents.lastBlock && graphEvents.lastBlock >= fromBlock ? graphEvents.lastBlock : fromBlock;
const rpcEvents = await this.getEventsFromRpc({ fromBlock: lastSyncBlock });
allEvents.push(...graphEvents.events);
allEvents.push(...rpcEvents.events);
const lastBlock = rpcEvents
? rpcEvents.lastBlock
: allEvents[allEvents.length - 1]
? allEvents[allEvents.length - 1].blockNumber
: fromBlock;
return {
events: allEvents,
lastBlock,
};
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
validateEvents({ events, lastBlock }: BaseEvents<EventType>) {}
/**
* Handle saving events
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async saveEvents({ events, lastBlock }: BaseEvents<EventType>) {}
/**
* Trigger saving and receiving latest events
*/
async updateEvents() {
const savedEvents = await this.getSavedEvents();
let fromBlock = this.deployedBlock;
if (savedEvents && savedEvents.lastBlock) {
fromBlock = savedEvents.lastBlock + 1;
}
const newEvents = await this.getLatestEvents({ fromBlock });
const eventSet = new Set();
let allEvents: EventType[] = [];
allEvents.push(...savedEvents.events);
allEvents.push(...newEvents.events);
allEvents = allEvents
.sort((a, b) => {
if (a.blockNumber === b.blockNumber) {
return a.logIndex - b.logIndex;
}
return a.blockNumber - b.blockNumber;
})
.filter(({ transactionHash, logIndex }) => {
const eventKey = `${transactionHash}_${logIndex}`;
const hasEvent = eventSet.has(eventKey);
eventSet.add(eventKey);
return !hasEvent;
});
const lastBlock = newEvents
? newEvents.lastBlock
: allEvents[allEvents.length - 1]
? allEvents[allEvents.length - 1].blockNumber
: null;
this.validateEvents({ events: allEvents, lastBlock });
await this.saveEvents({ events: allEvents, lastBlock });
return {
events: allEvents,
lastBlock,
};
}
}
export type BaseDepositsServiceConstructor = {
netId: NetIdType;
provider: Provider;
graphApi?: string;
subgraphName?: string;
Tornado: Tornado;
type: string;
amount: string;
currency: string;
deployedBlock?: number;
fetchDataOptions?: fetchDataOptions;
};
export type DepositsGraphParams = BaseGraphParams & {
amount: string;
currency: string;
};
export class BaseDepositsService extends BaseEventsService<DepositsEvents | WithdrawalsEvents> {
amount: string;
currency: string;
batchTransactionService: BatchTransactionService;
batchBlockService: BatchBlockService;
constructor({
netId,
provider,
graphApi,
subgraphName,
Tornado,
type,
amount,
currency,
deployedBlock,
fetchDataOptions,
}: BaseDepositsServiceConstructor) {
super({ netId, provider, graphApi, subgraphName, contract: Tornado, type, deployedBlock, fetchDataOptions });
this.amount = amount;
this.currency = currency;
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress,
});
this.batchBlockService = new BatchBlockService({
provider,
onProgress: this.updateBlockProgress,
});
}
getInstanceName(): string {
return `${this.getType().toLowerCase()}s_${this.netId}_${this.currency}_${this.amount}`;
}
getGraphMethod(): string {
return `getAll${this.getType()}s`;
}
getGraphParams(): DepositsGraphParams {
return {
graphApi: this.graphApi || '',
subgraphName: this.subgraphName || '',
amount: this.amount,
currency: this.currency,
fetchDataOptions: this.fetchDataOptions,
onProgress: this.updateGraphProgress,
};
}
async formatEvents(events: EventLog[]): Promise<(DepositsEvents | WithdrawalsEvents)[]> {
const type = this.getType().toLowerCase();
if (type === DEPOSIT) {
const formattedEvents = events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { commitment, leafIndex, timestamp } = args;
return {
blockNumber,
logIndex,
transactionHash,
commitment: commitment as string,
leafIndex: Number(leafIndex),
timestamp: Number(timestamp),
};
});
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(formattedEvents.map(({ transactionHash }) => transactionHash)),
]);
return formattedEvents.map((event) => {
const { from } = txs.find(({ hash }) => hash === event.transactionHash) as TransactionResponse;
return {
...event,
from,
};
});
} else {
const formattedEvents = events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { nullifierHash, to, fee } = args;
return {
blockNumber,
logIndex,
transactionHash,
nullifierHash: String(nullifierHash),
to: getAddress(to),
fee: String(fee),
};
});
const blocks = await this.batchBlockService.getBatchBlocks([
...new Set(formattedEvents.map(({ blockNumber }) => blockNumber)),
]);
return formattedEvents.map((event) => {
const { timestamp } = blocks.find(({ number }) => number === event.blockNumber) as Block;
return {
...event,
timestamp,
};
});
}
}
validateEvents({ events }: { events: (DepositsEvents | WithdrawalsEvents)[] }) {
if (events.length && this.getType().toLowerCase() === DEPOSIT) {
const lastEvent = events[events.length - 1] as DepositsEvents;
if (lastEvent.leafIndex !== events.length - 1) {
const errMsg = `Deposit events invalid wants ${events.length - 1} leafIndex have ${lastEvent.leafIndex}`;
throw new Error(errMsg);
}
}
}
}
export type BaseEchoServiceConstructor = {
netId: NetIdType;
provider: Provider;
graphApi?: string;
subgraphName?: string;
Echoer: Echoer;
deployedBlock?: number;
fetchDataOptions?: fetchDataOptions;
};
export class BaseEchoService extends BaseEventsService<EchoEvents> {
constructor({
netId,
provider,
graphApi,
subgraphName,
Echoer,
deployedBlock,
fetchDataOptions,
}: BaseEchoServiceConstructor) {
super({ netId, provider, graphApi, subgraphName, contract: Echoer, deployedBlock, fetchDataOptions });
}
getInstanceName(): string {
return `echo_${this.netId}`;
}
getType(): string {
return 'Echo';
}
getGraphMethod(): string {
return 'getAllGraphEchoEvents';
}
async formatEvents(events: EventLog[]) {
return events
.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { who, data } = args;
if (who && data) {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
};
return {
...eventObjects,
address: who,
encryptedAccount: data,
};
}
})
.filter((e) => e) as EchoEvents[];
}
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<EchoEvents>> {
// TheGraph doesn't support our batch sync due to missing blockNumber field
if (!this.graphApi || this.graphApi.includes('api.thegraph.com')) {
return {
events: [],
lastBlock: fromBlock,
};
}
return super.getEventsFromGraph({ fromBlock });
}
}
export type BaseEncryptedNotesServiceConstructor = {
netId: NetIdType;
provider: Provider;
graphApi?: string;
subgraphName?: string;
Router: TornadoRouter | TornadoProxyLight;
deployedBlock?: number;
fetchDataOptions?: fetchDataOptions;
};
export class BaseEncryptedNotesService extends BaseEventsService<EncryptedNotesEvents> {
constructor({
netId,
provider,
graphApi,
subgraphName,
Router,
deployedBlock,
fetchDataOptions,
}: BaseEncryptedNotesServiceConstructor) {
super({ netId, provider, graphApi, subgraphName, contract: Router, deployedBlock, fetchDataOptions });
}
getInstanceName(): string {
return `encrypted_notes_${this.netId}`;
}
getType(): string {
return 'EncryptedNote';
}
getGraphMethod(): string {
return 'getAllEncryptedNotes';
}
async formatEvents(events: EventLog[]) {
return events
.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const { encryptedNote } = args;
if (encryptedNote) {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
};
return {
...eventObjects,
encryptedNote,
};
}
})
.filter((e) => e) as EncryptedNotesEvents[];
}
}
export type BaseGovernanceServiceConstructor = {
netId: NetIdType;
provider: Provider;
graphApi?: string;
subgraphName?: string;
Governance: Governance;
deployedBlock?: number;
fetchDataOptions?: fetchDataOptions;
};
export class BaseGovernanceService extends BaseEventsService<AllGovernanceEvents> {
batchTransactionService: BatchTransactionService;
constructor({
netId,
provider,
graphApi,
subgraphName,
Governance,
deployedBlock,
fetchDataOptions,
}: BaseGovernanceServiceConstructor) {
super({ netId, provider, graphApi, subgraphName, contract: Governance, deployedBlock, fetchDataOptions });
this.batchTransactionService = new BatchTransactionService({
provider,
onProgress: this.updateTransactionProgress,
});
}
getInstanceName() {
return `governance_${this.netId}`;
}
getType() {
return '*';
}
getGraphMethod() {
return 'getAllGovernanceEvents';
}
async formatEvents(events: EventLog[]): Promise<AllGovernanceEvents[]> {
const proposalEvents: GovernanceProposalCreatedEvents[] = [];
const votedEvents: GovernanceVotedEvents[] = [];
const delegatedEvents: GovernanceDelegatedEvents[] = [];
const undelegatedEvents: GovernanceUndelegatedEvents[] = [];
events.forEach(({ blockNumber, index: logIndex, transactionHash, args, eventName: event }) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
event,
};
if (event === 'ProposalCreated') {
const { id, proposer, target, startTime, endTime, description } = args;
proposalEvents.push({
...eventObjects,
id: Number(id),
proposer,
target,
startTime: Number(startTime),
endTime: Number(endTime),
description,
});
}
if (event === 'Voted') {
const { proposalId, voter, support, votes } = args;
votedEvents.push({
...eventObjects,
proposalId: Number(proposalId),
voter,
support,
votes,
from: '',
input: '',
});
}
if (event === 'Delegated') {
const { account, to: delegateTo } = args;
delegatedEvents.push({
...eventObjects,
account,
delegateTo,
});
}
if (event === 'Undelegated') {
const { account, from: delegateFrom } = args;
undelegatedEvents.push({
...eventObjects,
account,
delegateFrom,
});
}
});
if (votedEvents.length) {
this.updateTransactionProgress({ percentage: 0 });
const txs = await this.batchTransactionService.getBatchTransactions([
...new Set(votedEvents.map(({ transactionHash }) => transactionHash)),
]);
votedEvents.forEach((event, index) => {
// eslint-disable-next-line prefer-const
let { data: input, from } = txs.find((t) => t.hash === event.transactionHash) as TransactionResponse;
// Filter spammy txs
if (!input || input.length > 2048) {
input = '';
}
votedEvents[index].from = from;
votedEvents[index].input = input;
});
}
return [...proposalEvents, ...votedEvents, ...delegatedEvents, ...undelegatedEvents];
}
async getEventsFromGraph({ fromBlock }: { fromBlock: number }): Promise<BaseEvents<AllGovernanceEvents>> {
// TheGraph doesn't support governance subgraphs
if (!this.graphApi || !this.subgraphName || this.graphApi.includes('api.thegraph.com')) {
return {
events: [],
lastBlock: fromBlock,
};
}
return super.getEventsFromGraph({ fromBlock });
}
}
export type BaseRegistryServiceConstructor = {
netId: NetIdType;
provider: Provider;
graphApi?: string;
subgraphName?: string;
RelayerRegistry: RelayerRegistry;
deployedBlock?: number;
fetchDataOptions?: fetchDataOptions;
};
export class BaseRegistryService extends BaseEventsService<RegistersEvents> {
constructor({
netId,
provider,
graphApi,
subgraphName,
RelayerRegistry,
deployedBlock,
fetchDataOptions,
}: BaseRegistryServiceConstructor) {
super({ netId, provider, graphApi, subgraphName, contract: RelayerRegistry, deployedBlock, fetchDataOptions });
}
getInstanceName() {
return `registered_${this.netId}`;
}
// Name of type used for events
getType() {
return 'RelayerRegistered';
}
// Name of method used for graph
getGraphMethod() {
return 'getAllRegisters';
}
async formatEvents(events: EventLog[]) {
return events.map(({ blockNumber, index: logIndex, transactionHash, args }) => {
const eventObjects = {
blockNumber,
logIndex,
transactionHash,
};
return {
...eventObjects,
ensName: args.ensName,
relayerAddress: args.relayerAddress,
};
});
}
async fetchRelayers(): Promise<RegistersEvents[]> {
return (await this.updateEvents()).events;
}
}

3
src/events/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './types';
export * from './base';
export * from './node';

780
src/events/node.ts Normal file
View File

@ -0,0 +1,780 @@
import Table from 'cli-table3';
import moment from 'moment';
import { BatchBlockOnProgress, BatchEventOnProgress } from '../batch';
import { saveUserFile, loadSavedEvents, loadCachedEvents } from '../data';
import {
BaseDepositsService,
BaseEncryptedNotesService,
BaseGovernanceService,
BaseRegistryService,
BaseDepositsServiceConstructor,
BaseEncryptedNotesServiceConstructor,
BaseGovernanceServiceConstructor,
BaseRegistryServiceConstructor,
BaseEchoServiceConstructor,
BaseEchoService,
} from './base';
import type {
BaseEvents,
DepositsEvents,
WithdrawalsEvents,
EncryptedNotesEvents,
RegistersEvents,
AllGovernanceEvents,
EchoEvents,
} from './types';
export type NodeDepositsServiceConstructor = BaseDepositsServiceConstructor & {
cacheDirectory?: string;
userDirectory?: string;
};
export class NodeDepositsService extends BaseDepositsService {
cacheDirectory?: string;
userDirectory?: string;
constructor({
netId,
provider,
graphApi,
subgraphName,
Tornado,
type,
amount,
currency,
deployedBlock,
fetchDataOptions,
cacheDirectory,
userDirectory,
}: NodeDepositsServiceConstructor) {
super({
netId,
provider,
graphApi,
subgraphName,
Tornado,
type,
amount,
currency,
deployedBlock,
fetchDataOptions,
});
this.cacheDirectory = cacheDirectory;
this.userDirectory = userDirectory;
}
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
}
}
}
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
console.log(`Fetched ${currentIndex} deposit txs of ${totalIndex}`);
}
}
updateBlockProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
console.log(`Fetched ${currentIndex} withdrawal blocks of ${totalIndex}`);
}
}
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events from graph node count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
}
}
}
async getEventsFromDB() {
if (!this.userDirectory) {
console.log(
'Updating events for',
this.amount,
this.currency.toUpperCase(),
`${this.getType().toLowerCase()}s\n`,
);
console.log(`savedEvents count - ${0}`);
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const savedEvents = await loadSavedEvents<DepositsEvents | WithdrawalsEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log('Updating events for', this.amount, this.currency.toUpperCase(), `${this.getType().toLowerCase()}s\n`);
console.log(`savedEvents count - ${savedEvents.events.length}`);
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
return savedEvents;
}
async getEventsFromCache() {
if (!this.cacheDirectory) {
console.log(`cachedEvents count - ${0}`);
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const cachedEvents = await loadCachedEvents<DepositsEvents | WithdrawalsEvents>({
name: this.getInstanceName(),
cacheDirectory: this.cacheDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
return cachedEvents;
}
async saveEvents({ events, lastBlock }: BaseEvents<DepositsEvents | WithdrawalsEvents>) {
const instanceName = this.getInstanceName();
console.log('\ntotalEvents count - ', events.length);
console.log(
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
);
const eventTable = new Table();
eventTable.push(
[{ colSpan: 2, content: `${this.getType()}s`, hAlign: 'center' }],
['Instance', `${this.netId} chain ${this.amount} ${this.currency.toUpperCase()}`],
['Anonymity set', `${events.length} equal user ${this.getType().toLowerCase()}s`],
[{ colSpan: 2, content: `Latest ${this.getType().toLowerCase()}s` }],
...events
.slice(events.length - 10)
.reverse()
.map(({ timestamp }, index) => {
const eventIndex = events.length - index;
const eventTime = moment.unix(timestamp).fromNow();
return [eventIndex, eventTime];
}),
);
console.log(eventTable.toString() + '\n');
if (this.userDirectory) {
await saveUserFile({
fileName: instanceName + '.json',
userDirectory: this.userDirectory,
dataString: JSON.stringify(events, null, 2) + '\n',
});
}
}
}
export type NodeEchoServiceConstructor = BaseEchoServiceConstructor & {
cacheDirectory?: string;
userDirectory?: string;
};
export class NodeEchoService extends BaseEchoService {
cacheDirectory?: string;
userDirectory?: string;
constructor({
netId,
provider,
graphApi,
subgraphName,
Echoer,
deployedBlock,
fetchDataOptions,
cacheDirectory,
userDirectory,
}: NodeEchoServiceConstructor) {
super({
netId,
provider,
graphApi,
subgraphName,
Echoer,
deployedBlock,
fetchDataOptions,
});
this.cacheDirectory = cacheDirectory;
this.userDirectory = userDirectory;
}
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
}
}
}
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events from graph node count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
}
}
}
async getEventsFromDB() {
if (!this.userDirectory) {
console.log(`Updating events for ${this.netId} chain echo events\n`);
console.log(`savedEvents count - ${0}`);
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const savedEvents = await loadSavedEvents<EchoEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`Updating events for ${this.netId} chain echo events\n`);
console.log(`savedEvents count - ${savedEvents.events.length}`);
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
return savedEvents;
}
async getEventsFromCache() {
if (!this.cacheDirectory) {
console.log(`cachedEvents count - ${0}`);
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const cachedEvents = await loadCachedEvents<EchoEvents>({
name: this.getInstanceName(),
cacheDirectory: this.cacheDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
return cachedEvents;
}
async saveEvents({ events, lastBlock }: BaseEvents<EchoEvents>) {
const instanceName = this.getInstanceName();
console.log('\ntotalEvents count - ', events.length);
console.log(
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
);
const eventTable = new Table();
eventTable.push(
[{ colSpan: 2, content: 'Echo Accounts', hAlign: 'center' }],
['Network', `${this.netId} chain`],
['Events', `${events.length} events`],
[{ colSpan: 2, content: 'Latest events' }],
...events
.slice(events.length - 10)
.reverse()
.map(({ blockNumber }, index) => {
const eventIndex = events.length - index;
return [eventIndex, blockNumber];
}),
);
console.log(eventTable.toString() + '\n');
if (this.userDirectory) {
await saveUserFile({
fileName: instanceName + '.json',
userDirectory: this.userDirectory,
dataString: JSON.stringify(events, null, 2) + '\n',
});
}
}
}
export type NodeEncryptedNotesServiceConstructor = BaseEncryptedNotesServiceConstructor & {
cacheDirectory?: string;
userDirectory?: string;
};
export class NodeEncryptedNotesService extends BaseEncryptedNotesService {
cacheDirectory?: string;
userDirectory?: string;
constructor({
netId,
provider,
graphApi,
subgraphName,
Router,
deployedBlock,
fetchDataOptions,
cacheDirectory,
userDirectory,
}: NodeEncryptedNotesServiceConstructor) {
super({
netId,
provider,
graphApi,
subgraphName,
Router,
deployedBlock,
fetchDataOptions,
});
this.cacheDirectory = cacheDirectory;
this.userDirectory = userDirectory;
}
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
}
}
}
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events from graph node count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
}
}
}
async getEventsFromDB() {
if (!this.userDirectory) {
console.log(`Updating events for ${this.netId} chain encrypted events\n`);
console.log(`savedEvents count - ${0}`);
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const savedEvents = await loadSavedEvents<EncryptedNotesEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`Updating events for ${this.netId} chain encrypted events\n`);
console.log(`savedEvents count - ${savedEvents.events.length}`);
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
return savedEvents;
}
async getEventsFromCache() {
if (!this.cacheDirectory) {
console.log(`cachedEvents count - ${0}`);
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const cachedEvents = await loadCachedEvents<EncryptedNotesEvents>({
name: this.getInstanceName(),
cacheDirectory: this.cacheDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
return cachedEvents;
}
async saveEvents({ events, lastBlock }: BaseEvents<EncryptedNotesEvents>) {
const instanceName = this.getInstanceName();
console.log('\ntotalEvents count - ', events.length);
console.log(
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
);
const eventTable = new Table();
eventTable.push(
[{ colSpan: 2, content: 'Encrypted Notes', hAlign: 'center' }],
['Network', `${this.netId} chain`],
['Events', `${events.length} events`],
[{ colSpan: 2, content: 'Latest events' }],
...events
.slice(events.length - 10)
.reverse()
.map(({ blockNumber }, index) => {
const eventIndex = events.length - index;
return [eventIndex, blockNumber];
}),
);
console.log(eventTable.toString() + '\n');
if (this.userDirectory) {
await saveUserFile({
fileName: instanceName + '.json',
userDirectory: this.userDirectory,
dataString: JSON.stringify(events, null, 2) + '\n',
});
}
}
}
export type NodeGovernanceServiceConstructor = BaseGovernanceServiceConstructor & {
cacheDirectory?: string;
userDirectory?: string;
};
export class NodeGovernanceService extends BaseGovernanceService {
cacheDirectory?: string;
userDirectory?: string;
constructor({
netId,
provider,
graphApi,
subgraphName,
Governance,
deployedBlock,
fetchDataOptions,
cacheDirectory,
userDirectory,
}: NodeGovernanceServiceConstructor) {
super({
netId,
provider,
graphApi,
subgraphName,
Governance,
deployedBlock,
fetchDataOptions,
});
this.cacheDirectory = cacheDirectory;
this.userDirectory = userDirectory;
}
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
}
}
}
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events from graph node count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
}
}
}
updateTransactionProgress({ currentIndex, totalIndex }: Parameters<BatchBlockOnProgress>[0]) {
if (totalIndex) {
console.log(`Fetched ${currentIndex} governance txs of ${totalIndex}`);
}
}
async getEventsFromDB() {
if (!this.userDirectory) {
console.log(`Updating events for ${this.netId} chain governance events\n`);
console.log(`savedEvents count - ${0}`);
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const savedEvents = await loadSavedEvents<AllGovernanceEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`Updating events for ${this.netId} chain governance events\n`);
console.log(`savedEvents count - ${savedEvents.events.length}`);
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
return savedEvents;
}
async getEventsFromCache() {
if (!this.cacheDirectory) {
console.log(`cachedEvents count - ${0}`);
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const cachedEvents = await loadCachedEvents<AllGovernanceEvents>({
name: this.getInstanceName(),
cacheDirectory: this.cacheDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
return cachedEvents;
}
async saveEvents({ events, lastBlock }: BaseEvents<AllGovernanceEvents>) {
const instanceName = this.getInstanceName();
console.log('\ntotalEvents count - ', events.length);
console.log(
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
);
const eventTable = new Table();
eventTable.push(
[{ colSpan: 2, content: 'Governance Events', hAlign: 'center' }],
['Network', `${this.netId} chain`],
['Events', `${events.length} events`],
[{ colSpan: 2, content: 'Latest events' }],
...events
.slice(events.length - 10)
.reverse()
.map(({ blockNumber }, index) => {
const eventIndex = events.length - index;
return [eventIndex, blockNumber];
}),
);
console.log(eventTable.toString() + '\n');
if (this.userDirectory) {
await saveUserFile({
fileName: instanceName + '.json',
userDirectory: this.userDirectory,
dataString: JSON.stringify(events, null, 2) + '\n',
});
}
}
}
export type NodeRegistryServiceConstructor = BaseRegistryServiceConstructor & {
cacheDirectory?: string;
userDirectory?: string;
};
export class NodeRegistryService extends BaseRegistryService {
cacheDirectory?: string;
userDirectory?: string;
constructor({
netId,
provider,
graphApi,
subgraphName,
RelayerRegistry,
deployedBlock,
fetchDataOptions,
cacheDirectory,
userDirectory,
}: NodeRegistryServiceConstructor) {
super({
netId,
provider,
graphApi,
subgraphName,
RelayerRegistry,
deployedBlock,
fetchDataOptions,
});
this.cacheDirectory = cacheDirectory;
this.userDirectory = userDirectory;
}
updateEventProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from ${fromBlock} to ${toBlock}\n`);
}
}
}
updateGraphProgress({ type, fromBlock, toBlock, count }: Parameters<BatchEventOnProgress>[0]) {
if (toBlock) {
console.log(`fromBlock - ${fromBlock}`);
console.log(`toBlock - ${toBlock}`);
if (count) {
console.log(`downloaded ${type} events from graph node count - ${count}`);
console.log('____________________________________________');
console.log(`Fetched ${type} events from graph node ${fromBlock} to ${toBlock}\n`);
}
}
}
async getEventsFromDB() {
if (!this.userDirectory) {
console.log(`Updating events for ${this.netId} chain registry events\n`);
console.log(`savedEvents count - ${0}`);
console.log(`savedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const savedEvents = await loadSavedEvents<RegistersEvents>({
name: this.getInstanceName(),
userDirectory: this.userDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`Updating events for ${this.netId} chain registry events\n`);
console.log(`savedEvents count - ${savedEvents.events.length}`);
console.log(`savedEvents lastBlock - ${savedEvents.lastBlock}\n`);
return savedEvents;
}
async getEventsFromCache() {
if (!this.cacheDirectory) {
console.log(`cachedEvents count - ${0}`);
console.log(`cachedEvents lastBlock - ${this.deployedBlock}\n`);
return {
events: [],
lastBlock: this.deployedBlock,
};
}
const cachedEvents = await loadCachedEvents<RegistersEvents>({
name: this.getInstanceName(),
cacheDirectory: this.cacheDirectory,
deployedBlock: this.deployedBlock,
});
console.log(`cachedEvents count - ${cachedEvents.events.length}`);
console.log(`cachedEvents lastBlock - ${cachedEvents.lastBlock}\n`);
return cachedEvents;
}
async saveEvents({ events, lastBlock }: BaseEvents<RegistersEvents>) {
const instanceName = this.getInstanceName();
console.log('\ntotalEvents count - ', events.length);
console.log(
`totalEvents lastBlock - ${events[events.length - 1] ? events[events.length - 1].blockNumber : lastBlock}\n`,
);
const eventTable = new Table();
eventTable.push(
[{ colSpan: 2, content: 'Registered Relayers', hAlign: 'center' }],
['Network', `${this.netId} chain`],
['Events', `${events.length} events`],
[{ colSpan: 2, content: 'Latest events' }],
...events
.slice(events.length - 10)
.reverse()
.map(({ blockNumber }, index) => {
const eventIndex = events.length - index;
return [eventIndex, blockNumber];
}),
);
console.log(eventTable.toString() + '\n');
if (this.userDirectory) {
await saveUserFile({
fileName: instanceName + '.json',
userDirectory: this.userDirectory,
dataString: JSON.stringify(events, null, 2) + '\n',
});
}
}
}

80
src/events/types.ts Normal file
View File

@ -0,0 +1,80 @@
import { RelayerParams } from '../relayerClient';
export interface BaseEvents<T> {
events: T[];
lastBlock: number | null;
}
export interface BaseGraphEvents<T> {
events: T[];
lastSyncBlock: number;
}
export interface MinimalEvents {
blockNumber: number;
logIndex: number;
transactionHash: string;
}
export type GovernanceEvents = MinimalEvents & {
event: string;
};
export type GovernanceProposalCreatedEvents = GovernanceEvents & {
id: number;
proposer: string;
target: string;
startTime: number;
endTime: number;
description: string;
};
export type GovernanceVotedEvents = GovernanceEvents & {
proposalId: number;
voter: string;
support: boolean;
votes: string;
from: string;
input: string;
};
export type GovernanceDelegatedEvents = GovernanceEvents & {
account: string;
delegateTo: string;
};
export type GovernanceUndelegatedEvents = GovernanceEvents & {
account: string;
delegateFrom: string;
};
export type AllGovernanceEvents =
| GovernanceProposalCreatedEvents
| GovernanceVotedEvents
| GovernanceDelegatedEvents
| GovernanceUndelegatedEvents;
export type RegistersEvents = MinimalEvents & RelayerParams;
export type DepositsEvents = MinimalEvents & {
commitment: string;
leafIndex: number;
timestamp: number;
from: string;
};
export type WithdrawalsEvents = MinimalEvents & {
nullifierHash: string;
to: string;
fee: string;
timestamp: number;
};
export type EchoEvents = MinimalEvents & {
address: string;
encryptedAccount: string;
};
export type EncryptedNotesEvents = MinimalEvents & {
encryptedNote: string;
};

124
src/fees.ts Normal file
View File

@ -0,0 +1,124 @@
import { Transaction, parseUnits } from 'ethers';
import type { BigNumberish, TransactionLike } from 'ethers';
import { OvmGasPriceOracle } from './typechain';
const DUMMY_ADDRESS = '0x1111111111111111111111111111111111111111';
const DUMMY_NONCE = '0x1111111111111111111111111111111111111111111111111111111111111111';
const DUMMY_WITHDRAW_DATA =
'0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111';
/**
* Example:
*
* amountInWei (0.1 ETH) * tokenDecimals (18) * tokenPriceInWei (0.0008) = 125 TOKEN
*/
export function convertETHToTokenAmount(
amountInWei: BigNumberish,
tokenPriceInWei: BigNumberish,
tokenDecimals: number = 18,
): bigint {
const tokenDecimalsMultiplier = BigInt(10 ** Number(tokenDecimals));
return (BigInt(amountInWei) * tokenDecimalsMultiplier) / BigInt(tokenPriceInWei);
}
export interface RelayerFeeParams {
gasPrice: BigNumberish;
gasLimit?: BigNumberish;
l1Fee?: BigNumberish;
denomination: BigNumberish;
ethRefund: BigNumberish;
tokenPriceInWei: BigNumberish;
tokenDecimals: number;
relayerFeePercent?: number;
isEth?: boolean;
premiumPercent?: number;
}
export class TornadoFeeOracle {
ovmGasPriceOracle?: OvmGasPriceOracle;
constructor(ovmGasPriceOracle?: OvmGasPriceOracle) {
if (ovmGasPriceOracle) {
this.ovmGasPriceOracle = ovmGasPriceOracle;
}
}
/**
* Calculate L1 fee for op-stack chains
*
* This is required since relayers would pay the full transaction fees for users
*/
fetchL1OptimismFee(tx?: TransactionLike): Promise<bigint> {
if (!this.ovmGasPriceOracle) {
return new Promise((resolve) => resolve(BigInt(0)));
}
if (!tx) {
// this tx is only used to simulate bytes size of the encoded tx so has nothing to with the accuracy
// inspired by the old style classic-ui calculation
tx = {
type: 0,
gasLimit: 1_000_000,
nonce: Number(DUMMY_NONCE),
data: DUMMY_WITHDRAW_DATA,
gasPrice: parseUnits('1', 'gwei'),
from: DUMMY_ADDRESS,
to: DUMMY_ADDRESS,
};
}
return this.ovmGasPriceOracle.getL1Fee.staticCall(Transaction.from(tx).unsignedSerialized);
}
/**
* We don't need to distinguish default refunds by tokens since most users interact with other defi protocols after withdrawal
* So we default with 1M gas which is enough for two or three swaps
* Using 30 gwei for default but it is recommended to supply cached gasPrice value from the UI
*/
defaultEthRefund(gasPrice?: BigNumberish, gasLimit?: BigNumberish): bigint {
return (gasPrice ? BigInt(gasPrice) : parseUnits('30', 'gwei')) * BigInt(gasLimit || 1_000_000);
}
/**
* Calculates token amount for required ethRefund purchases required to calculate fees
*/
calculateTokenAmount(ethRefund: BigNumberish, tokenPriceInEth: BigNumberish, tokenDecimals?: number): bigint {
return convertETHToTokenAmount(ethRefund, tokenPriceInEth, tokenDecimals);
}
/**
* Warning: For tokens you need to check if the fees are above denomination
* (Usually happens for small denomination pool or if the gas price is high)
*/
calculateRelayerFee({
gasPrice,
gasLimit = 600_000,
l1Fee = 0,
denomination,
ethRefund = BigInt(0),
tokenPriceInWei,
tokenDecimals = 18,
relayerFeePercent = 0.33,
isEth = true,
premiumPercent = 20,
}: RelayerFeeParams): bigint {
const gasCosts = BigInt(gasPrice) * BigInt(gasLimit) + BigInt(l1Fee);
const relayerFee = (BigInt(denomination) * BigInt(Math.floor(10000 * relayerFeePercent))) / BigInt(10000 * 100);
if (isEth) {
// Add 20% premium
return ((gasCosts + relayerFee) * BigInt(premiumPercent ? 100 + premiumPercent : 100)) / BigInt(100);
}
const feeInEth = gasCosts + BigInt(ethRefund);
return (
((convertETHToTokenAmount(feeInEth, tokenPriceInWei, tokenDecimals) + relayerFee) *
BigInt(premiumPercent ? 100 + premiumPercent : 100)) /
BigInt(100)
);
}
}

1138
src/graphql/index.ts Normal file

File diff suppressed because it is too large Load Diff

197
src/graphql/queries.ts Normal file
View File

@ -0,0 +1,197 @@
export const GET_STATISTIC = `
query getStatistic($currency: String!, $amount: String!, $first: Int, $orderBy: BigInt, $orderDirection: String) {
deposits(first: $first, orderBy: $orderBy, orderDirection: $orderDirection, where: { currency: $currency, amount: $amount }) {
index
timestamp
blockNumber
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const _META = `
query getMeta {
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_REGISTERED = `
query getRegistered($first: Int, $fromBlock: Int) {
relayers(first: $first, orderBy: blockRegistration, orderDirection: asc, where: {
blockRegistration_gte: $fromBlock
}) {
id
address
ensName
blockRegistration
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_DEPOSITS = `
query getDeposits($currency: String!, $amount: String!, $first: Int, $fromBlock: Int) {
deposits(first: $first, orderBy: index, orderDirection: asc, where: {
amount: $amount,
currency: $currency,
blockNumber_gte: $fromBlock
}) {
id
blockNumber
commitment
index
timestamp
from
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_WITHDRAWALS = `
query getWithdrawals($currency: String!, $amount: String!, $first: Int, $fromBlock: Int!) {
withdrawals(first: $first, orderBy: blockNumber, orderDirection: asc, where: {
currency: $currency,
amount: $amount,
blockNumber_gte: $fromBlock
}) {
id
blockNumber
nullifier
to
fee
timestamp
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_NOTE_ACCOUNTS = `
query getNoteAccount($address: String!) {
noteAccounts(where: { address: $address }) {
id
index
address
encryptedAccount
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_ECHO_EVENTS = `
query getNoteAccounts($first: Int, $fromBlock: Int) {
noteAccounts(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
id
blockNumber
address
encryptedAccount
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_ENCRYPTED_NOTES = `
query getEncryptedNotes($first: Int, $fromBlock: Int) {
encryptedNotes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
blockNumber
index
transactionHash
encryptedNote
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_GOVERNANCE_EVENTS = `
query getGovernanceEvents($first: Int, $fromBlock: Int) {
proposals(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
blockNumber
logIndex
transactionHash
proposalId
proposer
target
startTime
endTime
description
}
votes(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
blockNumber
logIndex
transactionHash
proposalId
voter
support
votes
from
input
}
delegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
blockNumber
logIndex
transactionHash
account
delegateTo
}
undelegates(first: $first, orderBy: blockNumber, orderDirection: asc, where: { blockNumber_gte: $fromBlock }) {
blockNumber
logIndex
transactionHash
account
delegateFrom
}
_meta {
block {
number
}
hasIndexingErrors
}
}
`;
export const GET_GOVERNANCE_APY = `
stakeDailyBurns(first: 30, orderBy: date, orderDirection: desc) {
id
date
dailyAmountBurned
}
`;

22
src/index.ts Normal file
View File

@ -0,0 +1,22 @@
export * from './events';
export * from './graphql';
export * from './schemas';
export * from './typechain';
export * from './batch';
export * from './data';
export * from './deposits';
export * from './encryptedNotes';
export * from './fees';
export * from './merkleTree';
export * from './mimc';
export * from './multicall';
export * from './networkConfig';
export * from './parser';
export * from './pedersen';
export * from './prices';
export * from './providers';
export * from './relayerClient';
export * from './tokens';
export * from './treeCache';
export * from './utils';
export * from './websnark';

199
src/merkleTree.ts Normal file
View File

@ -0,0 +1,199 @@
import { Worker as NodeWorker } from 'worker_threads';
import { MerkleTree, PartialMerkleTree, Element, TreeEdge } from '@tornado/fixed-merkle-tree';
import type { Tornado } from '@tornado/contracts';
import { isNode, toFixedHex } from './utils';
import { mimc } from './mimc';
import type { DepositType } from './deposits';
import type { DepositsEvents } from './events';
import type { NetIdType } from './networkConfig';
export type MerkleTreeConstructor = DepositType & {
Tornado: Tornado;
commitmentHex?: string;
merkleTreeHeight?: number;
emptyElement?: string;
merkleWorkerPath?: string;
};
export class MerkleTreeService {
currency: string;
amount: string;
netId: NetIdType;
Tornado: Tornado;
commitmentHex?: string;
instanceName: string;
merkleTreeHeight: number;
emptyElement: string;
merkleWorkerPath?: string;
constructor({
netId,
amount,
currency,
Tornado,
commitmentHex,
merkleTreeHeight = 20,
emptyElement = '21663839004416932945382355908790599225266501822907911457504978515578255421292',
merkleWorkerPath,
}: MerkleTreeConstructor) {
const instanceName = `${netId}_${currency}_${amount}`;
this.currency = currency;
this.amount = amount;
this.netId = Number(netId);
this.Tornado = Tornado;
this.instanceName = instanceName;
this.commitmentHex = commitmentHex;
this.merkleTreeHeight = merkleTreeHeight;
this.emptyElement = emptyElement;
this.merkleWorkerPath = merkleWorkerPath;
}
async createTree(events: Element[]) {
const { hash: hashFunction } = await mimc.getHash();
if (this.merkleWorkerPath) {
console.log('Using merkleWorker\n');
try {
if (isNode) {
const merkleWorkerPromise = new Promise((resolve, reject) => {
const worker = new NodeWorker(this.merkleWorkerPath as string, {
workerData: {
merkleTreeHeight: this.merkleTreeHeight,
elements: events,
zeroElement: this.emptyElement,
},
});
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${code}`));
}
});
}) as Promise<string>;
return MerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
} else {
const merkleWorkerPromise = new Promise((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const worker = new (Worker as any)(this.merkleWorkerPath);
worker.onmessage = (e: { data: string }) => {
resolve(e.data);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
worker.onerror = (e: any) => {
reject(e);
};
worker.postMessage({
merkleTreeHeight: this.merkleTreeHeight,
elements: events,
zeroElement: this.emptyElement,
});
}) as Promise<string>;
return MerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
}
} catch (err) {
console.log('merkleWorker failed, falling back to synchronous merkle tree');
console.log(err);
}
}
return new MerkleTree(this.merkleTreeHeight, events, {
zeroElement: this.emptyElement,
hashFunction,
});
}
async createPartialTree({ edge, elements }: { edge: TreeEdge; elements: Element[] }) {
const { hash: hashFunction } = await mimc.getHash();
if (this.merkleWorkerPath) {
console.log('Using merkleWorker\n');
try {
if (isNode) {
const merkleWorkerPromise = new Promise((resolve, reject) => {
const worker = new NodeWorker(this.merkleWorkerPath as string, {
workerData: {
merkleTreeHeight: this.merkleTreeHeight,
edge,
elements,
zeroElement: this.emptyElement,
},
});
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0) {
reject(new Error(`Worker stopped with exit code ${code}`));
}
});
}) as Promise<string>;
return PartialMerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
} else {
const merkleWorkerPromise = new Promise((resolve, reject) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const worker = new (Worker as any)(this.merkleWorkerPath);
worker.onmessage = (e: { data: string }) => {
resolve(e.data);
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
worker.onerror = (e: any) => {
reject(e);
};
worker.postMessage({
merkleTreeHeight: this.merkleTreeHeight,
edge,
elements,
zeroElement: this.emptyElement,
});
}) as Promise<string>;
return PartialMerkleTree.deserialize(JSON.parse(await merkleWorkerPromise), hashFunction);
}
} catch (err) {
console.log('merkleWorker failed, falling back to synchronous merkle tree');
console.log(err);
}
}
return new PartialMerkleTree(this.merkleTreeHeight, edge, elements, {
zeroElement: this.emptyElement,
hashFunction,
});
}
async verifyTree(events: DepositsEvents[]) {
console.log(
`\nCreating deposit tree for ${this.netId} ${this.amount} ${this.currency.toUpperCase()} would take a while\n`,
);
console.time('Created tree in');
const tree = await this.createTree(events.map(({ commitment }) => commitment));
console.timeEnd('Created tree in');
console.log('');
const isKnownRoot = await this.Tornado.isKnownRoot(toFixedHex(BigInt(tree.root)));
if (!isKnownRoot) {
const errMsg = `Deposit Event ${this.netId} ${this.amount} ${this.currency} is invalid`;
throw new Error(errMsg);
}
return tree;
}
}

70
src/merkleTreeWorker.ts Normal file
View File

@ -0,0 +1,70 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import workerThreads from 'worker_threads';
import { MerkleTree, Element, TreeEdge, PartialMerkleTree } from '@tornado/fixed-merkle-tree';
import { mimc } from './mimc';
import { isNode } from './utils';
interface WorkData {
merkleTreeHeight: number;
edge?: TreeEdge;
elements: Element[];
zeroElement: string;
}
async function nodePostWork() {
const { hash: hashFunction } = await mimc.getHash();
const { merkleTreeHeight, edge, elements, zeroElement } = workerThreads.workerData as WorkData;
if (edge) {
const merkleTree = new PartialMerkleTree(merkleTreeHeight, edge, elements, {
zeroElement,
hashFunction,
});
(workerThreads.parentPort as workerThreads.MessagePort).postMessage(merkleTree.toString());
return;
}
const merkleTree = new MerkleTree(merkleTreeHeight, elements, {
zeroElement,
hashFunction,
});
(workerThreads.parentPort as workerThreads.MessagePort).postMessage(merkleTree.toString());
}
if (isNode && workerThreads) {
nodePostWork();
} else if (!isNode && typeof addEventListener === 'function' && typeof postMessage === 'function') {
addEventListener('message', async (e: any) => {
let data;
if (e.data) {
data = e.data;
} else {
data = e;
}
const { hash: hashFunction } = await mimc.getHash();
const { merkleTreeHeight, edge, elements, zeroElement } = data as WorkData;
if (edge) {
const merkleTree = new PartialMerkleTree(merkleTreeHeight, edge, elements, {
zeroElement,
hashFunction,
});
postMessage(merkleTree.toString());
return;
}
const merkleTree = new MerkleTree(merkleTreeHeight, elements, {
zeroElement,
hashFunction,
});
postMessage(merkleTree.toString());
});
} else {
throw new Error('This browser / environment does not support workers!');
}

28
src/mimc.ts Normal file
View File

@ -0,0 +1,28 @@
import { MimcSponge, buildMimcSponge } from 'circomlibjs';
import type { Element, HashFunction } from '@tornado/fixed-merkle-tree';
export class Mimc {
sponge?: MimcSponge;
hash?: HashFunction<Element>;
mimcPromise: Promise<void>;
constructor() {
this.mimcPromise = this.initMimc();
}
async initMimc() {
this.sponge = await buildMimcSponge();
this.hash = (left, right) => this.sponge?.F.toString(this.sponge?.multiHash([BigInt(left), BigInt(right)]));
}
async getHash() {
await this.mimcPromise;
return {
sponge: this.sponge,
hash: this.hash,
};
}
}
export const mimc = new Mimc();

37
src/multicall.ts Normal file
View File

@ -0,0 +1,37 @@
import { BaseContract, Interface } from 'ethers';
import { Multicall } from './typechain';
export interface Call3 {
contract?: BaseContract;
address?: string;
interface?: Interface;
name: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params?: any[];
allowFailure?: boolean;
}
export async function multicall(Multicall: Multicall, calls: Call3[]) {
const calldata = calls.map((call) => {
const target = (call.contract?.target || call.address) as string;
const callInterface = (call.contract?.interface || call.interface) as Interface;
return {
target,
callData: callInterface.encodeFunctionData(call.name, call.params),
allowFailure: call.allowFailure ?? false,
};
});
const returnData = await Multicall.aggregate3.staticCall(calldata);
const res = returnData.map((call, i) => {
const callInterface = (calls[i].contract?.interface || calls[i].interface) as Interface;
const [result, data] = call;
const decodeResult =
result && data && data !== '0x' ? callInterface.decodeFunctionResult(calls[i].name, data) : null;
return !decodeResult ? null : decodeResult.length === 1 ? decodeResult[0] : decodeResult;
});
return res;
}

790
src/networkConfig.ts Normal file
View File

@ -0,0 +1,790 @@
/**
* Type of default supported networks
*/
export enum NetId {
MAINNET = 1,
BSC = 56,
POLYGON = 137,
OPTIMISM = 10,
ARBITRUM = 42161,
GNOSIS = 100,
AVALANCHE = 43114,
SEPOLIA = 11155111,
}
export type NetIdType = NetId | number;
export interface RpcUrl {
name: string;
url: string;
}
export type RpcUrls = {
[key in string]: RpcUrl;
};
export interface SubgraphUrl {
name: string;
url: string;
}
export type SubgraphUrls = {
[key in string]: SubgraphUrl;
};
export type TornadoInstance = {
instanceAddress: {
[key in string]: string;
};
optionalInstances?: string[];
tokenAddress?: string;
tokenGasLimit?: number;
symbol: string;
decimals: number;
gasLimit?: number;
};
export type TokenInstances = {
[key in string]: TornadoInstance;
};
export type Config = {
rpcCallRetryAttempt?: number;
// Should be in gwei
gasPrices: {
// fallback gasPrice / maxFeePerGas value
instant: number;
fast?: number;
standard?: number;
low?: number;
// fallback EIP-1559 params
maxPriorityFeePerGas?: number;
};
nativeCurrency: string;
currencyName: string;
explorerUrl: string;
merkleTreeHeight: number;
emptyElement: string;
networkName: string;
deployedBlock: number;
rpcUrls: RpcUrls;
multicallContract: string;
routerContract: string;
echoContract: string;
offchainOracleContract?: string;
tornContract?: string;
governanceContract?: string;
stakingRewardsContract?: string;
registryContract?: string;
aggregatorContract?: string;
reverseRecordsContract?: string;
gasPriceOracleContract?: string;
gasStationApi?: string;
ovmGasPriceOracleContract?: string;
tornadoSubgraph: string;
registrySubgraph?: string;
governanceSubgraph?: string;
subgraphs: SubgraphUrls;
tokens: TokenInstances;
optionalTokens?: string[];
ensSubdomainKey: string;
// Should be in seconds
pollInterval: number;
constants: {
GOVERNANCE_BLOCK?: number;
NOTE_ACCOUNT_BLOCK?: number;
ENCRYPTED_NOTES_BLOCK?: number;
REGISTRY_BLOCK?: number;
// Should be in seconds
MINING_BLOCK_TIME?: number;
};
};
export type networkConfig = {
[key in NetIdType]: Config;
};
const theGraph = {
name: 'Hosted Graph',
url: 'https://api.thegraph.com',
};
const tornado = {
name: 'Tornado Subgraphs',
url: 'https://tornadocash-rpc.com',
};
export const defaultConfig: networkConfig = {
[NetId.MAINNET]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 80,
fast: 50,
standard: 25,
low: 8,
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: 'https://etherscan.io',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Ethereum Mainnet',
deployedBlock: 9116966,
rpcUrls: {
tornado: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com',
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
},
mevblockerRPC: {
name: 'MevblockerRPC',
url: 'https://rpc.mevblocker.io',
},
stackup: {
name: 'Stackup RPC',
url: 'https://public.stackup.sh/api/v1/node/ethereum-mainnet',
},
noderealRPC: {
name: 'NodeReal RPC',
url: 'https://eth-mainnet.nodereal.io/v1/1659dfb40aa24bbb8153a677b98064d7',
},
notadegenRPC: {
name: 'NotADegen RPC',
url: 'https://rpc.notadegen.com/eth',
},
keydonixRPC: {
name: 'Keydonix RPC',
url: 'https://ethereum.keydonix.com/v1/mainnet',
},
oneRPC: {
name: '1RPC',
url: 'https://1rpc.io/eth',
},
},
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b',
echoContract: '0x9B27DD5Bb15d42DC224FCD0B7caEbBe16161Df42',
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
tornContract: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
governanceContract: '0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce',
stakingRewardsContract: '0x5B3f656C80E8ddb9ec01Dd9018815576E9238c29',
registryContract: '0x58E8dCC13BE9780fC42E8723D8EaD4CF46943dF2',
aggregatorContract: '0xE8F47A78A6D52D317D0D2FFFac56739fE14D1b49',
reverseRecordsContract: '0x3671aE578E63FdF66ad4F3E12CC0c0d71Ac7510C',
tornadoSubgraph: 'tornadocash/mainnet-tornado-subgraph',
registrySubgraph: 'tornadocash/tornado-relayer-registry',
governanceSubgraph: 'tornadocash/tornado-governance',
subgraphs: {
tornado,
theGraph,
},
tokens: {
eth: {
instanceAddress: {
'0.1': '0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc',
'1': '0x47CE0C6eD5B0Ce3d3A51fdb1C52DC66a7c3c2936',
'10': '0x910Cbd523D972eb0a6f4cAe4618aD62622b39DbF',
'100': '0xA160cdAB225685dA1d56aa342Ad8841c3b53f291',
},
symbol: 'ETH',
decimals: 18,
},
dai: {
instanceAddress: {
'100': '0xD4B88Df4D29F5CedD6857912842cff3b20C8Cfa3',
'1000': '0xFD8610d20aA15b7B2E3Be39B396a1bC3516c7144',
'10000': '0x07687e702b410Fa43f4cB4Af7FA097918ffD2730',
'100000': '0x23773E65ed146A459791799d01336DB287f25334',
},
tokenAddress: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
tokenGasLimit: 70_000,
symbol: 'DAI',
decimals: 18,
gasLimit: 700_000,
},
cdai: {
instanceAddress: {
'5000': '0x22aaA7720ddd5388A3c0A3333430953C68f1849b',
'50000': '0x03893a7c7463AE47D46bc7f091665f1893656003',
'500000': '0x2717c5e28cf931547B621a5dddb772Ab6A35B701',
'5000000': '0xD21be7248e0197Ee08E0c20D4a96DEBdaC3D20Af',
},
tokenAddress: '0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643',
tokenGasLimit: 200_000,
symbol: 'cDAI',
decimals: 8,
gasLimit: 700_000,
},
usdc: {
instanceAddress: {
'100': '0xd96f2B1c14Db8458374d9Aca76E26c3D18364307',
'1000': '0x4736dCf1b7A3d580672CcE6E7c65cd5cc9cFBa9D',
},
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
tokenGasLimit: 70_000,
symbol: 'USDC',
decimals: 6,
gasLimit: 700_000,
},
usdt: {
instanceAddress: {
'100': '0x169AD27A470D064DEDE56a2D3ff727986b15D52B',
'1000': '0x0836222F2B2B24A3F36f98668Ed8F0B38D1a872f',
},
tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
tokenGasLimit: 70_000,
symbol: 'USDT',
decimals: 6,
gasLimit: 700_000,
},
wbtc: {
instanceAddress: {
'0.1': '0x178169B423a011fff22B9e3F3abeA13414dDD0F1',
'1': '0x610B717796ad172B316836AC95a2ffad065CeaB4',
'10': '0xbB93e510BbCD0B7beb5A853875f9eC60275CF498',
},
tokenAddress: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
tokenGasLimit: 70_000,
symbol: 'WBTC',
decimals: 8,
gasLimit: 700_000,
},
},
ensSubdomainKey: 'mainnet-tornado',
pollInterval: 15,
constants: {
GOVERNANCE_BLOCK: 11474695,
NOTE_ACCOUNT_BLOCK: 11842486,
ENCRYPTED_NOTES_BLOCK: 12143762,
REGISTRY_BLOCK: 14173129,
MINING_BLOCK_TIME: 15,
},
},
[NetId.BSC]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 5,
fast: 5,
standard: 5,
low: 5,
},
nativeCurrency: 'bnb',
currencyName: 'BNB',
explorerUrl: 'https://bscscan.com',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Binance Smart Chain',
deployedBlock: 8158799,
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
tornadoSubgraph: 'tornadocash/bsc-tornado-subgraph',
subgraphs: {
tornado,
theGraph,
},
rpcUrls: {
tornado: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/bsc',
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://bsc-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
},
stackup: {
name: 'Stackup RPC',
url: 'https://public.stackup.sh/api/v1/node/bsc-mainnet',
},
noderealRPC: {
name: 'NodeReal RPC',
url: 'https://bsc-mainnet.nodereal.io/v1/64a9df0874fb4a93b9d0a3849de012d3',
},
oneRPC: {
name: '1RPC',
url: 'https://1rpc.io/bnb',
},
},
tokens: {
bnb: {
instanceAddress: {
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
},
symbol: 'BNB',
decimals: 18,
},
},
ensSubdomainKey: 'bsc-tornado',
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 8159269,
ENCRYPTED_NOTES_BLOCK: 8159269,
},
},
[NetId.POLYGON]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 100,
fast: 75,
standard: 50,
low: 30,
},
nativeCurrency: 'matic',
currencyName: 'MATIC',
explorerUrl: 'https://polygonscan.com',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Polygon (Matic) Network',
deployedBlock: 16257962,
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
gasPriceOracleContract: '0xF81A8D8D3581985D3969fe53bFA67074aDFa8F3C',
tornadoSubgraph: 'tornadocash/matic-tornado-subgraph',
subgraphs: {
tornado,
theGraph,
},
rpcUrls: {
chainnodes: {
name: 'Tornado RPC',
url: 'https://polygon-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
},
stackup: {
name: 'Stackup RPC',
url: 'https://public.stackup.sh/api/v1/node/polygon-mainnet',
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/matic',
},
},
tokens: {
matic: {
instanceAddress: {
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
'1000': '0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178',
'10000': '0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040',
'100000': '0xa5C2254e4253490C54cef0a4347fddb8f75A4998',
},
symbol: 'MATIC',
decimals: 18,
},
},
ensSubdomainKey: 'polygon-tornado',
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 16257996,
ENCRYPTED_NOTES_BLOCK: 16257996,
},
},
[NetId.OPTIMISM]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 0.001,
fast: 0.001,
standard: 0.001,
low: 0.001,
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: 'https://optimistic.etherscan.io',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Optimism',
deployedBlock: 2243689,
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
ovmGasPriceOracleContract: '0x420000000000000000000000000000000000000F',
tornadoSubgraph: 'tornadocash/optimism-tornado-subgraph',
subgraphs: {
tornado,
theGraph,
},
rpcUrls: {
tornado: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/op',
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://optimism-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
},
optimism: {
name: 'Optimism RPC',
url: 'https://mainnet.optimism.io',
},
stackup: {
name: 'Stackup RPC',
url: 'https://public.stackup.sh/api/v1/node/optimism-mainnet',
},
oneRpc: {
name: '1RPC',
url: 'https://1rpc.io/op',
},
},
tokens: {
eth: {
instanceAddress: {
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
},
symbol: 'ETH',
decimals: 18,
},
},
ensSubdomainKey: 'optimism-tornado',
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 2243694,
ENCRYPTED_NOTES_BLOCK: 2243694,
},
},
[NetId.ARBITRUM]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 4,
fast: 3,
standard: 2.52,
low: 2.29,
},
nativeCurrency: 'eth',
currencyName: 'ETH',
explorerUrl: 'https://arbiscan.io',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Arbitrum One',
deployedBlock: 3430648,
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
tornadoSubgraph: 'tornadocash/arbitrum-tornado-subgraph',
subgraphs: {
tornado,
theGraph,
},
rpcUrls: {
tornado: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/arbitrum',
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://arbitrum-one.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
},
arbitrum: {
name: 'Arbitrum RPC',
url: 'https://arb1.arbitrum.io/rpc',
},
stackup: {
name: 'Stackup RPC',
url: 'https://public.stackup.sh/api/v1/node/arbitrum-one',
},
oneRpc: {
name: '1rpc',
url: 'https://1rpc.io/arb',
},
},
tokens: {
eth: {
instanceAddress: {
'0.1': '0x84443CFd09A48AF6eF360C6976C5392aC5023a1F',
'1': '0xd47438C816c9E7f2E2888E060936a499Af9582b3',
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
},
symbol: 'ETH',
decimals: 18,
},
},
ensSubdomainKey: 'arbitrum-tornado',
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 3430605,
ENCRYPTED_NOTES_BLOCK: 3430605,
},
},
[NetId.GNOSIS]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 6,
fast: 5,
standard: 4,
low: 1,
},
nativeCurrency: 'xdai',
currencyName: 'xDAI',
explorerUrl: 'https://gnosisscan.io',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Gnosis Chain',
deployedBlock: 17754561,
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
tornadoSubgraph: 'tornadocash/xdai-tornado-subgraph',
subgraphs: {
tornado,
theGraph,
},
rpcUrls: {
tornado: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/gnosis',
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://gnosis-mainnet.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
},
gnosis: {
name: 'Gnosis RPC',
url: 'https://rpc.gnosischain.com',
},
stackup: {
name: 'Stackup RPC',
url: 'https://public.stackup.sh/api/v1/node/arbitrum-one',
},
blockPi: {
name: 'BlockPi',
url: 'https://gnosis.blockpi.network/v1/rpc/public',
},
},
tokens: {
xdai: {
instanceAddress: {
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
'1000': '0xdf231d99Ff8b6c6CBF4E9B9a945CBAcEF9339178',
'10000': '0xaf4c0B70B2Ea9FB7487C7CbB37aDa259579fe040',
'100000': '0xa5C2254e4253490C54cef0a4347fddb8f75A4998',
},
symbol: 'xDAI',
decimals: 18,
},
},
ensSubdomainKey: 'gnosis-tornado',
pollInterval: 15,
constants: {
NOTE_ACCOUNT_BLOCK: 17754564,
ENCRYPTED_NOTES_BLOCK: 17754564,
},
},
[NetId.AVALANCHE]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 225,
fast: 35,
standard: 25,
low: 25,
},
nativeCurrency: 'avax',
currencyName: 'AVAX',
explorerUrl: 'https://snowtrace.io',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Avalanche Mainnet',
deployedBlock: 4429818,
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x0D5550d52428E7e3175bfc9550207e4ad3859b17',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
offchainOracleContract: '0x0AdDd25a91563696D8567Df78D5A01C9a991F9B8',
tornadoSubgraph: 'tornadocash/avalanche-tornado-subgraph',
subgraphs: {
theGraph,
},
rpcUrls: {
oneRPC: {
name: 'OneRPC',
url: 'https://1rpc.io/avax/c',
},
avalancheRPC: {
name: 'Avalanche RPC',
url: 'https://api.avax.network/ext/bc/C/rpc',
},
meowRPC: {
name: 'Meow RPC',
url: 'https://avax.meowrpc.com',
},
},
tokens: {
avax: {
instanceAddress: {
'10': '0x330bdFADE01eE9bF63C209Ee33102DD334618e0a',
'100': '0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD',
'500': '0xaf8d1839c3c67cf571aa74B5c12398d4901147B3',
},
symbol: 'AVAX',
decimals: 18,
},
},
ensSubdomainKey: 'avalanche-tornado',
pollInterval: 10,
constants: {
NOTE_ACCOUNT_BLOCK: 4429813,
ENCRYPTED_NOTES_BLOCK: 4429813,
},
},
[NetId.SEPOLIA]: {
rpcCallRetryAttempt: 15,
gasPrices: {
instant: 2,
fast: 2,
standard: 2,
low: 2,
},
nativeCurrency: 'eth',
currencyName: 'SepoliaETH',
explorerUrl: 'https://sepolia.etherscan.io',
merkleTreeHeight: 20,
emptyElement: '21663839004416932945382355908790599225266501822907911457504978515578255421292',
networkName: 'Ethereum Sepolia',
deployedBlock: 5594395,
multicallContract: '0xcA11bde05977b3631167028862bE2a173976CA11',
routerContract: '0x1572AFE6949fdF51Cb3E0856216670ae9Ee160Ee',
echoContract: '0xa75BF2815618872f155b7C4B0C81bF990f5245E4',
tornContract: '0x3AE6667167C0f44394106E197904519D808323cA',
governanceContract: '0xe5324cD7602eeb387418e594B87aCADee08aeCAD',
stakingRewardsContract: '0x6d0018890751Efd31feb8166711B16732E2b496b',
registryContract: '0x1428e5d2356b13778A13108b10c440C83011dfB8',
aggregatorContract: '0x4088712AC9fad39ea133cdb9130E465d235e9642',
reverseRecordsContract: '0xEc29700C0283e5Be64AcdFe8077d6cC95dE23C23',
tornadoSubgraph: 'tornadocash/sepolia-tornado-subgraph',
subgraphs: {
tornado,
},
rpcUrls: {
tornado: {
name: 'Tornado RPC',
url: 'https://tornadocash-rpc.com/sepolia',
},
sepolia: {
name: 'Sepolia RPC',
url: 'https://rpc.sepolia.org',
},
chainnodes: {
name: 'Chainnodes RPC',
url: 'https://sepolia.chainnodes.org/d692ae63-0a7e-43e0-9da9-fe4f4cc6c607',
},
},
tokens: {
eth: {
instanceAddress: {
'0.1': '0x8C4A04d872a6C1BE37964A21ba3a138525dFF50b',
'1': '0x8cc930096B4Df705A007c4A039BDFA1320Ed2508',
'10': '0x8D10d506D29Fc62ABb8A290B99F66dB27Fc43585',
'100': '0x44c5C92ed73dB43888210264f0C8b36Fd68D8379',
},
symbol: 'ETH',
decimals: 18,
},
dai: {
instanceAddress: {
'100': '0x6921fd1a97441dd603a997ED6DDF388658daf754',
'1000': '0x50a637770F5d161999420F7d70d888DE47207145',
'10000': '0xecD649870407cD43923A816Cc6334a5bdf113621',
'100000': '0x73B4BD04bF83206B6e979BE2507098F92EDf4F90',
},
tokenAddress: '0xFF34B3d4Aee8ddCd6F9AFFFB6Fe49bD371b8a357',
tokenGasLimit: 70_000,
symbol: 'DAI',
decimals: 18,
gasLimit: 700_000,
},
},
ensSubdomainKey: 'sepolia-tornado',
pollInterval: 15,
constants: {
GOVERNANCE_BLOCK: 5594395,
NOTE_ACCOUNT_BLOCK: 5594395,
ENCRYPTED_NOTES_BLOCK: 5594395,
MINING_BLOCK_TIME: 15,
},
},
};
export const enabledChains = Object.values(NetId).filter((n) => typeof n === 'number') as NetIdType[];
/**
* Custom config object to extend default config
*
* Inspired by getUrlFunc from ethers.js
* https://github.com/ethers-io/ethers.js/blob/v6/src.ts/utils/fetch.ts#L59
*/
export let customConfig: networkConfig = {};
/**
* Add or override existing network config object
*
* Could be also called on the UI hook so that the UI could allow people to use custom privacy pools
*/
export function addNetwork(newConfig: networkConfig) {
enabledChains.push(
...Object.keys(newConfig)
.map((netId) => Number(netId))
.filter((netId) => !enabledChains.includes(netId)),
);
customConfig = {
...customConfig,
...newConfig,
};
}
export function getNetworkConfig(): networkConfig {
// customConfig object
const allConfig = {
...defaultConfig,
...customConfig,
};
return enabledChains.reduce((acc, curr) => {
acc[curr] = allConfig[curr];
return acc;
}, {} as networkConfig);
}
export function getConfig(netId: NetIdType) {
const allConfig = getNetworkConfig();
const chainConfig = allConfig[netId];
if (!chainConfig) {
const errMsg = `No config found for network ${netId}!`;
throw new Error(errMsg);
}
return chainConfig;
}
export function getInstanceByAddress({ netId, address }: { netId: NetIdType; address: string }) {
const { tokens } = getConfig(netId);
for (const [currency, { instanceAddress }] of Object.entries(tokens)) {
for (const [amount, instance] of Object.entries(instanceAddress)) {
if (instance === address) {
return {
amount,
currency,
};
}
}
}
}
export function getSubdomains() {
const allConfig = getNetworkConfig();
return enabledChains.map((chain) => allConfig[chain].ensSubdomainKey);
}

77
src/parser.ts Normal file
View File

@ -0,0 +1,77 @@
import { InvalidArgumentError } from 'commander';
import { computeAddress, getAddress, Mnemonic } from 'ethers';
import { validateUrl } from './utils';
export function parseNumber(value?: string | number): number {
if (!value || isNaN(Number(value))) {
throw new InvalidArgumentError('Invalid Number');
}
return Number(value);
}
export function parseUrl(value?: string): string {
if (!value || !validateUrl(value, ['http:', 'https:'])) {
throw new InvalidArgumentError('Invalid URL');
}
return value;
}
export function parseRelayer(value?: string): string {
if (!value || !(value.endsWith('.eth') || validateUrl(value, ['http:', 'https:']))) {
throw new InvalidArgumentError('Invalid Relayer ETH address or URL');
}
return value;
}
export function parseAddress(value?: string): string {
if (!value) {
throw new InvalidArgumentError('Invalid Address');
}
try {
return getAddress(value);
} catch {
throw new InvalidArgumentError('Invalid Address');
}
}
export function parseMnemonic(value?: string): string {
if (!value) {
throw new InvalidArgumentError('Invalid Mnemonic');
}
try {
Mnemonic.fromPhrase(value);
} catch {
throw new InvalidArgumentError('Invalid Mnemonic');
}
return value;
}
export function parseKey(value?: string): string {
if (!value) {
throw new InvalidArgumentError('Invalid Private Key');
}
if (value.length === 64) {
value = '0x' + value;
}
try {
computeAddress(value);
} catch {
throw new InvalidArgumentError('Invalid Private Key');
}
return value;
}
/**
* Recovery key shouldn't have a 0x prefix (Also this is how the UI generates)
*/
export function parseRecoveryKey(value?: string): string {
if (!value) {
throw new InvalidArgumentError('Invalid Recovery Key');
}
try {
computeAddress('0x' + value);
} catch {
throw new InvalidArgumentError('Invalid Recovery Key');
}
return value;
}

32
src/pedersen.ts Normal file
View File

@ -0,0 +1,32 @@
import { BabyJub, PedersenHash, Point, buildPedersenHash } from 'circomlibjs';
export class Pedersen {
pedersenHash?: PedersenHash;
babyJub?: BabyJub;
pedersenPromise: Promise<void>;
constructor() {
this.pedersenPromise = this.initPedersen();
}
async initPedersen() {
this.pedersenHash = await buildPedersenHash();
this.babyJub = this.pedersenHash.babyJub;
}
async unpackPoint(buffer: Uint8Array) {
await this.pedersenPromise;
return this.babyJub?.unpackPoint(this.pedersenHash?.hash(buffer) as Uint8Array);
}
toStringBuffer(buffer: Uint8Array): string {
return this.babyJub?.F.toString(buffer);
}
}
export const pedersen = new Pedersen();
export async function buffPedersenHash(buffer: Uint8Array): Promise<string> {
const [hash] = (await pedersen.unpackPoint(buffer)) as Point;
return pedersen.toStringBuffer(hash);
}

31
src/prices.ts Normal file
View File

@ -0,0 +1,31 @@
import { parseEther, type Provider } from 'ethers';
import type { OffchainOracle, Multicall } from './typechain';
import { multicall } from './multicall';
export class TokenPriceOracle {
oracle?: OffchainOracle;
multicall: Multicall;
provider: Provider;
constructor(provider: Provider, multicall: Multicall, oracle?: OffchainOracle) {
this.provider = provider;
this.multicall = multicall;
this.oracle = oracle;
}
fetchPrices(tokens: string[]): Promise<bigint[]> {
// setup mock price for testnets
if (!this.oracle) {
return new Promise((resolve) => resolve(tokens.map(() => parseEther('0.0001'))));
}
return multicall(
this.multicall,
tokens.map((token) => ({
contract: this.oracle,
name: 'getRateToEth',
params: [token, true],
})),
);
}
}

645
src/providers.ts Normal file
View File

@ -0,0 +1,645 @@
import type { EventEmitter } from 'stream';
import type { RequestOptions } from 'http';
import crossFetch from 'cross-fetch';
import { HttpProxyAgent } from 'http-proxy-agent';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { SocksProxyAgent } from 'socks-proxy-agent';
import {
FetchRequest,
JsonRpcApiProvider,
JsonRpcProvider,
Wallet,
HDNodeWallet,
FetchGetUrlFunc,
Provider,
SigningKey,
TransactionRequest,
JsonRpcSigner,
BrowserProvider,
Networkish,
Eip1193Provider,
VoidSigner,
Network,
parseUnits,
FetchUrlFeeDataNetworkPlugin,
FeeData,
EnsPlugin,
GasCostPlugin,
} from 'ethers';
import type { RequestInfo, RequestInit, Response, HeadersInit } from 'node-fetch';
import { GasPriceOracle, GasPriceOracle__factory, Multicall, Multicall__factory } from './typechain';
import { isNode, sleep } from './utils';
import type { Config, NetIdType } from './networkConfig';
import { multicall } from './multicall';
declare global {
interface Window {
ethereum?: Eip1193Provider & EventEmitter;
}
}
// Update this for every Tor Browser release
export const defaultUserAgent = 'Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0';
export const fetch = crossFetch as unknown as nodeFetch;
export type nodeFetch = (url: RequestInfo, init?: RequestInit) => Promise<Response>;
export type fetchDataOptions = RequestInit & {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
headers?: HeadersInit | any;
maxRetry?: number;
retryOn?: number;
userAgent?: string;
timeout?: number;
proxy?: string;
torPort?: number;
// eslint-disable-next-line @typescript-eslint/ban-types
debug?: Function;
returnResponse?: boolean;
};
export type NodeAgent = RequestOptions['agent'] | ((parsedUrl: URL) => RequestOptions['agent']);
export function getHttpAgent({
fetchUrl,
proxyUrl,
torPort,
retry,
}: {
fetchUrl: string;
proxyUrl?: string;
torPort?: number;
retry: number;
}): NodeAgent | undefined {
if (torPort) {
return new SocksProxyAgent(`socks5h://tor${retry}@127.0.0.1:${torPort}`);
}
if (!proxyUrl) {
return;
}
const isHttps = fetchUrl.includes('https://');
if (proxyUrl.includes('socks://') || proxyUrl.includes('socks4://') || proxyUrl.includes('socks5://')) {
return new SocksProxyAgent(proxyUrl);
}
if (proxyUrl.includes('http://') || proxyUrl.includes('https://')) {
if (isHttps) {
return new HttpsProxyAgent(proxyUrl);
}
return new HttpProxyAgent(proxyUrl);
}
}
export async function fetchData(url: string, options: fetchDataOptions = {}) {
const MAX_RETRY = options.maxRetry ?? 3;
const RETRY_ON = options.retryOn ?? 500;
const userAgent = options.userAgent ?? defaultUserAgent;
let retry = 0;
let errorObject;
if (!options.method) {
if (!options.body) {
options.method = 'GET';
} else {
options.method = 'POST';
}
}
if (!options.headers) {
options.headers = {};
}
if (isNode && !options.headers['User-Agent']) {
options.headers['User-Agent'] = userAgent;
}
while (retry < MAX_RETRY + 1) {
let timeout;
// Define promise timeout when the options.timeout is available
if (!options.signal && options.timeout) {
const controller = new AbortController();
options.signal = controller.signal;
// Define timeout in seconds
timeout = setTimeout(() => {
controller.abort();
}, options.timeout);
}
if (!options.agent && isNode && (options.proxy || options.torPort)) {
options.agent = getHttpAgent({
fetchUrl: url,
proxyUrl: options.proxy,
torPort: options.torPort,
retry,
});
}
if (options.debug && typeof options.debug === 'function') {
options.debug('request', {
url,
retry,
errorObject,
options,
});
}
try {
const resp = await fetch(url, {
method: options.method,
headers: options.headers,
body: options.body,
redirect: options.redirect,
signal: options.signal,
agent: options.agent,
});
if (options.debug && typeof options.debug === 'function') {
options.debug('response', resp);
}
if (!resp.ok) {
const errMsg = `Request to ${url} failed with error code ${resp.status}:\n` + (await resp.text());
throw new Error(errMsg);
}
if (options.returnResponse) {
return resp;
}
const contentType = resp.headers.get('content-type');
// If server returns JSON object, parse it and return as an object
if (contentType?.includes('application/json')) {
return await resp.json();
}
// Else if the server returns text parse it as a string
if (contentType?.includes('text')) {
return await resp.text();
}
// Return as a response object https://developer.mozilla.org/en-US/docs/Web/API/Response
return resp;
} catch (error) {
if (timeout) {
clearTimeout(timeout);
}
errorObject = error;
retry++;
await sleep(RETRY_ON);
} finally {
if (timeout) {
clearTimeout(timeout);
}
}
}
if (options.debug && typeof options.debug === 'function') {
options.debug('error', errorObject);
}
throw errorObject;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
export const fetchGetUrlFunc =
(options: fetchDataOptions = {}): FetchGetUrlFunc =>
async (req, _signal) => {
let signal;
if (_signal) {
const controller = new AbortController();
signal = controller.signal;
_signal.addListener(() => {
controller.abort();
});
}
const init = {
...options,
method: req.method || 'POST',
headers: req.headers,
body: req.body || undefined,
signal,
returnResponse: true,
};
const resp = await fetchData(req.url, init);
const headers = {} as { [key in string]: any };
resp.headers.forEach((value: any, key: string) => {
headers[key.toLowerCase()] = value;
});
const respBody = await resp.arrayBuffer();
const body = respBody == null ? null : new Uint8Array(respBody);
return {
statusCode: resp.status,
statusMessage: resp.statusText,
headers,
body,
};
};
/* eslint-enable @typescript-eslint/no-explicit-any */
// caching to improve performance
const oracleMapper = new Map();
const multicallMapper = new Map();
export type getProviderOptions = fetchDataOptions & {
pollingInterval?: number;
gasPriceOracle?: string;
gasStationApi?: string;
};
export function getGasOraclePlugin(networkKey: string, fetchOptions?: getProviderOptions) {
const gasStationApi = fetchOptions?.gasStationApi || 'https://gasstation.polygon.technology/v2';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return new FetchUrlFeeDataNetworkPlugin(gasStationApi, async (fetchFeeData, provider, request) => {
if (!oracleMapper.has(networkKey)) {
oracleMapper.set(networkKey, GasPriceOracle__factory.connect(fetchOptions?.gasPriceOracle as string, provider));
}
if (!multicallMapper.has(networkKey)) {
multicallMapper.set(
networkKey,
Multicall__factory.connect('0xcA11bde05977b3631167028862bE2a173976CA11', provider),
);
}
const Oracle = oracleMapper.get(networkKey) as GasPriceOracle;
const Multicall = multicallMapper.get(networkKey) as Multicall;
const [timestamp, heartbeat, feePerGas, priorityFeePerGas] = await multicall(Multicall, [
{
contract: Oracle,
name: 'timestamp',
},
{
contract: Oracle,
name: 'heartbeat',
},
{
contract: Oracle,
name: 'maxFeePerGas',
},
{
contract: Oracle,
name: 'maxPriorityFeePerGas',
},
]);
const isOutdated = Number(timestamp) <= Date.now() / 1000 - Number(heartbeat);
if (!isOutdated) {
const maxPriorityFeePerGas = (priorityFeePerGas * BigInt(13)) / BigInt(10);
const maxFeePerGas = feePerGas * BigInt(2) + maxPriorityFeePerGas;
return {
gasPrice: maxFeePerGas,
maxFeePerGas,
maxPriorityFeePerGas,
};
}
const fetchReq = new FetchRequest(gasStationApi);
fetchReq.getUrlFunc = fetchGetUrlFunc(fetchOptions);
if (isNode) {
// Prevent Cloudflare from blocking our request in node.js
fetchReq.setHeader('User-Agent', 'ethers');
}
const [
{
bodyJson: { fast },
},
{ gasPrice },
] = await Promise.all([fetchReq.send(), fetchFeeData()]);
return {
gasPrice,
maxFeePerGas: parseUnits(`${fast.maxFee}`, 9),
maxPriorityFeePerGas: parseUnits(`${fast.maxPriorityFee}`, 9),
};
});
}
export async function getProvider(rpcUrl: string, fetchOptions?: getProviderOptions): Promise<JsonRpcProvider> {
const fetchReq = new FetchRequest(rpcUrl);
fetchReq.getUrlFunc = fetchGetUrlFunc(fetchOptions);
// omit network plugins and mimic registerEth function (required for polygon)
const _staticNetwork = await new JsonRpcProvider(fetchReq).getNetwork();
const ensPlugin = _staticNetwork.getPlugin('org.ethers.plugins.network.Ens');
const gasCostPlugin = _staticNetwork.getPlugin('org.ethers.plugins.network.GasCost');
const gasStationPlugin = <FetchUrlFeeDataNetworkPlugin>(
_staticNetwork.getPlugin('org.ethers.plugins.network.FetchUrlFeeDataPlugin')
);
const staticNetwork = new Network(_staticNetwork.name, _staticNetwork.chainId);
if (ensPlugin) {
staticNetwork.attachPlugin(ensPlugin);
}
if (gasCostPlugin) {
staticNetwork.attachPlugin(gasCostPlugin);
}
if (fetchOptions?.gasPriceOracle) {
staticNetwork.attachPlugin(getGasOraclePlugin(`${_staticNetwork.chainId}_${rpcUrl}`, fetchOptions));
} else if (gasStationPlugin) {
staticNetwork.attachPlugin(gasStationPlugin);
}
const provider = new JsonRpcProvider(fetchReq, staticNetwork, {
staticNetwork,
});
provider.pollingInterval = fetchOptions?.pollingInterval || 1000;
return provider;
}
export function getProviderWithNetId(
netId: NetIdType,
rpcUrl: string,
config: Config,
fetchOptions?: getProviderOptions,
): JsonRpcProvider {
const { networkName, reverseRecordsContract, gasPriceOracleContract, gasStationApi, pollInterval } = config;
const hasEns = Boolean(reverseRecordsContract);
const fetchReq = new FetchRequest(rpcUrl);
fetchReq.getUrlFunc = fetchGetUrlFunc(fetchOptions);
const staticNetwork = new Network(networkName, netId);
if (hasEns) {
staticNetwork.attachPlugin(new EnsPlugin(null, Number(netId)));
}
staticNetwork.attachPlugin(new GasCostPlugin());
if (gasPriceOracleContract) {
staticNetwork.attachPlugin(
getGasOraclePlugin(`${netId}_${rpcUrl}`, {
gasPriceOracle: gasPriceOracleContract,
gasStationApi,
}),
);
}
const provider = new JsonRpcProvider(fetchReq, staticNetwork, {
staticNetwork,
});
provider.pollingInterval = fetchOptions?.pollingInterval || pollInterval * 1000;
return provider;
}
export const populateTransaction = async (
signer: TornadoWallet | TornadoVoidSigner | TornadoRpcSigner,
tx: TransactionRequest,
) => {
const provider = signer.provider as Provider;
if (!tx.from) {
tx.from = signer.address;
} else if (tx.from !== signer.address) {
const errMsg = `populateTransaction: signer mismatch for tx, wants ${tx.from} have ${signer.address}`;
throw new Error(errMsg);
}
const [feeData, nonce] = await Promise.all([
(async () => {
if (tx.maxFeePerGas && tx.maxPriorityFeePerGas) {
return new FeeData(null, BigInt(tx.maxFeePerGas), BigInt(tx.maxPriorityFeePerGas));
}
if (tx.gasPrice) {
return new FeeData(BigInt(tx.gasPrice), null, null);
}
const fetchedFeeData = await provider.getFeeData();
if (fetchedFeeData.maxFeePerGas && fetchedFeeData.maxPriorityFeePerGas) {
return new FeeData(
null,
(fetchedFeeData.maxFeePerGas * (BigInt(10000) + BigInt(signer.gasPriceBump))) / BigInt(10000),
fetchedFeeData.maxPriorityFeePerGas,
);
} else {
return new FeeData(
((fetchedFeeData.gasPrice as bigint) * (BigInt(10000) + BigInt(signer.gasPriceBump))) / BigInt(10000),
null,
null,
);
}
})(),
(async () => {
if (tx.nonce) {
return tx.nonce;
}
let fetchedNonce = await provider.getTransactionCount(signer.address, 'pending');
// Deal with cached nonce results
if (signer.bumpNonce && signer.nonce && signer.nonce >= fetchedNonce) {
console.log(
`populateTransaction: bumping nonce from ${fetchedNonce} to ${fetchedNonce + 1} for ${signer.address}`,
);
fetchedNonce++;
}
return fetchedNonce;
})(),
]);
tx.nonce = nonce;
// EIP-1559
if (feeData.maxFeePerGas && feeData.maxPriorityFeePerGas) {
tx.maxFeePerGas = feeData.maxFeePerGas;
tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
if (!tx.type) {
tx.type = 2;
}
delete tx.gasPrice;
} else if (feeData.gasPrice) {
tx.gasPrice = feeData.gasPrice;
if (!tx.type) {
tx.type = 0;
}
delete tx.maxFeePerGas;
delete tx.maxPriorityFeePerGas;
}
// gasLimit
tx.gasLimit =
tx.gasLimit ||
(await (async () => {
try {
const gasLimit = await provider.estimateGas(tx);
return gasLimit === BigInt(21000)
? gasLimit
: (gasLimit * (BigInt(10000) + BigInt(signer.gasLimitBump))) / BigInt(10000);
} catch (err) {
if (signer.gasFailover) {
console.log('populateTransaction: warning gas estimation failed falling back to 3M gas');
// Gas failover
return BigInt('3000000');
}
throw err;
}
})());
return tx;
};
export type TornadoWalletOptions = {
gasPriceBump?: number;
gasLimitBump?: number;
gasFailover?: boolean;
bumpNonce?: boolean;
};
export class TornadoWallet extends Wallet {
nonce?: number | null;
gasPriceBump: number;
gasLimitBump: number;
gasFailover: boolean;
bumpNonce: boolean;
constructor(
key: string | SigningKey,
provider?: null | Provider,
{ gasPriceBump, gasLimitBump, gasFailover, bumpNonce }: TornadoWalletOptions = {},
) {
super(key, provider);
// 10% bump from the recommended fee
this.gasPriceBump = gasPriceBump ?? 1000;
// 30% bump from the recommended gaslimit
this.gasLimitBump = gasLimitBump ?? 3000;
this.gasFailover = gasFailover ?? false;
// Disable bump nonce feature unless being used by the server environment
this.bumpNonce = bumpNonce ?? false;
}
static fromMnemonic(mneomnic: string, provider: Provider, index = 0, options?: TornadoWalletOptions) {
const defaultPath = `m/44'/60'/0'/0/${index}`;
const { privateKey } = HDNodeWallet.fromPhrase(mneomnic, undefined, defaultPath);
return new TornadoWallet(privateKey as unknown as SigningKey, provider, options);
}
async populateTransaction(tx: TransactionRequest) {
const txObject = await populateTransaction(this, tx);
this.nonce = txObject.nonce;
return super.populateTransaction(txObject);
}
}
export class TornadoVoidSigner extends VoidSigner {
nonce?: number | null;
gasPriceBump: number;
gasLimitBump: number;
gasFailover: boolean;
bumpNonce: boolean;
constructor(
address: string,
provider?: null | Provider,
{ gasPriceBump, gasLimitBump, gasFailover, bumpNonce }: TornadoWalletOptions = {},
) {
super(address, provider);
// 10% bump from the recommended fee
this.gasPriceBump = gasPriceBump ?? 1000;
// 30% bump from the recommended gaslimit
this.gasLimitBump = gasLimitBump ?? 3000;
this.gasFailover = gasFailover ?? false;
// turn off bumpNonce feature for view only wallet
this.bumpNonce = bumpNonce ?? false;
}
async populateTransaction(tx: TransactionRequest) {
const txObject = await populateTransaction(this, tx);
this.nonce = txObject.nonce;
return super.populateTransaction(txObject);
}
}
export class TornadoRpcSigner extends JsonRpcSigner {
nonce?: number | null;
gasPriceBump: number;
gasLimitBump: number;
gasFailover: boolean;
bumpNonce: boolean;
constructor(
provider: JsonRpcApiProvider,
address: string,
{ gasPriceBump, gasLimitBump, gasFailover, bumpNonce }: TornadoWalletOptions = {},
) {
super(provider, address);
// 10% bump from the recommended fee
this.gasPriceBump = gasPriceBump ?? 1000;
// 30% bump from the recommended gaslimit
this.gasLimitBump = gasLimitBump ?? 3000;
this.gasFailover = gasFailover ?? false;
// turn off bumpNonce feature for browser wallet
this.bumpNonce = bumpNonce ?? false;
}
async sendUncheckedTransaction(tx: TransactionRequest) {
return super.sendUncheckedTransaction(await populateTransaction(this, tx));
}
}
/* eslint-disable @typescript-eslint/no-explicit-any */
export type connectWalletFunc = (...args: any[]) => Promise<void>;
export type handleWalletFunc = (...args: any[]) => void;
/* eslint-enable @typescript-eslint/no-explicit-any */
export type TornadoBrowserProviderOptions = TornadoWalletOptions & {
webChainId?: NetIdType;
connectWallet?: connectWalletFunc;
handleNetworkChanges?: handleWalletFunc;
handleAccountChanges?: handleWalletFunc;
handleAccountDisconnect?: handleWalletFunc;
};
export class TornadoBrowserProvider extends BrowserProvider {
options?: TornadoBrowserProviderOptions;
constructor(ethereum: Eip1193Provider, network?: Networkish, options?: TornadoBrowserProviderOptions) {
super(ethereum, network);
this.options = options;
}
async getSigner(address: string): Promise<TornadoRpcSigner> {
const signerAddress = (await super.getSigner(address)).address;
if (
this.options?.webChainId &&
this.options?.connectWallet &&
Number(await super.send('eth_chainId', [])) !== Number(this.options?.webChainId)
) {
await this.options.connectWallet();
}
if (this.options?.handleNetworkChanges) {
window?.ethereum?.on('chainChanged', this.options.handleNetworkChanges);
}
if (this.options?.handleAccountChanges) {
window?.ethereum?.on('accountsChanged', this.options.handleAccountChanges);
}
if (this.options?.handleAccountDisconnect) {
window?.ethereum?.on('disconnect', this.options.handleAccountDisconnect);
}
return new TornadoRpcSigner(this, signerAddress, this.options);
}
}

434
src/relayerClient.ts Normal file
View File

@ -0,0 +1,434 @@
import { getAddress, namehash, parseEther } from 'ethers';
import type { Aggregator } from '@tornado/contracts';
import type { RelayerStructOutput } from '@tornado/contracts/dist/contracts/Governance/Aggregator/Aggregator';
import { sleep } from './utils';
import { NetId, NetIdType, Config } from './networkConfig';
import { fetchData, fetchDataOptions } from './providers';
import { ajv, jobsSchema, getStatusSchema } from './schemas';
import type { snarkProofs } from './websnark';
export const MIN_STAKE_BALANCE = parseEther('500');
export interface RelayerParams {
ensName: string;
relayerAddress?: string;
}
export interface Relayer {
netId: NetIdType;
url: string;
hostname: string;
rewardAccount: string;
instances: string[];
gasPrice?: number;
ethPrices?: {
[key in string]: string;
};
currentQueue: number;
tornadoServiceFee: number;
}
export type RelayerInfo = Relayer & {
ensName: string;
stakeBalance: bigint;
relayerAddress: string;
};
export type RelayerError = {
hostname: string;
relayerAddress?: string;
errorMessage?: string;
};
export interface RelayerStatus {
url: string;
rewardAccount: string;
instances: {
[key in string]: {
instanceAddress: {
[key in string]: string;
};
tokenAddress?: string;
symbol: string;
decimals: number;
};
};
gasPrices?: {
fast: number;
additionalProperties?: number;
};
netId: NetIdType;
ethPrices?: {
[key in string]: string;
};
tornadoServiceFee: number;
latestBlock?: number;
version: string;
health: {
status: string;
error: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
errorsLog: any[];
};
currentQueue: number;
}
export interface RelayerTornadoWithdraw {
id?: string;
error?: string;
}
export interface RelayerTornadoJobs {
error?: string;
id: string;
type?: string;
status: string;
contract?: string;
proof?: string;
args?: string[];
txHash?: string;
confirmations?: number;
failedReason?: string;
}
const semVerRegex =
/^(?<major>0|[1-9]\d*)\.(?<minor>0|[1-9]\d*)\.(?<patch>0|[1-9]\d*)(?:-(?<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
export interface semanticVersion {
major: string;
minor: string;
patch: string;
prerelease?: string;
buildmetadata?: string;
}
export function parseSemanticVersion(version: string) {
const { groups } = semVerRegex.exec(version) as RegExpExecArray;
return groups as unknown as semanticVersion;
}
export function isRelayerUpdated(relayerVersion: string, netId: NetIdType) {
const { major, patch, prerelease } = parseSemanticVersion(relayerVersion);
// Save backwards compatibility with V4 relayers for Ethereum Mainnet
const requiredMajor = netId === NetId.MAINNET ? '4' : '5';
const isUpdatedMajor = major === requiredMajor;
if (prerelease) return false;
return isUpdatedMajor && (Number(patch) >= 5 || netId !== NetId.MAINNET); // Patch checking - also backwards compatibility for Mainnet
}
export function calculateScore({ stakeBalance, tornadoServiceFee }: RelayerInfo, minFee = 0.33, maxFee = 0.53) {
if (tornadoServiceFee < minFee) {
tornadoServiceFee = minFee;
} else if (tornadoServiceFee >= maxFee) {
return BigInt(0);
}
const serviceFeeCoefficient = (tornadoServiceFee - minFee) ** 2;
const feeDiffCoefficient = 1 / (maxFee - minFee) ** 2;
const coefficientsMultiplier = 1 - feeDiffCoefficient * serviceFeeCoefficient;
return BigInt(Math.floor(Number(stakeBalance) * coefficientsMultiplier));
}
export function getWeightRandom(weightsScores: bigint[], random: bigint) {
for (let i = 0; i < weightsScores.length; i++) {
if (random < weightsScores[i]) {
return i;
}
random = random - weightsScores[i];
}
return Math.floor(Math.random() * weightsScores.length);
}
export type RelayerInstanceList = {
[key in string]: {
instanceAddress: {
[key in string]: string;
};
};
};
export function getSupportedInstances(instanceList: RelayerInstanceList) {
const rawList = Object.values(instanceList)
.map(({ instanceAddress }) => {
return Object.values(instanceAddress);
})
.flat();
return rawList.map((l) => getAddress(l));
}
export function pickWeightedRandomRelayer(relayers: RelayerInfo[], netId: NetIdType) {
let minFee: number, maxFee: number;
if (netId !== NetId.MAINNET) {
minFee = 0.01;
maxFee = 0.3;
}
const weightsScores = relayers.map((el) => calculateScore(el, minFee, maxFee));
const totalWeight = weightsScores.reduce((acc, curr) => {
return (acc = acc + curr);
}, BigInt('0'));
const random = BigInt(Number(totalWeight) * Math.random());
const weightRandomIndex = getWeightRandom(weightsScores, random);
return relayers[weightRandomIndex];
}
export interface RelayerClientConstructor {
netId: NetIdType;
config: Config;
Aggregator: Aggregator;
fetchDataOptions?: fetchDataOptions;
}
export type RelayerClientWithdraw = snarkProofs & {
contract: string;
};
export class RelayerClient {
netId: NetIdType;
config: Config;
Aggregator: Aggregator;
selectedRelayer?: Relayer;
fetchDataOptions?: fetchDataOptions;
constructor({ netId, config, Aggregator, fetchDataOptions }: RelayerClientConstructor) {
this.netId = netId;
this.config = config;
this.Aggregator = Aggregator;
this.fetchDataOptions = fetchDataOptions;
}
async askRelayerStatus({
hostname,
relayerAddress,
}: {
hostname: string;
relayerAddress?: string;
}): Promise<RelayerStatus> {
const url = `https://${!hostname.endsWith('/') ? hostname + '/' : hostname}`;
const rawStatus = (await fetchData(`${url}status`, {
...this.fetchDataOptions,
headers: {
'Content-Type': 'application/json, application/x-www-form-urlencoded',
},
timeout: this.fetchDataOptions?.torPort ? 10000 : 3000,
maxRetry: this.fetchDataOptions?.torPort ? 2 : 0,
})) as object;
const statusValidator = ajv.compile(getStatusSchema(this.netId, this.config));
if (!statusValidator(rawStatus)) {
throw new Error('Invalid status schema');
}
const status = {
...rawStatus,
url,
} as RelayerStatus;
if (status.currentQueue > 5) {
throw new Error('Withdrawal queue is overloaded');
}
if (status.netId !== this.netId) {
throw new Error('This relayer serves a different network');
}
if (relayerAddress && this.netId === NetId.MAINNET && status.rewardAccount !== relayerAddress) {
throw new Error('The Relayer reward address must match registered address');
}
if (!isRelayerUpdated(status.version, this.netId)) {
throw new Error('Outdated version.');
}
return status;
}
async filterRelayer(
curr: RelayerStructOutput,
relayer: RelayerParams,
subdomains: string[],
debugRelayer: boolean = false,
): Promise<RelayerInfo | RelayerError> {
const { ensSubdomainKey } = this.config;
const subdomainIndex = subdomains.indexOf(ensSubdomainKey);
const mainnetSubdomain = curr.records[0];
const hostname = curr.records[subdomainIndex];
const isHostWithProtocol = hostname.includes('http');
const { owner, balance: stakeBalance, isRegistered } = curr;
const { ensName, relayerAddress } = relayer;
const isOwner = !relayerAddress || relayerAddress === owner;
const hasMinBalance = stakeBalance >= MIN_STAKE_BALANCE;
const preCondition =
hostname && isOwner && mainnetSubdomain && isRegistered && hasMinBalance && !isHostWithProtocol;
if (preCondition || debugRelayer) {
try {
const status = await this.askRelayerStatus({ hostname, relayerAddress });
return {
netId: status.netId,
url: status.url,
hostname,
ensName,
stakeBalance,
relayerAddress,
rewardAccount: getAddress(status.rewardAccount),
instances: getSupportedInstances(status.instances),
gasPrice: status.gasPrices?.fast,
ethPrices: status.ethPrices,
currentQueue: status.currentQueue,
tornadoServiceFee: status.tornadoServiceFee,
} as RelayerInfo;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
if (debugRelayer) {
throw err;
}
return {
hostname,
relayerAddress,
errorMessage: err.message,
} as RelayerError;
}
} else {
if (debugRelayer) {
const errMsg = `Relayer ${hostname} condition not met`;
throw new Error(errMsg);
}
return {
hostname,
relayerAddress,
errorMessage: `Relayer ${hostname} condition not met`,
};
}
}
async getValidRelayers(
// this should be ascending order of events
relayers: RelayerParams[],
subdomains: string[],
debugRelayer: boolean = false,
): Promise<{
validRelayers: RelayerInfo[];
invalidRelayers: RelayerError[];
}> {
const relayersSet = new Set();
const uniqueRelayers = relayers.reverse().filter(({ ensName }) => {
if (!relayersSet.has(ensName)) {
relayersSet.add(ensName);
return true;
}
return false;
});
const relayerNameHashes = uniqueRelayers.map((r) => namehash(r.ensName));
const relayersData = await this.Aggregator.relayersData.staticCall(relayerNameHashes, subdomains);
const invalidRelayers: RelayerError[] = [];
const validRelayers = (
await Promise.all(
relayersData.map((curr, index) => this.filterRelayer(curr, uniqueRelayers[index], subdomains, debugRelayer)),
)
).filter((r) => {
if ((r as RelayerError).errorMessage) {
invalidRelayers.push(r);
return false;
}
return true;
}) as RelayerInfo[];
return {
validRelayers,
invalidRelayers,
};
}
pickWeightedRandomRelayer(relayers: RelayerInfo[]) {
return pickWeightedRandomRelayer(relayers, this.netId);
}
async tornadoWithdraw({ contract, proof, args }: RelayerClientWithdraw) {
const { url } = this.selectedRelayer as Relayer;
const withdrawResponse = (await fetchData(`${url}v1/tornadoWithdraw`, {
...this.fetchDataOptions,
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
contract,
proof,
args,
}),
})) as RelayerTornadoWithdraw;
const { id, error } = withdrawResponse;
if (error) {
throw new Error(error);
}
let relayerStatus: string | undefined;
const jobUrl = `${url}v1/jobs/${id}`;
console.log(`Job submitted: ${jobUrl}\n`);
while (!relayerStatus || !['FAILED', 'CONFIRMED'].includes(relayerStatus)) {
const jobResponse = await fetchData(jobUrl, {
...this.fetchDataOptions,
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (jobResponse.error) {
throw new Error(error);
}
const jobValidator = ajv.compile(jobsSchema);
if (!jobValidator(jobResponse)) {
const errMsg = `${jobUrl} has an invalid job response`;
throw new Error(errMsg);
}
const { status, txHash, confirmations, failedReason } = jobResponse as unknown as RelayerTornadoJobs;
if (relayerStatus !== status) {
if (status === 'FAILED') {
const errMsg = `Job ${status}: ${jobUrl} failed reason: ${failedReason}`;
throw new Error(errMsg);
} else if (status === 'SENT') {
console.log(`Job ${status}: ${jobUrl}, txhash: ${txHash}\n`);
} else if (status === 'MINED') {
console.log(`Job ${status}: ${jobUrl}, txhash: ${txHash}, confirmations: ${confirmations}\n`);
} else if (status === 'CONFIRMED') {
console.log(`Job ${status}: ${jobUrl}, txhash: ${txHash}, confirmations: ${confirmations}\n`);
} else {
console.log(`Job ${status}: ${jobUrl}\n`);
}
relayerStatus = status;
}
await sleep(3000);
}
}
}

21
src/schemas/index.ts Normal file
View File

@ -0,0 +1,21 @@
import Ajv from 'ajv';
import type { BigNumberish } from 'ethers';
export const ajv = new Ajv({ allErrors: true });
ajv.addKeyword({
keyword: 'BN',
// eslint-disable-next-line @typescript-eslint/no-explicit-any
validate: (schema: any, data: BigNumberish) => {
try {
BigInt(data);
return true;
} catch (e) {
return false;
}
},
errors: true,
});
export * from './status';
export * from './jobs';

59
src/schemas/jobs.ts Normal file
View File

@ -0,0 +1,59 @@
export type jobsSchema = {
type: string;
properties: {
error: {
type: string;
};
id: {
type: string;
};
type: {
type: string;
};
status: {
type: string;
};
contract: {
type: string;
};
proof: {
type: string;
};
args: {
type: string;
items: {
type: string;
};
};
txHash: {
type: string;
};
confirmations: {
type: string;
};
failedReason: {
type: string;
};
};
required: string[];
};
export const jobsSchema: jobsSchema = {
type: 'object',
properties: {
error: { type: 'string' },
id: { type: 'string' },
type: { type: 'string' },
status: { type: 'string' },
contract: { type: 'string' },
proof: { type: 'string' },
args: {
type: 'array',
items: { type: 'string' },
},
txHash: { type: 'string' },
confirmations: { type: 'number' },
failedReason: { type: 'string' },
},
required: ['id', 'status'],
};

181
src/schemas/status.ts Normal file
View File

@ -0,0 +1,181 @@
import { Config, NetId, NetIdType } from '../networkConfig';
export type statusInstanceType = {
type: string;
properties: {
instanceAddress: {
type: string;
properties: {
[key in string]: typeof addressType;
};
required: string[];
};
tokenAddress?: typeof addressType;
symbol?: { enum: string[] };
decimals: { enum: number[] };
};
required: string[];
};
export type statusInstancesType = {
type: string;
properties: {
[key in string]: statusInstanceType;
};
required: string[];
};
export type statusEthPricesType = {
type: string;
properties: {
[key in string]: typeof bnType;
};
required?: string[];
};
export type statusSchema = {
type: string;
properties: {
rewardAccount: typeof addressType;
instances?: statusInstancesType;
gasPrices: {
type: string;
properties: {
[key in string]: {
type: string;
};
};
required: string[];
};
netId: {
type: string;
};
ethPrices?: statusEthPricesType;
tornadoServiceFee?: {
type: string;
maximum: number;
minimum: number;
};
latestBlock?: {
type: string;
};
version: {
type: string;
};
health: {
type: string;
properties: {
status: { const: string };
error: { type: string };
};
required: string[];
};
currentQueue: {
type: string;
};
};
required: string[];
};
const addressType = { type: 'string', pattern: '^0x[a-fA-F0-9]{40}$' };
const bnType = { type: 'string', BN: true };
const statusSchema: statusSchema = {
type: 'object',
properties: {
rewardAccount: addressType,
gasPrices: {
type: 'object',
properties: {
fast: { type: 'number' },
additionalProperties: { type: 'number' },
},
required: ['fast'],
},
netId: { type: 'integer' },
tornadoServiceFee: { type: 'number', maximum: 20, minimum: 0 },
latestBlock: { type: 'number' },
version: { type: 'string' },
health: {
type: 'object',
properties: {
status: { const: 'true' },
error: { type: 'string' },
},
required: ['status'],
},
currentQueue: { type: 'number' },
},
required: ['rewardAccount', 'instances', 'netId', 'tornadoServiceFee', 'version', 'health'],
};
export function getStatusSchema(netId: NetIdType, config: Config) {
const { tokens, optionalTokens = [], nativeCurrency } = config;
// deep copy schema
const schema = JSON.parse(JSON.stringify(statusSchema)) as statusSchema;
const instances = Object.keys(tokens).reduce(
(acc: statusInstancesType, token) => {
const { instanceAddress, tokenAddress, symbol, decimals, optionalInstances = [] } = tokens[token];
const amounts = Object.keys(instanceAddress);
const instanceProperties: statusInstanceType = {
type: 'object',
properties: {
instanceAddress: {
type: 'object',
properties: amounts.reduce((acc: { [key in string]: typeof addressType }, cur) => {
acc[cur] = addressType;
return acc;
}, {}),
required: amounts.filter((amount) => !optionalInstances.includes(amount)),
},
decimals: { enum: [decimals] },
},
required: ['instanceAddress', 'decimals'].concat(
tokenAddress ? ['tokenAddress'] : [],
symbol ? ['symbol'] : [],
),
};
if (tokenAddress) {
instanceProperties.properties.tokenAddress = addressType;
}
if (symbol) {
instanceProperties.properties.symbol = { enum: [symbol] };
}
acc.properties[token] = instanceProperties;
if (!optionalTokens.includes(token)) {
acc.required.push(token);
}
return acc;
},
{
type: 'object',
properties: {},
required: [],
},
);
schema.properties.instances = instances;
if (netId === NetId.MAINNET) {
const _tokens = Object.keys(tokens).filter((t) => t !== nativeCurrency);
const ethPrices: statusEthPricesType = {
type: 'object',
properties: _tokens.reduce((acc: { [key in string]: typeof bnType }, token: string) => {
acc[token] = bnType;
return acc;
}, {}),
// required: _tokens
};
schema.properties.ethPrices = ethPrices;
// schema.required.push('ethPrices')
}
return schema;
}

90
src/tokens.ts Normal file
View File

@ -0,0 +1,90 @@
import { Provider, ZeroAddress } from 'ethers';
import { ERC20__factory, Multicall } from './typechain';
import { chunk } from './utils';
import { Call3, multicall } from './multicall';
export interface tokenBalances {
address: string;
name: string;
symbol: string;
decimals: number;
balance: bigint;
}
export async function getTokenBalances({
provider,
Multicall,
currencyName,
userAddress,
tokenAddresses = [],
}: {
provider: Provider;
Multicall: Multicall;
currencyName: string;
userAddress: string;
tokenAddresses: string[];
}): Promise<tokenBalances[]> {
const tokenCalls = tokenAddresses
.map((tokenAddress) => {
const Token = ERC20__factory.connect(tokenAddress, provider);
return [
{
contract: Token,
name: 'balanceOf',
params: [userAddress],
},
{
contract: Token,
name: 'name',
},
{
contract: Token,
name: 'symbol',
},
{
contract: Token,
name: 'decimals',
},
];
})
.flat() as Call3[];
const multicallResults = await multicall(Multicall, [
{
contract: Multicall,
name: 'getEthBalance',
params: [userAddress],
},
...(tokenCalls.length ? tokenCalls : []),
]);
const ethResults = multicallResults[0];
const tokenResults = multicallResults.slice(1).length
? chunk(multicallResults.slice(1), tokenCalls.length / tokenAddresses.length)
: [];
const tokenBalances = tokenResults.map((tokenResult, index) => {
const [tokenBalance, tokenName, tokenSymbol, tokenDecimals] = tokenResult;
const tokenAddress = tokenAddresses[index];
return {
address: tokenAddress,
name: tokenName,
symbol: tokenSymbol,
decimals: Number(tokenDecimals),
balance: tokenBalance,
};
});
return [
{
address: ZeroAddress,
name: currencyName,
symbol: currencyName,
decimals: 18,
balance: ethResults,
},
...tokenBalances,
];
}

113
src/treeCache.ts Normal file
View File

@ -0,0 +1,113 @@
/**
* Create tree cache file from node.js
*
* Only works for node.js, modified from https://github.com/tornadocash/tornado-classic-ui/blob/master/scripts/updateTree.js
*/
import { MerkleTree } from '@tornado/fixed-merkle-tree';
import BloomFilter from 'bloomfilter.js';
import { saveUserFile } from './data';
import { DepositsEvents } from './events';
import type { NetIdType } from './networkConfig';
export interface TreeCacheConstructor {
netId: NetIdType;
amount: string;
currency: string;
userDirectory: string;
PARTS_COUNT?: number;
LEAVES?: number;
zeroElement?: string;
}
export interface treeMetadata {
blockNumber: number;
logIndex: number;
transactionHash: string;
timestamp: number;
from: string;
leafIndex: number;
}
export class TreeCache {
netId: NetIdType;
amount: string;
currency: string;
userDirectory: string;
PARTS_COUNT: number;
constructor({ netId, amount, currency, userDirectory, PARTS_COUNT = 4 }: TreeCacheConstructor) {
this.netId = netId;
this.amount = amount;
this.currency = currency;
this.userDirectory = userDirectory;
this.PARTS_COUNT = PARTS_COUNT;
}
getInstanceName(): string {
return `deposits_${this.netId}_${this.currency}_${this.amount}`;
}
async createTree(events: DepositsEvents[], tree: MerkleTree) {
const bloom = new BloomFilter(events.length);
console.log(`Creating cached tree for ${this.getInstanceName()}\n`);
// events indexed by commitment
const eventsData = events.reduce(
(acc, { leafIndex, commitment, ...rest }, i) => {
if (leafIndex !== i) {
throw new Error(`leafIndex (${leafIndex}) !== i (${i})`);
}
acc[commitment] = { ...rest, leafIndex };
return acc;
},
{} as { [key in string]: treeMetadata },
);
const slices = tree.getTreeSlices(this.PARTS_COUNT);
await Promise.all(
slices.map(async (slice, index) => {
const metadata = slice.elements.reduce((acc, curr) => {
if (index < this.PARTS_COUNT - 1) {
bloom.add(curr);
}
acc.push(eventsData[curr]);
return acc;
}, [] as treeMetadata[]);
const dataString =
JSON.stringify(
{
...slice,
metadata,
},
null,
2,
) + '\n';
const fileName = `${this.getInstanceName()}_slice${index + 1}.json`;
await saveUserFile({
fileName,
userDirectory: this.userDirectory,
dataString,
});
}),
);
const dataString = bloom.serialize() + '\n';
const fileName = `${this.getInstanceName()}_bloom.json`;
await saveUserFile({
fileName,
userDirectory: this.userDirectory,
dataString,
});
}
}

756
src/typechain/ENS.ts Normal file
View File

@ -0,0 +1,756 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumberish,
BytesLike,
FunctionFragment,
Result,
Interface,
EventFragment,
AddressLike,
ContractRunner,
ContractMethod,
Listener,
} from "ethers";
import type {
TypedContractEvent,
TypedDeferredTopicFilter,
TypedEventLog,
TypedLogDescription,
TypedListener,
TypedContractMethod,
} from "./common";
export interface ENSInterface extends Interface {
getFunction(
nameOrSignature:
| "supportsInterface"
| "setText"
| "interfaceImplementer"
| "ABI"
| "setPubkey"
| "setContenthash"
| "addr(bytes32)"
| "addr(bytes32,uint256)"
| "setAuthorisation"
| "text"
| "setABI"
| "name"
| "setName"
| "setAddr(bytes32,uint256,bytes)"
| "setAddr(bytes32,address)"
| "contenthash"
| "pubkey"
| "setInterface"
| "authorisations"
): FunctionFragment;
getEvent(
nameOrSignatureOrTopic:
| "AuthorisationChanged"
| "TextChanged"
| "PubkeyChanged"
| "NameChanged"
| "InterfaceChanged"
| "ContenthashChanged"
| "AddrChanged"
| "AddressChanged"
| "ABIChanged"
): EventFragment;
encodeFunctionData(
functionFragment: "supportsInterface",
values: [BytesLike]
): string;
encodeFunctionData(
functionFragment: "setText",
values: [BytesLike, string, string]
): string;
encodeFunctionData(
functionFragment: "interfaceImplementer",
values: [BytesLike, BytesLike]
): string;
encodeFunctionData(
functionFragment: "ABI",
values: [BytesLike, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setPubkey",
values: [BytesLike, BytesLike, BytesLike]
): string;
encodeFunctionData(
functionFragment: "setContenthash",
values: [BytesLike, BytesLike]
): string;
encodeFunctionData(
functionFragment: "addr(bytes32)",
values: [BytesLike]
): string;
encodeFunctionData(
functionFragment: "addr(bytes32,uint256)",
values: [BytesLike, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setAuthorisation",
values: [BytesLike, AddressLike, boolean]
): string;
encodeFunctionData(
functionFragment: "text",
values: [BytesLike, string]
): string;
encodeFunctionData(
functionFragment: "setABI",
values: [BytesLike, BigNumberish, BytesLike]
): string;
encodeFunctionData(functionFragment: "name", values: [BytesLike]): string;
encodeFunctionData(
functionFragment: "setName",
values: [BytesLike, string]
): string;
encodeFunctionData(
functionFragment: "setAddr(bytes32,uint256,bytes)",
values: [BytesLike, BigNumberish, BytesLike]
): string;
encodeFunctionData(
functionFragment: "setAddr(bytes32,address)",
values: [BytesLike, AddressLike]
): string;
encodeFunctionData(
functionFragment: "contenthash",
values: [BytesLike]
): string;
encodeFunctionData(functionFragment: "pubkey", values: [BytesLike]): string;
encodeFunctionData(
functionFragment: "setInterface",
values: [BytesLike, BytesLike, AddressLike]
): string;
encodeFunctionData(
functionFragment: "authorisations",
values: [BytesLike, AddressLike, AddressLike]
): string;
decodeFunctionResult(
functionFragment: "supportsInterface",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "setText", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "interfaceImplementer",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "ABI", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "setPubkey", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "setContenthash",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "addr(bytes32)",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "addr(bytes32,uint256)",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setAuthorisation",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "text", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "setABI", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "name", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "setName", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "setAddr(bytes32,uint256,bytes)",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setAddr(bytes32,address)",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "contenthash",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "pubkey", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "setInterface",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "authorisations",
data: BytesLike
): Result;
}
export namespace AuthorisationChangedEvent {
export type InputTuple = [
node: BytesLike,
owner: AddressLike,
target: AddressLike,
isAuthorised: boolean
];
export type OutputTuple = [
node: string,
owner: string,
target: string,
isAuthorised: boolean
];
export interface OutputObject {
node: string;
owner: string;
target: string;
isAuthorised: boolean;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace TextChangedEvent {
export type InputTuple = [node: BytesLike, indexedKey: string, key: string];
export type OutputTuple = [node: string, indexedKey: string, key: string];
export interface OutputObject {
node: string;
indexedKey: string;
key: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace PubkeyChangedEvent {
export type InputTuple = [node: BytesLike, x: BytesLike, y: BytesLike];
export type OutputTuple = [node: string, x: string, y: string];
export interface OutputObject {
node: string;
x: string;
y: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace NameChangedEvent {
export type InputTuple = [node: BytesLike, name: string];
export type OutputTuple = [node: string, name: string];
export interface OutputObject {
node: string;
name: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace InterfaceChangedEvent {
export type InputTuple = [
node: BytesLike,
interfaceID: BytesLike,
implementer: AddressLike
];
export type OutputTuple = [
node: string,
interfaceID: string,
implementer: string
];
export interface OutputObject {
node: string;
interfaceID: string;
implementer: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace ContenthashChangedEvent {
export type InputTuple = [node: BytesLike, hash: BytesLike];
export type OutputTuple = [node: string, hash: string];
export interface OutputObject {
node: string;
hash: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace AddrChangedEvent {
export type InputTuple = [node: BytesLike, a: AddressLike];
export type OutputTuple = [node: string, a: string];
export interface OutputObject {
node: string;
a: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace AddressChangedEvent {
export type InputTuple = [
node: BytesLike,
coinType: BigNumberish,
newAddress: BytesLike
];
export type OutputTuple = [
node: string,
coinType: bigint,
newAddress: string
];
export interface OutputObject {
node: string;
coinType: bigint;
newAddress: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace ABIChangedEvent {
export type InputTuple = [node: BytesLike, contentType: BigNumberish];
export type OutputTuple = [node: string, contentType: bigint];
export interface OutputObject {
node: string;
contentType: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export interface ENS extends BaseContract {
connect(runner?: ContractRunner | null): ENS;
waitForDeployment(): Promise<this>;
interface: ENSInterface;
queryFilter<TCEvent extends TypedContractEvent>(
event: TCEvent,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
queryFilter<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
on<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
on<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
listeners<TCEvent extends TypedContractEvent>(
event: TCEvent
): Promise<Array<TypedListener<TCEvent>>>;
listeners(eventName?: string): Promise<Array<Listener>>;
removeAllListeners<TCEvent extends TypedContractEvent>(
event?: TCEvent
): Promise<this>;
supportsInterface: TypedContractMethod<
[interfaceID: BytesLike],
[boolean],
"view"
>;
setText: TypedContractMethod<
[node: BytesLike, key: string, value: string],
[void],
"nonpayable"
>;
interfaceImplementer: TypedContractMethod<
[node: BytesLike, interfaceID: BytesLike],
[string],
"view"
>;
ABI: TypedContractMethod<
[node: BytesLike, contentTypes: BigNumberish],
[[bigint, string]],
"view"
>;
setPubkey: TypedContractMethod<
[node: BytesLike, x: BytesLike, y: BytesLike],
[void],
"nonpayable"
>;
setContenthash: TypedContractMethod<
[node: BytesLike, hash: BytesLike],
[void],
"nonpayable"
>;
"addr(bytes32)": TypedContractMethod<[node: BytesLike], [string], "view">;
"addr(bytes32,uint256)": TypedContractMethod<
[node: BytesLike, coinType: BigNumberish],
[string],
"view"
>;
setAuthorisation: TypedContractMethod<
[node: BytesLike, target: AddressLike, isAuthorised: boolean],
[void],
"nonpayable"
>;
text: TypedContractMethod<[node: BytesLike, key: string], [string], "view">;
setABI: TypedContractMethod<
[node: BytesLike, contentType: BigNumberish, data: BytesLike],
[void],
"nonpayable"
>;
name: TypedContractMethod<[node: BytesLike], [string], "view">;
setName: TypedContractMethod<
[node: BytesLike, name: string],
[void],
"nonpayable"
>;
"setAddr(bytes32,uint256,bytes)": TypedContractMethod<
[node: BytesLike, coinType: BigNumberish, a: BytesLike],
[void],
"nonpayable"
>;
"setAddr(bytes32,address)": TypedContractMethod<
[node: BytesLike, a: AddressLike],
[void],
"nonpayable"
>;
contenthash: TypedContractMethod<[node: BytesLike], [string], "view">;
pubkey: TypedContractMethod<
[node: BytesLike],
[[string, string] & { x: string; y: string }],
"view"
>;
setInterface: TypedContractMethod<
[node: BytesLike, interfaceID: BytesLike, implementer: AddressLike],
[void],
"nonpayable"
>;
authorisations: TypedContractMethod<
[arg0: BytesLike, arg1: AddressLike, arg2: AddressLike],
[boolean],
"view"
>;
getFunction<T extends ContractMethod = ContractMethod>(
key: string | FunctionFragment
): T;
getFunction(
nameOrSignature: "supportsInterface"
): TypedContractMethod<[interfaceID: BytesLike], [boolean], "view">;
getFunction(
nameOrSignature: "setText"
): TypedContractMethod<
[node: BytesLike, key: string, value: string],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "interfaceImplementer"
): TypedContractMethod<
[node: BytesLike, interfaceID: BytesLike],
[string],
"view"
>;
getFunction(
nameOrSignature: "ABI"
): TypedContractMethod<
[node: BytesLike, contentTypes: BigNumberish],
[[bigint, string]],
"view"
>;
getFunction(
nameOrSignature: "setPubkey"
): TypedContractMethod<
[node: BytesLike, x: BytesLike, y: BytesLike],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "setContenthash"
): TypedContractMethod<
[node: BytesLike, hash: BytesLike],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "addr(bytes32)"
): TypedContractMethod<[node: BytesLike], [string], "view">;
getFunction(
nameOrSignature: "addr(bytes32,uint256)"
): TypedContractMethod<
[node: BytesLike, coinType: BigNumberish],
[string],
"view"
>;
getFunction(
nameOrSignature: "setAuthorisation"
): TypedContractMethod<
[node: BytesLike, target: AddressLike, isAuthorised: boolean],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "text"
): TypedContractMethod<[node: BytesLike, key: string], [string], "view">;
getFunction(
nameOrSignature: "setABI"
): TypedContractMethod<
[node: BytesLike, contentType: BigNumberish, data: BytesLike],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "name"
): TypedContractMethod<[node: BytesLike], [string], "view">;
getFunction(
nameOrSignature: "setName"
): TypedContractMethod<[node: BytesLike, name: string], [void], "nonpayable">;
getFunction(
nameOrSignature: "setAddr(bytes32,uint256,bytes)"
): TypedContractMethod<
[node: BytesLike, coinType: BigNumberish, a: BytesLike],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "setAddr(bytes32,address)"
): TypedContractMethod<
[node: BytesLike, a: AddressLike],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "contenthash"
): TypedContractMethod<[node: BytesLike], [string], "view">;
getFunction(
nameOrSignature: "pubkey"
): TypedContractMethod<
[node: BytesLike],
[[string, string] & { x: string; y: string }],
"view"
>;
getFunction(
nameOrSignature: "setInterface"
): TypedContractMethod<
[node: BytesLike, interfaceID: BytesLike, implementer: AddressLike],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "authorisations"
): TypedContractMethod<
[arg0: BytesLike, arg1: AddressLike, arg2: AddressLike],
[boolean],
"view"
>;
getEvent(
key: "AuthorisationChanged"
): TypedContractEvent<
AuthorisationChangedEvent.InputTuple,
AuthorisationChangedEvent.OutputTuple,
AuthorisationChangedEvent.OutputObject
>;
getEvent(
key: "TextChanged"
): TypedContractEvent<
TextChangedEvent.InputTuple,
TextChangedEvent.OutputTuple,
TextChangedEvent.OutputObject
>;
getEvent(
key: "PubkeyChanged"
): TypedContractEvent<
PubkeyChangedEvent.InputTuple,
PubkeyChangedEvent.OutputTuple,
PubkeyChangedEvent.OutputObject
>;
getEvent(
key: "NameChanged"
): TypedContractEvent<
NameChangedEvent.InputTuple,
NameChangedEvent.OutputTuple,
NameChangedEvent.OutputObject
>;
getEvent(
key: "InterfaceChanged"
): TypedContractEvent<
InterfaceChangedEvent.InputTuple,
InterfaceChangedEvent.OutputTuple,
InterfaceChangedEvent.OutputObject
>;
getEvent(
key: "ContenthashChanged"
): TypedContractEvent<
ContenthashChangedEvent.InputTuple,
ContenthashChangedEvent.OutputTuple,
ContenthashChangedEvent.OutputObject
>;
getEvent(
key: "AddrChanged"
): TypedContractEvent<
AddrChangedEvent.InputTuple,
AddrChangedEvent.OutputTuple,
AddrChangedEvent.OutputObject
>;
getEvent(
key: "AddressChanged"
): TypedContractEvent<
AddressChangedEvent.InputTuple,
AddressChangedEvent.OutputTuple,
AddressChangedEvent.OutputObject
>;
getEvent(
key: "ABIChanged"
): TypedContractEvent<
ABIChangedEvent.InputTuple,
ABIChangedEvent.OutputTuple,
ABIChangedEvent.OutputObject
>;
filters: {
"AuthorisationChanged(bytes32,address,address,bool)": TypedContractEvent<
AuthorisationChangedEvent.InputTuple,
AuthorisationChangedEvent.OutputTuple,
AuthorisationChangedEvent.OutputObject
>;
AuthorisationChanged: TypedContractEvent<
AuthorisationChangedEvent.InputTuple,
AuthorisationChangedEvent.OutputTuple,
AuthorisationChangedEvent.OutputObject
>;
"TextChanged(bytes32,string,string)": TypedContractEvent<
TextChangedEvent.InputTuple,
TextChangedEvent.OutputTuple,
TextChangedEvent.OutputObject
>;
TextChanged: TypedContractEvent<
TextChangedEvent.InputTuple,
TextChangedEvent.OutputTuple,
TextChangedEvent.OutputObject
>;
"PubkeyChanged(bytes32,bytes32,bytes32)": TypedContractEvent<
PubkeyChangedEvent.InputTuple,
PubkeyChangedEvent.OutputTuple,
PubkeyChangedEvent.OutputObject
>;
PubkeyChanged: TypedContractEvent<
PubkeyChangedEvent.InputTuple,
PubkeyChangedEvent.OutputTuple,
PubkeyChangedEvent.OutputObject
>;
"NameChanged(bytes32,string)": TypedContractEvent<
NameChangedEvent.InputTuple,
NameChangedEvent.OutputTuple,
NameChangedEvent.OutputObject
>;
NameChanged: TypedContractEvent<
NameChangedEvent.InputTuple,
NameChangedEvent.OutputTuple,
NameChangedEvent.OutputObject
>;
"InterfaceChanged(bytes32,bytes4,address)": TypedContractEvent<
InterfaceChangedEvent.InputTuple,
InterfaceChangedEvent.OutputTuple,
InterfaceChangedEvent.OutputObject
>;
InterfaceChanged: TypedContractEvent<
InterfaceChangedEvent.InputTuple,
InterfaceChangedEvent.OutputTuple,
InterfaceChangedEvent.OutputObject
>;
"ContenthashChanged(bytes32,bytes)": TypedContractEvent<
ContenthashChangedEvent.InputTuple,
ContenthashChangedEvent.OutputTuple,
ContenthashChangedEvent.OutputObject
>;
ContenthashChanged: TypedContractEvent<
ContenthashChangedEvent.InputTuple,
ContenthashChangedEvent.OutputTuple,
ContenthashChangedEvent.OutputObject
>;
"AddrChanged(bytes32,address)": TypedContractEvent<
AddrChangedEvent.InputTuple,
AddrChangedEvent.OutputTuple,
AddrChangedEvent.OutputObject
>;
AddrChanged: TypedContractEvent<
AddrChangedEvent.InputTuple,
AddrChangedEvent.OutputTuple,
AddrChangedEvent.OutputObject
>;
"AddressChanged(bytes32,uint256,bytes)": TypedContractEvent<
AddressChangedEvent.InputTuple,
AddressChangedEvent.OutputTuple,
AddressChangedEvent.OutputObject
>;
AddressChanged: TypedContractEvent<
AddressChangedEvent.InputTuple,
AddressChangedEvent.OutputTuple,
AddressChangedEvent.OutputObject
>;
"ABIChanged(bytes32,uint256)": TypedContractEvent<
ABIChangedEvent.InputTuple,
ABIChangedEvent.OutputTuple,
ABIChangedEvent.OutputObject
>;
ABIChanged: TypedContractEvent<
ABIChangedEvent.InputTuple,
ABIChangedEvent.OutputTuple,
ABIChangedEvent.OutputObject
>;
};
}

351
src/typechain/ERC20.ts Normal file
View File

@ -0,0 +1,351 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumberish,
BytesLike,
FunctionFragment,
Result,
Interface,
EventFragment,
AddressLike,
ContractRunner,
ContractMethod,
Listener,
} from "ethers";
import type {
TypedContractEvent,
TypedDeferredTopicFilter,
TypedEventLog,
TypedLogDescription,
TypedListener,
TypedContractMethod,
} from "./common";
export interface ERC20Interface extends Interface {
getFunction(
nameOrSignature:
| "totalSupply"
| "_totalSupply"
| "balanceOf"
| "name"
| "symbol"
| "decimals"
| "transfer"
| "allowance"
| "transferFrom"
| "approve"
| "nonces"
| "permit"
): FunctionFragment;
getEvent(nameOrSignatureOrTopic: "Approval" | "Transfer"): EventFragment;
encodeFunctionData(
functionFragment: "totalSupply",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "_totalSupply",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "balanceOf",
values: [AddressLike]
): string;
encodeFunctionData(functionFragment: "name", values?: undefined): string;
encodeFunctionData(functionFragment: "symbol", values?: undefined): string;
encodeFunctionData(functionFragment: "decimals", values?: undefined): string;
encodeFunctionData(
functionFragment: "transfer",
values: [AddressLike, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "allowance",
values: [AddressLike, AddressLike]
): string;
encodeFunctionData(
functionFragment: "transferFrom",
values: [AddressLike, AddressLike, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "approve",
values: [AddressLike, BigNumberish]
): string;
encodeFunctionData(functionFragment: "nonces", values: [AddressLike]): string;
encodeFunctionData(
functionFragment: "permit",
values: [
AddressLike,
AddressLike,
BigNumberish,
BigNumberish,
BigNumberish,
BytesLike,
BytesLike
]
): string;
decodeFunctionResult(
functionFragment: "totalSupply",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "_totalSupply",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "balanceOf", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "name", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "symbol", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "decimals", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "transfer", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "allowance", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "transferFrom",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "approve", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "nonces", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "permit", data: BytesLike): Result;
}
export namespace ApprovalEvent {
export type InputTuple = [
owner: AddressLike,
spender: AddressLike,
value: BigNumberish
];
export type OutputTuple = [owner: string, spender: string, value: bigint];
export interface OutputObject {
owner: string;
spender: string;
value: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace TransferEvent {
export type InputTuple = [
from: AddressLike,
to: AddressLike,
value: BigNumberish
];
export type OutputTuple = [from: string, to: string, value: bigint];
export interface OutputObject {
from: string;
to: string;
value: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export interface ERC20 extends BaseContract {
connect(runner?: ContractRunner | null): ERC20;
waitForDeployment(): Promise<this>;
interface: ERC20Interface;
queryFilter<TCEvent extends TypedContractEvent>(
event: TCEvent,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
queryFilter<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
on<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
on<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
listeners<TCEvent extends TypedContractEvent>(
event: TCEvent
): Promise<Array<TypedListener<TCEvent>>>;
listeners(eventName?: string): Promise<Array<Listener>>;
removeAllListeners<TCEvent extends TypedContractEvent>(
event?: TCEvent
): Promise<this>;
totalSupply: TypedContractMethod<[], [bigint], "view">;
_totalSupply: TypedContractMethod<[], [bigint], "view">;
balanceOf: TypedContractMethod<[who: AddressLike], [bigint], "view">;
name: TypedContractMethod<[], [string], "view">;
symbol: TypedContractMethod<[], [string], "view">;
decimals: TypedContractMethod<[], [bigint], "view">;
transfer: TypedContractMethod<
[to: AddressLike, value: BigNumberish],
[void],
"nonpayable"
>;
allowance: TypedContractMethod<
[owner: AddressLike, spender: AddressLike],
[bigint],
"view"
>;
transferFrom: TypedContractMethod<
[from: AddressLike, to: AddressLike, value: BigNumberish],
[void],
"nonpayable"
>;
approve: TypedContractMethod<
[spender: AddressLike, value: BigNumberish],
[void],
"nonpayable"
>;
nonces: TypedContractMethod<[owner: AddressLike], [bigint], "view">;
permit: TypedContractMethod<
[
owner: AddressLike,
spender: AddressLike,
amount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: BytesLike,
s: BytesLike
],
[void],
"nonpayable"
>;
getFunction<T extends ContractMethod = ContractMethod>(
key: string | FunctionFragment
): T;
getFunction(
nameOrSignature: "totalSupply"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "_totalSupply"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "balanceOf"
): TypedContractMethod<[who: AddressLike], [bigint], "view">;
getFunction(
nameOrSignature: "name"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "symbol"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "decimals"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "transfer"
): TypedContractMethod<
[to: AddressLike, value: BigNumberish],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "allowance"
): TypedContractMethod<
[owner: AddressLike, spender: AddressLike],
[bigint],
"view"
>;
getFunction(
nameOrSignature: "transferFrom"
): TypedContractMethod<
[from: AddressLike, to: AddressLike, value: BigNumberish],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "approve"
): TypedContractMethod<
[spender: AddressLike, value: BigNumberish],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "nonces"
): TypedContractMethod<[owner: AddressLike], [bigint], "view">;
getFunction(
nameOrSignature: "permit"
): TypedContractMethod<
[
owner: AddressLike,
spender: AddressLike,
amount: BigNumberish,
deadline: BigNumberish,
v: BigNumberish,
r: BytesLike,
s: BytesLike
],
[void],
"nonpayable"
>;
getEvent(
key: "Approval"
): TypedContractEvent<
ApprovalEvent.InputTuple,
ApprovalEvent.OutputTuple,
ApprovalEvent.OutputObject
>;
getEvent(
key: "Transfer"
): TypedContractEvent<
TransferEvent.InputTuple,
TransferEvent.OutputTuple,
TransferEvent.OutputObject
>;
filters: {
"Approval(address,address,uint256)": TypedContractEvent<
ApprovalEvent.InputTuple,
ApprovalEvent.OutputTuple,
ApprovalEvent.OutputObject
>;
Approval: TypedContractEvent<
ApprovalEvent.InputTuple,
ApprovalEvent.OutputTuple,
ApprovalEvent.OutputObject
>;
"Transfer(address,address,uint256)": TypedContractEvent<
TransferEvent.InputTuple,
TransferEvent.OutputTuple,
TransferEvent.OutputObject
>;
Transfer: TypedContractEvent<
TransferEvent.InputTuple,
TransferEvent.OutputTuple,
TransferEvent.OutputObject
>;
};
}

View File

@ -0,0 +1,271 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumberish,
BytesLike,
FunctionFragment,
Result,
Interface,
AddressLike,
ContractRunner,
ContractMethod,
Listener,
} from "ethers";
import type {
TypedContractEvent,
TypedDeferredTopicFilter,
TypedEventLog,
TypedListener,
TypedContractMethod,
} from "./common";
export interface GasPriceOracleInterface extends Interface {
getFunction(
nameOrSignature:
| "GAS_UNIT"
| "changeDerivationThresold"
| "changeGasUnit"
| "changeHeartbeat"
| "changeOwnership"
| "derivationThresold"
| "gasPrice"
| "heartbeat"
| "maxFeePerGas"
| "maxPriorityFeePerGas"
| "owner"
| "pastGasPrice"
| "setGasPrice"
| "timestamp"
): FunctionFragment;
encodeFunctionData(functionFragment: "GAS_UNIT", values?: undefined): string;
encodeFunctionData(
functionFragment: "changeDerivationThresold",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "changeGasUnit",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "changeHeartbeat",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "changeOwnership",
values: [AddressLike]
): string;
encodeFunctionData(
functionFragment: "derivationThresold",
values?: undefined
): string;
encodeFunctionData(functionFragment: "gasPrice", values?: undefined): string;
encodeFunctionData(functionFragment: "heartbeat", values?: undefined): string;
encodeFunctionData(
functionFragment: "maxFeePerGas",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "maxPriorityFeePerGas",
values?: undefined
): string;
encodeFunctionData(functionFragment: "owner", values?: undefined): string;
encodeFunctionData(
functionFragment: "pastGasPrice",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "setGasPrice",
values: [BigNumberish]
): string;
encodeFunctionData(functionFragment: "timestamp", values?: undefined): string;
decodeFunctionResult(functionFragment: "GAS_UNIT", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "changeDerivationThresold",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "changeGasUnit",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "changeHeartbeat",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "changeOwnership",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "derivationThresold",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "gasPrice", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "heartbeat", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "maxFeePerGas",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "maxPriorityFeePerGas",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "pastGasPrice",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setGasPrice",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "timestamp", data: BytesLike): Result;
}
export interface GasPriceOracle extends BaseContract {
connect(runner?: ContractRunner | null): GasPriceOracle;
waitForDeployment(): Promise<this>;
interface: GasPriceOracleInterface;
queryFilter<TCEvent extends TypedContractEvent>(
event: TCEvent,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
queryFilter<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
on<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
on<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
listeners<TCEvent extends TypedContractEvent>(
event: TCEvent
): Promise<Array<TypedListener<TCEvent>>>;
listeners(eventName?: string): Promise<Array<Listener>>;
removeAllListeners<TCEvent extends TypedContractEvent>(
event?: TCEvent
): Promise<this>;
GAS_UNIT: TypedContractMethod<[], [bigint], "view">;
changeDerivationThresold: TypedContractMethod<
[_derivationThresold: BigNumberish],
[void],
"nonpayable"
>;
changeGasUnit: TypedContractMethod<
[_gasUnit: BigNumberish],
[void],
"nonpayable"
>;
changeHeartbeat: TypedContractMethod<
[_heartbeat: BigNumberish],
[void],
"nonpayable"
>;
changeOwnership: TypedContractMethod<
[_owner: AddressLike],
[void],
"nonpayable"
>;
derivationThresold: TypedContractMethod<[], [bigint], "view">;
gasPrice: TypedContractMethod<[], [bigint], "view">;
heartbeat: TypedContractMethod<[], [bigint], "view">;
maxFeePerGas: TypedContractMethod<[], [bigint], "view">;
maxPriorityFeePerGas: TypedContractMethod<[], [bigint], "view">;
owner: TypedContractMethod<[], [string], "view">;
pastGasPrice: TypedContractMethod<[], [bigint], "view">;
setGasPrice: TypedContractMethod<
[_gasPrice: BigNumberish],
[void],
"nonpayable"
>;
timestamp: TypedContractMethod<[], [bigint], "view">;
getFunction<T extends ContractMethod = ContractMethod>(
key: string | FunctionFragment
): T;
getFunction(
nameOrSignature: "GAS_UNIT"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "changeDerivationThresold"
): TypedContractMethod<
[_derivationThresold: BigNumberish],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "changeGasUnit"
): TypedContractMethod<[_gasUnit: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "changeHeartbeat"
): TypedContractMethod<[_heartbeat: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "changeOwnership"
): TypedContractMethod<[_owner: AddressLike], [void], "nonpayable">;
getFunction(
nameOrSignature: "derivationThresold"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "gasPrice"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "heartbeat"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "maxFeePerGas"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "maxPriorityFeePerGas"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "owner"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "pastGasPrice"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "setGasPrice"
): TypedContractMethod<[_gasPrice: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "timestamp"
): TypedContractMethod<[], [bigint], "view">;
filters: {};
}

416
src/typechain/Multicall.ts Normal file
View File

@ -0,0 +1,416 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumberish,
BytesLike,
FunctionFragment,
Result,
Interface,
AddressLike,
ContractRunner,
ContractMethod,
Listener,
} from "ethers";
import type {
TypedContractEvent,
TypedDeferredTopicFilter,
TypedEventLog,
TypedListener,
TypedContractMethod,
} from "./common";
export declare namespace Multicall3 {
export type CallStruct = { target: AddressLike; callData: BytesLike };
export type CallStructOutput = [target: string, callData: string] & {
target: string;
callData: string;
};
export type Call3Struct = {
target: AddressLike;
allowFailure: boolean;
callData: BytesLike;
};
export type Call3StructOutput = [
target: string,
allowFailure: boolean,
callData: string
] & { target: string; allowFailure: boolean; callData: string };
export type ResultStruct = { success: boolean; returnData: BytesLike };
export type ResultStructOutput = [success: boolean, returnData: string] & {
success: boolean;
returnData: string;
};
export type Call3ValueStruct = {
target: AddressLike;
allowFailure: boolean;
value: BigNumberish;
callData: BytesLike;
};
export type Call3ValueStructOutput = [
target: string,
allowFailure: boolean,
value: bigint,
callData: string
] & {
target: string;
allowFailure: boolean;
value: bigint;
callData: string;
};
}
export interface MulticallInterface extends Interface {
getFunction(
nameOrSignature:
| "aggregate"
| "aggregate3"
| "aggregate3Value"
| "blockAndAggregate"
| "getBasefee"
| "getBlockHash"
| "getBlockNumber"
| "getChainId"
| "getCurrentBlockCoinbase"
| "getCurrentBlockDifficulty"
| "getCurrentBlockGasLimit"
| "getCurrentBlockTimestamp"
| "getEthBalance"
| "getLastBlockHash"
| "tryAggregate"
| "tryBlockAndAggregate"
): FunctionFragment;
encodeFunctionData(
functionFragment: "aggregate",
values: [Multicall3.CallStruct[]]
): string;
encodeFunctionData(
functionFragment: "aggregate3",
values: [Multicall3.Call3Struct[]]
): string;
encodeFunctionData(
functionFragment: "aggregate3Value",
values: [Multicall3.Call3ValueStruct[]]
): string;
encodeFunctionData(
functionFragment: "blockAndAggregate",
values: [Multicall3.CallStruct[]]
): string;
encodeFunctionData(
functionFragment: "getBasefee",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getBlockHash",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "getBlockNumber",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getChainId",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getCurrentBlockCoinbase",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getCurrentBlockDifficulty",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getCurrentBlockGasLimit",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getCurrentBlockTimestamp",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getEthBalance",
values: [AddressLike]
): string;
encodeFunctionData(
functionFragment: "getLastBlockHash",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "tryAggregate",
values: [boolean, Multicall3.CallStruct[]]
): string;
encodeFunctionData(
functionFragment: "tryBlockAndAggregate",
values: [boolean, Multicall3.CallStruct[]]
): string;
decodeFunctionResult(functionFragment: "aggregate", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "aggregate3", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "aggregate3Value",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "blockAndAggregate",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "getBasefee", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getBlockHash",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getBlockNumber",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "getChainId", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getCurrentBlockCoinbase",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getCurrentBlockDifficulty",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getCurrentBlockGasLimit",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getCurrentBlockTimestamp",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getEthBalance",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getLastBlockHash",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "tryAggregate",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "tryBlockAndAggregate",
data: BytesLike
): Result;
}
export interface Multicall extends BaseContract {
connect(runner?: ContractRunner | null): Multicall;
waitForDeployment(): Promise<this>;
interface: MulticallInterface;
queryFilter<TCEvent extends TypedContractEvent>(
event: TCEvent,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
queryFilter<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
on<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
on<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
listeners<TCEvent extends TypedContractEvent>(
event: TCEvent
): Promise<Array<TypedListener<TCEvent>>>;
listeners(eventName?: string): Promise<Array<Listener>>;
removeAllListeners<TCEvent extends TypedContractEvent>(
event?: TCEvent
): Promise<this>;
aggregate: TypedContractMethod<
[calls: Multicall3.CallStruct[]],
[[bigint, string[]] & { blockNumber: bigint; returnData: string[] }],
"payable"
>;
aggregate3: TypedContractMethod<
[calls: Multicall3.Call3Struct[]],
[Multicall3.ResultStructOutput[]],
"payable"
>;
aggregate3Value: TypedContractMethod<
[calls: Multicall3.Call3ValueStruct[]],
[Multicall3.ResultStructOutput[]],
"payable"
>;
blockAndAggregate: TypedContractMethod<
[calls: Multicall3.CallStruct[]],
[
[bigint, string, Multicall3.ResultStructOutput[]] & {
blockNumber: bigint;
blockHash: string;
returnData: Multicall3.ResultStructOutput[];
}
],
"payable"
>;
getBasefee: TypedContractMethod<[], [bigint], "view">;
getBlockHash: TypedContractMethod<
[blockNumber: BigNumberish],
[string],
"view"
>;
getBlockNumber: TypedContractMethod<[], [bigint], "view">;
getChainId: TypedContractMethod<[], [bigint], "view">;
getCurrentBlockCoinbase: TypedContractMethod<[], [string], "view">;
getCurrentBlockDifficulty: TypedContractMethod<[], [bigint], "view">;
getCurrentBlockGasLimit: TypedContractMethod<[], [bigint], "view">;
getCurrentBlockTimestamp: TypedContractMethod<[], [bigint], "view">;
getEthBalance: TypedContractMethod<[addr: AddressLike], [bigint], "view">;
getLastBlockHash: TypedContractMethod<[], [string], "view">;
tryAggregate: TypedContractMethod<
[requireSuccess: boolean, calls: Multicall3.CallStruct[]],
[Multicall3.ResultStructOutput[]],
"payable"
>;
tryBlockAndAggregate: TypedContractMethod<
[requireSuccess: boolean, calls: Multicall3.CallStruct[]],
[
[bigint, string, Multicall3.ResultStructOutput[]] & {
blockNumber: bigint;
blockHash: string;
returnData: Multicall3.ResultStructOutput[];
}
],
"payable"
>;
getFunction<T extends ContractMethod = ContractMethod>(
key: string | FunctionFragment
): T;
getFunction(
nameOrSignature: "aggregate"
): TypedContractMethod<
[calls: Multicall3.CallStruct[]],
[[bigint, string[]] & { blockNumber: bigint; returnData: string[] }],
"payable"
>;
getFunction(
nameOrSignature: "aggregate3"
): TypedContractMethod<
[calls: Multicall3.Call3Struct[]],
[Multicall3.ResultStructOutput[]],
"payable"
>;
getFunction(
nameOrSignature: "aggregate3Value"
): TypedContractMethod<
[calls: Multicall3.Call3ValueStruct[]],
[Multicall3.ResultStructOutput[]],
"payable"
>;
getFunction(
nameOrSignature: "blockAndAggregate"
): TypedContractMethod<
[calls: Multicall3.CallStruct[]],
[
[bigint, string, Multicall3.ResultStructOutput[]] & {
blockNumber: bigint;
blockHash: string;
returnData: Multicall3.ResultStructOutput[];
}
],
"payable"
>;
getFunction(
nameOrSignature: "getBasefee"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "getBlockHash"
): TypedContractMethod<[blockNumber: BigNumberish], [string], "view">;
getFunction(
nameOrSignature: "getBlockNumber"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "getChainId"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "getCurrentBlockCoinbase"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "getCurrentBlockDifficulty"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "getCurrentBlockGasLimit"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "getCurrentBlockTimestamp"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "getEthBalance"
): TypedContractMethod<[addr: AddressLike], [bigint], "view">;
getFunction(
nameOrSignature: "getLastBlockHash"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "tryAggregate"
): TypedContractMethod<
[requireSuccess: boolean, calls: Multicall3.CallStruct[]],
[Multicall3.ResultStructOutput[]],
"payable"
>;
getFunction(
nameOrSignature: "tryBlockAndAggregate"
): TypedContractMethod<
[requireSuccess: boolean, calls: Multicall3.CallStruct[]],
[
[bigint, string, Multicall3.ResultStructOutput[]] & {
blockNumber: bigint;
blockHash: string;
returnData: Multicall3.ResultStructOutput[];
}
],
"payable"
>;
filters: {};
}

View File

@ -0,0 +1,622 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumberish,
BytesLike,
FunctionFragment,
Result,
Interface,
EventFragment,
AddressLike,
ContractRunner,
ContractMethod,
Listener,
} from "ethers";
import type {
TypedContractEvent,
TypedDeferredTopicFilter,
TypedEventLog,
TypedLogDescription,
TypedListener,
TypedContractMethod,
} from "./common";
export interface OffchainOracleInterface extends Interface {
getFunction(
nameOrSignature:
| "addConnector"
| "addOracle"
| "connectors"
| "getRate"
| "getRateToEth"
| "getRateToEthWithCustomConnectors"
| "getRateToEthWithThreshold"
| "getRateWithCustomConnectors"
| "getRateWithThreshold"
| "multiWrapper"
| "oracles"
| "owner"
| "removeConnector"
| "removeOracle"
| "renounceOwnership"
| "setMultiWrapper"
| "transferOwnership"
): FunctionFragment;
getEvent(
nameOrSignatureOrTopic:
| "ConnectorAdded"
| "ConnectorRemoved"
| "MultiWrapperUpdated"
| "OracleAdded"
| "OracleRemoved"
| "OwnershipTransferred"
): EventFragment;
encodeFunctionData(
functionFragment: "addConnector",
values: [AddressLike]
): string;
encodeFunctionData(
functionFragment: "addOracle",
values: [AddressLike, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "connectors",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "getRate",
values: [AddressLike, AddressLike, boolean]
): string;
encodeFunctionData(
functionFragment: "getRateToEth",
values: [AddressLike, boolean]
): string;
encodeFunctionData(
functionFragment: "getRateToEthWithCustomConnectors",
values: [AddressLike, boolean, AddressLike[], BigNumberish]
): string;
encodeFunctionData(
functionFragment: "getRateToEthWithThreshold",
values: [AddressLike, boolean, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "getRateWithCustomConnectors",
values: [AddressLike, AddressLike, boolean, AddressLike[], BigNumberish]
): string;
encodeFunctionData(
functionFragment: "getRateWithThreshold",
values: [AddressLike, AddressLike, boolean, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "multiWrapper",
values?: undefined
): string;
encodeFunctionData(functionFragment: "oracles", values?: undefined): string;
encodeFunctionData(functionFragment: "owner", values?: undefined): string;
encodeFunctionData(
functionFragment: "removeConnector",
values: [AddressLike]
): string;
encodeFunctionData(
functionFragment: "removeOracle",
values: [AddressLike, BigNumberish]
): string;
encodeFunctionData(
functionFragment: "renounceOwnership",
values?: undefined
): string;
encodeFunctionData(
functionFragment: "setMultiWrapper",
values: [AddressLike]
): string;
encodeFunctionData(
functionFragment: "transferOwnership",
values: [AddressLike]
): string;
decodeFunctionResult(
functionFragment: "addConnector",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "addOracle", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "connectors", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "getRate", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getRateToEth",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getRateToEthWithCustomConnectors",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getRateToEthWithThreshold",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getRateWithCustomConnectors",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "getRateWithThreshold",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "multiWrapper",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "oracles", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "removeConnector",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "removeOracle",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "renounceOwnership",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setMultiWrapper",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "transferOwnership",
data: BytesLike
): Result;
}
export namespace ConnectorAddedEvent {
export type InputTuple = [connector: AddressLike];
export type OutputTuple = [connector: string];
export interface OutputObject {
connector: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace ConnectorRemovedEvent {
export type InputTuple = [connector: AddressLike];
export type OutputTuple = [connector: string];
export interface OutputObject {
connector: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace MultiWrapperUpdatedEvent {
export type InputTuple = [multiWrapper: AddressLike];
export type OutputTuple = [multiWrapper: string];
export interface OutputObject {
multiWrapper: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace OracleAddedEvent {
export type InputTuple = [oracle: AddressLike, oracleType: BigNumberish];
export type OutputTuple = [oracle: string, oracleType: bigint];
export interface OutputObject {
oracle: string;
oracleType: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace OracleRemovedEvent {
export type InputTuple = [oracle: AddressLike, oracleType: BigNumberish];
export type OutputTuple = [oracle: string, oracleType: bigint];
export interface OutputObject {
oracle: string;
oracleType: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace OwnershipTransferredEvent {
export type InputTuple = [previousOwner: AddressLike, newOwner: AddressLike];
export type OutputTuple = [previousOwner: string, newOwner: string];
export interface OutputObject {
previousOwner: string;
newOwner: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export interface OffchainOracle extends BaseContract {
connect(runner?: ContractRunner | null): OffchainOracle;
waitForDeployment(): Promise<this>;
interface: OffchainOracleInterface;
queryFilter<TCEvent extends TypedContractEvent>(
event: TCEvent,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
queryFilter<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
on<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
on<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
listeners<TCEvent extends TypedContractEvent>(
event: TCEvent
): Promise<Array<TypedListener<TCEvent>>>;
listeners(eventName?: string): Promise<Array<Listener>>;
removeAllListeners<TCEvent extends TypedContractEvent>(
event?: TCEvent
): Promise<this>;
addConnector: TypedContractMethod<
[connector: AddressLike],
[void],
"nonpayable"
>;
addOracle: TypedContractMethod<
[oracle: AddressLike, oracleKind: BigNumberish],
[void],
"nonpayable"
>;
connectors: TypedContractMethod<[], [string[]], "view">;
getRate: TypedContractMethod<
[srcToken: AddressLike, dstToken: AddressLike, useWrappers: boolean],
[bigint],
"view"
>;
getRateToEth: TypedContractMethod<
[srcToken: AddressLike, useSrcWrappers: boolean],
[bigint],
"view"
>;
getRateToEthWithCustomConnectors: TypedContractMethod<
[
srcToken: AddressLike,
useSrcWrappers: boolean,
customConnectors: AddressLike[],
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
getRateToEthWithThreshold: TypedContractMethod<
[
srcToken: AddressLike,
useSrcWrappers: boolean,
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
getRateWithCustomConnectors: TypedContractMethod<
[
srcToken: AddressLike,
dstToken: AddressLike,
useWrappers: boolean,
customConnectors: AddressLike[],
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
getRateWithThreshold: TypedContractMethod<
[
srcToken: AddressLike,
dstToken: AddressLike,
useWrappers: boolean,
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
multiWrapper: TypedContractMethod<[], [string], "view">;
oracles: TypedContractMethod<
[],
[[string[], bigint[]] & { allOracles: string[]; oracleTypes: bigint[] }],
"view"
>;
owner: TypedContractMethod<[], [string], "view">;
removeConnector: TypedContractMethod<
[connector: AddressLike],
[void],
"nonpayable"
>;
removeOracle: TypedContractMethod<
[oracle: AddressLike, oracleKind: BigNumberish],
[void],
"nonpayable"
>;
renounceOwnership: TypedContractMethod<[], [void], "nonpayable">;
setMultiWrapper: TypedContractMethod<
[_multiWrapper: AddressLike],
[void],
"nonpayable"
>;
transferOwnership: TypedContractMethod<
[newOwner: AddressLike],
[void],
"nonpayable"
>;
getFunction<T extends ContractMethod = ContractMethod>(
key: string | FunctionFragment
): T;
getFunction(
nameOrSignature: "addConnector"
): TypedContractMethod<[connector: AddressLike], [void], "nonpayable">;
getFunction(
nameOrSignature: "addOracle"
): TypedContractMethod<
[oracle: AddressLike, oracleKind: BigNumberish],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "connectors"
): TypedContractMethod<[], [string[]], "view">;
getFunction(
nameOrSignature: "getRate"
): TypedContractMethod<
[srcToken: AddressLike, dstToken: AddressLike, useWrappers: boolean],
[bigint],
"view"
>;
getFunction(
nameOrSignature: "getRateToEth"
): TypedContractMethod<
[srcToken: AddressLike, useSrcWrappers: boolean],
[bigint],
"view"
>;
getFunction(
nameOrSignature: "getRateToEthWithCustomConnectors"
): TypedContractMethod<
[
srcToken: AddressLike,
useSrcWrappers: boolean,
customConnectors: AddressLike[],
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
getFunction(
nameOrSignature: "getRateToEthWithThreshold"
): TypedContractMethod<
[
srcToken: AddressLike,
useSrcWrappers: boolean,
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
getFunction(
nameOrSignature: "getRateWithCustomConnectors"
): TypedContractMethod<
[
srcToken: AddressLike,
dstToken: AddressLike,
useWrappers: boolean,
customConnectors: AddressLike[],
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
getFunction(
nameOrSignature: "getRateWithThreshold"
): TypedContractMethod<
[
srcToken: AddressLike,
dstToken: AddressLike,
useWrappers: boolean,
thresholdFilter: BigNumberish
],
[bigint],
"view"
>;
getFunction(
nameOrSignature: "multiWrapper"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "oracles"
): TypedContractMethod<
[],
[[string[], bigint[]] & { allOracles: string[]; oracleTypes: bigint[] }],
"view"
>;
getFunction(
nameOrSignature: "owner"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "removeConnector"
): TypedContractMethod<[connector: AddressLike], [void], "nonpayable">;
getFunction(
nameOrSignature: "removeOracle"
): TypedContractMethod<
[oracle: AddressLike, oracleKind: BigNumberish],
[void],
"nonpayable"
>;
getFunction(
nameOrSignature: "renounceOwnership"
): TypedContractMethod<[], [void], "nonpayable">;
getFunction(
nameOrSignature: "setMultiWrapper"
): TypedContractMethod<[_multiWrapper: AddressLike], [void], "nonpayable">;
getFunction(
nameOrSignature: "transferOwnership"
): TypedContractMethod<[newOwner: AddressLike], [void], "nonpayable">;
getEvent(
key: "ConnectorAdded"
): TypedContractEvent<
ConnectorAddedEvent.InputTuple,
ConnectorAddedEvent.OutputTuple,
ConnectorAddedEvent.OutputObject
>;
getEvent(
key: "ConnectorRemoved"
): TypedContractEvent<
ConnectorRemovedEvent.InputTuple,
ConnectorRemovedEvent.OutputTuple,
ConnectorRemovedEvent.OutputObject
>;
getEvent(
key: "MultiWrapperUpdated"
): TypedContractEvent<
MultiWrapperUpdatedEvent.InputTuple,
MultiWrapperUpdatedEvent.OutputTuple,
MultiWrapperUpdatedEvent.OutputObject
>;
getEvent(
key: "OracleAdded"
): TypedContractEvent<
OracleAddedEvent.InputTuple,
OracleAddedEvent.OutputTuple,
OracleAddedEvent.OutputObject
>;
getEvent(
key: "OracleRemoved"
): TypedContractEvent<
OracleRemovedEvent.InputTuple,
OracleRemovedEvent.OutputTuple,
OracleRemovedEvent.OutputObject
>;
getEvent(
key: "OwnershipTransferred"
): TypedContractEvent<
OwnershipTransferredEvent.InputTuple,
OwnershipTransferredEvent.OutputTuple,
OwnershipTransferredEvent.OutputObject
>;
filters: {
"ConnectorAdded(address)": TypedContractEvent<
ConnectorAddedEvent.InputTuple,
ConnectorAddedEvent.OutputTuple,
ConnectorAddedEvent.OutputObject
>;
ConnectorAdded: TypedContractEvent<
ConnectorAddedEvent.InputTuple,
ConnectorAddedEvent.OutputTuple,
ConnectorAddedEvent.OutputObject
>;
"ConnectorRemoved(address)": TypedContractEvent<
ConnectorRemovedEvent.InputTuple,
ConnectorRemovedEvent.OutputTuple,
ConnectorRemovedEvent.OutputObject
>;
ConnectorRemoved: TypedContractEvent<
ConnectorRemovedEvent.InputTuple,
ConnectorRemovedEvent.OutputTuple,
ConnectorRemovedEvent.OutputObject
>;
"MultiWrapperUpdated(address)": TypedContractEvent<
MultiWrapperUpdatedEvent.InputTuple,
MultiWrapperUpdatedEvent.OutputTuple,
MultiWrapperUpdatedEvent.OutputObject
>;
MultiWrapperUpdated: TypedContractEvent<
MultiWrapperUpdatedEvent.InputTuple,
MultiWrapperUpdatedEvent.OutputTuple,
MultiWrapperUpdatedEvent.OutputObject
>;
"OracleAdded(address,uint8)": TypedContractEvent<
OracleAddedEvent.InputTuple,
OracleAddedEvent.OutputTuple,
OracleAddedEvent.OutputObject
>;
OracleAdded: TypedContractEvent<
OracleAddedEvent.InputTuple,
OracleAddedEvent.OutputTuple,
OracleAddedEvent.OutputObject
>;
"OracleRemoved(address,uint8)": TypedContractEvent<
OracleRemovedEvent.InputTuple,
OracleRemovedEvent.OutputTuple,
OracleRemovedEvent.OutputObject
>;
OracleRemoved: TypedContractEvent<
OracleRemovedEvent.InputTuple,
OracleRemovedEvent.OutputTuple,
OracleRemovedEvent.OutputObject
>;
"OwnershipTransferred(address,address)": TypedContractEvent<
OwnershipTransferredEvent.InputTuple,
OwnershipTransferredEvent.OutputTuple,
OwnershipTransferredEvent.OutputObject
>;
OwnershipTransferred: TypedContractEvent<
OwnershipTransferredEvent.InputTuple,
OwnershipTransferredEvent.OutputTuple,
OwnershipTransferredEvent.OutputObject
>;
};
}

View File

@ -0,0 +1,460 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BigNumberish,
BytesLike,
FunctionFragment,
Result,
Interface,
EventFragment,
AddressLike,
ContractRunner,
ContractMethod,
Listener,
} from "ethers";
import type {
TypedContractEvent,
TypedDeferredTopicFilter,
TypedEventLog,
TypedLogDescription,
TypedListener,
TypedContractMethod,
} from "./common";
export interface OvmGasPriceOracleInterface extends Interface {
getFunction(
nameOrSignature:
| "decimals"
| "gasPrice"
| "getL1Fee"
| "getL1GasUsed"
| "l1BaseFee"
| "overhead"
| "owner"
| "renounceOwnership"
| "scalar"
| "setDecimals"
| "setGasPrice"
| "setL1BaseFee"
| "setOverhead"
| "setScalar"
| "transferOwnership"
): FunctionFragment;
getEvent(
nameOrSignatureOrTopic:
| "DecimalsUpdated"
| "GasPriceUpdated"
| "L1BaseFeeUpdated"
| "OverheadUpdated"
| "OwnershipTransferred"
| "ScalarUpdated"
): EventFragment;
encodeFunctionData(functionFragment: "decimals", values?: undefined): string;
encodeFunctionData(functionFragment: "gasPrice", values?: undefined): string;
encodeFunctionData(functionFragment: "getL1Fee", values: [BytesLike]): string;
encodeFunctionData(
functionFragment: "getL1GasUsed",
values: [BytesLike]
): string;
encodeFunctionData(functionFragment: "l1BaseFee", values?: undefined): string;
encodeFunctionData(functionFragment: "overhead", values?: undefined): string;
encodeFunctionData(functionFragment: "owner", values?: undefined): string;
encodeFunctionData(
functionFragment: "renounceOwnership",
values?: undefined
): string;
encodeFunctionData(functionFragment: "scalar", values?: undefined): string;
encodeFunctionData(
functionFragment: "setDecimals",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setGasPrice",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setL1BaseFee",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setOverhead",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "setScalar",
values: [BigNumberish]
): string;
encodeFunctionData(
functionFragment: "transferOwnership",
values: [AddressLike]
): string;
decodeFunctionResult(functionFragment: "decimals", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "gasPrice", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "getL1Fee", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "getL1GasUsed",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "l1BaseFee", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "overhead", data: BytesLike): Result;
decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "renounceOwnership",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "scalar", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "setDecimals",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setGasPrice",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setL1BaseFee",
data: BytesLike
): Result;
decodeFunctionResult(
functionFragment: "setOverhead",
data: BytesLike
): Result;
decodeFunctionResult(functionFragment: "setScalar", data: BytesLike): Result;
decodeFunctionResult(
functionFragment: "transferOwnership",
data: BytesLike
): Result;
}
export namespace DecimalsUpdatedEvent {
export type InputTuple = [arg0: BigNumberish];
export type OutputTuple = [arg0: bigint];
export interface OutputObject {
arg0: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace GasPriceUpdatedEvent {
export type InputTuple = [arg0: BigNumberish];
export type OutputTuple = [arg0: bigint];
export interface OutputObject {
arg0: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace L1BaseFeeUpdatedEvent {
export type InputTuple = [arg0: BigNumberish];
export type OutputTuple = [arg0: bigint];
export interface OutputObject {
arg0: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace OverheadUpdatedEvent {
export type InputTuple = [arg0: BigNumberish];
export type OutputTuple = [arg0: bigint];
export interface OutputObject {
arg0: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace OwnershipTransferredEvent {
export type InputTuple = [previousOwner: AddressLike, newOwner: AddressLike];
export type OutputTuple = [previousOwner: string, newOwner: string];
export interface OutputObject {
previousOwner: string;
newOwner: string;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export namespace ScalarUpdatedEvent {
export type InputTuple = [arg0: BigNumberish];
export type OutputTuple = [arg0: bigint];
export interface OutputObject {
arg0: bigint;
}
export type Event = TypedContractEvent<InputTuple, OutputTuple, OutputObject>;
export type Filter = TypedDeferredTopicFilter<Event>;
export type Log = TypedEventLog<Event>;
export type LogDescription = TypedLogDescription<Event>;
}
export interface OvmGasPriceOracle extends BaseContract {
connect(runner?: ContractRunner | null): OvmGasPriceOracle;
waitForDeployment(): Promise<this>;
interface: OvmGasPriceOracleInterface;
queryFilter<TCEvent extends TypedContractEvent>(
event: TCEvent,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
queryFilter<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
on<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
on<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
listeners<TCEvent extends TypedContractEvent>(
event: TCEvent
): Promise<Array<TypedListener<TCEvent>>>;
listeners(eventName?: string): Promise<Array<Listener>>;
removeAllListeners<TCEvent extends TypedContractEvent>(
event?: TCEvent
): Promise<this>;
decimals: TypedContractMethod<[], [bigint], "view">;
gasPrice: TypedContractMethod<[], [bigint], "view">;
getL1Fee: TypedContractMethod<[_data: BytesLike], [bigint], "view">;
getL1GasUsed: TypedContractMethod<[_data: BytesLike], [bigint], "view">;
l1BaseFee: TypedContractMethod<[], [bigint], "view">;
overhead: TypedContractMethod<[], [bigint], "view">;
owner: TypedContractMethod<[], [string], "view">;
renounceOwnership: TypedContractMethod<[], [void], "nonpayable">;
scalar: TypedContractMethod<[], [bigint], "view">;
setDecimals: TypedContractMethod<
[_decimals: BigNumberish],
[void],
"nonpayable"
>;
setGasPrice: TypedContractMethod<
[_gasPrice: BigNumberish],
[void],
"nonpayable"
>;
setL1BaseFee: TypedContractMethod<
[_baseFee: BigNumberish],
[void],
"nonpayable"
>;
setOverhead: TypedContractMethod<
[_overhead: BigNumberish],
[void],
"nonpayable"
>;
setScalar: TypedContractMethod<[_scalar: BigNumberish], [void], "nonpayable">;
transferOwnership: TypedContractMethod<
[newOwner: AddressLike],
[void],
"nonpayable"
>;
getFunction<T extends ContractMethod = ContractMethod>(
key: string | FunctionFragment
): T;
getFunction(
nameOrSignature: "decimals"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "gasPrice"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "getL1Fee"
): TypedContractMethod<[_data: BytesLike], [bigint], "view">;
getFunction(
nameOrSignature: "getL1GasUsed"
): TypedContractMethod<[_data: BytesLike], [bigint], "view">;
getFunction(
nameOrSignature: "l1BaseFee"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "overhead"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "owner"
): TypedContractMethod<[], [string], "view">;
getFunction(
nameOrSignature: "renounceOwnership"
): TypedContractMethod<[], [void], "nonpayable">;
getFunction(
nameOrSignature: "scalar"
): TypedContractMethod<[], [bigint], "view">;
getFunction(
nameOrSignature: "setDecimals"
): TypedContractMethod<[_decimals: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "setGasPrice"
): TypedContractMethod<[_gasPrice: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "setL1BaseFee"
): TypedContractMethod<[_baseFee: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "setOverhead"
): TypedContractMethod<[_overhead: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "setScalar"
): TypedContractMethod<[_scalar: BigNumberish], [void], "nonpayable">;
getFunction(
nameOrSignature: "transferOwnership"
): TypedContractMethod<[newOwner: AddressLike], [void], "nonpayable">;
getEvent(
key: "DecimalsUpdated"
): TypedContractEvent<
DecimalsUpdatedEvent.InputTuple,
DecimalsUpdatedEvent.OutputTuple,
DecimalsUpdatedEvent.OutputObject
>;
getEvent(
key: "GasPriceUpdated"
): TypedContractEvent<
GasPriceUpdatedEvent.InputTuple,
GasPriceUpdatedEvent.OutputTuple,
GasPriceUpdatedEvent.OutputObject
>;
getEvent(
key: "L1BaseFeeUpdated"
): TypedContractEvent<
L1BaseFeeUpdatedEvent.InputTuple,
L1BaseFeeUpdatedEvent.OutputTuple,
L1BaseFeeUpdatedEvent.OutputObject
>;
getEvent(
key: "OverheadUpdated"
): TypedContractEvent<
OverheadUpdatedEvent.InputTuple,
OverheadUpdatedEvent.OutputTuple,
OverheadUpdatedEvent.OutputObject
>;
getEvent(
key: "OwnershipTransferred"
): TypedContractEvent<
OwnershipTransferredEvent.InputTuple,
OwnershipTransferredEvent.OutputTuple,
OwnershipTransferredEvent.OutputObject
>;
getEvent(
key: "ScalarUpdated"
): TypedContractEvent<
ScalarUpdatedEvent.InputTuple,
ScalarUpdatedEvent.OutputTuple,
ScalarUpdatedEvent.OutputObject
>;
filters: {
"DecimalsUpdated(uint256)": TypedContractEvent<
DecimalsUpdatedEvent.InputTuple,
DecimalsUpdatedEvent.OutputTuple,
DecimalsUpdatedEvent.OutputObject
>;
DecimalsUpdated: TypedContractEvent<
DecimalsUpdatedEvent.InputTuple,
DecimalsUpdatedEvent.OutputTuple,
DecimalsUpdatedEvent.OutputObject
>;
"GasPriceUpdated(uint256)": TypedContractEvent<
GasPriceUpdatedEvent.InputTuple,
GasPriceUpdatedEvent.OutputTuple,
GasPriceUpdatedEvent.OutputObject
>;
GasPriceUpdated: TypedContractEvent<
GasPriceUpdatedEvent.InputTuple,
GasPriceUpdatedEvent.OutputTuple,
GasPriceUpdatedEvent.OutputObject
>;
"L1BaseFeeUpdated(uint256)": TypedContractEvent<
L1BaseFeeUpdatedEvent.InputTuple,
L1BaseFeeUpdatedEvent.OutputTuple,
L1BaseFeeUpdatedEvent.OutputObject
>;
L1BaseFeeUpdated: TypedContractEvent<
L1BaseFeeUpdatedEvent.InputTuple,
L1BaseFeeUpdatedEvent.OutputTuple,
L1BaseFeeUpdatedEvent.OutputObject
>;
"OverheadUpdated(uint256)": TypedContractEvent<
OverheadUpdatedEvent.InputTuple,
OverheadUpdatedEvent.OutputTuple,
OverheadUpdatedEvent.OutputObject
>;
OverheadUpdated: TypedContractEvent<
OverheadUpdatedEvent.InputTuple,
OverheadUpdatedEvent.OutputTuple,
OverheadUpdatedEvent.OutputObject
>;
"OwnershipTransferred(address,address)": TypedContractEvent<
OwnershipTransferredEvent.InputTuple,
OwnershipTransferredEvent.OutputTuple,
OwnershipTransferredEvent.OutputObject
>;
OwnershipTransferred: TypedContractEvent<
OwnershipTransferredEvent.InputTuple,
OwnershipTransferredEvent.OutputTuple,
OwnershipTransferredEvent.OutputObject
>;
"ScalarUpdated(uint256)": TypedContractEvent<
ScalarUpdatedEvent.InputTuple,
ScalarUpdatedEvent.OutputTuple,
ScalarUpdatedEvent.OutputObject
>;
ScalarUpdated: TypedContractEvent<
ScalarUpdatedEvent.InputTuple,
ScalarUpdatedEvent.OutputTuple,
ScalarUpdatedEvent.OutputObject
>;
};
}

View File

@ -0,0 +1,88 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
BaseContract,
BytesLike,
FunctionFragment,
Result,
Interface,
AddressLike,
ContractRunner,
ContractMethod,
Listener,
} from "ethers";
import type {
TypedContractEvent,
TypedDeferredTopicFilter,
TypedEventLog,
TypedListener,
TypedContractMethod,
} from "./common";
export interface ReverseRecordsInterface extends Interface {
getFunction(nameOrSignature: "getNames"): FunctionFragment;
encodeFunctionData(
functionFragment: "getNames",
values: [AddressLike[]]
): string;
decodeFunctionResult(functionFragment: "getNames", data: BytesLike): Result;
}
export interface ReverseRecords extends BaseContract {
connect(runner?: ContractRunner | null): ReverseRecords;
waitForDeployment(): Promise<this>;
interface: ReverseRecordsInterface;
queryFilter<TCEvent extends TypedContractEvent>(
event: TCEvent,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
queryFilter<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
fromBlockOrBlockhash?: string | number | undefined,
toBlock?: string | number | undefined
): Promise<Array<TypedEventLog<TCEvent>>>;
on<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
on<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
event: TCEvent,
listener: TypedListener<TCEvent>
): Promise<this>;
once<TCEvent extends TypedContractEvent>(
filter: TypedDeferredTopicFilter<TCEvent>,
listener: TypedListener<TCEvent>
): Promise<this>;
listeners<TCEvent extends TypedContractEvent>(
event: TCEvent
): Promise<Array<TypedListener<TCEvent>>>;
listeners(eventName?: string): Promise<Array<Listener>>;
removeAllListeners<TCEvent extends TypedContractEvent>(
event?: TCEvent
): Promise<this>;
getNames: TypedContractMethod<[addresses: AddressLike[]], [string[]], "view">;
getFunction<T extends ContractMethod = ContractMethod>(
key: string | FunctionFragment
): T;
getFunction(
nameOrSignature: "getNames"
): TypedContractMethod<[addresses: AddressLike[]], [string[]], "view">;
filters: {};
}

131
src/typechain/common.ts Normal file
View File

@ -0,0 +1,131 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import type {
FunctionFragment,
Typed,
EventFragment,
ContractTransaction,
ContractTransactionResponse,
DeferredTopicFilter,
EventLog,
TransactionRequest,
LogDescription,
} from "ethers";
export interface TypedDeferredTopicFilter<_TCEvent extends TypedContractEvent>
extends DeferredTopicFilter {}
export interface TypedContractEvent<
InputTuple extends Array<any> = any,
OutputTuple extends Array<any> = any,
OutputObject = any
> {
(...args: Partial<InputTuple>): TypedDeferredTopicFilter<
TypedContractEvent<InputTuple, OutputTuple, OutputObject>
>;
name: string;
fragment: EventFragment;
getFragment(...args: Partial<InputTuple>): EventFragment;
}
type __TypechainAOutputTuple<T> = T extends TypedContractEvent<
infer _U,
infer W
>
? W
: never;
type __TypechainOutputObject<T> = T extends TypedContractEvent<
infer _U,
infer _W,
infer V
>
? V
: never;
export interface TypedEventLog<TCEvent extends TypedContractEvent>
extends Omit<EventLog, "args"> {
args: __TypechainAOutputTuple<TCEvent> & __TypechainOutputObject<TCEvent>;
}
export interface TypedLogDescription<TCEvent extends TypedContractEvent>
extends Omit<LogDescription, "args"> {
args: __TypechainAOutputTuple<TCEvent> & __TypechainOutputObject<TCEvent>;
}
export type TypedListener<TCEvent extends TypedContractEvent> = (
...listenerArg: [
...__TypechainAOutputTuple<TCEvent>,
TypedEventLog<TCEvent>,
...undefined[]
]
) => 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;
export type StateMutability = "nonpayable" | "payable" | "view";
export type BaseOverrides = Omit<TransactionRequest, "to" | "data">;
export type NonPayableOverrides = Omit<
BaseOverrides,
"value" | "blockTag" | "enableCcipRead"
>;
export type PayableOverrides = Omit<
BaseOverrides,
"blockTag" | "enableCcipRead"
>;
export type ViewOverrides = Omit<TransactionRequest, "to" | "data">;
export type Overrides<S extends StateMutability> = S extends "nonpayable"
? NonPayableOverrides
: S extends "payable"
? PayableOverrides
: ViewOverrides;
export type PostfixOverrides<A extends Array<any>, S extends StateMutability> =
| A
| [...A, Overrides<S>];
export type ContractMethodArgs<
A extends Array<any>,
S extends StateMutability
> = PostfixOverrides<{ [I in keyof A]-?: A[I] | Typed }, S>;
export type DefaultReturnType<R> = R extends Array<any> ? R[0] : R;
// export interface ContractMethod<A extends Array<any> = Array<any>, R = any, D extends R | ContractTransactionResponse = R | ContractTransactionResponse> {
export interface TypedContractMethod<
A extends Array<any> = Array<any>,
R = any,
S extends StateMutability = "payable"
> {
(...args: ContractMethodArgs<A, S>): S extends "view"
? Promise<DefaultReturnType<R>>
: Promise<ContractTransactionResponse>;
name: string;
fragment: FunctionFragment;
getFragment(...args: ContractMethodArgs<A, S>): FunctionFragment;
populateTransaction(
...args: ContractMethodArgs<A, S>
): Promise<ContractTransaction>;
staticCall(
...args: ContractMethodArgs<A, "view">
): Promise<DefaultReturnType<R>>;
send(...args: ContractMethodArgs<A, S>): Promise<ContractTransactionResponse>;
estimateGas(...args: ContractMethodArgs<A, S>): Promise<bigint>;
staticCallResult(...args: ContractMethodArgs<A, "view">): Promise<R>;
}

View File

@ -0,0 +1,698 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Interface, type ContractRunner } from "ethers";
import type { ENS, ENSInterface } from "../ENS";
const _abi = [
{
constant: true,
inputs: [
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4",
},
],
name: "supportsInterface",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
payable: false,
stateMutability: "pure",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "string",
name: "key",
type: "string",
},
{
internalType: "string",
name: "value",
type: "string",
},
],
name: "setText",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4",
},
],
name: "interfaceImplementer",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "uint256",
name: "contentTypes",
type: "uint256",
},
],
name: "ABI",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
{
internalType: "bytes",
name: "",
type: "bytes",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "bytes32",
name: "x",
type: "bytes32",
},
{
internalType: "bytes32",
name: "y",
type: "bytes32",
},
],
name: "setPubkey",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "bytes",
name: "hash",
type: "bytes",
},
],
name: "setContenthash",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
],
name: "addr",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bool",
name: "isAuthorised",
type: "bool",
},
],
name: "setAuthorisation",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "string",
name: "key",
type: "string",
},
],
name: "text",
outputs: [
{
internalType: "string",
name: "",
type: "string",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "uint256",
name: "contentType",
type: "uint256",
},
{
internalType: "bytes",
name: "data",
type: "bytes",
},
],
name: "setABI",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
],
name: "name",
outputs: [
{
internalType: "string",
name: "",
type: "string",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "string",
name: "name",
type: "string",
},
],
name: "setName",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "uint256",
name: "coinType",
type: "uint256",
},
{
internalType: "bytes",
name: "a",
type: "bytes",
},
],
name: "setAddr",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
],
name: "contenthash",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
],
name: "pubkey",
outputs: [
{
internalType: "bytes32",
name: "x",
type: "bytes32",
},
{
internalType: "bytes32",
name: "y",
type: "bytes32",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "address",
name: "a",
type: "address",
},
],
name: "setAddr",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: false,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "bytes4",
name: "interfaceID",
type: "bytes4",
},
{
internalType: "address",
name: "implementer",
type: "address",
},
],
name: "setInterface",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
internalType: "uint256",
name: "coinType",
type: "uint256",
},
],
name: "addr",
outputs: [
{
internalType: "bytes",
name: "",
type: "bytes",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
constant: true,
inputs: [
{
internalType: "bytes32",
name: "",
type: "bytes32",
},
{
internalType: "address",
name: "",
type: "address",
},
{
internalType: "address",
name: "",
type: "address",
},
],
name: "authorisations",
outputs: [
{
internalType: "bool",
name: "",
type: "bool",
},
],
payable: false,
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract ENS",
name: "_ens",
type: "address",
},
],
payable: false,
stateMutability: "nonpayable",
type: "constructor",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: true,
internalType: "address",
name: "owner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "target",
type: "address",
},
{
indexed: false,
internalType: "bool",
name: "isAuthorised",
type: "bool",
},
],
name: "AuthorisationChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: false,
internalType: "string",
name: "indexedKey",
type: "string",
},
{
indexed: false,
internalType: "string",
name: "key",
type: "string",
},
],
name: "TextChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: false,
internalType: "bytes32",
name: "x",
type: "bytes32",
},
{
indexed: false,
internalType: "bytes32",
name: "y",
type: "bytes32",
},
],
name: "PubkeyChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: false,
internalType: "string",
name: "name",
type: "string",
},
],
name: "NameChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: true,
internalType: "bytes4",
name: "interfaceID",
type: "bytes4",
},
{
indexed: false,
internalType: "address",
name: "implementer",
type: "address",
},
],
name: "InterfaceChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: false,
internalType: "bytes",
name: "hash",
type: "bytes",
},
],
name: "ContenthashChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: false,
internalType: "address",
name: "a",
type: "address",
},
],
name: "AddrChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: false,
internalType: "uint256",
name: "coinType",
type: "uint256",
},
{
indexed: false,
internalType: "bytes",
name: "newAddress",
type: "bytes",
},
],
name: "AddressChanged",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "bytes32",
name: "node",
type: "bytes32",
},
{
indexed: true,
internalType: "uint256",
name: "contentType",
type: "uint256",
},
],
name: "ABIChanged",
type: "event",
},
] as const;
export class ENS__factory {
static readonly abi = _abi;
static createInterface(): ENSInterface {
return new Interface(_abi) as ENSInterface;
}
static connect(address: string, runner?: ContractRunner | null): ENS {
return new Contract(address, _abi, runner) as unknown as ENS;
}
}

View File

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

View File

@ -0,0 +1,212 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Interface, type ContractRunner } from "ethers";
import type {
GasPriceOracle,
GasPriceOracleInterface,
} from "../GasPriceOracle";
const _abi = [
{
inputs: [],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "GAS_UNIT",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint32",
name: "_derivationThresold",
type: "uint32",
},
],
name: "changeDerivationThresold",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint32",
name: "_gasUnit",
type: "uint32",
},
],
name: "changeGasUnit",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint32",
name: "_heartbeat",
type: "uint32",
},
],
name: "changeHeartbeat",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "_owner",
type: "address",
},
],
name: "changeOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "derivationThresold",
outputs: [
{
internalType: "uint32",
name: "",
type: "uint32",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "gasPrice",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "heartbeat",
outputs: [
{
internalType: "uint32",
name: "",
type: "uint32",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "maxFeePerGas",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "maxPriorityFeePerGas",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "pastGasPrice",
outputs: [
{
internalType: "uint32",
name: "",
type: "uint32",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint32",
name: "_gasPrice",
type: "uint32",
},
],
name: "setGasPrice",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "timestamp",
outputs: [
{
internalType: "uint32",
name: "",
type: "uint32",
},
],
stateMutability: "view",
type: "function",
},
] as const;
export class GasPriceOracle__factory {
static readonly abi = _abi;
static createInterface(): GasPriceOracleInterface {
return new Interface(_abi) as GasPriceOracleInterface;
}
static connect(
address: string,
runner?: ContractRunner | null
): GasPriceOracle {
return new Contract(address, _abi, runner) as unknown as GasPriceOracle;
}
}

View File

@ -0,0 +1,457 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Interface, type ContractRunner } from "ethers";
import type { Multicall, MulticallInterface } from "../Multicall";
const _abi = [
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]",
},
],
name: "aggregate",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256",
},
{
internalType: "bytes[]",
name: "returnData",
type: "bytes[]",
},
],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bool",
name: "allowFailure",
type: "bool",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall3.Call3[]",
name: "calls",
type: "tuple[]",
},
],
name: "aggregate3",
outputs: [
{
components: [
{
internalType: "bool",
name: "success",
type: "bool",
},
{
internalType: "bytes",
name: "returnData",
type: "bytes",
},
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]",
},
],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bool",
name: "allowFailure",
type: "bool",
},
{
internalType: "uint256",
name: "value",
type: "uint256",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall3.Call3Value[]",
name: "calls",
type: "tuple[]",
},
],
name: "aggregate3Value",
outputs: [
{
components: [
{
internalType: "bool",
name: "success",
type: "bool",
},
{
internalType: "bytes",
name: "returnData",
type: "bytes",
},
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]",
},
],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]",
},
],
name: "blockAndAggregate",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256",
},
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32",
},
{
components: [
{
internalType: "bool",
name: "success",
type: "bool",
},
{
internalType: "bytes",
name: "returnData",
type: "bytes",
},
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]",
},
],
stateMutability: "payable",
type: "function",
},
{
inputs: [],
name: "getBasefee",
outputs: [
{
internalType: "uint256",
name: "basefee",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256",
},
],
name: "getBlockHash",
outputs: [
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getBlockNumber",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getChainId",
outputs: [
{
internalType: "uint256",
name: "chainid",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getCurrentBlockCoinbase",
outputs: [
{
internalType: "address",
name: "coinbase",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getCurrentBlockDifficulty",
outputs: [
{
internalType: "uint256",
name: "difficulty",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getCurrentBlockGasLimit",
outputs: [
{
internalType: "uint256",
name: "gaslimit",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getCurrentBlockTimestamp",
outputs: [
{
internalType: "uint256",
name: "timestamp",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "addr",
type: "address",
},
],
name: "getEthBalance",
outputs: [
{
internalType: "uint256",
name: "balance",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "getLastBlockHash",
outputs: [
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "bool",
name: "requireSuccess",
type: "bool",
},
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]",
},
],
name: "tryAggregate",
outputs: [
{
components: [
{
internalType: "bool",
name: "success",
type: "bool",
},
{
internalType: "bytes",
name: "returnData",
type: "bytes",
},
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]",
},
],
stateMutability: "payable",
type: "function",
},
{
inputs: [
{
internalType: "bool",
name: "requireSuccess",
type: "bool",
},
{
components: [
{
internalType: "address",
name: "target",
type: "address",
},
{
internalType: "bytes",
name: "callData",
type: "bytes",
},
],
internalType: "struct Multicall3.Call[]",
name: "calls",
type: "tuple[]",
},
],
name: "tryBlockAndAggregate",
outputs: [
{
internalType: "uint256",
name: "blockNumber",
type: "uint256",
},
{
internalType: "bytes32",
name: "blockHash",
type: "bytes32",
},
{
components: [
{
internalType: "bool",
name: "success",
type: "bool",
},
{
internalType: "bytes",
name: "returnData",
type: "bytes",
},
],
internalType: "struct Multicall3.Result[]",
name: "returnData",
type: "tuple[]",
},
],
stateMutability: "payable",
type: "function",
},
] as const;
export class Multicall__factory {
static readonly abi = _abi;
static createInterface(): MulticallInterface {
return new Interface(_abi) as MulticallInterface;
}
static connect(address: string, runner?: ContractRunner | null): Multicall {
return new Contract(address, _abi, runner) as unknown as Multicall;
}
}

View File

@ -0,0 +1,538 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Interface, type ContractRunner } from "ethers";
import type {
OffchainOracle,
OffchainOracleInterface,
} from "../OffchainOracle";
const _abi = [
{
inputs: [
{
internalType: "contract MultiWrapper",
name: "_multiWrapper",
type: "address",
},
{
internalType: "contract IOracle[]",
name: "existingOracles",
type: "address[]",
},
{
internalType: "enum OffchainOracle.OracleType[]",
name: "oracleTypes",
type: "uint8[]",
},
{
internalType: "contract IERC20[]",
name: "existingConnectors",
type: "address[]",
},
{
internalType: "contract IERC20",
name: "wBase",
type: "address",
},
{
internalType: "address",
name: "owner",
type: "address",
},
],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [],
name: "ArraysLengthMismatch",
type: "error",
},
{
inputs: [],
name: "ConnectorAlreadyAdded",
type: "error",
},
{
inputs: [],
name: "InvalidOracleTokenKind",
type: "error",
},
{
inputs: [],
name: "OracleAlreadyAdded",
type: "error",
},
{
inputs: [],
name: "SameTokens",
type: "error",
},
{
inputs: [],
name: "TooBigThreshold",
type: "error",
},
{
inputs: [],
name: "UnknownConnector",
type: "error",
},
{
inputs: [],
name: "UnknownOracle",
type: "error",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IERC20",
name: "connector",
type: "address",
},
],
name: "ConnectorAdded",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IERC20",
name: "connector",
type: "address",
},
],
name: "ConnectorRemoved",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract MultiWrapper",
name: "multiWrapper",
type: "address",
},
],
name: "MultiWrapperUpdated",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IOracle",
name: "oracle",
type: "address",
},
{
indexed: false,
internalType: "enum OffchainOracle.OracleType",
name: "oracleType",
type: "uint8",
},
],
name: "OracleAdded",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "contract IOracle",
name: "oracle",
type: "address",
},
{
indexed: false,
internalType: "enum OffchainOracle.OracleType",
name: "oracleType",
type: "uint8",
},
],
name: "OracleRemoved",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address",
},
],
name: "OwnershipTransferred",
type: "event",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "connector",
type: "address",
},
],
name: "addConnector",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "contract IOracle",
name: "oracle",
type: "address",
},
{
internalType: "enum OffchainOracle.OracleType",
name: "oracleKind",
type: "uint8",
},
],
name: "addOracle",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "connectors",
outputs: [
{
internalType: "contract IERC20[]",
name: "allConnectors",
type: "address[]",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address",
},
{
internalType: "contract IERC20",
name: "dstToken",
type: "address",
},
{
internalType: "bool",
name: "useWrappers",
type: "bool",
},
],
name: "getRate",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address",
},
{
internalType: "bool",
name: "useSrcWrappers",
type: "bool",
},
],
name: "getRateToEth",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address",
},
{
internalType: "bool",
name: "useSrcWrappers",
type: "bool",
},
{
internalType: "contract IERC20[]",
name: "customConnectors",
type: "address[]",
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256",
},
],
name: "getRateToEthWithCustomConnectors",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address",
},
{
internalType: "bool",
name: "useSrcWrappers",
type: "bool",
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256",
},
],
name: "getRateToEthWithThreshold",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address",
},
{
internalType: "contract IERC20",
name: "dstToken",
type: "address",
},
{
internalType: "bool",
name: "useWrappers",
type: "bool",
},
{
internalType: "contract IERC20[]",
name: "customConnectors",
type: "address[]",
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256",
},
],
name: "getRateWithCustomConnectors",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "srcToken",
type: "address",
},
{
internalType: "contract IERC20",
name: "dstToken",
type: "address",
},
{
internalType: "bool",
name: "useWrappers",
type: "bool",
},
{
internalType: "uint256",
name: "thresholdFilter",
type: "uint256",
},
],
name: "getRateWithThreshold",
outputs: [
{
internalType: "uint256",
name: "weightedRate",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "multiWrapper",
outputs: [
{
internalType: "contract MultiWrapper",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "oracles",
outputs: [
{
internalType: "contract IOracle[]",
name: "allOracles",
type: "address[]",
},
{
internalType: "enum OffchainOracle.OracleType[]",
name: "oracleTypes",
type: "uint8[]",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "contract IERC20",
name: "connector",
type: "address",
},
],
name: "removeConnector",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "contract IOracle",
name: "oracle",
type: "address",
},
{
internalType: "enum OffchainOracle.OracleType",
name: "oracleKind",
type: "uint8",
},
],
name: "removeOracle",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "contract MultiWrapper",
name: "_multiWrapper",
type: "address",
},
],
name: "setMultiWrapper",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "newOwner",
type: "address",
},
],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as const;
export class OffchainOracle__factory {
static readonly abi = _abi;
static createInterface(): OffchainOracleInterface {
return new Interface(_abi) as OffchainOracleInterface;
}
static connect(
address: string,
runner?: ContractRunner | null
): OffchainOracle {
return new Contract(address, _abi, runner) as unknown as OffchainOracle;
}
}

View File

@ -0,0 +1,321 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Interface, type ContractRunner } from "ethers";
import type {
OvmGasPriceOracle,
OvmGasPriceOracleInterface,
} from "../OvmGasPriceOracle";
const _abi = [
{
inputs: [
{
internalType: "address",
name: "_owner",
type: "address",
},
],
stateMutability: "nonpayable",
type: "constructor",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256",
},
],
name: "DecimalsUpdated",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256",
},
],
name: "GasPriceUpdated",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256",
},
],
name: "L1BaseFeeUpdated",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256",
},
],
name: "OverheadUpdated",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: "address",
name: "previousOwner",
type: "address",
},
{
indexed: true,
internalType: "address",
name: "newOwner",
type: "address",
},
],
name: "OwnershipTransferred",
type: "event",
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: "uint256",
name: "",
type: "uint256",
},
],
name: "ScalarUpdated",
type: "event",
},
{
inputs: [],
name: "decimals",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "gasPrice",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "bytes",
name: "_data",
type: "bytes",
},
],
name: "getL1Fee",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "bytes",
name: "_data",
type: "bytes",
},
],
name: "getL1GasUsed",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "l1BaseFee",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "overhead",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "owner",
outputs: [
{
internalType: "address",
name: "",
type: "address",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "renounceOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "scalar",
outputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
stateMutability: "view",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_decimals",
type: "uint256",
},
],
name: "setDecimals",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_gasPrice",
type: "uint256",
},
],
name: "setGasPrice",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_baseFee",
type: "uint256",
},
],
name: "setL1BaseFee",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_overhead",
type: "uint256",
},
],
name: "setOverhead",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "uint256",
name: "_scalar",
type: "uint256",
},
],
name: "setScalar",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{
internalType: "address",
name: "newOwner",
type: "address",
},
],
name: "transferOwnership",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
] as const;
export class OvmGasPriceOracle__factory {
static readonly abi = _abi;
static createInterface(): OvmGasPriceOracleInterface {
return new Interface(_abi) as OvmGasPriceOracleInterface;
}
static connect(
address: string,
runner?: ContractRunner | null
): OvmGasPriceOracle {
return new Contract(address, _abi, runner) as unknown as OvmGasPriceOracle;
}
}

View File

@ -0,0 +1,55 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Interface, type ContractRunner } from "ethers";
import type {
ReverseRecords,
ReverseRecordsInterface,
} from "../ReverseRecords";
const _abi = [
{
inputs: [
{
internalType: "contract ENS",
name: "_ens",
type: "address",
},
],
stateMutability: "nonpayable",
type: "constructor",
},
{
inputs: [
{
internalType: "address[]",
name: "addresses",
type: "address[]",
},
],
name: "getNames",
outputs: [
{
internalType: "string[]",
name: "r",
type: "string[]",
},
],
stateMutability: "view",
type: "function",
},
] as const;
export class ReverseRecords__factory {
static readonly abi = _abi;
static createInterface(): ReverseRecordsInterface {
return new Interface(_abi) as ReverseRecordsInterface;
}
static connect(
address: string,
runner?: ContractRunner | null
): ReverseRecords {
return new Contract(address, _abi, runner) as unknown as ReverseRecords;
}
}

View File

@ -0,0 +1,10 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export { ENS__factory } from "./ENS__factory";
export { ERC20__factory } from "./ERC20__factory";
export { GasPriceOracle__factory } from "./GasPriceOracle__factory";
export { Multicall__factory } from "./Multicall__factory";
export { OffchainOracle__factory } from "./OffchainOracle__factory";
export { OvmGasPriceOracle__factory } from "./OvmGasPriceOracle__factory";
export { ReverseRecords__factory } from "./ReverseRecords__factory";

18
src/typechain/index.ts Normal file
View File

@ -0,0 +1,18 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export type { ENS } from "./ENS";
export type { ERC20 } from "./ERC20";
export type { GasPriceOracle } from "./GasPriceOracle";
export type { Multicall } from "./Multicall";
export type { OffchainOracle } from "./OffchainOracle";
export type { OvmGasPriceOracle } from "./OvmGasPriceOracle";
export type { ReverseRecords } from "./ReverseRecords";
export * as factories from "./factories";
export { ENS__factory } from "./factories/ENS__factory";
export { ERC20__factory } from "./factories/ERC20__factory";
export { GasPriceOracle__factory } from "./factories/GasPriceOracle__factory";
export { Multicall__factory } from "./factories/Multicall__factory";
export { OffchainOracle__factory } from "./factories/OffchainOracle__factory";
export { OvmGasPriceOracle__factory } from "./factories/OvmGasPriceOracle__factory";
export { ReverseRecords__factory } from "./factories/ReverseRecords__factory";

25
src/types/bloomfilter.js.d.ts vendored Normal file
View File

@ -0,0 +1,25 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
declare module 'bloomfilter.js' {
export default class BloomFilter {
m: number;
k: number;
size: number;
bitview: any;
constructor(n: number, false_postive_tolerance?: number);
calculateHash(x: number, m: number, i: number): number;
test(data: any): boolean;
add(data: any): void;
bytelength(): number;
view(): Uint8Array;
serialize(): string;
deserialize(serialized: string): BloomFilter;
}
}

145
src/utils.ts Normal file
View File

@ -0,0 +1,145 @@
import { webcrypto } from 'crypto';
import BN from 'bn.js';
import type { BigNumberish } from 'ethers';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(BigInt.prototype as any).toJSON = function () {
return this.toString();
};
type bnInput = number | string | number[] | Uint8Array | Buffer | BN;
export const isNode =
!(
process as typeof process & {
browser?: boolean;
}
).browser && typeof globalThis.window === 'undefined';
export const crypto = isNode ? webcrypto : (globalThis.crypto as typeof webcrypto);
export const chunk = <T>(arr: T[], size: number): T[][] =>
[...Array(Math.ceil(arr.length / size))].map((_, i) => arr.slice(size * i, size + size * i));
export function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function validateUrl(url: string, protocols?: string[]) {
try {
const parsedUrl = new URL(url);
if (protocols && protocols.length) {
return protocols.map((p) => p.toLowerCase()).includes(parsedUrl.protocol);
}
return true;
} catch {
return false;
}
}
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
const totalSize = arrays.reduce((acc, e) => acc + e.length, 0);
const merged = new Uint8Array(totalSize);
arrays.forEach((array, i, arrays) => {
const offset = arrays.slice(0, i).reduce((acc, e) => acc + e.length, 0);
merged.set(array, offset);
});
return merged;
}
export function bufferToBytes(b: Buffer) {
return new Uint8Array(b.buffer);
}
export function bytesToBase64(bytes: Uint8Array) {
return btoa(String.fromCharCode.apply(null, Array.from(bytes)));
}
export function base64ToBytes(base64: string) {
return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0));
}
export function bytesToHex(bytes: Uint8Array) {
return (
'0x' +
Array.from(bytes)
.map((b) => b.toString(16).padStart(2, '0'))
.join('')
);
}
export function hexToBytes(hexString: string) {
if (hexString.slice(0, 2) === '0x') {
hexString = hexString.replace('0x', '');
}
if (hexString.length % 2 !== 0) {
hexString = '0' + hexString;
}
return Uint8Array.from((hexString.match(/.{1,2}/g) as string[]).map((byte) => parseInt(byte, 16)));
}
// Convert BE encoded bytes (Buffer | Uint8Array) array to BigInt
export function bytesToBN(bytes: Uint8Array) {
return BigInt(bytesToHex(bytes));
}
// Convert BigInt to BE encoded Uint8Array type
export function bnToBytes(bigint: bigint | string) {
// Parse bigint to hex string
let hexString: string = typeof bigint === 'bigint' ? bigint.toString(16) : bigint;
// Remove hex string prefix if exists
if (hexString.startsWith('0x')) {
hexString = hexString.replace('0x', '');
}
// Hex string length should be a multiplier of two (To make correct bytes)
if (hexString.length % 2 !== 0) {
hexString = '0' + hexString;
}
return Uint8Array.from((hexString.match(/.{1,2}/g) as string[]).map((byte) => parseInt(byte, 16)));
}
// Convert LE encoded bytes (Buffer | Uint8Array) array to BigInt
export function leBuff2Int(bytes: Uint8Array) {
return new BN(bytes, 16, 'le');
}
// Convert BigInt to LE encoded Uint8Array type
export function leInt2Buff(bigint: bnInput | bigint) {
return Uint8Array.from(new BN(bigint as bnInput).toArray('le', 31));
}
// Inherited from tornado-core and tornado-cli
export function toFixedHex(numberish: BigNumberish, length = 32) {
return (
'0x' +
BigInt(numberish)
.toString(16)
.padStart(length * 2, '0')
);
}
export function toFixedLength(string: string, length: number = 32) {
string = string.replace('0x', '');
return '0x' + string.padStart(length * 2, '0');
}
// Random BigInt in a range of bytes
export function rBigInt(nbytes: number = 31) {
return bytesToBN(crypto.getRandomValues(new Uint8Array(nbytes)));
}
// Used for JSON.stringify(value, bigIntReplacer, space)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function bigIntReplacer(key: any, value: any) {
return typeof value === 'bigint' ? value.toString() : value;
}
export function substring(str: string, length: number = 10) {
if (str.length < length * 2) {
return str;
}
return `${str.substring(0, length)}...${str.substring(str.length - length)}`;
}

85
src/websnark.ts Normal file
View File

@ -0,0 +1,85 @@
// @ts-expect-error no-websnark-types
import * as websnarkUtils from '@tornado/websnark/src/utils';
// @ts-expect-error no-websnark-types
import websnarkGroth from '@tornado/websnark/src/groth16';
import type { Element } from '@tornado/fixed-merkle-tree';
import type { AddressLike, BytesLike, BigNumberish } from 'ethers';
import { toFixedHex } from './utils';
export type snarkInputs = {
// Public snark inputs
root: Element;
nullifierHex: string;
recipient: AddressLike;
relayer: AddressLike;
fee: bigint;
refund: bigint;
// Private snark inputs
nullifier: bigint;
secret: bigint;
pathElements: Element[];
pathIndices: Element[];
};
export type snarkArgs = [
_root: BytesLike,
_nullifierHash: BytesLike,
_recipient: AddressLike,
_relayer: AddressLike,
_fee: BigNumberish,
_refund: BigNumberish,
];
export type snarkProofs = {
proof: BytesLike;
args: snarkArgs;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let groth16: any;
const groth16Promise = (async () => {
if (!groth16) {
groth16 = await websnarkGroth({ wasmInitialMemory: 2000 });
}
})();
export async function calculateSnarkProof(
input: snarkInputs,
circuit: object,
provingKey: ArrayBuffer,
): Promise<snarkProofs> {
await groth16Promise;
const snarkInput = {
root: input.root,
nullifierHash: BigInt(input.nullifierHex).toString(),
recipient: BigInt(input.recipient as string),
relayer: BigInt(input.relayer as string),
fee: input.fee,
refund: input.refund,
nullifier: input.nullifier,
secret: input.secret,
pathElements: input.pathElements,
pathIndices: input.pathIndices,
};
console.log('Start generating SNARK proof', snarkInput);
console.time('SNARK proof time');
const proofData = await websnarkUtils.genWitnessAndProve(groth16, snarkInput, circuit, provingKey);
const proof = websnarkUtils.toSolidityInput(proofData).proof as BytesLike;
console.timeEnd('SNARK proof time');
const args = [
toFixedHex(input.root, 32) as BytesLike,
toFixedHex(input.nullifierHex, 32) as BytesLike,
input.recipient,
input.relayer,
toFixedHex(input.fee, 32) as BigNumberish,
toFixedHex(input.refund, 32) as BigNumberish,
] as snarkArgs;
return { proof, args };
}

114
tsconfig.json Normal file
View File

@ -0,0 +1,114 @@
{
"compilerOptions": {
"typeRoots": [
"./node_modules/@types",
"./src/types",
],
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "commonjs", /* Specify what module code is generated. */
"rootDir": "./src", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
"resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
"outDir": "./dist", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
},
"include": ["./src/**/*"]
}

80
webpack.config.js Normal file
View File

@ -0,0 +1,80 @@
const esbuild = require('esbuild');
const path = require('path');
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin');
const esbuildLoader = {
test: /\.ts?$/,
loader: 'esbuild-loader',
options: {
loader: 'ts',
target: 'es2016',
implementation: esbuild
}
}
const commonAlias = {
fs: false,
'fs/promises': false,
'path': false,
'url': false,
'worker_threads': false,
'fflate': 'fflate/browser',
'@colors/colors': false,
'cli-table3': false,
'commander': false,
'http-proxy-agent': false,
'https-proxy-agent': false,
'socks-proxy-agent': false,
}
module.exports = [
{
mode: 'production',
module: {
rules: [esbuildLoader]
},
entry: './src/index.ts',
output: {
filename: 'index.umd.js',
path: path.resolve(__dirname, './dist'),
library: 'Tornado',
libraryTarget: 'umd'
},
plugins: [
new NodePolyfillPlugin(),
],
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias: {
...commonAlias,
}
},
optimization: {
minimize: false,
}
},
{
mode: 'production',
module: {
rules: [esbuildLoader]
},
entry: './src/merkleTreeWorker.ts',
output: {
filename: 'merkleTreeWorker.umd.js',
path: path.resolve(__dirname, './dist'),
libraryTarget: 'umd'
},
plugins: [
new NodePolyfillPlugin(),
],
resolve: {
extensions: ['.tsx', '.ts', '.js'],
alias: {
...commonAlias,
}
},
optimization: {
minimize: false,
}
}
];

5472
yarn.lock Normal file

File diff suppressed because it is too large Load Diff