fix: deposit and withdraw

This commit is contained in:
Nik Dement'ev 2020-12-24 14:39:20 +03:00
parent 60a5f77cc6
commit 58e99581e7
No known key found for this signature in database
GPG Key ID: 769B05D57CF16FE2
10 changed files with 692752 additions and 39 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
node_modules node_modules
.env .env
.idea

687488
build/circuits/tornado.json Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@ -0,0 +1,213 @@
[
{
"constant": true,
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "_totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "who",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"constant": true,
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]

View File

@ -0,0 +1,519 @@
[
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "_newOperator",
"type": "address"
}
],
"name": "changeOperator",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "nullifierHashes",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes",
"name": "_proof",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "_root",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_nullifierHash",
"type": "bytes32"
},
{
"internalType": "address payable",
"name": "_recipient",
"type": "address"
},
{
"internalType": "address payable",
"name": "_relayer",
"type": "address"
},
{
"internalType": "uint256",
"name": "_fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_refund",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "verifier",
"outputs": [
{
"internalType": "contract IVerifier",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "_left",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_right",
"type": "bytes32"
}
],
"name": "hashLeftRight",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "pure",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "FIELD_SIZE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "levels",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "operator",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "_root",
"type": "bytes32"
}
],
"name": "isKnownRoot",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "commitments",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "denomination",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "currentRootIndex",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "address",
"name": "_newVerifier",
"type": "address"
}
],
"name": "updateVerifier",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32[]",
"name": "_nullifierHashes",
"type": "bytes32[]"
}
],
"name": "isSpentArray",
"outputs": [
{
"internalType": "bool[]",
"name": "spent",
"type": "bool[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": false,
"inputs": [
{
"internalType": "bytes32",
"name": "_commitment",
"type": "bytes32"
}
],
"name": "deposit",
"outputs": [],
"payable": true,
"stateMutability": "payable",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "getLastRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "roots",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "ROOT_HISTORY_SIZE",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "bytes32",
"name": "_nullifierHash",
"type": "bytes32"
}
],
"name": "isSpent",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "zeros",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "ZERO_VALUE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "filledSubtrees",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "nextIndex",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IVerifier",
"name": "_verifier",
"type": "address"
},
{
"internalType": "uint256",
"name": "_denomination",
"type": "uint256"
},
{
"internalType": "uint32",
"name": "_merkleTreeHeight",
"type": "uint32"
},
{
"internalType": "address",
"name": "_operator",
"type": "address"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "commitment",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint32",
"name": "leafIndex",
"type": "uint32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "nullifierHash",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "relayer",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "fee",
"type": "uint256"
}
],
"name": "Withdrawal",
"type": "event"
}
]

View File

@ -0,0 +1,237 @@
[
{
"inputs": [
{
"internalType": "bytes32",
"name": "_tornadoTrees",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_governance",
"type": "bytes32"
},
{
"internalType": "bytes32[]",
"name": "_instances",
"type": "bytes32[]"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes",
"name": "encryptedNote",
"type": "bytes"
}
],
"name": "EncryptedNote",
"type": "event"
},
{
"inputs": [
{
"internalType": "bytes32[]",
"name": "domains",
"type": "bytes32[]"
}
],
"name": "bulkResolve",
"outputs": [
{
"internalType": "address[]",
"name": "result",
"type": "address[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "governance",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "",
"type": "address"
}
],
"name": "instances",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "node",
"type": "bytes32"
}
],
"name": "resolve",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tornadoTrees",
"outputs": [
{
"internalType": "contract ITornadoTrees",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "_tornado",
"type": "address"
},
{
"internalType": "bytes32",
"name": "_commitment",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "_encryptedNote",
"type": "bytes"
}
],
"name": "deposit",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "_instance",
"type": "address"
},
{
"internalType": "bool",
"name": "_update",
"type": "bool"
}
],
"name": "updateInstance",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract ITornadoInstance",
"name": "_tornado",
"type": "address"
},
{
"internalType": "bytes",
"name": "_proof",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "_root",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "_nullifierHash",
"type": "bytes32"
},
{
"internalType": "address payable",
"name": "_recipient",
"type": "address"
},
{
"internalType": "address payable",
"name": "_relayer",
"type": "address"
},
{
"internalType": "uint256",
"name": "_fee",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_refund",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IERC20",
"name": "_token",
"type": "address"
},
{
"internalType": "address payable",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_balance",
"type": "uint256"
}
],
"name": "rescueTokens",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]

