Correct gas estimation to v5 version

This commit is contained in:
Theo 2023-07-21 21:19:08 -07:00
parent d69a17073a
commit 85ca668396
7 changed files with 92 additions and 23 deletions

1
.npmrc Normal file

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

@ -1,5 +1,5 @@
{ {
"name": "relay", "name": "classic-relay",
"version": "5.0.0", "version": "5.0.0",
"description": "Relayer for Tornado.cash privacy solution. https://tornado.cash", "description": "Relayer for Tornado.cash privacy solution. https://tornado.cash",
"scripts": { "scripts": {
@ -25,6 +25,7 @@
"@fastify/cors": "^8.0.0", "@fastify/cors": "^8.0.0",
"@fastify/helmet": "^9.1.0", "@fastify/helmet": "^9.1.0",
"@fastify/sensible": "^5.1.0", "@fastify/sensible": "^5.1.0",
"@tornado/gas-price-oracle": "^0.5.3",
"ajv": "^8.11.0", "ajv": "^8.11.0",
"bullmq": "^1.80.6", "bullmq": "^1.80.6",
"compare-versions": "^4.1.3", "compare-versions": "^4.1.3",
@ -32,7 +33,6 @@
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"ethers": "^5.6.4", "ethers": "^5.6.4",
"fastify": "^4.2.0", "fastify": "^4.2.0",
"gas-price-oracle": "^0.5.0",
"ioredis": "^5.0.6", "ioredis": "^5.0.6",
"json-schema-to-ts": "^2.2.0", "json-schema-to-ts": "^2.2.0",
"node-fetch": "2", "node-fetch": "2",

@ -1,3 +1,5 @@
import { BigNumber, BigNumberish } from 'ethers';
export const parseJSON = (str: string) => { export const parseJSON = (str: string) => {
let parsed = null; let parsed = null;
try { try {
@ -8,3 +10,9 @@ export const parseJSON = (str: string) => {
return parsed; return parsed;
} }
}; };
export const bump = (value: BigNumberish, percent: number | BigNumber): BigNumber => {
const hundredPercents = BigNumber.from(100);
return BigNumber.from(value).mul(hundredPercents.add(percent)).div(hundredPercents);
};

@ -23,8 +23,8 @@ export const relayerProcessor: RelayerProcessor = async (job) => {
const txService = getTxService(); const txService = getTxService();
txService.currentJob = job; txService.currentJob = job;
const withdrawalData = job.data; const withdrawalData = job.data;
await txService.checkTornadoFee(withdrawalData);
const txData = await txService.prepareTxData(withdrawalData); const txData = await txService.prepareTxData(withdrawalData);
await txService.checkTornadoFee(withdrawalData, txData);
return await txService.sendTx(txData); return await txService.sendTx(txData);
} catch (e) { } catch (e) {
if ((e instanceof ExecutionError && e.code === 'REVERTED') || job.attemptsMade === txJobAttempts) { if ((e instanceof ExecutionError && e.code === 'REVERTED') || job.attemptsMade === txJobAttempts) {

@ -27,7 +27,7 @@ import { availableIds, netIds, NetInstances } from 'torn-token';
import { getAddress } from 'ethers/lib/utils'; import { getAddress } from 'ethers/lib/utils';
import { BigNumber, providers, Wallet } from 'ethers'; import { BigNumber, providers, Wallet } from 'ethers';
import { container, singleton } from 'tsyringe'; import { container, singleton } from 'tsyringe';
import { FallbackGasPrices } from 'gas-price-oracle'; import { FallbackGasPrices } from '@tornado/gas-price-oracle';
import { RedisStore } from '../modules/redis'; import { RedisStore } from '../modules/redis';
type relayerQueueName = `relayer_${availableIds}`; type relayerQueueName = `relayer_${availableIds}`;

@ -1,5 +1,5 @@
import { TransactionData, TxManager } from 'tx-manager'; import { TransactionData, TxManager } from 'tx-manager';
import { GasPriceOracle } from 'gas-price-oracle'; import { GasPriceOracle } from '@tornado/gas-price-oracle';
import { Provider } from '@ethersproject/providers'; import { Provider } from '@ethersproject/providers';
import { serialize } from '@ethersproject/transactions'; import { serialize } from '@ethersproject/transactions';
import { formatEther, parseUnits } from 'ethers/lib/utils'; import { formatEther, parseUnits } from 'ethers/lib/utils';
@ -12,7 +12,7 @@ import { Job } from 'bullmq';
import { RelayerJobData } from '../queue'; import { RelayerJobData } from '../queue';
import { ConfigService } from './config.service'; import { ConfigService } from './config.service';
import { container, injectable } from 'tsyringe'; import { container, injectable } from 'tsyringe';
import { parseJSON } from '../modules/utils'; import { parseJSON, bump } from '../modules/utils';
import { getOvmGasPriceOracle } from '../modules/contracts'; import { getOvmGasPriceOracle } from '../modules/contracts';
export type WithdrawalData = { export type WithdrawalData = {
@ -50,6 +50,9 @@ export class TxService {
const gasPriceOracleConfig = { const gasPriceOracleConfig = {
defaultRpc: rpcUrl, defaultRpc: rpcUrl,
chainId: netId, chainId: netId,
minPriority: netId === ChainIds.ethereum || ChainIds.goerli ? 2 : 0.05,
percentile: 5,
blocksCount: 20,
fallbackGasPrices: this.config?.fallbackGasPrices, fallbackGasPrices: this.config?.fallbackGasPrices,
}; };
this.txManager = new TxManager({ this.txManager = new TxManager({
@ -116,15 +119,64 @@ export class TxService {
} }
} }
async getGasPrice(): Promise<BigNumber> {
let bumpPercent: number;
switch (netId) {
case ChainIds.goerli:
bumpPercent = 50;
break;
case ChainIds.polygon:
case ChainIds.avalanche:
case ChainIds.xdai:
bumpPercent = 30;
break;
default:
bumpPercent = 10;
}
try {
const gasParams = await this.oracle.getTxGasParams({
legacySpeed: 'fast',
bumpPercent,
});
console.log(BigNumber.from(gasParams['maxFeePerGas']).toString());
return BigNumber.from(gasParams['maxFeePerGas'] || gasParams['gasPrice']);
} catch (e) {
const feeData = await this.provider.getFeeData();
return bump(feeData.maxFeePerGas, bumpPercent);
}
}
async estimateGasLimit(txData: TransactionData): Promise<BigNumber> {
try {
const fetchedGasLimit = await this.provider.estimateGas(txData);
const bumped = bump(fetchedGasLimit, 10);
console.log('Gas limit: ', bumped.toString());
return bumped;
} catch (e) {
console.log('Estimation error: ', e);
return BigNumber.from(this.gasLimit);
}
}
async prepareTxData(data: WithdrawalData): Promise<TransactionData> { async prepareTxData(data: WithdrawalData): Promise<TransactionData> {
const { contract, proof, args } = data; const { contract, proof, args } = data;
const calldata = this.tornadoProxy.interface.encodeFunctionData('withdraw', [contract, proof, ...args]); const calldata = this.tornadoProxy.interface.encodeFunctionData('withdraw', [contract, proof, ...args]);
return {
const gasPrice = await this.getGasPrice();
const incompleteTxData: TransactionData = {
value: args[5], value: args[5],
to: this.tornadoProxy.address, to: this.tornadoProxy.address,
from: this.txManager.address, // Required to estimate relayerRegistry.burn for Ethereum Mainnet withdrawals
data: calldata, data: calldata,
gasLimit: this.gasLimit, gasLimit: this.gasLimit,
gasPrice: gasPrice,
}; };
const gasLimit = await this.estimateGasLimit(incompleteTxData);
return Object.assign(incompleteTxData, { gasLimit });
} }
async getL1Fee(data: WithdrawalData, gasPrice: BigNumber) { async getL1Fee(data: WithdrawalData, gasPrice: BigNumber) {
@ -145,18 +197,14 @@ export class TxService {
return await ovmOracle.getL1Fee(tx); return await ovmOracle.getL1Fee(tx);
} }
async checkTornadoFee(data: WithdrawalData) { async checkTornadoFee(data: WithdrawalData, txData: TransactionData) {
const { contract, args } = data; const { contract, args } = data;
const instance = this.config.getInstance(contract); const instance = this.config.getInstance(contract);
if (!instance) throw new Error('Instance not found'); if (!instance) throw new Error('Instance not found');
const { currency, amount, decimals } = instance; const { currency, amount, decimals } = instance;
const [fee, refund] = [args[4], args[5]].map(BigNumber.from); const [fee, refund] = [args[4], args[5]].map(BigNumber.from);
const gasPrice = await this.getGasPrice(); const gasPrice = BigNumber.from(txData.gasPrice);
let gasLimit = this.gasLimit; let operationCost = gasPrice.mul(txData.gasLimit);
if (!this.config.isLightMode) {
gasLimit = gasLimits[RelayerJobType.TORNADO_WITHDRAW];
}
let operationCost = gasPrice.mul(gasLimit);
if (netId === ChainIds.optimism) { if (netId === ChainIds.optimism) {
const l1Fee = await this.getL1Fee(data, gasPrice); const l1Fee = await this.getL1Fee(data, gasPrice);
@ -183,15 +231,6 @@ export class TxService {
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.'); throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.');
} }
} }
async getGasPrice(): Promise<BigNumber> {
const gasPrices = await this.oracle.gasPrices();
let gasPrice = gasPrices['fast'];
if ('maxFeePerGas' in gasPrices) {
gasPrice = gasPrices['maxFeePerGas'];
}
return parseUnits(String(gasPrice), 'gwei');
}
} }
export default () => container.resolve(TxService); export default () => container.resolve(TxService);

@ -857,6 +857,15 @@
dependencies: dependencies:
defer-to-connect "^1.0.1" defer-to-connect "^1.0.1"
"@tornado/gas-price-oracle@^0.5.3":
version "0.5.3"
resolved "https://git.tornado.ws/api/packages/tornado-packages/npm/%40tornado%2Fgas-price-oracle/-/0.5.3/gas-price-oracle-0.5.3.tgz#fb5423dddee2f52edbc16174c5ddce90bea5413d"
integrity sha512-LpVfPiPIz3FOmJdiqJf/yoeO5n9/Pd5jgtdY+6hB9lNW0AiWhylhpScojICViS+3OL9QC8CoTlgr+kbfGeO9pQ==
dependencies:
axios "^0.21.2"
bignumber.js "^9.0.0"
node-cache "^5.1.2"
"@tsconfig/node10@^1.0.7": "@tsconfig/node10@^1.0.7":
version "1.0.9" version "1.0.9"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2"
@ -1835,6 +1844,11 @@ clone-response@^1.0.2:
dependencies: dependencies:
mimic-response "^1.0.0" mimic-response "^1.0.0"
clone@2.x:
version "2.1.2"
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
cluster-key-slot@^1.1.0: cluster-key-slot@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d" resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d"
@ -4427,6 +4441,13 @@ node-addon-api@^2.0.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32"
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
node-cache@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d"
integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==
dependencies:
clone "2.x"
node-fetch@2, node-fetch@^2.6.7: node-fetch@2, node-fetch@^2.6.7:
version "2.6.7" version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"