0x54ded708…1627sent to0xbd11994a…7699·#25,543,808·view on Etherscan
DOS_loader/* =============================================================================
* COS_loader.js — Decentralized Operation System boot splash + script loader
* -----------------------------------------------------------------------------
* Single self-contained bootloader. Include this ONE file first; it:
* 1. Paints a full-screen splash: title block ("DECENTRALIZED / OPERATING /
* SYSTEM" + subtitle), a page-centred radar drawing, and a terminal-style
* log with the load % pinned to the first line.
* 2. Fetches every script from ScriptyStorageV2 via eth_call
* getContent(name, 0x) through a parallel RPC pool (one worker per
* endpoint, paced + cooldown, mirroring COS_ethCalls.js rpcPool), then
* gunzips + injects them strictly in order. Each script prints a new line
* at the top of the log (previous pushed down); the radar sweep + % track
* overall progress (one full rotation == 100%).
* 3. On resize the title scales to the space above the centred drawing, and
* the drawing + log match the title block's width.
* 4. Once everything is loaded (init() builds the world), fades the splash out
* and reveals the world.
*
* Configure the script list via window.DOS_SCRIPTS BEFORE this file runs,
* or via DEFAULT_SCRIPTS declared by DOS_LoadingArray.js (injected by the
* renderer right before this file). Each entry:
* { data: "<scripty-content-name>", gzip: true|false, label: "Human name" }
* RPC endpoints come from the renderer-injected RPCString / WSRPCString.
* The shared RPC layer (RPCs, WS_RPCs, rpcPool, rpcSleep, parallelRpcMap,
* firstWorkingRpc, parseRpcString, parseClaimOrderString) is defined HERE and
* exposed on window — every script injected afterwards reuses it.
* The scripty address can be overridden via window.DOS_SCRIPTY_ADDRESS.
*
* The world's init() (called from COS_animate.js) can signal true readiness by
* setting window.__WORLD_READY = true or calling DOSLoader.finish().
*
* Styling: all colours are hardcoded. Only the viewport-driven sizes are left as
* CSS variables — --w (block width), --gap, and --tu (title unit, JS-refined).
* ========================================================================== */
(function () {
"use strict";
/* ------------------------------------------------------------------ config */
var SETTLE_MS = 1200; // (legacy) unused now that we wait for __WORLD_READY
var READY_TIMEOUT_MS = 30000; // hard cap: hide even if __WORLD_READY never set
// ScriptyStorageV2 — every module entry's `data` is a content name
// stored on this contract; blobs are fetched via getContent(name, 0x).
var SCRIPTY_ADDRESS = (typeof window.DOS_SCRIPTY_ADDRESS === "string" && window.DOS_SCRIPTY_ADDRESS)
? window.DOS_SCRIPTY_ADDRESS
: "0xbD11994aABB55Da86DC246EBB17C1Be0af5b7699";
var GETCONTENT_SELECTOR = "0x2647c23d"; // getContent(string,bytes)
// Module list — order matters. Each `data` is the ScriptyStorageV2 content
// name, `gzip` matches how each blob was stored. DEFAULT_SCRIPTS is declared
// by DOS_LoadingArray.js, injected by the renderer right before this file.
// Priority: window.DOS_SCRIPTS > DEFAULT_SCRIPTS.
var SCRIPTS = (window.DOS_SCRIPTS && window.DOS_SCRIPTS.length)
? window.DOS_SCRIPTS
: (typeof DEFAULT_SCRIPTS !== "undefined" ? DEFAULT_SCRIPTS : []);
// Script manifest for the Provenance window (os.js): on-chain content name +
// injected script size in bytes, filled in as each module is injected.
window.DOS_SCRIPT_INFO = SCRIPTS.map(function (sc) {
return { name: sc.data, label: sc.label, bytes: null };
});
function recordScriptSize(sc, jsCode) {
if (!sc || typeof jsCode !== "string") return;
for (var i = 0; i < window.DOS_SCRIPT_INFO.length; i++) {
if (window.DOS_SCRIPT_INFO[i].name === sc.data && window.DOS_SCRIPT_INFO[i].bytes == null) {
window.DOS_SCRIPT_INFO[i].bytes = new TextEncoder().encode(jsCode).length;
return;
}
}
}
/* ------------------------------------------------------------------- styles */
// Colours hardcoded; only --w / --gap / --tu are viewport-driven variables.
var css =
":root{" +
"--w:min(320px, 80vw);" +
"--gap:clamp(14px, 3.5vh, 40px);" +
"--tu:min(var(--w), calc(((100vh - var(--w)) / 2 - var(--gap)) / 1.1));" +
"}" +
"#dos-loader{position:fixed;inset:0;z-index:100000;background:#0a0b0d;color:#cfd3cf;" +
"font-family:'Andale Mono',monospace;display:grid;" +
"grid-template-rows:minmax(0,1fr) auto minmax(0,1fr);justify-items:center;" +
"opacity:1;transition:opacity .8s ease;pointer-events:auto;user-select:none;overflow:hidden;}" +
"#dos-loader.dos-hide{opacity:0;pointer-events:none;}" +
/* 1) title + subtitle : centred block, scales down to fit remaining height */
"#dos-head{grid-row:1;align-self:end;margin-bottom:var(--gap);font-weight:normal;" +
"text-align:center;box-sizing:border-box;}" +
"#dos-head .l{font-family:'MonoPixel','Andale Mono',monospace;line-height:1.25;" +
"letter-spacing:.02em;margin:0;white-space:nowrap;}" +
"#dos-head .l1{font-size:calc(var(--tu)*0.128*1.3);}" +
"#dos-head .l2{font-size:calc(var(--tu)*0.185*1.31);}" +
"#dos-head .l3{font-size:calc(var(--tu)*0.278*1.31);}" +
"#dos-head .sub{font-size:calc(var(--tu)*0.038*1.02);opacity:.72;letter-spacing:.02em;" +
"margin:calc(var(--tu)*0.05) 0 0;white-space:nowrap;}" +
/* 2) drawing : page-centred, width matched to the title block by JS */
"#dos-art{grid-row:2;width:var(--w);height:var(--w);display:block;}" +
/* 3) status : terminal log (newest first) + % pinned to the first line */
"#dos-status{grid-row:3;align-self:start;margin-top:var(--gap);width:var(--w);" +
"max-height:100%;overflow:hidden;display:flex;align-items:flex-start;gap:1em;" +
"font-size:calc(var(--w)*0.04);letter-spacing:.02em;line-height:1.5;}" +
"#dos-log{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;}" +
"#dos-log .line{display:flex;gap:.5em;white-space:nowrap;min-width:0;}" +
"#dos-log .line .mk{flex:0 0 auto;width:1em;color:#FFD65C;}" +
"#dos-log .line .tx{overflow:hidden;text-overflow:ellipsis;}" +
"#dos-log .line.cur{color:#cfd3cf;opacity:1;}" +
"#dos-log .line.done{opacity:.45;}" +
"#dos-log .line.err .mk{color:#ff6b6b;}" +
"#dos-log .line.err{color:#ff6b6b;opacity:1;}" +
"#dos-status-pct{flex:0 0 auto;font-family:'MonoPixel','Andale Mono',monospace;" +
"color:#FFD65C;font-variant-numeric:tabular-nums;}" +
/* SVG classes (scoped to the drawing) */
"#dos-art .cls-1{fill:#000;}" +
"#dos-art .cls-2{fill:none;}" +
"#dos-art .cls-1,#dos-art .cls-2,#dos-art .cls-3{stroke:#cfd3cf;stroke-miterlimit:10;}" +
"#dos-art .cls-2{stroke-width:2px;}" +
"#dos-art .cls-4,#dos-art .cls-5{fill:#cfd3cf;font-family:'MonoPixel','Andale Mono',monospace;}" +
"#dos-art .cls-4{font-size:36px;}" +
"#dos-art .cls-5{font-size:21px;}" +
"#dos-art .cls-6{fill:#FFD65C;}";
/* --------------------------------------------------------------- drawing SVG */
// Concentric-orbit "atom" scaled to nearly fill the 512 frame; the central
// squares stay on the foreground and the radar (#dos-radar) is filled + driven
// by JS. Colours hardcoded (amber accent #FFD65C, light strokes #cfd3cf).
function drawingSVG() {
return '' +
'<svg id="dos-art" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">' +
'<g id="Calque_2" transform="translate(256 256) scale(2) translate(-256 -256)">' +
// animated dashed outer ring (rotates 40s)
'<g>' +
'<circle cx="256" cy="256" r="127" fill="none" stroke="#FFD65C" stroke-width="2" stroke-dasharray="6 10">' +
'<animate attributeName="stroke-dashoffset" values="0;-32" dur="2s" repeatCount="indefinite"/>' +
'</circle>' +
'<animateTransform attributeName="transform" type="rotate" from="0 256 256" to="360 256 256" dur="40s" repeatCount="indefinite"/>' +
'</g>' +
// orbit r=101.23 (rotates -360 / 20s)
'<g>' +
'<path class="cls-2" d="M256,357.23c-55.82,0-101.23-45.41-101.23-101.23s45.41-101.23,101.23-101.23,101.23,45.41,101.23,101.23-45.41,101.23-101.23,101.23Z"/>' +
'<circle class="cls-3" cx="256" cy="154.77" r="6" fill="#000"/>' +
'<animateTransform attributeName="transform" type="rotate" from="0 256 256" to="-360 256 256" dur="20s" repeatCount="indefinite"/>' +
'</g>' +
// orbit r=61.23 (rotates -360 / 12s)
'<g class="cls-3">' +
'<path class="cls-2" d="M256,317.23c-33.76,0-61.23-27.47-61.23-61.23s27.47-61.23,61.23-61.23,61.23,27.47,61.23,61.23-27.47,61.23-61.23,61.23Z"/>' +
'<circle cx="256" cy="194.77" r="6" fill="#000"/>' +
'<circle cx="256" cy="317.23" r="6" fill="#000"/>' +
'<circle cx="212.71" cy="212.72" r="6" fill="#000"/>' +
'<circle cx="212.71" cy="299.29" r="6" fill="#000"/>' +
'<circle cx="299.28" cy="212.72" r="6" fill="#000"/>' +
'<circle cx="299.28" cy="299.28" r="6" fill="#000"/>' +
'<circle cx="194.77" cy="256" r="6" fill="#000"/>' +
'<circle cx="317.23" cy="256" r="6" fill="#000"/>' +
'<animateTransform attributeName="transform" type="rotate" from="0 256 256" to="-360 256 256" dur="12s" repeatCount="indefinite"/>' +
'</g>' +
// orbit r=41.23 (rotates 360 / 5s)
'<g class="cls-3">' +
'<circle class="cls-2" cx="256" cy="256" r="41.23"/>' +
'<circle cx="256" cy="214.77" r="6" fill="#000"/>' +
'<circle cx="256" cy="297.23" r="6" fill="#000"/>' +
'<animateTransform attributeName="transform" type="rotate" from="0 256 256" to="360 256 256" dur="5s" repeatCount="indefinite"/>' +
'</g>' +
// orbit r=81.23 (rotates 360 / 10s)
'<g class="cls-3">' +
'<path class="cls-2" d="M256,337.23c-44.79,0-81.23-36.44-81.23-81.23s36.44-81.23,81.23-81.23,81.23,36.44,81.23,81.23-36.44,81.23-81.23,81.23Z"/>' +
'<circle cx="256" cy="174.77" r="6" fill="#000"/>' +
'<circle cx="218.26" cy="184.09" r="6" fill="#000"/>' +
'<circle cx="189.15" cy="209.85" r="6" fill="#000"/>' +
'<circle cx="175.38" cy="246.2" r="6" fill="#000"/>' +
'<circle cx="180.04" cy="284.79" r="6" fill="#000"/>' +
'<circle cx="202.13" cy="316.78" r="6" fill="#000"/>' +
'<circle cx="236.57" cy="334.81" r="6" fill="#000"/>' +
'<circle cx="293.74" cy="184.09" r="6" fill="#000"/>' +
'<circle cx="322.85" cy="209.85" r="6" fill="#000"/>' +
'<circle cx="336.62" cy="246.2" r="6" fill="#000"/>' +
'<circle cx="331.96" cy="284.79" r="6" fill="#000"/>' +
'<circle cx="309.87" cy="316.78" r="6" fill="#000"/>' +
'<circle cx="275.43" cy="334.81" r="6" fill="#000"/>' +
'<animateTransform attributeName="transform" type="rotate" from="0 256 256" to="360 256 256" dur="10s" repeatCount="indefinite"/>' +
'</g>' +
// central squares (foreground)
'<rect class="cls-1" x="236" y="236" width="15" height="15"/>' +
'<rect class="cls-1" x="261" y="236" width="15" height="15"/>' +
'<rect class="cls-1" x="261" y="261" width="15" height="15"/>' +
'<rect class="cls-1" x="236" y="261" width="15" height="15"/>' +
// rotating radar (populated + driven by JS; progress == its rotation)
'<g id="dos-radar"></g>' +
// centre marker on top of the radar
'<rect id="dos-sndmark" fill="black" stroke="#FFD65C" x="253.5" y="253.5" width="5" height="5"/>' +
'</g>' +
'</svg>';
}
/* ------------------------------------------------------------------- build UI */
var el = {};
function build() {
var style = document.createElement("style");
style.id = "dos-loader-style";
style.textContent = css;
document.head.appendChild(style);
var root = document.createElement("div");
root.id = "dos-loader";
root.innerHTML =
'<div id="dos-head">' +
'<p class="l l1">DECENTRALIZED</p>' +
'<p class="l l2">OPERATING</p>' +
'<p class="l l3">SYSTEM</p>' +
'<p class="sub">A fully onchain world for all the cypherdudes</p>' +
'</div>' +
drawingSVG() +
'<div id="dos-status"><div id="dos-log"></div><span id="dos-status-pct">0%</span></div>';
document.body.appendChild(root);
el.root = root;
el.head = root.querySelector("#dos-head");
el.art = root.querySelector("#dos-art");
el.stat = root.querySelector("#dos-status");
el.radar = root.querySelector("#dos-radar");
el.pct = root.querySelector("#dos-status-pct");
el.log = root.querySelector("#dos-log");
buildRadar();
// Sound pre-arm: clicking the drawing toggles the ambient-audio intent
// (window.DOS_SOUND_ON). The click is the user gesture the AudioContext
// needs, so the shared context is created/resumed HERE and reused by the
// world (COS_main.js initCtx); init() then starts the drone and the HUD
// sound button shows ON. The centre marker fills amber while armed.
el.art.style.cursor = "pointer";
el.art.setAttribute("title", "click to toggle sound");
el.art.addEventListener("click", function () {
window.DOS_SOUND_ON = !window.DOS_SOUND_ON;
try {
var AC = window.AudioContext || window.webkitAudioContext;
if (!window.__DOS_AUDIO_CTX && AC) window.__DOS_AUDIO_CTX = new AC({ sampleRate: 44100 });
if (window.__DOS_AUDIO_CTX && window.__DOS_AUDIO_CTX.state === "suspended") window.__DOS_AUDIO_CTX.resume();
} catch (e) { }
// engine already injected -> apply immediately (starts/stops the drone)
if (typeof window.setSoundState === "function") window.setSoundState(window.DOS_SOUND_ON);
var mark = el.art.querySelector("#dos-sndmark");
if (mark) mark.setAttribute("fill", window.DOS_SOUND_ON ? "#FFD65C" : "black");
});
}
/* --------------------------------------------------------------- radar sweep */
// A leading line from the centre + a fading trail of wedges (built once).
function buildRadar() {
if (!el.radar) return;
var ACCENT = "#FFD65C", CX = 256, CY = 256, RIN = 0, ROUT = 128, SEG = 10, TRAIL = 58;
function pt(r, deg) { var a = deg * Math.PI / 180; return [CX + r * Math.sin(a), CY - r * Math.cos(a)]; }
function f(n) { return n.toFixed(2); }
var buf = "";
for (var k = 0; k < SEG; k++) {
var a0 = -(k + 1) * (TRAIL / SEG), a1 = -k * (TRAIL / SEG);
var P0i = pt(RIN, a0), P0o = pt(ROUT, a0), P1o = pt(ROUT, a1), P1i = pt(RIN, a1);
var op = (0.30 * (1 - k / SEG)).toFixed(3);
buf += '<path d="M' + f(P0i[0]) + ',' + f(P0i[1]) + ' L' + f(P0o[0]) + ',' + f(P0o[1]) +
' L' + f(P1o[0]) + ',' + f(P1o[1]) + ' L' + f(P1i[0]) + ',' + f(P1i[1]) +
' Z" fill="' + ACCENT + '" fill-opacity="' + op + '"/>';
}
var Lo = pt(ROUT, 0), Li = pt(RIN, 0);
buf += '<line x1="' + f(Li[0]) + '" y1="' + f(Li[1]) + '" x2="' + f(Lo[0]) + '" y2="' + f(Lo[1]) +
'" stroke="' + ACCENT + '" stroke-width="0.5" stroke-opacity="0.95"/>';
el.radar.innerHTML = buf;
}
/* ----------------------------------------------------------------- progress */
// Progress drives the radar rotation (one turn == 100%) and the %. The shown
// value eases toward the target so discrete script loads sweep smoothly.
var shown = 0, target = 0, raf = 0;
function applyProgress(t) {
if (el.radar) el.radar.setAttribute("transform", "rotate(" + (t * 360).toFixed(2) + " 256 256)");
if (el.pct) el.pct.textContent = Math.round(t * 100) + "%";
}
function tick() {
var d = target - shown;
if (Math.abs(d) < 0.001) { shown = target; applyProgress(shown); raf = 0; return; }
shown += d * 0.1;
applyProgress(shown);
raf = requestAnimationFrame(tick);
}
function setProgress(t) {
target = Math.max(0, Math.min(1, t));
if (!raf) raf = requestAnimationFrame(tick);
}
/* ------------------------------------------------------------ terminal log */
var curLine = null;
function markDone(line) {
if (!line) return;
if (!line.classList.contains("err")) line.className = "line done";
var mk = line.querySelector(".mk");
if (mk && !line.classList.contains("err")) mk.textContent = "✓"; // ✓
}
function addLine(text) {
if (!el.log) return;
markDone(curLine);
var d = document.createElement("div");
d.className = "line cur";
d.innerHTML = '<span class="mk">▸</span><span class="tx"></span>'; // ▸
d.querySelector(".tx").textContent = text;
el.log.insertBefore(d, el.log.firstChild); // newest on the first line
curLine = d;
}
function markError() {
if (!curLine) return;
curLine.className = "line err";
var mk = curLine.querySelector(".mk");
if (mk) mk.textContent = "✗"; // ✗
}
/* --------------------------------------------------------------- responsive */
// Title scales to the band above the centred drawing; the drawing (kept
// square) and the log are then matched to the title block's measured width.
function layout() {
if (!el.head || !el.root) return;
var vh = window.innerHeight, vw = window.innerWidth;
var gap = Math.max(14, Math.min(0.035 * vh, 40)); // mirrors --gap
// measure title width/height per 1px of --tu at a reference size
var REF = 200;
el.root.style.setProperty("--tu", REF + "px");
var r = el.head.getBoundingClientRect();
var wPer = r.width / REF, hPer = r.height / REF;
if (!wPer || !hPer) return;
// drawing side = title width; keep centred => title fits the band above it:
// hPer*tu <= (vh - wPer*tu)/2 - gap
var tuFit = (vh - 2 * gap) / (2 * hPer + wPer);
var tu = Math.max(24, Math.min(320, 0.92 * vw / wPer, tuFit));
el.root.style.setProperty("--tu", tu + "px");
var w = el.head.getBoundingClientRect().width;
el.art.style.width = w + "px";
el.art.style.height = w + "px";
el.stat.style.width = w + "px";
}
/* ---------------------------------------------------- scripty load + inject */
// Some historical on-chain blobs (notably the stored CypherDudesLayer1) still
// carry a leftover `const fonts = `…`;` that collides with the canonical one in
// CypherDudesFonts → "Identifier 'fonts' has already been declared". For the
// modules listed here we strip that single declaration from the fetched text
// before injecting, so CypherDudesFonts' `fonts` stays the only one.
var STRIP_FONTS_FROM = { CypherDudesLayer1: 1 };
// Removes the first `const|let|var fonts = <string-literal>;` from `src`.
// Handles backtick / single / double quoted literals (with escapes); if the
// RHS isn't a plain string literal it leaves the source untouched.
function stripFontsDecl(src) {
var re = /\b(?:const|let|var)\s+fonts\s*=\s*/g;
var m = re.exec(src);
if (!m) return src;
var start = m.index;
var i = re.lastIndex;
var q = src.charAt(i);
if (q !== "`" && q !== '"' && q !== "'") return src; // not a simple literal — leave it
i++; // past the opening delimiter
for (; i < src.length; i++) {
var ch = src.charAt(i);
if (ch === "\\") { i++; continue; } // skip escaped char
if (ch === q) { i++; break; } // closing delimiter
}
while (i < src.length && /\s/.test(src.charAt(i))) i++; // trailing whitespace
if (src.charAt(i) === ";") i++; // optional semicolon
return src.slice(0, start) + src.slice(i);
}
// (mirrors /Loader/loader.js)
async function loadHexScript(hex, isGzipped, sc) {
if (hex.startsWith("0x")) hex = hex.slice(2);
var bytes = new Uint8Array(hex.length / 2);
for (var i = 0; i < bytes.length; i++) bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
var jsCode;
if (isGzipped) {
// Two storage conventions supported:
// • RAW gzip bytes (store-artifacts.mjs / deploySepolia.js) — detected
// by the gzip magic number 0x1f 0x8b;
// • legacy base64-of-gzip text — decoded via atob first.
var gzipBytes;
if (bytes.length > 2 && bytes[0] === 0x1f && bytes[1] === 0x8b) {
gzipBytes = bytes;
} else {
var base64 = "";
for (var j = 0; j < bytes.length; j++) base64 += String.fromCharCode(bytes[j]);
gzipBytes = Uint8Array.from(atob(base64), function (c) { return c.charCodeAt(0); });
}
var stream = new Response(gzipBytes).body.pipeThrough(new DecompressionStream("gzip"));
jsCode = await new Response(stream).text();
} else {
jsCode = new TextDecoder("utf-8").decode(bytes);
}
if (sc && STRIP_FONTS_FROM[sc.data]) jsCode = stripFontsDecl(jsCode);
recordScriptSize(sc, jsCode);
var script = document.createElement("script");
script.textContent = jsCode;
document.head.appendChild(script); // inline scripts run synchronously on append
}
async function injectOne(txt, sc) {
txt = txt.trim();
// Heuristic: a hex blob is only [0-9a-fA-Fx]. If the fetched file is raw JS
// (e.g. a plain .js during local testing), inject it verbatim.
var isHex = /^(0x)?[0-9a-fA-F]+$/.test(txt);
if (isHex) {
await loadHexScript(txt, sc.gzip, sc);
} else {
if (sc && STRIP_FONTS_FROM[sc.data]) txt = stripFontsDecl(txt);
recordScriptSize(sc, txt);
var script = document.createElement("script");
script.textContent = txt;
document.head.appendChild(script);
}
}
/* ------------------------------------------- shared RPC layer (GLOBAL) */
// The loader is the FIRST script on the page, so the RPC plumbing lives here
// and is exposed on window: every script injected afterwards
// (COS_ethCalls.js, COS_main.js, recompose.min.js, ...) reuses this ONE
// pool instead of rebuilding its own — shared pacing + cooldown state.
//
// The onchain HTML build (DOSRenderer) injects the endpoint lists as strings
// BEFORE this file runs:
// RPCString / WSRPCString — getRpcs()/getWsRpcs() output: "['url','url',...]"
// ClaimOrderString — claimOrder storage string: "[0,1153,...]"
window.parseRpcString = function (s) {
if (!s || typeof s !== "string") return null;
try {
var a = JSON.parse(s.replace(/'/g, '"'));
return Array.isArray(a) ? a : null;
} catch (e) { console.warn("parseRpcString: invalid input", e); return null; }
};
window.parseClaimOrderString = function (s) {
if (!s || typeof s !== "string") return null;
try {
var a = JSON.parse(s);
return Array.isArray(a) ? a.map(Number) : null;
} catch (e) { console.warn("parseClaimOrderString: invalid input", e); return null; }
};
// Runtime endpoint arrays (contract-fed; window.RPCs may be pre-set for dev).
window.RPCs = (typeof RPCString !== "undefined" && window.parseRpcString(RPCString)) || window.RPCs || [];
window.WS_RPCs = (typeof WSRPCString !== "undefined" && window.parseRpcString(WSRPCString)) || window.WS_RPCs || [];
// --- Parallel RPC pool (same principle as recompose.min.js) --------------
// Every non-empty https RPC becomes an endpoint with its own request pacing
// (100ms between calls ≈ 10 req/s) and a jittered exponential cooldown when
// it fails / rate-limits. Work is consumed by one worker per endpoint, each
// call going to whichever endpoint frees up first — so N configured RPCs
// load the world roughly N times faster without hammering any provider.
window.rpcPool = {
eps: null,
build: function () {
this.eps = window.RPCs.filter(function (u) { return u; })
.map(function (url) { return { url: url, nextFree: 0, cooldownUntil: 0 }; });
return this.eps;
},
endpoints: function () { return (this.eps && this.eps.length) ? this.eps : this.build(); },
pick: function () {
var eps = this.endpoints();
var now = Date.now();
var best = eps[0], t = Math.max(now, best.cooldownUntil, best.nextFree);
for (var i = 1; i < eps.length; i++) {
var ti = Math.max(now, eps[i].cooldownUntil, eps[i].nextFree);
if (ti < t) { t = ti; best = eps[i]; }
}
best.nextFree = t + 100;
return { ep: best, waitMs: t - now };
},
cooldown: function (ep, attempt) {