Skip to main content
Glama
wallet-manager.ts7.01 kB
/** * Wallet Manager * Handles wallet creation, import, and management */ import { ethers } from 'ethers'; import * as bip39 from 'bip39'; import { WalletInfo } from './types.js'; export class WalletManager { private wallets: Map<string, ethers.Wallet | ethers.HDNodeWallet> = new Map(); private currentWallet: string | null = null; constructor() { this.loadWalletsFromEnv(); } /** * Load wallets from environment variables */ private loadWalletsFromEnv(): void { const privateKeys = process.env.HYPERION_PRIVATE_KEYS?.split(',') || []; const addresses = process.env.HYPERION_ADDRESSES?.split(',') || []; const currentAddress = process.env.HYPERION_CURRENT_ADDRESS; privateKeys.forEach((privateKey, index) => { if (privateKey.trim()) { try { const wallet = new ethers.Wallet(privateKey.trim()); const address = addresses[index]?.trim() || wallet.address; this.wallets.set(address.toLowerCase(), wallet); if (!this.currentWallet || address.toLowerCase() === currentAddress?.toLowerCase()) { this.currentWallet = address.toLowerCase(); } } catch (error) { console.warn(`Failed to load wallet ${index}: ${error}`); } } }); } /** * Create a new wallet */ createWallet(_name?: string): WalletInfo { try { // Generate a random mnemonic const mnemonic = bip39.generateMnemonic(); const wallet = ethers.Wallet.fromPhrase(mnemonic); // Store the wallet this.wallets.set(wallet.address.toLowerCase(), wallet); // Set as current wallet if it's the first one if (!this.currentWallet) { this.currentWallet = wallet.address.toLowerCase(); } return { address: wallet.address, privateKey: wallet.privateKey, mnemonic, publicKey: wallet.publicKey, }; } catch (error) { throw new Error(`Failed to create wallet: ${error}`); } } /** * Import wallet from private key or mnemonic */ importWallet(privateKey?: string, mnemonic?: string, _name?: string): WalletInfo { try { let wallet: ethers.Wallet | ethers.HDNodeWallet; if (privateKey) { wallet = new ethers.Wallet(privateKey); } else if (mnemonic) { if (!bip39.validateMnemonic(mnemonic)) { throw new Error('Invalid mnemonic phrase'); } wallet = ethers.Wallet.fromPhrase(mnemonic); } else { throw new Error('Either private key or mnemonic must be provided'); } // Store the wallet this.wallets.set(wallet.address.toLowerCase(), wallet); // Set as current wallet if it's the first one if (!this.currentWallet) { this.currentWallet = wallet.address.toLowerCase(); } return { address: wallet.address, privateKey: wallet.privateKey, mnemonic, publicKey: 'publicKey' in wallet ? (wallet as any).publicKey : undefined, }; } catch (error) { throw new Error(`Failed to import wallet: ${error}`); } } /** * Get wallet by address */ getWallet(address: string): ethers.Wallet | ethers.HDNodeWallet { const wallet = this.wallets.get(address.toLowerCase()); if (!wallet) { throw new Error(`Wallet not found for address: ${address}`); } return wallet; } /** * Get current wallet */ getCurrentWallet(): ethers.Wallet | ethers.HDNodeWallet { if (!this.currentWallet) { throw new Error('No current wallet set'); } return this.getWallet(this.currentWallet); } /** * Set current wallet */ setCurrentWallet(address: string): void { if (!this.wallets.has(address.toLowerCase())) { throw new Error(`Wallet not found for address: ${address}`); } this.currentWallet = address.toLowerCase(); } /** * List all available wallets */ listWallets(): WalletInfo[] { const walletList: WalletInfo[] = []; for (const [_address, wallet] of this.wallets) { walletList.push({ address: wallet.address, publicKey: 'publicKey' in wallet ? (wallet as any).publicKey : undefined, // Don't expose private keys in list }); } return walletList; } /** * Get wallet info with masked private key */ getWalletInfo(address: string): WalletInfo { const wallet = this.getWallet(address); return { address: wallet.address, privateKey: this.maskPrivateKey(wallet.privateKey), publicKey: 'publicKey' in wallet ? (wallet as any).publicKey : undefined, }; } /** * Get current wallet address */ getCurrentAddress(): string { if (!this.currentWallet) { throw new Error('No current wallet set'); } return this.wallets.get(this.currentWallet)!.address; } /** * Check if wallet exists */ hasWallet(address: string): boolean { return this.wallets.has(address.toLowerCase()); } /** * Remove wallet */ removeWallet(address: string): void { const addressLower = address.toLowerCase(); if (!this.wallets.has(addressLower)) { throw new Error(`Wallet not found for address: ${address}`); } this.wallets.delete(addressLower); // If this was the current wallet, set a new current wallet if (this.currentWallet === addressLower) { const remainingWallets = Array.from(this.wallets.keys()); this.currentWallet = remainingWallets.length > 0 ? remainingWallets[0] : null; } } /** * Get wallet count */ getWalletCount(): number { return this.wallets.size; } /** * Validate address format */ static isValidAddress(address: string): boolean { try { return ethers.isAddress(address); } catch { return false; } } /** * Validate private key format */ static isValidPrivateKey(privateKey: string): boolean { try { new ethers.Wallet(privateKey); return true; } catch { return false; } } /** * Validate mnemonic phrase */ static isValidMnemonic(mnemonic: string): boolean { return bip39.validateMnemonic(mnemonic); } /** * Mask private key for display */ private maskPrivateKey(privateKey: string): string { if (privateKey.length < 10) return privateKey; return `${privateKey.substring(0, 6)}...${privateKey.substring(privateKey.length - 4)}`; } /** * Generate a random wallet name */ private generateWalletName(): string { const adjectives = ['Swift', 'Bright', 'Noble', 'Wise', 'Bold', 'Quick', 'Strong', 'Smart']; const nouns = ['Wallet', 'Account', 'Vault', 'Keeper', 'Guardian', 'Holder', 'Signer']; const adjective = adjectives[Math.floor(Math.random() * adjectives.length)]; const noun = nouns[Math.floor(Math.random() * nouns.length)]; const number = Math.floor(Math.random() * 1000); return `${adjective}${noun}${number}`; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cuongpo/hyperion-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server