diff --git a/.env.example b/.env.example index f2df123..5616157 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,6 @@ NET_ID=42 RPC_URL=https://kovan.infura.io/v3/a3f4d001c1fc4a359ea70dd27fd9cb51 PRIVATE_KEY= ETH_MIXER_ADDRESS=0x1Cea940cA15a303A0E01B7F8589F39fF34308DB2 -DAI_MIXER_ADDRESS=0x7ed3fC8042e18db889A0466F49c438bB1410b3c7 \ No newline at end of file +DAI_MIXER_ADDRESS=0x7ed3fC8042e18db889A0466F49c438bB1410b3c7 + +APP_PORT=8000 \ No newline at end of file diff --git a/config.js b/config.js index 4cc7a4f..7aa7bbe 100644 --- a/config.js +++ b/config.js @@ -14,5 +14,6 @@ module.exports = { } ], defaultGasPrice: 2, gasOracleUrls: ['https://www.etherchain.org/api/gasPriceOracle', 'https://gasprice.poa.network/'], - ethdaiAddress: '0x7Ef645705cb7D401C5CD91a395cfcc3Db3C93689' + ethdaiAddress: '0x7Ef645705cb7D401C5CD91a395cfcc3Db3C93689', + port: process.env.APP_PORT } \ No newline at end of file diff --git a/index.js b/index.js index 8851e04..106a21e 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,6 @@ const { numberToHex, toWei, toHex, toBN, toChecksumAddress } = require('web3-utils') -const { netId, rpcUrl, privateKey, mixers, defaultGasPrice } = require('./config') -const { fetchGasPrice, isValidProof, isValidArgs, fetchDAIprice, isKnownContract } = require('./utils') +const { netId, rpcUrl, privateKey, mixers, defaultGasPrice, port } = require('./config') +const { fetchGasPrice, isValidProof, isValidArgs, fetchDAIprice, isKnownContract, isEnoughFee } = require('./utils') const Web3 = require('web3') const express = require('express') @@ -70,32 +70,15 @@ app.post('/relay', async (req, resp) => { toBN(args[5]) ] + 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' }) } - const expense = toBN(toWei(gasPrices.fast.toString(), 'gwei')).mul(toBN('1000000')) - let desiredFee - switch (currency) { - case 'eth': { - if (!refund.isZero()) { - return resp.status(400).json({ error: 'Cannot send refund for eth currency.' }) - } - desiredFee = expense - break - } - case 'dai': { - desiredFee = expense.add(refund).mul(toBN(ethPriceInDai)).div(toBN(10 ** 18)) - break - } - } - - if (fee.lt(desiredFee)) { - console.log('Fee is too low') - return resp.status(400).json({ error: 'Fee is too low. Try to resend.' }) - } - try { const mixer = new web3.eth.Contract(mixerABI, req.body.contract) const isSpent = await mixer.methods.isSpent(nullifierHash).call() @@ -106,6 +89,7 @@ app.post('/relay', async (req, resp) => { if (!isKnownRoot) { return resp.status(400).json({ error: 'The merkle root is too old or invalid.' }) } + const withdrawArgs = [ proof, root, @@ -115,14 +99,23 @@ app.post('/relay', async (req, resp) => { fee.toString(), refund.toString() ] - const gas = await mixer.methods.withdraw(...withdrawArgs).estimateGas({ + let gas = await mixer.methods.withdraw(...withdrawArgs).estimateGas({ from: web3.eth.defaultAccount, value: refund }) + + gas += 50000 + + const { isEnough, reason } = isEnoughFee({ gas, gasPrices, currency, refund, ethPriceInDai, fee }) + if (!isEnough) { + console.log(`Wrong fee: ${reason}`) + return resp.status(400).json({ error: reason }) + } + const result = mixer.methods.withdraw(...withdrawArgs).send({ from: web3.eth.defaultAccount, value: refund, - gas: numberToHex(gas + 50000), + gas: numberToHex(gas), gasPrice: toHex(toWei(gasPrices.fast.toString(), 'gwei')), // TODO: nonce }) @@ -139,7 +132,7 @@ app.post('/relay', async (req, resp) => { } }) -app.listen(8000) +app.listen(port || 8000) if (Number(netId) === 1) { fetchGasPrice({ gasPrices }) @@ -147,7 +140,7 @@ if (Number(netId) === 1) { console.log('Gas price oracle started.') } -console.log('Relayer started') +console.log('Relayer started on port', port || 8000) console.log(`relayerAddress: ${web3.eth.defaultAccount}`) console.log(`mixers: ${JSON.stringify(mixers)}`) console.log(`gasPrices: ${JSON.stringify(gasPrices)}`) diff --git a/utils.js b/utils.js index bf1b9fb..c2510ec 100644 --- a/utils.js +++ b/utils.js @@ -1,5 +1,5 @@ const fetch = require('node-fetch') -const { isHexStrict, hexToNumberString } = require('web3-utils') +const { isHexStrict, hexToNumberString, toBN, toWei } = require('web3-utils') const { gasOracleUrls, ethdaiAddress, mixers } = require('./config') const oracleABI = require('./abis/ETHDAIOracle.json') @@ -97,4 +97,23 @@ function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } -module.exports = { fetchGasPrice, isValidProof, isValidArgs, sleep, fetchDAIprice, isKnownContract } +function isEnoughFee({ gas, gasPrices, currency, refund, ethPriceInDai, fee }) { + const expense = toBN(toWei(gasPrices.fast.toString(), 'gwei')).mul(toBN(gas)) + let desiredFee + switch (currency) { + case 'eth': { + desiredFee = expense + break + } + case 'dai': { + desiredFee = expense.add(refund).mul(toBN(ethPriceInDai)).div(toBN(10 ** 18)) + break + } + } + if (fee.lt(desiredFee)) { + return { isEnough: false, reason: 'Not enough fee' } + } + return { isEnough: true } +} + +module.exports = { fetchGasPrice, isValidProof, isValidArgs, sleep, fetchDAIprice, isKnownContract, isEnoughFee }