@epicentral/sos-sdk 0.2.13 → 0.3.0-alpha.2

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
@@ -1,177 +1,164 @@
1
1
  # @epicentral/sos-sdk
2
2
 
3
- Solana Option Standard SDK. A frontend-first SDK for native options trading on Solana, built by Epicentral Labs.
3
+ TypeScript/JavaScript SDK for the Solana Option Standard (SOS) program. The frontend-first SDK for native options trading on Solana. Built with `@solana/kit` and related Solana libraries.
4
4
 
5
- Uses `@solana/kit` types (`Address`, `Instruction`, `Rpc`) across the public API.
6
-
7
- ## Install
5
+ ## Installation
8
6
 
9
7
  ```bash
10
- pnpm add @epicentral/sos-sdk @solana/kit decimal.js
8
+ pnpm add @epicentral/sos-sdk
11
9
  ```
12
10
 
11
+ Peer dependencies: `@solana/kit` (and your RPC client). The SDK uses `KitRpc` for account resolution and fetches.
12
+
13
13
  ## Overview
14
14
 
15
- - **LONG** Buy from pool, close to pool, exercise options.
16
- - **SHORT** — Mint options, unwind, sync, settle, claim premium, close option.
17
- - **Pool** — Deposit, withdraw, borrow, repay liquidity.
18
- - **OMLP** — Lender deposit and withdraw.
19
- - **Accounts** — PDA derivation and account fetchers for options, pools, vaults.
15
+ The SDK supports two main flows:
20
16
 
21
- Each flow exposes `build*Instruction` for single instruction composition and `build*Transaction` for full-flow `Instruction[]` construction.
17
+ - **Long (buyer)** Buy options from the pool, close positions, exercise.
18
+ - **Short (writer)** – Mint options (write), unwind unsold, settle collateral, claim theta.
22
19
 
23
- ## Usage
20
+ Additional modules:
24
21
 
25
- ### Build + send (recommended)
22
+ - **OMLP** Option Maker Liquidity Pool. Lenders deposit; writers borrow to collateralize shorts.
23
+ - **WSOL** – Helpers for wrapping/unwrapping SOL and creating token accounts.
26
24
 
27
- ```ts
28
- import {
29
- buildBuyFromPoolTransaction,
30
- sendBuiltTransaction,
31
- } from "@epicentral/sos-sdk";
25
+ ## High-Level Functions and Instructions
32
26
 
33
- const built = await buildBuyFromPoolTransaction(params);
34
- const signature = await sendBuiltTransaction({
35
- rpc,
36
- rpcSubscriptions,
37
- feePayer: walletSigner,
38
- instructions: built.instructions,
39
- });
40
- ```
27
+ ### Accounts, PDAs, and Fetchers
41
28
 
42
- ### Build + send (manual)
29
+ | Function | Description |
30
+ |----------|-------------|
31
+ | `resolveOptionAccounts` | Resolves option pool, mints, vaults, and collateral accounts from option identity (underlying, type, strike, expiration). |
32
+ | `deriveVaultPda` | Derives OMLP vault PDA from mint. |
33
+ | `derivePoolLoanPdaFromVault` | Derives PoolLoan PDA from vault, maker, nonce (canonical; matches program). |
34
+ | `derivePoolLoanPda` | *(Deprecated)* Legacy derivation; use `derivePoolLoanPdaFromVault`. |
35
+ | `deriveWriterPositionPda` | Derives writer position PDA from option pool and writer. |
36
+ | `deriveAssociatedTokenAddress` | Derives ATA for owner + mint. |
37
+ | `fetchVault` | Fetches vault account by address. |
38
+ | `fetchPoolLoansByMaker` | Fetches active pool loans for a maker. |
39
+ | `fetchOptionPool` | Fetches option pool account. |
40
+ | `fetchWriterPositionsByWriter` | Fetches writer positions for a writer. |
41
+ | `fetchWriterPositionsForPool` | Fetches writer positions for an option pool. |
42
+ | `fetchPositionAccountsByBuyer` | Fetches buyer position accounts. |
43
+ | `fetchAllOptionPools` | Fetches all option pools. |
44
+ | `fetchAllVaults` | Fetches all vaults. |
43
45
 
44
- ```ts
45
- import {
46
- appendTransactionMessageInstructions,
47
- createTransactionMessage,
48
- pipe,
49
- sendAndConfirmTransactionFactory,
50
- setTransactionMessageFeePayerSigner,
51
- setTransactionMessageLifetimeUsingBlockhash,
52
- signTransactionMessageWithSigners,
53
- } from "@solana/kit";
54
- import { buildBuyFromPoolTransaction } from "@epicentral/sos-sdk";
55
-
56
- const built = await buildBuyFromPoolTransaction(params);
57
- const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
58
-
59
- const txMessage = pipe(
60
- createTransactionMessage({ version: 0 }),
61
- (tx) => setTransactionMessageFeePayerSigner(walletSigner, tx),
62
- (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
63
- (tx) => appendTransactionMessageInstructions(built.instructions, tx)
64
- );
65
-
66
- const signedTx = await signTransactionMessageWithSigners(txMessage);
67
- await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTx, {
68
- commitment: "confirmed",
69
- });
70
- ```
46
+ ### Long (Buyer) Flows
71
47
 
72
- ### LONG
48
+ | Function | Description |
49
+ |----------|-------------|
50
+ | `buildBuyFromPoolTransactionWithDerivation` | Builds buy-from-pool transaction; resolves accounts from option identity. |
51
+ | `buildCloseLongToPoolTransactionWithDerivation` | Builds close-long-to-pool transaction. |
52
+ | `getBuyFromPoolRemainingAccounts` | Builds remaining_accounts for buy (writer positions, etc.). |
73
53
 
74
- ```ts
75
- import {
76
- buildBuyFromPoolTransaction,
77
- buildCloseLongToPoolTransaction,
78
- buildOptionExerciseTransaction,
79
- } from "@epicentral/sos-sdk";
54
+ ### Short (Writer) Flows
80
55
 
81
- // Open LONG
82
- const openLong = await buildBuyFromPoolTransaction(openLongParams);
56
+ | Function | Description |
57
+ |----------|-------------|
58
+ | `buildOptionMintTransactionWithDerivation` | Builds option mint (write) transaction. |
59
+ | `buildUnwindWriterUnsoldTransactionWithDerivation` | Builds unwind unsold transaction. |
60
+ | `buildUnwindWriterUnsoldWithLoanRepayment` | **Unwind + repay pool loans in one tx.** Use when closing unsold shorts that borrowed from OMLP. |
61
+ | `buildSyncWriterPositionTransaction` | Syncs writer position with pool accumulators. |
62
+ | `buildSettleMakerCollateralTransaction` | Settles maker collateral after buyer closes. |
63
+ | `buildCloseOptionTransaction` | Closes option token account. |
64
+ | `buildClaimThetaTransaction` | Claims theta (time-decay share) for writer. |
65
+ | `buildRepayPoolLoanFromCollateralInstruction` | Repays pool loan from collateral (short/pool). |
66
+ | `buildRepayPoolLoanInstruction` | Repays pool loan with external funds (short/pool). |
67
+ | `buildRepayPoolLoanFromWalletInstruction` | Repays pool loan from maker's wallet (stuck loan recovery). |
83
68
 
84
- // Close LONG
85
- const closeLong = await buildCloseLongToPoolTransaction(closeLongParams);
69
+ ### OMLP (Lending)
86
70
 
87
- // Exercise LONG
88
- const exercise = buildOptionExerciseTransaction({ optionAccount, positionAccount, /* ... */ });
89
- ```
71
+ | Function | Description |
72
+ |----------|-------------|
73
+ | `buildDepositToPositionTransaction` | Deposits liquidity to OMLP. |
74
+ | `buildWithdrawFromPositionTransaction` | Withdraws liquidity. |
75
+ | `withdrawAllFromPosition` | Withdraws full position (omlp/service). |
76
+ | `withdrawInterestFromPosition` | Withdraws accrued interest only (omlp/service). |
90
77
 
