Add v5 correct gas price/gas limit estimation

This commit is contained in:
Theo 2023-07-16 14:10:12 -07:00
parent 16a17079eb
commit 44f70bd41d
4 changed files with 1624 additions and 2442 deletions

@ -13,12 +13,12 @@
"prettier:fix": "npx prettier --write . --config .prettierrc", "prettier:fix": "npx prettier --write . --config .prettierrc",
"lint": "yarn eslint && yarn prettier:check", "lint": "yarn eslint && yarn prettier:check",
"test": "mocha", "test": "mocha",
"start": "docker-compose up -d redis && concurrently \"yarn server\" \"yarn priceWatcher\" \"yarn treeWatcher \"yarn worker\" \"yarn healthWatcher\"" "start": "docker-compose up -d redis && concurrently \"yarn server\" \"yarn priceWatcher\" \"yarn treeWatcher\" \"yarn worker\" \"yarn healthWatcher\""
}, },
"author": "tornado.cash", "author": "tornado.cash",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tornado/circomlib": "^0.0.20-p2", "circomlib": "git+https://git.tornado.ws/tornado-packages/circomlib.git#3b492f9801573eebcfe1b6c584afe8a3beecf2b4",
"ajv": "^6.12.5", "ajv": "^6.12.5",
"async-mutex": "^0.2.4", "async-mutex": "^0.2.4",
"bull": "^3.12.1", "bull": "^3.12.1",
@ -27,7 +27,7 @@
"eth-ens-namehash": "^2.0.8", "eth-ens-namehash": "^2.0.8",
"express": "^4.17.1", "express": "^4.17.1",
"fixed-merkle-tree": "^0.4.0", "fixed-merkle-tree": "^0.4.0",
"gas-price-oracle": "^0.4.7", "@tornado/gas-price-oracle": "^0.5.3",
"ioredis": "^4.14.1", "ioredis": "^4.14.1",
"node-fetch": "^2.6.7", "node-fetch": "^2.6.7",
"torn-token": "1.0.6", "torn-token": "1.0.6",

