@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
JavaScript
/**
* 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
};
}