91
- ### SHORT
78
+ Borrow/repay for writers: use `buildOptionMintTransactionWithDerivation` (with vault/poolLoan) and `buildRepayPoolLoanFromCollateralInstruction` or `buildUnwindWriterUnsoldWithLoanRepayment`.
92
79
 
93
- ```ts
94
- import {
95
- buildOptionMintTransaction,
96
- buildUnwindWriterUnsoldTransaction,
97
- buildSyncWriterPositionTransaction,
98
- buildSettleMakerCollateralTransaction,
99
- buildClaimPremiumTransaction,
100
- buildCloseOptionTransaction,
101
- } from "@epicentral/sos-sdk";
102
- import { OptionType } from "@epicentral/sos-sdk";
80
+ ### WSOL / Token Helpers
103
81
 
104
- // Mint (open SHORT)
105
- const built = await buildOptionMintTransaction({
106
- optionType: OptionType.Call,
107
- strikePrice,
108
- expirationDate,
109
- quantity,
110
- underlyingAsset,
111
- underlyingSymbol,
112
- makerCollateralAmount,
113
- borrowedAmount,
114
- maker,
115
- makerCollateralAccount,
116
- underlyingMint,
117
- });
82
+ | Function | Description |
83
+ |----------|-------------|
84
+ | `getWrapSOLInstructions` | Wraps SOL to WSOL. |
85
+ | `getUnwrapSOLInstructions` | Unwraps WSOL to SOL. |
86
+ | `getCreateAssociatedTokenIdempotentInstructionWithAddress` | Creates ATA if missing (idempotent). |
87
+ | `NATIVE_MINT` | WSOL mint address. |
118
88
 
119
- // Unwind / Sync / Settle
120
- const unwind = await buildUnwindWriterUnsoldTransaction(unwindParams);
121
- const sync = buildSyncWriterPositionTransaction(syncParams);
122
- const settle = await buildSettleMakerCollateralTransaction(settleParams);
89
+ ## Unwind with Loan Repayment
123
90
 
124
- // Claim premium / close option
125
- const claim = await buildClaimPremiumTransaction({ optionPool, makerPaymentAccount, premiumVault, maker });
126
- const close = buildCloseOptionTransaction({ optionAccount, optionMint, makerOptionAccount, maker });
127
- ```
91
+ When a writer unwinds an unsold short that had borrowed from the OMLP pool, the program repays lenders from the collateral vault inside `unwind_writer_unsold` (burn LONG+SHORT, repay loans, then return collateral to writer) in one instruction.
92
+
93
+ Use **`buildUnwindWriterUnsoldWithLoanRepayment`** so that:
94
+
95
+ 1. Active pool loans for the option’s underlying vault are fetched.
96
+ 2. `omlpVaultState` (Vault PDA), `omlpVault`, and `feeWallet` are passed as named accounts.
97
+ 3. `remaining_accounts` = **[PoolLoan₁, PoolLoan₂, ...]** only (capped at 20 loans per tx).
98
+ 4. One transaction burns, repays lenders from collateral vault, and returns collateral to the writer.
99
+
100
+ If there are no active pool loans for that vault, the API still works and passes empty `remaining_accounts`.
128
101
 
129
- ### Pool
102
+ **Alternative (repay then unwind):** For writers with more than ~20 active loans, (1) build `repay_pool_loan_from_collateral` instructions first to reduce loans, then (2) unwind with the remaining loans.
103
+
104
+ **Stuck loan (InsufficientEscrowBalance):** When standard repay fails with `InsufficientEscrowBalance` (escrow underfunded or drained), use `buildRepayPoolLoanFromWalletInstruction` or `buildRepayPoolLoanFromWalletTransaction`. Same accounts as `buildRepayPoolLoanInstruction`; maker pays full principal + interest + fees from their wallet.
105
+
106
+ ## Usage Examples
107
+
108
+ ### Buy from pool (with derivation)
130
109
 
131
110
  ```ts
132
111
  import {
133
- buildDepositToPoolTransaction,
134
- buildWithdrawFromPoolTransaction,
135
- buildBorrowFromPoolTransaction,
136
- buildRepayPoolLoanTransaction,
112
+ buildBuyFromPoolTransactionWithDerivation,
113
+ resolveOptionAccounts,
114
+ OptionType,
137
115
  } from "@epicentral/sos-sdk";
138
116
 
139
- const deposit = await buildDepositToPoolTransaction(depositToPoolParams);
140
- const withdraw = await buildWithdrawFromPoolTransaction(withdrawFromPoolParams);
141
- const borrow = await buildBorrowFromPoolTransaction(borrowFromPoolParams);
142
- const repay = await buildRepayPoolLoanTransaction(repayPoolLoanParams);
117
+ const tx = await buildBuyFromPoolTransactionWithDerivation({
118
+ underlyingAsset: "...",
119
+ optionType: OptionType.Call,
120
+ strikePrice: 100_000,
121
+ expirationDate: BigInt(1735689600),
122
+ buyer: walletAddress,
123
+ buyerPaymentAccount: buyerUsdcAta,
124
+ priceUpdate: pythPriceFeed,
125
+ quantity: 1_000_000,
126
+ premiumAmount: 50_000,
127
+ rpc,
128
+ });
143
129
  ```
144
130
 
145
- ### OMLP
131
+ ### Unwind with loan repayment
146
132
 
147
133
  ```ts
148
134
  import {
149
- buildDepositToPositionTransaction,
150
- buildWithdrawFromPositionTransaction,
135
+ buildUnwindWriterUnsoldWithLoanRepayment,
136
+ OptionType,
151
137
  } from "@epicentral/sos-sdk";
152
138
 
153
- const deposit = await buildDepositToPositionTransaction({
154
- vault,
155
- lenderTokenAccount,
156
- vaultTokenAccount,
157
- lender,
158
- amount,
159
- });
160
- const withdraw = await buildWithdrawFromPositionTransaction({
161
- vault,
162
- vaultTokenAccount,
163
- lenderTokenAccount,
164
- lender,
165
- amount,
139
+ const tx = await buildUnwindWriterUnsoldWithLoanRepayment({
140
+ underlyingAsset: "...",
141
+ optionType: OptionType.Call,
142
+ strikePrice: 100_000,
143
+ expirationDate: BigInt(1735689600),
144
+ writer: walletAddress,
145
+ unwindQty: 500_000,
146
+ rpc,
166
147
  });
167
148
  ```
168
149
 
169
- ### Fetch accounts
150
+ ## Types and Exports
170
151
 
171
- ```ts
172
- import { fetchOptionAccount, fetchOptionPool, fetchVault } from "@epicentral/sos-sdk";
152
+ Key types exported from the package:
173
153
 
174
- const option = await fetchOptionAccount(rpc, optionAddress);
175
- const pool = await fetchOptionPool(rpc, optionPoolAddress);
176
- const vault = await fetchVault(rpc, vaultAddress);
177
- ```
154
+ - `OptionType` Call or Put.
155
+ - `BuiltTransaction` `{ instructions: Instruction[] }`.
156
+ - `AddressLike` `string | Address`.
157
+ - `KitRpc` – RPC client type for fetches.
158
+ - `RemainingAccountInput` – `{ address, isWritable, isSigner? }`.
159
+
160
+ PDAs, fetchers, and builders are exported from the package root.
161
+
162
+ ## Program Compatibility
163
+
164
+ The SDK targets the Solana Option Standard program. Use `PROGRAM_ID` (or `getProgramId()`) from the package for the program address. Pass `programId` in builder params when using a different deployment.
package/accounts/pdas.ts CHANGED
@@ -258,6 +258,39 @@ export async function deriveEscrowAuthorityPda(
258
258
  });
259
259
  }
260
260
 