132
cli.js
View File

@ -17,8 +17,9 @@ const websnarkUtils = require('websnark/src/utils')
const { toWei, fromWei, toBN, BN } = require('web3-utils') const { toWei, fromWei, toBN, BN } = require('web3-utils')
const config = require('./config') const config = require('./config')
const program = require('commander') const program = require('commander')
const { GasPriceOracle } = require('gas-price-oracle')
let web3, tornado, circuit, proving_key, groth16, erc20, senderAccount, netId let web3, tornado, mixerContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId
let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY let MERKLE_TREE_HEIGHT, ETH_AMOUNT, TOKEN_AMOUNT, PRIVATE_KEY
/** Whether we are in a browser or node.js */ /** Whether we are in a browser or node.js */
@ -77,7 +78,7 @@ async function deposit({ currency, amount }) {
await printETHBalance({ address: senderAccount, name: 'Sender account' }) await printETHBalance({ address: senderAccount, name: 'Sender account' })
const value = isLocalRPC ? ETH_AMOUNT : fromDecimals({ amount, decimals: 18 }) const value = isLocalRPC ? ETH_AMOUNT : fromDecimals({ amount, decimals: 18 })
console.log('Submitting deposit transaction') console.log('Submitting deposit transaction')
await tornado.methods.deposit(toHex(deposit.commitment)).send({ value, from: senderAccount, gas: 2e6 }) await tornado.methods.deposit(tornadoInstance, toHex(deposit.commitment), []).send({ value, from: senderAccount, gas: 2e6 })
await printETHBalance({ address: tornado._address, name: 'Tornado' }) await printETHBalance({ address: tornado._address, name: 'Tornado' })
await printETHBalance({ address: senderAccount, name: 'Sender account' }) await printETHBalance({ address: senderAccount, name: 'Sender account' })
} else { // a token } else { // a token
@ -113,22 +114,26 @@ async function deposit({ currency, amount }) {
* @param deposit Deposit object * @param deposit Deposit object
*/ */
async function generateMerkleProof(deposit) { async function generateMerkleProof(deposit) {
let leafIndex = -1
// Get all deposit events from smart contract and assemble merkle tree from them // Get all deposit events from smart contract and assemble merkle tree from them
console.log('Getting current state from tornado contract') const events = await mixerContract.getPastEvents('Deposit', { fromBlock: 0, toBlock: 'latest' })
const events = await tornado.getPastEvents('Deposit', { fromBlock: 0, toBlock: 'latest' })
const leaves = events const leaves = events
.sort((a, b) => a.returnValues.leafIndex - b.returnValues.leafIndex) // Sort events in chronological order .sort((a, b) => a.returnValues.leafIndex - b.returnValues.leafIndex) // Sort events in chronological order
.map(e => e.returnValues.commitment) .map((e) => {
const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves) const index = toBN(e.returnValues.leafIndex).toNumber()
// Find current commitment in the tree if (toBN(e.returnValues.commitment).eq(toBN(deposit.commitmentHex))) {
const depositEvent = events.find(e => e.returnValues.commitment === toHex(deposit.commitment)) leafIndex = index
const leafIndex = depositEvent ? depositEvent.returnValues.leafIndex : -1 }
return e.returnValues.commitment.toString(10)
})
const tree = new merkleTree(MERKLE_TREE_HEIGHT, leaves)
// Validate that our data is correct // Validate that our data is correct
const root = await tree.root() const root = await tree.root()
const isValidRoot = await tornado.methods.isKnownRoot(toHex(root)).call() const isValidRoot = await mixerContract.methods.isKnownRoot(toHex(root)).call()
const isSpent = await tornado.methods.isSpent(toHex(deposit.nullifierHash)).call() const isSpent = await mixerContract.methods.isSpent(toHex(deposit.nullifierHash)).call()
assert(isValidRoot === true, 'Merkle tree is corrupted') assert(isValidRoot === true, 'Merkle tree is corrupted')
assert(isSpent === false, 'The note is already spent') assert(isSpent === false, 'The note is already spent')
assert(leafIndex >= 0, 'The deposit is not found in the tree') assert(leafIndex >= 0, 'The deposit is not found in the tree')
@ -199,28 +204,29 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
throw new Error('ENS name resolving is not supported. Please provide DNS name of the relayer. See instuctions in README.md') throw new Error('ENS name resolving is not supported. Please provide DNS name of the relayer. See instuctions in README.md')
} }
const relayerStatus = await axios.get(relayerURL + '/status') const relayerStatus = await axios.get(relayerURL + '/status')
const { relayerAddress, netId, gasPrices, ethPrices, relayerServiceFee } = relayerStatus.data
const { rewardAccount, netId, ethPrices, tornadoServiceFee } = relayerStatus.data
assert(netId === await web3.eth.net.getId() || netId === '*', 'This relay is for different network') assert(netId === await web3.eth.net.getId() || netId === '*', 'This relay is for different network')
console.log('Relay address: ', relayerAddress) console.log('Relay address: ', rewardAccount)
const gasPrice = await fetchGasPrice()
const decimals = isLocalRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals const decimals = isLocalRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals
const fee = calculateFee({ gasPrices, currency, amount, refund, ethPrices, relayerServiceFee, decimals }) const fee = calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerServiceFee: tornadoServiceFee, decimals })
if (fee.gt(fromDecimals({ amount, decimals }))) { if (fee.gt(fromDecimals({ amount, decimals }))) {
throw new Error('Too high refund') throw new Error('Too high refund')
} }
const { proof, args } = await generateProof({ deposit, recipient, relayerAddress, fee, refund })
const { proof, args } = await generateProof({ deposit, recipient, relayerAddress: rewardAccount, fee, refund })
console.log('Sending withdraw transaction through relay') console.log('Sending withdraw transaction through relay')
try { try {
const relay = await axios.post(relayerURL + '/relay', { contract: tornado._address, proof, args }) const response = await axios.post(relayerURL + '/v1/tornadoWithdraw', { contract: tornadoInstance, proof, args })
if (netId === 1 || netId === 42) {
console.log(`Transaction submitted through the relay. View transaction on etherscan https://${getCurrentNetworkName()}etherscan.io/tx/${relay.data.txHash}`)
} else {
console.log(`Transaction submitted through the relay. The transaction hash is ${relay.data.txHash}`)
}
const receipt = await waitForTxReceipt({ txHash: relay.data.txHash }) const { id } = response.data
console.log('Transaction mined in block', receipt.blockNumber)
const result = await getStatus(id, relayerURL)
console.log('STATUS', result)
} catch (e) { } catch (e) {
if (e.response) { if (e.response) {
console.error(e.response.data.error) console.error(e.response.data.error)
@ -232,7 +238,7 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
const { proof, args } = await generateProof({ deposit, recipient, refund }) const { proof, args } = await generateProof({ deposit, recipient, refund })
console.log('Submitting withdraw transaction') console.log('Submitting withdraw transaction')
await tornado.methods.withdraw(proof, ...args).send({ from: senderAccount, value: refund.toString(), gas: 1e6 }) await tornado.methods.withdraw(tornadoInstance, proof, ...args).send({ from: senderAccount, value: refund.toString(), gas: 1e6 })
.on('transactionHash', function (txHash) { .on('transactionHash', function (txHash) {
if (netId === 1 || netId === 42) { if (netId === 1 || netId === 42) {
console.log(`View transaction on etherscan https://${getCurrentNetworkName()}etherscan.io/tx/${txHash}`) console.log(`View transaction on etherscan https://${getCurrentNetworkName()}etherscan.io/tx/${txHash}`)
@ -246,6 +252,37 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
console.log('Done') console.log('Done')
} }
function getStatus (id, relayerURL) {
return new Promise((resolve) => {
async function getRelayerStatus() {
const responseStatus = await axios.get(relayerURL + '/v1/jobs/' + id)
if (responseStatus.status === 200) {
const { txHash, status, confirmations, failedReason } = responseStatus.data
console.log(`Current job status ${status}, confirmations: ${confirmations}`)
if (status === 'FAILED') {
throw new Error(status + ' failed reason:' + failedReason)
}
if (status === 'CONFIRMED') {
const receipt = await waitForTxReceipt({ txHash })
console.log(`Transaction submitted through the relay. View transaction on etherscan https://${getCurrentNetworkName()}etherscan.io/tx/${txHash}`)
console.log('Transaction mined in block', receipt.blockNumber)
resolve(status)
}
}
setTimeout(() => {
getRelayerStatus(id, relayerURL)
}, 3000)
}
getRelayerStatus()
})
}
function fromDecimals({ amount, decimals }) { function fromDecimals({ amount, decimals }) {
amount = amount.toString() amount = amount.toString()
let ether = amount.toString() let ether = amount.toString()
@ -338,20 +375,40 @@ function getCurrentNetworkName() {
switch (netId) { switch (netId) {
case 1: case 1:
return '' return ''
case 5:
return 'goerli.'
case 42: case 42:
return 'kovan.' return 'kovan.'
} }
} }
function calculateFee({ gasPrices, currency, amount, refund, ethPrices, relayerServiceFee, decimals }) { function gasPrices(value = 80) {
const tenPercent = (Number(value) * 5) / 100
const max = Math.max(tenPercent, 3)
const bumped = Math.floor(Number(value) + max)
return toHex(toWei(bumped.toString(), 'gwei'))
}
async function fetchGasPrice() {
try {
const oracle = new GasPriceOracle()
const gas = await oracle.gasPrices()
return gasPrices(gas.fast)
} catch (err) {
throw new Error(`Method fetchGasPrice has error ${err.message}`)
}
}
function calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerServiceFee, decimals }) {
const decimalsPoint = Math.floor(relayerServiceFee) === Number(relayerServiceFee) ? const decimalsPoint = Math.floor(relayerServiceFee) === Number(relayerServiceFee) ?
0 : 0 :
relayerServiceFee.toString().split('.')[1].length relayerServiceFee.toString().split('.')[1].length
const roundDecimal = 10 ** decimalsPoint const roundDecimal = 10 ** decimalsPoint
const total = toBN(fromDecimals({ amount, decimals })) const total = toBN(fromDecimals({ amount, decimals }))
const feePercent = total.mul(toBN(relayerServiceFee * roundDecimal)).div(toBN(roundDecimal * 100)) const feePercent = total.mul(toBN(relayerServiceFee * roundDecimal)).div(toBN(roundDecimal * 100))
const expense = toBN(toWei(gasPrices.fast.toString(), 'gwei')).mul(toBN(5e5)) const expense = toBN(gasPrice).mul(toBN(5e5))
let desiredFee let desiredFee
switch (currency) { switch (currency) {
case 'eth': { case 'eth': {
@ -471,15 +528,16 @@ async function loadWithdrawalData({ amount, currency, deposit }) {
* Init web3, contracts, and snark * Init web3, contracts, and snark
*/ */
async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) { async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) {
let contractJson, erc20ContractJson, erc20tornadoJson, tornadoAddress, tokenAddress let contractJson, mixerJson, erc20ContractJson, erc20tornadoJson, tornadoAddress, tokenAddress
// TODO do we need this? should it work in browser really? // TODO do we need this? should it work in browser really?
if (inBrowser) { if (inBrowser) {
// Initialize using injected web3 (Metamask) // Initialize using injected web3 (Metamask)
// To assemble web version run `npm run browserify` // To assemble web version run `npm run browserify`
web3 = new Web3(window.web3.currentProvider, null, { transactionConfirmationBlocks: 1 }) web3 = new Web3(window.web3.currentProvider, null, { transactionConfirmationBlocks: 1 })
contractJson = await (await fetch('build/contracts/ETHTornado.json')).json() contractJson = await (await fetch('build/contracts/TornadoProxy.abi.json')).json()
circuit = await (await fetch('build/circuits/withdraw.json')).json() mixerJson = await (await fetch('build/contracts/Mixer.abi.json')).json()
proving_key = await (await fetch('build/circuits/withdraw_proving_key.bin')).arrayBuffer() circuit = await (await fetch('build/circuits/tornado.json')).json()
proving_key = await (await fetch('build/circuits/tornadoProvingKey.bin')).arrayBuffer()
MERKLE_TREE_HEIGHT = 20 MERKLE_TREE_HEIGHT = 20
ETH_AMOUNT = 1e18 ETH_AMOUNT = 1e18
TOKEN_AMOUNT = 1e19 TOKEN_AMOUNT = 1e19
@ -487,9 +545,10 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) {
} else { } else {
// Initialize from local node // Initialize from local node
web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 }) web3 = new Web3(rpc, null, { transactionConfirmationBlocks: 1 })
contractJson = require('./build/contracts/ETHTornado.json') contractJson = require('./build/contracts/TornadoProxy.abi.json')
circuit = require('./build/circuits/withdraw.json') mixerJson = require('./build/contracts/Mixer.abi.json')
proving_key = fs.readFileSync('build/circuits/withdraw_proving_key.bin').buffer circuit = require('./build/circuits/tornado.json')
proving_key = fs.readFileSync('build/circuits/tornadoProvingKey.bin').buffer
MERKLE_TREE_HEIGHT = process.env.MERKLE_TREE_HEIGHT || 20 MERKLE_TREE_HEIGHT = process.env.MERKLE_TREE_HEIGHT || 20
ETH_AMOUNT = process.env.ETH_AMOUNT ETH_AMOUNT = process.env.ETH_AMOUNT
TOKEN_AMOUNT = process.env.TOKEN_AMOUNT TOKEN_AMOUNT = process.env.TOKEN_AMOUNT
@ -519,7 +578,9 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) {
senderAccount = (await web3.eth.getAccounts())[0] senderAccount = (await web3.eth.getAccounts())[0]
} else { } else {
try { try {
tornadoAddress = config.deployments[`netId${netId}`][currency].instanceAddress[amount] tornadoAddress = config.deployments[`netId${netId}`].proxy
tornadoInstance = config.deployments[`netId${netId}`][currency].instanceAddress[amount]
if (!tornadoAddress) { if (!tornadoAddress) {
throw new Error() throw new Error()
} }
@ -529,7 +590,8 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100' }) {
process.exit(1) process.exit(1)
} }
} }
tornado = new web3.eth.Contract(contractJson.abi, tornadoAddress) tornado = new web3.eth.Contract(contractJson, tornadoAddress)
mixerContract = new web3.eth.Contract(mixerJson, tornadoInstance)
erc20 = currency !== 'eth' ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : {} erc20 = currency !== 'eth' ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : {}
} }

View File

@ -67,7 +67,76 @@ module.exports = {
tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7', tokenAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
symbol: 'USDT', symbol: 'USDT',
decimals: 6 decimals: 6
} },
proxy: '0x905b63Fff465B9fFBF41DeA908CEb12478ec7601',
},
netId5: {
eth: {
instanceAddress: {
'0.1': '0x6Bf694a291DF3FeC1f7e69701E3ab6c592435Ae7',
'1': '0x3aac1cC67c2ec5Db4eA850957b967Ba153aD6279',
'10': '0x723B78e67497E85279CB204544566F4dC5d2acA0',
'100': '0x0E3A09dDA6B20aFbB34aC7cD4A6881493f3E7bf7'
},
symbol: 'ETH',
decimals: 18
},
dai: {
instanceAddress: {
'100': '0xdf2d3cC5F361CF95b3f62c4bB66deFe3FDE47e3D',
'1000': '0xD96291dFa35d180a71964D0894a1Ae54247C4ccD',
'10000': '0xb192794f72EA45e33C3DF6fe212B9c18f6F45AE3',
'100000': undefined
},
tokenAddress: '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa',
symbol: 'DAI',
decimals: 18
},
cdai: {
instanceAddress: {
'5000': '0x6Fc9386ABAf83147b3a89C36D422c625F44121C8',
'50000': '0x7182EA067e0f050997444FCb065985Fd677C16b6',
'500000': '0xC22ceFd90fbd1FdEeE554AE6Cc671179BC3b10Ae',
'5000000': undefined
},
tokenAddress: '0xe7bc397DBd069fC7d0109C0636d06888bb50668c',
symbol: 'cDAI',
decimals: 8
},
usdc: {
instanceAddress: {
'100': '0x137E2B6d185018e7f09f6cf175a970e7fC73826C',
'1000': '0xcC7f1633A5068E86E3830e692e3e3f8f520525Af',
'10000': '0x28C8f149a0ab8A9bdB006B8F984fFFCCE52ef5EF',
'100000': undefined
},
tokenAddress: '0x75B0622Cec14130172EaE9Cf166B92E5C112FaFF',
symbol: 'USDC',
decimals: 6
},
cusdc: {
instanceAddress: {
'5000': '0xc0648F28ABA385c8a1421Bbf1B59e3c474F89cB0',
'50000': '0x0C53853379c6b1A7B74E0A324AcbDD5Eabd4981D',
'500000': '0xf84016A0E03917cBe700D318EB1b7a53e6e3dEe1',
'5000000': undefined
},
tokenAddress: '0xcfC9bB230F00bFFDB560fCe2428b4E05F3442E35',
symbol: 'cUSDC',
decimals: 8
},
usdt: {
instanceAddress: {
'100': '0x327853Da7916a6A0935563FB1919A48843036b42',
'1000': '0x531AA4DF5858EA1d0031Dad16e3274609DE5AcC0',
'10000': '0x0958275F0362cf6f07D21373aEE0cf37dFe415dD',
'100000': '0x14aEd24B67EaF3FF28503eB92aeb217C47514364'
},
tokenAddress: '0x03c5F29e9296006876d8DF210BCFfD7EA5Db1Cf1',
symbol: 'USDT',
decimals: 6
},
proxy: '0x905b63Fff465B9fFBF41DeA908CEb12478ec7601',
}, },
netId42: { netId42: {
eth: { eth: {
@ -134,7 +203,8 @@ module.exports = {
tokenAddress: '0x03c5F29e9296006876d8DF210BCFfD7EA5Db1Cf1', tokenAddress: '0x03c5F29e9296006876d8DF210BCFfD7EA5Db1Cf1',
symbol: 'USDT', symbol: 'USDT',
decimals: 6 decimals: 6
} },
proxy: '0x26Dd6E44a7090a2598e280CBD1F5E6034F2edD04',
} }
} }
} }

View File

@ -11,9 +11,10 @@
"dependencies": { "dependencies": {
"axios": "^0.19.2", "axios": "^0.19.2",
"circom": "0.0.35", "circom": "0.0.35",
"circomlib": "git+https://github.com/tornadocash/circomlib.git#c372f14d324d57339c88451834bf2824e73bbdbc", "circomlib": "git+https://github.com/tornadocash/circomlib.git#3b492f9801573eebcfe1b6c584afe8a3beecf2b4",
"commander": "^5.1.0", "commander": "^5.1.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"gas-price-oracle": "^0.2.2",
"snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5", "snarkjs": "git+https://github.com/tornadocash/snarkjs.git#869181cfaf7526fe8972073d31655493a04326d5",
"web3": "^1.2.8", "web3": "^1.2.8",
"websnark": "git+https://github.com/tornadocash/websnark.git#4c0af6a8b65aabea3c09f377f63c44e7a58afa6d" "websnark": "git+https://github.com/tornadocash/websnark.git#4c0af6a8b65aabea3c09f377f63c44e7a58afa6d"

4122
yarn.lock Normal file

File diff suppressed because it is too large Load Diff