@epicentral/sos-sdk 0.15.2-beta → 0.16.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/README.md +4 -0
- package/generated/errors/optionProgram.ts +12 -0
- package/generated/instructions/index.ts +2 -0
- package/generated/instructions/initOptionSeries.ts +971 -0
- package/generated/instructions/optionMintMulti.ts +607 -0
- package/generated/programs/optionProgram.ts +32 -0
- package/generated/types/index.ts +1 -0
- package/generated/types/mintLegArgs.ts +67 -0
- package/index.ts +1 -0
- package/package.json +1 -1
- package/shared/option-program-parser.ts +12 -0
- package/short/multi-mint.ts +403 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This code was AUTOGENERATED using the Codama library.
|
|
3
|
+
* Please DO NOT EDIT THIS FILE, instead use visitors
|
|
4
|
+
* to add features, then rerun Codama to update it.
|
|
5
|
+
*
|
|
6
|
+
* @see https://github.com/codama-idl/codama
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
combineCodec,
|
|
11
|
+
getF64Decoder,
|
|
12
|
+
getF64Encoder,
|
|
13
|
+
getStructDecoder,
|
|
14
|
+
getStructEncoder,
|
|
15
|
+
getU64Decoder,
|
|
16
|
+
getU64Encoder,
|
|
17
|
+
type FixedSizeCodec,
|
|
18
|
+
type FixedSizeDecoder,
|
|
19
|
+
type FixedSizeEncoder,
|
|
20
|
+
} from "@solana/kit";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* One leg of a multi-strike mint. All amounts in base units
|
|
24
|
+
* (1 contract = 1_000_000; collateral in collateral-mint base units).
|
|
25
|
+
*/
|
|
26
|
+
export type MintLegArgs = {
|
|
27
|
+
strikePrice: number;
|
|
28
|
+
quantity: bigint;
|
|
29
|
+
makerCollateralAmount: bigint;
|
|
30
|
+
borrowedAmount: bigint;
|
|
31
|
+
maxRequiredCollateralAmount: bigint;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type MintLegArgsArgs = {
|
|
35
|
+
strikePrice: number;
|
|
36
|
+
quantity: number | bigint;
|
|
37
|
+
makerCollateralAmount: number | bigint;
|
|
38
|
+
borrowedAmount: number | bigint;
|
|
39
|
+
maxRequiredCollateralAmount: number | bigint;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function getMintLegArgsEncoder(): FixedSizeEncoder<MintLegArgsArgs> {
|
|
43
|
+
return getStructEncoder([
|
|
44
|
+
["strikePrice", getF64Encoder()],
|
|
45
|
+
["quantity", getU64Encoder()],
|
|
46
|
+
["makerCollateralAmount", getU64Encoder()],
|
|
47
|
+
["borrowedAmount", getU64Encoder()],
|
|
48
|
+
["maxRequiredCollateralAmount", getU64Encoder()],
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getMintLegArgsDecoder(): FixedSizeDecoder<MintLegArgs> {
|
|
53
|
+
return getStructDecoder([
|
|
54
|
+
["strikePrice", getF64Decoder()],
|
|
55
|
+
["quantity", getU64Decoder()],
|
|
56
|
+
["makerCollateralAmount", getU64Decoder()],
|
|
57
|
+
["borrowedAmount", getU64Decoder()],
|
|
58
|
+
["maxRequiredCollateralAmount", getU64Decoder()],
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getMintLegArgsCodec(): FixedSizeCodec<
|
|
63
|
+
MintLegArgsArgs,
|
|
64
|
+
MintLegArgs
|
|
65
|
+
> {
|
|
66
|
+
return combineCodec(getMintLegArgsEncoder(), getMintLegArgsDecoder());
|
|
67
|
+
}
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
parseInitConfigInstruction,
|
|
24
24
|
parseInitializeMarketDataInstruction,
|
|
25
25
|
parseInitOptionPoolInstruction,
|
|
26
|
+
parseInitOptionSeriesInstruction,
|
|
26
27
|
parseLiquidateWriterPositionInstruction,
|
|
27
28
|
parseLiquidateWriterPositionRescueInstruction,
|
|
28
29
|
parseMigrateCollateralPoolV1ToV2Instruction,
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
parseOmlpUpdateSupplyLimitInstruction,
|
|
39
40
|
parseOptionExerciseInstruction,
|
|
40
41
|
parseOptionMintInstruction,
|
|
42
|
+
parseOptionMintMultiInstruction,
|
|
41
43
|
parseOptionValidateInstruction,
|
|
42
44
|
parsePrepareBuyerSettlementInstruction,
|
|
43
45
|
parsePrepareMakerSettlementInstruction,
|
|
@@ -162,6 +164,11 @@ export function parseOptionProgramInstruction<
|
|
|
162
164
|
parseInitConfigInstruction(instruction),
|
|
163
165
|
instructionType,
|
|
164
166
|
);
|
|
167
|
+
case OptionProgramInstruction.InitOptionSeries:
|
|
168
|
+
return withInstructionType(
|
|
169
|
+
parseInitOptionSeriesInstruction(instruction),
|
|
170
|
+
instructionType,
|
|
171
|
+
);
|
|
165
172
|
case OptionProgramInstruction.InitOptionPool:
|
|
166
173
|
return withInstructionType(
|
|
167
174
|
parseInitOptionPoolInstruction(instruction),
|
|
@@ -247,6 +254,11 @@ export function parseOptionProgramInstruction<
|
|
|
247
254
|
parseOptionMintInstruction(instruction),
|
|
248
255
|
instructionType,
|
|
249
256
|
);
|
|
257
|
+
case OptionProgramInstruction.OptionMintMulti:
|
|
258
|
+
return withInstructionType(
|
|
259
|
+
parseOptionMintMultiInstruction(instruction),
|
|
260
|
+
instructionType,
|
|
261
|
+
);
|
|
250
262
|
case OptionProgramInstruction.OptionValidate:
|
|
251
263
|
return withInstructionType(
|
|
252
264
|
parseOptionValidateInstruction(instruction),
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-strike minting builders (Phase 2).
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. `deriveOptionSeriesLegAddresses` per strike (pure derivation).
|
|
6
|
+
* 2. `findLegsNeedingSeriesInit` — checks which strikes still need
|
|
7
|
+
* `init_option_series` (missing option account OR missing writer position
|
|
8
|
+
* for this maker). Series init creates NO Metaplex metadata: the option's
|
|
9
|
+
* identity lives on the OptionAccount PDA and clients render names from
|
|
10
|
+
* chain state.
|
|
11
|
+
* 3. `buildInitOptionSeriesInstruction` per needed strike (chunk 2 per tx).
|
|
12
|
+
* 4. `buildOptionMintMultiTransactionWithDerivation` — chunks legs into
|
|
13
|
+
* transactions of ≤ MAX_MINT_LEGS, one Switchboard quote per chunk, and
|
|
14
|
+
* per-leg account bundles in remaining_accounts after the quote triple.
|
|
15
|
+
*
|
|
16
|
+
* Per-leg remaining_accounts stride (must match the program's
|
|
17
|
+
* `multi_mint::MINT_LEG_ACCOUNT_STRIDE`):
|
|
18
|
+
* [option_account, option_pool, collateral_pool, collateral_vault,
|
|
19
|
+
* long_mint, short_mint, escrow_long_account, maker_short_account,
|
|
20
|
+
* writer_position, (pool_loan when leg.borrowedAmount > 0)]
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type { Address, Instruction, TransactionSigner } from "@solana/kit";
|
|
24
|
+
import {
|
|
25
|
+
getInitOptionSeriesInstructionAsync,
|
|
26
|
+
getOptionMintMultiInstructionAsync,
|
|
27
|
+
type OptionType,
|
|
28
|
+
} from "../generated";
|
|
29
|
+
import { PROGRAM_ID, toAddress } from "../client/program";
|
|
30
|
+
import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
|
|
31
|
+
import { fetchMarketDataAccount } from "../accounts/fetchers";
|
|
32
|
+
import {
|
|
33
|
+
deriveAssociatedTokenAddress,
|
|
34
|
+
deriveCollateralPoolPda,
|
|
35
|
+
deriveLongMintPda,
|
|
36
|
+
deriveMarketDataPda,
|
|
37
|
+
deriveOptionAccountPda,
|
|
38
|
+
deriveOptionPoolPda,
|
|
39
|
+
derivePoolLoanPdaFromWriterPosition,
|
|
40
|
+
deriveShortMintPda,
|
|
41
|
+
deriveWriterPositionPda,
|
|
42
|
+
} from "../accounts/pdas";
|
|
43
|
+
import { invariant } from "../shared/errors";
|
|
44
|
+
import {
|
|
45
|
+
appendRemainingAccounts,
|
|
46
|
+
type RemainingAccountInput,
|
|
47
|
+
} from "../shared/remaining-accounts";
|
|
48
|
+
import {
|
|
49
|
+
buildSwitchboardQuoteInstruction,
|
|
50
|
+
feedIdBytesToHex,
|
|
51
|
+
inferSwitchboardNetwork,
|
|
52
|
+
INSTRUCTIONS_SYSVAR_ADDRESS,
|
|
53
|
+
SLOT_HASHES_SYSVAR_ADDRESS,
|
|
54
|
+
SWITCHBOARD_DEFAULT_DEVNET_QUEUE,
|
|
55
|
+
} from "../oracle/switchboard";
|
|
56
|
+
import { deriveVolatilityQuoteAddressFromMarketData } from "../oracle/volatility-quote";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Effective legs per `option_mint_multi` transaction. The on-chain ceiling is
|
|
60
|
+
* 4 (`multi_mint::MAX_MINT_LEGS`); 3 keeps the quote + multi-mint transaction
|
|
61
|
+
* comfortably inside the 1232-byte wire limit with ALT compression and the
|
|
62
|
+
* ~1.4M CU budget.
|
|
63
|
+
*/
|
|
64
|
+
export const MAX_MINT_LEGS = 3;
|
|
65
|
+
|
|
66
|
+
/** Series-init instructions that fit in one transaction (no oracle quote needed). */
|
|
67
|
+
export const MAX_SERIES_INITS_PER_TRANSACTION = 2;
|
|
68
|
+
|
|
69
|
+
export interface MultiMintLegInput {
|
|
70
|
+
strikePrice: number;
|
|
71
|
+
/** Contracts in base units (1 contract = 1_000_000). */
|
|
72
|
+
quantity: bigint;
|
|
73
|
+
/** Maker's own deposit for this leg (collateral base units). */
|
|
74
|
+
makerCollateralAmount: bigint;
|
|
75
|
+
/** Borrowed portion for this leg (collateral base units). */
|
|
76
|
+
borrowedAmount: bigint;
|
|
77
|
+
/** Slippage guard: max on-chain required collateral accepted (base units). */
|
|
78
|
+
maxRequiredCollateralAmount: bigint;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface OptionSeriesLegAddresses {
|
|
82
|
+
strikePrice: number;
|
|
83
|
+
optionAccount: Address;
|
|
84
|
+
longMint: Address;
|
|
85
|
+
shortMint: Address;
|
|
86
|
+
optionPool: Address;
|
|
87
|
+
collateralPool: Address;
|
|
88
|
+
collateralVault: Address;
|
|
89
|
+
escrowLongAccount: Address;
|
|
90
|
+
premiumVault: Address;
|
|
91
|
+
makerShortAccount: Address;
|
|
92
|
+
writerPosition: Address;
|
|
93
|
+
poolLoan: Address;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function deriveOptionSeriesLegAddresses(params: {
|
|
97
|
+
underlyingAsset: AddressLike;
|
|
98
|
+
optionType: OptionType;
|
|
99
|
+
strikePrice: number;
|
|
100
|
+
expirationDate: bigint | number;
|
|
101
|
+
collateralMint: AddressLike;
|
|
102
|
+
maker: AddressLike;
|
|
103
|
+
programId?: AddressLike;
|
|
104
|
+
}): Promise<OptionSeriesLegAddresses> {
|
|
105
|
+
const programId = params.programId ?? PROGRAM_ID;
|
|
106
|
+
const [optionAccount] = await deriveOptionAccountPda({
|
|
107
|
+
underlyingAsset: params.underlyingAsset,
|
|
108
|
+
optionType: params.optionType,
|
|
109
|
+
strikePrice: params.strikePrice,
|
|
110
|
+
expirationDate: params.expirationDate,
|
|
111
|
+
programId,
|
|
112
|
+
});
|
|
113
|
+
const [[longMint], [shortMint], [optionPool], [collateralPool]] = await Promise.all([
|
|
114
|
+
deriveLongMintPda(optionAccount, programId),
|
|
115
|
+
deriveShortMintPda(optionAccount, programId),
|
|
116
|
+
deriveOptionPoolPda(optionAccount, programId),
|
|
117
|
+
deriveCollateralPoolPda(optionAccount, programId),
|
|
118
|
+
]);
|
|
119
|
+
const [[writerPosition], collateralVault, escrowLongAccount, premiumVault] =
|
|
120
|
+
await Promise.all([
|
|
121
|
+
deriveWriterPositionPda(optionPool, params.maker, programId),
|
|
122
|
+
deriveAssociatedTokenAddress(collateralPool, params.collateralMint, true),
|
|
123
|
+
deriveAssociatedTokenAddress(optionPool, longMint, true),
|
|
124
|
+
deriveAssociatedTokenAddress(optionPool, params.collateralMint, true),
|
|
125
|
+
]);
|
|
126
|
+
const [makerShortAccount, [poolLoan]] = await Promise.all([
|
|
127
|
+
deriveAssociatedTokenAddress(params.maker, shortMint),
|
|
128
|
+
derivePoolLoanPdaFromWriterPosition(writerPosition, programId),
|
|
129
|
+
]);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
strikePrice: params.strikePrice,
|
|
133
|
+
optionAccount,
|
|
134
|
+
longMint,
|
|
135
|
+
shortMint,
|
|
136
|
+
optionPool,
|
|
137
|
+
collateralPool,
|
|
138
|
+
collateralVault,
|
|
139
|
+
escrowLongAccount,
|
|
140
|
+
premiumVault,
|
|
141
|
+
makerShortAccount,
|
|
142
|
+
writerPosition,
|
|
143
|
+
poolLoan,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Strikes that still need `init_option_series` before `option_mint_multi`:
|
|
149
|
+
* the option account is missing OR the maker's writer position has not been
|
|
150
|
+
* allocated yet (both are created by series init).
|
|
151
|
+
*/
|
|
152
|
+
export async function findLegsNeedingSeriesInit(
|
|
153
|
+
rpc: KitRpc,
|
|
154
|
+
legs: OptionSeriesLegAddresses[]
|
|
155
|
+
): Promise<OptionSeriesLegAddresses[]> {
|
|
156
|
+
if (legs.length === 0) return [];
|
|
157
|
+
const addresses = legs.flatMap((leg) => [leg.optionAccount, leg.writerPosition]);
|
|
158
|
+
const response = await rpc
|
|
159
|
+
.getMultipleAccounts(addresses, { encoding: "base64" })
|
|
160
|
+
.send();
|
|
161
|
+
const needing: OptionSeriesLegAddresses[] = [];
|
|
162
|
+
legs.forEach((leg, i) => {
|
|
163
|
+
const optionInfo = response.value[i * 2];
|
|
164
|
+
const writerInfo = response.value[i * 2 + 1];
|
|
165
|
+
if (!optionInfo || !writerInfo) {
|
|
166
|
+
needing.push(leg);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return needing;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface BuildInitOptionSeriesParams {
|
|
173
|
+
underlyingAsset: AddressLike;
|
|
174
|
+
underlyingMint: AddressLike;
|
|
175
|
+
collateralMint: AddressLike;
|
|
176
|
+
optionType: OptionType;
|
|
177
|
+
strikePrice: number;
|
|
178
|
+
expirationDate: bigint | number;
|
|
179
|
+
maker: TransactionSigner<string>;
|
|
180
|
+
volatilityQuoteAccount: AddressLike;
|
|
181
|
+
programId?: AddressLike;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** One `init_option_series` instruction (no oracle quote, no metadata CPIs). */
|
|
185
|
+
export async function buildInitOptionSeriesInstruction(
|
|
186
|
+
params: BuildInitOptionSeriesParams
|
|
187
|
+
): Promise<Instruction<string>> {
|
|
188
|
+
return await getInitOptionSeriesInstructionAsync(
|
|
189
|
+
{
|
|
190
|
+
volatilityQuoteAccount: toAddress(params.volatilityQuoteAccount),
|
|
191
|
+
underlyingMint: toAddress(params.underlyingMint),
|
|
192
|
+
collateralMint: toAddress(params.collateralMint),
|
|
193
|
+
maker: params.maker,
|
|
194
|
+
optionType: params.optionType,
|
|
195
|
+
strikePrice: params.strikePrice,
|
|
196
|
+
expirationDate: params.expirationDate,
|
|
197
|
+
underlyingAsset: toAddress(params.underlyingAsset),
|
|
198
|
+
},
|
|
199
|
+
{ programAddress: params.programId ? toAddress(params.programId) : PROGRAM_ID }
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface BuildOptionMintMultiParams {
|
|
204
|
+
underlyingAsset: AddressLike;
|
|
205
|
+
underlyingMint: AddressLike;
|
|
206
|
+
/** Collateral mint shared by every leg (one collateral pool family). */
|
|
207
|
+
collateralMint: AddressLike;
|
|
208
|
+
optionType: OptionType;
|
|
209
|
+
expirationDate: bigint | number;
|
|
210
|
+
legs: MultiMintLegInput[];
|
|
211
|
+
maker: TransactionSigner<string>;
|
|
212
|
+
/** Maker's collateral ATA; derived when omitted. */
|
|
213
|
+
makerCollateralAccount?: AddressLike;
|
|
214
|
+
rpc: KitRpc;
|
|
215
|
+
rpcEndpoint: string;
|
|
216
|
+
programId?: AddressLike;
|
|
217
|
+
/** Required when any leg borrows. */
|
|
218
|
+
vault?: AddressLike;
|
|
219
|
+
escrowState?: AddressLike;
|
|
220
|
+
escrowAuthority?: AddressLike;
|
|
221
|
+
escrowTokenAccount?: AddressLike;
|
|
222
|
+
/** Legs per transaction (default {@link MAX_MINT_LEGS}, on-chain max 4). */
|
|
223
|
+
maxLegsPerTransaction?: number;
|
|
224
|
+
switchboardCrossbarUrl?: string;
|
|
225
|
+
switchboardNumSignatures?: number;
|
|
226
|
+
/**
|
|
227
|
+
* 0-based index of the Switchboard quote ix in the FINAL transaction after
|
|
228
|
+
* wallet-prepended compute-budget ixs (OPX standard layout: 2).
|
|
229
|
+
*/
|
|
230
|
+
switchboardQuoteInstructionIndex?: number;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export interface BuiltOptionMintMultiChunk extends BuiltTransaction {
|
|
234
|
+
/** Legs included in this transaction (same order as the stride bundles). */
|
|
235
|
+
legs: MultiMintLegInput[];
|
|
236
|
+
legAddresses: OptionSeriesLegAddresses[];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export interface BuildOptionMintMultiResult {
|
|
240
|
+
/** One entry per transaction to send sequentially. */
|
|
241
|
+
chunks: BuiltOptionMintMultiChunk[];
|
|
242
|
+
/** Per-leg derived addresses (for ALT extension before sending). */
|
|
243
|
+
legAddresses: OptionSeriesLegAddresses[];
|
|
244
|
+
/**
|
|
245
|
+
* Every per-leg address referenced via remaining_accounts — extend the
|
|
246
|
+
* program lookup table with these before sending so each chunk compresses
|
|
247
|
+
* under the 1232-byte limit.
|
|
248
|
+
*/
|
|
249
|
+
lookupAddresses: Address[];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Build `option_mint_multi` transactions for a strike ladder. Every leg's
|
|
254
|
+
* series must already exist (`init_option_series`); use
|
|
255
|
+
* {@link findLegsNeedingSeriesInit} + {@link buildInitOptionSeriesInstruction}
|
|
256
|
+
* first.
|
|
257
|
+
*/
|
|
258
|
+
export async function buildOptionMintMultiTransactionWithDerivation(
|
|
259
|
+
params: BuildOptionMintMultiParams
|
|
260
|
+
): Promise<BuildOptionMintMultiResult> {
|
|
261
|
+
invariant(params.legs.length > 0, "at least one leg is required");
|
|
262
|
+
const maxLegs = params.maxLegsPerTransaction ?? MAX_MINT_LEGS;
|
|
263
|
+
invariant(maxLegs >= 1 && maxLegs <= 4, "maxLegsPerTransaction must be 1-4");
|
|
264
|
+
|
|
265
|
+
const anyBorrow = params.legs.some((leg) => leg.borrowedAmount > 0n);
|
|
266
|
+
if (anyBorrow) {
|
|
267
|
+
invariant(!!params.vault, "vault is required when any leg borrows");
|
|
268
|
+
invariant(!!params.escrowState, "escrowState is required when any leg borrows");
|
|
269
|
+
invariant(
|
|
270
|
+
!!params.escrowAuthority,
|
|
271
|
+
"escrowAuthority is required when any leg borrows"
|
|
272
|
+
);
|
|
273
|
+
invariant(
|
|
274
|
+
!!params.escrowTokenAccount,
|
|
275
|
+
"escrowTokenAccount is required when any leg borrows"
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const programId = params.programId ?? PROGRAM_ID;
|
|
280
|
+
const [marketData] = await deriveMarketDataPda(params.underlyingAsset, programId);
|
|
281
|
+
const marketDataAccount = await fetchMarketDataAccount(params.rpc, marketData);
|
|
282
|
+
invariant(!!marketDataAccount, "Market data account not found for underlying asset.");
|
|
283
|
+
const volatilityQuoteAccount =
|
|
284
|
+
deriveVolatilityQuoteAddressFromMarketData(marketDataAccount);
|
|
285
|
+
const switchboardFeedId = feedIdBytesToHex(
|
|
286
|
+
Uint8Array.from(marketDataAccount.switchboardFeedId as unknown as Uint8Array)
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const makerAddress = toAddress(params.maker.address);
|
|
290
|
+
const makerCollateralAccount = params.makerCollateralAccount
|
|
291
|
+
? toAddress(params.makerCollateralAccount)
|
|
292
|
+
: await deriveAssociatedTokenAddress(makerAddress, params.collateralMint);
|
|
293
|
+
|
|
294
|
+
const legAddresses = await Promise.all(
|
|
295
|
+
params.legs.map((leg) =>
|
|
296
|
+
deriveOptionSeriesLegAddresses({
|
|
297
|
+
underlyingAsset: params.underlyingAsset,
|
|
298
|
+
optionType: params.optionType,
|
|
299
|
+
strikePrice: leg.strikePrice,
|
|
300
|
+
expirationDate: params.expirationDate,
|
|
301
|
+
collateralMint: params.collateralMint,
|
|
302
|
+
maker: makerAddress,
|
|
303
|
+
programId,
|
|
304
|
+
})
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const quoteNetwork = await inferSwitchboardNetwork(params.rpc);
|
|
309
|
+
|
|
310
|
+
const chunks: BuiltOptionMintMultiChunk[] = [];
|
|
311
|
+
for (let start = 0; start < params.legs.length; start += maxLegs) {
|
|
312
|
+
const chunkLegs = params.legs.slice(start, start + maxLegs);
|
|
313
|
+
const chunkAddresses = legAddresses.slice(start, start + maxLegs);
|
|
314
|
+
|
|
315
|
+
const multiIx = await getOptionMintMultiInstructionAsync(
|
|
316
|
+
{
|
|
317
|
+
volatilityQuoteAccount: toAddress(volatilityQuoteAccount),
|
|
318
|
+
underlyingMint: toAddress(params.underlyingMint),
|
|
319
|
+
collateralMint: toAddress(params.collateralMint),
|
|
320
|
+
makerCollateralAccount,
|
|
321
|
+
vault: params.vault ? toAddress(params.vault) : undefined,
|
|
322
|
+
escrowState: params.escrowState ? toAddress(params.escrowState) : undefined,
|
|
323
|
+
escrowAuthority: params.escrowAuthority
|
|
324
|
+
? toAddress(params.escrowAuthority)
|
|
325
|
+
: undefined,
|
|
326
|
+
escrowTokenAccount: params.escrowTokenAccount
|
|
327
|
+
? toAddress(params.escrowTokenAccount)
|
|
328
|
+
: undefined,
|
|
329
|
+
maker: params.maker,
|
|
330
|
+
optionType: params.optionType,
|
|
331
|
+
expirationDate: params.expirationDate,
|
|
332
|
+
underlyingAsset: toAddress(params.underlyingAsset),
|
|
333
|
+
legs: chunkLegs.map((leg) => ({
|
|
334
|
+
strikePrice: leg.strikePrice,
|
|
335
|
+
quantity: leg.quantity,
|
|
336
|
+
makerCollateralAmount: leg.makerCollateralAmount,
|
|
337
|
+
borrowedAmount: leg.borrowedAmount,
|
|
338
|
+
maxRequiredCollateralAmount: leg.maxRequiredCollateralAmount,
|
|
339
|
+
})),
|
|
340
|
+
},
|
|
341
|
+
{ programAddress: toAddress(programId) }
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
const remaining: RemainingAccountInput[] = [
|
|
345
|
+
{ address: SWITCHBOARD_DEFAULT_DEVNET_QUEUE, isWritable: false },
|
|
346
|
+
{ address: SLOT_HASHES_SYSVAR_ADDRESS, isWritable: false },
|
|
347
|
+
{ address: INSTRUCTIONS_SYSVAR_ADDRESS, isWritable: false },
|
|
348
|
+
];
|
|
349
|
+
chunkLegs.forEach((leg, i) => {
|
|
350
|
+
const addrs = chunkAddresses[i];
|
|
351
|
+
remaining.push(
|
|
352
|
+
{ address: addrs.optionAccount, isWritable: true },
|
|
353
|
+
{ address: addrs.optionPool, isWritable: true },
|
|
354
|
+
{ address: addrs.collateralPool, isWritable: true },
|
|
355
|
+
{ address: addrs.collateralVault, isWritable: true },
|
|
356
|
+
{ address: addrs.longMint, isWritable: true },
|
|
357
|
+
{ address: addrs.shortMint, isWritable: true },
|
|
358
|
+
{ address: addrs.escrowLongAccount, isWritable: true },
|
|
359
|
+
{ address: addrs.makerShortAccount, isWritable: true },
|
|
360
|
+
{ address: addrs.writerPosition, isWritable: true }
|
|
361
|
+
);
|
|
362
|
+
if (leg.borrowedAmount > 0n) {
|
|
363
|
+
remaining.push({ address: addrs.poolLoan, isWritable: true });
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const fullIx = appendRemainingAccounts(multiIx, remaining);
|
|
368
|
+
|
|
369
|
+
const quote = await buildSwitchboardQuoteInstruction({
|
|
370
|
+
rpcEndpoint: params.rpcEndpoint,
|
|
371
|
+
feedIdHex: switchboardFeedId,
|
|
372
|
+
network: quoteNetwork,
|
|
373
|
+
crossbarUrl: params.switchboardCrossbarUrl,
|
|
374
|
+
numSignatures: params.switchboardNumSignatures,
|
|
375
|
+
instructionIdx: params.switchboardQuoteInstructionIndex ?? 1,
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
chunks.push({
|
|
379
|
+
instructions: [quote.instruction, fullIx],
|
|
380
|
+
legs: chunkLegs,
|
|
381
|
+
legAddresses: chunkAddresses,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const lookupAddresses = Array.from(
|
|
386
|
+
new Set(
|
|
387
|
+
legAddresses.flatMap((addrs) => [
|
|
388
|
+
addrs.optionAccount,
|
|
389
|
+
addrs.optionPool,
|
|
390
|
+
addrs.collateralPool,
|
|
391
|
+
addrs.collateralVault,
|
|
392
|
+
addrs.longMint,
|
|
393
|
+
addrs.shortMint,
|
|
394
|
+
addrs.escrowLongAccount,
|
|
395
|
+
addrs.makerShortAccount,
|
|
396
|
+
addrs.writerPosition,
|
|
397
|
+
addrs.poolLoan,
|
|
398
|
+
])
|
|
399
|
+
)
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
return { chunks, legAddresses, lookupAddresses };
|
|
403
|
+
}
|