261
+ /**
262
+ * Derives the PoolLoan PDA using the canonical seeds: vault, maker, nonce.
263
+ * Matches the program's derivation in omlp_context.rs (BorrowFromPool).
264
+ *
265
+ * @param vault - OMLP vault PDA (from deriveVaultPda)
266
+ * @param maker - Writer/borrower pubkey
267
+ * @param nonce - Loan nonce (u64)
268
+ * @param programId - Optional program ID
269
+ */
270
+ export async function derivePoolLoanPdaFromVault(
271
+ vault: AddressLike,
272
+ maker: AddressLike,
273
+ nonce: bigint | number,
274
+ programId: AddressLike = PROGRAM_ID
275
+ ): Promise<readonly [Address, number]> {
276
+ const addressEncoder = getAddressEncoder();
277
+ return getProgramDerivedAddress({
278
+ programAddress: toAddress(programId),
279
+ seeds: [
280
+ new TextEncoder().encode("pool_loan"),
281
+ addressEncoder.encode(toAddress(vault)),
282
+ addressEncoder.encode(toAddress(maker)),
283
+ u64ToLeBytes(nonce),
284
+ ],
285
+ });
286
+ }
287
+
288
+ /**
289
+ * Derives the PoolLoan PDA using writer position and nonce.
290
+ *
291
+ * @deprecated This derivation does not match the program. Use {@link derivePoolLoanPdaFromVault}
292
+ * with (vault, maker, nonce) instead. Program seeds are: pool_loan, vault, maker, nonce (u64 le).
293
+ */
261
294
  export async function derivePoolLoanPda(
262
295
  writerPosition: AddressLike,
263
296
  nonce: bigint | number,
@@ -31,6 +31,7 @@ export * from "./optionMint";
31
31
  export * from "./optionValidate";
32
32
  export * from "./repayPoolLoan";
33
33
  export * from "./repayPoolLoanFromCollateral";
34
+ export * from "./repayPoolLoanFromWallet";
34
35
  export * from "./settleMakerCollateral";
35
36
  export * from "./syncWriterPosition";
36
37
  export * from "./transferAdmin";
@@ -0,0 +1,483 @@
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
+ fixDecoderSize,
12
+ fixEncoderSize,
13
+ getAddressEncoder,
14
+ getBytesDecoder,
15
+ getBytesEncoder,
16
+ getProgramDerivedAddress,
17
+ getStructDecoder,
18
+ getStructEncoder,
19
+ transformEncoder,
20
+ type AccountMeta,
21
+ type AccountSignerMeta,
22
+ type Address,
23
+ type FixedSizeCodec,
24
+ type FixedSizeDecoder,
25
+ type FixedSizeEncoder,
26
+ type Instruction,
27
+ type InstructionWithAccounts,
28
+ type InstructionWithData,
29
+ type ReadonlyAccount,
30
+ type ReadonlySignerAccount,
31
+ type ReadonlyUint8Array,
32
+ type TransactionSigner,
33
+ type WritableAccount,
34
+ } from "@solana/kit";
35
+ import { OPTION_PROGRAM_PROGRAM_ADDRESS } from "../programs";
36
+ import {
37
+ expectAddress,
38
+ getAccountMetaFactory,
39
+ type ResolvedAccount,
40
+ } from "../shared";
41
+
42
+ export const REPAY_POOL_LOAN_FROM_WALLET_DISCRIMINATOR = new Uint8Array([
43
+ 78, 130, 135, 90, 211, 21, 247, 247,
44
+ ]);
45
+
46
+ export function getRepayPoolLoanFromWalletDiscriminatorBytes() {
47
+ return fixEncoderSize(getBytesEncoder(), 8).encode(
48
+ REPAY_POOL_LOAN_FROM_WALLET_DISCRIMINATOR,
49
+ );
50
+ }
51
+
52
+ export type RepayPoolLoanFromWalletInstruction<
53
+ TProgram extends string = typeof OPTION_PROGRAM_PROGRAM_ADDRESS,
54
+ TAccountPoolLoan extends string | AccountMeta<string> = string,
55
+ TAccountVault extends string | AccountMeta<string> = string,
56
+ TAccountVaultTokenAccount extends string | AccountMeta<string> = string,
57
+ TAccountEscrowState extends string | AccountMeta<string> = string,
58
+ TAccountEscrowAuthority extends string | AccountMeta<string> = string,
59
+ TAccountEscrowTokenAccount extends string | AccountMeta<string> = string,
60
+ TAccountMakerTokenAccount extends string | AccountMeta<string> = string,
61
+ TAccountFeeWalletTokenAccount extends string | AccountMeta<string> = string,
62
+ TAccountMaker extends string | AccountMeta<string> = string,
63
+ TAccountTokenProgram extends string | AccountMeta<string> =
64
+ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
65
+ TRemainingAccounts extends readonly AccountMeta<string>[] = [],
66
+ > = Instruction<TProgram> &
67
+ InstructionWithData<ReadonlyUint8Array> &
68
+ InstructionWithAccounts<
69
+ [
70
+ TAccountPoolLoan extends string
71
+ ? WritableAccount<TAccountPoolLoan>
72
+ : TAccountPoolLoan,
73
+ TAccountVault extends string
74
+ ? WritableAccount<TAccountVault>
75
+ : TAccountVault,
76
+ TAccountVaultTokenAccount extends string
77
+ ? WritableAccount<TAccountVaultTokenAccount>
78
+ : TAccountVaultTokenAccount,
79
+ TAccountEscrowState extends string
80
+ ? ReadonlyAccount<TAccountEscrowState>
81
+ : TAccountEscrowState,
82
+ TAccountEscrowAuthority extends string
83
+ ? ReadonlyAccount<TAccountEscrowAuthority>
84
+ : TAccountEscrowAuthority,
85
+ TAccountEscrowTokenAccount extends string
86
+ ? WritableAccount<TAccountEscrowTokenAccount>
87
+ : TAccountEscrowTokenAccount,
88
+ TAccountMakerTokenAccount extends string
89
+ ? WritableAccount<TAccountMakerTokenAccount>
90
+ : TAccountMakerTokenAccount,
91
+ TAccountFeeWalletTokenAccount extends string
92
+ ? WritableAccount<TAccountFeeWalletTokenAccount>
93
+ : TAccountFeeWalletTokenAccount,
94
+ TAccountMaker extends string
95
+ ? ReadonlySignerAccount<TAccountMaker> &
96
+ AccountSignerMeta<TAccountMaker>
97
+ : TAccountMaker,
98
+ TAccountTokenProgram extends string
99
+ ? ReadonlyAccount<TAccountTokenProgram>
100
+ : TAccountTokenProgram,
101
+ ...TRemainingAccounts,
102
+ ]
103
+ >;
104
+
105
+ export type RepayPoolLoanFromWalletInstructionData = {
106
+ discriminator: ReadonlyUint8Array;
107
+ };
108
+
109
+ export type RepayPoolLoanFromWalletInstructionDataArgs = {};
110
+
111
+ export function getRepayPoolLoanFromWalletInstructionDataEncoder(): FixedSizeEncoder<RepayPoolLoanFromWalletInstructionDataArgs> {
112
+ return transformEncoder(
113
+ getStructEncoder([["discriminator", fixEncoderSize(getBytesEncoder(), 8)]]),
114
+ (value) => ({
115
+ ...value,
116
+ discriminator: REPAY_POOL_LOAN_FROM_WALLET_DISCRIMINATOR,
117
+ }),
118
+ );
119
+ }
120
+
121
+ export function getRepayPoolLoanFromWalletInstructionDataDecoder(): FixedSizeDecoder<RepayPoolLoanFromWalletInstructionData> {
122
+ return getStructDecoder([
123
+ ["discriminator", fixDecoderSize(getBytesDecoder(), 8)],
124
+ ]);
125
+ }
126
+
127
+ export function getRepayPoolLoanFromWalletInstructionDataCodec(): FixedSizeCodec<
128
+ RepayPoolLoanFromWalletInstructionDataArgs,
129
+ RepayPoolLoanFromWalletInstructionData
130
+ > {
131
+ return combineCodec(
132
+ getRepayPoolLoanFromWalletInstructionDataEncoder(),
133
+ getRepayPoolLoanFromWalletInstructionDataDecoder(),
134
+ );
135
+ }
136
+
137
+ export type RepayPoolLoanFromWalletAsyncInput<
138
+ TAccountPoolLoan extends string = string,
139
+ TAccountVault extends string = string,
140
+ TAccountVaultTokenAccount extends string = string,
141
+ TAccountEscrowState extends string = string,
142
+ TAccountEscrowAuthority extends string = string,
143
+ TAccountEscrowTokenAccount extends string = string,
144
+ TAccountMakerTokenAccount extends string = string,
145
+ TAccountFeeWalletTokenAccount extends string = string,
146
+ TAccountMaker extends string = string,
147
+ TAccountTokenProgram extends string = string,
148
+ > = {
149
+ poolLoan: Address<TAccountPoolLoan>;
150
+ vault: Address<TAccountVault>;
151
+ vaultTokenAccount: Address<TAccountVaultTokenAccount>;
152
+ escrowState: Address<TAccountEscrowState>;
153
+ escrowAuthority?: Address<TAccountEscrowAuthority>;
154
+ escrowTokenAccount: Address<TAccountEscrowTokenAccount>;
155
+ makerTokenAccount: Address<TAccountMakerTokenAccount>;
156
+ feeWalletTokenAccount: Address<TAccountFeeWalletTokenAccount>;
157
+ maker: TransactionSigner<TAccountMaker>;
158
+ tokenProgram?: Address<TAccountTokenProgram>;
159
+ };
160
+
161
+ export async function getRepayPoolLoanFromWalletInstructionAsync<
162
+ TAccountPoolLoan extends string,
163
+ TAccountVault extends string,
164
+ TAccountVaultTokenAccount extends string,
165
+ TAccountEscrowState extends string,
166
+ TAccountEscrowAuthority extends string,
167
+ TAccountEscrowTokenAccount extends string,
168
+ TAccountMakerTokenAccount extends string,
169
+ TAccountFeeWalletTokenAccount extends string,
170
+ TAccountMaker extends string,
171
+ TAccountTokenProgram extends string,
172
+ TProgramAddress extends Address = typeof OPTION_PROGRAM_PROGRAM_ADDRESS,
173
+ >(
174
+ input: RepayPoolLoanFromWalletAsyncInput<
175
+ TAccountPoolLoan,
176
+ TAccountVault,
177
+ TAccountVaultTokenAccount,
178
+ TAccountEscrowState,
179
+ TAccountEscrowAuthority,
180
+ TAccountEscrowTokenAccount,
181
+ TAccountMakerTokenAccount,
182
+ TAccountFeeWalletTokenAccount,
183
+ TAccountMaker,
184
+ TAccountTokenProgram
185
+ >,
186
+ config?: { programAddress?: TProgramAddress },
187
+ ): Promise<
188
+ RepayPoolLoanFromWalletInstruction<
189
+ TProgramAddress,
190
+ TAccountPoolLoan,
191
+ TAccountVault,
192
+ TAccountVaultTokenAccount,
193
+ TAccountEscrowState,
194
+ TAccountEscrowAuthority,
195
+ TAccountEscrowTokenAccount,
196
+ TAccountMakerTokenAccount,
197
+ TAccountFeeWalletTokenAccount,
198
+ TAccountMaker,
199
+ TAccountTokenProgram
200
+ >
201
+ > {
202
+ // Program address.
203
+ const programAddress =
204
+ config?.programAddress ?? OPTION_PROGRAM_PROGRAM_ADDRESS;
205
+
206
+ // Original accounts.
207
+ const originalAccounts = {
208
+ poolLoan: { value: input.poolLoan ?? null, isWritable: true },
209
+ vault: { value: input.vault ?? null, isWritable: true },
210
+ vaultTokenAccount: {
211
+ value: input.vaultTokenAccount ?? null,
212
+ isWritable: true,
213
+ },
214
+ escrowState: { value: input.escrowState ?? null, isWritable: false },
215
+ escrowAuthority: {
216
+ value: input.escrowAuthority ?? null,
217
+ isWritable: false,
218
+ },
219
+ escrowTokenAccount: {
220
+ value: input.escrowTokenAccount ?? null,
221
+ isWritable: true,
222
+ },
223
+ makerTokenAccount: {
224
+ value: input.makerTokenAccount ?? null,
225
+ isWritable: true,
226
+ },
227
+ feeWalletTokenAccount: {
228
+ value: input.feeWalletTokenAccount ?? null,
229
+ isWritable: true,
230
+ },
231
+ maker: { value: input.maker ?? null, isWritable: false },
232
+ tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
233
+ };
234
+ const accounts = originalAccounts as Record<
235
+ keyof typeof originalAccounts,
236
+ ResolvedAccount
237
+ >;
238
+
239
+ // Resolve default values.
240
+ if (!accounts.escrowAuthority.value) {
241
+ accounts.escrowAuthority.value = await getProgramDerivedAddress({
242
+ programAddress,
243
+ seeds: [
244
+ getBytesEncoder().encode(
245
+ new Uint8Array([
246
+ 101, 115, 99, 114, 111, 119, 95, 97, 117, 116, 104, 111, 114, 105,
247
+ 116, 121, 95, 118, 50,
248
+ ]),
249
+ ),
250
+ getAddressEncoder().encode(expectAddress(accounts.escrowState.value)),
251
+ ],
252
+ });
253
+ }
254
+ if (!accounts.tokenProgram.value) {
255
+ accounts.tokenProgram.value =
256
+ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
257
+ }
258
+
259
+ const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
260
+ return Object.freeze({
261
+ accounts: [
262
+ getAccountMeta(accounts.poolLoan),
263
+ getAccountMeta(accounts.vault),
264
+ getAccountMeta(accounts.vaultTokenAccount),
265
+ getAccountMeta(accounts.escrowState),
266
+ getAccountMeta(accounts.escrowAuthority),
267
+ getAccountMeta(accounts.escrowTokenAccount),
268
+ getAccountMeta(accounts.makerTokenAccount),
269
+ getAccountMeta(accounts.feeWalletTokenAccount),
270
+ getAccountMeta(accounts.maker),
271
+ getAccountMeta(accounts.tokenProgram),
272
+ ],
273
+ data: getRepayPoolLoanFromWalletInstructionDataEncoder().encode({}),
274
+ programAddress,
275
+ } as RepayPoolLoanFromWalletInstruction<
276
+ TProgramAddress,
277
+ TAccountPoolLoan,
278
+ TAccountVault,
279
+ TAccountVaultTokenAccount,
280
+ TAccountEscrowState,
281
+ TAccountEscrowAuthority,
282
+ TAccountEscrowTokenAccount,
283
+ TAccountMakerTokenAccount,
284
+ TAccountFeeWalletTokenAccount,
285
+ TAccountMaker,
286
+ TAccountTokenProgram
287
+ >);
288
+ }
289
+
290
+ export type RepayPoolLoanFromWalletInput<
291
+ TAccountPoolLoan extends string = string,
292
+ TAccountVault extends string = string,
293
+ TAccountVaultTokenAccount extends string = string,
294
+ TAccountEscrowState extends string = string,
295
+ TAccountEscrowAuthority extends string = string,
296
+ TAccountEscrowTokenAccount extends string = string,
297
+ TAccountMakerTokenAccount extends string = string,
298
+ TAccountFeeWalletTokenAccount extends string = string,
299
+ TAccountMaker extends string = string,
300
+ TAccountTokenProgram extends string = string,
301
+ > = {
302
+ poolLoan: Address<TAccountPoolLoan>;
303
+ vault: Address<TAccountVault>;
304
+ vaultTokenAccount: Address<TAccountVaultTokenAccount>;
305
+ escrowState: Address<TAccountEscrowState>;
306
+ escrowAuthority: Address<TAccountEscrowAuthority>;
307
+ escrowTokenAccount: Address<TAccountEscrowTokenAccount>;
308
+ makerTokenAccount: Address<TAccountMakerTokenAccount>;
309
+ feeWalletTokenAccount: Address<TAccountFeeWalletTokenAccount>;
310
+ maker: TransactionSigner<TAccountMaker>;
311
+ tokenProgram?: Address<TAccountTokenProgram>;
312
+ };
313
+
314
+ export function getRepayPoolLoanFromWalletInstruction<
315
+ TAccountPoolLoan extends string,
316
+ TAccountVault extends string,
317
+ TAccountVaultTokenAccount extends string,
318
+ TAccountEscrowState extends string,
319
+ TAccountEscrowAuthority extends string,
320
+ TAccountEscrowTokenAccount extends string,
321
+ TAccountMakerTokenAccount extends string,
322
+ TAccountFeeWalletTokenAccount extends string,
323
+ TAccountMaker extends string,
324
+ TAccountTokenProgram extends string,
325
+ TProgramAddress extends Address = typeof OPTION_PROGRAM_PROGRAM_ADDRESS,
326
+ >(
327
+ input: RepayPoolLoanFromWalletInput<
328
+ TAccountPoolLoan,
329
+ TAccountVault,
330
+ TAccountVaultTokenAccount,
331
+ TAccountEscrowState,
332
+ TAccountEscrowAuthority,
333
+ TAccountEscrowTokenAccount,
334
+ TAccountMakerTokenAccount,
335
+ TAccountFeeWalletTokenAccount,
336
+ TAccountMaker,
337
+ TAccountTokenProgram
338
+ >,
339
+ config?: { programAddress?: TProgramAddress },
340
+ ): RepayPoolLoanFromWalletInstruction<
341
+ TProgramAddress,
342
+ TAccountPoolLoan,
343
+ TAccountVault,
344
+ TAccountVaultTokenAccount,
345
+ TAccountEscrowState,
346
+ TAccountEscrowAuthority,
347
+ TAccountEscrowTokenAccount,
348
+ TAccountMakerTokenAccount,
349
+ TAccountFeeWalletTokenAccount,
350
+ TAccountMaker,
351
+ TAccountTokenProgram
352
+ > {
353
+ // Program address.
354
+ const programAddress =
355
+ config?.programAddress ?? OPTION_PROGRAM_PROGRAM_ADDRESS;
356
+
357
+ // Original accounts.
358
+ const originalAccounts = {
359
+ poolLoan: { value: input.poolLoan ?? null, isWritable: true },
360
+ vault: { value: input.vault ?? null, isWritable: true },
361
+ vaultTokenAccount: {
362
+ value: input.vaultTokenAccount ?? null,
363
+ isWritable: true,
364
+ },
365
+ escrowState: { value: input.escrowState ?? null, isWritable: false },
366
+ escrowAuthority: {
367
+ value: input.escrowAuthority ?? null,
368
+ isWritable: false,
369
+ },
370
+ escrowTokenAccount: {
371
+ value: input.escrowTokenAccount ?? null,
372
+ isWritable: true,
373
+ },
374
+ makerTokenAccount: {
375
+ value: input.makerTokenAccount ?? null,
376
+ isWritable: true,
377
+ },
378
+ feeWalletTokenAccount: {
379
+ value: input.feeWalletTokenAccount ?? null,
380
+ isWritable: true,
381
+ },
382
+ maker: { value: input.maker ?? null, isWritable: false },
383
+ tokenProgram: { value: input.tokenProgram ?? null, isWritable: false },
384
+ };
385
+ const accounts = originalAccounts as Record<
386
+ keyof typeof originalAccounts,
387
+ ResolvedAccount
388
+ >;
389
+
390
+ // Resolve default values.
391
+ if (!accounts.tokenProgram.value) {
392
+ accounts.tokenProgram.value =
393
+ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" as Address<"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA">;
394
+ }
395
+
396
+ const getAccountMeta = getAccountMetaFactory(programAddress, "programId");
397
+ return Object.freeze({
398
+ accounts: [
399
+ getAccountMeta(accounts.poolLoan),
400
+ getAccountMeta(accounts.vault),
401
+ getAccountMeta(accounts.vaultTokenAccount),
402
+ getAccountMeta(accounts.escrowState),
403
+ getAccountMeta(accounts.escrowAuthority),
404
+ getAccountMeta(accounts.escrowTokenAccount),
405
+ getAccountMeta(accounts.makerTokenAccount),
406
+ getAccountMeta(accounts.feeWalletTokenAccount),
407
+ getAccountMeta(accounts.maker),
408
+ getAccountMeta(accounts.tokenProgram),
409
+ ],
410
+ data: getRepayPoolLoanFromWalletInstructionDataEncoder().encode({}),
411
+ programAddress,
412
+ } as RepayPoolLoanFromWalletInstruction<
413
+ TProgramAddress,
414
+ TAccountPoolLoan,
415
+ TAccountVault,
416
+ TAccountVaultTokenAccount,
417
+ TAccountEscrowState,
418
+ TAccountEscrowAuthority,
419
+ TAccountEscrowTokenAccount,
420
+ TAccountMakerTokenAccount,
421
+ TAccountFeeWalletTokenAccount,
422
+ TAccountMaker,
423
+ TAccountTokenProgram
424
+ >);
425
+ }
426
+
427
+ export type ParsedRepayPoolLoanFromWalletInstruction<
428
+ TProgram extends string = typeof OPTION_PROGRAM_PROGRAM_ADDRESS,
429
+ TAccountMetas extends readonly AccountMeta[] = readonly AccountMeta[],
430
+ > = {
431
+ programAddress: Address<TProgram>;
432
+ accounts: {
433
+ poolLoan: TAccountMetas[0];
434
+ vault: TAccountMetas[1];
435
+ vaultTokenAccount: TAccountMetas[2];
436
+ escrowState: TAccountMetas[3];
437
+ escrowAuthority: TAccountMetas[4];
438
+ escrowTokenAccount: TAccountMetas[5];
439
+ makerTokenAccount: TAccountMetas[6];
440
+ feeWalletTokenAccount: TAccountMetas[7];
441
+ maker: TAccountMetas[8];
442
+ tokenProgram: TAccountMetas[9];
443
+ };
444
+ data: RepayPoolLoanFromWalletInstructionData;
445
+ };
446
+
447
+ export function parseRepayPoolLoanFromWalletInstruction<
448
+ TProgram extends string,
449
+ TAccountMetas extends readonly AccountMeta[],
450
+ >(
451
+ instruction: Instruction<TProgram> &
452
+ InstructionWithAccounts<TAccountMetas> &
453
+ InstructionWithData<ReadonlyUint8Array>,
454
+ ): ParsedRepayPoolLoanFromWalletInstruction<TProgram, TAccountMetas> {
455
+ if (instruction.accounts.length < 10) {
456
+ // TODO: Coded error.
457
+ throw new Error("Not enough accounts");
458
+ }
459
+ let accountIndex = 0;
460
+ const getNextAccount = () => {
461
+ const accountMeta = (instruction.accounts as TAccountMetas)[accountIndex]!;
462
+ accountIndex += 1;
463
+ return accountMeta;
464
+ };
465
+ return {
466
+ programAddress: instruction.programAddress,
467
+ accounts: {
468
+ poolLoan: getNextAccount(),
469
+ vault: getNextAccount(),
470
+ vaultTokenAccount: getNextAccount(),
471
+ escrowState: getNextAccount(),
472
+ escrowAuthority: getNextAccount(),
473
+ escrowTokenAccount: getNextAccount(),
474
+ makerTokenAccount: getNextAccount(),
475
+ feeWalletTokenAccount: getNextAccount(),
476
+ maker: getNextAccount(),
477
+ tokenProgram: getNextAccount(),
478
+ },
479
+ data: getRepayPoolLoanFromWalletInstructionDataDecoder().decode(
480
+ instruction.data,
481
+ ),
482
+ };
483
+ }
@@ -63,6 +63,7 @@ export type UnwindWriterUnsoldInstruction<
63
63
  TAccountWriterShortAccount extends string | AccountMeta<string> = string,
