pepay-streams-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +405 -0
  2. package/dist/api/index.d.mts +321 -0
  3. package/dist/api/index.d.ts +321 -0
  4. package/dist/api/index.js +312 -0
  5. package/dist/api/index.js.map +1 -0
  6. package/dist/api/index.mjs +306 -0
  7. package/dist/api/index.mjs.map +1 -0
  8. package/dist/automation/index.d.mts +140 -0
  9. package/dist/automation/index.d.ts +140 -0
  10. package/dist/automation/index.js +331 -0
  11. package/dist/automation/index.js.map +1 -0
  12. package/dist/automation/index.mjs +326 -0
  13. package/dist/automation/index.mjs.map +1 -0
  14. package/dist/campaigns/index.d.mts +286 -0
  15. package/dist/campaigns/index.d.ts +286 -0
  16. package/dist/campaigns/index.js +652 -0
  17. package/dist/campaigns/index.js.map +1 -0
  18. package/dist/campaigns/index.mjs +645 -0
  19. package/dist/campaigns/index.mjs.map +1 -0
  20. package/dist/claims/index.d.mts +190 -0
  21. package/dist/claims/index.d.ts +190 -0
  22. package/dist/claims/index.js +414 -0
  23. package/dist/claims/index.js.map +1 -0
  24. package/dist/claims/index.mjs +409 -0
  25. package/dist/claims/index.mjs.map +1 -0
  26. package/dist/index-BTG0TRJt.d.mts +555 -0
  27. package/dist/index-BTG0TRJt.d.ts +555 -0
  28. package/dist/index.d.mts +170 -0
  29. package/dist/index.d.ts +170 -0
  30. package/dist/index.js +2926 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/index.mjs +2888 -0
  33. package/dist/index.mjs.map +1 -0
  34. package/dist/marketplace/index.d.mts +225 -0
  35. package/dist/marketplace/index.d.ts +225 -0
  36. package/dist/marketplace/index.js +529 -0
  37. package/dist/marketplace/index.js.map +1 -0
  38. package/dist/marketplace/index.mjs +524 -0
  39. package/dist/marketplace/index.mjs.map +1 -0
  40. package/dist/react/index.d.mts +185 -0
  41. package/dist/react/index.d.ts +185 -0
  42. package/dist/react/index.js +340 -0
  43. package/dist/react/index.js.map +1 -0
  44. package/dist/react/index.mjs +333 -0
  45. package/dist/react/index.mjs.map +1 -0
  46. package/dist/staking/index.d.mts +158 -0
  47. package/dist/staking/index.d.ts +158 -0
  48. package/dist/staking/index.js +359 -0
  49. package/dist/staking/index.js.map +1 -0
  50. package/dist/staking/index.mjs +354 -0
  51. package/dist/staking/index.mjs.map +1 -0
  52. package/package.json +106 -0
  53. package/src/api/index.ts +577 -0
  54. package/src/automation/index.ts +436 -0
  55. package/src/campaigns/index.ts +835 -0
  56. package/src/claims/index.ts +530 -0
  57. package/src/client.ts +518 -0
  58. package/src/index.ts +101 -0
  59. package/src/marketplace/index.ts +730 -0
  60. package/src/react/index.ts +498 -0
  61. package/src/staking/index.ts +449 -0
  62. package/src/types/index.ts +631 -0
