pmxtjs 2.48.6 → 2.49.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.
Files changed (74) hide show
  1. package/dist/esm/generated/src/models/OrderLevel.d.ts +6 -0
  2. package/dist/esm/generated/src/models/OrderLevel.js +2 -0
  3. package/dist/esm/index.d.ts +2 -0
  4. package/dist/esm/index.js +1 -0
  5. package/dist/esm/pmxt/client.d.ts +106 -5
  6. package/dist/esm/pmxt/client.js +400 -6
  7. package/dist/esm/pmxt/constants.d.ts +11 -0
  8. package/dist/esm/pmxt/constants.js +13 -0
  9. package/dist/esm/pmxt/errors.d.ts +3 -0
  10. package/dist/esm/pmxt/errors.js +9 -0
  11. package/dist/esm/pmxt/escrow.d.ts +39 -0
  12. package/dist/esm/pmxt/escrow.js +78 -0
  13. package/dist/esm/pmxt/feed-client.d.ts +3 -0
  14. package/dist/esm/pmxt/feed-client.js +11 -2
  15. package/dist/esm/pmxt/hosted-errors.d.ts +84 -0
  16. package/dist/esm/pmxt/hosted-errors.js +186 -0
  17. package/dist/esm/pmxt/hosted-mappers.d.ts +45 -0
  18. package/dist/esm/pmxt/hosted-mappers.js +291 -0
  19. package/dist/esm/pmxt/hosted-routing.d.ts +69 -0
  20. package/dist/esm/pmxt/hosted-routing.js +119 -0
  21. package/dist/esm/pmxt/hosted-typed-data.d.ts +36 -0
  22. package/dist/esm/pmxt/hosted-typed-data.js +580 -0
  23. package/dist/esm/pmxt/models.d.ts +46 -8
  24. package/dist/esm/pmxt/server-manager.d.ts +4 -0
  25. package/dist/esm/pmxt/server-manager.js +6 -0
  26. package/dist/esm/pmxt/signers.d.ts +57 -0
  27. package/dist/esm/pmxt/signers.js +50 -0
  28. package/dist/esm/pmxt/ws-client.js +2 -1
  29. package/dist/generated/src/models/OrderLevel.d.ts +6 -0
  30. package/dist/generated/src/models/OrderLevel.js +2 -0
  31. package/dist/index.d.ts +2 -0
  32. package/dist/index.js +1 -0
  33. package/dist/pmxt/client.d.ts +106 -5
  34. package/dist/pmxt/client.js +399 -5
  35. package/dist/pmxt/constants.d.ts +11 -0
  36. package/dist/pmxt/constants.js +14 -1
  37. package/dist/pmxt/errors.d.ts +3 -0
  38. package/dist/pmxt/errors.js +11 -1
  39. package/dist/pmxt/escrow.d.ts +39 -0
  40. package/dist/pmxt/escrow.js +82 -0
  41. package/dist/pmxt/feed-client.d.ts +3 -0
  42. package/dist/pmxt/feed-client.js +11 -2
  43. package/dist/pmxt/hosted-errors.d.ts +84 -0
  44. package/dist/pmxt/hosted-errors.js +201 -0
  45. package/dist/pmxt/hosted-mappers.d.ts +45 -0
  46. package/dist/pmxt/hosted-mappers.js +302 -0
  47. package/dist/pmxt/hosted-routing.d.ts +69 -0
  48. package/dist/pmxt/hosted-routing.js +126 -0
  49. package/dist/pmxt/hosted-typed-data.d.ts +36 -0
  50. package/dist/pmxt/hosted-typed-data.js +619 -0
  51. package/dist/pmxt/models.d.ts +46 -8
  52. package/dist/pmxt/server-manager.d.ts +4 -0
  53. package/dist/pmxt/server-manager.js +6 -0
  54. package/dist/pmxt/signers.d.ts +57 -0
  55. package/dist/pmxt/signers.js +55 -0
  56. package/dist/pmxt/ws-client.js +2 -1
  57. package/generated/docs/OrderLevel.md +2 -0
  58. package/generated/package.json +1 -1
  59. package/generated/src/models/OrderLevel.ts +8 -0
  60. package/index.ts +1 -0
  61. package/package.json +11 -2
  62. package/pmxt/client.ts +495 -9
  63. package/pmxt/constants.ts +15 -0
  64. package/pmxt/errors.ts +11 -0
  65. package/pmxt/escrow.ts +93 -0
  66. package/pmxt/feed-client.ts +14 -2
  67. package/pmxt/hosted-errors.ts +216 -0
  68. package/pmxt/hosted-mappers.ts +312 -0
  69. package/pmxt/hosted-routing.ts +165 -0
  70. package/pmxt/hosted-typed-data.ts +767 -0
  71. package/pmxt/models.ts +65 -8
  72. package/pmxt/server-manager.ts +7 -0
  73. package/pmxt/signers.ts +86 -0
  74. package/pmxt/ws-client.ts +2 -1
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Hosted trading v0 response mappers.
3
+ *
4
+ * The hosted trading API exposes explicit `/v0/*` JSON shapes. These helpers
5
+ * translate those wire dictionaries to the SDK TypeScript interfaces.
6
+ *
7
+ * Mirrors `sdks/python/pmxt/_hosted_mappers.py`.
8
+ */
9
+ import { InvalidOrder } from "./errors.js";
10
+ // ---------------------------------------------------------------------------
11
+ // Precision helper
12
+ // ---------------------------------------------------------------------------
13
+ const SIX_DEC_SCALE = 1000000n;
14
+ /**
15
+ * Convert a decimal amount to integer micro-units (6-decimal grid).
16
+ *
17
+ * Rejects amounts whose fractional part has more than 6 digits. Pure integer
18
+ * math via BigInt — no float rounding involved.
19
+ */
20
+ export function to6dec(amount) {
21
+ const str = typeof amount === "bigint" ? `${amount}` : String(amount);
22
+ const negative = str.startsWith("-");
23
+ const cleaned = negative ? str.slice(1) : str;
24
+ const [intPart, fracPart = ""] = cleaned.split(".");
25
+ if (fracPart.length > 6) {
26
+ throw new InvalidOrder(`amount precision exceeds 6 decimals: ${amount}`);
27
+ }
28
+ if (!/^\d*$/.test(intPart) || !/^\d*$/.test(fracPart)) {
29
+ throw new InvalidOrder(`invalid amount: ${amount}`);
30
+ }
31
+ const padded = (fracPart + "000000").slice(0, 6);
32
+ const intMag = BigInt(intPart || "0");
33
+ const scaled = intMag * SIX_DEC_SCALE + BigInt(padded);
34
+ return negative ? -scaled : scaled;
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // Order mappers
38
+ // ---------------------------------------------------------------------------
39
+ /** Map an `OrderV0` JSON object to {@link Order}. */
40
+ export function orderFromV0(payload) {
41
+ const id = strOrEmpty(payload["id"]);
42
+ const marketId = strOrEmpty(payload["market_id"] ?? payload["marketId"]);
43
+ const outcomeId = strOrEmpty(payload["outcome_id"] ?? payload["outcomeId"]);
44
+ const status = strOrEmpty(payload["status"]);
45
+ const sideRaw = payload["side"];
46
+ const side = sideRaw === "sell" ? "sell" : "buy";
47
+ const typeRaw = payload["type"];
48
+ const type = typeRaw === "limit" ? "limit" : "market";
49
+ const order = {
50
+ id,
51
+ marketId,
52
+ outcomeId,
53
+ side,
54
+ type,
55
+ amount: floatOrZero(payload["amount"]),
56
+ status,
57
+ filled: floatOrZero(payload["filled"]),
58
+ remaining: floatOrZero(payload["remaining"]),
59
+ timestamp: timestampToMs(payload["timestamp"]),
60
+ };
61
+ const price = floatOrUndefined(payload["price"]);
62
+ if (price !== undefined)
63
+ order.price = price;
64
+ const fee = floatOrUndefined(payload["fee"]);
65
+ if (fee !== undefined)
66
+ order.fee = fee;
67
+ return order;
68
+ }
69
+ /** Map an {@link Order} back to an `OrderV0` JSON object. */
70
+ export function orderToV0(order) {
71
+ const out = {
72
+ id: order.id,
73
+ market_id: order.marketId,
74
+ outcome_id: order.outcomeId,
75
+ side: order.side,
76
+ type: order.type,
77
+ amount: order.amount,
78
+ status: order.status,
79
+ filled: order.filled,
80
+ remaining: order.remaining,
81
+ timestamp: msToTimestamp(order.timestamp),
82
+ };
83
+ if (order.price !== undefined)
84
+ out["price"] = order.price;
85
+ if (order.fee !== undefined)
86
+ out["fee"] = order.fee;
87
+ return out;
88
+ }
89
+ // ---------------------------------------------------------------------------
90
+ // UserTrade mappers
91
+ // ---------------------------------------------------------------------------
92
+ /** Map a `UserTradeV0` JSON object to {@link UserTrade}. */
93
+ export function userTradeFromV0(payload) {
94
+ const sideRaw = payload["side"];
95
+ const side = sideRaw === "buy" || sideRaw === "sell" ? sideRaw : "unknown";
96
+ const trade = {
97
+ id: strOrEmpty(payload["id"]),
98
+ price: floatOrZero(payload["price"]),
99
+ amount: floatOrZero(payload["amount"]),
100
+ side,
101
+ timestamp: timestampToMs(payload["timestamp"]),
102
+ };
103
+ const orderId = strOrUndefined(payload["order_id"] ?? payload["orderId"]);
104
+ if (orderId !== undefined)
105
+ trade.orderId = orderId;
106
+ const outcomeId = strOrUndefined(payload["outcome_id"] ?? payload["outcomeId"]);
107
+ if (outcomeId !== undefined)
108
+ trade.outcomeId = outcomeId;
109
+ const marketId = strOrUndefined(payload["market_id"] ?? payload["marketId"]);
110
+ if (marketId !== undefined)
111
+ trade.marketId = marketId;
112
+ return trade;
113
+ }
114
+ /** Map a {@link UserTrade} back to a `UserTradeV0` JSON object. */
115
+ export function userTradeToV0(trade) {
116
+ const out = {
117
+ id: trade.id,
118
+ side: trade.side,
119
+ amount: trade.amount,
120
+ price: trade.price,
121
+ timestamp: msToTimestamp(trade.timestamp),
122
+ };
123
+ if (trade.orderId !== undefined)
124
+ out["order_id"] = trade.orderId;
125
+ if (trade.outcomeId !== undefined)
126
+ out["outcome_id"] = trade.outcomeId;
127
+ if (trade.marketId !== undefined)
128
+ out["market_id"] = trade.marketId;
129
+ return out;
130
+ }
131
+ // ---------------------------------------------------------------------------
132
+ // Position mappers
133
+ // ---------------------------------------------------------------------------
134
+ /**
135
+ * Map a `PositionV0` JSON object to {@link Position}.
136
+ *
137
+ * Optional fields (`outcomeLabel`, `entryPrice`, `currentPrice`,
138
+ * `unrealizedPnL`, `realizedPnL`) surface as `undefined` when missing —
139
+ * **never** a fake `0` or `""`. This keeps the SDK honest about which data
140
+ * the server actually provided.
141
+ */
142
+ export function positionFromV0(payload) {
143
+ const size = floatOrZero(payload["shares"] ?? payload["size"]);
144
+ const entryPrice = floatOrUndefined(payload["entry_price"] ?? payload["entryPrice"]);
145
+ const currentPrice = floatOrUndefined(payload["current_price"] ?? payload["currentPrice"]);
146
+ let unrealizedPnL = floatOrUndefined(payload["unrealized_pnl"] ?? payload["unrealizedPnl"] ?? payload["unrealizedPnL"]);
147
+ if (unrealizedPnL === undefined &&
148
+ entryPrice !== undefined &&
149
+ currentPrice !== undefined) {
150
+ unrealizedPnL = (currentPrice - entryPrice) * size;
151
+ }
152
+ // Position requires marketId/outcomeId/outcomeLabel/entryPrice/currentPrice/unrealizedPnL
153
+ // in the current interface. We construct with safe defaults and only override
154
+ // when present. Per plan v5: the SDK should NOT fabricate financial data —
155
+ // when the server didn't supply entryPrice/currentPrice/outcomeLabel, we mark
156
+ // them via undefined (cast through a partial since the existing Position type
157
+ // hasn't yet been widened to Optional in this parallel-agent change).
158
+ const position = {
159
+ marketId: strOrEmpty(payload["market_id"] ?? payload["marketId"]),
160
+ outcomeId: strOrEmpty(payload["outcome_id"] ?? payload["outcomeId"]),
161
+ outcomeLabel: strOrUndefined(payload["outcome_label"] ?? payload["outcomeLabel"]),
162
+ size,
163
+ entryPrice: entryPrice,
164
+ currentPrice: currentPrice,
165
+ unrealizedPnL: unrealizedPnL,
166
+ realizedPnL: floatOrUndefined(payload["realized_pnl"] ?? payload["realizedPnl"] ?? payload["realizedPnL"]),
167
+ };
168
+ return position;
169
+ }
170
+ /** Map a {@link Position} back to a `PositionV0` JSON object. */
171
+ export function positionToV0(position) {
172
+ const out = {
173
+ market_id: position.marketId,
174
+ outcome_id: position.outcomeId,
175
+ shares: position.size,
176
+ };
177
+ if (position.outcomeLabel !== undefined && position.outcomeLabel !== "") {
178
+ out["outcome_label"] = position.outcomeLabel;
179
+ }
180
+ if (position.entryPrice !== undefined)
181
+ out["entry_price"] = position.entryPrice;
182
+ if (position.currentPrice !== undefined)
183
+ out["current_price"] = position.currentPrice;
184
+ if (position.unrealizedPnL !== undefined)
185
+ out["unrealized_pnl"] = position.unrealizedPnL;
186
+ if (position.realizedPnL !== undefined)
187
+ out["realized_pnl"] = position.realizedPnL;
188
+ return out;
189
+ }
190
+ // ---------------------------------------------------------------------------
191
+ // Balance mappers
192
+ // ---------------------------------------------------------------------------
193
+ /**
194
+ * Map a `BalanceV0` JSON object to {@link Balance}.
195
+ *
196
+ * Hosted-mode semantic: PreFundedEscrow doesn't reserve funds for resting
197
+ * orders, so `available = total` and `locked = 0`. Concurrent limit orders
198
+ * may fail at fill time if cumulative cost exceeds the escrow balance.
199
+ */
200
+ export function balanceFromV0(payload) {
201
+ const total = floatOrZero(payload["amount"] ?? payload["total"]);
202
+ const currency = strOrUndefined(payload["currency"]) ?? "USDC";
203
+ return {
204
+ currency,
205
+ total,
206
+ available: total,
207
+ locked: 0,
208
+ };
209
+ }
210
+ /** Map a {@link Balance} back to a `BalanceV0` JSON object. */
211
+ export function balanceToV0(balance) {
212
+ return {
213
+ currency: balance.currency,
214
+ amount: balance.total,
215
+ };
216
+ }
217
+ // ---------------------------------------------------------------------------
218
+ // Internal helpers
219
+ // ---------------------------------------------------------------------------
220
+ function strOrUndefined(value) {
221
+ if (value === null || value === undefined)
222
+ return undefined;
223
+ if (typeof value === "string")
224
+ return value || undefined;
225
+ return String(value);
226
+ }
227
+ function strOrEmpty(value) {
228
+ if (value === null || value === undefined)
229
+ return "";
230
+ if (typeof value === "string")
231
+ return value;
232
+ return String(value);
233
+ }
234
+ function floatOrUndefined(value) {
235
+ if (value === null || value === undefined)
236
+ return undefined;
237
+ if (typeof value === "number")
238
+ return Number.isFinite(value) ? value : undefined;
239
+ if (typeof value === "bigint")
240
+ return Number(value);
241
+ if (typeof value === "string") {
242
+ if (!value)
243
+ return undefined;
244
+ const parsed = Number(value);
245
+ return Number.isFinite(parsed) ? parsed : undefined;
246
+ }
247
+ return undefined;
248
+ }
249
+ function floatOrZero(value) {
250
+ const converted = floatOrUndefined(value);
251
+ return converted !== undefined ? converted : 0;
252
+ }
253
+ /**
254
+ * Parse an ISO-8601 string (or numeric) timestamp to milliseconds since epoch.
255
+ * Returns `0` if the input is null/undefined/empty/unparseable.
256
+ */
257
+ function timestampToMs(value) {
258
+ if (value === null || value === undefined)
259
+ return 0;
260
+ if (typeof value === "number") {
261
+ return Number.isFinite(value) ? Math.trunc(value) : 0;
262
+ }
263
+ if (typeof value === "bigint")
264
+ return Number(value);
265
+ if (typeof value === "string") {
266
+ if (!value)
267
+ return 0;
268
+ const normalized = value.endsWith("Z")
269
+ ? value
270
+ : /[+-]\d{2}:?\d{2}$/.test(value)
271
+ ? value
272
+ : value + "Z";
273
+ const ms = Date.parse(normalized);
274
+ return Number.isFinite(ms) ? ms : 0;
275
+ }
276
+ return 0;
277
+ }
278
+ /** Convert milliseconds-since-epoch back to an ISO-8601 string. */
279
+ function msToTimestamp(value) {
280
+ if (value === null || value === undefined)
281
+ return undefined;
282
+ if (typeof value === "string")
283
+ return value;
284
+ if (typeof value === "number" && Number.isFinite(value)) {
285
+ return new Date(value).toISOString();
286
+ }
287
+ if (typeof value === "bigint") {
288
+ return new Date(Number(value)).toISOString();
289
+ }
290
+ return undefined;
291
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Hosted-mode HTTP routing for the pmxt TypeScript SDK.
3
+ *
4
+ * When a client is configured with a `pmxtApiKey`, supported method calls are
5
+ * dispatched to the pmxt-hosted trading API instead of the venue's native
6
+ * client. This module owns:
7
+ *
8
+ * - the static route table mapping SDK method names to hosted endpoints,
9
+ * - the venue allowlist for hosted trading,
10
+ * - wallet address resolution for user-scoped reads/trades, and
11
+ * - the low-level `_tradingRequest` HTTP helper.
12
+ *
13
+ * Mirrors `sdks/python/pmxt/_hosted_routing.py`.
14
+ */
15
+ export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
16
+ export type HostedBase = "catalog" | "trading";
17
+ export declare const HOSTED_CATALOG_BASE_URL = "https://api.pmxt.dev";
18
+ export declare const HOSTED_TRADING_BASE_URL = "https://trade.pmxt.dev";
19
+ export declare const HOSTED_TRADING_VENUES: ReadonlySet<string>;
20
+ export interface HostedRoute {
21
+ method: HttpMethod;
22
+ path: string;
23
+ base: HostedBase;
24
+ requiresWalletAddress?: boolean;
25
+ }
26
+ export declare const HOSTED_METHOD_ROUTES: ReadonlyMap<string, HostedRoute>;
27
+ /**
28
+ * Minimal duck-typed view of a venue client used by hosted-mode helpers.
29
+ *
30
+ * Defining this as a structural interface keeps `hosted-routing` decoupled
31
+ * from the concrete `Exchange` class and makes unit testing trivial.
32
+ */
33
+ export interface HostedClientLike {
34
+ pmxtApiKey?: string;
35
+ exchangeName: string;
36
+ walletAddress?: string;
37
+ }
38
+ /**
39
+ * Throw {@link NotSupported} if the client is in hosted mode (i.e. has a
40
+ * `pmxtApiKey`) but targets a venue outside the hosted trading allowlist.
41
+ * No-op for non-hosted clients.
42
+ */
43
+ export declare function ensureHostedTradingSupported(client: HostedClientLike): void;
44
+ /**
45
+ * Resolve the wallet address to use for a user-scoped hosted call, preferring
46
+ * an explicit per-call override over the client default. Throws
47
+ * {@link MissingWalletAddress} when neither is set.
48
+ */
49
+ export declare function resolveWalletAddress(client: HostedClientLike, override?: string): string;
50
+ /**
51
+ * Substitute `{name}` placeholders in a route path with URL-encoded values
52
+ * from `params`. Throws if a referenced parameter is missing.
53
+ */
54
+ export declare function formatRoutePath(route: HostedRoute, params: Record<string, string | number | undefined>): string;
55
+ export interface TradingRequestOptions {
56
+ method: HttpMethod;
57
+ path: string;
58
+ body?: unknown;
59
+ params?: Record<string, string | number | boolean | undefined>;
60
+ }
61
+ /**
62
+ * Issue a request against the hosted trading API.
63
+ *
64
+ * NOTE: writes (POST/PUT/DELETE) are NEVER retried. `built_order_id` and
65
+ * `cancel_id` are single-use server-side, so replaying a lost response would
66
+ * 404 even though the original write succeeded. Callers needing retry logic
67
+ * must layer it on top with awareness of this invariant.
68
+ */
69
+ export declare function _tradingRequest(client: HostedClientLike, opts: TradingRequestOptions): Promise<unknown>;
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Hosted-mode HTTP routing for the pmxt TypeScript SDK.
3
+ *
4
+ * When a client is configured with a `pmxtApiKey`, supported method calls are
5
+ * dispatched to the pmxt-hosted trading API instead of the venue's native
6
+ * client. This module owns:
7
+ *
8
+ * - the static route table mapping SDK method names to hosted endpoints,
9
+ * - the venue allowlist for hosted trading,
10
+ * - wallet address resolution for user-scoped reads/trades, and
11
+ * - the low-level `_tradingRequest` HTTP helper.
12
+ *
13
+ * Mirrors `sdks/python/pmxt/_hosted_routing.py`.
14
+ */
15
+ import { NotSupported } from "./errors.js";
16
+ import { MissingWalletAddress, raiseFromResponse } from "./hosted-errors.js";
17
+ export const HOSTED_CATALOG_BASE_URL = "https://api.pmxt.dev";
18
+ export const HOSTED_TRADING_BASE_URL = "https://trade.pmxt.dev";
19
+ export const HOSTED_TRADING_VENUES = new Set([
20
+ "polymarket",
21
+ "opinion",
22
+ ]);
23
+ export const HOSTED_METHOD_ROUTES = new Map([
24
+ ["createOrder", { method: "POST", path: "/v0/trade/build-order", base: "trading" }],
25
+ ["buildOrder", { method: "POST", path: "/v0/trade/build-order", base: "trading" }],
26
+ ["submitOrder", { method: "POST", path: "/v0/trade/submit-order", base: "trading" }],
27
+ ["cancelOrderBuild", { method: "POST", path: "/v0/orders/cancel/build", base: "trading" }],
28
+ ["cancelOrder", { method: "POST", path: "/v0/orders/cancel", base: "trading" }],
29
+ ["fetchOrder", { method: "GET", path: "/v0/orders/{order_id}", base: "trading" }],
30
+ ["fetchOpenOrders", { method: "GET", path: "/v0/orders/open", base: "trading", requiresWalletAddress: true }],
31
+ ["fetchMyTrades", { method: "GET", path: "/v0/user/{address}/trades", base: "trading", requiresWalletAddress: true }],
32
+ ["fetchBalance", { method: "GET", path: "/v0/user/{address}/balances", base: "trading", requiresWalletAddress: true }],
33
+ ["fetchPositions", { method: "GET", path: "/v0/user/{address}/positions", base: "trading", requiresWalletAddress: true }],
34
+ ["escrowApproveTx", { method: "POST", path: "/v0/escrow/approve", base: "trading" }],
35
+ ["escrowDepositTx", { method: "POST", path: "/v0/escrow/deposit", base: "trading" }],
36
+ ["escrowWithdrawTx", { method: "POST", path: "/v0/escrow/withdraw", base: "trading" }],
37
+ ["escrowWithdrawals", { method: "GET", path: "/v0/escrow/{address}/withdrawals", base: "trading", requiresWalletAddress: true }],
38
+ ]);
39
+ /**
40
+ * Throw {@link NotSupported} if the client is in hosted mode (i.e. has a
41
+ * `pmxtApiKey`) but targets a venue outside the hosted trading allowlist.
42
+ * No-op for non-hosted clients.
43
+ */
44
+ export function ensureHostedTradingSupported(client) {
45
+ if (!client.pmxtApiKey)
46
+ return;
47
+ if (!HOSTED_TRADING_VENUES.has(client.exchangeName)) {
48
+ throw new NotSupported(`Hosted trading is only supported for Polymarket and Opinion; ${client.exchangeName} is not supported with pmxtApiKey.`);
49
+ }
50
+ }
51
+ /**
52
+ * Resolve the wallet address to use for a user-scoped hosted call, preferring
53
+ * an explicit per-call override over the client default. Throws
54
+ * {@link MissingWalletAddress} when neither is set.
55
+ */
56
+ export function resolveWalletAddress(client, override) {
57
+ if (override)
58
+ return override;
59
+ if (client.walletAddress)
60
+ return client.walletAddress;
61
+ throw new MissingWalletAddress("walletAddress is required for hosted-mode reads and trades; pass it to the exchange constructor or as an override.");
62
+ }
63
+ /**
64
+ * Substitute `{name}` placeholders in a route path with URL-encoded values
65
+ * from `params`. Throws if a referenced parameter is missing.
66
+ */
67
+ export function formatRoutePath(route, params) {
68
+ return route.path.replace(/\{(\w+)\}/g, (_, key) => {
69
+ const value = params[key];
70
+ if (value === undefined || value === null) {
71
+ throw new Error(`path parameter cannot be undefined: ${key}`);
72
+ }
73
+ return encodeURIComponent(String(value));
74
+ });
75
+ }
76
+ /**
77
+ * Issue a request against the hosted trading API.
78
+ *
79
+ * NOTE: writes (POST/PUT/DELETE) are NEVER retried. `built_order_id` and
80
+ * `cancel_id` are single-use server-side, so replaying a lost response would
81
+ * 404 even though the original write succeeded. Callers needing retry logic
82
+ * must layer it on top with awareness of this invariant.
83
+ */
84
+ export async function _tradingRequest(client, opts) {
85
+ if (!client.pmxtApiKey) {
86
+ throw new Error("hosted request requires pmxtApiKey");
87
+ }
88
+ const base = HOSTED_TRADING_BASE_URL.replace(/\/$/, "");
89
+ const path = opts.path.startsWith("/") ? opts.path : `/${opts.path}`;
90
+ let url = base + path;
91
+ if (opts.params) {
92
+ const qs = new URLSearchParams();
93
+ for (const [k, v] of Object.entries(opts.params)) {
94
+ if (v !== undefined)
95
+ qs.append(k, String(v));
96
+ }
97
+ const q = qs.toString();
98
+ if (q)
99
+ url += `?${q}`;
100
+ }
101
+ const headers = {
102
+ Authorization: `Bearer ${client.pmxtApiKey}`,
103
+ };
104
+ if (opts.body !== undefined) {
105
+ headers["Content-Type"] = "application/json";
106
+ }
107
+ const resp = await fetch(url, {
108
+ method: opts.method,
109
+ headers,
110
+ body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
111
+ });
112
+ if (!resp.ok) {
113
+ await raiseFromResponse(resp);
114
+ }
115
+ const text = await resp.text();
116
+ if (!text)
117
+ return null;
118
+ return JSON.parse(text);
119
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Hosted trading EIP-712 validation guardrails.
3
+ *
4
+ * Three layers (mirrors `sdks/python/pmxt/_hosted_typeddata.py`):
5
+ * 1. Schema validation — per-route shape, domain, types, message keys, deadline
6
+ * 2. Economic match — typed_data economics agree with the user's build request
7
+ * 3. Post-sign — exact 65-byte length, low-s canonical, v ∈ {27, 28}, recovery
8
+ *
9
+ * Layer 3 uses the optional `ethers` peer dependency for typed-data verification.
10
+ */
11
+ import { TypedData } from "./signers.js";
12
+ export type HostedRoute = "polymarket_buy" | "polymarket_sell" | "opinion_buy" | "opinion_sell_polygon" | "opinion_sell_bsc_pull" | "cancel_polymarket" | "cancel_opinion_polygon" | "cancel_opinion_bsc_pull";
13
+ export declare const SECP256K1_HALF_N = 57896044618658097711785492504343953926418782139537452191302581570759080747168n;
14
+ /**
15
+ * Validate the structural shape of a typed-data payload before signing.
16
+ * Throws {@link InvalidSignature} on any mismatch.
17
+ */
18
+ export declare function validateTypedData(typedData: TypedData, route: string, walletAddress: string): void;
19
+ /**
20
+ * Reject typed-data whose economics don't agree with the user's original
21
+ * build request / build response. Guards against a compromised server
22
+ * returning valid-shape typed-data with altered amounts or wrong target.
23
+ */
24
+ export declare function validateEconomics(typedData: TypedData, route: string, buildRequest: any, buildResponse: any): void;
25
+ /**
26
+ * Verify a signature against typed-data and return the normalized signature.
27
+ *
28
+ * Performs:
29
+ * - exact 65-byte length (0x + 130 hex)
30
+ * - low-s canonical check
31
+ * - v ∈ {27, 28} (normalizes {0,1} → {27,28})
32
+ * - typed-data recovery, asserting recovered address === walletAddress
33
+ *
34
+ * Throws {@link InvalidSignature} on any failure.
35
+ */
36
+ export declare function verifySignature(typedData: TypedData, signature: string, walletAddress: string): string;