mirror of
https://github.com/tornadocash/tornado-relayer.git
synced 2024-10-01 08:25:37 -04:00
prettier + tidy
This commit is contained in:
parent
17d48f9508
commit
9cd97feef0
@ -22,7 +22,13 @@
|
||||
}
|
||||
],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "single", { "avoidEscape": true }],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true
|
||||
}
|
||||
],
|
||||
"semi": ["error", "never"],
|
||||
"object-curly-spacing": ["error", "always"],
|
||||
"require-await": "error",
|
||||
|
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -2,8 +2,8 @@ name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '*' ]
|
||||
tags: [ 'v[0-9]+.[0-9]+.[0-9]+' ]
|
||||
branches: ['*']
|
||||
tags: ['v[0-9]+.[0-9]+.[0-9]+']
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
1
.prettierignore
Normal file
1
.prettierignore
Normal file
@ -0,0 +1 @@
|
||||
keys/TreeUpdate.json
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"arrowParens": "avoid",
|
||||
"singleQuote": true,
|
||||
"printWidth": 110,
|
||||
"trailingComma": "all"
|
||||
|
@ -1,28 +1,28 @@
|
||||
[
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "contract IERC20[]",
|
||||
"name": "fromTokens",
|
||||
"type": "address[]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "oneUnitAmounts",
|
||||
"type": "uint256[]"
|
||||
}
|
||||
],
|
||||
"name": "getPricesInETH",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "prices",
|
||||
"type": "uint256[]"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
{
|
||||
"constant": true,
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "contract IERC20[]",
|
||||
"name": "fromTokens",
|
||||
"type": "address[]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "oneUnitAmounts",
|
||||
"type": "uint256[]"
|
||||
}
|
||||
],
|
||||
"name": "getPricesInETH",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "prices",
|
||||
"type": "uint256[]"
|
||||
}
|
||||
],
|
||||
"payable": false,
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
}
|
||||
]
|
||||
|
10
package.json
10
package.json
@ -5,7 +5,10 @@
|
||||
"scripts": {
|
||||
"server": "node src/server.js",
|
||||
"treeUpdater": "node src/treeWatcher",
|
||||
"eslint": "npx eslint --ignore-path .gitignore .",
|
||||
"eslint": "eslint --ext .js --ignore-path .gitignore .",
|
||||
"prettier:check": "npx prettier --check . --config .prettierrc",
|
||||
"prettier:fix": "npx prettier --write . --config .prettierrc",
|
||||
"lint": "yarn eslint && yarn prettier:check",
|
||||
"test": "mocha"
|
||||
},
|
||||
"author": "tornado.cash",
|
||||
@ -34,6 +37,9 @@
|
||||
"devDependencies": {
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^6.6.0",
|
||||
"mocha": "^8.1.3"
|
||||
"eslint-config-prettier": "^6.12.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"mocha": "^8.1.3",
|
||||
"prettier": "^2.1.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,56 +0,0 @@
|
||||
const Web3 = require('web3')
|
||||
const { defaultGasPrice, oracleRpcUrl, oracleAddress } = require('../config')
|
||||
const { getArgsForOracle } = require('./utils')
|
||||
const { redisClient } = require('./redis')
|
||||
const priceOracleABI = require('../abis/PriceOracle.abi.json')
|
||||
|
||||
class Fetcher {
|
||||
constructor(web3) {
|
||||
this.web3 = web3
|
||||
this.oracleWeb3 = new Web3(oracleRpcUrl)
|
||||
this.oracle = new this.oracleWeb3.eth.Contract(priceOracleABI, oracleAddress)
|
||||
this.ethPrices = {
|
||||
dai: '6700000000000000', // 0.0067
|
||||
cdai: '157380000000000',
|
||||
cusdc: '164630000000000',
|
||||
usdc: '7878580000000000',
|
||||
usdt: '7864940000000000'
|
||||
}
|
||||
this.tokenAddresses
|
||||
this.oneUintAmount
|
||||
this.currencyLookup
|
||||
this.gasPrices = {
|
||||
fast: defaultGasPrice
|
||||
}
|
||||
|
||||
const { tokenAddresses, oneUintAmount, currencyLookup } = getArgsForOracle()
|
||||
this.tokenAddresses = tokenAddresses
|
||||
this.oneUintAmount = oneUintAmount
|
||||
this.currencyLookup = currencyLookup
|
||||
}
|
||||
async fetchPrices() {
|
||||
try {
|
||||
let prices = await this.oracle.methods.getPricesInETH(this.tokenAddresses, this.oneUintAmount).call()
|
||||
this.ethPrices = prices.reduce((acc, price, i) => {
|
||||
acc[this.currencyLookup[this.tokenAddresses[i]]] = price
|
||||
return acc
|
||||
}, {})
|
||||
setTimeout(() => this.fetchPrices(), 1000 * 30)
|
||||
} catch (e) {
|
||||
console.error('fetchPrices', e.message)
|
||||
setTimeout(() => this.fetchPrices(), 1000 * 30)
|
||||
}
|
||||
}
|
||||
async fetchNonce() {
|
||||
try {
|
||||
const nonce = await this.web3.eth.getTransactionCount(this.web3.eth.defaultAccount)
|
||||
await redisClient.set('nonce', nonce)
|
||||
console.log(`Current nonce: ${nonce}`)
|
||||
} catch (e) {
|
||||
console.error('fetchNonce failed', e.message)
|
||||
setTimeout(this.fetchNonce, 3000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Fetcher
|
@ -1,98 +0,0 @@
|
||||
const express = require('express')
|
||||
const {
|
||||
netId,
|
||||
port,
|
||||
relayerServiceFee,
|
||||
gasBumpPercentage,
|
||||
pendingTxTimeout,
|
||||
watherInterval,
|
||||
maxGasPrice
|
||||
} = require('../config')
|
||||
const relayController = require('./relayController')
|
||||
const { fetcher, web3, gasPriceOracle } = require('./instances')
|
||||
const { getMixers } = require('./utils')
|
||||
const mixers = getMixers()
|
||||
const { redisClient } = require('./redis')
|
||||
const { version } = require('../package.json')
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
if (err) {
|
||||
console.log('Invalid Request data')
|
||||
res.send('Invalid Request data')
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.header('Access-Control-Allow-Origin', '*')
|
||||
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
|
||||
next()
|
||||
})
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
// just for testing purposes
|
||||
res.send(
|
||||
'This is <a href=https://tornado.cash>tornado.cash</a> Relayer service. Check the <a href=/status>/status</a> for settings'
|
||||
)
|
||||
})
|
||||
|
||||
app.get('/status', async function (req, res) {
|
||||
let nonce = await redisClient.get('nonce')
|
||||
let latestBlock = null
|
||||
try {
|
||||
latestBlock = await web3.eth.getBlockNumber()
|
||||
} catch (e) {
|
||||
console.error('Problem with RPC', e)
|
||||
}
|
||||
const { ethPrices } = fetcher
|
||||
res.json({
|
||||
relayerAddress: web3.eth.defaultAccount,
|
||||
mixers,
|
||||
gasPrices: await gasPriceOracle.gasPrices(),
|
||||
netId,
|
||||
ethPrices,
|
||||
relayerServiceFee,
|
||||
nonce,
|
||||
version,
|
||||
latestBlock
|
||||
})
|
||||
})
|
||||
|
||||
app.post('/relay', relayController)
|
||||
console.log('Version:', version)
|
||||
let server = app.listen(port || 8000)
|
||||
server.setTimeout(600000)
|
||||
console.log('Gas price oracle started.')
|
||||
fetcher.fetchPrices()
|
||||
fetcher.fetchNonce()
|
||||
|
||||
console.log('Relayer started on port', port || 8000)
|
||||
console.log(`relayerAddress: ${web3.eth.defaultAccount}`)
|
||||
console.log(`mixers: ${JSON.stringify(mixers)}`)
|
||||
console.log(`netId: ${netId}`)
|
||||
console.log(`ethPrices: ${JSON.stringify(fetcher.ethPrices)}`)
|
||||
|
||||
const {
|
||||
GAS_PRICE_BUMP_PERCENTAGE,
|
||||
ALLOWABLE_PENDING_TX_TIMEOUT,
|
||||
NONCE_WATCHER_INTERVAL,
|
||||
MAX_GAS_PRICE
|
||||
} = process.env
|
||||
if (!NONCE_WATCHER_INTERVAL) {
|
||||
console.log(`NONCE_WATCHER_INTERVAL is not set. Using default value ${watherInterval / 1000} sec`)
|
||||
}
|
||||
|
||||
if (!GAS_PRICE_BUMP_PERCENTAGE) {
|
||||
console.log(`GAS_PRICE_BUMP_PERCENTAGE is not set. Using default value ${gasBumpPercentage}%`)
|
||||
}
|
||||
|
||||
if (!ALLOWABLE_PENDING_TX_TIMEOUT) {
|
||||
console.log(`ALLOWABLE_PENDING_TX_TIMEOUT is not set. Using default value ${pendingTxTimeout / 1000} sec`)
|
||||
}
|
||||
|
||||
if (!MAX_GAS_PRICE) {
|
||||
console.log(`ALLOWABLE_PENDING_TX_TIMEOUT is not set. Using default value ${maxGasPrice} Gwei`)
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
const { rpcUrl } = require('../config')
|
||||
const Fetcher = require('./Fetcher')
|
||||
const Sender = require('./sender')
|
||||
const { GasPriceOracle } = require('gas-price-oracle')
|
||||
const web3 = require('./setupWeb3')
|
||||
const fetcher = new Fetcher(web3)
|
||||
const sender = new Sender(web3)
|
||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: rpcUrl })
|
||||
|
||||
module.exports = {
|
||||
fetcher,
|
||||
web3,
|
||||
sender,
|
||||
gasPriceOracle
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
const { redisUrl } = require('../config')
|
||||
const Redis = require('ioredis')
|
||||
const redisClient = new Redis(redisUrl)
|
||||
const subscriber = new Redis(redisUrl)
|
||||
|
||||
const redisOpts = {
|
||||
createClient: function (type) {
|
||||
switch (type) {
|
||||
case 'client':
|
||||
return redisClient
|
||||
case 'subscriber':
|
||||
return subscriber
|
||||
default:
|
||||
return new Redis(redisUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { redisOpts, redisClient }
|
@ -1,164 +0,0 @@
|
||||
const Queue = require('bull')
|
||||
const { numberToHex, toWei, toHex, toBN, toChecksumAddress } = require('web3-utils')
|
||||
const mixerABI = require('../abis/mixerABI.json')
|
||||
const { isValidProof, isValidArgs, isKnownContract, isEnoughFee } = require('./utils')
|
||||
const config = require('../config')
|
||||
const { redisClient, redisOpts } = require('./redis')
|
||||
const { GasPriceOracle } = require('gas-price-oracle')
|
||||
const gasPriceOracle = new GasPriceOracle({ defaultRpc: rpcUrl })
|
||||
|
||||
const { web3, fetcher, sender, gasPriceOracle } = require('./instances')
|
||||
const withdrawQueue = new Queue('withdraw', redisOpts)
|
||||
|
||||
const reponseCbs = {}
|
||||
let respLambda = (job, { msg, status }) => {
|
||||
const resp = reponseCbs[job.id]
|
||||
resp.status(status).json(msg)
|
||||
delete reponseCbs[job.id]
|
||||
}
|
||||
withdrawQueue.on('completed', respLambda)
|
||||
|
||||
async function relayController(req, resp) {
|
||||
let requestJob
|
||||
|
||||
const { proof, args, contract } = req.body
|
||||
let { valid, reason } = isValidProof(proof)
|
||||
if (!valid) {
|
||||
console.log('Proof is invalid:', reason)
|
||||
return resp.status(400).json({ error: 'Proof format is invalid' })
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-extra-semi
|
||||
;({ valid, reason } = isValidArgs(args))
|
||||
if (!valid) {
|
||||
console.log('Args are invalid:', reason)
|
||||
return resp.status(400).json({ error: 'Withdraw arguments are invalid' })
|
||||
}
|
||||
|
||||
let currency, amount
|
||||
;({ valid, currency, amount } = isKnownContract(contract))
|
||||
if (!valid) {
|
||||
console.log('Contract does not exist:', contract)
|
||||
return resp.status(400).json({ error: 'This relayer does not support the token' })
|
||||
}
|
||||
|
||||
const [root, nullifierHash, recipient, relayer, fee, refund] = [
|
||||
args[0],
|
||||
args[1],
|
||||
toChecksumAddress(args[2]),
|
||||
toChecksumAddress(args[3]),
|
||||
toBN(args[4]),
|
||||
toBN(args[5])
|
||||
]
|
||||
console.log('fee, refund', fee.toString(), refund.toString(), recipient)
|
||||
if (currency === 'eth' && !refund.isZero()) {
|
||||
return resp.status(400).json({ error: 'Cannot send refund for eth currency.' })
|
||||
}
|
||||
|
||||
if (relayer !== web3.eth.defaultAccount) {
|
||||
console.log('This proof is for different relayer:', relayer)
|
||||
return resp.status(400).json({ error: 'Relayer address is invalid' })
|
||||
}
|
||||
|
||||
requestJob = await withdrawQueue.add(
|
||||
{
|
||||
contract,
|
||||
nullifierHash,
|
||||
root,
|
||||
proof,
|
||||
args,
|
||||
currency,
|
||||
amount,
|
||||
fee: fee.toString(),
|
||||
refund: refund.toString()
|
||||
},
|
||||
{ removeOnComplete: true }
|
||||
)
|
||||
reponseCbs[requestJob.id] = resp
|
||||
}
|
||||
|
||||
withdrawQueue.process(async function (job, done) {
|
||||
console.log(Date.now(), ' withdraw started', job.id)
|
||||
const gasPrices = await gasPriceOracle.gasPrices()
|
||||
const { contract, nullifierHash, root, proof, args, refund, currency, amount, fee } = job.data
|
||||
console.log(JSON.stringify(job.data))
|
||||
// job.data contains the custom data passed when the job was created
|
||||
// job.id contains id of this job.
|
||||
try {
|
||||
const mixer = new web3.eth.Contract(mixerABI, contract)
|
||||
const isSpent = await mixer.methods.isSpent(nullifierHash).call()
|
||||
if (isSpent) {
|
||||
done(null, {
|
||||
status: 400,
|
||||
msg: {
|
||||
error: 'The note has been spent.'
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
const isKnownRoot = await mixer.methods.isKnownRoot(root).call()
|
||||
if (!isKnownRoot) {
|
||||
done(null, {
|
||||
status: 400,
|
||||
msg: {
|
||||
error: 'The merkle root is too old or invalid.'
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let gas = await mixer.methods.withdraw(proof, ...args).estimateGas({
|
||||
from: web3.eth.defaultAccount,
|
||||
value: refund
|
||||
})
|
||||
|
||||
gas += 50000
|
||||
const ethPrices = fetcher.ethPrices
|
||||
const { isEnough, reason } = isEnoughFee({
|
||||
gas,
|
||||
gasPrices,
|
||||
currency,
|
||||
amount,
|
||||
refund: toBN(refund),
|
||||
ethPrices,
|
||||
fee: toBN(fee)
|
||||
})
|
||||
if (!isEnough) {
|
||||
console.log(`Wrong fee: ${reason}`)
|
||||
done(null, {
|
||||
status: 400,
|
||||
msg: { error: reason }
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const data = mixer.methods.withdraw(proof, ...args).encodeABI()
|
||||
let nonce = Number(await redisClient.get('nonce'))
|
||||
console.log('nonce', nonce)
|
||||
const tx = {
|
||||
from: web3.eth.defaultAccount,
|
||||
value: numberToHex(refund),
|
||||
gas: numberToHex(gas),
|
||||
gasPrice: toHex(toWei(gasPrices.fast.toString(), 'gwei')),
|
||||
// you can use this gasPrice to test watcher
|
||||
// gasPrice: numberToHex(100000000),
|
||||
to: mixer._address,
|
||||
netId: config.netId,
|
||||
data,
|
||||
nonce
|
||||
}
|
||||
tx.date = Date.now()
|
||||
await redisClient.set('tx:' + nonce, JSON.stringify(tx))
|
||||
nonce += 1
|
||||
await redisClient.set('nonce', nonce)
|
||||
sender.sendTx(tx, done)
|
||||
} catch (e) {
|
||||
console.error(e, 'estimate gas failed')
|
||||
done(null, {
|
||||
status: 400,
|
||||
msg: { error: 'Internal Relayer Error. Please use a different relayer service' }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = relayController
|
@ -1,81 +0,0 @@
|
||||
const { redisClient } = require('./redis')
|
||||
const config = require('../config')
|
||||
const { toBN, toHex, toWei, BN, fromWei } = require('web3-utils')
|
||||
|
||||
class Sender {
|
||||
constructor(web3) {
|
||||
this.web3 = web3
|
||||
this.watherInterval = config.watherInterval
|
||||
this.pendingTxTimeout = config.pendingTxTimeout
|
||||
this.gasBumpPercentage = 100 + Number(config.gasBumpPercentage)
|
||||
this.watcher()
|
||||
}
|
||||
|
||||
async watcher() {
|
||||
try {
|
||||
const networkNonce = await this.web3.eth.getTransactionCount(this.web3.eth.defaultAccount)
|
||||
let tx = await redisClient.get('tx:' + networkNonce)
|
||||
if (tx) {
|
||||
tx = JSON.parse(tx)
|
||||
if (Date.now() - tx.date > this.pendingTxTimeout) {
|
||||
const newGasPrice = toBN(tx.gasPrice).mul(toBN(this.gasBumpPercentage)).div(toBN(100))
|
||||
const maxGasPrice = toBN(toWei(config.maxGasPrice.toString(), 'Gwei'))
|
||||
tx.gasPrice = toHex(BN.min(newGasPrice, maxGasPrice))
|
||||
tx.date = Date.now()
|
||||
await redisClient.set('tx:' + tx.nonce, JSON.stringify(tx))
|
||||
console.log('resubmitting with gas price', fromWei(tx.gasPrice.toString(), 'gwei'), ' gwei')
|
||||
this.sendTx(tx, null, 9999)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('watcher error:', e)
|
||||
} finally {
|
||||
setTimeout(() => this.watcher(), this.watherInterval)
|
||||
}
|
||||
}
|
||||
|
||||
async sendTx(tx, done, retryAttempt = 1) {
|
||||
let signedTx = await this.web3.eth.accounts.signTransaction(tx, config.privateKey)
|
||||
let result = this.web3.eth.sendSignedTransaction(signedTx.rawTransaction)
|
||||
|
||||
result
|
||||
.once('transactionHash', (txHash) => {
|
||||
console.log(`A new successfully sent tx ${txHash}`)
|
||||
if (done) {
|
||||
done(null, {
|
||||
status: 200,
|
||||
msg: { txHash }
|
||||
})
|
||||
}
|
||||
})
|
||||
.on('error', async (e) => {
|
||||
console.log(`Error for tx with nonce ${tx.nonce}\n${e.message}`)
|
||||
if (
|
||||
e.message ===
|
||||
'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.' ||
|
||||
e.message === 'Returned error: Transaction nonce is too low. Try incrementing the nonce.' ||
|
||||
e.message === 'Returned error: nonce too low' ||
|
||||
e.message === 'Returned error: replacement transaction underpriced'
|
||||
) {
|
||||
console.log('nonce too low, retrying')
|
||||
if (retryAttempt <= 10) {
|
||||
retryAttempt++
|
||||
const newNonce = tx.nonce + 1
|
||||
tx.nonce = newNonce
|
||||
await redisClient.set('nonce', newNonce)
|
||||
await redisClient.set('tx:' + newNonce, JSON.stringify(tx))
|
||||
this.sendTx(tx, done, retryAttempt)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (done) {
|
||||
done(null, {
|
||||
status: 400,
|
||||
msg: { error: 'Internal Relayer Error. Please use a different relayer service' }
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Sender
|
@ -1,16 +0,0 @@
|
||||
const Web3 = require('web3')
|
||||
const { rpcUrl, privateKey } = require('../config')
|
||||
|
||||
function setup() {
|
||||
try {
|
||||
const web3 = new Web3(rpcUrl, null, { transactionConfirmationBlocks: 1 })
|
||||
const account = web3.eth.accounts.privateKeyToAccount('0x' + privateKey)
|
||||
web3.eth.accounts.wallet.add('0x' + privateKey)
|
||||
web3.eth.defaultAccount = account.address
|
||||
return web3
|
||||
} catch (e) {
|
||||
console.error('web3 failed')
|
||||
}
|
||||
}
|
||||
const web3 = setup()
|
||||
module.exports = web3
|
@ -1,19 +0,0 @@
|
||||
const fs = require('fs')
|
||||
const { Controller } = require('tornado-cash-anonymity-mining')
|
||||
const { web3 } = require('./instances')
|
||||
const { farmingAddress, farmingMerkleTreeHeight } = require('../config')
|
||||
|
||||
const contract = web3.eth.contract(require('../abis/mining.abi.json'), farmingAddress)
|
||||
const provingKeys = {
|
||||
treeUpdateCircuit: require('.../keys/TreeUpdate.json'),
|
||||
treeUpdateProvingKey: fs.readFileSync('../keys/TreeUpdate_proving_key.bin').buffer,
|
||||
}
|
||||
const controller = new Controller({
|
||||
contract,
|
||||
provingKeys,
|
||||
merkleTreeHeight: farmingMerkleTreeHeight,
|
||||
})
|
||||
|
||||
|
||||
// await controller.init()
|
||||
// await controller.treeUpdate(commitment)
|
178
src.bak/utils.js
178
src.bak/utils.js
@ -1,178 +0,0 @@
|
||||
const { isHexStrict, toBN, toWei, BN } = require('web3-utils')
|
||||
const { netId, mixers, relayerServiceFee } = require('../config')
|
||||
|
||||
function isValidProof(proof) {
|
||||
// validator expects `websnarkUtils.toSolidityInput(proof)` output
|
||||
|
||||
if (!proof) {
|
||||
return { valid: false, reason: 'The proof is empty.' }
|
||||
}
|
||||
|
||||
if (!isHexStrict(proof) || proof.length !== 2 + 2 * 8 * 32) {
|
||||
return { valid: false, reason: 'Corrupted proof' }
|
||||
}
|
||||
|
||||
return { valid: true }
|
||||
}
|
||||
|
||||
function isValidArgs(args) {
|
||||
if (!args) {
|
||||
return { valid: false, reason: 'Args are empty' }
|
||||
}
|
||||
|
||||
if (args.length !== 6) {
|
||||
return { valid: false, reason: 'Length of args is lower than 6' }
|
||||
}
|
||||
|
||||
for (let signal of args) {
|
||||
if (!isHexStrict(signal)) {
|
||||
return { valid: false, reason: `Corrupted signal ${signal}` }
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
args[0].length !== 66 ||
|
||||
args[1].length !== 66 ||
|
||||
args[2].length !== 42 ||
|
||||
args[3].length !== 42 ||
|
||||
args[4].length !== 66 ||
|
||||
args[5].length !== 66
|
||||
) {
|
||||
return { valid: false, reason: 'The length one of the signals is incorrect' }
|
||||
}
|
||||
|
||||
return { valid: true }
|
||||
}
|
||||
|
||||
function isKnownContract(contract) {
|
||||
const mixers = getMixers()
|
||||
for (let currency of Object.keys(mixers)) {
|
||||
for (let amount of Object.keys(mixers[currency].mixerAddress)) {
|
||||
if (mixers[currency].mixerAddress[amount] === contract) {
|
||||
return { valid: true, currency, amount }
|
||||
}
|
||||
}
|
||||
}
|
||||
return { valid: false }
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
function fromDecimals(value, decimals) {
|
||||
value = value.toString()
|
||||
let ether = value.toString()
|
||||
const base = new BN('10').pow(new BN(decimals))
|
||||
const baseLength = base.toString(10).length - 1 || 1
|
||||
|
||||
const negative = ether.substring(0, 1) === '-'
|
||||
if (negative) {
|
||||
ether = ether.substring(1)
|
||||
}
|
||||
|
||||
if (ether === '.') {
|
||||
throw new Error('[ethjs-unit] while converting number ' + value + ' to wei, invalid value')
|
||||
}
|
||||
|
||||
// Split it into a whole and fractional part
|
||||
const comps = ether.split('.')
|
||||
if (comps.length > 2) {
|
||||
throw new Error('[ethjs-unit] while converting number ' + value + ' to wei, too many decimal points')
|
||||
}
|
||||
|
||||
let whole = comps[0]
|
||||
let fraction = comps[1]
|
||||
|
||||
if (!whole) {
|
||||
whole = '0'
|
||||
}
|
||||
if (!fraction) {
|
||||
fraction = '0'
|
||||
}
|
||||
if (fraction.length > baseLength) {
|
||||
throw new Error('[ethjs-unit] while converting number ' + value + ' to wei, too many decimal places')
|
||||
}
|
||||
|
||||
while (fraction.length < baseLength) {
|
||||
fraction += '0'
|
||||
}
|
||||
|
||||
whole = new BN(whole)
|
||||
fraction = new BN(fraction)
|
||||
let wei = whole.mul(base).add(fraction)
|
||||
|
||||
if (negative) {
|
||||
wei = wei.mul(negative)
|
||||
}
|
||||
|
||||
return new BN(wei.toString(10), 10)
|
||||
}
|
||||
|
||||
function isEnoughFee({ gas, gasPrices, currency, amount, refund, ethPrices, fee }) {
|
||||
const { decimals } = mixers[`netId${netId}`][currency]
|
||||
const decimalsPoint =
|
||||
Math.floor(relayerServiceFee) === relayerServiceFee
|
||||
? 0
|
||||
: relayerServiceFee.toString().split('.')[1].length
|
||||
|
||||
const roundDecimal = 10 ** decimalsPoint
|
||||
const feePercent = toBN(fromDecimals(amount, decimals))
|
||||
.mul(toBN(relayerServiceFee * roundDecimal))
|
||||
.div(toBN(roundDecimal * 100))
|
||||
const expense = toBN(toWei(gasPrices.fast.toString(), 'gwei')).mul(toBN(gas))
|
||||
let desiredFee
|
||||
switch (currency) {
|
||||
case 'eth': {
|
||||
desiredFee = expense.add(feePercent)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
desiredFee = expense
|
||||
.add(refund)
|
||||
.mul(toBN(10 ** decimals))
|
||||
.div(toBN(ethPrices[currency]))
|
||||
desiredFee = desiredFee.add(feePercent)
|
||||
break
|
||||
}
|
||||
}
|
||||
console.log(
|
||||
'sent fee, desired fee, feePercent',
|
||||
fee.toString(),
|
||||
desiredFee.toString(),
|
||||
feePercent.toString()
|
||||
)
|
||||
if (fee.lt(desiredFee)) {
|
||||
return { isEnough: false, reason: 'Not enough fee' }
|
||||
}
|
||||
return { isEnough: true }
|
||||
}
|
||||
|
||||
function getArgsForOracle() {
|
||||
const tokens = mixers['netId1']
|
||||
const tokenAddresses = []
|
||||
const oneUintAmount = []
|
||||
const currencyLookup = {}
|
||||
Object.entries(tokens).map(([currency, data]) => {
|
||||
if (currency !== 'eth') {
|
||||
tokenAddresses.push(data.tokenAddress)
|
||||
oneUintAmount.push(toBN('10').pow(toBN(data.decimals.toString())).toString())
|
||||
currencyLookup[data.tokenAddress] = currency
|
||||
}
|
||||
})
|
||||
return { tokenAddresses, oneUintAmount, currencyLookup }
|
||||
}
|
||||
|
||||
function getMixers() {
|
||||
return mixers[`netId${netId}`]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isValidProof,
|
||||
isValidArgs,
|
||||
sleep,
|
||||
isKnownContract,
|
||||
isEnoughFee,
|
||||
getMixers,
|
||||
getArgsForOracle
|
||||
}
|
@ -1,4 +1,8 @@
|
||||
const { getTornadoWithdrawInputError, getMiningRewardInputError, getMiningWithdrawInputError } = require('./validator')
|
||||
const {
|
||||
getTornadoWithdrawInputError,
|
||||
getMiningRewardInputError,
|
||||
getMiningWithdrawInputError,
|
||||
} = require('./validator')
|
||||
const { postJob } = require('./queue')
|
||||
|
||||
async function tornadoWithdraw(req, res) {
|
||||
|
@ -18,7 +18,7 @@ async function fetchEvents(from = 0, to = 'latest') {
|
||||
})
|
||||
return events
|
||||
.sort((a, b) => a.returnValues.index - b.returnValues.index)
|
||||
.map((e) => toBN(e.returnValues.commitment))
|
||||
.map(e => toBN(e.returnValues.commitment))
|
||||
}
|
||||
|
||||
async function processNewEvent(err, event) {
|
||||
@ -91,7 +91,7 @@ async function init() {
|
||||
|
||||
init()
|
||||
|
||||
process.on('unhandledRejection', (error) => {
|
||||
process.on('unhandledRejection', error => {
|
||||
console.error('Unhandled promise rejection', error)
|
||||
process.exit(1)
|
||||
})
|
||||
|
@ -2,7 +2,7 @@ const { instances, netId } = require('../config')
|
||||
const { poseidon } = require('circomlib')
|
||||
const { toBN, toChecksumAddress } = require('web3-utils')
|
||||
|
||||
const sleep = (ms) => new Promise((res) => setTimeout(res, ms))
|
||||
const sleep = ms => new Promise(res => setTimeout(res, ms))
|
||||
|
||||
function getInstance(address) {
|
||||
address = toChecksumAddress(address)
|
||||
@ -27,7 +27,7 @@ function getInstance(address) {
|
||||
// }
|
||||
// }
|
||||
|
||||
const poseidonHash = (items) => toBN(poseidon(items).toString())
|
||||
const poseidonHash = items => toBN(poseidon(items).toString())
|
||||
const poseidonHash2 = (a, b) => poseidonHash([a, b])
|
||||
|
||||
function setSafeInterval(func, interval) {
|
||||
@ -44,10 +44,10 @@ function setSafeInterval(func, interval) {
|
||||
function when(source, event) {
|
||||
return new Promise((resolve, reject) => {
|
||||
source
|
||||
.once(event, (payload) => {
|
||||
.once(event, payload => {
|
||||
resolve(payload)
|
||||
})
|
||||
.on('error', (error) => {
|
||||
.on('error', error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
|
@ -115,7 +115,7 @@ async function processTornadoWithdraw(job) {
|
||||
await currentTx
|
||||
.send()
|
||||
.on('transactionHash', updateTxHash)
|
||||
.on('mined', (receipt) => {
|
||||
.on('mined', receipt => {
|
||||
console.log('Mined in block', receipt.blockNumber)
|
||||
})
|
||||
.on('confirmations', updateConfirmations)
|
||||
@ -141,7 +141,7 @@ async function processMiningReward(job) {
|
||||
await currentTx
|
||||
.send()
|
||||
.on('transactionHash', updateTxHash)
|
||||
.on('mined', (receipt) => {
|
||||
.on('mined', receipt => {
|
||||
console.log('Mined in block', receipt.blockNumber)
|
||||
})
|
||||
.on('confirmations', updateConfirmations)
|
||||
@ -167,7 +167,7 @@ async function processMiningWithdraw(job) {
|
||||
await currentTx
|
||||
.send()
|
||||
.on('transactionHash', updateTxHash)
|
||||
.on('mined', (receipt) => {
|
||||
.on('mined', receipt => {
|
||||
console.log('Mined in block', receipt.blockNumber)
|
||||
})
|
||||
.on('confirmations', updateConfirmations)
|
||||
|
@ -1,48 +0,0 @@
|
||||
const { toHex, toWei } = require('web3-utils')
|
||||
const TxManager = require('./src/TxManager')
|
||||
const { rpcUrl, privateKey } = require('./config')
|
||||
const logHandles = require('why-is-node-running')
|
||||
|
||||
const manager = new TxManager({
|
||||
privateKey,
|
||||
rpcUrl,
|
||||
config: {
|
||||
CONFIRMATIONS: 1,
|
||||
GAS_BUMP_INTERVAL: 1000 * 15,
|
||||
},
|
||||
})
|
||||
|
||||
const tx1 = {
|
||||
value: 1,
|
||||
gasPrice: toHex(toWei('1', 'gwei')),
|
||||
to: '0xA43Ce8Cc89Eff3AA5593c742fC56A30Ef2427CB0',
|
||||
}
|
||||
|
||||
const tx2 = {
|
||||
value: 2,
|
||||
// gasPrice: toHex(toWei('1', 'gwei')),
|
||||
to: '0x0039F22efB07A647557C7C5d17854CFD6D489eF3',
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const tx = manager.createTx(tx1)
|
||||
|
||||
setTimeout(() => tx.cancel(), 1000)
|
||||
// setTimeout(() => tx.replace(tx2), 1000)
|
||||
|
||||
const receipt = await tx.send()
|
||||
.on('transactionHash', (hash) => {
|
||||
console.log('hash', hash)
|
||||
})
|
||||
.on('mined', (receipt) => {
|
||||
console.log('Mined in block', receipt.blockNumber)
|
||||
})
|
||||
.on('confirmations', (confirmations) => {
|
||||
console.log('confirmations', confirmations)
|
||||
})
|
||||
|
||||
console.log('receipt', receipt)
|
||||
// setTimeout(logHandles, 100)
|
||||
}
|
||||
|
||||
main()
|
36
yarn.lock
36
yarn.lock
@ -1262,6 +1262,20 @@ escape-string-regexp@^1.0.5:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
|
||||
eslint-config-prettier@^6.12.0:
|
||||
version "6.12.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.12.0.tgz#9eb2bccff727db1c52104f0b49e87ea46605a0d2"
|
||||
integrity sha512-9jWPlFlgNwRUYVoujvWTQ1aMO8o6648r+K7qU7K5Jmkbyqav1fuEZC0COYpGBxyiAJb65Ra9hrmFx19xRGwXWw==
|
||||
dependencies:
|
||||
get-stdin "^6.0.0"
|
||||
|
||||
eslint-plugin-prettier@^3.1.4:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2"
|
||||
integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg==
|
||||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
eslint-scope@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848"
|
||||
@ -1673,6 +1687,11 @@ fast-deep-equal@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-diff@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
|
||||
integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
@ -1876,6 +1895,11 @@ get-port@^5.1.1:
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
|
||||
integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
|
||||
|
||||
get-stdin@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
|
||||
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
|
||||
|
||||
get-stream@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
||||
@ -3176,6 +3200,18 @@ prepend-http@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||
|
||||
prettier-linter-helpers@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
|
||||
integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
|
||||
dependencies:
|
||||
fast-diff "^1.1.2"
|
||||
|
||||
prettier@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5"
|
||||
integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg==
|
||||
|
||||
process@~0.5.1:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
|
||||
|
Loading…
Reference in New Issue
Block a user