UNPKG

p-sdk-wallet

Version:

A comprehensive wallet SDK for React Native (pwc), supporting multi-chain and multi-account features.

300 lines (299 loc) 11.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.QRCodeService = void 0; const EncryptionService_1 = require("../crypto/EncryptionService"); /** * Service for generating and scanning QR codes for wallet operations. * Supports address sharing, mnemonic backup, and transaction signing. */ class QRCodeService { /** * Generates QR code data for a wallet address * @param address - The wallet address to encode * @param label - Optional label for the address * @returns QR code data string */ static generateAddressQR(address, label) { const qrData = { type: 'address', data: { address: address, label: label || '', }, timestamp: Date.now(), version: this.QR_VERSION, }; return JSON.stringify(qrData); } /** * Generates QR code data for encrypted mnemonic backup * @param mnemonic - The mnemonic phrase to encrypt * @param password - Password for encryption * @param chainType - Type of blockchain (evm, solana, etc.) * @param accountCount - Number of accounts to import * @returns Promise resolving to QR code data string * @throws Error if encryption fails */ static async generateMnemonicQR(mnemonic, password, chainType = 'evm', accountCount = 1) { // Encrypt the mnemonic const encryptedMnemonic = await EncryptionService_1.EncryptionService.encrypt(mnemonic, password); const walletImportData = { encryptedMnemonic, chainType, accountCount, }; const qrData = { type: 'wallet-import', data: walletImportData, timestamp: Date.now(), version: this.QR_VERSION, }; return JSON.stringify(qrData); } /** * Generates QR code data for transaction signing * @param transactionData - Transaction details * @returns QR code data string */ static generateTransactionQR(transactionData) { const qrData = { type: 'transaction', data: transactionData, timestamp: Date.now(), version: this.QR_VERSION, }; return JSON.stringify(qrData); } /** * Parses QR code data and validates its structure * @param qrString - The QR code string to parse * @returns Parsed QR code data * @throws Error if QR data is invalid or unsupported */ static parseQRData(qrString) { try { const qrData = JSON.parse(qrString); // Validate required fields if (!qrData.type || !qrData.data || !qrData.timestamp || !qrData.version) { throw new Error('Invalid QR code format: missing required fields'); } // Validate type const validTypes = ['address', 'mnemonic', 'transaction', 'wallet-import']; if (!validTypes.includes(qrData.type)) { throw new Error(`Unsupported QR code type: ${qrData.type}`); } // Validate timestamp (not too old, not in future) const now = Date.now(); const maxAge = 24 * 60 * 60 * 1000; // 24 hours if (qrData.timestamp > now || qrData.timestamp < now - maxAge) { throw new Error('QR code is too old or has invalid timestamp'); } return qrData; } catch (error) { if (error instanceof SyntaxError) { throw new Error('Invalid QR code: not a valid JSON'); } throw error; } } /** * Extracts wallet address from QR code data * @param qrString - The QR code string * @returns The wallet address * @throws Error if QR code doesn't contain address data */ static extractAddress(qrString) { const qrData = this.parseQRData(qrString); if (qrData.type !== 'address') { throw new Error('QR code does not contain address data'); } const address = qrData.data.address; if (!address || typeof address !== 'string') { throw new Error('Invalid address in QR code'); } return address; } /** * Extracts wallet import data from QR code * @param qrString - The QR code string * @returns Wallet import data * @throws Error if QR code doesn't contain wallet import data */ static extractWalletImportData(qrString) { const qrData = this.parseQRData(qrString); if (qrData.type !== 'wallet-import') { throw new Error('QR code does not contain wallet import data'); } const importData = qrData.data; // Validate required fields if (!importData.encryptedMnemonic || !importData.chainType) { throw new Error('Invalid wallet import data in QR code'); } return importData; } /** * Extracts transaction data from QR code * @param qrString - The QR code string * @returns Transaction data * @throws Error if QR code doesn't contain transaction data */ static extractTransactionData(qrString) { const qrData = this.parseQRData(qrString); if (qrData.type !== 'transaction') { throw new Error('QR code does not contain transaction data'); } const txData = qrData.data; // Validate required fields if (!txData.txType || !txData.chainId || !txData.to || !txData.amount) { throw new Error('Invalid transaction data in QR code'); } return txData; } /** * Validates if a string is a valid Ethereum address * @param address - Address to validate * @returns True if valid Ethereum address */ static isValidEthereumAddress(address) { return /^0x[a-fA-F0-9]{40}$/.test(address); } /** * Validates if a string is a valid Solana address * @param address - Address to validate * @returns True if valid Solana address */ static isValidSolanaAddress(address) { return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address); } /** * Gets the address type based on format * @param address - Address to check * @returns 'ethereum', 'solana', or 'unknown' */ static getAddressType(address) { if (this.isValidEthereumAddress(address)) { return 'ethereum'; } if (this.isValidSolanaAddress(address)) { return 'solana'; } return 'unknown'; } /** * Clears sensitive data from memory * @param data - Sensitive data to clear */ static clearSensitiveData(data) { // Overwrite the string with zeros (as much as possible in JS) if (typeof data === 'string') { // Note: JavaScript strings are immutable, so we can't directly clear them // But we can help the garbage collector by removing references setTimeout(() => { // This helps reduce the time sensitive data stays in memory }, 0); } } // --- Utility Functions for External Use --- /** * Generates QR code data for a wallet address from Vault. * Utility function to be used outside of Vault class. * @param vault - The vault instance * @param accountIndex - Index of the account to generate QR for (default: 0) * @param label - Optional label for the address * @returns QR code data string * @throws Error if account index is invalid * * @example * ```typescript * import { QRCodeService, Vault } from 'pwc-wallet-sdk'; * * // Generate QR for first account * const qrData = QRCodeService.generateAddressQRFromVault(vault, 0, 'My Wallet'); * ``` */ static generateAddressQRFromVault(vault, accountIndex = 0, label) { const accounts = vault.getAccounts(); if (accountIndex < 0 || accountIndex >= accounts.length) { throw new Error(`Invalid account index: ${accountIndex}. Available accounts: ${accounts.length}`); } const account = accounts[accountIndex]; return this.generateAddressQR(account.address, label || account.name); } /** * Generates QR code data for encrypted mnemonic backup from Vault. * Utility function to be used outside of Vault class. * @param vault - The vault instance * @param password - Password for encryption * @param accountCount - Number of accounts to import (default: 1) * @returns Promise resolving to QR code data string * @throws Error if encryption fails or no HD keyring available * * @example * ```typescript * import { QRCodeService, Vault } from 'pwc-wallet-sdk'; * * // Generate backup QR with password * const qrData = await QRCodeService.generateMnemonicQRFromVault(vault, 'my-secure-password', 3); * ``` */ static async generateMnemonicQRFromVault(vault, password, accountCount = 1) { const hdKeyring = vault.keyrings.find((k) => k.type === 'HD'); if (!hdKeyring) { throw new Error('No HD keyring available. Cannot generate mnemonic QR.'); } // Get the mnemonic from the HD keyring const mnemonic = hdKeyring.getMnemonic(); if (!mnemonic) { throw new Error('Mnemonic not available in HD keyring.'); } const chainType = vault.chainId === 'solana' ? 'solana' : 'evm'; return this.generateMnemonicQR(mnemonic, password, chainType, accountCount); } /** * Validates QR code data without processing it. * @param qrString - QR code string to validate * @returns Object containing validation result and data type * * @example * ```typescript * // Validate QR code before processing * const validation = QRCodeService.validateQRCode(qrString); * console.log('QR type:', validation.type); * console.log('Is valid:', validation.isValid); * ``` */ static validateQRCode(qrString) { try { const qrData = this.parseQRData(qrString); return { isValid: true, type: qrData.type, data: qrData.data }; } catch (error) { return { isValid: false, type: 'unknown', data: null }; } } /** * Generates an EIP-681 compatible QR string for wallet address. * This format is compatible with Metamask, Binance, Trust Wallet, etc. * @param address - The wallet address to encode * @returns EIP-681 URI string (e.g., 'ethereum:0x1234...') * @example * ```typescript * const qrString = QRCodeService.generateEIP681AddressQR('0x1234...'); * // Use this string with a QR code generator for maximum wallet compatibility * ``` */ static generateEIP681AddressQR(address) { return `ethereum:${address}`; } } exports.QRCodeService = QRCodeService; QRCodeService.QR_VERSION = '1.0';