629 lines
24 KiB
JavaScript
629 lines
24 KiB
JavaScript
/* .SOL DOMAINS — Solana Name Service Lookup & Registration */
|
|
|
|
const SNS_API = 'https://sns-sdk-proxy.bonfida.workers.dev';
|
|
const SNS_REG = 'https://www.sns.id/domain/';
|
|
const SOLSCAN = 'https://solscan.io/account/';
|
|
const REFERRAL_WALLET = '9NuiHh5wgRPx69BFGP1ZR8kHiBENGoJrXs5GpZzKAyn8';
|
|
|
|
let walletAddress = null;
|
|
let currentTab = 'search';
|
|
|
|
// ─── DOM ────────────────────────────────────────────────────
|
|
const $ = s => document.querySelector(s);
|
|
const $$ = s => document.querySelectorAll(s);
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
initTabs();
|
|
initSearch();
|
|
initWallet();
|
|
});
|
|
|
|
// ─── TABS ───────────────────────────────────────────────────
|
|
function initTabs() {
|
|
$$('.sol-tab').forEach(tab => {
|
|
tab.addEventListener('click', () => {
|
|
const t = tab.dataset.tab;
|
|
switchTab(t);
|
|
});
|
|
});
|
|
}
|
|
|
|
function switchTab(t) {
|
|
currentTab = t;
|
|
$$('.sol-tab').forEach(tab => tab.classList.toggle('active', tab.dataset.tab === t));
|
|
$$('.sol-panel').forEach(p => p.classList.toggle('hidden', p.id !== `panel-${t}`));
|
|
if (t === 'mydomains' && walletAddress) loadMyDomains();
|
|
}
|
|
|
|
// ─── SEARCH / LOOKUP ────────────────────────────────────────
|
|
function initSearch() {
|
|
const input = $('#sol-search');
|
|
const btn = $('#sol-search-go');
|
|
if (!input || !btn) return;
|
|
btn.addEventListener('click', () => doSearch());
|
|
input.addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(); });
|
|
|
|
const revInput = $('#sol-reverse');
|
|
const revBtn = $('#sol-reverse-go');
|
|
if (revInput && revBtn) {
|
|
revBtn.addEventListener('click', () => doReverse());
|
|
revInput.addEventListener('keydown', e => { if (e.key === 'Enter') doReverse(); });
|
|
}
|
|
}
|
|
|
|
async function doSearch() {
|
|
const raw = $('#sol-search').value.trim().toLowerCase();
|
|
if (!raw) return;
|
|
const domain = raw.replace(/\.sol$/i, '');
|
|
const results = $('#search-results');
|
|
results.innerHTML = loadingHTML('RESOLVING DOMAIN...');
|
|
|
|
try {
|
|
const res = await fetch(`${SNS_API}/resolve/${domain}`);
|
|
const data = await res.json();
|
|
if (data.s === 'ok' && data.result) {
|
|
await showTakenDomain(domain, data.result);
|
|
} else {
|
|
showAvailableDomain(domain);
|
|
}
|
|
} catch (err) {
|
|
results.innerHTML = errorHTML(`Network error: ${err.message}`);
|
|
}
|
|
}
|
|
|
|
async function showTakenDomain(domain, owner) {
|
|
const results = $('#search-results');
|
|
let favourite = null;
|
|
try {
|
|
const favRes = await fetch(`${SNS_API}/favourite-domain/${owner}`);
|
|
const favData = await favRes.json();
|
|
if (favData.s === 'ok') favourite = favData.result || null;
|
|
} catch(e) {}
|
|
|
|
results.innerHTML = `
|
|
<div class="sol-result-card">
|
|
<div class="sol-result-header">
|
|
<div class="sol-result-domain">${esc(domain)}<span class="sol-ext">.sol</span></div>
|
|
<div class="sol-result-status taken">REGISTERED</div>
|
|
</div>
|
|
<div class="sol-result-body">
|
|
<div>
|
|
<div class="sol-result-field">
|
|
<span class="sol-result-label">OWNER</span>
|
|
<div class="sol-result-value">
|
|
<a href="${SOLSCAN}${esc(owner)}" target="_blank" rel="noopener">${truncAddr(owner)}</a>
|
|
</div>
|
|
</div>
|
|
${favourite ? `
|
|
<div class="sol-result-field">
|
|
<span class="sol-result-label">OWNER'S FAVOURITE</span>
|
|
<div class="sol-result-value">${esc(favourite)}.sol</div>
|
|
</div>` : ''}
|
|
</div>
|
|
<div>
|
|
<div class="sol-result-field">
|
|
<span class="sol-result-label">SOLSCAN</span>
|
|
<div class="sol-result-value">
|
|
<a href="${SOLSCAN}${esc(owner)}" target="_blank" rel="noopener">View on Solscan ↗</a>
|
|
</div>
|
|
</div>
|
|
<div class="sol-result-field">
|
|
<span class="sol-result-label">SNS</span>
|
|
<div class="sol-result-value">
|
|
<a href="${SNS_REG}${esc(domain)}" target="_blank" rel="noopener">View on SNS.id ↗</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function showAvailableDomain(domain) {
|
|
const results = $('#search-results');
|
|
const len = domain.length;
|
|
let priceEstimate = '';
|
|
if (len <= 1) priceEstimate = '~750 USDC';
|
|
else if (len === 2) priceEstimate = '~700 USDC';
|
|
else if (len === 3) priceEstimate = '~640 USDC';
|
|
else if (len === 4) priceEstimate = '~160 USDC';
|
|
else priceEstimate = '~20 USDC';
|
|
|
|
const regUrl = `${SNS_REG}${encodeURIComponent(domain)}?ref=${REFERRAL_WALLET}`;
|
|
|
|
results.innerHTML = `
|
|
<div class="sol-result-card">
|
|
<div class="sol-result-header">
|
|
<div class="sol-result-domain">${esc(domain)}<span class="sol-ext">.sol</span></div>
|
|
<div class="sol-result-status available">AVAILABLE</div>
|
|
</div>
|
|
<div class="sol-result-body">
|
|
<div>
|
|
<div class="sol-result-field">
|
|
<span class="sol-result-label">LENGTH</span>
|
|
<div class="sol-result-value">${len} character${len !== 1 ? 's' : ''}</div>
|
|
</div>
|
|
<div class="sol-result-field">
|
|
<span class="sol-result-label">ESTIMATED COST</span>
|
|
<div class="sol-price">
|
|
<span class="sol-price-amount">${priceEstimate.split(' ')[0].replace('~','')}</span>
|
|
<span class="sol-price-currency">${priceEstimate.split(' ')[1] || 'USDC'}</span>
|
|
<span class="sol-price-usd">(estimated)</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div class="sol-result-field">
|
|
<span class="sol-result-label">REGISTRATION</span>
|
|
<div class="sol-result-value">Purchase your .sol domain</div>
|
|
</div>
|
|
<a href="${regUrl}" target="_blank" rel="noopener" class="sol-register-btn">REGISTER ${esc(domain.toUpperCase())}.SOL ↗</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
// ─── REVERSE LOOKUP ─────────────────────────────────────────
|
|
async function doReverse() {
|
|
const addr = $('#sol-reverse').value.trim();
|
|
if (!addr) return;
|
|
|
|
const results = $('#reverse-results');
|
|
results.innerHTML = loadingHTML('SCANNING WALLET...');
|
|
|
|
try {
|
|
const res = await fetch(`${SNS_API}/domains/${addr}`);
|
|
const data = await res.json();
|
|
|
|
// Normalise response to array — API may return various formats
|
|
let domains = [];
|
|
if (Array.isArray(data)) {
|
|
domains = data;
|
|
} else if (data && typeof data === 'object') {
|
|
if (Array.isArray(data.result)) {
|
|
domains = data.result;
|
|
} else if (typeof data.result === 'string') {
|
|
domains = [data.result];
|
|
} else if (data.result && typeof data.result === 'object' && !Array.isArray(data.result)) {
|
|
// Single domain object
|
|
domains = [data.result];
|
|
}
|
|
}
|
|
|
|
if (!domains || domains.length === 0) {
|
|
results.innerHTML = `<div class="sol-empty">NO .SOL DOMAINS FOUND FOR THIS WALLET</div>`;
|
|
return;
|
|
}
|
|
|
|
let favourite = null;
|
|
try {
|
|
const favRes = await fetch(`${SNS_API}/favourite-domain/${addr}`);
|
|
const favData = await favRes.json();
|
|
if (favData.s === 'ok') favourite = favData.result || null;
|
|
} catch(e) {}
|
|
|
|
results.innerHTML = `
|
|
<div class="sol-stats-bar">
|
|
<div class="sol-stat">DOMAINS: <span class="sol-stat-value">${domains.length}</span></div>
|
|
${favourite ? `<div class="sol-stat">FAVOURITE: <span class="sol-stat-value">${favourite}.sol</span></div>` : ''}
|
|
<div class="sol-stat">WALLET: <span class="sol-stat-value">${truncAddr(addr)}</span></div>
|
|
</div>
|
|
<div class="sol-domains-grid">
|
|
${domains.map(d => {
|
|
const name = typeof d === 'string' ? d : (d.domain || d.name || String(d));
|
|
const isFav = favourite && name === favourite;
|
|
return `
|
|
<div class="sol-domain-card">
|
|
<div class="sol-domain-name">${esc(name)}<span class="sol-ext">.sol</span></div>
|
|
${isFav ? '<div class="sol-domain-fav">★ FAVOURITE</div>' : ''}
|
|
</div>
|
|
`;
|
|
}).join('')}
|
|
</div>
|
|
`;
|
|
} catch(err) {
|
|
results.innerHTML = errorHTML(`Network error: ${err.message}`);
|
|
}
|
|
}
|
|
|
|
// ─── WALLET ─────────────────────────────────────────────────
|
|
let connectedProvider = null;
|
|
|
|
// ── Wallet Standard Discovery ──
|
|
// MetaMask Solana and modern wallets register via the Wallet Standard protocol
|
|
const walletStandardWallets = [];
|
|
let walletStandardReady = false;
|
|
|
|
function initWalletStandard() {
|
|
// Listen for wallets that register AFTER us
|
|
window.addEventListener('wallet-standard:register-wallet', (event) => {
|
|
try {
|
|
const callback = event.detail;
|
|
if (typeof callback === 'function') {
|
|
callback({
|
|
register: (...wallets) => {
|
|
for (const w of wallets) {
|
|
if (!walletStandardWallets.some(existing => existing.name === w.name)) {
|
|
walletStandardWallets.push(w);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} catch(e) { console.warn('WS register error:', e); }
|
|
});
|
|
|
|
// Announce that the app is ready — catches wallets that loaded BEFORE us
|
|
try {
|
|
const appReadyEvent = new CustomEvent('wallet-standard:app-ready', {
|
|
detail: {
|
|
register: (...wallets) => {
|
|
for (const w of wallets) {
|
|
if (!walletStandardWallets.some(existing => existing.name === w.name)) {
|
|
walletStandardWallets.push(w);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
window.dispatchEvent(appReadyEvent);
|
|
} catch(e) { console.warn('WS app-ready error:', e); }
|
|
|
|
walletStandardReady = true;
|
|
}
|
|
|
|
// Initialise Wallet Standard immediately
|
|
initWalletStandard();
|
|
|
|
function getAvailableWallets() {
|
|
const found = [];
|
|
const seen = new Set();
|
|
|
|
function add(name, icon, provider, url, isWalletStandard) {
|
|
if (!provider || seen.has(name)) return;
|
|
seen.add(name);
|
|
found.push({ name, icon, provider, url, isWalletStandard: !!isWalletStandard });
|
|
}
|
|
|
|
// ── 1. Wallet Standard wallets (MetaMask Solana, etc.) ──
|
|
for (const w of walletStandardWallets) {
|
|
try {
|
|
const hasConnect = w.features?.['standard:connect'];
|
|
if (!hasConnect) continue;
|
|
const name = (w.name || 'WALLET').toUpperCase();
|
|
// Use wallet's icon (base64 data URI) or fallback emoji
|
|
let icon = '◈';
|
|
if (name.includes('METAMASK')) icon = '🦊';
|
|
else if (name.includes('PHANTOM')) icon = '👻';
|
|
else if (name.includes('SOLFLARE')) icon = '🔆';
|
|
else if (name.includes('BACKPACK')) icon = '🎒';
|
|
else if (name.includes('COINBASE')) icon = '🔵';
|
|
else if (name.includes('TRUST')) icon = '🛡️';
|
|
else if (name.includes('JUPITER')) icon = '🪐';
|
|
const url = w.url || '#';
|
|
add(name, icon, w, url, true);
|
|
} catch(e) {}
|
|
}
|
|
|
|
// ── 2. Legacy injection points ──
|
|
|
|
// Phantom
|
|
try {
|
|
const p = window.phantom?.solana;
|
|
if (p && p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app');
|
|
} catch(e) {}
|
|
|
|
// Solflare
|
|
try {
|
|
const s = window.solflare;
|
|
if (s && s.isSolflare) add('SOLFLARE', '🔆', s, 'https://solflare.com');
|
|
} catch(e) {}
|
|
|
|
// Backpack
|
|
try {
|
|
const b = window.backpack;
|
|
if (b && b.isBackpack) add('BACKPACK', '🎒', b, 'https://backpack.app');
|
|
} catch(e) {}
|
|
|
|
// Coinbase
|
|
try {
|
|
if (window.coinbaseSolana) add('COINBASE', '🔵', window.coinbaseSolana, 'https://coinbase.com/wallet');
|
|
} catch(e) {}
|
|
|
|
// Trust
|
|
try {
|
|
const t = window.trustwallet?.solana;
|
|
if (t) add('TRUST', '🛡️', t, 'https://trustwallet.com');
|
|
} catch(e) {}
|
|
|
|
// ── 3. Generic window.solana ──
|
|
try {
|
|
const ws = window.solana;
|
|
if (ws && !seen.has('SOLANA WALLET')) {
|
|
if (ws.isJupiter && !seen.has('JUPITER')) add('JUPITER', '🪐', ws, 'https://jup.ag');
|
|
else if (ws.isMetaMask && !seen.has('METAMASK')) add('METAMASK', '🦊', ws, 'https://metamask.io');
|
|
else if (ws.isPhantom && !seen.has('PHANTOM')) { /* skip */ }
|
|
else if (ws.isSolflare && !seen.has('SOLFLARE')){ /* skip */ }
|
|
else if (ws.isBackpack && !seen.has('BACKPACK')){ /* skip */ }
|
|
else if (!seen.has('PHANTOM') && !seen.has('METAMASK')) add('SOLANA WALLET', '◈', ws, '#');
|
|
}
|
|
} catch(e) {}
|
|
|
|
// ── 4. Jupiter specific ──
|
|
try {
|
|
const j = window.jupiter?.solana;
|
|
if (j) add('JUPITER', '🪐', j, 'https://jup.ag');
|
|
} catch(e) {}
|
|
|
|
// ── 5. Multi-provider array ──
|
|
try {
|
|
const providers = window.solana?.providers;
|
|
if (Array.isArray(providers)) {
|
|
for (const p of providers) {
|
|
if (p.isPhantom) add('PHANTOM', '👻', p, 'https://phantom.app');
|
|
else if (p.isJupiter) add('JUPITER', '🪐', p, 'https://jup.ag');
|
|
else if (p.isSolflare) add('SOLFLARE', '🔆', p, 'https://solflare.com');
|
|
else if (p.isBackpack) add('BACKPACK', '🎒', p, 'https://backpack.app');
|
|
else if (p.isMetaMask) add('METAMASK', '🦊', p, 'https://metamask.io');
|
|
}
|
|
}
|
|
} catch(e) {}
|
|
|
|
return found;
|
|
}
|
|
|
|
const KNOWN_WALLETS = [
|
|
{ name: 'PHANTOM', icon: '👻', url: 'https://phantom.app' },
|
|
{ name: 'JUPITER', icon: '🪐', url: 'https://jup.ag' },
|
|
{ name: 'SOLFLARE', icon: '🔆', url: 'https://solflare.com' },
|
|
{ name: 'BACKPACK', icon: '🎒', url: 'https://backpack.app' },
|
|
{ name: 'COINBASE', icon: '🔵', url: 'https://coinbase.com/wallet' },
|
|
{ name: 'TRUST', icon: '🛡️', url: 'https://trustwallet.com' },
|
|
{ name: 'METAMASK', icon: '🦊', url: 'https://metamask.io' },
|
|
];
|
|
|
|
function initWallet() {
|
|
const btn = $('#wallet-btn');
|
|
if (!btn) return;
|
|
btn.addEventListener('click', toggleWallet);
|
|
createWalletModal();
|
|
}
|
|
|
|
function createWalletModal() {
|
|
const modal = document.createElement('div');
|
|
modal.id = 'wallet-modal';
|
|
modal.className = 'sol-modal hidden';
|
|
modal.innerHTML = `
|
|
<div class="sol-modal-backdrop"></div>
|
|
<div class="sol-modal-content">
|
|
<div class="sol-modal-header">
|
|
<span class="sol-modal-title">SELECT WALLET</span>
|
|
<button class="sol-modal-close" id="modal-close">✕</button>
|
|
</div>
|
|
<div class="sol-modal-body" id="wallet-list"></div>
|
|
<div class="sol-modal-footer">
|
|
<span class="sol-modal-hint">No wallet? Install one to connect to Solana</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
document.body.appendChild(modal);
|
|
modal.querySelector('.sol-modal-backdrop').addEventListener('click', closeModal);
|
|
modal.querySelector('#modal-close').addEventListener('click', closeModal);
|
|
}
|
|
|
|
function openModal() {
|
|
const modal = $('#wallet-modal');
|
|
const list = $('#wallet-list');
|
|
|
|
// Small delay to let async provider injection complete
|
|
setTimeout(() => {
|
|
const available = getAvailableWallets();
|
|
const detectedNames = new Set(available.map(w => w.name));
|
|
|
|
let html = '';
|
|
|
|
if (available.length > 0) {
|
|
html += '<div class="sol-wallet-divider">DETECTED</div>';
|
|
html += available.map(w => `
|
|
<button class="sol-wallet-option detected" data-wallet="${w.name}">
|
|
<span class="sol-wallet-option-icon">${w.icon}</span>
|
|
<span class="sol-wallet-option-name">${w.name}</span>
|
|
<span class="sol-wallet-option-status detected">DETECTED</span>
|
|
</button>
|
|
`).join('');
|
|
}
|
|
|
|
const notInstalled = KNOWN_WALLETS.filter(w => !detectedNames.has(w.name));
|
|
if (notInstalled.length > 0) {
|
|
html += '<div class="sol-wallet-divider">NOT INSTALLED</div>';
|
|
html += notInstalled.map(w => `
|
|
<a href="${w.url}" target="_blank" rel="noopener" class="sol-wallet-option not-installed">
|
|
<span class="sol-wallet-option-icon">${w.icon}</span>
|
|
<span class="sol-wallet-option-name">${w.name}</span>
|
|
<span class="sol-wallet-option-status">INSTALL ↗</span>
|
|
</a>
|
|
`).join('');
|
|
}
|
|
|
|
if (available.length === 0) {
|
|
html = `<div class="sol-empty" style="padding:1rem;">
|
|
NO SOLANA WALLETS DETECTED<br><br>
|
|
Install a Solana wallet extension to connect.<br>
|
|
<span style="color:#14F195;font-size:0.6rem;">If you just installed one, refresh the page.</span>
|
|
</div>`;
|
|
}
|
|
|
|
list.innerHTML = html;
|
|
|
|
list.querySelectorAll('.sol-wallet-option.detected').forEach(btn => {
|
|
btn.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
const name = btn.dataset.wallet;
|
|
const wallet = available.find(w => w.name === name);
|
|
if (wallet) connectWallet(wallet);
|
|
});
|
|
});
|
|
|
|
modal.classList.remove('hidden');
|
|
}, 150);
|
|
}
|
|
|
|
function closeModal() {
|
|
const modal = $('#wallet-modal');
|
|
if (modal) modal.classList.add('hidden');
|
|
}
|
|
|
|
async function toggleWallet() {
|
|
if (walletAddress) {
|
|
disconnectWallet();
|
|
return;
|
|
}
|
|
openModal();
|
|
}
|
|
|
|
async function connectWallet(wallet) {
|
|
try {
|
|
closeModal();
|
|
|
|
if (wallet.isWalletStandard) {
|
|
// Wallet Standard connect (MetaMask Solana, etc.)
|
|
const connectFeature = wallet.provider.features['standard:connect'];
|
|
const result = await connectFeature.connect();
|
|
const accounts = result.accounts || [];
|
|
if (accounts.length > 0) {
|
|
walletAddress = accounts[0].address;
|
|
connectedProvider = wallet;
|
|
updateWalletUI();
|
|
} else {
|
|
throw new Error('No accounts returned');
|
|
}
|
|
} else {
|
|
// Legacy provider connect (Phantom, Solflare, etc.)
|
|
const resp = await wallet.provider.connect();
|
|
walletAddress = resp.publicKey.toString();
|
|
connectedProvider = wallet;
|
|
updateWalletUI();
|
|
}
|
|
} catch(err) {
|
|
console.error('Wallet connection failed:', err);
|
|
const status = $('#wallet-status');
|
|
if (status) status.innerHTML = `<span style="color:rgba(255,50,50,0.8)">CONNECTION REJECTED</span>`;
|
|
}
|
|
}
|
|
|
|
function disconnectWallet() {
|
|
if (connectedProvider && connectedProvider.provider) {
|
|
try { connectedProvider.provider.disconnect(); } catch(e) {}
|
|
}
|
|
walletAddress = null;
|
|
connectedProvider = null;
|
|
updateWalletUI();
|
|
}
|
|
|
|
function updateWalletUI() {
|
|
const status = $('#wallet-status');
|
|
const btn = $('#wallet-btn');
|
|
|
|
if (walletAddress) {
|
|
const wName = connectedProvider ? connectedProvider.name : 'WALLET';
|
|
const wIcon = connectedProvider ? connectedProvider.icon : '◈';
|
|
status.className = 'sol-wallet-status connected';
|
|
status.innerHTML = `● CONNECTED VIA ${wIcon} ${wName} <span class="sol-wallet-address">${truncAddr(walletAddress)}</span>`;
|
|
btn.className = 'sol-wallet-btn disconnect';
|
|
btn.textContent = 'DISCONNECT';
|
|
} else {
|
|
status.className = 'sol-wallet-status';
|
|
status.innerHTML = '○ NOT CONNECTED';
|
|
btn.className = 'sol-wallet-btn';
|
|
btn.textContent = 'CONNECT WALLET';
|
|
const panel = $('#mydomains-content');
|
|
if (panel) panel.innerHTML = `<div class="sol-empty">CONNECT WALLET TO VIEW YOUR DOMAINS</div>`;
|
|
}
|
|
}
|
|
|
|
// ─── MY DOMAINS ─────────────────────────────────────────────
|
|
async function loadMyDomains() {
|
|
if (!walletAddress) return;
|
|
const content = $('#mydomains-content');
|
|
content.innerHTML = loadingHTML('LOADING YOUR DOMAINS...');
|
|
|
|
try {
|
|
const res = await fetch(`${SNS_API}/domains/${walletAddress}`);
|
|
const data = await res.json();
|
|
|
|
// Normalise to array
|
|
let domains = [];
|
|
if (Array.isArray(data)) {
|
|
domains = data;
|
|
} else if (data && typeof data === 'object') {
|
|
if (Array.isArray(data.result)) {
|
|
domains = data.result;
|
|
} else if (typeof data.result === 'string') {
|
|
domains = [data.result];
|
|
} else if (data.result && typeof data.result === 'object') {
|
|
domains = [data.result];
|
|
}
|
|
}
|
|
|
|
let favourite = null;
|
|
try {
|
|
const favRes = await fetch(`${SNS_API}/favourite-domain/${walletAddress}`);
|
|
const favData = await favRes.json();
|
|
if (favData.s === 'ok') favourite = favData.result || null;
|
|
} catch(e) {}
|
|
|
|
if (!domains || domains.length === 0) {
|
|
const regUrl = `${SNS_REG}?ref=${REFERRAL_WALLET}`;
|
|
content.innerHTML = `
|
|
<div class="sol-empty">
|
|
NO .SOL DOMAINS FOUND<br>
|
|
<a href="${regUrl}" target="_blank" rel="noopener" style="color:#14F195; font-size:0.7rem">Register one on SNS.id ↗</a>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
content.innerHTML = `
|
|
<div class="sol-stats-bar">
|
|
<div class="sol-stat">YOUR DOMAINS: <span class="sol-stat-value">${domains.length}</span></div>
|
|
${favourite ? `<div class="sol-stat">FAVOURITE: <span class="sol-stat-value">${favourite}.sol</span></div>` : ''}
|
|
</div>
|
|
<div class="sol-domains-grid">
|
|
${domains.map(d => {
|
|
const name = typeof d === 'string' ? d : (d.domain || d.name || String(d));
|
|
const isFav = favourite && name === favourite;
|
|
return `
|
|
<div class="sol-domain-card">
|
|
<div class="sol-domain-name">${esc(name)}<span class="sol-ext">.sol</span></div>
|
|
${isFav ? '<div class="sol-domain-fav">★ FAVOURITE</div>' : ''}
|
|
</div>
|
|
`;
|
|
}).join('')}
|
|
</div>
|
|
`;
|
|
} catch(err) {
|
|
content.innerHTML = errorHTML(`Network error: ${err.message}`);
|
|
}
|
|
}
|
|
|
|
// ─── HELPERS ────────────────────────────────────────────────
|
|
function truncAddr(a) {
|
|
if (!a || a.length < 12) return a;
|
|
return a.slice(0, 6) + '...' + a.slice(-4);
|
|
}
|
|
|
|
function esc(s) {
|
|
const d = document.createElement('div');
|
|
d.textContent = s;
|
|
return d.innerHTML;
|
|
}
|
|
|
|
function loadingHTML(msg) {
|
|
return `<div class="sol-loading"><div class="sol-loading-spinner"></div><div class="sol-loading-text">${msg}</div></div>`;
|
|
}
|
|
|
|
function errorHTML(msg) {
|
|
return `<div class="sol-error">ERROR // ${msg}</div>`;
|
|
}
|