diff --git a/.env.example b/.env.example index d0e8929..f2df123 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,5 @@ NET_ID=42 RPC_URL=https://kovan.infura.io/v3/a3f4d001c1fc4a359ea70dd27fd9cb51 PRIVATE_KEY= -MIXER_ADDRESS=0xb2aD997a43768aB9279Cd9E72D5B75D789a09011 \ No newline at end of file +ETH_MIXER_ADDRESS=0x1Cea940cA15a303A0E01B7F8589F39fF34308DB2 +DAI_MIXER_ADDRESS=0x7ed3fC8042e18db889A0466F49c438bB1410b3c7 \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index 4e28574..e1b8769 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,7 +16,8 @@ "rules": { "indent": [ "error", - 2 + 2, + {"SwitchCase": 1} ], "linebreak-style": [ "error", diff --git a/config.js b/config.js index 3ffdb09..4cc7a4f 100644 --- a/config.js +++ b/config.js @@ -4,7 +4,14 @@ module.exports = { netId: process.env.NET_ID || 42, rpcUrl: process.env.RPC_URL || 'https://kovan.infura.io/v3/a3f4d001c1fc4a359ea70dd27fd9cb51', privateKey: process.env.PRIVATE_KEY, - mixerAddress: process.env.MIXER_ADDRESS, + mixers: [ { + address: process.env.ETH_MIXER_ADDRESS, + currency: 'eth' + }, + { + address: process.env.DAI_MIXER_ADDRESS, + currency: 'dai' + } ], defaultGasPrice: 2, gasOracleUrls: ['https://www.etherchain.org/api/gasPriceOracle', 'https://gasprice.poa.network/'], ethdaiAddress: '0x7Ef645705cb7D401C5CD91a395cfcc3Db3C93689' diff --git a/index.js b/index.js index cf0f3b3..0f097a7 100644 --- a/index.js +++ b/index.js @@ -20,8 +20,8 @@ app.use(function(req, res, next) { next() }) -const { netId, rpcUrl, privateKey, mixerAddress, defaultGasPrice } = require('./config') -const { fetchGasPrice, isValidProof, fetchDAIprice } = require('./utils') +const { netId, rpcUrl, privateKey, mixers, defaultGasPrice } = require('./config') +const { fetchGasPrice, isValidProof, fetchDAIprice, isKnownContract } = require('./utils') const web3 = new Web3(rpcUrl, null, { transactionConfirmationBlocks: 1 }) const account = web3.eth.accounts.privateKeyToAccount('0x' + privateKey) @@ -29,33 +29,46 @@ web3.eth.accounts.wallet.add('0x' + privateKey) web3.eth.defaultAccount = account.address const mixerABI = require('./abis/mixerABI.json') -const mixer = new web3.eth.Contract(mixerABI, mixerAddress) const gasPrices = { fast: defaultGasPrice } const ethPriceInDai = toWei('200') app.get('/', function (req, res) { // just for testing purposes - res.send(`Tornado mixer relayer. Gas Price is ${JSON.stringify(gasPrices)}. Mixer address is ${mixerAddress}`) + res.send(`Tornado mixer relayer. Gas Price is ${JSON.stringify(gasPrices)}. Mixer addresses are ${mixers}`) }) app.post('/relay', async (req, resp) => { - console.log(JSON.stringify(req.body, null, 2)) - const { valid , reason } = isValidProof(req.body) + let { valid , reason } = isValidProof(req.body.proof) if (!valid) { console.log('Proof is invalid:', reason) return resp.status(400).json({ error: 'Proof is invalid' }) } + let currency + ( { valid, currency } = isKnownContract(req.body.contract)) - let { pi_a, pi_b, pi_c, publicSignals } = req.body + let { pi_a, pi_b, pi_c, publicSignals } = req.body.proof const fee = toBN(publicSignals[3]) - const desiredFee = toBN(toWei(gasPrices.fast.toString(), 'gwei')).mul(toBN('1000000')) + const expense = toBN(toWei(gasPrices.fast.toString(), 'gwei')).mul(toBN('1000000')) + let desiredFee + switch (currency) { + case 'eth': { + desiredFee = expense + break + } + case 'dai': { + desiredFee = expense.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 nullifier = publicSignals[1] const isSpent = await mixer.methods.isSpent(nullifier).call() if (isSpent) { diff --git a/utils.js b/utils.js index b98f643..6bca065 100644 --- a/utils.js +++ b/utils.js @@ -1,6 +1,6 @@ const fetch = require('node-fetch') const { isHexStrict, hexToNumberString } = require('web3-utils') -const { gasOracleUrls, ethdaiAddress } = require('./config') +const { gasOracleUrls, ethdaiAddress, mixers } = require('./config') const oracleABI = require('./abis/ETHDAIOracle.json') async function fetchGasPrice({ gasPrices, oracleIndex = 0 }) { @@ -90,8 +90,17 @@ function isValidProof(proof) { return { valid: true } } +function isKnownContract(contract) { + for (let i = 0; i < mixers.length; i++) { + if (mixers[i].address === contract) { + return { valid: true, currency: mixers[i].currency } + } + } + return { valid: false } +} + function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) } -module.exports = { fetchGasPrice, isValidProof, sleep, fetchDAIprice } +module.exports = { fetchGasPrice, isValidProof, sleep, fetchDAIprice, isKnownContract }