64
64
  TAccountCollateralVault extends string | AccountMeta<string> = string,
65
65
  TAccountWriterCollateralAccount extends string | AccountMeta<string> = string,
66
+ TAccountOmlpVaultState extends string | AccountMeta<string> = string,
66
67
  TAccountOmlpVault extends string | AccountMeta<string> = string,
67
68
  TAccountFeeWallet extends string | AccountMeta<string> = string,
68
69
  TAccountWriter extends string | AccountMeta<string> = string,
@@ -105,6 +106,9 @@ export type UnwindWriterUnsoldInstruction<
105
106
  TAccountWriterCollateralAccount extends string
106
107
  ? WritableAccount<TAccountWriterCollateralAccount>
107
108
  : TAccountWriterCollateralAccount,
109
+ TAccountOmlpVaultState extends string
110
+ ? WritableAccount<TAccountOmlpVaultState>
111
+ : TAccountOmlpVaultState,
108
112
  TAccountOmlpVault extends string
109
113
  ? WritableAccount<TAccountOmlpVault>
110
114
  : TAccountOmlpVault,
@@ -175,6 +179,7 @@ export type UnwindWriterUnsoldAsyncInput<
175
179
  TAccountWriterShortAccount extends string = string,
176
180
  TAccountCollateralVault extends string = string,
