context-markets 0.4.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/dist/index.js ADDED
@@ -0,0 +1,1228 @@
1
+ // src/config.ts
2
+ var API_BASE = "https://api-testnet.context.markets/v2";
3
+ var SETTLEMENT_ADDRESS = "0xD91935a82Af48ff79a68134d9Eab8fc9e5d3504D";
4
+ var HOLDINGS_ADDRESS = "0x0a6D61723E8AE8e34734A84075a1b58aB3eEca6a";
5
+ var USDC_ADDRESS = "0xBbee2756d3169CF7065e5E9C4A5EA9b1D1Fd415e";
6
+ var PERMIT2_ADDRESS = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
7
+ var CHAIN_ID = 84532;
8
+ var EIP712_DOMAIN = {
9
+ name: "Settlement",
10
+ version: "1",
11
+ chainId: CHAIN_ID,
12
+ verifyingContract: SETTLEMENT_ADDRESS
13
+ };
14
+ var ORDER_TYPES = {
15
+ Order: [
16
+ { name: "marketId", type: "bytes32" },
17
+ { name: "trader", type: "address" },
18
+ { name: "price", type: "uint256" },
19
+ { name: "size", type: "uint256" },
20
+ { name: "outcomeIndex", type: "uint8" },
21
+ { name: "side", type: "uint8" },
22
+ { name: "nonce", type: "bytes32" },
23
+ { name: "expiry", type: "uint256" },
24
+ { name: "maxFee", type: "uint256" },
25
+ { name: "makerRoleConstraint", type: "uint8" },
26
+ { name: "inventoryModeConstraint", type: "uint8" }
27
+ ]
28
+ };
29
+ var MARKET_ORDER_INTENT_TYPES = {
30
+ MarketOrderIntent: [
31
+ { name: "marketId", type: "bytes32" },
32
+ { name: "trader", type: "address" },
33
+ { name: "maxSize", type: "uint256" },
34
+ { name: "maxPrice", type: "uint256" },
35
+ { name: "outcomeIndex", type: "uint8" },
36
+ { name: "side", type: "uint8" },
37
+ { name: "nonce", type: "bytes32" },
38
+ { name: "expiry", type: "uint256" },
39
+ { name: "maxFee", type: "uint256" }
40
+ ]
41
+ };
42
+ var CANCEL_TYPES = {
43
+ CancelNonce: [
44
+ { name: "trader", type: "address" },
45
+ { name: "nonce", type: "bytes32" }
46
+ ]
47
+ };
48
+ var HOLDINGS_EIP712_DOMAIN = {
49
+ name: "Holdings",
50
+ version: "1",
51
+ chainId: CHAIN_ID,
52
+ verifyingContract: HOLDINGS_ADDRESS
53
+ };
54
+ var PERMIT2_EIP712_DOMAIN = {
55
+ name: "Permit2",
56
+ chainId: CHAIN_ID,
57
+ verifyingContract: PERMIT2_ADDRESS
58
+ };
59
+ var OPERATOR_APPROVAL_TYPES = {
60
+ OperatorApproval: [
61
+ { name: "user", type: "address" },
62
+ { name: "operator", type: "address" },
63
+ { name: "approved", type: "bool" },
64
+ { name: "nonce", type: "uint256" },
65
+ { name: "deadline", type: "uint256" }
66
+ ]
67
+ };
68
+ var PERMIT_TRANSFER_FROM_TYPES = {
69
+ TokenPermissions: [
70
+ { name: "token", type: "address" },
71
+ { name: "amount", type: "uint256" }
72
+ ],
73
+ PermitTransferFrom: [
74
+ { name: "permitted", type: "TokenPermissions" },
75
+ { name: "spender", type: "address" },
76
+ { name: "nonce", type: "uint256" },
77
+ { name: "deadline", type: "uint256" }
78
+ ]
79
+ };
80
+ var ERC20_ABI = [
81
+ {
82
+ name: "approve",
83
+ type: "function",
84
+ stateMutability: "nonpayable",
85
+ inputs: [
86
+ { name: "spender", type: "address" },
87
+ { name: "amount", type: "uint256" }
88
+ ],
89
+ outputs: [{ name: "", type: "bool" }]
90
+ },
91
+ {
92
+ name: "allowance",
93
+ type: "function",
94
+ stateMutability: "view",
95
+ inputs: [
96
+ { name: "owner", type: "address" },
97
+ { name: "spender", type: "address" }
98
+ ],
99
+ outputs: [{ name: "", type: "uint256" }]
100
+ }
101
+ ];
102
+ var HOLDINGS_ABI = [
103
+ {
104
+ name: "balanceOf",
105
+ type: "function",
106
+ stateMutability: "view",
107
+ inputs: [
108
+ { name: "user", type: "address" },
109
+ { name: "token", type: "address" }
110
+ ],
111
+ outputs: [{ name: "", type: "uint256" }]
112
+ },
113
+ {
114
+ name: "setOperator",
115
+ type: "function",
116
+ stateMutability: "nonpayable",
117
+ inputs: [
118
+ { name: "operator", type: "address" },
119
+ { name: "approved", type: "bool" }
120
+ ],
121
+ outputs: []
122
+ },
123
+ {
124
+ name: "isOperatorFor",
125
+ type: "function",
126
+ stateMutability: "view",
127
+ inputs: [
128
+ { name: "owner", type: "address" },
129
+ { name: "operator", type: "address" }
130
+ ],
131
+ outputs: [{ name: "", type: "bool" }]
132
+ },
133
+ {
134
+ name: "deposit",
135
+ type: "function",
136
+ stateMutability: "nonpayable",
137
+ inputs: [
138
+ { name: "token", type: "address" },
139
+ { name: "amount", type: "uint256" }
140
+ ],
141
+ outputs: []
142
+ },
143
+ {
144
+ name: "withdraw",
145
+ type: "function",
146
+ stateMutability: "nonpayable",
147
+ inputs: [
148
+ { name: "token", type: "address" },
149
+ { name: "amount", type: "uint256" }
150
+ ],
151
+ outputs: []
152
+ }
153
+ ];
154
+ var SETTLEMENT_ABI = [
155
+ {
156
+ name: "mintCompleteSetsFromHoldings",
157
+ type: "function",
158
+ stateMutability: "nonpayable",
159
+ inputs: [
160
+ { name: "marketId", type: "bytes32" },
161
+ { name: "amount", type: "uint256" }
162
+ ],
163
+ outputs: []
164
+ },
165
+ {
166
+ name: "burnCompleteSetsFromHoldings",
167
+ type: "function",
168
+ stateMutability: "nonpayable",
169
+ inputs: [
170
+ { name: "marketId", type: "bytes32" },
171
+ { name: "amount", type: "uint256" },
172
+ { name: "recipient", type: "address" },
173
+ { name: "creditInternal", type: "bool" }
174
+ ],
175
+ outputs: []
176
+ }
177
+ ];
178
+ var OPERATOR_NONCE_ABI = [
179
+ {
180
+ name: "operatorNonce",
181
+ type: "function",
182
+ stateMutability: "view",
183
+ inputs: [{ name: "user", type: "address" }],
184
+ outputs: [{ name: "", type: "uint256" }]
185
+ }
186
+ ];
187
+
188
+ // src/errors.ts
189
+ var ContextApiError = class extends Error {
190
+ status;
191
+ body;
192
+ constructor(status, body) {
193
+ const message = typeof body === "object" && body !== null && "message" in body ? String(body.message) : `API request failed with status ${status}`;
194
+ super(message);
195
+ this.name = "ContextApiError";
196
+ this.status = status;
197
+ this.body = body;
198
+ }
199
+ };
200
+ var ContextSigningError = class extends Error {
201
+ constructor(message, cause) {
202
+ super(message);
203
+ this.name = "ContextSigningError";
204
+ if (cause) this.cause = cause;
205
+ }
206
+ };
207
+ var ContextConfigError = class extends Error {
208
+ constructor(message) {
209
+ super(message);
210
+ this.name = "ContextConfigError";
211
+ }
212
+ };
213
+
214
+ // src/http.ts
215
+ function createHttpClient(options = {}) {
216
+ const apiKey = options.apiKey;
217
+ const baseUrl = options.baseUrl ?? API_BASE;
218
+ const fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);
219
+ function headers() {
220
+ const h = {
221
+ "Content-Type": "application/json"
222
+ };
223
+ if (apiKey) {
224
+ h["Authorization"] = `Bearer ${apiKey}`;
225
+ }
226
+ return h;
227
+ }
228
+ async function request(method, url, body) {
229
+ const init = { method, headers: headers() };
230
+ if (body !== void 0) {
231
+ init.body = JSON.stringify(body);
232
+ }
233
+ const res = await fetchFn(url, init);
234
+ if (!res.ok) {
235
+ const respBody = await res.json().catch(() => null);
236
+ throw new ContextApiError(res.status, respBody);
237
+ }
238
+ return res.json();
239
+ }
240
+ return {
241
+ async get(path, params) {
242
+ let url = `${baseUrl}${path}`;
243
+ if (params) {
244
+ const searchParams = [];
245
+ for (const [k, v] of Object.entries(params)) {
246
+ if (v !== void 0) {
247
+ searchParams.push(
248
+ `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`
249
+ );
250
+ }
251
+ }
252
+ if (searchParams.length > 0) {
253
+ url += `?${searchParams.join("&")}`;
254
+ }
255
+ }
256
+ return request("GET", url);
257
+ },
258
+ async post(path, body) {
259
+ return request("POST", `${baseUrl}${path}`, body);
260
+ },
261
+ async delete(path, body) {
262
+ return request("DELETE", `${baseUrl}${path}`, body);
263
+ }
264
+ };
265
+ }
266
+
267
+ // src/signing/eip712.ts
268
+ import {
269
+ createWalletClient,
270
+ http,
271
+ keccak256,
272
+ toBytes
273
+ } from "viem";
274
+ import { privateKeyToAccount } from "viem/accounts";
275
+ import { baseSepolia } from "viem/chains";
276
+ function resolveSigner(input) {
277
+ if ("privateKey" in input) {
278
+ const account = privateKeyToAccount(input.privateKey);
279
+ const walletClient = createWalletClient({
280
+ account,
281
+ chain: baseSepolia,
282
+ transport: http()
283
+ });
284
+ return { account, walletClient };
285
+ }
286
+ if ("account" in input) {
287
+ const walletClient = createWalletClient({
288
+ account: input.account,
289
+ chain: baseSepolia,
290
+ transport: http()
291
+ });
292
+ return { account: input.account, walletClient };
293
+ }
294
+ if ("walletClient" in input) {
295
+ const account = input.walletClient.account;
296
+ if (!account) {
297
+ throw new ContextSigningError(
298
+ "WalletClient must have an account configured"
299
+ );
300
+ }
301
+ return { account, walletClient: input.walletClient };
302
+ }
303
+ throw new ContextSigningError("Invalid signer input");
304
+ }
305
+ function randomNonce() {
306
+ return keccak256(toBytes(`${Date.now()}_${Math.random()}`));
307
+ }
308
+ async function signOrder(walletClient, account, order) {
309
+ try {
310
+ return await walletClient.signTypedData({
311
+ account,
312
+ domain: EIP712_DOMAIN,
313
+ types: ORDER_TYPES,
314
+ primaryType: "Order",
315
+ message: order
316
+ });
317
+ } catch (err) {
318
+ throw new ContextSigningError("Failed to sign order", err);
319
+ }
320
+ }
321
+ async function signMarketOrderIntent(walletClient, account, intent) {
322
+ try {
323
+ return await walletClient.signTypedData({
324
+ account,
325
+ domain: EIP712_DOMAIN,
326
+ types: MARKET_ORDER_INTENT_TYPES,
327
+ primaryType: "MarketOrderIntent",
328
+ message: intent
329
+ });
330
+ } catch (err) {
331
+ throw new ContextSigningError("Failed to sign market order intent", err);
332
+ }
333
+ }
334
+ async function signCancel(walletClient, account, trader, nonce) {
335
+ try {
336
+ return await walletClient.signTypedData({
337
+ account,
338
+ domain: EIP712_DOMAIN,
339
+ types: CANCEL_TYPES,
340
+ primaryType: "CancelNonce",
341
+ message: { trader, nonce }
342
+ });
343
+ } catch (err) {
344
+ throw new ContextSigningError("Failed to sign cancel", err);
345
+ }
346
+ }
347
+
348
+ // src/constants.ts
349
+ var PRICE_MULTIPLIER = 10000n;
350
+ var SIZE_MULTIPLIER = 1000000n;
351
+ var FEE_DIVISOR = 100n;
352
+ var DEFAULT_EXPIRY_SECONDS = 31536e3;
353
+
354
+ // src/order-builder/helpers.ts
355
+ function encodePriceCents(priceCents) {
356
+ if (!Number.isFinite(priceCents) || priceCents < 1 || priceCents > 99) {
357
+ throw new RangeError(`priceCents must be 1-99, got ${priceCents}`);
358
+ }
359
+ return BigInt(Math.round(priceCents * Number(PRICE_MULTIPLIER)));
360
+ }
361
+ function encodeSize(size) {
362
+ if (!Number.isFinite(size) || size < 0.01) {
363
+ throw new RangeError(`size must be >= 0.01, got ${size}`);
364
+ }
365
+ return BigInt(Math.round(size * Number(SIZE_MULTIPLIER)));
366
+ }
367
+ function calculateMaxFee(price, size) {
368
+ const fee = price * size / FEE_DIVISOR / SIZE_MULTIPLIER;
369
+ return fee < 1n ? 1n : fee;
370
+ }
371
+ function decodePriceCents(raw) {
372
+ return Number(raw) / Number(PRICE_MULTIPLIER);
373
+ }
374
+ function decodeSize(raw) {
375
+ return Number(raw) / Number(SIZE_MULTIPLIER);
376
+ }
377
+
378
+ // src/order-builder/builder.ts
379
+ var OrderBuilder = class {
380
+ constructor(walletClient, account) {
381
+ this.walletClient = walletClient;
382
+ this.account = account;
383
+ }
384
+ get address() {
385
+ return this.account.address;
386
+ }
387
+ async buildAndSign(req) {
388
+ const price = encodePriceCents(req.priceCents);
389
+ const size = encodeSize(req.size);
390
+ const maxFee = calculateMaxFee(price, size);
391
+ const nonce = randomNonce();
392
+ const expirySeconds = req.expirySeconds ?? DEFAULT_EXPIRY_SECONDS;
393
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + expirySeconds);
394
+ const order = {
395
+ marketId: req.marketId,
396
+ trader: this.address,
397
+ price,
398
+ size,
399
+ outcomeIndex: req.outcome === "yes" ? 1 : 0,
400
+ side: req.side === "buy" ? 0 : 1,
401
+ nonce,
402
+ expiry,
403
+ maxFee,
404
+ makerRoleConstraint: req.makerRoleConstraint ?? 0,
405
+ inventoryModeConstraint: req.inventoryModeConstraint ?? 0
406
+ };
407
+ const signature = await signOrder(this.walletClient, this.account, order);
408
+ return {
409
+ type: "limit",
410
+ ...order,
411
+ price: order.price.toString(),
412
+ size: order.size.toString(),
413
+ expiry: order.expiry.toString(),
414
+ maxFee: order.maxFee.toString(),
415
+ signature
416
+ };
417
+ }
418
+ async buildAndSignMarket(req) {
419
+ const maxPrice = encodePriceCents(req.maxPriceCents);
420
+ const maxSize = encodeSize(req.maxSize);
421
+ const maxFee = calculateMaxFee(maxPrice, maxSize);
422
+ const nonce = randomNonce();
423
+ const expirySeconds = req.expirySeconds ?? DEFAULT_EXPIRY_SECONDS;
424
+ const expiry = BigInt(Math.floor(Date.now() / 1e3) + expirySeconds);
425
+ const intent = {
426
+ marketId: req.marketId,
427
+ trader: this.address,
428
+ maxPrice,
429
+ maxSize,
430
+ outcomeIndex: req.outcome === "yes" ? 1 : 0,
431
+ side: req.side === "buy" ? 0 : 1,
432
+ nonce,
433
+ expiry,
434
+ maxFee
435
+ };
436
+ const signature = await signMarketOrderIntent(
437
+ this.walletClient,
438
+ this.account,
439
+ intent
440
+ );
441
+ return {
442
+ type: "market",
443
+ ...intent,
444
+ maxPrice: intent.maxPrice.toString(),
445
+ maxSize: intent.maxSize.toString(),
446
+ expiry: intent.expiry.toString(),
447
+ maxFee: intent.maxFee.toString(),
448
+ signature
449
+ };
450
+ }
451
+ async signCancel(nonce) {
452
+ return signCancel(
453
+ this.walletClient,
454
+ this.account,
455
+ this.address,
456
+ nonce
457
+ );
458
+ }
459
+ };
460
+
461
+ // src/generated/endpoints.ts
462
+ var ENDPOINTS = {
463
+ activity: {
464
+ global: "/activity"
465
+ },
466
+ markets: {
467
+ list: "/markets",
468
+ search: "/markets/search",
469
+ create: "/markets/create",
470
+ get: (id) => `/markets/${id}`,
471
+ activity: (id) => `/markets/${id}/activity`,
472
+ oracle: (id) => `/markets/${id}/oracle`,
473
+ oracleQuotes: (id) => `/markets/${id}/oracle/quotes`,
474
+ oracleQuotesLatest: (id) => `/markets/${id}/oracle/quotes/latest`,
475
+ orderbook: (id) => `/markets/${id}/orderbook`,
476
+ prices: (id) => `/markets/${id}/prices`,
477
+ quotes: (id) => `/markets/${id}/quotes`,
478
+ simulate: (id) => `/markets/${id}/simulate`
479
+ },
480
+ orders: {
481
+ create: "/orders",
482
+ recent: "/orders/recent",
483
+ get: (id) => `/orders/${id}`,
484
+ cancel: "/orders/cancel",
485
+ cancelReplace: "/orders/cancel-replace",
486
+ bulk: "/orders/bulk",
487
+ bulkCreate: "/orders/bulk/create",
488
+ bulkCancel: "/orders/bulk/cancel",
489
+ simulate: "/orders/simulate",
490
+ list: "/orders"
491
+ },
492
+ portfolio: {
493
+ get: (address) => `/portfolio/${address}`,
494
+ claimable: (address) => `/portfolio/${address}/claimable`,
495
+ positions: (address) => `/portfolio/${address}/positions`,
496
+ stats: (address) => `/portfolio/${address}/stats`
497
+ },
498
+ balance: {
499
+ mintTestUsdc: "/balance/mint-test-usdc",
500
+ tokenBalance: "/balance",
501
+ settlement: "/balance/settlement",
502
+ get: (address) => `/balance/${address}`
503
+ },
504
+ gasless: {
505
+ operator: "/gasless/operator",
506
+ depositWithPermit: "/gasless/deposit-with-permit"
507
+ },
508
+ questions: {
509
+ submit: "/questions",
510
+ agentSubmit: "/questions/agent-submit",
511
+ submission: (id) => `/questions/submissions/${id}`
512
+ }
513
+ };
514
+
515
+ // src/modules/markets.ts
516
+ var Markets = class {
517
+ constructor(http2) {
518
+ this.http = http2;
519
+ }
520
+ async list(params) {
521
+ return this.http.get(ENDPOINTS.markets.list, {
522
+ search: params?.query,
523
+ status: params?.status,
524
+ sortBy: params?.sortBy,
525
+ sort: params?.sort,
526
+ limit: params?.limit,
527
+ cursor: params?.cursor,
528
+ visibility: params?.visibility,
529
+ resolutionStatus: params?.resolutionStatus,
530
+ creator: params?.creator,
531
+ category: params?.category,
532
+ createdAfter: params?.createdAfter
533
+ });
534
+ }
535
+ async search(params) {
536
+ return this.http.get(ENDPOINTS.markets.search, {
537
+ q: params.q,
538
+ limit: params.limit,
539
+ offset: params.offset
540
+ });
541
+ }
542
+ async get(id) {
543
+ const res = await this.http.get(
544
+ ENDPOINTS.markets.get(id)
545
+ );
546
+ return res.market;
547
+ }
548
+ async quotes(marketId) {
549
+ return this.http.get(ENDPOINTS.markets.quotes(marketId));
550
+ }
551
+ async orderbook(marketId, params) {
552
+ return this.http.get(ENDPOINTS.markets.orderbook(marketId), {
553
+ depth: params?.depth,
554
+ outcomeIndex: params?.outcomeIndex
555
+ });
556
+ }
557
+ async fullOrderbook(marketId, params) {
558
+ const [no, yes] = await Promise.all([
559
+ this.orderbook(marketId, { ...params, outcomeIndex: 0 }),
560
+ this.orderbook(marketId, { ...params, outcomeIndex: 1 })
561
+ ]);
562
+ return {
563
+ marketId: yes.marketId,
564
+ yes: { bids: yes.bids, asks: yes.asks },
565
+ no: { bids: no.bids, asks: no.asks },
566
+ timestamp: yes.timestamp
567
+ };
568
+ }
569
+ async simulate(marketId, params) {
570
+ return this.http.post(
571
+ ENDPOINTS.markets.simulate(marketId),
572
+ {
573
+ side: params.side,
574
+ amount: params.amount,
575
+ amountType: params.amountType ?? "usd",
576
+ ...params.trader ? { trader: params.trader } : {}
577
+ }
578
+ );
579
+ }
580
+ async priceHistory(marketId, params) {
581
+ return this.http.get(ENDPOINTS.markets.prices(marketId), {
582
+ timeframe: params?.timeframe ?? params?.interval
583
+ });
584
+ }
585
+ async oracle(marketId) {
586
+ return this.http.get(ENDPOINTS.markets.oracle(marketId));
587
+ }
588
+ async oracleQuotes(marketId) {
589
+ return this.http.get(
590
+ ENDPOINTS.markets.oracleQuotes(marketId)
591
+ );
592
+ }
593
+ async latestOracleQuote(marketId) {
594
+ return this.http.get(
595
+ ENDPOINTS.markets.oracleQuotesLatest(marketId)
596
+ );
597
+ }
598
+ async requestOracleQuote(marketId) {
599
+ return this.http.post(
600
+ ENDPOINTS.markets.oracleQuotes(marketId),
601
+ {}
602
+ );
603
+ }
604
+ async activity(marketId, params) {
605
+ return this.http.get(
606
+ ENDPOINTS.markets.activity(marketId),
607
+ {
608
+ cursor: params?.cursor,
609
+ limit: params?.limit,
610
+ types: params?.types,
611
+ startTime: params?.startTime,
612
+ endTime: params?.endTime
613
+ }
614
+ );
615
+ }
616
+ async create(questionId) {
617
+ return this.http.post(ENDPOINTS.markets.create, {
618
+ questionId
619
+ });
620
+ }
621
+ async globalActivity(params) {
622
+ return this.http.get(ENDPOINTS.activity.global, {
623
+ cursor: params?.cursor,
624
+ limit: params?.limit,
625
+ types: params?.types,
626
+ startTime: params?.startTime,
627
+ endTime: params?.endTime
628
+ });
629
+ }
630
+ };
631
+
632
+ // src/modules/questions.ts
633
+ var DEFAULT_POLL_INTERVAL_MS = 2e3;
634
+ var DEFAULT_MAX_ATTEMPTS = 45;
635
+ var Questions = class {
636
+ constructor(http2) {
637
+ this.http = http2;
638
+ }
639
+ async submit(question) {
640
+ return this.http.post(ENDPOINTS.questions.submit, {
641
+ question
642
+ });
643
+ }
644
+ async agentSubmit(draft) {
645
+ return this.http.post(
646
+ ENDPOINTS.questions.agentSubmit,
647
+ draft
648
+ );
649
+ }
650
+ async agentSubmitAndWait(draft, options) {
651
+ const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
652
+ const maxAttempts = options?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
653
+ const { submissionId } = await this.agentSubmit(draft);
654
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
655
+ const submission = await this.getSubmission(submissionId);
656
+ if (submission.status === "completed") {
657
+ return submission;
658
+ }
659
+ if (submission.status === "failed") {
660
+ throw new Error(
661
+ `Agent submission ${submissionId} failed`
662
+ );
663
+ }
664
+ if (attempt < maxAttempts) {
665
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
666
+ }
667
+ }
668
+ throw new Error(
669
+ `Agent submission ${submissionId} did not complete within ${maxAttempts} attempts`
670
+ );
671
+ }
672
+ async getSubmission(submissionId) {
673
+ return this.http.get(
674
+ ENDPOINTS.questions.submission(submissionId)
675
+ );
676
+ }
677
+ async submitAndWait(question, options) {
678
+ const pollIntervalMs = options?.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
679
+ const maxAttempts = options?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
680
+ const { submissionId } = await this.submit(question);
681
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
682
+ const submission = await this.getSubmission(submissionId);
683
+ if (submission.status === "completed") {
684
+ return submission;
685
+ }
686
+ if (submission.status === "failed") {
687
+ throw new Error(
688
+ `Question submission ${submissionId} failed`
689
+ );
690
+ }
691
+ if (attempt < maxAttempts) {
692
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
693
+ }
694
+ }
695
+ throw new Error(
696
+ `Question submission ${submissionId} did not complete within ${maxAttempts} attempts`
697
+ );
698
+ }
699
+ };
700
+
701
+ // src/modules/orders.ts
702
+ var Orders = class {
703
+ constructor(http2, builder, address) {
704
+ this.http = http2;
705
+ this.builder = builder;
706
+ this.address = address;
707
+ }
708
+ requireSigner() {
709
+ if (!this.builder) {
710
+ throw new ContextConfigError(
711
+ "A signer is required for write operations. Pass a signer to ContextClient."
712
+ );
713
+ }
714
+ return this.builder;
715
+ }
716
+ requireAddress() {
717
+ if (!this.address) {
718
+ throw new ContextConfigError(
719
+ "A signer is required for this operation. Pass a signer to ContextClient."
720
+ );
721
+ }
722
+ return this.address;
723
+ }
724
+ // ─── Read ───
725
+ async list(params) {
726
+ return this.http.get(ENDPOINTS.orders.list, {
727
+ trader: params?.trader,
728
+ marketId: params?.marketId,
729
+ status: params?.status,
730
+ cursor: params?.cursor,
731
+ limit: params?.limit
732
+ });
733
+ }
734
+ async listAll(params) {
735
+ const allOrders = [];
736
+ let cursor;
737
+ do {
738
+ const res = await this.http.get(ENDPOINTS.orders.list, {
739
+ trader: params?.trader,
740
+ marketId: params?.marketId,
741
+ status: params?.status,
742
+ cursor
743
+ });
744
+ const orders = res.orders ?? [];
745
+ allOrders.push(...orders);
746
+ cursor = res.cursor ?? void 0;
747
+ if (orders.length === 0) break;
748
+ } while (cursor);
749
+ return allOrders;
750
+ }
751
+ async mine(marketId) {
752
+ return this.list({
753
+ trader: this.requireAddress(),
754
+ marketId
755
+ });
756
+ }
757
+ async allMine(marketId) {
758
+ return this.listAll({
759
+ trader: this.requireAddress(),
760
+ marketId
761
+ });
762
+ }
763
+ async get(id) {
764
+ const res = await this.http.get(
765
+ ENDPOINTS.orders.get(id)
766
+ );
767
+ return res.order;
768
+ }
769
+ async recent(params) {
770
+ return this.http.get(ENDPOINTS.orders.recent, {
771
+ trader: params?.trader,
772
+ marketId: params?.marketId,
773
+ status: params?.status,
774
+ limit: params?.limit,
775
+ windowSeconds: params?.windowSeconds
776
+ });
777
+ }
778
+ async simulate(params) {
779
+ return this.http.post(
780
+ ENDPOINTS.orders.simulate,
781
+ params
782
+ );
783
+ }
784
+ // ─── Write ───
785
+ async create(req) {
786
+ const builder = this.requireSigner();
787
+ const signed = await builder.buildAndSign(req);
788
+ return this.http.post(ENDPOINTS.orders.create, signed);
789
+ }
790
+ async createMarket(req) {
791
+ const builder = this.requireSigner();
792
+ const signed = await builder.buildAndSignMarket(req);
793
+ return this.http.post(ENDPOINTS.orders.create, signed);
794
+ }
795
+ async cancel(nonce) {
796
+ const builder = this.requireSigner();
797
+ const signature = await builder.signCancel(nonce);
798
+ return this.http.post(ENDPOINTS.orders.cancel, {
799
+ trader: builder.address,
800
+ nonce,
801
+ signature
802
+ });
803
+ }
804
+ async cancelReplace(cancelNonce, newOrder) {
805
+ const builder = this.requireSigner();
806
+ const cancelSig = await builder.signCancel(cancelNonce);
807
+ const signed = await builder.buildAndSign(newOrder);
808
+ return this.http.post(
809
+ ENDPOINTS.orders.cancelReplace,
810
+ {
811
+ cancel: {
812
+ trader: builder.address,
813
+ nonce: cancelNonce,
814
+ signature: cancelSig
815
+ },
816
+ create: signed
817
+ }
818
+ );
819
+ }
820
+ async bulkCreate(orders) {
821
+ const builder = this.requireSigner();
822
+ const signed = await Promise.all(
823
+ orders.map((req) => builder.buildAndSign(req))
824
+ );
825
+ const res = await this.http.post(
826
+ ENDPOINTS.orders.bulkCreate,
827
+ { orders: signed }
828
+ );
829
+ return res.results;
830
+ }
831
+ async bulkCancel(nonces) {
832
+ const builder = this.requireSigner();
833
+ const cancels = await Promise.all(
834
+ nonces.map(async (nonce) => {
835
+ const signature = await builder.signCancel(nonce);
836
+ return { trader: builder.address, nonce, signature };
837
+ })
838
+ );
839
+ const res = await this.http.post(
840
+ ENDPOINTS.orders.bulkCancel,
841
+ { cancels }
842
+ );
843
+ return res.results;
844
+ }
845
+ async bulk(creates, cancelNonces) {
846
+ const builder = this.requireSigner();
847
+ const createOps = await Promise.all(
848
+ creates.map(async (req) => ({
849
+ type: "create",
850
+ order: await builder.buildAndSign(req)
851
+ }))
852
+ );
853
+ const cancelOps = await Promise.all(
854
+ cancelNonces.map(async (nonce) => ({
855
+ type: "cancel",
856
+ cancel: {
857
+ trader: builder.address,
858
+ nonce,
859
+ signature: await builder.signCancel(nonce)
860
+ }
861
+ }))
862
+ );
863
+ return this.http.post(ENDPOINTS.orders.bulk, {
864
+ operations: [...createOps, ...cancelOps]
865
+ });
866
+ }
867
+ };
868
+
869
+ // src/modules/portfolio.ts
870
+ var PortfolioModule = class {
871
+ constructor(http2, defaultAddress) {
872
+ this.http = http2;
873
+ this.defaultAddress = defaultAddress;
874
+ }
875
+ resolveAddress(address) {
876
+ const resolved = address ?? this.defaultAddress;
877
+ if (!resolved) {
878
+ throw new Error(
879
+ "Address required. Either pass an address or configure a signer."
880
+ );
881
+ }
882
+ return resolved;
883
+ }
884
+ async get(address, params) {
885
+ return this.http.get(
886
+ ENDPOINTS.portfolio.get(this.resolveAddress(address)),
887
+ {
888
+ kind: params?.kind,
889
+ marketId: params?.marketId,
890
+ cursor: params?.cursor,
891
+ pageSize: params?.pageSize
892
+ }
893
+ );
894
+ }
895
+ async positions(address, params) {
896
+ return this.http.get(
897
+ ENDPOINTS.portfolio.positions(this.resolveAddress(address)),
898
+ {
899
+ marketId: params?.marketId,
900
+ status: params?.status,
901
+ search: params?.search,
902
+ cursor: params?.cursor,
903
+ limit: params?.limit
904
+ }
905
+ );
906
+ }
907
+ async claimable(address) {
908
+ return this.http.get(
909
+ ENDPOINTS.portfolio.claimable(this.resolveAddress(address))
910
+ );
911
+ }
912
+ async stats(address) {
913
+ return this.http.get(
914
+ ENDPOINTS.portfolio.stats(this.resolveAddress(address))
915
+ );
916
+ }
917
+ async balance(address) {
918
+ return this.http.get(
919
+ ENDPOINTS.balance.get(this.resolveAddress(address))
920
+ );
921
+ }
922
+ async tokenBalance(address, tokenAddress) {
923
+ return this.http.get(ENDPOINTS.balance.tokenBalance, {
924
+ address,
925
+ tokenAddress
926
+ });
927
+ }
928
+ async settlementBalance(address, tokenAddress) {
929
+ return this.http.get(ENDPOINTS.balance.settlement, {
930
+ address,
931
+ tokenAddress
932
+ });
933
+ }
934
+ };
935
+
936
+ // src/modules/account.ts
937
+ import {
938
+ createPublicClient,
939
+ http as viemHttp,
940
+ maxUint256,
941
+ parseUnits
942
+ } from "viem";
943
+ import { baseSepolia as baseSepolia2 } from "viem/chains";
944
+ var AccountModule = class {
945
+ constructor(http2, walletClient, account, rpcUrl) {
946
+ this.http = http2;
947
+ this.walletClient = walletClient;
948
+ this.account = account;
949
+ this.publicClient = createPublicClient({
950
+ chain: baseSepolia2,
951
+ transport: viemHttp(rpcUrl)
952
+ });
953
+ }
954
+ publicClient;
955
+ get address() {
956
+ if (!this.account) {
957
+ throw new ContextConfigError(
958
+ "A signer is required for account operations."
959
+ );
960
+ }
961
+ return this.account.address;
962
+ }
963
+ requireWallet() {
964
+ if (!this.walletClient) {
965
+ throw new ContextConfigError(
966
+ "A signer is required for account operations."
967
+ );
968
+ }
969
+ return this.walletClient;
970
+ }
971
+ requireAccount() {
972
+ if (!this.account) {
973
+ throw new ContextConfigError(
974
+ "A signer is required for account operations."
975
+ );
976
+ }
977
+ return this.account;
978
+ }
979
+ async status() {
980
+ const addr = this.address;
981
+ const [ethBalance, usdcAllowance, isOperatorApproved] = await Promise.all([
982
+ this.publicClient.getBalance({ address: addr }),
983
+ this.publicClient.readContract({
984
+ address: USDC_ADDRESS,
985
+ abi: ERC20_ABI,
986
+ functionName: "allowance",
987
+ args: [addr, HOLDINGS_ADDRESS]
988
+ }),
989
+ this.publicClient.readContract({
990
+ address: HOLDINGS_ADDRESS,
991
+ abi: HOLDINGS_ABI,
992
+ functionName: "isOperatorFor",
993
+ args: [addr, SETTLEMENT_ADDRESS]
994
+ })
995
+ ]);
996
+ return {
997
+ address: addr,
998
+ ethBalance,
999
+ usdcAllowance,
1000
+ isOperatorApproved,
1001
+ needsApprovals: usdcAllowance === 0n || !isOperatorApproved,
1002
+ needsGaslessSetup: !isOperatorApproved
1003
+ };
1004
+ }
1005
+ async setup() {
1006
+ const wallet = this.requireWallet();
1007
+ const account = this.requireAccount();
1008
+ const walletStatus = await this.status();
1009
+ let usdcApprovalTx = null;
1010
+ let operatorApprovalTx = null;
1011
+ if (walletStatus.usdcAllowance === 0n) {
1012
+ usdcApprovalTx = await wallet.writeContract({
1013
+ account,
1014
+ chain: baseSepolia2,
1015
+ address: USDC_ADDRESS,
1016
+ abi: ERC20_ABI,
1017
+ functionName: "approve",
1018
+ args: [HOLDINGS_ADDRESS, maxUint256]
1019
+ });
1020
+ }
1021
+ if (!walletStatus.isOperatorApproved) {
1022
+ operatorApprovalTx = await wallet.writeContract({
1023
+ account,
1024
+ chain: baseSepolia2,
1025
+ address: HOLDINGS_ADDRESS,
1026
+ abi: HOLDINGS_ABI,
1027
+ functionName: "setOperator",
1028
+ args: [SETTLEMENT_ADDRESS, true]
1029
+ });
1030
+ }
1031
+ return { usdcApprovalTx, operatorApprovalTx };
1032
+ }
1033
+ async mintTestUsdc(amount = 1e3) {
1034
+ return this.http.post(ENDPOINTS.balance.mintTestUsdc, {
1035
+ address: this.address,
1036
+ amount: amount.toString()
1037
+ });
1038
+ }
1039
+ async deposit(amount) {
1040
+ const wallet = this.requireWallet();
1041
+ const account = this.requireAccount();
1042
+ const amountRaw = parseUnits(amount.toString(), 6);
1043
+ const hash = await wallet.writeContract({
1044
+ account,
1045
+ chain: baseSepolia2,
1046
+ address: HOLDINGS_ADDRESS,
1047
+ abi: HOLDINGS_ABI,
1048
+ functionName: "deposit",
1049
+ args: [USDC_ADDRESS, amountRaw]
1050
+ });
1051
+ await this.publicClient.waitForTransactionReceipt({ hash });
1052
+ return hash;
1053
+ }
1054
+ async withdraw(amount) {
1055
+ const wallet = this.requireWallet();
1056
+ const account = this.requireAccount();
1057
+ const amountRaw = parseUnits(amount.toString(), 6);
1058
+ const hash = await wallet.writeContract({
1059
+ account,
1060
+ chain: baseSepolia2,
1061
+ address: HOLDINGS_ADDRESS,
1062
+ abi: HOLDINGS_ABI,
1063
+ functionName: "withdraw",
1064
+ args: [USDC_ADDRESS, amountRaw]
1065
+ });
1066
+ await this.publicClient.waitForTransactionReceipt({ hash });
1067
+ return hash;
1068
+ }
1069
+ async mintCompleteSets(marketId, amount) {
1070
+ const wallet = this.requireWallet();
1071
+ const account = this.requireAccount();
1072
+ const amountRaw = parseUnits(amount.toString(), 6);
1073
+ const hash = await wallet.writeContract({
1074
+ account,
1075
+ chain: baseSepolia2,
1076
+ address: SETTLEMENT_ADDRESS,
1077
+ abi: SETTLEMENT_ABI,
1078
+ functionName: "mintCompleteSetsFromHoldings",
1079
+ args: [marketId, amountRaw]
1080
+ });
1081
+ await this.publicClient.waitForTransactionReceipt({ hash });
1082
+ return hash;
1083
+ }
1084
+ async burnCompleteSets(marketId, amount, creditInternal = true) {
1085
+ const wallet = this.requireWallet();
1086
+ const account = this.requireAccount();
1087
+ const amountRaw = parseUnits(amount.toString(), 6);
1088
+ const hash = await wallet.writeContract({
1089
+ account,
1090
+ chain: baseSepolia2,
1091
+ address: SETTLEMENT_ADDRESS,
1092
+ abi: SETTLEMENT_ABI,
1093
+ functionName: "burnCompleteSetsFromHoldings",
1094
+ args: [marketId, amountRaw, this.address, creditInternal]
1095
+ });
1096
+ await this.publicClient.waitForTransactionReceipt({ hash });
1097
+ return hash;
1098
+ }
1099
+ // ─── Gasless (high-level: sign + relay) ───
1100
+ async gaslessSetup() {
1101
+ const wallet = this.requireWallet();
1102
+ const account = this.requireAccount();
1103
+ const nonce = await this.publicClient.readContract({
1104
+ address: HOLDINGS_ADDRESS,
1105
+ abi: OPERATOR_NONCE_ABI,
1106
+ functionName: "operatorNonce",
1107
+ args: [this.address]
1108
+ });
1109
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + 3600);
1110
+ const signature = await wallet.signTypedData({
1111
+ account,
1112
+ domain: HOLDINGS_EIP712_DOMAIN,
1113
+ types: OPERATOR_APPROVAL_TYPES,
1114
+ primaryType: "OperatorApproval",
1115
+ message: {
1116
+ user: this.address,
1117
+ operator: SETTLEMENT_ADDRESS,
1118
+ approved: true,
1119
+ nonce,
1120
+ deadline
1121
+ }
1122
+ });
1123
+ return this.relayOperatorApproval({
1124
+ user: this.address,
1125
+ approved: true,
1126
+ nonce: nonce.toString(),
1127
+ deadline: deadline.toString(),
1128
+ signature
1129
+ });
1130
+ }
1131
+ async gaslessDeposit(amount) {
1132
+ const wallet = this.requireWallet();
1133
+ const account = this.requireAccount();
1134
+ const amountRaw = parseUnits(amount.toString(), 6);
1135
+ const nonce = BigInt(Date.now());
1136
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + 3600);
1137
+ const signature = await wallet.signTypedData({
1138
+ account,
1139
+ domain: PERMIT2_EIP712_DOMAIN,
1140
+ types: PERMIT_TRANSFER_FROM_TYPES,
1141
+ primaryType: "PermitTransferFrom",
1142
+ message: {
1143
+ permitted: {
1144
+ token: USDC_ADDRESS,
1145
+ amount: amountRaw
1146
+ },
1147
+ spender: HOLDINGS_ADDRESS,
1148
+ nonce,
1149
+ deadline
1150
+ }
1151
+ });
1152
+ return this.relayDeposit({
1153
+ user: this.address,
1154
+ amount: amountRaw.toString(),
1155
+ nonce: nonce.toString(),
1156
+ deadline: deadline.toString(),
1157
+ signature
1158
+ });
1159
+ }
1160
+ // ─── Gasless Relay (low-level) ───
1161
+ async relayOperatorApproval(req) {
1162
+ return this.http.post(
1163
+ ENDPOINTS.gasless.operator,
1164
+ req
1165
+ );
1166
+ }
1167
+ async relayDeposit(req) {
1168
+ return this.http.post(
1169
+ ENDPOINTS.gasless.depositWithPermit,
1170
+ req
1171
+ );
1172
+ }
1173
+ };
1174
+
1175
+ // src/client.ts
1176
+ var ContextClient = class {
1177
+ markets;
1178
+ questions;
1179
+ orders;
1180
+ portfolio;
1181
+ account;
1182
+ /** The trader's on-chain address, or null if no signer was provided. */
1183
+ address;
1184
+ constructor(options = {}) {
1185
+ const http2 = createHttpClient({
1186
+ apiKey: options.apiKey,
1187
+ baseUrl: options.baseUrl
1188
+ });
1189
+ let builder = null;
1190
+ let address = null;
1191
+ let walletClient = null;
1192
+ let account = null;
1193
+ if (options.signer) {
1194
+ const resolved = resolveSigner(options.signer);
1195
+ walletClient = resolved.walletClient;
1196
+ account = resolved.account;
1197
+ address = resolved.account.address;
1198
+ builder = new OrderBuilder(walletClient, account);
1199
+ }
1200
+ this.address = address;
1201
+ this.markets = new Markets(http2);
1202
+ this.questions = new Questions(http2);
1203
+ this.orders = new Orders(http2, builder, address);
1204
+ this.portfolio = new PortfolioModule(http2, address);
1205
+ this.account = new AccountModule(http2, walletClient, account, options.rpcUrl);
1206
+ }
1207
+ };
1208
+ export {
1209
+ API_BASE,
1210
+ CHAIN_ID,
1211
+ ContextApiError,
1212
+ ContextClient,
1213
+ ContextConfigError,
1214
+ ContextSigningError,
1215
+ ENDPOINTS,
1216
+ HOLDINGS_ADDRESS,
1217
+ HOLDINGS_EIP712_DOMAIN,
1218
+ PERMIT2_ADDRESS,
1219
+ PERMIT2_EIP712_DOMAIN,
1220
+ SETTLEMENT_ADDRESS,
1221
+ USDC_ADDRESS,
1222
+ calculateMaxFee,
1223
+ decodePriceCents,
1224
+ decodeSize,
1225
+ encodePriceCents,
1226
+ encodeSize
1227
+ };
1228
+ //# sourceMappingURL=index.js.map