Compare commits

...

2 Commits

Author SHA1 Message Date
serinko 6ff3c21a7d fix logic 2025-09-05 18:14:03 +02:00
serinko 0003bf1a8b initialise attempt to list 10 top nodes automatically 2025-09-05 17:47:23 +02:00
2 changed files with 39 additions and 0 deletions
+6
View File
@@ -1 +1,7 @@
export const TABLET_WIDTH = "(min-width:700px)";
// import auto-picking function
import { fetchRecommendedNodes } from "./lib/recommended";
// export a promise that resolves to number[]
export const RECOMMENDED_NODES = fetchRecommendedNodes();
+33
View File
@@ -0,0 +1,33 @@
// return the top 10 node_ids matching the constraints, computed at runtime.
export async function fetchRecommendedNodes(): Promise<number[]> {
const url = "https://api.nym.spectredao.net/api/v1/nodes?size=3000";
const res = await fetch(url, { cache: "no-store" });
if (!res.ok) throw new Error(`Failed to fetch nodes: ${res.status}`);
const data: any[] = await res.json();
const MIN_STAKE = 50_000_000_000; // 50B
const filtered = data.filter((n) => {
const ws9000 = n?.description?.mixnet_websockets?.ws_port === 9000;
const wgOn = n?.description?.wireguard != null;
const pm = Number(n?.rewarding_details?.cost_params?.profit_margin_percent ?? "1");
const pmOk = !Number.isNaN(pm) && pm <= 0.2;
const role = n?.description?.declared_role ?? {};
const rolesOk = !!(role.entry && role.exit_ipr && role.exit_nr);
const stakeOk = Number(n?.total_stake ?? 0) > MIN_STAKE;
return ws9000 && wgOn && pmOk && rolesOk && stakeOk;
});
filtered.sort((a, b) => {
const ua = Number(a?.uptime ?? 0), ub = Number(b?.uptime ?? 0);
if (ub !== ua) return ub - ua; // uptime DESC
const sa = Number(a?.total_stake ?? 0), sb = Number(b?.total_stake ?? 0);
return sa - sb; // stake ASC
});
return filtered.slice(0, 10).map((n) => Number(n?.node_id));
}