2020-05-22 12:37:21 +03:00
#!/usr/bin/env node
2020-05-21 22:29:33 +03:00
// Temporary demo client
// Works both in browser and node.js
require ( 'dotenv' ) . config ( )
2021-02-14 23:40:23 +03:00
2020-05-21 22:29:33 +03:00
const axios = require ( 'axios' )
const assert = require ( 'assert' )
const Web3 = require ( 'web3' )
2021-02-14 23:40:23 +03:00
2020-05-21 22:29:33 +03:00
const { toWei , fromWei , toBN , BN } = require ( 'web3-utils' )
const config = require ( './config' )
const program = require ( 'commander' )
2020-12-24 14:39:20 +03:00
const { GasPriceOracle } = require ( 'gas-price-oracle' )
2020-05-21 22:29:33 +03:00
2021-02-14 23:40:23 +03:00
const { initialize , createDeposit , generateProof , toHex , rbigint , bigInt } = require ( './core' )
let web3 , tornado , mixerContract , tornadoInstance , erc20 , senderAccount , netId
2020-05-21 22:29:33 +03:00
let MERKLE _TREE _HEIGHT , ETH _AMOUNT , TOKEN _AMOUNT , PRIVATE _KEY
let isLocalRPC = false
/** Display ETH account balance */
async function printETHBalance ( { address , name } ) {
2020-05-22 12:35:00 +03:00
console . log ( ` ${ name } ETH balance is ` , web3 . utils . fromWei ( await web3 . eth . getBalance ( address ) ) )
2020-05-21 22:29:33 +03:00
}
/** Display ERC20 account balance */
async function printERC20Balance ( { address , name , tokenAddress } ) {
2020-05-22 12:35:00 +03:00
const erc20ContractJson = require ( './build/contracts/ERC20Mock.json' )
erc20 = tokenAddress ? new web3 . eth . Contract ( erc20ContractJson . abi , tokenAddress ) : erc20
console . log ( ` ${ name } Token Balance is ` , web3 . utils . fromWei ( await erc20 . methods . balanceOf ( address ) . call ( ) ) )
2020-05-21 22:29:33 +03:00
}
/ * *
* Make a deposit
* @ param currency С urrency
* @ param amount Deposit amount
* /
async function deposit ( { currency , amount } ) {
2021-02-14 21:23:17 +03:00
const deposit = createDeposit ( {
nullifier : rbigint ( 31 ) ,
secret : rbigint ( 31 )
} )
2020-05-22 12:35:00 +03:00
const note = toHex ( deposit . preimage , 62 )
const noteString = ` tornado- ${ currency } - ${ amount } - ${ netId } - ${ note } `
console . log ( ` Your note: ${ noteString } ` )
if ( currency === 'eth' ) {
await printETHBalance ( { address : tornado . _address , name : 'Tornado' } )
await printETHBalance ( { address : senderAccount , name : 'Sender account' } )
const value = isLocalRPC ? ETH _AMOUNT : fromDecimals ( { amount , decimals : 18 } )
console . log ( 'Submitting deposit transaction' )
2020-12-24 14:39:20 +03:00
await tornado . methods . deposit ( tornadoInstance , toHex ( deposit . commitment ) , [ ] ) . send ( { value , from : senderAccount , gas : 2e6 } )
2020-05-22 12:35:00 +03:00
await printETHBalance ( { address : tornado . _address , name : 'Tornado' } )
await printETHBalance ( { address : senderAccount , name : 'Sender account' } )
2021-02-14 21:23:17 +03:00
} else {
// a token
2020-05-22 12:35:00 +03:00
await printERC20Balance ( { address : tornado . _address , name : 'Tornado' } )
await printERC20Balance ( { address : senderAccount , name : 'Sender account' } )
const decimals = isLocalRPC ? 18 : config . deployments [ ` netId ${ netId } ` ] [ currency ] . decimals
const tokenAmount = isLocalRPC ? TOKEN _AMOUNT : fromDecimals ( { amount , decimals } )
if ( isLocalRPC ) {
console . log ( 'Minting some test tokens to deposit' )
await erc20 . methods . mint ( senderAccount , tokenAmount ) . send ( { from : senderAccount , gas : 2e6 } )
}
2020-05-21 22:29:33 +03:00
2020-05-22 12:35:00 +03:00
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 erc20 . methods . approve ( tornado . _address , tokenAmount ) . send ( { from : senderAccount , gas : 1e6 } )
2020-05-21 22:29:33 +03:00
}
2020-05-22 12:35:00 +03:00
console . log ( 'Submitting deposit transaction' )
await tornado . methods . deposit ( toHex ( deposit . commitment ) ) . send ( { from : senderAccount , gas : 2e6 } )
await printERC20Balance ( { address : tornado . _address , name : 'Tornado' } )
await printERC20Balance ( { address : senderAccount , name : 'Sender account' } )
}
return noteString
2020-05-21 22:29:33 +03:00
}
/ * *
2021-02-14 23:40:23 +03:00
* Do an ETH withdrawal
* @ param noteString Note to withdraw
* @ param recipient Recipient address
2020-05-21 22:29:33 +03:00
* /
2021-02-14 23:40:23 +03:00
async function withdraw ( { deposit , currency , amount , recipient , relayerURL , refund = '0' } ) {
2020-05-22 12:35:00 +03:00
// Get all deposit events from smart contract and assemble merkle tree from them
2021-02-14 21:23:17 +03:00
const events = await mixerContract . getPastEvents ( 'Deposit' , {
fromBlock : 0 ,
toBlock : 'latest'
} )
2020-05-22 12:35:00 +03:00
if ( currency === 'eth' && refund !== '0' ) {
throw new Error ( 'The ETH purchase is supposted to be 0 for ETH withdrawals' )
}
refund = toWei ( refund )
if ( relayerURL ) {
if ( relayerURL . endsWith ( '.eth' ) ) {
throw new Error ( 'ENS name resolving is not supported. Please provide DNS name of the relayer. See instuctions in README.md' )
2020-05-21 22:29:33 +03:00
}
2020-05-22 12:35:00 +03:00
const relayerStatus = await axios . get ( relayerURL + '/status' )
2020-12-24 14:39:20 +03:00
const { rewardAccount , netId , ethPrices , tornadoServiceFee } = relayerStatus . data
2021-02-14 21:23:17 +03:00
assert ( netId === ( await web3 . eth . net . getId ( ) ) || netId === '*' , 'This relay is for different network' )
2020-12-24 14:39:20 +03:00
console . log ( 'Relay address: ' , rewardAccount )
const gasPrice = await fetchGasPrice ( )
2020-05-22 12:35:00 +03:00
const decimals = isLocalRPC ? 18 : config . deployments [ ` netId ${ netId } ` ] [ currency ] . decimals
2021-02-14 21:23:17 +03:00
const fee = calculateFee ( {
currency ,
gasPrice ,
amount ,
refund ,
ethPrices ,
relayerServiceFee : tornadoServiceFee ,
decimals
} )
2020-05-22 12:35:00 +03:00
if ( fee . gt ( fromDecimals ( { amount , decimals } ) ) ) {
throw new Error ( 'Too high refund' )
2020-05-21 22:29:33 +03:00
}
2020-12-24 14:39:20 +03:00
2021-02-14 21:23:17 +03:00
const { proof , args } = await generateProof ( {
deposit ,
recipient ,
relayerAddress : rewardAccount ,
fee ,
2021-02-14 23:40:23 +03:00
refund ,
events
2021-02-14 21:23:17 +03:00
} )
2020-05-21 22:29:33 +03:00
2021-02-14 23:40:23 +03:00
// Validate that our data is correct
// const isValidRoot = await mixerContract.methods.isKnownRoot(toHex(root)).call()
// const isSpent = await mixerContract.methods.isSpent(toHex(deposit.nullifierHash)).call()
// assert(isValidRoot === true, 'Merkle tree is corrupted')
// assert(isSpent === false, 'The note is already spent')
// assert(leafIndex >= 0, 'The deposit is not found in the tree')
2020-05-22 12:35:00 +03:00
console . log ( 'Sending withdraw transaction through relay' )
try {
2021-02-14 21:23:17 +03:00
const response = await axios . post ( relayerURL + '/v1/tornadoWithdraw' , {
contract : tornadoInstance ,
proof ,
args
} )
2020-05-22 12:35:00 +03:00
2020-12-24 14:39:20 +03:00
const { id } = response . data
const result = await getStatus ( id , relayerURL )
console . log ( 'STATUS' , result )
2020-05-22 12:35:00 +03:00
} catch ( e ) {
if ( e . response ) {
console . error ( e . response . data . error )
} else {
console . error ( e . message )
}
2020-05-21 22:29:33 +03:00
}
2021-02-14 21:23:17 +03:00
} else {
// using private key
2021-02-14 23:40:23 +03:00
const { proof , args } = await generateProof ( { deposit , recipient , refund , events } )
2020-05-22 12:35:00 +03:00
console . log ( 'Submitting withdraw transaction' )
2021-02-14 21:23:17 +03:00
await tornado . methods
. withdraw ( tornadoInstance , proof , ... args )
. send ( { from : senderAccount , value : refund . toString ( ) , gas : 1e6 } )
2020-05-22 12:35:00 +03:00
. on ( 'transactionHash' , function ( txHash ) {
if ( netId === 1 || netId === 42 ) {
console . log ( ` View transaction on etherscan https:// ${ getCurrentNetworkName ( ) } etherscan.io/tx/ ${ txHash } ` )
} else {
console . log ( ` The transaction hash is ${ txHash } ` )
}
2021-02-14 21:23:17 +03:00
} )
. on ( 'error' , function ( e ) {
2020-05-22 12:35:00 +03:00
console . error ( 'on transactionHash error' , e . message )
} )
}
console . log ( 'Done' )
}
2020-05-21 22:29:33 +03:00
2021-02-14 21:23:17 +03:00
function getStatus ( id , relayerURL ) {
2020-12-24 14:39:20 +03:00
return new Promise ( ( resolve ) => {
async function getRelayerStatus ( ) {
const responseStatus = await axios . get ( relayerURL + '/v1/jobs/' + id )
if ( responseStatus . status === 200 ) {
const { txHash , status , confirmations , failedReason } = responseStatus . data
console . log ( ` Current job status ${ status } , confirmations: ${ confirmations } ` )
if ( status === 'FAILED' ) {
throw new Error ( status + ' failed reason:' + failedReason )
}
if ( status === 'CONFIRMED' ) {
const receipt = await waitForTxReceipt ( { txHash } )
2021-02-14 21:23:17 +03:00
console . log (
` Transaction submitted through the relay. View transaction on etherscan https:// ${ getCurrentNetworkName ( ) } etherscan.io/tx/ ${ txHash } `
)
2020-12-24 14:39:20 +03:00
console . log ( 'Transaction mined in block' , receipt . blockNumber )
resolve ( status )
}
}
setTimeout ( ( ) => {
getRelayerStatus ( id , relayerURL )
} , 3000 )
}
getRelayerStatus ( )
} )
}
2020-05-22 12:35:00 +03:00
function fromDecimals ( { amount , decimals } ) {
amount = amount . toString ( )
let ether = amount . toString ( )
const base = new BN ( '10' ) . pow ( new BN ( decimals ) )
const baseLength = base . toString ( 10 ) . length - 1 || 1
const negative = ether . substring ( 0 , 1 ) === '-'
if ( negative ) {
ether = ether . substring ( 1 )
}
if ( ether === '.' ) {
throw new Error ( '[ethjs-unit] while converting number ' + amount + ' to wei, invalid value' )
}
// Split it into a whole and fractional part
const comps = ether . split ( '.' )
if ( comps . length > 2 ) {
2021-02-14 21:23:17 +03:00
throw new Error ( '[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal points' )
2020-05-22 12:35:00 +03:00
}
let whole = comps [ 0 ]
let fraction = comps [ 1 ]
if ( ! whole ) {
whole = '0'
}
if ( ! fraction ) {
fraction = '0'
}
if ( fraction . length > baseLength ) {
2021-02-14 21:23:17 +03:00
throw new Error ( '[ethjs-unit] while converting number ' + amount + ' to wei, too many decimal places' )
2020-05-22 12:35:00 +03:00
}
while ( fraction . length < baseLength ) {
fraction += '0'
}
whole = new BN ( whole )
fraction = new BN ( fraction )
let wei = whole . mul ( base ) . add ( fraction )
if ( negative ) {
wei = wei . mul ( negative )
}
return new BN ( wei . toString ( 10 ) , 10 )
2020-05-21 22:29:33 +03:00
}
function toDecimals ( value , decimals , fixed ) {
2020-05-22 12:35:00 +03:00
const zero = new BN ( 0 )
const negative1 = new BN ( - 1 )
decimals = decimals || 18
fixed = fixed || 7
value = new BN ( value )
const negative = value . lt ( zero )
const base = new BN ( '10' ) . pow ( new BN ( decimals ) )
const baseLength = base . toString ( 10 ) . length - 1 || 1
if ( negative ) {
value = value . mul ( negative1 )
}
let fraction = value . mod ( base ) . toString ( 10 )
while ( fraction . length < baseLength ) {
fraction = ` 0 ${ fraction } `
}
fraction = fraction . match ( /^([0-9]*[1-9]|0)(0*)/ ) [ 1 ]
const whole = value . div ( base ) . toString ( 10 )
value = ` ${ whole } ${ fraction === '0' ? '' : ` . ${ fraction } ` } `
if ( negative ) {
value = ` - ${ value } `
}
if ( fixed ) {
value = value . slice ( 0 , fixed )
}
return value
2020-05-21 22:29:33 +03:00
}
function getCurrentNetworkName ( ) {
2020-05-22 12:35:00 +03:00
switch ( netId ) {
2021-02-14 21:23:17 +03:00
case 1 :
return ''
case 5 :
return 'goerli.'
case 42 :
return 'kovan.'
2020-05-22 12:35:00 +03:00
}
2020-05-21 22:29:33 +03:00
}
2020-12-24 14:39:20 +03:00
function gasPrices ( value = 80 ) {
const tenPercent = ( Number ( value ) * 5 ) / 100
const max = Math . max ( tenPercent , 3 )
const bumped = Math . floor ( Number ( value ) + max )
return toHex ( toWei ( bumped . toString ( ) , 'gwei' ) )
}
async function fetchGasPrice ( ) {
try {
const oracle = new GasPriceOracle ( )
const gas = await oracle . gasPrices ( )
return gasPrices ( gas . fast )
} catch ( err ) {
throw new Error ( ` Method fetchGasPrice has error ${ err . message } ` )
}
}
function calculateFee ( { currency , gasPrice , amount , refund , ethPrices , relayerServiceFee , decimals } ) {
2021-02-14 21:23:17 +03:00
const decimalsPoint =
Math . floor ( relayerServiceFee ) === Number ( relayerServiceFee ) ? 0 : relayerServiceFee . toString ( ) . split ( '.' ) [ 1 ] . length
2020-05-22 12:35:00 +03:00
const roundDecimal = 10 * * decimalsPoint
2020-05-22 12:37:21 +03:00
const total = toBN ( fromDecimals ( { amount , decimals } ) )
const feePercent = total . mul ( toBN ( relayerServiceFee * roundDecimal ) ) . div ( toBN ( roundDecimal * 100 ) )
2020-12-24 14:39:20 +03:00
const expense = toBN ( gasPrice ) . mul ( toBN ( 5e5 ) )
2020-05-22 12:37:21 +03:00
let desiredFee
2020-05-22 12:35:00 +03:00
switch ( currency ) {
2021-02-14 21:23:17 +03:00
case 'eth' : {
desiredFee = expense . add ( feePercent )
break
}
default : {
desiredFee = expense
. add ( toBN ( refund ) )
. mul ( toBN ( 10 * * decimals ) )
. div ( toBN ( ethPrices [ currency ] ) )
desiredFee = desiredFee . add ( feePercent )
break
}
2020-05-22 12:35:00 +03:00
}
2020-05-22 12:37:21 +03:00
return desiredFee
2020-05-21 22:29:33 +03:00
}
/ * *
* Waits for transaction to be mined
* @ param txHash Hash of transaction
* @ param attempts
* @ param delay
* /
function waitForTxReceipt ( { txHash , attempts = 60 , delay = 1000 } ) {
2020-05-22 12:35:00 +03:00
return new Promise ( ( resolve , reject ) => {
const checkForTx = async ( txHash , retryAttempt = 0 ) => {
const result = await web3 . eth . getTransactionReceipt ( txHash )
if ( ! result || ! result . blockNumber ) {
if ( retryAttempt <= attempts ) {
setTimeout ( ( ) => checkForTx ( txHash , retryAttempt + 1 ) , delay )
} else {
reject ( new Error ( 'tx was not mined' ) )
2020-05-21 22:29:33 +03:00
}
2020-05-22 12:35:00 +03:00
} else {
resolve ( result )
}
}
checkForTx ( txHash )
} )
2020-05-21 22:29:33 +03:00
}
/ * *
* Parses Tornado . cash note
* @ param noteString the note
* /
function parseNote ( noteString ) {
2020-05-22 12:35:00 +03:00
const noteRegex = /tornado-(?<currency>\w+)-(?<amount>[\d.]+)-(?<netId>\d+)-0x(?<note>[0-9a-fA-F]{124})/g
const match = noteRegex . exec ( noteString )
if ( ! match ) {
throw new Error ( 'The note has invalid format' )
}
const buf = Buffer . from ( match . groups . note , 'hex' )
const nullifier = bigInt . leBuff2int ( buf . slice ( 0 , 31 ) )
const secret = bigInt . leBuff2int ( buf . slice ( 31 , 62 ) )
const deposit = createDeposit ( { nullifier , secret } )
const netId = Number ( match . groups . netId )
2021-02-14 21:23:17 +03:00
return {
currency : match . groups . currency ,
amount : match . groups . amount ,
netId ,
deposit
}
2020-05-21 22:29:33 +03:00
}
async function loadDepositData ( { deposit } ) {
2020-05-22 12:35:00 +03:00
try {
const eventWhenHappened = await tornado . getPastEvents ( 'Deposit' , {
filter : {
commitment : deposit . commitmentHex
} ,
fromBlock : 0 ,
toBlock : 'latest'
} )
if ( eventWhenHappened . length === 0 ) {
throw new Error ( 'There is no related deposit, the note is invalid' )
}
2020-05-21 22:29:33 +03:00
2020-05-22 12:35:00 +03:00
const { timestamp } = eventWhenHappened [ 0 ] . returnValues
const txHash = eventWhenHappened [ 0 ] . transactionHash
const isSpent = await tornado . methods . isSpent ( deposit . nullifierHex ) . call ( )
const receipt = await web3 . eth . getTransactionReceipt ( txHash )
2020-05-21 22:29:33 +03:00
2021-02-14 21:23:17 +03:00
return {
timestamp ,
txHash ,
isSpent ,
from : receipt . from ,
commitment : deposit . commitmentHex
}
2020-05-22 12:35:00 +03:00
} catch ( e ) {
console . error ( 'loadDepositData' , e )
}
return { }
2020-05-21 22:29:33 +03:00
}
async function loadWithdrawalData ( { amount , currency , deposit } ) {
2020-05-22 12:35:00 +03:00
try {
const events = await await tornado . getPastEvents ( 'Withdrawal' , {
fromBlock : 0 ,
toBlock : 'latest'
} )
const withdrawEvent = events . filter ( ( event ) => {
return event . returnValues . nullifierHash === deposit . nullifierHex
} ) [ 0 ]
const fee = withdrawEvent . returnValues . fee
const decimals = config . deployments [ ` netId ${ netId } ` ] [ currency ] . decimals
2021-02-14 21:23:17 +03:00
const withdrawalAmount = toBN ( fromDecimals ( { amount , decimals } ) ) . sub ( toBN ( fee ) )
2020-05-22 12:35:00 +03:00
const { timestamp } = await web3 . eth . getBlock ( withdrawEvent . blockHash )
return {
amount : toDecimals ( withdrawalAmount , decimals , 9 ) ,
txHash : withdrawEvent . transactionHash ,
to : withdrawEvent . returnValues . to ,
timestamp ,
nullifier : deposit . nullifierHex ,
fee : toDecimals ( fee , decimals , 9 )
2020-05-21 22:29:33 +03:00
}
2020-05-22 12:35:00 +03:00
} catch ( e ) {
console . error ( 'loadWithdrawalData' , e )
}
2020-05-21 22:29:33 +03:00
}
/ * *
* Init web3 , contracts , and snark
* /
async function init ( { rpc , noteNetId , currency = 'dai' , amount = '100' } ) {
2020-12-24 14:39:20 +03:00
let contractJson , mixerJson , erc20ContractJson , erc20tornadoJson , tornadoAddress , tokenAddress
2021-02-14 23:40:23 +03:00
web3 = new Web3 ( rpc , null , { transactionConfirmationBlocks : 1 } )
contractJson = require ( './build/contracts/TornadoProxy.abi.json' )
mixerJson = require ( './build/contracts/Mixer.abi.json' )
MERKLE _TREE _HEIGHT = process . env . MERKLE _TREE _HEIGHT || 20
await initialize ( { merkleTreeHeight : MERKLE _TREE _HEIGHT } )
ETH _AMOUNT = process . env . ETH _AMOUNT
TOKEN _AMOUNT = process . env . TOKEN _AMOUNT
PRIVATE _KEY = process . env . PRIVATE _KEY
if ( PRIVATE _KEY ) {
const account = web3 . eth . accounts . privateKeyToAccount ( '0x' + PRIVATE _KEY )
web3 . eth . accounts . wallet . add ( '0x' + PRIVATE _KEY )
web3 . eth . defaultAccount = account . address
senderAccount = account . address
2020-05-22 12:35:00 +03:00
} else {
2021-02-14 23:40:23 +03:00
console . log ( 'Warning! PRIVATE_KEY not found. Please provide PRIVATE_KEY in .env file if you deposit' )
2020-05-22 12:35:00 +03:00
}
2021-02-14 23:40:23 +03:00
erc20ContractJson = require ( './build/contracts/ERC20Mock.json' )
erc20tornadoJson = require ( './build/contracts/ERC20Tornado.json' )
2020-05-22 12:35:00 +03:00
netId = await web3 . eth . net . getId ( )
if ( noteNetId && Number ( noteNetId ) !== netId ) {
throw new Error ( 'This note is for a different network. Specify the --rpc option explicitly' )
}
isLocalRPC = netId > 42
if ( isLocalRPC ) {
tornadoAddress = currency === 'eth' ? contractJson . networks [ netId ] . address : erc20tornadoJson . networks [ netId ] . address
tokenAddress = currency !== 'eth' ? erc20ContractJson . networks [ netId ] . address : null
senderAccount = ( await web3 . eth . getAccounts ( ) ) [ 0 ]
} else {
try {
2020-12-24 14:39:20 +03:00
tornadoAddress = config . deployments [ ` netId ${ netId } ` ] . proxy
tornadoInstance = config . deployments [ ` netId ${ netId } ` ] [ currency ] . instanceAddress [ amount ]
2020-05-22 12:35:00 +03:00
if ( ! tornadoAddress ) {
throw new Error ( )
}
tokenAddress = config . deployments [ ` netId ${ netId } ` ] [ currency ] . tokenAddress
} catch ( e ) {
console . error ( 'There is no such tornado instance, check the currency and amount you provide' )
process . exit ( 1 )
2020-05-21 22:29:33 +03:00
}
2020-05-22 12:35:00 +03:00
}
2020-12-24 14:39:20 +03:00
tornado = new web3 . eth . Contract ( contractJson , tornadoAddress )
mixerContract = new web3 . eth . Contract ( mixerJson , tornadoInstance )
2020-05-22 12:35:00 +03:00
erc20 = currency !== 'eth' ? new web3 . eth . Contract ( erc20ContractJson . abi , tokenAddress ) : { }
2020-05-21 22:29:33 +03:00
}
async function main ( ) {
2021-02-14 23:40:23 +03:00
program
. option ( '-r, --rpc <URL>' , 'The RPC, CLI should interact with' , 'http://localhost:8545' )
. option ( '-R, --relayer <URL>' , 'Withdraw via relayer' )
program
. command ( 'deposit <currency> <amount>' )
. description (
'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 ) => {
currency = currency . toLowerCase ( )
await init ( { rpc : program . rpc , currency , amount } )
await deposit ( { currency , amount } )
} )
program
. command ( 'withdraw <note> <recipient> [ETH_purchase]' )
. description (
'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 ) => {
2020-05-22 12:35:00 +03:00
const { currency , amount , netId , deposit } = parseNote ( noteString )
2021-02-14 23:40:23 +03:00
await init ( { rpc : program . rpc , noteNetId : netId , currency , amount } )
await withdraw ( {
deposit ,
currency ,
amount ,
recipient ,
refund ,
relayerURL : program . relayer
2020-05-22 12:35:00 +03:00
} )
2021-02-14 23:40:23 +03:00
} )
program
. command ( 'balance <address> [token_address]' )
. description ( 'Check ETH and ERC20 balance' )
. action ( async ( address , tokenAddress ) => {
await init ( { rpc : program . rpc } )
await printETHBalance ( { address , name : '' } )
if ( tokenAddress ) {
await printERC20Balance ( { address , name : '' , tokenAddress } )
}
} )
program
. command ( 'compliance <note>' )
. description (
'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 ) => {
const { currency , amount , netId , deposit } = parseNote ( noteString )
await init ( { rpc : program . rpc , noteNetId : netId , currency , amount } )
const depositInfo = await loadDepositData ( { deposit } )
const depositDate = new Date ( depositInfo . timestamp * 1000 )
console . log ( '\n=============Deposit=================' )
console . log ( 'Deposit :' , amount , currency )
console . log ( 'Date :' , depositDate . toLocaleDateString ( ) , depositDate . toLocaleTimeString ( ) )
console . log ( 'From :' , ` https:// ${ getCurrentNetworkName ( ) } etherscan.io/address/ ${ depositInfo . from } ` )
console . log ( 'Transaction :' , ` https:// ${ getCurrentNetworkName ( ) } etherscan.io/tx/ ${ depositInfo . txHash } ` )
console . log ( 'Commitment :' , depositInfo . commitment )
if ( deposit . isSpent ) {
console . log ( 'The note was not spent' )
}
const withdrawInfo = await loadWithdrawalData ( {
amount ,
currency ,
deposit
2020-05-22 12:35:00 +03:00
} )
2021-02-14 23:40:23 +03:00
const withdrawalDate = new Date ( withdrawInfo . timestamp * 1000 )
console . log ( '\n=============Withdrawal==============' )
console . log ( 'Withdrawal :' , withdrawInfo . amount , currency )
console . log ( 'Relayer Fee :' , withdrawInfo . fee , currency )
console . log ( 'Date :' , withdrawalDate . toLocaleDateString ( ) , withdrawalDate . toLocaleTimeString ( ) )
console . log ( 'To :' , ` https:// ${ getCurrentNetworkName ( ) } etherscan.io/address/ ${ withdrawInfo . to } ` )
console . log ( 'Transaction :' , ` https:// ${ getCurrentNetworkName ( ) } etherscan.io/tx/ ${ withdrawInfo . txHash } ` )
console . log ( 'Nullifier :' , withdrawInfo . nullifier )
} )
program
. command ( 'test' )
. description ( 'Perform an automated test. It deposits and withdraws one ETH and one ERC20 note. Uses ganache.' )
. action ( async ( ) => {
console . log ( 'Start performing ETH deposit-withdraw test' )
let currency = 'eth'
let amount = '0.1'
await init ( { rpc : program . rpc , currency , amount } )
let noteString = await deposit ( { currency , amount } )
let parsedNote = parseNote ( noteString )
await withdraw ( {
deposit : parsedNote . deposit ,
currency ,
amount ,
recipient : senderAccount ,
relayerURL : program . relayer
2020-05-22 12:35:00 +03:00
} )
2021-02-14 23:40:23 +03:00
console . log ( '\nStart performing DAI deposit-withdraw test' )
currency = 'dai'
amount = '100'
await init ( { rpc : program . rpc , currency , amount } )
noteString = await deposit ( { currency , amount } )
parsedNote = parseNote ( noteString )
await withdraw ( {
deposit : parsedNote . deposit ,
currency ,
amount ,
recipient : senderAccount ,
refund : '0.02' ,
relayerURL : program . relayer
2020-05-22 12:35:00 +03:00
} )
2021-02-14 23:40:23 +03:00
} )
try {
await program . parseAsync ( process . argv )
process . exit ( 0 )
} catch ( e ) {
console . log ( 'Error:' , e )
process . exit ( 1 )
2020-05-22 12:35:00 +03:00
}
2020-05-21 22:29:33 +03:00
}
main ( )