@pulsepairs/sdk 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DOCUMENTATION.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@pulsepairs/sdk` — Reference Documentation
2
2
 
3
- **Version:** 0.6.0 · **License:** UNLICENSED · **Runtime:** Node ≥ 18 or a modern browser
3
+ **Version:** 0.7.0 · **License:** UNLICENSED · **Runtime:** Node ≥ 18 or a modern browser
4
4
 
5
5
  Complete reference for the UpDown (PulsePairs) TypeScript SDK: every export, its
6
6
  units, its failure modes, and the protocol it speaks.
@@ -699,19 +699,32 @@ fresh nonce. Single-writer bots may prefer a monotonic counter — seed it from
699
699
  centsToBps(cents: number): number // 55 → 5500; 49.5 → 4950. Throws outside (0,100)
700
700
  bpsToCents(bps: number): number // 5500 → 55. Throws outside (0,10000)
701
701
  parseStake(usd: string | number): bigint // "5.50" → 5_500_000n. Rejects negative/non-finite
702
- assertStakeBounds(amountAtomic: bigint): void
702
+ assertStakeBounds(amountAtomic: bigint, side?: "BUY" | "SELL"): void
703
703
  feeAtomic(notionalAtomic, priceBps, cfg): bigint
704
704
 
705
- MIN_STAKE_ATOMIC = 5_000_000n // $5
706
- MAX_STAKE_ATOMIC = 500_000_000n // $500
705
+ MIN_STAKE_ATOMIC = 1_000_000n // $1 of share FACE value (BUY only)
706
+ MAX_STAKE_ATOMIC = 500_000_000n // $500 of share FACE value
707
707
  ```
708
708
 
709
709
  Both `centsToBps` and `bpsToCents` are exclusive at both ends — 0¢ and 100¢ are
710
710
  not tradeable prices, and passing either throws.
711
711
 
712
- `assertStakeBounds` is defence in depth. The frontend gates these bounds and the
713
- backend enforces only the upper one today, so validating **before signing**
714
- means a bad stake never becomes a signed payload at all.
712
+ `assertStakeBounds` is defence in depth. The backend enforces the same window at
713
+ the API boundary, so validating **before signing** means a bad stake never
714
+ becomes a signed payload at all.
715
+
716
+ Two things about the window that bite integrators:
717
+
718
+ - **The bound is on FACE, not cash.** `Order.amount` is a share count; the buyer
719
+ pays `amount × price / 10000`. A $1 minimum is $1 of face — about **$0.51 of
720
+ cash at 51¢**, $0.09 at 9¢. If your UI collects a cash budget, convert to
721
+ shares first (`cash / price`), which is always ≥ the cash figure, so a
722
+ cash-denominated gate at $1 sits safely above this bound at every price.
723
+ - **The minimum is entry-only.** Pass `side: "SELL"` for exits — a holder left
724
+ with 0.42 shares after a partial fill must be able to sell them. The maximum
725
+ still applies to both sides.
726
+
727
+ > Changed 2026-07-23 (SDK 0.7.0): minimum was `5_000_000n` ($5, both sides).
715
728
 
716
729
  `feeAtomic` mirrors the backend and contract formula (§3.4). Treats a missing
717
730
  `cfg.feeModel` as probability-weighted:
