UNPKG

@gorbchain-xyz/chaindecode

Version:

GorbchainSDK V1.3+ - Complete Solana development toolkit with advanced cryptography, messaging, and collaboration features. Build secure applications with blockchain, DeFi, and end-to-end encryption.

346 lines (345 loc) 15.2 kB
/** * Rich Token Operations - Enhanced token functions with metadata and context * * These functions provide comprehensive token information including metadata, * market data, and decoded transaction context for frontend developers. */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; /** * Get rich token accounts with complete metadata and context * * This function enhances the basic getTokenAccountsByOwner with: * - Complete token metadata (name, symbol, image, etc.) * - Market data (price, market cap, etc.) * - NFT collection information * - Portfolio analysis and summary * - Performance metrics * * @param sdk - GorbchainSDK instance * @param ownerAddress - Wallet address to get token accounts for * @param options - Configuration options * @returns Promise resolving to rich token accounts with metadata * * @example * ```typescript * const sdk = new GorbchainSDK({ rpcEndpoint: 'https://rpc.gorbchain.xyz' }); * * const richTokens = await getRichTokenAccountsByOwner(sdk, 'wallet_address', { * includeMetadata: true, * includeMarketData: true, * includeNFTs: true * }); * * console.log(`Found ${richTokens.accounts.length} tokens`); * console.log(`Portfolio value: $${richTokens.summary.totalValueUsd}`); * * // Access individual token data * richTokens.accounts.forEach(token => { * console.log(`${token.metadata.symbol}: ${token.balance} tokens`); * if (token.metadata.isNFT) { * console.log(`NFT: ${token.metadata.name} from ${token.metadata.collection?.name}`); * } * }); * ``` */ export function getRichTokenAccountsByOwner(sdk_1, ownerAddress_1) { return __awaiter(this, arguments, void 0, function* (sdk, ownerAddress, options = {}) { const startTime = Date.now(); const { includeMetadata = true, includeMarketData = false, includeNFTs = true, includeZeroBalance = false, maxConcurrentRequests = 5, customPrograms = [] } = options; try { // Get all token holdings using the existing enhanced method const holdings = yield sdk.getAllTokenHoldings(ownerAddress, { includeStandardTokens: true, includeCustomTokens: true, includeNFTs, customPrograms }); // Filter out zero balance accounts if requested const filteredHoldings = includeZeroBalance ? holdings.holdings : holdings.holdings.filter(holding => { var _a; return parseFloat(((_a = holding.balance) === null || _a === void 0 ? void 0 : _a.toString()) || '0') > 0; }); // Convert to rich token accounts with metadata const richAccounts = []; let failedMetadataRequests = 0; // Process accounts in batches to avoid overwhelming the network const batchSize = maxConcurrentRequests; for (let i = 0; i < filteredHoldings.length; i += batchSize) { const batch = filteredHoldings.slice(i, i + batchSize); const batchPromises = batch.map((holding) => __awaiter(this, void 0, void 0, function* () { try { return yield enrichTokenAccount(sdk, holding, { includeMetadata, includeMarketData }); } catch (error) { failedMetadataRequests++; console.warn(`Failed to enrich token account ${holding.mint}:`, error); // Return basic account info even if enrichment fails return createBasicRichAccount(holding); } })); const enrichedBatch = yield Promise.all(batchPromises); richAccounts.push(...enrichedBatch); } // Generate portfolio summary const summary = generatePortfolioSummary(richAccounts); const duration = Date.now() - startTime; return { accounts: richAccounts, summary, meta: { metadataResolved: includeMetadata, marketDataFetched: includeMarketData, failedMetadataRequests, duration, timestamp: Date.now() } }; } catch (error) { throw new Error(`Failed to get rich token accounts: ${error instanceof Error ? error.message : 'Unknown error'}`); } }); } /** * Enrich a single token account with metadata and market data */ function enrichTokenAccount(sdk, holding, options) { return __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j; const { includeMetadata, includeMarketData } = options; // Note: Could get basic account information for enhanced data // const accountInfo = await sdk.rpc.getAccountInfo(holding.account || holding.address); // Start with basic token data const richAccount = { address: holding.account || holding.address, owner: holding.owner || '', mint: holding.mint || '', amount: ((_a = holding.amount) === null || _a === void 0 ? void 0 : _a.toString()) || '0', balance: ((_b = holding.balance) === null || _b === void 0 ? void 0 : _b.formatted) || ((_c = holding.balance) === null || _c === void 0 ? void 0 : _c.toString()) || '0', decimals: holding.decimals || 0, frozen: holding.frozen || false, metadata: { name: (_d = holding.metadata) === null || _d === void 0 ? void 0 : _d.name, symbol: (_e = holding.metadata) === null || _e === void 0 ? void 0 : _e.symbol, description: (_f = holding.metadata) === null || _f === void 0 ? void 0 : _f.description, image: (_g = holding.metadata) === null || _g === void 0 ? void 0 : _g.image, isNFT: holding.isNFT || false, attributes: ((_h = holding.metadata) === null || _h === void 0 ? void 0 : _h.attributes) || [] }, program: { id: holding.programId || 'unknown', type: determineTokenType(holding.programId), version: holding.programVersion }, created: { // Note: Basic getAccountInfo doesn't include slot/blockTime // Would need enhanced account info for creation data } }; // Enhance with additional metadata if requested if (includeMetadata && holding.mint) { try { const enhancedMetadata = yield fetchTokenMetadata(sdk, holding.mint); if (enhancedMetadata) { richAccount.metadata = Object.assign(Object.assign({}, richAccount.metadata), enhancedMetadata); } } catch (error) { console.warn(`Failed to fetch metadata for ${holding.mint}:`, error); } } // Add market data if requested if (includeMarketData && holding.mint) { try { const marketData = yield fetchMarketData(holding.mint, (_j = holding.metadata) === null || _j === void 0 ? void 0 : _j.symbol); if (marketData) { richAccount.market = marketData; } } catch (error) { console.warn(`Failed to fetch market data for ${holding.mint}:`, error); } } return richAccount; }); } /** * Create a basic rich account structure when enrichment fails */ function createBasicRichAccount(holding) { var _a, _b, _c, _d, _e; return { address: holding.account || holding.address || 'unknown', owner: holding.owner || 'unknown', mint: holding.mint || 'unknown', amount: ((_a = holding.amount) === null || _a === void 0 ? void 0 : _a.toString()) || '0', balance: ((_b = holding.balance) === null || _b === void 0 ? void 0 : _b.formatted) || ((_c = holding.balance) === null || _c === void 0 ? void 0 : _c.toString()) || '0', decimals: holding.decimals || 0, frozen: holding.frozen || false, metadata: { name: ((_d = holding.metadata) === null || _d === void 0 ? void 0 : _d.name) || 'Unknown Token', symbol: ((_e = holding.metadata) === null || _e === void 0 ? void 0 : _e.symbol) || 'UNKNOWN', isNFT: holding.isNFT || false, attributes: [] }, program: { id: holding.programId || 'unknown', type: 'custom', }, created: {} }; } /** * Determine token type based on program ID */ function determineTokenType(programId) { if (!programId) return 'custom'; switch (programId) { case 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA': return 'spl-token'; case 'FGyzDo6bhE7gFmSYymmFnJ3SZZu3xWGBA7sNHXR7QQsn': return 'token-2022'; case 'BvoSmPBF6mBRxBMY9FPguw1zUoUg3xrc5CaWf7y5ACkc': case 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s': return 'nft'; default: return 'custom'; } } // Token metadata cache for better performance const tokenMetadataCache = new Map(); const TOKEN_CACHE_DURATION = 10 * 60 * 1000; // 10 minutes for token metadata /** * Fetch additional token metadata from various sources with caching */ function fetchTokenMetadata(sdk, mintAddress) { return __awaiter(this, void 0, void 0, function* () { // Check cache first const now = Date.now(); const cached = tokenMetadataCache.get(mintAddress); if (cached && (now - cached.timestamp < TOKEN_CACHE_DURATION)) { return cached.data; } try { // Try to get account info for the mint const mintInfo = yield sdk.rpc.getAccountInfo(mintAddress); if (!mintInfo) { // Cache null result to avoid repeated failed requests tokenMetadataCache.set(mintAddress, { data: null, timestamp: now }); return null; } // Basic metadata structure const metadata = {}; // Try to decode mint account data if it's a known token program if (mintInfo.owner) { const programId = mintInfo.owner.toString(); // For NFTs, try to fetch metadata from metadata account if (programId === 'BvoSmPBF6mBRxBMY9FPguw1zUoUg3xrc5CaWf7y5ACkc' || programId === 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s') { metadata.isNFT = true; // Try to fetch NFT metadata (only if not cached separately) try { const nftMetadata = yield fetchNFTMetadata(sdk, mintAddress); if (nftMetadata) { Object.assign(metadata, nftMetadata); } } catch (error) { // Silently fail for better performance } } } // Cache the result tokenMetadataCache.set(mintAddress, { data: metadata, timestamp: now }); return metadata; } catch (error) { // Cache failed attempts to avoid retrying immediately tokenMetadataCache.set(mintAddress, { data: null, timestamp: now }); return null; } }); } /** * Fetch NFT-specific metadata */ function fetchNFTMetadata(_sdk, _mintAddress) { return __awaiter(this, void 0, void 0, function* () { // This would integrate with NFT metadata standards // For now, return basic structure return { isNFT: true, collection: { name: 'Unknown Collection', family: 'Unknown', verified: false } }; }); } /** * Fetch market data for a token (placeholder - would integrate with price APIs) */ function fetchMarketData(_mintAddress, _symbol) { return __awaiter(this, void 0, void 0, function* () { // This would integrate with price APIs like CoinGecko, Jupiter, etc. // For now, return null as this requires external API integration return null; }); } /** * Generate portfolio summary from rich accounts */ function generatePortfolioSummary(accounts) { const totalTokens = accounts.filter(acc => !acc.metadata.isNFT).length; const totalNFTs = accounts.filter(acc => acc.metadata.isNFT).length; // Calculate total USD value if market data is available let totalValueUsd = 0; let hasMarketData = false; accounts.forEach(account => { var _a; if (((_a = account.market) === null || _a === void 0 ? void 0 : _a.priceUsd) && !account.metadata.isNFT) { const balance = parseFloat(account.balance.replace(/[^0-9.-]/g, '')); totalValueUsd += balance * account.market.priceUsd; hasMarketData = true; } }); // Calculate diversity score (simplified) const diversityScore = Math.min(accounts.length / 10, 1); // Normalize to 0-1 // Get top holdings const sortedAccounts = accounts .filter(acc => !acc.metadata.isNFT) .sort((a, b) => { var _a, _b; const aValue = ((_a = a.market) === null || _a === void 0 ? void 0 : _a.priceUsd) ? parseFloat(a.balance.replace(/[^0-9.-]/g, '')) * a.market.priceUsd : 0; const bValue = ((_b = b.market) === null || _b === void 0 ? void 0 : _b.priceUsd) ? parseFloat(b.balance.replace(/[^0-9.-]/g, '')) * b.market.priceUsd : 0; return bValue - aValue; }); const topHoldings = sortedAccounts.slice(0, 5).map(account => { var _a; const value = ((_a = account.market) === null || _a === void 0 ? void 0 : _a.priceUsd) ? parseFloat(account.balance.replace(/[^0-9.-]/g, '')) * account.market.priceUsd : 0; return { symbol: account.metadata.symbol || 'UNKNOWN', percentage: totalValueUsd > 0 ? (value / totalValueUsd) * 100 : 0, valueUsd: value > 0 ? value : undefined }; }); return { totalTokens, totalNFTs, totalValueUsd: hasMarketData ? totalValueUsd : undefined, diversityScore, topHoldings }; }