mirror of
https://github.com/tornadocash/tornado-cli.git
synced 2024-10-01 07:15:36 -04:00
Merge 21b667add9
into 378ddf8b8b
This commit is contained in:
commit
fce5240348
@ -6,6 +6,11 @@ TOKEN_AMOUNT=100000000000000000
|
|||||||
ERC20_TOKEN=
|
ERC20_TOKEN=
|
||||||
|
|
||||||
PRIVATE_KEY=
|
PRIVATE_KEY=
|
||||||
|
RPC=
|
||||||
|
RELAYER=
|
||||||
|
TOR=
|
||||||
|
LOCALTX=
|
||||||
|
ONLYRPC=
|
||||||
|
|
||||||
#ERC20_TOKEN=0xf3e0d7bf58c5d455d31ef1c2d5375904df525105
|
#ERC20_TOKEN=0xf3e0d7bf58c5d455d31ef1c2d5375904df525105
|
||||||
#TOKEN_AMOUNT=1000000
|
#TOKEN_AMOUNT=1000000
|
||||||
|
16
README.md
16
README.md
@ -121,9 +121,13 @@ Backed up invoice as ./backup-tornadoInvoice-eth-0.1-5-0x1b680c7d.txt
|
|||||||
Creating deposit transaction with `depositInvoice` only requires valid deposit note created by `createNote` command, so that the deposit note could be stored without exposed anywhere.
|
Creating deposit transaction with `depositInvoice` only requires valid deposit note created by `createNote` command, so that the deposit note could be stored without exposed anywhere.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ node cli.js depositInvoice <invoice>
|
$ node cli.js depositInvoice <invoice> --rpc <rpc url> --tor <torPort>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that `--tor <torPort>` is optional.
|
||||||
|
|
||||||
|
For RPC nodes please refer to the list of public RPC nodes below.
|
||||||
|
|
||||||
##### Example:
|
##### Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -144,9 +148,17 @@ Sender account balance is x.xxxxxxx ETH
|
|||||||
#### To withdraw, you will need deposit note that matches with your deposit transaction.
|
#### To withdraw, you will need deposit note that matches with your deposit transaction.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ node cli.js withdraw <note> <recipient>
|
$ node cli.js withdraw <note> <recipient> --rpc <rpc url> --relayer <relayer url> --tor <torPort>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that `--relayer <relayer url>`, `--tor <torPort>` is optional.
|
||||||
|
|
||||||
|
If you want to use Tornado Cash relayer for your first withdrawal to your new ethereum account, please refer to the list of relayers below.
|
||||||
|
|
||||||
|
If you don't need relayer while doing withdrawals, you must apply your withdrawal account's private key to `.env` file.
|
||||||
|
|
||||||
|
Copy the `PRIVATE_KEY=` line of `.env.example` to `.env`, and add your private key behind the `=`.
|
||||||
|
|
||||||
##### Example:
|
##### Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
489
cli.js
489
cli.js
@ -22,7 +22,7 @@ const { GasPriceOracle } = require('gas-price-oracle');
|
|||||||
const SocksProxyAgent = require('socks-proxy-agent');
|
const SocksProxyAgent = require('socks-proxy-agent');
|
||||||
const is_ip_private = require('private-ip');
|
const is_ip_private = require('private-ip');
|
||||||
|
|
||||||
let web3, torPort, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, doNotSubmitTx, multiCall, privateRpc, subgraph;
|
let web3, torPort, tornado, tornadoContract, tornadoInstance, circuit, proving_key, groth16, erc20, senderAccount, netId, netName, netSymbol, doNotSubmitTx, multiCall, privateRpc, subgraph, isMobile;
|
||||||
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 */
|
||||||
@ -58,6 +58,7 @@ async function useMultiCall(queryArray) {
|
|||||||
async function printETHBalance({ address, name }) {
|
async function printETHBalance({ address, name }) {
|
||||||
const checkBalance = new BigNumber(await web3.eth.getBalance(address)).div(BigNumber(10).pow(18));
|
const checkBalance = new BigNumber(await web3.eth.getBalance(address)).div(BigNumber(10).pow(18));
|
||||||
console.log(`${name} balance is`, rmDecimalBN(checkBalance), `${netSymbol}`);
|
console.log(`${name} balance is`, rmDecimalBN(checkBalance), `${netSymbol}`);
|
||||||
|
return checkBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Display ERC20 account balance */
|
/** Display ERC20 account balance */
|
||||||
@ -65,19 +66,28 @@ async function printERC20Balance({ address, name, tokenAddress }) {
|
|||||||
let tokenDecimals, tokenBalance, tokenName, tokenSymbol;
|
let tokenDecimals, tokenBalance, tokenName, tokenSymbol;
|
||||||
const erc20ContractJson = require('./build/contracts/ERC20Mock.json');
|
const erc20ContractJson = require('./build/contracts/ERC20Mock.json');
|
||||||
erc20 = tokenAddress ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : erc20;
|
erc20 = tokenAddress ? new web3.eth.Contract(erc20ContractJson.abi, tokenAddress) : erc20;
|
||||||
if (!isTestRPC && !multiCall) {
|
if (!isTestRPC && multiCall) {
|
||||||
const tokenCall = await useMultiCall([[tokenAddress, erc20.methods.balanceOf(address).encodeABI()], [tokenAddress, erc20.methods.decimals().encodeABI()], [tokenAddress, erc20.methods.name().encodeABI()], [tokenAddress, erc20.methods.symbol().encodeABI()]]);
|
const tokenCall = await useMultiCall([
|
||||||
|
[tokenAddress, erc20.methods.balanceOf(address).encodeABI()],
|
||||||
|
[tokenAddress, erc20.methods.decimals().encodeABI()],
|
||||||
|
[tokenAddress, erc20.methods.name().encodeABI()],
|
||||||
|
[tokenAddress, erc20.methods.symbol().encodeABI()]
|
||||||
|
]);
|
||||||
tokenDecimals = parseInt(tokenCall[1]);
|
tokenDecimals = parseInt(tokenCall[1]);
|
||||||
tokenBalance = new BigNumber(tokenCall[0]).div(BigNumber(10).pow(tokenDecimals));
|
tokenBalance = new BigNumber(tokenCall[0]).div(BigNumber(10).pow(tokenDecimals));
|
||||||
tokenName = web3.eth.abi.decodeParameter('string', tokenCall[2]);
|
tokenName = web3.eth.abi.decodeParameter('string', tokenCall[2]);
|
||||||
tokenSymbol = web3.eth.abi.decodeParameter('string', tokenCall[3]);
|
tokenSymbol = web3.eth.abi.decodeParameter('string', tokenCall[3]);
|
||||||
} else {
|
} else {
|
||||||
tokenDecimals = await erc20.methods.decimals().call();
|
[ tokenDecimals, tokenBalance, tokenName, tokenSymbol ] = await Promise.all([
|
||||||
tokenBalance = new BigNumber(await erc20.methods.balanceOf(address).call()).div(BigNumber(10).pow(tokenDecimals));
|
erc20.methods.decimals().call(),
|
||||||
tokenName = await erc20.methods.name().call();
|
erc20.methods.balanceOf(address).call(),
|
||||||
tokenSymbol = await erc20.methods.symbol().call();
|
erc20.methods.name().call(),
|
||||||
|
erc20.methods.symbol().call()
|
||||||
|
]);
|
||||||
|
tokenBalance = new BigNumber(tokenBalance).div(BigNumber(10).pow(tokenDecimals));
|
||||||
}
|
}
|
||||||
console.log(`${name}`, tokenName, `Balance is`, rmDecimalBN(tokenBalance), tokenSymbol);
|
console.log(`${name}`, tokenName, `Balance is`, rmDecimalBN(tokenBalance), tokenSymbol);
|
||||||
|
return tokenBalance;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitTransaction(signedTX) {
|
async function submitTransaction(signedTX) {
|
||||||
@ -92,8 +102,10 @@ async function submitTransaction(signedTX) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function generateTransaction(to, encodedData, value = 0) {
|
async function generateTransaction(to, encodedData, value = 0) {
|
||||||
const nonce = await web3.eth.getTransactionCount(senderAccount);
|
const [ nonce, gasPrice ] = await Promise.all([
|
||||||
let gasPrice = await fetchGasPrice();
|
web3.eth.getTransactionCount(senderAccount),
|
||||||
|
fetchGasPrice()
|
||||||
|
]);
|
||||||
let gasLimit;
|
let gasLimit;
|
||||||
|
|
||||||
async function estimateGas() {
|
async function estimateGas() {
|
||||||
@ -207,8 +219,10 @@ async function createInvoice({ currency, amount, chainId }) {
|
|||||||
const invoiceString = `tornadoInvoice-${currency}-${amount}-${chainId}-${commitmentNote}`;
|
const invoiceString = `tornadoInvoice-${currency}-${amount}-${chainId}-${commitmentNote}`;
|
||||||
console.log(`Your invoice for deposit: ${invoiceString}`);
|
console.log(`Your invoice for deposit: ${invoiceString}`);
|
||||||
|
|
||||||
await backupNote({ currency, amount, netId: chainId, note, noteString });
|
await Promise.all([
|
||||||
await backupInvoice({ currency, amount, netId: chainId, commitmentNote, invoiceString });
|
backupNote({ currency, amount, netId: chainId, note, noteString }),
|
||||||
|
backupInvoice({ currency, amount, netId: chainId, commitmentNote, invoiceString })
|
||||||
|
]);
|
||||||
|
|
||||||
return (noteString, invoiceString);
|
return (noteString, invoiceString);
|
||||||
}
|
}
|
||||||
@ -237,25 +251,30 @@ async function deposit({ currency, amount, commitmentNote }) {
|
|||||||
commitment = toHex(commitmentNote);
|
commitment = toHex(commitmentNote);
|
||||||
}
|
}
|
||||||
if (currency === netSymbol.toLowerCase()) {
|
if (currency === netSymbol.toLowerCase()) {
|
||||||
await printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' });
|
await Promise.all([
|
||||||
await printETHBalance({ address: senderAccount, name: 'Sender account' });
|
printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' }),
|
||||||
|
printETHBalance({ address: senderAccount, name: 'Sender account' })
|
||||||
|
]);
|
||||||
const value = isTestRPC ? ETH_AMOUNT : fromDecimals({ amount, decimals: 18 });
|
const value = isTestRPC ? ETH_AMOUNT : fromDecimals({ amount, decimals: 18 });
|
||||||
console.log('Submitting deposit transaction');
|
console.log('Submitting deposit transaction');
|
||||||
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, commitment, []).encodeABI(), value);
|
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, commitment, []).encodeABI(), value);
|
||||||
await printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' });
|
await Promise.all([
|
||||||
await printETHBalance({ address: senderAccount, name: 'Sender account' });
|
printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' }),
|
||||||
|
printETHBalance({ address: senderAccount, name: 'Sender account' })
|
||||||
|
]);
|
||||||
} else {
|
} else {
|
||||||
// a token
|
// a token
|
||||||
await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' });
|
const [ balance1, balance2, allowance ] = await Promise.all([
|
||||||
await printERC20Balance({ address: senderAccount, name: 'Sender account' });
|
printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' }),
|
||||||
|
printERC20Balance({ address: senderAccount, name: 'Sender account' }),
|
||||||
|
erc20.methods.allowance(senderAccount, tornado._address).call({ from: senderAccount })
|
||||||
|
]);
|
||||||
const decimals = isTestRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals;
|
const decimals = isTestRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals;
|
||||||
const tokenAmount = isTestRPC ? TOKEN_AMOUNT : fromDecimals({ amount, decimals });
|
const tokenAmount = isTestRPC ? TOKEN_AMOUNT : fromDecimals({ amount, decimals });
|
||||||
if (isTestRPC) {
|
if (isTestRPC) {
|
||||||
console.log('Minting some test tokens to deposit');
|
console.log('Minting some test tokens to deposit');
|
||||||
await generateTransaction(erc20Address, erc20.methods.mint(senderAccount, tokenAmount).encodeABI());
|
await generateTransaction(erc20Address, erc20.methods.mint(senderAccount, tokenAmount).encodeABI());
|
||||||
}
|
}
|
||||||
|
|
||||||
const allowance = await erc20.methods.allowance(senderAccount, tornado._address).call({ from: senderAccount });
|
|
||||||
console.log('Current allowance is', fromWei(allowance));
|
console.log('Current allowance is', fromWei(allowance));
|
||||||
if (toBN(allowance).lt(toBN(tokenAmount))) {
|
if (toBN(allowance).lt(toBN(tokenAmount))) {
|
||||||
console.log('Approving tokens for deposit');
|
console.log('Approving tokens for deposit');
|
||||||
@ -264,8 +283,10 @@ async function deposit({ currency, amount, commitmentNote }) {
|
|||||||
|
|
||||||
console.log('Submitting deposit transaction');
|
console.log('Submitting deposit transaction');
|
||||||
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, commitment, []).encodeABI());
|
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, commitment, []).encodeABI());
|
||||||
await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' });
|
await Promise.all([
|
||||||
await printERC20Balance({ address: senderAccount, name: 'Sender account' });
|
printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' }),
|
||||||
|
printERC20Balance({ address: senderAccount, name: 'Sender account' })
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!commitmentNote) {
|
if(!commitmentNote) {
|
||||||
@ -300,15 +321,20 @@ async function generateMerkleProof(deposit, currency, amount) {
|
|||||||
// Validate that our data is correct
|
// Validate that our data is correct
|
||||||
const root = tree.root();
|
const root = tree.root();
|
||||||
let isValidRoot, isSpent;
|
let isValidRoot, isSpent;
|
||||||
if (!isTestRPC && !multiCall) {
|
if (!isTestRPC && multiCall) {
|
||||||
const callContract = await useMultiCall([[tornadoContract._address, tornadoContract.methods.isKnownRoot(toHex(root)).encodeABI()], [tornadoContract._address, tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).encodeABI()]])
|
const callContract = await useMultiCall([
|
||||||
|
[tornadoContract._address, tornadoContract.methods.isKnownRoot(toHex(root)).encodeABI()],
|
||||||
|
[tornadoContract._address, tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).encodeABI()]
|
||||||
|
]);
|
||||||
isValidRoot = web3.eth.abi.decodeParameter('bool', callContract[0]);
|
isValidRoot = web3.eth.abi.decodeParameter('bool', callContract[0]);
|
||||||
isSpent = web3.eth.abi.decodeParameter('bool', callContract[1]);
|
isSpent = web3.eth.abi.decodeParameter('bool', callContract[1]);
|
||||||
} else {
|
} else {
|
||||||
isValidRoot = await tornadoContract.methods.isKnownRoot(toHex(root)).call();
|
[ isValidRoot, isSpent ] = await Promise.all([
|
||||||
isSpent = await tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).call();
|
tornadoContract.methods.isKnownRoot(toHex(root)).call(),
|
||||||
|
tornadoContract.methods.isSpent(toHex(deposit.nullifierHash)).call()
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
assert(isValidRoot === true, 'Merkle tree is corrupted');
|
assert(isValidRoot === true, 'Merkle tree is corrupted. Some events could be missing, please try again.');
|
||||||
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');
|
||||||
|
|
||||||
@ -346,6 +372,22 @@ async function generateProof({ deposit, currency, amount, recipient, relayerAddr
|
|||||||
pathIndices: pathIndices
|
pathIndices: pathIndices
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!groth16) {
|
||||||
|
const wasmMemory = isMobile ? 1000 : 2000;
|
||||||
|
// groth16 initialises a lot of Promises that will never be resolved, that's why we need to use process.exit to terminate the CLI
|
||||||
|
groth16 = await buildGroth16({ wasmInitialMemory: wasmMemory });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!circuit && !proving_key) {
|
||||||
|
if (inBrowser) {
|
||||||
|
circuit = await (await fetch('build/circuits/tornado.json')).json();
|
||||||
|
proving_key = await (await fetch('build/circuits/tornadoProvingKey.bin')).arrayBuffer();
|
||||||
|
} else {
|
||||||
|
circuit = require('./build/circuits/tornado.json');
|
||||||
|
proving_key = fs.readFileSync('build/circuits/tornadoProvingKey.bin').buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log('Generating SNARK proof');
|
console.log('Generating SNARK proof');
|
||||||
console.time('Proof time');
|
console.time('Proof time');
|
||||||
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key);
|
const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key);
|
||||||
@ -382,10 +424,13 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
|
|||||||
if (torPort) {
|
if (torPort) {
|
||||||
options = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } }
|
options = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } }
|
||||||
}
|
}
|
||||||
const relayerStatus = await axios.get(relayerURL + '/status', options);
|
const [ relayerStatus, globalNetId ] = await Promise.all([
|
||||||
|
axios.get(relayerURL + '/status', options),
|
||||||
|
web3.eth.net.getId()
|
||||||
|
]);
|
||||||
|
|
||||||
const { rewardAccount, netId, ethPrices, tornadoServiceFee } = 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 === globalNetId || netId === '*', 'This relay is for different network');
|
||||||
console.log('Relay address:', rewardAccount);
|
console.log('Relay address:', rewardAccount);
|
||||||
|
|
||||||
const gasPrice = await fetchGasPrice();
|
const gasPrice = await fetchGasPrice();
|
||||||
@ -427,6 +472,7 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, refu
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// using private key
|
// using private key
|
||||||
|
assert(senderAccount, 'Private Key should be supplied through .env file if you want to make withdrawal without relayers');
|
||||||
|
|
||||||
// check if the address of recepient matches with the account of provided private key from environment to prevent accidental use of deposit address for withdrawal transaction.
|
// check if the address of recepient matches with the account of provided private key from environment to prevent accidental use of deposit address for withdrawal transaction.
|
||||||
assert(recipient.toLowerCase() == senderAccount.toLowerCase(), 'Withdrawal recepient mismatches with the account of provided private key from environment file');
|
assert(recipient.toLowerCase() == senderAccount.toLowerCase(), 'Withdrawal recepient mismatches with the account of provided private key from environment file');
|
||||||
@ -459,15 +505,22 @@ async function send({ address, amount, tokenAddress }) {
|
|||||||
const erc20ContractJson = require('./build/contracts/ERC20Mock.json');
|
const erc20ContractJson = require('./build/contracts/ERC20Mock.json');
|
||||||
erc20 = new web3.eth.Contract(erc20ContractJson.abi, tokenAddress);
|
erc20 = new web3.eth.Contract(erc20ContractJson.abi, tokenAddress);
|
||||||
let tokenBalance, tokenDecimals, tokenSymbol;
|
let tokenBalance, tokenDecimals, tokenSymbol;
|
||||||
if (!isTestRPC && !multiCall) {
|
if (!isTestRPC && multiCall) {
|
||||||
const callToken = await useMultiCall([[tokenAddress, erc20.methods.balanceOf(senderAccount).encodeABI()], [tokenAddress, erc20.methods.decimals().encodeABI()], [tokenAddress, erc20.methods.symbol().encodeABI()]]);
|
const callToken = await useMultiCall([
|
||||||
|
[tokenAddress, erc20.methods.balanceOf(senderAccount).encodeABI()],
|
||||||
|
[tokenAddress, erc20.methods.decimals().encodeABI()],
|
||||||
|
[tokenAddress, erc20.methods.symbol().encodeABI()]
|
||||||
|
]);
|
||||||
tokenBalance = new BigNumber(callToken[0]);
|
tokenBalance = new BigNumber(callToken[0]);
|
||||||
tokenDecimals = parseInt(callToken[1]);
|
tokenDecimals = parseInt(callToken[1]);
|
||||||
tokenSymbol = web3.eth.abi.decodeParameter('string', callToken[2]);
|
tokenSymbol = web3.eth.abi.decodeParameter('string', callToken[2]);
|
||||||
} else {
|
} else {
|
||||||
tokenBalance = new BigNumber(await erc20.methods.balanceOf(senderAccount).call());
|
[ tokenBalance, tokenDecimals, tokenSymbol ] = await Promise.all([
|
||||||
tokenDecimals = await erc20.methods.decimals().call();
|
erc20.methods.balanceOf(senderAccount).call(),
|
||||||
tokenSymbol = await erc20.methods.symbol().call();
|
erc20.methods.decimals().call(),
|
||||||
|
erc20.methods.symbol().call()
|
||||||
|
]);
|
||||||
|
tokenBalance = new BigNumber(tokenBalance);
|
||||||
}
|
}
|
||||||
const toSend = new BigNumber(amount).times(BigNumber(10).pow(tokenDecimals));
|
const toSend = new BigNumber(amount).times(BigNumber(10).pow(tokenDecimals));
|
||||||
if (tokenBalance.lt(toSend)) {
|
if (tokenBalance.lt(toSend)) {
|
||||||
@ -478,7 +531,12 @@ async function send({ address, amount, tokenAddress }) {
|
|||||||
await generateTransaction(tokenAddress, encodeTransfer);
|
await generateTransaction(tokenAddress, encodeTransfer);
|
||||||
console.log('Sent', amount, tokenSymbol, 'to', address);
|
console.log('Sent', amount, tokenSymbol, 'to', address);
|
||||||
} else {
|
} else {
|
||||||
const balance = new BigNumber(await web3.eth.getBalance(senderAccount));
|
const fetchInfo = await Promise.all([
|
||||||
|
web3.eth.getBalance(senderAccount),
|
||||||
|
fetchGasPrice()
|
||||||
|
]);
|
||||||
|
const balance = new BigNumber(fetchInfo[0]);
|
||||||
|
const gasPrice = new BigNumber(fetchInfo[1]);
|
||||||
assert(balance.toNumber() !== 0, "You have 0 balance, can't send transaction");
|
assert(balance.toNumber() !== 0, "You have 0 balance, can't send transaction");
|
||||||
if (amount) {
|
if (amount) {
|
||||||
toSend = new BigNumber(amount).times(BigNumber(10).pow(18));
|
toSend = new BigNumber(amount).times(BigNumber(10).pow(18));
|
||||||
@ -488,10 +546,9 @@ async function send({ address, amount, tokenAddress }) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('Amount not defined, sending all available amounts');
|
console.log('Amount not defined, sending all available amounts');
|
||||||
const gasPrice = new BigNumber(await fetchGasPrice());
|
|
||||||
const gasLimit = new BigNumber(21000);
|
const gasLimit = new BigNumber(21000);
|
||||||
if (netId == 1) {
|
if (netId == 1) {
|
||||||
const priorityFee = new BigNumber(await gasPrices(3));
|
const priorityFee = new BigNumber(gasPrices(3));
|
||||||
toSend = balance.minus(gasLimit.times(gasPrice.plus(priorityFee)));
|
toSend = balance.minus(gasLimit.times(gasPrice.plus(priorityFee)));
|
||||||
} else {
|
} else {
|
||||||
toSend = balance.minus(gasLimit.times(gasPrice));
|
toSend = balance.minus(gasLimit.times(gasPrice));
|
||||||
@ -727,7 +784,7 @@ function calculateFee({ currency, gasPrice, amount, refund, ethPrices, relayerSe
|
|||||||
Math.floor(relayerServiceFee) === Number(relayerServiceFee) ? 0 : relayerServiceFee.toString().split('.')[1].length;
|
Math.floor(relayerServiceFee) === Number(relayerServiceFee) ? 0 : 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(Math.floor(relayerServiceFee * roundDecimal))).div(toBN(roundDecimal * 100));
|
||||||
const expense = toBN(gasPrice).mul(toBN(5e5));
|
const expense = toBN(gasPrice).mul(toBN(5e5));
|
||||||
let desiredFee;
|
let desiredFee;
|
||||||
switch (currency) {
|
switch (currency) {
|
||||||
@ -772,40 +829,28 @@ function waitForTxReceipt({ txHash, attempts = 60, delay = 1000 }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initJson(file) {
|
function initJson(file) {
|
||||||
return new Promise((resolve, reject) => {
|
if (fs.existsSync(file)) {
|
||||||
fs.readFile(file, 'utf8', (error, data) => {
|
return JSON.parse(fs.readFileSync(file, { encoding: 'utf8' }));
|
||||||
if (error) {
|
|
||||||
resolve([]);
|
|
||||||
}
|
}
|
||||||
try {
|
return [];
|
||||||
resolve(JSON.parse(data));
|
|
||||||
} catch (error) {
|
|
||||||
resolve([]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadCachedEvents({ type, currency, amount }) {
|
function loadCachedEvents({ type, currency, amount }) {
|
||||||
try {
|
const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`;
|
||||||
const module = require(`./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`);
|
const events = initJson(fileName);
|
||||||
|
|
||||||
if (module) {
|
|
||||||
const events = module;
|
|
||||||
|
|
||||||
|
if (events.length > 0) {
|
||||||
return {
|
return {
|
||||||
events,
|
events,
|
||||||
lastBlock: events[events.length - 1].blockNumber
|
lastBlock: events[events.length - 1].blockNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
console.log("Error fetching cached files, syncing from block", deployedBlockNumber);
|
console.log("Error fetching cached files, syncing from block", deployedBlockNumber);
|
||||||
return {
|
return {
|
||||||
events: [],
|
events: [],
|
||||||
lastBlock: deployedBlockNumber,
|
lastBlock: deployedBlockNumber,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchEvents({ type, currency, amount }) {
|
async function fetchEvents({ type, currency, amount }) {
|
||||||
if (type === "withdraw") {
|
if (type === "withdraw") {
|
||||||
@ -813,7 +858,7 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cachedEvents = loadCachedEvents({ type, currency, amount });
|
const cachedEvents = loadCachedEvents({ type, currency, amount });
|
||||||
const startBlock = cachedEvents.lastBlock + 1;
|
let startBlock = cachedEvents.lastBlock + 1;
|
||||||
|
|
||||||
console.log("Loaded cached",amount,currency.toUpperCase(),type,"events for",startBlock,"block");
|
console.log("Loaded cached",amount,currency.toUpperCase(),type,"events for",startBlock,"block");
|
||||||
console.log("Fetching",amount,currency.toUpperCase(),type,"events for",netName,"network");
|
console.log("Fetching",amount,currency.toUpperCase(),type,"events for",netName,"network");
|
||||||
@ -853,14 +898,6 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapLatestEvents() {
|
|
||||||
if (type === "deposit"){
|
|
||||||
mapDepositEvents();
|
|
||||||
} else {
|
|
||||||
mapWithdrawEvents();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchWeb3Events(i) {
|
async function fetchWeb3Events(i) {
|
||||||
let j;
|
let j;
|
||||||
if (i + chunks - 1 > targetBlock) {
|
if (i + chunks - 1 > targetBlock) {
|
||||||
@ -868,10 +905,18 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
} else {
|
} else {
|
||||||
j = i + chunks - 1;
|
j = i + chunks - 1;
|
||||||
}
|
}
|
||||||
await tornadoContract.getPastEvents(capitalizeFirstLetter(type), {
|
|
||||||
|
try {
|
||||||
|
const getPastEvents = await tornadoContract.getPastEvents(capitalizeFirstLetter(type), {
|
||||||
fromBlock: i,
|
fromBlock: i,
|
||||||
toBlock: j,
|
toBlock: j,
|
||||||
}).then(r => { fetchedEvents = fetchedEvents.concat(r); console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", j) }, err => { console.error(i + " failed fetching", type, "events from node", err); process.exit(1); }).catch(console.log);
|
});
|
||||||
|
fetchedEvents = fetchedEvents.concat(getPastEvents);
|
||||||
|
console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", j);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(i + " failed fetching", type, "events from node", e);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (type === "deposit"){
|
if (type === "deposit"){
|
||||||
mapDepositEvents();
|
mapDepositEvents();
|
||||||
@ -880,12 +925,12 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateCache() {
|
function updateCache() {
|
||||||
try {
|
try {
|
||||||
const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`;
|
const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`;
|
||||||
const localEvents = await initJson(fileName);
|
const localEvents = initJson(fileName);
|
||||||
const events = localEvents.concat(fetchedEvents);
|
const events = localEvents.concat(fetchedEvents);
|
||||||
await fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8');
|
fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error('Writing cache file failed:',error);
|
throw new Error('Writing cache file failed:',error);
|
||||||
}
|
}
|
||||||
@ -894,6 +939,7 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
await updateCache();
|
await updateCache();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
throw new Error("Error while updating cache");
|
throw new Error("Error while updating cache");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
@ -905,7 +951,7 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
options = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } };
|
options = { httpsAgent: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort), headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0' } };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queryLatestTimestamp() {
|
async function queryLatestBlockNumber() {
|
||||||
try {
|
try {
|
||||||
const variables = {
|
const variables = {
|
||||||
currency: currency.toString(),
|
currency: currency.toString(),
|
||||||
@ -915,8 +961,8 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
const query = {
|
const query = {
|
||||||
query: `
|
query: `
|
||||||
query($currency: String, $amount: String){
|
query($currency: String, $amount: String){
|
||||||
deposits(first: 1, orderBy: timestamp, orderDirection: desc, where: {currency: $currency, amount: $amount}) {
|
deposits(first: 1, orderBy: blockNumber, orderDirection: desc, where: {currency: $currency, amount: $amount}) {
|
||||||
timestamp
|
blockNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@ -924,14 +970,14 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
}
|
}
|
||||||
const querySubgraph = await axios.post(subgraph, query, options);
|
const querySubgraph = await axios.post(subgraph, query, options);
|
||||||
const queryResult = querySubgraph.data.data.deposits;
|
const queryResult = querySubgraph.data.data.deposits;
|
||||||
const result = queryResult[0].timestamp;
|
const result = queryResult[0].blockNumber;
|
||||||
return Number(result);
|
return Number(result);
|
||||||
} else {
|
} else {
|
||||||
const query = {
|
const query = {
|
||||||
query: `
|
query: `
|
||||||
query($currency: String, $amount: String){
|
query($currency: String, $amount: String){
|
||||||
withdrawals(first: 1, orderBy: timestamp, orderDirection: desc, where: {currency: $currency, amount: $amount}) {
|
withdrawals(first: 1, orderBy: blockNumber, orderDirection: desc, where: {currency: $currency, amount: $amount}) {
|
||||||
timestamp
|
blockNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
@ -939,7 +985,7 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
}
|
}
|
||||||
const querySubgraph = await axios.post(subgraph, query, options);
|
const querySubgraph = await axios.post(subgraph, query, options);
|
||||||
const queryResult = querySubgraph.data.data.withdrawals;
|
const queryResult = querySubgraph.data.data.withdrawals;
|
||||||
const result = queryResult[0].timestamp;
|
const result = queryResult[0].blockNumber;
|
||||||
return Number(result);
|
return Number(result);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -947,18 +993,18 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function queryFromGraph(timestamp) {
|
async function queryFromGraph(blockNumber) {
|
||||||
try {
|
try {
|
||||||
const variables = {
|
const variables = {
|
||||||
currency: currency.toString(),
|
currency: currency.toString(),
|
||||||
amount: amount.toString(),
|
amount: amount.toString(),
|
||||||
timestamp: timestamp
|
blockNumber: blockNumber
|
||||||
}
|
}
|
||||||
if (type === "deposit") {
|
if (type === "deposit") {
|
||||||
const query = {
|
const query = {
|
||||||
query: `
|
query: `
|
||||||
query($currency: String, $amount: String, $timestamp: Int){
|
query($currency: String, $amount: String, $blockNumber: Int){
|
||||||
deposits(orderBy: timestamp, first: 1000, where: {currency: $currency, amount: $amount, timestamp_gt: $timestamp}) {
|
deposits(orderBy: blockNumber, first: 1000, where: {currency: $currency, amount: $amount, blockNumber_gte: $blockNumber}) {
|
||||||
blockNumber
|
blockNumber
|
||||||
transactionHash
|
transactionHash
|
||||||
commitment
|
commitment
|
||||||
@ -984,8 +1030,8 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
} else {
|
} else {
|
||||||
const query = {
|
const query = {
|
||||||
query: `
|
query: `
|
||||||
query($currency: String, $amount: String, $timestamp: Int){
|
query($currency: String, $amount: String, $blockNumber: Int){
|
||||||
withdrawals(orderBy: timestamp, first: 1000, where: {currency: $currency, amount: $amount, timestamp_gt: $timestamp}) {
|
withdrawals(orderBy: blockNumber, first: 1000, where: {currency: $currency, amount: $amount, blockNumber_gte: $blockNumber}) {
|
||||||
blockNumber
|
blockNumber
|
||||||
transactionHash
|
transactionHash
|
||||||
nullifier
|
nullifier
|
||||||
@ -1014,12 +1060,28 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateCache(fetchedEvents) {
|
function updateCache(fetchedEvents) {
|
||||||
try {
|
try {
|
||||||
|
let events = [];
|
||||||
const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`;
|
const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`;
|
||||||
const localEvents = await initJson(fileName);
|
const localEvents = initJson(fileName);
|
||||||
const events = localEvents.concat(fetchedEvents);
|
const totalEvents = localEvents.concat(fetchedEvents);
|
||||||
await fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8');
|
if (type === "deposit") {
|
||||||
|
const commit = new Set();
|
||||||
|
events = totalEvents.filter((r) => {
|
||||||
|
const notSameCommit = commit.has(r.commitment);
|
||||||
|
commit.add(r.commitment);
|
||||||
|
return !notSameCommit;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const nullifi = new Set();
|
||||||
|
events = totalEvents.filter((r) => {
|
||||||
|
const notSameNull = nullifi.has(r.nullifierHash);
|
||||||
|
nullifi.add(r.nullifierHash);
|
||||||
|
return !notSameNull;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error('Writing cache file failed:',error);
|
throw new Error('Writing cache file failed:',error);
|
||||||
}
|
}
|
||||||
@ -1027,39 +1089,43 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
|
|
||||||
async function fetchGraphEvents() {
|
async function fetchGraphEvents() {
|
||||||
console.log("Querying latest events from TheGraph");
|
console.log("Querying latest events from TheGraph");
|
||||||
const latestTimestamp = await queryLatestTimestamp();
|
const latestBlockNumber = await queryLatestBlockNumber();
|
||||||
if (latestTimestamp) {
|
if (latestBlockNumber) {
|
||||||
const getCachedBlock = await web3.eth.getBlock(startBlock);
|
for (let i = startBlock; i < latestBlockNumber;) {
|
||||||
const cachedTimestamp = getCachedBlock.timestamp;
|
|
||||||
for (let i = cachedTimestamp; i < latestTimestamp;) {
|
|
||||||
const result = await queryFromGraph(i);
|
const result = await queryFromGraph(i);
|
||||||
if (Object.keys(result).length === 0) {
|
if (Object.keys(result).length < 20) {
|
||||||
i = latestTimestamp;
|
const block = new Set();
|
||||||
|
const filteredResult = result.filter((r) => {
|
||||||
|
const notSameBlock = block.has(r.blockNumber);
|
||||||
|
block.add(r.blockNumber);
|
||||||
|
return !notSameBlock;
|
||||||
|
});
|
||||||
|
if (Object.keys(filteredResult).length === 1) {
|
||||||
|
i = latestBlockNumber;
|
||||||
} else {
|
} else {
|
||||||
if (type === "deposit") {
|
|
||||||
const resultBlock = result[result.length - 1].blockNumber;
|
const resultBlock = result[result.length - 1].blockNumber;
|
||||||
const resultTimestamp = result[result.length - 1].timestamp;
|
|
||||||
await updateCache(result);
|
await updateCache(result);
|
||||||
i = resultTimestamp;
|
i = resultBlock;
|
||||||
console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock));
|
console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const resultBlock = result[result.length - 1].blockNumber;
|
const resultBlock = result[result.length - 1].blockNumber;
|
||||||
const getResultBlock = await web3.eth.getBlock(resultBlock);
|
|
||||||
const resultTimestamp = getResultBlock.timestamp;
|
|
||||||
await updateCache(result);
|
await updateCache(result);
|
||||||
i = resultTimestamp;
|
i = resultBlock;
|
||||||
console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock));
|
console.log("Fetched", amount, currency.toUpperCase(), type, "events to block:", Number(resultBlock));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return latestBlockNumber;
|
||||||
} else {
|
} else {
|
||||||
console.log("Fallback to web3 events");
|
console.log("Fallback to web3 events");
|
||||||
await syncEvents();
|
return startBlock - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await fetchGraphEvents();
|
await fetchGraphEvents();
|
||||||
|
startBlock = loadCachedEvents({ type, currency, amount }).lastBlock + 1;
|
||||||
|
await syncEvents();
|
||||||
}
|
}
|
||||||
if (!privateRpc && !subgraph && !isTestRPC) {
|
if (!privateRpc && subgraph && !isTestRPC) {
|
||||||
await syncGraphEvents();
|
await syncGraphEvents();
|
||||||
} else {
|
} else {
|
||||||
await syncEvents();
|
await syncEvents();
|
||||||
@ -1069,7 +1135,7 @@ async function fetchEvents({ type, currency, amount }) {
|
|||||||
const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`;
|
const fileName = `./cache/${netName.toLowerCase()}/${type}s_${currency}_${amount}.json`;
|
||||||
const updatedEvents = await initJson(fileName);
|
const updatedEvents = await initJson(fileName);
|
||||||
const updatedBlock = updatedEvents[updatedEvents.length - 1].blockNumber;
|
const updatedBlock = updatedEvents[updatedEvents.length - 1].blockNumber;
|
||||||
console.log("Cache updated for Tornado",type,amount,currency,"instance to block",updatedBlock,"successfully");
|
console.log("Cache updated for Tornado", type, amount, currency.toUpperCase(), "instance to block", updatedBlock);
|
||||||
console.log(`Total ${type}s:`, updatedEvents.length);
|
console.log(`Total ${type}s:`, updatedEvents.length);
|
||||||
return updatedEvents;
|
return updatedEvents;
|
||||||
}
|
}
|
||||||
@ -1128,7 +1194,7 @@ function parseInvoice(invoiceString) {
|
|||||||
async function loadDepositData({ amount, currency, deposit }) {
|
async function loadDepositData({ amount, currency, deposit }) {
|
||||||
try {
|
try {
|
||||||
const cachedEvents = await fetchEvents({ type: 'deposit', currency, amount });
|
const cachedEvents = await fetchEvents({ type: 'deposit', currency, amount });
|
||||||
const eventWhenHappened = await cachedEvents.filter(function (event) {
|
const eventWhenHappened = cachedEvents.filter(function (event) {
|
||||||
return event.commitment === deposit.commitmentHex;
|
return event.commitment === deposit.commitmentHex;
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
@ -1138,8 +1204,11 @@ async function loadDepositData({ amount, currency, deposit }) {
|
|||||||
|
|
||||||
const timestamp = eventWhenHappened.timestamp;
|
const timestamp = eventWhenHappened.timestamp;
|
||||||
const txHash = eventWhenHappened.transactionHash;
|
const txHash = eventWhenHappened.transactionHash;
|
||||||
const isSpent = await tornadoContract.methods.isSpent(deposit.nullifierHex).call();
|
|
||||||
const receipt = await web3.eth.getTransactionReceipt(txHash);
|
const [ isSpent, receipt ] = await Promise.all([
|
||||||
|
tornadoContract.methods.isSpent(deposit.nullifierHex).call(),
|
||||||
|
web3.eth.getTransactionReceipt(txHash)
|
||||||
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timestamp,
|
timestamp,
|
||||||
@ -1192,8 +1261,6 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', balanceC
|
|||||||
});
|
});
|
||||||
contractJson = await (await fetch('build/contracts/TornadoProxy.abi.json')).json();
|
contractJson = await (await fetch('build/contracts/TornadoProxy.abi.json')).json();
|
||||||
instanceJson = await (await fetch('build/contracts/Instance.abi.json')).json();
|
instanceJson = await (await fetch('build/contracts/Instance.abi.json')).json();
|
||||||
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;
|
||||||
@ -1239,8 +1306,6 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', balanceC
|
|||||||
}
|
}
|
||||||
contractJson = require('./build/contracts/TornadoProxy.abi.json');
|
contractJson = require('./build/contracts/TornadoProxy.abi.json');
|
||||||
instanceJson = require('./build/contracts/Instance.abi.json');
|
instanceJson = require('./build/contracts/Instance.abi.json');
|
||||||
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;
|
||||||
@ -1261,8 +1326,6 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', balanceC
|
|||||||
erc20ContractJson = require('./build/contracts/ERC20Mock.json');
|
erc20ContractJson = require('./build/contracts/ERC20Mock.json');
|
||||||
erc20tornadoJson = require('./build/contracts/ERC20Tornado.json');
|
erc20tornadoJson = require('./build/contracts/ERC20Tornado.json');
|
||||||
}
|
}
|
||||||
// groth16 initialises a lot of Promises that will never be resolved, that's why we need to use process.exit to terminate the CLI
|
|
||||||
groth16 = await buildGroth16();
|
|
||||||
netId = await web3.eth.net.getId();
|
netId = await web3.eth.net.getId();
|
||||||
netName = getCurrentNetworkName();
|
netName = getCurrentNetworkName();
|
||||||
netSymbol = getCurrentNetworkSymbol();
|
netSymbol = getCurrentNetworkSymbol();
|
||||||
@ -1330,7 +1393,7 @@ async function main() {
|
|||||||
.option('-r, --rpc <URL>', 'The RPC that CLI should interact with', 'http://localhost:8545')
|
.option('-r, --rpc <URL>', 'The RPC that CLI should interact with', 'http://localhost:8545')
|
||||||
.option('-R, --relayer <URL>', 'Withdraw via relayer')
|
.option('-R, --relayer <URL>', 'Withdraw via relayer')
|
||||||
.option('-T, --tor <PORT>', 'Optional tor port')
|
.option('-T, --tor <PORT>', 'Optional tor port')
|
||||||
.option('-L, --local', 'Local Node - Does not submit signed transaction to the node')
|
.option('-L, --localtx', 'Local Node - Does not submit signed transaction to the node')
|
||||||
.option('-o, --onlyrpc', 'Only rpc mode - Does not enable thegraph api nor remote ip detection');
|
.option('-o, --onlyrpc', 'Only rpc mode - Does not enable thegraph api nor remote ip detection');
|
||||||
program
|
program
|
||||||
.command('createNote <currency> <amount> <chainId>')
|
.command('createNote <currency> <amount> <chainId>')
|
||||||
@ -1347,12 +1410,29 @@ async function main() {
|
|||||||
'Submit a deposit of invoice from default eth account and return the resulting note.'
|
'Submit a deposit of invoice from default eth account and return the resulting note.'
|
||||||
)
|
)
|
||||||
.action(async (invoice) => {
|
.action(async (invoice) => {
|
||||||
if (program.onlyrpc) {
|
let rpc, localtx;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.LOCALTX) {
|
||||||
|
localtx = process.env.LOCALTX;
|
||||||
|
} else {
|
||||||
|
localtx = program.localtx;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
torPort = program.tor;
|
|
||||||
const { currency, amount, netId, commitmentNote } = parseInvoice(invoice);
|
const { currency, amount, netId, commitmentNote } = parseInvoice(invoice);
|
||||||
await init({ rpc: program.rpc, currency, amount, localMode: program.local });
|
await init({ rpc: rpc, currency, amount, localMode: localtx });
|
||||||
console.log("Creating", currency.toUpperCase(), amount, "deposit for", netName, "Tornado Cash Instance");
|
console.log("Creating", currency.toUpperCase(), amount, "deposit for", netName, "Tornado Cash Instance");
|
||||||
await deposit({ currency, amount, commitmentNote });
|
await deposit({ currency, amount, commitmentNote });
|
||||||
});
|
});
|
||||||
@ -1362,12 +1442,29 @@ async function main() {
|
|||||||
'Submit a deposit of specified currency and amount from default eth account and return the resulting note. The currency is one of (ETH|DAI|cDAI|USDC|cUSDC|USDT). The amount depends on currency, see config.js file or visit https://tornado.cash.'
|
'Submit a deposit of specified currency and amount from default eth account and return the resulting note. The currency is one of (ETH|DAI|cDAI|USDC|cUSDC|USDT). The amount depends on currency, see config.js file or visit https://tornado.cash.'
|
||||||
)
|
)
|
||||||
.action(async (currency, amount) => {
|
.action(async (currency, amount) => {
|
||||||
if (program.onlyrpc) {
|
let rpc, localtx;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.LOCALTX) {
|
||||||
|
localtx = process.env.LOCALTX;
|
||||||
|
} else {
|
||||||
|
localtx = program.localtx;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
currency = currency.toLowerCase();
|
currency = currency.toLowerCase();
|
||||||
torPort = program.tor;
|
await init({ rpc: rpc, currency, amount, localMode: localtx });
|
||||||
await init({ rpc: program.rpc, currency, amount, localMode: program.local });
|
|
||||||
await deposit({ currency, amount });
|
await deposit({ currency, amount });
|
||||||
});
|
});
|
||||||
program
|
program
|
||||||
@ -1376,30 +1473,64 @@ async function main() {
|
|||||||
'Withdraw a note to a recipient account using relayer or specified private key. You can exchange some of your deposit`s tokens to ETH during the withdrawal by specifing ETH_purchase (e.g. 0.01) to pay for gas in future transactions. Also see the --relayer option.'
|
'Withdraw a note to a recipient account using relayer or specified private key. You can exchange some of your deposit`s tokens to ETH during the withdrawal by specifing ETH_purchase (e.g. 0.01) to pay for gas in future transactions. Also see the --relayer option.'
|
||||||
)
|
)
|
||||||
.action(async (noteString, recipient, refund) => {
|
.action(async (noteString, recipient, refund) => {
|
||||||
if (program.onlyrpc) {
|
let rpc, localtx, relayer;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.LOCALTX) {
|
||||||
|
localtx = process.env.LOCALTX;
|
||||||
|
} else {
|
||||||
|
localtx = program.localtx;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
|
if (process.env.RELAYER) {
|
||||||
|
relayer = process.env.RELAYER;
|
||||||
|
} else {
|
||||||
|
relayer = program.relayer;
|
||||||
|
}
|
||||||
const { currency, amount, netId, deposit } = parseNote(noteString);
|
const { currency, amount, netId, deposit } = parseNote(noteString);
|
||||||
torPort = program.tor;
|
await init({ rpc: rpc, noteNetId: netId, currency, amount, localMode: localtx });
|
||||||
await init({ rpc: program.rpc, noteNetId: netId, currency, amount, localMode: program.local });
|
|
||||||
await withdraw({
|
await withdraw({
|
||||||
deposit,
|
deposit,
|
||||||
currency,
|
currency,
|
||||||
amount,
|
amount,
|
||||||
recipient,
|
recipient,
|
||||||
refund,
|
refund,
|
||||||
relayerURL: program.relayer
|
relayerURL: relayer
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
program
|
program
|
||||||
.command('balance [address] [token_address]')
|
.command('balance [address] [token_address]')
|
||||||
.description('Check ETH and ERC20 balance')
|
.description('Check ETH and ERC20 balance')
|
||||||
.action(async (address, tokenAddress) => {
|
.action(async (address, tokenAddress) => {
|
||||||
if (program.onlyrpc) {
|
let rpc;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
torPort = program.tor;
|
await init({ rpc: rpc, balanceCheck: true });
|
||||||
await init({ rpc: program.rpc, balanceCheck: true });
|
|
||||||
if (!address && senderAccount) {
|
if (!address && senderAccount) {
|
||||||
console.log("Using address", senderAccount, "from private key");
|
console.log("Using address", senderAccount, "from private key");
|
||||||
address = senderAccount;
|
address = senderAccount;
|
||||||
@ -1413,22 +1544,51 @@ async function main() {
|
|||||||
.command('send <address> [amount] [token_address]')
|
.command('send <address> [amount] [token_address]')
|
||||||
.description('Send ETH or ERC to address')
|
.description('Send ETH or ERC to address')
|
||||||
.action(async (address, amount, tokenAddress) => {
|
.action(async (address, amount, tokenAddress) => {
|
||||||
if (program.onlyrpc) {
|
let rpc, localtx;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.LOCALTX) {
|
||||||
|
localtx = process.env.LOCALTX;
|
||||||
|
} else {
|
||||||
|
localtx = program.localtx;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
torPort = program.tor;
|
await init({ rpc: rpc, balanceCheck: true, localMode: localtx });
|
||||||
await init({ rpc: program.rpc, balanceCheck: true, localMode: program.local });
|
|
||||||
await send({ address, amount, tokenAddress });
|
await send({ address, amount, tokenAddress });
|
||||||
});
|
});
|
||||||
program
|
program
|
||||||
.command('broadcast <signedTX>')
|
.command('broadcast <signedTX>')
|
||||||
.description('Submit signed TX to the remote node')
|
.description('Submit signed TX to the remote node')
|
||||||
.action(async (signedTX) => {
|
.action(async (signedTX) => {
|
||||||
if (program.onlyrpc) {
|
let rpc;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
torPort = program.tor;
|
await init({ rpc: rpc, balanceCheck: true });
|
||||||
await init({ rpc: program.rpc, balanceCheck: true });
|
|
||||||
await submitTransaction(signedTX);
|
await submitTransaction(signedTX);
|
||||||
});
|
});
|
||||||
program
|
program
|
||||||
@ -1437,12 +1597,24 @@ async function main() {
|
|||||||
'Shows the deposit and withdrawal of the provided note. This might be necessary to show the origin of assets held in your withdrawal address.'
|
'Shows the deposit and withdrawal of the provided note. This might be necessary to show the origin of assets held in your withdrawal address.'
|
||||||
)
|
)
|
||||||
.action(async (noteString) => {
|
.action(async (noteString) => {
|
||||||
if (program.onlyrpc) {
|
let rpc;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
const { currency, amount, netId, deposit } = parseNote(noteString);
|
const { currency, amount, netId, deposit } = parseNote(noteString);
|
||||||
torPort = program.tor;
|
await init({ rpc: rpc, noteNetId: netId, currency, amount });
|
||||||
await init({ rpc: program.rpc, noteNetId: netId, currency, amount });
|
|
||||||
const depositInfo = await loadDepositData({ amount, currency, deposit });
|
const depositInfo = await loadDepositData({ amount, currency, deposit });
|
||||||
const depositDate = new Date(depositInfo.timestamp * 1000);
|
const depositDate = new Date(depositInfo.timestamp * 1000);
|
||||||
console.log('\n=============Deposit=================');
|
console.log('\n=============Deposit=================');
|
||||||
@ -1475,13 +1647,25 @@ async function main() {
|
|||||||
'Sync the local cache file of deposit / withdrawal events for specific currency.'
|
'Sync the local cache file of deposit / withdrawal events for specific currency.'
|
||||||
)
|
)
|
||||||
.action(async (type, currency, amount) => {
|
.action(async (type, currency, amount) => {
|
||||||
if (program.onlyrpc) {
|
let rpc;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
privateRpc = true;
|
privateRpc = true;
|
||||||
}
|
}
|
||||||
console.log("Starting event sync command");
|
console.log("Starting event sync command");
|
||||||
currency = currency.toLowerCase();
|
currency = currency.toLowerCase();
|
||||||
torPort = program.tor;
|
await init({ rpc: rpc, type, currency, amount });
|
||||||
await init({ rpc: program.rpc, type, currency, amount });
|
|
||||||
const cachedEvents = await fetchEvents({ type, currency, amount });
|
const cachedEvents = await fetchEvents({ type, currency, amount });
|
||||||
console.log("Synced event for", type, amount, currency.toUpperCase(), netName, "Tornado instance to block", cachedEvents[cachedEvents.length - 1].blockNumber);
|
console.log("Synced event for", type, amount, currency.toUpperCase(), netName, "Tornado instance to block", cachedEvents[cachedEvents.length - 1].blockNumber);
|
||||||
});
|
});
|
||||||
@ -1493,7 +1677,28 @@ async function main() {
|
|||||||
console.log('Start performing ETH deposit-withdraw test');
|
console.log('Start performing ETH deposit-withdraw test');
|
||||||
let currency = 'eth';
|
let currency = 'eth';
|
||||||
let amount = '0.1';
|
let amount = '0.1';
|
||||||
await init({ rpc: program.rpc, currency, amount });
|
let rpc, relayer;
|
||||||
|
if (process.env.RPC) {
|
||||||
|
rpc = process.env.RPC;
|
||||||
|
} else {
|
||||||
|
rpc = program.rpc;
|
||||||
|
}
|
||||||
|
if (process.env.TOR) {
|
||||||
|
torPort = process.env.TOR;
|
||||||
|
} else if (program.tor) {
|
||||||
|
torPort = program.tor;
|
||||||
|
}
|
||||||
|
if (process.env.ONLYRPC) {
|
||||||
|
privateRpc = true;
|
||||||
|
} else if (program.onlyrpc) {
|
||||||
|
privateRpc = true;
|
||||||
|
}
|
||||||
|
if (process.env.RELAYER) {
|
||||||
|
relayer = process.env.RELAYER;
|
||||||
|
} else {
|
||||||
|
relayer = program.relayer;
|
||||||
|
}
|
||||||
|
await init({ rpc: rpc, currency, amount });
|
||||||
let noteString = await deposit({ currency, amount });
|
let noteString = await deposit({ currency, amount });
|
||||||
let parsedNote = parseNote(noteString);
|
let parsedNote = parseNote(noteString);
|
||||||
await withdraw({
|
await withdraw({
|
||||||
@ -1501,13 +1706,13 @@ async function main() {
|
|||||||
currency,
|
currency,
|
||||||
amount,
|
amount,
|
||||||
recipient: senderAccount,
|
recipient: senderAccount,
|
||||||
relayerURL: program.relayer
|
relayerURL: relayer
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('\nStart performing DAI deposit-withdraw test');
|
console.log('\nStart performing DAI deposit-withdraw test');
|
||||||
currency = 'dai';
|
currency = 'dai';
|
||||||
amount = '100';
|
amount = '100';
|
||||||
await init({ rpc: program.rpc, currency, amount });
|
await init({ rpc: rpc, currency, amount });
|
||||||
noteString = await deposit({ currency, amount });
|
noteString = await deposit({ currency, amount });
|
||||||
parsedNote = parseNote(noteString);
|
parsedNote = parseNote(noteString);
|
||||||
await withdraw({
|
await withdraw({
|
||||||
@ -1516,7 +1721,7 @@ async function main() {
|
|||||||
amount,
|
amount,
|
||||||
recipient: senderAccount,
|
recipient: senderAccount,
|
||||||
refund: '0.02',
|
refund: '0.02',
|
||||||
relayerURL: program.relayer
|
relayerURL: relayer
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
|
Loading…
Reference in New Issue
Block a user