@epicentral/sos-sdk 0.6.1-alpha → 0.7.0-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 +37 -5
- package/client/lookup-table.ts +4 -2
- package/client/types.ts +1 -0
- package/generated/accounts/vault.ts +13 -1
- package/generated/instructions/index.ts +1 -0
- package/generated/instructions/omlpCreateVault.ts +6 -0
- package/generated/instructions/omlpUpdateMaxBorrowCap.ts +304 -0
- package/generated/instructions/optionMint.ts +6 -0
- package/generated/programs/optionProgram.ts +18 -2
- package/index.ts +7 -0
- package/long/builders.ts +67 -7
- package/long/exercise.ts +22 -3
- package/omlp/builders.ts +53 -3
- package/oracle/switchboard.ts +90 -2
- package/package.json +4 -1
- package/shared/trade-config.ts +27 -0
- package/shared/transactions.ts +37 -9
- package/short/builders.ts +45 -5
- package/short/preflight.ts +16 -13
package/long/builders.ts
CHANGED
|
@@ -24,7 +24,13 @@ import {
|
|
|
24
24
|
} from "../wsol/instructions";
|
|
25
25
|
import { fetchMarketDataAccount, fetchOptionPool } from "../accounts/fetchers";
|
|
26
26
|
import { getBuyFromPoolRemainingAccounts } from "./remaining-accounts";
|
|
27
|
+
import { applySlippageBps } from "./quotes";
|
|
28
|
+
import {
|
|
29
|
+
buildSwitchboardCrank,
|
|
30
|
+
prependSwitchboardCrank,
|
|
31
|
+
} from "../oracle/switchboard";
|
|
27
32
|
import { fetchWriterPositionsForPool } from "../accounts/list";
|
|
33
|
+
import { getGlobalTradeConfig } from "../shared/trade-config";
|
|
28
34
|
import bs58 from "bs58";
|
|
29
35
|
|
|
30
36
|
export interface BuildBuyFromPoolParams {
|
|
@@ -143,6 +149,9 @@ export interface BuildBuyFromPoolTransactionWithDerivationParams {
|
|
|
143
149
|
buyerPosition?: AddressLike;
|
|
144
150
|
buyerOptionAccount?: AddressLike;
|
|
145
151
|
remainingAccounts?: RemainingAccountInput[];
|
|
152
|
+
disableSwitchboardCrank?: boolean;
|
|
153
|
+
switchboardCrossbarUrl?: string;
|
|
154
|
+
switchboardNumSignatures?: number;
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
const DEFAULT_MARKET_ORDER_SLIPPAGE_BUFFER_BASE_UNITS = 500_000n;
|
|
@@ -224,7 +233,7 @@ export async function buildBuyFromPoolTransactionWithDerivation(
|
|
|
224
233
|
Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
225
234
|
);
|
|
226
235
|
|
|
227
|
-
|
|
236
|
+
const actionTx = await buildBuyFromPoolTransaction({
|
|
228
237
|
optionPool: resolved.optionPool,
|
|
229
238
|
optionAccount: resolved.optionAccount,
|
|
230
239
|
longMint: resolved.longMint,
|
|
@@ -241,6 +250,20 @@ export async function buildBuyFromPoolTransactionWithDerivation(
|
|
|
241
250
|
buyerOptionAccount,
|
|
242
251
|
remainingAccounts: params.remainingAccounts,
|
|
243
252
|
});
|
|
253
|
+
|
|
254
|
+
if (params.disableSwitchboardCrank) {
|
|
255
|
+
return actionTx;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const crank = await buildSwitchboardCrank({
|
|
259
|
+
rpc: params.rpc,
|
|
260
|
+
payer: params.buyer,
|
|
261
|
+
switchboardFeed,
|
|
262
|
+
marketData: resolved.marketData,
|
|
263
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
264
|
+
numSignatures: params.switchboardNumSignatures,
|
|
265
|
+
});
|
|
266
|
+
return prependSwitchboardCrank(crank, actionTx);
|
|
244
267
|
}
|
|
245
268
|
|
|
246
269
|
export interface BuildBuyFromPoolMarketOrderParams
|
|
@@ -311,10 +334,16 @@ export async function buildBuyFromPoolMarketOrderTransactionWithDerivation(
|
|
|
311
334
|
`This may indicate data staleness - please refresh and retry.`
|
|
312
335
|
);
|
|
313
336
|
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
337
|
+
const globalTradeConfig = getGlobalTradeConfig();
|
|
338
|
+
const hasExplicitSlippageBuffer =
|
|
339
|
+
params.slippageBufferBaseUnits !== undefined ||
|
|
340
|
+
params.slippageBufferLamports !== undefined;
|
|
341
|
+
const slippageBuffer = hasExplicitSlippageBuffer
|
|
342
|
+
? normalizeMarketOrderSlippageBuffer(params, refetchedPool.underlyingMint)
|
|
343
|
+
: globalTradeConfig.slippageBps !== undefined
|
|
344
|
+
? applySlippageBps(params.quotedPremiumTotal, globalTradeConfig.slippageBps) -
|
|
345
|
+
BigInt(params.quotedPremiumTotal)
|
|
346
|
+
: normalizeMarketOrderSlippageBuffer(params, refetchedPool.underlyingMint);
|
|
318
347
|
const maxPremiumAmount = BigInt(params.quotedPremiumTotal) + slippageBuffer;
|
|
319
348
|
assertPositiveAmount(maxPremiumAmount, "maxPremiumAmount");
|
|
320
349
|
|
|
@@ -329,7 +358,7 @@ export async function buildBuyFromPoolMarketOrderTransactionWithDerivation(
|
|
|
329
358
|
Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
330
359
|
);
|
|
331
360
|
|
|
332
|
-
|
|
361
|
+
const actionTx = await buildBuyFromPoolTransaction({
|
|
333
362
|
optionPool: resolved.optionPool,
|
|
334
363
|
optionAccount: resolved.optionAccount,
|
|
335
364
|
longMint: resolved.longMint,
|
|
@@ -346,6 +375,20 @@ export async function buildBuyFromPoolMarketOrderTransactionWithDerivation(
|
|
|
346
375
|
buyerOptionAccount,
|
|
347
376
|
remainingAccounts,
|
|
348
377
|
});
|
|
378
|
+
|
|
379
|
+
if (params.disableSwitchboardCrank) {
|
|
380
|
+
return actionTx;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const crank = await buildSwitchboardCrank({
|
|
384
|
+
rpc: params.rpc,
|
|
385
|
+
payer: params.buyer,
|
|
386
|
+
switchboardFeed,
|
|
387
|
+
marketData: resolved.marketData,
|
|
388
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
389
|
+
numSignatures: params.switchboardNumSignatures,
|
|
390
|
+
});
|
|
391
|
+
return prependSwitchboardCrank(crank, actionTx);
|
|
349
392
|
}
|
|
350
393
|
|
|
351
394
|
export async function buildCloseLongToPoolInstruction(
|
|
@@ -438,6 +481,9 @@ export interface BuildCloseLongToPoolTransactionWithDerivationParams {
|
|
|
438
481
|
*/
|
|
439
482
|
unwrapPayoutSol?: boolean;
|
|
440
483
|
remainingAccounts?: RemainingAccountInput[];
|
|
484
|
+
disableSwitchboardCrank?: boolean;
|
|
485
|
+
switchboardCrossbarUrl?: string;
|
|
486
|
+
switchboardNumSignatures?: number;
|
|
441
487
|
}
|
|
442
488
|
|
|
443
489
|
export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
@@ -485,7 +531,7 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
485
531
|
Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
486
532
|
);
|
|
487
533
|
|
|
488
|
-
|
|
534
|
+
const actionTx = await buildCloseLongToPoolTransaction({
|
|
489
535
|
optionPool: resolved.optionPool,
|
|
490
536
|
optionAccount: resolved.optionAccount,
|
|
491
537
|
collateralPool: resolved.collateralPool,
|
|
@@ -507,4 +553,18 @@ export async function buildCloseLongToPoolTransactionWithDerivation(
|
|
|
507
553
|
unwrapPayoutSol,
|
|
508
554
|
remainingAccounts: params.remainingAccounts,
|
|
509
555
|
});
|
|
556
|
+
|
|
557
|
+
if (params.disableSwitchboardCrank) {
|
|
558
|
+
return actionTx;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const crank = await buildSwitchboardCrank({
|
|
562
|
+
rpc: params.rpc,
|
|
563
|
+
payer: params.buyer,
|
|
564
|
+
switchboardFeed,
|
|
565
|
+
marketData: resolved.marketData,
|
|
566
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
567
|
+
numSignatures: params.switchboardNumSignatures,
|
|
568
|
+
});
|
|
569
|
+
return prependSwitchboardCrank(crank, actionTx);
|
|
510
570
|
}
|
package/long/exercise.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { getOptionExerciseInstruction } from "../generated/instructions";
|
|
2
2
|
import type { Instruction } from "@solana/kit";
|
|
3
3
|
import { toAddress } from "../client/program";
|
|
4
|
-
import type { AddressLike, BuiltTransaction } from "../client/types";
|
|
4
|
+
import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
|
|
5
|
+
import {
|
|
6
|
+
buildSwitchboardCrank,
|
|
7
|
+
prependSwitchboardCrank,
|
|
8
|
+
} from "../oracle/switchboard";
|
|
5
9
|
|
|
6
10
|
export interface BuildOptionExerciseParams {
|
|
7
11
|
optionAccount: AddressLike;
|
|
@@ -16,6 +20,10 @@ export interface BuildOptionExerciseParams {
|
|
|
16
20
|
escrowAuthority: AddressLike;
|
|
17
21
|
buyer: AddressLike;
|
|
18
22
|
tokenProgram?: AddressLike;
|
|
23
|
+
rpc?: KitRpc;
|
|
24
|
+
disableSwitchboardCrank?: boolean;
|
|
25
|
+
switchboardCrossbarUrl?: string;
|
|
26
|
+
switchboardNumSignatures?: number;
|
|
19
27
|
}
|
|
20
28
|
|
|
21
29
|
/**
|
|
@@ -48,7 +56,18 @@ export function buildOptionExerciseInstruction(
|
|
|
48
56
|
*/
|
|
49
57
|
export function buildOptionExerciseTransaction(
|
|
50
58
|
params: BuildOptionExerciseParams
|
|
51
|
-
): BuiltTransaction {
|
|
59
|
+
): Promise<BuiltTransaction> {
|
|
52
60
|
const instruction = buildOptionExerciseInstruction(params);
|
|
53
|
-
|
|
61
|
+
const actionTx = { instructions: [instruction] };
|
|
62
|
+
if (params.disableSwitchboardCrank || !params.rpc) {
|
|
63
|
+
return Promise.resolve(actionTx);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return buildSwitchboardCrank({
|
|
67
|
+
rpc: params.rpc,
|
|
68
|
+
payer: params.buyer,
|
|
69
|
+
switchboardFeed: params.switchboardFeed,
|
|
70
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
71
|
+
numSignatures: params.switchboardNumSignatures,
|
|
72
|
+
}).then((crank) => prependSwitchboardCrank(crank, actionTx));
|
|
54
73
|
}
|
package/omlp/builders.ts
CHANGED
|
@@ -4,9 +4,13 @@ import {
|
|
|
4
4
|
} from "../generated/instructions";
|
|
5
5
|
import type { Instruction } from "@solana/kit";
|
|
6
6
|
import { toAddress } from "../client/program";
|
|
7
|
-
import type { AddressLike, BuiltTransaction } from "../client/types";
|
|
7
|
+
import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
|
|
8
8
|
import { assertPositiveAmount } from "../shared/amounts";
|
|
9
9
|
import { getCloseAccountInstruction, NATIVE_MINT } from "../wsol/instructions";
|
|
10
|
+
import {
|
|
11
|
+
buildSwitchboardCrank,
|
|
12
|
+
prependSwitchboardCrank,
|
|
13
|
+
} from "../oracle/switchboard";
|
|
10
14
|
|
|
11
15
|
export interface BuildDepositToPositionParams {
|
|
12
16
|
vault: AddressLike;
|
|
@@ -15,6 +19,12 @@ export interface BuildDepositToPositionParams {
|
|
|
15
19
|
lender: AddressLike;
|
|
16
20
|
amount: bigint | number;
|
|
17
21
|
position?: AddressLike;
|
|
22
|
+
rpc?: KitRpc;
|
|
23
|
+
switchboardFeed?: AddressLike;
|
|
24
|
+
marketData?: AddressLike;
|
|
25
|
+
disableSwitchboardCrank?: boolean;
|
|
26
|
+
switchboardCrossbarUrl?: string;
|
|
27
|
+
switchboardNumSignatures?: number;
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
export interface BuildWithdrawFromPositionParams {
|
|
@@ -26,6 +36,12 @@ export interface BuildWithdrawFromPositionParams {
|
|
|
26
36
|
position?: AddressLike;
|
|
27
37
|
unwrapSol?: boolean;
|
|
28
38
|
vaultMint?: AddressLike;
|
|
39
|
+
rpc?: KitRpc;
|
|
40
|
+
switchboardFeed?: AddressLike;
|
|
41
|
+
marketData?: AddressLike;
|
|
42
|
+
disableSwitchboardCrank?: boolean;
|
|
43
|
+
switchboardCrossbarUrl?: string;
|
|
44
|
+
switchboardNumSignatures?: number;
|
|
29
45
|
}
|
|
30
46
|
|
|
31
47
|
export async function buildDepositToPositionInstruction(
|
|
@@ -49,7 +65,24 @@ export async function buildDepositToPositionTransaction(
|
|
|
49
65
|
params: BuildDepositToPositionParams
|
|
50
66
|
): Promise<BuiltTransaction> {
|
|
51
67
|
const instruction = await buildDepositToPositionInstruction(params);
|
|
52
|
-
|
|
68
|
+
const actionTx = { instructions: [instruction] };
|
|
69
|
+
if (
|
|
70
|
+
params.disableSwitchboardCrank ||
|
|
71
|
+
!params.rpc ||
|
|
72
|
+
(!params.switchboardFeed && !params.marketData)
|
|
73
|
+
) {
|
|
74
|
+
return actionTx;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const crank = await buildSwitchboardCrank({
|
|
78
|
+
rpc: params.rpc,
|
|
79
|
+
payer: params.lender,
|
|
80
|
+
switchboardFeed: params.switchboardFeed,
|
|
81
|
+
marketData: params.marketData,
|
|
82
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
83
|
+
numSignatures: params.switchboardNumSignatures,
|
|
84
|
+
});
|
|
85
|
+
return prependSwitchboardCrank(crank, actionTx);
|
|
53
86
|
}
|
|
54
87
|
|
|
55
88
|
export async function buildWithdrawFromPositionInstruction(
|
|
@@ -90,5 +123,22 @@ export async function buildWithdrawFromPositionTransaction(
|
|
|
90
123
|
);
|
|
91
124
|
}
|
|
92
125
|
|
|
93
|
-
|
|
126
|
+
const actionTx = { instructions };
|
|
127
|
+
if (
|
|
128
|
+
params.disableSwitchboardCrank ||
|
|
129
|
+
!params.rpc ||
|
|
130
|
+
(!params.switchboardFeed && !params.marketData)
|
|
131
|
+
) {
|
|
132
|
+
return actionTx;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const crank = await buildSwitchboardCrank({
|
|
136
|
+
rpc: params.rpc,
|
|
137
|
+
payer: params.lender,
|
|
138
|
+
switchboardFeed: params.switchboardFeed,
|
|
139
|
+
marketData: params.marketData,
|
|
140
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
141
|
+
numSignatures: params.switchboardNumSignatures,
|
|
142
|
+
});
|
|
143
|
+
return prependSwitchboardCrank(crank, actionTx);
|
|
94
144
|
}
|
package/oracle/switchboard.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import type { Address } from "@solana/kit";
|
|
1
|
+
import type { Address, Instruction } from "@solana/kit";
|
|
2
|
+
import { fromLegacyTransactionInstruction } from "@solana/compat";
|
|
3
|
+
import { CrossbarClient } from "@switchboard-xyz/common";
|
|
2
4
|
import bs58 from "bs58";
|
|
3
5
|
import { toAddress } from "../client/program";
|
|
4
|
-
import type { AddressLike, KitRpc } from "../client/types";
|
|
6
|
+
import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
|
|
5
7
|
import { fetchMarketDataAccount } from "../accounts/fetchers";
|
|
6
8
|
import { invariant } from "../shared/errors";
|
|
7
9
|
|
|
10
|
+
const DEVNET_GENESIS_HASH = "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG";
|
|
11
|
+
const MAINNET_BETA_GENESIS_HASH = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d";
|
|
12
|
+
|
|
13
|
+
export type SwitchboardNetwork = "devnet" | "mainnet";
|
|
14
|
+
|
|
8
15
|
export async function resolveSwitchboardFeedFromMarketData(
|
|
9
16
|
rpc: KitRpc,
|
|
10
17
|
marketData: AddressLike
|
|
@@ -54,3 +61,84 @@ export async function buildSwitchboardPullFeedUpdate<
|
|
|
54
61
|
lookupTables: luts ?? [],
|
|
55
62
|
};
|
|
56
63
|
}
|
|
64
|
+
|
|
65
|
+
export interface BuildSwitchboardCrankParams {
|
|
66
|
+
rpc: KitRpc;
|
|
67
|
+
payer: AddressLike;
|
|
68
|
+
switchboardFeed?: AddressLike;
|
|
69
|
+
marketData?: AddressLike;
|
|
70
|
+
network?: SwitchboardNetwork;
|
|
71
|
+
crossbarUrl?: string;
|
|
72
|
+
numSignatures?: number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface SwitchboardCrankResult {
|
|
76
|
+
instructions: Instruction<string>[];
|
|
77
|
+
addressLookupTableAddresses: AddressLike[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function inferSwitchboardNetwork(
|
|
81
|
+
rpc: KitRpc
|
|
82
|
+
): Promise<SwitchboardNetwork> {
|
|
83
|
+
const genesisHash = await rpc.getGenesisHash().send();
|
|
84
|
+
if (genesisHash === DEVNET_GENESIS_HASH) {
|
|
85
|
+
return "devnet";
|
|
86
|
+
}
|
|
87
|
+
if (genesisHash === MAINNET_BETA_GENESIS_HASH) {
|
|
88
|
+
return "mainnet";
|
|
89
|
+
}
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Unable to infer Switchboard network from genesis hash: ${genesisHash}`
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function buildSwitchboardCrank(
|
|
96
|
+
params: BuildSwitchboardCrankParams
|
|
97
|
+
): Promise<SwitchboardCrankResult> {
|
|
98
|
+
const resolvedFeed =
|
|
99
|
+
params.switchboardFeed ??
|
|
100
|
+
(params.marketData
|
|
101
|
+
? await resolveSwitchboardFeedFromMarketData(params.rpc, params.marketData)
|
|
102
|
+
: undefined);
|
|
103
|
+
|
|
104
|
+
invariant(
|
|
105
|
+
!!resolvedFeed,
|
|
106
|
+
"switchboardFeed or marketData is required to build Switchboard crank instructions."
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const network = params.network ?? (await inferSwitchboardNetwork(params.rpc));
|
|
110
|
+
const crossbar = params.crossbarUrl
|
|
111
|
+
? new CrossbarClient(params.crossbarUrl)
|
|
112
|
+
: CrossbarClient.default();
|
|
113
|
+
const updates = await crossbar.fetchSolanaUpdates(
|
|
114
|
+
network,
|
|
115
|
+
[toAddress(resolvedFeed)],
|
|
116
|
+
toAddress(params.payer),
|
|
117
|
+
params.numSignatures
|
|
118
|
+
);
|
|
119
|
+
const update = updates[0];
|
|
120
|
+
|
|
121
|
+
const instructions =
|
|
122
|
+
update?.pullIxns?.map((instruction) =>
|
|
123
|
+
fromLegacyTransactionInstruction(instruction)
|
|
124
|
+
) ?? [];
|
|
125
|
+
const addressLookupTableAddresses = update?.lookupTables ?? [];
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
instructions,
|
|
129
|
+
addressLookupTableAddresses,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function prependSwitchboardCrank(
|
|
134
|
+
crank: SwitchboardCrankResult,
|
|
135
|
+
action: BuiltTransaction
|
|
136
|
+
): BuiltTransaction {
|
|
137
|
+
return {
|
|
138
|
+
instructions: [...crank.instructions, ...action.instructions],
|
|
139
|
+
addressLookupTableAddresses: [
|
|
140
|
+
...(crank.addressLookupTableAddresses ?? []),
|
|
141
|
+
...(action.addressLookupTableAddresses ?? []),
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epicentral/sos-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0-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",
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
"@solana-program/address-lookup-table": "^0.11.0",
|
|
19
19
|
"@solana-program/compute-budget": "^0.13.0",
|
|
20
20
|
"@solana-program/system": "^0.11.0",
|
|
21
|
+
"@solana/compat": "^6.1.0",
|
|
21
22
|
"@solana/kit": "^6.1.0",
|
|
23
|
+
"@switchboard-xyz/common": "^5.7.0",
|
|
24
|
+
"@switchboard-xyz/on-demand": "^3.9.0",
|
|
22
25
|
"bs58": "^6.0.0",
|
|
23
26
|
"decimal.js": "^10.4.3"
|
|
24
27
|
},
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface TradeConfig {
|
|
2
|
+
slippageBps?: number;
|
|
3
|
+
computeUnitLimit?: number;
|
|
4
|
+
computeUnitPriceMicroLamports?: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
let globalTradeConfig: TradeConfig = {};
|
|
8
|
+
|
|
9
|
+
export function setGlobalTradeConfig(config: TradeConfig): void {
|
|
10
|
+
globalTradeConfig = { ...config };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function updateGlobalTradeConfig(config: Partial<TradeConfig>): void {
|
|
14
|
+
globalTradeConfig = { ...globalTradeConfig, ...config };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getGlobalTradeConfig(): TradeConfig {
|
|
18
|
+
return { ...globalTradeConfig };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resetGlobalTradeConfig(): void {
|
|
22
|
+
globalTradeConfig = {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function resolveTradeConfig(overrides?: Partial<TradeConfig>): TradeConfig {
|
|
26
|
+
return { ...globalTradeConfig, ...overrides };
|
|
27
|
+
}
|
package/shared/transactions.ts
CHANGED
|
@@ -21,6 +21,11 @@ import {
|
|
|
21
21
|
import type { Instruction } from "@solana/kit";
|
|
22
22
|
import { toAddress } from "../client/program";
|
|
23
23
|
import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
|
|
24
|
+
import {
|
|
25
|
+
getLookupTableAddressForNetwork,
|
|
26
|
+
type LookupTableNetwork,
|
|
27
|
+
} from "../client/lookup-table";
|
|
28
|
+
import { getGlobalTradeConfig } from "./trade-config";
|
|
24
29
|
|
|
25
30
|
export interface SendBuiltTransactionParams extends BuiltTransaction {
|
|
26
31
|
rpc: KitRpc;
|
|
@@ -29,7 +34,18 @@ export interface SendBuiltTransactionParams extends BuiltTransaction {
|
|
|
29
34
|
commitment?: "processed" | "confirmed" | "finalized";
|
|
30
35
|
computeUnitLimit?: number;
|
|
31
36
|
computeUnitPriceMicroLamports?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Address Lookup Table addresses to compress the transaction.
|
|
39
|
+
* REQUIRED for option_mint and other large transactions to avoid
|
|
40
|
+
* "encoding overruns Uint8Array" (Solana's 1232-byte tx limit).
|
|
41
|
+
* Use getLookupTableAddressForNetwork(network) or pass network to auto-include.
|
|
42
|
+
*/
|
|
32
43
|
addressLookupTableAddresses?: AddressLike[];
|
|
44
|
+
/**
|
|
45
|
+
* When set, automatically includes the option program's lookup table for this network.
|
|
46
|
+
* Use this when sending option_mint, buy_from_pool, or other large transactions.
|
|
47
|
+
*/
|
|
48
|
+
network?: LookupTableNetwork;
|
|
33
49
|
}
|
|
34
50
|
|
|
35
51
|
/**
|
|
@@ -41,23 +57,38 @@ export async function sendBuiltTransaction(
|
|
|
41
57
|
params: SendBuiltTransactionParams
|
|
42
58
|
): Promise<string> {
|
|
43
59
|
const commitment = params.commitment ?? "confirmed";
|
|
60
|
+
const globalTradeConfig = getGlobalTradeConfig();
|
|
44
61
|
const { value: latestBlockhash } = await params.rpc.getLatestBlockhash().send();
|
|
45
62
|
|
|
46
63
|
const computeBudgetInstructions: Instruction<string>[] = [];
|
|
47
|
-
|
|
64
|
+
const computeUnitLimit =
|
|
65
|
+
params.computeUnitLimit ?? globalTradeConfig.computeUnitLimit;
|
|
66
|
+
if (computeUnitLimit !== undefined) {
|
|
48
67
|
computeBudgetInstructions.push(
|
|
49
|
-
getSetComputeUnitLimitInstruction({ units:
|
|
68
|
+
getSetComputeUnitLimitInstruction({ units: computeUnitLimit })
|
|
50
69
|
);
|
|
51
70
|
}
|
|
52
|
-
|
|
71
|
+
const computeUnitPriceMicroLamports =
|
|
72
|
+
params.computeUnitPriceMicroLamports ??
|
|
73
|
+
globalTradeConfig.computeUnitPriceMicroLamports;
|
|
74
|
+
if (computeUnitPriceMicroLamports !== undefined) {
|
|
53
75
|
computeBudgetInstructions.push(
|
|
54
76
|
getSetComputeUnitPriceInstruction({
|
|
55
|
-
microLamports:
|
|
77
|
+
microLamports: computeUnitPriceMicroLamports,
|
|
56
78
|
})
|
|
57
79
|
);
|
|
58
80
|
}
|
|
59
81
|
const allInstructions = [...computeBudgetInstructions, ...params.instructions];
|
|
60
82
|
|
|
83
|
+
// Resolve address lookup tables: explicit list, or from network for option program
|
|
84
|
+
let addressLookupTableAddresses = params.addressLookupTableAddresses ?? [];
|
|
85
|
+
if (params.network) {
|
|
86
|
+
const programAlt = getLookupTableAddressForNetwork(params.network);
|
|
87
|
+
if (programAlt && !addressLookupTableAddresses.some((a) => String(a) === String(programAlt))) {
|
|
88
|
+
addressLookupTableAddresses = [programAlt, ...addressLookupTableAddresses];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
61
92
|
let txMessage = pipe(
|
|
62
93
|
createTransactionMessage({ version: 0 }),
|
|
63
94
|
(tx) => setTransactionMessageFeePayerSigner(params.feePayer, tx),
|
|
@@ -65,12 +96,9 @@ export async function sendBuiltTransaction(
|
|
|
65
96
|
(tx) => appendTransactionMessageInstructions(allInstructions, tx)
|
|
66
97
|
);
|
|
67
98
|
|
|
68
|
-
if (
|
|
69
|
-
params.addressLookupTableAddresses &&
|
|
70
|
-
params.addressLookupTableAddresses.length > 0
|
|
71
|
-
) {
|
|
99
|
+
if (addressLookupTableAddresses.length > 0) {
|
|
72
100
|
const addressesByAddressLookupTable: AddressesByLookupTableAddress = {};
|
|
73
|
-
for (const altAddress of
|
|
101
|
+
for (const altAddress of addressLookupTableAddresses) {
|
|
74
102
|
const resolvedAddress = toAddress(altAddress);
|
|
75
103
|
const { data } = await fetchAddressLookupTable(params.rpc, resolvedAddress);
|
|
76
104
|
addressesByAddressLookupTable[resolvedAddress] = data.addresses;
|
package/short/builders.ts
CHANGED
|
@@ -31,6 +31,12 @@ import {
|
|
|
31
31
|
getWrapSOLInstructions,
|
|
32
32
|
} from "../wsol/instructions";
|
|
33
33
|
import { preflightUnwindWriterUnsold } from "./preflight";
|
|
34
|
+
import {
|
|
35
|
+
buildSwitchboardCrank,
|
|
36
|
+
prependSwitchboardCrank,
|
|
37
|
+
} from "../oracle/switchboard";
|
|
38
|
+
import { applySlippageBps } from "../long/quotes";
|
|
39
|
+
import { getGlobalTradeConfig } from "../shared/trade-config";
|
|
34
40
|
import bs58 from "bs58";
|
|
35
41
|
|
|
36
42
|
export interface BuildOptionMintParams {
|
|
@@ -48,6 +54,7 @@ export interface BuildOptionMintParams {
|
|
|
48
54
|
collateralMint?: AddressLike;
|
|
49
55
|
makerCollateralAmount: bigint | number;
|
|
50
56
|
borrowedAmount: bigint | number;
|
|
57
|
+
maxRequiredCollateralAmount?: bigint | number;
|
|
51
58
|
maker: AddressLike;
|
|
52
59
|
makerCollateralAccount: AddressLike;
|
|
53
60
|
underlyingMint: AddressLike;
|
|
@@ -133,6 +140,16 @@ export async function buildOptionMintInstruction(
|
|
|
133
140
|
invariant(params.underlyingSymbol.length > 0, "underlyingSymbol is required.");
|
|
134
141
|
|
|
135
142
|
const borrowedAmount = BigInt(params.borrowedAmount);
|
|
143
|
+
const globalTradeConfig = getGlobalTradeConfig();
|
|
144
|
+
const maxRequiredCollateralAmount =
|
|
145
|
+
params.maxRequiredCollateralAmount !== undefined
|
|
146
|
+
? BigInt(params.maxRequiredCollateralAmount)
|
|
147
|
+
: globalTradeConfig.slippageBps !== undefined
|
|
148
|
+
? applySlippageBps(
|
|
149
|
+
BigInt(params.makerCollateralAmount) + borrowedAmount,
|
|
150
|
+
globalTradeConfig.slippageBps
|
|
151
|
+
)
|
|
152
|
+
: BigInt(params.makerCollateralAmount) + borrowedAmount;
|
|
136
153
|
if (borrowedAmount > 0n) {
|
|
137
154
|
invariant(!!params.vault, "vault is required when borrowedAmount > 0");
|
|
138
155
|
invariant(
|
|
@@ -209,6 +226,7 @@ export async function buildOptionMintInstruction(
|
|
|
209
226
|
collateralMintArg: toAddress(params.collateralMint ?? params.underlyingMint),
|
|
210
227
|
makerCollateralAmount: params.makerCollateralAmount,
|
|
211
228
|
borrowedAmount: params.borrowedAmount,
|
|
229
|
+
maxRequiredCollateralAmount,
|
|
212
230
|
});
|
|
213
231
|
|
|
214
232
|
return appendRemainingAccounts(kitInstruction, params.remainingAccounts);
|
|
@@ -251,6 +269,7 @@ export interface BuildOptionMintTransactionWithDerivationParams {
|
|
|
251
269
|
collateralMint?: AddressLike;
|
|
252
270
|
makerCollateralAmount: bigint | number;
|
|
253
271
|
borrowedAmount: bigint | number;
|
|
272
|
+
maxRequiredCollateralAmount?: bigint | number;
|
|
254
273
|
maker: AddressLike;
|
|
255
274
|
/**
|
|
256
275
|
* Optional. When omitted, the SDK derives the maker's collateral ATA for collateralMint
|
|
@@ -268,6 +287,9 @@ export interface BuildOptionMintTransactionWithDerivationParams {
|
|
|
268
287
|
escrowTokenAccount?: AddressLike;
|
|
269
288
|
poolLoan?: AddressLike;
|
|
270
289
|
remainingAccounts?: RemainingAccountInput[];
|
|
290
|
+
disableSwitchboardCrank?: boolean;
|
|
291
|
+
switchboardCrossbarUrl?: string;
|
|
292
|
+
switchboardNumSignatures?: number;
|
|
271
293
|
}
|
|
272
294
|
|
|
273
295
|
export async function buildOptionMintTransactionWithDerivation(
|
|
@@ -357,9 +379,23 @@ export async function buildOptionMintTransactionWithDerivation(
|
|
|
357
379
|
makerCollateralAccount
|
|
358
380
|
);
|
|
359
381
|
|
|
360
|
-
|
|
382
|
+
const actionTx = {
|
|
361
383
|
instructions: [createAtaIx, ...tx.instructions],
|
|
362
384
|
};
|
|
385
|
+
|
|
386
|
+
if (params.disableSwitchboardCrank) {
|
|
387
|
+
return actionTx;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const crank = await buildSwitchboardCrank({
|
|
391
|
+
rpc: params.rpc,
|
|
392
|
+
payer: params.maker,
|
|
393
|
+
switchboardFeed,
|
|
394
|
+
marketData: resolved.marketData,
|
|
395
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
396
|
+
numSignatures: params.switchboardNumSignatures,
|
|
397
|
+
});
|
|
398
|
+
return prependSwitchboardCrank(crank, actionTx);
|
|
363
399
|
}
|
|
364
400
|
|
|
365
401
|
export async function buildUnwindWriterUnsoldInstruction(
|
|
@@ -480,8 +516,12 @@ export async function buildUnwindWriterUnsoldWithLoanRepayment(
|
|
|
480
516
|
]);
|
|
481
517
|
|
|
482
518
|
const vaultLoans = loans
|
|
483
|
-
.filter((item) => toAddress(item.data.vault) === vaultPdaStr)
|
|
484
|
-
|
|
519
|
+
.filter((item) => toAddress(item.data.vault) === vaultPdaStr);
|
|
520
|
+
|
|
521
|
+
invariant(
|
|
522
|
+
vaultLoans.length <= MAX_POOL_LOANS_PER_UNWIND,
|
|
523
|
+
`Too many active pool loans for unwind: ${vaultLoans.length}. Max supported in one unwind is ${MAX_POOL_LOANS_PER_UNWIND}.`
|
|
524
|
+
);
|
|
485
525
|
|
|
486
526
|
const remainingAccounts: RemainingAccountInput[] = vaultLoans.map((item) => ({
|
|
487
527
|
address: item.address,
|
|
@@ -515,8 +555,8 @@ export async function buildUnwindWriterUnsoldWithLoanRepayment(
|
|
|
515
555
|
: 0n;
|
|
516
556
|
|
|
517
557
|
invariant(
|
|
518
|
-
preflight.
|
|
519
|
-
`Unwind cannot
|
|
558
|
+
preflight.canRepayRequestedSlice,
|
|
559
|
+
`Unwind cannot repay the requested slice: required=${preflight.summary.proportionalTotalOwed} available_now=${preflight.summary.collateralVaultAvailable + preflight.summary.walletFallbackAvailable} native_sol_available=${preflight.summary.nativeSolAvailable} remaining_shortfall=${preflight.summary.proportionalTotalOwed - (preflight.summary.collateralVaultAvailable + preflight.summary.walletFallbackAvailable + preflight.summary.nativeSolAvailable)}`
|
|
520
560
|
);
|
|
521
561
|
if (isWsolPath && lamportsToWrap > 0n && !params.includeWrapForShortfall) {
|
|
522
562
|
invariant(
|