src/orders/errors.ts
v0.3.0 · 2.6 KB
// Errors thrown by the orders module for invalid inputs and unusable responses.
//
// Market conditions (no mid price, size below the minimum, an exchange rejection, an unknown
// transport outcome) are NOT thrown: they come back as results, so one bad order never aborts the
// rest of a batch (a throw would also drop the reduce-only closes of that batch).
/** Machine-readable error code. */
export type HlOrderErrorCode =
/** A caller-supplied argument is malformed or contradictory; nothing was sent. */
| 'INVALID_ARGUMENT'
/** An /info answer has an unexpected shape; it must not be read as "flat" or "no orders". */
| 'INVALID_RESPONSE'
/** The agent address is not in `extraAgents` of the master account. */
| 'AGENT_NOT_LISTED'
/** The agent is listed but its `validUntil` is in the past. */
| 'AGENT_EXPIRED'
/** `extraAgents` could not be read; approval is unproven. */
| 'AGENT_QUERY_FAILED'
/** A built order failed the final pre-send self-check (a bug, never expected). */
| 'INVALID_ORDER';
/** Error thrown by the orders module. */
export class HlOrderError extends Error {
override readonly name = 'HlOrderError';
readonly code: HlOrderErrorCode;
constructor(code: HlOrderErrorCode, message: string, options?: { cause?: unknown }) {
super(message, options);
this.code = code;
}
}
/** @internal Throws `INVALID_ARGUMENT`. */
export function invalidArgument(message: string): never {
throw new HlOrderError('INVALID_ARGUMENT', message);
}
/** @internal Short, log-safe description of an unexpected value. */
export function describeValue(value: unknown): string {
if (value === null) return 'null';
if (Array.isArray(value)) return 'array';
if (typeof value === 'string') return JSON.stringify(value.length > 40 ? `${value.slice(0, 40)}...` : value);
if (typeof value === 'object') return 'object';
return String(value);
}
/** @internal Message of an unknown thrown value. */
export function messageOf(err: unknown): string {
if (typeof err === 'object' && err !== null && typeof (err as { message?: unknown }).message === 'string') {
return (err as { message: string }).message;
}
return String(err);
}
const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
/** @internal Validates a 0x + 40 hex address and lowercases it (HL compares addresses lowercase). */
export function normalizeAddress(value: unknown, field: string): `0x${string}` {
if (typeof value !== 'string' || !ADDRESS_RE.test(value)) {
invalidArgument(`Invalid ${field} ${describeValue(value)}: expected 0x + 40 hex characters`);
}
return value.toLowerCase() as `0x${string}`;
}