177
181
  TAccountWriterCollateralAccount extends string = string,
182
+ TAccountOmlpVaultState extends string = string,
178
183
  TAccountOmlpVault extends string = string,
179
184
  TAccountFeeWallet extends string = string,
180
185
  TAccountWriter extends string = string,
@@ -201,6 +206,8 @@ export type UnwindWriterUnsoldAsyncInput<
201
206
  collateralVault: Address<TAccountCollateralVault>;
202
207
  /** Writer's underlying token account (receives returned collateral) */
203
208
  writerCollateralAccount: Address<TAccountWriterCollateralAccount>;
209
+ /** OMLP Vault state PDA (for total_loans, add_interest_to_index) - optional, required when repaying loans */
210
+ omlpVaultState?: Address<TAccountOmlpVaultState>;
204
211
  /** OMLP vault token account (receives loan repayments) - optional */
205
212
  omlpVault?: Address<TAccountOmlpVault>;
206
213
  /** Protocol fee wallet (receives protocol fees) - optional */
@@ -222,6 +229,7 @@ export async function getUnwindWriterUnsoldInstructionAsync<
222
229
  TAccountWriterShortAccount extends string,
223
230
  TAccountCollateralVault extends string,
224
231
  TAccountWriterCollateralAccount extends string,
232
+ TAccountOmlpVaultState extends string,
225
233
  TAccountOmlpVault extends string,
