onst s = document.getElementById('cfgSecondary').value; const sym = parseInt(document.getElementById('cfgSymbol').value); const pat = parseInt(document.getElementById('cfgPattern').value); return [ shape, parseInt(p.slice(1, 3), 16), parseInt(p.slice(3, 5), 16), parseInt(p.slice(5, 7), 16), parseInt(s.slice(1, 3), 16), parseInt(s.slice(3, 5), 16), parseInt(s.slice(5, 7), 16), sym, pat ]; } function updatePreview() { const cfg = getConfig(); const nextId = document.getElementById('sMinted').textContent; const displayId = nextId && nextId !== '--' ? nextId : '?'; document.getElementById('preview').innerHTML = ExoCore.renderSVGPreview(cfg, { tokenId: displayId }); } function randomize() { document.getElementById('cfgShape').value = Math.floor(Math.random() * 6); const rc = () => '#' + Math.floor(Math.random() * 16777215).toString(16).padStart(6, '0'); document.getElementById('cfgPrimary').value = rc(); document.getElementById('cfgSecondary').value = rc(); document.getElementById('cfgSymbol').value = Math.floor(Math.random() * 8); document.getElementById('cfgPattern').value = Math.floor(Math.random() * 6); updatePreview(); } async function loadStats() { try { const [price, phase, nextId, supply] = await Promise.all([ ExoCore.withFallback(() => ExoCore.contracts.core.getMintPrice()), ExoCore.withFallback(() => ExoCore.contracts.core.getMintPhase()), ExoCore.withFallback(() => ExoCore.contracts.core.nextTokenId()), ExoCore.withFallback(() => ExoCore.contracts.core.totalSupply()), ]); mintPrice = price; const next = Number(nextId); document.getElementById('sPhase').textContent = phase.toUpperCase(); document.getElementById('sPrice').textContent = ExoCore.formatEth(price) + ' ETH'; document.getElementById('sMinted').textContent = Number(supply); document.getElementById('sGenesis').textContent = Math.max(0, 1000 - next + 1); } catch (e) { console.error('Stats error:', e); } } async function onConnect() { try { await ExoCore.connectWallet(); onAccountChange(); } catch (e) { ExoUI.showStatus('connectStatus', e.message, 'error'); } } async function onAccountChange() { const account = ExoCore.account; if (!account) { document.getElementById('connectBtn').textContent = 'Connect Wallet'; document.getElementById('walletInfo').classList.add('hidden'); document.getElementById('mintBtn').disabled = true; return; } document.getElementById('connectBtn').textContent = ExoCore.truncAddr(account); document.getElementById('connectBtn').style.borderColor = 'var(--gold)'; document.getElementById('connectBtn').style.color = 'var(--gold)'; document.getElementById('walletInfo').classList.remove('hidden'); document.getElementById('addrDisplay').textContent = account; document.getElementById('mintBtn').disabled = false; try { const [isWL, usedFree, count] = await Promise.all([ ExoCore.contracts.core.whitelist(account), ExoCore.contracts.core.usedFreeMint(account), ExoCore.contracts.core.mintCount(account), ]); const badges = document.getElementById('badges'); badges.innerHTML = ''; if (isWL) { badges.innerHTML = 'Whitelisted'; if (!usedFree) badges.innerHTML += ' Free Mint'; } if (Number(count) >= 3) { document.getElementById('mintBtn').disabled = true; document.getElementById('mintBtn').textContent = 'Limit Reached (3/3)'; } else if (isWL && !usedFree) { document.getElementById('mintBtn').textContent = 'Mint Exoskeleton (Free)'; } else { document.getElementById('mintBtn').textContent = 'Mint Exoskeleton'; } } catch (e) { console.error('Wallet check error:', e); } } window.addEventListener('exo:accountChanged', onAccountChange); async function doMint() { const account = ExoCore.account; if (!account) { ExoUI.showStatus('mintStatus', 'Connect wallet first', 'error'); return; } const btn = document.getElementById('mintBtn'); btn.disabled = true; btn.textContent = 'Minting...'; ExoUI.showStatus('mintStatus', 'Confirm transaction in your wallet...', 'pending'); try { const cfg = getConfig(); const configBytes = new Uint8Array(cfg); const core = ExoCore.getSignedContract('core'); const [isWL, usedFree] = await Promise.all([ ExoCore.contracts.core.whitelist(account), ExoCore.contracts.core.usedFreeMint(account), ]); const value = (isWL && !usedFree) ? 0n : mintPrice; const tx = await core.mint(configBytes, { value }); ExoUI.showStatus('mintStatus', `Minting... TX: ${tx.hash.slice(0, 14)}...`, 'pending'); ExoUI.toast( `Transaction submitted: ${tx.hash.slice(0, 10)}...`, 'pending', 0 ); const receipt = await tx.wait(); const transferLog = receipt.logs.find(l => l.address.toLowerCase() === ExoCore.CONTRACTS.core.toLowerCase() && l.topics[0] === '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef' ); const tokenId = transferLog ? Number(BigInt(transferLog.topics[3])) : '?'; ExoUI.showStatus('mintStatus', `Minted Exoskeleton #${tokenId}! View your Exoskeleton ยท TX`, 'ok'); ExoUI.toast( `Exoskeleton #${tokenId} minted! View`, 'success', 8000 ); btn.textContent = 'Minted #' + tokenId; loadStats(); } catch (e) { const msg = e.code === 'ACTION_REJECTED' ? 'Transaction rejected by user' : e.code === 'INSUFFICIENT_FUNDS' ? 'Insufficient ETH balance' : (e.shortMessage || e.message || String(e)); ExoUI.showStatus('mintStatus', msg, 'error'); btn.textContent = 'Mint Exoskeleton'; btn.disabled = false; } } ExoCore.onReady(() => { loadStats(); updatePreview(); setInterval(loadStats, 30000); });