"use strict"; // Solana Name Service resolver — .sol names via Bonfida's public SDK proxy. // SNS stores URL and IPFS records on-chain; the proxy exposes them as a // REST endpoint so Theseus doesn't need the Solana web3.js stack bundled in. // // Proxy responses (as of 2026-10): // GET /resolve/ → { s: "ok", result: "
" } // or { s: "error", result: "..." } // GET /record//url → { s: "ok", result: "https://…" } // GET /record//ipfs → { s: "ok", result: "" } // GET /record//arwv → { s: "ok", result: "" } // // We prefer URL (clearnet redirect) over IPFS (gateway) over Arweave // (gateway) because URL is explicit intent and the user's gateway choice // shouldn't matter for a plain redirect. // // If the user's IP is Cloudflare-blocked for the default proxy, they can // paste any Solana RPC + sns-sdk-proxy compatible endpoint into Advanced. const POS_TTL_MS = 10 * 60 * 1000; const NEG_TTL_MS = 60 * 1000; const RPC_TIMEOUT_MS = 5000; const CACHE = new Map(); async function proxyGet(base, path) { const url = base.replace(/\/+$/, "") + path; const ctrl = new AbortController(); const t = setTimeout(() => ctrl.abort(new Error("SNS proxy timeout")), RPC_TIMEOUT_MS); try { const res = await fetch(url, { method: "GET", headers: { "accept": "application/json", "user-agent": "Theseus/0.3.99 (silentmode.st)" }, signal: ctrl.signal, }); if (!res.ok) throw new Error(`SNS HTTP ${res.status}`); const json = await res.json(); if (json?.s === "ok" && json.result) return String(json.result); return null; // no record / name not registered } finally { clearTimeout(t); } } async function resolve(host, { rpc, gateway } = {}) { if (!host) return null; const name = String(host).toLowerCase(); if (!name.endsWith(".sol") || name === ".sol" || name === "sol") return null; const cached = CACHE.get(name); if (cached && cached.exp > Date.now()) return cached.result; // "rpc" here is the SNS proxy base URL (not a Solana JSON-RPC); naming // is for consistency with the other adapters. const base = rpc || "https://sns-sdk-proxy.bonfida.workers.dev"; const gw = (gateway || "https://ipfs.io").replace(/\/+$/, ""); const bareName = name.replace(/\.sol$/i, ""); try { // URL takes precedence (explicit redirect intent). const urlRec = await proxyGet(base, `/record/${encodeURIComponent(bareName)}.sol/url`).catch(() => null); if (urlRec) { const u = /^https?:\/\//i.test(urlRec) ? urlRec : `https://${urlRec}`; const result = { url: u, protocol: "sns", kind: "url" }; CACHE.set(name, { result, exp: Date.now() + POS_TTL_MS }); return result; } // IPFS fallback. const ipfsRec = await proxyGet(base, `/record/${encodeURIComponent(bareName)}.sol/ipfs`).catch(() => null); if (ipfsRec) { const result = { url: `${gw}/ipfs/${ipfsRec}`, protocol: "sns", kind: "ipfs", cid: ipfsRec }; CACHE.set(name, { result, exp: Date.now() + POS_TTL_MS }); return result; } const result = { error: "no URL or IPFS record on this .sol name" }; CACHE.set(name, { result, exp: Date.now() + NEG_TTL_MS }); return result; } catch (err) { const result = { error: String(err?.message || err) }; CACHE.set(name, { result, exp: Date.now() + NEG_TTL_MS }); return result; } } function clearCache() { CACHE.clear(); } module.exports = { resolve, clearCache };