@pulsepairs/sdk 0.8.0 → 0.9.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
@@ -389,6 +389,8 @@ in **bps** (10000 = full face), fees atomic. `Option` is `1 = UP`, `2 = DOWN`.
389
389
 
390
390
  ```
391
391
  Clients UpDownHttpClient, UpDownWsClient, wsUrlFromHttpBase
392
+ Maker writes UpDownHttpClient.postOrder, cancelOrder, amendOrder,
393
+ (v0.9.0) amendOrdersBulk, postOrdersBulk, bulkFailures
392
394
  EIP-712 ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES,
393
395
  buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData,
394
396
  freshSessionId, freshNonce, domainForSettlement,
@@ -411,6 +413,76 @@ Enums/types OrderType, OrderSide, Option, ApiConfig, PairConfig, PostOrderB
411
413
 
412
414
  ---
413
415
 
416
+ ## Cancel-and-replace, and bulk writes (v0.9.0)
417
+
418
+ **`amendOrder` replaces a resting order in one request.** Doing it as
419
+ cancel-then-place leaves the book without your quote for a round trip, and a
420
+ fill can land on the original inside that window — so the two-step is not just
421
+ slower, it is a different outcome. The server does both under one lock, and a
422
+ rejected amend leaves the **original** resting, never nothing.
423
+
424
+ ```ts
425
+ const res = await api.amendOrder({
426
+ cancelOrderId: resting.id,
427
+ order: { maker, market, option: Option.UP, side: 0, type: 0,
428
+ price: 5100, amount: "10000000", maxFee, nonce: freshNonce(),
429
+ expiry, signature },
430
+ });
431
+ res.replaced; // the order id that was retired
432
+ res.priorityKept; // false when the amend forfeited queue position
433
+ res.remaining; // atomic USDT carried over to the replacement
434
+ ```
435
+
436
+ `order` is a **freshly signed order**, exactly as `postOrder` takes — it carries
437
+ its own nonce and expiry. An amend is a new order, not an edit.
438
+
439
+ Pass `expectedRemaining` to make it conditional: the amend is refused with
440
+ `REMAINING_MISMATCH` unless the resting order still has exactly that much
441
+ unfilled. Omit it to amend regardless of intervening fills.
442
+
443
+ ### Bulk: read `results[]`, never the status code
444
+
445
+ `amendOrdersBulk` and `postOrdersBulk` answer **HTTP 200 with each order's
446
+ outcome in the body**. A rejection — bad signature, stale expiry, rate-limit
447
+ shed — never reaches a status code, so a caller that treats 200 as "all placed"
448
+ silently counts every shed order as a success. Use `bulkFailures`:
449
+
450
+ ```ts
451
+ const res = await api.postOrdersBulk({ orders: ladder });
452
+ for (const f of bulkFailures(res)) {
453
+ console.warn(`orders[${f.index}] rejected: ${f.error}`, f.scope ?? "");
454
+ }
455
+ ```
456
+
457
+ `index` is the position in the array you sent — results are positional.
458
+
459
+ Two behavioural differences worth knowing, because they change how latency
460
+ scales:
461
+
462
+ | | fan-out | latency vs batch size |
463
+ |---|---|---|
464
+ | `postOrdersBulk` | concurrent | roughly flat |
465
+ | `amendOrdersBulk` | **serial** | roughly linear |
466
+
467
+ Amends run serially on purpose: two amends racing on neighbouring price levels
468
+ would otherwise see each other's half-applied state. Both cap at 20 per request.
469
+
470
+ ### Verifying against a live matcher
471
+
472
+ `npm test` is network-free. To round-trip all three against the dev stack:
473
+
474
+ ```bash
475
+ UPND_INTEGRATION=1 PRIVATE_KEY=0x… npm run test:integration
476
+ ```
477
+
478
+ It rests an order, amends it, bulk-places two, bulk-amends those, and cancels
479
+ everything in a `finally` — including on failure. It defaults to dev and refuses
480
+ to run against prod without `UPND_ALLOW_PROD=1`, because it places real orders
481
+ and only dev's USDT is play money. Needs a live 5m market with >120s left, so
482
+ the keeper has to be cycling.
483
+
484
+ ---
485
+
414
486
  ## Field-name gotchas (live with them)
415
487
 
416
488
  - `OrderRow.nonce` / `OrderRow.expiry` are JSON **strings** (uint256 precision).
package/dist/http.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ApiConfig, Balance, CancelOrderBody, MarketDetail, MarketListItem, OrderBookFull, OrdersResponse, PairSymbol, PostOrderBody, Position, Stats, Trade, Version } from "./types.js";
1
+ import type { AmendOrderBody, AmendOrderResult, ApiConfig, Balance, BulkAmendBody, BulkAmendResults, BulkFailure, BulkOrdersBody, BulkOrdersResults, CancelOrderBody, MarketDetail, MarketListItem, OrderBookFull, OrdersResponse, PairSymbol, PostOrderBody, Position, Stats, Trade, Version } from "./types.js";
2
2
  import { type PortfolioPnl } from "./pnl.js";
3
3
  export declare class UpDownHttpClient {
4
4
  private readonly baseUrl;
@@ -85,5 +85,71 @@ export declare class UpDownHttpClient {
85
85
  id: string;
86
86
  status: "CANCEL_PENDING" | string;
87
87
  }>;
88
+ /**
89
+ * Atomic cancel-and-replace: retire a resting order and rest its replacement
90
+ * in ONE request.
91
+ *
92
+ * This is the call a quoting bot makes most. Doing it as cancel-then-place
93
+ * leaves the book without your quote for a round trip, and a fill can land
94
+ * on the original in that window — so the two-step is not just slower, it is
95
+ * a different outcome. The server does both under one lock; a rejected amend
96
+ * leaves the ORIGINAL resting, never nothing.
97
+ *
98
+ * `order` must be a freshly signed order, exactly as `postOrder` takes. It
99
+ * carries its own nonce and expiry: the replacement is a new order, not an
100
+ * edit of the old one.
101
+ *
102
+ * Rejections arrive as thrown errors here, unlike the bulk form — this route
103
+ * answers a real status code (404 `ORDER_NOT_RESTING`, 409
104
+ * `REMAINING_MISMATCH` / `FILL_IN_FLIGHT`, 400 `SCOPE_MISMATCH` /
105
+ * `EMPTY_REPLACEMENT`).
106
+ */
107
+ amendOrder(body: AmendOrderBody): Promise<AmendOrderResult>;
108
+ /**
109
+ * Many amends in one request — a ladder refresh.
110
+ *
111
+ * READ `results[]`, NOT THE STATUS CODE. This route answers `200` with each
112
+ * amend's outcome in the body, so a rejection never reaches `parseJson`'s
113
+ * throw. `bulkFailures()` below exists to make that hard to forget.
114
+ *
115
+ * Results are positional: `results[i]` is the outcome of `amends[i]`.
116
+ *
117
+ * Server-side these run SERIALLY, not concurrently — two amends racing on
118
+ * neighbouring price levels would otherwise see each other's half-applied
119
+ * state. So latency scales with batch size roughly linearly, unlike
120
+ * `postOrdersBulk`. Capped at 20 per request.
121
+ */
122
+ amendOrdersBulk(body: BulkAmendBody): Promise<BulkAmendResults>;
123
+ /**
124
+ * Place many orders in one request.
125
+ *
126
+ * Same 200-with-per-order-results contract as `amendOrdersBulk` — read
127
+ * `results[]`. Unlike amends these fan out CONCURRENTLY server-side, so the
128
+ * whole request costs roughly one order's latency rather than N.
129
+ *
130
+ * Results are positional: `results[i]` is the outcome of `orders[i]`.
131
+ * Capped at 20 per request.
132
+ */
133
+ postOrdersBulk(body: BulkOrdersBody): Promise<BulkOrdersResults>;
88
134
  }
135
+ /**
136
+ * The rejected entries of a bulk response, with their request index.
137
+ *
138
+ * Exists because the failure mode it prevents has already happened twice, on
139
+ * both sides of this integration: a bulk call answers `200`, the caller treats
140
+ * that as "all placed", and every per-order rejection — bad signature, stale
141
+ * expiry, rate-limit shed — is silently counted as a success. A reported "no
142
+ * 429s at 600/min" was a true statement about a blind metric for exactly this
143
+ * reason.
144
+ *
145
+ * `index` is the position in the request array, so a caller can correlate a
146
+ * failure back to the order it sent.
147
+ */
148
+ export declare function bulkFailures(response: {
149
+ results: Array<{
150
+ ok: boolean;
151
+ } & Record<string, unknown>>;
152
+ }): Array<BulkFailure & {
153
+ index: number;
154
+ }>;
89
155
  export declare function wsUrlFromHttpBase(httpBase: string): string;
package/dist/http.js CHANGED
@@ -181,6 +181,94 @@ export class UpDownHttpClient {
181
181
  });
182
182
  return parseJson(res);
183
183
  }
184
+ /**
185
+ * Atomic cancel-and-replace: retire a resting order and rest its replacement
186
+ * in ONE request.
187
+ *
188
+ * This is the call a quoting bot makes most. Doing it as cancel-then-place
189
+ * leaves the book without your quote for a round trip, and a fill can land
190
+ * on the original in that window — so the two-step is not just slower, it is
191
+ * a different outcome. The server does both under one lock; a rejected amend
192
+ * leaves the ORIGINAL resting, never nothing.
193
+ *
194
+ * `order` must be a freshly signed order, exactly as `postOrder` takes. It
195
+ * carries its own nonce and expiry: the replacement is a new order, not an
196
+ * edit of the old one.
197
+ *
198
+ * Rejections arrive as thrown errors here, unlike the bulk form — this route
199
+ * answers a real status code (404 `ORDER_NOT_RESTING`, 409
200
+ * `REMAINING_MISMATCH` / `FILL_IN_FLIGHT`, 400 `SCOPE_MISMATCH` /
201
+ * `EMPTY_REPLACEMENT`).
202
+ */
203
+ async amendOrder(body) {
204
+ const res = await fetch(buildUrl(this.baseUrl, "/orders/amend"), {
205
+ method: "POST",
206
+ headers: { "Content-Type": "application/json" },
207
+ body: JSON.stringify(body),
208
+ });
209
+ return parseJson(res);
210
+ }
211
+ /**
212
+ * Many amends in one request — a ladder refresh.
213
+ *
214
+ * READ `results[]`, NOT THE STATUS CODE. This route answers `200` with each
215
+ * amend's outcome in the body, so a rejection never reaches `parseJson`'s
216
+ * throw. `bulkFailures()` below exists to make that hard to forget.
217
+ *
218
+ * Results are positional: `results[i]` is the outcome of `amends[i]`.
219
+ *
220
+ * Server-side these run SERIALLY, not concurrently — two amends racing on
221
+ * neighbouring price levels would otherwise see each other's half-applied
222
+ * state. So latency scales with batch size roughly linearly, unlike
223
+ * `postOrdersBulk`. Capped at 20 per request.
224
+ */
225
+ async amendOrdersBulk(body) {
226
+ const res = await fetch(buildUrl(this.baseUrl, "/orders/amend/bulk"), {
227
+ method: "POST",
228
+ headers: { "Content-Type": "application/json" },
229
+ body: JSON.stringify(body),
230
+ });
231
+ return parseJson(res);
232
+ }
233
+ /**
234
+ * Place many orders in one request.
235
+ *
236
+ * Same 200-with-per-order-results contract as `amendOrdersBulk` — read
237
+ * `results[]`. Unlike amends these fan out CONCURRENTLY server-side, so the
238
+ * whole request costs roughly one order's latency rather than N.
239
+ *
240
+ * Results are positional: `results[i]` is the outcome of `orders[i]`.
241
+ * Capped at 20 per request.
242
+ */
243
+ async postOrdersBulk(body) {
244
+ const res = await fetch(buildUrl(this.baseUrl, "/orders/bulk"), {
245
+ method: "POST",
246
+ headers: { "Content-Type": "application/json" },
247
+ body: JSON.stringify(body),
248
+ });
249
+ return parseJson(res);
250
+ }
251
+ }
252
+ /**
253
+ * The rejected entries of a bulk response, with their request index.
254
+ *
255
+ * Exists because the failure mode it prevents has already happened twice, on
256
+ * both sides of this integration: a bulk call answers `200`, the caller treats
257
+ * that as "all placed", and every per-order rejection — bad signature, stale
258
+ * expiry, rate-limit shed — is silently counted as a success. A reported "no
259
+ * 429s at 600/min" was a true statement about a blind metric for exactly this
260
+ * reason.
261
+ *
262
+ * `index` is the position in the request array, so a caller can correlate a
263
+ * failure back to the order it sent.
264
+ */
265
+ export function bulkFailures(response) {
266
+ const out = [];
267
+ response.results.forEach((r, index) => {
268
+ if (!r.ok)
269
+ out.push({ ...r, index });
270
+ });
271
+ return out;
184
272
  }
185
273
  export function wsUrlFromHttpBase(httpBase) {
186
274
  const u = new URL(httpBase);
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { UpDownHttpClient, wsUrlFromHttpBase } from "./http.js";
1
+ export { UpDownHttpClient, wsUrlFromHttpBase, bulkFailures } from "./http.js";
2
2
  export { UpDownWsClient, type UpDownWsMessage, type SubscribePayload, type WsAuthCredentials, type ConnectAuthedOptions, } from "./ws.js";
3
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";
@@ -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, 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";
10
+ export { OrderType, OrderSide, Option, takerPriceBps, type AmendOrderBody, type AmendOrderResult, type ApiConfig, type Balance, type BulkAmendBody, type BulkAmendResults, type BulkFailure, type BulkOrdersBody, type BulkOrdersResults, 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 SubmittedOrder, type Stats, type Trade, type Version, } from "./types.js";
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // HTTP + WS clients
2
- export { UpDownHttpClient, wsUrlFromHttpBase } from "./http.js";
2
+ export { UpDownHttpClient, wsUrlFromHttpBase, bulkFailures } from "./http.js";
3
3
  export { UpDownWsClient, } from "./ws.js";
4
4
  // EIP-712 helpers
5
5
  export { ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES, buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData, freshSessionId, freshNonce, domainForSettlement, findPairBySettlement, parseCompositeMarketKey, centsToBps, bpsToCents, parseStake, assertStakeBounds, feeAtomic,
package/dist/types.d.ts CHANGED
@@ -244,6 +244,92 @@ export type Version = {
244
244
  };
245
245
  /** Atomic-USDT body for `POST /orders`. The backend re-verifies the
246
246
  * signature against the per-pair domain on receipt. */
247
+ /**
248
+ * An order as the API reports it back after a successful submission — the
249
+ * shape `POST /orders`, `POST /orders/bulk` and both amend routes return.
250
+ */
251
+ export type SubmittedOrder = {
252
+ id: string;
253
+ status: string;
254
+ market: string;
255
+ option: number;
256
+ side: string;
257
+ type: string;
258
+ price: number;
259
+ amount: string;
260
+ /** Atomic. `"0"` on a MARKET/IOC order that found no liquidity — the field
261
+ * that distinguishes "placed" from "placed and did nothing". */
262
+ filledAmount: string;
263
+ createdAt: number | string;
264
+ };
265
+ /**
266
+ * A per-order failure inside a `200`.
267
+ *
268
+ * `/orders/bulk` and `/orders/amend/bulk` answer 200 with each order's outcome
269
+ * in the body, so a rejection for a bad signature, a stale expiry or the
270
+ * per-maker rate limit NEVER surfaces as an HTTP status. Any success rate or
271
+ * latency figure derived from status codes silently excludes this entire
272
+ * class — a mistake both this venue and LedgerCore made independently in their
273
+ * own instrumentation. Read `results[]`.
274
+ */
275
+ export type BulkFailure = {
276
+ ok: false;
277
+ error: string;
278
+ code?: string;
279
+ /** `"maker"` when the per-wallet per-second bucket shed this order. */
280
+ scope?: string;
281
+ /** Present on an amend rejected for a fill that landed first. */
282
+ filled?: string;
283
+ remaining?: string;
284
+ };
285
+ /** One amend: retire `cancelOrderId`, rest `order` in its place, atomically. */
286
+ export type AmendOrderBody = {
287
+ /** The resting order to replace. */
288
+ cancelOrderId: string;
289
+ /**
290
+ * Optimistic-concurrency guard, atomic USDT. When set, the amend is refused
291
+ * with `REMAINING_MISMATCH` unless the resting order still has exactly this
292
+ * much unfilled. Omit to amend regardless of intervening fills.
293
+ */
294
+ expectedRemaining?: string;
295
+ /** The replacement — a fully signed order, same shape as `POST /orders`. */
296
+ order: PostOrderBody;
297
+ };
298
+ /** `POST /orders/amend` success. */
299
+ export type AmendOrderResult = SubmittedOrder & {
300
+ /** Echo of the `cancelOrderId` that was retired. */
301
+ replaced: string;
302
+ /**
303
+ * Whether the replacement kept the original's queue position. False when the
304
+ * amend changed a term that forfeits priority (price, or an increase in
305
+ * size) — worth reading, because a maker that assumes priority survived will
306
+ * mis-model its own fill probability.
307
+ */
308
+ priorityKept: boolean;
309
+ /** Atomic USDT already filled on the ORIGINAL order before it was retired. */
310
+ filled: string;
311
+ /** Atomic USDT that was still resting and has moved to the replacement. */
312
+ remaining: string;
313
+ };
314
+ /** `POST /orders/amend/bulk` — amends run SERIALLY server-side. */
315
+ export type BulkAmendBody = {
316
+ amends: AmendOrderBody[];
317
+ };
318
+ export type BulkAmendResults = {
319
+ results: Array<({
320
+ ok: true;
321
+ } & AmendOrderResult) | BulkFailure>;
322
+ };
323
+ /** `POST /orders/bulk` — orders fan out CONCURRENTLY server-side. */
324
+ export type BulkOrdersBody = {
325
+ orders: PostOrderBody[];
326
+ };
327
+ export type BulkOrdersResults = {
328
+ results: Array<{
329
+ ok: true;
330
+ order: SubmittedOrder;
331
+ } | BulkFailure>;
332
+ };
247
333
  export type PostOrderBody = {
248
334
  maker: string;
249
335
  /** Composite key `<settlement>-<marketId>` — used for routing only. */
package/dist/ws.js CHANGED
@@ -212,7 +212,48 @@ export class UpDownWsClient {
212
212
  const delay = attempt > 12 ? 30_000 : Math.min(30_000, 1000 * 2 ** Math.min(attempt, 5));
213
213
  this.timer = setTimeout(() => this.openSocket(), delay);
214
214
  };
215
- ws.onerror = () => ws.close();
215
+ // NEVER call close() synchronously from here.
216
+ //
217
+ // `onerror = () => ws.close()` stood here through 0.8.0 and took
218
+ // LedgerCore's process down twice against our 502s:
219
+ // "RangeError: Maximum call stack size exceeded" at dist/ws.js:215. On a
220
+ // WebSocket implementation that fails a CONNECTING socket SYNCHRONOUSLY,
221
+ // close() dispatches `error` before it returns, which re-enters this
222
+ // handler, which calls close() again — unbounded recursion on the stack
223
+ // rather than a loop that backoff could ever slow down. A handshake that
224
+ // fails fast, like a 502 from a load balancer, is exactly the trigger.
225
+ //
226
+ // Node's built-in WebSocket and the `ws` package both defer that error to a
227
+ // later tick and so survive it, which is why this went unnoticed here: the
228
+ // bug is invisible on the runtimes we test on and fatal on the ones we
229
+ // don't. The guard below therefore does not depend on knowing which
230
+ // implementation is underneath.
231
+ //
232
+ // The close is also redundant on every conforming implementation: a socket
233
+ // that emits `error` emits `close` afterwards, and `onclose` already owns
234
+ // reconnect. It is kept only for an implementation that errors without ever
235
+ // closing, deferred to a later tick where re-entry cannot grow the stack.
236
+ let errorHandled = false;
237
+ ws.onerror = () => {
238
+ if (errorHandled)
239
+ return; // re-entry is the bug; make it impossible
240
+ errorHandled = true;
241
+ if (this.closed || this.ws !== ws)
242
+ return;
243
+ setTimeout(() => {
244
+ // `onclose` normally fires on its own and has already scheduled the
245
+ // reconnect. Only force it if this socket errored and then just sat
246
+ // there, which would otherwise strand the client with no retry.
247
+ if (ws.readyState === 3 /* CLOSED */)
248
+ return;
249
+ try {
250
+ ws.close();
251
+ }
252
+ catch {
253
+ /* ignore */
254
+ }
255
+ }, 0);
256
+ };
216
257
  }
217
258
  async startHandshake() {
218
259
  if (!this.mode || !this.ws)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pulsepairs/sdk",
3
- "version": "0.8.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.9.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",
@@ -34,7 +34,8 @@
34
34
  "scripts": {
35
35
  "clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
36
36
  "build": "npm run clean && tsc",
37
- "test": "node scripts/eip712-golden.test.mjs && node scripts/rawtx-tier.test.mjs && node scripts/hot-key-safety.test.mjs && node scripts/reconcile.test.mjs && node scripts/pnl.test.mjs && node scripts/ws-subscribe.test.mjs",
37
+ "test": "node scripts/eip712-golden.test.mjs && node scripts/rawtx-tier.test.mjs && node scripts/hot-key-safety.test.mjs && node scripts/reconcile.test.mjs && node scripts/pnl.test.mjs && node scripts/ws-subscribe.test.mjs && node scripts/ws-onerror.test.mjs && node scripts/amend-wrappers.test.mjs && node scripts/amend-integration.test.mjs",
38
+ "test:integration": "UPND_INTEGRATION=1 node scripts/amend-integration.test.mjs",
38
39
  "prepublishOnly": "npm run build && npm test",
39
40
  "example:taker": "npx tsx examples/simple-taker.ts",
40
41
  "example:maker": "npx tsx examples/simple-maker.ts",