Initial commit

This commit is contained in:
Theo 2023-08-17 14:38:38 -07:00
commit b7769d4548
31 changed files with 3955 additions and 0 deletions

93
.gitignore vendored Normal file

@ -0,0 +1,93 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp
test

1
.npmrc Normal file

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

1
.nvmrc Normal file

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

1
.prettierignore Normal file

@ -0,0 +1 @@
lib

8
.prettierrc Normal file

@ -0,0 +1,8 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 120,
"tabWidth": 2
}

21
LICENSE Normal file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Tornado Cash Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

38
abis/Multicall.abi.json Normal file

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

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

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

45
package.json Normal file

@ -0,0 +1,45 @@
{
"name": "@tornado/tornado-oracles",
"version": "0.1.0",
"description": "Gas oracle for Tornado-specific transactions",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
"scripts": {
"build": "tsc",
"build:esm": "tsc -p tsconfig.esm.json",
"prettier:check": "prettier --check . --config .prettierrc",
"prettier:fix": "prettier --write . --config .prettierrc",
"build:abi": "yarn typechain --target ethers-v5 --out-dir src/contracts ./abis/*.abi.json",
"test": "echo \"Error: no test specified\" && exit 1",
"prepare": "npm run build && npm run build:esm"
},
"repository": {
"type": "git",
"url": "https://git.tornado.ws/tornado-packages/tornado-gas-oracle.git"
},
"keywords": [
"Gas",
"price",
"Gas",
"Gas",
"limit",
"Oracle"
],
"author": "Theo",
"license": "MIT",
"dependencies": {
"@tornado/gas-price-oracle": "^0.5.3",
"@tornado/tornado-config": "^2.0.0",
"bignumber.js": "^9.1.1",
"ethers": "5.7"
},
"devDependencies": {
"@typechain/ethers-v5": "^11.1.1",
"@types/node": "^20.4.10",
"nodemon": "^3.0.1",
"prettier": "^3.0.1",
"ts-node": "^10.9.1",
"typechain": "^8.3.1",
"typescript": "^5.1.6"
}
}

86
src/config.ts Normal file

@ -0,0 +1,86 @@
import { LegacyGasPrices } from './types';
export enum ChainId {
MAINNET = 1,
GOERLI = 5,
BSC = 56,
XDAI = 100,
POLYGON = 137,
OPTIMISM = 10,
ARBITRUM = 42161,
AVAX = 43114,
}
export type GasPricesConfig = {
[chainId in ChainId]: LegacyGasPrices;
};
export const defaultGasPrices: GasPricesConfig = {
[ChainId.MAINNET]: {
instant: 80,
fast: 50,
standard: 25,
low: 8,
},
[ChainId.GOERLI]: {
instant: 80,
fast: 50,
standard: 25,
low: 8,
},
[ChainId.OPTIMISM]: {
instant: 0.001,
fast: 0.001,
standard: 0.001,
low: 0.001,
},
[ChainId.XDAI]: {
instant: 6,
fast: 5,
standard: 4,
low: 1,
},
[ChainId.BSC]: {
instant: 5,
fast: 4,
standard: 3,
low: 3,
},
[ChainId.POLYGON]: {
instant: 100,
fast: 75,
standard: 50,
low: 30,
},
[ChainId.ARBITRUM]: {
instant: 4,
fast: 3,
standard: 2.52,
low: 2.29,
},
[ChainId.AVAX]: {
instant: 225,
fast: 35,
standard: 25,
low: 25,
},
};
type GasLimitConfig = {
[chainId in ChainId]: number;
};
export const defaultWithdrawalGasLimit: GasLimitConfig = {
[ChainId.MAINNET]: 550000,
[ChainId.GOERLI]: 550000,
[ChainId.ARBITRUM]: 1900000,
[ChainId.OPTIMISM]: 440000,
[ChainId.AVAX]: 390000,
[ChainId.BSC]: 390000,
[ChainId.POLYGON]: 390000,
[ChainId.XDAI]: 390000,
};
export const optimismL1FeeOracleAddress = '0x420000000000000000000000000000000000000F';
export const offchainOracleAddress = '0x07D91f5fb9Bf7798734C3f606dB065549F6893bb';
export const multiCallAddress = '0xda3c19c6fe954576707fa24695efb830d9cca1ca';

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

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

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

