483 lines
16 KiB
JavaScript
483 lines
16 KiB
JavaScript
// Import our WebAssembly module
|
|
import init, {
|
|
create_key_space,
|
|
encrypt_key_space,
|
|
decrypt_key_space,
|
|
logout,
|
|
create_keypair,
|
|
select_keypair,
|
|
list_keypairs,
|
|
keypair_pub_key,
|
|
create_ethereum_wallet,
|
|
create_ethereum_wallet_from_name,
|
|
create_ethereum_wallet_from_private_key,
|
|
get_ethereum_address,
|
|
get_ethereum_private_key,
|
|
format_eth_balance,
|
|
clear_ethereum_wallets
|
|
} from '../../pkg/webassembly.js';
|
|
|
|
// Helper function to convert ArrayBuffer to hex string
|
|
function bufferToHex(buffer) {
|
|
return Array.from(new Uint8Array(buffer))
|
|
.map(b => b.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
}
|
|
|
|
// Helper function to convert hex string to Uint8Array
|
|
function hexToBuffer(hex) {
|
|
const bytes = new Uint8Array(hex.length / 2);
|
|
for (let i = 0; i < hex.length; i += 2) {
|
|
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
// IndexedDB setup for Ethereum wallets
|
|
const DB_NAME = 'EthWalletDB';
|
|
const DB_VERSION = 1;
|
|
const STORE_NAME = 'ethWallets';
|
|
|
|
// Initialize the database
|
|
function initDatabase() {
|
|
return new Promise((resolve, reject) => {
|
|
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
|
|
request.onerror = (event) => {
|
|
console.error('Error opening Ethereum wallet database:', event.target.error);
|
|
reject('Error opening database: ' + event.target.error);
|
|
};
|
|
|
|
request.onsuccess = (event) => {
|
|
const db = event.target.result;
|
|
resolve(db);
|
|
};
|
|
|
|
request.onupgradeneeded = (event) => {
|
|
const db = event.target.result;
|
|
// Create object store for Ethereum wallets if it doesn't exist
|
|
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
const store = db.createObjectStore(STORE_NAME, { keyPath: 'address' });
|
|
store.createIndex('address', 'address', { unique: true });
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
// Get database connection
|
|
function getDB() {
|
|
return initDatabase();
|
|
}
|
|
|
|
// Save Ethereum wallet to IndexedDB
|
|
async function saveEthWalletToStorage(address, privateKey) {
|
|
try {
|
|
const db = await getDB();
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([STORE_NAME], 'readwrite');
|
|
const store = transaction.objectStore(STORE_NAME);
|
|
|
|
const wallet = {
|
|
address: address,
|
|
privateKey: privateKey,
|
|
created: new Date()
|
|
};
|
|
|
|
const request = store.put(wallet);
|
|
|
|
request.onsuccess = () => {
|
|
resolve();
|
|
};
|
|
|
|
request.onerror = (event) => {
|
|
console.error('Error saving Ethereum wallet:', event.target.error);
|
|
reject('Error saving wallet: ' + event.target.error);
|
|
};
|
|
|
|
transaction.oncomplete = () => {
|
|
db.close();
|
|
};
|
|
});
|
|
} catch (error) {
|
|
console.error('Database error in saveEthWalletToStorage:', error);
|
|
}
|
|
}
|
|
|
|
// Get Ethereum wallet from IndexedDB
|
|
async function getEthWalletFromStorage(address) {
|
|
try {
|
|
const db = await getDB();
|
|
return new Promise((resolve, reject) => {
|
|
const transaction = db.transaction([STORE_NAME], 'readonly');
|
|
const store = transaction.objectStore(STORE_NAME);
|
|
const request = store.get(address);
|
|
|
|
request.onsuccess = (event) => {
|
|
const wallet = event.target.result;
|
|
if (wallet) {
|
|
resolve(wallet.privateKey);
|
|
} else {
|
|
resolve(null);
|
|
}
|
|
};
|
|
|
|
request.onerror = (event) => {
|
|
console.error('Error retrieving Ethereum wallet:', event.target.error);
|
|
reject('Error retrieving wallet: ' + event.target.error);
|
|
};
|
|
|
|
transaction.oncomplete = () => {
|
|
db.close();
|
|
};
|
|
});
|
|
} catch (error) {
|
|
console.error('Database error in getEthWalletFromStorage:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Session state
|
|
let selectedKeypair = null;
|
|
let hasEthereumWallet = false;
|
|
|
|
// Update UI based on login state
|
|
function updateLoginUI() {
|
|
const loginStatus = document.getElementById('login-status');
|
|
|
|
try {
|
|
// Try to list keypairs to check if logged in
|
|
const keypairs = list_keypairs();
|
|
|
|
if (keypairs && keypairs.length > 0) {
|
|
loginStatus.textContent = 'Status: Logged in';
|
|
loginStatus.className = 'status logged-in';
|
|
|
|
// Update keypairs list
|
|
updateKeypairsList();
|
|
} else {
|
|
loginStatus.textContent = 'Status: Not logged in. Please login in the Main Crypto Demo page first.';
|
|
loginStatus.className = 'status logged-out';
|
|
|
|
// Hide Ethereum wallet info when logged out
|
|
document.getElementById('ethereum-wallet-info').classList.add('hidden');
|
|
hasEthereumWallet = false;
|
|
}
|
|
} catch (e) {
|
|
loginStatus.textContent = 'Status: Not logged in. Please login in the Main Crypto Demo page first.';
|
|
loginStatus.className = 'status logged-out';
|
|
|
|
// Hide Ethereum wallet info when logged out
|
|
document.getElementById('ethereum-wallet-info').classList.add('hidden');
|
|
hasEthereumWallet = false;
|
|
}
|
|
}
|
|
|
|
// Update the keypairs dropdown list
|
|
function updateKeypairsList() {
|
|
const selectKeypair = document.getElementById('select-keypair');
|
|
|
|
// Clear existing options
|
|
while (selectKeypair.options.length > 1) {
|
|
selectKeypair.remove(1);
|
|
}
|
|
|
|
try {
|
|
// Get keypairs list
|
|
const keypairs = list_keypairs();
|
|
|
|
// Add options for each keypair
|
|
keypairs.forEach(keypairName => {
|
|
const option = document.createElement('option');
|
|
option.value = keypairName;
|
|
option.textContent = keypairName;
|
|
selectKeypair.appendChild(option);
|
|
});
|
|
|
|
// If there's a selected keypair, select it in the dropdown
|
|
if (selectedKeypair) {
|
|
selectKeypair.value = selectedKeypair;
|
|
}
|
|
} catch (e) {
|
|
console.error('Error updating keypairs list:', e);
|
|
}
|
|
}
|
|
|
|
// Select a keypair
|
|
async function performSelectKeypair() {
|
|
const keypairName = document.getElementById('select-keypair').value;
|
|
|
|
if (!keypairName) {
|
|
document.getElementById('keypair-management-result').textContent = 'Please select a keypair';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Select keypair
|
|
const result = select_keypair(keypairName);
|
|
if (result === 0) {
|
|
selectedKeypair = keypairName;
|
|
document.getElementById('keypair-management-result').textContent = `Selected keypair "${keypairName}"`;
|
|
|
|
// Hide Ethereum wallet info when changing keypairs
|
|
document.getElementById('ethereum-wallet-info').classList.add('hidden');
|
|
hasEthereumWallet = false;
|
|
} else {
|
|
document.getElementById('keypair-management-result').textContent = `Error selecting keypair: ${result}`;
|
|
}
|
|
} catch (e) {
|
|
document.getElementById('keypair-management-result').textContent = `Error: ${e}`;
|
|
}
|
|
}
|
|
|
|
// Create an Ethereum wallet from the selected keypair
|
|
async function performCreateEthereumWallet() {
|
|
if (!selectedKeypair) {
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Please select a keypair first';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Show loading state
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Creating wallet...';
|
|
|
|
// Create Ethereum wallet
|
|
console.log('Creating Ethereum wallet from keypair:', selectedKeypair);
|
|
const result = create_ethereum_wallet();
|
|
console.log('Create Ethereum wallet result:', result);
|
|
|
|
if (result === 0) {
|
|
hasEthereumWallet = true;
|
|
|
|
// Get and display Ethereum address
|
|
const address = get_ethereum_address();
|
|
console.log('Generated Ethereum address:', address);
|
|
document.getElementById('ethereum-address-value').textContent = address;
|
|
|
|
// Get and display private key
|
|
const privateKey = get_ethereum_private_key();
|
|
document.getElementById('ethereum-private-key-value').textContent = privateKey;
|
|
|
|
// Show the wallet info
|
|
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
|
|
|
try {
|
|
// Save the wallet to IndexedDB
|
|
console.log('Saving wallet to IndexedDB:', address);
|
|
await saveEthWalletToStorage(address, privateKey);
|
|
console.log('Wallet saved successfully');
|
|
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Successfully created Ethereum wallet';
|
|
} catch (saveError) {
|
|
console.error('Error saving wallet to IndexedDB:', saveError);
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Wallet created but failed to save to storage';
|
|
}
|
|
} else {
|
|
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
|
|
}
|
|
} catch (e) {
|
|
console.error('Error in performCreateEthereumWallet:', e);
|
|
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
|
}
|
|
}
|
|
|
|
// Create an Ethereum wallet from a name and the selected keypair
|
|
async function performCreateEthereumWalletFromName() {
|
|
if (!selectedKeypair) {
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Please select a keypair first';
|
|
return;
|
|
}
|
|
|
|
const name = document.getElementById('wallet-name').value.trim();
|
|
|
|
if (!name) {
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Please enter a name for derivation';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Show loading state
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Creating wallet...';
|
|
|
|
// Create Ethereum wallet from name
|
|
console.log('Creating Ethereum wallet from name:', name);
|
|
const result = create_ethereum_wallet_from_name(name);
|
|
console.log('Create Ethereum wallet from name result:', result);
|
|
|
|
if (result === 0) {
|
|
hasEthereumWallet = true;
|
|
|
|
// Get and display Ethereum address
|
|
const address = get_ethereum_address();
|
|
console.log('Generated Ethereum address:', address);
|
|
document.getElementById('ethereum-address-value').textContent = address;
|
|
|
|
// Get and display private key
|
|
const privateKey = get_ethereum_private_key();
|
|
document.getElementById('ethereum-private-key-value').textContent = privateKey;
|
|
|
|
// Show the wallet info
|
|
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
|
|
|
try {
|
|
// Save the wallet to IndexedDB
|
|
console.log('Saving wallet to IndexedDB:', address);
|
|
await saveEthWalletToStorage(address, privateKey);
|
|
console.log('Wallet saved successfully');
|
|
|
|
document.getElementById('ethereum-wallet-result').textContent = `Successfully created Ethereum wallet from name "${name}"`;
|
|
} catch (saveError) {
|
|
console.error('Error saving wallet to IndexedDB:', saveError);
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Wallet created but failed to save to storage';
|
|
}
|
|
} else {
|
|
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
|
|
}
|
|
} catch (e) {
|
|
console.error('Error in performCreateEthereumWalletFromName:', e);
|
|
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
|
}
|
|
}
|
|
|
|
// Create an Ethereum wallet from a private key
|
|
async function performCreateEthereumWalletFromPrivateKey() {
|
|
const privateKey = document.getElementById('private-key').value.trim();
|
|
|
|
if (!privateKey) {
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Please enter a private key';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Show loading state
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Creating wallet...';
|
|
|
|
// Create Ethereum wallet from private key
|
|
console.log('Creating Ethereum wallet from private key');
|
|
const result = create_ethereum_wallet_from_private_key(privateKey);
|
|
console.log('Create Ethereum wallet from private key result:', result);
|
|
|
|
if (result === 0) {
|
|
hasEthereumWallet = true;
|
|
|
|
// Get and display Ethereum address
|
|
const address = get_ethereum_address();
|
|
console.log('Generated Ethereum address:', address);
|
|
document.getElementById('ethereum-address-value').textContent = address;
|
|
|
|
// Get and display private key
|
|
const displayPrivateKey = get_ethereum_private_key();
|
|
document.getElementById('ethereum-private-key-value').textContent = displayPrivateKey;
|
|
|
|
// Show the wallet info
|
|
document.getElementById('ethereum-wallet-info').classList.remove('hidden');
|
|
|
|
try {
|
|
// Save the wallet to IndexedDB
|
|
console.log('Saving wallet to IndexedDB:', address);
|
|
await saveEthWalletToStorage(address, displayPrivateKey);
|
|
console.log('Wallet saved successfully');
|
|
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Successfully imported Ethereum wallet from private key';
|
|
} catch (saveError) {
|
|
console.error('Error saving wallet to IndexedDB:', saveError);
|
|
document.getElementById('ethereum-wallet-result').textContent = 'Wallet imported but failed to save to storage';
|
|
}
|
|
} else {
|
|
document.getElementById('ethereum-wallet-result').textContent = `Error importing Ethereum wallet: ${result}`;
|
|
}
|
|
} catch (e) {
|
|
console.error('Error in performCreateEthereumWalletFromPrivateKey:', e);
|
|
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
|
|
}
|
|
}
|
|
|
|
// Check the balance of an Ethereum address
|
|
async function checkBalance() {
|
|
if (!hasEthereumWallet) {
|
|
document.getElementById('balance-result').textContent = 'Please create an Ethereum wallet first';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const address = get_ethereum_address();
|
|
|
|
document.getElementById('balance-result').textContent = 'Checking balance...';
|
|
|
|
// Use the Ethereum Web3 API directly from JavaScript
|
|
const response = await fetch(GNOSIS_RPC_URL, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
method: 'eth_getBalance',
|
|
params: [address, 'latest'],
|
|
id: 1,
|
|
}),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.error) {
|
|
document.getElementById('balance-result').textContent = `Error: ${data.error.message}`;
|
|
return;
|
|
}
|
|
|
|
const balanceHex = data.result;
|
|
const formattedBalance = format_eth_balance(balanceHex);
|
|
|
|
document.getElementById('balance-result').textContent = `Balance: ${formattedBalance}`;
|
|
} catch (e) {
|
|
document.getElementById('balance-result').textContent = `Error: ${e}`;
|
|
}
|
|
}
|
|
|
|
// Copy text to clipboard
|
|
function copyToClipboard(text, successMessage) {
|
|
navigator.clipboard.writeText(text)
|
|
.then(() => {
|
|
alert(successMessage);
|
|
})
|
|
.catch(err => {
|
|
console.error('Could not copy text: ', err);
|
|
});
|
|
}
|
|
|
|
// Constants
|
|
const GNOSIS_RPC_URL = "https://rpc.gnosis.gateway.fm";
|
|
const GNOSIS_EXPLORER = "https://gnosisscan.io";
|
|
|
|
async function run() {
|
|
// Initialize the WebAssembly module
|
|
await init();
|
|
|
|
console.log('WebAssembly crypto module initialized!');
|
|
|
|
// Set up the keypair selection
|
|
document.getElementById('select-keypair').addEventListener('change', performSelectKeypair);
|
|
|
|
// Set up the Ethereum wallet management
|
|
document.getElementById('create-ethereum-wallet-button').addEventListener('click', performCreateEthereumWallet);
|
|
document.getElementById('create-from-name-button').addEventListener('click', performCreateEthereumWalletFromName);
|
|
document.getElementById('import-private-key-button').addEventListener('click', performCreateEthereumWalletFromPrivateKey);
|
|
|
|
// Set up the copy buttons
|
|
document.getElementById('copy-address-button').addEventListener('click', () => {
|
|
const address = document.getElementById('ethereum-address-value').textContent;
|
|
copyToClipboard(address, 'Ethereum address copied to clipboard!');
|
|
});
|
|
|
|
document.getElementById('copy-private-key-button').addEventListener('click', () => {
|
|
const privateKey = document.getElementById('ethereum-private-key-value').textContent;
|
|
copyToClipboard(privateKey, 'Private key copied to clipboard!');
|
|
});
|
|
|
|
// Set up the balance check
|
|
document.getElementById('check-balance-button').addEventListener('click', checkBalance);
|
|
|
|
// Initialize UI
|
|
updateLoginUI();
|
|
}
|
|
|
|
run().catch(console.error); |