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