30
src/contracts/common.ts Normal file

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

@ -0,0 +1,15 @@
import { OptimismL1FeeOracleAbi__factory, OffchainOracleAbi__factory, MulticallAbi__factory } from './';
import { optimismL1FeeOracleAddress, offchainOracleAddress, multiCallAddress } from '../config';
import { Provider } from '@ethersproject/abstract-provider';
export const getOptimismL1FeeOracle = (provider: Provider) => {
return OptimismL1FeeOracleAbi__factory.connect(optimismL1FeeOracleAddress, provider);
};
export const getOffchainOracleContract = (provider: Provider) => {
return OffchainOracleAbi__factory.connect(offchainOracleAddress, provider);
};
export const getMultiCallContract = (provider: Provider) => {
return MulticallAbi__factory.connect(multiCallAddress, provider);
};

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

@ -0,0 +1,352 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from 'ethers';
import type { Provider } from '@ethersproject/providers';
import type { OffchainOracleAbi, OffchainOracleAbiInterface } from '../OffchainOracleAbi';
const _abi = [
{
inputs: [
{
internalType: 'contract MultiWrapper',
name: '_multiWrapper',
type: 'address',
},
{
internalType: 'contract IOracle[]',
name: 'existingOracles',
type: 'address[]',
},
{
internalType: 'enum OffchainOracle.OracleType[]',
name: 'oracleTypes',
type: 'uint8[]',
},
{
internalType: 'contract IERC20[]',
name: 'existingConnectors',
type: 'address[]',
},
{
internalType: 'contract IERC20',
name: 'wBase',
type: 'address',
},
],
stateMutability: 'nonpayable',
type: 'constructor',
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'contract IERC20',
name: 'connector',
type: 'address',
},
],
name: 'ConnectorAdded',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'contract IERC20',
name: 'connector',
type: 'address',
},
],
name: 'ConnectorRemoved',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'contract MultiWrapper',
name: 'multiWrapper',
type: 'address',
},
],
name: 'MultiWrapperUpdated',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'contract IOracle',
name: 'oracle',
type: 'address',
},
{
indexed: false,
internalType: 'enum OffchainOracle.OracleType',
name: 'oracleType',
type: 'uint8',
},
],
name: 'OracleAdded',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: false,
internalType: 'contract IOracle',
name: 'oracle',
type: 'address',
},
{
indexed: false,
internalType: 'enum OffchainOracle.OracleType',
name: 'oracleType',
type: 'uint8',
},
],
name: 'OracleRemoved',
type: 'event',
},
{
anonymous: false,
inputs: [
{
indexed: true,
internalType: 'address',
name: 'previousOwner',
type: 'address',
},
{
indexed: true,
internalType: 'address',
name: 'newOwner',
type: 'address',
},
],
name: 'OwnershipTransferred',
type: 'event',
},
{
inputs: [
{
internalType: 'contract IERC20',
name: 'connector',
type: 'address',
},
],
name: 'addConnector',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'contract IOracle',
name: 'oracle',
type: 'address',
},
{
internalType: 'enum OffchainOracle.OracleType',
name: 'oracleKind',
type: 'uint8',
},
],
name: 'addOracle',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'connectors',
outputs: [
{
internalType: 'contract IERC20[]',
name: 'allConnectors',
type: 'address[]',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'contract IERC20',
name: 'srcToken',
type: 'address',
},
{
internalType: 'contract IERC20',
name: 'dstToken',
type: 'address',
},
{
internalType: 'bool',
name: 'useWrappers',
type: 'bool',
},
],
name: 'getRate',
outputs: [
{
internalType: 'uint256',
name: 'weightedRate',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'contract IERC20',
name: 'srcToken',
type: 'address',
},
{
internalType: 'bool',
name: 'useSrcWrappers',
type: 'bool',
},
],
name: 'getRateToEth',
outputs: [
{
internalType: 'uint256',
name: 'weightedRate',
type: 'uint256',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'multiWrapper',
outputs: [
{
internalType: 'contract MultiWrapper',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'oracles',
outputs: [
{
internalType: 'contract IOracle[]',
name: 'allOracles',
type: 'address[]',
},
{
internalType: 'enum OffchainOracle.OracleType[]',
name: 'oracleTypes',
type: 'uint8[]',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [],
name: 'owner',
outputs: [
{
internalType: 'address',
name: '',
type: 'address',
},
],
stateMutability: 'view',
type: 'function',
},
{
inputs: [
{
internalType: 'contract IERC20',
name: 'connector',
type: 'address',
},
],
name: 'removeConnector',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'contract IOracle',
name: 'oracle',
type: 'address',
},
{
internalType: 'enum OffchainOracle.OracleType',
name: 'oracleKind',
type: 'uint8',
},
],
name: 'removeOracle',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [],
name: 'renounceOwnership',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'contract MultiWrapper',
name: '_multiWrapper',
type: 'address',
},
],
name: 'setMultiWrapper',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
inputs: [
{
internalType: 'address',
name: 'newOwner',
type: 'address',
},
],
name: 'transferOwnership',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
] as const;
export class OffchainOracleAbi__factory {
static readonly abi = _abi;
static createInterface(): OffchainOracleAbiInterface {
return new utils.Interface(_abi) as OffchainOracleAbiInterface;
}
static connect(address: string, signerOrProvider: Signer | Provider): OffchainOracleAbi {
return new Contract(address, _abi, signerOrProvider) as OffchainOracleAbi;
}
}

@ -0,0 +1,316 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
import { Contract, Signer, utils } from 'ethers';
import type { Provider } from '@ethersproject/providers';
import type { OptimismL1FeeOracleAbi, OptimismL1FeeOracleAbiInterface } from '../OptimismL1FeeOracleAbi';
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 OptimismL1FeeOracleAbi__factory {
static readonly abi = _abi;
static createInterface(): OptimismL1FeeOracleAbiInterface {
return new utils.Interface(_abi) as OptimismL1FeeOracleAbiInterface;
}
static connect(address: string, signerOrProvider: Signer | Provider): OptimismL1FeeOracleAbi {
return new Contract(address, _abi, signerOrProvider) as OptimismL1FeeOracleAbi;
}
}

@ -0,0 +1,6 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export { MulticallAbi__factory } from './MulticallAbi__factory';
export { OffchainOracleAbi__factory } from './OffchainOracleAbi__factory';
export { OptimismL1FeeOracleAbi__factory } from './OptimismL1FeeOracleAbi__factory';

10
src/contracts/index.ts Normal file

@ -0,0 +1,10 @@
/* Autogenerated file. Do not edit manually. */
/* tslint:disable */
/* eslint-disable */
export type { MulticallAbi } from './MulticallAbi';
export type { OffchainOracleAbi } from './OffchainOracleAbi';
export type { OptimismL1FeeOracleAbi } from './OptimismL1FeeOracleAbi';
export * as factories from './factories';
export { MulticallAbi__factory } from './factories/MulticallAbi__factory';
export { OffchainOracleAbi__factory } from './factories/OffchainOracleAbi__factory';
export { OptimismL1FeeOracleAbi__factory } from './factories/OptimismL1FeeOracleAbi__factory';

141
src/feeOracle.ts Normal file

@ -0,0 +1,141 @@
import { GasPriceOracle } from '@tornado/gas-price-oracle';
import { BigNumber, BigNumberish, ethers } from 'ethers';
import { parseUnits } from 'ethers/lib/utils';
import { TransactionData, TxType, ITornadoFeeOracle, GasPrice, LegacyGasPriceKey } from './types';
import { Provider } from '@ethersproject/abstract-provider';
import { ChainId, defaultGasPrices } from './config';
import { bump, calculateGasPriceInWei, fromGweiToWeiHex, serializeTx } from './utils';
import { getOptimismL1FeeOracle } from './contracts/factories';
import { AvailableTokenSymbols } from '@tornado/tornado-config';
export abstract class TornadoFeeOracle implements ITornadoFeeOracle {
protected provider: Provider;
public constructor(
protected chainId: ChainId,
rpcUrl: string,
protected oracle: GasPriceOracle,
) {
this.provider = new ethers.providers.JsonRpcProvider(rpcUrl);
}
/**
* Because Optimism transaction published on Mainnet, for each OP transaction we need to calculate L1 security fee:
* https://community.optimism.io/docs/developers/build/transaction-fees/#priority-fee
* @param {TransactionData} tx Transaction data to estimate L1 additional fee
* @returns Fee in WEI (MATIC)
*/
private async fetchL1OptimismFee(tx?: TransactionData): Promise<BigNumber> {
if (this.chainId != ChainId.OPTIMISM) return BigNumber.from(0);
const optimismL1FeeOracle = getOptimismL1FeeOracle(this.provider);
return await optimismL1FeeOracle.getL1Fee(serializeTx(tx));
}
/**
* Estimates next block gas for signed, unsigned or incomplete Tornado transaction
* @param {TransactionData} tx Transaction data in web3 / ethers format
* @param {TxType} type Tornado transaction type: withdrawal by user, withdrawal by relayer or 'other'
* @param {LegacyGasPriceKey} speed Preferred transaction speed, if uses legacy gas (before EIP-1559)
* @returns {Promise<string>} Gas value in WEI (hex-format)
*/
public async getGas(
tx?: TransactionData,
type: TxType = 'other',
speed: LegacyGasPriceKey = 'fast',
): Promise<BigNumber> {
const gasPrice = await this.getGasPrice(type, speed);
let gas = BigNumber.from(0);
gas = gas.add(calculateGasPriceInWei(gasPrice));
if (tx) tx = Object.assign(tx, { gasPrice });
gas = gas.mul(await this.getGasLimit(tx, type));
if (this.chainId === ChainId.OPTIMISM) gas = gas.add(await this.fetchL1OptimismFee(tx));
return gas;
}
/**
* Estimate next block gas price
* @param {TxType} type Tornado transaction type (to select correct default bump percent)
* @param {LegacyGasPriceKey} speed Preferred transaction speed, if uses legacy gas (before EIP-1559)
* @param {number} bumpPercent Gas bump percent to prioritize transaction
* @returns {GasPrice} Estimated gas price info in WEI (hexed) - legacy object with gasPrice property or EIP-1559 object with maxFeePerGas
* and maxPriorityFeePerGas properties
*/
async getGasPrice(
type: TxType = 'other',
speed: LegacyGasPriceKey = 'fast',
bumpPercent: number = 0,
): Promise<GasPrice> {
try {
return await this.oracle.getTxGasParams({ legacySpeed: speed, bumpPercent });
} catch (e) {
return { gasPrice: bump(fromGweiToWeiHex(defaultGasPrices[this.chainId][speed]), bumpPercent) };
}
}
/**
* Estimates gas limit for transaction (or basic gas limit, if no tx data provided)
* @param {TransactionData} tx Transaction data (object in web3 / ethers format)
* @param {TxType} type Tornado transaction type: withdrawal by user, withdrawal by relayer or 'other'
* @returns {Promise<BigNumber>} Gas limit
*/
abstract getGasLimit(tx?: TransactionData, type?: TxType): Promise<BigNumber>;
/**
* If user withdraw non-native tokens on ETH or Goerli, we need to calculate refund value:
* if the withdrawal is successful, this amount will be returned to the user after the transfer to the relayer,
* and if the relayer pays a commission and the transfer of tokens fails, this commission will remain to the relayer.
*
* Refund needed that recipient can use tokens after withdrawal (covers gas fee for send/swap)
* @returns {Promise<BigNumber>} Refund amount
*/
async calculateRefundInETH(): Promise<BigNumber> {
return (await this.getGas()).mul(2);
}
/**
* Estimates fee for withdrawal via relayer depending on type: gas bump percent is bigger, if it calculates by user,
* so that the real commission from the relayer side is a little less,
* in order to the relayer can send a transaction without fear that he will go into the red
* @param {TxType} type Tornado transaction type: withdrawal costs calculation from user side or from relayer side
* @param {TransactionData} tx Transaction data (object in web3 / ethers format)
* @param {number} relayerFeePercent Relayer fee percent from the transaction amount (for example, 0.15 for BNB or 0.4 for ETH Mainnet)
* @param {string} currency Currency symbol
* @param {number} amount Withdrawal amount in selected currency
* @param {number} decimals Token (currency) decimals
* @param {BigNumberish} refund Refund in ETH, if withdrawed other tokens on Mainnet (not ETH)
* @param {BigNumberish} tokenPriceInEth If withdrawing other token on Mainnet or Goerli, need to provide token price in ETH (in WEI)
* @returns {Promise<BigNumber>} Fee in WEI
*/
async calculateWithdrawalFeeViaRelayer(
type: TxType,
tx: TransactionData,
relayerFeePercent: number,
currency: AvailableTokenSymbols,
amount: string,
decimals: number,
refund: BigNumberish = 0,
tokenPriceInEth?: BigNumberish,
): Promise<BigNumber> {
let withdrawalFee = BigNumber.from(0);
const gasCosts = await this.getGas(tx, type);
withdrawalFee = gasCosts;
const relayerFee = parseUnits(amount, decimals)
.mul(`${relayerFeePercent * 1e10}`)
.div(`${100 * 1e10}`);
if ((this.chainId === ChainId.MAINNET || this.chainId === ChainId.GOERLI) && currency.toLowerCase() != 'eth') {
if (!tokenPriceInEth) {
console.error('Token price is required argument, if not native chain token is withdrawed');
return BigNumber.from(0);
}
const tokenDecimals = BigNumber.from(10).pow(decimals);
return withdrawalFee.add(refund).mul(tokenDecimals).div(tokenPriceInEth).add(relayerFee);
}
return withdrawalFee.add(relayerFee);
}
}

30
src/feeOracleV4.ts Normal file

@ -0,0 +1,30 @@
import { defaultWithdrawalGasLimit } from './config';
import { TornadoFeeOracle } from './feeOracle';
import { ITornadoFeeOracle, TransactionData, TxType, LegacyGasPrices } from './types';
import { GasPriceOracle } from '@tornado/gas-price-oracle';
import { BigNumber } from 'ethers';
import { bump } from './utils';
export class TornadoFeeOracleV4 extends TornadoFeeOracle implements ITornadoFeeOracle {
public constructor(chainId: number, rpcUrl: string, defaultGasPrices?: LegacyGasPrices) {
const oracleConfig = {
chainId,
defaultRpc: rpcUrl,
defaultFallbackGasPrices: defaultGasPrices,
};
const gasPriceOracle = new GasPriceOracle(oracleConfig);
super(chainId, rpcUrl, gasPriceOracle);
}
async getGasLimit(tx?: TransactionData, type: TxType = 'other'): Promise<BigNumber> {
if (type === 'user_withdrawal') return BigNumber.from(defaultWithdrawalGasLimit[this.chainId]);
// Need to bump relayer gas limit for transaction, because predefined gas limit to small to be 100% sure that transaction will be sent
// This leads to fact that relayer often pays extra for gas from his own funds, however, this was designed by previous developers
if (type === 'relayer_withdrawal') return bump(defaultWithdrawalGasLimit[this.chainId], 20);
if (!tx) return BigNumber.from(21_000);
return this.provider.estimateGas(tx);
}
}

65
src/feeOracleV5.ts Normal file

@ -0,0 +1,65 @@
import { ChainId, defaultWithdrawalGasLimit } from './config';
import { TornadoFeeOracle } from './feeOracle';
import { ITornadoFeeOracle, GasPrice, TransactionData, TxType, LegacyGasPrices, LegacyGasPriceKey } from './types';
import { GasPriceOracle } from '@tornado/gas-price-oracle';
import { BigNumber } from 'ethers';
import { bump } from './utils';
export class TornadoFeeOracleV5 extends TornadoFeeOracle implements ITornadoFeeOracle {
public constructor(chainId: number, rpcUrl: string, defaultGasPrices?: LegacyGasPrices) {
const oracleConfig = {
chainId,
defaultRpc: rpcUrl,
minPriority: chainId === 1 || chainId === 5 ? 2 : 0.05,
percentile: 5,
blocksCount: 20,
defaultFallbackGasPrices: defaultGasPrices,
};
const gasPriceOracle = new GasPriceOracle(oracleConfig);
super(chainId, rpcUrl, gasPriceOracle);
}
async getGasLimit(tx?: TransactionData, type: TxType = 'other', bumpPercent: number = 20): Promise<BigNumber> {
if (!tx) return BigNumber.from(21_000);
/* Relayer gas limit must be lower so that fluctuations in gas price cannot lead to the fact that
* the relayer will actually pay for gas more than the money allocated for this by the user
* (that is, in fact, relayer will pay for gas from his own money, unless we make the bump percent less for him)
*/
if (type === 'relayer_withdrawal') bumpPercent = 10;
if (type === 'user_withdrawal') bumpPercent = 30;
try {
const fetchedGasLimit = await this.provider.estimateGas(tx);
return bump(fetchedGasLimit, bumpPercent);
} catch (e) {
if (type.endsWith('withdrawal')) return bump(defaultWithdrawalGasLimit[this.chainId], bumpPercent);
return BigNumber.from(21_000);
}
}
async getGasPrice(
type: TxType = 'other',
speed: LegacyGasPriceKey = 'fast',
bumpPercent?: number,
): Promise<GasPrice> {
// Only if bump percent didn't provided (if user provides 0, no need to recalculate)
if (bumpPercent === undefined) {
switch (this.chainId) {
case ChainId.GOERLI:
bumpPercent = type === 'user_withdrawal' ? 100 : 50;
break;
case ChainId.POLYGON:
case ChainId.AVAX:
case ChainId.XDAI:
bumpPercent = 30;
break;
default:
bumpPercent = 10;
}
}
return super.getGasPrice(type, speed, bumpPercent);
}
}

6
src/index.ts Normal file

@ -0,0 +1,6 @@
import { bump } from './utils';
export * from './feeOracleV4';
export * from './feeOracleV5';
export * from './tokenPriceOracle';
export { bump };

100
src/tokenPriceOracle.ts Normal file

@ -0,0 +1,100 @@
import { MultiCall } from './contracts/MulticallAbi';
import { Token, TokenPrices, TokenSymbol } from './types';
import { MulticallAbi, OffchainOracleAbi } from './contracts';
import { getMultiCallContract, getOffchainOracleContract } from './contracts/factories';
import { Provider } from '@ethersproject/abstract-provider';
import { ethers, BigNumber } from 'ethers';
import { defaultAbiCoder } from 'ethers/lib/utils';
import { instances, Instances } from '@tornado/tornado-config';
import { ChainId } from './config';
/**
* Filter non-native tokens from Tornado instances config
* @param instances Tornado instances from torn-token config ('@tornado/tornado-config' library)
* @returns Array of non-native tokens
*/
function filterTokensFromTornadoInstances(instances: Instances): Token[] {
const ethInstances = Object.values(instances[ChainId.MAINNET]);
return ethInstances.filter((tokenInstance) => !!tokenInstance.tokenAddress) as Token[];
}
const tornToken: Token = {
tokenAddress: '0x77777FeDdddFfC19Ff86DB637967013e6C6A116C',
symbol: 'torn',
decimals: 18,
};
const tornadoTokens = [tornToken, ...filterTokensFromTornadoInstances(instances)];
const defaultTokenPrices: TokenPrices = {
cdai: '12143621157112',
dai: '543174175301531',
usdc: '543496781058130',
usdt: '540358243246849',
wbtc: '15944433090979762571',
torn: '1683813509941878',
};
export class TokenPriceOracle {
private oracle: OffchainOracleAbi;
private multiCall: MulticallAbi;
private provider: Provider;
/**
*
* @param rpcUrl
* @param tokens
*/
constructor(
rpcUrl: string,
private tokens: Token[] = tornadoTokens,
) {
this.provider = new ethers.providers.JsonRpcProvider(rpcUrl);
this.oracle = getOffchainOracleContract(this.provider);
this.multiCall = getMultiCallContract(this.provider);
}
/**
* Prepare data for MultiCall contract
* @param {Token[]} tokens Tokens array
* @returns Valid structure to provide to MultiCall contract
*/
prepareCallData(tokens: Token[] = this.tokens): MultiCall.CallStruct[] {
return tokens.map((token) => ({
to: this.oracle.address,
data: this.oracle.interface.encodeFunctionData('getRateToEth', [token.tokenAddress, true]),
}));
}
/**
* Fetch actual tokens price rate to ETH from offchain oracles
* @param {Token[]} tokens Token array
* @returns {TokenPrices} Object with token price rate to ETH in WEI
*/
async fetchPrices(tokens: Token[] = this.tokens): Promise<TokenPrices> {
try {
if (!tokens?.length) return {};
const callData = this.prepareCallData(tokens);
const { results, success } = await this.multiCall.multicall(callData);
const prices: TokenPrices = {};
for (let i = 0; i < results.length; i++) {
const tokenSymbol = tokens[i].symbol.toLowerCase() as TokenSymbol;
if (!success[i]) {
if (defaultTokenPrices[tokenSymbol]) prices[tokenSymbol] = defaultTokenPrices[tokenSymbol];
continue;
}
const decodedRate = defaultAbiCoder.decode(['uint256'], results[i]).toString();
const tokenDecimals = BigNumber.from(10).pow(tokens[i].decimals);
const ethDecimals = BigNumber.from(10).pow(18);
const price = BigNumber.from(decodedRate).mul(tokenDecimals).div(ethDecimals);
prices[tokenSymbol] = price.toString();
}
return prices;
} catch (e) {
console.error('Cannot get token prices, return default: ' + e);
return defaultTokenPrices;
}
}
}

62
src/types.ts Normal file

@ -0,0 +1,62 @@
import { BigNumber, BigNumberish, BytesLike } from 'ethers';
import { GasPriceKey } from '@tornado/gas-price-oracle/lib/services';
import { AvailableTokenSymbols } from '@tornado/tornado-config';
export type LegacyGasPriceKey = GasPriceKey;
// Gas Prices in WEI
export type GasPrice = { maxFeePerGas: BigNumberish; maxPriorityFeePerGas: BigNumberish } | { gasPrice: BigNumberish };
export type LegacyGasPrices = {
[gasPriceType in LegacyGasPriceKey]: number;
};
export type TxType = 'user_withdrawal' | 'relayer_withdrawal' | 'other';
export interface TransactionData {
to: string;
from?: string;
nonce?: number;
gasLimit?: BigNumberish;
gasPrice?: BigNumberish;
data?: string;
value: BigNumberish;
chainId?: number;
type?: number;
maxFeePerGas?: BigNumberish;
maxPriorityFeePerGas?: BigNumberish;
}
export interface ITornadoFeeOracle {
getGas: (tx?: TransactionData, type?: TxType) => Promise<BigNumber>;
getGasPrice: (type?: TxType, speed?: LegacyGasPriceKey, bumpPercent?: number) => Promise<GasPrice>;
getGasLimit: (tx?: TransactionData, type?: TxType, bumpPercent?: number) => Promise<BigNumber>;
calculateRefundInETH: () => Promise<BigNumber>;
calculateWithdrawalFeeViaRelayer: (
type: TxType,
tx: TransactionData,
relayerFeePercent: number,
currency: AvailableTokenSymbols,
amount: string,
decimals: number,
refund: BigNumberish,
tokenPriceInEth?: BigNumberish,
) => Promise<BigNumber>;
}
export type WithdrawalData = {
contract: string;
proof: BytesLike;
args: [BytesLike, BytesLike, string, string, BigNumberish, BigNumberish];
};
export type TornadoPoolInstance = {
currency: AvailableTokenSymbols;
amount: number;
decimals: number;
};
export type TokenSymbol = 'dai' | 'cdai' | 'usdc' | 'usdt' | 'wbtc' | 'torn';
export type Token = {
tokenAddress: string;
symbol: TokenSymbol;
decimals: number;
};
export type TokenPrices = { [tokenSymbol in TokenSymbol]?: BigNumberish };

34
src/utils.ts Normal file

@ -0,0 +1,34 @@
import { serialize } from '@ethersproject/transactions';
import { GasPrice, TransactionData } from './types';
import { BigNumber, BigNumberish } from 'ethers';
import BigNumberFloat from 'bignumber.js';
BigNumberFloat.config({ EXPONENTIAL_AT: 100 });
const GWEI = 1e9;
export function serializeTx(tx?: TransactionData | string): string {
if (!tx) tx = '0x';
if (typeof tx === 'string') return tx;
return serialize(tx);
}
export function calculateGasPriceInWei(gasPrice: GasPrice): BigNumber {
// @ts-ignore
return gasPrice.gasPrice || gasPrice.maxFeePerGas;
}
export function bump(value: BigNumberish, percent: number): BigNumber {
const hundredPercents = BigNumberFloat(100);
return BigNumber.from(
BigNumberFloat(BigNumber.from(value).toHexString())
.times(hundredPercents.plus(BigNumberFloat(percent)))
.div(hundredPercents)
.toString(),
);
}
export function fromGweiToWeiHex(value: number | string): BigNumberish {
return BigNumber.from(BigNumberFloat(value).times(GWEI).toString()).toHexString();
}

9
tsconfig.esm.json Normal file

@ -0,0 +1,9 @@
{
"extends": "./tsconfig",
"compilerOptions": {
"declaration": false,
"target": "es2015",
"module": "es2015",
"outDir": "lib/esm"
}
}

33
tsconfig.json Normal file

@ -0,0 +1,33 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"lib": ["es5", "es6", "es2021", "esnext"],
"experimentalDecorators": true,
"sourceMap": true,
"outDir": "lib",
"noImplicitAny": true,
"allowJs": true,
"declaration": true,
"skipLibCheck": true,
"esModuleInterop": true,
"checkJs": false,
"noUnusedLocals": true,
"strictNullChecks": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"allowSyntheticDefaultImports": true,
"strictFunctionTypes": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"types": ["@types/node"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "src/tests"]
}

1151
yarn.lock Normal file

File diff suppressed because it is too large Load Diff