UNPKG

p-sdk-wallet

Version:

A comprehensive wallet SDK for React Native (pwc), supporting multi-chain and multi-account features.

163 lines (162 loc) 5.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MetadataResolver = void 0; /** * Service for resolving NFT metadata from various sources (IPFS, HTTP) */ class MetadataResolver { /** * Resolves metadata from a URI (IPFS or HTTP) * @param uri - The metadata URI to resolve * @returns Promise resolving to NFT metadata */ async resolveMetadata(uri) { // Check cache first const cached = this.getCachedMetadata(uri); if (cached) { return cached; } let resolvedUri; if (uri.startsWith('ipfs://')) { resolvedUri = await this.resolveIPFS(uri); } else if (uri.startsWith('http://') || uri.startsWith('https://')) { resolvedUri = uri; } else { throw new Error(`Unsupported URI format: ${uri}`); } const metadata = await this.fetchMetadata(resolvedUri); this.cacheMetadata(uri, metadata); return metadata; } /** * Resolves IPFS URI to HTTP URL using multiple gateways * @param ipfsUri - IPFS URI (ipfs://...) * @returns Promise resolving to HTTP URL */ async resolveIPFS(ipfsUri) { const ipfsHash = ipfsUri.replace('ipfs://', ''); // Try multiple gateways for redundancy for (const gateway of MetadataResolver.IPFS_GATEWAYS) { try { const url = gateway + ipfsHash; const response = await fetch(url, { method: 'HEAD' }); if (response.ok) { return url; } } catch (error) { console.warn(`IPFS gateway ${gateway} failed:`, error); continue; } } throw new Error(`Failed to resolve IPFS URI: ${ipfsUri}`); } /** * Fetches metadata from HTTP URL * @param url - HTTP URL to fetch metadata from * @returns Promise resolving to NFT metadata */ async fetchMetadata(url) { try { const response = await fetch(url, { method: 'GET', headers: { 'Accept': 'application/json', 'User-Agent': 'PWC-Wallet-SDK/1.0' } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const rawData = await response.json(); return this.parseMetadata(rawData); } catch (error) { throw new Error(`Failed to fetch metadata from ${url}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } /** * Parses raw metadata into standardized format * @param rawData - Raw metadata from contract * @returns Parsed NFT metadata */ parseMetadata(rawData) { // Handle different metadata formats const metadata = { name: rawData.name || rawData.title || 'Unknown NFT', description: rawData.description || '', image: rawData.image || rawData.image_url || '', external_url: rawData.external_url || rawData.externalUrl || undefined, attributes: [] }; // Parse attributes if (rawData.attributes && Array.isArray(rawData.attributes)) { metadata.attributes = rawData.attributes.map((attr) => ({ trait_type: attr.trait_type || attr.traitType || '', value: attr.value || '', display_type: attr.display_type || attr.displayType || undefined })); } // Validate required fields if (!metadata.name || !metadata.image) { throw new Error('Invalid metadata: missing required fields (name, image)'); } return metadata; } /** * Gets cached metadata if available and not expired * @param uri - Metadata URI * @returns Cached metadata or null if not found/expired */ getCachedMetadata(uri) { const cached = MetadataResolver.metadataCache.get(uri); if (!cached) { return null; } const now = Date.now(); if (now - cached.timestamp > MetadataResolver.CACHE_DURATION) { MetadataResolver.metadataCache.delete(uri); return null; } return cached.data; } /** * Caches metadata for future use * @param uri - Metadata URI * @param metadata - Metadata to cache */ cacheMetadata(uri, metadata) { MetadataResolver.metadataCache.set(uri, { data: metadata, timestamp: Date.now() }); } /** * Clears the metadata cache */ static clearCache() { MetadataResolver.metadataCache.clear(); } /** * Gets cache statistics * @returns Cache statistics */ static getCacheStats() { return { size: MetadataResolver.metadataCache.size, entries: Array.from(MetadataResolver.metadataCache.values()).length }; } } exports.MetadataResolver = MetadataResolver; MetadataResolver.IPFS_GATEWAYS = [ 'https://ipfs.io/ipfs/', 'https://gateway.pinata.cloud/ipfs/', 'https://cloudflare-ipfs.com/ipfs/', 'https://dweb.link/ipfs/', 'https://gateway.ipfs.io/ipfs/' ]; MetadataResolver.CACHE_DURATION = 5 * 60 * 1000; // 5 minutes MetadataResolver.metadataCache = new Map();