mirror of
https://github.com/tornadocash/tornado-cli.git
synced 2025-07-30 01:38:38 -04:00
new feature - invoice
This commit is contained in:
parent
863474c203
commit
bbb41f1a8c
2 changed files with 306 additions and 154 deletions
26
README.md
26
README.md
|
@ -57,6 +57,32 @@ Transaction mined in block 17036120
|
||||||
Done
|
Done
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### using invoice
|
||||||
|

|
||||||
|
|
||||||
|
```bash
|
||||||
|
node cli.js invoice ETH 0.1 --rpc https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161
|
||||||
|
Connecting to remote node
|
||||||
|
Your remote IP address is xx.xx.xx.xx from xx.
|
||||||
|
Your note: tornado-eth-0.1-5-0x1d9771a7b9f8b6c03d33116208ce8db1aa559d33e65d22dd2ff78375fc6b635f930536d2432b4bde0178c72cfc79d6b27023c5d9de60985f186b34c18c00
|
||||||
|
Your invoice: tornadoInvoice-eth-0.1-5-0x1b680c7dda0c2dd1b85f0fe126d49b16ed594b3cd6d5114db5f4593877a6b84f
|
||||||
|
Backed up deposit note as ./backup-tornado-eth-0.1-5-0x1d9771a7.txt
|
||||||
|
Backed up invoice as ./backup-tornadoInvoice-eth-0.1-5-0x1b680c7d.txt
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
node cli.js payInvoice tornadoInvoice-eth-0.1-5-0x1b680c7dda0c2dd1b85f0fe126d49b16ed594b3cd6d5114db5f4593877a6b84f --rpc https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161
|
||||||
|
Connecting to remote node
|
||||||
|
Your remote IP address is xx.xx.xx.xx from xx.
|
||||||
|
Tornado contract balance is 823.6 ETH
|
||||||
|
Sender account balance is 0.79544229 ETH
|
||||||
|
Submitting deposit transaction
|
||||||
|
Submitting transaction to the remote node
|
||||||
|
View transaction on block explorer https://goerli.etherscan.io/tx/0x6ded443caed8d6f2666841149532c64bee149a9a8e1070ed4c91a12dd1837747
|
||||||
|
Tornado contract balance is 823.7 ETH
|
||||||
|
Sender account balance is 0.694488819 ETH
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### List of public rpc & relayers for withdrawal
|
### List of public rpc & relayers for withdrawal
|
||||||
|
|
||||||
Infura API key fetched from https://rpc.info (Same one with Metamask)
|
Infura API key fetched from https://rpc.info (Same one with Metamask)
|
||||||
|
|
242
cli.js
242
cli.js
|
@ -43,7 +43,7 @@ function toHex(number, length = 32) {
|
||||||
/** Display ETH account balance */
|
/** Display ETH account balance */
|
||||||
async function printETHBalance({ address, name }) {
|
async function printETHBalance({ address, name }) {
|
||||||
const checkBalance = new BigNumber(await web3.eth.getBalance(address));
|
const checkBalance = new BigNumber(await web3.eth.getBalance(address));
|
||||||
console.log(`${name} balance is`, checkBalance.div(BigNumber(10).pow(18)).toString(),`${netSymbol}`)
|
console.log(`${name} balance is`, checkBalance.div(BigNumber(10).pow(18)).toString(), `${netSymbol}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Display ERC20 account balance */
|
/** Display ERC20 account balance */
|
||||||
|
@ -54,7 +54,7 @@ async function printERC20Balance({ address, name, tokenAddress }) {
|
||||||
const tokenDecimals = await erc20.methods.decimals().call()
|
const tokenDecimals = await erc20.methods.decimals().call()
|
||||||
const tokenName = await erc20.methods.name().call()
|
const tokenName = await erc20.methods.name().call()
|
||||||
const tokenSymbol = await erc20.methods.symbol().call()
|
const tokenSymbol = await erc20.methods.symbol().call()
|
||||||
console.log(`${name}`,tokenName,`Token Balance is`,tokenBalance.div(BigNumber(10).pow(tokenDecimals)).toString(),tokenSymbol)
|
console.log(`${name}`, tokenName, `Token Balance is`, tokenBalance.div(BigNumber(10).pow(tokenDecimals)).toString(), tokenSymbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitTransaction(signedTX) {
|
async function submitTransaction(signedTX) {
|
||||||
|
@ -75,11 +75,11 @@ async function generateTransaction(to, encodedData, value = 0) {
|
||||||
|
|
||||||
async function estimateGas() {
|
async function estimateGas() {
|
||||||
const fetchedGas = await web3.eth.estimateGas({
|
const fetchedGas = await web3.eth.estimateGas({
|
||||||
from : senderAccount,
|
from: senderAccount,
|
||||||
to : to,
|
to: to,
|
||||||
value : value,
|
value: value,
|
||||||
nonce : nonce,
|
nonce: nonce,
|
||||||
data : encodedData
|
data: encodedData
|
||||||
})
|
})
|
||||||
const bumped = Math.floor(fetchedGas * 1.3)
|
const bumped = Math.floor(fetchedGas * 1.3)
|
||||||
return web3.utils.toHex(bumped)
|
return web3.utils.toHex(bumped)
|
||||||
|
@ -94,32 +94,32 @@ async function generateTransaction(to, encodedData, value = 0) {
|
||||||
// Generate EIP-1559 transaction
|
// Generate EIP-1559 transaction
|
||||||
if (netId == 1) {
|
if (netId == 1) {
|
||||||
return {
|
return {
|
||||||
to : to,
|
to: to,
|
||||||
value : value,
|
value: value,
|
||||||
nonce : nonce,
|
nonce: nonce,
|
||||||
maxFeePerGas : gasPrice,
|
maxFeePerGas: gasPrice,
|
||||||
maxPriorityFeePerGas : web3.utils.toHex(web3.utils.toWei('3', 'gwei')),
|
maxPriorityFeePerGas: web3.utils.toHex(web3.utils.toWei('3', 'gwei')),
|
||||||
gas : gasLimit,
|
gas: gasLimit,
|
||||||
data : encodedData
|
data: encodedData
|
||||||
}
|
}
|
||||||
} else if (netId == 5 || netId == 137 || netId == 43114) {
|
} else if (netId == 5 || netId == 137 || netId == 43114) {
|
||||||
return {
|
return {
|
||||||
to : to,
|
to: to,
|
||||||
value : value,
|
value: value,
|
||||||
nonce : nonce,
|
nonce: nonce,
|
||||||
maxFeePerGas : gasPrice,
|
maxFeePerGas: gasPrice,
|
||||||
maxPriorityFeePerGas : gasPrice,
|
maxPriorityFeePerGas: gasPrice,
|
||||||
gas : gasLimit,
|
gas: gasLimit,
|
||||||
data : encodedData
|
data: encodedData
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
to : to,
|
to: to,
|
||||||
value : value,
|
value: value,
|
||||||
nonce : nonce,
|
nonce: nonce,
|
||||||
gasPrice : gasPrice,
|
gasPrice: gasPrice,
|
||||||
gas : gasLimit,
|
gas: gasLimit,
|
||||||
data : encodedData
|
data: encodedData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,10 +128,10 @@ async function generateTransaction(to, encodedData, value = 0) {
|
||||||
if (!isLocalNode) {
|
if (!isLocalNode) {
|
||||||
await submitTransaction(signed.rawTransaction);
|
await submitTransaction(signed.rawTransaction);
|
||||||
} else {
|
} else {
|
||||||
console.log('\n=============Raw TX=================','\n')
|
console.log('\n=============Raw TX=================', '\n')
|
||||||
console.log(`Please submit this raw tx to https://${getExplorerLink()}/pushTx, or otherwise broadcast with node cli.js broadcast command.`,`\n`)
|
console.log(`Please submit this raw tx to https://${getExplorerLink()}/pushTx, or otherwise broadcast with node cli.js broadcast command.`, `\n`)
|
||||||
console.log(signed.rawTransaction,`\n`)
|
console.log(signed.rawTransaction, `\n`)
|
||||||
console.log('=====================================','\n')
|
console.log('=====================================', '\n')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,9 +151,20 @@ function createDeposit({ nullifier, secret }) {
|
||||||
async function backupNote({ currency, amount, netId, note, noteString }) {
|
async function backupNote({ currency, amount, netId, note, noteString }) {
|
||||||
try {
|
try {
|
||||||
await fs.writeFileSync(`./backup-tornado-${currency}-${amount}-${netId}-${note.slice(0, 10)}.txt`, noteString, 'utf8');
|
await fs.writeFileSync(`./backup-tornado-${currency}-${amount}-${netId}-${note.slice(0, 10)}.txt`, noteString, 'utf8');
|
||||||
console.log("Backed up deposit note as",`./backup-tornado-${currency}-${amount}-${netId}-${note.slice(0, 10)}.txt`)
|
console.log("Backed up deposit note as", `./backup-tornado-${currency}-${amount}-${netId}-${note.slice(0, 10)}.txt`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error('Writing backup note failed:',e)
|
throw new Error('Writing backup note failed:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function backupInvoice({ currency, amount, netId, commitmentNote, invoiceString }) {
|
||||||
|
try {
|
||||||
|
await fs.writeFileSync(`./backup-tornadoInvoice-${currency}-${amount}-${netId}-${commitmentNote.slice(0, 10)}.txt`, invoiceString, 'utf8');
|
||||||
|
console.log("Backed up invoice as", `./backup-tornadoInvoice-${currency}-${amount}-${netId}-${commitmentNote.slice(0, 10)}.txt`)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error('Writing backup note failed:', e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +218,75 @@ async function deposit({ currency, amount }) {
|
||||||
return noteString
|
return noteString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* create a Invoice
|
||||||
|
* @param currency Сurrency
|
||||||
|
* @param amount Deposit amount
|
||||||
|
*/
|
||||||
|
async function createInvoice({ currency, amount }) {
|
||||||
|
const deposit = createDeposit({
|
||||||
|
nullifier: rbigint(31),
|
||||||
|
secret: rbigint(31)
|
||||||
|
})
|
||||||
|
const note = toHex(deposit.preimage, 62)
|
||||||
|
const noteString = `tornado-${currency}-${amount}-${netId}-${note}`
|
||||||
|
console.log(`Your note: ${noteString}`)
|
||||||
|
|
||||||
|
const commitmentNote = toHex(deposit.commitment)
|
||||||
|
const invoiceString = `tornadoInvoice-${currency}-${amount}-${netId}-${commitmentNote}`
|
||||||
|
console.log(`Your invoice: ${invoiceString}`)
|
||||||
|
|
||||||
|
await backupNote({ currency, amount, netId, note, noteString })
|
||||||
|
await backupInvoice({ currency, amount, netId, commitmentNote, invoiceString })
|
||||||
|
|
||||||
|
return (noteString, invoiceString)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a payment
|
||||||
|
* @param currency Сurrency
|
||||||
|
* @param amount Deposit amount
|
||||||
|
*/
|
||||||
|
async function payInvoice({ currency, amount, netId, commitmentNote }) {
|
||||||
|
assert(senderAccount != null, 'Error! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you deposit')
|
||||||
|
|
||||||
|
if (currency === netSymbol.toLowerCase()) {
|
||||||
|
await printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' })
|
||||||
|
await printETHBalance({ address: senderAccount, name: 'Sender account' })
|
||||||
|
const value = isTestRPC ? ETH_AMOUNT : fromDecimals({ amount, decimals: 18 })
|
||||||
|
console.log('Submitting deposit transaction')
|
||||||
|
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, toHex(commitmentNote), []).encodeABI(), value)
|
||||||
|
await printETHBalance({ address: tornadoContract._address, name: 'Tornado contract' })
|
||||||
|
await printETHBalance({ address: senderAccount, name: 'Sender account' })
|
||||||
|
} else {
|
||||||
|
// a token
|
||||||
|
await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' })
|
||||||
|
await printERC20Balance({ address: senderAccount, name: 'Sender account' })
|
||||||
|
const decimals = isTestRPC ? 18 : config.deployments[`netId${netId}`][currency].decimals
|
||||||
|
const tokenAmount = isTestRPC ? TOKEN_AMOUNT : fromDecimals({ amount, decimals })
|
||||||
|
if (isTestRPC) {
|
||||||
|
console.log('Minting some test tokens to deposit')
|
||||||
|
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))
|
||||||
|
if (toBN(allowance).lt(toBN(tokenAmount))) {
|
||||||
|
console.log('Approving tokens for deposit')
|
||||||
|
await generateTransaction(erc20Address, erc20.methods.approve(tornado._address, tokenAmount).encodeABI())
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Submitting deposit transaction')
|
||||||
|
await generateTransaction(contractAddress, tornado.methods.deposit(tornadoInstance, toHex(commitmentNote), []).encodeABI())
|
||||||
|
await printERC20Balance({ address: tornadoContract._address, name: 'Tornado contract' })
|
||||||
|
await printERC20Balance({ address: senderAccount, name: 'Sender account' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate merkle tree for a deposit.
|
* Generate merkle tree for a deposit.
|
||||||
* Download deposit events from the tornado, reconstructs merkle tree, finds our deposit leaf
|
* Download deposit events from the tornado, reconstructs merkle tree, finds our deposit leaf
|
||||||
|
@ -306,7 +386,7 @@ async function withdraw({ deposit, currency, amount, recipient, relayerURL, torP
|
||||||
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')
|
||||||
}
|
}
|
||||||
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 = await axios.get(relayerURL + '/status', options)
|
||||||
|
|
||||||
|
@ -389,19 +469,19 @@ async function send({ address, amount, tokenAddress }) {
|
||||||
const tokenSymbol = await erc20.methods.symbol().call()
|
const tokenSymbol = await erc20.methods.symbol().call()
|
||||||
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)) {
|
||||||
console.error("You have",tokenBalance.div(BigNumber(10).pow(tokenDecimals)).toString(),tokenSymbol,", you can't send more than you have")
|
console.error("You have", tokenBalance.div(BigNumber(10).pow(tokenDecimals)).toString(), tokenSymbol, ", you can't send more than you have")
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
const encodeTransfer = erc20.methods.transfer(address, toSend).encodeABI()
|
const encodeTransfer = erc20.methods.transfer(address, toSend).encodeABI()
|
||||||
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 balance = new BigNumber(await web3.eth.getBalance(senderAccount));
|
||||||
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))
|
||||||
if (balance.lt(toSend)) {
|
if (balance.lt(toSend)) {
|
||||||
console.error("You have",balance.div(BigNumber(10).pow(18)),netSymbol+", you can't send more than you have.")
|
console.error("You have", balance.div(BigNumber(10).pow(18)), netSymbol + ", you can't send more than you have.")
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -416,7 +496,7 @@ async function send({ address, amount, tokenAddress }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await generateTransaction(address, null, toSend)
|
await generateTransaction(address, null, toSend)
|
||||||
console.log('Sent',toSend.div(BigNumber(10).pow(18)).toString(),netSymbol,'to',address);
|
console.log('Sent', toSend.div(BigNumber(10).pow(18)).toString(), netSymbol, 'to', address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +796,7 @@ function loadCachedEvents({ type, currency, amount }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} 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,
|
||||||
|
@ -724,7 +804,7 @@ function loadCachedEvents({ type, currency, amount }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchEvents({ type, currency, amount}) {
|
async function fetchEvents({ type, currency, amount }) {
|
||||||
if (type === "withdraw") {
|
if (type === "withdraw") {
|
||||||
type = "withdrawal"
|
type = "withdrawal"
|
||||||
}
|
}
|
||||||
|
@ -732,26 +812,26 @@ async function fetchEvents({ type, currency, amount}) {
|
||||||
const cachedEvents = loadCachedEvents({ type, currency, amount })
|
const cachedEvents = loadCachedEvents({ type, currency, amount })
|
||||||
const startBlock = cachedEvents.lastBlock + 1
|
const 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")
|
||||||
|
|
||||||
async function syncEvents() {
|
async function syncEvents() {
|
||||||
try {
|
try {
|
||||||
let targetBlock = await web3.eth.getBlockNumber();
|
let targetBlock = await web3.eth.getBlockNumber();
|
||||||
let chunks = 1000;
|
let chunks = 1000;
|
||||||
for (let i=startBlock; i < targetBlock; i+=chunks) {
|
for (let i = startBlock; i < targetBlock; i += chunks) {
|
||||||
let fetchedEvents = [];
|
let fetchedEvents = [];
|
||||||
async function fetchLatestEvents(i) {
|
async function fetchLatestEvents(i) {
|
||||||
let j;
|
let j;
|
||||||
if (i+chunks-1 > targetBlock) {
|
if (i + chunks - 1 > targetBlock) {
|
||||||
j = targetBlock;
|
j = targetBlock;
|
||||||
} else {
|
} else {
|
||||||
j = i+chunks-1;
|
j = i + chunks - 1;
|
||||||
}
|
}
|
||||||
await tornadoContract.getPastEvents(capitalizeFirstLetter(type), {
|
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);
|
}).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);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function mapDepositEvents() {
|
async function mapDepositEvents() {
|
||||||
|
@ -781,7 +861,7 @@ async function fetchEvents({ type, currency, amount}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function mapLatestEvents() {
|
async function mapLatestEvents() {
|
||||||
if (type === "deposit"){
|
if (type === "deposit") {
|
||||||
await mapDepositEvents();
|
await mapDepositEvents();
|
||||||
} else {
|
} else {
|
||||||
await mapWithdrawEvents();
|
await mapWithdrawEvents();
|
||||||
|
@ -795,7 +875,7 @@ async function fetchEvents({ type, currency, amount}) {
|
||||||
const events = localEvents.concat(fetchedEvents);
|
const events = localEvents.concat(fetchedEvents);
|
||||||
await fs.writeFileSync(fileName, JSON.stringify(events, null, 2), 'utf8')
|
await 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await fetchLatestEvents(i);
|
await fetchLatestEvents(i);
|
||||||
|
@ -813,7 +893,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, "instance to block", updatedBlock, "successfully")
|
||||||
console.log(`Total ${type}s:`, updatedEvents.length)
|
console.log(`Total ${type}s:`, updatedEvents.length)
|
||||||
return updatedEvents;
|
return updatedEvents;
|
||||||
}
|
}
|
||||||
|
@ -847,6 +927,29 @@ function parseNote(noteString) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses Tornado.cash note
|
||||||
|
* @param invoiceString the note
|
||||||
|
*/
|
||||||
|
function parseInvoice(noteString) {
|
||||||
|
const noteRegex = /tornadoInvoice-(?<currency>\w+)-(?<amount>[\d.]+)-(?<netId>\d+)-0x(?<commitmentNote>[0-9a-fA-F]{64})/g
|
||||||
|
const match = noteRegex.exec(noteString)
|
||||||
|
if (!match) {
|
||||||
|
throw new Error('The note has invalid format')
|
||||||
|
}
|
||||||
|
|
||||||
|
const netId = Number(match.groups.netId)
|
||||||
|
const buf = Buffer.from(match.groups.commitmentNote, 'hex')
|
||||||
|
const commitmentNote = toHex(buf.slice(0, 32))
|
||||||
|
|
||||||
|
return {
|
||||||
|
currency: match.groups.currency,
|
||||||
|
amount: match.groups.amount,
|
||||||
|
netId,
|
||||||
|
commitmentNote
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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 })
|
||||||
|
@ -924,16 +1027,16 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort,
|
||||||
let ipOptions = {};
|
let ipOptions = {};
|
||||||
if (torPort && rpc.includes("https")) {
|
if (torPort && rpc.includes("https")) {
|
||||||
console.log("Using tor network")
|
console.log("Using tor network")
|
||||||
web3Options = { agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:'+torPort) }, timeout: 60000 }
|
web3Options = { agent: { https: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 }
|
||||||
// Use forked web3-providers-http from local file to modify user-agent header value which improves privacy.
|
// Use forked web3-providers-http from local file to modify user-agent header value which improves privacy.
|
||||||
web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 })
|
web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 })
|
||||||
ipOptions = { 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' } }
|
ipOptions = { 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' } }
|
||||||
} else if (torPort && rpc.includes("http")) {
|
} else if (torPort && rpc.includes("http")) {
|
||||||
console.log("Using tor network")
|
console.log("Using tor network")
|
||||||
web3Options = { agent: { http: new SocksProxyAgent('socks5h://127.0.0.1:'+torPort) }, timeout: 60000 }
|
web3Options = { agent: { http: new SocksProxyAgent('socks5h://127.0.0.1:' + torPort) }, timeout: 60000 }
|
||||||
// Use forked web3-providers-http from local file to modify user-agent header value which improves privacy.
|
// Use forked web3-providers-http from local file to modify user-agent header value which improves privacy.
|
||||||
web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 })
|
web3 = new Web3(new Web3HttpProvider(rpc, web3Options), null, { transactionConfirmationBlocks: 1 })
|
||||||
ipOptions = { 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' } }
|
ipOptions = { 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' } }
|
||||||
} else if (rpc.includes("ipc")) {
|
} else if (rpc.includes("ipc")) {
|
||||||
console.log("Using ipc connection")
|
console.log("Using ipc connection")
|
||||||
web3 = new Web3(new Web3.providers.IpcProvider(rpc, net), null, { transactionConfirmationBlocks: 1 })
|
web3 = new Web3(new Web3.providers.IpcProvider(rpc, net), null, { transactionConfirmationBlocks: 1 })
|
||||||
|
@ -948,7 +1051,7 @@ async function init({ rpc, noteNetId, currency = 'dai', amount = '100', torPort,
|
||||||
try {
|
try {
|
||||||
const fetchRemoteIP = await axios.get('https://ip.tornado.cash', ipOptions)
|
const fetchRemoteIP = await axios.get('https://ip.tornado.cash', ipOptions)
|
||||||
const { country, ip } = fetchRemoteIP.data
|
const { country, ip } = fetchRemoteIP.data
|
||||||
console.log('Your remote IP address is',ip,'from',country+'.');
|
console.log('Your remote IP address is', ip, 'from', country + '.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Could not fetch remote IP from ip.tornado.cash, use VPN if the problem repeats.');
|
console.error('Could not fetch remote IP from ip.tornado.cash, use VPN if the problem repeats.');
|
||||||
}
|
}
|
||||||
|
@ -1054,6 +1157,29 @@ async function main() {
|
||||||
await init({ rpc: program.rpc, currency, amount, torPort: program.tor, localMode: program.local })
|
await init({ rpc: program.rpc, currency, amount, torPort: program.tor, localMode: program.local })
|
||||||
await deposit({ currency, amount })
|
await deposit({ currency, amount })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('invoice <currency> <amount>')
|
||||||
|
.description(
|
||||||
|
'Create invoice of specified currency and amount from default eth account and return the invoice (to pay) and note (to claim). 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) => {
|
||||||
|
currency = currency.toLowerCase()
|
||||||
|
await init({ rpc: program.rpc, currency, amount, torPort: program.tor, localMode: program.local })
|
||||||
|
await createInvoice({ currency, amount })
|
||||||
|
})
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('payInvoice <invoice>')
|
||||||
|
.description(
|
||||||
|
'pay invoice of specified currency and amount from default eth account. 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 (invoice) => {
|
||||||
|
const { currency, amount, netId, commitmentNote } = parseInvoice(invoice)
|
||||||
|
await init({ rpc: program.rpc, currency, amount, torPort: program.tor, localMode: program.local })
|
||||||
|
await payInvoice({ currency, amount, netId, commitmentNote })
|
||||||
|
})
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('withdraw <note> <recipient> [ETH_purchase]')
|
.command('withdraw <note> <recipient> [ETH_purchase]')
|
||||||
.description(
|
.description(
|
||||||
|
@ -1078,7 +1204,7 @@ async function main() {
|
||||||
.action(async (address, tokenAddress) => {
|
.action(async (address, tokenAddress) => {
|
||||||
await init({ rpc: program.rpc, torPort: program.tor, balanceCheck: true })
|
await init({ rpc: program.rpc, torPort: program.tor, 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;
|
||||||
}
|
}
|
||||||
await printETHBalance({ address, name: 'Account' })
|
await printETHBalance({ address, name: 'Account' })
|
||||||
|
@ -1121,7 +1247,7 @@ async function main() {
|
||||||
console.log('The note was not spent')
|
console.log('The note was not spent')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('=====================================','\n')
|
console.log('=====================================', '\n')
|
||||||
|
|
||||||
const withdrawInfo = await loadWithdrawalData({ amount, currency, deposit })
|
const withdrawInfo = await loadWithdrawalData({ amount, currency, deposit })
|
||||||
const withdrawalDate = new Date(withdrawInfo.timestamp * 1000)
|
const withdrawalDate = new Date(withdrawInfo.timestamp * 1000)
|
||||||
|
@ -1132,7 +1258,7 @@ async function main() {
|
||||||
console.log('To :', `https://${getExplorerLink()}/address/${withdrawInfo.to}`)
|
console.log('To :', `https://${getExplorerLink()}/address/${withdrawInfo.to}`)
|
||||||
console.log('Transaction :', `https://${getExplorerLink()}/tx/${withdrawInfo.txHash}`)
|
console.log('Transaction :', `https://${getExplorerLink()}/tx/${withdrawInfo.txHash}`)
|
||||||
console.log('Nullifier :', withdrawInfo.nullifier)
|
console.log('Nullifier :', withdrawInfo.nullifier)
|
||||||
console.log('=====================================','\n')
|
console.log('=====================================', '\n')
|
||||||
})
|
})
|
||||||
program
|
program
|
||||||
.command('syncEvents <type> <currency> <amount>')
|
.command('syncEvents <type> <currency> <amount>')
|
||||||
|
@ -1144,7 +1270,7 @@ async function main() {
|
||||||
currency = currency.toLowerCase()
|
currency = currency.toLowerCase()
|
||||||
await init({ rpc: program.rpc, type, currency, amount, torPort: program.tor })
|
await init({ rpc: program.rpc, type, currency, amount, torPort: program.tor })
|
||||||
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)
|
||||||
})
|
})
|
||||||
program
|
program
|
||||||
.command('test')
|
.command('test')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue