works for regular tornado. dirty and WIP though
This commit is contained in:
parent
d888fdbd44
commit
c16164876e
@ -1,6 +1,11 @@
|
|||||||
require('dotenv').config()
|
require('dotenv').config()
|
||||||
|
|
||||||
module.exports = {
|
function updateConfig(options) {
|
||||||
|
config = Object.assign(config, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
updateConfig,
|
||||||
netId: Number(process.env.NET_ID) || 42,
|
netId: Number(process.env.NET_ID) || 42,
|
||||||
redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
|
redisUrl: process.env.REDIS_URL || 'redis://127.0.0.1:6379',
|
||||||
rpcUrl: process.env.RPC_URL || 'https://kovan.infura.io/',
|
rpcUrl: process.env.RPC_URL || 'https://kovan.infura.io/',
|
||||||
@ -154,3 +159,5 @@ module.exports = {
|
|||||||
gasBumpPercentage: process.env.GAS_PRICE_BUMP_PERCENTAGE || 20,
|
gasBumpPercentage: process.env.GAS_PRICE_BUMP_PERCENTAGE || 20,
|
||||||
rewardAccount: '0x0000000000000000000000000000000000000000',
|
rewardAccount: '0x0000000000000000000000000000000000000000',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = config
|
||||||
|
@ -10,7 +10,7 @@ async function tornadoWithdraw(req, res) {
|
|||||||
|
|
||||||
const { proof, args, contract } = req.body
|
const { proof, args, contract } = req.body
|
||||||
const id = await postJob({
|
const id = await postJob({
|
||||||
type: 'withdraw',
|
type: 'tornadoWithdraw',
|
||||||
data: { proof, args, contract },
|
data: { proof, args, contract },
|
||||||
})
|
})
|
||||||
return res.json({ id })
|
return res.json({ id })
|
||||||
|
@ -6,11 +6,10 @@ const redis = new Redis(redisUrl)
|
|||||||
|
|
||||||
const queue = new Queue('proofs', redisUrl)
|
const queue = new Queue('proofs', redisUrl)
|
||||||
|
|
||||||
async function postJob(type, data) {
|
async function postJob({ type, data }) {
|
||||||
const id = uuid()
|
const id = uuid()
|
||||||
|
|
||||||
const job = await queue.add(
|
const job = await queue.add(
|
||||||
'proofs',
|
|
||||||
{
|
{
|
||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
const express = require('express')
|
const express = require('express')
|
||||||
const status = require('status')
|
const status = require('./status')
|
||||||
const controller = require('controller')
|
const controller = require('./controller')
|
||||||
const { port } = require('../config')
|
const { port } = require('../config')
|
||||||
const { version } = require('../package.json')
|
const { version } = require('../package.json')
|
||||||
|
const worker = require('./worker')
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
@ -27,8 +28,11 @@ app.get('/', status.index)
|
|||||||
app.get('/v1/status', status.status)
|
app.get('/v1/status', status.status)
|
||||||
app.post('/v1/jobs/:id', status.getJob)
|
app.post('/v1/jobs/:id', status.getJob)
|
||||||
app.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
|
app.post('/v1/tornadoWithdraw', controller.tornadoWithdraw)
|
||||||
app.post('/v1/miningReward', controller.miningReward)
|
app.get('/status', status.status)
|
||||||
app.post('/v1/miningWithdraw', controller.miningWithdraw)
|
app.post('/relay', controller.tornadoWithdraw)
|
||||||
|
// app.post('/v1/miningReward', controller.miningReward)
|
||||||
|
// app.post('/v1/miningWithdraw', controller.miningWithdraw)
|
||||||
|
|
||||||
console.log('Version:', version)
|
worker.start()
|
||||||
app.listen(port)
|
app.listen(port || 8000)
|
||||||
|
console.log(`Relayer ${version} started on port ${port || 8000}`)
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
const queue = require('queue')
|
const queue = require('./queue')
|
||||||
|
const { GasPriceOracle } = require('gas-price-oracle')
|
||||||
|
const gasPriceOracle = new GasPriceOracle()
|
||||||
|
const { netId, relayerServiceFee, instances } = require('../config')
|
||||||
|
const { version } = require('../package.json')
|
||||||
|
|
||||||
async function status(req, res) {
|
async function status(req, res) {
|
||||||
let nonce = await redisClient.get('nonce')
|
const ethPrices = {
|
||||||
let latestBlock = null
|
dai: '6700000000000000', // 0.0067
|
||||||
try {
|
cdai: '157380000000000',
|
||||||
latestBlock = await web3.eth.getBlockNumber()
|
cusdc: '164630000000000',
|
||||||
} catch (e) {
|
usdc: '7878580000000000',
|
||||||
console.error('Problem with RPC', e)
|
usdt: '7864940000000000',
|
||||||
}
|
}
|
||||||
const { ethPrices } = fetcher
|
|
||||||
res.json({
|
res.json({
|
||||||
relayerAddress: web3.eth.defaultAccount,
|
relayerAddress: require('../config').rewardAccount,
|
||||||
mixers,
|
instances: instances.netId42,
|
||||||
gasPrices: await gasPriceOracle.gasPrices(),
|
gasPrices: await gasPriceOracle.gasPrices(),
|
||||||
netId,
|
netId,
|
||||||
ethPrices,
|
ethPrices,
|
||||||
relayerServiceFee,
|
relayerServiceFee,
|
||||||
nonce,
|
nonce: 123,
|
||||||
version,
|
version,
|
||||||
latestBlock
|
latestBlock: 12312312,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function index(req, res) {
|
function index(req, res) {
|
||||||
res.send('This is <a href=https://tornado.cash>tornado.cash</a> Relayer service. Check the <a href=/v1/status>/status</a> for settings')
|
res.send(
|
||||||
|
'This is <a href=https://tornado.cash>tornado.cash</a> Relayer service. Check the <a href=/v1/status>/status</a> for settings',
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getJob(req, res) {
|
async function getJob(req, res) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
const { isAddress } = require('web3-utils')
|
const { isAddress, toChecksumAddress } = require('web3-utils')
|
||||||
const { getInstance } = require('./utils')
|
const { getInstance } = require('./utils')
|
||||||
const { rewardAccount } = require('../config')
|
|
||||||
|
|
||||||
const Ajv = require('ajv')
|
const Ajv = require('ajv')
|
||||||
const ajv = new Ajv({ format: 'fast' })
|
const ajv = new Ajv({ format: 'fast' })
|
||||||
@ -13,7 +12,7 @@ ajv.addKeyword('isAddress', {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
errors: true
|
errors: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
ajv.addKeyword('isKnownContract', {
|
ajv.addKeyword('isKnownContract', {
|
||||||
@ -24,18 +23,18 @@ ajv.addKeyword('isKnownContract', {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
errors: true
|
errors: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
ajv.addKeyword('isFeeRecipient', {
|
ajv.addKeyword('isFeeRecipient', {
|
||||||
validate: (schema, data) => {
|
validate: (schema, data) => {
|
||||||
try {
|
try {
|
||||||
return rewardAccount === data
|
return require('../config').rewardAccount === toChecksumAddress(data)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
errors: true
|
errors: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const addressType = { type: 'string', pattern: '^0x[a-fA-F0-9]{40}$', isAddress: true }
|
const addressType = { type: 'string', pattern: '^0x[a-fA-F0-9]{40}$', isAddress: true }
|
||||||
@ -54,11 +53,11 @@ const tornadoWithdrawSchema = {
|
|||||||
type: 'array',
|
type: 'array',
|
||||||
maxItems: 6,
|
maxItems: 6,
|
||||||
minItems: 6,
|
minItems: 6,
|
||||||
items: [bytes32Type, bytes32Type, addressType, relayerType, bytes32Type, bytes32Type]
|
items: [bytes32Type, bytes32Type, addressType, relayerType, bytes32Type, bytes32Type],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['proof', 'contract', 'args']
|
required: ['proof', 'contract', 'args'],
|
||||||
}
|
}
|
||||||
|
|
||||||
const miningRewardSchema = {
|
const miningRewardSchema = {
|
||||||
@ -79,10 +78,10 @@ const miningRewardSchema = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
relayer: relayerType,
|
relayer: relayerType,
|
||||||
encryptedAccount: encryptedAccountType
|
encryptedAccount: encryptedAccountType,
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['relayer', 'encryptedAccount']
|
required: ['relayer', 'encryptedAccount'],
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@ -91,11 +90,17 @@ const miningRewardSchema = {
|
|||||||
inputNullifierHash: bytes32Type,
|
inputNullifierHash: bytes32Type,
|
||||||
outputRoot: bytes32Type,
|
outputRoot: bytes32Type,
|
||||||
outputPathIndices: bytes32Type,
|
outputPathIndices: bytes32Type,
|
||||||
outputCommitment: bytes32Type
|
outputCommitment: bytes32Type,
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['inputRoot', 'inputNullifierHash', 'outputRoot', 'outputPathIndices', 'outputCommitment']
|
required: [
|
||||||
}
|
'inputRoot',
|
||||||
|
'inputNullifierHash',
|
||||||
|
'outputRoot',
|
||||||
|
'outputPathIndices',
|
||||||
|
'outputCommitment',
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: [
|
required: [
|
||||||
@ -107,12 +112,12 @@ const miningRewardSchema = {
|
|||||||
'depositRoot',
|
'depositRoot',
|
||||||
'withdrawalRoot',
|
'withdrawalRoot',
|
||||||
'extData',
|
'extData',
|
||||||
'account'
|
'account',
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['proof', 'args']
|
required: ['proof', 'args'],
|
||||||
}
|
}
|
||||||
|
|
||||||
const miningWithdrawSchema = {
|
const miningWithdrawSchema = {
|
||||||
@ -130,10 +135,10 @@ const miningWithdrawSchema = {
|
|||||||
properties: {
|
properties: {
|
||||||
recipient: addressType,
|
recipient: addressType,
|
||||||
relayer: relayerType,
|
relayer: relayerType,
|
||||||
encryptedAccount: encryptedAccountType
|
encryptedAccount: encryptedAccountType,
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['relayer', 'encryptedAccount', 'recipient']
|
required: ['relayer', 'encryptedAccount', 'recipient'],
|
||||||
},
|
},
|
||||||
account: {
|
account: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
@ -142,18 +147,24 @@ const miningWithdrawSchema = {
|
|||||||
inputNullifierHash: bytes32Type,
|
inputNullifierHash: bytes32Type,
|
||||||
outputRoot: bytes32Type,
|
outputRoot: bytes32Type,
|
||||||
outputPathIndices: bytes32Type,
|
outputPathIndices: bytes32Type,
|
||||||
outputCommitment: bytes32Type
|
outputCommitment: bytes32Type,
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['inputRoot', 'inputNullifierHash', 'outputRoot', 'outputPathIndices', 'outputCommitment']
|
required: [
|
||||||
}
|
'inputRoot',
|
||||||
|
'inputNullifierHash',
|
||||||
|
'outputRoot',
|
||||||
|
'outputPathIndices',
|
||||||
|
'outputCommitment',
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['amount', 'fee', 'extDataHash', 'extData', 'account']
|
required: ['amount', 'fee', 'extDataHash', 'extData', 'account'],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
additionalProperties: false,
|
additionalProperties: false,
|
||||||
required: ['proof', 'args']
|
required: ['proof', 'args'],
|
||||||
}
|
}
|
||||||
|
|
||||||
const validateTornadoWithdraw = ajv.compile(tornadoWithdrawSchema)
|
const validateTornadoWithdraw = ajv.compile(tornadoWithdrawSchema)
|
||||||
@ -184,5 +195,5 @@ function getMiningWithdrawInputError(data) {
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
getTornadoWithdrawInputError,
|
getTornadoWithdrawInputError,
|
||||||
getMiningRewardInputError,
|
getMiningRewardInputError,
|
||||||
getMiningWithdrawInputError
|
getMiningWithdrawInputError,
|
||||||
}
|
}
|
||||||
|
132
src/worker.js
132
src/worker.js
@ -1,27 +1,27 @@
|
|||||||
const { queue } = require('./queue')
|
|
||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const { rpcUrl, redisUrl, privateKey, netId, gasBumpInterval, gasBumpPercentage, maxGasPrice } = require('../config')
|
const { numberToHex, toBN } = require('web3-utils')
|
||||||
const { numberToHex, toWei, toHex, toBN, fromWei, toChecksumAddress, BN } = require('web3-utils')
|
|
||||||
const tornadoABI = require('../abis/tornadoABI.json')
|
|
||||||
const MerkleTree = require('fixed-merkle-tree')
|
const MerkleTree = require('fixed-merkle-tree')
|
||||||
const { setSafeInterval, poseidonHash2 } = require('./utils')
|
|
||||||
const Redis = require('ioredis')
|
const Redis = require('ioredis')
|
||||||
const redis = new Redis(redisUrl)
|
|
||||||
const redisSubscribe = new Redis(redisUrl)
|
|
||||||
const { GasPriceOracle } = require('gas-price-oracle')
|
const { GasPriceOracle } = require('gas-price-oracle')
|
||||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: rpcUrl })
|
|
||||||
queue.process(process)
|
const tornadoABI = require('../abis/tornadoABI.json')
|
||||||
redisSubscribe.subscribe('treeUpdate', fetchTree)
|
const { queue } = require('./queue')
|
||||||
|
const { poseidonHash2 } = require('./utils')
|
||||||
|
const { rpcUrl, redisUrl, privateKey, updateConfig, rewardAccount } = require('../config')
|
||||||
|
const TxManager = require('./TxManager')
|
||||||
|
|
||||||
let web3
|
let web3
|
||||||
let nonce
|
|
||||||
let currentTx
|
let currentTx
|
||||||
let currentJob
|
let currentJob
|
||||||
let tree
|
let tree
|
||||||
|
let txManager
|
||||||
|
const redis = new Redis(redisUrl)
|
||||||
|
const redisSubscribe = new Redis(redisUrl)
|
||||||
|
const gasPriceOracle = new GasPriceOracle({ defaultRpc: rpcUrl })
|
||||||
|
|
||||||
async function fetchTree() {
|
async function fetchTree() {
|
||||||
const elements = await redis.get('tree:elements')
|
const elements = await redis.get('tree:elements')
|
||||||
const convert = (_, val) => typeof(val) === 'string' ? toBN(val) : val
|
const convert = (_, val) => (typeof val === 'string' ? toBN(val) : val)
|
||||||
tree = MerkleTree.deserialize(JSON.parse(elements, convert), poseidonHash2)
|
tree = MerkleTree.deserialize(JSON.parse(elements, convert), poseidonHash2)
|
||||||
|
|
||||||
if (currentTx) {
|
if (currentTx) {
|
||||||
@ -29,75 +29,55 @@ async function fetchTree() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function watcher() {
|
async function start() {
|
||||||
if (currentTx && Date.now() - currentTx.date > gasBumpInterval) {
|
|
||||||
bumpGasPrice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function bumpGasPrice() {
|
|
||||||
const newGasPrice = toBN(currentTx.gasPrice).mul(toBN(gasBumpPercentage)).div(toBN(100))
|
|
||||||
const maxGasPrice = toBN(toWei(maxGasPrice.toString(), 'Gwei'))
|
|
||||||
currentTx.gasPrice = toHex(BN.min(newGasPrice, maxGasPrice))
|
|
||||||
currentTx.date = Date.now()
|
|
||||||
console.log(`Resubmitting with gas price ${fromWei(currentTx.gasPrice.toString(), 'gwei')} gwei`)
|
|
||||||
await sendTx(currentTx, updateTxHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function init() {
|
|
||||||
web3 = new Web3(rpcUrl, null, { transactionConfirmationBlocks: 1 })
|
web3 = new Web3(rpcUrl, null, { transactionConfirmationBlocks: 1 })
|
||||||
const account = web3.eth.accounts.privateKeyToAccount('0x' + privateKey)
|
const account = web3.eth.accounts.privateKeyToAccount('0x' + privateKey)
|
||||||
web3.eth.accounts.wallet.add('0x' + privateKey)
|
web3.eth.accounts.wallet.add('0x' + privateKey)
|
||||||
web3.eth.defaultAccount = account.address
|
web3.eth.defaultAccount = account.address
|
||||||
nonce = await web3.eth.getTransactionCount(account.address, 'latest')
|
updateConfig({ rewardAccount: account.address })
|
||||||
|
txManager = new TxManager({ privateKey, rpcUrl })
|
||||||
|
queue.process(process)
|
||||||
|
redisSubscribe.subscribe('treeUpdate', fetchTree)
|
||||||
await fetchTree()
|
await fetchTree()
|
||||||
setSafeInterval(watcher, 1000)
|
console.log('Worker started')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkTornadoFee(contract, fee, refund) {
|
async function checkTornadoFee(/* contract, fee, refund*/) {
|
||||||
|
const { fast } = await gasPriceOracle.gasPrices()
|
||||||
|
console.log('fast', fast)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function process(job) {
|
async function process(job) {
|
||||||
if (job.type !== 'tornadoWithdraw') {
|
if (job.data.type !== 'tornadoWithdraw') {
|
||||||
throw new Error('not implemented')
|
throw new Error('not implemented')
|
||||||
}
|
}
|
||||||
currentJob = job
|
currentJob = job
|
||||||
console.log(Date.now(), ' withdraw started', job.id)
|
console.log(Date.now(), ' withdraw started', job.id)
|
||||||
const { proof, args, contract } = job.data
|
const { proof, args, contract } = job.data.data
|
||||||
const fee = toBN(args[4])
|
const fee = toBN(args[4])
|
||||||
const refund = toBN(args[5])
|
const refund = toBN(args[5])
|
||||||
await checkTornadoFee(contract, fee, refund)
|
await checkTornadoFee(contract, fee, refund)
|
||||||
|
|
||||||
|
|
||||||
const instance = new web3.eth.Contract(tornadoABI, contract)
|
const instance = new web3.eth.Contract(tornadoABI, contract)
|
||||||
const data = instance.methods.withdraw(proof, ...args).encodeABI()
|
const data = instance.methods.withdraw(proof, ...args).encodeABI()
|
||||||
const gasPrices = await gasPriceOracle.gasPrices()
|
currentTx = await txManager.createTx({
|
||||||
currentTx = {
|
|
||||||
from: web3.eth.defaultAccount,
|
|
||||||
value: numberToHex(refund),
|
value: numberToHex(refund),
|
||||||
gasPrice: toHex(toWei(gasPrices.fast.toString(), 'gwei')),
|
|
||||||
to: contract,
|
to: contract,
|
||||||
netId,
|
|
||||||
data,
|
data,
|
||||||
nonce,
|
})
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line require-atomic-updates
|
await currentTx
|
||||||
currentTx.gas = await web3.eth.estimateGas(currentTx)
|
.send()
|
||||||
}
|
.on('transactionHash', updateTxHash)
|
||||||
catch (e) {
|
.on('mined', (receipt) => {
|
||||||
|
console.log('Mined in block', receipt.blockNumber)
|
||||||
|
})
|
||||||
|
.on('confirmations', updateConfirmations)
|
||||||
|
} catch (e) {
|
||||||
console.error('Revert', e)
|
console.error('Revert', e)
|
||||||
throw new Error(`Revert by smart contract ${e.message}`)
|
throw new Error(`Revert by smart contract ${e.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce++
|
|
||||||
await sendTx(currentTx, updateTxHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function waitForTx(hash) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateTxHash(txHash) {
|
async function updateTxHash(txHash) {
|
||||||
@ -106,46 +86,10 @@ async function updateTxHash(txHash) {
|
|||||||
await currentJob.update(currentJob.data)
|
await currentJob.update(currentJob.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendTx(tx, onTxHash, retryAttempt) {
|
async function updateConfirmations(confirmations) {
|
||||||
let signedTx = await this.web3.eth.accounts.signTransaction(tx, privateKey)
|
console.log(`Confirmations count ${confirmations}`)
|
||||||
let result = this.web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
currentJob.data.confirmations = confirmations
|
||||||
|
await currentJob.update(currentJob.data)
|
||||||
if (onTxHash) {
|
|
||||||
result.once('transactionHash', onTxHash)
|
|
||||||
}
|
|
||||||
|
|
||||||
try { // await returns once tx is mined
|
|
||||||
await result
|
|
||||||
} catch (e) {
|
|
||||||
console.log(`Error for tx with nonce ${tx.nonce}\n${e.message}`)
|
|
||||||
if (nonceErrors.includes(e.message)) {
|
|
||||||
console.log('nonce too low, retrying')
|
|
||||||
if (retryAttempt <= 10) {
|
|
||||||
tx.nonce++
|
|
||||||
return sendTx(tx, onTxHash, retryAttempt + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gasPriceErrors.includes(e.message)) {
|
|
||||||
return bumpGasPrice()
|
|
||||||
}
|
|
||||||
throw new Error(e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonceErrors = [
|
module.exports = { start, process }
|
||||||
'Returned error: Transaction nonce is too low. Try incrementing the nonce.',
|
|
||||||
'Returned error: nonce too low',
|
|
||||||
]
|
|
||||||
|
|
||||||
const gasPriceErrors = [
|
|
||||||
'Returned error: Transaction gas price supplied is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.',
|
|
||||||
'Returned error: replacement transaction underpriced',
|
|
||||||
]
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
await init()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// main()
|
|
||||||
fetchTree()
|
|
||||||
|
Loading…
Reference in New Issue
Block a user