@liberfi.io/react-predict 0.1.1

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/dist/index.js ADDED
@@ -0,0 +1,1536 @@
1
+ 'use strict';
2
+
3
+ var utils = require('@liberfi.io/utils');
4
+ var react = require('react');
5
+ var jsxRuntime = require('react/jsx-runtime');
6
+ var reactQuery = require('@tanstack/react-query');
7
+
8
+ // src/client/client.ts
9
+ function buildQuery(params) {
10
+ const qs = new URLSearchParams();
11
+ for (const [key, value] of Object.entries(params)) {
12
+ if (value !== void 0 && value !== null) {
13
+ qs.set(key, String(value));
14
+ }
15
+ }
16
+ const str = qs.toString();
17
+ return str ? `?${str}` : "";
18
+ }
19
+ var PredictClient = class {
20
+ constructor(endpoint) {
21
+ this.endpoint = endpoint;
22
+ }
23
+ // -------------------------------------------------------------------------
24
+ // Events
25
+ // -------------------------------------------------------------------------
26
+ /**
27
+ * List prediction events with optional filtering, sorting, and pagination.
28
+ *
29
+ * Maps to `GET /api/v1/events`.
30
+ *
31
+ * @param params - Optional query parameters (filter, sort, pagination).
32
+ * @returns A paginated page of events.
33
+ */
34
+ async listEvents(params) {
35
+ const query = buildQuery(params ?? {});
36
+ const url = `${this.endpoint}/api/v1/events${query}`;
37
+ return await utils.httpGet(url);
38
+ }
39
+ /**
40
+ * Fetch a single prediction event by its slug.
41
+ *
42
+ * Maps to `GET /api/v1/events/:slug?source=...`.
43
+ *
44
+ * @param slug - Canonical event slug (e.g. "will-trump-win-2024" for Polymarket
45
+ * or "KXBTCD-25FEB-T68000" for DFlow).
46
+ * @param source - Upstream provider (`"dflow"` or `"polymarket"`).
47
+ * @returns The matching event.
48
+ * @throws When the server responds with 404 or any other non-2xx status.
49
+ */
50
+ async getEvent(slug, source) {
51
+ const query = source ? buildQuery({ source }) : "";
52
+ const url = `${this.endpoint}/api/v1/events/${encodeURIComponent(slug)}${query}`;
53
+ return await utils.httpGet(url);
54
+ }
55
+ /**
56
+ * Fetch events similar to the given slug.
57
+ *
58
+ * Maps to `GET /api/v1/events/:slug/similar?source=...`.
59
+ */
60
+ async getSimilarEvents(slug, source, params) {
61
+ const query = buildQuery({ source, ...params });
62
+ const url = `${this.endpoint}/api/v1/events/${encodeURIComponent(slug)}/similar${query}`;
63
+ return await utils.httpGet(url);
64
+ }
65
+ // -------------------------------------------------------------------------
66
+ // Markets
67
+ // -------------------------------------------------------------------------
68
+ /**
69
+ * Fetch a single prediction market by its slug.
70
+ *
71
+ * Maps to `GET /api/v1/markets/:slug?source=...`.
72
+ *
73
+ * @param slug - Canonical market slug.
74
+ * @param source - Upstream provider (`"dflow"` or `"polymarket"`).
75
+ * @returns The matching market.
76
+ * @throws When the server responds with 404 or any other non-2xx status.
77
+ */
78
+ async getMarket(slug, source) {
79
+ const query = source ? buildQuery({ source }) : "";
80
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}${query}`;
81
+ return await utils.httpGet(url);
82
+ }
83
+ /** Maps to `GET /api/v1/markets/:slug/orderbook?source=...`. */
84
+ async getOrderbook(slug, source) {
85
+ const query = buildQuery({ source });
86
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/orderbook${query}`;
87
+ return await utils.httpGet(url);
88
+ }
89
+ /** Maps to `GET /api/v1/markets/:slug/trades?source=...`. */
90
+ async listMarketTrades(slug, params) {
91
+ const query = buildQuery(params);
92
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/trades${query}`;
93
+ return await utils.httpGet(url);
94
+ }
95
+ /** Maps to `GET /api/v1/markets/:slug/price-history?source=...&range=...`. */
96
+ async getPriceHistory(slug, source, range) {
97
+ const query = buildQuery({ source, range });
98
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/price-history${query}`;
99
+ return await utils.httpGet(url);
100
+ }
101
+ /** Maps to `GET /api/v1/markets/:slug/candlesticks?interval=...&limit=...`. */
102
+ async listCandlesticks(slug, params) {
103
+ const query = buildQuery(params ?? {});
104
+ const url = `${this.endpoint}/api/v1/markets/${encodeURIComponent(slug)}/candlesticks${query}`;
105
+ return await utils.httpGet(url);
106
+ }
107
+ // -------------------------------------------------------------------------
108
+ // Positions
109
+ // -------------------------------------------------------------------------
110
+ /**
111
+ * Maps to `GET /api/v1/positions?source=...&user=...`.
112
+ *
113
+ * @param user - Wallet address.
114
+ * @param source - Provider source. Omit to aggregate all providers.
115
+ */
116
+ async getPositions(user, source) {
117
+ const query = buildQuery({ source, user });
118
+ const url = `${this.endpoint}/api/v1/positions${query}`;
119
+ return await utils.httpGet(url);
120
+ }
121
+ // -------------------------------------------------------------------------
122
+ // Balance
123
+ // -------------------------------------------------------------------------
124
+ /**
125
+ * Get the on-chain USDC balance for a wallet.
126
+ *
127
+ * Maps to `GET /api/v1/balance?source=...&user=...`.
128
+ *
129
+ * @param source - Provider source (`"dflow"` for Solana, `"polymarket"` for Polygon).
130
+ * @param user - Wallet address.
131
+ */
132
+ async getBalance(source, user) {
133
+ const query = buildQuery({ source, user });
134
+ const url = `${this.endpoint}/api/v1/balance${query}`;
135
+ return await utils.httpGet(url);
136
+ }
137
+ // -------------------------------------------------------------------------
138
+ // Orders
139
+ // -------------------------------------------------------------------------
140
+ /** Maps to `GET /api/v1/orders?source=...&wallet_address=...`. */
141
+ async listOrders(params) {
142
+ const query = buildQuery(params);
143
+ const url = `${this.endpoint}/api/v1/orders${query}`;
144
+ return await utils.httpGet(url);
145
+ }
146
+ /** Maps to `GET /api/v1/orders/:id?source=...`. */
147
+ async getOrder(id, source) {
148
+ const query = buildQuery({ source });
149
+ const url = `${this.endpoint}/api/v1/orders/${encodeURIComponent(id)}${query}`;
150
+ return await utils.httpGet(url);
151
+ }
152
+ /** Maps to `DELETE /api/v1/orders/:id?source=...`. */
153
+ async cancelOrder(id, source) {
154
+ const query = buildQuery({ source });
155
+ const url = `${this.endpoint}/api/v1/orders/${encodeURIComponent(id)}${query}`;
156
+ return await utils.httpDelete(url);
157
+ }
158
+ // -------------------------------------------------------------------------
159
+ // Polymarket trading
160
+ // -------------------------------------------------------------------------
161
+ /**
162
+ * Create a Polymarket limit order via the prediction-server proxy.
163
+ *
164
+ * Maps to `POST /api/v1/orders/polymarket`.
165
+ *
166
+ * The caller must attach Polymarket CLOB authentication headers:
167
+ * - `POLY_ADDRESS` — the EVM wallet address.
168
+ * - `POLY_SIGNATURE` — L1 signature or L2 API key.
169
+ * - `POLY_TIMESTAMP` — Unix milliseconds string.
170
+ * - `POLY_NONCE` — credential nonce (use `"0"` for in-session derived creds).
171
+ *
172
+ * @param input - Order parameters.
173
+ * @param headers - Polymarket CLOB auth headers (`POLY_*`).
174
+ */
175
+ async createPolymarketOrder(input, headers) {
176
+ const url = `${this.endpoint}/api/v1/orders/polymarket`;
177
+ return await utils.httpPost(url, input, { headers });
178
+ }
179
+ // -------------------------------------------------------------------------
180
+ // DFlow trading
181
+ // -------------------------------------------------------------------------
182
+ /** Maps to `POST /api/v1/orders/dflow/quote`. */
183
+ async createDFlowQuote(body) {
184
+ const url = `${this.endpoint}/api/v1/orders/dflow/quote`;
185
+ return await utils.httpPost(url, body);
186
+ }
187
+ /** Maps to `POST /api/v1/orders/dflow/submit`. */
188
+ async submitDFlowTransaction(body) {
189
+ const url = `${this.endpoint}/api/v1/orders/dflow/submit`;
190
+ return await utils.httpPost(url, body);
191
+ }
192
+ // -------------------------------------------------------------------------
193
+ // Trades by wallet
194
+ // -------------------------------------------------------------------------
195
+ /** Maps to `GET /api/v1/trades?source=...&wallet=...`. */
196
+ async listTrades(params) {
197
+ const query = buildQuery(params);
198
+ const url = `${this.endpoint}/api/v1/trades${query}`;
199
+ return await utils.httpGet(url);
200
+ }
201
+ };
202
+ function createPredictClient(endpoint) {
203
+ return new PredictClient(endpoint);
204
+ }
205
+
206
+ // src/client/ws.ts
207
+ var DEFAULT_RECONNECT_BASE = 1e3;
208
+ var DEFAULT_RECONNECT_MAX = 3e4;
209
+ var DEFAULT_PING_INTERVAL = 2e4;
210
+ var DEFAULT_PONG_TIMEOUT = 1e4;
211
+ var PredictWsClient = class {
212
+ ws = null;
213
+ wsUrl;
214
+ autoReconnect;
215
+ reconnectBase;
216
+ reconnectMax;
217
+ pingMs;
218
+ pongTimeoutMs;
219
+ status = "disconnected";
220
+ reconnectAttempts = 0;
221
+ reconnectTimer = null;
222
+ pingTimer = null;
223
+ pongTimer = null;
224
+ shouldReconnect = true;
225
+ listeners = {
226
+ connect: [],
227
+ disconnect: [],
228
+ reconnecting: [],
229
+ error: [],
230
+ status: [],
231
+ subscribed: [],
232
+ orderbook: [],
233
+ prices: [],
234
+ trades: []
235
+ };
236
+ subs = {
237
+ channels: /* @__PURE__ */ new Map([
238
+ ["orderbook", /* @__PURE__ */ new Set()],
239
+ ["prices", /* @__PURE__ */ new Set()],
240
+ ["trades", /* @__PURE__ */ new Set()]
241
+ ])
242
+ };
243
+ constructor(config) {
244
+ this.wsUrl = config.wsUrl;
245
+ this.autoReconnect = config.autoReconnect ?? true;
246
+ this.reconnectBase = config.reconnectIntervalBase ?? DEFAULT_RECONNECT_BASE;
247
+ this.reconnectMax = config.reconnectMaxInterval ?? DEFAULT_RECONNECT_MAX;
248
+ this.pingMs = config.pingInterval ?? DEFAULT_PING_INTERVAL;
249
+ this.pongTimeoutMs = config.pongTimeout ?? DEFAULT_PONG_TIMEOUT;
250
+ if (config.autoConnect !== false) {
251
+ this.connect();
252
+ }
253
+ }
254
+ // -------------------------------------------------------------------------
255
+ // Connection management
256
+ // -------------------------------------------------------------------------
257
+ connect() {
258
+ if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
259
+ return;
260
+ }
261
+ this.shouldReconnect = true;
262
+ this.setStatus("connecting");
263
+ try {
264
+ this.ws = new WebSocket(this.wsUrl);
265
+ this.ws.onopen = () => {
266
+ this.reconnectAttempts = 0;
267
+ this.setStatus("connected");
268
+ this.emit("connect", void 0);
269
+ this.restoreSubscriptions();
270
+ this.startPing();
271
+ };
272
+ this.ws.onmessage = (event) => {
273
+ this.handleMessage(event.data);
274
+ };
275
+ this.ws.onerror = () => {
276
+ };
277
+ this.ws.onclose = (event) => {
278
+ this.stopPing();
279
+ this.setStatus("disconnected");
280
+ this.emit("disconnect", { code: event.code, reason: event.reason });
281
+ if (this.shouldReconnect && this.autoReconnect) {
282
+ this.scheduleReconnect();
283
+ }
284
+ };
285
+ } catch {
286
+ if (this.shouldReconnect && this.autoReconnect) {
287
+ this.scheduleReconnect();
288
+ }
289
+ }
290
+ }
291
+ disconnect() {
292
+ this.shouldReconnect = false;
293
+ this.stopPing();
294
+ this.clearReconnectTimer();
295
+ if (this.ws) {
296
+ this.ws.close();
297
+ this.ws = null;
298
+ }
299
+ this.setStatus("disconnected");
300
+ }
301
+ /** Disconnect and clear all subscription state and listeners. */
302
+ destroy() {
303
+ this.disconnect();
304
+ for (const ch of this.subs.channels.values()) ch.clear();
305
+ for (const key of Object.keys(this.listeners)) {
306
+ this.listeners[key].length = 0;
307
+ }
308
+ }
309
+ getStatus() {
310
+ return this.status;
311
+ }
312
+ isConnected() {
313
+ return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
314
+ }
315
+ // -------------------------------------------------------------------------
316
+ // Subscription — low-level (multi-channel, multi-slug)
317
+ // -------------------------------------------------------------------------
318
+ /**
319
+ * Subscribe to one or more channels for the given market slugs.
320
+ * The server acknowledges with a `subscribed` message.
321
+ */
322
+ subscribe(channels, marketSlugs) {
323
+ for (const ch of channels) {
324
+ const set = this.subs.channels.get(ch);
325
+ for (const slug of marketSlugs) set.add(slug);
326
+ }
327
+ this.send({ type: "subscribe", channels, market_slugs: marketSlugs });
328
+ }
329
+ /**
330
+ * Unsubscribe from one or more channels for the given market slugs.
331
+ */
332
+ unsubscribe(channels, marketSlugs) {
333
+ for (const ch of channels) {
334
+ const set = this.subs.channels.get(ch);
335
+ for (const slug of marketSlugs) set.delete(slug);
336
+ }
337
+ this.send({ type: "unsubscribe", channels, market_slugs: marketSlugs });
338
+ }
339
+ // -------------------------------------------------------------------------
340
+ // Subscription — convenience (single channel)
341
+ // -------------------------------------------------------------------------
342
+ /**
343
+ * Subscribe to price updates for the given slugs.
344
+ * Returns an unsubscribe function.
345
+ */
346
+ subscribePrices(slugs, onUpdate) {
347
+ this.subscribe(["prices"], slugs);
348
+ const off = this.on("prices", onUpdate);
349
+ return () => {
350
+ off();
351
+ this.unsubscribe(["prices"], slugs);
352
+ };
353
+ }
354
+ /**
355
+ * Subscribe to orderbook snapshots for the given slugs.
356
+ * Returns an unsubscribe function.
357
+ */
358
+ subscribeOrderbook(slugs, onUpdate) {
359
+ this.subscribe(["orderbook"], slugs);
360
+ const off = this.on("orderbook", onUpdate);
361
+ return () => {
362
+ off();
363
+ this.unsubscribe(["orderbook"], slugs);
364
+ };
365
+ }
366
+ /**
367
+ * Subscribe to trade events for the given slugs.
368
+ * Returns an unsubscribe function.
369
+ */
370
+ subscribeTrades(slugs, onUpdate) {
371
+ this.subscribe(["trades"], slugs);
372
+ const off = this.on("trades", onUpdate);
373
+ return () => {
374
+ off();
375
+ this.unsubscribe(["trades"], slugs);
376
+ };
377
+ }
378
+ // -------------------------------------------------------------------------
379
+ // Status observation
380
+ // -------------------------------------------------------------------------
381
+ /** Register a callback for connection status changes. Returns unsubscribe. */
382
+ onStatusChange(cb) {
383
+ return this.on("status", cb);
384
+ }
385
+ /** Register a callback for server-sent errors. Returns unsubscribe. */
386
+ onError(cb) {
387
+ return this.on("error", cb);
388
+ }
389
+ // -------------------------------------------------------------------------
390
+ // Event emitter
391
+ // -------------------------------------------------------------------------
392
+ on(event, callback) {
393
+ const list = this.listeners[event];
394
+ list.push(callback);
395
+ return () => this.off(event, callback);
396
+ }
397
+ off(event, callback) {
398
+ const list = this.listeners[event];
399
+ const idx = list.indexOf(callback);
400
+ if (idx !== -1) list.splice(idx, 1);
401
+ }
402
+ // -------------------------------------------------------------------------
403
+ // Private
404
+ // -------------------------------------------------------------------------
405
+ emit(event, data) {
406
+ const list = this.listeners[event];
407
+ for (const cb of list) {
408
+ try {
409
+ cb(data);
410
+ } catch (e) {
411
+ }
412
+ }
413
+ }
414
+ send(msg) {
415
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return false;
416
+ try {
417
+ this.ws.send(JSON.stringify(msg));
418
+ return true;
419
+ } catch {
420
+ return false;
421
+ }
422
+ }
423
+ setStatus(next) {
424
+ if (this.status !== next) {
425
+ this.status = next;
426
+ this.emit("status", next);
427
+ }
428
+ }
429
+ handleMessage(raw) {
430
+ let msg;
431
+ try {
432
+ msg = JSON.parse(raw);
433
+ } catch {
434
+ return;
435
+ }
436
+ if (msg.type) {
437
+ switch (msg.type) {
438
+ case "pong":
439
+ this.clearPongTimer();
440
+ return;
441
+ case "subscribed":
442
+ this.emit("subscribed", {
443
+ channels: msg.channels,
444
+ market_slugs: msg.market_slugs
445
+ });
446
+ return;
447
+ case "error":
448
+ this.emit("error", {
449
+ code: msg.code,
450
+ message: msg.message
451
+ });
452
+ return;
453
+ }
454
+ }
455
+ if (msg.channel && msg.data) {
456
+ const dataMsg = msg;
457
+ switch (dataMsg.channel) {
458
+ case "orderbook":
459
+ this.emit("orderbook", dataMsg);
460
+ break;
461
+ case "prices":
462
+ this.emit("prices", dataMsg);
463
+ break;
464
+ case "trades":
465
+ this.emit("trades", dataMsg);
466
+ break;
467
+ }
468
+ }
469
+ }
470
+ scheduleReconnect() {
471
+ if (this.reconnectTimer) return;
472
+ const delay = Math.min(
473
+ this.reconnectBase * Math.pow(2, this.reconnectAttempts),
474
+ this.reconnectMax
475
+ );
476
+ this.setStatus("reconnecting");
477
+ this.emit("reconnecting", { attempt: this.reconnectAttempts + 1, delay });
478
+ this.reconnectTimer = setTimeout(() => {
479
+ this.reconnectTimer = null;
480
+ this.reconnectAttempts++;
481
+ this.connect();
482
+ }, delay);
483
+ }
484
+ clearReconnectTimer() {
485
+ if (this.reconnectTimer) {
486
+ clearTimeout(this.reconnectTimer);
487
+ this.reconnectTimer = null;
488
+ }
489
+ }
490
+ restoreSubscriptions() {
491
+ for (const [channel, slugs] of this.subs.channels.entries()) {
492
+ if (slugs.size > 0) {
493
+ this.send({
494
+ type: "subscribe",
495
+ channels: [channel],
496
+ market_slugs: Array.from(slugs)
497
+ });
498
+ }
499
+ }
500
+ }
501
+ startPing() {
502
+ this.stopPing();
503
+ this.pingTimer = setInterval(() => {
504
+ this.send({ type: "ping" });
505
+ this.armPongTimeout();
506
+ }, this.pingMs);
507
+ }
508
+ stopPing() {
509
+ if (this.pingTimer) {
510
+ clearInterval(this.pingTimer);
511
+ this.pingTimer = null;
512
+ }
513
+ this.clearPongTimer();
514
+ }
515
+ /**
516
+ * Start a timer that fires if the server does not reply with pong
517
+ * within `pongTimeoutMs`. On timeout the socket is closed so
518
+ * the normal reconnect flow kicks in.
519
+ */
520
+ armPongTimeout() {
521
+ this.clearPongTimer();
522
+ this.pongTimer = setTimeout(() => {
523
+ this.pongTimer = null;
524
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
525
+ this.ws.close(4e3, "pong timeout");
526
+ }
527
+ }, this.pongTimeoutMs);
528
+ }
529
+ clearPongTimer() {
530
+ if (this.pongTimer) {
531
+ clearTimeout(this.pongTimer);
532
+ this.pongTimer = null;
533
+ }
534
+ }
535
+ };
536
+ function createPredictWsClient(config) {
537
+ return new PredictWsClient(config);
538
+ }
539
+ var PredictContext = react.createContext(null);
540
+ function PredictProvider({
541
+ client,
542
+ wsClient,
543
+ children
544
+ }) {
545
+ const ws = wsClient ?? null;
546
+ const value = react.useMemo(() => ({ client, wsClient: ws }), [client, ws]);
547
+ return /* @__PURE__ */ jsxRuntime.jsx(PredictContext.Provider, { value, children });
548
+ }
549
+
550
+ // src/utils/polymarket-hmac.ts
551
+ function encode(str) {
552
+ return new TextEncoder().encode(str);
553
+ }
554
+ function base64ToBytes(b64) {
555
+ const binary = atob(b64);
556
+ const bytes = new Uint8Array(binary.length);
557
+ for (let i = 0; i < binary.length; i++) {
558
+ bytes[i] = binary.charCodeAt(i);
559
+ }
560
+ return bytes;
561
+ }
562
+ function bytesToBase64(bytes) {
563
+ let binary = "";
564
+ for (let i = 0; i < bytes.byteLength; i++) {
565
+ binary += String.fromCharCode(bytes[i]);
566
+ }
567
+ return btoa(binary);
568
+ }
569
+ async function hmacSha256Base64(secretBase64, message) {
570
+ const keyBytes = base64ToBytes(secretBase64);
571
+ const cryptoKey = await crypto.subtle.importKey(
572
+ "raw",
573
+ keyBytes.buffer,
574
+ { name: "HMAC", hash: "SHA-256" },
575
+ false,
576
+ ["sign"]
577
+ );
578
+ const msgBytes = encode(message);
579
+ const signature = await crypto.subtle.sign(
580
+ "HMAC",
581
+ cryptoKey,
582
+ msgBytes.buffer
583
+ );
584
+ return bytesToBase64(new Uint8Array(signature));
585
+ }
586
+ async function buildPolymarketL2Headers(address, input) {
587
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
588
+ const body = input.body ?? "";
589
+ const message = `${timestamp}${input.method}${input.requestPath}${body}`;
590
+ const signature = await hmacSha256Base64(input.secret, message);
591
+ return {
592
+ POLY_ADDRESS: address,
593
+ POLY_SIGNATURE: signature,
594
+ POLY_TIMESTAMP: timestamp,
595
+ POLY_API_KEY: input.apiKey,
596
+ POLY_PASSPHRASE: input.passphrase
597
+ };
598
+ }
599
+ var CLOB_AUTH_DOMAIN = {
600
+ name: "ClobAuthDomain",
601
+ version: "1",
602
+ chainId: 137
603
+ };
604
+ var CLOB_AUTH_TYPES = {
605
+ ClobAuth: [
606
+ { name: "address", type: "address" },
607
+ { name: "timestamp", type: "string" },
608
+ { name: "nonce", type: "uint256" },
609
+ { name: "message", type: "string" }
610
+ ]
611
+ };
612
+ function buildClobAuthMessage(input) {
613
+ return {
614
+ address: input.address,
615
+ timestamp: input.timestamp,
616
+ nonce: input.nonce,
617
+ message: "This message attests that I control the given wallet"
618
+ };
619
+ }
620
+ async function derivePolymarketApiKey(address, signature, timestamp, nonce) {
621
+ const res = await fetch(
622
+ `https://clob.polymarket.com/auth/derive-api-key?nonce=${nonce}`,
623
+ {
624
+ method: "GET",
625
+ headers: {
626
+ "Content-Type": "application/json",
627
+ POLY_ADDRESS: address,
628
+ POLY_SIGNATURE: signature,
629
+ POLY_TIMESTAMP: timestamp,
630
+ POLY_NONCE: String(nonce)
631
+ }
632
+ }
633
+ );
634
+ if (!res.ok) {
635
+ const text = await res.text().catch(() => res.statusText);
636
+ throw new Error(
637
+ `Polymarket credential exchange failed (${res.status}): ${text}`
638
+ );
639
+ }
640
+ return res.json();
641
+ }
642
+ var PolymarketContext = react.createContext(
643
+ null
644
+ );
645
+ function PolymarketProvider({ children }) {
646
+ const [credentials, setCredentials] = react.useState(
647
+ null
648
+ );
649
+ const [isAuthenticating, setIsAuthenticating] = react.useState(false);
650
+ const [authError, setAuthError] = react.useState(null);
651
+ const lastAuthAddressRef = react.useRef(null);
652
+ const authenticate = react.useCallback(
653
+ async (signer) => {
654
+ if (lastAuthAddressRef.current === signer.address && credentials !== null) {
655
+ return;
656
+ }
657
+ setIsAuthenticating(true);
658
+ setAuthError(null);
659
+ try {
660
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
661
+ const nonce = 0;
662
+ const message = buildClobAuthMessage({
663
+ address: signer.address,
664
+ timestamp,
665
+ nonce
666
+ });
667
+ const signature = await signer.signTypedData(
668
+ CLOB_AUTH_DOMAIN,
669
+ CLOB_AUTH_TYPES,
670
+ message
671
+ );
672
+ const { apiKey, secret, passphrase } = await derivePolymarketApiKey(
673
+ signer.address,
674
+ signature,
675
+ timestamp,
676
+ nonce
677
+ );
678
+ lastAuthAddressRef.current = signer.address;
679
+ setCredentials({ apiKey, secret, passphrase, address: signer.address });
680
+ } catch (err) {
681
+ setAuthError(
682
+ err instanceof Error ? err : new Error("Polymarket authentication failed")
683
+ );
684
+ lastAuthAddressRef.current = null;
685
+ } finally {
686
+ setIsAuthenticating(false);
687
+ }
688
+ },
689
+ [credentials]
690
+ );
691
+ const clearCredentials = react.useCallback(() => {
692
+ setCredentials(null);
693
+ lastAuthAddressRef.current = null;
694
+ }, []);
695
+ return /* @__PURE__ */ jsxRuntime.jsx(
696
+ PolymarketContext.Provider,
697
+ {
698
+ value: {
699
+ credentials,
700
+ isAuthenticating,
701
+ authError,
702
+ authenticate,
703
+ clearCredentials
704
+ },
705
+ children
706
+ }
707
+ );
708
+ }
709
+ function usePredictClient() {
710
+ const context = react.useContext(PredictContext);
711
+ if (!context) {
712
+ throw new Error("usePredictClient must be used within a PredictProvider");
713
+ }
714
+ return context.client;
715
+ }
716
+ function eventsQueryKey(params) {
717
+ return ["predict", "events", params ?? {}];
718
+ }
719
+ async function fetchEvents(client, params) {
720
+ return client.listEvents(params);
721
+ }
722
+ function useEvents(params, queryOptions = {}) {
723
+ const client = usePredictClient();
724
+ return reactQuery.useQuery({
725
+ queryKey: eventsQueryKey(params),
726
+ queryFn: () => client.listEvents(params),
727
+ ...queryOptions
728
+ });
729
+ }
730
+
731
+ // src/hooks/predict/events.params.ts
732
+ var DEFAULT_PAGE_SIZE = 48;
733
+ function resolveTagSlug(selection) {
734
+ if (!selection) return void 0;
735
+ return selection.tagSlug ?? selection.categorySlug ?? void 0;
736
+ }
737
+ function resolveEventsParams(input = {}) {
738
+ const {
739
+ tagSlugSelection,
740
+ limit = DEFAULT_PAGE_SIZE,
741
+ status = "open",
742
+ with_markets = true,
743
+ source,
744
+ sort_by,
745
+ sort_asc
746
+ } = input;
747
+ const tag_slug = resolveTagSlug(tagSlugSelection);
748
+ return {
749
+ limit,
750
+ status,
751
+ with_markets,
752
+ ...source ? { source } : {},
753
+ ...tag_slug ? { tag_slug } : {},
754
+ ...sort_by ? { sort_by } : {},
755
+ ...sort_asc !== void 0 ? { sort_asc } : {}
756
+ };
757
+ }
758
+ function infiniteEventsQueryKey(params) {
759
+ return ["predict", "events-infinite", params];
760
+ }
761
+ async function fetchEventsPage(client, params) {
762
+ return client.listEvents(params);
763
+ }
764
+ function eventQueryKey(slug, source) {
765
+ return ["predict", "event", slug, source];
766
+ }
767
+ async function fetchEvent(client, slug, source) {
768
+ return client.getEvent(slug, source);
769
+ }
770
+
771
+ // src/hooks/predict/useEvent.ts
772
+ function useEvent(params, queryOptions = {}) {
773
+ const client = usePredictClient();
774
+ return reactQuery.useQuery({
775
+ queryKey: eventQueryKey(params.slug, params.source),
776
+ queryFn: () => fetchEvent(client, params.slug, params.source),
777
+ enabled: Boolean(params.slug),
778
+ ...queryOptions
779
+ });
780
+ }
781
+ function useInfiniteEvents(params, queryOptions = {}) {
782
+ const client = usePredictClient();
783
+ return reactQuery.useInfiniteQuery({
784
+ queryKey: infiniteEventsQueryKey(params),
785
+ queryFn: ({ pageParam }) => fetchEventsPage(client, {
786
+ ...params,
787
+ ...pageParam !== void 0 ? { cursor: pageParam } : {}
788
+ }),
789
+ initialPageParam: void 0,
790
+ getNextPageParam: (lastPage) => lastPage.has_more && lastPage.next_cursor ? lastPage.next_cursor : void 0,
791
+ ...queryOptions
792
+ });
793
+ }
794
+ function searchEventsQueryKey(params) {
795
+ return ["predict", "search", params];
796
+ }
797
+ function useSearchEvents(params, queryOptions) {
798
+ const client = usePredictClient();
799
+ const apiParams = react.useMemo(
800
+ () => ({
801
+ search: params.keyword,
802
+ limit: params.limit ?? 20,
803
+ status: params.status ?? "open",
804
+ source: params.source,
805
+ with_markets: params.with_markets ?? false
806
+ }),
807
+ [
808
+ params.keyword,
809
+ params.limit,
810
+ params.status,
811
+ params.source,
812
+ params.with_markets
813
+ ]
814
+ );
815
+ return reactQuery.useInfiniteQuery({
816
+ queryKey: searchEventsQueryKey(apiParams),
817
+ queryFn: ({ pageParam }) => client.listEvents({
818
+ ...apiParams,
819
+ ...pageParam !== void 0 ? { cursor: pageParam } : {}
820
+ }),
821
+ initialPageParam: void 0,
822
+ getNextPageParam: (lastPage) => lastPage.has_more && lastPage.next_cursor ? lastPage.next_cursor : void 0,
823
+ enabled: queryOptions?.enabled ?? !!params.keyword
824
+ });
825
+ }
826
+ function similarEventsQueryKey(slug, source, params) {
827
+ return ["predict", "similar-events", slug, source, params];
828
+ }
829
+ function useSimilarEvents(params, queryOptions = {}) {
830
+ const client = usePredictClient();
831
+ const apiParams = {
832
+ limit: params.limit,
833
+ same_source: params.same_source
834
+ };
835
+ return reactQuery.useQuery({
836
+ queryKey: similarEventsQueryKey(params.slug, params.source, apiParams),
837
+ queryFn: () => client.getSimilarEvents(params.slug, params.source, apiParams),
838
+ enabled: Boolean(params.slug),
839
+ staleTime: 5 * 6e4,
840
+ ...queryOptions
841
+ });
842
+ }
843
+ function marketQueryKey(slug, source) {
844
+ return ["predict", "market", slug, source];
845
+ }
846
+ async function fetchMarket(client, slug, source) {
847
+ return client.getMarket(slug, source);
848
+ }
849
+ function useMarket(params, queryOptions = {}) {
850
+ const client = usePredictClient();
851
+ return reactQuery.useQuery({
852
+ queryKey: marketQueryKey(params.slug, params.source),
853
+ queryFn: () => fetchMarket(client, params.slug, params.source),
854
+ enabled: Boolean(params.slug),
855
+ ...queryOptions
856
+ });
857
+ }
858
+ function priceHistoryQueryKey(slug, source, range) {
859
+ return ["predict", "price-history", slug, source, range];
860
+ }
861
+ function usePriceHistory(params, queryOptions = {}) {
862
+ const client = usePredictClient();
863
+ return reactQuery.useQuery({
864
+ queryKey: priceHistoryQueryKey(params.slug, params.source, params.range),
865
+ queryFn: () => client.getPriceHistory(params.slug, params.source, params.range),
866
+ enabled: Boolean(params.slug),
867
+ staleTime: 6e4,
868
+ ...queryOptions
869
+ });
870
+ }
871
+
872
+ // src/hooks/predict/useMarketHistory.ts
873
+ var ChartRange = {
874
+ ONE_DAY: "1d",
875
+ ONE_WEEK: "1w",
876
+ ONE_MONTH: "1m",
877
+ ALL: "all"
878
+ };
879
+ var RANGE_MAP = {
880
+ [ChartRange.ONE_DAY]: "1d",
881
+ [ChartRange.ONE_WEEK]: "1w",
882
+ [ChartRange.ONE_MONTH]: "1m",
883
+ [ChartRange.ALL]: "all"
884
+ };
885
+ function useMarketHistory(markets, range = ChartRange.ALL) {
886
+ const client = usePredictClient();
887
+ const apiRange = RANGE_MAP[range];
888
+ const queries = reactQuery.useQueries({
889
+ queries: markets.map((market) => ({
890
+ queryKey: priceHistoryQueryKey(market.slug, market.source, apiRange),
891
+ queryFn: () => client.getPriceHistory(market.slug, market.source, apiRange),
892
+ staleTime: 6e4,
893
+ enabled: Boolean(market.slug)
894
+ }))
895
+ });
896
+ const isLoading = queries.some((q) => q.isLoading);
897
+ const series = react.useMemo(() => {
898
+ return markets.map((market, idx) => {
899
+ const result = queries[idx];
900
+ const points = result?.data?.points ?? [];
901
+ if (points.length > 0) {
902
+ return {
903
+ marketSlug: market.slug,
904
+ label: market.outcomes?.[0]?.label ?? market.question,
905
+ data: points.map((pt) => ({
906
+ timestamp: pt.t * 1e3,
907
+ price: pt.p
908
+ }))
909
+ };
910
+ }
911
+ const yesOutcome = market.outcomes?.find((o) => o.label.toLowerCase() === "yes") ?? market.outcomes?.[0];
912
+ const currentPrice = yesOutcome?.price ?? 0.5;
913
+ const now = Date.now();
914
+ const dayMs = 864e5;
915
+ return {
916
+ marketSlug: market.slug,
917
+ label: market.outcomes?.[0]?.label ?? market.question,
918
+ data: [
919
+ { timestamp: now - 7 * dayMs, price: currentPrice },
920
+ { timestamp: now, price: currentPrice }
921
+ ]
922
+ };
923
+ });
924
+ }, [markets, queries]);
925
+ return { series, isLoading };
926
+ }
927
+ function orderbookQueryKey(slug, source) {
928
+ return ["predict", "orderbook", slug, source];
929
+ }
930
+ function useOrderbook(params, queryOptions = {}) {
931
+ const client = usePredictClient();
932
+ return reactQuery.useQuery({
933
+ queryKey: orderbookQueryKey(params.slug, params.source),
934
+ queryFn: () => client.getOrderbook(params.slug, params.source),
935
+ enabled: Boolean(params.slug),
936
+ refetchInterval: 5e3,
937
+ ...queryOptions
938
+ });
939
+ }
940
+ function marketTradesQueryKey(slug, params) {
941
+ return ["predict", "market-trades", slug, params];
942
+ }
943
+ function useMarketTrades(params, queryOptions = {}) {
944
+ const client = usePredictClient();
945
+ const apiParams = {
946
+ source: params.source,
947
+ limit: params.limit,
948
+ cursor: params.cursor,
949
+ side: params.side
950
+ };
951
+ return reactQuery.useQuery({
952
+ queryKey: marketTradesQueryKey(params.slug, apiParams),
953
+ queryFn: () => client.listMarketTrades(params.slug, apiParams),
954
+ enabled: Boolean(params.slug),
955
+ ...queryOptions
956
+ });
957
+ }
958
+ function candlesticksQueryKey(slug, params) {
959
+ return ["predict", "candlesticks", slug, params];
960
+ }
961
+ function useCandlesticks(params, queryOptions = {}) {
962
+ const client = usePredictClient();
963
+ const apiParams = {
964
+ interval: params.interval,
965
+ limit: params.limit
966
+ };
967
+ return reactQuery.useQuery({
968
+ queryKey: candlesticksQueryKey(params.slug, apiParams),
969
+ queryFn: () => client.listCandlesticks(params.slug, apiParams),
970
+ enabled: Boolean(params.slug),
971
+ staleTime: 6e4,
972
+ ...queryOptions
973
+ });
974
+ }
975
+ function positionsQueryKey(user, source) {
976
+ return ["predict", "positions", source ?? "all", user];
977
+ }
978
+ function usePositions(params, queryOptions = {}) {
979
+ const client = usePredictClient();
980
+ return reactQuery.useQuery({
981
+ queryKey: positionsQueryKey(params.user, params.source),
982
+ queryFn: () => client.getPositions(params.user, params.source),
983
+ enabled: Boolean(params.user),
984
+ staleTime: 1e4,
985
+ ...queryOptions
986
+ });
987
+ }
988
+ function balanceQueryKey(source, user) {
989
+ return ["predict", "balance", source, user];
990
+ }
991
+ function useBalance(params, queryOptions = {}) {
992
+ const client = usePredictClient();
993
+ return reactQuery.useQuery({
994
+ queryKey: balanceQueryKey(params.source, params.user),
995
+ queryFn: () => client.getBalance(params.source, params.user),
996
+ enabled: Boolean(params.user && params.source),
997
+ staleTime: 1e4,
998
+ ...queryOptions
999
+ });
1000
+ }
1001
+ function ordersQueryKey(params) {
1002
+ return ["predict", "orders", params];
1003
+ }
1004
+ function useOrders(params, queryOptions = {}) {
1005
+ const client = usePredictClient();
1006
+ return reactQuery.useQuery({
1007
+ queryKey: ordersQueryKey(params),
1008
+ queryFn: () => client.listOrders(params),
1009
+ enabled: Boolean(params.wallet_address),
1010
+ ...queryOptions
1011
+ });
1012
+ }
1013
+ function orderQueryKey(id, source) {
1014
+ return ["predict", "order", id, source];
1015
+ }
1016
+ function useOrder(params, queryOptions = {}) {
1017
+ const client = usePredictClient();
1018
+ return reactQuery.useQuery({
1019
+ queryKey: orderQueryKey(params.id, params.source),
1020
+ queryFn: () => client.getOrder(params.id, params.source),
1021
+ enabled: Boolean(params.id),
1022
+ refetchInterval: 1e3,
1023
+ ...queryOptions
1024
+ });
1025
+ }
1026
+ function useCancelOrder(mutationOptions = {}) {
1027
+ const client = usePredictClient();
1028
+ const queryClient = reactQuery.useQueryClient();
1029
+ return reactQuery.useMutation({
1030
+ mutationFn: (vars) => client.cancelOrder(vars.id, vars.source),
1031
+ onSuccess: () => {
1032
+ queryClient.invalidateQueries({ queryKey: ["predict", "orders"] });
1033
+ },
1034
+ ...mutationOptions
1035
+ });
1036
+ }
1037
+ function tradesQueryKey(params) {
1038
+ return ["predict", "trades-by-wallet", params];
1039
+ }
1040
+ function useTrades(params, queryOptions = {}) {
1041
+ const client = usePredictClient();
1042
+ return reactQuery.useQuery({
1043
+ queryKey: tradesQueryKey(params),
1044
+ queryFn: () => client.listTrades(params),
1045
+ enabled: Boolean(params.wallet),
1046
+ ...queryOptions
1047
+ });
1048
+ }
1049
+ function dflowQuoteQueryKey(params) {
1050
+ return ["predict", "dflow-quote", params];
1051
+ }
1052
+ function useDFlowQuote(params, queryOptions = {}) {
1053
+ const client = usePredictClient();
1054
+ const enabled = Boolean(params.inputMint) && Boolean(params.outputMint) && Boolean(params.userPublicKey) && params.amount !== "0" && params.amount !== "";
1055
+ return reactQuery.useQuery({
1056
+ queryKey: dflowQuoteQueryKey(params),
1057
+ queryFn: () => client.createDFlowQuote(params),
1058
+ enabled,
1059
+ staleTime: 1e4,
1060
+ ...queryOptions
1061
+ });
1062
+ }
1063
+ function useDFlowSubmit(mutationOptions = {}) {
1064
+ const client = usePredictClient();
1065
+ const queryClient = reactQuery.useQueryClient();
1066
+ return reactQuery.useMutation({
1067
+ mutationFn: (body) => client.submitDFlowTransaction(body),
1068
+ onSuccess: () => {
1069
+ queryClient.invalidateQueries({ queryKey: ["predict", "orders"] });
1070
+ queryClient.invalidateQueries({ queryKey: ["predict", "positions"] });
1071
+ },
1072
+ ...mutationOptions
1073
+ });
1074
+ }
1075
+ function usePredictWsClient() {
1076
+ const context = react.useContext(PredictContext);
1077
+ if (!context) {
1078
+ throw new Error("usePredictWsClient must be used within a PredictProvider");
1079
+ }
1080
+ const { wsClient } = context;
1081
+ const [wsStatus, setWsStatus] = react.useState(
1082
+ wsClient?.getStatus() ?? "disconnected"
1083
+ );
1084
+ react.useEffect(() => {
1085
+ if (!wsClient) {
1086
+ setWsStatus("disconnected");
1087
+ return;
1088
+ }
1089
+ setWsStatus(wsClient.getStatus());
1090
+ return wsClient.onStatusChange(setWsStatus);
1091
+ }, [wsClient]);
1092
+ return {
1093
+ wsClient,
1094
+ wsStatus,
1095
+ isWsConnected: wsStatus === "connected"
1096
+ };
1097
+ }
1098
+ function usePricesSubscription({
1099
+ wsClient,
1100
+ slugs,
1101
+ enabled = true,
1102
+ onUpdate
1103
+ }) {
1104
+ const [prices, setPrices] = react.useState(
1105
+ () => /* @__PURE__ */ new Map()
1106
+ );
1107
+ const [isSubscribed, setIsSubscribed] = react.useState(false);
1108
+ const onUpdateRef = react.useRef(onUpdate);
1109
+ onUpdateRef.current = onUpdate;
1110
+ const slugsKey = slugs.join(",");
1111
+ const handleUpdate = react.useCallback((msg) => {
1112
+ setPrices((prev) => {
1113
+ const next = new Map(prev);
1114
+ next.set(msg.data.market_slug, msg.data);
1115
+ return next;
1116
+ });
1117
+ onUpdateRef.current?.(msg);
1118
+ }, []);
1119
+ react.useEffect(() => {
1120
+ if (!wsClient || !enabled || slugs.length === 0) {
1121
+ setIsSubscribed(false);
1122
+ return;
1123
+ }
1124
+ const unsub = wsClient.subscribePrices(slugs, handleUpdate);
1125
+ setIsSubscribed(true);
1126
+ return () => {
1127
+ unsub();
1128
+ setIsSubscribed(false);
1129
+ };
1130
+ }, [wsClient, enabled, slugsKey, handleUpdate]);
1131
+ return { prices, isSubscribed };
1132
+ }
1133
+ function useOrderbookSubscription({
1134
+ wsClient,
1135
+ slug,
1136
+ enabled = true,
1137
+ onUpdate
1138
+ }) {
1139
+ const [orderbook, setOrderbook] = react.useState(null);
1140
+ const [isSubscribed, setIsSubscribed] = react.useState(false);
1141
+ const onUpdateRef = react.useRef(onUpdate);
1142
+ onUpdateRef.current = onUpdate;
1143
+ const handleUpdate = react.useCallback(
1144
+ (msg) => {
1145
+ if (msg.data.market_slug === slug) {
1146
+ setOrderbook(msg.data);
1147
+ onUpdateRef.current?.(msg);
1148
+ }
1149
+ },
1150
+ [slug]
1151
+ );
1152
+ react.useEffect(() => {
1153
+ if (!wsClient || !enabled || !slug) {
1154
+ setIsSubscribed(false);
1155
+ return;
1156
+ }
1157
+ setOrderbook(null);
1158
+ const unsub = wsClient.subscribeOrderbook([slug], handleUpdate);
1159
+ setIsSubscribed(true);
1160
+ return () => {
1161
+ unsub();
1162
+ setIsSubscribed(false);
1163
+ };
1164
+ }, [wsClient, enabled, slug, handleUpdate]);
1165
+ return { orderbook, isSubscribed };
1166
+ }
1167
+ var DEFAULT_MAX_HISTORY = 100;
1168
+ function useTradesSubscription({
1169
+ wsClient,
1170
+ slug,
1171
+ enabled = true,
1172
+ maxHistory = DEFAULT_MAX_HISTORY,
1173
+ onUpdate
1174
+ }) {
1175
+ const [trades, setTrades] = react.useState([]);
1176
+ const [isSubscribed, setIsSubscribed] = react.useState(false);
1177
+ const onUpdateRef = react.useRef(onUpdate);
1178
+ onUpdateRef.current = onUpdate;
1179
+ const maxRef = react.useRef(maxHistory);
1180
+ maxRef.current = maxHistory;
1181
+ const handleUpdate = react.useCallback(
1182
+ (msg) => {
1183
+ if (msg.data.market_slug === slug) {
1184
+ setTrades((prev) => {
1185
+ const next = [msg.data, ...prev];
1186
+ return next.length > maxRef.current ? next.slice(0, maxRef.current) : next;
1187
+ });
1188
+ onUpdateRef.current?.(msg);
1189
+ }
1190
+ },
1191
+ [slug]
1192
+ );
1193
+ const clearHistory = react.useCallback(() => {
1194
+ setTrades([]);
1195
+ }, []);
1196
+ react.useEffect(() => {
1197
+ if (!wsClient || !enabled || !slug) {
1198
+ setIsSubscribed(false);
1199
+ return;
1200
+ }
1201
+ setTrades([]);
1202
+ const unsub = wsClient.subscribeTrades([slug], handleUpdate);
1203
+ setIsSubscribed(true);
1204
+ return () => {
1205
+ unsub();
1206
+ setIsSubscribed(false);
1207
+ };
1208
+ }, [wsClient, enabled, slug, handleUpdate]);
1209
+ return { trades, isSubscribed, clearHistory };
1210
+ }
1211
+ var REST_POLL_INTERVAL = 5e3;
1212
+ function useRealtimeOrderbook(params, queryOptions = {}) {
1213
+ const { wsClient } = usePredictWsClient();
1214
+ const queryClient = reactQuery.useQueryClient();
1215
+ const subParams = {
1216
+ wsClient,
1217
+ slug: params.slug,
1218
+ enabled: Boolean(params.slug)
1219
+ };
1220
+ const { isSubscribed } = useOrderbookSubscription(subParams);
1221
+ const qk = orderbookQueryKey(params.slug, params.source);
1222
+ const qkRef = react.useRef(qk);
1223
+ qkRef.current = qk;
1224
+ react.useEffect(() => {
1225
+ if (!wsClient || !params.slug) return;
1226
+ const unsub = wsClient.subscribeOrderbook([params.slug], (msg) => {
1227
+ const ob = {
1228
+ market_id: msg.data.market_slug,
1229
+ bids: msg.data.bids,
1230
+ asks: msg.data.asks,
1231
+ spread: msg.data.spread
1232
+ };
1233
+ queryClient.setQueryData(qkRef.current, ob);
1234
+ });
1235
+ return unsub;
1236
+ }, [wsClient, params.slug, queryClient]);
1237
+ return useOrderbook(params, {
1238
+ refetchInterval: isSubscribed ? false : REST_POLL_INTERVAL,
1239
+ ...queryOptions
1240
+ });
1241
+ }
1242
+
1243
+ // src/hooks/predict/useRealtimePrices.ts
1244
+ function useRealtimePrices({
1245
+ slugs,
1246
+ enabled = true,
1247
+ onUpdate
1248
+ }) {
1249
+ const { wsClient } = usePredictWsClient();
1250
+ return usePricesSubscription({
1251
+ wsClient,
1252
+ slugs,
1253
+ enabled,
1254
+ onUpdate
1255
+ });
1256
+ }
1257
+ function useRealtimeTrades({
1258
+ slug,
1259
+ enabled = true,
1260
+ maxHistory,
1261
+ onUpdate,
1262
+ syncToQueryCache = true
1263
+ }) {
1264
+ const { wsClient } = usePredictWsClient();
1265
+ const queryClient = reactQuery.useQueryClient();
1266
+ const slugRef = react.useRef(slug);
1267
+ slugRef.current = slug;
1268
+ react.useEffect(() => {
1269
+ if (!wsClient || !slug || !enabled || !syncToQueryCache) return;
1270
+ const unsub = wsClient.subscribeTrades([slug], (msg) => {
1271
+ const trade = msg.data;
1272
+ const prefix = ["predict", "market-trades", slugRef.current];
1273
+ queryClient.setQueriesData(
1274
+ { queryKey: prefix },
1275
+ (old) => {
1276
+ if (!old) return old;
1277
+ const syntheticTrade = {
1278
+ id: trade.trade_id ?? `ws-${msg.ts}`,
1279
+ source: "dflow",
1280
+ side: trade.side,
1281
+ outcome: trade.outcome ?? "",
1282
+ outcome_index: 0,
1283
+ price: trade.price,
1284
+ size: trade.size,
1285
+ usd_size: trade.size,
1286
+ timestamp: Math.floor(msg.ts / 1e3),
1287
+ tx_hash: "",
1288
+ wallet: "",
1289
+ type: "TRADE"
1290
+ };
1291
+ return {
1292
+ ...old,
1293
+ items: [syntheticTrade, ...old.items]
1294
+ };
1295
+ }
1296
+ );
1297
+ });
1298
+ return unsub;
1299
+ }, [wsClient, slug, enabled, syncToQueryCache, queryClient]);
1300
+ return useTradesSubscription({
1301
+ wsClient,
1302
+ slug,
1303
+ enabled,
1304
+ maxHistory,
1305
+ onUpdate
1306
+ });
1307
+ }
1308
+ function usePolymarket() {
1309
+ const context = react.useContext(PolymarketContext);
1310
+ if (!context) {
1311
+ throw new Error("usePolymarket must be used within a PolymarketProvider");
1312
+ }
1313
+ return context;
1314
+ }
1315
+
1316
+ // src/utils/polymarket-order.ts
1317
+ var CTF_EXCHANGE_ADDRESS = "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E";
1318
+ var NEG_RISK_CTF_EXCHANGE_ADDRESS = "0xC5d563A36AE78145C45a50134d48A1215220f80a";
1319
+ var USDC_ADDRESS = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";
1320
+ var POLYGON_CHAIN_ID = 137;
1321
+ function buildCtfExchangeDomain(negRisk = false) {
1322
+ return {
1323
+ name: "CTFExchange",
1324
+ version: "1",
1325
+ chainId: POLYGON_CHAIN_ID,
1326
+ verifyingContract: negRisk ? NEG_RISK_CTF_EXCHANGE_ADDRESS : CTF_EXCHANGE_ADDRESS
1327
+ };
1328
+ }
1329
+ var CTF_ORDER_TYPES = {
1330
+ Order: [
1331
+ { name: "salt", type: "uint256" },
1332
+ { name: "maker", type: "address" },
1333
+ { name: "signer", type: "address" },
1334
+ { name: "taker", type: "address" },
1335
+ { name: "tokenId", type: "uint256" },
1336
+ { name: "makerAmount", type: "uint256" },
1337
+ { name: "takerAmount", type: "uint256" },
1338
+ { name: "expiration", type: "uint256" },
1339
+ { name: "nonce", type: "uint256" },
1340
+ { name: "feeRateBps", type: "uint256" },
1341
+ { name: "side", type: "uint8" },
1342
+ { name: "signatureType", type: "uint8" }
1343
+ ]
1344
+ };
1345
+ var ORDER_TYPE = {
1346
+ GTC: 0,
1347
+ // Good-Till-Cancelled
1348
+ FOK: 1,
1349
+ // Fill-Or-Kill
1350
+ GTD: 2,
1351
+ // Good-Till-Date
1352
+ FAK: 3
1353
+ // Fill-And-Kill
1354
+ };
1355
+ var SIDE = { BUY: 0, SELL: 1 };
1356
+ var TICK_DECIMALS = {
1357
+ "0.1": 1,
1358
+ "0.01": 2,
1359
+ "0.001": 3,
1360
+ "0.0001": 4
1361
+ };
1362
+ function roundToTick(price, tickSize) {
1363
+ const decimals = TICK_DECIMALS[tickSize] ?? 2;
1364
+ return price.toFixed(decimals);
1365
+ }
1366
+ function toMicroUsdc(amount) {
1367
+ return BigInt(Math.round(amount * 1e6));
1368
+ }
1369
+ function buildOrderMessage(input) {
1370
+ const side = input.side === "BUY" ? SIDE.BUY : SIDE.SELL;
1371
+ const priceStr = roundToTick(input.price, input.tickSize);
1372
+ const priceNum = parseFloat(priceStr);
1373
+ const sizeShares = toMicroUsdc(input.size);
1374
+ const sizeUsdc = toMicroUsdc(input.size * priceNum);
1375
+ const makerAmount = side === SIDE.BUY ? sizeUsdc.toString() : sizeShares.toString();
1376
+ const takerAmount = side === SIDE.BUY ? sizeShares.toString() : sizeUsdc.toString();
1377
+ const maker = input.funderAddress ?? input.signerAddress;
1378
+ return {
1379
+ salt: Math.floor(Math.random() * 1e15).toString(),
1380
+ maker,
1381
+ signer: input.signerAddress,
1382
+ taker: "0x0000000000000000000000000000000000000000",
1383
+ tokenId: input.tokenId,
1384
+ makerAmount,
1385
+ takerAmount,
1386
+ expiration: String(input.expiration ?? 0),
1387
+ nonce: "0",
1388
+ feeRateBps: "0",
1389
+ side,
1390
+ signatureType: input.signatureType
1391
+ };
1392
+ }
1393
+ function buildSignedOrder(input) {
1394
+ const message = buildOrderMessage(input);
1395
+ return {
1396
+ ...message,
1397
+ signature: input.signature,
1398
+ orderType: input.orderType ?? "GTC"
1399
+ };
1400
+ }
1401
+
1402
+ // src/hooks/polymarket/useCreatePolymarketOrder.ts
1403
+ function useCreatePolymarketOrder(mutationOptions = {}) {
1404
+ const client = usePredictClient();
1405
+ const { credentials, authenticate } = usePolymarket();
1406
+ const queryClient = reactQuery.useQueryClient();
1407
+ return reactQuery.useMutation({
1408
+ mutationFn: async ({
1409
+ input,
1410
+ signer
1411
+ }) => {
1412
+ const creds = credentials;
1413
+ if (!creds) {
1414
+ await authenticate(signer);
1415
+ if (!creds) {
1416
+ throw new Error(
1417
+ "Polymarket authentication failed. Cannot create order without credentials."
1418
+ );
1419
+ }
1420
+ }
1421
+ const domain = buildCtfExchangeDomain(input.negRisk ?? false);
1422
+ const orderMessage = buildOrderMessage({
1423
+ ...input,
1424
+ signerAddress: signer.address,
1425
+ signatureType: signer.signatureType
1426
+ });
1427
+ const signature = await signer.signTypedData(
1428
+ domain,
1429
+ CTF_ORDER_TYPES,
1430
+ orderMessage
1431
+ );
1432
+ const signedOrder = buildSignedOrder({
1433
+ ...input,
1434
+ signerAddress: signer.address,
1435
+ signatureType: signer.signatureType,
1436
+ signature
1437
+ });
1438
+ const body = JSON.stringify(signedOrder);
1439
+ const headers = await buildPolymarketL2Headers(creds.address, {
1440
+ apiKey: creds.apiKey,
1441
+ secret: creds.secret,
1442
+ passphrase: creds.passphrase,
1443
+ method: "POST",
1444
+ requestPath: "/api/v1/orders/polymarket",
1445
+ body
1446
+ });
1447
+ return client.createPolymarketOrder(
1448
+ signedOrder,
1449
+ headers
1450
+ );
1451
+ },
1452
+ onSuccess: () => {
1453
+ queryClient.invalidateQueries({ queryKey: ["predict", "orders"] });
1454
+ queryClient.invalidateQueries({ queryKey: ["predict", "positions"] });
1455
+ },
1456
+ ...mutationOptions
1457
+ });
1458
+ }
1459
+
1460
+ exports.CLOB_AUTH_DOMAIN = CLOB_AUTH_DOMAIN;
1461
+ exports.CLOB_AUTH_TYPES = CLOB_AUTH_TYPES;
1462
+ exports.CTF_EXCHANGE_ADDRESS = CTF_EXCHANGE_ADDRESS;
1463
+ exports.CTF_ORDER_TYPES = CTF_ORDER_TYPES;
1464
+ exports.ChartRange = ChartRange;
1465
+ exports.NEG_RISK_CTF_EXCHANGE_ADDRESS = NEG_RISK_CTF_EXCHANGE_ADDRESS;
1466
+ exports.ORDER_TYPE = ORDER_TYPE;
1467
+ exports.POLYGON_CHAIN_ID = POLYGON_CHAIN_ID;
1468
+ exports.PolymarketContext = PolymarketContext;
1469
+ exports.PolymarketProvider = PolymarketProvider;
1470
+ exports.PredictClient = PredictClient;
1471
+ exports.PredictContext = PredictContext;
1472
+ exports.PredictProvider = PredictProvider;
1473
+ exports.PredictWsClient = PredictWsClient;
1474
+ exports.SIDE = SIDE;
1475
+ exports.USDC_ADDRESS = USDC_ADDRESS;
1476
+ exports.balanceQueryKey = balanceQueryKey;
1477
+ exports.buildClobAuthMessage = buildClobAuthMessage;
1478
+ exports.buildCtfExchangeDomain = buildCtfExchangeDomain;
1479
+ exports.buildOrderMessage = buildOrderMessage;
1480
+ exports.buildPolymarketL2Headers = buildPolymarketL2Headers;
1481
+ exports.buildSignedOrder = buildSignedOrder;
1482
+ exports.candlesticksQueryKey = candlesticksQueryKey;
1483
+ exports.createPredictClient = createPredictClient;
1484
+ exports.createPredictWsClient = createPredictWsClient;
1485
+ exports.derivePolymarketApiKey = derivePolymarketApiKey;
1486
+ exports.dflowQuoteQueryKey = dflowQuoteQueryKey;
1487
+ exports.eventQueryKey = eventQueryKey;
1488
+ exports.eventsQueryKey = eventsQueryKey;
1489
+ exports.fetchEvent = fetchEvent;
1490
+ exports.fetchEvents = fetchEvents;
1491
+ exports.fetchEventsPage = fetchEventsPage;
1492
+ exports.fetchMarket = fetchMarket;
1493
+ exports.hmacSha256Base64 = hmacSha256Base64;
1494
+ exports.infiniteEventsQueryKey = infiniteEventsQueryKey;
1495
+ exports.marketQueryKey = marketQueryKey;
1496
+ exports.marketTradesQueryKey = marketTradesQueryKey;
1497
+ exports.orderQueryKey = orderQueryKey;
1498
+ exports.orderbookQueryKey = orderbookQueryKey;
1499
+ exports.ordersQueryKey = ordersQueryKey;
1500
+ exports.positionsQueryKey = positionsQueryKey;
1501
+ exports.priceHistoryQueryKey = priceHistoryQueryKey;
1502
+ exports.resolveEventsParams = resolveEventsParams;
1503
+ exports.resolveTagSlug = resolveTagSlug;
1504
+ exports.similarEventsQueryKey = similarEventsQueryKey;
1505
+ exports.tradesQueryKey = tradesQueryKey;
1506
+ exports.useBalance = useBalance;
1507
+ exports.useCancelOrder = useCancelOrder;
1508
+ exports.useCandlesticks = useCandlesticks;
1509
+ exports.useCreatePolymarketOrder = useCreatePolymarketOrder;
1510
+ exports.useDFlowQuote = useDFlowQuote;
1511
+ exports.useDFlowSubmit = useDFlowSubmit;
1512
+ exports.useEvent = useEvent;
1513
+ exports.useEvents = useEvents;
1514
+ exports.useInfiniteEvents = useInfiniteEvents;
1515
+ exports.useMarket = useMarket;
1516
+ exports.useMarketHistory = useMarketHistory;
1517
+ exports.useMarketTrades = useMarketTrades;
1518
+ exports.useOrder = useOrder;
1519
+ exports.useOrderbook = useOrderbook;
1520
+ exports.useOrderbookSubscription = useOrderbookSubscription;
1521
+ exports.useOrders = useOrders;
1522
+ exports.usePolymarket = usePolymarket;
1523
+ exports.usePositions = usePositions;
1524
+ exports.usePredictClient = usePredictClient;
1525
+ exports.usePredictWsClient = usePredictWsClient;
1526
+ exports.usePriceHistory = usePriceHistory;
1527
+ exports.usePricesSubscription = usePricesSubscription;
1528
+ exports.useRealtimeOrderbook = useRealtimeOrderbook;
1529
+ exports.useRealtimePrices = useRealtimePrices;
1530
+ exports.useRealtimeTrades = useRealtimeTrades;
1531
+ exports.useSearchEvents = useSearchEvents;
1532
+ exports.useSimilarEvents = useSimilarEvents;
1533
+ exports.useTrades = useTrades;
1534
+ exports.useTradesSubscription = useTradesSubscription;
1535
+ //# sourceMappingURL=index.js.map
1536
+ //# sourceMappingURL=index.js.map