@pulsepairs/sdk 0.6.0 → 0.8.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,8 +1,8 @@
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
- Complete reference for the UpDown (PulsePairs) TypeScript SDK: every export, its
5
+ Complete reference for the UpDown TypeScript SDK: every export, its
6
6
  units, its failure modes, and the protocol it speaks.
7
7
 
8
8
  The [`README.md`](./README.md) is the quickstart. **This** document is the
@@ -61,19 +61,28 @@ on-chain fill. You never pay gas to place, amend or cancel an order — only for
61
61
  the one-time ERC-20 `approve` (and, on the smart-account path, the one-time
62
62
  account deploy).
63
63
 
64
- ### The demo environment
64
+ ### The environments
65
65
 
66
- A full stack runs 24/7 on Arbitrum One with a mock oracle and mintable test USDT:
66
+ Two stacks, both running 24/7 on **Arbitrum One** (dev is not a testnet — it is
67
+ mainnet with a mock oracle and play money):
67
68
 
68
69
  ```
69
- UI https://demo-pulsepairs.rainwins.com
70
- REST https://api.demo-pulsepairs.rainwins.com
71
- WS wss://api.demo-pulsepairs.rainwins.com/stream
72
- Faucet POST /test/devmint (10k cap, 1 mint / address / 5 min; also seeds gas ETH)
70
+ DEV REST https://dev-api-updown.rain.trade
71
+ WS wss://dev-api-updown.rain.trade/stream
72
+ Money mock USDT, mintable
73
+ Oracle mock Data Streams; chart shows the CEX composite
74
+ Faucet POST /test/devmint (10k cap, 1 mint / address / 5 min; also seeds gas ETH)
75
+
76
+ PROD REST https://prod-api-updown.rain.trade
77
+ WS wss://prod-api-updown.rain.trade/stream
78
+ Money real USDT0
79
+ Oracle real Chainlink Data Streams; chart shows the DON benchmark it settles on
80
+ Faucet none — /test/devmint 404s
73
81
  ```
74
82
 
75
83
  Mint to the **smart-account** address if you are on the Account Kit path, not the
76
- owner EOA.
84
+ owner EOA. Read `GET /config` on whichever host you point at for chain id and
85
+ contract addresses; never hardcode them.
77
86
 
78
87
  ---
79
88
 
@@ -204,8 +213,25 @@ signed message. `PostOrderBody` additionally accepts the string forms
204
213
  (`"BUY"`, `"LIMIT"`, …) for `side`/`type`, but the *signature* is always over the
205
214
  numbers.
206
215
 
207
- `MARKET` orders carry `price: 0` (both in the signed message as `0n` and in the
208
- POST body as `0`).
216
+ A `MARKET` order's `price` is its **slippage bound**, not a fill price: the worst
217
+ price the order will accept — a CAP on a buy, a FLOOR on a sell. It is enforced
218
+ twice, so it is not advisory: the engine breaks its match loop on it (an order
219
+ whose depth sat outside the bound is cancelled `PRICE_THROUGH_LIMIT`, not
220
+ `NO_LIQUIDITY`), and `UpDownSettlement.enterPosition` reverts `OrdersNotCrossed()`
221
+ if a fill would cross it.
222
+
223
+ `price: 0` is **rejected at submit** — *"Orders must specify a non-zero price.
224
+ Market orders should be signed at the worst-acceptable slippage price."* There is
225
+ no unbounded market order. Earlier versions of this document said MARKET orders
226
+ carry `price: 0`; that was true of an older protocol and following it now
227
+ produces an error on every order.
228
+
229
+ Express the pad in **cents**, not as a percentage. A percentage bound is worth
230
+ almost nothing at long-shot prices, which is where these markets spend their
231
+ final minutes: 5% of an 8c option is 0.4c, against a book quoting a 7.39c
232
+ spread — one production order missed a fill by 0.23c with depth to spare that
233
+ way. `centsToBps` / `bpsToCents` are exported for the conversion; 100 bps is 1
234
+ cent exactly.
209
235
 
