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);
});