import { InvalidInputError } from "./errors.js"; import type { OutcomeBalances, PortfolioTrade } from "./binary/portfolio.js"; import type { BinaryMarket } from "./markets.js"; import type { FillRow } from "./fills.js"; /** * Raw integer (string or bigint) → human number for DISPLAY ONLY. Lossy past * ~15 significant digits (it's an IEEE double) — never round-trip money through * this; use it for labels/charts. `decimals` defaults to the demo's 6 (tUSDC), * but pass the market's real `quoteDecimals`/`baseDecimals` when you have them. * * @category converting */ export declare function toHuman(raw: bigint | string, decimals?: number): number; /** * Raw integer → exact human STRING (no precision loss). For display where you * want the full value, or to feed another decimal library. * * @category converting */ export declare function toHumanString(raw: bigint | string, decimals?: number): string; /** * Human amount (number or decimal string) → raw bigint at `decimals`, for the * write API (`placeOrder` price/quantity, `mintSet` amount, …). Accepts a string * to avoid float rounding (`fromHuman("0.1")`), or a number for convenience. * * **Gotchas** * * A number carrying more fraction digits than `decimals` is ROUNDED to scale * (half-away-from-zero, via `toFixed`): `fromHuman(0.1234567, 6)` is * `123457n`. That is a deliberate, long-standing choice — the alternative * (throwing) would reject ordinary computed values like a mid price. Pass a * string when you need the exact value preserved or rejected instead. * * @category converting */ export declare function fromHuman(human: number | string, decimals?: number): bigint; /** * A JS number → a plain decimal string at most `decimals` places wide, which is * what `parseUnits` accepts (it rejects "1e-7" exponent notation and mantissas * longer than `decimals`). * * Use this for every number → raw conversion in the SDK, so the write path has * ONE definition of "the value the caller typed". `toFixed(decimals)` alone is * not that: it prints the float's binary expansion rather than the number the * caller wrote, so at 18 decimals `(0.05).toFixed(18)` is * "0.050000000000000003". Three wei is enough to miss a 1e15 tick and have the * pool reject the order with `InvalidPrice` — so on an 18-decimal venue almost * every ordinary probability was unplaceable through the number path, while a * 6-decimal venue (where the expansion rounds away) never showed it. * * `String(n)` gives the shortest representation that round-trips to the same * double, i.e. the number the caller meant ("0.05"). Prefer it whenever it fits * the scale, and keep `toFixed` for the two cases it exists for: exponent * notation, and a mantissa longer than the token's decimals (which it rounds to * scale — see `fromHuman`'s Gotchas). * * Note this fixes only conversion, not arithmetic done before it: a caller who * computes `3 * 0.05` hands over `0.15000000000000002`, a genuinely different * number, and gets it converted faithfully. Snapping a COMPUTED price onto the * venue's grid is `exchange.priceToPrecision`'s job. */ export declare function humanToDecimalString(n: number, decimals: number): string; /** * Raw YES price → YES probability in [0, 1]. * * @category converting */ export declare function priceToProbability(rawPrice: bigint | string, decimals?: number): number; /** * YES probability in [0, 1] → raw YES price (the `price` field of placeOrder). * * @category converting */ export declare function probabilityToPrice(probability: number, decimals?: number): bigint; /** * Best YES bid/ask of a binary book (raw quote units), for marking a position. * Either side may be absent on a one-sided book. Feed it the top level of * `getBinaryOrderBook` / `getLiveBinaryOrderBook`. * * @category converting */ export interface YesBookTop { /** Best resting YES bid (raw), if any. */ bestBid?: bigint; /** Best resting YES ask (raw), if any. */ bestAsk?: bigint; } /** * Mark-to-market YES price (raw): the book mid when two-sided, otherwise the * last trade clamped into the surviving side's bound. The clamp reconciles the * two failure modes of a one-sided book: marking to the lone quote outright * flashes the full bid-ask spread as a loss right after a taker clears the top * of the book, while marking to the last trade alone freezes the mark at a * stale print when the market genuinely runs away — a resting quote BEYOND the * last print is live, executable information and supersedes it. With no last * trade the lone side is all there is; `null` only when the book is empty and * nothing has traded. * * @category converting */ export declare function markYesPrice(top: YesBookTop, lastPrice: bigint | string | null | undefined): bigint | null; /** * The settlement vector a voided market actually pays, as the fraction one * outcome token of `outcomeIdx` redeems for — `numerators[outcomeIdx] / * denominator`. `null` only when neither vector field is present. * * A void pays whatever vector the contract stored, NOT a fixed half. Under the * `UNIFORM` void policy that vector is `[D/2, D/2]` and the half is right; * under `CLOB_SNAPSHOT` a void can store `[p, D−p]` at the closing YES price, * and then a half is wrong on both legs. * * Read the vector, never the policy. `BinaryMarket.sol` writes the uniform * vector on every `CLOB_SNAPSHOT` fallback (no capture pool, a reverting * `closingPrice()`, a one-sided or empty book, every overflow guard), so a * snapshot market frequently pays uniform. Deciding from `voidPolicy` would * misreport exactly those markets — the stored vector already encodes the * outcome of every fallback, and `voidPolicy` is display provenance. * * Callers keep their own pre-vector behavior on `null`: a voided market can * have both vector fields absent when it was indexed before those fields * existed. A partially present or invalid vector throws * {@link InvalidInputError}. Invalid data must not become a plausible payout * estimate. * * **Errors** * * Throws {@link InvalidInputError} when a present vector is malformed or does * not satisfy the binary void-vector invariants. * * @category converting */ export declare function voidPayoutFraction(numerators: readonly string[] | null | undefined, denominator: string | null | undefined, outcomeIdx: 0 | 1): { numerator: bigint; denominator: bigint; } | null; /** Errors raised by {@link voidPayoutFraction}. */ export type VoidPayoutFractionError = InvalidInputError; /** * One outcome leg's slice of a {@link BinaryPnl}. HUMAN numbers * (quote/collateral units) throughout — display-grade, like {@link toHuman}. * * @category converting */ export interface BinaryOutcomePnl { /** Realized on this outcome's sells: proceeds − avg cost of the tokens sold. */ realized: number; /** The remaining balance marked to `mark`, minus its avg cost. `null` when {@link mark} is unknown. */ unrealized: number | null; /** Average cost per token of the fills-derived holding; 0 when none held. */ avgCost: number; /** * The price this leg is valued at: the book-clamped last while trading (see * {@link markYesPrice}; the NO leg at its complement), or the settlement * payout once resolved — 1 for the winning outcome, 0 for the loser, and on * a void this leg's share of the market's stored payout vector (a half under * the `UNIFORM` void policy, `[p, D−p]` on a captured `CLOB_SNAPSHOT` void). * * `null` when the market has NO price to value against — it has never traded * and no book top was supplied. Do not read that as `0`: a zero YES mark is * also a full-value NO mark, which reports a fabricated total loss on one leg * and a fabricated total gain on the other. {@link value} and * {@link unrealized} are `null` with it. */ mark: number | null; /** Mark value of the current balance: `balance × mark`. `null` when {@link mark} is unknown. */ value: number | null; } /** * Realized + unrealized PnL for one account in one binary market, computed * purely from its fills + current outcome balances + the market row. Returns * HUMAN numbers (quote/collateral units), keyed per outcome (YES/NO) plus a * combined total — display-grade, like {@link toHuman}. * * @category converting */ export interface BinaryPnl { /** PnL attributable to the YES outcome (human quote units). */ yes: BinaryOutcomePnl; /** PnL attributable to the NO outcome (human quote units). */ no: BinaryOutcomePnl; /** yes.realized + no.realized. */ realized: number; /** yes.unrealized + no.unrealized; `null` when the market has no price to mark against. */ unrealized: number | null; /** realized + unrealized; `null` when the market has no price to mark against. */ total: number | null; } /** * One account-perspective fill for {@link computeBinaryPnl}: which outcome, how * many tokens, at what raw YES-terms price, and whether the account BOUGHT. * * @category converting */ export interface BinaryPnlFill { /** 0 = YES, 1 = NO. */ outcomeIndex: number; /** True = the account bought (added) this outcome; false = sold (reduced). */ isBuy: boolean; /** Fill quantity, raw outcome-token units (decimal string or bigint). */ quantity: string | bigint; /** Fill price for the fill's own outcome, raw quote units (0..10^decimals). */ price: string | bigint; } /** * Derive per-account {@link BinaryPnlFill}s from the raw {@link FillRow}s the * indexer returns (as from `getUserFills`), from `account`'s perspective. * Skips fills whose side/kind the indexer hasn't fully bridged (unknown side), * and re-expresses the fill's price into the outcome the account traded (the * book is YES-terms; a NO trade prices at `1 − yesPrice`). * * SCOPE IS THE CALLER'S JOB: no market filter is applied here, so pass ONE * market's fills when the result feeds cost basis, selected by * {@link FillRow.market} (never by `pool` — see there). * * @category converting */ export declare function binaryFillsFor(account: string, fills: FillRow[], decimals?: number): BinaryPnlFill[]; /** * Derive {@link BinaryPnlFill}s from one market's slice of a portfolio's * trades (as from `getPortfolio`). The portfolio view already resolves the * account's own side per fill, so this only re-expresses the YES-terms * `fillPrice` into the traded outcome (a NO trade prices at `1 − yesPrice`) * and skips fills whose side the indexer hasn't bridged yet. * * **Gotchas** * * The portfolio reads cut `trades` two ways, and BOTH have to be checked before a * total derived from them means anything — this function reports no warning of its own. * * They cap the page (default 50) and page newest-first: `tradesTruncated` says whether * that cap was hit. They also WINDOW the leg — by default to the last seven days — and * `tradesSince` is the bound that was applied. A wallet whose fills predate the window * comes back with `tradesTruncated: false` and an incomplete `trades`, so the flag alone * is not enough. For a whole-history PnL ask for the history first: `since: 0` (with a * `tradesLimit` to match), then check `tradesTruncated`. * * @category converting */ export declare function binaryFillsFromPortfolio(trades: PortfolioTrade[], decimals?: number): BinaryPnlFill[]; /** * Realized + unrealized binary PnL for one account, avg-cost basis — a PURE * helper (no indexer/chain dependency). `fills` are the account's own trades * (from {@link binaryFillsFor}); `balances` its current YES/NO holdings (from * `getOutcomeBalances`); `market` supplies decimals + resolution state. * * Realized PnL accrues on sells (proceeds − avg cost of the tokens sold). * Unrealized marks the remaining position: to the book-clamped last price * while trading (see {@link markYesPrice}; pass `opts.bookTop` so a live * quote beyond a stale print corrects the mark), and to the settlement payout * once resolved: 1 for the winning outcome, 0 for the loser, and on a void * each leg's share of the market's stored payout vector — a half per side * under the `UNIFORM` void policy, `[p, D−p]` at the closing YES price on a * `CLOB_SNAPSHOT` void that captured a two-sided close. * * An unresolved market that has never traded has no mark. When * `market.lastPrice` is null and `opts.bookTop` supplies no quote, `mark`, * `value` and `unrealized` are `null` on both legs, as are the combined * `unrealized` and `total`. `realized` and `avgCost` stay exact, because * neither depends on a mark. * * **Errors** * * Throws {@link InvalidInputError} when a void has a present but invalid payout * vector. A missing legacy vector keeps the documented half-payout fallback. * * @category converting */ export declare function computeBinaryPnl(fills: BinaryPnlFill[], balances: OutcomeBalances, market: Pick, opts?: { /** Top of the YES book — clamps the mark to live quotes (see {@link markYesPrice}). */ bookTop?: YesBookTop; }): BinaryPnl; /** Errors raised by {@link computeBinaryPnl}. */ export type ComputeBinaryPnlError = InvalidInputError; /** * Convert a raw balance to the largest JS number that does not exceed it — * full token precision, no lot/display quantum. `Number(formatUnits(raw))` * rounds to nearest, so on high-precision balances it can land a ULP above * the true value; this nudges the result down until it round-trips back to * ≤ `raw`. Size "max" orders against this, never against a * nearest-rounded conversion — a max that exceeds the wallet by one ULP is * an on-chain "insufficient balance" revert. * * The round-trip is verified through the same number-to-raw conversion the * write path applies, so the guarantee holds for the value `createOrder` will * actually send rather than for a differently-rounded stand-in. * * @category converting */ export declare function balanceFloor(raw: bigint, decimals?: number): number; /** * Floor a raw on-chain balance to a whole multiple of `quantum` (a human * decimal string — the market's lot size for base amounts, or a display * precision like "0.01" for quote) and return it as a JS number that is * provably ≤ the true balance. Flooring happens in raw (bigint) space, so * float rounding can never push a "max" order above the wallet. * * @category converting */ export declare function floorRawBalance(raw: bigint, decimals: number, quantum: string): number; /** * Ceil a raw on-chain amount UP to a whole multiple of `quantum` (a human * decimal string — the market's lot size for base amounts, or a display * precision like "0.01" for quote). The mirror of {@link floorRawBalance}, * for sizing a **minimum**: a minimum rounded DOWN falls below what the pool * requires and the order is rejected as under the minimum — the exact mirror * of the max-order revert the floors prevent. An amount below one quantum * rises to one whole quantum rather than collapsing to `0` the way the floor * does. * * Returns a `bigint`, unlike the floors, which return `number`. A minimum is * checked against the lot boundary exactly, and no `double` can hold one: the * nearest `double` to `0.1` at 18 decimals sits tens of wei off, which puts it * below the minimum itself. Convert at the edge if you need a number — * `Number(formatUnits(result, decimals))`. * * @category converting */ export declare function ceilRawAmount(raw: bigint, decimals: number, quantum: string): bigint; /** * Market-implied **Up** probability in [0, 1] from a raw YES price, or `null` * when the price is missing/unusable. One source for header stats and trade * rails so they always agree — pass the book mid, falling back to last price. * * @category converting */ export declare function upProbability(rawYes: bigint | string | null | undefined, decimals: number | undefined): number | null; /** * The Up probability as a whole-percent integer (e.g. `0.546` → `55`). * * @category converting */ export declare function upPercent(rawYes: bigint | string | null | undefined, decimals: number | undefined): number | null;