src/signer/health.ts
v0.1.0 · 3.6 KB
export interface SidecarHealthView {
pubkey: string | null;
authority: string | null;
traderPda: string | null;
traderPdaIndex: number | null;
subaccountIndex: number | null;
readonly: boolean;
signerLoaded: boolean;
bindingOk: boolean;
bindingProblems: string[];
positionAuthority: string | null;
solLamports: bigint | null;
keyRiskWarning: string | null;
keyRiskSwap: string | null;
capabilitiesState: string | null;
collateralQuoteLots: bigint | null;
rpcOk: boolean;
inflight: number | null;
stale: number | null;
maxOrderNotionalUsd: number | null;
maxOrderNotionalMode: PhoenixOrderCapMode | null;
maxOrderCollateralMult: number | null;
maxOrderNotionalFixedUsd: number | null;
maxOrderNotionalUnknown: boolean | null;
}
export type PhoenixOrderCapMode = 'off' | 'fixed' | 'collateral' | 'min';
const ORDER_CAP_MODES: readonly PhoenixOrderCapMode[] = ['off', 'fixed', 'collateral', 'min'];
function usdOrNull(v: unknown): number | null {
if (typeof v === 'number') return Number.isFinite(v) && v >= 0 ? v : null;
if (typeof v === 'string' && /^\d{1,15}(\.\d{1,6})?$/.test(v)) return Number(v);
return null;
}
export function decodeSidecarHealth(body: unknown): SidecarHealthView | null {
if (!body || typeof body !== 'object' || Array.isArray(body)) return null;
const b = body as Record<string, unknown>;
const s = (v: unknown): string | null => (typeof v === 'string' && v ? v : null);
const n = (v: unknown): number | null => (typeof v === 'number' && Number.isInteger(v) ? v : null);
let sol: bigint | null = null;
if (typeof b.solLamports === 'string' && /^\d{1,20}$/.test(b.solLamports)) sol = BigInt(b.solLamports);
let collateral: bigint | null = null;
if (typeof b.collateralQuoteLots === 'string' && /^-?\d{1,19}$/.test(b.collateralQuoteLots))
collateral = BigInt(b.collateralQuoteLots);
const caps = b.capabilities as
| {
state?: unknown;
}
| null
| undefined;
const risk = b.keyRisk as
| {
warning?: unknown;
positionAuthoritySwap?: unknown;
}
| null
| undefined;
return {
pubkey: s(b.pubkey),
authority: s(b.authority),
traderPda: s(b.traderPda),
traderPdaIndex: n(b.traderPdaIndex),
subaccountIndex: n(b.subaccountIndex),
readonly: b.readonly === true,
signerLoaded: b.signerLoaded === true,
bindingOk: b.bindingOk === true,
bindingProblems: Array.isArray(b.bindingProblems)
? b.bindingProblems.filter((x): x is string => typeof x === 'string')
: [],
positionAuthority: s(b.positionAuthority),
solLamports: sol,
keyRiskWarning: s(risk?.warning),
keyRiskSwap: s(risk?.positionAuthoritySwap),
capabilitiesState: s(caps?.state),
collateralQuoteLots: collateral,
rpcOk: b.rpcOk === true,
inflight: typeof b.inflight === 'number' && Number.isInteger(b.inflight) && b.inflight >= 0 ? b.inflight : null,
stale: typeof b.stale === 'number' && Number.isInteger(b.stale) && b.stale >= 0 ? b.stale : null,
maxOrderNotionalUsd: usdOrNull(b.maxOrderNotionalUsd),
maxOrderNotionalMode: ORDER_CAP_MODES.includes(b.maxOrderNotionalMode as PhoenixOrderCapMode)
? (b.maxOrderNotionalMode as PhoenixOrderCapMode)
: null,
maxOrderCollateralMult:
typeof b.maxOrderCollateralMult === 'number' &&
Number.isFinite(b.maxOrderCollateralMult) &&
b.maxOrderCollateralMult > 0
? b.maxOrderCollateralMult
: null,
maxOrderNotionalFixedUsd: usdOrNull(b.maxOrderNotionalFixedUsd),
maxOrderNotionalUnknown: typeof b.maxOrderNotionalUnknown === 'boolean' ? b.maxOrderNotionalUnknown : null,
};
}