@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 +67 -23
- package/README.md +37 -11
- package/dist/eip712.d.ts +22 -9
- package/dist/eip712.js +23 -13
- package/dist/http.d.ts +13 -1
- package/dist/http.js +12 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/dist/types.d.ts +26 -3
- package/dist/types.js +1 -1
- package/package.json +5 -1
package/DOCUMENTATION.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# `@pulsepairs/sdk` — Reference Documentation
|
|
2
2
|
|
|
3
|
-
**Version:** 0.
|
|
3
|
+
**Version:** 0.7.0 · **License:** UNLICENSED · **Runtime:** Node ≥ 18 or a modern browser
|
|
4
4
|
|
|
5
|
-
Complete reference for the UpDown
|
|
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
|
|
64
|
+
### The environments
|
|
65
65
|
|
|
66
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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`
|
|
208
|
-
|
|
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
|
|
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
|
|
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
|
|
432
|
-
// → "wss://api
|
|
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 =
|
|
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
|
|
713
|
-
|
|
714
|
-
|
|
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:
|
|
1238
|
-
downPrice:
|
|
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**
|
|
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
|
|
27
|
-
>
|
|
28
|
-
>
|
|
29
|
-
>
|
|
30
|
-
>
|
|
31
|
-
>
|
|
32
|
-
>
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
/**
|
|
222
|
-
|
|
223
|
-
|
|
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`
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
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
|
-
/**
|
|
224
|
-
|
|
225
|
-
|
|
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`
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
85
|
-
|
|
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.
|
|
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"
|