const {ipcRenderer} = require("electron"); class PaperclipWallets { constructor() { this.addressList = []; this.price = 0; } _getPrice() { return this.price; } _setPrice(price) { this.price = price; } getAddressList() { return this.addressList; } clearAddressList() { this.addressList = []; } getAddressExists(address) { if (address) { return this.addressList.indexOf(address) > -1; } return false; } addAddressToList(address) { if (address) { this.addressList.push(address); } } async createNewWallet(password) { try { const ClipsCrypto = require('../modules/clips-crypto.js'); const keyPair = ClipsCrypto.generateKeyPair(); // Store wallet in database const wallet = { address: keyPair.address, publicKey: keyPair.publicKey, privateKey: keyPair.privateKey, // In production, encrypt this with password name: `Account ${this.addressList.length + 1}`, created: Date.now() }; PaperclipDatabase.saveWallet(wallet); this.addAddressToList(keyPair.address); return wallet; } catch (error) { throw new Error(`Failed to create wallet: ${error.message}`); } } async importFromPrivateKey(privateKeyHex, password) { try { const ClipsCrypto = require('../modules/clips-crypto.js'); const keyPair = ClipsCrypto.importPrivateKey(privateKeyHex); const wallet = { address: keyPair.address, publicKey: keyPair.publicKey, privateKey: keyPair.privateKey, name: `Imported Account`, created: Date.now() }; PaperclipDatabase.saveWallet(wallet); this.addAddressToList(keyPair.address); return wallet; } catch (error) { throw new Error(`Failed to import wallet: ${error.message}`); } } async sendTransaction(fromAddress, toAddress, amount, password) { try { // Get wallet data const wallet = PaperclipDatabase.getWallet(fromAddress); if (!wallet) { throw new Error("Wallet not found"); } // Validate address format const ClipsCrypto = require('../modules/clips-crypto.js'); if (!ClipsCrypto.isValidAddress(toAddress)) { throw new Error("Invalid recipient address"); } // Get current nonce const nonce = await ipcRenderer.invoke("paperclip-get-nonce", fromAddress); // Create and sign transaction const transaction = ClipsCrypto.createTransaction({ sender: fromAddress, receiver: toAddress, amount: parseInt(amount), nonce: nonce, type: 'transfer', data: '', privateKey: wallet.privateKey }); // Broadcast transaction const result = await ipcRenderer.invoke("paperclip-broadcast-tx", ClipsCrypto.transactionToHex(transaction)); if (result.success) { return { hash: result.hash, height: result.height }; } else { throw new Error(result.error || "Transaction failed"); } } catch (error) { throw new Error(`Transaction failed: ${error.message}`); } } async getWalletBalance(address) { try { const balance = await ipcRenderer.invoke("paperclip-get-balance", address); return balance; } catch (error) { console.error(`Failed to get balance for ${address}:`, error); return 0; } } async refreshWalletData() { try { this.clearAddressList(); const wallets = PaperclipDatabase.getWallets(); if (!wallets || !wallets.addresses) { return { sumBalance: 0, addressData: [], sumBalanceFormatted: "0 CLIPS" }; } const data = { sumBalance: 0, addressData: [] }; // Get balance for each wallet for (const address of wallets.addresses) { const wallet = PaperclipDatabase.getWallet(address); const balance = await this.getWalletBalance(address); data.addressData.push({ address: address, name: wallet ? wallet.name : `Account`, balance: balance, balanceFormatted: this.formatBalance(balance) }); data.sumBalance += balance; this.addAddressToList(address); } data.sumBalanceFormatted = this.formatBalance(data.sumBalance); return data; } catch (error) { throw new Error(`Failed to refresh wallet data: ${error.message}`); } } formatBalance(balance) { if (balance === 0) { return "0 CLIPS"; } if (balance >= 1000000) { return (balance / 1000000).toFixed(2) + "M CLIPS"; } else if (balance >= 1000) { return (balance / 1000).toFixed(2) + "K CLIPS"; } else { return balance.toLocaleString() + " CLIPS"; } } validateAddress(address) { const ClipsCrypto = require('../modules/clips-crypto.js'); return ClipsCrypto.isValidAddress(address); } validateAmount(amount) { const num = parseFloat(amount); return !isNaN(num) && num > 0; } // UI Event Handlers async handleCreateNewWallet() { const password = $("#walletPasswordFirst").val(); if (!password) { throw new Error("Password cannot be empty"); } if (password !== $("#walletPasswordSecond").val()) { throw new Error("Passwords do not match"); } return await this.createNewWallet(password); } async handleImportPrivateKey() { const privateKey = $("#inputPrivateKey").val(); const password = $("#keyPasswordFirst").val(); if (!privateKey) { throw new Error("Private key cannot be empty"); } if (!password) { throw new Error("Password cannot be empty"); } if (password !== $("#keyPasswordSecond").val()) { throw new Error("Passwords do not match"); } return await this.importFromPrivateKey(privateKey, password); } async handleSendTransaction() { const fromAddress = $("#sendFromAddress").val(); const toAddress = $("#sendToAddress").val(); const amount = $("#sendAmount").val(); const password = $("#sendPassword").val(); if (!fromAddress) { throw new Error("From address is required"); } if (!toAddress) { throw new Error("To address is required"); } if (!this.validateAddress(toAddress)) { throw new Error("Invalid recipient address format"); } if (!this.validateAmount(amount)) { throw new Error("Invalid amount"); } if (!password) { throw new Error("Password is required"); } return await this.sendTransaction(fromAddress, toAddress, amount, password); } } // Create global instance const ClipsWallet = new PaperclipWallets(); // Make it available globally window.ClipsWallet = ClipsWallet;