@epicentral/sos-sdk 0.11.0-beta → 0.11.2-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.
@@ -96,7 +96,12 @@ export async function fetchAccountData(
96
96
  rpc: KitRpc,
97
97
  address: AddressLike
98
98
  ): Promise<Uint8Array | null> {
99
- const response = await rpc.getAccountInfo(toAddress(address), { encoding: "base64" }).send();
99
+ const response = await rpc
100
+ .getAccountInfo(toAddress(address), {
101
+ encoding: "base64",
102
+ commitment: "confirmed",
103
+ })
104
+ .send();
100
105
  const accountInfo = response.value;
101
106
  if (!accountInfo) return null;
102
107
  const [data] = accountInfo.data;
@@ -201,7 +206,12 @@ export async function accountExists(
201
206
  rpc: KitRpc,
202
207
  address: AddressLike
203
208
  ): Promise<boolean> {
204
- const response = await rpc.getAccountInfo(toAddress(address), { encoding: "base64" }).send();
209
+ const response = await rpc
210
+ .getAccountInfo(toAddress(address), {
211
+ encoding: "base64",
212
+ commitment: "confirmed",
213
+ })
214
+ .send();
205
215
  return response.value !== null;
206
216
  }
207
217
 
@@ -210,7 +220,9 @@ export async function fetchManyAccounts(
210
220
  addresses: AddressLike[]
211
221
  ): Promise<Array<{ address: Address; exists: boolean }>> {
212
222
  const keys = addresses.map((value) => toAddress(value));
213
- const response = await rpc.getMultipleAccounts(keys, { encoding: "base64" }).send();
223
+ const response = await rpc
224
+ .getMultipleAccounts(keys, { encoding: "base64", commitment: "confirmed" })
225
+ .send();
214
226
  const infos = response.value;
215
227
  return keys.map((key, index) => ({
216
228
  address: key,
@@ -230,7 +242,9 @@ export async function fetchOptionAccounts(
230
242
  ): Promise<Map<Address, OptionAccount | null>> {
231
243
  if (addresses.length === 0) return new Map();
232
244
  const keys = addresses.map((value) => toAddress(value));
233
- const response = await rpc.getMultipleAccounts(keys, { encoding: "base64" }).send();
245
+ const response = await rpc
246
+ .getMultipleAccounts(keys, { encoding: "base64", commitment: "confirmed" })
247
+ .send();
234
248
  const infos = response.value;
235
249
  const result = new Map<Address, OptionAccount | null>();
236
250
  for (let i = 0; i < keys.length; i++) {
package/accounts/list.ts CHANGED
@@ -89,6 +89,7 @@ async function fetchAndDecodeProgramAccounts<T>(
89
89
  const response = await rpc
90
90
  .getProgramAccounts(toAddress(programAddress), {
91
91
  encoding: "base64",
92
+ commitment: "confirmed",
92
93
  filters: filters as never,
93
94
  })
94
95
  .send();
@@ -158,13 +159,19 @@ export async function fetchPositionAccountsByBuyer(
158
159
 
159
160
  export async function fetchPoolLoansByMaker(
160
161
  rpc: KitRpc,
161
- maker: AddressLike
162
+ maker: AddressLike,
163
+ programId?: AddressLike
162
164
  ): Promise<Array<ListedAccount<PoolLoan>>> {
163
- const decoded = await fetchAndDecodeProgramAccounts(rpc, getPoolLoanDecoder(), [
164
- discriminatorFilter(POOL_LOAN_DISCRIMINATOR),
165
- ownerFilter(maker),
166
- { dataSize: BigInt(getPoolLoanSize()) },
167
- ]);
165
+ const decoded = await fetchAndDecodeProgramAccounts(
166
+ rpc,
167
+ getPoolLoanDecoder(),
168
+ [
169
+ discriminatorFilter(POOL_LOAN_DISCRIMINATOR),
170
+ ownerFilter(maker),
171
+ { dataSize: BigInt(getPoolLoanSize()) },
172
+ ],
173
+ programId ?? PROGRAM_ID
174
+ );
168
175
  return decoded.filter(
169
176
  (item: { address: Address; data: PoolLoan }) =>
170
177
  item.data.status === ACTIVE_POOL_LOAN_STATUS
package/index.ts CHANGED
@@ -5,9 +5,11 @@ export { OptionType } from "./generated/types";
5
5
 
6
6
  export type { OptionAccount } from "./generated/accounts/optionAccount";
7
7
  export {
8
+ identifyOptionProgramInstruction,
8
9
  OptionProgramInstruction,
9
- parseOptionProgramInstruction,
10
+ type ParsedOptionProgramInstruction,
10
11
  } from "./generated/programs/optionProgram";
12
+ export { parseOptionProgramInstruction } from "./shared/option-program-parser";
11
13
 
12
14
  export * from "./accounts/pdas";
13
15
  export * from "./accounts/fetchers";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicentral/sos-sdk",
3
- "version": "0.11.0-beta",
3
+ "version": "0.11.2-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",
@@ -0,0 +1,317 @@
1
+ import type {
2
+ AccountMeta,
3
+ Instruction,
4
+ InstructionWithAccounts,
5
+ InstructionWithData,
6
+ ReadonlyUint8Array,
7
+ } from "@solana/kit";
8
+ import {
9
+ parseAcceptAdminInstruction,
10
+ parseAutoExerciseAllExpiredInstruction,
11
+ parseAutoExerciseExpiredInstruction,
12
+ parseBorrowFromPoolInstruction,
13
+ parseBuyFromPoolInstruction,
14
+ parseClaimBuyerSettlementInstruction,
15
+ parseClaimMakerRemainingInstruction,
16
+ parseClaimMakerSettlementInstruction,
17
+ parseCloseLongToPoolInstruction,
18
+ parseCloseOptionInstruction,
19
+ parseCreateEscrowV2Instruction,
20
+ parseDepositCollateralInstruction,
21
+ parseDepositToPositionInstruction,
22
+ parseInitCollateralPoolInstruction,
23
+ parseInitConfigInstruction,
24
+ parseInitializeMarketDataInstruction,
25
+ parseInitOptionPoolInstruction,
26
+ parseLiquidateWriterPositionInstruction,
27
+ parseLiquidateWriterPositionRescueInstruction,
28
+ parseMigrateCollateralPoolV1ToV2Instruction,
29
+ parseOmlpCreateVaultInstruction,
30
+ parseOmlpUpdateFeeWalletInstruction,
31
+ parseOmlpUpdateInterestModelInstruction,
32
+ parseOmlpUpdateLiquidationThresholdInstruction,
33
+ parseOmlpUpdateMaintenanceBufferInstruction,
34
+ parseOmlpUpdateMaxBorrowCapInstruction,
35
+ parseOmlpUpdateMaxLeverageInstruction,
36
+ parseOmlpUpdateProtocolFeeInstruction,
37
+ parseOmlpUpdateSupplyLimitInstruction,
38
+ parseOptionExerciseInstruction,
39
+ parseOptionMintInstruction,
40
+ parseOptionValidateInstruction,
41
+ parsePrepareBuyerSettlementInstruction,
42
+ parsePrepareMakerSettlementInstruction,
43
+ parseRepayPoolLoanFromCollateralInstruction,
44
+ parseRepayPoolLoanFromWalletInstruction,
45
+ parseRepayPoolLoanInstruction,
46
+ parseSettleMakerCollateralInstruction,
47
+ parseSyncWriterPositionInstruction,
48
+ parseTransferAdminInstruction,
49
+ parseUnwindWriterUnsoldInstruction,
50
+ parseUpdateImpliedVolatilityInstruction,
51
+ parseUpdateMarketDataInstruction,
52
+ parseWithdrawFromPositionInstruction,
53
+ parseWriteToPoolInstruction,
54
+ } from "../generated/instructions";
55
+ import {
56
+ identifyOptionProgramInstruction,
57
+ OptionProgramInstruction,
58
+ type ParsedOptionProgramInstruction,
59
+ } from "../generated/programs/optionProgram";
60
+
61
+ type OptionProgramInstructionInput<
62
+ TProgram extends string,
63
+ TAccountMetas extends readonly AccountMeta[],
64
+ > = Instruction<TProgram> &
65
+ InstructionWithAccounts<TAccountMetas> &
66
+ InstructionWithData<ReadonlyUint8Array>;
67
+
68
+ function withInstructionType<TProgram extends string>(
69
+ parsed: Omit<ParsedOptionProgramInstruction<TProgram>, "instructionType">,
70
+ instructionType: ParsedOptionProgramInstruction<TProgram>["instructionType"],
71
+ ): ParsedOptionProgramInstruction<TProgram> {
72
+ return {
73
+ instructionType,
74
+ ...parsed,
75
+ } as ParsedOptionProgramInstruction<TProgram>;
76
+ }
77
+
78
+ export function parseOptionProgramInstruction<
79
+ TProgram extends string,
80
+ TAccountMetas extends readonly AccountMeta[],
81
+ >(
82
+ instruction: OptionProgramInstructionInput<TProgram, TAccountMetas>,
83
+ ): ParsedOptionProgramInstruction<TProgram> {
84
+ const instructionType = identifyOptionProgramInstruction(instruction);
85
+
86
+ switch (instructionType) {
87
+ case OptionProgramInstruction.AcceptAdmin:
88
+ return withInstructionType(
89
+ parseAcceptAdminInstruction(instruction),
90
+ instructionType,
91
+ );
92
+ case OptionProgramInstruction.AutoExerciseAllExpired:
93
+ return withInstructionType(
94
+ parseAutoExerciseAllExpiredInstruction(instruction),
95
+ instructionType,
96
+ );
97
+ case OptionProgramInstruction.AutoExerciseExpired:
98
+ return withInstructionType(
99
+ parseAutoExerciseExpiredInstruction(instruction),
100
+ instructionType,
101
+ );
102
+ case OptionProgramInstruction.BorrowFromPool:
103
+ return withInstructionType(
104
+ parseBorrowFromPoolInstruction(instruction),
105
+ instructionType,
106
+ );
107
+ case OptionProgramInstruction.BuyFromPool:
108
+ return withInstructionType(
109
+ parseBuyFromPoolInstruction(instruction),
110
+ instructionType,
111
+ );
112
+ case OptionProgramInstruction.ClaimBuyerSettlement:
113
+ return withInstructionType(
114
+ parseClaimBuyerSettlementInstruction(instruction),
115
+ instructionType,
116
+ );
117
+ case OptionProgramInstruction.ClaimMakerRemaining:
118
+ return withInstructionType(
119
+ parseClaimMakerRemainingInstruction(instruction),
120
+ instructionType,
121
+ );
122
+ case OptionProgramInstruction.ClaimMakerSettlement:
123
+ return withInstructionType(
124
+ parseClaimMakerSettlementInstruction(instruction),
125
+ instructionType,
126
+ );
127
+ case OptionProgramInstruction.CloseLongToPool:
128
+ return withInstructionType(
129
+ parseCloseLongToPoolInstruction(instruction),
130
+ instructionType,
131
+ );
132
+ case OptionProgramInstruction.CloseOption:
133
+ return withInstructionType(
134
+ parseCloseOptionInstruction(instruction),
135
+ instructionType,
136
+ );
137
+ case OptionProgramInstruction.CreateEscrowV2:
138
+ return withInstructionType(
139
+ parseCreateEscrowV2Instruction(instruction),
140
+ instructionType,
141
+ );
142
+ case OptionProgramInstruction.DepositCollateral:
143
+ return withInstructionType(
144
+ parseDepositCollateralInstruction(instruction),
145
+ instructionType,
146
+ );
147
+ case OptionProgramInstruction.DepositToPosition:
148
+ return withInstructionType(
149
+ parseDepositToPositionInstruction(instruction),
150
+ instructionType,
151
+ );
152
+ case OptionProgramInstruction.InitCollateralPool:
153
+ return withInstructionType(
154
+ parseInitCollateralPoolInstruction(instruction),
155
+ instructionType,
156
+ );
157
+ case OptionProgramInstruction.InitConfig:
158
+ return withInstructionType(
159
+ parseInitConfigInstruction(instruction),
160
+ instructionType,
161
+ );
162
+ case OptionProgramInstruction.InitOptionPool:
163
+ return withInstructionType(
164
+ parseInitOptionPoolInstruction(instruction),
165
+ instructionType,
166
+ );
167
+ case OptionProgramInstruction.InitializeMarketData:
168
+ return withInstructionType(
169
+ parseInitializeMarketDataInstruction(instruction),
170
+ instructionType,
171
+ );
172
+ case OptionProgramInstruction.LiquidateWriterPosition:
173
+ return withInstructionType(
174
+ parseLiquidateWriterPositionInstruction(instruction),
175
+ instructionType,
176
+ );
177
+ case OptionProgramInstruction.LiquidateWriterPositionRescue:
178
+ return withInstructionType(
179
+ parseLiquidateWriterPositionRescueInstruction(instruction),
180
+ instructionType,
181
+ );
182
+ case OptionProgramInstruction.MigrateCollateralPoolV1ToV2:
183
+ return withInstructionType(
184
+ parseMigrateCollateralPoolV1ToV2Instruction(instruction),
185
+ instructionType,
186
+ );
187
+ case OptionProgramInstruction.OmlpCreateVault:
188
+ return withInstructionType(
189
+ parseOmlpCreateVaultInstruction(instruction),
190
+ instructionType,
191
+ );
192
+ case OptionProgramInstruction.OmlpUpdateFeeWallet:
193
+ return withInstructionType(
194
+ parseOmlpUpdateFeeWalletInstruction(instruction),
195
+ instructionType,
196
+ );
197
+ case OptionProgramInstruction.OmlpUpdateInterestModel:
198
+ return withInstructionType(
199
+ parseOmlpUpdateInterestModelInstruction(instruction),
200
+ instructionType,
201
+ );
202
+ case OptionProgramInstruction.OmlpUpdateLiquidationThreshold:
203
+ return withInstructionType(
204
+ parseOmlpUpdateLiquidationThresholdInstruction(instruction),
205
+ instructionType,
206
+ );
207
+ case OptionProgramInstruction.OmlpUpdateMaintenanceBuffer:
208
+ return withInstructionType(
209
+ parseOmlpUpdateMaintenanceBufferInstruction(instruction),
210
+ instructionType,
211
+ );
212
+ case OptionProgramInstruction.OmlpUpdateMaxBorrowCap:
213
+ return withInstructionType(
214
+ parseOmlpUpdateMaxBorrowCapInstruction(instruction),
215
+ instructionType,
216
+ );
217
+ case OptionProgramInstruction.OmlpUpdateMaxLeverage:
218
+ return withInstructionType(
219
+ parseOmlpUpdateMaxLeverageInstruction(instruction),
220
+ instructionType,
221
+ );
222
+ case OptionProgramInstruction.OmlpUpdateProtocolFee:
223
+ return withInstructionType(
224
+ parseOmlpUpdateProtocolFeeInstruction(instruction),
225
+ instructionType,
226
+ );
227
+ case OptionProgramInstruction.OmlpUpdateSupplyLimit:
228
+ return withInstructionType(
229
+ parseOmlpUpdateSupplyLimitInstruction(instruction),
230
+ instructionType,
231
+ );
232
+ case OptionProgramInstruction.OptionExercise:
233
+ return withInstructionType(
234
+ parseOptionExerciseInstruction(instruction),
235
+ instructionType,
236
+ );
237
+ case OptionProgramInstruction.OptionMint:
238
+ return withInstructionType(
239
+ parseOptionMintInstruction(instruction),
240
+ instructionType,
241
+ );
242
+ case OptionProgramInstruction.OptionValidate:
243
+ return withInstructionType(
244
+ parseOptionValidateInstruction(instruction),
245
+ instructionType,
246
+ );
247
+ case OptionProgramInstruction.PrepareBuyerSettlement:
248
+ return withInstructionType(
249
+ parsePrepareBuyerSettlementInstruction(instruction),
250
+ instructionType,
251
+ );
252
+ case OptionProgramInstruction.PrepareMakerSettlement:
253
+ return withInstructionType(
254
+ parsePrepareMakerSettlementInstruction(instruction),
255
+ instructionType,
256
+ );
257
+ case OptionProgramInstruction.RepayPoolLoan:
258
+ return withInstructionType(
259
+ parseRepayPoolLoanInstruction(instruction),
260
+ instructionType,
261
+ );
262
+ case OptionProgramInstruction.RepayPoolLoanFromCollateral:
263
+ return withInstructionType(
264
+ parseRepayPoolLoanFromCollateralInstruction(instruction),
265
+ instructionType,
266
+ );
267
+ case OptionProgramInstruction.RepayPoolLoanFromWallet:
268
+ return withInstructionType(
269
+ parseRepayPoolLoanFromWalletInstruction(instruction),
270
+ instructionType,
271
+ );
272
+ case OptionProgramInstruction.SettleMakerCollateral:
273
+ return withInstructionType(
274
+ parseSettleMakerCollateralInstruction(instruction),
275
+ instructionType,
276
+ );
277
+ case OptionProgramInstruction.SyncWriterPosition:
278
+ return withInstructionType(
279
+ parseSyncWriterPositionInstruction(instruction),
280
+ instructionType,
281
+ );
282
+ case OptionProgramInstruction.TransferAdmin:
283
+ return withInstructionType(
284
+ parseTransferAdminInstruction(instruction),
285
+ instructionType,
286
+ );
287
+ case OptionProgramInstruction.UnwindWriterUnsold:
288
+ return withInstructionType(
289
+ parseUnwindWriterUnsoldInstruction(instruction),
290
+ instructionType,
291
+ );
292
+ case OptionProgramInstruction.UpdateImpliedVolatility:
293
+ return withInstructionType(
294
+ parseUpdateImpliedVolatilityInstruction(instruction),
295
+ instructionType,
296
+ );
297
+ case OptionProgramInstruction.UpdateMarketData:
298
+ return withInstructionType(
299
+ parseUpdateMarketDataInstruction(instruction),
300
+ instructionType,
301
+ );
302
+ case OptionProgramInstruction.WithdrawFromPosition:
303
+ return withInstructionType(
304
+ parseWithdrawFromPositionInstruction(instruction),
305
+ instructionType,
306
+ );
307
+ case OptionProgramInstruction.WriteToPool:
308
+ return withInstructionType(
309
+ parseWriteToPoolInstruction(instruction),
310
+ instructionType,
311
+ );
312
+ default: {
313
+ const exhaustiveCheck: never = instructionType;
314
+ throw new Error(`Unsupported option program instruction: ${exhaustiveCheck}`);
315
+ }
316
+ }
317
+ }
package/short/builders.ts CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  type OptionType,
12
12
  } from "../generated";
13
13
  import type { Instruction, TransactionSigner } from "@solana/kit";
14
- import { toAddress } from "../client/program";
14
+ import { PROGRAM_ID, toAddress } from "../client/program";
15
15
  import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
16
16
  import { fetchMarketDataAccount, fetchVault, fetchWriterPosition } from "../accounts/fetchers";
17
17
  import { fetchPoolLoansByMaker } from "../accounts/list";
@@ -87,6 +87,8 @@ export interface BuildOptionMintParams {
87
87
  escrowAuthority?: AddressLike;
88
88
  escrowTokenAccount?: AddressLike;
89
89
  poolLoan?: AddressLike;
90
+ /** Override program id for instruction encoding (must match PDA derivation program). */
91
+ programId?: AddressLike;
90
92
  /**
91
93
  * When true (default), appends an SPL CloseAccount instruction after option_mint to close the
92
94
  * maker's LONG token account (reclaim rent). The program transfers all LONG to escrow, so the
@@ -239,53 +241,56 @@ export async function buildOptionMintInstruction(
239
241
  "longMetadataAccount and shortMetadataAccount are required (or provide longMint/shortMint to derive)."
240
242
  );
241
243
 
242
- const kitInstruction = await getOptionMintInstructionAsync({
243
- optionAccount: params.optionAccount ? toAddress(params.optionAccount) : undefined,
244
- longMint: params.longMint ? toAddress(params.longMint) : undefined,
245
- shortMint: params.shortMint ? toAddress(params.shortMint) : undefined,
246
- mintAuthority: params.mintAuthority ? toAddress(params.mintAuthority) : undefined,
247
- makerLongAccount: params.makerLongAccount
248
- ? toAddress(params.makerLongAccount)
249
- : undefined,
250
- makerShortAccount: params.makerShortAccount
251
- ? toAddress(params.makerShortAccount)
252
- : undefined,
253
- longMetadataAccount: toAddress(longMetadata!),
254
- shortMetadataAccount: toAddress(shortMetadata!),
255
- marketData: params.marketData ? toAddress(params.marketData) : undefined,
256
- underlyingMint: toAddress(params.underlyingMint),
257
- collateralMint: toAddress(params.collateralMint ?? params.underlyingMint),
258
- optionPool: params.optionPool ? toAddress(params.optionPool) : undefined,
259
- escrowLongAccount: params.escrowLongAccount
260
- ? toAddress(params.escrowLongAccount)
261
- : undefined,
262
- premiumVault: params.premiumVault ? toAddress(params.premiumVault) : undefined,
263
- collateralPool: params.collateralPool ? toAddress(params.collateralPool) : undefined,
264
- collateralVault: params.collateralVault ? toAddress(params.collateralVault) : undefined,
265
- makerCollateralAccount: toAddress(params.makerCollateralAccount),
266
- writerPosition: params.writerPosition ? toAddress(params.writerPosition) : undefined,
267
- vault: params.vault ? toAddress(params.vault) : undefined,
268
- vaultTokenAccount: params.vaultTokenAccount
269
- ? toAddress(params.vaultTokenAccount)
270
- : undefined,
271
- escrowState: params.escrowState ? toAddress(params.escrowState) : undefined,
272
- escrowAuthority: params.escrowAuthority ? toAddress(params.escrowAuthority) : undefined,
273
- escrowTokenAccount: params.escrowTokenAccount
274
- ? toAddress(params.escrowTokenAccount)
275
- : undefined,
276
- poolLoan: params.poolLoan ? toAddress(params.poolLoan) : undefined,
277
- maker: toAddress(params.maker) as any,
278
- optionType: params.optionType,
279
- strikePrice: params.strikePrice,
280
- expirationDate: params.expirationDate,
281
- quantity: params.quantity,
282
- underlyingAsset: toAddress(params.underlyingAsset),
283
- underlyingSymbol: params.underlyingSymbol,
284
- collateralMintArg: toAddress(params.collateralMint ?? params.underlyingMint),
285
- makerCollateralAmount: params.makerCollateralAmount,
286
- borrowedAmount: params.borrowedAmount,
287
- maxRequiredCollateralAmount,
288
- });
244
+ const kitInstruction = await getOptionMintInstructionAsync(
245
+ {
246
+ optionAccount: params.optionAccount ? toAddress(params.optionAccount) : undefined,
247
+ longMint: params.longMint ? toAddress(params.longMint) : undefined,
248
+ shortMint: params.shortMint ? toAddress(params.shortMint) : undefined,
249
+ mintAuthority: params.mintAuthority ? toAddress(params.mintAuthority) : undefined,
250
+ makerLongAccount: params.makerLongAccount
251
+ ? toAddress(params.makerLongAccount)
252
+ : undefined,
253
+ makerShortAccount: params.makerShortAccount
254
+ ? toAddress(params.makerShortAccount)
255
+ : undefined,
256
+ longMetadataAccount: toAddress(longMetadata!),
257
+ shortMetadataAccount: toAddress(shortMetadata!),
258
+ marketData: params.marketData ? toAddress(params.marketData) : undefined,
259
+ underlyingMint: toAddress(params.underlyingMint),
260
+ collateralMint: toAddress(params.collateralMint ?? params.underlyingMint),
261
+ optionPool: params.optionPool ? toAddress(params.optionPool) : undefined,
262
+ escrowLongAccount: params.escrowLongAccount
263
+ ? toAddress(params.escrowLongAccount)
264
+ : undefined,
265
+ premiumVault: params.premiumVault ? toAddress(params.premiumVault) : undefined,
266
+ collateralPool: params.collateralPool ? toAddress(params.collateralPool) : undefined,
267
+ collateralVault: params.collateralVault ? toAddress(params.collateralVault) : undefined,
268
+ makerCollateralAccount: toAddress(params.makerCollateralAccount),
269
+ writerPosition: params.writerPosition ? toAddress(params.writerPosition) : undefined,
270
+ vault: params.vault ? toAddress(params.vault) : undefined,
271
+ vaultTokenAccount: params.vaultTokenAccount
272
+ ? toAddress(params.vaultTokenAccount)
273
+ : undefined,
274
+ escrowState: params.escrowState ? toAddress(params.escrowState) : undefined,
275
+ escrowAuthority: params.escrowAuthority ? toAddress(params.escrowAuthority) : undefined,
276
+ escrowTokenAccount: params.escrowTokenAccount
277
+ ? toAddress(params.escrowTokenAccount)
278
+ : undefined,
279
+ poolLoan: params.poolLoan ? toAddress(params.poolLoan) : undefined,
280
+ maker: toAddress(params.maker) as any,
281
+ optionType: params.optionType,
282
+ strikePrice: params.strikePrice,
283
+ expirationDate: params.expirationDate,
284
+ quantity: params.quantity,
285
+ underlyingAsset: toAddress(params.underlyingAsset),
286
+ underlyingSymbol: params.underlyingSymbol,
287
+ collateralMintArg: toAddress(params.collateralMint ?? params.underlyingMint),
288
+ makerCollateralAmount: params.makerCollateralAmount,
289
+ borrowedAmount: params.borrowedAmount,
290
+ maxRequiredCollateralAmount,
291
+ },
292
+ { programAddress: params.programId ? toAddress(params.programId) : PROGRAM_ID }
293
+ );
289
294
 
290
295
  const quoteVerificationAccounts: RemainingAccountInput[] = [
291
296
  {
@@ -452,15 +457,10 @@ export async function buildOptionMintTransactionWithDerivation(
452
457
  params.programId
453
458
  );
454
459
 
455
- // option_mint + borrow: `sync_collateral_pool_debt` requires
456
- // snapshot.active_loan_count == writer.active_loan_count
457
- // after the writer block (+1 for this borrow). The named `pool_loan` is the new loan from
458
- // TX A. gPA can return more PoolLoans than `writer.active_loan_count` (on-chain desync
459
- // or stale actives). We pass exactly `activeLoanCount` pre-ix prior loans, excluding the
460
- // new `pool_loan`, matching `writer_position` (and preferring same vault) with canonical
461
- // `toAddress` equality. Prefer *higher* `nonce` first so we do not pick a stale low-nonce
462
- // account that the sync path skips (discriminator / borrow_mut), which yields under-count
463
- // and 6102.
460
+ // option_mint + borrow: `sync_collateral_pool_debt` needs the active loan snapshot.
461
+ // The named `pool_loan` is the freshly borrowed loan from TX A; pass only the prior
462
+ // active loans as remaining accounts. If confirmed gPA already sees the new loan,
463
+ // subtract it from `writer.activeLoanCount` so it is not double-counted.
464
464
  let priorActivePoolLoans: RemainingAccountInput[] = [];
465
465
  if (borrowedAmount > 0n) {
466
466
  const wp = toAddress(writerPositionPdaForIx);
@@ -468,9 +468,16 @@ export async function buildOptionMintTransactionWithDerivation(
468
468
  const vaultPda = toAddress(params.vault!);
469
469
  const [writerPos, activeLoans] = await Promise.all([
470
470
  fetchWriterPosition(params.rpc, writerPositionPdaForIx),
471
- fetchPoolLoansByMaker(params.rpc, params.maker),
471
+ fetchPoolLoansByMaker(params.rpc, params.maker, params.programId),
472
472
  ]);
473
- const priorCount = writerPos?.activeLoanCount ?? 0;
473
+ const newLoanAlreadyActive = activeLoans.some(
474
+ (loan) => toAddress(loan.address) === newLoan
475
+ );
476
+ const activeLoanCount = writerPos?.activeLoanCount ?? 0;
477
+ const priorCount = Math.max(
478
+ 0,
479
+ activeLoanCount - (newLoanAlreadyActive ? 1 : 0)
480
+ );
474
481
  const forWriter = (l: (typeof activeLoans)[0]) =>
475
482
  toAddress(l.data.writerPosition) === wp && toAddress(l.address) !== newLoan;
476
483
  const sortByNonceDesc = (
@@ -669,7 +676,7 @@ export async function buildUnwindWriterUnsoldWithLoanRepayment(
669
676
  );
670
677
 
671
678
  const [loans, vault, writerPosition] = await Promise.all([
672
- fetchPoolLoansByMaker(params.rpc, params.writer),
679
+ fetchPoolLoansByMaker(params.rpc, params.writer, params.programId),
673
680
  fetchVault(params.rpc, vaultPda),
674
681
  fetchWriterPosition(params.rpc, writerPositionAddress),
675
682
  ]);
@@ -274,7 +274,7 @@ export async function preflightUnwindWriterUnsold(
274
274
  writerPosition = await fetchWriterPosition(params.rpc, writerPositionAddress);
275
275
  collateralPool = await fetchCollateralPool(params.rpc, resolved.collateralPool);
276
276
  vault = await fetchVault(params.rpc, vaultPda);
277
- loans = await fetchPoolLoansByMaker(params.rpc, params.writer);
277
+ loans = await fetchPoolLoansByMaker(params.rpc, params.writer, params.programId);
278
278
  currentSlot = await params.rpc.getSlot().send();
279
279
 
280
280
  invariant(!!writerPosition, "Writer position is required for unwind preflight.");