2020-09-28 05:28:34 +03:00
|
|
|
const Web3 = require('web3')
|
|
|
|
const { GasPriceOracle } = require('gas-price-oracle')
|
2021-06-17 16:18:22 +03:00
|
|
|
const { toBN, toWei, fromWei, toHex } = require('web3-utils')
|
2020-09-30 18:35:48 +03:00
|
|
|
|
2021-06-03 17:23:30 +03:00
|
|
|
const proxyLightABI = require('../abis/proxyLightABI.json')
|
2020-09-30 18:35:48 +03:00
|
|
|
const { queue } = require('./queue')
|
2021-06-03 17:23:30 +03:00
|
|
|
const { getInstance, fromDecimals } = require('./utils')
|
2020-11-27 19:21:56 +03:00
|
|
|
const { jobType, status } = require('./constants')
|
2020-10-05 17:22:52 +03:00
|
|
|
const {
|
2021-03-17 15:39:34 +03:00
|
|
|
netId,
|
2021-06-17 16:18:22 +03:00
|
|
|
gasPrices: GAS_PRICES,
|
2020-10-05 17:22:52 +03:00
|
|
|
gasLimits,
|
|
|
|
instances,
|
2021-03-17 15:39:34 +03:00
|
|
|
privateKey,
|
2021-06-03 17:23:30 +03:00
|
|
|
proxyLight,
|
2021-03-17 15:39:34 +03:00
|
|
|
httpRpcUrl,
|
|
|
|
tornadoServiceFee,
|
2020-10-06 14:20:26 +03:00
|
|
|
} = require('./config')
|
2020-10-02 13:16:43 +03:00
|
|
|
const { TxManager } = require('tx-manager')
|
2020-09-28 05:28:34 +03:00
|
|
|
|
|
|
|
let web3
|
|
|
|
let currentTx
|
2020-09-29 06:17:42 +03:00
|
|
|
let currentJob
|
2020-09-30 18:35:48 +03:00
|
|
|
let txManager
|
2021-06-03 17:23:30 +03:00
|
|
|
let gasPriceOracle
|
2020-09-28 05:28:34 +03:00
|
|
|
|
2021-06-03 17:23:30 +03:00
|
|
|
function start() {
|
2021-03-02 07:38:16 +03:00
|
|
|
try {
|
|
|
|
web3 = new Web3(httpRpcUrl)
|
|
|
|
const { CONFIRMATIONS, MAX_GAS_PRICE } = process.env
|
2021-06-29 00:48:01 +03:00
|
|
|
const gasPriceOracleConfig = {
|
|
|
|
chainId: netId,
|
|
|
|
defaultFallbackGasPrices: GAS_PRICES,
|
2021-06-03 17:23:30 +03:00
|
|
|
}
|
|
|
|
|
2021-06-29 00:48:01 +03:00
|
|
|
gasPriceOracle = new GasPriceOracle(gasPriceOracleConfig)
|
|
|
|
|
2021-03-02 07:38:16 +03:00
|
|
|
txManager = new TxManager({
|
|
|
|
privateKey,
|
|
|
|
rpcUrl: httpRpcUrl,
|
|
|
|
config: { CONFIRMATIONS, MAX_GAS_PRICE, THROW_ON_REVERT: false },
|
2021-06-03 17:23:30 +03:00
|
|
|
gasPriceOracleConfig,
|
2021-03-02 07:38:16 +03:00
|
|
|
})
|
2021-06-03 17:23:30 +03:00
|
|
|
|
2021-03-02 07:38:16 +03:00
|
|
|
queue.process(processJob)
|
|
|
|
console.log('Worker started')
|
|
|
|
} catch (e) {
|
|
|
|
console.error('error on start worker', e.message)
|
2020-10-02 08:38:31 +03:00
|
|
|
}
|
2020-09-28 05:28:34 +03:00
|
|
|
}
|
|
|
|
|
2021-06-17 16:18:22 +03:00
|
|
|
async function getGasPrices() {
|
2021-06-29 00:48:01 +03:00
|
|
|
const networksWithOracle = [56, 137]
|
|
|
|
if (networksWithOracle.includes(netId)) {
|
|
|
|
return await gasPriceOracle.gasPrices()
|
2021-06-17 16:18:22 +03:00
|
|
|
}
|
|
|
|
|
2021-06-29 00:48:01 +03:00
|
|
|
return GAS_PRICES
|
2021-06-17 16:18:22 +03:00
|
|
|
}
|
|
|
|
|
2020-10-02 15:09:33 +03:00
|
|
|
async function checkTornadoFee({ args, contract }) {
|
2020-10-05 17:22:52 +03:00
|
|
|
const { currency, amount } = getInstance(contract)
|
2021-06-03 17:23:30 +03:00
|
|
|
const { decimals } = instances[currency]
|
|
|
|
const fee = toBN(args[4])
|
|
|
|
|
2021-06-17 16:18:22 +03:00
|
|
|
const { fast } = await getGasPrices()
|
2020-10-05 17:22:52 +03:00
|
|
|
|
2020-10-06 14:20:26 +03:00
|
|
|
const expense = toBN(toWei(fast.toString(), 'gwei')).mul(toBN(gasLimits[jobType.TORNADO_WITHDRAW]))
|
2020-10-05 17:22:52 +03:00
|
|
|
const feePercent = toBN(fromDecimals(amount, decimals))
|
2021-05-23 13:29:07 +03:00
|
|
|
.mul(toBN(parseInt(tornadoServiceFee * 1e10)))
|
2020-10-05 17:22:52 +03:00
|
|
|
.div(toBN(1e10 * 100))
|
2021-06-03 17:23:30 +03:00
|
|
|
const desiredFee = expense.add(feePercent)
|
|
|
|
|
2020-10-05 17:22:52 +03:00
|
|
|
console.log(
|
|
|
|
'sent fee, desired fee, feePercent',
|
|
|
|
fromWei(fee.toString()),
|
|
|
|
fromWei(desiredFee.toString()),
|
|
|
|
fromWei(feePercent.toString()),
|
|
|
|
)
|
|
|
|
if (fee.lt(desiredFee)) {
|
|
|
|
throw new Error('Provided fee is not enough. Probably it is a Gas Price spike, try to resubmit.')
|
|
|
|
}
|
2020-09-28 05:28:34 +03:00
|
|
|
}
|
|
|
|
|
2021-06-17 16:18:22 +03:00
|
|
|
async function getTxObject({ data }) {
|
2021-06-03 17:23:30 +03:00
|
|
|
const contract = new web3.eth.Contract(proxyLightABI, proxyLight)
|
2020-10-06 11:51:41 +03:00
|
|
|
|
2021-06-03 17:23:30 +03:00
|
|
|
const calldata = contract.methods.withdraw(data.contract, data.proof, ...data.args).encodeABI()
|
2021-03-17 15:39:34 +03:00
|
|
|
|
2021-06-29 00:48:01 +03:00
|
|
|
const { fast } = await getGasPrices()
|
|
|
|
|
|
|
|
return {
|
2021-06-03 17:23:30 +03:00
|
|
|
value: data.args[5],
|
|
|
|
to: contract._address,
|
|
|
|
data: calldata,
|
|
|
|
gasLimit: gasLimits[jobType.TORNADO_WITHDRAW],
|
2021-06-29 00:48:01 +03:00
|
|
|
gasPrice: toHex(toWei(fast.toString(), 'gwei')),
|
2020-11-25 21:41:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-10 18:20:02 +03:00
|
|
|
async function processJob(job) {
|
2020-10-01 09:30:50 +03:00
|
|
|
try {
|
2020-10-05 17:22:52 +03:00
|
|
|
if (!jobType[job.data.type]) {
|
|
|
|
throw new Error(`Unknown job type: ${job.data.type}`)
|
|
|
|
}
|
|
|
|
currentJob = job
|
|
|
|
await updateStatus(status.ACCEPTED)
|
|
|
|
console.log(`Start processing a new ${job.data.type} job #${job.id}`)
|
2020-11-25 21:41:10 +03:00
|
|
|
await submitTx(job)
|
|
|
|
} catch (e) {
|
2020-11-26 00:42:16 +03:00
|
|
|
console.error('processJob', e.message)
|
2020-11-25 21:41:10 +03:00
|
|
|
await updateStatus(status.FAILED)
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
}
|
2020-10-14 14:43:38 +03:00
|
|
|
|
2021-06-03 17:23:30 +03:00
|
|
|
async function submitTx(job) {
|
|
|
|
await checkTornadoFee(job.data)
|
2021-03-17 15:39:34 +03:00
|
|
|
currentTx = await txManager.createTx(await getTxObject(job))
|
2020-10-05 17:22:52 +03:00
|
|
|
|
2020-11-25 21:41:10 +03:00
|
|
|
try {
|
|
|
|
const receipt = await currentTx
|
|
|
|
.send()
|
|
|
|
.on('transactionHash', txHash => {
|
|
|
|
updateTxHash(txHash)
|
|
|
|
updateStatus(status.SENT)
|
|
|
|
})
|
|
|
|
.on('mined', receipt => {
|
|
|
|
console.log('Mined in block', receipt.blockNumber)
|
|
|
|
updateStatus(status.MINED)
|
|
|
|
})
|
|
|
|
.on('confirmations', updateConfirmations)
|
2020-10-05 17:22:52 +03:00
|
|
|
|
2020-11-25 21:41:10 +03:00
|
|
|
if (receipt.status === 1) {
|
2020-10-05 17:22:52 +03:00
|
|
|
await updateStatus(status.CONFIRMED)
|
2020-11-25 21:41:10 +03:00
|
|
|
} else {
|
2021-06-03 17:23:30 +03:00
|
|
|
throw new Error('Submitted transaction failed')
|
2020-10-05 17:22:52 +03:00
|
|
|
}
|
2020-10-01 09:30:50 +03:00
|
|
|
} catch (e) {
|
2020-11-25 21:41:10 +03:00
|
|
|
// todo this could result in duplicated error logs
|
|
|
|
// todo handle a case where account tree is still not up to date (wait and retry)?
|
2021-06-03 17:23:30 +03:00
|
|
|
throw new Error(`Revert by smart contract ${e.message}`)
|
2020-10-01 09:30:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 06:17:42 +03:00
|
|
|
async function updateTxHash(txHash) {
|
|
|
|
console.log(`A new successfully sent tx ${txHash}`)
|
|
|
|
currentJob.data.txHash = txHash
|
|
|
|
await currentJob.update(currentJob.data)
|
|
|
|
}
|
|
|
|
|
2020-09-30 18:35:48 +03:00
|
|
|
async function updateConfirmations(confirmations) {
|
|
|
|
console.log(`Confirmations count ${confirmations}`)
|
|
|
|
currentJob.data.confirmations = confirmations
|
|
|
|
await currentJob.update(currentJob.data)
|
2020-09-28 05:28:34 +03:00
|
|
|
}
|
|
|
|
|
2020-10-02 15:09:33 +03:00
|
|
|
async function updateStatus(status) {
|
|
|
|
console.log(`Job status updated ${status}`)
|
|
|
|
currentJob.data.status = status
|
|
|
|
await currentJob.update(currentJob.data)
|
|
|
|
}
|
|
|
|
|
2020-10-05 17:22:52 +03:00
|
|
|
start()
|