Skip to main content
Glama
Bichev
by Bichev
demo-wallet-client.ts10.8 kB
import { CoinbaseClient } from './coinbase-client.js'; import { DemoWallet, DemoTransaction, PurchaseCalculation } from './types.js'; /** * Demo Wallet Client for simulating crypto purchases * Perfect for educational demos like "buy a beer worth of Bitcoin" */ export class DemoWalletClient { private wallet: DemoWallet; private coinbaseClient: CoinbaseClient; constructor(coinbaseClient: CoinbaseClient) { this.coinbaseClient = coinbaseClient; // Initialize demo wallet with starting balance this.wallet = { balances: { USD: 1000.00, // Start with $1000 USD BTC: 0, ETH: 0, USDC: 0 }, transactions: [], inventory: { beers: 0, items: [] }, createdAt: new Date(), lastUpdated: new Date() }; } /** * Get current wallet balances */ getWallet(): DemoWallet { return { ...this.wallet }; } /** * Get balance for specific currency */ getBalance(currency: string): number { return this.wallet.balances[currency.toUpperCase()] || 0; } /** * Calculate how much crypto you can buy for a "beer" */ async calculateBeerCost( currency: string = 'BTC', beerCount: number = 1, pricePerBeer: number = 5 ): Promise<PurchaseCalculation> { const usdAmount = beerCount * pricePerBeer; const currencyPair = `${currency.toUpperCase()}-USD`; try { const priceData = await this.coinbaseClient.getSpotPrice(currencyPair); const currentPrice = parseFloat(priceData.data.amount); const cryptoAmount = usdAmount / currentPrice; return { usdAmount, cryptoAmount, cryptoCurrency: currency.toUpperCase(), currentPrice, description: `${beerCount} beer${beerCount > 1 ? 's' : ''} 🍺 = ${cryptoAmount.toFixed(8)} ${currency.toUpperCase()}` }; } catch (error) { throw new Error(`Failed to calculate beer cost: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Simulate purchasing crypto with USD */ async simulatePurchase( fromCurrency: string, toCurrency: string, amount: number, description?: string ): Promise<DemoTransaction> { const from = fromCurrency.toUpperCase(); const to = toCurrency.toUpperCase(); // Check if user has enough balance const currentBalance = this.getBalance(from); if (currentBalance < amount) { throw new Error( `Insufficient ${from} balance. Available: ${currentBalance.toFixed(2)}, Required: ${amount.toFixed(2)}` ); } // Get current price let currentPrice: number; let cryptoAmount: number; if (from === 'USD' && to !== 'USD') { // Buying crypto with USD const currencyPair = `${to}-USD`; try { const priceData = await this.coinbaseClient.getSpotPrice(currencyPair); currentPrice = parseFloat(priceData.data.amount); cryptoAmount = amount / currentPrice; } catch (error) { throw new Error(`Failed to get price for ${currencyPair}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } else if (to === 'USD' && from !== 'USD') { // Selling crypto for USD const currencyPair = `${from}-USD`; try { const priceData = await this.coinbaseClient.getSpotPrice(currencyPair); currentPrice = parseFloat(priceData.data.amount); cryptoAmount = amount * currentPrice; } catch (error) { throw new Error(`Failed to get price for ${currencyPair}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } else { throw new Error('At least one currency must be USD for demo transactions'); } // Create transaction const transactionType: 'buy' | 'sell' = from === 'USD' ? 'buy' : 'sell'; const transaction: DemoTransaction = { id: `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, type: transactionType, fromCurrency: from, toCurrency: to, fromAmount: from === 'USD' ? amount : amount, toAmount: from === 'USD' ? cryptoAmount : cryptoAmount, price: currentPrice, description: description || `${transactionType === 'buy' ? 'Bought' : 'Sold'} ${to} with ${from}`, timestamp: new Date(), status: 'completed' }; // Update balances this.wallet.balances[from] = (this.wallet.balances[from] || 0) - transaction.fromAmount; this.wallet.balances[to] = (this.wallet.balances[to] || 0) + transaction.toAmount; // Add to transaction history this.wallet.transactions.unshift(transaction); this.wallet.lastUpdated = new Date(); return transaction; } /** * Get transaction history */ getTransactionHistory(limit: number = 10, currency?: string): DemoTransaction[] { let transactions = this.wallet.transactions; // Filter by currency if specified if (currency) { const curr = currency.toUpperCase(); transactions = transactions.filter( tx => tx.fromCurrency === curr || tx.toCurrency === curr ); } return transactions.slice(0, limit); } /** * Add funds to wallet (for testing) */ addFunds(currency: string, amount: number): void { const curr = currency.toUpperCase(); this.wallet.balances[curr] = (this.wallet.balances[curr] || 0) + amount; this.wallet.lastUpdated = new Date(); } /** * Reset wallet to initial state */ resetWallet(): void { this.wallet = { balances: { USD: 1000.00, BTC: 0, ETH: 0, USDC: 0 }, transactions: [], inventory: { beers: 0, items: [] }, createdAt: new Date(), lastUpdated: new Date() }; } /** * Get wallet statistics */ getWalletStats(): { totalTransactions: number; totalSpentUSD: number; totalCryptoBought: { [currency: string]: number }; portfolioValue: Promise<number>; } { const totalTransactions = this.wallet.transactions.length; const totalSpentUSD = this.wallet.transactions .filter(tx => tx.fromCurrency === 'USD') .reduce((sum, tx) => sum + tx.fromAmount, 0); const totalCryptoBought: { [currency: string]: number } = {}; this.wallet.transactions .filter(tx => tx.type === 'buy' && tx.toCurrency !== 'USD') .forEach(tx => { totalCryptoBought[tx.toCurrency] = (totalCryptoBought[tx.toCurrency] || 0) + tx.toAmount; }); const portfolioValue = this.calculatePortfolioValue(); return { totalTransactions, totalSpentUSD, totalCryptoBought, portfolioValue }; } /** * Calculate current portfolio value in USD */ private async calculatePortfolioValue(): Promise<number> { let totalValue = this.wallet.balances.USD || 0; // Add value of crypto holdings for (const [currency, balance] of Object.entries(this.wallet.balances)) { if (currency !== 'USD' && balance > 0) { try { const priceData = await this.coinbaseClient.getSpotPrice(`${currency}-USD`); const price = parseFloat(priceData.data.amount); totalValue += balance * price; } catch (error) { // Skip if can't get price console.error(`Could not get price for ${currency}:`, error); } } } return totalValue; } /** * Buy a virtual beer with cryptocurrency! * This creates a complete circular economy: USD → BTC → Beer */ async buyVirtualBeer( quantity: number = 1, currency: string = 'BTC', pricePerBeer: number = 5 ): Promise<{ success: boolean; message: string; transaction?: DemoTransaction; needsMoreCrypto?: boolean; suggestedAmount?: number; }> { const crypto = currency.toUpperCase(); const totalUsdCost = quantity * pricePerBeer; try { // Get current crypto price const currencyPair = `${crypto}-USD`; const priceData = await this.coinbaseClient.getSpotPrice(currencyPair); const cryptoPrice = parseFloat(priceData.data.amount); const cryptoNeeded = totalUsdCost / cryptoPrice; // Check if user has enough crypto const currentBalance = this.getBalance(crypto); if (currentBalance < cryptoNeeded) { return { success: false, needsMoreCrypto: true, suggestedAmount: totalUsdCost, message: `❌ Insufficient ${crypto} balance!\n\n` + `You need: ${cryptoNeeded.toFixed(8)} ${crypto}\n` + `You have: ${currentBalance.toFixed(8)} ${crypto}\n\n` + `💡 Suggestion: First buy $${totalUsdCost.toFixed(2)} worth of ${crypto}, then try again!\n` + `Say: "Buy $${totalUsdCost} worth of ${crypto}"` }; } // User has enough crypto - execute the beer purchase! const transactionType: 'buy' | 'sell' | 'transfer' = 'buy'; const transaction: DemoTransaction = { id: `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, type: transactionType, fromCurrency: crypto, toCurrency: 'BEER', fromAmount: cryptoNeeded, toAmount: quantity, price: cryptoPrice, description: `🍺 Bought ${quantity} virtual beer${quantity > 1 ? 's' : ''} with ${crypto}`, timestamp: new Date(), status: 'completed' }; // Deduct crypto from balance this.wallet.balances[crypto] = (this.wallet.balances[crypto] || 0) - cryptoNeeded; // Add beers to inventory this.wallet.inventory.beers += quantity; // Add item to inventory list this.wallet.inventory.items.push({ id: transaction.id, name: 'Beer', emoji: '🍺', quantity: quantity, purchasePrice: cryptoNeeded, purchaseCurrency: crypto, purchaseDate: new Date() }); // Record transaction this.wallet.transactions.unshift(transaction); this.wallet.lastUpdated = new Date(); return { success: true, transaction, message: `🍺 Beer Purchase Successful!\n\n` + `You bought ${quantity} virtual beer${quantity > 1 ? 's' : ''} for ${cryptoNeeded.toFixed(8)} ${crypto}\n` + `Price: $${cryptoPrice.toLocaleString()} per ${crypto}\n\n` + `🎉 Total beers in inventory: ${this.wallet.inventory.beers}\n` + `Remaining ${crypto}: ${this.wallet.balances[crypto].toFixed(8)}` }; } catch (error) { throw new Error(`Failed to buy virtual beer: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Get inventory */ getInventory() { return this.wallet.inventory; } }

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/Bichev/coinbase-chat-mcp'

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