@ -1,6 +1,6 @@
const { instances, netId } = require('./config') const { instances, netId } = require('./config')
const { poseidon } = require('@tornado/circomlib') const { poseidon } = require('circomlib')
const { toBN, toChecksumAddress, BN, fromWei, isAddress, toWei } = require('web3-utils') const { toBN, toChecksumAddress, BN, fromWei, isAddress, toWei, toHex } = require('web3-utils')
const TOKENS = { const TOKENS = {
torn: { torn: {
@ -161,6 +161,7 @@ module.exports = {
toChecksumAddress, toChecksumAddress,
fromWei, fromWei,
toWei, toWei,
toHex,
BN, BN,
isAddress, isAddress,
RelayerError, RelayerError,

@ -1,6 +1,6 @@
const fs = require('fs') const fs = require('fs')
const MerkleTree = require('fixed-merkle-tree') const MerkleTree = require('fixed-merkle-tree')
const { GasPriceOracle } = require('gas-price-oracle') const { GasPriceOracle } = require('@tornado/gas-price-oracle')
const { Utils, Controller } = require('tornado-anonymity-mining') const { Utils, Controller } = require('tornado-anonymity-mining')
const swapABI = require('../abis/swap.abi.json') const swapABI = require('../abis/swap.abi.json')
@ -15,6 +15,7 @@ const {
sleep, sleep,
toBN, toBN,
toWei, toWei,
toHex,
fromWei, fromWei,
toChecksumAddress, toChecksumAddress,
RelayerError, RelayerError,
@ -46,7 +47,7 @@ let txManager
let controller let controller
let swap let swap
let minerContract let minerContract
const gasPriceOracle = new GasPriceOracle({ defaultRpc: oracleRpcUrl }) let gasPriceOracle
async function fetchTree() { async function fetchTree() {
const elements = await redis.get('tree:elements') const elements = await redis.get('tree:elements')
@ -93,6 +94,12 @@ async function start() {
BASE_FEE_RESERVE_PERCENTAGE: baseFeeReserve, BASE_FEE_RESERVE_PERCENTAGE: baseFeeReserve,
}, },
}) })
gasPriceOracle = new GasPriceOracle({
defaultRpc: oracleRpcUrl,
minPriority: 2,
percentile: 5,
blocksCount: 20,
})
swap = new web3.eth.Contract(swapABI, await resolver.resolve(torn.rewardSwap.address)) swap = new web3.eth.Contract(swapABI, await resolver.resolve(torn.rewardSwap.address))
minerContract = new web3.eth.Contract(miningABI, await resolver.resolve(torn.miningV2.address)) minerContract = new web3.eth.Contract(miningABI, await resolver.resolve(torn.miningV2.address))
redisSubscribe.subscribe('treeUpdate', fetchTree) redisSubscribe.subscribe('treeUpdate', fetchTree)
@ -111,31 +118,50 @@ async function start() {
} }
} }
function checkFee({ data }) { function checkFee({ data }, gasInfo) {
if (data.type === jobType.TORNADO_WITHDRAW) { if (data.type === jobType.TORNADO_WITHDRAW) {
return checkTornadoFee(data) return checkTornadoFee(data, gasInfo)
} }
return checkMiningFee(data) return checkMiningFee(data)
} }
async function getGasPrice() { async function getGasPrice() {
const block = await web3.eth.getBlock('latest') try {
const { maxFeePerGas, gasPrice } = await gasPriceOracle.getTxGasParams({
legacySpeed: 'fast',
bumpPercent: 10,
})
if (block && block.baseFeePerGas) { return toBN(maxFeePerGas || gasPrice)
return toBN(block.baseFeePerGas) } catch (e) {
const block = await web3.eth.getBlock('latest')
if (block && block.baseFeePerGas) {
return toBN(block.baseFeePerGas)
}
const gasPrice = await web3.eth.getGasPrice()
return toBN(gasPrice)
} }
const { fast } = await gasPriceOracle.gasPrices()
return toBN(toWei(fast.toString(), 'gwei'))
} }
async function checkTornadoFee({ args, contract }) { async function estimateWithdrawalGasLimit(tx) {
try {
const fetchedGasLimit = await web3.eth.estimateGas(tx)
const bumped = Math.floor(fetchedGasLimit * 1.2)
return bumped
} catch (e) {
console.log('Estimation error: ', e)
return gasLimits[jobType.TORNADO_WITHDRAW]
}
}
async function checkTornadoFee({ args, contract }, { gasLimit, gasPrice }) {
const { currency, amount, decimals } = getInstance(contract) const { currency, amount, decimals } = getInstance(contract)
const [fee, refund] = [args[4], args[5]].map(toBN) const [fee, refund] = [args[4], args[5]].map(toBN)
const gasPrice = await getGasPrice()
const ethPrice = await redis.hget('prices', currency) const ethPrice = await redis.hget('prices', currency)
const expense = gasPrice.mul(toBN(gasLimits[jobType.TORNADO_WITHDRAW])) const expense = toBN(gasPrice).mul(toBN(gasLimit))
const feePercent = toBN(fromDecimals(amount, decimals)) const feePercent = toBN(fromDecimals(amount, decimals))
.mul(toBN(parseInt(tornadoServiceFee * 1e10))) .mul(toBN(parseInt(tornadoServiceFee * 1e10)))
@ -233,12 +259,17 @@ async function getTxObject({ data }) {
calldata = contract.methods.withdraw(data.proof, ...data.args).encodeABI() calldata = contract.methods.withdraw(data.proof, ...data.args).encodeABI()
} }
return { const gasPrice = await getGasPrice()
const incompleteTx = {
value: data.args[5], value: data.args[5],
from: txManager.address, // Required, because without it relayerRegistry.burn will fail, because msg.sender is not relayer
to: contract._address, to: contract._address,
data: calldata, data: calldata,
gasLimit: gasLimits['WITHDRAW_WITH_EXTRA'], gasPrice: toHex(gasPrice),
} }
const gasLimit = await estimateWithdrawalGasLimit(incompleteTx)
return Object.assign(incompleteTx, { gasLimit })
} else { } else {
const method = data.type === jobType.MINING_REWARD ? 'reward' : 'withdraw' const method = data.type === jobType.MINING_REWARD ? 'reward' : 'withdraw'
const calldata = minerContract.methods[method](data.proof, data.args).encodeABI() const calldata = minerContract.methods[method](data.proof, data.args).encodeABI()
@ -281,8 +312,9 @@ async function processJob(job) {
} }
async function submitTx(job, retry = 0) { async function submitTx(job, retry = 0) {
await checkFee(job) const rawTx = await getTxObject(job)
currentTx = await txManager.createTx(await getTxObject(job)) await checkFee(job, rawTx)
currentTx = await txManager.createTx(rawTx)
if (job.data.type !== jobType.TORNADO_WITHDRAW) { if (job.data.type !== jobType.TORNADO_WITHDRAW) {
await fetchTree() await fetchTree()

3989
yarn.lock

File diff suppressed because it is too large Load Diff