226
234
  TAccountFeeWallet extends string,
227
235
  TAccountWriter extends string,
@@ -240,6 +248,7 @@ export async function getUnwindWriterUnsoldInstructionAsync<
240
248
  TAccountWriterShortAccount,
241
249
  TAccountCollateralVault,
242
250
  TAccountWriterCollateralAccount,
251
+ TAccountOmlpVaultState,
243
252
  TAccountOmlpVault,
244
253
  TAccountFeeWallet,
245
254
  TAccountWriter,
@@ -260,6 +269,7 @@ export async function getUnwindWriterUnsoldInstructionAsync<
260
269
  TAccountWriterShortAccount,
261
270
  TAccountCollateralVault,
262
271
  TAccountWriterCollateralAccount,
272
+ TAccountOmlpVaultState,
263
273
  TAccountOmlpVault,
264
274
  TAccountFeeWallet,
265
275
  TAccountWriter,
@@ -292,6 +302,7 @@ export async function getUnwindWriterUnsoldInstructionAsync<
292
302
  value: input.writerCollateralAccount ?? null,
293
303
  isWritable: true,
294
304
  },
305
+ omlpVaultState: { value: input.omlpVaultState ?? null, isWritable: true },
295
306
  omlpVault: { value: input.omlpVault ?? null, isWritable: true },
296
307
  feeWallet: { value: input.feeWallet ?? null, isWritable: true },
297
308
  writer: { value: input.writer ?? null, isWritable: true },
@@ -358,6 +369,7 @@ export async function getUnwindWriterUnsoldInstructionAsync<
358
369
  getAccountMeta(accounts.writerShortAccount),
359
370
  getAccountMeta(accounts.collateralVault),
360
371
  getAccountMeta(accounts.writerCollateralAccount),
372
+ getAccountMeta(accounts.omlpVaultState),
361
373
  getAccountMeta(accounts.omlpVault),
362
374
  getAccountMeta(accounts.feeWallet),
363
375
  getAccountMeta(accounts.writer),
@@ -380,6 +392,7 @@ export async function getUnwindWriterUnsoldInstructionAsync<
380
392
  TAccountWriterShortAccount,
381
393
  TAccountCollateralVault,
382
394
  TAccountWriterCollateralAccount,
395
+ TAccountOmlpVaultState,
383
396
  TAccountOmlpVault,
384
397
  TAccountFeeWallet,
385
398
  TAccountWriter,
@@ -399,6 +412,7 @@ export type UnwindWriterUnsoldInput<
399
412
  TAccountWriterShortAccount extends string = string,
400
413
  TAccountCollateralVault extends string = string,
401
414
  TAccountWriterCollateralAccount extends string = string,
415
+ TAccountOmlpVaultState extends string = string,
402
416
  TAccountOmlpVault extends string = string,
403
417
  TAccountFeeWallet extends string = string,
404
418
  TAccountWriter extends string = string,
@@ -425,6 +439,8 @@ export type UnwindWriterUnsoldInput<
425
439
  collateralVault: Address<TAccountCollateralVault>;
426
440
  /** Writer's underlying token account (receives returned collateral) */
427
441
  writerCollateralAccount: Address<TAccountWriterCollateralAccount>;
442
+ /** OMLP Vault state PDA (for total_loans, add_interest_to_index) - optional, required when repaying loans */
443
+ omlpVaultState?: Address<TAccountOmlpVaultState>;
428
444
  /** OMLP vault token account (receives loan repayments) - optional */
429
445
  omlpVault?: Address<TAccountOmlpVault>;
430
446
  /** Protocol fee wallet (receives protocol fees) - optional */
@@ -446,6 +462,7 @@ export function getUnwindWriterUnsoldInstruction<
446
462
  TAccountWriterShortAccount extends string,
447
463
  TAccountCollateralVault extends string,
448
464
  TAccountWriterCollateralAccount extends string,
465
+ TAccountOmlpVaultState extends string,
449
466
  TAccountOmlpVault extends string,
450
467
  TAccountFeeWallet extends string,
451
468
  TAccountWriter extends string,
@@ -464,6 +481,7 @@ export function getUnwindWriterUnsoldInstruction<
464
481
  TAccountWriterShortAccount,
465
482
  TAccountCollateralVault,
466
483
  TAccountWriterCollateralAccount,
484
+ TAccountOmlpVaultState,
467
485
  TAccountOmlpVault,
468
486
  TAccountFeeWallet,
469
487
  TAccountWriter,
@@ -483,6 +501,7 @@ export function getUnwindWriterUnsoldInstruction<
483
501
  TAccountWriterShortAccount,
484
502
  TAccountCollateralVault,
485
503
  TAccountWriterCollateralAccount,
504
+ TAccountOmlpVaultState,
486
505
  TAccountOmlpVault,
487
506
  TAccountFeeWallet,
488
507
  TAccountWriter,
@@ -514,6 +533,7 @@ export function getUnwindWriterUnsoldInstruction<
514
533
  value: input.writerCollateralAccount ?? null,
515
534
  isWritable: true,
516
535
  },
536
+ omlpVaultState: { value: input.omlpVaultState ?? null, isWritable: true },
517
537
  omlpVault: { value: input.omlpVault ?? null, isWritable: true },
518
538
  feeWallet: { value: input.feeWallet ?? null, isWritable: true },
519
539
  writer: { value: input.writer ?? null, isWritable: true },
@@ -551,6 +571,7 @@ export function getUnwindWriterUnsoldInstruction<
551
571
  getAccountMeta(accounts.writerShortAccount),
552
572
  getAccountMeta(accounts.collateralVault),
553
573
  getAccountMeta(accounts.writerCollateralAccount),
574
+ getAccountMeta(accounts.omlpVaultState),
554
575
  getAccountMeta(accounts.omlpVault),
555
576
  getAccountMeta(accounts.feeWallet),
556
577
  getAccountMeta(accounts.writer),
@@ -573,6 +594,7 @@ export function getUnwindWriterUnsoldInstruction<
573
594
  TAccountWriterShortAccount,
574
595
  TAccountCollateralVault,
575
596
  TAccountWriterCollateralAccount,
597
+ TAccountOmlpVaultState,
576
598
  TAccountOmlpVault,
577
599
  TAccountFeeWallet,
578
600
  TAccountWriter,
