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