@epicentral/sos-sdk 0.12.2-beta → 0.13.0-beta
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/generated/accounts/marketDataAccount.ts +13 -1
- package/generated/instructions/buyFromPool.ts +43 -15
- package/generated/instructions/closeLongToPool.ts +40 -12
- package/generated/instructions/index.ts +1 -0
- package/generated/instructions/initializeMarketData.ts +13 -0
- package/generated/instructions/liquidateWriterPosition.ts +39 -11
- package/generated/instructions/migrateMarketDataVolOracle.ts +248 -0
- package/generated/instructions/optionMint.ts +50 -22
- package/generated/instructions/updateImpliedVolatility.ts +19 -2
- package/generated/programs/optionProgram.ts +16 -0
- package/index.ts +6 -0
- package/long/builders.ts +36 -10
- package/oracle/volatility-quote.ts +65 -0
- package/package.json +7 -1
- package/shared/option-program-parser.ts +6 -0
- package/short/builders.ts +93 -0
|
@@ -48,6 +48,7 @@ export type UpdateImpliedVolatilityInstruction<
|
|
|
48
48
|
TAccountOptionAccount extends string | AccountMeta<string> = string,
|
|
49
49
|
TAccountOptionPool extends string | AccountMeta<string> = string,
|
|
50
50
|
TAccountMarketData extends string | AccountMeta<string> = string,
|
|
51
|
+
TAccountVolatilityQuoteAccount extends string | AccountMeta<string> = string,
|
|
51
52
|
TAccountKeeper extends string | AccountMeta<string> = string,
|
|
52
53
|
TRemainingAccounts extends readonly AccountMeta<string>[] = [],
|
|
53
54
|
> = Instruction<TProgram> &
|
|
@@ -63,6 +64,9 @@ export type UpdateImpliedVolatilityInstruction<
|
|
|
63
64
|
TAccountMarketData extends string
|
|
64
65
|
? ReadonlyAccount<TAccountMarketData>
|
|
65
66
|
: TAccountMarketData,
|
|
67
|
+
TAccountVolatilityQuoteAccount extends string
|
|
68
|
+
? ReadonlyAccount<TAccountVolatilityQuoteAccount>
|
|
69
|
+
: TAccountVolatilityQuoteAccount,
|
|
66
70
|
TAccountKeeper extends string
|
|
67
71
|
? ReadonlySignerAccount<TAccountKeeper> &
|
|
68
72
|
AccountSignerMeta<TAccountKeeper>
|
|
@@ -107,6 +111,7 @@ export type UpdateImpliedVolatilityInput<
|
|
|
107
111
|
TAccountOptionAccount extends string = string,
|
|
108
112
|
TAccountOptionPool extends string = string,
|
|
109
113
|
TAccountMarketData extends string = string,
|
|
114
|
+
TAccountVolatilityQuoteAccount extends string = string,
|
|
110
115
|
TAccountKeeper extends string = string,
|
|
111
116
|
> = {
|
|
112
117
|
/** The option account to update IV for */
|
|
@@ -115,6 +120,7 @@ export type UpdateImpliedVolatilityInput<
|
|
|
115
120
|
optionPool: Address<TAccountOptionPool>;
|
|
116
121
|
/** Market data - provides baseline historical volatility */
|
|
117
122
|
marketData: Address<TAccountMarketData>;
|
|
123
|
+
volatilityQuoteAccount: Address<TAccountVolatilityQuoteAccount>;
|
|
118
124
|
/** Keeper/anyone can call this instruction (permissionless update) */
|
|
119
125
|
keeper: TransactionSigner<TAccountKeeper>;
|
|
120
126
|
};
|
|
@@ -123,6 +129,7 @@ export function getUpdateImpliedVolatilityInstruction<
|
|
|
123
129
|
TAccountOptionAccount extends string,
|
|
124
130
|
TAccountOptionPool extends string,
|
|
125
131
|
TAccountMarketData extends string,
|
|
132
|
+
TAccountVolatilityQuoteAccount extends string,
|
|
126
133
|
TAccountKeeper extends string,
|
|
127
134
|
TProgramAddress extends Address = typeof OPTION_PROGRAM_PROGRAM_ADDRESS,
|
|
128
135
|
>(
|
|
@@ -130,6 +137,7 @@ export function getUpdateImpliedVolatilityInstruction<
|
|
|
130
137
|
TAccountOptionAccount,
|
|
131
138
|
TAccountOptionPool,
|
|
132
139
|
TAccountMarketData,
|
|
140
|
+
TAccountVolatilityQuoteAccount,
|
|
133
141
|
TAccountKeeper
|
|
134
142
|
>,
|
|
135
143
|
config?: { programAddress?: TProgramAddress },
|
|
@@ -138,6 +146,7 @@ export function getUpdateImpliedVolatilityInstruction<
|
|
|
138
146
|
TAccountOptionAccount,
|
|
139
147
|
TAccountOptionPool,
|
|
140
148
|
TAccountMarketData,
|
|
149
|
+
TAccountVolatilityQuoteAccount,
|
|
141
150
|
TAccountKeeper
|
|
142
151
|
> {
|
|
143
152
|
// Program address.
|
|
@@ -149,6 +158,10 @@ export function getUpdateImpliedVolatilityInstruction<
|
|
|
149
158
|
optionAccount: { value: input.optionAccount ?? null, isWritable: true },
|
|
150
159
|
optionPool: { value: input.optionPool ?? null, isWritable: false },
|
|
151
160
|
marketData: { value: input.marketData ?? null, isWritable: false },
|
|
161
|
+
volatilityQuoteAccount: {
|
|
162
|
+
value: input.volatilityQuoteAccount ?? null,
|
|
163
|
+
isWritable: false,
|
|
164
|
+
},
|
|
152
165
|
keeper: { value: input.keeper ?? null, isWritable: false },
|
|
153
166
|
};
|
|
154
167
|
const accounts = originalAccounts as Record<
|
|
@@ -162,6 +175,7 @@ export function getUpdateImpliedVolatilityInstruction<
|
|
|
162
175
|
getAccountMeta(accounts.optionAccount),
|
|
163
176
|
getAccountMeta(accounts.optionPool),
|
|
164
177
|
getAccountMeta(accounts.marketData),
|
|
178
|
+
getAccountMeta(accounts.volatilityQuoteAccount),
|
|
165
179
|
getAccountMeta(accounts.keeper),
|
|
166
180
|
],
|
|
167
181
|
data: getUpdateImpliedVolatilityInstructionDataEncoder().encode({}),
|
|
@@ -171,6 +185,7 @@ export function getUpdateImpliedVolatilityInstruction<
|
|
|
171
185
|
TAccountOptionAccount,
|
|
172
186
|
TAccountOptionPool,
|
|
173
187
|
TAccountMarketData,
|
|
188
|
+
TAccountVolatilityQuoteAccount,
|
|
174
189
|
TAccountKeeper
|
|
175
190
|
>);
|
|
176
191
|
}
|
|
@@ -187,8 +202,9 @@ export type ParsedUpdateImpliedVolatilityInstruction<
|
|
|
187
202
|
optionPool: TAccountMetas[1];
|
|
188
203
|
/** Market data - provides baseline historical volatility */
|
|
189
204
|
marketData: TAccountMetas[2];
|
|
205
|
+
volatilityQuoteAccount: TAccountMetas[3];
|
|
190
206
|
/** Keeper/anyone can call this instruction (permissionless update) */
|
|
191
|
-
keeper: TAccountMetas[
|
|
207
|
+
keeper: TAccountMetas[4];
|
|
192
208
|
};
|
|
193
209
|
data: UpdateImpliedVolatilityInstructionData;
|
|
194
210
|
};
|
|
@@ -201,7 +217,7 @@ export function parseUpdateImpliedVolatilityInstruction<
|
|
|
201
217
|
InstructionWithAccounts<TAccountMetas> &
|
|
202
218
|
InstructionWithData<ReadonlyUint8Array>,
|
|
203
219
|
): ParsedUpdateImpliedVolatilityInstruction<TProgram, TAccountMetas> {
|
|
204
|
-
if (instruction.accounts.length <
|
|
220
|
+
if (instruction.accounts.length < 5) {
|
|
205
221
|
// TODO: Coded error.
|
|
206
222
|
throw new Error("Not enough accounts");
|
|
207
223
|
}
|
|
@@ -217,6 +233,7 @@ export function parseUpdateImpliedVolatilityInstruction<
|
|
|
217
233
|
optionAccount: getNextAccount(),
|
|
218
234
|
optionPool: getNextAccount(),
|
|
219
235
|
marketData: getNextAccount(),
|
|
236
|
+
volatilityQuoteAccount: getNextAccount(),
|
|
220
237
|
keeper: getNextAccount(),
|
|
221
238
|
},
|
|
222
239
|
data: getUpdateImpliedVolatilityInstructionDataDecoder().decode(
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
type ParsedLiquidateWriterPositionInstruction,
|
|
35
35
|
type ParsedLiquidateWriterPositionRescueInstruction,
|
|
36
36
|
type ParsedMigrateCollateralPoolV1ToV2Instruction,
|
|
37
|
+
type ParsedMigrateMarketDataVolOracleInstruction,
|
|
37
38
|
type ParsedOmlpCreateVaultInstruction,
|
|
38
39
|
type ParsedOmlpUpdateFeeWalletInstruction,
|
|
39
40
|
type ParsedOmlpUpdateInterestModelInstruction,
|
|
@@ -265,6 +266,7 @@ export enum OptionProgramInstruction {
|
|
|
265
266
|
LiquidateWriterPosition,
|
|
266
267
|
LiquidateWriterPositionRescue,
|
|
267
268
|
MigrateCollateralPoolV1ToV2,
|
|
269
|
+
MigrateMarketDataVolOracle,
|
|
268
270
|
OmlpCreateVault,
|
|
269
271
|
OmlpUpdateFeeWallet,
|
|
270
272
|
OmlpUpdateInterestModel,
|
|
@@ -516,6 +518,17 @@ export function identifyOptionProgramInstruction(
|
|
|
516
518
|
) {
|
|
517
519
|
return OptionProgramInstruction.MigrateCollateralPoolV1ToV2;
|
|
518
520
|
}
|
|
521
|
+
if (
|
|
522
|
+
containsBytes(
|
|
523
|
+
data,
|
|
524
|
+
fixEncoderSize(getBytesEncoder(), 8).encode(
|
|
525
|
+
new Uint8Array([230, 102, 218, 98, 194, 231, 97, 241]),
|
|
526
|
+
),
|
|
527
|
+
0,
|
|
528
|
+
)
|
|
529
|
+
) {
|
|
530
|
+
return OptionProgramInstruction.MigrateMarketDataVolOracle;
|
|
531
|
+
}
|
|
519
532
|
if (
|
|
520
533
|
containsBytes(
|
|
521
534
|
data,
|
|
@@ -859,6 +872,9 @@ export type ParsedOptionProgramInstruction<
|
|
|
859
872
|
| ({
|
|
860
873
|
instructionType: OptionProgramInstruction.MigrateCollateralPoolV1ToV2;
|
|
861
874
|
} & ParsedMigrateCollateralPoolV1ToV2Instruction<TProgram>)
|
|
875
|
+
| ({
|
|
876
|
+
instructionType: OptionProgramInstruction.MigrateMarketDataVolOracle;
|
|
877
|
+
} & ParsedMigrateMarketDataVolOracleInstruction<TProgram>)
|
|
862
878
|
| ({
|
|
863
879
|
instructionType: OptionProgramInstruction.OmlpCreateVault;
|
|
864
880
|
} & ParsedOmlpCreateVaultInstruction<TProgram>)
|
package/index.ts
CHANGED
|
@@ -64,6 +64,12 @@ export {
|
|
|
64
64
|
type SwitchboardNetwork,
|
|
65
65
|
} from "./oracle/switchboard";
|
|
66
66
|
|
|
67
|
+
export {
|
|
68
|
+
deriveVolatilityQuoteAddress,
|
|
69
|
+
deriveVolatilityQuoteAddressFromMarketData,
|
|
70
|
+
isVolOracleEnabled,
|
|
71
|
+
} from "./oracle/volatility-quote";
|
|
72
|
+
|
|
67
73
|
export {
|
|
68
74
|
getWrapSOLInstructions,
|
|
69
75
|
getUnwrapSOLInstructions,
|
package/long/builders.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
inferSwitchboardNetwork,
|
|
37
37
|
prependSwitchboardQuote,
|
|
38
38
|
} from "../oracle/switchboard";
|
|
39
|
+
import { deriveVolatilityQuoteAddressFromMarketData } from "../oracle/volatility-quote";
|
|
39
40
|
import { fetchWriterPositionsForPool } from "../accounts/list";
|
|
40
41
|
import { getGlobalTradeConfig } from "../shared/trade-config";
|
|
41
42
|
|
|
@@ -45,6 +46,8 @@ export interface BuildBuyFromPoolParams {
|
|
|
45
46
|
longMint: AddressLike;
|
|
46
47
|
underlyingMint: AddressLike;
|
|
47
48
|
marketData: AddressLike;
|
|
49
|
+
/** Authority-updated Switchboard HV quote PDA (see deriveVolatilityQuoteAddressFromMarketData). */
|
|
50
|
+
volatilityQuoteAccount: AddressLike;
|
|
48
51
|
/** Program `switchboard_queue` — use {@link getDefaultSwitchboardQueueAddress} for the cluster. */
|
|
49
52
|
switchboardQueue: AddressLike;
|
|
50
53
|
buyer: AddressLike;
|
|
@@ -92,6 +95,8 @@ export interface BuildCloseLongToPoolParams {
|
|
|
92
95
|
escrowLongAccount: AddressLike;
|
|
93
96
|
premiumVault: AddressLike;
|
|
94
97
|
marketData: AddressLike;
|
|
98
|
+
/** Authority-updated Switchboard HV quote PDA (see deriveVolatilityQuoteAddressFromMarketData). */
|
|
99
|
+
volatilityQuoteAccount: AddressLike;
|
|
95
100
|
switchboardQueue: AddressLike;
|
|
96
101
|
buyer: AddressLike;
|
|
97
102
|
buyerLongAccount: AddressLike;
|
|
@@ -131,6 +136,7 @@ export async function buildBuyFromPoolInstruction(
|
|
|
131
136
|
longMint: toAddress(params.longMint),
|
|
132
137
|
underlyingMint: toAddress(params.underlyingMint),
|
|
133
138
|
marketData: toAddress(params.marketData),
|
|
139
|
+
volatilityQuoteAccount: toAddress(params.volatilityQuoteAccount),
|
|
134
140
|
switchboardQueue: toAddress(params.switchboardQueue),
|
|
135
141
|
buyer: toAddress(params.buyer) as any,
|
|
136
142
|
buyerPosition: params.buyerPosition ? toAddress(params.buyerPosition) : undefined,
|
|
@@ -217,6 +223,8 @@ export interface BuildBuyFromPoolTransactionWithDerivationParams {
|
|
|
217
223
|
* Use **1** if only SetComputeUnitLimit is prepended (e.g. `omitComputeUnitPriceInstruction`); **0** if no CU prepend.
|
|
218
224
|
*/
|
|
219
225
|
switchboardQuoteInstructionIndex?: number;
|
|
226
|
+
/** Optional override — derived from on-chain market data when omitted. */
|
|
227
|
+
volatilityQuoteAccount?: AddressLike;
|
|
220
228
|
}
|
|
221
229
|
|
|
222
230
|
const DEFAULT_MARKET_ORDER_SLIPPAGE_BUFFER_BASE_UNITS = 500_000n;
|
|
@@ -303,12 +311,22 @@ export async function buildBuyFromPoolTransactionWithDerivation(
|
|
|
303
311
|
}),
|
|
304
312
|
]);
|
|
305
313
|
|
|
314
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
315
|
+
invariant(
|
|
316
|
+
!!marketDataAccount,
|
|
317
|
+
"Market data account not found for resolved option market."
|
|
318
|
+
);
|
|
319
|
+
const volatilityQuoteAccount =
|
|
320
|
+
params.volatilityQuoteAccount ??
|
|
321
|
+
deriveVolatilityQuoteAddressFromMarketData(marketDataAccount);
|
|
322
|
+
|
|
306
323
|
const buyParams = {
|
|
307
324
|
optionPool: resolved.optionPool,
|
|
308
325
|
optionAccount: resolved.optionAccount,
|
|
309
326
|
longMint: resolved.longMint,
|
|
310
327
|
underlyingMint: resolved.underlyingMint!,
|
|
311
328
|
marketData: resolved.marketData,
|
|
329
|
+
volatilityQuoteAccount,
|
|
312
330
|
buyer: params.buyer,
|
|
313
331
|
paymentMint: paymentAccounts.paymentMint,
|
|
314
332
|
buyerPaymentAccount: paymentAccounts.buyerPaymentAccount,
|
|
@@ -321,11 +339,6 @@ export async function buildBuyFromPoolTransactionWithDerivation(
|
|
|
321
339
|
remainingAccounts: params.remainingAccounts,
|
|
322
340
|
};
|
|
323
341
|
|
|
324
|
-
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
325
|
-
invariant(
|
|
326
|
-
!!marketDataAccount,
|
|
327
|
-
"Market data account not found for resolved option market."
|
|
328
|
-
);
|
|
329
342
|
const feedIdHex = feedIdBytesToHex(
|
|
330
343
|
Uint8Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
331
344
|
);
|
|
@@ -460,12 +473,22 @@ export async function buildBuyFromPoolMarketOrderTransactionWithDerivation(
|
|
|
460
473
|
const maxPremiumAmount = quotePremium + slippageBuffer;
|
|
461
474
|
assertPositiveAmount(maxPremiumAmount, "maxPremiumAmount");
|
|
462
475
|
|
|
476
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
477
|
+
invariant(
|
|
478
|
+
!!marketDataAccount,
|
|
479
|
+
"Market data account not found for resolved option market."
|
|
480
|
+
);
|
|
481
|
+
const volatilityQuoteAccount =
|
|
482
|
+
params.volatilityQuoteAccount ??
|
|
483
|
+
deriveVolatilityQuoteAddressFromMarketData(marketDataAccount);
|
|
484
|
+
|
|
463
485
|
const marketBuyParams = {
|
|
464
486
|
optionPool: resolved.optionPool,
|
|
465
487
|
optionAccount: resolved.optionAccount,
|
|
466
488
|
longMint: resolved.longMint,
|
|
467
489
|
underlyingMint: refetchedPool.underlyingMint,
|
|
468
490
|
marketData: resolved.marketData,
|
|
491
|
+
volatilityQuoteAccount,
|
|
469
492
|
buyer: params.buyer,
|
|
470
493
|
paymentMint: paymentAccounts.paymentMint,
|
|
471
494
|
buyerPaymentAccount: paymentAccounts.buyerPaymentAccount,
|
|
@@ -478,11 +501,6 @@ export async function buildBuyFromPoolMarketOrderTransactionWithDerivation(
|
|
|
478
501
|
remainingAccounts,
|
|
479
502
|
};
|
|
480
503
|
|
|
481
|
-
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
482
|
-
invariant(
|
|
483
|
-
!!marketDataAccount,
|
|
484
|
-
"Market data account not found for resolved option market."
|
|
485
|
-
);
|
|
486
504
|
const feedIdHex = feedIdBytesToHex(
|
|
487
505
|
Uint8Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
488
506
|
);
|
|
@@ -541,6 +559,7 @@ export async function buildCloseLongToPoolInstruction(
|
|
|
541
559
|
escrowLongAccount: toAddress(params.escrowLongAccount),
|
|
542
560
|
premiumVault: toAddress(params.premiumVault),
|
|
543
561
|
marketData: toAddress(params.marketData),
|
|
562
|
+
volatilityQuoteAccount: toAddress(params.volatilityQuoteAccount),
|
|
544
563
|
switchboardQueue: toAddress(params.switchboardQueue),
|
|
545
564
|
buyer: toAddress(params.buyer) as any,
|
|
546
565
|
buyerLongAccount: toAddress(params.buyerLongAccount),
|
|
@@ -652,6 +671,8 @@ export interface BuildCloseLongToPoolTransactionWithDerivationParams {
|
|
|
652
671
|
* OPX `sendInstructions` prepends only `SetComputeUnitLimit` before program ixs → default **1** (quote after CU limit).
|
|
653
672
|
*/
|
|
654
673
|
switchboardQuoteInstructionIndex?: number;
|
|
674
|
+
/** Optional override — derived from on-chain market data when omitted. */
|
|
675
|
+
volatilityQuoteAccount?: AddressLike;
|
|
655
676
|
}
|
|
656
677
|
|
|
657
678
|
export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
@@ -719,6 +740,9 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
719
740
|
!!marketDataAccount,
|
|
720
741
|
"Market data account not found for resolved option market."
|
|
721
742
|
);
|
|
743
|
+
const volatilityQuoteAccount =
|
|
744
|
+
params.volatilityQuoteAccount ??
|
|
745
|
+
deriveVolatilityQuoteAddressFromMarketData(marketDataAccount);
|
|
722
746
|
const feedIdHex = feedIdBytesToHex(
|
|
723
747
|
Uint8Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
724
748
|
);
|
|
@@ -738,6 +762,7 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
738
762
|
escrowLongAccount: resolved.escrowLongAccount!,
|
|
739
763
|
premiumVault: resolved.premiumVault!,
|
|
740
764
|
marketData: resolved.marketData,
|
|
765
|
+
volatilityQuoteAccount,
|
|
741
766
|
switchboardQueue,
|
|
742
767
|
buyer: params.buyer,
|
|
743
768
|
buyerLongAccount: params.buyerLongAccount,
|
|
@@ -778,6 +803,7 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
778
803
|
escrowLongAccount: resolved.escrowLongAccount!,
|
|
779
804
|
premiumVault: resolved.premiumVault!,
|
|
780
805
|
marketData: resolved.marketData,
|
|
806
|
+
volatilityQuoteAccount,
|
|
781
807
|
switchboardQueue: getDefaultSwitchboardQueueAddress(network),
|
|
782
808
|
buyer: params.buyer,
|
|
783
809
|
buyerLongAccount: params.buyerLongAccount,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { address, type Address } from "@solana/kit";
|
|
3
|
+
import { toAddress } from "../client/program";
|
|
4
|
+
import type { AddressLike } from "../client/types";
|
|
5
|
+
import type { MarketDataAccount } from "../generated/accounts/marketDataAccount";
|
|
6
|
+
|
|
7
|
+
const ZERO_PUBKEY = "11111111111111111111111111111111";
|
|
8
|
+
const QUOTE_PROGRAM_ID = new PublicKey("orac1eFjzWL5R3RbbdMV68K9H6TaCVVcL6LjvQQWAbz");
|
|
9
|
+
const AUTHORITY_QUOTE_SCHEME_TAG = Buffer.from("AUTH");
|
|
10
|
+
|
|
11
|
+
function feedIdBytes(feedId: MarketDataAccount["switchboardVolatilityFeedId"]): Uint8Array {
|
|
12
|
+
return Uint8Array.from(feedId);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function feedIdIsZero(feedId: MarketDataAccount["switchboardVolatilityFeedId"]): boolean {
|
|
16
|
+
const bytes = feedIdBytes(feedId);
|
|
17
|
+
return bytes.length === 32 && bytes.every((b) => b === 0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isVolOracleEnabled(
|
|
21
|
+
marketData: Pick<
|
|
22
|
+
MarketDataAccount,
|
|
23
|
+
"switchboardVolatilityFeedId" | "volatilityQuoteAuthority"
|
|
24
|
+
>
|
|
25
|
+
): boolean {
|
|
26
|
+
return (
|
|
27
|
+
!feedIdIsZero(marketData.switchboardVolatilityFeedId) &&
|
|
28
|
+
String(marketData.volatilityQuoteAuthority) !== ZERO_PUBKEY
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Derives the Switchboard authority quote PDA for an HV feed.
|
|
34
|
+
*/
|
|
35
|
+
export function deriveVolatilityQuoteAddress(
|
|
36
|
+
authority: AddressLike,
|
|
37
|
+
switchboardVolatilityFeedId: MarketDataAccount["switchboardVolatilityFeedId"]
|
|
38
|
+
): Address {
|
|
39
|
+
const authorityPk = new PublicKey(String(toAddress(authority)));
|
|
40
|
+
const feedHash = Buffer.from(feedIdBytes(switchboardVolatilityFeedId));
|
|
41
|
+
const [quoteAccount] = PublicKey.findProgramAddressSync(
|
|
42
|
+
[AUTHORITY_QUOTE_SCHEME_TAG, authorityPk.toBuffer(), feedHash],
|
|
43
|
+
QUOTE_PROGRAM_ID
|
|
44
|
+
);
|
|
45
|
+
return address(quoteAccount.toBase58());
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* When vol oracle is disabled on market data, returns the system program as a
|
|
50
|
+
* read-only placeholder (the program skips quote reads in that case).
|
|
51
|
+
*/
|
|
52
|
+
export function deriveVolatilityQuoteAddressFromMarketData(
|
|
53
|
+
marketData: Pick<
|
|
54
|
+
MarketDataAccount,
|
|
55
|
+
"volatilityQuoteAuthority" | "switchboardVolatilityFeedId"
|
|
56
|
+
>
|
|
57
|
+
): Address {
|
|
58
|
+
if (!isVolOracleEnabled(marketData)) {
|
|
59
|
+
return address(ZERO_PUBKEY);
|
|
60
|
+
}
|
|
61
|
+
return deriveVolatilityQuoteAddress(
|
|
62
|
+
marketData.volatilityQuoteAuthority,
|
|
63
|
+
marketData.switchboardVolatilityFeedId
|
|
64
|
+
);
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epicentral/sos-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0-beta",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Solana Option Standard SDK. The frontend-first SDK for Native Options Trading on Solana. Created by Epicentral Labs.",
|
|
6
6
|
"type": "module",
|
|
@@ -20,11 +20,17 @@
|
|
|
20
20
|
"@solana-program/system": "^0.11.0",
|
|
21
21
|
"@solana/compat": "^6.1.0",
|
|
22
22
|
"@solana/kit": "^6.1.0",
|
|
23
|
+
"@solana/web3.js": "^1.98.0",
|
|
23
24
|
"@switchboard-xyz/common": "^5.7.0",
|
|
24
25
|
"@switchboard-xyz/on-demand": "^3.9.0",
|
|
25
26
|
"bs58": "^6.0.0",
|
|
26
27
|
"decimal.js": "^10.4.3"
|
|
27
28
|
},
|
|
29
|
+
"overrides": {
|
|
30
|
+
"@switchboard-xyz/on-demand": {
|
|
31
|
+
"@switchboard-xyz/common": "$@switchboard-xyz/common"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
28
34
|
"scripts": {
|
|
29
35
|
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
30
36
|
"publish-beta": "dotenv -e .env -- pnpm publish --access public --tag beta",
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
parseLiquidateWriterPositionInstruction,
|
|
27
27
|
parseLiquidateWriterPositionRescueInstruction,
|
|
28
28
|
parseMigrateCollateralPoolV1ToV2Instruction,
|
|
29
|
+
parseMigrateMarketDataVolOracleInstruction,
|
|
29
30
|
parseOmlpCreateVaultInstruction,
|
|
30
31
|
parseOmlpUpdateFeeWalletInstruction,
|
|
31
32
|
parseOmlpUpdateInterestModelInstruction,
|
|
@@ -179,6 +180,11 @@ export function parseOptionProgramInstruction<
|
|
|
179
180
|
parseLiquidateWriterPositionRescueInstruction(instruction),
|
|
180
181
|
instructionType,
|
|
181
182
|
);
|
|
183
|
+
case OptionProgramInstruction.MigrateMarketDataVolOracle:
|
|
184
|
+
return withInstructionType(
|
|
185
|
+
parseMigrateMarketDataVolOracleInstruction(instruction),
|
|
186
|
+
instructionType,
|
|
187
|
+
);
|
|
182
188
|
case OptionProgramInstruction.MigrateCollateralPoolV1ToV2:
|
|
183
189
|
return withInstructionType(
|
|
184
190
|
parseMigrateCollateralPoolV1ToV2Instruction(instruction),
|
package/short/builders.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getClaimBuyerSettlementInstructionAsync,
|
|
3
3
|
getClaimMakerSettlementInstructionAsync,
|
|
4
|
+
getLiquidateWriterPositionInstructionAsync,
|
|
4
5
|
getLiquidateWriterPositionRescueInstructionAsync,
|
|
5
6
|
getOptionMintInstructionAsync,
|
|
6
7
|
getPrepareBuyerSettlementInstructionAsync,
|
|
@@ -42,6 +43,7 @@ import {
|
|
|
42
43
|
SLOT_HASHES_SYSVAR_ADDRESS,
|
|
43
44
|
SWITCHBOARD_DEFAULT_DEVNET_QUEUE,
|
|
44
45
|
} from "../oracle/switchboard";
|
|
46
|
+
import { deriveVolatilityQuoteAddressFromMarketData } from "../oracle/volatility-quote";
|
|
45
47
|
import { applySlippageBps } from "../long/quotes";
|
|
46
48
|
import { getGlobalTradeConfig } from "../shared/trade-config";
|
|
47
49
|
|
|
@@ -75,6 +77,8 @@ export interface BuildOptionMintParams {
|
|
|
75
77
|
makerLongAccount?: AddressLike;
|
|
76
78
|
makerShortAccount?: AddressLike;
|
|
77
79
|
marketData?: AddressLike;
|
|
80
|
+
/** Authority-updated Switchboard HV quote PDA (derived from market data when omitted). */
|
|
81
|
+
volatilityQuoteAccount?: AddressLike;
|
|
78
82
|
optionPool?: AddressLike;
|
|
79
83
|
escrowLongAccount?: AddressLike;
|
|
80
84
|
premiumVault?: AddressLike;
|
|
@@ -240,6 +244,10 @@ export async function buildOptionMintInstruction(
|
|
|
240
244
|
!!longMetadata && !!shortMetadata,
|
|
241
245
|
"longMetadataAccount and shortMetadataAccount are required (or provide longMint/shortMint to derive)."
|
|
242
246
|
);
|
|
247
|
+
invariant(
|
|
248
|
+
!!params.volatilityQuoteAccount,
|
|
249
|
+
"volatilityQuoteAccount is required (use buildOptionMintTransactionWithDerivation or deriveVolatilityQuoteAddressFromMarketData)."
|
|
250
|
+
);
|
|
243
251
|
|
|
244
252
|
const kitInstruction = await getOptionMintInstructionAsync(
|
|
245
253
|
{
|
|
@@ -256,6 +264,7 @@ export async function buildOptionMintInstruction(
|
|
|
256
264
|
longMetadataAccount: toAddress(longMetadata!),
|
|
257
265
|
shortMetadataAccount: toAddress(shortMetadata!),
|
|
258
266
|
marketData: params.marketData ? toAddress(params.marketData) : undefined,
|
|
267
|
+
volatilityQuoteAccount: toAddress(params.volatilityQuoteAccount!),
|
|
259
268
|
underlyingMint: toAddress(params.underlyingMint),
|
|
260
269
|
collateralMint: toAddress(params.collateralMint ?? params.underlyingMint),
|
|
261
270
|
optionPool: params.optionPool ? toAddress(params.optionPool) : undefined,
|
|
@@ -383,6 +392,8 @@ export interface BuildOptionMintTransactionWithDerivationParams {
|
|
|
383
392
|
* under Solana's 1232-byte versioned-transaction limit when OMLP accounts are present.
|
|
384
393
|
*/
|
|
385
394
|
skipCreateCollateralAta?: boolean;
|
|
395
|
+
/** Optional override — derived from on-chain market data when omitted. */
|
|
396
|
+
volatilityQuoteAccount?: AddressLike;
|
|
386
397
|
}
|
|
387
398
|
|
|
388
399
|
export async function buildOptionMintTransactionWithDerivation(
|
|
@@ -447,6 +458,9 @@ export async function buildOptionMintTransactionWithDerivation(
|
|
|
447
458
|
!!marketDataAccount,
|
|
448
459
|
"Market data account not found for resolved option market."
|
|
449
460
|
);
|
|
461
|
+
const volatilityQuoteAccount =
|
|
462
|
+
params.volatilityQuoteAccount ??
|
|
463
|
+
deriveVolatilityQuoteAddressFromMarketData(marketDataAccount);
|
|
450
464
|
const switchboardFeedId = feedIdBytesToHex(
|
|
451
465
|
Uint8Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
452
466
|
);
|
|
@@ -514,6 +528,7 @@ export async function buildOptionMintTransactionWithDerivation(
|
|
|
514
528
|
makerLongAccount,
|
|
515
529
|
makerShortAccount,
|
|
516
530
|
marketData: resolved.marketData,
|
|
531
|
+
volatilityQuoteAccount,
|
|
517
532
|
optionPool: resolved.optionPool,
|
|
518
533
|
escrowLongAccount: resolved.escrowLongAccount,
|
|
519
534
|
premiumVault: resolved.premiumVault,
|
|
@@ -957,6 +972,84 @@ export async function buildClaimMakerSettlementInstruction(
|
|
|
957
972
|
});
|
|
958
973
|
}
|
|
959
974
|
|
|
975
|
+
export interface BuildLiquidateWriterPositionParams {
|
|
976
|
+
optionPool: AddressLike;
|
|
977
|
+
optionAccount: AddressLike;
|
|
978
|
+
/** Market data PDA (`["market_data", underlying_asset]`). */
|
|
979
|
+
marketData: AddressLike;
|
|
980
|
+
/** Authority-updated Switchboard HV quote PDA (derived from market data when omitted). */
|
|
981
|
+
volatilityQuoteAccount?: AddressLike;
|
|
982
|
+
collateralPool: AddressLike;
|
|
983
|
+
writerPosition: AddressLike;
|
|
984
|
+
longMint: AddressLike;
|
|
985
|
+
escrowLongAccount: AddressLike;
|
|
986
|
+
underlyingMint: AddressLike;
|
|
987
|
+
switchboardQueue: AddressLike;
|
|
988
|
+
/** OMLP Vault state PDA (mut: decrements total_loans, etc.). */
|
|
989
|
+
omlpVault: AddressLike;
|
|
990
|
+
collateralVault: AddressLike;
|
|
991
|
+
premiumVault: AddressLike;
|
|
992
|
+
omlpVaultTokenAccount: AddressLike;
|
|
993
|
+
feeWallet: AddressLike;
|
|
994
|
+
/** Permissionless liquidator signer. */
|
|
995
|
+
liquidator: TransactionSigner<string>;
|
|
996
|
+
/** Every active PoolLoan for this writer/vault (SDK: loadWriterLoans). */
|
|
997
|
+
remainingAccounts?: RemainingAccountInput[];
|
|
998
|
+
rpc?: KitRpc;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
export async function buildLiquidateWriterPositionInstruction(
|
|
1002
|
+
params: BuildLiquidateWriterPositionParams
|
|
1003
|
+
): Promise<Instruction<string>> {
|
|
1004
|
+
let volatilityQuoteAccount = params.volatilityQuoteAccount;
|
|
1005
|
+
if (!volatilityQuoteAccount) {
|
|
1006
|
+
invariant(!!params.rpc, "rpc is required when volatilityQuoteAccount is omitted");
|
|
1007
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc!, params.marketData);
|
|
1008
|
+
invariant(
|
|
1009
|
+
!!marketDataAccount,
|
|
1010
|
+
"Market data account not found for resolved option market."
|
|
1011
|
+
);
|
|
1012
|
+
volatilityQuoteAccount = deriveVolatilityQuoteAddressFromMarketData(marketDataAccount);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
const kitInstruction = await getLiquidateWriterPositionInstructionAsync({
|
|
1016
|
+
optionPool: toAddress(params.optionPool),
|
|
1017
|
+
optionAccount: toAddress(params.optionAccount),
|
|
1018
|
+
marketData: toAddress(params.marketData),
|
|
1019
|
+
volatilityQuoteAccount: toAddress(volatilityQuoteAccount),
|
|
1020
|
+
collateralPool: toAddress(params.collateralPool),
|
|
1021
|
+
writerPosition: toAddress(params.writerPosition),
|
|
1022
|
+
longMint: toAddress(params.longMint),
|
|
1023
|
+
escrowLongAccount: toAddress(params.escrowLongAccount),
|
|
1024
|
+
underlyingMint: toAddress(params.underlyingMint),
|
|
1025
|
+
switchboardQueue: toAddress(params.switchboardQueue),
|
|
1026
|
+
omlpVault: toAddress(params.omlpVault),
|
|
1027
|
+
collateralVault: toAddress(params.collateralVault),
|
|
1028
|
+
premiumVault: toAddress(params.premiumVault),
|
|
1029
|
+
omlpVaultTokenAccount: toAddress(params.omlpVaultTokenAccount),
|
|
1030
|
+
feeWallet: toAddress(params.feeWallet),
|
|
1031
|
+
liquidator: params.liquidator as any,
|
|
1032
|
+
});
|
|
1033
|
+
return appendRemainingAccounts(kitInstruction, [
|
|
1034
|
+
{
|
|
1035
|
+
address: SLOT_HASHES_SYSVAR_ADDRESS,
|
|
1036
|
+
isWritable: false,
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
address: INSTRUCTIONS_SYSVAR_ADDRESS,
|
|
1040
|
+
isWritable: false,
|
|
1041
|
+
},
|
|
1042
|
+
...(params.remainingAccounts ?? []),
|
|
1043
|
+
]);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
export async function buildLiquidateWriterPositionTransaction(
|
|
1047
|
+
params: BuildLiquidateWriterPositionParams
|
|
1048
|
+
): Promise<BuiltTransaction> {
|
|
1049
|
+
const instruction = await buildLiquidateWriterPositionInstruction(params);
|
|
1050
|
+
return { instructions: [instruction] };
|
|
1051
|
+
}
|
|
1052
|
+
|
|
960
1053
|
/**
|
|
961
1054
|
* Parameters for the permissioned rescue liquidation path. Gated on-chain
|
|
962
1055
|
* to `Vault::keeper`; the SDK does not re-verify keeper identity, but the
|