@@ -607,13 +629,15 @@ export type ParsedUnwindWriterUnsoldInstruction<
607
629
  collateralVault: TAccountMetas[8];
608
630
  /** Writer's underlying token account (receives returned collateral) */
609
631
  writerCollateralAccount: TAccountMetas[9];
632
+ /** OMLP Vault state PDA (for total_loans, add_interest_to_index) - optional, required when repaying loans */
633
+ omlpVaultState?: TAccountMetas[10] | undefined;
610
634
  /** OMLP vault token account (receives loan repayments) - optional */
611
- omlpVault?: TAccountMetas[10] | undefined;
635
+ omlpVault?: TAccountMetas[11] | undefined;
612
636
  /** Protocol fee wallet (receives protocol fees) - optional */
613
- feeWallet?: TAccountMetas[11] | undefined;
614
- writer: TAccountMetas[12];
615
- tokenProgram: TAccountMetas[13];
616
- systemProgram: TAccountMetas[14];
637
+ feeWallet?: TAccountMetas[12] | undefined;
638
+ writer: TAccountMetas[13];
639
+ tokenProgram: TAccountMetas[14];
640
+ systemProgram: TAccountMetas[15];
617
641
  };
618
642
  data: UnwindWriterUnsoldInstructionData;
619
643
  };
@@ -626,7 +650,7 @@ export function parseUnwindWriterUnsoldInstruction<
626
650
  InstructionWithAccounts<TAccountMetas> &
627
651
  InstructionWithData<ReadonlyUint8Array>,
