@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.js ADDED
@@ -0,0 +1,1111 @@
1
+ // src/config.ts
2
+ var API_BASE = "https://api-testnet.context.markets/v2";
3
+ var SETTLEMENT_ADDRESS = "0xABfB9e3Dc252D59e4e4A3c3537D96F3F207C9b2c";
4
+ var HOLDINGS_ADDRESS = "0x769341425095155C0A0620eBC308d4C05980B84a";
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: [{ name: "", type: "bool" }]
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 (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 (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/endpoints.ts
462
+ var ENDPOINTS = {
463
+ markets: {
464
+ list: "/markets",
465
+ get: (id) => `/markets/${id}`,
466
+ quotes: (id) => `/markets/${id}/quotes`,
467
+ orderbook: (id) => `/markets/${id}/orderbook`,
468
+ simulate: (id) => `/markets/${id}/simulate`,
469
+ prices: (id) => `/markets/${id}/prices`,
470
+ oracle: (id) => `/markets/${id}/oracle`,
471
+ oracleQuotes: (id) => `/markets/${id}/oracle/quotes`,
472
+ activity: (id) => `/markets/${id}/activity`
473
+ },
474
+ orders: {
475
+ create: "/orders",
476
+ list: "/orders",
477
+ recent: "/orders/recent",
478
+ get: (id) => `/orders/${id}`,
479
+ cancel: "/orders/cancels",
480
+ cancelReplace: "/orders/cancel-replace",
481
+ simulate: "/orders/simulate",
482
+ bulk: "/orders/bulk",
483
+ bulkCreate: "/orders/bulk/create",
484
+ bulkCancel: "/orders/bulk/cancel"
485
+ },
486
+ portfolio: {
487
+ get: (address) => `/portfolio/${address}`,
488
+ claimable: (address) => `/portfolio/${address}/claimable`,
489
+ stats: (address) => `/portfolio/${address}/stats`
490
+ },
491
+ balance: {
492
+ get: (address) => `/balance/${address}`,
493
+ tokenBalance: "/balance",
494
+ settlement: "/balance/settlement",
495
+ mintTestUsdc: "/balance/mint-test-usdc"
496
+ },
497
+ activity: {
498
+ global: "/activity"
499
+ },
500
+ gasless: {
501
+ operator: "/gasless/operator",
502
+ depositWithPermit: "/gasless/deposit-with-permit"
503
+ }
504
+ };
505
+
506
+ // src/modules/markets.ts
507
+ var Markets = class {
508
+ constructor(http2) {
509
+ this.http = http2;
510
+ }
511
+ async list(params) {
512
+ return this.http.get(ENDPOINTS.markets.list, {
513
+ search: params?.query,
514
+ status: params?.status,
515
+ sortBy: params?.sortBy,
516
+ sort: params?.sort,
517
+ limit: params?.limit,
518
+ cursor: params?.cursor,
519
+ visibility: params?.visibility,
520
+ resolutionStatus: params?.resolutionStatus,
521
+ creator: params?.creator,
522
+ category: params?.category,
523
+ createdAfter: params?.createdAfter
524
+ });
525
+ }
526
+ async get(id) {
527
+ const res = await this.http.get(
528
+ ENDPOINTS.markets.get(id)
529
+ );
530
+ return res.market;
531
+ }
532
+ async quotes(marketId) {
533
+ return this.http.get(ENDPOINTS.markets.quotes(marketId));
534
+ }
535
+ async orderbook(marketId, params) {
536
+ return this.http.get(ENDPOINTS.markets.orderbook(marketId), {
537
+ depth: params?.depth,
538
+ outcomeIndex: params?.outcomeIndex
539
+ });
540
+ }
541
+ async fullOrderbook(marketId, params) {
542
+ const [no, yes] = await Promise.all([
543
+ this.orderbook(marketId, { ...params, outcomeIndex: 0 }),
544
+ this.orderbook(marketId, { ...params, outcomeIndex: 1 })
545
+ ]);
546
+ return {
547
+ marketId: yes.marketId,
548
+ yes: { bids: yes.bids, asks: yes.asks },
549
+ no: { bids: no.bids, asks: no.asks },
550
+ timestamp: yes.timestamp
551
+ };
552
+ }
553
+ async simulate(marketId, params) {
554
+ return this.http.post(
555
+ ENDPOINTS.markets.simulate(marketId),
556
+ {
557
+ side: params.side,
558
+ amount: params.amount,
559
+ amountType: params.amountType ?? "usd",
560
+ ...params.trader ? { trader: params.trader } : {}
561
+ }
562
+ );
563
+ }
564
+ async priceHistory(marketId, params) {
565
+ return this.http.get(ENDPOINTS.markets.prices(marketId), {
566
+ timeframe: params?.timeframe ?? params?.interval
567
+ });
568
+ }
569
+ async oracle(marketId) {
570
+ return this.http.get(ENDPOINTS.markets.oracle(marketId));
571
+ }
572
+ async oracleQuotes(marketId) {
573
+ return this.http.get(
574
+ ENDPOINTS.markets.oracleQuotes(marketId)
575
+ );
576
+ }
577
+ async requestOracleQuote(marketId) {
578
+ return this.http.post(
579
+ ENDPOINTS.markets.oracleQuotes(marketId),
580
+ {}
581
+ );
582
+ }
583
+ async activity(marketId, params) {
584
+ return this.http.get(
585
+ ENDPOINTS.markets.activity(marketId),
586
+ {
587
+ cursor: params?.cursor,
588
+ limit: params?.limit,
589
+ types: params?.types,
590
+ startTime: params?.startTime,
591
+ endTime: params?.endTime
592
+ }
593
+ );
594
+ }
595
+ async globalActivity(params) {
596
+ return this.http.get(ENDPOINTS.activity.global, {
597
+ cursor: params?.cursor,
598
+ limit: params?.limit,
599
+ types: params?.types,
600
+ startTime: params?.startTime,
601
+ endTime: params?.endTime
602
+ });
603
+ }
604
+ };
605
+
606
+ // src/modules/orders.ts
607
+ var Orders = class {
608
+ constructor(http2, builder, address) {
609
+ this.http = http2;
610
+ this.builder = builder;
611
+ this.address = address;
612
+ }
613
+ requireSigner() {
614
+ if (!this.builder) {
615
+ throw new ContextConfigError(
616
+ "A signer is required for write operations. Pass a signer to ContextClient."
617
+ );
618
+ }
619
+ return this.builder;
620
+ }
621
+ requireAddress() {
622
+ if (!this.address) {
623
+ throw new ContextConfigError(
624
+ "A signer is required for this operation. Pass a signer to ContextClient."
625
+ );
626
+ }
627
+ return this.address;
628
+ }
629
+ // ─── Read ───
630
+ async list(params) {
631
+ return this.http.get(ENDPOINTS.orders.list, {
632
+ trader: params?.trader,
633
+ marketId: params?.marketId,
634
+ status: params?.status,
635
+ cursor: params?.cursor,
636
+ limit: params?.limit
637
+ });
638
+ }
639
+ async listAll(params) {
640
+ const allOrders = [];
641
+ let cursor;
642
+ do {
643
+ const res = await this.http.get(ENDPOINTS.orders.list, {
644
+ trader: params?.trader,
645
+ marketId: params?.marketId,
646
+ status: params?.status,
647
+ cursor
648
+ });
649
+ const orders = res.orders ?? [];
650
+ allOrders.push(...orders);
651
+ cursor = res.cursor ?? void 0;
652
+ if (orders.length === 0) break;
653
+ } while (cursor);
654
+ return allOrders;
655
+ }
656
+ async mine(marketId) {
657
+ return this.list({
658
+ trader: this.requireAddress(),
659
+ marketId
660
+ });
661
+ }
662
+ async allMine(marketId) {
663
+ return this.listAll({
664
+ trader: this.requireAddress(),
665
+ marketId
666
+ });
667
+ }
668
+ async get(id) {
669
+ const res = await this.http.get(
670
+ ENDPOINTS.orders.get(id)
671
+ );
672
+ return res.order;
673
+ }
674
+ async recent(params) {
675
+ return this.http.get(ENDPOINTS.orders.recent, {
676
+ trader: params?.trader,
677
+ marketId: params?.marketId,
678
+ status: params?.status,
679
+ limit: params?.limit,
680
+ windowSeconds: params?.windowSeconds
681
+ });
682
+ }
683
+ async simulate(params) {
684
+ return this.http.post(
685
+ ENDPOINTS.orders.simulate,
686
+ params
687
+ );
688
+ }
689
+ // ─── Write ───
690
+ async create(req) {
691
+ const builder = this.requireSigner();
692
+ const signed = await builder.buildAndSign(req);
693
+ return this.http.post(ENDPOINTS.orders.create, signed);
694
+ }
695
+ async createMarket(req) {
696
+ const builder = this.requireSigner();
697
+ const signed = await builder.buildAndSignMarket(req);
698
+ return this.http.post(ENDPOINTS.orders.create, signed);
699
+ }
700
+ async cancel(nonce) {
701
+ const builder = this.requireSigner();
702
+ const signature = await builder.signCancel(nonce);
703
+ return this.http.post(ENDPOINTS.orders.cancel, {
704
+ trader: builder.address,
705
+ nonce,
706
+ signature
707
+ });
708
+ }
709
+ async cancelReplace(cancelNonce, newOrder) {
710
+ const builder = this.requireSigner();
711
+ const cancelSig = await builder.signCancel(cancelNonce);
712
+ const signed = await builder.buildAndSign(newOrder);
713
+ return this.http.post(
714
+ ENDPOINTS.orders.cancelReplace,
715
+ {
716
+ cancel: {
717
+ trader: builder.address,
718
+ nonce: cancelNonce,
719
+ signature: cancelSig
720
+ },
721
+ create: signed
722
+ }
723
+ );
724
+ }
725
+ async bulkCreate(orders) {
726
+ const builder = this.requireSigner();
727
+ const signed = await Promise.all(
728
+ orders.map((req) => builder.buildAndSign(req))
729
+ );
730
+ const res = await this.http.post(
731
+ ENDPOINTS.orders.bulkCreate,
732
+ { orders: signed }
733
+ );
734
+ return res.results;
735
+ }
736
+ async bulkCancel(nonces) {
737
+ const builder = this.requireSigner();
738
+ const cancels = await Promise.all(
739
+ nonces.map(async (nonce) => {
740
+ const signature = await builder.signCancel(nonce);
741
+ return { trader: builder.address, nonce, signature };
742
+ })
743
+ );
744
+ const res = await this.http.post(
745
+ ENDPOINTS.orders.bulkCancel,
746
+ { cancels }
747
+ );
748
+ return res.results;
749
+ }
750
+ async bulk(creates, cancelNonces) {
751
+ const builder = this.requireSigner();
752
+ const createOps = await Promise.all(
753
+ creates.map(async (req) => ({
754
+ type: "create",
755
+ order: await builder.buildAndSign(req)
756
+ }))
757
+ );
758
+ const cancelOps = await Promise.all(
759
+ cancelNonces.map(async (nonce) => ({
760
+ type: "cancel",
761
+ cancel: {
762
+ trader: builder.address,
763
+ nonce,
764
+ signature: await builder.signCancel(nonce)
765
+ }
766
+ }))
767
+ );
768
+ return this.http.post(ENDPOINTS.orders.bulk, {
769
+ operations: [...createOps, ...cancelOps]
770
+ });
771
+ }
772
+ };
773
+
774
+ // src/modules/portfolio.ts
775
+ var PortfolioModule = class {
776
+ constructor(http2, defaultAddress) {
777
+ this.http = http2;
778
+ this.defaultAddress = defaultAddress;
779
+ }
780
+ resolveAddress(address) {
781
+ const resolved = address ?? this.defaultAddress;
782
+ if (!resolved) {
783
+ throw new Error(
784
+ "Address required. Either pass an address or configure a signer."
785
+ );
786
+ }
787
+ return resolved;
788
+ }
789
+ async get(address, params) {
790
+ return this.http.get(
791
+ ENDPOINTS.portfolio.get(this.resolveAddress(address)),
792
+ {
793
+ kind: params?.kind,
794
+ marketId: params?.marketId,
795
+ cursor: params?.cursor,
796
+ pageSize: params?.pageSize
797
+ }
798
+ );
799
+ }
800
+ async claimable(address) {
801
+ return this.http.get(
802
+ ENDPOINTS.portfolio.claimable(this.resolveAddress(address))
803
+ );
804
+ }
805
+ async stats(address) {
806
+ return this.http.get(
807
+ ENDPOINTS.portfolio.stats(this.resolveAddress(address))
808
+ );
809
+ }
810
+ async balance(address) {
811
+ return this.http.get(
812
+ ENDPOINTS.balance.get(this.resolveAddress(address))
813
+ );
814
+ }
815
+ async tokenBalance(address, tokenAddress) {
816
+ return this.http.get(ENDPOINTS.balance.tokenBalance, {
817
+ address,
818
+ tokenAddress
819
+ });
820
+ }
821
+ };
822
+
823
+ // src/modules/account.ts
824
+ import {
825
+ createPublicClient,
826
+ http as viemHttp,
827
+ maxUint256,
828
+ parseUnits
829
+ } from "viem";
830
+ import { baseSepolia as baseSepolia2 } from "viem/chains";
831
+ var AccountModule = class {
832
+ constructor(http2, walletClient, account, rpcUrl) {
833
+ this.http = http2;
834
+ this.walletClient = walletClient;
835
+ this.account = account;
836
+ this.publicClient = createPublicClient({
837
+ chain: baseSepolia2,
838
+ transport: viemHttp(rpcUrl)
839
+ });
840
+ }
841
+ publicClient;
842
+ get address() {
843
+ if (!this.account) {
844
+ throw new ContextConfigError(
845
+ "A signer is required for account operations."
846
+ );
847
+ }
848
+ return this.account.address;
849
+ }
850
+ requireWallet() {
851
+ if (!this.walletClient) {
852
+ throw new ContextConfigError(
853
+ "A signer is required for account operations."
854
+ );
855
+ }
856
+ return this.walletClient;
857
+ }
858
+ requireAccount() {
859
+ if (!this.account) {
860
+ throw new ContextConfigError(
861
+ "A signer is required for account operations."
862
+ );
863
+ }
864
+ return this.account;
865
+ }
866
+ async status() {
867
+ const addr = this.address;
868
+ const [ethBalance, usdcAllowance, isOperatorApproved] = await Promise.all([
869
+ this.publicClient.getBalance({ address: addr }),
870
+ this.publicClient.readContract({
871
+ address: USDC_ADDRESS,
872
+ abi: ERC20_ABI,
873
+ functionName: "allowance",
874
+ args: [addr, HOLDINGS_ADDRESS]
875
+ }),
876
+ this.publicClient.readContract({
877
+ address: HOLDINGS_ADDRESS,
878
+ abi: HOLDINGS_ABI,
879
+ functionName: "isOperatorFor",
880
+ args: [addr, SETTLEMENT_ADDRESS]
881
+ })
882
+ ]);
883
+ return {
884
+ address: addr,
885
+ ethBalance,
886
+ usdcAllowance,
887
+ isOperatorApproved,
888
+ needsApprovals: usdcAllowance === 0n || !isOperatorApproved
889
+ };
890
+ }
891
+ async setup() {
892
+ const wallet = this.requireWallet();
893
+ const account = this.requireAccount();
894
+ const walletStatus = await this.status();
895
+ let usdcApprovalTx = null;
896
+ let operatorApprovalTx = null;
897
+ if (walletStatus.usdcAllowance === 0n) {
898
+ usdcApprovalTx = await wallet.writeContract({
899
+ account,
900
+ chain: baseSepolia2,
901
+ address: USDC_ADDRESS,
902
+ abi: ERC20_ABI,
903
+ functionName: "approve",
904
+ args: [HOLDINGS_ADDRESS, maxUint256]
905
+ });
906
+ }
907
+ if (!walletStatus.isOperatorApproved) {
908
+ operatorApprovalTx = await wallet.writeContract({
909
+ account,
910
+ chain: baseSepolia2,
911
+ address: HOLDINGS_ADDRESS,
912
+ abi: HOLDINGS_ABI,
913
+ functionName: "setOperator",
914
+ args: [SETTLEMENT_ADDRESS, true]
915
+ });
916
+ }
917
+ return { usdcApprovalTx, operatorApprovalTx };
918
+ }
919
+ async mintTestUsdc(amount = 1e3) {
920
+ return this.http.post(ENDPOINTS.balance.mintTestUsdc, {
921
+ address: this.address,
922
+ amount: amount.toString()
923
+ });
924
+ }
925
+ async deposit(amount) {
926
+ const wallet = this.requireWallet();
927
+ const account = this.requireAccount();
928
+ const amountRaw = parseUnits(amount.toString(), 6);
929
+ const hash = await wallet.writeContract({
930
+ account,
931
+ chain: baseSepolia2,
932
+ address: HOLDINGS_ADDRESS,
933
+ abi: HOLDINGS_ABI,
934
+ functionName: "deposit",
935
+ args: [USDC_ADDRESS, amountRaw]
936
+ });
937
+ await this.publicClient.waitForTransactionReceipt({ hash });
938
+ return hash;
939
+ }
940
+ async withdraw(amount) {
941
+ const wallet = this.requireWallet();
942
+ const account = this.requireAccount();
943
+ const amountRaw = parseUnits(amount.toString(), 6);
944
+ const hash = await wallet.writeContract({
945
+ account,
946
+ chain: baseSepolia2,
947
+ address: HOLDINGS_ADDRESS,
948
+ abi: HOLDINGS_ABI,
949
+ functionName: "withdraw",
950
+ args: [USDC_ADDRESS, amountRaw]
951
+ });
952
+ await this.publicClient.waitForTransactionReceipt({ hash });
953
+ return hash;
954
+ }
955
+ async mintCompleteSets(marketId, amount) {
956
+ const wallet = this.requireWallet();
957
+ const account = this.requireAccount();
958
+ const amountRaw = parseUnits(amount.toString(), 6);
959
+ const hash = await wallet.writeContract({
960
+ account,
961
+ chain: baseSepolia2,
962
+ address: SETTLEMENT_ADDRESS,
963
+ abi: SETTLEMENT_ABI,
964
+ functionName: "mintCompleteSetsFromHoldings",
965
+ args: [marketId, amountRaw]
966
+ });
967
+ await this.publicClient.waitForTransactionReceipt({ hash });
968
+ return hash;
969
+ }
970
+ async burnCompleteSets(marketId, amount, creditInternal = true) {
971
+ const wallet = this.requireWallet();
972
+ const account = this.requireAccount();
973
+ const amountRaw = parseUnits(amount.toString(), 6);
974
+ const hash = await wallet.writeContract({
975
+ account,
976
+ chain: baseSepolia2,
977
+ address: SETTLEMENT_ADDRESS,
978
+ abi: SETTLEMENT_ABI,
979
+ functionName: "burnCompleteSetsFromHoldings",
980
+ args: [marketId, amountRaw, this.address, creditInternal]
981
+ });
982
+ await this.publicClient.waitForTransactionReceipt({ hash });
983
+ return hash;
984
+ }
985
+ // ─── Gasless (high-level: sign + relay) ───
986
+ async gaslessSetup() {
987
+ const wallet = this.requireWallet();
988
+ const account = this.requireAccount();
989
+ const nonce = await this.publicClient.readContract({
990
+ address: HOLDINGS_ADDRESS,
991
+ abi: OPERATOR_NONCE_ABI,
992
+ functionName: "operatorNonce",
993
+ args: [this.address]
994
+ });
995
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + 3600);
996
+ const signature = await wallet.signTypedData({
997
+ account,
998
+ domain: HOLDINGS_EIP712_DOMAIN,
999
+ types: OPERATOR_APPROVAL_TYPES,
1000
+ primaryType: "OperatorApproval",
1001
+ message: {
1002
+ user: this.address,
1003
+ operator: SETTLEMENT_ADDRESS,
1004
+ approved: true,
1005
+ nonce,
1006
+ deadline
1007
+ }
1008
+ });
1009
+ return this.relayOperatorApproval({
1010
+ user: this.address,
1011
+ approved: true,
1012
+ nonce: nonce.toString(),
1013
+ deadline: deadline.toString(),
1014
+ signature
1015
+ });
1016
+ }
1017
+ async gaslessDeposit(amount) {
1018
+ const wallet = this.requireWallet();
1019
+ const account = this.requireAccount();
1020
+ const amountRaw = parseUnits(amount.toString(), 6);
1021
+ const nonce = BigInt(Date.now());
1022
+ const deadline = BigInt(Math.floor(Date.now() / 1e3) + 3600);
1023
+ const signature = await wallet.signTypedData({
1024
+ account,
1025
+ domain: PERMIT2_EIP712_DOMAIN,
1026
+ types: PERMIT_TRANSFER_FROM_TYPES,
1027
+ primaryType: "PermitTransferFrom",
1028
+ message: {
1029
+ permitted: {
1030
+ token: USDC_ADDRESS,
1031
+ amount: amountRaw
1032
+ },
1033
+ spender: HOLDINGS_ADDRESS,
1034
+ nonce,
1035
+ deadline
1036
+ }
1037
+ });
1038
+ return this.relayDeposit({
1039
+ user: this.address,
1040
+ amount: amountRaw.toString(),
1041
+ nonce: nonce.toString(),
1042
+ deadline: deadline.toString(),
1043
+ signature
1044
+ });
1045
+ }
1046
+ // ─── Gasless Relay (low-level) ───
1047
+ async relayOperatorApproval(req) {
1048
+ return this.http.post(
1049
+ ENDPOINTS.gasless.operator,
1050
+ req
1051
+ );
1052
+ }
1053
+ async relayDeposit(req) {
1054
+ return this.http.post(
1055
+ ENDPOINTS.gasless.depositWithPermit,
1056
+ req
1057
+ );
1058
+ }
1059
+ };
1060
+
1061
+ // src/client.ts
1062
+ var ContextClient = class {
1063
+ markets;
1064
+ orders;
1065
+ portfolio;
1066
+ account;
1067
+ /** The trader's on-chain address, or null if no signer was provided. */
1068
+ address;
1069
+ constructor(options = {}) {
1070
+ const http2 = createHttpClient({
1071
+ apiKey: options.apiKey,
1072
+ baseUrl: options.baseUrl
1073
+ });
1074
+ let builder = null;
1075
+ let address = null;
1076
+ let walletClient = null;
1077
+ let account = null;
1078
+ if (options.signer) {
1079
+ const resolved = resolveSigner(options.signer);
1080
+ walletClient = resolved.walletClient;
1081
+ account = resolved.account;
1082
+ address = resolved.account.address;
1083
+ builder = new OrderBuilder(walletClient, account);
1084
+ }
1085
+ this.address = address;
1086
+ this.markets = new Markets(http2);
1087
+ this.orders = new Orders(http2, builder, address);
1088
+ this.portfolio = new PortfolioModule(http2, address);
1089
+ this.account = new AccountModule(http2, walletClient, account, options.rpcUrl);
1090
+ }
1091
+ };
1092
+ export {
1093
+ API_BASE,
1094
+ CHAIN_ID,
1095
+ ContextApiError,
1096
+ ContextClient,
1097
+ ContextConfigError,
1098
+ ContextSigningError,
1099
+ HOLDINGS_ADDRESS,
1100
+ HOLDINGS_EIP712_DOMAIN,
1101
+ PERMIT2_ADDRESS,
1102
+ PERMIT2_EIP712_DOMAIN,
1103
+ SETTLEMENT_ADDRESS,
1104
+ USDC_ADDRESS,
1105
+ calculateMaxFee,
1106
+ decodePriceCents,
1107
+ decodeSize,
1108
+ encodePriceCents,
1109
+ encodeSize
1110
+ };
1111
+ //# sourceMappingURL=index.js.map