From d2acbf8e7ff3688696b07594e2a79287c3e7841c Mon Sep 17 00:00:00 2001 From: Alejo Acosta Date: Thu, 24 Oct 2024 15:20:18 -0300 Subject: [PATCH] add testing script in examples folder --- examples/signing/pubkey-to-address.js | 37 +++++ .../qi-wallet-alice-bob-send-receive.js | 87 ++++++++++++ examples/wallets/qi-wallet-convert-to-quai.js | 73 ++++++++++ examples/wallets/qi-wallet-send-qi-to-bob.js | 87 ++++++++++++ examples/wallets/utils.js | 132 ++++++++++++++++++ 5 files changed, 416 insertions(+) create mode 100644 examples/signing/pubkey-to-address.js create mode 100644 examples/wallets/qi-wallet-alice-bob-send-receive.js create mode 100644 examples/wallets/qi-wallet-convert-to-quai.js create mode 100644 examples/wallets/qi-wallet-send-qi-to-bob.js create mode 100644 examples/wallets/utils.js diff --git a/examples/signing/pubkey-to-address.js b/examples/signing/pubkey-to-address.js new file mode 100644 index 00000000..f1c91e6f --- /dev/null +++ b/examples/signing/pubkey-to-address.js @@ -0,0 +1,37 @@ +const quais = require('../../lib/commonjs/quais'); + + +async function main() { + // Check if a public key is provided as a command line argument + if (process.argv.length < 3) { + console.error('Please provide a public key as a command line argument'); + process.exit(1); + } + + const pubkey = process.argv[2]; + + // Verify if the provided string is a valid public key of the type 0x0250495cb2f9535c684ebe4687b501c0d41a623d68c118b8dcecd393370f1d90e6 + if (!quais.isHexString(pubkey) || pubkey.length !== 68) { + console.error('Invalid public key format'); + process.exit(1); + } + + + try { + // Compute the address from the public key + const address = quais.computeAddress(pubkey); + console.log(`Public Key: ${pubkey}`); + console.log(`Derived Address: ${address}`); + } catch (error) { + console.error('Error computing address:', error.message); + process.exit(1); + } + +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/qi-wallet-alice-bob-send-receive.js b/examples/wallets/qi-wallet-alice-bob-send-receive.js new file mode 100644 index 00000000..8489da2c --- /dev/null +++ b/examples/wallets/qi-wallet-alice-bob-send-receive.js @@ -0,0 +1,87 @@ +const quais = require('../../lib/commonjs/quais'); +const { printWalletInfo } = require('./utils'); +require('dotenv').config(); + +async function main() { + // Create provider + console.log('RPC URL: ', process.env.RPC_URL); + const provider = new quais.JsonRpcProvider(process.env.RPC_URL); + + // Create wallet and connect to provider + const mnemonic = quais.Mnemonic.fromPhrase(process.env.MNEMONIC); + const aliceQiWallet = quais.QiHDWallet.fromMnemonic(mnemonic); + const alicePaymentCode = aliceQiWallet.getPaymentCode(0); + aliceQiWallet.connect(provider); + + const bobMnemonic = quais.Mnemonic.fromPhrase("innocent perfect bus miss prevent night oval position aspect nut angle usage expose grace juice"); + const bobQiWallet = quais.QiHDWallet.fromMnemonic(bobMnemonic); + bobQiWallet.connect(provider); + const bobPaymentCode = bobQiWallet.getPaymentCode(0); + aliceQiWallet.openChannel(bobPaymentCode); + + console.log('Scanning Alice Qi wallet...'); + await aliceQiWallet.scan(quais.Zone.Cyprus1); + console.log('Alice Qi wallet scan complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // Bob opens a channel with Alice + bobQiWallet.openChannel(alicePaymentCode); + console.log('Scanning Bob Qi wallet...'); + await bobQiWallet.scan(quais.Zone.Cyprus1); + console.log('Bob Qi wallet scan complete'); + printWalletInfo('Bob', bobQiWallet); + + // Alice sends 50 Qi to Bob + console.log('\nAlice sends 50 Qi to Bob'); + const tx = await aliceQiWallet.sendTransaction(bobPaymentCode, 50000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + // console.log('Transaction sent: ', tx); + console.log(`Transaction hash: ${tx.hash}`); + console.log(`Tx contains ${tx.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx.txInputs)}`); + console.log(`Tx contains ${tx.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response = await tx.wait(); + console.log('Transaction confirmed in block: ', response.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); + + console.log('\nBob sends back 25 Qi to Alice'); + const tx2 = await bobQiWallet.sendTransaction(alicePaymentCode, 25000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + console.log(`Transaction hash: ${tx2.hash}`); + console.log(`Tx contains ${tx2.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx2.txInputs)}`); + console.log(`Tx contains ${tx2.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response2 = await tx2.wait(); + console.log('Transaction confirmed in block: ', response2.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/qi-wallet-convert-to-quai.js b/examples/wallets/qi-wallet-convert-to-quai.js new file mode 100644 index 00000000..94b0355f --- /dev/null +++ b/examples/wallets/qi-wallet-convert-to-quai.js @@ -0,0 +1,73 @@ +const quais = require('../../lib/commonjs/quais'); +const { printWalletInfo } = require('./utils'); +require('dotenv').config(); + +async function main() { + // Create provider + console.log('RPC URL: ', process.env.RPC_URL); + const provider = new quais.JsonRpcProvider(process.env.RPC_URL); + + // Create Alice's Qi wallet and connect to provider + const mnemonic = quais.Mnemonic.fromPhrase(process.env.MNEMONIC); + const aliceQiWallet = quais.QiHDWallet.fromMnemonic(mnemonic); + aliceQiWallet.connect(provider); + + // Initialize Alice's Qi wallet + console.log('\nInitializing Alice Qi wallet...'); + await aliceQiWallet.scan(quais.Zone.Cyprus1); + console.log('Alice Qi wallet scan complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // Create Alice's Quai wallet and connect to provider + const aliceQuaiWallet = quais.QuaiHDWallet.fromMnemonic(mnemonic); + aliceQuaiWallet.connect(provider); + + // derive quai address + const quaiAddressInfo = await aliceQuaiWallet.getNextAddress(0, quais.Zone.Cyprus1); + console.log('\nAlice Quai address:', quaiAddressInfo.address); + + console.log('\nAlice converts 100 Qi to Quai...'); + + const tx = await aliceQiWallet.convertToQuai(quaiAddressInfo.address, 100000); + // console.log('Transaction sent: ', tx); + console.log(`Transaction hash: ${tx.hash}`); + console.log(`Tx contains ${tx.txInputs?.length} inputs`); + console.log(`Tx contains ${tx.txOutputs?.length} outputs`); + // wait for the transaction to be confirmed + console.log('Waiting for transaction to be confirmed...'); + const response = await tx.wait(); + console.log('Transaction confirmed in block: ', response.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // print Alice's Quai address balance + const balance = await provider.getBalance(quaiAddressInfo.address); + console.log('\nAlice Quai address balance:', quais.formatQuai(balance)); + + // repeat the same process of converting 100 Qi to Quai + console.log('Alice converts another 100 Qi to Quai...'); + const tx2 = await aliceQiWallet.convertToQuai(quaiAddressInfo.address, 100000); + console.log(`Tx contains ${tx2.txInputs?.length} inputs`); + console.log(`Tx contains ${tx2.txOutputs?.length} outputs`); + console.log('Waiting for transaction to be confirmed...'); + const response2 = await tx2.wait(); + console.log('Transaction confirmed in block: ', response2.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/qi-wallet-send-qi-to-bob.js b/examples/wallets/qi-wallet-send-qi-to-bob.js new file mode 100644 index 00000000..6581eb25 --- /dev/null +++ b/examples/wallets/qi-wallet-send-qi-to-bob.js @@ -0,0 +1,87 @@ +const quais = require('../../lib/commonjs/quais'); +const { printWalletInfo } = require('./utils'); +require('dotenv').config(); + +async function main() { + // Create provider + console.log('RPC URL: ', process.env.RPC_URL); + const provider = new quais.JsonRpcProvider(process.env.RPC_URL); + + // Create wallet and connect to provider + const mnemonic = quais.Mnemonic.fromPhrase(process.env.MNEMONIC); + const aliceQiWallet = quais.QiHDWallet.fromMnemonic(mnemonic); + const alicePaymentCode = aliceQiWallet.getPaymentCode(0); + aliceQiWallet.connect(provider); + + const bobMnemonic = quais.Mnemonic.fromPhrase("innocent perfect bus miss prevent night oval position aspect nut angle usage expose grace juice"); + const bobQiWallet = quais.QiHDWallet.fromMnemonic(bobMnemonic); + bobQiWallet.connect(provider); + const bobPaymentCode = bobQiWallet.getPaymentCode(0); + aliceQiWallet.openChannel(bobPaymentCode); + + console.log('Scanning Alice Qi wallet...'); + await aliceQiWallet.scan(quais.Zone.Cyprus1); + console.log('Alice Qi wallet scan complete'); + + printWalletInfo('Alice', aliceQiWallet); + + // Bob opens a channel with Alice + bobQiWallet.openChannel(alicePaymentCode); + console.log('Scanning Bob Qi wallet...'); + await bobQiWallet.scan(quais.Zone.Cyprus1); + console.log('Bob Qi wallet scan complete'); + printWalletInfo('Bob', bobQiWallet); + + // Alice sends 50 Qi to Bob + console.log('\nAlice sends 50 Qi to Bob'); + const tx = await aliceQiWallet.sendTransaction(bobPaymentCode, 50000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + // console.log('Transaction sent: ', tx); + console.log(`Transaction hash: ${tx.hash}`); + console.log(`Tx contains ${tx.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx.txInputs)}`); + console.log(`Tx contains ${tx.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response = await tx.wait(); + console.log('Transaction confirmed in block: ', response.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); + + console.log('\nAlice sends another 50 Qi to Bob'); + const tx2 = await aliceQiWallet.sendTransaction(bobPaymentCode, 50000n, quais.Zone.Cyprus1, quais.Zone.Cyprus1); + console.log(`Transaction hash: ${tx2.hash}`); + console.log(`Tx contains ${tx2.txInputs?.length} inputs`); + console.log(`Tx inputs: ${JSON.stringify(tx2.txInputs)}`); + console.log(`Tx contains ${tx2.txOutputs?.length} outputs`); + + console.log('Waiting for transaction to be confirmed...'); + const response2 = await tx2.wait(); + console.log('Transaction confirmed in block: ', response2.blockNumber); + + console.log('Syncing Alice Qi wallet...'); + await aliceQiWallet.sync(quais.Zone.Cyprus1); + console.log('Alice Qi wallet sync complete'); + + printWalletInfo('Alice', aliceQiWallet); + + console.log('Syncing Bob Qi wallet...'); + await bobQiWallet.sync(quais.Zone.Cyprus1); + console.log('Bob Qi wallet sync complete'); + printWalletInfo('Bob', bobQiWallet); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/examples/wallets/utils.js b/examples/wallets/utils.js new file mode 100644 index 00000000..3229874d --- /dev/null +++ b/examples/wallets/utils.js @@ -0,0 +1,132 @@ +const quais = require('../../lib/commonjs/quais'); +const printWalletInfo = (name, wallet) => { + const serializedWallet = wallet.serialize(); + + // Helper function to get addresses by type and status + const getAddressesByType = (type) => { + if (type == 'BIP44:external' || type == 'BIP44:change') { + return serializedWallet.addresses + .filter(addr => addr.derivationPath === type); + } + return serializedWallet.addresses + .filter(addr => !addr.derivationPath.startsWith('BIP44:')); + }; + + const printUnusedAddressesData = (type) => { + const addresses = getAddressesByType(type); + + // Find the index where the last group of UNUSED addresses starts + let lastUnusedGroupStartIndex = addresses.length; + for (let i = addresses.length - 1; i >= 0; i--) { + if (addresses[i].status !== 'UNUSED') { + break; + } + lastUnusedGroupStartIndex = i; + } + + // Filter addresses: UNUSED and not part of the last group + const filteredUnusedAddresses = addresses.filter((addr, index) => + addr.status === 'UNUSED' && index < lastUnusedGroupStartIndex + ); + + if (filteredUnusedAddresses.length > 0) { + const outpoints = serializedWallet.outpoints; + for (const addr of filteredUnusedAddresses) { + const outpoint = outpoints.find(outpoint => outpoint.address === addr.address); + if (outpoint) { + console.log(`\tOutpoint for UNUSED address ${addr.address}: ${JSON.stringify(outpoint)}`); + } else { + console.log(`\tOutpoint for UNUSED address ${addr.address} not found`); + } + } + } + }; + + const summary = { + 'BIP44 External Addresses': getAddressesByType('BIP44:external').length, + 'BIP44 Change Addresses': getAddressesByType('BIP44:change').length, + 'BIP47 Addresses': getAddressesByType('BIP47').length, + 'Available Outpoints': serializedWallet.outpoints.length, + 'Pending Outpoints': serializedWallet.pendingOutpoints.length, + 'Sender Payment Code Info': Object.keys(serializedWallet.senderPaymentCodeInfo).length, + 'Coin Type': serializedWallet.coinType, + 'Version': serializedWallet.version + }; + console.log(`\n**************************************************** ${name} Qi wallet summary: ************************************************\n`); + console.table(summary); + + // Print BIP44 External Addresses + console.log(`\n${name} BIP44 External Addresses:`); + printAddressTable(getAddressesByType('BIP44:external')); + printUnusedAddressesData('BIP44:external'); + + // Print BIP44 Change Addresses + console.log(`\n${name} BIP44 Change Addresses:`); + printAddressTable(getAddressesByType('BIP44:change')); + printUnusedAddressesData('BIP44:change'); + + // Print BIP47 Addresses + console.log(`\n${name} BIP47 Addresses:`); + printAddressTable(getAddressesByType('BIP47')); + printUnusedAddressesData('BIP47'); + + // Print Outpoints + console.log(`\n${name} Wallet Outpoints:`); + printOutpointTable(serializedWallet.outpoints); + + // Print Pending Outpoints + // console.log(`\n${name} Wallet Pending Outpoints:`); + // printOutpointTable(serializedWallet.pendingOutpoints); + + // Print Sender Payment Code Info + console.log(`\n${name} Wallet Sender Payment Code Info:`); + printPaymentCodeInfo(serializedWallet.senderPaymentCodeInfo); + + // Print wallet Qi balance + const walletBalance = wallet.getBalanceForZone(quais.Zone.Cyprus1); + console.log(`\n=> ${name} Wallet balance: ${quais.formatQi(walletBalance)} Qi\n`); +} + +function printAddressTable(addresses) { + const addressTable = addresses.map(addr => ({ + PubKey: addr.pubKey, + Address: addr.address, + Index: addr.index, + Change: addr.change ? 'Yes' : 'No', + Zone: addr.zone, + Status: addr.status, + DerivationPath: addr.derivationPath + })); + console.table(addressTable); +} + +function printOutpointTable(outpoints) { + const outpointTable = outpoints.map(outpoint => ({ + Address: outpoint.address, + Denomination: outpoint.outpoint.denomination, + Index: outpoint.outpoint.index, + TxHash: outpoint.outpoint.txhash, + Zone: outpoint.zone, + Account: outpoint.account, + })); + console.table(outpointTable); +} + +function printPaymentCodeInfo(paymentCodeInfo) { + for (const [paymentCode, addressInfoArray] of Object.entries(paymentCodeInfo)) { + console.log(`Payment Code: ${paymentCode}`); + const paymentCodeTable = addressInfoArray.map(info => ({ + Address: info.address, + PubKey: info.pubKey, + Index: info.index, + Zone: info.zone, + Status: info.status + })); + console.table(paymentCodeTable); + } +} + + +module.exports = { + printWalletInfo, +};