@pulsepairs/sdk 0.5.1 → 0.6.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/README.md CHANGED
@@ -19,6 +19,10 @@ It gives you three things:
19
19
  Alchemy smart-contract account (the SAME wallet rain.trade uses), plus
20
20
  gasless onboarding / approve / withdraw.
21
21
 
22
+ > This README is the quickstart. For the exhaustive reference — every export,
23
+ > its units, its failure modes, the WS protocol, the full type surface and a
24
+ > troubleshooting table — see **[`DOCUMENTATION.md`](./DOCUMENTATION.md)**.
25
+
22
26
  > **Live integration environment:** a public demo of the full stack (markets
23
27
  > cycling 24/7 on Arbitrum One, mock oracle, mintable test USDT) runs at
24
28
  > **https://demo-pulsepairs.rainwins.com** with the matcher API/WS at
package/dist/http.d.ts CHANGED
@@ -18,6 +18,18 @@ export declare class UpDownHttpClient {
18
18
  getOrderbook(marketAddress: string): Promise<OrderBookFull>;
19
19
  getBalance(wallet: string): Promise<Balance>;
20
20
  getPositions(wallet: string): Promise<Position[]>;
21
+ /**
22
+ * Open positions PLUS the wallet-level realized-from-sells scalar (atomic
23
+ * USDT, signed). Uses `?includeRealized=1`; `getPositions` keeps the plain
24
+ * bare-array response for callers that don't need the realized term.
25
+ *
26
+ * Tolerant of a backend that predates the envelope: if the body comes back a
27
+ * bare array, the realized term is simply reported as "0" rather than failing.
28
+ */
29
+ getPositionsWithRealized(wallet: string): Promise<{
30
+ positions: Position[];
31
+ realizedFromSells: string;
32
+ }>;
21
33
  getTrades(wallet: string, opts?: {
22
34
  limit?: number;
23
35
  offset?: number;
package/dist/http.js CHANGED
@@ -69,6 +69,21 @@ export class UpDownHttpClient {
69
69
  const res = await fetch(buildUrl(this.baseUrl, `/positions/${wallet}`));
70
70
  return parseJson(res);
71
71
  }
72
+ /**
73
+ * Open positions PLUS the wallet-level realized-from-sells scalar (atomic
74
+ * USDT, signed). Uses `?includeRealized=1`; `getPositions` keeps the plain
75
+ * bare-array response for callers that don't need the realized term.
76
+ *
77
+ * Tolerant of a backend that predates the envelope: if the body comes back a
78
+ * bare array, the realized term is simply reported as "0" rather than failing.
79
+ */
80
+ async getPositionsWithRealized(wallet) {
81
+ const res = await fetch(buildUrl(this.baseUrl, `/positions/${wallet}`, { includeRealized: 1 }));
82
+ const body = await parseJson(res);
83
+ if (Array.isArray(body))
84
+ return { positions: body, realizedFromSells: "0" };
85
+ return { positions: body.positions ?? [], realizedFromSells: body.realizedFromSells ?? "0" };
86
+ }
72
87
  async getTrades(wallet, opts) {
73
88
  const res = await fetch(buildUrl(this.baseUrl, `/trades/${wallet}`, {
74
89
  limit: opts?.limit,
@@ -91,9 +106,15 @@ export class UpDownHttpClient {
91
106
  * @param opts.usdtDecimals display-float decimals (default 6).
92
107
  */
93
108
  async getPnl(wallet, opts) {
94
- const positions = await this.getPositions(wallet);
109
+ // Fetch the realized-from-sells scalar alongside the positions so the
110
+ // rolled-up PnL carries realized (from manual sells) as well as unrealized.
111
+ // Backward-tolerant: an old backend yields "0" here, not an error.
112
+ const { positions, realizedFromSells } = await this.getPositionsWithRealized(wallet);
95
113
  if (positions.length === 0) {
96
- return computePortfolioPnl([], new Map(), { usdtDecimals: opts?.usdtDecimals });
114
+ return computePortfolioPnl([], new Map(), {
115
+ usdtDecimals: opts?.usdtDecimals,
116
+ realizedFromSells,
117
+ });
97
118
  }
98
119
  const marketKeys = Array.from(new Set(positions.map((p) => p.market)));
99
120
  const details = await Promise.all(marketKeys.map((key) => this.getMarket(key).catch(() => null)));
@@ -111,6 +132,7 @@ export class UpDownHttpClient {
111
132
  return computePortfolioPnl(positions, marketsByKey, {
112
133
  fees,
113
134
  usdtDecimals: opts?.usdtDecimals,
135
+ realizedFromSells,
114
136
  });
115
137
  }
116
138
  async getOrders(wallet, opts) {
package/dist/index.d.ts CHANGED
@@ -7,4 +7,4 @@ export { readOnChainHolderShares, reconcileFills, reportedFromPositions, type On
7
7
  export { UpDownAccountKitSigner, bareErc1271Signer, stripErc6492Wrapper, isErc6492Signature, type UpDownAccountKitConfig, type Eip1193Provider, type GrantSessionResult, type RawTypedDataSigner, } from "./accountKit.js";
8
8
  export { buildApproveSettlementTx, buildInstallOrderSessionTx, signWithOrderSession, type RawTransaction, type OrderSessionRecord, } from "./accountKit.js";
9
9
  export { PRICE_BPS_SCALE, WIN_MARK_BPS, LOSE_MARK_BPS, DEFAULT_USDT_DECIMALS, atomicToUsdt, markValueAtomic, midBps, markForOption, isResolvedWinner, positionPnl, feesPaidByWallet, portfolioPnl, computePortfolioPnl, type MarkSource, type PositionPnl, type PortfolioPnl, type PositionPnlOptions, } from "./pnl.js";
10
- export { OrderType, OrderSide, Option, type ApiConfig, type Balance, type CancelOrderBody, type Eip712Domain, type MarketDetail, type MarketListItem, type OptionValue, type OrderBookFull, type OrderBookLevel, type OrderBookSide, type OrderRow, type OrderSideKey, type OrderSideValue, type OrderStatus, type OrderTypeKey, type OrderTypeValue, type OrdersResponse, type PairConfig, type PairSymbol, type PostOrderBody, type Position, type Stats, type Trade, type Version, } from "./types.js";
10
+ export { OrderType, OrderSide, Option, takerPriceBps, type ApiConfig, type Balance, type CancelOrderBody, type Eip712Domain, type MarketDetail, type MarketListItem, type OptionValue, type OrderBookFull, type OrderBookLevel, type OrderBookSide, type OrderRow, type OrderSideKey, type OrderSideValue, type OrderStatus, type OrderTypeKey, type OrderTypeValue, type OrdersResponse, type PairConfig, type PairSymbol, type PostOrderBody, type Position, type Stats, type Trade, type Version, } from "./types.js";
package/dist/index.js CHANGED
@@ -25,4 +25,4 @@ export { buildApproveSettlementTx, buildInstallOrderSessionTx, signWithOrderSess
25
25
  // contract). `UpDownHttpClient.getPnl` is the batteries-included wrapper.
26
26
  export { PRICE_BPS_SCALE, WIN_MARK_BPS, LOSE_MARK_BPS, DEFAULT_USDT_DECIMALS, atomicToUsdt, markValueAtomic, midBps, markForOption, isResolvedWinner, positionPnl, feesPaidByWallet, portfolioPnl, computePortfolioPnl, } from "./pnl.js";
27
27
  // Types
28
- export { OrderType, OrderSide, Option, } from "./types.js";
28
+ export { OrderType, OrderSide, Option, takerPriceBps, } from "./types.js";
package/dist/pnl.d.ts CHANGED
@@ -82,6 +82,18 @@ export type PortfolioPnl = {
82
82
  * (see `feesPaidByWallet`); "0" otherwise. Not netted into `unrealizedPnl`
83
83
  * — cost basis is fee-exclusive, so fees stay a separate, explicit line. */
84
84
  fees: string;
85
+ /**
86
+ * Atomic USDT (signed) realized P&L booked from manual market-SELLs, across
87
+ * ALL of the wallet's positions — including ones sold to zero net shares,
88
+ * which never appear in `positions`. "0" when the caller did not supply it
89
+ * (or the backend predates the `?includeRealized=1` envelope).
90
+ *
91
+ * Report it ALONGSIDE `unrealizedPnl`, which already carries settled
92
+ * survivors at full-face/zero (`resolved: true`); the two together are the
93
+ * wallet's complete realized+unrealized P&L. It is average-cost per sell, so
94
+ * only the combined figure is order-invariant — never surface it standalone.
95
+ */
96
+ realizedFromSells: string;
85
97
  };
86
98
  /** Options controlling how one position is valued. */
87
99
  export type PositionPnlOptions = {
@@ -130,9 +142,13 @@ export declare function positionPnl(position: Position, opts?: PositionPnlOption
130
142
  * so treat it as an upper bound on the wallet's own fee drag. */
131
143
  export declare function feesPaidByWallet(wallet: string, trades: Trade[]): string;
132
144
  /** Roll individual position PnLs up into a portfolio total. Pass `fees` (from
133
- * `feesPaidByWallet`) to carry a fee line through; it is reported, not netted. */
145
+ * `feesPaidByWallet`) to carry a fee line through; it is reported, not netted.
146
+ * Pass `realizedFromSells` (atomic USDT string, from `GET
147
+ * /positions/:wallet?includeRealized=1`) to carry the manual-sell realized
148
+ * term; likewise reported, never netted into `unrealizedPnl`. */
134
149
  export declare function portfolioPnl(items: PositionPnl[], opts?: {
135
150
  fees?: string;
151
+ realizedFromSells?: string;
136
152
  usdtDecimals?: number;
137
153
  }): PortfolioPnl;
138
154
  /**
@@ -146,5 +162,6 @@ export declare function portfolioPnl(items: PositionPnl[], opts?: {
146
162
  */
147
163
  export declare function computePortfolioPnl(positions: Position[], marketsByKey: Map<string, Pick<MarketDetail, "winner" | "orderBook">>, opts?: {
148
164
  fees?: string;
165
+ realizedFromSells?: string;
149
166
  usdtDecimals?: number;
150
167
  }): PortfolioPnl;
package/dist/pnl.js CHANGED
@@ -113,7 +113,10 @@ export function feesPaidByWallet(wallet, trades) {
113
113
  return total.toString();
114
114
  }
115
115
  /** Roll individual position PnLs up into a portfolio total. Pass `fees` (from
116
- * `feesPaidByWallet`) to carry a fee line through; it is reported, not netted. */
116
+ * `feesPaidByWallet`) to carry a fee line through; it is reported, not netted.
117
+ * Pass `realizedFromSells` (atomic USDT string, from `GET
118
+ * /positions/:wallet?includeRealized=1`) to carry the manual-sell realized
119
+ * term; likewise reported, never netted into `unrealizedPnl`. */
117
120
  export function portfolioPnl(items, opts = {}) {
118
121
  const decimals = opts.usdtDecimals ?? DEFAULT_USDT_DECIMALS;
119
122
  let costBasis = 0n;
@@ -131,6 +134,7 @@ export function portfolioPnl(items, opts = {}) {
131
134
  unrealizedPnlUsdt: atomicToUsdt(unrealizedPnl, decimals),
132
135
  roiPct: pctOf(unrealizedPnl, costBasis),
133
136
  fees: opts.fees ?? "0",
137
+ realizedFromSells: opts.realizedFromSells ?? "0",
134
138
  };
135
139
  }
136
140
  /**
package/dist/types.d.ts CHANGED
@@ -140,18 +140,40 @@ export type Position = {
140
140
  export type Trade = {
141
141
  tradeId: string;
142
142
  market: string;
143
+ /** The TAKER leg's option (1=UP, 2=DOWN). On a MINT/MERGE the maker leg is
144
+ * the opposite option. */
143
145
  option: number;
144
146
  buyOrderId: string;
145
147
  sellOrderId: string;
148
+ /** NORMAL: the buyer. MINT/MERGE: the aggressor (taker) — a buyer on MINT,
149
+ * a seller on MERGE. */
146
150
  buyer: string;
151
+ /** NORMAL: the seller. MINT/MERGE: the resting maker, on the OPPOSITE option
152
+ * to `option`. */
147
153
  seller: string;
154
+ /** Basis points — the resting MAKER's pegged execution price. On a
155
+ * complementary fill the taker executed at the complement; render taker
156
+ * fills with `takerPrice`, not this. */
148
157
  price: number;
149
158
  amount: string;
150
159
  platformFee: string;
151
160
  makerFee: string;
152
161
  settlementStatus: string;
153
162
  createdAt: string;
163
+ /** Match geometry. NORMAL = same-option fill. MINT/MERGE = complementary
164
+ * cross (opposite-option legs). Optional so a pre-2026-07-17 backend
165
+ * (field absent) degrades rather than breaking a consumer. */
166
+ matchType?: "NORMAL" | "MINT" | "MERGE";
167
+ /** Basis points the taker actually executed at: `price` on NORMAL,
168
+ * `10000 − price` on MINT/MERGE. Prefer this over `price` when rendering a
169
+ * wallet's own fills. Optional for the same back-compat reason. */
170
+ takerPrice?: number;
154
171
  };
172
+ /** The price the AGGRESSOR paid/received per share, in bps. Mirrors the
173
+ * backend's `takerPriceBps`: the complement on a complementary fill, else the
174
+ * row price. Falls back to computing the complement if a row predates the
175
+ * `takerPrice` field. */
176
+ export declare function takerPriceBps(trade: Pick<Trade, "price" | "matchType" | "takerPrice">): number;
155
177
  export type OrderStatus = "OPEN" | "PARTIALLY_FILLED" | "FILLED" | "CANCEL_PENDING" | "CANCELLED" | string;
156
178
  export type OrderRow = {
157
179
  orderId: string;
package/dist/types.js CHANGED
@@ -18,3 +18,14 @@ export const Option = {
18
18
  UP: 1,
19
19
  DOWN: 2,
20
20
  };
21
+ /** The price the AGGRESSOR paid/received per share, in bps. Mirrors the
22
+ * backend's `takerPriceBps`: the complement on a complementary fill, else the
23
+ * row price. Falls back to computing the complement if a row predates the
24
+ * `takerPrice` field. */
25
+ export function takerPriceBps(trade) {
26
+ if (typeof trade.takerPrice === "number")
27
+ return trade.takerPrice;
28
+ return trade.matchType === "MINT" || trade.matchType === "MERGE"
29
+ ? 10000 - trade.price
30
+ : trade.price;
31
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pulsepairs/sdk",
3
- "version": "0.5.1",
3
+ "version": "0.6.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",
@@ -27,7 +27,8 @@
27
27
  },
28
28
  "files": [
29
29
  "dist",
30
- "README.md"
30
+ "README.md",
31
+ "DOCUMENTATION.md"
31
32
  ],
32
33
  "sideEffects": false,
33
34
  "scripts": {