/* AutoFirmer — Vest session hook. * * Runs in the page's MAIN world on next.vestmarkets.com, at document_start * (manifest content script) or injected later into a tab that was already open * (background.js relayVestSession). Installs once per page. * * Vest's app renews its own ~15-minute access token by itself: at load, then * whenever one nears expiry (POST /v3/auth/refresh, or /v3/auth/register at * sign-in). Those answers carry the token, its expiry and the login's wallet. * This hook reads them off the app's own XHRs and keeps the latest on * `window.__afVestSession`, where background.js collects it and relays it to * the local dashboard, whose Vest client trades with it. * * It sends nothing anywhere, changes no request and never renews anything * itself: the session stays the app's, refreshed by the app with its own * device key, so the tab is never logged out by the bot. * * The app also renews each trading account's own token through the same * /v3/auth/refresh, sending that account's refresh token in the body (the * login's rides in a cookie) and getting it back renewed. Those tokens see * one account only, so they are skipped: relaying one would leave the * dashboard with a single account. */ (() => { if (window.__afVestHook) return; window.__afVestHook = true; const AUTH = /^https:\/\/api-gateway\.hz\.vestmarkets\.com\/v3\/auth\/(refresh|register)(\?|$)/; /** A request body that carries a refresh token: an account's renewal. */ function accountRenewal(reqBody) { return typeof reqBody === 'string' && /"refreshToken"\s*:\s*"[^"]/.test(reqBody); } function take(url, status, text, accountRequest) { if (accountRequest || status < 200 || status >= 300 || !AUTH.test(url)) return; let body; try { body = JSON.parse(text); } catch { return; } if (!body || typeof body.accessToken !== 'string' || !body.accessToken) return; if (typeof body.refreshToken === 'string' && body.refreshToken) return; // an account's token, renewed const expiresAt = Number(body.accessExpiresAtMs); if (!Number.isFinite(expiresAt)) return; window.__afVestSession = { accessToken: body.accessToken, expiresAt, primaryAddr: typeof body.primaryAddr === 'string' ? body.primaryAddr : '', userId: typeof body.userId === 'string' ? body.userId : '', at: Date.now(), }; } const open = XMLHttpRequest.prototype.open; const send = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (method, url) { try { this.__afVestUrl = String(url); } catch { /* ignore */ } return open.apply(this, arguments); }; XMLHttpRequest.prototype.send = function (reqBody) { if (this.__afVestUrl && AUTH.test(this.__afVestUrl)) { let acct = false; try { acct = accountRenewal(reqBody); } catch { /* ignore */ } this.addEventListener('loadend', () => { try { if (this.responseType === '' || this.responseType === 'text') take(this.__afVestUrl, this.status, this.responseText, acct); else if (this.responseType === 'json') take(this.__afVestUrl, this.status, JSON.stringify(this.response), acct); } catch { /* never break the app */ } }); } return send.apply(this, arguments); }; const fetch0 = window.fetch; window.fetch = async function (input, init) { const res = await fetch0.apply(this, arguments); try { const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input && input.url; const acct = accountRenewal(init && init.body); if (url && AUTH.test(url)) res.clone().text().then((t) => take(url, res.status, t, acct)).catch(() => {}); } catch { /* never break the app */ } return res; }; })();