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)
|
||||||
|
|
126
cli.js
126
cli.js
|
@ -157,6 +157,17 @@ async function backupNote({ currency, amount, netId, note, noteString }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a deposit
|
* Make a deposit
|
||||||
* @param currency Сurrency
|
* @param currency Сurrency
|
||||||
|
@ -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
|
||||||
|
@ -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 })
|
||||||
|
@ -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(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue