@epicentral/sos-sdk 0.5.0-alpha.8 → 0.6.1-alpha
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.
Potentially problematic release.
This version of @epicentral/sos-sdk might be problematic. Click here for more details.
- package/README.md +60 -9
- package/client/lookup-table.ts +1 -1
- package/generated/accounts/index.ts +0 -1
- package/generated/accounts/marketDataAccount.ts +4 -4
- package/generated/errors/optionProgram.ts +83 -75
- package/generated/instructions/autoExerciseAllExpired.ts +31 -28
- package/generated/instructions/autoExerciseExpired.ts +31 -28
- package/generated/instructions/buyFromPool.ts +31 -28
- package/generated/instructions/closeLongToPool.ts +31 -28
- package/generated/instructions/initializeMarketData.ts +6 -6
- package/generated/instructions/liquidateWriterPosition.ts +31 -28
- package/generated/instructions/optionExercise.ts +28 -25
- package/generated/instructions/optionMint.ts +28 -34
- package/generated/instructions/optionValidate.ts +17 -16
- package/generated/programs/optionProgram.ts +2 -301
- package/generated/types/index.ts +0 -2
- package/index.ts +6 -0
- package/long/builders.ts +103 -12
- package/long/exercise.ts +2 -2
- package/omlp/builders.ts +22 -2
- package/omlp/service.ts +41 -4
- package/oracle/switchboard.ts +56 -0
- package/package.json +1 -1
- package/short/builders.ts +40 -13
package/long/builders.ts
CHANGED
|
@@ -17,10 +17,15 @@ import {
|
|
|
17
17
|
type RemainingAccountInput,
|
|
18
18
|
} from "../shared/remaining-accounts";
|
|
19
19
|
import type { OptionType } from "../generated/types";
|
|
20
|
-
import {
|
|
21
|
-
|
|
20
|
+
import {
|
|
21
|
+
getCloseAccountInstruction,
|
|
22
|
+
getCreateAssociatedTokenIdempotentInstructionWithAddress,
|
|
23
|
+
NATIVE_MINT,
|
|
24
|
+
} from "../wsol/instructions";
|
|
25
|
+
import { fetchMarketDataAccount, fetchOptionPool } from "../accounts/fetchers";
|
|
22
26
|
import { getBuyFromPoolRemainingAccounts } from "./remaining-accounts";
|
|
23
27
|
import { fetchWriterPositionsForPool } from "../accounts/list";
|
|
28
|
+
import bs58 from "bs58";
|
|
24
29
|
|
|
25
30
|
export interface BuildBuyFromPoolParams {
|
|
26
31
|
optionPool: AddressLike;
|
|
@@ -28,7 +33,7 @@ export interface BuildBuyFromPoolParams {
|
|
|
28
33
|
longMint: AddressLike;
|
|
29
34
|
underlyingMint: AddressLike;
|
|
30
35
|
marketData: AddressLike;
|
|
31
|
-
|
|
36
|
+
switchboardFeed: AddressLike;
|
|
32
37
|
buyer: AddressLike;
|
|
33
38
|
buyerPaymentAccount: AddressLike;
|
|
34
39
|
escrowLongAccount: AddressLike;
|
|
@@ -49,7 +54,7 @@ export interface BuildCloseLongToPoolParams {
|
|
|
49
54
|
escrowLongAccount: AddressLike;
|
|
50
55
|
premiumVault: AddressLike;
|
|
51
56
|
marketData: AddressLike;
|
|
52
|
-
|
|
57
|
+
switchboardFeed: AddressLike;
|
|
53
58
|
buyer: AddressLike;
|
|
54
59
|
buyerLongAccount: AddressLike;
|
|
55
60
|
buyerPayoutAccount: AddressLike;
|
|
@@ -58,6 +63,16 @@ export interface BuildCloseLongToPoolParams {
|
|
|
58
63
|
minPayoutAmount: bigint | number;
|
|
59
64
|
buyerPosition?: AddressLike;
|
|
60
65
|
omlpVault?: AddressLike;
|
|
66
|
+
/**
|
|
67
|
+
* When true, appends an SPL CloseAccount to close the buyer's LONG token account after close_long_to_pool (reclaim rent).
|
|
68
|
+
* Set to true only when closing the entire position; for partial closes the LONG ATA still holds remaining tokens.
|
|
69
|
+
*/
|
|
70
|
+
closeLongTokenAccount?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* When true and underlying is WSOL, appends an SPL CloseAccount to unwrap the payout ATA so the buyer receives native SOL.
|
|
73
|
+
* Ignored when underlyingMint is not WSOL.
|
|
74
|
+
*/
|
|
75
|
+
unwrapPayoutSol?: boolean;
|
|
61
76
|
remainingAccounts?: RemainingAccountInput[];
|
|
62
77
|
}
|
|
63
78
|
|
|
@@ -73,7 +88,7 @@ export async function buildBuyFromPoolInstruction(
|
|
|
73
88
|
longMint: toAddress(params.longMint),
|
|
74
89
|
underlyingMint: toAddress(params.underlyingMint),
|
|
75
90
|
marketData: toAddress(params.marketData),
|
|
76
|
-
|
|
91
|
+
switchboardFeed: toAddress(params.switchboardFeed),
|
|
77
92
|
buyer: toAddress(params.buyer) as any,
|
|
78
93
|
buyerPosition: params.buyerPosition ? toAddress(params.buyerPosition) : undefined,
|
|
79
94
|
buyerOptionAccount: params.buyerOptionAccount
|
|
@@ -120,7 +135,7 @@ export interface BuildBuyFromPoolTransactionWithDerivationParams {
|
|
|
120
135
|
expirationDate: bigint | number;
|
|
121
136
|
buyer: AddressLike;
|
|
122
137
|
buyerPaymentAccount: AddressLike;
|
|
123
|
-
|
|
138
|
+
switchboardFeed?: AddressLike;
|
|
124
139
|
quantity: bigint | number;
|
|
125
140
|
premiumAmount: bigint | number;
|
|
126
141
|
rpc: KitRpc;
|
|
@@ -198,13 +213,24 @@ export async function buildBuyFromPoolTransactionWithDerivation(
|
|
|
198
213
|
: deriveAssociatedTokenAddress(params.buyer, resolved.longMint),
|
|
199
214
|
]);
|
|
200
215
|
|
|
216
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
217
|
+
invariant(
|
|
218
|
+
!!marketDataAccount,
|
|
219
|
+
"Market data account not found for resolved option market."
|
|
220
|
+
);
|
|
221
|
+
const switchboardFeed =
|
|
222
|
+
params.switchboardFeed ??
|
|
223
|
+
bs58.encode(
|
|
224
|
+
Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
225
|
+
);
|
|
226
|
+
|
|
201
227
|
return buildBuyFromPoolTransaction({
|
|
202
228
|
optionPool: resolved.optionPool,
|
|
203
229
|
optionAccount: resolved.optionAccount,
|
|
204
230
|
longMint: resolved.longMint,
|
|
205
231
|
underlyingMint: resolved.underlyingMint!,
|
|
206
232
|
marketData: resolved.marketData,
|
|
207
|
-
|
|
233
|
+
switchboardFeed,
|
|
208
234
|
buyer: params.buyer,
|
|
209
235
|
buyerPaymentAccount: params.buyerPaymentAccount,
|
|
210
236
|
escrowLongAccount: resolved.escrowLongAccount!,
|
|
@@ -292,13 +318,24 @@ export async function buildBuyFromPoolMarketOrderTransactionWithDerivation(
|
|
|
292
318
|
const maxPremiumAmount = BigInt(params.quotedPremiumTotal) + slippageBuffer;
|
|
293
319
|
assertPositiveAmount(maxPremiumAmount, "maxPremiumAmount");
|
|
294
320
|
|
|
321
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
322
|
+
invariant(
|
|
323
|
+
!!marketDataAccount,
|
|
324
|
+
"Market data account not found for resolved option market."
|
|
325
|
+
);
|
|
326
|
+
const switchboardFeed =
|
|
327
|
+
params.switchboardFeed ??
|
|
328
|
+
bs58.encode(
|
|
329
|
+
Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
330
|
+
);
|
|
331
|
+
|
|
295
332
|
return buildBuyFromPoolTransaction({
|
|
296
333
|
optionPool: resolved.optionPool,
|
|
297
334
|
optionAccount: resolved.optionAccount,
|
|
298
335
|
longMint: resolved.longMint,
|
|
299
336
|
underlyingMint: refetchedPool.underlyingMint,
|
|
300
337
|
marketData: resolved.marketData,
|
|
301
|
-
|
|
338
|
+
switchboardFeed,
|
|
302
339
|
buyer: params.buyer,
|
|
303
340
|
buyerPaymentAccount: params.buyerPaymentAccount,
|
|
304
341
|
escrowLongAccount: refetchedPool.escrowLongAccount,
|
|
@@ -329,7 +366,7 @@ export async function buildCloseLongToPoolInstruction(
|
|
|
329
366
|
escrowLongAccount: toAddress(params.escrowLongAccount),
|
|
330
367
|
premiumVault: toAddress(params.premiumVault),
|
|
331
368
|
marketData: toAddress(params.marketData),
|
|
332
|
-
|
|
369
|
+
switchboardFeed: toAddress(params.switchboardFeed),
|
|
333
370
|
buyer: toAddress(params.buyer) as any,
|
|
334
371
|
buyerLongAccount: toAddress(params.buyerLongAccount),
|
|
335
372
|
buyerPayoutAccount: toAddress(params.buyerPayoutAccount),
|
|
@@ -347,7 +384,32 @@ export async function buildCloseLongToPoolTransaction(
|
|
|
347
384
|
params: BuildCloseLongToPoolParams
|
|
348
385
|
): Promise<BuiltTransaction> {
|
|
349
386
|
const instruction = await buildCloseLongToPoolInstruction(params);
|
|
350
|
-
|
|
387
|
+
const instructions = [instruction];
|
|
388
|
+
|
|
389
|
+
if (params.closeLongTokenAccount === true) {
|
|
390
|
+
instructions.push(
|
|
391
|
+
getCloseAccountInstruction(
|
|
392
|
+
params.buyerLongAccount,
|
|
393
|
+
params.buyer,
|
|
394
|
+
params.buyer
|
|
395
|
+
)
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const shouldUnwrapPayout =
|
|
400
|
+
params.unwrapPayoutSol === true &&
|
|
401
|
+
toAddress(params.underlyingMint) === toAddress(NATIVE_MINT);
|
|
402
|
+
if (shouldUnwrapPayout) {
|
|
403
|
+
instructions.push(
|
|
404
|
+
getCloseAccountInstruction(
|
|
405
|
+
params.buyerPayoutAccount,
|
|
406
|
+
params.buyer,
|
|
407
|
+
params.buyer
|
|
408
|
+
)
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return { instructions };
|
|
351
413
|
}
|
|
352
414
|
|
|
353
415
|
export interface BuildCloseLongToPoolTransactionWithDerivationParams {
|
|
@@ -358,13 +420,23 @@ export interface BuildCloseLongToPoolTransactionWithDerivationParams {
|
|
|
358
420
|
buyer: AddressLike;
|
|
359
421
|
buyerLongAccount: AddressLike;
|
|
360
422
|
buyerPayoutAccount: AddressLike;
|
|
361
|
-
|
|
423
|
+
switchboardFeed?: AddressLike;
|
|
362
424
|
quantity: bigint | number;
|
|
363
425
|
minPayoutAmount: bigint | number;
|
|
364
426
|
rpc: KitRpc;
|
|
365
427
|
programId?: AddressLike;
|
|
366
428
|
buyerPosition?: AddressLike;
|
|
367
429
|
omlpVault?: AddressLike;
|
|
430
|
+
/**
|
|
431
|
+
* When true (default), appends CloseAccount for the buyer's LONG token account after close_long_to_pool.
|
|
432
|
+
* Set to false when doing a partial close (LONG ATA still holds remaining tokens).
|
|
433
|
+
*/
|
|
434
|
+
closeLongTokenAccount?: boolean;
|
|
435
|
+
/**
|
|
436
|
+
* When true (default for WSOL underlying), appends CloseAccount to unwrap payout WSOL ATA to native SOL.
|
|
437
|
+
* Only applies when option underlying is WSOL.
|
|
438
|
+
*/
|
|
439
|
+
unwrapPayoutSol?: boolean;
|
|
368
440
|
remainingAccounts?: RemainingAccountInput[];
|
|
369
441
|
}
|
|
370
442
|
|
|
@@ -396,6 +468,23 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
396
468
|
params.programId
|
|
397
469
|
))[0];
|
|
398
470
|
|
|
471
|
+
const isWsolUnderlying =
|
|
472
|
+
toAddress(resolved.underlyingMint!) === toAddress(NATIVE_MINT);
|
|
473
|
+
const closeLongTokenAccount =
|
|
474
|
+
params.closeLongTokenAccount !== false;
|
|
475
|
+
const unwrapPayoutSol =
|
|
476
|
+
params.unwrapPayoutSol !== false && isWsolUnderlying;
|
|
477
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
478
|
+
invariant(
|
|
479
|
+
!!marketDataAccount,
|
|
480
|
+
"Market data account not found for resolved option market."
|
|
481
|
+
);
|
|
482
|
+
const switchboardFeed =
|
|
483
|
+
params.switchboardFeed ??
|
|
484
|
+
bs58.encode(
|
|
485
|
+
Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
486
|
+
);
|
|
487
|
+
|
|
399
488
|
return buildCloseLongToPoolTransaction({
|
|
400
489
|
optionPool: resolved.optionPool,
|
|
401
490
|
optionAccount: resolved.optionAccount,
|
|
@@ -405,7 +494,7 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
405
494
|
escrowLongAccount: resolved.escrowLongAccount!,
|
|
406
495
|
premiumVault: resolved.premiumVault!,
|
|
407
496
|
marketData: resolved.marketData,
|
|
408
|
-
|
|
497
|
+
switchboardFeed,
|
|
409
498
|
buyer: params.buyer,
|
|
410
499
|
buyerLongAccount: params.buyerLongAccount,
|
|
411
500
|
buyerPayoutAccount: params.buyerPayoutAccount,
|
|
@@ -414,6 +503,8 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
414
503
|
minPayoutAmount: params.minPayoutAmount,
|
|
415
504
|
buyerPosition,
|
|
416
505
|
omlpVault: params.omlpVault,
|
|
506
|
+
closeLongTokenAccount,
|
|
507
|
+
unwrapPayoutSol,
|
|
417
508
|
remainingAccounts: params.remainingAccounts,
|
|
418
509
|
});
|
|
419
510
|
}
|
package/long/exercise.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface BuildOptionExerciseParams {
|
|
|
8
8
|
positionAccount: AddressLike;
|
|
9
9
|
marketData: AddressLike;
|
|
10
10
|
underlyingMint: AddressLike;
|
|
11
|
-
|
|
11
|
+
switchboardFeed: AddressLike;
|
|
12
12
|
buyerPaymentAccount: AddressLike;
|
|
13
13
|
makerCollateralAccount: AddressLike;
|
|
14
14
|
escrowState: AddressLike;
|
|
@@ -30,7 +30,7 @@ export function buildOptionExerciseInstruction(
|
|
|
30
30
|
positionAccount: toAddress(params.positionAccount),
|
|
31
31
|
marketData: toAddress(params.marketData),
|
|
32
32
|
underlyingMint: toAddress(params.underlyingMint),
|
|
33
|
-
|
|
33
|
+
switchboardFeed: toAddress(params.switchboardFeed),
|
|
34
34
|
buyerPaymentAccount: toAddress(params.buyerPaymentAccount),
|
|
35
35
|
makerCollateralAccount: toAddress(params.makerCollateralAccount),
|
|
36
36
|
escrowState: toAddress(params.escrowState),
|
package/omlp/builders.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { Instruction } from "@solana/kit";
|
|
|
6
6
|
import { toAddress } from "../client/program";
|
|
7
7
|
import type { AddressLike, BuiltTransaction } from "../client/types";
|
|
8
8
|
import { assertPositiveAmount } from "../shared/amounts";
|
|
9
|
+
import { getCloseAccountInstruction, NATIVE_MINT } from "../wsol/instructions";
|
|
9
10
|
|
|
10
11
|
export interface BuildDepositToPositionParams {
|
|
11
12
|
vault: AddressLike;
|
|
@@ -23,6 +24,8 @@ export interface BuildWithdrawFromPositionParams {
|
|
|
23
24
|
lender: AddressLike;
|
|
24
25
|
amount: bigint | number;
|
|
25
26
|
position?: AddressLike;
|
|
27
|
+
unwrapSol?: boolean;
|
|
28
|
+
vaultMint?: AddressLike;
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
export async function buildDepositToPositionInstruction(
|
|
@@ -69,6 +72,23 @@ export async function buildWithdrawFromPositionInstruction(
|
|
|
69
72
|
export async function buildWithdrawFromPositionTransaction(
|
|
70
73
|
params: BuildWithdrawFromPositionParams
|
|
71
74
|
): Promise<BuiltTransaction> {
|
|
72
|
-
const
|
|
73
|
-
|
|
75
|
+
const withdrawInstruction = await buildWithdrawFromPositionInstruction(params);
|
|
76
|
+
const instructions: Instruction<string>[] = [withdrawInstruction];
|
|
77
|
+
|
|
78
|
+
const shouldUnwrapSol =
|
|
79
|
+
params.unwrapSol === true &&
|
|
80
|
+
params.vaultMint !== undefined &&
|
|
81
|
+
toAddress(params.vaultMint) === toAddress(NATIVE_MINT);
|
|
82
|
+
|
|
83
|
+
if (shouldUnwrapSol) {
|
|
84
|
+
instructions.push(
|
|
85
|
+
getCloseAccountInstruction(
|
|
86
|
+
params.lenderTokenAccount,
|
|
87
|
+
params.lender,
|
|
88
|
+
params.lender
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return { instructions };
|
|
74
94
|
}
|
package/omlp/service.ts
CHANGED
|
@@ -12,10 +12,25 @@ import {
|
|
|
12
12
|
type BuildWithdrawFromPositionParams,
|
|
13
13
|
} from "./builders";
|
|
14
14
|
|
|
15
|
+
const INTEREST_FP_SCALE = 1_000_000_000_000n;
|
|
16
|
+
|
|
15
17
|
function positiveDiff(a: bigint, b: bigint): bigint {
|
|
16
18
|
return a > b ? a - b : 0n;
|
|
17
19
|
}
|
|
18
20
|
|
|
21
|
+
function calculatePendingInterest(
|
|
22
|
+
deposited: bigint,
|
|
23
|
+
vaultAccInterestPerShareFp: bigint,
|
|
24
|
+
positionInterestIndexSnapshotFp: bigint
|
|
25
|
+
): bigint {
|
|
26
|
+
const deltaFp = positiveDiff(
|
|
27
|
+
vaultAccInterestPerShareFp,
|
|
28
|
+
positionInterestIndexSnapshotFp
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return (deposited * deltaFp) / INTEREST_FP_SCALE;
|
|
32
|
+
}
|
|
33
|
+
|
|
19
34
|
export async function depositToPosition(
|
|
20
35
|
params: BuildDepositToPositionParams
|
|
21
36
|
) {
|
|
@@ -50,14 +65,23 @@ export async function withdrawAllFromPosition(
|
|
|
50
65
|
position.totalInterestEarned,
|
|
51
66
|
position.interestClaimed
|
|
52
67
|
);
|
|
53
|
-
const
|
|
68
|
+
const pendingInterest = calculatePendingInterest(
|
|
69
|
+
position.deposited,
|
|
70
|
+
vault.accInterestPerShareFp,
|
|
71
|
+
position.interestIndexSnapshotFp
|
|
72
|
+
);
|
|
73
|
+
const userMax = position.deposited + unclaimedInterest + pendingInterest;
|
|
54
74
|
const poolAvailable = positiveDiff(vault.totalLiquidity, vault.totalLoans);
|
|
55
75
|
const amount = userMax < poolAvailable ? userMax : poolAvailable;
|
|
56
76
|
if (amount <= 0n) {
|
|
57
77
|
throw new Error("No withdrawable balance available right now.");
|
|
58
78
|
}
|
|
59
79
|
|
|
60
|
-
const built = await buildWithdrawFromPositionTransaction({
|
|
80
|
+
const built = await buildWithdrawFromPositionTransaction({
|
|
81
|
+
...params,
|
|
82
|
+
amount,
|
|
83
|
+
vaultMint: vault.mint,
|
|
84
|
+
});
|
|
61
85
|
return { instructions: built.instructions, amount };
|
|
62
86
|
}
|
|
63
87
|
|
|
@@ -83,13 +107,26 @@ export async function withdrawInterestFromPosition(
|
|
|
83
107
|
position.totalInterestEarned,
|
|
84
108
|
position.interestClaimed
|
|
85
109
|
);
|
|
110
|
+
const pendingInterest = calculatePendingInterest(
|
|
111
|
+
position.deposited,
|
|
112
|
+
vault.accInterestPerShareFp,
|
|
113
|
+
position.interestIndexSnapshotFp
|
|
114
|
+
);
|
|
115
|
+
const totalClaimableInterest = unclaimedInterest + pendingInterest;
|
|
86
116
|
const poolAvailable = positiveDiff(vault.totalLiquidity, vault.totalLoans);
|
|
87
|
-
const amount =
|
|
117
|
+
const amount =
|
|
118
|
+
totalClaimableInterest < poolAvailable
|
|
119
|
+
? totalClaimableInterest
|
|
120
|
+
: poolAvailable;
|
|
88
121
|
if (amount <= 0n) {
|
|
89
122
|
throw new Error("No claimable interest available right now.");
|
|
90
123
|
}
|
|
91
124
|
|
|
92
|
-
const built = await buildWithdrawFromPositionTransaction({
|
|
125
|
+
const built = await buildWithdrawFromPositionTransaction({
|
|
126
|
+
...params,
|
|
127
|
+
amount,
|
|
128
|
+
vaultMint: vault.mint,
|
|
129
|
+
});
|
|
93
130
|
return { instructions: built.instructions, amount };
|
|
94
131
|
}
|
|
95
132
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Address } from "@solana/kit";
|
|
2
|
+
import bs58 from "bs58";
|
|
3
|
+
import { toAddress } from "../client/program";
|
|
4
|
+
import type { AddressLike, KitRpc } from "../client/types";
|
|
5
|
+
import { fetchMarketDataAccount } from "../accounts/fetchers";
|
|
6
|
+
import { invariant } from "../shared/errors";
|
|
7
|
+
|
|
8
|
+
export async function resolveSwitchboardFeedFromMarketData(
|
|
9
|
+
rpc: KitRpc,
|
|
10
|
+
marketData: AddressLike
|
|
11
|
+
): Promise<Address> {
|
|
12
|
+
const account = await fetchMarketDataAccount(rpc, marketData);
|
|
13
|
+
invariant(!!account, "Market data account not found.");
|
|
14
|
+
return toAddress(
|
|
15
|
+
bs58.encode(
|
|
16
|
+
Array.from(account.switchboardFeedId as unknown as Uint8Array)
|
|
17
|
+
)
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SwitchboardPullFeedLike<TInstruction = unknown, TLookupTable = unknown> {
|
|
22
|
+
fetchUpdateIx(args: {
|
|
23
|
+
crossbarClient: unknown;
|
|
24
|
+
chain?: "solana";
|
|
25
|
+
network?: "devnet" | "mainnet";
|
|
26
|
+
}): Promise<[TInstruction | null, unknown, unknown, TLookupTable[]]>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface BuildSwitchboardPullFeedUpdateParams<
|
|
30
|
+
TInstruction = unknown,
|
|
31
|
+
TLookupTable = unknown,
|
|
32
|
+
> {
|
|
33
|
+
pullFeed: SwitchboardPullFeedLike<TInstruction, TLookupTable>;
|
|
34
|
+
crossbarClient: unknown;
|
|
35
|
+
chain?: "solana";
|
|
36
|
+
network?: "devnet" | "mainnet";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function buildSwitchboardPullFeedUpdate<
|
|
40
|
+
TInstruction = unknown,
|
|
41
|
+
TLookupTable = unknown,
|
|
42
|
+
>(
|
|
43
|
+
params: BuildSwitchboardPullFeedUpdateParams<TInstruction, TLookupTable>
|
|
44
|
+
): Promise<{ updateInstructions: TInstruction[]; lookupTables: TLookupTable[] }> {
|
|
45
|
+
const [pullIx, _responses, _success, luts] = await params.pullFeed.fetchUpdateIx({
|
|
46
|
+
crossbarClient: params.crossbarClient,
|
|
47
|
+
chain: params.chain ?? "solana",
|
|
48
|
+
network: params.network,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const updateInstructions: TInstruction[] = pullIx ? [pullIx] : [];
|
|
52
|
+
return {
|
|
53
|
+
updateInstructions,
|
|
54
|
+
lookupTables: luts ?? [],
|
|
55
|
+
};
|
|
56
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epicentral/sos-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1-alpha",
|
|
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",
|
package/short/builders.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import type { Instruction, TransactionSigner } from "@solana/kit";
|
|
9
9
|
import { toAddress } from "../client/program";
|
|
10
10
|
import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
|
|
11
|
-
import { fetchVault } from "../accounts/fetchers";
|
|
11
|
+
import { fetchMarketDataAccount, fetchVault } from "../accounts/fetchers";
|
|
12
12
|
import { fetchPoolLoansByMaker } from "../accounts/list";
|
|
13
13
|
import { resolveOptionAccounts } from "../accounts/resolve-option";
|
|
14
14
|
import {
|
|
@@ -25,11 +25,13 @@ import {
|
|
|
25
25
|
type RemainingAccountInput,
|
|
26
26
|
} from "../shared/remaining-accounts";
|
|
27
27
|
import {
|
|
28
|
+
getCloseAccountInstruction,
|
|
28
29
|
NATIVE_MINT,
|
|
29
30
|
getCreateAssociatedTokenIdempotentInstructionWithAddress,
|
|
30
31
|
getWrapSOLInstructions,
|
|
31
32
|
} from "../wsol/instructions";
|
|
32
33
|
import { preflightUnwindWriterUnsold } from "./preflight";
|
|
34
|
+
import bs58 from "bs58";
|
|
33
35
|
|
|
34
36
|
export interface BuildOptionMintParams {
|
|
35
37
|
optionType: OptionType;
|
|
@@ -49,11 +51,8 @@ export interface BuildOptionMintParams {
|
|
|
49
51
|
maker: AddressLike;
|
|
50
52
|
makerCollateralAccount: AddressLike;
|
|
51
53
|
underlyingMint: AddressLike;
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
* Required to convert USD collateral requirement to token units.
|
|
55
|
-
*/
|
|
56
|
-
priceUpdate: AddressLike;
|
|
54
|
+
/** Switchboard pull feed account for collateral calculation. */
|
|
55
|
+
switchboardFeed: AddressLike;
|
|
57
56
|
longMetadataAccount?: AddressLike;
|
|
58
57
|
shortMetadataAccount?: AddressLike;
|
|
59
58
|
optionAccount?: AddressLike;
|
|
@@ -75,6 +74,12 @@ export interface BuildOptionMintParams {
|
|
|
75
74
|
escrowAuthority?: AddressLike;
|
|
76
75
|
escrowTokenAccount?: AddressLike;
|
|
77
76
|
poolLoan?: AddressLike;
|
|
77
|
+
/**
|
|
78
|
+
* When true (default), appends an SPL CloseAccount instruction after option_mint to close the
|
|
79
|
+
* maker's LONG token account (reclaim rent). The program transfers all LONG to escrow, so the
|
|
80
|
+
* maker's LONG ATA is left with zero balance and can be closed in the same transaction.
|
|
81
|
+
*/
|
|
82
|
+
closeMakerLongAccount?: boolean;
|
|
78
83
|
remainingAccounts?: RemainingAccountInput[];
|
|
79
84
|
}
|
|
80
85
|
|
|
@@ -193,7 +198,7 @@ export async function buildOptionMintInstruction(
|
|
|
193
198
|
? toAddress(params.escrowTokenAccount)
|
|
194
199
|
: undefined,
|
|
195
200
|
poolLoan: params.poolLoan ? toAddress(params.poolLoan) : undefined,
|
|
196
|
-
|
|
201
|
+
switchboardFeed: toAddress(params.switchboardFeed),
|
|
197
202
|
maker: toAddress(params.maker) as any,
|
|
198
203
|
optionType: params.optionType,
|
|
199
204
|
strikePrice: params.strikePrice,
|
|
@@ -213,7 +218,21 @@ export async function buildOptionMintTransaction(
|
|
|
213
218
|
params: BuildOptionMintParams
|
|
214
219
|
): Promise<BuiltTransaction> {
|
|
215
220
|
const instruction = await buildOptionMintInstruction(params);
|
|
216
|
-
|
|
221
|
+
const instructions: Instruction<string>[] = [instruction];
|
|
222
|
+
|
|
223
|
+
const shouldCloseMakerLong =
|
|
224
|
+
params.closeMakerLongAccount !== false && params.makerLongAccount != null;
|
|
225
|
+
if (shouldCloseMakerLong) {
|
|
226
|
+
instructions.push(
|
|
227
|
+
getCloseAccountInstruction(
|
|
228
|
+
params.makerLongAccount!,
|
|
229
|
+
params.maker,
|
|
230
|
+
params.maker
|
|
231
|
+
)
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return { instructions };
|
|
217
236
|
}
|
|
218
237
|
|
|
219
238
|
export interface BuildOptionMintTransactionWithDerivationParams {
|
|
@@ -238,11 +257,8 @@ export interface BuildOptionMintTransactionWithDerivationParams {
|
|
|
238
257
|
* (or underlyingMint if collateralMint is not provided).
|
|
239
258
|
*/
|
|
240
259
|
makerCollateralAccount?: AddressLike;
|
|
241
|
-
/**
|
|
242
|
-
|
|
243
|
-
* Required to convert USD collateral requirement to token units.
|
|
244
|
-
*/
|
|
245
|
-
priceUpdate: AddressLike;
|
|
260
|
+
/** Optional explicit Switchboard feed account override. */
|
|
261
|
+
switchboardFeed?: AddressLike;
|
|
246
262
|
rpc: KitRpc;
|
|
247
263
|
programId?: AddressLike;
|
|
248
264
|
vault?: AddressLike;
|
|
@@ -294,6 +310,16 @@ export async function buildOptionMintTransactionWithDerivation(
|
|
|
294
310
|
const makerCollateralAccount = params.makerCollateralAccount
|
|
295
311
|
? toAddress(params.makerCollateralAccount)
|
|
296
312
|
: await deriveAssociatedTokenAddress(params.maker, collateralMint);
|
|
313
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc, resolved.marketData);
|
|
314
|
+
invariant(
|
|
315
|
+
!!marketDataAccount,
|
|
316
|
+
"Market data account not found for resolved option market."
|
|
317
|
+
);
|
|
318
|
+
const switchboardFeed =
|
|
319
|
+
params.switchboardFeed ??
|
|
320
|
+
bs58.encode(
|
|
321
|
+
Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
322
|
+
);
|
|
297
323
|
|
|
298
324
|
const tx = await buildOptionMintTransaction({
|
|
299
325
|
...params,
|
|
@@ -313,6 +339,7 @@ export async function buildOptionMintTransactionWithDerivation(
|
|
|
313
339
|
premiumVault: resolved.premiumVault,
|
|
314
340
|
collateralPool: resolved.collateralPool,
|
|
315
341
|
collateralVault: resolved.collateralVault,
|
|
342
|
+
switchboardFeed,
|
|
316
343
|
vault: params.vault,
|
|
317
344
|
vaultTokenAccount: params.vaultTokenAccount,
|
|
318
345
|
escrowState: params.escrowState,
|