Skip to content
markpaper

src/assets/types.ts

v0.3.0 · 4.2 KB

Download file
// Public types of the assets module.

/** Market kind of a Hyperliquid instrument. HIP-3 (builder-deployed) perps are `perp` with a non-empty `dex`. */
export type MarketKind = 'perp' | 'spot';

/** Resolved, validated metadata of one tradable instrument. Objects are frozen. */
export interface AssetInfo {
  /**
   * Canonical coin name as used by `/info`, WS and fills:
   * `BTC` (main perp), `xyz:TSLA` (HIP-3), `PURR/USDC` or `@107` (spot).
   */
  readonly coin: string;
  /** Numeric asset id for the `a` / `asset` fields of exchange actions. */
  readonly assetId: number;
  /** Position inside its universe (perp) or the pair `index` (spot). */
  readonly index: number;
  /** Size step is `10^-szDecimals`. */
  readonly szDecimals: number;
  /**
   * Maximum decimals allowed in a price: `max(0, 6 - szDecimals)` for perps,
   * `max(0, 8 - szDecimals)` for spot (@experimental: the spot rule comes from a single source and was never
   * verified with live spot orders; an order at this precision may be rejected).
   * The 5-significant-figures rule applies on top of this.
   */
  readonly maxPxDecimals: number;
  /** Leverage ceiling from meta. Always 1 for spot. */
  readonly maxLeverage: number;
  /** Cross margin is not allowed; send `isCross: false` in `updateLeverage`. Always false for spot. */
  readonly onlyIsolated: boolean;
  /** Market is delisted: no new orders. */
  readonly isDelisted: boolean;
  /** Perp dex name: `''` for the main dex and for spot, `xyz` etc. for HIP-3. */
  readonly dex: string;
  /** Position of the dex in `perpDexs` (0 = main). `null` for spot. */
  readonly dexIndex: number | null;
  readonly market: MarketKind;
  /** Spot only: base token name (e.g. `PURR`). */
  readonly base?: string;
  /** Spot only: quote token name (e.g. `USDC`). */
  readonly quote?: string;
}

/** Spot token metadata from `spotMeta.tokens`. */
export interface SpotTokenInfo {
  readonly name: string;
  readonly index: number;
  /** `0x` + 32 hex. */
  readonly tokenId: string;
  readonly szDecimals: number;
  readonly weiDecimals: number;
  readonly isCanonical: boolean;
  /** Token identifier for transfers such as `sendAsset`: `NAME:0x…` (e.g. `USDC:0x6d1e7cde53ba9467b783cb7c530ce054`). */
  readonly wireId: string;
}

/** Why a coin is definitely not listed. */
export type UnlistedReason =
  /** The (fresh, verified) universe of its dex does not contain the coin. */
  | 'not_in_universe'
  /** A fresh, valid `perpDexs` has no dex with this name. */
  | 'dex_not_found'
  /** Malformed coin name or asset id. */
  | 'invalid';

/** Why the listing state cannot be determined right now. */
export type UnknownReason =
  | 'not_loaded'
  | 'load_failed'
  | 'invalid_response'
  | 'cross_check_failed'
  | 'coverage_failed'
  | 'registry_empty'
  | 'append_only_violation'
  | 'stale'
  | 'dex_unverified'
  | 'topology_unsafe'
  | 'dex_not_configured'
  | 'ambiguous';

/**
 * Three-valued listing check. `unknown` is NOT `unlisted`: a failed or stale meta must never be read as
 * a delisting (that gives false deactivations and false exits).
 */
export type AssetLookup =
  | { readonly status: 'listed'; readonly coin: string; readonly asset: AssetInfo }
  | { readonly status: 'unlisted'; readonly coin: string; readonly reason: UnlistedReason }
  | { readonly status: 'unknown'; readonly coin: string; readonly reason: UnknownReason; readonly error?: unknown };

/** Diagnostic events. Handlers must be cheap; exceptions thrown by them are swallowed. */
export type AssetRegistryEvent =
  | { type: 'loaded'; source: string; count: number }
  | { type: 'load_failed'; source: string; reason: UnknownReason | UnlistedReason; error: unknown; servingStale: boolean }
  | { type: 'append_only_violation'; source: string; issues: string[] }
  | { type: 'coverage_missing'; dex: string; missing: string[] }
  /**
   * Tradable (not delisted) assets present in a HIP-3 meta but not yet in the dex's `assetToStreamingOiCap` registry.
   * Delisted assets dropped from the registry are not reported: on mainnet they are the usual source of such extras.
   */
  | { type: 'new_listings'; dex: string; coins: string[] }
  | { type: 'registry_empty'; dex: string }
  | { type: 'topology_changed'; dex: string; previousIndex: number; currentIndex: number | null; duplicated: boolean };
All files