@optionsfi/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.mjs ADDED
@@ -0,0 +1,3457 @@
1
+ // src/client/RFQClient.ts
2
+ import WebSocket from "ws";
3
+ import { Connection } from "@solana/web3.js";
4
+
5
+ // src/constants/config.ts
6
+ var DEVNET_CONFIG = {
7
+ rpcUrl: "https://api.devnet.solana.com",
8
+ rfqRouterUrl: "wss://rfq.optionsfi.xyz",
9
+ programId: "A4jgqct3bwTwRmHECHdPpbH3a8ksaVb7rny9pMUGFo94",
10
+ network: "devnet",
11
+ rfqTimeoutMs: 3e4
12
+ };
13
+ var MAINNET_CONFIG = {
14
+ rpcUrl: "https://api.mainnet-beta.solana.com",
15
+ rfqRouterUrl: "wss://rfq.optionsfi.xyz",
16
+ programId: "A4jgqct3bwTwRmHECHdPpbH3a8ksaVb7rny9pMUGFo94",
17
+ network: "mainnet-beta",
18
+ rfqTimeoutMs: 3e4
19
+ };
20
+ var DEFAULT_PRICING_PARAMS = {
21
+ /** Default risk-free rate (5%) */
22
+ riskFreeRate: 0.05,
23
+ /** Default volatility lookback period in days */
24
+ volatilityLookbackDays: 30,
25
+ /** Maximum acceptable deviation from fair value (5%) */
26
+ maxQuoteDeviationBps: 500,
27
+ /** Trading days per year for volatility annualization */
28
+ tradingDaysPerYear: 252
29
+ };
30
+ var RFQ_DEFAULTS = {
31
+ /** Default RFQ timeout in milliseconds */
32
+ timeoutMs: 3e4,
33
+ /** Minimum quote collection time */
34
+ minQuoteTimeMs: 5e3,
35
+ /** Quote expiry buffer (how long before expiry to stop accepting) */
36
+ quoteExpiryBufferMs: 2e3
37
+ };
38
+ var TOKEN_DECIMALS = {
39
+ USDC: 6,
40
+ SOL: 9,
41
+ NVDAX: 6
42
+ // Example: tokenized NVIDIA stock
43
+ };
44
+
45
+ // src/client/RFQClient.ts
46
+ var RFQClient = class {
47
+ connection;
48
+ wsConnection = null;
49
+ config;
50
+ quoteCallbacks = /* @__PURE__ */ new Map();
51
+ eventCallbacks = /* @__PURE__ */ new Set();
52
+ activeRFQs = /* @__PURE__ */ new Map();
53
+ isConnected = false;
54
+ reconnectAttempts = 0;
55
+ maxReconnectAttempts = 5;
56
+ /**
57
+ * Create a new RFQ Client
58
+ *
59
+ * @param config - Configuration for connecting to OptionsFi
60
+ */
61
+ constructor(config) {
62
+ this.config = {
63
+ ...config,
64
+ rfqTimeoutMs: config.rfqTimeoutMs ?? RFQ_DEFAULTS.timeoutMs
65
+ };
66
+ this.connection = new Connection(config.rpcUrl, "confirmed");
67
+ }
68
+ /**
69
+ * Connect to the RFQ router WebSocket
70
+ *
71
+ * @returns Promise that resolves when connected
72
+ * @throws Error if connection fails
73
+ */
74
+ async connect() {
75
+ return new Promise((resolve, reject) => {
76
+ try {
77
+ this.wsConnection = new WebSocket(this.config.rfqRouterUrl);
78
+ this.wsConnection.on("open", () => {
79
+ console.log("Connected to OptionsFi RFQ Router");
80
+ this.isConnected = true;
81
+ this.reconnectAttempts = 0;
82
+ resolve();
83
+ });
84
+ this.wsConnection.on("message", (data) => {
85
+ try {
86
+ const message = JSON.parse(data.toString());
87
+ this.handleMessage(message);
88
+ } catch (error) {
89
+ console.error("Failed to parse WebSocket message:", error);
90
+ }
91
+ });
92
+ this.wsConnection.on("error", (error) => {
93
+ console.error("WebSocket error:", error);
94
+ this.emitEvent({
95
+ type: "connection_error",
96
+ rfqId: "",
97
+ data: error,
98
+ timestamp: Date.now()
99
+ });
100
+ if (!this.isConnected) {
101
+ reject(error);
102
+ }
103
+ });
104
+ this.wsConnection.on("close", () => {
105
+ console.log("WebSocket connection closed");
106
+ this.isConnected = false;
107
+ this.attemptReconnect();
108
+ });
109
+ } catch (error) {
110
+ reject(error);
111
+ }
112
+ });
113
+ }
114
+ /**
115
+ * Disconnect from the RFQ router
116
+ */
117
+ disconnect() {
118
+ if (this.wsConnection) {
119
+ this.wsConnection.close();
120
+ this.wsConnection = null;
121
+ this.isConnected = false;
122
+ }
123
+ }
124
+ /**
125
+ * Check if the client is connected
126
+ */
127
+ get connected() {
128
+ return this.isConnected;
129
+ }
130
+ /**
131
+ * Create a new RFQ and broadcast to market makers
132
+ *
133
+ * @param params - RFQ parameters
134
+ * @returns RFQ ID
135
+ * @throws Error if not connected or params invalid
136
+ */
137
+ async createRFQ(params) {
138
+ if (!this.wsConnection || !this.isConnected) {
139
+ throw new Error("Not connected. Call connect() first.");
140
+ }
141
+ this.validateRFQParams(params);
142
+ const rfqId = this.generateRFQId();
143
+ const rfq = {
144
+ id: rfqId,
145
+ params,
146
+ quotes: [],
147
+ status: "open",
148
+ createdAt: Date.now()
149
+ };
150
+ this.activeRFQs.set(rfqId, rfq);
151
+ const message = {
152
+ type: "create_rfq",
153
+ rfqId,
154
+ underlying: params.asset,
155
+ optionType: params.optionType,
156
+ strike: params.strike,
157
+ expiry: params.expiry,
158
+ size: params.quantity.toString(),
159
+ premiumFloor: params.premiumFloor?.toString() ?? "0",
160
+ timestamp: Date.now()
161
+ };
162
+ this.wsConnection.send(JSON.stringify(message));
163
+ return rfqId;
164
+ }
165
+ /**
166
+ * Subscribe to quotes for a specific RFQ
167
+ *
168
+ * @param rfqId - RFQ to subscribe to
169
+ * @param callback - Called when a quote is received
170
+ */
171
+ subscribeToQuotes(rfqId, callback) {
172
+ this.quoteCallbacks.set(rfqId, callback);
173
+ if (this.wsConnection && this.isConnected) {
174
+ this.wsConnection.send(JSON.stringify({
175
+ type: "subscribe_quotes",
176
+ rfqId
177
+ }));
178
+ }
179
+ }
180
+ /**
181
+ * Unsubscribe from quotes for an RFQ
182
+ *
183
+ * @param rfqId - RFQ to unsubscribe from
184
+ */
185
+ unsubscribeFromQuotes(rfqId) {
186
+ this.quoteCallbacks.delete(rfqId);
187
+ }
188
+ /**
189
+ * Accept a quote and execute the option trade
190
+ *
191
+ * @param rfqId - RFQ ID
192
+ * @param quoteId - Quote to accept
193
+ * @param wallet - Solana wallet for signing
194
+ * @returns Transaction signature
195
+ */
196
+ async executeOption(rfqId, quoteId, wallet) {
197
+ const rfq = this.activeRFQs.get(rfqId);
198
+ if (!rfq) {
199
+ throw new Error(`RFQ ${rfqId} not found`);
200
+ }
201
+ const quote = rfq.quotes.find((q) => q.id === quoteId);
202
+ if (!quote) {
203
+ throw new Error(`Quote ${quoteId} not found in RFQ ${rfqId}`);
204
+ }
205
+ if (this.wsConnection && this.isConnected) {
206
+ this.wsConnection.send(JSON.stringify({
207
+ type: "accept_quote",
208
+ rfqId,
209
+ quoteId
210
+ }));
211
+ }
212
+ const tx = await this.buildExecutionTransaction(rfq, quote);
213
+ const signedTx = await wallet.signTransaction(tx);
214
+ const signature = await this.connection.sendRawTransaction(
215
+ signedTx.serialize()
216
+ );
217
+ await this.connection.confirmTransaction(signature, "confirmed");
218
+ rfq.status = "filled";
219
+ rfq.fill = {
220
+ quoteId,
221
+ marketMaker: quote.marketMaker,
222
+ premium: quote.premium,
223
+ filledAt: Date.now(),
224
+ transactionSignature: signature
225
+ };
226
+ return signature;
227
+ }
228
+ /**
229
+ * Cancel an open RFQ
230
+ *
231
+ * @param rfqId - RFQ to cancel
232
+ */
233
+ async cancelRFQ(rfqId) {
234
+ const rfq = this.activeRFQs.get(rfqId);
235
+ if (!rfq) {
236
+ throw new Error(`RFQ ${rfqId} not found`);
237
+ }
238
+ if (rfq.status !== "open") {
239
+ throw new Error(`Cannot cancel RFQ with status: ${rfq.status}`);
240
+ }
241
+ if (this.wsConnection && this.isConnected) {
242
+ this.wsConnection.send(JSON.stringify({
243
+ type: "cancel_rfq",
244
+ rfqId
245
+ }));
246
+ }
247
+ rfq.status = "cancelled";
248
+ this.quoteCallbacks.delete(rfqId);
249
+ }
250
+ /**
251
+ * Get an active RFQ by ID
252
+ *
253
+ * @param rfqId - RFQ ID
254
+ * @returns RFQ or undefined
255
+ */
256
+ getRFQ(rfqId) {
257
+ return this.activeRFQs.get(rfqId);
258
+ }
259
+ /**
260
+ * Get all active RFQs
261
+ */
262
+ getAllRFQs() {
263
+ return Array.from(this.activeRFQs.values());
264
+ }
265
+ /**
266
+ * Subscribe to all RFQ events
267
+ *
268
+ * @param callback - Called for each event
269
+ * @returns Unsubscribe function
270
+ */
271
+ onEvent(callback) {
272
+ this.eventCallbacks.add(callback);
273
+ return () => this.eventCallbacks.delete(callback);
274
+ }
275
+ // ============================================================================
276
+ // Private Methods
277
+ // ============================================================================
278
+ handleMessage(message) {
279
+ if (message.type === "quote" && message.rfqId) {
280
+ const quote = {
281
+ id: message.quoteId || `quote_${Date.now()}`,
282
+ rfqId: message.rfqId,
283
+ marketMaker: message.maker,
284
+ premium: BigInt(message.premium),
285
+ timestamp: Date.now(),
286
+ expiresAt: message.expiresAt || Date.now() + 6e4
287
+ };
288
+ const rfq = this.activeRFQs.get(message.rfqId);
289
+ if (rfq && rfq.status === "open") {
290
+ rfq.quotes.push(quote);
291
+ }
292
+ const callback = this.quoteCallbacks.get(message.rfqId);
293
+ if (callback) {
294
+ callback(quote);
295
+ }
296
+ this.emitEvent({
297
+ type: "quote_received",
298
+ rfqId: message.rfqId,
299
+ data: quote,
300
+ timestamp: Date.now()
301
+ });
302
+ } else if (message.type === "fill" && message.rfqId) {
303
+ const rfq = this.activeRFQs.get(message.rfqId);
304
+ if (rfq) {
305
+ rfq.status = "filled";
306
+ this.emitEvent({
307
+ type: "rfq_filled",
308
+ rfqId: message.rfqId,
309
+ data: rfq,
310
+ timestamp: Date.now()
311
+ });
312
+ }
313
+ }
314
+ }
315
+ emitEvent(event) {
316
+ for (const callback of this.eventCallbacks) {
317
+ try {
318
+ callback(event);
319
+ } catch (error) {
320
+ console.error("Event callback error:", error);
321
+ }
322
+ }
323
+ }
324
+ validateRFQParams(params) {
325
+ if (!params.asset || params.asset.length === 0) {
326
+ throw new Error("Asset is required");
327
+ }
328
+ if (params.quantity <= 0) {
329
+ throw new Error("Quantity must be positive");
330
+ }
331
+ if (params.strike <= 0) {
332
+ throw new Error("Strike must be positive");
333
+ }
334
+ if (params.expiry <= Math.floor(Date.now() / 1e3)) {
335
+ throw new Error("Expiry must be in the future");
336
+ }
337
+ if (!["call", "put"].includes(params.optionType)) {
338
+ throw new Error('Option type must be "call" or "put"');
339
+ }
340
+ if (!["buy", "sell"].includes(params.side)) {
341
+ throw new Error('Side must be "buy" or "sell"');
342
+ }
343
+ }
344
+ generateRFQId() {
345
+ return `rfq_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
346
+ }
347
+ async buildExecutionTransaction(rfq, quote) {
348
+ throw new Error(
349
+ "Transaction building not yet implemented. Use VaultInstructions class to build transactions manually."
350
+ );
351
+ }
352
+ attemptReconnect() {
353
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
354
+ console.error("Max reconnection attempts reached");
355
+ return;
356
+ }
357
+ this.reconnectAttempts++;
358
+ const delay = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), 3e4);
359
+ console.log(`Attempting reconnection in ${delay}ms (attempt ${this.reconnectAttempts})`);
360
+ setTimeout(() => {
361
+ this.connect().catch((error) => {
362
+ console.error("Reconnection failed:", error);
363
+ });
364
+ }, delay);
365
+ }
366
+ };
367
+
368
+ // src/client/VaultInstructions.ts
369
+ import * as anchor from "@coral-xyz/anchor";
370
+ import {
371
+ PublicKey as PublicKey3
372
+ } from "@solana/web3.js";
373
+
374
+ // src/constants/addresses.ts
375
+ import { PublicKey as PublicKey2 } from "@solana/web3.js";
376
+ var VAULT_PROGRAM_ID = new PublicKey2(
377
+ "A4jgqct3bwTwRmHECHdPpbH3a8ksaVb7rny9pMUGFo94"
378
+ );
379
+ var DEVNET_USDC_MINT = new PublicKey2(
380
+ "Gh9ZwEmdLJ8DscKNTkTqPbNwLNNBjuSzaG9Vp2KGtKJr"
381
+ );
382
+ var MAINNET_USDC_MINT = new PublicKey2(
383
+ "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
384
+ );
385
+ var PYTH_PRICE_FEEDS = {
386
+ /** NVDA/USD price feed */
387
+ NVDA: "0x4244d07890e4610f46bbde67de8f43a4bf8b569eebe904f136b469f148503b7f",
388
+ /** SOL/USD price feed */
389
+ SOL: "0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d",
390
+ /** BTC/USD price feed */
391
+ BTC: "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43",
392
+ /** ETH/USD price feed */
393
+ ETH: "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"
394
+ };
395
+ var PYTH_HERMES_URL = "https://hermes.pyth.network";
396
+ function deriveVaultPda(assetId) {
397
+ return PublicKey2.findProgramAddressSync(
398
+ [Buffer.from("vault"), Buffer.from(assetId)],
399
+ VAULT_PROGRAM_ID
400
+ );
401
+ }
402
+ function deriveWithdrawalPda(vault, user, epoch) {
403
+ return PublicKey2.findProgramAddressSync(
404
+ [
405
+ Buffer.from("withdrawal"),
406
+ vault.toBuffer(),
407
+ user.toBuffer(),
408
+ Buffer.from(epoch.toString())
409
+ ],
410
+ VAULT_PROGRAM_ID
411
+ );
412
+ }
413
+ function deriveShareEscrowPda(vault) {
414
+ return PublicKey2.findProgramAddressSync(
415
+ [Buffer.from("share_escrow"), vault.toBuffer()],
416
+ VAULT_PROGRAM_ID
417
+ );
418
+ }
419
+ function deriveWhitelistPda(vault) {
420
+ return PublicKey2.findProgramAddressSync(
421
+ [Buffer.from("whitelist"), vault.toBuffer()],
422
+ VAULT_PROGRAM_ID
423
+ );
424
+ }
425
+
426
+ // src/idl/vault.json
427
+ var vault_default = {
428
+ address: "A4jgqct3bwTwRmHECHdPpbH3a8ksaVb7rny9pMUGFo94",
429
+ metadata: {
430
+ name: "vault",
431
+ version: "0.1.0",
432
+ spec: "0.1.0",
433
+ description: "OptionsFi V2 - Covered Call Vault"
434
+ },
435
+ instructions: [
436
+ {
437
+ name: "add_market_maker",
438
+ docs: [
439
+ "Add a market maker to the whitelist"
440
+ ],
441
+ discriminator: [
442
+ 24,
443
+ 255,
444
+ 135,
445
+ 32,
446
+ 193,
447
+ 2,
448
+ 112,
449
+ 44
450
+ ],
451
+ accounts: [
452
+ {
453
+ name: "vault",
454
+ pda: {
455
+ seeds: [
456
+ {
457
+ kind: "const",
458
+ value: [
459
+ 118,
460
+ 97,
461
+ 117,
462
+ 108,
463
+ 116
464
+ ]
465
+ },
466
+ {
467
+ kind: "account",
468
+ path: "vault.asset_id",
469
+ account: "Vault"
470
+ }
471
+ ]
472
+ },
473
+ relations: [
474
+ "whitelist"
475
+ ]
476
+ },
477
+ {
478
+ name: "whitelist",
479
+ writable: true,
480
+ pda: {
481
+ seeds: [
482
+ {
483
+ kind: "const",
484
+ value: [
485
+ 119,
486
+ 104,
487
+ 105,
488
+ 116,
489
+ 101,
490
+ 108,
491
+ 105,
492
+ 115,
493
+ 116
494
+ ]
495
+ },
496
+ {
497
+ kind: "account",
498
+ path: "vault"
499
+ }
500
+ ]
501
+ }
502
+ },
503
+ {
504
+ name: "authority",
505
+ writable: true,
506
+ signer: true,
507
+ relations: [
508
+ "vault",
509
+ "whitelist"
510
+ ]
511
+ }
512
+ ],
513
+ args: [
514
+ {
515
+ name: "market_maker",
516
+ type: "pubkey"
517
+ }
518
+ ]
519
+ },
520
+ {
521
+ name: "advance_epoch",
522
+ docs: [
523
+ "Advance epoch (called by keeper after settlement)",
524
+ "Premium earned is credited to total_assets, increasing share value"
525
+ ],
526
+ discriminator: [
527
+ 93,
528
+ 138,
529
+ 234,
530
+ 218,
531
+ 241,
532
+ 230,
533
+ 132,
534
+ 38
535
+ ],
536
+ accounts: [
537
+ {
538
+ name: "vault",
539
+ writable: true,
540
+ pda: {
541
+ seeds: [
542
+ {
543
+ kind: "const",
544
+ value: [
545
+ 118,
546
+ 97,
547
+ 117,
548
+ 108,
549
+ 116
550
+ ]
551
+ },
552
+ {
553
+ kind: "account",
554
+ path: "vault.asset_id",
555
+ account: "Vault"
556
+ }
557
+ ]
558
+ }
559
+ },
560
+ {
561
+ name: "authority",
562
+ signer: true,
563
+ relations: [
564
+ "vault"
565
+ ]
566
+ }
567
+ ],
568
+ args: [
569
+ {
570
+ name: "premium_earned",
571
+ type: "u64"
572
+ }
573
+ ]
574
+ },
575
+ {
576
+ name: "cancel_param_change",
577
+ docs: [
578
+ "Cancel a queued parameter change"
579
+ ],
580
+ discriminator: [
581
+ 150,
582
+ 147,
583
+ 92,
584
+ 108,
585
+ 72,
586
+ 160,
587
+ 224,
588
+ 55
589
+ ],
590
+ accounts: [
591
+ {
592
+ name: "vault",
593
+ writable: true,
594
+ pda: {
595
+ seeds: [
596
+ {
597
+ kind: "const",
598
+ value: [
599
+ 118,
600
+ 97,
601
+ 117,
602
+ 108,
603
+ 116
604
+ ]
605
+ },
606
+ {
607
+ kind: "account",
608
+ path: "vault.asset_id",
609
+ account: "Vault"
610
+ }
611
+ ]
612
+ }
613
+ },
614
+ {
615
+ name: "authority",
616
+ signer: true,
617
+ relations: [
618
+ "vault"
619
+ ]
620
+ }
621
+ ],
622
+ args: []
623
+ },
624
+ {
625
+ name: "close_orphaned_token_account",
626
+ docs: [
627
+ "Close an orphaned token account that was owned by a vault PDA",
628
+ "This is used to clean up old share escrows, vault token accounts, etc.",
629
+ "after a vault has been force-closed, enabling reuse of asset IDs.",
630
+ "",
631
+ "The caller must provide the correct asset_id that was used to derive the vault PDA.",
632
+ "The vault PDA must NOT exist anymore (force-closed)."
633
+ ],
634
+ discriminator: [
635
+ 232,
636
+ 127,
637
+ 173,
638
+ 13,
639
+ 92,
640
+ 144,
641
+ 116,
642
+ 98
643
+ ],
644
+ accounts: [
645
+ {
646
+ name: "vault_pda",
647
+ docs: [
648
+ "We verify this matches the derived PDA in the instruction"
649
+ ],
650
+ writable: true
651
+ },
652
+ {
653
+ name: "token_account",
654
+ docs: [
655
+ "The token account to close (share escrow, vault token account, etc.)"
656
+ ],
657
+ writable: true
658
+ },
659
+ {
660
+ name: "authority",
661
+ docs: [
662
+ "Authority receiving the lamports"
663
+ ],
664
+ writable: true,
665
+ signer: true
666
+ },
667
+ {
668
+ name: "token_program",
669
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
670
+ }
671
+ ],
672
+ args: [
673
+ {
674
+ name: "asset_id",
675
+ type: "string"
676
+ }
677
+ ]
678
+ },
679
+ {
680
+ name: "close_vault",
681
+ docs: [
682
+ "Close a vault and recover rent (authority only)",
683
+ "Vault must be empty (no assets, shares, or pending withdrawals)"
684
+ ],
685
+ discriminator: [
686
+ 141,
687
+ 103,
688
+ 17,
689
+ 126,
690
+ 72,
691
+ 75,
692
+ 29,
693
+ 29
694
+ ],
695
+ accounts: [
696
+ {
697
+ name: "vault",
698
+ writable: true,
699
+ pda: {
700
+ seeds: [
701
+ {
702
+ kind: "const",
703
+ value: [
704
+ 118,
705
+ 97,
706
+ 117,
707
+ 108,
708
+ 116
709
+ ]
710
+ },
711
+ {
712
+ kind: "account",
713
+ path: "vault.asset_id",
714
+ account: "Vault"
715
+ }
716
+ ]
717
+ }
718
+ },
719
+ {
720
+ name: "authority",
721
+ writable: true,
722
+ signer: true,
723
+ relations: [
724
+ "vault"
725
+ ]
726
+ }
727
+ ],
728
+ args: []
729
+ },
730
+ {
731
+ name: "collect_premium",
732
+ docs: [
733
+ "Collect premium from market maker (called during epoch roll)",
734
+ "Transfers USDC from payer to vault's premium account",
735
+ "SECURITY FIX M-1: Now requires authority signature to prevent front-running"
736
+ ],
737
+ discriminator: [
738
+ 166,
739
+ 199,
740
+ 123,
741
+ 128,
742
+ 71,
743
+ 141,
744
+ 223,
745
+ 204
746
+ ],
747
+ accounts: [
748
+ {
749
+ name: "vault",
750
+ pda: {
751
+ seeds: [
752
+ {
753
+ kind: "const",
754
+ value: [
755
+ 118,
756
+ 97,
757
+ 117,
758
+ 108,
759
+ 116
760
+ ]
761
+ },
762
+ {
763
+ kind: "account",
764
+ path: "vault.asset_id",
765
+ account: "Vault"
766
+ }
767
+ ]
768
+ }
769
+ },
770
+ {
771
+ name: "vault_premium_account",
772
+ writable: true
773
+ },
774
+ {
775
+ name: "payer_token_account",
776
+ writable: true
777
+ },
778
+ {
779
+ name: "payer",
780
+ writable: true,
781
+ signer: true
782
+ },
783
+ {
784
+ name: "authority",
785
+ docs: [
786
+ "SECURITY FIX M-1: Authority must sign to prevent front-running premium collection"
787
+ ],
788
+ signer: true,
789
+ relations: [
790
+ "vault"
791
+ ]
792
+ },
793
+ {
794
+ name: "token_program",
795
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
796
+ }
797
+ ],
798
+ args: [
799
+ {
800
+ name: "amount",
801
+ type: "u64"
802
+ }
803
+ ]
804
+ },
805
+ {
806
+ name: "create_share_metadata",
807
+ docs: [
808
+ "Create metadata for the share token (vNVDAx, etc.)",
809
+ "Only callable by vault authority since vault PDA is the mint authority"
810
+ ],
811
+ discriminator: [
812
+ 176,
813
+ 243,
814
+ 233,
815
+ 202,
816
+ 218,
817
+ 168,
818
+ 53,
819
+ 158
820
+ ],
821
+ accounts: [
822
+ {
823
+ name: "vault",
824
+ pda: {
825
+ seeds: [
826
+ {
827
+ kind: "const",
828
+ value: [
829
+ 118,
830
+ 97,
831
+ 117,
832
+ 108,
833
+ 116
834
+ ]
835
+ },
836
+ {
837
+ kind: "account",
838
+ path: "vault.asset_id",
839
+ account: "Vault"
840
+ }
841
+ ]
842
+ }
843
+ },
844
+ {
845
+ name: "share_mint",
846
+ relations: [
847
+ "vault"
848
+ ]
849
+ },
850
+ {
851
+ name: "metadata",
852
+ writable: true
853
+ },
854
+ {
855
+ name: "payer",
856
+ writable: true,
857
+ signer: true
858
+ },
859
+ {
860
+ name: "authority",
861
+ signer: true,
862
+ relations: [
863
+ "vault"
864
+ ]
865
+ },
866
+ {
867
+ name: "system_program",
868
+ address: "11111111111111111111111111111111"
869
+ },
870
+ {
871
+ name: "rent",
872
+ address: "SysvarRent111111111111111111111111111111111"
873
+ },
874
+ {
875
+ name: "token_metadata_program",
876
+ address: "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
877
+ }
878
+ ],
879
+ args: [
880
+ {
881
+ name: "name",
882
+ type: "string"
883
+ },
884
+ {
885
+ name: "symbol",
886
+ type: "string"
887
+ },
888
+ {
889
+ name: "uri",
890
+ type: "string"
891
+ }
892
+ ]
893
+ },
894
+ {
895
+ name: "deposit",
896
+ docs: [
897
+ "Deposit underlying tokens and receive vault shares"
898
+ ],
899
+ discriminator: [
900
+ 242,
901
+ 35,
902
+ 198,
903
+ 137,
904
+ 82,
905
+ 225,
906
+ 242,
907
+ 182
908
+ ],
909
+ accounts: [
910
+ {
911
+ name: "vault",
912
+ writable: true,
913
+ pda: {
914
+ seeds: [
915
+ {
916
+ kind: "const",
917
+ value: [
918
+ 118,
919
+ 97,
920
+ 117,
921
+ 108,
922
+ 116
923
+ ]
924
+ },
925
+ {
926
+ kind: "account",
927
+ path: "vault.asset_id",
928
+ account: "Vault"
929
+ }
930
+ ]
931
+ }
932
+ },
933
+ {
934
+ name: "share_mint",
935
+ writable: true
936
+ },
937
+ {
938
+ name: "vault_token_account",
939
+ writable: true
940
+ },
941
+ {
942
+ name: "user_token_account",
943
+ writable: true
944
+ },
945
+ {
946
+ name: "user_share_account",
947
+ writable: true
948
+ },
949
+ {
950
+ name: "share_escrow",
951
+ writable: true
952
+ },
953
+ {
954
+ name: "user",
955
+ writable: true,
956
+ signer: true
957
+ },
958
+ {
959
+ name: "token_program",
960
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
961
+ }
962
+ ],
963
+ args: [
964
+ {
965
+ name: "amount",
966
+ type: "u64"
967
+ }
968
+ ]
969
+ },
970
+ {
971
+ name: "execute_param_change",
972
+ docs: [
973
+ "SECURITY FIX M-3: Execute queued parameter changes after timelock"
974
+ ],
975
+ discriminator: [
976
+ 162,
977
+ 166,
978
+ 56,
979
+ 243,
980
+ 168,
981
+ 135,
982
+ 66,
983
+ 175
984
+ ],
985
+ accounts: [
986
+ {
987
+ name: "vault",
988
+ writable: true,
989
+ pda: {
990
+ seeds: [
991
+ {
992
+ kind: "const",
993
+ value: [
994
+ 118,
995
+ 97,
996
+ 117,
997
+ 108,
998
+ 116
999
+ ]
1000
+ },
1001
+ {
1002
+ kind: "account",
1003
+ path: "vault.asset_id",
1004
+ account: "Vault"
1005
+ }
1006
+ ]
1007
+ }
1008
+ },
1009
+ {
1010
+ name: "authority",
1011
+ signer: true,
1012
+ relations: [
1013
+ "vault"
1014
+ ]
1015
+ }
1016
+ ],
1017
+ args: []
1018
+ },
1019
+ {
1020
+ name: "force_close_vault",
1021
+ docs: [
1022
+ "Force close a vault account (bypasses deserialization)",
1023
+ "USE WITH CAUTION: Only for recovering from incompatible account structures",
1024
+ "SECURITY FIX C-2: Now verifies caller is the stored vault authority"
1025
+ ],
1026
+ discriminator: [
1027
+ 229,
1028
+ 45,
1029
+ 206,
1030
+ 4,
1031
+ 96,
1032
+ 129,
1033
+ 140,
1034
+ 24
1035
+ ],
1036
+ accounts: [
1037
+ {
1038
+ name: "vault",
1039
+ writable: true
1040
+ },
1041
+ {
1042
+ name: "authority",
1043
+ writable: true,
1044
+ signer: true
1045
+ }
1046
+ ],
1047
+ args: [
1048
+ {
1049
+ name: "asset_id",
1050
+ type: "string"
1051
+ }
1052
+ ]
1053
+ },
1054
+ {
1055
+ name: "initialize_vault",
1056
+ docs: [
1057
+ "Initialize a new vault for a specific xStock asset"
1058
+ ],
1059
+ discriminator: [
1060
+ 48,
1061
+ 191,
1062
+ 163,
1063
+ 44,
1064
+ 71,
1065
+ 129,
1066
+ 63,
1067
+ 164
1068
+ ],
1069
+ accounts: [
1070
+ {
1071
+ name: "vault",
1072
+ writable: true,
1073
+ pda: {
1074
+ seeds: [
1075
+ {
1076
+ kind: "const",
1077
+ value: [
1078
+ 118,
1079
+ 97,
1080
+ 117,
1081
+ 108,
1082
+ 116
1083
+ ]
1084
+ },
1085
+ {
1086
+ kind: "arg",
1087
+ path: "asset_id"
1088
+ }
1089
+ ]
1090
+ }
1091
+ },
1092
+ {
1093
+ name: "underlying_mint"
1094
+ },
1095
+ {
1096
+ name: "premium_mint"
1097
+ },
1098
+ {
1099
+ name: "share_mint",
1100
+ writable: true,
1101
+ signer: true
1102
+ },
1103
+ {
1104
+ name: "vault_token_account",
1105
+ writable: true,
1106
+ signer: true
1107
+ },
1108
+ {
1109
+ name: "premium_token_account",
1110
+ writable: true,
1111
+ signer: true
1112
+ },
1113
+ {
1114
+ name: "share_escrow",
1115
+ writable: true,
1116
+ pda: {
1117
+ seeds: [
1118
+ {
1119
+ kind: "const",
1120
+ value: [
1121
+ 115,
1122
+ 104,
1123
+ 97,
1124
+ 114,
1125
+ 101,
1126
+ 95,
1127
+ 101,
1128
+ 115,
1129
+ 99,
1130
+ 114,
1131
+ 111,
1132
+ 119
1133
+ ]
1134
+ },
1135
+ {
1136
+ kind: "account",
1137
+ path: "vault"
1138
+ }
1139
+ ]
1140
+ }
1141
+ },
1142
+ {
1143
+ name: "authority",
1144
+ writable: true,
1145
+ signer: true
1146
+ },
1147
+ {
1148
+ name: "system_program",
1149
+ address: "11111111111111111111111111111111"
1150
+ },
1151
+ {
1152
+ name: "token_program",
1153
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1154
+ },
1155
+ {
1156
+ name: "rent",
1157
+ address: "SysvarRent111111111111111111111111111111111"
1158
+ }
1159
+ ],
1160
+ args: [
1161
+ {
1162
+ name: "asset_id",
1163
+ type: "string"
1164
+ },
1165
+ {
1166
+ name: "utilization_cap_bps",
1167
+ type: "u16"
1168
+ },
1169
+ {
1170
+ name: "min_epoch_duration",
1171
+ type: "i64"
1172
+ }
1173
+ ]
1174
+ },
1175
+ {
1176
+ name: "initialize_whitelist",
1177
+ docs: [
1178
+ "Initialize the market maker whitelist"
1179
+ ],
1180
+ discriminator: [
1181
+ 223,
1182
+ 228,
1183
+ 11,
1184
+ 219,
1185
+ 112,
1186
+ 174,
1187
+ 108,
1188
+ 18
1189
+ ],
1190
+ accounts: [
1191
+ {
1192
+ name: "vault",
1193
+ pda: {
1194
+ seeds: [
1195
+ {
1196
+ kind: "const",
1197
+ value: [
1198
+ 118,
1199
+ 97,
1200
+ 117,
1201
+ 108,
1202
+ 116
1203
+ ]
1204
+ },
1205
+ {
1206
+ kind: "account",
1207
+ path: "vault.asset_id",
1208
+ account: "Vault"
1209
+ }
1210
+ ]
1211
+ }
1212
+ },
1213
+ {
1214
+ name: "whitelist",
1215
+ writable: true,
1216
+ pda: {
1217
+ seeds: [
1218
+ {
1219
+ kind: "const",
1220
+ value: [
1221
+ 119,
1222
+ 104,
1223
+ 105,
1224
+ 116,
1225
+ 101,
1226
+ 108,
1227
+ 105,
1228
+ 115,
1229
+ 116
1230
+ ]
1231
+ },
1232
+ {
1233
+ kind: "account",
1234
+ path: "vault"
1235
+ }
1236
+ ]
1237
+ }
1238
+ },
1239
+ {
1240
+ name: "authority",
1241
+ writable: true,
1242
+ signer: true,
1243
+ relations: [
1244
+ "vault"
1245
+ ]
1246
+ },
1247
+ {
1248
+ name: "system_program",
1249
+ address: "11111111111111111111111111111111"
1250
+ }
1251
+ ],
1252
+ args: []
1253
+ },
1254
+ {
1255
+ name: "pay_settlement",
1256
+ docs: [
1257
+ "Pay out to market maker for ITM settlement",
1258
+ "Only callable by vault authority",
1259
+ "SECURITY: Settlement capped at epoch premium earned"
1260
+ ],
1261
+ discriminator: [
1262
+ 65,
1263
+ 54,
1264
+ 44,
1265
+ 166,
1266
+ 205,
1267
+ 55,
1268
+ 164,
1269
+ 205
1270
+ ],
1271
+ accounts: [
1272
+ {
1273
+ name: "vault",
1274
+ pda: {
1275
+ seeds: [
1276
+ {
1277
+ kind: "const",
1278
+ value: [
1279
+ 118,
1280
+ 97,
1281
+ 117,
1282
+ 108,
1283
+ 116
1284
+ ]
1285
+ },
1286
+ {
1287
+ kind: "account",
1288
+ path: "vault.asset_id",
1289
+ account: "Vault"
1290
+ }
1291
+ ]
1292
+ },
1293
+ relations: [
1294
+ "whitelist"
1295
+ ]
1296
+ },
1297
+ {
1298
+ name: "whitelist",
1299
+ pda: {
1300
+ seeds: [
1301
+ {
1302
+ kind: "const",
1303
+ value: [
1304
+ 119,
1305
+ 104,
1306
+ 105,
1307
+ 116,
1308
+ 101,
1309
+ 108,
1310
+ 105,
1311
+ 115,
1312
+ 116
1313
+ ]
1314
+ },
1315
+ {
1316
+ kind: "account",
1317
+ path: "vault"
1318
+ }
1319
+ ]
1320
+ }
1321
+ },
1322
+ {
1323
+ name: "vault_premium_account",
1324
+ writable: true
1325
+ },
1326
+ {
1327
+ name: "recipient_token_account",
1328
+ writable: true
1329
+ },
1330
+ {
1331
+ name: "recipient"
1332
+ },
1333
+ {
1334
+ name: "authority",
1335
+ signer: true,
1336
+ relations: [
1337
+ "vault"
1338
+ ]
1339
+ },
1340
+ {
1341
+ name: "token_program",
1342
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1343
+ }
1344
+ ],
1345
+ args: [
1346
+ {
1347
+ name: "amount",
1348
+ type: "u64"
1349
+ }
1350
+ ]
1351
+ },
1352
+ {
1353
+ name: "process_withdrawal",
1354
+ docs: [
1355
+ "Process withdrawal after epoch settles",
1356
+ "SECURITY FIX H-3: Added min_expected_amount for slippage protection"
1357
+ ],
1358
+ discriminator: [
1359
+ 51,
1360
+ 97,
1361
+ 236,
1362
+ 17,
1363
+ 37,
1364
+ 33,
1365
+ 196,
1366
+ 64
1367
+ ],
1368
+ accounts: [
1369
+ {
1370
+ name: "vault",
1371
+ writable: true,
1372
+ pda: {
1373
+ seeds: [
1374
+ {
1375
+ kind: "const",
1376
+ value: [
1377
+ 118,
1378
+ 97,
1379
+ 117,
1380
+ 108,
1381
+ 116
1382
+ ]
1383
+ },
1384
+ {
1385
+ kind: "account",
1386
+ path: "vault.asset_id",
1387
+ account: "Vault"
1388
+ }
1389
+ ]
1390
+ },
1391
+ relations: [
1392
+ "withdrawal_request"
1393
+ ]
1394
+ },
1395
+ {
1396
+ name: "withdrawal_request",
1397
+ writable: true
1398
+ },
1399
+ {
1400
+ name: "share_mint",
1401
+ writable: true
1402
+ },
1403
+ {
1404
+ name: "vault_token_account",
1405
+ writable: true
1406
+ },
1407
+ {
1408
+ name: "user_token_account",
1409
+ writable: true
1410
+ },
1411
+ {
1412
+ name: "share_escrow",
1413
+ writable: true
1414
+ },
1415
+ {
1416
+ name: "vault_premium_account",
1417
+ writable: true
1418
+ },
1419
+ {
1420
+ name: "user_premium_account",
1421
+ writable: true,
1422
+ pda: {
1423
+ seeds: [
1424
+ {
1425
+ kind: "account",
1426
+ path: "user"
1427
+ },
1428
+ {
1429
+ kind: "const",
1430
+ value: [
1431
+ 6,
1432
+ 221,
1433
+ 246,
1434
+ 225,
1435
+ 215,
1436
+ 101,
1437
+ 161,
1438
+ 147,
1439
+ 217,
1440
+ 203,
1441
+ 225,
1442
+ 70,
1443
+ 206,
1444
+ 235,
1445
+ 121,
1446
+ 172,
1447
+ 28,
1448
+ 180,
1449
+ 133,
1450
+ 237,
1451
+ 95,
1452
+ 91,
1453
+ 55,
1454
+ 145,
1455
+ 58,
1456
+ 140,
1457
+ 245,
1458
+ 133,
1459
+ 126,
1460
+ 255,
1461
+ 0,
1462
+ 169
1463
+ ]
1464
+ },
1465
+ {
1466
+ kind: "account",
1467
+ path: "premium_mint"
1468
+ }
1469
+ ],
1470
+ program: {
1471
+ kind: "const",
1472
+ value: [
1473
+ 140,
1474
+ 151,
1475
+ 37,
1476
+ 143,
1477
+ 78,
1478
+ 36,
1479
+ 137,
1480
+ 241,
1481
+ 187,
1482
+ 61,
1483
+ 16,
1484
+ 41,
1485
+ 20,
1486
+ 142,
1487
+ 13,
1488
+ 131,
1489
+ 11,
1490
+ 90,
1491
+ 19,
1492
+ 153,
1493
+ 218,
1494
+ 255,
1495
+ 16,
1496
+ 132,
1497
+ 4,
1498
+ 142,
1499
+ 123,
1500
+ 216,
1501
+ 219,
1502
+ 233,
1503
+ 248,
1504
+ 89
1505
+ ]
1506
+ }
1507
+ }
1508
+ },
1509
+ {
1510
+ name: "premium_mint"
1511
+ },
1512
+ {
1513
+ name: "user",
1514
+ writable: true,
1515
+ signer: true,
1516
+ relations: [
1517
+ "withdrawal_request"
1518
+ ]
1519
+ },
1520
+ {
1521
+ name: "token_program",
1522
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1523
+ },
1524
+ {
1525
+ name: "associated_token_program",
1526
+ address: "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"
1527
+ },
1528
+ {
1529
+ name: "system_program",
1530
+ address: "11111111111111111111111111111111"
1531
+ }
1532
+ ],
1533
+ args: [
1534
+ {
1535
+ name: "min_expected_amount",
1536
+ type: "u64"
1537
+ }
1538
+ ]
1539
+ },
1540
+ {
1541
+ name: "queue_param_change",
1542
+ docs: [
1543
+ "SECURITY FIX M-3: Queue a parameter change with timelock",
1544
+ "Changes take effect after TIMELOCK_DURATION (24 hours)"
1545
+ ],
1546
+ discriminator: [
1547
+ 140,
1548
+ 242,
1549
+ 124,
1550
+ 63,
1551
+ 143,
1552
+ 237,
1553
+ 195,
1554
+ 231
1555
+ ],
1556
+ accounts: [
1557
+ {
1558
+ name: "vault",
1559
+ writable: true,
1560
+ pda: {
1561
+ seeds: [
1562
+ {
1563
+ kind: "const",
1564
+ value: [
1565
+ 118,
1566
+ 97,
1567
+ 117,
1568
+ 108,
1569
+ 116
1570
+ ]
1571
+ },
1572
+ {
1573
+ kind: "account",
1574
+ path: "vault.asset_id",
1575
+ account: "Vault"
1576
+ }
1577
+ ]
1578
+ }
1579
+ },
1580
+ {
1581
+ name: "authority",
1582
+ signer: true,
1583
+ relations: [
1584
+ "vault"
1585
+ ]
1586
+ }
1587
+ ],
1588
+ args: [
1589
+ {
1590
+ name: "new_min_epoch_duration",
1591
+ type: {
1592
+ option: "i64"
1593
+ }
1594
+ },
1595
+ {
1596
+ name: "new_utilization_cap_bps",
1597
+ type: {
1598
+ option: "u16"
1599
+ }
1600
+ }
1601
+ ]
1602
+ },
1603
+ {
1604
+ name: "record_notional_exposure",
1605
+ docs: [
1606
+ "Record notional exposure when an RFQ is filled (fractional options)",
1607
+ "Premium is in premium_mint tokens (USDC)"
1608
+ ],
1609
+ discriminator: [
1610
+ 26,
1611
+ 180,
1612
+ 108,
1613
+ 160,
1614
+ 15,
1615
+ 34,
1616
+ 179,
1617
+ 128
1618
+ ],
1619
+ accounts: [
1620
+ {
1621
+ name: "vault",
1622
+ writable: true,
1623
+ pda: {
1624
+ seeds: [
1625
+ {
1626
+ kind: "const",
1627
+ value: [
1628
+ 118,
1629
+ 97,
1630
+ 117,
1631
+ 108,
1632
+ 116
1633
+ ]
1634
+ },
1635
+ {
1636
+ kind: "account",
1637
+ path: "vault.asset_id",
1638
+ account: "Vault"
1639
+ }
1640
+ ]
1641
+ }
1642
+ },
1643
+ {
1644
+ name: "authority",
1645
+ signer: true,
1646
+ relations: [
1647
+ "vault"
1648
+ ]
1649
+ }
1650
+ ],
1651
+ args: [
1652
+ {
1653
+ name: "notional_tokens",
1654
+ type: "u64"
1655
+ },
1656
+ {
1657
+ name: "premium",
1658
+ type: "u64"
1659
+ }
1660
+ ]
1661
+ },
1662
+ {
1663
+ name: "remove_market_maker",
1664
+ docs: [
1665
+ "SECURITY FIX M-4: Remove a market maker from the whitelist"
1666
+ ],
1667
+ discriminator: [
1668
+ 101,
1669
+ 241,
1670
+ 39,
1671
+ 132,
1672
+ 210,
1673
+ 208,
1674
+ 183,
1675
+ 254
1676
+ ],
1677
+ accounts: [
1678
+ {
1679
+ name: "vault",
1680
+ pda: {
1681
+ seeds: [
1682
+ {
1683
+ kind: "const",
1684
+ value: [
1685
+ 118,
1686
+ 97,
1687
+ 117,
1688
+ 108,
1689
+ 116
1690
+ ]
1691
+ },
1692
+ {
1693
+ kind: "account",
1694
+ path: "vault.asset_id",
1695
+ account: "Vault"
1696
+ }
1697
+ ]
1698
+ },
1699
+ relations: [
1700
+ "whitelist"
1701
+ ]
1702
+ },
1703
+ {
1704
+ name: "whitelist",
1705
+ writable: true,
1706
+ pda: {
1707
+ seeds: [
1708
+ {
1709
+ kind: "const",
1710
+ value: [
1711
+ 119,
1712
+ 104,
1713
+ 105,
1714
+ 116,
1715
+ 101,
1716
+ 108,
1717
+ 105,
1718
+ 115,
1719
+ 116
1720
+ ]
1721
+ },
1722
+ {
1723
+ kind: "account",
1724
+ path: "vault"
1725
+ }
1726
+ ]
1727
+ }
1728
+ },
1729
+ {
1730
+ name: "authority",
1731
+ writable: true,
1732
+ signer: true,
1733
+ relations: [
1734
+ "vault",
1735
+ "whitelist"
1736
+ ]
1737
+ }
1738
+ ],
1739
+ args: [
1740
+ {
1741
+ name: "market_maker",
1742
+ type: "pubkey"
1743
+ }
1744
+ ]
1745
+ },
1746
+ {
1747
+ name: "request_withdrawal",
1748
+ docs: [
1749
+ "Request withdrawal (queued until epoch end)"
1750
+ ],
1751
+ discriminator: [
1752
+ 251,
1753
+ 85,
1754
+ 121,
1755
+ 205,
1756
+ 56,
1757
+ 201,
1758
+ 12,
1759
+ 177
1760
+ ],
1761
+ accounts: [
1762
+ {
1763
+ name: "vault",
1764
+ writable: true,
1765
+ pda: {
1766
+ seeds: [
1767
+ {
1768
+ kind: "const",
1769
+ value: [
1770
+ 118,
1771
+ 97,
1772
+ 117,
1773
+ 108,
1774
+ 116
1775
+ ]
1776
+ },
1777
+ {
1778
+ kind: "account",
1779
+ path: "vault.asset_id",
1780
+ account: "Vault"
1781
+ }
1782
+ ]
1783
+ }
1784
+ },
1785
+ {
1786
+ name: "withdrawal_request",
1787
+ writable: true,
1788
+ pda: {
1789
+ seeds: [
1790
+ {
1791
+ kind: "const",
1792
+ value: [
1793
+ 119,
1794
+ 105,
1795
+ 116,
1796
+ 104,
1797
+ 100,
1798
+ 114,
1799
+ 97,
1800
+ 119,
1801
+ 97,
1802
+ 108
1803
+ ]
1804
+ },
1805
+ {
1806
+ kind: "account",
1807
+ path: "vault"
1808
+ },
1809
+ {
1810
+ kind: "account",
1811
+ path: "user"
1812
+ },
1813
+ {
1814
+ kind: "account",
1815
+ path: "vault.epoch",
1816
+ account: "Vault"
1817
+ }
1818
+ ]
1819
+ }
1820
+ },
1821
+ {
1822
+ name: "share_escrow",
1823
+ writable: true
1824
+ },
1825
+ {
1826
+ name: "user_share_account",
1827
+ writable: true
1828
+ },
1829
+ {
1830
+ name: "user",
1831
+ writable: true,
1832
+ signer: true
1833
+ },
1834
+ {
1835
+ name: "system_program",
1836
+ address: "11111111111111111111111111111111"
1837
+ },
1838
+ {
1839
+ name: "token_program",
1840
+ address: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
1841
+ }
1842
+ ],
1843
+ args: [
1844
+ {
1845
+ name: "shares",
1846
+ type: "u64"
1847
+ }
1848
+ ]
1849
+ },
1850
+ {
1851
+ name: "set_min_epoch_duration",
1852
+ docs: [
1853
+ "DEPRECATED: Direct parameter changes now require timelock",
1854
+ "Kept for backwards compatibility but will fail"
1855
+ ],
1856
+ discriminator: [
1857
+ 172,
1858
+ 45,
1859
+ 116,
1860
+ 18,
1861
+ 195,
1862
+ 137,
1863
+ 80,
1864
+ 225
1865
+ ],
1866
+ accounts: [
1867
+ {
1868
+ name: "vault",
1869
+ writable: true,
1870
+ pda: {
1871
+ seeds: [
1872
+ {
1873
+ kind: "const",
1874
+ value: [
1875
+ 118,
1876
+ 97,
1877
+ 117,
1878
+ 108,
1879
+ 116
1880
+ ]
1881
+ },
1882
+ {
1883
+ kind: "account",
1884
+ path: "vault.asset_id",
1885
+ account: "Vault"
1886
+ }
1887
+ ]
1888
+ }
1889
+ },
1890
+ {
1891
+ name: "authority",
1892
+ signer: true,
1893
+ relations: [
1894
+ "vault"
1895
+ ]
1896
+ }
1897
+ ],
1898
+ args: [
1899
+ {
1900
+ name: "_duration",
1901
+ type: "i64"
1902
+ }
1903
+ ]
1904
+ },
1905
+ {
1906
+ name: "set_pause",
1907
+ docs: [
1908
+ "SECURITY: Pause or unpause the vault (emergency control)",
1909
+ "When paused, deposits and withdrawal requests are blocked"
1910
+ ],
1911
+ discriminator: [
1912
+ 63,
1913
+ 32,
1914
+ 154,
1915
+ 2,
1916
+ 56,
1917
+ 103,
1918
+ 79,
1919
+ 45
1920
+ ],
1921
+ accounts: [
1922
+ {
1923
+ name: "vault",
1924
+ writable: true,
1925
+ pda: {
1926
+ seeds: [
1927
+ {
1928
+ kind: "const",
1929
+ value: [
1930
+ 118,
1931
+ 97,
1932
+ 117,
1933
+ 108,
1934
+ 116
1935
+ ]
1936
+ },
1937
+ {
1938
+ kind: "account",
1939
+ path: "vault.asset_id",
1940
+ account: "Vault"
1941
+ }
1942
+ ]
1943
+ }
1944
+ },
1945
+ {
1946
+ name: "authority",
1947
+ signer: true,
1948
+ relations: [
1949
+ "vault"
1950
+ ]
1951
+ }
1952
+ ],
1953
+ args: [
1954
+ {
1955
+ name: "paused",
1956
+ type: "bool"
1957
+ }
1958
+ ]
1959
+ },
1960
+ {
1961
+ name: "set_utilization_cap",
1962
+ docs: [
1963
+ "DEPRECATED: Direct parameter changes now require timelock",
1964
+ "Kept for backwards compatibility but will fail"
1965
+ ],
1966
+ discriminator: [
1967
+ 54,
1968
+ 143,
1969
+ 110,
1970
+ 156,
1971
+ 48,
1972
+ 172,
1973
+ 7,
1974
+ 64
1975
+ ],
1976
+ accounts: [
1977
+ {
1978
+ name: "vault",
1979
+ writable: true,
1980
+ pda: {
1981
+ seeds: [
1982
+ {
1983
+ kind: "const",
1984
+ value: [
1985
+ 118,
1986
+ 97,
1987
+ 117,
1988
+ 108,
1989
+ 116
1990
+ ]
1991
+ },
1992
+ {
1993
+ kind: "account",
1994
+ path: "vault.asset_id",
1995
+ account: "Vault"
1996
+ }
1997
+ ]
1998
+ }
1999
+ },
2000
+ {
2001
+ name: "authority",
2002
+ signer: true,
2003
+ relations: [
2004
+ "vault"
2005
+ ]
2006
+ }
2007
+ ],
2008
+ args: [
2009
+ {
2010
+ name: "_cap_bps",
2011
+ type: "u16"
2012
+ }
2013
+ ]
2014
+ }
2015
+ ],
2016
+ accounts: [
2017
+ {
2018
+ name: "Vault",
2019
+ discriminator: [
2020
+ 211,
2021
+ 8,
2022
+ 232,
2023
+ 43,
2024
+ 2,
2025
+ 152,
2026
+ 117,
2027
+ 119
2028
+ ]
2029
+ },
2030
+ {
2031
+ name: "VaultWhitelist",
2032
+ discriminator: [
2033
+ 82,
2034
+ 19,
2035
+ 233,
2036
+ 96,
2037
+ 174,
2038
+ 0,
2039
+ 57,
2040
+ 17
2041
+ ]
2042
+ },
2043
+ {
2044
+ name: "WithdrawalRequest",
2045
+ discriminator: [
2046
+ 242,
2047
+ 88,
2048
+ 147,
2049
+ 173,
2050
+ 182,
2051
+ 62,
2052
+ 229,
2053
+ 193
2054
+ ]
2055
+ }
2056
+ ],
2057
+ events: [
2058
+ {
2059
+ name: "DepositEvent",
2060
+ discriminator: [
2061
+ 120,
2062
+ 248,
2063
+ 61,
2064
+ 83,
2065
+ 31,
2066
+ 142,
2067
+ 107,
2068
+ 144
2069
+ ]
2070
+ },
2071
+ {
2072
+ name: "EpochAdvancedEvent",
2073
+ discriminator: [
2074
+ 26,
2075
+ 197,
2076
+ 195,
2077
+ 116,
2078
+ 126,
2079
+ 48,
2080
+ 210,
2081
+ 42
2082
+ ]
2083
+ },
2084
+ {
2085
+ name: "MarketMakerRemovedEvent",
2086
+ discriminator: [
2087
+ 238,
2088
+ 48,
2089
+ 105,
2090
+ 84,
2091
+ 228,
2092
+ 245,
2093
+ 204,
2094
+ 75
2095
+ ]
2096
+ },
2097
+ {
2098
+ name: "NotionalExposureEvent",
2099
+ discriminator: [
2100
+ 220,
2101
+ 74,
2102
+ 165,
2103
+ 136,
2104
+ 237,
2105
+ 183,
2106
+ 23,
2107
+ 38
2108
+ ]
2109
+ },
2110
+ {
2111
+ name: "ParamChangeExecutedEvent",
2112
+ discriminator: [
2113
+ 208,
2114
+ 23,
2115
+ 211,
2116
+ 151,
2117
+ 8,
2118
+ 56,
2119
+ 124,
2120
+ 26
2121
+ ]
2122
+ },
2123
+ {
2124
+ name: "ParamChangeQueuedEvent",
2125
+ discriminator: [
2126
+ 44,
2127
+ 143,
2128
+ 34,
2129
+ 222,
2130
+ 153,
2131
+ 125,
2132
+ 73,
2133
+ 23
2134
+ ]
2135
+ },
2136
+ {
2137
+ name: "PremiumCollectedEvent",
2138
+ discriminator: [
2139
+ 76,
2140
+ 52,
2141
+ 166,
2142
+ 111,
2143
+ 182,
2144
+ 211,
2145
+ 215,
2146
+ 144
2147
+ ]
2148
+ },
2149
+ {
2150
+ name: "SettlementPaidEvent",
2151
+ discriminator: [
2152
+ 97,
2153
+ 3,
2154
+ 234,
2155
+ 177,
2156
+ 141,
2157
+ 83,
2158
+ 59,
2159
+ 26
2160
+ ]
2161
+ },
2162
+ {
2163
+ name: "VaultPausedEvent",
2164
+ discriminator: [
2165
+ 75,
2166
+ 189,
2167
+ 120,
2168
+ 167,
2169
+ 117,
2170
+ 229,
2171
+ 155,
2172
+ 60
2173
+ ]
2174
+ },
2175
+ {
2176
+ name: "WithdrawalProcessedEvent",
2177
+ discriminator: [
2178
+ 23,
2179
+ 252,
2180
+ 30,
2181
+ 4,
2182
+ 24,
2183
+ 110,
2184
+ 166,
2185
+ 133
2186
+ ]
2187
+ },
2188
+ {
2189
+ name: "WithdrawalRequestedEvent",
2190
+ discriminator: [
2191
+ 82,
2192
+ 227,
2193
+ 155,
2194
+ 140,
2195
+ 223,
2196
+ 124,
2197
+ 77,
2198
+ 243
2199
+ ]
2200
+ }
2201
+ ],
2202
+ errors: [
2203
+ {
2204
+ code: 6e3,
2205
+ name: "ZeroAmount",
2206
+ msg: "Amount must be greater than zero"
2207
+ },
2208
+ {
2209
+ code: 6001,
2210
+ name: "ZeroShares",
2211
+ msg: "Calculated shares must be greater than zero"
2212
+ },
2213
+ {
2214
+ code: 6002,
2215
+ name: "InsufficientShares",
2216
+ msg: "Insufficient shares"
2217
+ },
2218
+ {
2219
+ code: 6003,
2220
+ name: "AlreadyProcessed",
2221
+ msg: "Withdrawal already processed"
2222
+ },
2223
+ {
2224
+ code: 6004,
2225
+ name: "EpochNotSettled",
2226
+ msg: "Epoch has not settled yet"
2227
+ },
2228
+ {
2229
+ code: 6005,
2230
+ name: "Overflow",
2231
+ msg: "Arithmetic overflow"
2232
+ },
2233
+ {
2234
+ code: 6006,
2235
+ name: "ExceedsUtilizationCap",
2236
+ msg: "Exceeds utilization cap"
2237
+ },
2238
+ {
2239
+ code: 6007,
2240
+ name: "NotWhitelisted",
2241
+ msg: "Recipient is not whitelisted"
2242
+ },
2243
+ {
2244
+ code: 6008,
2245
+ name: "WhitelistFull",
2246
+ msg: "Whitelist is full"
2247
+ },
2248
+ {
2249
+ code: 6009,
2250
+ name: "AlreadyWhitelisted",
2251
+ msg: "Market maker already whitelisted"
2252
+ },
2253
+ {
2254
+ code: 6010,
2255
+ name: "InsufficientLiquidity",
2256
+ msg: "Insufficient liquidity for first deposit"
2257
+ },
2258
+ {
2259
+ code: 6011,
2260
+ name: "EpochTooShort",
2261
+ msg: "Epoch duration too short"
2262
+ },
2263
+ {
2264
+ code: 6012,
2265
+ name: "ExcessivePremium",
2266
+ msg: "Premium exceeds safety limits"
2267
+ },
2268
+ {
2269
+ code: 6013,
2270
+ name: "ExcessiveYield",
2271
+ msg: "Implied yield too high"
2272
+ },
2273
+ {
2274
+ code: 6014,
2275
+ name: "ExcessiveSettlement",
2276
+ msg: "Settlement exceeds epoch premium earned"
2277
+ },
2278
+ {
2279
+ code: 6015,
2280
+ name: "InsufficientVaultBalance",
2281
+ msg: "Insufficient vault balance for withdrawal"
2282
+ },
2283
+ {
2284
+ code: 6016,
2285
+ name: "VaultPaused",
2286
+ msg: "Vault is paused"
2287
+ },
2288
+ {
2289
+ code: 6017,
2290
+ name: "VaultNotEmpty",
2291
+ msg: "Vault must be empty to close"
2292
+ },
2293
+ {
2294
+ code: 6018,
2295
+ name: "InvalidVaultPda",
2296
+ msg: "Invalid vault PDA"
2297
+ },
2298
+ {
2299
+ code: 6019,
2300
+ name: "SlippageExceeded",
2301
+ msg: "Withdrawal amount below minimum expected (slippage protection)"
2302
+ },
2303
+ {
2304
+ code: 6020,
2305
+ name: "DivisionByZero",
2306
+ msg: "Division by zero"
2307
+ },
2308
+ {
2309
+ code: 6021,
2310
+ name: "TimelockNotExpired",
2311
+ msg: "Timelock has not expired yet"
2312
+ },
2313
+ {
2314
+ code: 6022,
2315
+ name: "UseTimelockForParamChange",
2316
+ msg: "Direct param changes deprecated - use queue_param_change"
2317
+ },
2318
+ {
2319
+ code: 6023,
2320
+ name: "InvalidParameter",
2321
+ msg: "Invalid parameter value"
2322
+ },
2323
+ {
2324
+ code: 6024,
2325
+ name: "UnauthorizedForceClose",
2326
+ msg: "Caller is not the vault authority"
2327
+ },
2328
+ {
2329
+ code: 6025,
2330
+ name: "VaultStillExists",
2331
+ msg: "Vault still exists - close it first before cleaning up token accounts"
2332
+ }
2333
+ ],
2334
+ types: [
2335
+ {
2336
+ name: "DepositEvent",
2337
+ type: {
2338
+ kind: "struct",
2339
+ fields: [
2340
+ {
2341
+ name: "vault",
2342
+ type: "pubkey"
2343
+ },
2344
+ {
2345
+ name: "user",
2346
+ type: "pubkey"
2347
+ },
2348
+ {
2349
+ name: "amount",
2350
+ type: "u64"
2351
+ },
2352
+ {
2353
+ name: "shares_minted",
2354
+ type: "u64"
2355
+ },
2356
+ {
2357
+ name: "epoch",
2358
+ type: "u64"
2359
+ }
2360
+ ]
2361
+ }
2362
+ },
2363
+ {
2364
+ name: "EpochAdvancedEvent",
2365
+ type: {
2366
+ kind: "struct",
2367
+ fields: [
2368
+ {
2369
+ name: "vault",
2370
+ type: "pubkey"
2371
+ },
2372
+ {
2373
+ name: "new_epoch",
2374
+ type: "u64"
2375
+ },
2376
+ {
2377
+ name: "premium_earned",
2378
+ type: "u64"
2379
+ },
2380
+ {
2381
+ name: "notional_exposed",
2382
+ type: "u64"
2383
+ },
2384
+ {
2385
+ name: "avg_premium_bps",
2386
+ type: "u32"
2387
+ },
2388
+ {
2389
+ name: "total_assets",
2390
+ type: "u64"
2391
+ },
2392
+ {
2393
+ name: "total_shares",
2394
+ type: "u64"
2395
+ },
2396
+ {
2397
+ name: "premium_balance_usdc",
2398
+ type: "u64"
2399
+ }
2400
+ ]
2401
+ }
2402
+ },
2403
+ {
2404
+ name: "MarketMakerRemovedEvent",
2405
+ docs: [
2406
+ "SECURITY FIX M-4: Event emitted when a market maker is removed"
2407
+ ],
2408
+ type: {
2409
+ kind: "struct",
2410
+ fields: [
2411
+ {
2412
+ name: "vault",
2413
+ type: "pubkey"
2414
+ },
2415
+ {
2416
+ name: "market_maker",
2417
+ type: "pubkey"
2418
+ }
2419
+ ]
2420
+ }
2421
+ },
2422
+ {
2423
+ name: "NotionalExposureEvent",
2424
+ type: {
2425
+ kind: "struct",
2426
+ fields: [
2427
+ {
2428
+ name: "vault",
2429
+ type: "pubkey"
2430
+ },
2431
+ {
2432
+ name: "epoch",
2433
+ type: "u64"
2434
+ },
2435
+ {
2436
+ name: "notional_tokens",
2437
+ type: "u64"
2438
+ },
2439
+ {
2440
+ name: "premium",
2441
+ type: "u64"
2442
+ },
2443
+ {
2444
+ name: "total_notional_this_epoch",
2445
+ type: "u64"
2446
+ },
2447
+ {
2448
+ name: "total_premium_this_epoch",
2449
+ type: "u64"
2450
+ },
2451
+ {
2452
+ name: "avg_premium_bps",
2453
+ type: "u32"
2454
+ }
2455
+ ]
2456
+ }
2457
+ },
2458
+ {
2459
+ name: "ParamChangeExecutedEvent",
2460
+ type: {
2461
+ kind: "struct",
2462
+ fields: [
2463
+ {
2464
+ name: "vault",
2465
+ type: "pubkey"
2466
+ },
2467
+ {
2468
+ name: "new_min_epoch_duration",
2469
+ type: "i64"
2470
+ },
2471
+ {
2472
+ name: "new_utilization_cap_bps",
2473
+ type: "u16"
2474
+ }
2475
+ ]
2476
+ }
2477
+ },
2478
+ {
2479
+ name: "ParamChangeQueuedEvent",
2480
+ type: {
2481
+ kind: "struct",
2482
+ fields: [
2483
+ {
2484
+ name: "vault",
2485
+ type: "pubkey"
2486
+ },
2487
+ {
2488
+ name: "new_min_epoch_duration",
2489
+ type: {
2490
+ option: "i64"
2491
+ }
2492
+ },
2493
+ {
2494
+ name: "new_utilization_cap_bps",
2495
+ type: {
2496
+ option: "u16"
2497
+ }
2498
+ },
2499
+ {
2500
+ name: "unlock_time",
2501
+ type: "i64"
2502
+ }
2503
+ ]
2504
+ }
2505
+ },
2506
+ {
2507
+ name: "PremiumCollectedEvent",
2508
+ type: {
2509
+ kind: "struct",
2510
+ fields: [
2511
+ {
2512
+ name: "vault",
2513
+ type: "pubkey"
2514
+ },
2515
+ {
2516
+ name: "payer",
2517
+ type: "pubkey"
2518
+ },
2519
+ {
2520
+ name: "amount",
2521
+ type: "u64"
2522
+ },
2523
+ {
2524
+ name: "epoch",
2525
+ type: "u64"
2526
+ }
2527
+ ]
2528
+ }
2529
+ },
2530
+ {
2531
+ name: "SettlementPaidEvent",
2532
+ type: {
2533
+ kind: "struct",
2534
+ fields: [
2535
+ {
2536
+ name: "vault",
2537
+ type: "pubkey"
2538
+ },
2539
+ {
2540
+ name: "recipient",
2541
+ type: "pubkey"
2542
+ },
2543
+ {
2544
+ name: "amount",
2545
+ type: "u64"
2546
+ },
2547
+ {
2548
+ name: "epoch",
2549
+ type: "u64"
2550
+ }
2551
+ ]
2552
+ }
2553
+ },
2554
+ {
2555
+ name: "Vault",
2556
+ type: {
2557
+ kind: "struct",
2558
+ fields: [
2559
+ {
2560
+ name: "authority",
2561
+ type: "pubkey"
2562
+ },
2563
+ {
2564
+ name: "asset_id",
2565
+ type: "string"
2566
+ },
2567
+ {
2568
+ name: "underlying_mint",
2569
+ type: "pubkey"
2570
+ },
2571
+ {
2572
+ name: "share_mint",
2573
+ type: "pubkey"
2574
+ },
2575
+ {
2576
+ name: "vault_token_account",
2577
+ type: "pubkey"
2578
+ },
2579
+ {
2580
+ name: "premium_mint",
2581
+ type: "pubkey"
2582
+ },
2583
+ {
2584
+ name: "premium_token_account",
2585
+ type: "pubkey"
2586
+ },
2587
+ {
2588
+ name: "share_escrow",
2589
+ type: "pubkey"
2590
+ },
2591
+ {
2592
+ name: "total_assets",
2593
+ type: "u64"
2594
+ },
2595
+ {
2596
+ name: "total_shares",
2597
+ type: "u64"
2598
+ },
2599
+ {
2600
+ name: "virtual_offset",
2601
+ docs: [
2602
+ "Virtual offset for share calculations (prevents first-depositor attacks)",
2603
+ "effective_shares = total_shares + virtual_offset"
2604
+ ],
2605
+ type: "u64"
2606
+ },
2607
+ {
2608
+ name: "epoch",
2609
+ type: "u64"
2610
+ },
2611
+ {
2612
+ name: "utilization_cap_bps",
2613
+ type: "u16"
2614
+ },
2615
+ {
2616
+ name: "min_epoch_duration",
2617
+ type: "i64"
2618
+ },
2619
+ {
2620
+ name: "last_roll_timestamp",
2621
+ type: "i64"
2622
+ },
2623
+ {
2624
+ name: "pending_withdrawals",
2625
+ type: "u64"
2626
+ },
2627
+ {
2628
+ name: "epoch_notional_exposed",
2629
+ type: "u64"
2630
+ },
2631
+ {
2632
+ name: "epoch_premium_earned",
2633
+ type: "u64"
2634
+ },
2635
+ {
2636
+ name: "epoch_premium_per_token_bps",
2637
+ type: "u32"
2638
+ },
2639
+ {
2640
+ name: "premium_balance_usdc",
2641
+ docs: [
2642
+ "Accumulated USDC premium balance (separate from underlying TVL)"
2643
+ ],
2644
+ type: "u64"
2645
+ },
2646
+ {
2647
+ name: "is_paused",
2648
+ docs: [
2649
+ "SECURITY: Emergency pause flag"
2650
+ ],
2651
+ type: "bool"
2652
+ },
2653
+ {
2654
+ name: "pending_min_epoch_duration",
2655
+ docs: [
2656
+ "SECURITY FIX M-3: Timelock for parameter changes"
2657
+ ],
2658
+ type: "i64"
2659
+ },
2660
+ {
2661
+ name: "pending_utilization_cap",
2662
+ type: "u16"
2663
+ },
2664
+ {
2665
+ name: "param_change_unlock_time",
2666
+ type: "i64"
2667
+ },
2668
+ {
2669
+ name: "bump",
2670
+ type: "u8"
2671
+ }
2672
+ ]
2673
+ }
2674
+ },
2675
+ {
2676
+ name: "VaultPausedEvent",
2677
+ type: {
2678
+ kind: "struct",
2679
+ fields: [
2680
+ {
2681
+ name: "vault",
2682
+ type: "pubkey"
2683
+ },
2684
+ {
2685
+ name: "paused",
2686
+ type: "bool"
2687
+ },
2688
+ {
2689
+ name: "timestamp",
2690
+ type: "i64"
2691
+ }
2692
+ ]
2693
+ }
2694
+ },
2695
+ {
2696
+ name: "VaultWhitelist",
2697
+ type: {
2698
+ kind: "struct",
2699
+ fields: [
2700
+ {
2701
+ name: "authority",
2702
+ type: "pubkey"
2703
+ },
2704
+ {
2705
+ name: "vault",
2706
+ type: "pubkey"
2707
+ },
2708
+ {
2709
+ name: "market_makers",
2710
+ type: {
2711
+ vec: "pubkey"
2712
+ }
2713
+ },
2714
+ {
2715
+ name: "bump",
2716
+ type: "u8"
2717
+ }
2718
+ ]
2719
+ }
2720
+ },
2721
+ {
2722
+ name: "WithdrawalProcessedEvent",
2723
+ type: {
2724
+ kind: "struct",
2725
+ fields: [
2726
+ {
2727
+ name: "vault",
2728
+ type: "pubkey"
2729
+ },
2730
+ {
2731
+ name: "user",
2732
+ type: "pubkey"
2733
+ },
2734
+ {
2735
+ name: "shares",
2736
+ type: "u64"
2737
+ },
2738
+ {
2739
+ name: "amount",
2740
+ type: "u64"
2741
+ },
2742
+ {
2743
+ name: "epoch",
2744
+ type: "u64"
2745
+ }
2746
+ ]
2747
+ }
2748
+ },
2749
+ {
2750
+ name: "WithdrawalRequest",
2751
+ type: {
2752
+ kind: "struct",
2753
+ fields: [
2754
+ {
2755
+ name: "user",
2756
+ type: "pubkey"
2757
+ },
2758
+ {
2759
+ name: "vault",
2760
+ type: "pubkey"
2761
+ },
2762
+ {
2763
+ name: "shares",
2764
+ type: "u64"
2765
+ },
2766
+ {
2767
+ name: "request_epoch",
2768
+ type: "u64"
2769
+ },
2770
+ {
2771
+ name: "processed",
2772
+ type: "bool"
2773
+ }
2774
+ ]
2775
+ }
2776
+ },
2777
+ {
2778
+ name: "WithdrawalRequestedEvent",
2779
+ type: {
2780
+ kind: "struct",
2781
+ fields: [
2782
+ {
2783
+ name: "vault",
2784
+ type: "pubkey"
2785
+ },
2786
+ {
2787
+ name: "user",
2788
+ type: "pubkey"
2789
+ },
2790
+ {
2791
+ name: "shares",
2792
+ type: "u64"
2793
+ },
2794
+ {
2795
+ name: "epoch",
2796
+ type: "u64"
2797
+ }
2798
+ ]
2799
+ }
2800
+ }
2801
+ ]
2802
+ };
2803
+
2804
+ // src/client/VaultInstructions.ts
2805
+ var TOKEN_PROGRAM_ID = new PublicKey3("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
2806
+ var VaultInstructions = class {
2807
+ connection;
2808
+ program = null;
2809
+ idl;
2810
+ /**
2811
+ * Create a new VaultInstructions client
2812
+ *
2813
+ * @param connection - Solana connection
2814
+ * @param idl - Optional IDL (uses bundled vault IDL by default)
2815
+ */
2816
+ constructor(connection, idl) {
2817
+ this.connection = connection;
2818
+ this.idl = idl || vault_default;
2819
+ }
2820
+ /**
2821
+ * Initialize the Anchor program interface
2822
+ *
2823
+ * @param wallet - Optional wallet for signing (not required for instruction building)
2824
+ */
2825
+ async initialize(wallet) {
2826
+ const provider = new anchor.AnchorProvider(
2827
+ this.connection,
2828
+ wallet || {},
2829
+ { commitment: "confirmed" }
2830
+ );
2831
+ this.program = new anchor.Program(this.idl, VAULT_PROGRAM_ID, provider);
2832
+ }
2833
+ /**
2834
+ * Set the program IDL
2835
+ *
2836
+ * @param idl - Vault program IDL
2837
+ */
2838
+ setIDL(idl) {
2839
+ this.idl = idl;
2840
+ }
2841
+ /**
2842
+ * Check if the client is initialized
2843
+ */
2844
+ get isInitialized() {
2845
+ return this.program !== null;
2846
+ }
2847
+ /**
2848
+ * Fetch vault account data
2849
+ *
2850
+ * @param assetId - Asset identifier (e.g., "NVDAX")
2851
+ * @returns Vault data or null if not found
2852
+ */
2853
+ async fetchVault(assetId) {
2854
+ if (!this.program) {
2855
+ throw new Error("Client not initialized. Call initialize() first.");
2856
+ }
2857
+ try {
2858
+ const [vaultPda] = deriveVaultPda(assetId);
2859
+ const vault = await this.program.account.vault.fetch(vaultPda);
2860
+ return {
2861
+ authority: vault.authority,
2862
+ assetId: vault.assetId,
2863
+ underlyingMint: vault.underlyingMint,
2864
+ shareMint: vault.shareMint,
2865
+ vaultTokenAccount: vault.vaultTokenAccount,
2866
+ premiumMint: vault.premiumMint,
2867
+ premiumTokenAccount: vault.premiumTokenAccount,
2868
+ shareEscrow: vault.shareEscrow,
2869
+ totalAssets: BigInt(vault.totalAssets.toString()),
2870
+ totalShares: BigInt(vault.totalShares.toString()),
2871
+ virtualOffset: BigInt(vault.virtualOffset.toString()),
2872
+ epoch: BigInt(vault.epoch.toString()),
2873
+ utilizationCapBps: vault.utilizationCapBps,
2874
+ minEpochDuration: BigInt(vault.minEpochDuration.toString()),
2875
+ lastRollTimestamp: BigInt(vault.lastRollTimestamp.toString()),
2876
+ pendingWithdrawals: BigInt(vault.pendingWithdrawals.toString()),
2877
+ epochNotionalExposed: BigInt(vault.epochNotionalExposed.toString()),
2878
+ epochPremiumEarned: BigInt(vault.epochPremiumEarned.toString()),
2879
+ epochPremiumPerTokenBps: vault.epochPremiumPerTokenBps,
2880
+ isPaused: vault.isPaused,
2881
+ bump: vault.bump
2882
+ };
2883
+ } catch (error) {
2884
+ if (error.message?.includes("Account does not exist")) {
2885
+ return null;
2886
+ }
2887
+ throw error;
2888
+ }
2889
+ }
2890
+ /**
2891
+ * Build instruction to record notional exposure after RFQ execution
2892
+ *
2893
+ * @param params - Exposure parameters
2894
+ * @returns TransactionInstruction
2895
+ */
2896
+ async recordNotionalExposure(params) {
2897
+ if (!this.program) {
2898
+ throw new Error("Client not initialized. Call initialize() first.");
2899
+ }
2900
+ return await this.program.methods.recordNotionalExposure(
2901
+ new anchor.BN(params.notionalTokens.toString()),
2902
+ new anchor.BN(params.premium.toString())
2903
+ ).accounts({
2904
+ vault: params.vault,
2905
+ authority: params.authority
2906
+ }).instruction();
2907
+ }
2908
+ /**
2909
+ * Build instruction to collect premium from market maker
2910
+ *
2911
+ * @param params - Collection parameters
2912
+ * @returns TransactionInstruction
2913
+ */
2914
+ async collectPremium(params) {
2915
+ if (!this.program) {
2916
+ throw new Error("Client not initialized. Call initialize() first.");
2917
+ }
2918
+ const vault = await this.program.account.vault.fetch(params.vault);
2919
+ return await this.program.methods.collectPremium(new anchor.BN(params.amount.toString())).accounts({
2920
+ vault: params.vault,
2921
+ vaultPremiumAccount: vault.premiumTokenAccount,
2922
+ payerTokenAccount: params.payerTokenAccount,
2923
+ payer: params.authority,
2924
+ // Payer is typically the authority
2925
+ authority: params.authority,
2926
+ tokenProgram: TOKEN_PROGRAM_ID
2927
+ }).instruction();
2928
+ }
2929
+ /**
2930
+ * Build instruction to pay settlement to market maker
2931
+ *
2932
+ * @param params - Settlement parameters
2933
+ * @returns TransactionInstruction
2934
+ */
2935
+ async paySettlement(params) {
2936
+ if (!this.program) {
2937
+ throw new Error("Client not initialized. Call initialize() first.");
2938
+ }
2939
+ const vault = await this.program.account.vault.fetch(params.vault);
2940
+ return await this.program.methods.paySettlement(new anchor.BN(params.amount.toString())).accounts({
2941
+ vault: params.vault,
2942
+ vaultPremiumAccount: vault.premiumTokenAccount,
2943
+ recipientTokenAccount: params.recipientTokenAccount,
2944
+ recipient: params.recipient,
2945
+ authority: params.authority,
2946
+ tokenProgram: TOKEN_PROGRAM_ID
2947
+ }).instruction();
2948
+ }
2949
+ /**
2950
+ * Build instruction to advance epoch
2951
+ *
2952
+ * @param params - Advance epoch parameters
2953
+ * @returns TransactionInstruction
2954
+ */
2955
+ async advanceEpoch(params) {
2956
+ if (!this.program) {
2957
+ throw new Error("Client not initialized. Call initialize() first.");
2958
+ }
2959
+ return await this.program.methods.advanceEpoch(new anchor.BN(params.premiumEarned.toString())).accounts({
2960
+ vault: params.vault,
2961
+ authority: params.authority
2962
+ }).instruction();
2963
+ }
2964
+ /**
2965
+ * Calculate share price from vault data
2966
+ *
2967
+ * @param vault - Vault data
2968
+ * @param decimals - Token decimals (default: 6)
2969
+ * @returns Share price as a number
2970
+ */
2971
+ static calculateSharePrice(vault, decimals = 6) {
2972
+ if (vault.totalShares === 0n) {
2973
+ return 1;
2974
+ }
2975
+ const totalAssets = Number(vault.totalAssets) / 10 ** decimals;
2976
+ const totalShares = Number(vault.totalShares) / 10 ** decimals;
2977
+ const virtualOffset = Number(vault.virtualOffset) / 10 ** decimals;
2978
+ return (totalAssets + virtualOffset) / totalShares;
2979
+ }
2980
+ /**
2981
+ * Calculate utilization percentage from vault data
2982
+ *
2983
+ * @param vault - Vault data
2984
+ * @returns Utilization as decimal (0-1)
2985
+ */
2986
+ static calculateUtilization(vault) {
2987
+ if (vault.totalAssets === 0n) {
2988
+ return 0;
2989
+ }
2990
+ return Number(vault.epochNotionalExposed) / Number(vault.totalAssets);
2991
+ }
2992
+ /**
2993
+ * Check if vault can accept more exposure
2994
+ *
2995
+ * @param vault - Vault data
2996
+ * @param additionalNotional - Additional notional to expose
2997
+ * @returns Whether the vault can accept the exposure
2998
+ */
2999
+ static canAcceptExposure(vault, additionalNotional) {
3000
+ const maxExposure = vault.totalAssets * BigInt(vault.utilizationCapBps) / 10000n;
3001
+ const newExposure = vault.epochNotionalExposed + additionalNotional;
3002
+ return newExposure <= maxExposure;
3003
+ }
3004
+ /**
3005
+ * Calculate remaining capacity for option writing
3006
+ *
3007
+ * @param vault - Vault data
3008
+ * @returns Remaining notional capacity
3009
+ */
3010
+ static getRemainingCapacity(vault) {
3011
+ const maxExposure = vault.totalAssets * BigInt(vault.utilizationCapBps) / 10000n;
3012
+ const remaining = maxExposure - vault.epochNotionalExposed;
3013
+ return remaining > 0n ? remaining : 0n;
3014
+ }
3015
+ };
3016
+
3017
+ // src/utils/pricing.ts
3018
+ var OptionPricing = class {
3019
+ /**
3020
+ * Calculate option prices using Black-Scholes formula
3021
+ *
3022
+ * @param params - Pricing parameters
3023
+ * @returns Call and put prices with deltas
3024
+ */
3025
+ static blackScholes(params) {
3026
+ const { spot, strike, timeToExpiry, riskFreeRate, volatility } = params;
3027
+ if (timeToExpiry <= 0) {
3028
+ const intrinsicCall = Math.max(0, spot - strike);
3029
+ const intrinsicPut = Math.max(0, strike - spot);
3030
+ return {
3031
+ call: intrinsicCall,
3032
+ put: intrinsicPut,
3033
+ delta: {
3034
+ call: intrinsicCall > 0 ? 1 : 0,
3035
+ put: intrinsicPut > 0 ? -1 : 0
3036
+ }
3037
+ };
3038
+ }
3039
+ if (volatility <= 0) {
3040
+ throw new Error("Volatility must be positive");
3041
+ }
3042
+ const sqrtT = Math.sqrt(timeToExpiry);
3043
+ const d1 = (Math.log(spot / strike) + (riskFreeRate + 0.5 * volatility * volatility) * timeToExpiry) / (volatility * sqrtT);
3044
+ const d2 = d1 - volatility * sqrtT;
3045
+ const callPrice = spot * this.normalCDF(d1) - strike * Math.exp(-riskFreeRate * timeToExpiry) * this.normalCDF(d2);
3046
+ const putPrice = strike * Math.exp(-riskFreeRate * timeToExpiry) * this.normalCDF(-d2) - spot * this.normalCDF(-d1);
3047
+ const callDelta = this.normalCDF(d1);
3048
+ const putDelta = callDelta - 1;
3049
+ return {
3050
+ call: Math.max(0, callPrice),
3051
+ put: Math.max(0, putPrice),
3052
+ delta: { call: callDelta, put: putDelta }
3053
+ };
3054
+ }
3055
+ /**
3056
+ * Calculate covered call premium
3057
+ *
3058
+ * @param params - Premium calculation parameters
3059
+ * @returns Premium in same units as spot price
3060
+ */
3061
+ static calculateCoveredCallPremium(params) {
3062
+ const {
3063
+ spot,
3064
+ strikePercent,
3065
+ daysToExpiry,
3066
+ volatility,
3067
+ riskFreeRate = DEFAULT_PRICING_PARAMS.riskFreeRate
3068
+ } = params;
3069
+ const strike = spot * strikePercent;
3070
+ const timeToExpiry = daysToExpiry / 365;
3071
+ const prices = this.blackScholes({
3072
+ spot,
3073
+ strike,
3074
+ timeToExpiry,
3075
+ riskFreeRate,
3076
+ volatility
3077
+ });
3078
+ return prices.call;
3079
+ }
3080
+ /**
3081
+ * Calculate premium as basis points of notional
3082
+ *
3083
+ * @param premium - Premium amount
3084
+ * @param spot - Spot price
3085
+ * @returns Premium in basis points
3086
+ */
3087
+ static premiumToBps(premium, spot) {
3088
+ return Math.round(premium / spot * 1e4);
3089
+ }
3090
+ /**
3091
+ * Convert basis points to premium
3092
+ *
3093
+ * @param bps - Basis points
3094
+ * @param spot - Spot price
3095
+ * @returns Premium amount
3096
+ */
3097
+ static bpsToPremium(bps, spot) {
3098
+ return bps / 1e4 * spot;
3099
+ }
3100
+ /**
3101
+ * Calculate time to expiry in years
3102
+ *
3103
+ * @param expiryTimestamp - Expiry Unix timestamp (seconds)
3104
+ * @returns Time in years
3105
+ */
3106
+ static timeToExpiry(expiryTimestamp) {
3107
+ const now = Date.now() / 1e3;
3108
+ const secondsToExpiry = expiryTimestamp - now;
3109
+ return secondsToExpiry / (365.25 * 24 * 60 * 60);
3110
+ }
3111
+ /**
3112
+ * Suggest a strike price based on delta target
3113
+ *
3114
+ * @param spotPrice - Current spot price
3115
+ * @param deltaBps - Delta in basis points (e.g., 1000 = 10% OTM)
3116
+ * @param optionType - 'call' or 'put'
3117
+ * @returns Suggested strike price
3118
+ */
3119
+ static suggestStrike(spotPrice, deltaBps, optionType = "call") {
3120
+ const multiplier = optionType === "call" ? 1 : -1;
3121
+ return spotPrice * (1 + multiplier * deltaBps / 1e4);
3122
+ }
3123
+ /**
3124
+ * Validate a quote against fair value
3125
+ *
3126
+ * @param quotePremium - Premium from quote
3127
+ * @param fairValue - Calculated fair value
3128
+ * @param maxDeviationBps - Maximum acceptable deviation in bps (default: 500)
3129
+ * @returns Validation result
3130
+ */
3131
+ static validateQuote(quotePremium, fairValue, maxDeviationBps = DEFAULT_PRICING_PARAMS.maxQuoteDeviationBps) {
3132
+ if (fairValue <= 0) {
3133
+ return {
3134
+ isValid: false,
3135
+ reason: "Fair value must be positive",
3136
+ fairValue
3137
+ };
3138
+ }
3139
+ const deviation = Math.abs(quotePremium - fairValue) / fairValue;
3140
+ const deviationBps = Math.round(deviation * 1e4);
3141
+ const maxDeviation = maxDeviationBps / 1e4;
3142
+ if (deviation > maxDeviation) {
3143
+ return {
3144
+ isValid: false,
3145
+ reason: `Quote deviates ${deviationBps} bps from fair value (max: ${maxDeviationBps} bps)`,
3146
+ fairValue,
3147
+ deviationBps
3148
+ };
3149
+ }
3150
+ return {
3151
+ isValid: true,
3152
+ fairValue,
3153
+ deviationBps
3154
+ };
3155
+ }
3156
+ /**
3157
+ * Calculate historical volatility from price data
3158
+ *
3159
+ * @param prices - Array of closing prices (oldest first)
3160
+ * @param tradingDaysPerYear - Trading days for annualization (default: 252)
3161
+ * @returns Annualized volatility
3162
+ */
3163
+ static calculateHistoricalVolatility(prices, tradingDaysPerYear = DEFAULT_PRICING_PARAMS.tradingDaysPerYear) {
3164
+ if (prices.length < 2) {
3165
+ throw new Error("Need at least 2 prices to calculate volatility");
3166
+ }
3167
+ const logReturns = [];
3168
+ for (let i = 1; i < prices.length; i++) {
3169
+ const logReturn = Math.log(prices[i] / prices[i - 1]);
3170
+ logReturns.push(logReturn);
3171
+ }
3172
+ const mean = logReturns.reduce((sum, r) => sum + r, 0) / logReturns.length;
3173
+ const variance = logReturns.reduce((sum, r) => sum + Math.pow(r - mean, 2), 0) / (logReturns.length - 1);
3174
+ const dailyStdDev = Math.sqrt(variance);
3175
+ return dailyStdDev * Math.sqrt(tradingDaysPerYear);
3176
+ }
3177
+ /**
3178
+ * Calculate implied volatility from option price using Newton-Raphson
3179
+ *
3180
+ * @param optionPrice - Market price of option
3181
+ * @param spot - Spot price
3182
+ * @param strike - Strike price
3183
+ * @param timeToExpiry - Time to expiry in years
3184
+ * @param riskFreeRate - Risk-free rate
3185
+ * @param optionType - 'call' or 'put'
3186
+ * @param maxIterations - Maximum iterations (default: 100)
3187
+ * @returns Implied volatility
3188
+ */
3189
+ static calculateImpliedVolatility(optionPrice, spot, strike, timeToExpiry, riskFreeRate, optionType, maxIterations = 100) {
3190
+ let sigma = Math.sqrt(2 * Math.PI / timeToExpiry) * (optionPrice / spot);
3191
+ const tolerance = 1e-6;
3192
+ for (let i = 0; i < maxIterations; i++) {
3193
+ const prices = this.blackScholes({
3194
+ spot,
3195
+ strike,
3196
+ timeToExpiry,
3197
+ riskFreeRate,
3198
+ volatility: sigma
3199
+ });
3200
+ const price = optionType === "call" ? prices.call : prices.put;
3201
+ const diff = price - optionPrice;
3202
+ if (Math.abs(diff) < tolerance) {
3203
+ return sigma;
3204
+ }
3205
+ const sqrtT = Math.sqrt(timeToExpiry);
3206
+ const d1 = (Math.log(spot / strike) + (riskFreeRate + 0.5 * sigma * sigma) * timeToExpiry) / (sigma * sqrtT);
3207
+ const vega = spot * sqrtT * this.normalPDF(d1);
3208
+ if (vega < 1e-10) {
3209
+ throw new Error("Implied volatility calculation failed: vega too small");
3210
+ }
3211
+ sigma = sigma - diff / vega;
3212
+ sigma = Math.max(0.01, Math.min(5, sigma));
3213
+ }
3214
+ throw new Error("Implied volatility calculation did not converge");
3215
+ }
3216
+ // ============================================================================
3217
+ // Private Helper Functions
3218
+ // ============================================================================
3219
+ /**
3220
+ * Standard normal CDF using Abramowitz & Stegun approximation
3221
+ */
3222
+ static normalCDF(x) {
3223
+ const a1 = 0.254829592;
3224
+ const a2 = -0.284496736;
3225
+ const a3 = 1.421413741;
3226
+ const a4 = -1.453152027;
3227
+ const a5 = 1.061405429;
3228
+ const p = 0.3275911;
3229
+ const sign = x < 0 ? -1 : 1;
3230
+ x = Math.abs(x) / Math.sqrt(2);
3231
+ const t = 1 / (1 + p * x);
3232
+ const y = 1 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);
3233
+ return 0.5 * (1 + sign * y);
3234
+ }
3235
+ /**
3236
+ * Standard normal PDF
3237
+ */
3238
+ static normalPDF(x) {
3239
+ return Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI);
3240
+ }
3241
+ };
3242
+
3243
+ // src/utils/validation.ts
3244
+ var ValidationError = class extends Error {
3245
+ field;
3246
+ constructor(field, message) {
3247
+ super(`Validation error on '${field}': ${message}`);
3248
+ this.name = "ValidationError";
3249
+ this.field = field;
3250
+ }
3251
+ };
3252
+ function validateRFQParams(params) {
3253
+ if (!params.asset || params.asset.trim().length === 0) {
3254
+ throw new ValidationError("asset", "Asset identifier is required");
3255
+ }
3256
+ if (!["buy", "sell"].includes(params.side)) {
3257
+ throw new ValidationError("side", 'Side must be "buy" or "sell"');
3258
+ }
3259
+ if (!["call", "put"].includes(params.optionType)) {
3260
+ throw new ValidationError("optionType", 'Option type must be "call" or "put"');
3261
+ }
3262
+ if (params.strike <= 0) {
3263
+ throw new ValidationError("strike", "Strike must be a positive number");
3264
+ }
3265
+ if (params.expiry <= Math.floor(Date.now() / 1e3)) {
3266
+ throw new ValidationError("expiry", "Expiry must be in the future");
3267
+ }
3268
+ if (params.quantity <= 0n) {
3269
+ throw new ValidationError("quantity", "Quantity must be positive");
3270
+ }
3271
+ if (!params.vaultAddress || params.vaultAddress.length < 32) {
3272
+ throw new ValidationError("vaultAddress", "Valid vault address is required");
3273
+ }
3274
+ if (params.premiumFloor !== void 0 && params.premiumFloor < 0n) {
3275
+ throw new ValidationError("premiumFloor", "Premium floor cannot be negative");
3276
+ }
3277
+ }
3278
+ function validateBlackScholesParams(params) {
3279
+ if (params.spot <= 0) {
3280
+ throw new ValidationError("spot", "Spot price must be positive");
3281
+ }
3282
+ if (params.strike <= 0) {
3283
+ throw new ValidationError("strike", "Strike price must be positive");
3284
+ }
3285
+ if (params.timeToExpiry <= 0) {
3286
+ throw new ValidationError("timeToExpiry", "Time to expiry must be positive");
3287
+ }
3288
+ if (params.volatility <= 0) {
3289
+ throw new ValidationError("volatility", "Volatility must be positive");
3290
+ }
3291
+ if (params.volatility > 5) {
3292
+ throw new ValidationError("volatility", "Volatility seems too high (>500%)");
3293
+ }
3294
+ if (params.riskFreeRate < -0.1 || params.riskFreeRate > 1) {
3295
+ throw new ValidationError("riskFreeRate", "Risk-free rate seems unreasonable");
3296
+ }
3297
+ }
3298
+ function validatePublicKey(address, fieldName = "address") {
3299
+ const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
3300
+ if (!base58Regex.test(address)) {
3301
+ throw new ValidationError(fieldName, "Invalid Solana public key format");
3302
+ }
3303
+ }
3304
+ function validateTokenAmount(amount, fieldName = "amount", decimals = 6) {
3305
+ if (amount < 0n) {
3306
+ throw new ValidationError(fieldName, "Amount cannot be negative");
3307
+ }
3308
+ const maxReasonable = BigInt(1e15) * BigInt(10 ** decimals);
3309
+ if (amount > maxReasonable) {
3310
+ throw new ValidationError(fieldName, "Amount seems unreasonably large");
3311
+ }
3312
+ }
3313
+ function validateTimestamp(timestamp, fieldName = "timestamp", options = {}) {
3314
+ if (!Number.isInteger(timestamp) || timestamp < 0) {
3315
+ throw new ValidationError(fieldName, "Invalid timestamp");
3316
+ }
3317
+ const now = Math.floor(Date.now() / 1e3);
3318
+ if (options.mustBeFuture && timestamp <= now) {
3319
+ throw new ValidationError(fieldName, "Timestamp must be in the future");
3320
+ }
3321
+ if (options.maxFutureDays) {
3322
+ const maxTimestamp = now + options.maxFutureDays * 24 * 60 * 60;
3323
+ if (timestamp > maxTimestamp) {
3324
+ throw new ValidationError(
3325
+ fieldName,
3326
+ `Timestamp cannot be more than ${options.maxFutureDays} days in the future`
3327
+ );
3328
+ }
3329
+ }
3330
+ }
3331
+
3332
+ // src/utils/formatting.ts
3333
+ function formatTokenAmount(amount, decimals, maxDecimals = 2) {
3334
+ const divisor = BigInt(10 ** decimals);
3335
+ const intPart = amount / divisor;
3336
+ const fracPart = amount % divisor;
3337
+ const fracStr = fracPart.toString().padStart(decimals, "0");
3338
+ const truncatedFrac = fracStr.slice(0, maxDecimals);
3339
+ if (maxDecimals === 0 || parseInt(truncatedFrac) === 0) {
3340
+ return intPart.toString();
3341
+ }
3342
+ const trimmedFrac = truncatedFrac.replace(/0+$/, "");
3343
+ return trimmedFrac ? `${intPart}.${trimmedFrac}` : intPart.toString();
3344
+ }
3345
+ function parseTokenAmount(amount, decimals) {
3346
+ const parts = amount.split(".");
3347
+ const intPart = parts[0] || "0";
3348
+ let fracPart = parts[1] || "";
3349
+ fracPart = fracPart.slice(0, decimals).padEnd(decimals, "0");
3350
+ return BigInt(intPart) * BigInt(10 ** decimals) + BigInt(fracPart);
3351
+ }
3352
+ function formatUSDC(amount, maxDecimals = 2) {
3353
+ return formatTokenAmount(amount, TOKEN_DECIMALS.USDC, maxDecimals);
3354
+ }
3355
+ function parseUSDC(amount) {
3356
+ return parseTokenAmount(amount, TOKEN_DECIMALS.USDC);
3357
+ }
3358
+ function formatPrice(price, decimals = 2) {
3359
+ return price.toLocaleString("en-US", {
3360
+ minimumFractionDigits: decimals,
3361
+ maximumFractionDigits: decimals
3362
+ });
3363
+ }
3364
+ function formatPercent(value, decimals = 2) {
3365
+ return `${(value * 100).toFixed(decimals)}%`;
3366
+ }
3367
+ function formatBps(bps) {
3368
+ return `${bps.toLocaleString()} bps`;
3369
+ }
3370
+ function bpsToPercent(bps) {
3371
+ return bps / 100;
3372
+ }
3373
+ function percentToBps(percent) {
3374
+ return Math.round(percent * 100);
3375
+ }
3376
+ function formatTimestamp(timestamp) {
3377
+ return new Date(timestamp * 1e3).toISOString();
3378
+ }
3379
+ function formatDate(timestamp, options = {
3380
+ year: "numeric",
3381
+ month: "short",
3382
+ day: "numeric",
3383
+ hour: "2-digit",
3384
+ minute: "2-digit"
3385
+ }) {
3386
+ return new Date(timestamp * 1e3).toLocaleString("en-US", options);
3387
+ }
3388
+ function formatTimeToExpiry(expiryTimestamp) {
3389
+ const now = Math.floor(Date.now() / 1e3);
3390
+ const secondsRemaining = expiryTimestamp - now;
3391
+ if (secondsRemaining <= 0) {
3392
+ return "Expired";
3393
+ }
3394
+ const days = Math.floor(secondsRemaining / 86400);
3395
+ const hours = Math.floor(secondsRemaining % 86400 / 3600);
3396
+ const minutes = Math.floor(secondsRemaining % 3600 / 60);
3397
+ if (days > 0) {
3398
+ return `${days}d ${hours}h`;
3399
+ }
3400
+ if (hours > 0) {
3401
+ return `${hours}h ${minutes}m`;
3402
+ }
3403
+ return `${minutes}m`;
3404
+ }
3405
+ function shortenAddress(address, chars = 4) {
3406
+ if (address.length <= chars * 2 + 3) {
3407
+ return address;
3408
+ }
3409
+ return `${address.slice(0, chars)}...${address.slice(-chars)}`;
3410
+ }
3411
+ function formatRFQId(rfqId) {
3412
+ const parts = rfqId.split("_");
3413
+ if (parts.length >= 3) {
3414
+ return `RFQ-${parts[2].toUpperCase()}`;
3415
+ }
3416
+ return rfqId.slice(0, 12);
3417
+ }
3418
+ export {
3419
+ DEFAULT_PRICING_PARAMS,
3420
+ DEVNET_CONFIG,
3421
+ DEVNET_USDC_MINT,
3422
+ MAINNET_CONFIG,
3423
+ MAINNET_USDC_MINT,
3424
+ OptionPricing,
3425
+ PYTH_HERMES_URL,
3426
+ PYTH_PRICE_FEEDS,
3427
+ RFQClient,
3428
+ RFQ_DEFAULTS,
3429
+ TOKEN_DECIMALS,
3430
+ vault_default as VAULT_IDL,
3431
+ VAULT_PROGRAM_ID,
3432
+ ValidationError,
3433
+ VaultInstructions,
3434
+ bpsToPercent,
3435
+ deriveShareEscrowPda,
3436
+ deriveVaultPda,
3437
+ deriveWhitelistPda,
3438
+ deriveWithdrawalPda,
3439
+ formatBps,
3440
+ formatDate,
3441
+ formatPercent,
3442
+ formatPrice,
3443
+ formatRFQId,
3444
+ formatTimeToExpiry,
3445
+ formatTimestamp,
3446
+ formatTokenAmount,
3447
+ formatUSDC,
3448
+ parseTokenAmount,
3449
+ parseUSDC,
3450
+ percentToBps,
3451
+ shortenAddress,
3452
+ validateBlackScholesParams,
3453
+ validatePublicKey,
3454
+ validateRFQParams,
3455
+ validateTimestamp,
3456
+ validateTokenAmount
3457
+ };