@@ -0,0 +1,730 @@
1
+ /**
2
+ * Marketplace Module
3
+ *
4
+ * Provides methods for P2P vesting marketplace:
5
+ * - Create orders (INSTANT, VESTED, TRADABLE)
6
+ * - Fill orders
7
+ * - Cancel orders
8
+ * - Order queries
9
+ */
10
+ import {
11
+ type PublicClient,
12
+ type WalletClient,
13
+ type Address,
14
+ type Hash,
15
+ type Hex,
16
+ zeroAddress,
17
+ } from 'viem';
18
+ import { DIAMOND_ABI } from '@pepay-streams/abi/diamond';
19
+ import type {
20
+ CreateInstantOrderParams,
21
+ CreateVestedOrderParams,
22
+ CreateTradableOrderParams,
23
+ OrderInfo,
24
+ OrderQuote,
25
+ TransactionResult,
26
+ OrderType,
27
+ } from '../types';
28
+
29
+ /**
30
+ * Visibility enum matching contract
31
+ */
32
+ enum Visibility {
33
+ PUBLIC = 0,
34
+ PRIVATE = 1,
35
+ }
36
+
37
+ /**
38
+ * StartMode enum matching contract
39
+ */
40
+ enum StartMode {
41
+ UPON_FILL = 0,
42
+ FIXED_TIMESTAMP = 1,
43
+ }
44
+
45
+ /**
46
+ * Marketplace module for P2P trading of token positions
47
+ */
48
+ export class MarketplaceModule {
49
+ constructor(
50
+ private readonly publicClient: PublicClient,
51
+ private readonly walletClient: WalletClient | undefined,
52
+ private readonly diamondAddress: Address
53
+ ) {}
54
+
55
+ // ============================================================================
56
+ // Order Creation
57
+ // ============================================================================
58
+
59
+ /**
60
+ * Create an INSTANT order
61
+ *
62
+ * Seller deposits tokens upfront, buyer pays and receives immediately.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // Approve first
67
+ * await sdk.approve(sellToken, amount);
68
+ *
69
+ * const result = await sdk.marketplace.createInstantOrder({
70
+ * sellToken: '0x...',
71
+ * sellAmount: parseEther('1000'),
72
+ * payToken: zeroAddress, // Native ETH
73
+ * pricePerToken: parseEther('0.001'), // 0.001 ETH per token
74
+ * expiryTTL: 7 * 24 * 60 * 60, // 7 days
75
+ * });
76
+ * ```
77
+ */
78
+ async createInstantOrder(
79
+ params: CreateInstantOrderParams
80
+ ): Promise<TransactionResult> {
81
+ const wallet = this.requireWallet();
82
+
83
+ const visibility =
84
+ params.privateBuyers && params.privateBuyers.length > 0
85
+ ? Visibility.PRIVATE
86
+ : Visibility.PUBLIC;
87
+
88
+ const expiry = BigInt(
89
+ Math.floor(Date.now() / 1000) + (params.expiryTTL ?? 7 * 24 * 60 * 60)
90
+ );
91
+
92
+ const args = {
93
+ payToken: params.payToken,
94
+ price: params.pricePerToken,
95
+ sellToken: params.sellToken,
96
+ sellAmount: params.sellAmount,
97
+ visibility,
98
+ allowedBuyers: params.privateBuyers ?? [],
99
+ expiry,
100
+ };
101
+
102
+ const hash = await wallet.writeContract({
103
+ chain: wallet.chain,
104
+ account: wallet.account!,
105
+ address: this.diamondAddress,
106
+ abi: DIAMOND_ABI,
107
+ functionName: 'createOrderInstant',
108
+ args: [args],
109
+ });
110
+
111
+ return this.createTransactionResult(hash);
112
+ }
113
+
114
+ /**
115
+ * Create a VESTED order
116
+ *
117
+ * Seller deposits tokens that vest over time for the buyer.
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * const result = await sdk.marketplace.createVestedOrder({
122
+ * sellToken: '0x...',
123
+ * sellAmount: parseEther('10000'),
124
+ * payToken: '0x...', // USDC
125
+ * pricePerToken: 1000000n, // 1 USDC (6 decimals)
126
+ * vestingDuration: 365 * 24 * 60 * 60, // 1 year
127
+ * cliffDuration: 30 * 24 * 60 * 60, // 30 day cliff
128
+ * startMode: 'upon_fill', // Vesting starts when order is filled
129
+ * });
130
+ * ```
131
+ */
132
+ async createVestedOrder(
133
+ params: CreateVestedOrderParams
134
+ ): Promise<TransactionResult> {
135
+ const wallet = this.requireWallet();
136
+
137
+ const visibility =
138
+ params.privateBuyers && params.privateBuyers.length > 0
139
+ ? Visibility.PRIVATE
140
+ : Visibility.PUBLIC;
141
+
142
+ const expiry = BigInt(
143
+ Math.floor(Date.now() / 1000) + (params.expiryTTL ?? 7 * 24 * 60 * 60)
144
+ );
145
+
146
+ // Convert cliff duration to basis points (percentage of total duration)
147
+ const cliffBps =
148
+ params.cliffDuration && params.vestingDuration
149
+ ? Math.floor((params.cliffDuration / params.vestingDuration) * 10000)
150
+ : 0;
151
+
152
+ const args = {
153
+ payToken: params.payToken,
154
+ price: params.pricePerToken,
155
+ sellToken: params.sellToken,
156
+ sellAmount: params.sellAmount,
157
+ visibility,
158
+ allowedBuyers: params.privateBuyers ?? [],
159
+ expiry,
160
+ vest: {
161
+ startMode:
162
+ params.startMode === 'fixed_timestamp'
163
+ ? StartMode.FIXED_TIMESTAMP
164
+ : StartMode.UPON_FILL,
165
+ startAt: BigInt(params.fixedStartTime ?? 0),
166
+ durationSec: params.vestingDuration,
167
+ stepSec: 0, // Linear vesting
168
+ cliffBps,
169
+ },
170
+ };
171
+
172
+ const hash = await wallet.writeContract({
173
+ chain: wallet.chain,
174
+ account: wallet.account!,
175
+ address: this.diamondAddress,
176
+ abi: DIAMOND_ABI,
177
+ functionName: 'createOrderVested',
178
+ args: [args],
179
+ });
180
+
181
+ return this.createTransactionResult(hash);
182
+ }
183
+
184
+ /**
185
+ * Create a TRADABLE order
186
+ *
187
+ * List an unvested campaign position for sale. No upfront deposit -
188
+ * the position is transferred when the order is filled.
189
+ *
190
+ * @example
191
+ * ```typescript
192
+ * // Must have a position in a VestedAirdrop campaign with trading enabled
193
+ * const result = await sdk.marketplace.createTradableOrder({
194
+ * campaignId: 10n,
195
+ * pricePerToken: parseEther('0.05'), // 0.05 ETH per token
196
+ * payToken: zeroAddress, // Native ETH
197
+ * expiryTTL: 30 * 24 * 60 * 60, // 30 days
198
+ * });
199
+ * console.log('Order created, tx:', result.hash);
200
+ * ```
201
+ */
202
+ async createTradableOrder(
203
+ params: CreateTradableOrderParams
204
+ ): Promise<TransactionResult> {
205
+ const wallet = this.requireWallet();
206
+
207
+ const visibility =
208
+ params.privateBuyers && params.privateBuyers.length > 0
209
+ ? Visibility.PRIVATE
210
+ : Visibility.PUBLIC;
211
+
212
+ const expiry = BigInt(
213
+ Math.floor(Date.now() / 1000) + (params.expiryTTL ?? 30 * 24 * 60 * 60)
214
+ );
215
+
216
+ const args = {
217
+ payToken: params.payToken,
218
+ price: params.pricePerToken,
219
+ campaignId: params.campaignId,
220
+ visibility,
221
+ allowedBuyers: params.privateBuyers ?? [],
222
+ expiry,
223
+ escrowRouting: zeroAddress, // Default to no escrow routing
224
+ };
225
+
226
+ const hash = await wallet.writeContract({
227
+ chain: wallet.chain,
228
+ account: wallet.account!,
229
+ address: this.diamondAddress,
230
+ abi: DIAMOND_ABI,
231
+ functionName: 'createOrderTradable',
232
+ args: [args],
233
+ });
234
+
235
+ return this.createTransactionResult(hash);
236
+ }
237
+
238
+ // ============================================================================
239
+ // Order Settlement
240
+ // ============================================================================
241
+
242
+ /**
243
+ * Fill an order (buy)
244
+ *
245
+ * For native payment orders, send ETH as value.
246
+ * For token payment orders, approve the Diamond first.
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * // Get quote first
251
+ * const quote = await sdk.marketplace.getQuote(orderId);
252
+ *
253
+ * // Fill with native ETH
254
+ * const result = await sdk.marketplace.fillOrder(orderId, {
255
+ * value: quote.totalPrice,
256
+ * });
257
+ *
258
+ * // Or fill with ERC20 (approve first)
259
+ * await sdk.approve(payToken, quote.totalPrice);
260
+ * await sdk.marketplace.fillOrder(orderId);
261
+ * ```
262
+ */
263
+ async fillOrder(
264
+ orderId: bigint,
265
+ options?: { value?: bigint }
266
+ ): Promise<TransactionResult> {
267
+ const wallet = this.requireWallet();
268
+
269
+ const hash = await wallet.writeContract({
270
+ chain: wallet.chain,
271
+ account: wallet.account!,
272
+ address: this.diamondAddress,
273
+ abi: DIAMOND_ABI,
274
+ functionName: 'fillOrder',
275
+ args: [orderId],
276
+ value: options?.value,
277
+ });
278
+
279
+ return this.createTransactionResult(hash);
280
+ }
281
+
282
+ /**
283
+ * Fill an order using Permit2 (gasless token approval)
284
+ */
285
+ async fillOrderWithPermit(params: {
286
+ orderId: bigint;
287
+ permit2Payload: Hex;
288
+ }): Promise<TransactionResult> {
289
+ const wallet = this.requireWallet();
290
+
291
+ const hash = await wallet.writeContract({
292
+ chain: wallet.chain,
293
+ account: wallet.account!,
294
+ address: this.diamondAddress,
295
+ abi: DIAMOND_ABI,
296
+ functionName: 'fillOrderWithPermit',
297
+ args: [params.orderId, params.permit2Payload],
298
+ });
299
+
300
+ return this.createTransactionResult(hash);
301
+ }
302
+
303
+ /**
304
+ * Cancel an order (seller only)
305
+ *
306
+ * Returns deposited tokens to seller.
307
+ *
308
+ * @example
309
+ * ```typescript
310
+ * const result = await sdk.marketplace.cancelOrder(orderId);
311
+ * ```
312
+ */
313
+ async cancelOrder(orderId: bigint): Promise<TransactionResult> {
314
+ const wallet = this.requireWallet();
315
+
316
+ const hash = await wallet.writeContract({
317
+ chain: wallet.chain,
318
+ account: wallet.account!,
319
+ address: this.diamondAddress,
320
+ abi: DIAMOND_ABI,
321
+ functionName: 'cancelOrder',
322
+ args: [orderId],
323
+ });
324
+
325
+ return this.createTransactionResult(hash);
326
+ }
327
+
328
+ /**
329
+ * Expire an order (anyone can call after expiry)
330
+ *
331
+ * Returns deposited tokens to seller.
332
+ */
333
+ async expireOrder(orderId: bigint): Promise<TransactionResult> {
334
+ const wallet = this.requireWallet();
335
+
336
+ const hash = await wallet.writeContract({
337
+ chain: wallet.chain,
338
+ account: wallet.account!,
339
+ address: this.diamondAddress,
340
+ abi: DIAMOND_ABI,
341
+ functionName: 'expireOrder',
342
+ args: [orderId],
343
+ });
344
+
345
+ return this.createTransactionResult(hash);
346
+ }
347
+
348
+ // ============================================================================
349
+ // Order Queries
350
+ // ============================================================================
351
+
352
+ /**
353
+ * Get order information by ID
354
+ *
355
+ * @example
356
+ * ```typescript
357
+ * const order = await sdk.marketplace.getOrder(1n);
358
+ * console.log('Seller:', order.seller);
359
+ * console.log('Price:', formatEther(order.totalPrice));
360
+ * console.log('Status:', order.status);
361
+ * ```
362
+ */
363
+ async getOrder(orderId: bigint): Promise<OrderInfo> {
364
+ const result = await this.publicClient.readContract({
365
+ address: this.diamondAddress,
366
+ abi: DIAMOND_ABI,
367
+ functionName: 'getOrder',
368
+ args: [orderId],
369
+ });
370
+
371
+ return this.parseOrderInfo(result as unknown as OrderStruct);
372
+ }
373
+
374
+ /**
375
+ * Get order quote (price breakdown)
376
+ *
377
+ * @example
378
+ * ```typescript
379
+ * const quote = await sdk.marketplace.getQuote(orderId);
380
+ * console.log('Total price:', formatEther(quote.totalPrice));
381
+ * console.log('Protocol fee:', formatEther(quote.protocolFee));
382
+ * ```
383
+ */
384
+ async getQuote(orderId: bigint): Promise<OrderQuote> {
385
+ const result = await this.publicClient.readContract({
386
+ address: this.diamondAddress,
387
+ abi: DIAMOND_ABI,
388
+ functionName: 'quote',
389
+ args: [orderId],
390
+ }) as { payToken: Address; feeBps: number; price: bigint; feeAmount: bigint; proceeds: bigint };
391
+
392
+ // Also get the order to know if it's still valid
393
+ const order = await this.getOrder(orderId);
394
+
395
+ return {
396
+ orderId,
397
+ totalPrice: result.price,
398
+ protocolFee: result.feeAmount,
399
+ buyerReceives: order.sellAmount,
400
+ sellerReceives: result.proceeds,
401
+ isValid: order.status === 'open' && order.expiresAt > Math.floor(Date.now() / 1000),
402
+ expiresAt: order.expiresAt,
403
+ };
404
+ }
405
+
406
+ /**
407
+ * Get detailed quote for filling an order
408
+ */
409
+ async getQuoteFill(orderId: bigint): Promise<{
410
+ payToken: Address;
411
+ price: bigint;
412
+ feeBps: number;
413
+ feeAmount: bigint;
414
+ feeReceiver: Address;
415
+ proceeds: bigint;
416
+ seller: Address;
417
+ sellToken: Address;
418
+ sellAmount: bigint;
419
+ deliveryKind: number;
420
+ escrowFlushedToRouting: boolean;
421
+ }> {
422
+ const result = await this.publicClient.readContract({
423
+ address: this.diamondAddress,
424
+ abi: DIAMOND_ABI,
425
+ functionName: 'quoteFill',
426
+ args: [orderId],
427
+ }) as {
428
+ payToken: Address;
429
+ price: bigint;
430
+ feeBps: number;
431
+ feeAmount: bigint;
432
+ feeReceiver: Address;
433
+ proceeds: bigint;
434
+ seller: Address;
435
+ sellToken: Address;
436
+ sellAmount: bigint;
437
+ deliveryKind: number;
438
+ escrowFlushedToRouting: boolean;
439
+ };
440
+
441
+ return result;
442
+ }
443
+
444
+ /**
445
+ * Get next order ID (also indicates total orders created)
446
+ */
447
+ async getNextOrderId(): Promise<bigint> {
448
+ const result = await this.publicClient.readContract({
449
+ address: this.diamondAddress,
450
+ abi: DIAMOND_ABI,
451
+ functionName: 'nextOrderId',
452
+ args: [],
453
+ });
454
+
455
+ return result as bigint;
456
+ }
457
+
458
+ /**
459
+ * Get orders by seller with pagination
460
+ */
461
+ async getOrdersBySeller(
462
+ seller: Address,
463
+ offset = 0,
464
+ limit = 100
465
+ ): Promise<OrderInfo[]> {
466
+ const result = await this.publicClient.readContract({
467
+ address: this.diamondAddress,
468
+ abi: DIAMOND_ABI,
469
+ functionName: 'ordersOf',
470
+ args: [seller, BigInt(offset), BigInt(limit)],
471
+ }) as unknown as OrderStruct[];
472
+
473
+ return result.map((order) => this.parseOrderInfo(order));
474
+ }
475
+
476
+ /**
477
+ * Get orders for a campaign
478
+ */
479
+ async getOrdersForCampaign(campaignId: bigint): Promise<{
480
+ activeOrderId: bigint;
481
+ historicalOrderIds: bigint[];
482
+ }> {
483
+ const result = await this.publicClient.readContract({
484
+ address: this.diamondAddress,
485
+ abi: DIAMOND_ABI,
486
+ functionName: 'ordersByCampaign',
487
+ args: [campaignId],
488
+ }) as { activeOrderId: bigint; historicalOrderIds: bigint[] };
489
+
490
+ return result;
491
+ }
492
+
493
+ /**
494
+ * Get active order for a campaign
495
+ */
496
+ async getActiveOrderForCampaign(campaignId: bigint): Promise<bigint> {
497
+ const result = await this.publicClient.readContract({
498
+ address: this.diamondAddress,
499
+ abi: DIAMOND_ABI,
500
+ functionName: 'getActiveOrderForCampaign',
501
+ args: [campaignId],
502
+ });
503
+
504
+ return result as bigint;
505
+ }
506
+
507
+ /**
508
+ * Preview available amount to sell for a campaign
509
+ */
510
+ async previewAvailableToSell(campaignId: bigint): Promise<{
511
+ futureUnlockable: bigint;
512
+ claimableNow: bigint;
513
+ asOf: number;
514
+ }> {
515
+ const result = await this.publicClient.readContract({
516
+ address: this.diamondAddress,
517
+ abi: DIAMOND_ABI,
518
+ functionName: 'previewAvailableToSell',
519
+ args: [campaignId],
520
+ }) as { futureUnlockable: bigint; claimableNow: bigint; asOf: bigint };
521
+
522
+ return {
523
+ futureUnlockable: result.futureUnlockable,
524
+ claimableNow: result.claimableNow,
525
+ asOf: Number(result.asOf),
526
+ };
527
+ }
528
+
529
+ /**
530
+ * Get all open orders
531
+ */
532
+ async getOpenOrders(limit = 100): Promise<OrderInfo[]> {
533
+ const nextId = await this.getNextOrderId();
534
+ const orders: OrderInfo[] = [];
535
+
536
+ for (let i = 0n; i < nextId && orders.length < limit; i++) {
537
+ try {
538
+ const order = await this.getOrder(i);
539
+ if (order.status === 'open') {
540
+ orders.push(order);
541
+ }
542
+ } catch {
543
+ // Order may not exist, skip
544
+ }
545
+ }
546
+
547
+ return orders;
548
+ }
549
+
550
+ // ============================================================================
551
+ // Validation
552
+ // ============================================================================
553
+
554
+ /**
555
+ * Check if trading is allowed for a campaign
556
+ */
557
+ async isTradingEnabled(campaignId: bigint): Promise<boolean> {
558
+ const result = await this.publicClient.readContract({
559
+ address: this.diamondAddress,
560
+ abi: DIAMOND_ABI,
561
+ functionName: 'isTradeLocked',
562
+ args: [campaignId],
563
+ });
564
+ return !(result as boolean);
565
+ }
566
+
567
+ // ============================================================================
568
+ // Trade Policy (Creator Functions)
569
+ // ============================================================================
570
+
571
+ /**
572
+ * Lock trading for a campaign (creator only)
573
+ *
574
+ * Prevents new orders from being created for this campaign.
575
+ */
576
+ async lockTrading(campaignId: bigint): Promise<TransactionResult> {
577
+ const wallet = this.requireWallet();
578
+
579
+ const hash = await wallet.writeContract({
580
+ chain: wallet.chain,
581
+ account: wallet.account!,
582
+ address: this.diamondAddress,
583
+ abi: DIAMOND_ABI,
584
+ functionName: 'setTradeLocked',
585
+ args: [campaignId, true],
586
+ });
587
+
588
+ return this.createTransactionResult(hash);
589
+ }
590
+
591
+ /**
592
+ * Unlock trading for a campaign (creator only)
593
+ */
594
+ async unlockTrading(campaignId: bigint): Promise<TransactionResult> {
595
+ const wallet = this.requireWallet();
596
+
597
+ const hash = await wallet.writeContract({
598
+ chain: wallet.chain,
599
+ account: wallet.account!,
600
+ address: this.diamondAddress,
601
+ abi: DIAMOND_ABI,
602
+ functionName: 'setTradeLocked',
603
+ args: [campaignId, false],
604
+ });
605
+
606
+ return this.createTransactionResult(hash);
607
+ }
608
+
609
+ // ============================================================================
610
+ // Utility Functions
611
+ // ============================================================================
612
+
613
+ /**
614
+ * Calculate total price for a given amount at price per token
615
+ */
616
+ calculateTotalPrice(amount: bigint, pricePerToken: bigint): bigint {
617
+ return (amount * pricePerToken) / BigInt(1e18);
618
+ }
619
+
620
+ /**
621
+ * Check if payment is in native token (ETH/BNB/etc)
622
+ */
623
+ isNativePayment(payToken: Address): boolean {
624
+ return payToken === zeroAddress;
625
+ }
626
+
627
+ // ============================================================================
628
+ // Helpers
629
+ // ============================================================================
630
+
631
+ private requireWallet(): WalletClient {
632
+ if (!this.walletClient) {
633
+ throw new Error(
634
+ 'Wallet client required for write operations. Initialize SDK with a signer.'
635
+ );
636
+ }
637
+ return this.walletClient;
638
+ }
639
+
640
+ private parseOrderInfo(order: OrderStruct): OrderInfo {
641
+ const orderTypeMap: Record<number, OrderType> = {
642
+ 0: 'instant',
643
+ 1: 'vested',
644
+ 2: 'tradable',
645
+ };
646
+
647
+ const statusMap: Record<number, OrderInfo['status']> = {
648
+ 0: 'open',
649
+ 1: 'filled',
650
+ 2: 'canceled',
651
+ 3: 'expired',
652
+ };
653
+
654
+ const result: OrderInfo = {
655
+ id: order.id,
656
+ orderType: orderTypeMap[order.kind] ?? 'instant',
657
+ seller: order.seller,
658
+ sellToken: order.sellToken,
659
+ sellAmount: order.sellAmount,
660
+ payToken: order.payToken,
661
+ totalPrice: order.price,
662
+ pricePerToken: order.sellAmount > 0n
663
+ ? (order.price * BigInt(1e18)) / order.sellAmount
664
+ : 0n,
665
+ status: statusMap[order.status] ?? 'open',
666
+ createdAt: 0, // Not stored in Order struct
667
+ expiresAt: Number(order.expiry),
668
+ };
669
+
670
+ // Add optional fields only if they have values
671
+ if (order.campaignId > 0n) {
672
+ result.campaignId = order.campaignId;
673
+ }
674
+ if (order.vest.durationSec > 0) {
675
+ result.vestingDuration = order.vest.durationSec;
676
+ }
677
+ if (order.vest.cliffBps > 0) {
678
+ result.cliffDuration = Math.floor((order.vest.durationSec * order.vest.cliffBps) / 10000);
679
+ }
680
+
681
+ return result;
682
+ }
683
+
684
+ private createTransactionResult(hash: Hash): TransactionResult {
685
+ return {
686
+ hash,
687
+ wait: async () => {
688
+ const receipt = await this.publicClient.waitForTransactionReceipt({
689
+ hash,
690
+ });
691
+ return {
692
+ blockNumber: receipt.blockNumber,
693
+ transactionHash: receipt.transactionHash,
694
+ gasUsed: receipt.gasUsed,
695
+ status: receipt.status,
696
+ logs: receipt.logs,
697
+ };
698
+ },
699
+ };
700
+ }
701
+ }
702
+
703
+ /**
704
+ * Order struct matching contract
705
+ */
706
+ interface OrderStruct {
707
+ id: bigint;
708
+ seller: Address;
709
+ payToken: Address;
710
+ price: bigint;
711
+ kind: number;
712
+ sellToken: Address;
713
+ sellAmount: bigint;
714
+ campaignId: bigint;
715
+ visibility: number;
716
+ allowedBuyers: readonly Address[];
717
+ expiry: bigint;
718
+ status: number;
719
+ vest: {
720
+ startMode: number;
721
+ startAt: bigint;
722
+ durationSec: number;
723
+ stepSec: number;
724
+ cliffBps: number;
725
+ };
726
+ listedBehaviour: number;
727
+ escrowRouting: Address;
728
+ }
729
+
730
+ export default MarketplaceModule;