210
236
  ### 3.4 Fees and `maxFee`
211
237
 
@@ -306,7 +332,7 @@ otherwise be replayable forever.
306
332
 
307
333
  ```ts
308
334
  import { UpDownHttpClient } from "@pulsepairs/sdk";
309
- const api = new UpDownHttpClient("https://api.demo-pulsepairs.rainwins.com");
335
+ const api = new UpDownHttpClient("https://dev-api-updown.rain.trade");
310
336
  ```
311
337
 
312
338
  A thin, dependency-free wrapper over `fetch`. Trailing slashes on the base URL
@@ -331,7 +357,7 @@ runtimes) — they are not normalised.
331
357
  | `getVersion()` | `GET /version` | `Version` — `{ commit, bootedAt, env, nodeVersion }` |
332
358
  | `getHealth()` | `GET /health` | `{ status, relayer?, uptime? }` |
333
359
  | `getConfig()` | `GET /config` | `ApiConfig` — **fetch this first, never hardcode addresses** |
334
- | `getMarkets(opts?)` | `GET /markets` | `MarketListItem[]`; `opts.timeframe` ∈ `300 \| 900 \| 3600`, `opts.pair` ∈ `"BTC-USD" \| "ETH-USD"` |
360
+ | `getMarkets(opts?)` | `GET /markets` | `MarketListItem[]`; `opts.timeframe` ∈ `300 \| 900` (60m retired 2026-08-18 — no new 3600s markets; the API still accepts `3600` and returns the historical ones), `opts.pair` ∈ `"BTC-USD" \| "ETH-USD"` |
335
361
  | `getMarket(address)` | `GET /markets/:address` | `MarketDetail` — list item + `timeRemainingSeconds` + top-of-book |
336
362
  | `getOrderbook(market)` | `GET /orderbook/:address` | `OrderBookFull` — full depth, both options, bids and asks |
337
363
  | `getBalance(wallet)` | `GET /balance/:wallet` | `Balance` — `available`, `inOrders`, `cachedBalance`, `withdrawNonce` |
@@ -428,8 +454,8 @@ the book asynchronously.
428
454
  ### `wsUrlFromHttpBase(httpBase)`
429
455
 
430
456
  ```ts
431
- wsUrlFromHttpBase("https://api.demo-pulsepairs.rainwins.com")
432
- // → "wss://api.demo-pulsepairs.rainwins.com/stream"
457
+ wsUrlFromHttpBase("https://dev-api-updown.rain.trade")
458
+ // → "wss://dev-api-updown.rain.trade/stream"
433
459
  ```
434
460
 
435
461
  Swaps `https:`→`wss:` / `http:`→`ws:`, forces the path to `/stream`, and strips
@@ -699,19 +725,37 @@ fresh nonce. Single-writer bots may prefer a monotonic counter — seed it from
699
725
  centsToBps(cents: number): number // 55 → 5500; 49.5 → 4950. Throws outside (0,100)
700
726
  bpsToCents(bps: number): number // 5500 → 55. Throws outside (0,10000)
701
727
  parseStake(usd: string | number): bigint // "5.50" → 5_500_000n. Rejects negative/non-finite
702
- assertStakeBounds(amountAtomic: bigint): void
728
+ assertStakeBounds(amountAtomic: bigint, side?: "BUY" | "SELL"): void
703
729
  feeAtomic(notionalAtomic, priceBps, cfg): bigint
704
730
 
