.7"/>`; } else if (sym === 6) { o += ``; o += ``; o += ``; o += ``; o += ``; o += ``; o += ``; o += ``; o += ``; } else if (sym === 7) { o += ``; } // Pattern (scaled with reputation) if (pat === 1) { for (let x = 170; x <= 330; x += 36) o += ``; for (let y = 170; y <= 310; y += 36) o += ``; } else if (pat === 2) { for (let i = 0; i < cx * 2 && i < 20; i++) { o += ``; } } else if (pat === 3) { for (let i = 0; i < cx && i < 10; i++) { const x = 180 + i * 15; o += ``; } } else if (pat === 4) { for (let i = 0; i < cx && i < 8; i++) { const x1 = 200 + (i * 31 % 100), y1 = 200 + (i * 47 % 80); const x2 = 200 + ((i + 1) * 31 % 100), y2 = 200 + ((i + 1) * 47 % 80); o += ``; o += ``; } } else if (pat === 5) { for (let i = 1; i <= cx && i <= 5; i++) { o += ``; } } // Activity nodes (module dots in orbit) if (mods > 0) { for (let i = 0; i < Math.min(mods, 8); i++) { const angle = (i / Math.min(mods, 8)) * Math.PI * 2 - Math.PI / 2; const rx = 95, ry = 95; const nx = 250 + Math.cos(angle) * rx; const ny = 240 + Math.sin(angle) * ry; o += ``; } } // Text overlays o += `EXOSKELETON`; o += `#${displayId}`; o += `${escHtml(displayName)}`; if (genesis) o += 'GENESIS'; // Stats bar o += ``; o += `MSG:${msgs}`; o += `STO:${sto}`; o += `MOD:${mods}`; // Reputation glow if (repScore >= 100) { const glowIntensity = Math.min(repScore / 10000, 0.5); o += ``; } return o; } // ── Parse Visual Config Bytes ── function parseVisualConfig(configHex) { if (!configHex || configHex.length < 18) return null; const h = configHex.replace('0x', ''); return { shape: parseInt(h.substring(0, 2), 16), pr: parseInt(h.substring(2, 4), 16), pg: parseInt(h.substring(4, 6), 16), pb: parseInt(h.substring(6, 8), 16), sr: parseInt(h.substring(8, 10), 16), sg: parseInt(h.substring(10, 12), 16), sb: parseInt(h.substring(12, 14), 16), symbol: parseInt(h.substring(14, 16), 16), pattern: parseInt(h.substring(16, 18), 16), }; } // ── Wait for ethers.js to load, then init ── function onReady(fn) { if (typeof ethers !== 'undefined') { initProvider(); fn(); } else { const check = setInterval(() => { if (typeof ethers !== 'undefined') { clearInterval(check); initProvider(); fn(); } }, 50); setTimeout(() => clearInterval(check), 10000); } } // ── Marketplace Helpers ── let _marketplaceIface = null; function getMarketplaceIface() { if (!_marketplaceIface) _marketplaceIface = new ethers.Interface(MARKETPLACE_ABI); return _marketplaceIface; } async function loadMarketplaceStats() { initProvider(); const stats = await withFallback(() => contracts.marketplace.getStats()); return { totalModules: Number(stats.totalModules), totalApproved: Number(stats.totalApproved), totalActivations: Number(stats.totalActivations), totalPlatformRevenue: stats.totalPlatformRevenue, pendingCount: Number(stats.pendingCount), listingFees: stats.listingFees, }; } async function loadAllMarketplaceModules() { initProvider(); const names = await withFallback(() => contracts.marketplace.getAllModuleNames()); if (names.length === 0) return []; const mc = new ethers.Contract(MULTICALL3, MULTICALL_ABI, provider); const mIface = getMarketplaceIface(); const calls = names.map(n => ({ target: CONTRACTS.marketplace, callData: mIface.encodeFunctionData('getModule', [n]), })); const results = await mc.tryAggregate(false, calls); const modules = []; for (let i = 0; i < names.length; i++) { if (results[i].success) { try { const d = mIface.decodeFunctionResult('getModule', results[i].returnData); modules.push({ nameHash: names[i], builder: d.builder, name: d.name, description: d.description, version: d.version, price: d.price, status: Number(d.status), submittedAt: Number(d.submittedAt), approvedAt: Number(d.approvedAt), activations: Number(d.moduleActivations), revenue: d.moduleRevenue, }); } catch (e) {} } } return modules; } async function loadBuilderProfile(address) { initProvider(); const b = await withFallback(() => contracts.marketplace.getBuilder(address)); return { name: b.name, bio: b.bio, modulesSubmitted: Number(b.modulesSubmitted), totalEarnings: b.totalEarnings, registered: b.registered, }; } // ── Exports (attach to window for use in HTML pages) ── window.ExoCore = { CONTRACTS, CHAIN_ID, CHAIN_ID_HEX, RPC_URLS, SHAPES, SYMBOLS, PATTERNS, CORE_ABI, REGISTRY_ABI, RENDERER_ABI, WALLET_ABI, MARKETPLACE_ABI, get provider() { return provider; }, get contracts() { return contracts; }, get account() { return walletAccount; }, get signer() { return walletSigner; }, initProvider, withFallback, withRetry, loadTokenBatch, loadAllTokens, renderTokenSVG, connectWallet, autoConnect, getSignedContract, getOwnedTokens, loadMarketplaceStats, loadAllMarketplaceModules, loadBuilderProfile, getTrustTier, truncAddr, formatAge, formatScore, formatEth, escHtml, renderSVGPreview, parseVisualConfig, onReady, };