- Remove Ethereum/Web3 references from HTML - Create PaperclipWallets class with Ed25519 key support - Add PaperclipDatabase for wallet storage - Update module loading order in index.html - Convert button classes from btn-etho to btn-clips
271 lines
6.8 KiB
JavaScript
271 lines
6.8 KiB
JavaScript
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; |