628
652
  ): ParsedUnwindWriterUnsoldInstruction<TProgram, TAccountMetas> {
629
- if (instruction.accounts.length < 15) {
653
+ if (instruction.accounts.length < 16) {
630
654
  // TODO: Coded error.
631
655
  throw new Error("Not enough accounts");
632
656
  }
@@ -655,6 +679,7 @@ export function parseUnwindWriterUnsoldInstruction<
655
679
  writerShortAccount: getNextAccount(),
656
680
  collateralVault: getNextAccount(),
657
681
  writerCollateralAccount: getNextAccount(),
682
+ omlpVaultState: getNextOptionalAccount(),
658
683
  omlpVault: getNextOptionalAccount(),
659
684
  feeWallet: getNextOptionalAccount(),
660
685
  writer: getNextAccount(),
@@ -41,6 +41,7 @@ import {
41
41
  parseOptionMintInstruction,
42
42
  parseOptionValidateInstruction,
43
43
  parseRepayPoolLoanFromCollateralInstruction,
44
+ parseRepayPoolLoanFromWalletInstruction,
44
45
  parseRepayPoolLoanInstruction,
45
46
  parseSettleMakerCollateralInstruction,
46
47
  parseSyncWriterPositionInstruction,
@@ -74,6 +75,7 @@ import {
74
75
  type ParsedOptionMintInstruction,
75
76
  type ParsedOptionValidateInstruction,
76
77
  type ParsedRepayPoolLoanFromCollateralInstruction,
78
+ type ParsedRepayPoolLoanFromWalletInstruction,
77
79
  type ParsedRepayPoolLoanInstruction,
78
80
  type ParsedSettleMakerCollateralInstruction,
79
81
  type ParsedSyncWriterPositionInstruction,
@@ -282,6 +284,7 @@ export enum OptionProgramInstruction {
282
284
  OptionValidate,
283
285
  RepayPoolLoan,
284
286
  RepayPoolLoanFromCollateral,
287
+ RepayPoolLoanFromWallet,
285
288
  SettleMakerCollateral,
286
289
  SyncWriterPosition,
287
290
  TransferAdmin,
@@ -571,6 +574,17 @@ export function identifyOptionProgramInstruction(
571
574
  ) {
572
575
  return OptionProgramInstruction.RepayPoolLoanFromCollateral;
573
576
  }
577
+ if (
578
+ containsBytes(
579
+ data,
580
+ fixEncoderSize(getBytesEncoder(), 8).encode(
581
+ new Uint8Array([78, 130, 135, 90, 211, 21, 247, 247]),
582
+ ),
583
+ 0,
584
+ )
585
+ ) {
586
+ return OptionProgramInstruction.RepayPoolLoanFromWallet;
587
+ }
574
588
  if (
575
589
  containsBytes(
576
590
  data,
@@ -742,6 +756,9 @@ export type ParsedOptionProgramInstruction<
742
756
  | ({
743
757
  instructionType: OptionProgramInstruction.RepayPoolLoanFromCollateral;
744
758
  } & ParsedRepayPoolLoanFromCollateralInstruction<TProgram>)
759
+ | ({
760
+ instructionType: OptionProgramInstruction.RepayPoolLoanFromWallet;
761
+ } & ParsedRepayPoolLoanFromWalletInstruction<TProgram>)
745
762
  | ({
746
763
  instructionType: OptionProgramInstruction.SettleMakerCollateral;
747
764
  } & ParsedSettleMakerCollateralInstruction<TProgram>)
@@ -947,6 +964,13 @@ export function parseOptionProgramInstruction<TProgram extends string>(
947
964
  ...parseRepayPoolLoanFromCollateralInstruction(instruction),
948
965
  };
949
966
  }
967
+ case OptionProgramInstruction.RepayPoolLoanFromWallet: {
968
+ assertIsInstructionWithAccounts(instruction);
969
+ return {
970
+ instructionType: OptionProgramInstruction.RepayPoolLoanFromWallet,
971
+ ...parseRepayPoolLoanFromWalletInstruction(instruction),
972
+ };
973
+ }
950
974
  case OptionProgramInstruction.SettleMakerCollateral: {
951
975
  assertIsInstructionWithAccounts(instruction);
952
976
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicentral/sos-sdk",
3
- "version": "0.2.13",
3
+ "version": "0.3.0-alpha.2",
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",
@@ -23,6 +23,9 @@
23
23
  "decimal.js": "^10.4.3"
24
24
  },
25
25
  "scripts": {
26
- "typecheck": "tsc --project tsconfig.json --noEmit"
26
+ "typecheck": "tsc --project tsconfig.json --noEmit",
27
+ "publish-beta": "dotenv -e .env -- pnpm publish --access public --tag beta",
28
+ "publish-alpha": "dotenv -e .env -- pnpm publish --access public --tag alpha",
29
+ "deprecate": "dotenv -e .env -- pnpm exec npm deprecate"
27
30
  }
28
31
  }
package/short/builders.ts CHANGED
@@ -8,11 +8,14 @@ import {
8
8
  import type { Instruction } 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";
12
+ import { fetchPoolLoansByMaker } from "../accounts/list";
11
13
  import { resolveOptionAccounts } from "../accounts/resolve-option";
12
14
  import {
13
15
  deriveAssociatedTokenAddress,
14
16
  deriveMakerCollateralSharePda,
15
17
  deriveMetadataPda,
18
+ deriveVaultPda,
16
19
  deriveWriterPositionPda,
17
20
  } from "../accounts/pdas";
18
21
  import { assertNonNegativeAmount, assertPositiveAmount } from "../shared/amounts";
@@ -59,6 +62,9 @@ export interface BuildOptionMintParams {
59
62
  remainingAccounts?: RemainingAccountInput[];
60
63
  }
61
64
 
65
+ /** Max PoolLoans per unwind to stay under 64-account tx limit (~18 named + ~20 loans) */
66
+ const MAX_POOL_LOANS_PER_UNWIND = 20;
67
+
62
68
  export interface BuildUnwindWriterUnsoldParams {
63
69
  optionPool: AddressLike;
64
70
  optionAccount: AddressLike;
@@ -72,6 +78,7 @@ export interface BuildUnwindWriterUnsoldParams {
72
78
  unwindQty: bigint | number;
73
79
  collateralPool?: AddressLike;
74
80
  writerPosition?: AddressLike;
81
+ omlpVaultState?: AddressLike;
75
82
  omlpVault?: AddressLike;
76
83
  feeWallet?: AddressLike;
77
84
  remainingAccounts?: RemainingAccountInput[];
@@ -308,6 +315,7 @@ export async function buildUnwindWriterUnsoldInstruction(
308
315
  writerShortAccount: toAddress(params.writerShortAccount),
309
316
  collateralVault: toAddress(params.collateralVault),
310
317
  writerCollateralAccount: toAddress(params.writerCollateralAccount),
318
+ omlpVaultState: params.omlpVaultState ? toAddress(params.omlpVaultState) : undefined,
311
319
  omlpVault: params.omlpVault ? toAddress(params.omlpVault) : undefined,
312
320
  feeWallet: params.feeWallet ? toAddress(params.feeWallet) : undefined,
313
321
  writer: toAddress(params.writer) as any,
@@ -333,11 +341,96 @@ export interface BuildUnwindWriterUnsoldTransactionWithDerivationParams {
333
341
  unwindQty: bigint | number;
334
342
  rpc: KitRpc;
335
343
  programId?: AddressLike;
344
+ omlpVaultState?: AddressLike;
336
345
  omlpVault?: AddressLike;
337
346
  feeWallet?: AddressLike;
347
+ /**
348
+ * When repaying pool loans: [PoolLoan₁, PoolLoan₂, ...] (all writable).
349
+ * omlpVaultState, omlpVault, feeWallet must also be passed.
350
+ * Prefer {@link buildUnwindWriterUnsoldWithLoanRepayment} to build this automatically.
351
+ */
338
352
  remainingAccounts?: RemainingAccountInput[];
339
353
  }
340
354
 
355
+ export interface BuildUnwindWriterUnsoldWithLoanRepaymentParams {
356
+ underlyingAsset: AddressLike;
357
+ optionType: OptionType;
358
+ strikePrice: number;
359
+ expirationDate: bigint | number;
360
+ writer: AddressLike;
361
+ unwindQty: bigint | number;
362
+ rpc: KitRpc;
363
+ programId?: AddressLike;
364
+ /** Override when pool fetch is not used; otherwise resolved from option pool. */
365
+ underlyingMint?: AddressLike;
366
+ }
367
+
368
+ /**
369
+ * Builds an unwind_writer_unsold transaction that also repays any active pool loans
370
+ * for the option's underlying vault. When a writer unwinds an unsold short that had
371
+ * borrowed from OMLP, the program repays lenders from the collateral vault (burn,
372
+ * repay, then return collateral to writer) in one instruction.
373
+ *
374
+ * Passes omlpVaultState (Vault PDA), omlpVault, feeWallet as named accounts.
375
+ * remaining_accounts: [PoolLoan₁, PoolLoan₂, ...] only (capped at 20).
376
+ * If no active pool loans exist, still passes vault accounts so the API works for all unwinds.
377
+ */
378
+ export async function buildUnwindWriterUnsoldWithLoanRepayment(
379
+ params: BuildUnwindWriterUnsoldWithLoanRepaymentParams
380
+ ): Promise<BuiltTransaction> {
381
+ const resolved = await resolveOptionAccounts({
382
+ underlyingAsset: params.underlyingAsset,
383
+ optionType: params.optionType,
384
+ strikePrice: params.strikePrice,
385
+ expirationDate: params.expirationDate,
386
+ programId: params.programId,
387
+ rpc: params.rpc,
388
+ });
389
+
390
+ const underlyingMint = params.underlyingMint ?? resolved.underlyingMint;
391
+ invariant(
392
+ !!underlyingMint,
393
+ "underlyingMint is required; ensure rpc is provided and option pool is initialized, or pass underlyingMint."
394
+ );
395
+
396
+ const [vaultPda] = await deriveVaultPda(underlyingMint, params.programId);
397
+ const vaultPdaStr = toAddress(vaultPda);
398
+
399
+ const [loans, vault] = await Promise.all([
400
+ fetchPoolLoansByMaker(params.rpc, params.writer),
401
+ fetchVault(params.rpc, vaultPda),
402
+ ]);
403
+
404
+ const vaultLoans = loans
405
+ .filter((item) => toAddress(item.data.vault) === vaultPdaStr)
406
+ .slice(0, MAX_POOL_LOANS_PER_UNWIND);
407
+
408
+ const remainingAccounts: RemainingAccountInput[] = vaultLoans.map((item) => ({
409
+ address: item.address,
410
+ isWritable: true,
411
+ }));
412
+
413
+ const omlpVault = await deriveAssociatedTokenAddress(vaultPda, underlyingMint);
414
+ const feeWallet = vault
415
+ ? await deriveAssociatedTokenAddress(vault.feeWallet, underlyingMint)
416
+ : undefined;
417
+
418
+ return buildUnwindWriterUnsoldTransactionWithDerivation({
419
+ underlyingAsset: params.underlyingAsset,
420
+ optionType: params.optionType,
421
+ strikePrice: params.strikePrice,
422
+ expirationDate: params.expirationDate,
423
+ writer: params.writer,
424
+ unwindQty: params.unwindQty,
425
+ rpc: params.rpc,
426
+ programId: params.programId,
427
+ omlpVaultState: vaultPda,
428
+ omlpVault,
429
+ feeWallet,
430
+ remainingAccounts,
431
+ });
432
+ }
433
+
341
434
  export async function buildUnwindWriterUnsoldTransactionWithDerivation(
342
435
  params: BuildUnwindWriterUnsoldTransactionWithDerivationParams
343
436
  ): Promise<BuiltTransaction> {
@@ -375,6 +468,7 @@ export async function buildUnwindWriterUnsoldTransactionWithDerivation(
375
468
  unwindQty: params.unwindQty,
376
469
  collateralPool: resolved.collateralPool,
377
470
  writerPosition: writerPosition[0],
471
+ omlpVaultState: params.omlpVaultState,
378
472
  omlpVault: params.omlpVault,
379
473
  feeWallet: params.feeWallet,
380
474
  remainingAccounts: params.remainingAccounts,
package/short/pool.ts CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  getBorrowFromPoolInstructionAsync,
3
3
  getRepayPoolLoanInstructionAsync,
4
4
  getRepayPoolLoanFromCollateralInstructionAsync,
5
+ getRepayPoolLoanFromWalletInstructionAsync,
5
6
  } from "../generated/instructions";
6
7
  import type { Instruction } from "@solana/kit";
7
8
  import { toAddress } from "../client/program";
@@ -114,6 +115,39 @@ export async function buildRepayPoolLoanTransaction(
114
115
  return { instructions: [instruction] };
115
116
  }
116
117
 
118
+ /**
119
+ * Repay a pool loan entirely from maker's wallet (no escrow used).
120
+ * Use when standard repay fails with InsufficientEscrowBalance (stuck loan).
121
+ */
122
+ export async function buildRepayPoolLoanFromWalletInstruction(
123
+ params: BuildRepayPoolLoanParams
124
+ ): Promise<Instruction<string>> {
125
+ return getRepayPoolLoanFromWalletInstructionAsync({
126
+ poolLoan: toAddress(params.poolLoan),
127
+ vault: toAddress(params.vault),
128
+ vaultTokenAccount: toAddress(params.vaultTokenAccount),
129
+ escrowState: toAddress(params.escrowState),
130
+ escrowAuthority: params.escrowAuthority
131
+ ? toAddress(params.escrowAuthority)
132
+ : undefined,
133
+ escrowTokenAccount: toAddress(params.escrowTokenAccount),
134
+ makerTokenAccount: toAddress(params.makerTokenAccount),
135
+ feeWalletTokenAccount: toAddress(params.feeWalletTokenAccount),
136
+ maker: toAddress(params.maker) as any,
137
+ tokenProgram: params.tokenProgram ? toAddress(params.tokenProgram) : undefined,
138
+ });
139
+ }
140
+
141
+ /**
142
+ * Builds the repay-from-wallet transaction (stuck loan recovery).
143
+ */
144
+ export async function buildRepayPoolLoanFromWalletTransaction(
145
+ params: BuildRepayPoolLoanParams
146
+ ): Promise<BuiltTransaction> {
147
+ const instruction = await buildRepayPoolLoanFromWalletInstruction(params);
148
+ return { instructions: [instruction] };
149
+ }
150
+
117
151
  export async function buildRepayPoolLoanFromCollateralInstruction(
118
152
  params: BuildRepayPoolLoanFromCollateralParams
119
153
  ): Promise<Instruction<string>> {