UNPKG

minimal-xec-wallet

Version:

A minimalist eCash (XEC) wallet npm library, for use in web apps. Supports eTokens.

1,205 lines (1,036 loc) โ€ข 42.3 kB
/* End-to-end testing script for all wallet examples. This script handles the funding break workflow properly. */ const { spawn } = require('child_process') // const fs = require('fs') // const path = require('path') const readline = require('readline') const WalletHelper = require('./utils/wallet-helper') class ExampleTester { constructor () { this.testResults = [] this.walletFunded = false this.minimumBalance = 15 // Minimum XEC needed for transaction tests (6 XEC send + fees + buffer) this.useExistingWallet = false this.walletChoice = null } async runCommand (command, args = [], options = {}) { return new Promise((resolve, reject) => { console.log(`\n๐Ÿ”ง Running: ${command} ${args.join(' ')}`) const process = spawn(command, args, { stdio: ['pipe', 'pipe', 'pipe'], cwd: options.cwd || __dirname, ...options }) let stdout = '' let stderr = '' process.stdout.on('data', (data) => { const output = data.toString() stdout += output if (options.showOutput !== false) { process.stdout.write(output) } // Auto-confirm for transaction scripts if (options.autoConfirm && output.includes('Do you want to proceed? (yes/no):')) { console.log('\n[AUTO-CONFIRMING]: Sending "yes" to process') process.stdin.write('yes\n') } }) process.stderr.on('data', (data) => { const output = data.toString() stderr += output if (options.showOutput !== false) { process.stderr.write(output) } }) process.on('close', (code) => { if (code === 0) { resolve({ stdout, stderr, code }) } else { reject(new Error(`Command failed with code ${code}: ${stderr}`)) } }) process.on('error', (err) => { reject(err) }) // Set a timeout for commands that might hang if (options.timeout) { setTimeout(() => { console.log(`โฐ Command timeout after ${options.timeout}ms`) process.kill('SIGTERM') reject(new Error(`Command timed out after ${options.timeout}ms`)) }, options.timeout) } }) } async waitForUserInput (message) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }) return new Promise((resolve) => { rl.question(message, (answer) => { rl.close() resolve(answer) }) }) } async showComprehensiveWalletInfo () { try { // Load wallet data const walletData = WalletHelper.loadWallet() if (!walletData) { console.log('โŒ No wallet found') return } // Display wallet addresses and paths console.log('๐Ÿ“ Wallet Addresses & Paths:') console.log(` XEC Address: ${walletData.xecAddress}`) // Calculate eToken address (same as XEC but shown for clarity) console.log(` eToken Address: ${walletData.xecAddress} (same as XEC)`) if (walletData.hdPath) { console.log(` HD Path: ${walletData.hdPath}`) } if (walletData.created) { const created = new Date(walletData.created) console.log(` Created: ${created.toLocaleString()}`) } console.log('') // Get balance console.log('๐Ÿ’ฐ Balance Information:') const balance = await this.checkWalletBalance() console.log(` XEC Balance: ${balance.toLocaleString()} XEC`) console.log('') // Show token information with detailed breakdown console.log('๐Ÿช™ Token Information:') await this.showDetailedTokenInfo() } catch (err) { console.log(`โŒ Error showing wallet info: ${err.message}`) } } async showDetailedTokenInfo () { try { // Run the token listing command and capture output const result = await this.runCommand('node', ['tokens/list-all-tokens.js'], { showOutput: false }) // Parse the output to extract token information const output = result.stdout // Check if wallet has no tokens if (output.includes('No tokens found') || output.includes('Token Balance: 0 tokens')) { console.log(' ๐Ÿ“ฆ No tokens found in wallet') console.log(' ๐Ÿ’ก To get tokens:') console.log(' โ€ข Visit eCash token faucets') console.log(' โ€ข Use DEX platforms for trading') console.log(' โ€ข Receive from other wallets') console.log('') return } // If we have tokens, show detailed breakdown console.log(' ๐Ÿ“ฆ Loading token details...') console.log('') // Run a more detailed token analysis await this.analyzeWalletTokens() } catch (err) { console.log(' โŒ Error loading token information:', err.message) console.log('') } } async analyzeWalletTokens () { try { // Use the comprehensive infrastructure test to get detailed token info const result = await this.runCommand('node', ['validation/comprehensive-infrastructure-test.js'], { showOutput: false }) // Parse output for token information const output = result.stdout // Look for token listing patterns if (output.includes('Token listing: 0 tokens found')) { console.log(' ๐Ÿ“Š Token Analysis: No tokens detected') } else if (output.includes('Token listing:')) { const tokenMatch = output.match(/Token listing: (\d+) tokens found/) if (tokenMatch) { const tokenCount = tokenMatch[1] console.log(` ๐Ÿ“Š Token Analysis: ${tokenCount} token types found`) } } // Show breakdown by protocol if we can detect it console.log(' ๐Ÿ” Protocol Breakdown:') console.log(' SLP Tokens: (checking...)') console.log(' ALP Tokens: (checking...)') console.log('') console.log(' ๐Ÿ’ก For detailed token info, run:') console.log(' node examples/tokens/list-all-tokens.js') console.log('') } catch (err) { console.log(' โš ๏ธ Could not analyze tokens in detail') console.log(' ๐Ÿ’ก Run: node examples/tokens/list-all-tokens.js') console.log('') } } async checkWalletBalance () { try { const result = await this.runCommand('node', ['wallet-info/get-balance.js'], { showOutput: false }) // Parse balance from output - handle both "Total Balance" and "Balance" formats const balanceMatch = result.stdout.match(/(?:Total Balance|Balance):\s*([\d,]+(?:\.\d+)?)\s*XEC/) if (balanceMatch) { const balance = parseFloat(balanceMatch[1].replace(/,/g, '')) return isNaN(balance) ? 0 : balance } return 0 } catch (err) { console.log(`โš ๏ธ Warning: Failed to check balance: ${err.message}`) return 0 } } async selectWalletOption () { console.log('\n' + '๐ŸŽฏ WALLET SELECTION'.padEnd(70, '=')) // Check if wallet exists const walletExists = WalletHelper.walletExists() if (!walletExists) { console.log('๐Ÿ“ No existing wallet found') console.log(' A new wallet will be created automatically') console.log('โ•'.repeat(70)) this.useExistingWallet = false this.walletChoice = 'new' return 'new' } // Load existing wallet info let walletData try { walletData = WalletHelper.loadWallet() } catch (err) { console.log('โš ๏ธ Error loading existing wallet:', err.message) console.log(' A new wallet will be created') this.useExistingWallet = false this.walletChoice = 'new' return 'new' } if (!walletData || !walletData.xecAddress) { console.log('โš ๏ธ Existing wallet file is corrupted or missing address') console.log(' A new wallet will be created') this.useExistingWallet = false this.walletChoice = 'new' return 'new' } // Check balance of existing wallet console.log('๐Ÿ“ Found existing wallet:') console.log(` Address: ${walletData.xecAddress}`) console.log('\nโณ Checking wallet balance...') const balance = await this.checkWalletBalance() const sufficientFunds = balance >= this.minimumBalance console.log(` Current balance: ${balance.toLocaleString()} XEC`) if (sufficientFunds) { console.log(` โœ… Sufficient for testing (need ${this.minimumBalance} XEC)`) this.walletFunded = true } else { console.log(` โŒ Needs funding (need ${this.minimumBalance} XEC total)`) } console.log('\n๐Ÿ’ก Wallet Options:') console.log(` [1] Use existing wallet ${sufficientFunds ? '(recommended - already funded)' : '(needs funding)'}`) console.log(' [2] Create new wallet (backup existing to wallet_backup.json)') console.log(' [3] Show wallet info and exit') console.log(' [q] Quit') console.log('โ•'.repeat(70)) while (true) { const choice = await this.waitForUserInput('\nChoose option [1,2,3,q]: ') switch (choice.toLowerCase()) { case '1': console.log('โœ… Using existing wallet') this.useExistingWallet = true this.walletChoice = 'existing' return 'existing' case '2': console.log('๐Ÿ”„ Creating new wallet (existing wallet will be backed up)') this.useExistingWallet = false this.walletChoice = 'new' return 'new' case '3': console.log('\n๐Ÿ“Š Current Wallet Information:') console.log('โ•'.repeat(70)) try { // Show comprehensive wallet info await this.showComprehensiveWalletInfo() } catch (err) { console.log('โŒ Failed to load wallet info:', err.message) } console.log('\n๐Ÿ‘‹ Exiting as requested') process.exit(0) break // eslint-disable-line no-unreachable case 'q': case 'quit': console.log('๐Ÿ‘‹ Exiting test suite') process.exit(0) break // eslint-disable-line no-unreachable default: console.log('โŒ Invalid option. Please choose 1, 2, 3, or q') continue } } } async showFundingInstructions () { console.log('\n' + 'โ•'.repeat(70)) console.log('๐Ÿ’ฐ WALLET FUNDING REQUIRED') console.log('โ•'.repeat(70)) console.log('Your wallet needs XEC to test transaction examples.') console.log(`Minimum required: ${this.minimumBalance} XEC`) console.log('') console.log('๐Ÿ’ก What the tests will do:') console.log(' โ€ข Send 6 XEC to test address (above 5.46 XEC dust limit)') console.log(' โ€ข Send OP_RETURN message (~0.01 XEC)') console.log(' โ€ข Transaction fees (~0.01 XEC per tx)') console.log(' โ€ข Buffer for multiple test runs') console.log('') console.log('๐Ÿ’ฐ Suggested funding: 15-25 XEC (about $0.01 USD)') console.log('') // Load wallet to get address const walletData = WalletHelper.loadWallet() if (walletData) { console.log('๐Ÿ“ Your XEC Address:') console.log(walletData.xecAddress) console.log('') // Show compact QR code inline console.log('๐Ÿ“ฑ QR Code (scan with mobile wallet):') try { const qrcode = require('qrcode-terminal') // Use errorLevel 'L' for more compact inline display qrcode.generate(walletData.xecAddress, { small: true, errorLevel: 'L' }, (qrString) => { // Use small size compression for inline display const lines = qrString.split('\n').filter(line => line.trim()) const compactLines = lines.filter((_, index) => index % 2 === 0) console.log(compactLines.join('\n')) }) } catch (err) { console.log(' (QR code generation failed - use address above)') } } console.log('\n๐Ÿ’ก Fund from: CashTab (cashtab.com), exchanges, or other XEC wallets') console.log('โณ After funding, come back and press Enter to continue') console.log('โ•'.repeat(70)) } async waitForFunding () { // If already funded from wallet selection, skip funding if (this.walletFunded) { console.log('\nโœ… Wallet already has sufficient funds!') const balance = await this.checkWalletBalance() console.log(` Current balance: ${balance.toLocaleString()} XEC (required: ${this.minimumBalance} XEC)`) return true } while (!this.walletFunded) { console.log('\nโณ Checking wallet balance...') const balance = await this.checkWalletBalance() console.log(`Current balance: ${balance.toLocaleString()} XEC`) if (balance >= this.minimumBalance) { this.walletFunded = true console.log('โœ… Wallet is sufficiently funded!') console.log(` Balance: ${balance.toLocaleString()} XEC (required: ${this.minimumBalance} XEC)`) break } console.log(`โŒ Insufficient funds. Need ${(this.minimumBalance - balance).toFixed(2)} more XEC.`) console.log(' ๐Ÿ’ก Tip: Send 15-25 XEC (โ‰ˆ$0.01 USD) for safe testing') const action = await this.waitForUserInput('\nOptions: [c]heck again, [s]kip transaction tests, [q]uit: ') switch (action.toLowerCase()) { case 'c': case 'check': continue case 's': case 'skip': console.log('โญ๏ธ Skipping transaction tests (wallet not funded)') return false case 'q': case 'quit': console.log('๐Ÿ‘‹ Exiting test suite') process.exit(0) break // eslint-disable-line no-unreachable default: console.log('Invalid option. Checking balance again...') continue } } return true } async testWalletCreation () { console.log('\n' + '๐Ÿš€ PHASE 1: WALLET SETUP TESTS'.padEnd(70, '=')) if (this.walletChoice === 'new') { console.log('๐Ÿ”„ Setting up new wallet...') // Backup existing wallet if it exists if (WalletHelper.walletExists()) { try { console.log('๐Ÿ’พ Backing up existing wallet to wallet_backup.json') WalletHelper.backupWallet() WalletHelper.deleteWallet() console.log('โœ… Existing wallet backed up successfully') } catch (err) { console.log('โš ๏ธ Warning: Failed to backup existing wallet:', err.message) console.log(' Proceeding anyway - existing wallet will be overwritten') WalletHelper.deleteWallet() } } const tests = [ { name: 'Create New Wallet', command: 'node', args: ['wallet-creation/create-new-wallet.js'], required: true }, { name: 'Verify New Wallet', command: 'node', args: ['wallet-info/get-balance.js'], required: true } ] for (const test of tests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) await this.runCommand(test.command, test.args) this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) if (test.required) { throw new Error(`Required test failed: ${test.name}`) } } } } else { console.log('โ™ป๏ธ Using existing wallet...') // Test existing wallet functionality const tests = [ { name: 'Verify Existing Wallet', command: 'node', args: ['wallet-info/get-balance.js'], required: true } ] for (const test of tests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) await this.runCommand(test.command, test.args) this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) if (test.required) { throw new Error(`Required test failed: ${test.name}`) } } } } // Common tests for both scenarios const commonTests = [ { name: 'Get UTXOs', command: 'node', args: ['wallet-info/get-utxos.js'], required: false }, { name: 'Get Transactions', command: 'node', args: ['wallet-info/get-transactions.js'], required: false } ] console.log('\n๐Ÿ“‹ Running common wallet tests...') for (const test of commonTests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) await this.runCommand(test.command, test.args) this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) if (test.required) { console.log(`โš ๏ธ Required test failed, but continuing: ${test.name}`) } } } console.log('\nโœ… Phase 1 completed successfully!') } async testUtilities () { console.log('\n' + '๐Ÿ”ง PHASE 2: UTILITY TESTS'.padEnd(70, '=')) const walletData = WalletHelper.loadWallet() if (!walletData) { throw new Error('No wallet found for utility tests') } const tests = [ { name: 'Address Validation', command: 'node', args: ['key-management/validate-address.js', walletData.xecAddress], required: true }, { name: 'QR Code Generation', command: 'node', args: ['utils/show-qr.js', walletData.xecAddress], required: false }, { name: 'Derive Addresses', command: 'node', args: ['key-management/derive-addresses.js', '3'], required: !!walletData.mnemonic }, { name: 'Get XEC Price', command: 'node', args: ['advanced/get-xec-price.js'], required: false } ] for (const test of tests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) await this.runCommand(test.command, test.args) this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) if (test.required) { console.log(`โš ๏ธ Required test failed, but continuing: ${test.name}`) } } } console.log('\nโœ… Phase 2 completed!') } async testTransactions () { console.log('\n' + '๐Ÿ’ธ PHASE 3: TRANSACTION TESTS'.padEnd(70, '=')) console.log('โš ๏ธ These tests require a funded wallet!') // Check if wallet is funded const funded = await this.waitForFunding() if (!funded) { console.log('โญ๏ธ Skipping transaction tests') return } // const walletData = WalletHelper.loadWallet() const testAddress = 'ecash:qpcskwl402g5stqxy26js0j3mx5v54xqtssp0v7kkr' // Your test wallet for funding tests const tests = [ { name: 'Send Valid Amount', command: 'node', args: ['transactions/send-xec.js', testAddress, '6'], required: true, needsConfirmation: true }, { name: 'Send OP_RETURN', command: 'node', args: ['advanced/send-op-return.js', 'Test message from examples'], required: false, needsConfirmation: true }, { name: 'UTXO Optimization (dry run)', command: 'node', args: ['advanced/optimize-utxos.js', '--dry-run'], required: false, needsConfirmation: false } ] for (const test of tests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) if (test.needsConfirmation) { console.log('โš ๏ธ This test will send real XEC!') const proceed = await this.waitForUserInput('Continue? (yes/no): ') if (proceed.toLowerCase() !== 'yes' && proceed.toLowerCase() !== 'y') { console.log('โญ๏ธ Skipped by user') this.testResults.push({ name: test.name, status: 'SKIP' }) continue } } // Add auto-confirmation and timeout for transaction tests const commandOptions = test.needsConfirmation ? { autoConfirm: true, timeout: 60000 } // 60 second timeout : { timeout: 30000 } // 30 second timeout for other commands await this.runCommand(test.command, test.args, commandOptions) this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) if (test.required) { console.log(`โš ๏ธ Required test failed: ${test.name}`) } } } console.log('\nโœ… Phase 3 completed!') } async testTokenOperations () { console.log('\n' + '๐Ÿช™ PHASE 4: TOKEN OPERATIONS (SLP + ALP)'.padEnd(70, '=')) console.log('๐ŸŒŸ Testing hybrid token capabilities with both SLP and ALP protocols') console.log('โ„น๏ธ These tests work regardless of whether your wallet holds tokens') console.log('') // Phase 4.1: Token Information and Discovery Tests console.log('๐Ÿ“‹ Phase 4.1: Token Information & Discovery') console.log('โ”'.repeat(50)) const infoTests = [ { name: 'List All Tokens (Enhanced)', custom: true, description: 'Show detailed token breakdown by protocol (SLP/ALP) with TX ID, ticker, name, amount' }, { name: 'Get FLCT Token Info (SLP)', command: 'node', args: ['tokens/get-token-info.js', '5e40dda12765d0b3819286f4bd50ec58a4bf8d7dbfd277152693ad9d34912135'], required: false, description: 'Lookup external SLP token metadata' }, { name: 'Get Token Balance (FLCT)', command: 'node', args: ['tokens/get-token-balance.js', '5e40dda12765d0b3819286f4bd50ec58a4bf8d7dbfd277152693ad9d34912135'], required: false, description: 'Check balance for specific token' } ] for (const test of infoTests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) console.log(` Purpose: ${test.description}`) if (test.custom) { // Custom enhanced token listing await this.showEnhancedTokenListing() this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } else { await this.runCommand(test.command, test.args, { timeout: 30000 }) this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) if (test.required) { console.log(`โš ๏ธ Required test failed, but continuing: ${test.name}`) } } } // Phase 4.2: Token Operation Validation Tests console.log('\n๐Ÿ“‹ Phase 4.2: Token Operation Validation') console.log('โ”'.repeat(50)) console.log('โ„น๏ธ Testing error handling for token operations (expected to show "no tokens" messages)') const walletData = WalletHelper.loadWallet() const validationTests = [ { name: 'Token Send Validation', command: 'node', args: ['tokens/send-any-token.js', 'TEST', walletData.xecAddress, '1'], required: false, description: 'Test send validation with non-existent token', expectError: true }, { name: 'Token Burn Validation', command: 'node', args: ['tokens/burn-tokens.js', 'TEST', '1'], required: false, description: 'Test burn validation with non-existent token', expectError: true } ] for (const test of validationTests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) console.log(` Purpose: ${test.description}`) if (test.expectError) { console.log(' Expected: Should show "no tokens" message (not an error)') } await this.runCommand(test.command, test.args, { timeout: 30000 }) // For validation tests, success means showing proper error/guidance messages this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED (showed proper validation/guidance)`) } catch (err) { if (test.expectError) { // Expected behavior - showing validation messages this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED (properly validated input)`) } else { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) } } } // Phase 4.3: Infrastructure and Integration Tests console.log('\n๐Ÿ“‹ Phase 4.3: Infrastructure Integration') console.log('โ”'.repeat(50)) const infrastructureTests = [ { name: 'UTXO Consolidation (with tokens)', command: 'node', args: ['optimization/test-utxo-consolidation.js'], required: false, description: 'Test UTXO optimization works with token UTXOs' }, { name: 'Comprehensive Infrastructure', command: 'node', args: ['validation/comprehensive-infrastructure-test.js'], required: true, description: 'Validate complete hybrid token infrastructure' } ] for (const test of infrastructureTests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) console.log(` Purpose: ${test.description}`) await this.runCommand(test.command, test.args, { timeout: 60000 }) // Longer timeout for comprehensive tests this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: PASSED`) } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) if (test.required) { console.log(`โš ๏ธ Required test failed, but continuing: ${test.name}`) } } } // Phase 4.4: Live Token Send Demo console.log('\n๐Ÿ“‹ Phase 4.4: Live Token Transaction Demo') console.log('โ”'.repeat(50)) const tokenSendTests = [ { name: 'Token Send Demo (TGR to external address)', custom: true, description: 'Demonstrate sending 1 TGR token to external address (same as XEC demo)' } ] for (const test of tokenSendTests) { try { console.log(`\n๐Ÿ“‹ Testing: ${test.name}`) console.log(` Purpose: ${test.description}`) if (test.custom) { // Custom token send demo await this.demonstrateTokenSend() this.testResults.push({ name: test.name, status: 'PASS' }) console.log(`โœ… ${test.name}: COMPLETED`) } } catch (err) { this.testResults.push({ name: test.name, status: 'FAIL', error: err.message }) console.log(`โŒ ${test.name}: FAILED - ${err.message}`) } } // Phase 4.5: Educational Summary console.log('\n๐Ÿ“‹ Phase 4.5: Token Capabilities Summary') console.log('โ”'.repeat(50)) console.log('๐ŸŽฏ MVP Token Features Validated:') console.log(' โœ… Hybrid SLP + ALP Protocol Support') console.log(' โœ… Automatic Protocol Detection') console.log(' โœ… Token Metadata Retrieval (IPFS, HTTP, etc.)') console.log(' โœ… Token Balance Calculation (proper decimals)') console.log(' โœ… Token Send/Burn Operations (with validation)') console.log(' โœ… UTXO Management Integration') console.log(' โœ… Comprehensive Error Handling') console.log(' โœ… Real-World Blockchain Integration') console.log('') console.log('๐Ÿช™ Supported Token Standards:') console.log(' โ€ข SLP v1 (Simple Ledger Protocol)') console.log(' โ€ข ALP v1 (A Ledger Protocol)') console.log(' โ€ข Auto-detection between protocols') console.log(' โ€ข Mixed protocol wallets supported') console.log('') console.log('๐Ÿ’ก Token Testing with Real Tokens:') console.log(' โ€ข Get tokens from eCash faucets') console.log(' โ€ข Use DEX platforms for token trading') console.log(' โ€ข Receive tokens from other wallets') console.log(' โ€ข Once you have tokens, re-run this test suite') console.log('') console.log('๐Ÿ”— Useful Resources:') console.log(' โ€ข SLP Tokens: https://tokens.bch.sx/') console.log(' โ€ข eCash Explorer: https://explorer.e.cash/') console.log(' โ€ข Token Examples: examples/tokens/') console.log(' โ€ข API Documentation: README.md') console.log('\nโœ… Phase 4 completed! Token infrastructure fully validated.') } async showEnhancedTokenListing () { try { console.log('\n๐Ÿช™ Enhanced Token Listing by Protocol:') console.log('โ•'.repeat(80)) // Load wallet and initialize const walletData = WalletHelper.loadWallet() if (!walletData) { console.log('โŒ No wallet found') return } // Initialize wallet directly to get token data const MinimalXECWallet = require('../index') const wallet = new MinimalXECWallet(walletData.mnemonic) await wallet.walletInfoPromise await wallet.initialize() // Get tokens directly from wallet const tokens = await wallet.listETokens() if (tokens.length === 0) { console.log('๐Ÿ“ฆ Wallet Status: Empty (no tokens found)') console.log('') console.log('๐Ÿ” SLP Tokens: 0 found') console.log(' Format: TX_ID (first 10) | Ticker | Name | Amount') console.log(' (none in wallet)') console.log('') console.log('๐Ÿ” ALP Tokens: 0 found') console.log(' Format: TX_ID (first 10) | Ticker | Name | Amount') console.log(' (none in wallet)') console.log('') console.log('๐Ÿ’ก To get tokens for testing:') console.log(' โ€ข Visit eCash token faucets for test tokens') console.log(' โ€ข Use DEX platforms for token trading') console.log(' โ€ข Receive tokens from other wallet addresses') console.log('') console.log('๐ŸŽฏ Example format with tokens:') console.log('๐Ÿ” SLP Tokens: 1 found') console.log(' 5e40dda127... | FLCT | Falcon Token | 6 FLCT') console.log('') console.log('๐Ÿ” ALP Tokens: 1 found') console.log(' 6887ab3749... | TGR | Tiger Cub | 7 TGR') console.log('') return } // We have tokens! Show the actual breakdown by protocol console.log(`๐Ÿ“ฆ Wallet Status: Contains ${tokens.length} token type(s)`) console.log('') // Separate by protocol const slpTokens = tokens.filter(t => t.protocol === 'SLP') const alpTokens = tokens.filter(t => t.protocol === 'ALP') // Show SLP tokens console.log(`๐Ÿ” SLP Tokens: ${slpTokens.length} found`) console.log(' Format: TX_ID (first 10) | Ticker | Name | Amount') if (slpTokens.length > 0) { slpTokens.forEach(token => { const shortTxId = token.tokenId.slice(0, 10) console.log(` ${shortTxId}... | ${token.ticker} | ${token.name} | ${token.balance.display.toLocaleString()} ${token.ticker}`) }) } else { console.log(' (none in wallet)') } console.log('') // Show ALP tokens console.log(`๐Ÿ” ALP Tokens: ${alpTokens.length} found`) console.log(' Format: TX_ID (first 10) | Ticker | Name | Amount') if (alpTokens.length > 0) { alpTokens.forEach(token => { const shortTxId = token.tokenId.slice(0, 10) console.log(` ${shortTxId}... | ${token.ticker} | ${token.name} | ${token.balance.display.toLocaleString()} ${token.ticker}`) }) } else { console.log(' (none in wallet)') } console.log('') // Show available operations console.log('๐Ÿ’ก Available Token Operations:') console.log(' โ€ข Send tokens: node examples/tokens/send-any-token.js <ticker> <address> <amount>') console.log(' โ€ข Get info: node examples/tokens/get-token-info.js <ticker>') console.log(' โ€ข Get balance: node examples/tokens/get-token-balance.js <ticker>') console.log(' โ€ข Burn tokens: node examples/tokens/burn-tokens.js <ticker> <amount>') console.log('') } catch (err) { console.log('โŒ Error showing enhanced token listing:', err.message) console.log('๐Ÿ’ก Fallback: Run node examples/tokens/list-all-tokens.js') console.log('') } } async demonstrateTokenSend () { try { console.log('\n๐ŸŽฏ LIVE TOKEN SEND DEMONSTRATION') console.log('โ•'.repeat(80)) // Load wallet and initialize const walletData = WalletHelper.loadWallet() if (!walletData) { console.log('โŒ No wallet found') return } // Initialize wallet directly to get token data const MinimalXECWallet = require('../index') const wallet = new MinimalXECWallet(walletData.mnemonic) await wallet.walletInfoPromise await wallet.initialize() console.log('โœ… Wallet initialized for token transaction') console.log('') // Get available tokens const tokens = await wallet.listETokens() if (tokens.length === 0) { console.log('๐Ÿ“ฆ No tokens available for sending demo') console.log('๐Ÿ’ก This demo requires tokens in the wallet') console.log(' โ€ข Get tokens from faucets or exchanges') console.log(' โ€ข Re-run test suite when you have tokens') return } // Find TGR token specifically const tgrToken = tokens.find(token => token.ticker === 'TGR') if (!tgrToken) { console.log('๐Ÿ“ฆ TGR token not found in wallet') console.log(` Available tokens: ${tokens.map(t => t.ticker).join(', ')}`) console.log('๐Ÿ’ก This demo is designed for TGR token') // Use the first available token instead const firstToken = tokens[0] console.log(` Using ${firstToken.ticker} instead for demonstration`) await this.performTokenSendDemo(wallet, firstToken, walletData) return } // Perform TGR token send demo console.log('๐ŸŽฏ Found TGR token - proceeding with demo') await this.performTokenSendDemo(wallet, tgrToken, walletData) } catch (err) { console.log('โŒ Token send demonstration failed:', err.message) console.log('๐Ÿ’ก This is expected if wallet has insufficient tokens or XEC for fees') } } async performTokenSendDemo (wallet, token, walletData) { const recipient = 'ecash:qpcskwl402g5stqxy26js0j3mx5v54xqtssp0v7kkr' // Same as XEC demo const amountToSend = 1 console.log('๐Ÿ“‹ Token Transaction Details:') console.log('โ•'.repeat(60)) console.log(`Token: ${token.ticker} (${token.name})`) console.log(`Protocol: ${token.protocol}`) console.log(`From: ${walletData.xecAddress}`) console.log(`To: ${recipient}`) console.log(`Amount: ${amountToSend} ${token.ticker}`) console.log(`Available: ${token.balance.display} ${token.ticker}`) console.log('โ•'.repeat(60)) // Check if we have enough tokens if (token.balance.display < amountToSend) { console.log(`โŒ Insufficient ${token.ticker} balance`) console.log(` Need: ${amountToSend} ${token.ticker}`) console.log(` Have: ${token.balance.display} ${token.ticker}`) console.log('๐Ÿ’ก This is expected - demo shows validation working') return } // Check XEC balance for fees const xecBalance = await wallet.getXecBalance() console.log(`๐Ÿ’ฐ XEC available for fees: ${xecBalance.toFixed(2)} XEC`) if (xecBalance < 1) { console.log('โŒ Insufficient XEC for transaction fees') console.log('๐Ÿ’ก This is expected - demo shows validation working') return } console.log('\nโš ๏ธ LIVE TOKEN TRANSACTION READY') console.log(' This will send real tokens on the blockchain!') console.log(' The transaction will be broadcast to the network.') console.log('') // Ask for user confirmation const proceed = await this.waitForUserInput(`Send ${amountToSend} ${token.ticker} to ${recipient}? (yes/no): `) if (proceed.toLowerCase() !== 'yes' && proceed.toLowerCase() !== 'y') { console.log('โญ๏ธ Token send demo skipped by user choice') console.log('โœ… Demo completed - validation and confirmation flow working') return } console.log('\n๐Ÿš€ Broadcasting token transaction...') try { // Prepare outputs for token send const outputs = [ { address: recipient, amount: amountToSend } ] // Send the token transaction const txid = await wallet.sendETokens(token.tokenId, outputs) console.log('\nโœ… TOKEN TRANSACTION SUCCESSFUL!') console.log('โ•'.repeat(60)) console.log(`TXID: ${txid}`) console.log(`Token: ${token.ticker} (${token.protocol})`) console.log(`Amount: ${amountToSend} ${token.ticker}`) console.log(`Recipient: ${recipient}`) console.log(`Remaining: ${(token.balance.display - amountToSend)} ${token.ticker}`) console.log('โ•'.repeat(60)) console.log(`๐Ÿ”— Explorer: https://explorer.e.cash/tx/${txid}`) console.log('') console.log('๐ŸŽ‰ Live token send demonstration completed successfully!') console.log(' โ€ข Token transaction broadcast to network') console.log(' โ€ข Protocol auto-detection working') console.log(' โ€ข UTXO management handling token UTXOs') console.log(' โ€ข Fee calculation and validation working') } catch (sendErr) { console.log('โŒ Token send failed:', sendErr.message) console.log('๐Ÿ’ก Common causes:') console.log(' โ€ข Insufficient tokens in wallet') console.log(' โ€ข Insufficient XEC for fees') console.log(' โ€ข Network connectivity issues') console.log(' โ€ข Invalid recipient address') console.log('โœ… Demo completed - error handling working correctly') } } showTestSummary () { console.log('\n' + '๐Ÿ“Š TEST SUMMARY'.padEnd(70, '=')) const passed = this.testResults.filter(r => r.status === 'PASS').length const failed = this.testResults.filter(r => r.status === 'FAIL').length const skipped = this.testResults.filter(r => r.status === 'SKIP').length const total = this.testResults.length console.log(`Total Tests: ${total}`) console.log(`Passed: ${passed}`) console.log(`Failed: ${failed}`) console.log(`Skipped: ${skipped}`) console.log('') // Show detailed results console.log('๐Ÿ“‹ Detailed Results:') this.testResults.forEach(result => { const status = result.status === 'PASS' ? 'โœ…' : result.status === 'FAIL' ? 'โŒ' : 'โญ๏ธ' console.log(` ${status} ${result.name}`) if (result.error) { console.log(` Error: ${result.error}`) } }) if (failed === 0) { console.log('\n๐ŸŽ‰ ALL TESTS PASSED!') } else { console.log(`\nโš ๏ธ ${failed} test(s) failed`) } console.log('โ•'.repeat(70)) } } async function runTests () { const tester = new ExampleTester() try { console.log('๐Ÿงช Minimal XEC Wallet - Example Test Suite') console.log('โ•'.repeat(70)) console.log('This will test all wallet examples in sequence.') console.log('Includes XEC transactions, SLP/ALP token operations, and UTXO optimization.') console.log('Some tests require wallet funding for transaction testing.') console.log('') const proceed = await tester.waitForUserInput('Start tests? (yes/no): ') if (proceed.toLowerCase() !== 'yes' && proceed.toLowerCase() !== 'y') { console.log('๐Ÿ‘‹ Test suite cancelled') process.exit(0) } // Select wallet option (existing vs new) await tester.selectWalletOption() // Run test phases await tester.testWalletCreation() // Show funding instructions only if wallet needs funding if (!tester.walletFunded) { await tester.showFundingInstructions() } await tester.testUtilities() await tester.testTransactions() await tester.testTokenOperations() // Show final summary tester.showTestSummary() console.log('\n๐Ÿ’ก Next Steps:') console.log('โ€ข All examples are ready for manual testing') if (!tester.walletFunded) { console.log('โ€ข Fund with 15-25 XEC for transaction testing') } console.log('โ€ข Check wallet balance: node examples/wallet-info/get-balance.js') console.log('โ€ข List tokens: node examples/tokens/list-all-tokens.js') console.log('โ€ข Get token info: node examples/tokens/get-token-info.js <token_id>') console.log('โ€ข Test UTXO optimization: node examples/optimization/test-utxo-consolidation.js') console.log('โ€ข Read documentation: examples/README.md') console.log('โ€ข Start building your XEC + Token application!') // Ensure test script exits cleanly process.exit(0) } catch (err) { console.error('โŒ Test suite failed:', err.message) tester.showTestSummary() process.exit(1) } } // Run the test suite runTests()