Files
skills/docs/money/finance/cryptocurrency/src/error-codes/error-codes.md
2026-05-29 08:20:56 +01:00

4.9 KiB

error-codes

(no submodules)

Purpose

Declares the stable, namespaced error registry for @suaveplan/cryptocurrency and the typed CryptocurrencyError class that wraps every failure path. The registry uses defineCodes("CRYPTOCURRENCY", [...]) to produce a frozen, literal-typed map ({ readonly UNKNOWN_CHAIN: "CRYPTOCURRENCY.UNKNOWN_CHAIN", ... }) so consumers can dispatch programmatically on err.code === CRYPTOCURRENCY_CODES.UNKNOWN_CHAIN with full TypeScript narrowing.

Centralising the registry guarantees the error contract is single-sourced — every throw site in the package picks from the same five-code set, and the exposed CryptocurrencyErrorCode union travels with consumers' code so a future code addition triggers an exhaustiveness check at every switch site.

Features

  • Five codes covering every distinct failure mode in the migrated surface: UNKNOWN_CHAIN, UNKNOWN_CURRENCY, UNKNOWN_UNIT, INVALID_ADDRESS, INVALID_HASH.
  • CryptocurrencyError extends SuaveplanError<CryptocurrencyErrorCode> with optional chainId / currency / unit discriminants for diagnostics.
  • CryptocurrencyErrorCode literal union exported for consumer narrowing.
  • CryptocurrencyErrorOptions extends SuaveplanErrorOptions so cause, context, traceId, and clock are all available.
  • The registry is Object.freezed so consumers cannot mutate the code map.

Usage: Basic

import {
    CRYPTOCURRENCY_CODES,
    CryptocurrencyError,
} from "@suaveplan/cryptocurrency";

throw new CryptocurrencyError(
    CRYPTOCURRENCY_CODES.UNKNOWN_CHAIN,
    "Unknown chain: ethereum",
    { chainId: "ethereum" }
);

Usage: Advanced

import {
    CRYPTOCURRENCY_CODES,
    CryptocurrencyError,
    type CryptocurrencyErrorCode,
} from "@suaveplan/cryptocurrency";

function describeFailure(err: unknown): string {
    if (!(err instanceof CryptocurrencyError)) return "non-crypto error";
    const code: CryptocurrencyErrorCode = err.code;
    switch (code) {
        case CRYPTOCURRENCY_CODES.UNKNOWN_CHAIN:
            return `unknown chain: ${err.chainId ?? "<unset>"}`;
        case CRYPTOCURRENCY_CODES.UNKNOWN_CURRENCY:
            return `unknown currency: ${err.currency ?? "<unset>"}`;
        case CRYPTOCURRENCY_CODES.UNKNOWN_UNIT:
            return `unknown unit '${err.unit}' for ${err.currency}`;
        case CRYPTOCURRENCY_CODES.INVALID_ADDRESS:
            return `invalid address${err.chainId ? ` for ${err.chainId}` : ""}`;
        case CRYPTOCURRENCY_CODES.INVALID_HASH:
            return `invalid hash${err.chainId ? ` for ${err.chainId}` : ""}`;
    }
}

try {
    // some package call
} catch (err) {
    console.error(describeFailure(err));
}

The exhaustive switch above will fail to compile if a new code is added without a matching arm — that is the point of exporting CryptocurrencyErrorCode.

API Reference

Export Kind Notes
CRYPTOCURRENCY_CODES const frozen { readonly <REASON>: "CRYPTOCURRENCY.<REASON>" } map
CryptocurrencyErrorCode type union of every registered code's string literal
CryptocurrencyError class extends SuaveplanError<CryptocurrencyErrorCode>; constructor (code, message, options?)
CryptocurrencyErrorOptions interface extends SuaveplanErrorOptions; adds optional chainId, currency, unit

CryptocurrencyError discriminants

  • chainId?: ChainId — set on UNKNOWN_CHAIN, INVALID_ADDRESS, INVALID_HASH raises.
  • currency?: string — set on UNKNOWN_CURRENCY, UNKNOWN_UNIT raises.
  • unit?: string — set on UNKNOWN_UNIT raises.

All discriminants are read-only and undefined when not supplied — consumers should narrow with if (err.chainId) rather than asserting non-null.

Implementation Notes

  • INVALID_ADDRESS and INVALID_HASH are exposed in the registry even though the package's own validators return structured AddressValidation / TxHashValidation records rather than throwing. The codes are there for application-tier consumers that want to wrap a failed validation in a typed throw. They are exercised by direct construction in error-codes.test.ts, satisfying the conformance rule (§13 step 4 in AGENTS.md) that every registered code must be reachable from a call site somewhere in src/.
  • defineCodes produces a Object.freezed record — mutating CRYPTOCURRENCY_CODES throws in strict mode. Consumers must rely on the literal types, not runtime extension.
  • CryptocurrencyError extends SuaveplanError (rule §63 of CLAUDE.md) so every error in the package participates in cause-chain traversal, OTel span events, redaction, and i18n message resolution.
  • The error's name field is automatically set to "CryptocurrencyError" by SuaveplanError's use of new.target.name.
  • The optional discriminants are assigned via if (options.X !== undefined) so the field stays undefined (and serialises away from JSON) when not provided.