@epicentral/sos-sdk 0.5.0-alpha.6 → 0.5.0-alpha.8

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 CHANGED
@@ -179,7 +179,8 @@ Use **`preflightUnwindWriterUnsold`** before building the transaction to get:
179
179
  - **Collateral return calculation** (proportional share, returnable amount).
180
180
  - Collateral-vault available, wallet fallback required, and shortfall.
181
181
  - **Top-up UX fields:** `collateralVaultShortfall`, `needsWalletTopUp`.
182
- - `canRepayFully` so UI can block early with actionable messaging.
182
+ - WSOL repay metadata: `solTopUpRequired`, `topUpRequiredForRepay`, `nativeSolAvailable`.
183
+ - `canRepayFully`, which now reflects effective repay solvency (including native SOL top-up capacity for WSOL paths).
183
184
 
184
185
  If there are no active pool loans for that vault, the API still works and passes empty `remaining_accounts`.
185
186
 
@@ -205,10 +206,6 @@ const preflight = await preflightUnwindWriterUnsold({
205
206
  rpc,
206
207
  });
207
208
 
208
- if (!preflight.canRepayFully) {
209
- throw new Error(`Unwind blocked. Shortfall: ${preflight.summary.shortfall.toString()}`);
210
- }
211
-
212
209
  const tx = await buildUnwindWriterUnsoldWithLoanRepayment({
213
210
  underlyingAsset,
214
211
  optionType,
@@ -217,9 +214,15 @@ const tx = await buildUnwindWriterUnsoldWithLoanRepayment({
217
214
  writer,
218
215
  unwindQty,
219
216
  rpc,
217
+ includeWrapForShortfall: true, // for WSOL paths, auto-wrap net top-up when needed
218
+ writerSigner: walletSigner, // required when wrapping is needed
220
219
  });
221
220
  ```
222
221
 
222
+ Notes:
223
+ - For WSOL underlyings, the builder wraps only the net required amount: `max(0, walletFallbackRequired - walletFallbackAvailable)`.
224
+ - If repayment is still insolvent after considering vault + fallback + native SOL top-up capacity, the builder throws an actionable insolvency error.
225
+
223
226
  ## Usage Examples
224
227
 
225
228
  ### Buy From Pool (market order, high-level)
@@ -4,7 +4,7 @@ import { PROGRAM_ID } from "./program";
4
4
  import type { KitRpc } from "./types";
5
5
 
6
6
  export const LOOKUP_TABLE_ADDRESSES: Record<"devnet" | "mainnet", Address | null> = {
7
- devnet: address("FhUHHULWJr1maH33Ec4zdR6dCG7pFX2i31o85KvNyCim"),
7
+ devnet: address("ApcccYeyDGBieh7fGospRV7v2pJ3UjmZEMKU8V7D3eBE"),
8
8
  mainnet: null,
9
9
  };
10
10
 
@@ -88,7 +88,7 @@ import {
88
88
  } from "../instructions";
89
89
 
90
90
  export const OPTION_PROGRAM_PROGRAM_ADDRESS =
91
- "B3wF41HnvA7KE69aDPi6f1WULZBQjeEneuW3BJG574ea" as Address<"B3wF41HnvA7KE69aDPi6f1WULZBQjeEneuW3BJG574ea">;
91
+ "AA1nq9oeALQRYoN6WJvmFw6oRuek5X4KfTr7vb1i2dMQ" as Address<"AA1nq9oeALQRYoN6WJvmFw6oRuek5X4KfTr7vb1i2dMQ">;
92
92
 
93
93
  export enum OptionProgramAccount {
94
94
  CollateralPool,
@@ -679,7 +679,7 @@ export function identifyOptionProgramInstruction(
679
679
  }
680
680
 
681
681
  export type ParsedOptionProgramInstruction<
682
- TProgram extends string = "B3wF41HnvA7KE69aDPi6f1WULZBQjeEneuW3BJG574ea",
682
+ TProgram extends string = "AA1nq9oeALQRYoN6WJvmFw6oRuek5X4KfTr7vb1i2dMQ",
683
683
  > =
684
684
  | ({
685
685
  instructionType: OptionProgramInstruction.AcceptAdmin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicentral/sos-sdk",
3
- "version": "0.5.0-alpha.6",
3
+ "version": "0.5.0-alpha.8",
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
@@ -468,6 +468,7 @@ export async function buildUnwindWriterUnsoldWithLoanRepayment(
468
468
  const writerRepaymentAccount =
469
469
  params.writerRepaymentAccount ??
470
470
  (await deriveAssociatedTokenAddress(params.writer, underlyingMint));
471
+ const writerDefaultRepaymentAta = await deriveAssociatedTokenAddress(params.writer, underlyingMint);
471
472
 
472
473
  const preflight = await preflightUnwindWriterUnsold({
473
474
  underlyingAsset: params.underlyingAsset,
@@ -480,10 +481,22 @@ export async function buildUnwindWriterUnsoldWithLoanRepayment(
480
481
  programId: params.programId,
481
482
  underlyingMint,
482
483
  });
484
+ const isWsolPath = toAddress(underlyingMint) === toAddress(NATIVE_MINT);
485
+ const lamportsToWrap =
486
+ preflight.summary.walletFallbackRequired > preflight.summary.walletFallbackAvailable
487
+ ? preflight.summary.walletFallbackRequired - preflight.summary.walletFallbackAvailable
488
+ : 0n;
489
+
483
490
  invariant(
484
491
  preflight.canRepayFully,
485
- `Unwind cannot fully repay loans: shortfall=${preflight.summary.shortfall}`
492
+ `Unwind cannot fully repay loans: 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)}`
486
493
  );
494
+ if (isWsolPath && lamportsToWrap > 0n && !params.includeWrapForShortfall) {
495
+ invariant(
496
+ false,
497
+ `Unwind requires WSOL top-up of ${lamportsToWrap} lamports. Rebuild with includeWrapForShortfall=true and writerSigner.`
498
+ );
499
+ }
487
500
 
488
501
  const unwindTx = await buildUnwindWriterUnsoldTransactionWithDerivation({
489
502
  underlyingAsset: params.underlyingAsset,
@@ -501,11 +514,11 @@ export async function buildUnwindWriterUnsoldWithLoanRepayment(
501
514
  remainingAccounts,
502
515
  });
503
516
 
504
- if (
505
- params.includeWrapForShortfall &&
506
- toAddress(underlyingMint) === toAddress(NATIVE_MINT) &&
507
- preflight.summary.walletFallbackRequired > 0n
508
- ) {
517
+ if (params.includeWrapForShortfall && isWsolPath && lamportsToWrap > 0n) {
518
+ invariant(
519
+ toAddress(writerRepaymentAccount) === toAddress(writerDefaultRepaymentAta),
520
+ "WSOL auto-wrap requires writerRepaymentAccount to be the writer WSOL ATA."
521
+ );
509
522
  invariant(
510
523
  !!params.writerSigner,
511
524
  "writerSigner is required when includeWrapForShortfall=true for WSOL shortfall top-up."
@@ -513,7 +526,7 @@ export async function buildUnwindWriterUnsoldWithLoanRepayment(
513
526
  const wrapInstructions = await getWrapSOLInstructions({
514
527
  payer: params.writerSigner,
515
528
  owner: params.writer,
516
- lamports: preflight.summary.walletFallbackRequired,
529
+ lamports: lamportsToWrap,
517
530
  });
518
531
  return {
519
532
  instructions: [...wrapInstructions, ...unwindTx.instructions],
@@ -6,6 +6,7 @@ import { fetchPoolLoansByMaker } from "../accounts/list";
6
6
  import { deriveAssociatedTokenAddress, deriveVaultPda, deriveWriterPositionPda } from "../accounts/pdas";
7
7
  import { resolveOptionAccounts } from "../accounts/resolve-option";
8
8
  import { invariant } from "../shared/errors";
9
+ import { NATIVE_MINT } from "../wsol/instructions";
9
10
 
10
11
  const TOKEN_ACCOUNT_AMOUNT_OFFSET = 64;
11
12
  const BPS_DENOMINATOR = 10_000n;
@@ -78,6 +79,10 @@ export interface UnwindPreflightSummary {
78
79
  /** For top-up UX: explicit shortfall fields */
79
80
  collateralVaultShortfall: bigint;
80
81
  needsWalletTopUp: boolean;
82
+ /** WSOL-only top-up metadata */
83
+ solTopUpRequired: bigint;
84
+ topUpRequiredForRepay: boolean;
85
+ nativeSolAvailable: bigint;
81
86
  }
82
87
 
83
88
  export interface UnwindPreflightResult {
@@ -111,9 +116,11 @@ export async function preflightUnwindWriterUnsold(
111
116
  const underlyingMint = params.underlyingMint ?? resolved.underlyingMint;
112
117
  const [vaultPda] = await deriveVaultPda(underlyingMint, params.programId);
113
118
  const vaultPdaAddress = toAddress(vaultPda);
119
+ const writerAddress = toAddress(params.writer);
120
+ const writerDefaultRepaymentAta = await deriveAssociatedTokenAddress(params.writer, underlyingMint);
114
121
  const writerRepaymentAccount =
115
122
  params.writerRepaymentAccount ??
116
- (await deriveAssociatedTokenAddress(params.writer, underlyingMint));
123
+ writerDefaultRepaymentAta;
117
124
  const writerRepaymentAddress = toAddress(writerRepaymentAccount);
118
125
  const [writerPositionAddress] = await deriveWriterPositionPda(
119
126
  resolved.optionPool,
@@ -162,6 +169,9 @@ export async function preflightUnwindWriterUnsold(
162
169
  shortfall: 0n,
163
170
  collateralVaultShortfall: 0n,
164
171
  needsWalletTopUp: false,
172
+ solTopUpRequired: 0n,
173
+ topUpRequiredForRepay: false,
174
+ nativeSolAvailable: 0n,
165
175
  },
166
176
  };
167
177
  }
@@ -192,6 +202,9 @@ export async function preflightUnwindWriterUnsold(
192
202
  shortfall: 0n,
193
203
  collateralVaultShortfall: 0n,
194
204
  needsWalletTopUp: false,
205
+ solTopUpRequired: 0n,
206
+ topUpRequiredForRepay: false,
207
+ nativeSolAvailable: 0n,
195
208
  },
196
209
  };
197
210
  }
@@ -242,10 +255,15 @@ export async function preflightUnwindWriterUnsold(
242
255
  { principal: 0n, interest: 0n, fees: 0n, owed: 0n }
243
256
  );
244
257
 
245
- const [collateralVaultAvailable, walletFallbackAvailable] = await Promise.all([
258
+ const isWsolRepaymentPath = toAddress(underlyingMint) === toAddress(NATIVE_MINT);
259
+ const canTopUpByWrapping =
260
+ isWsolRepaymentPath && writerRepaymentAddress === toAddress(writerDefaultRepaymentAta);
261
+ const [collateralVaultAvailable, walletFallbackAvailable, nativeBalanceResponse] = await Promise.all([
246
262
  fetchTokenAmount(params.rpc, resolved.collateralVault!),
247
263
  fetchTokenAmount(params.rpc, writerRepaymentAddress),
264
+ canTopUpByWrapping ? params.rpc.getBalance(writerAddress).send() : Promise.resolve({ value: 0n }),
248
265
  ]);
266
+ const nativeSolAvailable = nativeBalanceResponse.value;
249
267
 
250
268
  // Calculate proportional obligations for partial unwinds
251
269
  const writtenQty = toBigInt(writerPosition.writtenQty);
@@ -270,6 +288,12 @@ export async function preflightUnwindWriterUnsold(
270
288
  proportionalTotalOwed > collateralVaultAvailable ? proportionalTotalOwed - collateralVaultAvailable : 0n;
271
289
  const totalAvailable = collateralVaultAvailable + walletFallbackAvailable;
272
290
  const shortfall = proportionalTotalOwed > totalAvailable ? proportionalTotalOwed - totalAvailable : 0n;
291
+ const solTopUpRequired =
292
+ walletFallbackRequired > walletFallbackAvailable ? walletFallbackRequired - walletFallbackAvailable : 0n;
293
+ const topUpRequiredForRepay = solTopUpRequired > 0n;
294
+ const effectiveTotalAvailable = totalAvailable + (canTopUpByWrapping ? nativeSolAvailable : 0n);
295
+ const effectiveShortfall =
296
+ proportionalTotalOwed > effectiveTotalAvailable ? proportionalTotalOwed - effectiveTotalAvailable : 0n;
273
297
 
274
298
  // For top-up UX: explicit collateral vault shortfall
275
299
  const collateralVaultShortfall = returnableCollateral > collateralVaultAvailable
@@ -279,8 +303,11 @@ export async function preflightUnwindWriterUnsold(
279
303
 
280
304
  return {
281
305
  canUnwind: true,
282
- canRepayFully: shortfall === 0n,
283
- reason: shortfall === 0n ? undefined : "Insufficient combined collateral vault + writer fallback funds",
306
+ canRepayFully: effectiveShortfall === 0n,
307
+ reason:
308
+ effectiveShortfall === 0n
309
+ ? undefined
310
+ : "Insufficient combined collateral vault + wallet fallback funds (including SOL top-up capacity for WSOL)",
284
311
  writerPositionAddress: String(writerPositionAddress),
285
312
  writerRepaymentAccount: String(writerRepaymentAddress),
286
313
  collateralVaultAddress: String(resolved.collateralVault),
@@ -303,6 +330,9 @@ export async function preflightUnwindWriterUnsold(
303
330
  shortfall,
304
331
  collateralVaultShortfall,
305
332
  needsWalletTopUp,
333
+ solTopUpRequired,
334
+ topUpRequiredForRepay,
335
+ nativeSolAvailable,
306
336
  },
307
337
  };
308
338
  }