webassembly/www/js/ethereum.js
2025-04-21 11:54:18 +02:00

528 lines
17 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;
}
// LocalStorage functions for key spaces
const STORAGE_PREFIX = 'crypto_space_';
const ETH_WALLET_PREFIX = 'eth_wallet_';
// Save encrypted space to localStorage
function saveSpaceToStorage(spaceName, encryptedData) {
localStorage.setItem(`${STORAGE_PREFIX}${spaceName}`, encryptedData);
}
// Get encrypted space from localStorage
function getSpaceFromStorage(spaceName) {
return localStorage.getItem(`${STORAGE_PREFIX}${spaceName}`);
}
// Save Ethereum wallet to localStorage
function saveEthWalletToStorage(address, privateKey) {
localStorage.setItem(`${ETH_WALLET_PREFIX}${address}`, privateKey);
}
// Get Ethereum wallet from localStorage
function getEthWalletFromStorage(address) {
return localStorage.getItem(`${ETH_WALLET_PREFIX}${address}`);
}
// Session state
let isLoggedIn = false;
let currentSpace = null;
let selectedKeypair = null;
let hasEthereumWallet = false;
// Update UI based on login state
function updateLoginUI() {
const loginForm = document.getElementById('login-form');
const logoutForm = document.getElementById('logout-form');
const loginStatus = document.getElementById('login-status');
const currentSpaceName = document.getElementById('current-space-name');
if (isLoggedIn) {
loginForm.classList.add('hidden');
logoutForm.classList.remove('hidden');
loginStatus.textContent = 'Status: Logged in';
loginStatus.className = 'status logged-in';
currentSpaceName.textContent = currentSpace;
} else {
loginForm.classList.remove('hidden');
logoutForm.classList.add('hidden');
loginStatus.textContent = 'Status: Not logged in';
loginStatus.className = 'status logged-out';
currentSpaceName.textContent = '';
// 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);
}
}
// Login to a space
async function performLogin() {
const spaceName = document.getElementById('space-name').value.trim();
const password = document.getElementById('space-password').value;
if (!spaceName || !password) {
document.getElementById('space-result').textContent = 'Please enter both space name and password';
return;
}
try {
// Get encrypted space from localStorage
const encryptedSpace = getSpaceFromStorage(spaceName);
if (!encryptedSpace) {
document.getElementById('space-result').textContent = `Space "${spaceName}" not found`;
return;
}
// Decrypt the space
const result = decrypt_key_space(encryptedSpace, password);
if (result === 0) {
isLoggedIn = true;
currentSpace = spaceName;
updateLoginUI();
updateKeypairsList();
document.getElementById('space-result').textContent = `Successfully logged in to space "${spaceName}"`;
} else {
document.getElementById('space-result').textContent = `Error logging in: ${result}`;
}
} catch (e) {
document.getElementById('space-result').textContent = `Error: ${e}`;
}
}
// Create a new space
async function performCreateSpace() {
const spaceName = document.getElementById('space-name').value.trim();
const password = document.getElementById('space-password').value;
if (!spaceName || !password) {
document.getElementById('space-result').textContent = 'Please enter both space name and password';
return;
}
// Check if space already exists
if (getSpaceFromStorage(spaceName)) {
document.getElementById('space-result').textContent = `Space "${spaceName}" already exists`;
return;
}
try {
// Create new space
const result = create_key_space(spaceName);
if (result === 0) {
// Encrypt and save the space
const encryptedSpace = encrypt_key_space(password);
saveSpaceToStorage(spaceName, encryptedSpace);
isLoggedIn = true;
currentSpace = spaceName;
updateLoginUI();
updateKeypairsList();
document.getElementById('space-result').textContent = `Successfully created space "${spaceName}"`;
} else {
document.getElementById('space-result').textContent = `Error creating space: ${result}`;
}
} catch (e) {
document.getElementById('space-result').textContent = `Error: ${e}`;
}
}
// Logout from current space
function performLogout() {
logout();
clear_ethereum_wallets();
isLoggedIn = false;
currentSpace = null;
selectedKeypair = null;
hasEthereumWallet = false;
updateLoginUI();
document.getElementById('space-result').textContent = 'Logged out successfully';
}
// Create a new keypair
async function performCreateKeypair() {
if (!isLoggedIn) {
document.getElementById('keypair-management-result').textContent = 'Please login first';
return;
}
const keypairName = document.getElementById('keypair-name').value.trim();
if (!keypairName) {
document.getElementById('keypair-management-result').textContent = 'Please enter a keypair name';
return;
}
try {
// Create new keypair
const result = create_keypair(keypairName);
if (result === 0) {
document.getElementById('keypair-management-result').textContent = `Successfully created keypair "${keypairName}"`;
// Update keypairs list
updateKeypairsList();
// Select the new keypair
selectedKeypair = keypairName;
document.getElementById('select-keypair').value = keypairName;
// Save the updated space to localStorage
saveCurrentSpace();
} else {
document.getElementById('keypair-management-result').textContent = `Error creating keypair: ${result}`;
}
} catch (e) {
document.getElementById('keypair-management-result').textContent = `Error: ${e}`;
}
}
// Select a keypair
async function performSelectKeypair() {
if (!isLoggedIn) {
document.getElementById('keypair-management-result').textContent = 'Please login first';
return;
}
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}`;
}
}
// Save the current space to localStorage
function saveCurrentSpace() {
if (!isLoggedIn || !currentSpace) return;
try {
const password = document.getElementById('space-password').value;
if (!password) {
console.error('Password not available for saving space');
return;
}
const encryptedSpace = encrypt_key_space(password);
saveSpaceToStorage(currentSpace, encryptedSpace);
} catch (e) {
console.error('Error saving space:', e);
}
}
// Create an Ethereum wallet from the selected keypair
async function performCreateEthereumWallet() {
if (!isLoggedIn) {
document.getElementById('ethereum-wallet-result').textContent = 'Please login first';
return;
}
if (!selectedKeypair) {
document.getElementById('ethereum-wallet-result').textContent = 'Please select a keypair first';
return;
}
try {
// Create Ethereum wallet
const result = create_ethereum_wallet();
if (result === 0) {
hasEthereumWallet = true;
// Get and display Ethereum address
const address = get_ethereum_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');
// Save the wallet to localStorage
saveEthWalletToStorage(address, privateKey);
document.getElementById('ethereum-wallet-result').textContent = 'Successfully created Ethereum wallet';
// Save the updated space to localStorage
saveCurrentSpace();
} else {
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
}
} catch (e) {
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
}
}
// Create an Ethereum wallet from a name and the selected keypair
async function performCreateEthereumWalletFromName() {
if (!isLoggedIn) {
document.getElementById('ethereum-wallet-result').textContent = 'Please login first';
return;
}
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 {
// Create Ethereum wallet from name
const result = create_ethereum_wallet_from_name(name);
if (result === 0) {
hasEthereumWallet = true;
// Get and display Ethereum address
const address = get_ethereum_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');
// Save the wallet to localStorage
saveEthWalletToStorage(address, privateKey);
document.getElementById('ethereum-wallet-result').textContent = `Successfully created Ethereum wallet from name "${name}"`;
// Save the updated space to localStorage
saveCurrentSpace();
} else {
document.getElementById('ethereum-wallet-result').textContent = `Error creating Ethereum wallet: ${result}`;
}
} catch (e) {
document.getElementById('ethereum-wallet-result').textContent = `Error: ${e}`;
}
}
// Create an Ethereum wallet from a private key
async function performCreateEthereumWalletFromPrivateKey() {
if (!isLoggedIn) {
document.getElementById('ethereum-wallet-result').textContent = 'Please login first';
return;
}
const privateKey = document.getElementById('private-key').value.trim();
if (!privateKey) {
document.getElementById('ethereum-wallet-result').textContent = 'Please enter a private key';
return;
}
try {
// Create Ethereum wallet from private key
const result = create_ethereum_wallet_from_private_key(privateKey);
if (result === 0) {
hasEthereumWallet = true;
// Get and display Ethereum address
const address = get_ethereum_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');
// Save the wallet to localStorage
saveEthWalletToStorage(address, displayPrivateKey);
document.getElementById('ethereum-wallet-result').textContent = 'Successfully imported Ethereum wallet from private key';
// Save the updated space to localStorage
saveCurrentSpace();
} else {
document.getElementById('ethereum-wallet-result').textContent = `Error importing Ethereum wallet: ${result}`;
}
} catch (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 login/space management
document.getElementById('login-button').addEventListener('click', performLogin);
document.getElementById('create-space-button').addEventListener('click', performCreateSpace);
document.getElementById('logout-button').addEventListener('click', performLogout);
// Set up the keypair management
document.getElementById('create-keypair-button').addEventListener('click', performCreateKeypair);
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);