fix: 4 fixes - direct reg via sns.id with referral, reverse lookup array normalisation, wallet detection (Jupiter/MetaMask), dead bonfida URL
This commit is contained in:
parent
8297281ddb
commit
f1a4437bf9
1 changed files with 141 additions and 73 deletions
208
js/soldomains.js
208
js/soldomains.js
|
|
@ -1,8 +1,9 @@
|
||||||
/* .SOL DOMAINS — Solana Name Service Lookup & Registration */
|
/* .SOL DOMAINS — Solana Name Service Lookup & Registration */
|
||||||
|
|
||||||
const SNS_API = 'https://sns-sdk-proxy.bonfida.workers.dev';
|
const SNS_API = 'https://sns-sdk-proxy.bonfida.workers.dev';
|
||||||
const BONFIDA_REG = 'https://naming.bonfida.org/#/domain/';
|
const SNS_REG = 'https://www.sns.id/domain/';
|
||||||
const SOLSCAN = 'https://solscan.io/account/';
|
const SOLSCAN = 'https://solscan.io/account/';
|
||||||
|
const REFERRAL_WALLET = '9NuiHh5wgRPx69BFGP1ZR8kHiBENGoJrXs5GpZzKAyn8';
|
||||||
|
|
||||||
let walletAddress = null;
|
let walletAddress = null;
|
||||||
let currentTab = 'search';
|
let currentTab = 'search';
|
||||||
|
|
@ -31,7 +32,6 @@ function switchTab(t) {
|
||||||
currentTab = t;
|
currentTab = t;
|
||||||
$$('.sol-tab').forEach(tab => tab.classList.toggle('active', tab.dataset.tab === 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}`));
|
$$('.sol-panel').forEach(p => p.classList.toggle('hidden', p.id !== `panel-${t}`));
|
||||||
|
|
||||||
if (t === 'mydomains' && walletAddress) loadMyDomains();
|
if (t === 'mydomains' && walletAddress) loadMyDomains();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,11 +40,9 @@ function initSearch() {
|
||||||
const input = $('#sol-search');
|
const input = $('#sol-search');
|
||||||
const btn = $('#sol-search-go');
|
const btn = $('#sol-search-go');
|
||||||
if (!input || !btn) return;
|
if (!input || !btn) return;
|
||||||
|
|
||||||
btn.addEventListener('click', () => doSearch());
|
btn.addEventListener('click', () => doSearch());
|
||||||
input.addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(); });
|
input.addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(); });
|
||||||
|
|
||||||
// Reverse lookup
|
|
||||||
const revInput = $('#sol-reverse');
|
const revInput = $('#sol-reverse');
|
||||||
const revBtn = $('#sol-reverse-go');
|
const revBtn = $('#sol-reverse-go');
|
||||||
if (revInput && revBtn) {
|
if (revInput && revBtn) {
|
||||||
|
|
@ -56,8 +54,6 @@ function initSearch() {
|
||||||
async function doSearch() {
|
async function doSearch() {
|
||||||
const raw = $('#sol-search').value.trim().toLowerCase();
|
const raw = $('#sol-search').value.trim().toLowerCase();
|
||||||
if (!raw) return;
|
if (!raw) return;
|
||||||
|
|
||||||
// Strip .sol if user typed it
|
|
||||||
const domain = raw.replace(/\.sol$/i, '');
|
const domain = raw.replace(/\.sol$/i, '');
|
||||||
const results = $('#search-results');
|
const results = $('#search-results');
|
||||||
results.innerHTML = loadingHTML('RESOLVING DOMAIN...');
|
results.innerHTML = loadingHTML('RESOLVING DOMAIN...');
|
||||||
|
|
@ -65,12 +61,9 @@ async function doSearch() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${SNS_API}/resolve/${domain}`);
|
const res = await fetch(`${SNS_API}/resolve/${domain}`);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
||||||
if (data.s === 'ok' && data.result) {
|
if (data.s === 'ok' && data.result) {
|
||||||
// Domain is registered — show owner info
|
|
||||||
await showTakenDomain(domain, data.result);
|
await showTakenDomain(domain, data.result);
|
||||||
} else {
|
} else {
|
||||||
// Domain is available
|
|
||||||
showAvailableDomain(domain);
|
showAvailableDomain(domain);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -80,25 +73,11 @@ async function doSearch() {
|
||||||
|
|
||||||
async function showTakenDomain(domain, owner) {
|
async function showTakenDomain(domain, owner) {
|
||||||
const results = $('#search-results');
|
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;
|
let favourite = null;
|
||||||
try {
|
try {
|
||||||
const favRes = await fetch(`${SNS_API}/favourite-domain/${owner}`);
|
const favRes = await fetch(`${SNS_API}/favourite-domain/${owner}`);
|
||||||
if (favRes.ok) {
|
|
||||||
const favData = await favRes.json();
|
const favData = await favRes.json();
|
||||||
favourite = favData.result || null;
|
if (favData.s === 'ok') favourite = favData.result || null;
|
||||||
}
|
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
results.innerHTML = `
|
results.innerHTML = `
|
||||||
|
|
@ -129,9 +108,9 @@ async function showTakenDomain(domain, owner) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sol-result-field">
|
<div class="sol-result-field">
|
||||||
<span class="sol-result-label">BONFIDA</span>
|
<span class="sol-result-label">SNS</span>
|
||||||
<div class="sol-result-value">
|
<div class="sol-result-value">
|
||||||
<a href="${BONFIDA_REG}${esc(domain)}" target="_blank" rel="noopener">View on Bonfida ↗</a>
|
<a href="${SNS_REG}${esc(domain)}" target="_blank" rel="noopener">View on SNS.id ↗</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -150,6 +129,8 @@ function showAvailableDomain(domain) {
|
||||||
else if (len === 4) priceEstimate = '~160 USDC';
|
else if (len === 4) priceEstimate = '~160 USDC';
|
||||||
else priceEstimate = '~20 USDC';
|
else priceEstimate = '~20 USDC';
|
||||||
|
|
||||||
|
const regUrl = `${SNS_REG}${encodeURIComponent(domain)}?ref=${REFERRAL_WALLET}`;
|
||||||
|
|
||||||
results.innerHTML = `
|
results.innerHTML = `
|
||||||
<div class="sol-result-card">
|
<div class="sol-result-card">
|
||||||
<div class="sol-result-header">
|
<div class="sol-result-header">
|
||||||
|
|
@ -174,9 +155,9 @@ function showAvailableDomain(domain) {
|
||||||
<div>
|
<div>
|
||||||
<div class="sol-result-field">
|
<div class="sol-result-field">
|
||||||
<span class="sol-result-label">REGISTRATION</span>
|
<span class="sol-result-label">REGISTRATION</span>
|
||||||
<div class="sol-result-value">Register via Bonfida's official portal</div>
|
<div class="sol-result-value">Purchase your .sol domain</div>
|
||||||
</div>
|
</div>
|
||||||
<a href="${BONFIDA_REG}${esc(domain)}" target="_blank" rel="noopener" class="sol-register-btn">REGISTER ${esc(domain.toUpperCase())}.SOL ↗</a>
|
<a href="${regUrl}" target="_blank" rel="noopener" class="sol-register-btn">REGISTER ${esc(domain.toUpperCase())}.SOL ↗</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -192,28 +173,34 @@ async function doReverse() {
|
||||||
results.innerHTML = loadingHTML('SCANNING WALLET...');
|
results.innerHTML = loadingHTML('SCANNING WALLET...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get all domains for this wallet
|
|
||||||
const res = await fetch(`${SNS_API}/domains/${addr}`);
|
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 data = await res.json();
|
||||||
const domains = data.result || data;
|
|
||||||
|
|
||||||
if (!domains || !domains.length) {
|
// 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>`;
|
results.innerHTML = `<div class="sol-empty">NO .SOL DOMAINS FOUND FOR THIS WALLET</div>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get favourite
|
|
||||||
let favourite = null;
|
let favourite = null;
|
||||||
try {
|
try {
|
||||||
const favRes = await fetch(`${SNS_API}/favourite-domain/${addr}`);
|
const favRes = await fetch(`${SNS_API}/favourite-domain/${addr}`);
|
||||||
if (favRes.ok) {
|
|
||||||
const favData = await favRes.json();
|
const favData = await favRes.json();
|
||||||
favourite = favData.result || null;
|
if (favData.s === 'ok') favourite = favData.result || null;
|
||||||
}
|
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
results.innerHTML = `
|
results.innerHTML = `
|
||||||
|
|
@ -224,7 +211,7 @@ async function doReverse() {
|
||||||
</div>
|
</div>
|
||||||
<div class="sol-domains-grid">
|
<div class="sol-domains-grid">
|
||||||
${domains.map(d => {
|
${domains.map(d => {
|
||||||
const name = typeof d === 'string' ? d : (d.domain || d);
|
const name = typeof d === 'string' ? d : (d.domain || d.name || String(d));
|
||||||
const isFav = favourite && name === favourite;
|
const isFav = favourite && name === favourite;
|
||||||
return `
|
return `
|
||||||
<div class="sol-domain-card">
|
<div class="sol-domain-card">
|
||||||
|
|
@ -244,26 +231,93 @@ async function doReverse() {
|
||||||
let connectedProvider = null;
|
let connectedProvider = null;
|
||||||
|
|
||||||
const WALLETS = [
|
const WALLETS = [
|
||||||
{ name: 'PHANTOM', icon: '👻', detect: () => window.phantom?.solana, url: 'https://phantom.app' },
|
{
|
||||||
{ name: 'SOLFLARE', icon: '🔆', detect: () => window.solflare, url: 'https://solflare.com' },
|
name: 'PHANTOM',
|
||||||
{ name: 'BACKPACK', icon: '🎒', detect: () => window.backpack, url: 'https://backpack.app' },
|
icon: '👻',
|
||||||
{ name: 'COINBASE', icon: '🔵', detect: () => window.coinbaseSolana, url: 'https://coinbase.com/wallet' },
|
detect: () => {
|
||||||
{ name: 'TRUST', icon: '🛡️', detect: () => window.trustwallet?.solana, url: 'https://trustwallet.com' },
|
const p = window.phantom?.solana;
|
||||||
{ name: 'METAMASK', icon: '🦊', detect: () => window.ethereum?.isSolana ? window.ethereum : null, url: 'https://metamask.io' },
|
return (p && p.isPhantom) ? p : null;
|
||||||
|
},
|
||||||
|
url: 'https://phantom.app'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'JUPITER',
|
||||||
|
icon: '🪐',
|
||||||
|
detect: () => {
|
||||||
|
if (window.jupiter?.solana) return window.jupiter.solana;
|
||||||
|
if (window.solana?.isJupiter) return window.solana;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
url: 'https://jup.ag'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'SOLFLARE',
|
||||||
|
icon: '🔆',
|
||||||
|
detect: () => {
|
||||||
|
const s = window.solflare;
|
||||||
|
return (s && s.isSolflare) ? s : null;
|
||||||
|
},
|
||||||
|
url: 'https://solflare.com'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'BACKPACK',
|
||||||
|
icon: '🎒',
|
||||||
|
detect: () => {
|
||||||
|
const b = window.backpack;
|
||||||
|
return (b && b.isBackpack) ? b : null;
|
||||||
|
},
|
||||||
|
url: 'https://backpack.app'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'COINBASE',
|
||||||
|
icon: '🔵',
|
||||||
|
detect: () => window.coinbaseSolana || null,
|
||||||
|
url: 'https://coinbase.com/wallet'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TRUST',
|
||||||
|
icon: '🛡️',
|
||||||
|
detect: () => window.trustwallet?.solana || null,
|
||||||
|
url: 'https://trustwallet.com'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'METAMASK',
|
||||||
|
icon: '🦊',
|
||||||
|
detect: () => {
|
||||||
|
// MetaMask Solana injects at window.solana with isMetaMask
|
||||||
|
if (window.solana?.isMetaMask) return window.solana;
|
||||||
|
// Or check ethereum provider flags
|
||||||
|
if (window.ethereum?.isMetaMask && window.ethereum?.solana) return window.ethereum.solana;
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
url: 'https://metamask.io'
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function getAvailableWallets() {
|
function getAvailableWallets() {
|
||||||
const available = [];
|
const available = [];
|
||||||
|
const matchedProviders = new Set();
|
||||||
|
|
||||||
for (const w of WALLETS) {
|
for (const w of WALLETS) {
|
||||||
try {
|
try {
|
||||||
const provider = w.detect();
|
const provider = w.detect();
|
||||||
if (provider) available.push({ ...w, provider });
|
if (provider && !matchedProviders.has(provider)) {
|
||||||
|
available.push({ ...w, provider });
|
||||||
|
matchedProviders.add(provider);
|
||||||
|
}
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
}
|
}
|
||||||
// Fallback: check generic window.solana if no specific wallet matched
|
|
||||||
|
// Generic fallback: window.solana if nothing matched
|
||||||
if (available.length === 0 && window.solana) {
|
if (available.length === 0 && window.solana) {
|
||||||
available.push({ name: 'SOLANA WALLET', icon: '◈', provider: window.solana, url: '#' });
|
available.push({
|
||||||
|
name: 'SOLANA WALLET',
|
||||||
|
icon: '◈',
|
||||||
|
provider: window.solana,
|
||||||
|
url: '#'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,7 +346,6 @@ function createWalletModal() {
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
document.body.appendChild(modal);
|
document.body.appendChild(modal);
|
||||||
|
|
||||||
modal.querySelector('.sol-modal-backdrop').addEventListener('click', closeModal);
|
modal.querySelector('.sol-modal-backdrop').addEventListener('click', closeModal);
|
||||||
modal.querySelector('#modal-close').addEventListener('click', closeModal);
|
modal.querySelector('#modal-close').addEventListener('click', closeModal);
|
||||||
}
|
}
|
||||||
|
|
@ -301,21 +354,24 @@ function openModal() {
|
||||||
const modal = $('#wallet-modal');
|
const modal = $('#wallet-modal');
|
||||||
const list = $('#wallet-list');
|
const list = $('#wallet-list');
|
||||||
const available = getAvailableWallets();
|
const available = getAvailableWallets();
|
||||||
|
const detectedNames = new Set(available.map(w => w.name));
|
||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
|
|
||||||
|
// Show detected wallets as clickable buttons
|
||||||
if (available.length > 0) {
|
if (available.length > 0) {
|
||||||
|
html += '<div class="sol-wallet-divider">DETECTED</div>';
|
||||||
html += available.map(w => `
|
html += available.map(w => `
|
||||||
<button class="sol-wallet-option" data-wallet="${w.name}">
|
<button class="sol-wallet-option detected" data-wallet="${w.name}">
|
||||||
<span class="sol-wallet-option-icon">${w.icon}</span>
|
<span class="sol-wallet-option-icon">${w.icon}</span>
|
||||||
<span class="sol-wallet-option-name">${w.name}</span>
|
<span class="sol-wallet-option-name">${w.name}</span>
|
||||||
<span class="sol-wallet-option-status">DETECTED</span>
|
<span class="sol-wallet-option-status detected">DETECTED</span>
|
||||||
</button>
|
</button>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show install links for wallets not detected
|
// Show not-installed wallets as links (exclude detected ones)
|
||||||
const notInstalled = WALLETS.filter(w => { try { return !w.detect(); } catch(e) { return true; } });
|
const notInstalled = WALLETS.filter(w => !detectedNames.has(w.name));
|
||||||
if (notInstalled.length > 0) {
|
if (notInstalled.length > 0) {
|
||||||
html += '<div class="sol-wallet-divider">NOT INSTALLED</div>';
|
html += '<div class="sol-wallet-divider">NOT INSTALLED</div>';
|
||||||
html += notInstalled.map(w => `
|
html += notInstalled.map(w => `
|
||||||
|
|
@ -327,11 +383,17 @@ function openModal() {
|
||||||
`).join('');
|
`).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.</div>';
|
||||||
|
}
|
||||||
|
|
||||||
list.innerHTML = html;
|
list.innerHTML = html;
|
||||||
|
|
||||||
// Bind click handlers for detected wallets
|
// Bind click handlers only for detected wallets (buttons, not links)
|
||||||
list.querySelectorAll('.sol-wallet-option:not(.not-installed)').forEach(btn => {
|
list.querySelectorAll('.sol-wallet-option.detected').forEach(btn => {
|
||||||
btn.addEventListener('click', () => {
|
btn.addEventListener('click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
const name = btn.dataset.wallet;
|
const name = btn.dataset.wallet;
|
||||||
const wallet = available.find(w => w.name === name);
|
const wallet = available.find(w => w.name === name);
|
||||||
if (wallet) connectWallet(wallet);
|
if (wallet) connectWallet(wallet);
|
||||||
|
|
@ -406,29 +468,35 @@ async function loadMyDomains() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${SNS_API}/domains/${walletAddress}`);
|
const res = await fetch(`${SNS_API}/domains/${walletAddress}`);
|
||||||
if (!res.ok) {
|
const data = await res.json();
|
||||||
content.innerHTML = errorHTML('Failed to load domains.');
|
|
||||||
return;
|
// 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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await res.json();
|
|
||||||
const domains = data.result || data;
|
|
||||||
|
|
||||||
// Get favourite
|
|
||||||
let favourite = null;
|
let favourite = null;
|
||||||
try {
|
try {
|
||||||
const favRes = await fetch(`${SNS_API}/favourite-domain/${walletAddress}`);
|
const favRes = await fetch(`${SNS_API}/favourite-domain/${walletAddress}`);
|
||||||
if (favRes.ok) {
|
|
||||||
const favData = await favRes.json();
|
const favData = await favRes.json();
|
||||||
favourite = favData.result || null;
|
if (favData.s === 'ok') favourite = favData.result || null;
|
||||||
}
|
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
if (!domains || !domains.length) {
|
if (!domains || domains.length === 0) {
|
||||||
|
const regUrl = `${SNS_REG}?ref=${REFERRAL_WALLET}`;
|
||||||
content.innerHTML = `
|
content.innerHTML = `
|
||||||
<div class="sol-empty">
|
<div class="sol-empty">
|
||||||
NO .SOL DOMAINS FOUND<br>
|
NO .SOL DOMAINS FOUND<br>
|
||||||
<a href="${BONFIDA_REG}" target="_blank" rel="noopener" style="color:#14F195; font-size:0.6rem">Register one on Bonfida ↗</a>
|
<a href="${regUrl}" target="_blank" rel="noopener" style="color:#14F195; font-size:0.7rem">Register one on SNS.id ↗</a>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
return;
|
return;
|
||||||
|
|
@ -441,7 +509,7 @@ async function loadMyDomains() {
|
||||||
</div>
|
</div>
|
||||||
<div class="sol-domains-grid">
|
<div class="sol-domains-grid">
|
||||||
${domains.map(d => {
|
${domains.map(d => {
|
||||||
const name = typeof d === 'string' ? d : (d.domain || d);
|
const name = typeof d === 'string' ? d : (d.domain || d.name || String(d));
|
||||||
const isFav = favourite && name === favourite;
|
const isFav = favourite && name === favourite;
|
||||||
return `
|
return `
|
||||||
<div class="sol-domain-card">
|
<div class="sol-domain-card">
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue