/* .SOL DOMAINS — Solana Name Service Lookup & Registration */
const SNS_API = 'https://sns-sdk-proxy.bonfida.workers.dev';
const BONFIDA_REG = 'https://naming.bonfida.org/#/domain/';
const SOLSCAN = 'https://solscan.io/account/';
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(); });
// Reverse lookup
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;
// Strip .sol if user typed it
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) {
// Domain is registered — show owner info
await showTakenDomain(domain, data.result);
} else {
// Domain is available
showAvailableDomain(domain);
}
} catch (err) {
results.innerHTML = errorHTML(`Network error: ${err.message}`);
}
}
async function showTakenDomain(domain, owner) {
const results = $('#search-results');
// Try to get more data
let records = {};
try {
const recRes = await fetch(`${SNS_API}/record-v2/${domain}/SOL`);
if (recRes.ok) {
const recData = await recRes.json();
records.sol = recData.result || null;
}
} catch(e) {}
// Try favourite domain of owner
let favourite = null;
try {
const favRes = await fetch(`${SNS_API}/favourite-domain/${owner}`);
if (favRes.ok) {
const favData = await favRes.json();
favourite = favData.result || null;
}
} catch(e) {}
results.innerHTML = `
${favourite ? `
OWNER'S FAVOURITE
${esc(favourite)}.sol
` : ''}
`;
}
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';
results.innerHTML = `
LENGTH
${len} character${len !== 1 ? 's' : ''}
ESTIMATED COST
${priceEstimate.split(' ')[0].replace('~','')}
${priceEstimate.split(' ')[1] || 'USDC'}
(estimated)
`;
}
// ─── REVERSE LOOKUP ─────────────────────────────────────────
async function doReverse() {
const addr = $('#sol-reverse').value.trim();
if (!addr) return;
const results = $('#reverse-results');
results.innerHTML = loadingHTML('SCANNING WALLET...');
try {
// Get all domains for this wallet
const res = await fetch(`${SNS_API}/domains/${addr}`);
if (!res.ok) {
results.innerHTML = errorHTML('Could not resolve wallet. Check the address.');
return;
}
const data = await res.json();
const domains = data.result || data;
if (!domains || !domains.length) {
results.innerHTML = `NO .SOL DOMAINS FOUND FOR THIS WALLET
`;
return;
}
// Get favourite
let favourite = null;
try {
const favRes = await fetch(`${SNS_API}/favourite-domain/${addr}`);
if (favRes.ok) {
const favData = await favRes.json();
favourite = favData.result || null;
}
} catch(e) {}
results.innerHTML = `
DOMAINS: ${domains.length}
${favourite ? `
FAVOURITE: ${favourite}.sol
` : ''}
WALLET: ${truncAddr(addr)}
${domains.map(d => {
const name = typeof d === 'string' ? d : (d.domain || d);
const isFav = favourite && name === favourite;
return `
${esc(name)}.sol
${isFav ? '
★ FAVOURITE
' : ''}
`;
}).join('')}
`;
} catch(err) {
results.innerHTML = errorHTML(`Network error: ${err.message}`);
}
}
// ─── WALLET ─────────────────────────────────────────────────
let connectedProvider = null;
const WALLETS = [
{ name: 'PHANTOM', icon: '👻', detect: () => window.phantom?.solana, url: 'https://phantom.app' },
{ name: 'SOLFLARE', icon: '🔆', detect: () => window.solflare, url: 'https://solflare.com' },
{ name: 'BACKPACK', icon: '🎒', detect: () => window.backpack, url: 'https://backpack.app' },
{ name: 'COINBASE', icon: '🔵', detect: () => window.coinbaseSolana, url: 'https://coinbase.com/wallet' },
{ name: 'TRUST', icon: '🛡️', detect: () => window.trustwallet?.solana, url: 'https://trustwallet.com' },
{ name: 'METAMASK', icon: '🦊', detect: () => window.ethereum?.isSolana ? window.ethereum : null, url: 'https://metamask.io' },
];
function getAvailableWallets() {
const available = [];
for (const w of WALLETS) {
try {
const provider = w.detect();
if (provider) available.push({ ...w, provider });
} catch(e) {}
}
// Fallback: check generic window.solana if no specific wallet matched
if (available.length === 0 && window.solana) {
available.push({ name: 'SOLANA WALLET', icon: '◈', provider: window.solana, url: '#' });
}
return available;
}
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 = `
`;
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');
const available = getAvailableWallets();
let html = '';
if (available.length > 0) {
html += available.map(w => `
`).join('');
}
// Show install links for wallets not detected
const notInstalled = WALLETS.filter(w => { try { return !w.detect(); } catch(e) { return true; } });
if (notInstalled.length > 0) {
html += 'NOT INSTALLED
';
html += notInstalled.map(w => `
${w.icon}
${w.name}
INSTALL ↗
`).join('');
}
list.innerHTML = html;
// Bind click handlers for detected wallets
list.querySelectorAll('.sol-wallet-option:not(.not-installed)').forEach(btn => {
btn.addEventListener('click', () => {
const name = btn.dataset.wallet;
const wallet = available.find(w => w.name === name);
if (wallet) connectWallet(wallet);
});
});
modal.classList.remove('hidden');
}
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();
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 = `CONNECTION REJECTED`;
}
}
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} ${truncAddr(walletAddress)}`;
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 = `CONNECT WALLET TO VIEW YOUR DOMAINS
`;
}
}
// ─── 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}`);
if (!res.ok) {
content.innerHTML = errorHTML('Failed to load domains.');
return;
}
const data = await res.json();
const domains = data.result || data;
// Get favourite
let favourite = null;
try {
const favRes = await fetch(`${SNS_API}/favourite-domain/${walletAddress}`);
if (favRes.ok) {
const favData = await favRes.json();
favourite = favData.result || null;
}
} catch(e) {}
if (!domains || !domains.length) {
content.innerHTML = `
`;
return;
}
content.innerHTML = `
YOUR DOMAINS: ${domains.length}
${favourite ? `
FAVOURITE: ${favourite}.sol
` : ''}
${domains.map(d => {
const name = typeof d === 'string' ? d : (d.domain || d);
const isFav = favourite && name === favourite;
return `
${esc(name)}.sol
${isFav ? '
★ FAVOURITE
' : ''}
`;
}).join('')}
`;
} 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 ``;
}
function errorHTML(msg) {
return `ERROR // ${msg}
`;
}