705
- MIN_STAKE_ATOMIC = 5_000_000n // $5
706
- MAX_STAKE_ATOMIC = 500_000_000n // $500
731
+ MIN_STAKE_ATOMIC = 1_000_000n // $1 of share FACE value (BUY only)
707
732
  ```
708
733
 
734
+ > **`MAX_STAKE_ATOMIC` was removed in `0.8.0`** — the venue no longer caps order
735
+ > size, so importing it is a compile error. If you were relying on it, pick your
736
+ > own ceiling; nothing server-side will stop an oversized `amount` except your
737
+ > own collateral (`CollateralGate` checks balance and allowance on-chain before
738
+ > matching).
739
+
709
740
  Both `centsToBps` and `bpsToCents` are exclusive at both ends — 0¢ and 100¢ are
710
741
  not tradeable prices, and passing either throws.
711
742
 
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.
743
+ `assertStakeBounds` is defence in depth. The backend enforces the same window at
744
+ the API boundary, so validating **before signing** means a bad stake never
745
+ becomes a signed payload at all.
746
+
747
+ Two things about the window that bite integrators:
748
+
749
+ - **The bound is on FACE, not cash.** `Order.amount` is a share count; the buyer
750
+ pays `amount × price / 10000`. A $1 minimum is $1 of face — about **$0.51 of
751
+ cash at 51¢**, $0.09 at 9¢. If your UI collects a cash budget, convert to
752
+ shares first (`cash / price`), which is always ≥ the cash figure, so a
753
+ cash-denominated gate at $1 sits safely above this bound at every price.
754
+ - **The minimum is entry-only.** Pass `side: "SELL"` for exits — a holder left
755
+ with 0.42 shares after a partial fill must be able to sell them. There is no
756
+ maximum on either side any more.
757
+
758
+ > Changed 2026-07-23 (SDK 0.7.0): minimum was `5_000_000n` ($5, both sides).
715
759
 
716
760
  `feeAtomic` mirrors the backend and contract formula (§3.4). Treats a missing
717
761
  `cfg.feeModel` as probability-weighted:
@@ -1234,8 +1278,8 @@ type MarketListItem = {
1234
1278
  duration: number; // sec
1235
1279
  status: "ACTIVE" | "TRADING_ENDED" | "RESOLVED" | "CLAIMED" | string;
1236
1280
  winner: number | null; // 1 = UP, 2 = DOWN, null = unresolved
1237
- upPrice: string;
1238
- downPrice: string;
1281
+ upPrice: number | null; // bps of $1 from the live book mid; null = no price
1282
+ downPrice: number | null; // always 10000 - upPrice; null exactly when upPrice is
1239
1283
  strikePrice?: string;
1240
1284
  settlementPrice?: string;
1241
1285
  volume: string;
package/README.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # @pulsepairs/sdk
2
2
 
3
- Standalone SDK for the **UpDown** (PulsePairs) up/down prediction markets —
3
+ Standalone SDK for the **UpDown** up/down prediction markets —
4
4
  BTC-USD / ETH-USD, UP or DOWN, on 5-minute / 15-minute / 1-hour cycles.
5
5
 
6
+ > **On the name:** the product is **UpDown**. "PulsePairs" is a retired name that
7
+ > survives in two places for compatibility reasons only — this npm package's name
8
+ > (`@pulsepairs/sdk`, renaming it would break every consumer) and two EIP-712 domain
9
+ > strings that are hashed into signatures (`PulsePairs WebSocket Auth`,
10
+ > `PulsePairsAuthDomain`). Neither can be changed without breaking deployed clients.
11
+
6
12
  This is the package [rain.trade](https://rain.trade) integrates to surface
7
13
  UpDown markets alongside Rain's own markets ("satellite" integration, Option A).
8
14
  It is deliberately **segregated** from `rain-sdk-v2`: bump this package's version
@@ -23,13 +29,20 @@ It gives you three things:
23
29
  > its units, its failure modes, the WS protocol, the full type surface and a
24
30
  > troubleshooting table — see **[`DOCUMENTATION.md`](./DOCUMENTATION.md)**.
25
31
 
26
- > **Live integration environment:** a public demo of the full stack (markets
27
- > cycling 24/7 on Arbitrum One, mock oracle, mintable test USDT) runs at
28
- > **https://demo-pulsepairs.rainwins.com** with the matcher API/WS at
29
- > **`https://api.demo-pulsepairs.rainwins.com`** (`wss://…/stream`). Every
30
- > example below targets it by default. Test funds: `POST /test/devmint`
31
- > (10k cap, 1 mint per address per 5 min — mint to the **smart-account**
32
- > address, not the owner EOA; it also seeds a little gas ETH).
32
+ > **Live environments — there are two, both on Arbitrum One:**
33
+ >
34
+ > | | REST | WebSocket |
35
+ > |---|---|---|
36
+ > | **Dev** — mock oracle, mock USDT, faucet on | `https://dev-api-updown.rain.trade` | `wss://dev-api-updown.rain.trade/stream` |
37
+ > | **Prod** — real Chainlink DON, real USDT0 | `https://prod-api-updown.rain.trade` | `wss://prod-api-updown.rain.trade/stream` |
38
+ >
39
+ > Examples below target **dev** by default — markets cycle 24/7 there and a
40
+ > mistake costs play money. Test funds: `POST /test/devmint` (10k cap, 1 mint per
41
+ > address per 5 min — mint to the **smart-account** address, not the owner EOA; it
42
+ > also seeds a little gas ETH). The faucet 404s on prod.
43
+ >
44
+ > The older `demo-pulsepairs.rainwins.com` / `testnet-pulsepairs.rainwins.com`
45
+ > hosts are retired; there is no Arbitrum Sepolia deployment.
33
46
 
