@moneolabs/core
Advanced tools
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/assets.ts","../src/money.ts","../src/duration.ts","../src/clock.ts","../src/ids.ts","../src/prices.ts"],"sourcesContent":["export { MoneoError, ParseError, ValidationError, InsufficientFundsError } from \"./errors.js\";\n\nexport {\n type AssetSpec,\n registerAsset,\n getAsset,\n hasAsset,\n assetForSign,\n knownAssets,\n} from \"./assets.js\";\n\nexport {\n type Money,\n type MoneyInput,\n type Rounding,\n isMoney,\n money,\n fromUnits,\n zero,\n parseMoney,\n formatMoney,\n toDecimalString,\n toNumber,\n addMoney,\n subMoney,\n negateMoney,\n absMoney,\n sumMoney,\n scaleMoney,\n splitMoney,\n cmpMoney,\n gtMoney,\n gteMoney,\n ltMoney,\n lteMoney,\n eqMoney,\n isZeroMoney,\n isNegativeMoney,\n isPositiveMoney,\n maxMoney,\n minMoney,\n convertPrecision,\n} from \"./money.js\";\n\nexport { type DurationInput, parseDuration, formatDuration } from \"./duration.js\";\n\nexport { type Clock, type ManualClock, systemClock, manualClock } from \"./clock.js\";\n\nexport { id, fingerprint, stableStringify } from \"./ids.js\";\n\nexport {\n type PriceSource,\n peggedPrices,\n fixedPrices,\n cachedPrices,\n valueInUsd,\n valueFromUsd,\n usd,\n} from \"./prices.js\";\n","/**\n * Every error thrown by a Moneo package carries a stable `code`. Agents act on\n * codes, not on message text, so messages stay free to change.\n */\nexport class MoneoError extends Error {\n readonly code: string;\n readonly details: Record<string, unknown>;\n\n constructor(code: string, message: string, details: Record<string, unknown> = {}) {\n super(message);\n this.name = \"MoneoError\";\n this.code = code;\n this.details = details;\n }\n}\n\n/** Input could not be understood: a malformed amount, duration, or policy. */\nexport class ParseError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"parse_error\", message, details);\n this.name = \"ParseError\";\n }\n}\n\n/** Input was understood but is not usable: mismatched assets, negative caps. */\nexport class ValidationError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"validation_error\", message, details);\n this.name = \"ValidationError\";\n }\n}\n\n/** The operation is legal but the funds are not there. */\nexport class InsufficientFundsError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"insufficient_funds\", message, details);\n this.name = \"InsufficientFundsError\";\n }\n}\n","import { ValidationError } from \"./errors.js\";\n\nexport interface AssetSpec {\n /** Canonical ticker, uppercase. */\n readonly symbol: string;\n /** Number of minor units per whole unit, as a power of ten. */\n readonly decimals: number;\n /** Currency sign used when formatting, if the asset has one. */\n readonly sign?: string;\n /** True for assets pegged 1:1 to the US dollar. */\n readonly usdPegged?: boolean;\n}\n\n/**\n * What an agent on Robinhood Chain actually holds: fiat for stating limits,\n * USDG for settlement, and ETH because gas is still ETH. Tokenized equities\n * are not listed here because the list changes; register the ones you trade\n * with `registerAsset({ symbol: \"AAPLX\", decimals: 18 })`.\n */\nconst BUILT_IN: AssetSpec[] = [\n { symbol: \"USD\", decimals: 2, sign: \"$\", usdPegged: true },\n { symbol: \"EUR\", decimals: 2, sign: \"€\" },\n { symbol: \"GBP\", decimals: 2, sign: \"£\" },\n { symbol: \"USDG\", decimals: 6, usdPegged: true },\n { symbol: \"USDC\", decimals: 6, usdPegged: true },\n { symbol: \"ETH\", decimals: 18 },\n];\n\nconst registry = new Map<string, AssetSpec>(BUILT_IN.map((a) => [a.symbol, a]));\n\n/** Signs that unambiguously identify an asset when parsing a string. */\nconst signIndex = new Map<string, string>(\n BUILT_IN.filter((a) => a.sign).map((a) => [a.sign as string, a.symbol]),\n);\n\n/**\n * Teach the SDK about an asset it does not ship with. Tokenized equities and\n * house currencies are the common cases.\n */\nexport function registerAsset(spec: AssetSpec): AssetSpec {\n const symbol = spec.symbol.toUpperCase();\n if (!Number.isInteger(spec.decimals) || spec.decimals < 0 || spec.decimals > 30) {\n throw new ValidationError(`asset ${symbol} needs decimals between 0 and 30`, {\n decimals: spec.decimals,\n });\n }\n const normalized: AssetSpec = { ...spec, symbol };\n registry.set(symbol, normalized);\n if (normalized.sign && !signIndex.has(normalized.sign)) {\n signIndex.set(normalized.sign, symbol);\n }\n return normalized;\n}\n\nexport function getAsset(symbol: string): AssetSpec {\n const spec = registry.get(symbol.toUpperCase());\n if (!spec) {\n throw new ValidationError(`unknown asset \"${symbol}\". Call registerAsset() to add it.`, {\n symbol,\n });\n }\n return spec;\n}\n\nexport function hasAsset(symbol: string): boolean {\n return registry.has(symbol.toUpperCase());\n}\n\nexport function assetForSign(sign: string): string | undefined {\n return signIndex.get(sign);\n}\n\nexport function knownAssets(): AssetSpec[] {\n return [...registry.values()];\n}\n","import { assetForSign, getAsset } from \"./assets.js\";\nimport { ParseError, ValidationError } from \"./errors.js\";\n\n/**\n * An exact amount of one asset.\n *\n * Amounts are stored as integer minor units in a bigint, never as a float. A\n * budget that drifts by a fraction of a cent every time it is checked is a\n * budget that eventually lets something through, so there is no floating point\n * anywhere in the arithmetic below.\n */\nexport interface Money {\n readonly asset: string;\n readonly units: bigint;\n readonly decimals: number;\n}\n\n/** Anything the SDKs will accept where an amount is expected. */\nexport type MoneyInput = Money | string | number | bigint;\n\nconst AMOUNT_PATTERN =\n /^\\s*(?<sign>[-+])?\\s*(?<symbolPrefix>[^\\d\\s.,+-]+)?\\s*(?<digits>[\\d,]*(?:\\.\\d+)?)\\s*(?<symbolSuffix>[A-Za-z][A-Za-z0-9]*)?\\s*$/u;\n\nexport function isMoney(value: unknown): value is Money {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Money).asset === \"string\" &&\n typeof (value as Money).units === \"bigint\" &&\n typeof (value as Money).decimals === \"number\"\n );\n}\n\n/** Build a Money from a whole-unit decimal string or number. */\nexport function money(amount: string | number | bigint, asset: string): Money {\n const spec = getAsset(asset);\n if (typeof amount === \"bigint\") {\n return { asset: spec.symbol, units: amount, decimals: spec.decimals };\n }\n const text = typeof amount === \"number\" ? numberToDecimalString(amount) : amount.trim();\n return {\n asset: spec.symbol,\n units: decimalToUnits(text, spec.decimals),\n decimals: spec.decimals,\n };\n}\n\n/** Build a Money directly from minor units, skipping decimal parsing. */\nexport function fromUnits(units: bigint, asset: string): Money {\n const spec = getAsset(asset);\n return { asset: spec.symbol, units, decimals: spec.decimals };\n}\n\nexport function zero(asset: string): Money {\n return fromUnits(0n, asset);\n}\n\n/**\n * Parse any accepted amount form. Strings may carry the asset themselves,\n * either as a sign (\"$250.00\") or a ticker (\"0.0277 ETH\"). Bare numbers need\n * `defaultAsset`.\n */\nexport function parseMoney(input: MoneyInput, defaultAsset?: string): Money {\n if (isMoney(input)) return input;\n if (typeof input === \"bigint\" || typeof input === \"number\") {\n if (!defaultAsset) {\n throw new ParseError('a bare number needs an asset: parseMoney(250, \"USD\")', { input });\n }\n return money(input, defaultAsset);\n }\n\n const match = AMOUNT_PATTERN.exec(input);\n if (!match?.groups) {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n const { sign, symbolPrefix, digits, symbolSuffix } = match.groups;\n if (!digits || digits === \".\" || digits === \"\") {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n let asset = defaultAsset;\n if (symbolPrefix) {\n const bySign = assetForSign(symbolPrefix);\n if (!bySign) {\n throw new ParseError(`unknown currency sign \"${symbolPrefix}\" in \"${input}\"`, { input });\n }\n asset = bySign;\n }\n if (symbolSuffix) asset = symbolSuffix;\n if (!asset) {\n throw new ParseError(`\"${input}\" does not name an asset and no default was given`, { input });\n }\n\n const spec = getAsset(asset);\n const units = decimalToUnits(digits.replace(/,/g, \"\"), spec.decimals);\n return {\n asset: spec.symbol,\n units: sign === \"-\" ? -units : units,\n decimals: spec.decimals,\n };\n}\n\n/**\n * Render for humans. Uses the asset sign when it has one, otherwise appends the\n * ticker. Trailing zeros are kept for currencies and trimmed for high precision\n * assets, where eighteen zeros help nobody.\n */\nexport function formatMoney(input: Money, options: { compact?: boolean } = {}): string {\n const m = input;\n const spec = getAsset(m.asset);\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = abs / scale;\n const fraction = abs % scale;\n\n let fractionText = m.decimals > 0 ? fraction.toString().padStart(m.decimals, \"0\") : \"\";\n if (options.compact !== false && m.decimals > 2) {\n // Keep at least two places so small currency-like values stay readable.\n fractionText = fractionText.replace(/0+$/, \"\");\n if (fractionText.length < 2) fractionText = fractionText.padEnd(2, \"0\");\n }\n\n const wholeText = groupThousands(whole.toString());\n const body = fractionText ? `${wholeText}.${fractionText}` : wholeText;\n const signText = negative ? \"-\" : \"\";\n\n return spec.sign ? `${signText}${spec.sign}${body}` : `${signText}${body} ${spec.symbol}`;\n}\n\n/** Exact decimal string with no sign, grouping, or ticker. Good for storage. */\nexport function toDecimalString(m: Money): string {\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = (abs / scale).toString();\n if (m.decimals === 0) return `${negative ? \"-\" : \"\"}${whole}`;\n const fraction = (abs % scale).toString().padStart(m.decimals, \"0\");\n return `${negative ? \"-\" : \"\"}${whole}.${fraction}`;\n}\n\n/** Lossy on purpose. Use for display and ratios, never for balances. */\nexport function toNumber(m: Money): number {\n return Number(toDecimalString(m));\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"add\");\n return { asset: a.asset, units: a.units + b.units, decimals: a.decimals };\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"subtract\");\n return { asset: a.asset, units: a.units - b.units, decimals: a.decimals };\n}\n\nexport function negateMoney(m: Money): Money {\n return { asset: m.asset, units: -m.units, decimals: m.decimals };\n}\n\nexport function absMoney(m: Money): Money {\n return m.units < 0n ? negateMoney(m) : m;\n}\n\n/** Sum of amounts, all of which must share an asset. */\nexport function sumMoney(amounts: readonly Money[], asset?: string): Money {\n const first = amounts[0];\n if (!first) {\n if (!asset) throw new ValidationError(\"sumMoney() of an empty list needs an asset\");\n return zero(asset);\n }\n return amounts.reduce((acc, next) => addMoney(acc, next), zero(first.asset));\n}\n\n/**\n * Scale by a rational number. Ratios are taken as numerator and denominator so\n * that percentages and slippage bounds stay exact.\n */\nexport function scaleMoney(\n m: Money,\n numerator: bigint | number,\n denominator: bigint | number = 1n,\n rounding: Rounding = \"half-up\",\n): Money {\n const [n, d] = toRational(numerator, denominator);\n if (d === 0n) throw new ValidationError(\"cannot scale by a zero denominator\");\n return { asset: m.asset, units: divideRounded(m.units * n, d, rounding), decimals: m.decimals };\n}\n\n/** Split into `parts` amounts that add back up to the original, to the unit. */\nexport function splitMoney(m: Money, parts: number): Money[] {\n if (!Number.isInteger(parts) || parts < 1) {\n throw new ValidationError(`cannot split into ${parts} parts`, { parts });\n }\n const count = BigInt(parts);\n const base = m.units / count;\n let remainder = m.units - base * count;\n const step = remainder < 0n ? -1n : 1n;\n const slices: Money[] = [];\n for (let i = 0; i < parts; i++) {\n let units = base;\n if (remainder !== 0n) {\n units += step;\n remainder -= step;\n }\n slices.push({ asset: m.asset, units, decimals: m.decimals });\n }\n return slices;\n}\n\nexport function cmpMoney(a: Money, b: Money): -1 | 0 | 1 {\n assertSameAsset(a, b, \"compare\");\n if (a.units < b.units) return -1;\n if (a.units > b.units) return 1;\n return 0;\n}\n\nexport const gtMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) > 0;\nexport const gteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) >= 0;\nexport const ltMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) < 0;\nexport const lteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) <= 0;\nexport const eqMoney = (a: Money, b: Money): boolean => a.asset === b.asset && a.units === b.units;\n\nexport const isZeroMoney = (m: Money): boolean => m.units === 0n;\nexport const isNegativeMoney = (m: Money): boolean => m.units < 0n;\nexport const isPositiveMoney = (m: Money): boolean => m.units > 0n;\n\nexport function maxMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) >= 0 ? a : b;\n}\n\nexport function minMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) <= 0 ? a : b;\n}\n\n/** Move an amount to another asset's precision, rounding if it shrinks. */\nexport function convertPrecision(m: Money, asset: string, rounding: Rounding = \"half-up\"): Money {\n const spec = getAsset(asset);\n if (spec.decimals === m.decimals) return { ...m, asset: spec.symbol };\n if (spec.decimals > m.decimals) {\n const factor = 10n ** BigInt(spec.decimals - m.decimals);\n return { asset: spec.symbol, units: m.units * factor, decimals: spec.decimals };\n }\n const factor = 10n ** BigInt(m.decimals - spec.decimals);\n return {\n asset: spec.symbol,\n units: divideRounded(m.units, factor, rounding),\n decimals: spec.decimals,\n };\n}\n\nexport type Rounding = \"half-up\" | \"down\" | \"up\";\n\n/* -------------------------------------------------------------------------- */\n\nfunction assertSameAsset(a: Money, b: Money, verb: string): void {\n if (a.asset !== b.asset) {\n throw new ValidationError(`cannot ${verb} ${a.asset} and ${b.asset}`, {\n left: a.asset,\n right: b.asset,\n });\n }\n}\n\nfunction groupThousands(digits: string): string {\n return digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\nfunction decimalToUnits(text: string, decimals: number): bigint {\n const cleaned = text.replace(/,/g, \"\").trim();\n if (!/^[-+]?\\d*(\\.\\d*)?$/.test(cleaned) || cleaned === \"\" || cleaned === \".\") {\n throw new ParseError(`could not read \"${text}\" as a decimal number`, { text });\n }\n const negative = cleaned.startsWith(\"-\");\n const unsigned = cleaned.replace(/^[-+]/, \"\");\n const [wholePart = \"\", fractionPart = \"\"] = unsigned.split(\".\");\n const whole = wholePart === \"\" ? \"0\" : wholePart;\n\n if (fractionPart.length > decimals) {\n // Silently dropping precision here is how you lose a customer's money.\n const extra = fractionPart.slice(decimals).replace(/0+$/, \"\");\n if (extra.length > 0) {\n throw new ParseError(`\"${text}\" has more precision than ${decimals} decimal places allow`, {\n text,\n decimals,\n });\n }\n }\n\n const padded = fractionPart.padEnd(decimals, \"0\").slice(0, decimals);\n const units = BigInt(whole + padded);\n return negative ? -units : units;\n}\n\n/** Render a JS number without exponent notation, so parsing stays exact. */\nfunction numberToDecimalString(value: number): string {\n if (!Number.isFinite(value)) {\n throw new ParseError(`${value} is not a usable amount`, { value });\n }\n if (Number.isInteger(value)) return value.toFixed(0);\n const text = value.toString();\n if (!text.includes(\"e\") && !text.includes(\"E\")) return text;\n // Exponent form: expand with enough places to keep every significant digit.\n return value.toFixed(20).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n}\n\n/**\n * Turn a possibly fractional ratio into an exact pair of integers by shifting\n * both sides by the same power of ten. `scaleMoney(m, 0.003)` becomes 3/1000,\n * so a slippage bound never drifts.\n */\nfunction toRational(numerator: bigint | number, denominator: bigint | number): [bigint, bigint] {\n if (typeof numerator === \"bigint\" && typeof denominator === \"bigint\") {\n return [numerator, denominator];\n }\n const nText =\n typeof numerator === \"bigint\" ? numerator.toString() : numberToDecimalString(numerator);\n const dText =\n typeof denominator === \"bigint\" ? denominator.toString() : numberToDecimalString(denominator);\n const places = Math.max(decimalPlaces(nText), decimalPlaces(dText));\n return [shiftDecimal(nText, places), shiftDecimal(dText, places)];\n}\n\nfunction decimalPlaces(text: string): number {\n const dot = text.indexOf(\".\");\n return dot === -1 ? 0 : text.length - dot - 1;\n}\n\nfunction shiftDecimal(text: string, places: number): bigint {\n const negative = text.startsWith(\"-\");\n const unsigned = text.replace(/^[-+]/, \"\");\n const [whole = \"0\", fraction = \"\"] = unsigned.split(\".\");\n const value = BigInt((whole || \"0\") + fraction.padEnd(places, \"0\"));\n return negative ? -value : value;\n}\n\nfunction divideRounded(numerator: bigint, denominator: bigint, rounding: Rounding): bigint {\n if (denominator === 0n) throw new ValidationError(\"division by zero\");\n const negative = numerator < 0n !== denominator < 0n;\n const absN = numerator < 0n ? -numerator : numerator;\n const absD = denominator < 0n ? -denominator : denominator;\n const quotient = absN / absD;\n const remainder = absN % absD;\n\n let result = quotient;\n if (remainder !== 0n) {\n if (rounding === \"up\") result = quotient + 1n;\n else if (rounding === \"half-up\" && remainder * 2n >= absD) result = quotient + 1n;\n }\n return negative ? -result : result;\n}\n","import { ParseError } from \"./errors.js\";\n\n/** A window like \"30m\", \"24h\", \"7d\". Numbers are read as milliseconds. */\nexport type DurationInput = string | number;\n\nconst UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n};\n\nconst PATTERN = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/giu;\n\n/**\n * Parse a duration to milliseconds. Compound forms work too, so \"1h30m\" and\n * \"90m\" agree.\n */\nexport function parseDuration(input: DurationInput): number {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input) || input < 0) {\n throw new ParseError(`${input} is not a usable duration`, { input });\n }\n return input;\n }\n\n const text = input.trim();\n if (text === \"\") throw new ParseError(\"empty duration\", { input });\n\n let total = 0;\n let matched = 0;\n PATTERN.lastIndex = 0;\n for (const match of text.matchAll(PATTERN)) {\n const [whole, amount, unit] = match;\n total += Number(amount) * (UNITS[unit!.toLowerCase()] ?? 0);\n matched += whole.length;\n }\n\n // Guard against \"30 potatoes\" quietly parsing as 30 milliseconds.\n if (matched === 0 || matched !== text.replace(/\\s+/g, \"\").length) {\n throw new ParseError(`could not read \"${input}\" as a duration`, { input });\n }\n return total;\n}\n\n/** Render milliseconds back to the shortest readable form. */\nexport function formatDuration(ms: number): string {\n if (ms === 0) return \"0s\";\n const order: [string, number][] = [\n [\"w\", UNITS.w!],\n [\"d\", UNITS.d!],\n [\"h\", UNITS.h!],\n [\"m\", UNITS.m!],\n [\"s\", UNITS.s!],\n [\"ms\", UNITS.ms!],\n ];\n let left = Math.round(ms);\n const parts: string[] = [];\n for (const [unit, size] of order) {\n const count = Math.floor(left / size);\n if (count > 0) {\n parts.push(`${count}${unit}`);\n left -= count * size;\n }\n }\n return parts.join(\"\");\n}\n","import { parseDuration, type DurationInput } from \"./duration.js\";\n\n/**\n * Time is injected everywhere it matters. Rolling budgets, velocity limits, and\n * TWAP schedules all read the clock, and a test suite that has to wait thirty\n * real minutes to check a thirty minute order is a test suite nobody runs.\n */\nexport interface Clock {\n now(): number;\n sleep(ms: number): Promise<void>;\n}\n\nexport const systemClock: Clock = {\n now: () => Date.now(),\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface ManualClock extends Clock {\n /** Move time forward and resolve anything sleeping through that window. */\n advance(by: DurationInput): Promise<void>;\n set(time: number): void;\n}\n\n/** A clock you drive by hand. Sleeps resolve the moment time passes them. */\nexport function manualClock(start: number | Date = 0): ManualClock {\n let current = start instanceof Date ? start.getTime() : start;\n let waiters: { at: number; resolve: () => void }[] = [];\n\n /** Resolve everything due now. Returns whether anything was woken. */\n const wakeDue = (): boolean => {\n const due = waiters.filter((w) => w.at <= current).sort((a, b) => a.at - b.at);\n if (due.length === 0) return false;\n waiters = waiters.filter((w) => w.at > current);\n for (const waiter of due) waiter.resolve();\n return true;\n };\n\n /** Yield past the microtask queue so woken tasks actually get to run. */\n const settleQueue = (): Promise<void> =>\n new Promise((resolve) => {\n setImmediate(resolve);\n });\n\n return {\n now: () => current,\n\n sleep(ms) {\n if (ms <= 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n waiters.push({ at: current + ms, resolve });\n });\n },\n\n /**\n * Move time forward and let everything that was waiting run to a stop.\n *\n * A woken task usually schedules another sleep, and with a big enough jump\n * that new sleep can already be due. One pass is not enough, so this drains\n * repeatedly until no waiter is left in the past.\n */\n async advance(by) {\n current += parseDuration(by);\n for (let pass = 0; pass < 10_000; pass++) {\n await settleQueue();\n if (!wakeDue()) return;\n }\n throw new Error(\n \"manualClock.advance() never settled: a sleep loop is scheduling work faster than time passes\",\n );\n },\n\n set(time) {\n current = time;\n wakeDue();\n },\n };\n}\n","import { randomBytes, createHash } from \"node:crypto\";\n\nconst ALPHABET = \"0123456789abcdefghjkmnpqrstvwxyz\"; // Crockford base32, no look-alikes.\n\n/**\n * A prefixed, URL safe id. The prefix survives into logs and error messages,\n * which is the whole point: `wlt_` and `pol_` should never be confusable.\n */\nexport function id(prefix: string, bytes = 12): string {\n return `${prefix}_${encode(randomBytes(bytes))}`;\n}\n\nfunction encode(buffer: Buffer): string {\n let bits = 0;\n let value = 0;\n let out = \"\";\n for (const byte of buffer) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += ALPHABET[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n }\n if (bits > 0) out += ALPHABET[(value << (5 - bits)) & 31];\n return out;\n}\n\n/**\n * A short, stable fingerprint of any JSON-shaped value. Object keys are sorted\n * before hashing, so two policies that differ only in key order produce the\n * same version.\n */\nexport function fingerprint(value: unknown, length = 10): string {\n const hash = createHash(\"sha256\").update(stableStringify(value)).digest(\"hex\");\n return hash.slice(0, length);\n}\n\nexport function stableStringify(value: unknown): string {\n // bigint first: JSON.stringify throws on it, and amounts are bigints here.\n if (typeof value === \"bigint\") return `\"${value.toString()}\"`;\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`);\n return `{${entries.join(\",\")}}`;\n}\n","import { getAsset } from \"./assets.js\";\nimport { ValidationError } from \"./errors.js\";\nimport { convertPrecision, money, scaleMoney, type Money } from \"./money.js\";\n\n/**\n * Limits are written in dollars, but agents move ETH, USDC, and tokenized\n * equities. Something has to price one in terms of the other, and it should be\n * yours to choose, so it is an interface rather than a hardcoded feed.\n */\nexport interface PriceSource {\n /** USD value of one whole unit of `asset`. */\n usdPrice(asset: string): Promise<number>;\n}\n\n/** Knows only that dollar-pegged assets are worth a dollar. Refuses the rest. */\nexport const peggedPrices: PriceSource = {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n if (spec.usdPegged) return 1;\n throw new ValidationError(\n `no USD price for ${spec.symbol}. Pass a PriceSource that covers it.`,\n { asset: spec.symbol },\n );\n },\n};\n\n/** A fixed price table. Useful for tests, backtests, and offline policy runs. */\nexport function fixedPrices(table: Record<string, number>): PriceSource {\n const normalized = new Map(\n Object.entries(table).map(([asset, price]) => [asset.toUpperCase(), price]),\n );\n return {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n const price = normalized.get(spec.symbol);\n if (price !== undefined) return price;\n if (spec.usdPegged) return 1;\n throw new ValidationError(`no USD price for ${spec.symbol}`, { asset: spec.symbol });\n },\n };\n}\n\n/** Wraps another source with a short time-to-live cache. */\nexport function cachedPrices(\n source: PriceSource,\n options: { ttlMs?: number; now?: () => number } = {},\n): PriceSource {\n const ttl = options.ttlMs ?? 5_000;\n const now = options.now ?? Date.now;\n const cache = new Map<string, { price: number; at: number }>();\n return {\n async usdPrice(asset) {\n const key = asset.toUpperCase();\n const hit = cache.get(key);\n if (hit && now() - hit.at < ttl) return hit.price;\n const price = await source.usdPrice(key);\n cache.set(key, { price, at: now() });\n return price;\n },\n };\n}\n\n/** Value an amount in USD. Dollar-pegged assets skip the price lookup. */\nexport async function valueInUsd(amount: Money, prices: PriceSource): Promise<Money> {\n const spec = getAsset(amount.asset);\n if (spec.symbol === \"USD\") return amount;\n if (spec.usdPegged) return convertPrecision(amount, \"USD\");\n\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price < 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n // Scale in the asset's own precision first, then step down to cents once.\n const scaled = scaleMoney(amount, ...ratioFrom(price));\n return convertPrecision(scaled, \"USD\");\n}\n\n/** Convert a USD amount into whole units of another asset. */\nexport async function valueFromUsd(usd: Money, asset: string, prices: PriceSource): Promise<Money> {\n const spec = getAsset(asset);\n if (spec.symbol === \"USD\") return usd;\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price <= 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n const [numerator, denominator] = ratioFrom(price);\n const widened = convertPrecision(usd, spec.symbol);\n return scaleMoney(widened, denominator, numerator);\n}\n\n/** Express a float price as an exact numerator and denominator. */\nfunction ratioFrom(price: number): [bigint, bigint] {\n const text = price.toString();\n if (text.includes(\"e\") || text.includes(\"E\")) {\n const fixed = price.toFixed(12).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n return ratioFromDecimalString(fixed);\n }\n return ratioFromDecimalString(text);\n}\n\nfunction ratioFromDecimalString(text: string): [bigint, bigint] {\n const dot = text.indexOf(\".\");\n if (dot === -1) return [BigInt(text), 1n];\n const places = text.length - dot - 1;\n return [BigInt(text.replace(\".\", \"\")), 10n ** BigInt(places)];\n}\n\n/** Convenience for the common case of stating a dollar figure. */\nexport function usd(amount: string | number | bigint): Money {\n return money(amount, \"USD\");\n}\n"],"mappings":"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","names":["factor","usd"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/assets.ts","../src/money.ts","../src/duration.ts","../src/clock.ts","../src/ids.ts","../src/prices.ts"],"sourcesContent":["export { MoneoError, ParseError, ValidationError, InsufficientFundsError } from \"./errors.js\";\n\nexport {\n type AssetSpec,\n registerAsset,\n getAsset,\n hasAsset,\n assetForSign,\n knownAssets,\n} from \"./assets.js\";\n\nexport {\n type Money,\n type MoneyInput,\n type Rounding,\n isMoney,\n money,\n fromUnits,\n zero,\n parseMoney,\n formatMoney,\n toDecimalString,\n toNumber,\n addMoney,\n subMoney,\n negateMoney,\n absMoney,\n sumMoney,\n scaleMoney,\n splitMoney,\n cmpMoney,\n gtMoney,\n gteMoney,\n ltMoney,\n lteMoney,\n eqMoney,\n isZeroMoney,\n isNegativeMoney,\n isPositiveMoney,\n maxMoney,\n minMoney,\n convertPrecision,\n} from \"./money.js\";\n\nexport { type DurationInput, parseDuration, formatDuration } from \"./duration.js\";\n\nexport { type Clock, type ManualClock, systemClock, manualClock } from \"./clock.js\";\n\nexport { id, fingerprint, stableStringify } from \"./ids.js\";\n\nexport {\n type PriceSource,\n peggedPrices,\n fixedPrices,\n cachedPrices,\n valueInUsd,\n valueFromUsd,\n usd,\n} from \"./prices.js\";\n","/**\n * Every error thrown by a Moneo package carries a stable `code`. Agents act on\n * codes, not on message text, so messages stay free to change.\n */\nexport class MoneoError extends Error {\n readonly code: string;\n readonly details: Record<string, unknown>;\n\n constructor(code: string, message: string, details: Record<string, unknown> = {}) {\n super(message);\n this.name = \"MoneoError\";\n this.code = code;\n this.details = details;\n }\n}\n\n/** Input could not be understood: a malformed amount, duration, or policy. */\nexport class ParseError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"parse_error\", message, details);\n this.name = \"ParseError\";\n }\n}\n\n/** Input was understood but is not usable: mismatched assets, negative caps. */\nexport class ValidationError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"validation_error\", message, details);\n this.name = \"ValidationError\";\n }\n}\n\n/** The operation is legal but the funds are not there. */\nexport class InsufficientFundsError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"insufficient_funds\", message, details);\n this.name = \"InsufficientFundsError\";\n }\n}\n","import { ValidationError } from \"./errors.js\";\n\nexport interface AssetSpec {\n /** Canonical ticker, uppercase. */\n readonly symbol: string;\n /** Number of minor units per whole unit, as a power of ten. */\n readonly decimals: number;\n /** Currency sign used when formatting, if the asset has one. */\n readonly sign?: string;\n /** True for assets pegged 1:1 to the US dollar. */\n readonly usdPegged?: boolean;\n}\n\n/**\n * What an agent on Robinhood Chain actually holds: fiat for stating limits,\n * USDG for settlement, and ETH because gas is still ETH. Tokenized equities\n * are not listed here because the list changes; register the ones you trade\n * with `registerAsset({ symbol: \"AAPL\", decimals: 18 })`.\n */\nconst BUILT_IN: AssetSpec[] = [\n { symbol: \"USD\", decimals: 2, sign: \"$\", usdPegged: true },\n { symbol: \"EUR\", decimals: 2, sign: \"€\" },\n { symbol: \"GBP\", decimals: 2, sign: \"£\" },\n { symbol: \"USDG\", decimals: 6, usdPegged: true },\n { symbol: \"USDC\", decimals: 6, usdPegged: true },\n { symbol: \"ETH\", decimals: 18 },\n];\n\nconst registry = new Map<string, AssetSpec>(BUILT_IN.map((a) => [a.symbol, a]));\n\n/** Signs that unambiguously identify an asset when parsing a string. */\nconst signIndex = new Map<string, string>(\n BUILT_IN.filter((a) => a.sign).map((a) => [a.sign as string, a.symbol]),\n);\n\n/**\n * Teach the SDK about an asset it does not ship with. Tokenized equities and\n * house currencies are the common cases.\n */\nexport function registerAsset(spec: AssetSpec): AssetSpec {\n const symbol = spec.symbol.toUpperCase();\n if (!Number.isInteger(spec.decimals) || spec.decimals < 0 || spec.decimals > 30) {\n throw new ValidationError(`asset ${symbol} needs decimals between 0 and 30`, {\n decimals: spec.decimals,\n });\n }\n const normalized: AssetSpec = { ...spec, symbol };\n registry.set(symbol, normalized);\n if (normalized.sign && !signIndex.has(normalized.sign)) {\n signIndex.set(normalized.sign, symbol);\n }\n return normalized;\n}\n\nexport function getAsset(symbol: string): AssetSpec {\n const spec = registry.get(symbol.toUpperCase());\n if (!spec) {\n throw new ValidationError(`unknown asset \"${symbol}\". Call registerAsset() to add it.`, {\n symbol,\n });\n }\n return spec;\n}\n\nexport function hasAsset(symbol: string): boolean {\n return registry.has(symbol.toUpperCase());\n}\n\nexport function assetForSign(sign: string): string | undefined {\n return signIndex.get(sign);\n}\n\nexport function knownAssets(): AssetSpec[] {\n return [...registry.values()];\n}\n","import { assetForSign, getAsset } from \"./assets.js\";\nimport { ParseError, ValidationError } from \"./errors.js\";\n\n/**\n * An exact amount of one asset.\n *\n * Amounts are stored as integer minor units in a bigint, never as a float. A\n * budget that drifts by a fraction of a cent every time it is checked is a\n * budget that eventually lets something through, so there is no floating point\n * anywhere in the arithmetic below.\n */\nexport interface Money {\n readonly asset: string;\n readonly units: bigint;\n readonly decimals: number;\n}\n\n/** Anything the SDKs will accept where an amount is expected. */\nexport type MoneyInput = Money | string | number | bigint;\n\nconst AMOUNT_PATTERN =\n /^\\s*(?<sign>[-+])?\\s*(?<symbolPrefix>[^\\d\\s.,+-]+)?\\s*(?<digits>[\\d,]*(?:\\.\\d+)?)\\s*(?<symbolSuffix>[A-Za-z][A-Za-z0-9]*)?\\s*$/u;\n\nexport function isMoney(value: unknown): value is Money {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Money).asset === \"string\" &&\n typeof (value as Money).units === \"bigint\" &&\n typeof (value as Money).decimals === \"number\"\n );\n}\n\n/** Build a Money from a whole-unit decimal string or number. */\nexport function money(amount: string | number | bigint, asset: string): Money {\n const spec = getAsset(asset);\n if (typeof amount === \"bigint\") {\n return { asset: spec.symbol, units: amount, decimals: spec.decimals };\n }\n const text = typeof amount === \"number\" ? numberToDecimalString(amount) : amount.trim();\n return {\n asset: spec.symbol,\n units: decimalToUnits(text, spec.decimals),\n decimals: spec.decimals,\n };\n}\n\n/** Build a Money directly from minor units, skipping decimal parsing. */\nexport function fromUnits(units: bigint, asset: string): Money {\n const spec = getAsset(asset);\n return { asset: spec.symbol, units, decimals: spec.decimals };\n}\n\nexport function zero(asset: string): Money {\n return fromUnits(0n, asset);\n}\n\n/**\n * Parse any accepted amount form. Strings may carry the asset themselves,\n * either as a sign (\"$250.00\") or a ticker (\"0.0277 ETH\"). Bare numbers need\n * `defaultAsset`.\n */\nexport function parseMoney(input: MoneyInput, defaultAsset?: string): Money {\n if (isMoney(input)) return input;\n if (typeof input === \"bigint\" || typeof input === \"number\") {\n if (!defaultAsset) {\n throw new ParseError('a bare number needs an asset: parseMoney(250, \"USD\")', { input });\n }\n return money(input, defaultAsset);\n }\n\n const match = AMOUNT_PATTERN.exec(input);\n if (!match?.groups) {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n const { sign, symbolPrefix, digits, symbolSuffix } = match.groups;\n if (!digits || digits === \".\" || digits === \"\") {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n let asset = defaultAsset;\n if (symbolPrefix) {\n const bySign = assetForSign(symbolPrefix);\n if (!bySign) {\n throw new ParseError(`unknown currency sign \"${symbolPrefix}\" in \"${input}\"`, { input });\n }\n asset = bySign;\n }\n if (symbolSuffix) asset = symbolSuffix;\n if (!asset) {\n throw new ParseError(`\"${input}\" does not name an asset and no default was given`, { input });\n }\n\n const spec = getAsset(asset);\n const units = decimalToUnits(digits.replace(/,/g, \"\"), spec.decimals);\n return {\n asset: spec.symbol,\n units: sign === \"-\" ? -units : units,\n decimals: spec.decimals,\n };\n}\n\n/**\n * Render for humans. Uses the asset sign when it has one, otherwise appends the\n * ticker. Trailing zeros are kept for currencies and trimmed for high precision\n * assets, where eighteen zeros help nobody.\n */\nexport function formatMoney(input: Money, options: { compact?: boolean } = {}): string {\n const m = input;\n const spec = getAsset(m.asset);\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = abs / scale;\n const fraction = abs % scale;\n\n let fractionText = m.decimals > 0 ? fraction.toString().padStart(m.decimals, \"0\") : \"\";\n if (options.compact !== false && m.decimals > 2) {\n // Keep at least two places so small currency-like values stay readable.\n fractionText = fractionText.replace(/0+$/, \"\");\n if (fractionText.length < 2) fractionText = fractionText.padEnd(2, \"0\");\n }\n\n const wholeText = groupThousands(whole.toString());\n const body = fractionText ? `${wholeText}.${fractionText}` : wholeText;\n const signText = negative ? \"-\" : \"\";\n\n return spec.sign ? `${signText}${spec.sign}${body}` : `${signText}${body} ${spec.symbol}`;\n}\n\n/** Exact decimal string with no sign, grouping, or ticker. Good for storage. */\nexport function toDecimalString(m: Money): string {\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = (abs / scale).toString();\n if (m.decimals === 0) return `${negative ? \"-\" : \"\"}${whole}`;\n const fraction = (abs % scale).toString().padStart(m.decimals, \"0\");\n return `${negative ? \"-\" : \"\"}${whole}.${fraction}`;\n}\n\n/** Lossy on purpose. Use for display and ratios, never for balances. */\nexport function toNumber(m: Money): number {\n return Number(toDecimalString(m));\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"add\");\n return { asset: a.asset, units: a.units + b.units, decimals: a.decimals };\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"subtract\");\n return { asset: a.asset, units: a.units - b.units, decimals: a.decimals };\n}\n\nexport function negateMoney(m: Money): Money {\n return { asset: m.asset, units: -m.units, decimals: m.decimals };\n}\n\nexport function absMoney(m: Money): Money {\n return m.units < 0n ? negateMoney(m) : m;\n}\n\n/** Sum of amounts, all of which must share an asset. */\nexport function sumMoney(amounts: readonly Money[], asset?: string): Money {\n const first = amounts[0];\n if (!first) {\n if (!asset) throw new ValidationError(\"sumMoney() of an empty list needs an asset\");\n return zero(asset);\n }\n return amounts.reduce((acc, next) => addMoney(acc, next), zero(first.asset));\n}\n\n/**\n * Scale by a rational number. Ratios are taken as numerator and denominator so\n * that percentages and slippage bounds stay exact.\n */\nexport function scaleMoney(\n m: Money,\n numerator: bigint | number,\n denominator: bigint | number = 1n,\n rounding: Rounding = \"half-up\",\n): Money {\n const [n, d] = toRational(numerator, denominator);\n if (d === 0n) throw new ValidationError(\"cannot scale by a zero denominator\");\n return { asset: m.asset, units: divideRounded(m.units * n, d, rounding), decimals: m.decimals };\n}\n\n/** Split into `parts` amounts that add back up to the original, to the unit. */\nexport function splitMoney(m: Money, parts: number): Money[] {\n if (!Number.isInteger(parts) || parts < 1) {\n throw new ValidationError(`cannot split into ${parts} parts`, { parts });\n }\n const count = BigInt(parts);\n const base = m.units / count;\n let remainder = m.units - base * count;\n const step = remainder < 0n ? -1n : 1n;\n const slices: Money[] = [];\n for (let i = 0; i < parts; i++) {\n let units = base;\n if (remainder !== 0n) {\n units += step;\n remainder -= step;\n }\n slices.push({ asset: m.asset, units, decimals: m.decimals });\n }\n return slices;\n}\n\nexport function cmpMoney(a: Money, b: Money): -1 | 0 | 1 {\n assertSameAsset(a, b, \"compare\");\n if (a.units < b.units) return -1;\n if (a.units > b.units) return 1;\n return 0;\n}\n\nexport const gtMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) > 0;\nexport const gteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) >= 0;\nexport const ltMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) < 0;\nexport const lteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) <= 0;\nexport const eqMoney = (a: Money, b: Money): boolean => a.asset === b.asset && a.units === b.units;\n\nexport const isZeroMoney = (m: Money): boolean => m.units === 0n;\nexport const isNegativeMoney = (m: Money): boolean => m.units < 0n;\nexport const isPositiveMoney = (m: Money): boolean => m.units > 0n;\n\nexport function maxMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) >= 0 ? a : b;\n}\n\nexport function minMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) <= 0 ? a : b;\n}\n\n/** Move an amount to another asset's precision, rounding if it shrinks. */\nexport function convertPrecision(m: Money, asset: string, rounding: Rounding = \"half-up\"): Money {\n const spec = getAsset(asset);\n if (spec.decimals === m.decimals) return { ...m, asset: spec.symbol };\n if (spec.decimals > m.decimals) {\n const factor = 10n ** BigInt(spec.decimals - m.decimals);\n return { asset: spec.symbol, units: m.units * factor, decimals: spec.decimals };\n }\n const factor = 10n ** BigInt(m.decimals - spec.decimals);\n return {\n asset: spec.symbol,\n units: divideRounded(m.units, factor, rounding),\n decimals: spec.decimals,\n };\n}\n\nexport type Rounding = \"half-up\" | \"down\" | \"up\";\n\n/* -------------------------------------------------------------------------- */\n\nfunction assertSameAsset(a: Money, b: Money, verb: string): void {\n if (a.asset !== b.asset) {\n throw new ValidationError(`cannot ${verb} ${a.asset} and ${b.asset}`, {\n left: a.asset,\n right: b.asset,\n });\n }\n}\n\nfunction groupThousands(digits: string): string {\n return digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\nfunction decimalToUnits(text: string, decimals: number): bigint {\n const cleaned = text.replace(/,/g, \"\").trim();\n if (!/^[-+]?\\d*(\\.\\d*)?$/.test(cleaned) || cleaned === \"\" || cleaned === \".\") {\n throw new ParseError(`could not read \"${text}\" as a decimal number`, { text });\n }\n const negative = cleaned.startsWith(\"-\");\n const unsigned = cleaned.replace(/^[-+]/, \"\");\n const [wholePart = \"\", fractionPart = \"\"] = unsigned.split(\".\");\n const whole = wholePart === \"\" ? \"0\" : wholePart;\n\n if (fractionPart.length > decimals) {\n // Silently dropping precision here is how you lose a customer's money.\n const extra = fractionPart.slice(decimals).replace(/0+$/, \"\");\n if (extra.length > 0) {\n throw new ParseError(`\"${text}\" has more precision than ${decimals} decimal places allow`, {\n text,\n decimals,\n });\n }\n }\n\n const padded = fractionPart.padEnd(decimals, \"0\").slice(0, decimals);\n const units = BigInt(whole + padded);\n return negative ? -units : units;\n}\n\n/** Render a JS number without exponent notation, so parsing stays exact. */\nfunction numberToDecimalString(value: number): string {\n if (!Number.isFinite(value)) {\n throw new ParseError(`${value} is not a usable amount`, { value });\n }\n if (Number.isInteger(value)) return value.toFixed(0);\n const text = value.toString();\n if (!text.includes(\"e\") && !text.includes(\"E\")) return text;\n // Exponent form: expand with enough places to keep every significant digit.\n return value.toFixed(20).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n}\n\n/**\n * Turn a possibly fractional ratio into an exact pair of integers by shifting\n * both sides by the same power of ten. `scaleMoney(m, 0.003)` becomes 3/1000,\n * so a slippage bound never drifts.\n */\nfunction toRational(numerator: bigint | number, denominator: bigint | number): [bigint, bigint] {\n if (typeof numerator === \"bigint\" && typeof denominator === \"bigint\") {\n return [numerator, denominator];\n }\n const nText =\n typeof numerator === \"bigint\" ? numerator.toString() : numberToDecimalString(numerator);\n const dText =\n typeof denominator === \"bigint\" ? denominator.toString() : numberToDecimalString(denominator);\n const places = Math.max(decimalPlaces(nText), decimalPlaces(dText));\n return [shiftDecimal(nText, places), shiftDecimal(dText, places)];\n}\n\nfunction decimalPlaces(text: string): number {\n const dot = text.indexOf(\".\");\n return dot === -1 ? 0 : text.length - dot - 1;\n}\n\nfunction shiftDecimal(text: string, places: number): bigint {\n const negative = text.startsWith(\"-\");\n const unsigned = text.replace(/^[-+]/, \"\");\n const [whole = \"0\", fraction = \"\"] = unsigned.split(\".\");\n const value = BigInt((whole || \"0\") + fraction.padEnd(places, \"0\"));\n return negative ? -value : value;\n}\n\nfunction divideRounded(numerator: bigint, denominator: bigint, rounding: Rounding): bigint {\n if (denominator === 0n) throw new ValidationError(\"division by zero\");\n const negative = numerator < 0n !== denominator < 0n;\n const absN = numerator < 0n ? -numerator : numerator;\n const absD = denominator < 0n ? -denominator : denominator;\n const quotient = absN / absD;\n const remainder = absN % absD;\n\n let result = quotient;\n if (remainder !== 0n) {\n if (rounding === \"up\") result = quotient + 1n;\n else if (rounding === \"half-up\" && remainder * 2n >= absD) result = quotient + 1n;\n }\n return negative ? -result : result;\n}\n","import { ParseError } from \"./errors.js\";\n\n/** A window like \"30m\", \"24h\", \"7d\". Numbers are read as milliseconds. */\nexport type DurationInput = string | number;\n\nconst UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n};\n\nconst PATTERN = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/giu;\n\n/**\n * Parse a duration to milliseconds. Compound forms work too, so \"1h30m\" and\n * \"90m\" agree.\n */\nexport function parseDuration(input: DurationInput): number {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input) || input < 0) {\n throw new ParseError(`${input} is not a usable duration`, { input });\n }\n return input;\n }\n\n const text = input.trim();\n if (text === \"\") throw new ParseError(\"empty duration\", { input });\n\n let total = 0;\n let matched = 0;\n PATTERN.lastIndex = 0;\n for (const match of text.matchAll(PATTERN)) {\n const [whole, amount, unit] = match;\n total += Number(amount) * (UNITS[unit!.toLowerCase()] ?? 0);\n matched += whole.length;\n }\n\n // Guard against \"30 potatoes\" quietly parsing as 30 milliseconds.\n if (matched === 0 || matched !== text.replace(/\\s+/g, \"\").length) {\n throw new ParseError(`could not read \"${input}\" as a duration`, { input });\n }\n return total;\n}\n\n/** Render milliseconds back to the shortest readable form. */\nexport function formatDuration(ms: number): string {\n if (ms === 0) return \"0s\";\n const order: [string, number][] = [\n [\"w\", UNITS.w!],\n [\"d\", UNITS.d!],\n [\"h\", UNITS.h!],\n [\"m\", UNITS.m!],\n [\"s\", UNITS.s!],\n [\"ms\", UNITS.ms!],\n ];\n let left = Math.round(ms);\n const parts: string[] = [];\n for (const [unit, size] of order) {\n const count = Math.floor(left / size);\n if (count > 0) {\n parts.push(`${count}${unit}`);\n left -= count * size;\n }\n }\n return parts.join(\"\");\n}\n","import { parseDuration, type DurationInput } from \"./duration.js\";\n\n/**\n * Time is injected everywhere it matters. Rolling budgets, velocity limits, and\n * TWAP schedules all read the clock, and a test suite that has to wait thirty\n * real minutes to check a thirty minute order is a test suite nobody runs.\n */\nexport interface Clock {\n now(): number;\n sleep(ms: number): Promise<void>;\n}\n\nexport const systemClock: Clock = {\n now: () => Date.now(),\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface ManualClock extends Clock {\n /** Move time forward and resolve anything sleeping through that window. */\n advance(by: DurationInput): Promise<void>;\n set(time: number): void;\n}\n\n/** A clock you drive by hand. Sleeps resolve the moment time passes them. */\nexport function manualClock(start: number | Date = 0): ManualClock {\n let current = start instanceof Date ? start.getTime() : start;\n let waiters: { at: number; resolve: () => void }[] = [];\n\n /** Resolve everything due now. Returns whether anything was woken. */\n const wakeDue = (): boolean => {\n const due = waiters.filter((w) => w.at <= current).sort((a, b) => a.at - b.at);\n if (due.length === 0) return false;\n waiters = waiters.filter((w) => w.at > current);\n for (const waiter of due) waiter.resolve();\n return true;\n };\n\n /** Yield past the microtask queue so woken tasks actually get to run. */\n const settleQueue = (): Promise<void> =>\n new Promise((resolve) => {\n setImmediate(resolve);\n });\n\n return {\n now: () => current,\n\n sleep(ms) {\n if (ms <= 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n waiters.push({ at: current + ms, resolve });\n });\n },\n\n /**\n * Move time forward and let everything that was waiting run to a stop.\n *\n * A woken task usually schedules another sleep, and with a big enough jump\n * that new sleep can already be due. One pass is not enough, so this drains\n * repeatedly until no waiter is left in the past.\n */\n async advance(by) {\n current += parseDuration(by);\n for (let pass = 0; pass < 10_000; pass++) {\n await settleQueue();\n if (!wakeDue()) return;\n }\n throw new Error(\n \"manualClock.advance() never settled: a sleep loop is scheduling work faster than time passes\",\n );\n },\n\n set(time) {\n current = time;\n wakeDue();\n },\n };\n}\n","import { randomBytes, createHash } from \"node:crypto\";\n\nconst ALPHABET = \"0123456789abcdefghjkmnpqrstvwxyz\"; // Crockford base32, no look-alikes.\n\n/**\n * A prefixed, URL safe id. The prefix survives into logs and error messages,\n * which is the whole point: `wlt_` and `pol_` should never be confusable.\n */\nexport function id(prefix: string, bytes = 12): string {\n return `${prefix}_${encode(randomBytes(bytes))}`;\n}\n\nfunction encode(buffer: Buffer): string {\n let bits = 0;\n let value = 0;\n let out = \"\";\n for (const byte of buffer) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += ALPHABET[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n }\n if (bits > 0) out += ALPHABET[(value << (5 - bits)) & 31];\n return out;\n}\n\n/**\n * A short, stable fingerprint of any JSON-shaped value. Object keys are sorted\n * before hashing, so two policies that differ only in key order produce the\n * same version.\n */\nexport function fingerprint(value: unknown, length = 10): string {\n const hash = createHash(\"sha256\").update(stableStringify(value)).digest(\"hex\");\n return hash.slice(0, length);\n}\n\nexport function stableStringify(value: unknown): string {\n // bigint first: JSON.stringify throws on it, and amounts are bigints here.\n if (typeof value === \"bigint\") return `\"${value.toString()}\"`;\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`);\n return `{${entries.join(\",\")}}`;\n}\n","import { getAsset } from \"./assets.js\";\nimport { ValidationError } from \"./errors.js\";\nimport { convertPrecision, money, scaleMoney, type Money } from \"./money.js\";\n\n/**\n * Limits are written in dollars, but agents move ETH, USDC, and tokenized\n * equities. Something has to price one in terms of the other, and it should be\n * yours to choose, so it is an interface rather than a hardcoded feed.\n */\nexport interface PriceSource {\n /** USD value of one whole unit of `asset`. */\n usdPrice(asset: string): Promise<number>;\n}\n\n/** Knows only that dollar-pegged assets are worth a dollar. Refuses the rest. */\nexport const peggedPrices: PriceSource = {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n if (spec.usdPegged) return 1;\n throw new ValidationError(\n `no USD price for ${spec.symbol}. Pass a PriceSource that covers it.`,\n { asset: spec.symbol },\n );\n },\n};\n\n/** A fixed price table. Useful for tests, backtests, and offline policy runs. */\nexport function fixedPrices(table: Record<string, number>): PriceSource {\n const normalized = new Map(\n Object.entries(table).map(([asset, price]) => [asset.toUpperCase(), price]),\n );\n return {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n const price = normalized.get(spec.symbol);\n if (price !== undefined) return price;\n if (spec.usdPegged) return 1;\n throw new ValidationError(`no USD price for ${spec.symbol}`, { asset: spec.symbol });\n },\n };\n}\n\n/** Wraps another source with a short time-to-live cache. */\nexport function cachedPrices(\n source: PriceSource,\n options: { ttlMs?: number; now?: () => number } = {},\n): PriceSource {\n const ttl = options.ttlMs ?? 5_000;\n const now = options.now ?? Date.now;\n const cache = new Map<string, { price: number; at: number }>();\n return {\n async usdPrice(asset) {\n const key = asset.toUpperCase();\n const hit = cache.get(key);\n if (hit && now() - hit.at < ttl) return hit.price;\n const price = await source.usdPrice(key);\n cache.set(key, { price, at: now() });\n return price;\n },\n };\n}\n\n/** Value an amount in USD. Dollar-pegged assets skip the price lookup. */\nexport async function valueInUsd(amount: Money, prices: PriceSource): Promise<Money> {\n const spec = getAsset(amount.asset);\n if (spec.symbol === \"USD\") return amount;\n if (spec.usdPegged) return convertPrecision(amount, \"USD\");\n\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price < 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n // Scale in the asset's own precision first, then step down to cents once.\n const scaled = scaleMoney(amount, ...ratioFrom(price));\n return convertPrecision(scaled, \"USD\");\n}\n\n/** Convert a USD amount into whole units of another asset. */\nexport async function valueFromUsd(usd: Money, asset: string, prices: PriceSource): Promise<Money> {\n const spec = getAsset(asset);\n if (spec.symbol === \"USD\") return usd;\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price <= 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n const [numerator, denominator] = ratioFrom(price);\n const widened = convertPrecision(usd, spec.symbol);\n return scaleMoney(widened, denominator, numerator);\n}\n\n/** Express a float price as an exact numerator and denominator. */\nfunction ratioFrom(price: number): [bigint, bigint] {\n const text = price.toString();\n if (text.includes(\"e\") || text.includes(\"E\")) {\n const fixed = price.toFixed(12).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n return ratioFromDecimalString(fixed);\n }\n return ratioFromDecimalString(text);\n}\n\nfunction ratioFromDecimalString(text: string): [bigint, bigint] {\n const dot = text.indexOf(\".\");\n if (dot === -1) return [BigInt(text), 1n];\n const places = text.length - dot - 1;\n return [BigInt(text.replace(\".\", \"\")), 10n ** BigInt(places)];\n}\n\n/** Convenience for the common case of stating a dollar figure. */\nexport function usd(amount: string | number | bigint): Money {\n return money(amount, \"USD\");\n}\n"],"mappings":"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","names":["factor","usd"]} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/errors.ts","../src/assets.ts","../src/money.ts","../src/duration.ts","../src/clock.ts","../src/ids.ts","../src/prices.ts"],"sourcesContent":["/**\n * Every error thrown by a Moneo package carries a stable `code`. Agents act on\n * codes, not on message text, so messages stay free to change.\n */\nexport class MoneoError extends Error {\n readonly code: string;\n readonly details: Record<string, unknown>;\n\n constructor(code: string, message: string, details: Record<string, unknown> = {}) {\n super(message);\n this.name = \"MoneoError\";\n this.code = code;\n this.details = details;\n }\n}\n\n/** Input could not be understood: a malformed amount, duration, or policy. */\nexport class ParseError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"parse_error\", message, details);\n this.name = \"ParseError\";\n }\n}\n\n/** Input was understood but is not usable: mismatched assets, negative caps. */\nexport class ValidationError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"validation_error\", message, details);\n this.name = \"ValidationError\";\n }\n}\n\n/** The operation is legal but the funds are not there. */\nexport class InsufficientFundsError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"insufficient_funds\", message, details);\n this.name = \"InsufficientFundsError\";\n }\n}\n","import { ValidationError } from \"./errors.js\";\n\nexport interface AssetSpec {\n /** Canonical ticker, uppercase. */\n readonly symbol: string;\n /** Number of minor units per whole unit, as a power of ten. */\n readonly decimals: number;\n /** Currency sign used when formatting, if the asset has one. */\n readonly sign?: string;\n /** True for assets pegged 1:1 to the US dollar. */\n readonly usdPegged?: boolean;\n}\n\n/**\n * What an agent on Robinhood Chain actually holds: fiat for stating limits,\n * USDG for settlement, and ETH because gas is still ETH. Tokenized equities\n * are not listed here because the list changes; register the ones you trade\n * with `registerAsset({ symbol: \"AAPLX\", decimals: 18 })`.\n */\nconst BUILT_IN: AssetSpec[] = [\n { symbol: \"USD\", decimals: 2, sign: \"$\", usdPegged: true },\n { symbol: \"EUR\", decimals: 2, sign: \"€\" },\n { symbol: \"GBP\", decimals: 2, sign: \"£\" },\n { symbol: \"USDG\", decimals: 6, usdPegged: true },\n { symbol: \"USDC\", decimals: 6, usdPegged: true },\n { symbol: \"ETH\", decimals: 18 },\n];\n\nconst registry = new Map<string, AssetSpec>(BUILT_IN.map((a) => [a.symbol, a]));\n\n/** Signs that unambiguously identify an asset when parsing a string. */\nconst signIndex = new Map<string, string>(\n BUILT_IN.filter((a) => a.sign).map((a) => [a.sign as string, a.symbol]),\n);\n\n/**\n * Teach the SDK about an asset it does not ship with. Tokenized equities and\n * house currencies are the common cases.\n */\nexport function registerAsset(spec: AssetSpec): AssetSpec {\n const symbol = spec.symbol.toUpperCase();\n if (!Number.isInteger(spec.decimals) || spec.decimals < 0 || spec.decimals > 30) {\n throw new ValidationError(`asset ${symbol} needs decimals between 0 and 30`, {\n decimals: spec.decimals,\n });\n }\n const normalized: AssetSpec = { ...spec, symbol };\n registry.set(symbol, normalized);\n if (normalized.sign && !signIndex.has(normalized.sign)) {\n signIndex.set(normalized.sign, symbol);\n }\n return normalized;\n}\n\nexport function getAsset(symbol: string): AssetSpec {\n const spec = registry.get(symbol.toUpperCase());\n if (!spec) {\n throw new ValidationError(`unknown asset \"${symbol}\". Call registerAsset() to add it.`, {\n symbol,\n });\n }\n return spec;\n}\n\nexport function hasAsset(symbol: string): boolean {\n return registry.has(symbol.toUpperCase());\n}\n\nexport function assetForSign(sign: string): string | undefined {\n return signIndex.get(sign);\n}\n\nexport function knownAssets(): AssetSpec[] {\n return [...registry.values()];\n}\n","import { assetForSign, getAsset } from \"./assets.js\";\nimport { ParseError, ValidationError } from \"./errors.js\";\n\n/**\n * An exact amount of one asset.\n *\n * Amounts are stored as integer minor units in a bigint, never as a float. A\n * budget that drifts by a fraction of a cent every time it is checked is a\n * budget that eventually lets something through, so there is no floating point\n * anywhere in the arithmetic below.\n */\nexport interface Money {\n readonly asset: string;\n readonly units: bigint;\n readonly decimals: number;\n}\n\n/** Anything the SDKs will accept where an amount is expected. */\nexport type MoneyInput = Money | string | number | bigint;\n\nconst AMOUNT_PATTERN =\n /^\\s*(?<sign>[-+])?\\s*(?<symbolPrefix>[^\\d\\s.,+-]+)?\\s*(?<digits>[\\d,]*(?:\\.\\d+)?)\\s*(?<symbolSuffix>[A-Za-z][A-Za-z0-9]*)?\\s*$/u;\n\nexport function isMoney(value: unknown): value is Money {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Money).asset === \"string\" &&\n typeof (value as Money).units === \"bigint\" &&\n typeof (value as Money).decimals === \"number\"\n );\n}\n\n/** Build a Money from a whole-unit decimal string or number. */\nexport function money(amount: string | number | bigint, asset: string): Money {\n const spec = getAsset(asset);\n if (typeof amount === \"bigint\") {\n return { asset: spec.symbol, units: amount, decimals: spec.decimals };\n }\n const text = typeof amount === \"number\" ? numberToDecimalString(amount) : amount.trim();\n return {\n asset: spec.symbol,\n units: decimalToUnits(text, spec.decimals),\n decimals: spec.decimals,\n };\n}\n\n/** Build a Money directly from minor units, skipping decimal parsing. */\nexport function fromUnits(units: bigint, asset: string): Money {\n const spec = getAsset(asset);\n return { asset: spec.symbol, units, decimals: spec.decimals };\n}\n\nexport function zero(asset: string): Money {\n return fromUnits(0n, asset);\n}\n\n/**\n * Parse any accepted amount form. Strings may carry the asset themselves,\n * either as a sign (\"$250.00\") or a ticker (\"0.0277 ETH\"). Bare numbers need\n * `defaultAsset`.\n */\nexport function parseMoney(input: MoneyInput, defaultAsset?: string): Money {\n if (isMoney(input)) return input;\n if (typeof input === \"bigint\" || typeof input === \"number\") {\n if (!defaultAsset) {\n throw new ParseError('a bare number needs an asset: parseMoney(250, \"USD\")', { input });\n }\n return money(input, defaultAsset);\n }\n\n const match = AMOUNT_PATTERN.exec(input);\n if (!match?.groups) {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n const { sign, symbolPrefix, digits, symbolSuffix } = match.groups;\n if (!digits || digits === \".\" || digits === \"\") {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n let asset = defaultAsset;\n if (symbolPrefix) {\n const bySign = assetForSign(symbolPrefix);\n if (!bySign) {\n throw new ParseError(`unknown currency sign \"${symbolPrefix}\" in \"${input}\"`, { input });\n }\n asset = bySign;\n }\n if (symbolSuffix) asset = symbolSuffix;\n if (!asset) {\n throw new ParseError(`\"${input}\" does not name an asset and no default was given`, { input });\n }\n\n const spec = getAsset(asset);\n const units = decimalToUnits(digits.replace(/,/g, \"\"), spec.decimals);\n return {\n asset: spec.symbol,\n units: sign === \"-\" ? -units : units,\n decimals: spec.decimals,\n };\n}\n\n/**\n * Render for humans. Uses the asset sign when it has one, otherwise appends the\n * ticker. Trailing zeros are kept for currencies and trimmed for high precision\n * assets, where eighteen zeros help nobody.\n */\nexport function formatMoney(input: Money, options: { compact?: boolean } = {}): string {\n const m = input;\n const spec = getAsset(m.asset);\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = abs / scale;\n const fraction = abs % scale;\n\n let fractionText = m.decimals > 0 ? fraction.toString().padStart(m.decimals, \"0\") : \"\";\n if (options.compact !== false && m.decimals > 2) {\n // Keep at least two places so small currency-like values stay readable.\n fractionText = fractionText.replace(/0+$/, \"\");\n if (fractionText.length < 2) fractionText = fractionText.padEnd(2, \"0\");\n }\n\n const wholeText = groupThousands(whole.toString());\n const body = fractionText ? `${wholeText}.${fractionText}` : wholeText;\n const signText = negative ? \"-\" : \"\";\n\n return spec.sign ? `${signText}${spec.sign}${body}` : `${signText}${body} ${spec.symbol}`;\n}\n\n/** Exact decimal string with no sign, grouping, or ticker. Good for storage. */\nexport function toDecimalString(m: Money): string {\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = (abs / scale).toString();\n if (m.decimals === 0) return `${negative ? \"-\" : \"\"}${whole}`;\n const fraction = (abs % scale).toString().padStart(m.decimals, \"0\");\n return `${negative ? \"-\" : \"\"}${whole}.${fraction}`;\n}\n\n/** Lossy on purpose. Use for display and ratios, never for balances. */\nexport function toNumber(m: Money): number {\n return Number(toDecimalString(m));\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"add\");\n return { asset: a.asset, units: a.units + b.units, decimals: a.decimals };\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"subtract\");\n return { asset: a.asset, units: a.units - b.units, decimals: a.decimals };\n}\n\nexport function negateMoney(m: Money): Money {\n return { asset: m.asset, units: -m.units, decimals: m.decimals };\n}\n\nexport function absMoney(m: Money): Money {\n return m.units < 0n ? negateMoney(m) : m;\n}\n\n/** Sum of amounts, all of which must share an asset. */\nexport function sumMoney(amounts: readonly Money[], asset?: string): Money {\n const first = amounts[0];\n if (!first) {\n if (!asset) throw new ValidationError(\"sumMoney() of an empty list needs an asset\");\n return zero(asset);\n }\n return amounts.reduce((acc, next) => addMoney(acc, next), zero(first.asset));\n}\n\n/**\n * Scale by a rational number. Ratios are taken as numerator and denominator so\n * that percentages and slippage bounds stay exact.\n */\nexport function scaleMoney(\n m: Money,\n numerator: bigint | number,\n denominator: bigint | number = 1n,\n rounding: Rounding = \"half-up\",\n): Money {\n const [n, d] = toRational(numerator, denominator);\n if (d === 0n) throw new ValidationError(\"cannot scale by a zero denominator\");\n return { asset: m.asset, units: divideRounded(m.units * n, d, rounding), decimals: m.decimals };\n}\n\n/** Split into `parts` amounts that add back up to the original, to the unit. */\nexport function splitMoney(m: Money, parts: number): Money[] {\n if (!Number.isInteger(parts) || parts < 1) {\n throw new ValidationError(`cannot split into ${parts} parts`, { parts });\n }\n const count = BigInt(parts);\n const base = m.units / count;\n let remainder = m.units - base * count;\n const step = remainder < 0n ? -1n : 1n;\n const slices: Money[] = [];\n for (let i = 0; i < parts; i++) {\n let units = base;\n if (remainder !== 0n) {\n units += step;\n remainder -= step;\n }\n slices.push({ asset: m.asset, units, decimals: m.decimals });\n }\n return slices;\n}\n\nexport function cmpMoney(a: Money, b: Money): -1 | 0 | 1 {\n assertSameAsset(a, b, \"compare\");\n if (a.units < b.units) return -1;\n if (a.units > b.units) return 1;\n return 0;\n}\n\nexport const gtMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) > 0;\nexport const gteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) >= 0;\nexport const ltMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) < 0;\nexport const lteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) <= 0;\nexport const eqMoney = (a: Money, b: Money): boolean => a.asset === b.asset && a.units === b.units;\n\nexport const isZeroMoney = (m: Money): boolean => m.units === 0n;\nexport const isNegativeMoney = (m: Money): boolean => m.units < 0n;\nexport const isPositiveMoney = (m: Money): boolean => m.units > 0n;\n\nexport function maxMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) >= 0 ? a : b;\n}\n\nexport function minMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) <= 0 ? a : b;\n}\n\n/** Move an amount to another asset's precision, rounding if it shrinks. */\nexport function convertPrecision(m: Money, asset: string, rounding: Rounding = \"half-up\"): Money {\n const spec = getAsset(asset);\n if (spec.decimals === m.decimals) return { ...m, asset: spec.symbol };\n if (spec.decimals > m.decimals) {\n const factor = 10n ** BigInt(spec.decimals - m.decimals);\n return { asset: spec.symbol, units: m.units * factor, decimals: spec.decimals };\n }\n const factor = 10n ** BigInt(m.decimals - spec.decimals);\n return {\n asset: spec.symbol,\n units: divideRounded(m.units, factor, rounding),\n decimals: spec.decimals,\n };\n}\n\nexport type Rounding = \"half-up\" | \"down\" | \"up\";\n\n/* -------------------------------------------------------------------------- */\n\nfunction assertSameAsset(a: Money, b: Money, verb: string): void {\n if (a.asset !== b.asset) {\n throw new ValidationError(`cannot ${verb} ${a.asset} and ${b.asset}`, {\n left: a.asset,\n right: b.asset,\n });\n }\n}\n\nfunction groupThousands(digits: string): string {\n return digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\nfunction decimalToUnits(text: string, decimals: number): bigint {\n const cleaned = text.replace(/,/g, \"\").trim();\n if (!/^[-+]?\\d*(\\.\\d*)?$/.test(cleaned) || cleaned === \"\" || cleaned === \".\") {\n throw new ParseError(`could not read \"${text}\" as a decimal number`, { text });\n }\n const negative = cleaned.startsWith(\"-\");\n const unsigned = cleaned.replace(/^[-+]/, \"\");\n const [wholePart = \"\", fractionPart = \"\"] = unsigned.split(\".\");\n const whole = wholePart === \"\" ? \"0\" : wholePart;\n\n if (fractionPart.length > decimals) {\n // Silently dropping precision here is how you lose a customer's money.\n const extra = fractionPart.slice(decimals).replace(/0+$/, \"\");\n if (extra.length > 0) {\n throw new ParseError(`\"${text}\" has more precision than ${decimals} decimal places allow`, {\n text,\n decimals,\n });\n }\n }\n\n const padded = fractionPart.padEnd(decimals, \"0\").slice(0, decimals);\n const units = BigInt(whole + padded);\n return negative ? -units : units;\n}\n\n/** Render a JS number without exponent notation, so parsing stays exact. */\nfunction numberToDecimalString(value: number): string {\n if (!Number.isFinite(value)) {\n throw new ParseError(`${value} is not a usable amount`, { value });\n }\n if (Number.isInteger(value)) return value.toFixed(0);\n const text = value.toString();\n if (!text.includes(\"e\") && !text.includes(\"E\")) return text;\n // Exponent form: expand with enough places to keep every significant digit.\n return value.toFixed(20).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n}\n\n/**\n * Turn a possibly fractional ratio into an exact pair of integers by shifting\n * both sides by the same power of ten. `scaleMoney(m, 0.003)` becomes 3/1000,\n * so a slippage bound never drifts.\n */\nfunction toRational(numerator: bigint | number, denominator: bigint | number): [bigint, bigint] {\n if (typeof numerator === \"bigint\" && typeof denominator === \"bigint\") {\n return [numerator, denominator];\n }\n const nText =\n typeof numerator === \"bigint\" ? numerator.toString() : numberToDecimalString(numerator);\n const dText =\n typeof denominator === \"bigint\" ? denominator.toString() : numberToDecimalString(denominator);\n const places = Math.max(decimalPlaces(nText), decimalPlaces(dText));\n return [shiftDecimal(nText, places), shiftDecimal(dText, places)];\n}\n\nfunction decimalPlaces(text: string): number {\n const dot = text.indexOf(\".\");\n return dot === -1 ? 0 : text.length - dot - 1;\n}\n\nfunction shiftDecimal(text: string, places: number): bigint {\n const negative = text.startsWith(\"-\");\n const unsigned = text.replace(/^[-+]/, \"\");\n const [whole = \"0\", fraction = \"\"] = unsigned.split(\".\");\n const value = BigInt((whole || \"0\") + fraction.padEnd(places, \"0\"));\n return negative ? -value : value;\n}\n\nfunction divideRounded(numerator: bigint, denominator: bigint, rounding: Rounding): bigint {\n if (denominator === 0n) throw new ValidationError(\"division by zero\");\n const negative = numerator < 0n !== denominator < 0n;\n const absN = numerator < 0n ? -numerator : numerator;\n const absD = denominator < 0n ? -denominator : denominator;\n const quotient = absN / absD;\n const remainder = absN % absD;\n\n let result = quotient;\n if (remainder !== 0n) {\n if (rounding === \"up\") result = quotient + 1n;\n else if (rounding === \"half-up\" && remainder * 2n >= absD) result = quotient + 1n;\n }\n return negative ? -result : result;\n}\n","import { ParseError } from \"./errors.js\";\n\n/** A window like \"30m\", \"24h\", \"7d\". Numbers are read as milliseconds. */\nexport type DurationInput = string | number;\n\nconst UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n};\n\nconst PATTERN = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/giu;\n\n/**\n * Parse a duration to milliseconds. Compound forms work too, so \"1h30m\" and\n * \"90m\" agree.\n */\nexport function parseDuration(input: DurationInput): number {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input) || input < 0) {\n throw new ParseError(`${input} is not a usable duration`, { input });\n }\n return input;\n }\n\n const text = input.trim();\n if (text === \"\") throw new ParseError(\"empty duration\", { input });\n\n let total = 0;\n let matched = 0;\n PATTERN.lastIndex = 0;\n for (const match of text.matchAll(PATTERN)) {\n const [whole, amount, unit] = match;\n total += Number(amount) * (UNITS[unit!.toLowerCase()] ?? 0);\n matched += whole.length;\n }\n\n // Guard against \"30 potatoes\" quietly parsing as 30 milliseconds.\n if (matched === 0 || matched !== text.replace(/\\s+/g, \"\").length) {\n throw new ParseError(`could not read \"${input}\" as a duration`, { input });\n }\n return total;\n}\n\n/** Render milliseconds back to the shortest readable form. */\nexport function formatDuration(ms: number): string {\n if (ms === 0) return \"0s\";\n const order: [string, number][] = [\n [\"w\", UNITS.w!],\n [\"d\", UNITS.d!],\n [\"h\", UNITS.h!],\n [\"m\", UNITS.m!],\n [\"s\", UNITS.s!],\n [\"ms\", UNITS.ms!],\n ];\n let left = Math.round(ms);\n const parts: string[] = [];\n for (const [unit, size] of order) {\n const count = Math.floor(left / size);\n if (count > 0) {\n parts.push(`${count}${unit}`);\n left -= count * size;\n }\n }\n return parts.join(\"\");\n}\n","import { parseDuration, type DurationInput } from \"./duration.js\";\n\n/**\n * Time is injected everywhere it matters. Rolling budgets, velocity limits, and\n * TWAP schedules all read the clock, and a test suite that has to wait thirty\n * real minutes to check a thirty minute order is a test suite nobody runs.\n */\nexport interface Clock {\n now(): number;\n sleep(ms: number): Promise<void>;\n}\n\nexport const systemClock: Clock = {\n now: () => Date.now(),\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface ManualClock extends Clock {\n /** Move time forward and resolve anything sleeping through that window. */\n advance(by: DurationInput): Promise<void>;\n set(time: number): void;\n}\n\n/** A clock you drive by hand. Sleeps resolve the moment time passes them. */\nexport function manualClock(start: number | Date = 0): ManualClock {\n let current = start instanceof Date ? start.getTime() : start;\n let waiters: { at: number; resolve: () => void }[] = [];\n\n /** Resolve everything due now. Returns whether anything was woken. */\n const wakeDue = (): boolean => {\n const due = waiters.filter((w) => w.at <= current).sort((a, b) => a.at - b.at);\n if (due.length === 0) return false;\n waiters = waiters.filter((w) => w.at > current);\n for (const waiter of due) waiter.resolve();\n return true;\n };\n\n /** Yield past the microtask queue so woken tasks actually get to run. */\n const settleQueue = (): Promise<void> =>\n new Promise((resolve) => {\n setImmediate(resolve);\n });\n\n return {\n now: () => current,\n\n sleep(ms) {\n if (ms <= 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n waiters.push({ at: current + ms, resolve });\n });\n },\n\n /**\n * Move time forward and let everything that was waiting run to a stop.\n *\n * A woken task usually schedules another sleep, and with a big enough jump\n * that new sleep can already be due. One pass is not enough, so this drains\n * repeatedly until no waiter is left in the past.\n */\n async advance(by) {\n current += parseDuration(by);\n for (let pass = 0; pass < 10_000; pass++) {\n await settleQueue();\n if (!wakeDue()) return;\n }\n throw new Error(\n \"manualClock.advance() never settled: a sleep loop is scheduling work faster than time passes\",\n );\n },\n\n set(time) {\n current = time;\n wakeDue();\n },\n };\n}\n","import { randomBytes, createHash } from \"node:crypto\";\n\nconst ALPHABET = \"0123456789abcdefghjkmnpqrstvwxyz\"; // Crockford base32, no look-alikes.\n\n/**\n * A prefixed, URL safe id. The prefix survives into logs and error messages,\n * which is the whole point: `wlt_` and `pol_` should never be confusable.\n */\nexport function id(prefix: string, bytes = 12): string {\n return `${prefix}_${encode(randomBytes(bytes))}`;\n}\n\nfunction encode(buffer: Buffer): string {\n let bits = 0;\n let value = 0;\n let out = \"\";\n for (const byte of buffer) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += ALPHABET[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n }\n if (bits > 0) out += ALPHABET[(value << (5 - bits)) & 31];\n return out;\n}\n\n/**\n * A short, stable fingerprint of any JSON-shaped value. Object keys are sorted\n * before hashing, so two policies that differ only in key order produce the\n * same version.\n */\nexport function fingerprint(value: unknown, length = 10): string {\n const hash = createHash(\"sha256\").update(stableStringify(value)).digest(\"hex\");\n return hash.slice(0, length);\n}\n\nexport function stableStringify(value: unknown): string {\n // bigint first: JSON.stringify throws on it, and amounts are bigints here.\n if (typeof value === \"bigint\") return `\"${value.toString()}\"`;\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`);\n return `{${entries.join(\",\")}}`;\n}\n","import { getAsset } from \"./assets.js\";\nimport { ValidationError } from \"./errors.js\";\nimport { convertPrecision, money, scaleMoney, type Money } from \"./money.js\";\n\n/**\n * Limits are written in dollars, but agents move ETH, USDC, and tokenized\n * equities. Something has to price one in terms of the other, and it should be\n * yours to choose, so it is an interface rather than a hardcoded feed.\n */\nexport interface PriceSource {\n /** USD value of one whole unit of `asset`. */\n usdPrice(asset: string): Promise<number>;\n}\n\n/** Knows only that dollar-pegged assets are worth a dollar. Refuses the rest. */\nexport const peggedPrices: PriceSource = {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n if (spec.usdPegged) return 1;\n throw new ValidationError(\n `no USD price for ${spec.symbol}. Pass a PriceSource that covers it.`,\n { asset: spec.symbol },\n );\n },\n};\n\n/** A fixed price table. Useful for tests, backtests, and offline policy runs. */\nexport function fixedPrices(table: Record<string, number>): PriceSource {\n const normalized = new Map(\n Object.entries(table).map(([asset, price]) => [asset.toUpperCase(), price]),\n );\n return {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n const price = normalized.get(spec.symbol);\n if (price !== undefined) return price;\n if (spec.usdPegged) return 1;\n throw new ValidationError(`no USD price for ${spec.symbol}`, { asset: spec.symbol });\n },\n };\n}\n\n/** Wraps another source with a short time-to-live cache. */\nexport function cachedPrices(\n source: PriceSource,\n options: { ttlMs?: number; now?: () => number } = {},\n): PriceSource {\n const ttl = options.ttlMs ?? 5_000;\n const now = options.now ?? Date.now;\n const cache = new Map<string, { price: number; at: number }>();\n return {\n async usdPrice(asset) {\n const key = asset.toUpperCase();\n const hit = cache.get(key);\n if (hit && now() - hit.at < ttl) return hit.price;\n const price = await source.usdPrice(key);\n cache.set(key, { price, at: now() });\n return price;\n },\n };\n}\n\n/** Value an amount in USD. Dollar-pegged assets skip the price lookup. */\nexport async function valueInUsd(amount: Money, prices: PriceSource): Promise<Money> {\n const spec = getAsset(amount.asset);\n if (spec.symbol === \"USD\") return amount;\n if (spec.usdPegged) return convertPrecision(amount, \"USD\");\n\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price < 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n // Scale in the asset's own precision first, then step down to cents once.\n const scaled = scaleMoney(amount, ...ratioFrom(price));\n return convertPrecision(scaled, \"USD\");\n}\n\n/** Convert a USD amount into whole units of another asset. */\nexport async function valueFromUsd(usd: Money, asset: string, prices: PriceSource): Promise<Money> {\n const spec = getAsset(asset);\n if (spec.symbol === \"USD\") return usd;\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price <= 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n const [numerator, denominator] = ratioFrom(price);\n const widened = convertPrecision(usd, spec.symbol);\n return scaleMoney(widened, denominator, numerator);\n}\n\n/** Express a float price as an exact numerator and denominator. */\nfunction ratioFrom(price: number): [bigint, bigint] {\n const text = price.toString();\n if (text.includes(\"e\") || text.includes(\"E\")) {\n const fixed = price.toFixed(12).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n return ratioFromDecimalString(fixed);\n }\n return ratioFromDecimalString(text);\n}\n\nfunction ratioFromDecimalString(text: string): [bigint, bigint] {\n const dot = text.indexOf(\".\");\n if (dot === -1) return [BigInt(text), 1n];\n const places = text.length - dot - 1;\n return [BigInt(text.replace(\".\", \"\")), 10n ** BigInt(places)];\n}\n\n/** Convenience for the common case of stating a dollar figure. */\nexport function usd(amount: string | number | bigint): Money {\n return money(amount, \"USD\");\n}\n"],"mappings":"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","names":["factor","usd"]} | ||
| {"version":3,"sources":["../src/errors.ts","../src/assets.ts","../src/money.ts","../src/duration.ts","../src/clock.ts","../src/ids.ts","../src/prices.ts"],"sourcesContent":["/**\n * Every error thrown by a Moneo package carries a stable `code`. Agents act on\n * codes, not on message text, so messages stay free to change.\n */\nexport class MoneoError extends Error {\n readonly code: string;\n readonly details: Record<string, unknown>;\n\n constructor(code: string, message: string, details: Record<string, unknown> = {}) {\n super(message);\n this.name = \"MoneoError\";\n this.code = code;\n this.details = details;\n }\n}\n\n/** Input could not be understood: a malformed amount, duration, or policy. */\nexport class ParseError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"parse_error\", message, details);\n this.name = \"ParseError\";\n }\n}\n\n/** Input was understood but is not usable: mismatched assets, negative caps. */\nexport class ValidationError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"validation_error\", message, details);\n this.name = \"ValidationError\";\n }\n}\n\n/** The operation is legal but the funds are not there. */\nexport class InsufficientFundsError extends MoneoError {\n constructor(message: string, details: Record<string, unknown> = {}) {\n super(\"insufficient_funds\", message, details);\n this.name = \"InsufficientFundsError\";\n }\n}\n","import { ValidationError } from \"./errors.js\";\n\nexport interface AssetSpec {\n /** Canonical ticker, uppercase. */\n readonly symbol: string;\n /** Number of minor units per whole unit, as a power of ten. */\n readonly decimals: number;\n /** Currency sign used when formatting, if the asset has one. */\n readonly sign?: string;\n /** True for assets pegged 1:1 to the US dollar. */\n readonly usdPegged?: boolean;\n}\n\n/**\n * What an agent on Robinhood Chain actually holds: fiat for stating limits,\n * USDG for settlement, and ETH because gas is still ETH. Tokenized equities\n * are not listed here because the list changes; register the ones you trade\n * with `registerAsset({ symbol: \"AAPL\", decimals: 18 })`.\n */\nconst BUILT_IN: AssetSpec[] = [\n { symbol: \"USD\", decimals: 2, sign: \"$\", usdPegged: true },\n { symbol: \"EUR\", decimals: 2, sign: \"€\" },\n { symbol: \"GBP\", decimals: 2, sign: \"£\" },\n { symbol: \"USDG\", decimals: 6, usdPegged: true },\n { symbol: \"USDC\", decimals: 6, usdPegged: true },\n { symbol: \"ETH\", decimals: 18 },\n];\n\nconst registry = new Map<string, AssetSpec>(BUILT_IN.map((a) => [a.symbol, a]));\n\n/** Signs that unambiguously identify an asset when parsing a string. */\nconst signIndex = new Map<string, string>(\n BUILT_IN.filter((a) => a.sign).map((a) => [a.sign as string, a.symbol]),\n);\n\n/**\n * Teach the SDK about an asset it does not ship with. Tokenized equities and\n * house currencies are the common cases.\n */\nexport function registerAsset(spec: AssetSpec): AssetSpec {\n const symbol = spec.symbol.toUpperCase();\n if (!Number.isInteger(spec.decimals) || spec.decimals < 0 || spec.decimals > 30) {\n throw new ValidationError(`asset ${symbol} needs decimals between 0 and 30`, {\n decimals: spec.decimals,\n });\n }\n const normalized: AssetSpec = { ...spec, symbol };\n registry.set(symbol, normalized);\n if (normalized.sign && !signIndex.has(normalized.sign)) {\n signIndex.set(normalized.sign, symbol);\n }\n return normalized;\n}\n\nexport function getAsset(symbol: string): AssetSpec {\n const spec = registry.get(symbol.toUpperCase());\n if (!spec) {\n throw new ValidationError(`unknown asset \"${symbol}\". Call registerAsset() to add it.`, {\n symbol,\n });\n }\n return spec;\n}\n\nexport function hasAsset(symbol: string): boolean {\n return registry.has(symbol.toUpperCase());\n}\n\nexport function assetForSign(sign: string): string | undefined {\n return signIndex.get(sign);\n}\n\nexport function knownAssets(): AssetSpec[] {\n return [...registry.values()];\n}\n","import { assetForSign, getAsset } from \"./assets.js\";\nimport { ParseError, ValidationError } from \"./errors.js\";\n\n/**\n * An exact amount of one asset.\n *\n * Amounts are stored as integer minor units in a bigint, never as a float. A\n * budget that drifts by a fraction of a cent every time it is checked is a\n * budget that eventually lets something through, so there is no floating point\n * anywhere in the arithmetic below.\n */\nexport interface Money {\n readonly asset: string;\n readonly units: bigint;\n readonly decimals: number;\n}\n\n/** Anything the SDKs will accept where an amount is expected. */\nexport type MoneyInput = Money | string | number | bigint;\n\nconst AMOUNT_PATTERN =\n /^\\s*(?<sign>[-+])?\\s*(?<symbolPrefix>[^\\d\\s.,+-]+)?\\s*(?<digits>[\\d,]*(?:\\.\\d+)?)\\s*(?<symbolSuffix>[A-Za-z][A-Za-z0-9]*)?\\s*$/u;\n\nexport function isMoney(value: unknown): value is Money {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as Money).asset === \"string\" &&\n typeof (value as Money).units === \"bigint\" &&\n typeof (value as Money).decimals === \"number\"\n );\n}\n\n/** Build a Money from a whole-unit decimal string or number. */\nexport function money(amount: string | number | bigint, asset: string): Money {\n const spec = getAsset(asset);\n if (typeof amount === \"bigint\") {\n return { asset: spec.symbol, units: amount, decimals: spec.decimals };\n }\n const text = typeof amount === \"number\" ? numberToDecimalString(amount) : amount.trim();\n return {\n asset: spec.symbol,\n units: decimalToUnits(text, spec.decimals),\n decimals: spec.decimals,\n };\n}\n\n/** Build a Money directly from minor units, skipping decimal parsing. */\nexport function fromUnits(units: bigint, asset: string): Money {\n const spec = getAsset(asset);\n return { asset: spec.symbol, units, decimals: spec.decimals };\n}\n\nexport function zero(asset: string): Money {\n return fromUnits(0n, asset);\n}\n\n/**\n * Parse any accepted amount form. Strings may carry the asset themselves,\n * either as a sign (\"$250.00\") or a ticker (\"0.0277 ETH\"). Bare numbers need\n * `defaultAsset`.\n */\nexport function parseMoney(input: MoneyInput, defaultAsset?: string): Money {\n if (isMoney(input)) return input;\n if (typeof input === \"bigint\" || typeof input === \"number\") {\n if (!defaultAsset) {\n throw new ParseError('a bare number needs an asset: parseMoney(250, \"USD\")', { input });\n }\n return money(input, defaultAsset);\n }\n\n const match = AMOUNT_PATTERN.exec(input);\n if (!match?.groups) {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n const { sign, symbolPrefix, digits, symbolSuffix } = match.groups;\n if (!digits || digits === \".\" || digits === \"\") {\n throw new ParseError(`could not read \"${input}\" as an amount`, { input });\n }\n\n let asset = defaultAsset;\n if (symbolPrefix) {\n const bySign = assetForSign(symbolPrefix);\n if (!bySign) {\n throw new ParseError(`unknown currency sign \"${symbolPrefix}\" in \"${input}\"`, { input });\n }\n asset = bySign;\n }\n if (symbolSuffix) asset = symbolSuffix;\n if (!asset) {\n throw new ParseError(`\"${input}\" does not name an asset and no default was given`, { input });\n }\n\n const spec = getAsset(asset);\n const units = decimalToUnits(digits.replace(/,/g, \"\"), spec.decimals);\n return {\n asset: spec.symbol,\n units: sign === \"-\" ? -units : units,\n decimals: spec.decimals,\n };\n}\n\n/**\n * Render for humans. Uses the asset sign when it has one, otherwise appends the\n * ticker. Trailing zeros are kept for currencies and trimmed for high precision\n * assets, where eighteen zeros help nobody.\n */\nexport function formatMoney(input: Money, options: { compact?: boolean } = {}): string {\n const m = input;\n const spec = getAsset(m.asset);\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = abs / scale;\n const fraction = abs % scale;\n\n let fractionText = m.decimals > 0 ? fraction.toString().padStart(m.decimals, \"0\") : \"\";\n if (options.compact !== false && m.decimals > 2) {\n // Keep at least two places so small currency-like values stay readable.\n fractionText = fractionText.replace(/0+$/, \"\");\n if (fractionText.length < 2) fractionText = fractionText.padEnd(2, \"0\");\n }\n\n const wholeText = groupThousands(whole.toString());\n const body = fractionText ? `${wholeText}.${fractionText}` : wholeText;\n const signText = negative ? \"-\" : \"\";\n\n return spec.sign ? `${signText}${spec.sign}${body}` : `${signText}${body} ${spec.symbol}`;\n}\n\n/** Exact decimal string with no sign, grouping, or ticker. Good for storage. */\nexport function toDecimalString(m: Money): string {\n const negative = m.units < 0n;\n const abs = negative ? -m.units : m.units;\n const scale = 10n ** BigInt(m.decimals);\n const whole = (abs / scale).toString();\n if (m.decimals === 0) return `${negative ? \"-\" : \"\"}${whole}`;\n const fraction = (abs % scale).toString().padStart(m.decimals, \"0\");\n return `${negative ? \"-\" : \"\"}${whole}.${fraction}`;\n}\n\n/** Lossy on purpose. Use for display and ratios, never for balances. */\nexport function toNumber(m: Money): number {\n return Number(toDecimalString(m));\n}\n\nexport function addMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"add\");\n return { asset: a.asset, units: a.units + b.units, decimals: a.decimals };\n}\n\nexport function subMoney(a: Money, b: Money): Money {\n assertSameAsset(a, b, \"subtract\");\n return { asset: a.asset, units: a.units - b.units, decimals: a.decimals };\n}\n\nexport function negateMoney(m: Money): Money {\n return { asset: m.asset, units: -m.units, decimals: m.decimals };\n}\n\nexport function absMoney(m: Money): Money {\n return m.units < 0n ? negateMoney(m) : m;\n}\n\n/** Sum of amounts, all of which must share an asset. */\nexport function sumMoney(amounts: readonly Money[], asset?: string): Money {\n const first = amounts[0];\n if (!first) {\n if (!asset) throw new ValidationError(\"sumMoney() of an empty list needs an asset\");\n return zero(asset);\n }\n return amounts.reduce((acc, next) => addMoney(acc, next), zero(first.asset));\n}\n\n/**\n * Scale by a rational number. Ratios are taken as numerator and denominator so\n * that percentages and slippage bounds stay exact.\n */\nexport function scaleMoney(\n m: Money,\n numerator: bigint | number,\n denominator: bigint | number = 1n,\n rounding: Rounding = \"half-up\",\n): Money {\n const [n, d] = toRational(numerator, denominator);\n if (d === 0n) throw new ValidationError(\"cannot scale by a zero denominator\");\n return { asset: m.asset, units: divideRounded(m.units * n, d, rounding), decimals: m.decimals };\n}\n\n/** Split into `parts` amounts that add back up to the original, to the unit. */\nexport function splitMoney(m: Money, parts: number): Money[] {\n if (!Number.isInteger(parts) || parts < 1) {\n throw new ValidationError(`cannot split into ${parts} parts`, { parts });\n }\n const count = BigInt(parts);\n const base = m.units / count;\n let remainder = m.units - base * count;\n const step = remainder < 0n ? -1n : 1n;\n const slices: Money[] = [];\n for (let i = 0; i < parts; i++) {\n let units = base;\n if (remainder !== 0n) {\n units += step;\n remainder -= step;\n }\n slices.push({ asset: m.asset, units, decimals: m.decimals });\n }\n return slices;\n}\n\nexport function cmpMoney(a: Money, b: Money): -1 | 0 | 1 {\n assertSameAsset(a, b, \"compare\");\n if (a.units < b.units) return -1;\n if (a.units > b.units) return 1;\n return 0;\n}\n\nexport const gtMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) > 0;\nexport const gteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) >= 0;\nexport const ltMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) < 0;\nexport const lteMoney = (a: Money, b: Money): boolean => cmpMoney(a, b) <= 0;\nexport const eqMoney = (a: Money, b: Money): boolean => a.asset === b.asset && a.units === b.units;\n\nexport const isZeroMoney = (m: Money): boolean => m.units === 0n;\nexport const isNegativeMoney = (m: Money): boolean => m.units < 0n;\nexport const isPositiveMoney = (m: Money): boolean => m.units > 0n;\n\nexport function maxMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) >= 0 ? a : b;\n}\n\nexport function minMoney(a: Money, b: Money): Money {\n return cmpMoney(a, b) <= 0 ? a : b;\n}\n\n/** Move an amount to another asset's precision, rounding if it shrinks. */\nexport function convertPrecision(m: Money, asset: string, rounding: Rounding = \"half-up\"): Money {\n const spec = getAsset(asset);\n if (spec.decimals === m.decimals) return { ...m, asset: spec.symbol };\n if (spec.decimals > m.decimals) {\n const factor = 10n ** BigInt(spec.decimals - m.decimals);\n return { asset: spec.symbol, units: m.units * factor, decimals: spec.decimals };\n }\n const factor = 10n ** BigInt(m.decimals - spec.decimals);\n return {\n asset: spec.symbol,\n units: divideRounded(m.units, factor, rounding),\n decimals: spec.decimals,\n };\n}\n\nexport type Rounding = \"half-up\" | \"down\" | \"up\";\n\n/* -------------------------------------------------------------------------- */\n\nfunction assertSameAsset(a: Money, b: Money, verb: string): void {\n if (a.asset !== b.asset) {\n throw new ValidationError(`cannot ${verb} ${a.asset} and ${b.asset}`, {\n left: a.asset,\n right: b.asset,\n });\n }\n}\n\nfunction groupThousands(digits: string): string {\n return digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\nfunction decimalToUnits(text: string, decimals: number): bigint {\n const cleaned = text.replace(/,/g, \"\").trim();\n if (!/^[-+]?\\d*(\\.\\d*)?$/.test(cleaned) || cleaned === \"\" || cleaned === \".\") {\n throw new ParseError(`could not read \"${text}\" as a decimal number`, { text });\n }\n const negative = cleaned.startsWith(\"-\");\n const unsigned = cleaned.replace(/^[-+]/, \"\");\n const [wholePart = \"\", fractionPart = \"\"] = unsigned.split(\".\");\n const whole = wholePart === \"\" ? \"0\" : wholePart;\n\n if (fractionPart.length > decimals) {\n // Silently dropping precision here is how you lose a customer's money.\n const extra = fractionPart.slice(decimals).replace(/0+$/, \"\");\n if (extra.length > 0) {\n throw new ParseError(`\"${text}\" has more precision than ${decimals} decimal places allow`, {\n text,\n decimals,\n });\n }\n }\n\n const padded = fractionPart.padEnd(decimals, \"0\").slice(0, decimals);\n const units = BigInt(whole + padded);\n return negative ? -units : units;\n}\n\n/** Render a JS number without exponent notation, so parsing stays exact. */\nfunction numberToDecimalString(value: number): string {\n if (!Number.isFinite(value)) {\n throw new ParseError(`${value} is not a usable amount`, { value });\n }\n if (Number.isInteger(value)) return value.toFixed(0);\n const text = value.toString();\n if (!text.includes(\"e\") && !text.includes(\"E\")) return text;\n // Exponent form: expand with enough places to keep every significant digit.\n return value.toFixed(20).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n}\n\n/**\n * Turn a possibly fractional ratio into an exact pair of integers by shifting\n * both sides by the same power of ten. `scaleMoney(m, 0.003)` becomes 3/1000,\n * so a slippage bound never drifts.\n */\nfunction toRational(numerator: bigint | number, denominator: bigint | number): [bigint, bigint] {\n if (typeof numerator === \"bigint\" && typeof denominator === \"bigint\") {\n return [numerator, denominator];\n }\n const nText =\n typeof numerator === \"bigint\" ? numerator.toString() : numberToDecimalString(numerator);\n const dText =\n typeof denominator === \"bigint\" ? denominator.toString() : numberToDecimalString(denominator);\n const places = Math.max(decimalPlaces(nText), decimalPlaces(dText));\n return [shiftDecimal(nText, places), shiftDecimal(dText, places)];\n}\n\nfunction decimalPlaces(text: string): number {\n const dot = text.indexOf(\".\");\n return dot === -1 ? 0 : text.length - dot - 1;\n}\n\nfunction shiftDecimal(text: string, places: number): bigint {\n const negative = text.startsWith(\"-\");\n const unsigned = text.replace(/^[-+]/, \"\");\n const [whole = \"0\", fraction = \"\"] = unsigned.split(\".\");\n const value = BigInt((whole || \"0\") + fraction.padEnd(places, \"0\"));\n return negative ? -value : value;\n}\n\nfunction divideRounded(numerator: bigint, denominator: bigint, rounding: Rounding): bigint {\n if (denominator === 0n) throw new ValidationError(\"division by zero\");\n const negative = numerator < 0n !== denominator < 0n;\n const absN = numerator < 0n ? -numerator : numerator;\n const absD = denominator < 0n ? -denominator : denominator;\n const quotient = absN / absD;\n const remainder = absN % absD;\n\n let result = quotient;\n if (remainder !== 0n) {\n if (rounding === \"up\") result = quotient + 1n;\n else if (rounding === \"half-up\" && remainder * 2n >= absD) result = quotient + 1n;\n }\n return negative ? -result : result;\n}\n","import { ParseError } from \"./errors.js\";\n\n/** A window like \"30m\", \"24h\", \"7d\". Numbers are read as milliseconds. */\nexport type DurationInput = string | number;\n\nconst UNITS: Record<string, number> = {\n ms: 1,\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n w: 604_800_000,\n};\n\nconst PATTERN = /(\\d+(?:\\.\\d+)?)\\s*(ms|s|m|h|d|w)/giu;\n\n/**\n * Parse a duration to milliseconds. Compound forms work too, so \"1h30m\" and\n * \"90m\" agree.\n */\nexport function parseDuration(input: DurationInput): number {\n if (typeof input === \"number\") {\n if (!Number.isFinite(input) || input < 0) {\n throw new ParseError(`${input} is not a usable duration`, { input });\n }\n return input;\n }\n\n const text = input.trim();\n if (text === \"\") throw new ParseError(\"empty duration\", { input });\n\n let total = 0;\n let matched = 0;\n PATTERN.lastIndex = 0;\n for (const match of text.matchAll(PATTERN)) {\n const [whole, amount, unit] = match;\n total += Number(amount) * (UNITS[unit!.toLowerCase()] ?? 0);\n matched += whole.length;\n }\n\n // Guard against \"30 potatoes\" quietly parsing as 30 milliseconds.\n if (matched === 0 || matched !== text.replace(/\\s+/g, \"\").length) {\n throw new ParseError(`could not read \"${input}\" as a duration`, { input });\n }\n return total;\n}\n\n/** Render milliseconds back to the shortest readable form. */\nexport function formatDuration(ms: number): string {\n if (ms === 0) return \"0s\";\n const order: [string, number][] = [\n [\"w\", UNITS.w!],\n [\"d\", UNITS.d!],\n [\"h\", UNITS.h!],\n [\"m\", UNITS.m!],\n [\"s\", UNITS.s!],\n [\"ms\", UNITS.ms!],\n ];\n let left = Math.round(ms);\n const parts: string[] = [];\n for (const [unit, size] of order) {\n const count = Math.floor(left / size);\n if (count > 0) {\n parts.push(`${count}${unit}`);\n left -= count * size;\n }\n }\n return parts.join(\"\");\n}\n","import { parseDuration, type DurationInput } from \"./duration.js\";\n\n/**\n * Time is injected everywhere it matters. Rolling budgets, velocity limits, and\n * TWAP schedules all read the clock, and a test suite that has to wait thirty\n * real minutes to check a thirty minute order is a test suite nobody runs.\n */\nexport interface Clock {\n now(): number;\n sleep(ms: number): Promise<void>;\n}\n\nexport const systemClock: Clock = {\n now: () => Date.now(),\n sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n};\n\nexport interface ManualClock extends Clock {\n /** Move time forward and resolve anything sleeping through that window. */\n advance(by: DurationInput): Promise<void>;\n set(time: number): void;\n}\n\n/** A clock you drive by hand. Sleeps resolve the moment time passes them. */\nexport function manualClock(start: number | Date = 0): ManualClock {\n let current = start instanceof Date ? start.getTime() : start;\n let waiters: { at: number; resolve: () => void }[] = [];\n\n /** Resolve everything due now. Returns whether anything was woken. */\n const wakeDue = (): boolean => {\n const due = waiters.filter((w) => w.at <= current).sort((a, b) => a.at - b.at);\n if (due.length === 0) return false;\n waiters = waiters.filter((w) => w.at > current);\n for (const waiter of due) waiter.resolve();\n return true;\n };\n\n /** Yield past the microtask queue so woken tasks actually get to run. */\n const settleQueue = (): Promise<void> =>\n new Promise((resolve) => {\n setImmediate(resolve);\n });\n\n return {\n now: () => current,\n\n sleep(ms) {\n if (ms <= 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n waiters.push({ at: current + ms, resolve });\n });\n },\n\n /**\n * Move time forward and let everything that was waiting run to a stop.\n *\n * A woken task usually schedules another sleep, and with a big enough jump\n * that new sleep can already be due. One pass is not enough, so this drains\n * repeatedly until no waiter is left in the past.\n */\n async advance(by) {\n current += parseDuration(by);\n for (let pass = 0; pass < 10_000; pass++) {\n await settleQueue();\n if (!wakeDue()) return;\n }\n throw new Error(\n \"manualClock.advance() never settled: a sleep loop is scheduling work faster than time passes\",\n );\n },\n\n set(time) {\n current = time;\n wakeDue();\n },\n };\n}\n","import { randomBytes, createHash } from \"node:crypto\";\n\nconst ALPHABET = \"0123456789abcdefghjkmnpqrstvwxyz\"; // Crockford base32, no look-alikes.\n\n/**\n * A prefixed, URL safe id. The prefix survives into logs and error messages,\n * which is the whole point: `wlt_` and `pol_` should never be confusable.\n */\nexport function id(prefix: string, bytes = 12): string {\n return `${prefix}_${encode(randomBytes(bytes))}`;\n}\n\nfunction encode(buffer: Buffer): string {\n let bits = 0;\n let value = 0;\n let out = \"\";\n for (const byte of buffer) {\n value = (value << 8) | byte;\n bits += 8;\n while (bits >= 5) {\n out += ALPHABET[(value >>> (bits - 5)) & 31];\n bits -= 5;\n }\n }\n if (bits > 0) out += ALPHABET[(value << (5 - bits)) & 31];\n return out;\n}\n\n/**\n * A short, stable fingerprint of any JSON-shaped value. Object keys are sorted\n * before hashing, so two policies that differ only in key order produce the\n * same version.\n */\nexport function fingerprint(value: unknown, length = 10): string {\n const hash = createHash(\"sha256\").update(stableStringify(value)).digest(\"hex\");\n return hash.slice(0, length);\n}\n\nexport function stableStringify(value: unknown): string {\n // bigint first: JSON.stringify throws on it, and amounts are bigints here.\n if (typeof value === \"bigint\") return `\"${value.toString()}\"`;\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`);\n return `{${entries.join(\",\")}}`;\n}\n","import { getAsset } from \"./assets.js\";\nimport { ValidationError } from \"./errors.js\";\nimport { convertPrecision, money, scaleMoney, type Money } from \"./money.js\";\n\n/**\n * Limits are written in dollars, but agents move ETH, USDC, and tokenized\n * equities. Something has to price one in terms of the other, and it should be\n * yours to choose, so it is an interface rather than a hardcoded feed.\n */\nexport interface PriceSource {\n /** USD value of one whole unit of `asset`. */\n usdPrice(asset: string): Promise<number>;\n}\n\n/** Knows only that dollar-pegged assets are worth a dollar. Refuses the rest. */\nexport const peggedPrices: PriceSource = {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n if (spec.usdPegged) return 1;\n throw new ValidationError(\n `no USD price for ${spec.symbol}. Pass a PriceSource that covers it.`,\n { asset: spec.symbol },\n );\n },\n};\n\n/** A fixed price table. Useful for tests, backtests, and offline policy runs. */\nexport function fixedPrices(table: Record<string, number>): PriceSource {\n const normalized = new Map(\n Object.entries(table).map(([asset, price]) => [asset.toUpperCase(), price]),\n );\n return {\n async usdPrice(asset) {\n const spec = getAsset(asset);\n const price = normalized.get(spec.symbol);\n if (price !== undefined) return price;\n if (spec.usdPegged) return 1;\n throw new ValidationError(`no USD price for ${spec.symbol}`, { asset: spec.symbol });\n },\n };\n}\n\n/** Wraps another source with a short time-to-live cache. */\nexport function cachedPrices(\n source: PriceSource,\n options: { ttlMs?: number; now?: () => number } = {},\n): PriceSource {\n const ttl = options.ttlMs ?? 5_000;\n const now = options.now ?? Date.now;\n const cache = new Map<string, { price: number; at: number }>();\n return {\n async usdPrice(asset) {\n const key = asset.toUpperCase();\n const hit = cache.get(key);\n if (hit && now() - hit.at < ttl) return hit.price;\n const price = await source.usdPrice(key);\n cache.set(key, { price, at: now() });\n return price;\n },\n };\n}\n\n/** Value an amount in USD. Dollar-pegged assets skip the price lookup. */\nexport async function valueInUsd(amount: Money, prices: PriceSource): Promise<Money> {\n const spec = getAsset(amount.asset);\n if (spec.symbol === \"USD\") return amount;\n if (spec.usdPegged) return convertPrecision(amount, \"USD\");\n\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price < 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n // Scale in the asset's own precision first, then step down to cents once.\n const scaled = scaleMoney(amount, ...ratioFrom(price));\n return convertPrecision(scaled, \"USD\");\n}\n\n/** Convert a USD amount into whole units of another asset. */\nexport async function valueFromUsd(usd: Money, asset: string, prices: PriceSource): Promise<Money> {\n const spec = getAsset(asset);\n if (spec.symbol === \"USD\") return usd;\n const price = await prices.usdPrice(spec.symbol);\n if (!Number.isFinite(price) || price <= 0) {\n throw new ValidationError(`price source returned ${price} for ${spec.symbol}`, {\n asset: spec.symbol,\n price,\n });\n }\n const [numerator, denominator] = ratioFrom(price);\n const widened = convertPrecision(usd, spec.symbol);\n return scaleMoney(widened, denominator, numerator);\n}\n\n/** Express a float price as an exact numerator and denominator. */\nfunction ratioFrom(price: number): [bigint, bigint] {\n const text = price.toString();\n if (text.includes(\"e\") || text.includes(\"E\")) {\n const fixed = price.toFixed(12).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n return ratioFromDecimalString(fixed);\n }\n return ratioFromDecimalString(text);\n}\n\nfunction ratioFromDecimalString(text: string): [bigint, bigint] {\n const dot = text.indexOf(\".\");\n if (dot === -1) return [BigInt(text), 1n];\n const places = text.length - dot - 1;\n return [BigInt(text.replace(\".\", \"\")), 10n ** BigInt(places)];\n}\n\n/** Convenience for the common case of stating a dollar figure. */\nexport function usd(amount: string | number | bigint): Money {\n return money(amount, \"USD\");\n}\n"],"mappings":"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","names":["factor","usd"]} |
+1
-1
| { | ||
| "name": "@moneolabs/core", | ||
| "version": "0.2.1", | ||
| "version": "0.3.0", | ||
| "description": "Shared primitives for the Moneo SDKs: exact money arithmetic, assets, durations, clocks, and ids.", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
+4
-4
@@ -53,4 +53,4 @@ # @moneolabs/core | ||
| ```ts | ||
| registerAsset({ symbol: "AAPLX", decimals: 18 }); | ||
| parseMoney("10.5 AAPLX"); | ||
| registerAsset({ symbol: "AAPL", decimals: 18 }); | ||
| parseMoney("10.5 AAPL"); | ||
| ``` | ||
@@ -76,5 +76,5 @@ | ||
| ```ts | ||
| await valueInUsd(parseMoney("2 AAPLX"), fixedPrices({ AAPLX: 228.41 })); // $456.82 | ||
| await valueInUsd(parseMoney("2 AAPL"), fixedPrices({ AAPL: 309.92 })); // $619.84 | ||
| await valueInUsd(parseMoney("100 USDG"), peggedPrices); // $100.00, no lookup | ||
| await valueInUsd(parseMoney("1 AAPLX"), peggedPrices); // throws | ||
| await valueInUsd(parseMoney("1 AAPL"), peggedPrices); // throws | ||
| ``` | ||
@@ -81,0 +81,0 @@ |
150900
0