Vest platform: a client for Vest Markets, traded on a session the extension relays
Vest (next.vestmarkets.com) is a perps prop firm: index, metal and energy
perps on CME hours, plus stocks, FX and crypto. It joins as a fourth
platform, 'vest', on the DXtrade engine paths. Those are now keyed by
isNotionalPlatform() instead of === 'dxtrade': the firm's own symbols,
percent fees, notional sizing from volatility, and a scheduler lane of its
own.
- Login is relayed, not stored. Vest signs people in with Google through
Privy and renews the session with a device key the browser won't export.
Extension 0.17.0's vest-relay.js reads the token the Vest app renews for
itself (~15 min). It renews nothing, so the tab is never logged out. The
background script posts that token to POST /api/vest/session, which holds
it in memory only. With no Vest tab open on a machine that trades Vest,
the extension opens one.
- The firm's credential is the Vest login's wallet address. The dashboard's
form offers the relayed wallet with one click.
- VestClient (lib/vest-class.ts), mapped from the web app's traffic and code
since Vest publishes no API:
- Reads the capital accounts, balances (free amount plus position
collateral), positions and open orders with the user token.
- Mints per-account tokens for orders and transactions.
- Names accounts for their rules (V50K-E-D3-T10-34, V50K-F-D3-34,
V25K-I-7) so account-type prefixes can match.
- Takes Vest's own max-drawdown and daily-loss floors. dayStartBalance =
floor + the type's limit, so the engine's room is never more than
Vest's.
- Uses each symbol's taker fee and margin rate from exchangeInfo, and
hourly volatility over in-session hours only.
- Places take-profit and stop as position legs, which Vest cancels as an
OCO pair.
- Daily P&L and fund rows come from /v3/transactions by Vest's 20:00 ET
day. A books check holds an account, naming the transaction types, when
the balance and the transactions disagree.
- scripts/check-vest.mjs (in npm run check): Vest's day across DST, account
names, parsing, volatility gaps, market hours, the session store.
Not yet run against Vest with a live session: the order and exit bodies
come from the app's code, and the transaction classification from its enum.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5ec5abde6e
commit
96fdd9ca23
@@ -12,6 +12,9 @@
|
||||
* ready at all times; a run happens because someone asked for one.
|
||||
*
|
||||
* On a firm site, the person's 2FA code for it shows in a corner chip.
|
||||
*
|
||||
* A signed-in Vest tab's session is relayed to the dashboard, whose Vest client
|
||||
* trades with it (relayVestSession).
|
||||
*/
|
||||
|
||||
const DEFAULTS = {
|
||||
@@ -1185,6 +1188,109 @@ async function tick() {
|
||||
} catch (err) {
|
||||
await setState({ connected: true, error: `2FA chip: ${err.message}` });
|
||||
}
|
||||
|
||||
try {
|
||||
await relayVestSession(cfg);
|
||||
} catch (err) {
|
||||
await setState({ connected: true, vest: `Vest relay: ${err.message}` });
|
||||
}
|
||||
}
|
||||
|
||||
// ── Vest: relay the signed-in tab's session to the dashboard ────────────────
|
||||
//
|
||||
// Vest signs people in through Google in the browser and renews the session
|
||||
// with a device key the browser won't hand out, so the dashboard cannot log in
|
||||
// by itself. vest-relay.js, in the Vest tab, keeps the latest access token the
|
||||
// Vest app renewed for itself (~15 minutes each); this lifts it and posts it to
|
||||
// the dashboard whenever it changes. Nothing here renews or signs anything: the
|
||||
// session stays the app's.
|
||||
|
||||
const VEST_PATTERN = 'https://next.vestmarkets.com/*';
|
||||
const VEST_URL = 'https://next.vestmarkets.com/portfolio';
|
||||
const VEST_EVERY_MS = 10_000;
|
||||
/** A Vest tab opened for the relay isn't opened again for this long. */
|
||||
const VEST_REOPEN_MS = 10 * 60_000;
|
||||
|
||||
let vestLastCheck = 0;
|
||||
let vestLastRelayed = '';
|
||||
let vestOpenedAt = 0;
|
||||
|
||||
/** Runs in the Vest tab's MAIN world: the session vest-relay.js holds, while
|
||||
* it has life left. Self-contained. */
|
||||
function pageVestSession() {
|
||||
const s = window.__afVestSession;
|
||||
return { hooked: window.__afVestHook === true, session: s && s.expiresAt > Date.now() + 30000 ? s : null };
|
||||
}
|
||||
|
||||
/** The wallets the dashboard's Vest firms trade; [] from an older dashboard. */
|
||||
async function fetchVestWanted(cfg) {
|
||||
try {
|
||||
const res = await fetch(`${cfg.apiBase}/api/vest/session`, { cache: 'no-store' });
|
||||
if (!res.ok) return [];
|
||||
const body = await res.json();
|
||||
return Array.isArray(body.wanted) ? body.wanted : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async function relayVestSession(cfg) {
|
||||
const now = Date.now();
|
||||
if (now - vestLastCheck < VEST_EVERY_MS) return;
|
||||
vestLastCheck = now;
|
||||
|
||||
const tabs = (await chrome.tabs.query({ url: VEST_PATTERN })).filter((t) => /^https:/.test(t.url || ''));
|
||||
if (tabs.length === 0) {
|
||||
// This machine trades Vest: open the app in the background. It signs
|
||||
// itself back in from its 30-day session, and the hook catches that.
|
||||
const wanted = await fetchVestWanted(cfg);
|
||||
if (wanted.length === 0) return;
|
||||
if (now - vestOpenedAt > VEST_REOPEN_MS) {
|
||||
vestOpenedAt = now;
|
||||
await chrome.tabs.create({ url: VEST_URL, active: false });
|
||||
await setState({ vest: 'Opened a Vest tab to relay its session' });
|
||||
} else {
|
||||
await setState({ vest: 'No Vest tab open' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let found = null;
|
||||
for (const tab of tabs) {
|
||||
// A discarded tab stops renewing the session; keep Chrome from it.
|
||||
if (tab.autoDiscardable !== false) chrome.tabs.update(tab.id, { autoDiscardable: false }).catch(() => {});
|
||||
const [injection] = await chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id }, world: 'MAIN', func: pageVestSession,
|
||||
}).catch(() => [null]);
|
||||
const out = injection?.result;
|
||||
if (out && !out.hooked) {
|
||||
// A tab open from before this build: hook it now, and its app's
|
||||
// next renewal is caught.
|
||||
await chrome.scripting.executeScript({ target: { tabId: tab.id }, world: 'MAIN', files: ['vest-relay.js'] }).catch(() => {});
|
||||
}
|
||||
if (out?.session) { found = out.session; break; }
|
||||
}
|
||||
if (!found) {
|
||||
await setState({ vest: 'Vest tab open — waiting for its session (sign in there if it shows Log In)' });
|
||||
return;
|
||||
}
|
||||
|
||||
const fingerprint = `${found.expiresAt}:${found.accessToken.length}:${found.accessToken.slice(-16)}`;
|
||||
if (fingerprint === vestLastRelayed) return;
|
||||
const res = await fetch(`${cfg.apiBase}/api/vest/session`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
accessToken: found.accessToken, expiresAt: found.expiresAt, primaryAddr: found.primaryAddr, userId: found.userId,
|
||||
}),
|
||||
});
|
||||
const body = await res.json().catch(() => ({}));
|
||||
if (res.status === 404) throw new Error('this dashboard is too old for the Vest relay');
|
||||
if (!res.ok) throw new Error(`dashboard refused the session: ${body.reason || body.error || `HTTP ${res.status}`}`);
|
||||
vestLastRelayed = fingerprint;
|
||||
const short = `${found.primaryAddr.slice(0, 6)}…${found.primaryAddr.slice(-4)}`;
|
||||
const mins = Math.max(0, Math.round((found.expiresAt - Date.now()) / 60000));
|
||||
await setState({ vest: `Relayed ${short}${body.used ? '' : ' (no Vest firm here uses this wallet)'} · good ${mins}m` });
|
||||
}
|
||||
|
||||
// ── 2FA: the person's authenticator code for the firm site you are on ───────
|
||||
|
||||
Reference in New Issue
Block a user