34
47
  ---
35
48
 
@@ -96,15 +109,28 @@ await ensureSettlementAllowance({
96
109
 
97
110
  const maxFee = (amount * BigInt(cfg.platformFeeBps + cfg.makerFeeBps)) / 10000n; // peak fee
98
111
  const nonce = freshNonce(); // CSPRNG; never Math.random/Date.now
112
+
113
+ // A MARKET order's signed `price` is its SLIPPAGE BOUND — the worst price it
114
+ // will accept — not a fill price. `price: 0` is REJECTED at submit ("Orders must
115
+ // specify a non-zero price"), so there is no unbounded market order to sign.
116
+ // Derive the bound from the current quote and pad it in CENTS: a percentage pad
117
+ // is worth almost nothing at long-shot prices, where these markets spend their
118
+ // final minutes (5% of an 8c option is 0.4c, against a 7.39c spread). 100 bps =
119
+ // 1 cent exactly. On a BUY the bound is reference + pad (the most you will pay);
120
+ // on a SELL it is reference − pad, because there the price is a FLOOR.
121
+ const reference = live.upPrice; // bps; use downPrice for DOWN
122
+ if (reference === null) throw new Error("no quote to bound against — refusing to guess");
123
+ const price = BigInt(Math.min(9999, Math.max(1, reference + 5 * 100))); // +5c
124
+
99
125
  const typedData = buildOrderTypedData({
100
126
  cfg, settlementAddress,
101
127
  message: { maker: account.address, market: BigInt(marketId), option: BigInt(Option.UP),
102
- side: OrderSide.BUY, type: OrderType.MARKET, price: 0n, amount, maxFee, nonce,
128
+ side: OrderSide.BUY, type: OrderType.MARKET, price, amount, maxFee, nonce,
103
129
  expiry: BigInt(live.endTime) },
104
130
  });
105
131
  const signature = await account.signTypedData(typedData);
106
132
  await api.postOrder({ maker: account.address, market: live.address, option: Option.UP,
107
- side: OrderSide.BUY, type: OrderType.MARKET, price: 0, amount: amount.toString(),
133
+ side: OrderSide.BUY, type: OrderType.MARKET, price: Number(price), amount: amount.toString(),
108
134
  maxFee: maxFee.toString(), nonce: Number(nonce), expiry: live.endTime, signature });
109
135
  ```
110
136
 
@@ -330,7 +356,7 @@ number. `getPnl` assembles one, marking each position:
330
356
  flagged `markSource: "cost"` so you can tell it apart).
331
357
 
332
358
  ```ts
333
- const client = new UpDownHttpClient("https://api.demo-pulsepairs.rainwins.com");
359
+ const client = new UpDownHttpClient("https://dev-api-updown.rain.trade");
334
360
 
335
361
  const pnl = await client.getPnl(wallet, { includeFees: true });
336
362
  console.log(pnl.unrealizedPnlUsdt, pnl.roiPct); // e.g. -3, -30
package/dist/eip712.d.ts CHANGED
@@ -218,20 +218,33 @@ 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;
223
- export declare const MAX_STAKE_ATOMIC = 500000000n;
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;
224
228
  /** Parse a USD string (e.g. "5", "5.50", "12.345") into atomic USDT.
225
229
  * Rejects negatives + non-finite. */
226
230
  export declare function parseStake(usd: string | number): bigint;
227
231
  /**
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.
232
+ * Defense-in-depth stake clamp. Throws if `amountAtomic` (share face value)
233
+ * is below the documented `$1` entry floor; the backend applies the same
234
+ * floor at the API boundary. SDK callers should validate before signing so a
235
+ * bad stake never produces a signed payload at all.
236
+ *
237
+ * Pass `side: "SELL"` for exits: the minimum is ENTRY-ONLY, so a holder left
238
+ * with a sub-$1 position (routine after a partial fill) can always sell it.
239
+ * The default stays "BUY" so existing callers keep the strict branch.
240
+ *
241
+ * 2026-08-03: the $500 ceiling was REMOVED (backend `lib/stakeBounds.ts` did
242
+ * the same). Size is bounded by collateral — the venue's pre-match gate reads
243
+ * balance AND allowance on-chain and refuses a cross it cannot settle — so the
244
+ * cap was never what limited risk, only what limited legitimate size. Callers
245
+ * upgrading from <=0.7.0 will find previously-rejected amounts now sign.
233
246
  */
234
- export declare function assertStakeBounds(amountAtomic: bigint): void;
247
+ export declare function assertStakeBounds(amountAtomic: bigint, side?: "BUY" | "SELL"): void;
235
248
  /**
236
249
  * Probability-weighted fee in atomic USDT. Mirrors the backend / contract
237
250
  * formula. Returns the fee for `notionalAtomic` filled at `priceBps`.
package/dist/eip712.js CHANGED
@@ -220,9 +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;
225
- export const MAX_STAKE_ATOMIC = 500000000n;
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;
226
230
  /** Parse a USD string (e.g. "5", "5.50", "12.345") into atomic USDT.
227
231
  * Rejects negatives + non-finite. */
228
232
  export function parseStake(usd) {
@@ -233,19 +237,25 @@ export function parseStake(usd) {
233
237
  return BigInt(Math.round(n * 1e6));
234
238
  }
235
239
  /**
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.
240
+ * Defense-in-depth stake clamp. Throws if `amountAtomic` (share face value)
241
+ * is below the documented `$1` entry floor; the backend applies the same
242
+ * floor at the API boundary. SDK callers should validate before signing so a
243
+ * bad stake never produces a signed payload at all.
244
+ *
245
+ * Pass `side: "SELL"` for exits: the minimum is ENTRY-ONLY, so a holder left
246
+ * with a sub-$1 position (routine after a partial fill) can always sell it.
247
+ * The default stays "BUY" so existing callers keep the strict branch.
248
+ *
249
+ * 2026-08-03: the $500 ceiling was REMOVED (backend `lib/stakeBounds.ts` did
250
+ * the same). Size is bounded by collateral — the venue's pre-match gate reads
251
+ * balance AND allowance on-chain and refuses a cross it cannot settle — so the
252
+ * cap was never what limited risk, only what limited legitimate size. Callers
253
+ * upgrading from <=0.7.0 will find previously-rejected amounts now sign.
241
254
  */
242
- export function assertStakeBounds(amountAtomic) {
243
- if (amountAtomic < MIN_STAKE_ATOMIC) {
255
+ export function assertStakeBounds(amountAtomic, side = "BUY") {
256
+ if (side === "BUY" && amountAtomic < MIN_STAKE_ATOMIC) {
244
257
  throw new Error(`stake below $${Number(MIN_STAKE_ATOMIC) / 1e6} minimum`);
245
258
  }
246
- if (amountAtomic > MAX_STAKE_ATOMIC) {
247
- throw new Error(`stake above $${Number(MAX_STAKE_ATOMIC) / 1e6} maximum`);
248
- }
249
259
  }
250
260
  /**
251
261
  * Probability-weighted fee in atomic USDT. Mirrors the backend / contract
package/dist/http.d.ts CHANGED
@@ -10,8 +10,20 @@ export declare class UpDownHttpClient {
10
10
  uptime?: number;
11
11
  }>;
12
12
  getConfig(): Promise<ApiConfig>;
13
+ /**
14
+ * List markets.
15
+ *
16
+ * `timeframe` narrowed from `300 | 900 | 3600` to `300 | 900`: 60m was
17
+ * disabled on the cyclers 2026-08-18, so no new 3600s markets exist to list.
18
+ *
19
+ * The narrowing is a COMPILE-TIME steer, not a runtime break. The backend
20
+ * still accepts `?timeframe=3600` and returns the historical 60m markets, so
21
+ * a caller pinned to an older SDK keeps working and anything reading history
22
+ * can pass 3600 through a cast. New code gets a type error instead of a
23
+ * silently empty list, which is the failure that is hard to diagnose.
24
+ */
13
25
  getMarkets(opts?: {
14
- timeframe?: 300 | 900 | 3600;
26
+ timeframe?: 300 | 900;
15
27
  pair?: PairSymbol;
16
28
  }): Promise<MarketListItem[]>;
17
29
  getMarket(address: string): Promise<MarketDetail>;
package/dist/http.js CHANGED
@@ -46,6 +46,18 @@ export class UpDownHttpClient {
46
46
  const res = await fetch(buildUrl(this.baseUrl, "/config"));
47
47
  return parseJson(res);
48
48
  }
49
+ /**
50
+ * List markets.
51
+ *
52
+ * `timeframe` narrowed from `300 | 900 | 3600` to `300 | 900`: 60m was
53
+ * disabled on the cyclers 2026-08-18, so no new 3600s markets exist to list.
54
+ *
55
+ * The narrowing is a COMPILE-TIME steer, not a runtime break. The backend
56
+ * still accepts `?timeframe=3600` and returns the historical 60m markets, so
57
+ * a caller pinned to an older SDK keeps working and anything reading history
58
+ * can pass 3600 through a cast. New code gets a type error instead of a
59
+ * silently empty list, which is the failure that is hard to diagnose.
60
+ */
49
61
  async getMarkets(opts) {
50
62
  const res = await fetch(buildUrl(this.baseUrl, "/markets", {
51
63
  timeframe: opts?.timeframe,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { UpDownHttpClient, wsUrlFromHttpBase } from "./http.js";
2
2
  export { UpDownWsClient, type UpDownWsMessage, type SubscribePayload, type WsAuthCredentials, type ConnectAuthedOptions, } from "./ws.js";
3
- export { ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES, buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData, freshSessionId, freshNonce, domainForSettlement, findPairBySettlement, parseCompositeMarketKey, centsToBps, bpsToCents, parseStake, assertStakeBounds, feeAtomic, MIN_STAKE_ATOMIC, MAX_STAKE_ATOMIC, type OrderSignMessage, type CancelSignMessage, type WsAuthMessage, type ParsedComposite, } from "./eip712.js";
3
+ export { ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES, buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData, freshSessionId, freshNonce, domainForSettlement, findPairBySettlement, parseCompositeMarketKey, centsToBps, bpsToCents, parseStake, assertStakeBounds, feeAtomic, MIN_STAKE_ATOMIC, type OrderSignMessage, type CancelSignMessage, type WsAuthMessage, type ParsedComposite, } from "./eip712.js";
4
4
  export { CLOB_AUTH_TYPES, CLOB_AUTH_MESSAGE, buildClobAuthTypedData, buildHmacSignature, HMAC_HEADERS, type ClobAuthDomain, type ClobAuthMessage, } from "./auth.js";
5
5
  export { ensureSettlementAllowance, MAX_UINT256, DEFAULT_APPROVAL_AMOUNT, type EnsureAllowanceResult, } from "./approve.js";
6
6
  export { readOnChainHolderShares, reconcileFills, reportedFromPositions, type OnChainHolderShares, type ShareReconciliation, type ReconcileStatus, type FillReconciliationReport, } from "./reconcile.js";
package/dist/index.js CHANGED
@@ -2,7 +2,11 @@
2
2
  export { UpDownHttpClient, wsUrlFromHttpBase } from "./http.js";
3
3
  export { UpDownWsClient, } from "./ws.js";
4
4
  // EIP-712 helpers
5
- export { ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES, buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData, freshSessionId, freshNonce, domainForSettlement, findPairBySettlement, parseCompositeMarketKey, centsToBps, bpsToCents, parseStake, assertStakeBounds, feeAtomic, MIN_STAKE_ATOMIC, MAX_STAKE_ATOMIC, } from "./eip712.js";
5
+ export { ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES, buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData, freshSessionId, freshNonce, domainForSettlement, findPairBySettlement, parseCompositeMarketKey, centsToBps, bpsToCents, parseStake, assertStakeBounds, feeAtomic,
6
+ // MAX_STAKE_ATOMIC was removed in 0.8.0 — the venue no longer caps order
7
+ // size. Importing it is now a compile error rather than a silent behaviour
8
+ // change, which is the intended way for a consumer to learn the cap is gone.
9
+ MIN_STAKE_ATOMIC, } from "./eip712.js";
6
10
  // Phase 3 / Gate 1 — L2 HMAC auth helpers
7
11
  export { CLOB_AUTH_TYPES, CLOB_AUTH_MESSAGE, buildClobAuthTypedData, buildHmacSignature, HMAC_HEADERS, } from "./auth.js";
8
12
  // Approve helper
package/dist/types.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * UpDown SDK type surface — mirrors the backend's REST + WS response shapes
3
- * one-to-one. Keep field names and casing in sync with `docs/api.md`; if a
3
+ * one-to-one. Keep field names and casing in sync with `docs/archive/api-superseded-2026-08-12.md`; if a
4
4
  * shape drifts, update both in the same commit.
5
5
  */
6
6
  export type PairSymbol = "BTC-USD" | "ETH-USD";
@@ -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/dist/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * UpDown SDK type surface — mirrors the backend's REST + WS response shapes
3
- * one-to-one. Keep field names and casing in sync with `docs/api.md`; if a
3
+ * one-to-one. Keep field names and casing in sync with `docs/archive/api-superseded-2026-08-12.md`; if a
4
4
  * shape drifts, update both in the same commit.
5
5
  */
6
6
  /** Numeric enum values match the backend's on-chain encoding. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulsepairs/sdk",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
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.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -62,6 +62,10 @@
62
62
  }
63
63
  },
64
64
  "devDependencies": {
65
+ "@aa-sdk/core": "^4.88.5",
66
+ "@account-kit/infra": "^4.88.5",
67
+ "@account-kit/smart-contracts": "^4.88.5",
68
+ "@account-kit/wallet-client": "^4.88.5",
65
69
  "@types/node": "^22.10.0",
66
70
  "typescript": "^5.9.3",
67
71
  "viem": "^2.47.0"