@@ -1234,8 +1247,8 @@ type MarketListItem = {
1234
1247
  duration: number; // sec
1235
1248
  status: "ACTIVE" | "TRADING_ENDED" | "RESOLVED" | "CLAIMED" | string;
1236
1249
  winner: number | null; // 1 = UP, 2 = DOWN, null = unresolved
1237
- upPrice: string;
1238
- downPrice: string;
1250
+ upPrice: number | null; // bps of $1 from the live book mid; null = no price
1251
+ downPrice: number | null; // always 10000 - upPrice; null exactly when upPrice is
1239
1252
  strikePrice?: string;
1240
1253
  settlementPrice?: string;
1241
1254
  volume: string;
package/dist/eip712.d.ts CHANGED
@@ -218,20 +218,28 @@ export declare function parseCompositeMarketKey(market: string): ParsedComposite
218
218
  export declare function centsToBps(cents: number): number;
219
219
  /** Bps → cents (5500 → 55). Inverse of `centsToBps`. */
220
220
  export declare function bpsToCents(bps: number): number;
221
- /** Atomic-USDT denomination of the documented $5–$500 stake window. */
222
- export declare const MIN_STAKE_ATOMIC = 5000000n;
221
+ /**
222
+ * Atomic denomination of the documented stake window, in SHARE FACE VALUE
223
+ * — `Order.amount` is a share count, not the cash spent (the buyer pays
224
+ * `amount × price / 10000`). A $1 floor is $1 of face: ~$0.51 of cash at
225
+ * 51¢. Lowered from $5 → $1 on 2026-07-23 (Polymarket parity).
226
+ */
227
+ export declare const MIN_STAKE_ATOMIC = 1000000n;
223
228
  export declare const MAX_STAKE_ATOMIC = 500000000n;
224
229
  /** Parse a USD string (e.g. "5", "5.50", "12.345") into atomic USDT.
225
230
  * Rejects negatives + non-finite. */
226
231
  export declare function parseStake(usd: string | number): bigint;
227
232
  /**
228
- * Defense-in-depth stake clamp. Throws if `amountAtomic` is outside the
229
- * documented `$5 ≤ stake ≤ $500` window. Frontend gates these too; backend
230
- * enforces only the upper bound today (BUG-S2.1 — see PULSEPAIRS_BACKLOG).
231
- * SDK callers should validate before signing so a bad stake never produces
232
- * a signed payload at all.
233
+ * Defense-in-depth stake clamp. Throws if `amountAtomic` (share face value)
234
+ * is outside the documented `$1 ≤ stake ≤ $500` window; the backend applies
235
+ * the same window at the API boundary. SDK callers should validate before
236
+ * signing so a bad stake never produces a signed payload at all.
237
+ *
238
+ * Pass `side: "SELL"` for exits: the minimum is ENTRY-ONLY, so a holder left
239
+ * with a sub-$1 position (routine after a partial fill) can always sell it.
240
+ * The default stays "BUY" so existing callers keep the strict branch.
233
241
  */
234
- export declare function assertStakeBounds(amountAtomic: bigint): void;
242
+ export declare function assertStakeBounds(amountAtomic: bigint, side?: "BUY" | "SELL"): void;
235
243
  /**
236
244
  * Probability-weighted fee in atomic USDT. Mirrors the backend / contract
237
245
  * formula. Returns the fee for `notionalAtomic` filled at `priceBps`.
package/dist/eip712.js CHANGED
@@ -220,8 +220,13 @@ export function bpsToCents(bps) {
220
220
  }
221
221
  return bps / 100;
222
222
  }
223
- /** Atomic-USDT denomination of the documented $5–$500 stake window. */
224
- export const MIN_STAKE_ATOMIC = 5000000n;
223
+ /**
224
+ * Atomic denomination of the documented stake window, in SHARE FACE VALUE
225
+ * — `Order.amount` is a share count, not the cash spent (the buyer pays
226
+ * `amount × price / 10000`). A $1 floor is $1 of face: ~$0.51 of cash at
227
+ * 51¢. Lowered from $5 → $1 on 2026-07-23 (Polymarket parity).
228
+ */
229
+ export const MIN_STAKE_ATOMIC = 1000000n;
225
230
  export const MAX_STAKE_ATOMIC = 500000000n;
226
231
  /** Parse a USD string (e.g. "5", "5.50", "12.345") into atomic USDT.
227
232
  * Rejects negatives + non-finite. */
@@ -233,14 +238,17 @@ export function parseStake(usd) {
233
238
  return BigInt(Math.round(n * 1e6));
234
239
  }
235
240
  /**
236
- * Defense-in-depth stake clamp. Throws if `amountAtomic` is outside the
237
- * documented `$5 ≤ stake ≤ $500` window. Frontend gates these too; backend
238
- * enforces only the upper bound today (BUG-S2.1 — see PULSEPAIRS_BACKLOG).
239
- * SDK callers should validate before signing so a bad stake never produces
240
- * a signed payload at all.
241
+ * Defense-in-depth stake clamp. Throws if `amountAtomic` (share face value)
242
+ * is outside the documented `$1 ≤ stake ≤ $500` window; the backend applies
243
+ * the same window at the API boundary. SDK callers should validate before
244
+ * signing so a bad stake never produces a signed payload at all.
245
+ *
246
+ * Pass `side: "SELL"` for exits: the minimum is ENTRY-ONLY, so a holder left
247
+ * with a sub-$1 position (routine after a partial fill) can always sell it.
248
+ * The default stays "BUY" so existing callers keep the strict branch.
241
249
  */
242
- export function assertStakeBounds(amountAtomic) {
243
- if (amountAtomic < MIN_STAKE_ATOMIC) {
250
+ export function assertStakeBounds(amountAtomic, side = "BUY") {
251
+ if (side === "BUY" && amountAtomic < MIN_STAKE_ATOMIC) {
244
252
  throw new Error(`stake below $${Number(MIN_STAKE_ATOMIC) / 1e6} minimum`);
245
253
  }
246
254
  if (amountAtomic > MAX_STAKE_ATOMIC) {
package/dist/types.d.ts CHANGED
@@ -81,8 +81,24 @@ export type MarketListItem = {
81
81
  duration: number;
82
82
  status: "ACTIVE" | "TRADING_ENDED" | "RESOLVED" | "CLAIMED" | string;
83
83
  winner: number | null;
84
- upPrice: string;
85
- downPrice: string;
84
+ /**
85
+ * Odds in basis points of $1 (5000 = 50%), from the mid of the live book.
86
+ * `null` when no price is defined (empty / one-sided book, or trading not
87
+ * open) — render "—", never 0%. `0` / `10000` occur only on a settled
88
+ * market, where they are the outcome. When non-null,
89
+ * `upPrice + downPrice === 10000`.
90
+ *
91
+ * BREAKING 2026-07-26: previously `string`, and previously carried the
92
+ * on-chain cumulative collateral flow rather than any price.
93
+ */
94
+ upPrice: number | null;
95
+ downPrice: number | null;
96
+ /**
97
+ * Why `upPrice`/`downPrice` are what they are, so a `null` is explained
98
+ * rather than merely absent. Widened to `string` deliberately: this is an
99
+ * OPEN set and new values may be added — do not switch exhaustively.
100
+ */
101
+ priceSource?: "settled" | "pending_resolution" | "not_trading" | "book_mid" | "none" | string;
86
102
  strikePrice?: string;
87
103
  settlementPrice?: string;
88
104
  volume: string;
@@ -159,6 +175,13 @@ export type Trade = {
159
175
  platformFee: string;
160
176
  makerFee: string;
161
177
  settlementStatus: string;
178
+ /** Hash of the on-chain settlement tx, for linking a fill to the explorer.
179
+ * Null until the tx is broadcast — and reset to null if a reconcile drops
180
+ * it — so treat absence as "pending", not "missing". Complementary
181
+ * (MINT/MERGE) fills settle in batches, so the same hash legitimately
182
+ * repeats across several trades. Optional for back-compat with a
183
+ * pre-2026-07-21 backend. */
184
+ settlementTxHash?: string | null;
162
185
  createdAt: string;
163
186
  /** Match geometry. NORMAL = same-option fill. MINT/MERGE = complementary
164
187
  * cross (opposite-option legs). Optional so a pre-2026-07-17 backend
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pulsepairs/sdk",
3
- "version": "0.6.0",
4
- "description": "Standalone SDK for the UpDown (PulsePairs) up/down prediction markets — matcher REST/WS client, EIP-712 order signing, trade-math, and Alchemy Account Kit (smart-account) order signing for rain.trade integration.",
3
+ "version": "0.7.0",
4
+ "description": "Standalone SDK for the UpDown (PulsePairs) up/down prediction markets \u2014 matcher REST/WS client, EIP-712 order signing, trade-math, and Alchemy Account Kit (smart-account) order signing for rain.trade integration.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",