@epicentral/sos-sdk 0.4.0-alpha.2 → 0.4.0-alpha.3
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 +29 -10
- package/generated/errors/optionProgram.ts +58 -46
- package/generated/instructions/liquidateWriterPosition.ts +67 -11
- package/generated/programs/optionProgram.ts +2 -2
- package/long/builders.ts +19 -0
- package/long/preflight.ts +6 -2
- package/long/remaining-accounts.ts +14 -1
- package/package.json +1 -1
- package/short/preflight.ts +62 -2
package/README.md
CHANGED
|
@@ -90,11 +90,23 @@ Borrow/repay for writers: use `buildOptionMintTransactionWithDerivation` (with v
|
|
|
90
90
|
|
|
91
91
|
## Unwind with Loan Repayment
|
|
92
92
|
|
|
93
|
-
When a writer unwinds an unsold short that had borrowed from the OMLP pool, the program
|
|
93
|
+
When a writer unwinds an unsold short that had borrowed from the OMLP pool, the program repays proportionally to the unwind ratio inside `unwind_writer_unsold`:
|
|
94
94
|
|
|
95
|
+
**Proportional Repayment (partial unwinds):**
|
|
96
|
+
- Unwind ratio = `unwind_qty / written_qty`
|
|
97
|
+
- Principal repaid = `total_loan_principal * unwind_ratio`
|
|
98
|
+
- Interest repaid = `total_accrued_interest * unwind_ratio`
|
|
99
|
+
- Protocol fees repaid = `total_accrued_fees * unwind_ratio`
|
|
100
|
+
|
|
101
|
+
**Repayment order:**
|
|
95
102
|
1. Collateral vault funds first.
|
|
96
103
|
2. Writer fallback wallet source (`writerRepaymentAccount`) for any shortfall.
|
|
97
|
-
3. If combined funds cannot cover principal + interest + protocol fees, unwind fails with a protocol custom error (not a generic SPL `0x1`).
|
|
104
|
+
3. If combined funds cannot cover proportional principal + interest + protocol fees, unwind fails with a protocol custom error (not a generic SPL `0x1`).
|
|
105
|
+
|
|
106
|
+
**Collateral Return:**
|
|
107
|
+
- Proportional collateral share = `(collateral_deposited * unwind_qty) / written_qty`
|
|
108
|
+
- Returnable collateral = `proportional_share - amount_already_repaid_from_vault`
|
|
109
|
+
- If vault lacks sufficient post-repayment balance, fails with `InsufficientCollateralVault` (6090)
|
|
98
110
|
|
|
99
111
|
Use **`buildUnwindWriterUnsoldWithLoanRepayment`** so that:
|
|
100
112
|
|
|
@@ -106,7 +118,10 @@ Use **`buildUnwindWriterUnsoldWithLoanRepayment`** so that:
|
|
|
106
118
|
Use **`preflightUnwindWriterUnsold`** before building the transaction to get:
|
|
107
119
|
|
|
108
120
|
- Per-loan principal/interest/protocol-fee breakdown.
|
|
109
|
-
-
|
|
121
|
+
- **Proportional obligations** for partial unwinds (principal, interest, fees, total owed).
|
|
122
|
+
- **Collateral return calculation** (proportional share, returnable amount).
|
|
123
|
+
- Collateral-vault available, wallet fallback required, and shortfall.
|
|
124
|
+
- **Top-up UX fields:** `collateralVaultShortfall`, `needsWalletTopUp`.
|
|
110
125
|
- `canRepayFully` so UI can block early with actionable messaging.
|
|
111
126
|
|
|
112
127
|
If there are no active pool loans for that vault, the API still works and passes empty `remaining_accounts`.
|
|
@@ -198,14 +213,18 @@ const tx = await buildBuyFromPoolMarketOrderTransactionWithDerivation({
|
|
|
198
213
|
- Convenience for SOL/WSOL: `slippageBufferLamports`
|
|
199
214
|
- Default buffer: `500_000` base units (0.0005 SOL lamports)
|
|
200
215
|
|
|
201
|
-
### Buy liquidity errors (6041)
|
|
216
|
+
### Buy liquidity errors (6041 split into 6042/6043)
|
|
217
|
+
|
|
218
|
+
The program uses distinct error codes for liquidity failures:
|
|
219
|
+
|
|
220
|
+
- `InsufficientPoolAggregateLiquidity` (6042) – `option_pool.total_available < quantity`
|
|
221
|
+
- `InsufficientWriterPositionLiquidity` (6043) – remaining writer-position accounts cannot cover full quantity in the smallest-first fill loop
|
|
222
|
+
|
|
223
|
+
**Ghost Liquidity:** When `total_available` appears sufficient but active writer positions cannot cover the request. This happens when positions are settled/liquidated but still counted in the aggregate. The SDK now filters inactive positions, and the program skips them in the fill loop.
|
|
202
224
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
- Recommended client flow:
|
|
207
|
-
1. Run `preflightBuyFromPoolMarketOrder` for UX gating.
|
|
208
|
-
2. Build via `buildBuyFromPoolMarketOrderTransactionWithDerivation` so pool + remaining accounts are refetched immediately before build.
|
|
225
|
+
**Recommended client flow:**
|
|
226
|
+
1. Run `preflightBuyFromPoolMarketOrder` for UX gating (checks both pool and active writer liquidity).
|
|
227
|
+
2. Build via `buildBuyFromPoolMarketOrderTransactionWithDerivation` – it refetches pool + remaining accounts and asserts active writer liquidity >= requested quantity before building.
|
|
209
228
|
|
|
210
229
|
### Unwind with loan repayment
|
|
211
230
|
|
|
@@ -98,98 +98,104 @@ export const OPTION_PROGRAM_ERROR__PRICE_IMPACT_TOO_HIGH = 0x1797; // 6039
|
|
|
98
98
|
export const OPTION_PROGRAM_ERROR__SLIPPAGE_TOLERANCE_EXCEEDED = 0x1798; // 6040
|
|
99
99
|
/** InsufficientPoolLiquidity: Insufficient pool liquidity available */
|
|
100
100
|
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_LIQUIDITY = 0x1799; // 6041
|
|
101
|
+
/** InsufficientPoolAggregateLiquidity: Insufficient aggregate pool liquidity for requested quantity */
|
|
102
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_AGGREGATE_LIQUIDITY = 0x179a; // 6042
|
|
103
|
+
/** InsufficientWriterPositionLiquidity: Insufficient active writer liquidity to fill requested quantity */
|
|
104
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_WRITER_POSITION_LIQUIDITY = 0x179b; // 6043
|
|
101
105
|
/** InsufficientUserBalance: Insufficient user balance for withdrawal */
|
|
102
|
-
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_USER_BALANCE =
|
|
106
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_USER_BALANCE = 0x179c; // 6044
|
|
103
107
|
/** UnhealthyPosition: Health ratio below liquidation threshold */
|
|
104
|
-
export const OPTION_PROGRAM_ERROR__UNHEALTHY_POSITION =
|
|
108
|
+
export const OPTION_PROGRAM_ERROR__UNHEALTHY_POSITION = 0x179d; // 6045
|
|
105
109
|
/** UnauthorizedOmlp: Unauthorized to perform this OMLP action */
|
|
106
|
-
export const OPTION_PROGRAM_ERROR__UNAUTHORIZED_OMLP =
|
|
110
|
+
export const OPTION_PROGRAM_ERROR__UNAUTHORIZED_OMLP = 0x179e; // 6046
|
|
107
111
|
/** InvalidTenor: Invalid loan tenor */
|
|
108
|
-
export const OPTION_PROGRAM_ERROR__INVALID_TENOR =
|
|
112
|
+
export const OPTION_PROGRAM_ERROR__INVALID_TENOR = 0x179f; // 6047
|
|
109
113
|
/** InvalidRate: Invalid interest rate */
|
|
110
|
-
export const OPTION_PROGRAM_ERROR__INVALID_RATE =
|
|
114
|
+
export const OPTION_PROGRAM_ERROR__INVALID_RATE = 0x17a0; // 6048
|
|
111
115
|
/** NotForeclosable: Loan is not eligible for foreclosure */
|
|
112
|
-
export const OPTION_PROGRAM_ERROR__NOT_FORECLOSABLE =
|
|
116
|
+
export const OPTION_PROGRAM_ERROR__NOT_FORECLOSABLE = 0x17a1; // 6049
|
|
113
117
|
/** InsufficientVaultLiquidity: Vault has insufficient liquidity */
|
|
114
|
-
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_VAULT_LIQUIDITY =
|
|
118
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_VAULT_LIQUIDITY = 0x17a2; // 6050
|
|
115
119
|
/** InsufficientLoanCollateral: Collateral insufficient for loan */
|
|
116
|
-
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_LOAN_COLLATERAL =
|
|
120
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_LOAN_COLLATERAL = 0x17a3; // 6051
|
|
117
121
|
/** OracleTooStale: Oracle price is stale - cannot validate */
|
|
118
|
-
export const OPTION_PROGRAM_ERROR__ORACLE_TOO_STALE =
|
|
122
|
+
export const OPTION_PROGRAM_ERROR__ORACLE_TOO_STALE = 0x17a4; // 6052
|
|
119
123
|
/** ValidationRequired: Must call option_validate before borrow/settlement */
|
|
120
|
-
export const OPTION_PROGRAM_ERROR__VALIDATION_REQUIRED =
|
|
124
|
+
export const OPTION_PROGRAM_ERROR__VALIDATION_REQUIRED = 0x17a5; // 6053
|
|
121
125
|
/** HealthCalculationFailed: Health ratio calculation failed */
|
|
122
|
-
export const OPTION_PROGRAM_ERROR__HEALTH_CALCULATION_FAILED =
|
|
126
|
+
export const OPTION_PROGRAM_ERROR__HEALTH_CALCULATION_FAILED = 0x17a6; // 6054
|
|
123
127
|
/** ContractAlreadySettled: Contract already settled */
|
|
124
|
-
export const OPTION_PROGRAM_ERROR__CONTRACT_ALREADY_SETTLED =
|
|
128
|
+
export const OPTION_PROGRAM_ERROR__CONTRACT_ALREADY_SETTLED = 0x17a7; // 6055
|
|
125
129
|
/** NoYieldAvailable: No yield available to claim */
|
|
126
|
-
export const OPTION_PROGRAM_ERROR__NO_YIELD_AVAILABLE =
|
|
130
|
+
export const OPTION_PROGRAM_ERROR__NO_YIELD_AVAILABLE = 0x17a8; // 6056
|
|
127
131
|
/** UnauthorizedAccess: Unauthorized access - you don't own this resource */
|
|
128
|
-
export const OPTION_PROGRAM_ERROR__UNAUTHORIZED_ACCESS =
|
|
132
|
+
export const OPTION_PROGRAM_ERROR__UNAUTHORIZED_ACCESS = 0x17a9; // 6057
|
|
129
133
|
/** InsufficientQuantity: Insufficient quantity available in ask position */
|
|
130
|
-
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_QUANTITY =
|
|
134
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_QUANTITY = 0x17aa; // 6058
|
|
131
135
|
/** NothingToClaim: Nothing to claim - no unclaimed premium */
|
|
132
|
-
export const OPTION_PROGRAM_ERROR__NOTHING_TO_CLAIM =
|
|
136
|
+
export const OPTION_PROGRAM_ERROR__NOTHING_TO_CLAIM = 0x17ab; // 6059
|
|
133
137
|
/** PoolNotActive: Pool is not active */
|
|
134
|
-
export const OPTION_PROGRAM_ERROR__POOL_NOT_ACTIVE =
|
|
138
|
+
export const OPTION_PROGRAM_ERROR__POOL_NOT_ACTIVE = 0x17ac; // 6060
|
|
135
139
|
/** PoolAlreadyExists: Pool already exists for this option */
|
|
136
|
-
export const OPTION_PROGRAM_ERROR__POOL_ALREADY_EXISTS =
|
|
140
|
+
export const OPTION_PROGRAM_ERROR__POOL_ALREADY_EXISTS = 0x17ad; // 6061
|
|
137
141
|
/** PoolNotExercised: Pool has not been exercised yet */
|
|
138
|
-
export const OPTION_PROGRAM_ERROR__POOL_NOT_EXERCISED =
|
|
142
|
+
export const OPTION_PROGRAM_ERROR__POOL_NOT_EXERCISED = 0x17ae; // 6062
|
|
139
143
|
/** AlreadySettled: Maker's collateral share has already been settled */
|
|
140
|
-
export const OPTION_PROGRAM_ERROR__ALREADY_SETTLED =
|
|
144
|
+
export const OPTION_PROGRAM_ERROR__ALREADY_SETTLED = 0x17af; // 6063
|
|
141
145
|
/** InsufficientPoolCollateral: Insufficient collateral in pool for exercise */
|
|
142
|
-
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_COLLATERAL =
|
|
146
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_COLLATERAL = 0x17b0; // 6064
|
|
143
147
|
/** CollateralPoolNotFound: Collateral pool does not exist */
|
|
144
|
-
export const OPTION_PROGRAM_ERROR__COLLATERAL_POOL_NOT_FOUND =
|
|
148
|
+
export const OPTION_PROGRAM_ERROR__COLLATERAL_POOL_NOT_FOUND = 0x17b1; // 6065
|
|
145
149
|
/** NoCollateralToWithdraw: No collateral to withdraw */
|
|
146
|
-
export const OPTION_PROGRAM_ERROR__NO_COLLATERAL_TO_WITHDRAW =
|
|
150
|
+
export const OPTION_PROGRAM_ERROR__NO_COLLATERAL_TO_WITHDRAW = 0x17b2; // 6066
|
|
147
151
|
/** OptionNotExpired: Option has not expired yet - cannot settle */
|
|
148
|
-
export const OPTION_PROGRAM_ERROR__OPTION_NOT_EXPIRED =
|
|
152
|
+
export const OPTION_PROGRAM_ERROR__OPTION_NOT_EXPIRED = 0x17b3; // 6067
|
|
149
153
|
/** SupplyLimitExceeded: Deposit would exceed vault supply limit */
|
|
150
|
-
export const OPTION_PROGRAM_ERROR__SUPPLY_LIMIT_EXCEEDED =
|
|
154
|
+
export const OPTION_PROGRAM_ERROR__SUPPLY_LIMIT_EXCEEDED = 0x17b4; // 6068
|
|
151
155
|
/** InvalidFeeWallet: Invalid fee wallet - must match protocol constant */
|
|
152
|
-
export const OPTION_PROGRAM_ERROR__INVALID_FEE_WALLET =
|
|
156
|
+
export const OPTION_PROGRAM_ERROR__INVALID_FEE_WALLET = 0x17b5; // 6069
|
|
153
157
|
/** InvalidProtocolFee: Invalid protocol fee rate */
|
|
154
|
-
export const OPTION_PROGRAM_ERROR__INVALID_PROTOCOL_FEE =
|
|
158
|
+
export const OPTION_PROGRAM_ERROR__INVALID_PROTOCOL_FEE = 0x17b6; // 6070
|
|
155
159
|
/** UnderlyingAssetMismatch: Underlying asset mismatch - market data or mint does not match option */
|
|
156
|
-
export const OPTION_PROGRAM_ERROR__UNDERLYING_ASSET_MISMATCH =
|
|
160
|
+
export const OPTION_PROGRAM_ERROR__UNDERLYING_ASSET_MISMATCH = 0x17b7; // 6071
|
|
157
161
|
/** InvalidMint: Invalid token mint - does not match expected underlying asset */
|
|
158
|
-
export const OPTION_PROGRAM_ERROR__INVALID_MINT =
|
|
162
|
+
export const OPTION_PROGRAM_ERROR__INVALID_MINT = 0x17b8; // 6072
|
|
159
163
|
/** BatchSizeExceeded: Batch size exceeds maximum allowed (10 positions) */
|
|
160
|
-
export const OPTION_PROGRAM_ERROR__BATCH_SIZE_EXCEEDED =
|
|
164
|
+
export const OPTION_PROGRAM_ERROR__BATCH_SIZE_EXCEEDED = 0x17b9; // 6073
|
|
161
165
|
/** NoPositionsProvided: No positions provided in batch */
|
|
162
|
-
export const OPTION_PROGRAM_ERROR__NO_POSITIONS_PROVIDED =
|
|
166
|
+
export const OPTION_PROGRAM_ERROR__NO_POSITIONS_PROVIDED = 0x17ba; // 6074
|
|
163
167
|
/** PositionOptionMismatch: Position account does not belong to this option */
|
|
164
|
-
export const OPTION_PROGRAM_ERROR__POSITION_OPTION_MISMATCH =
|
|
168
|
+
export const OPTION_PROGRAM_ERROR__POSITION_OPTION_MISMATCH = 0x17bb; // 6075
|
|
165
169
|
/** OptionPoolMismatch: Option account does not match the option pool's option account */
|
|
166
|
-
export const OPTION_PROGRAM_ERROR__OPTION_POOL_MISMATCH =
|
|
170
|
+
export const OPTION_PROGRAM_ERROR__OPTION_POOL_MISMATCH = 0x17bc; // 6076
|
|
167
171
|
/** InvalidSeed: Invalid seed - must be exactly 32 bytes */
|
|
168
|
-
export const OPTION_PROGRAM_ERROR__INVALID_SEED =
|
|
172
|
+
export const OPTION_PROGRAM_ERROR__INVALID_SEED = 0x17bd; // 6077
|
|
169
173
|
/** InvalidAuthority: Invalid authority - does not match escrow authority */
|
|
170
|
-
export const OPTION_PROGRAM_ERROR__INVALID_AUTHORITY =
|
|
174
|
+
export const OPTION_PROGRAM_ERROR__INVALID_AUTHORITY = 0x17be; // 6078
|
|
171
175
|
/** EscrowAccountRequired: Escrow accounts required when borrowed_amount > 0 */
|
|
172
|
-
export const OPTION_PROGRAM_ERROR__ESCROW_ACCOUNT_REQUIRED =
|
|
176
|
+
export const OPTION_PROGRAM_ERROR__ESCROW_ACCOUNT_REQUIRED = 0x17bf; // 6079
|
|
173
177
|
/** InvalidEscrowMaker: Escrow state maker does not match instruction maker */
|
|
174
|
-
export const OPTION_PROGRAM_ERROR__INVALID_ESCROW_MAKER =
|
|
178
|
+
export const OPTION_PROGRAM_ERROR__INVALID_ESCROW_MAKER = 0x17c0; // 6080
|
|
175
179
|
/** EscrowMintMismatch: Escrow collateral mint does not match collateral pool mint */
|
|
176
|
-
export const OPTION_PROGRAM_ERROR__ESCROW_MINT_MISMATCH =
|
|
180
|
+
export const OPTION_PROGRAM_ERROR__ESCROW_MINT_MISMATCH = 0x17c1; // 6081
|
|
177
181
|
/** InsufficientEscrowBalance: Insufficient balance in escrow token account */
|
|
178
|
-
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_ESCROW_BALANCE =
|
|
182
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_ESCROW_BALANCE = 0x17c2; // 6082
|
|
179
183
|
/** InvalidEscrowOwner: Escrow token account owner mismatch */
|
|
180
|
-
export const OPTION_PROGRAM_ERROR__INVALID_ESCROW_OWNER =
|
|
184
|
+
export const OPTION_PROGRAM_ERROR__INVALID_ESCROW_OWNER = 0x17c3; // 6083
|
|
181
185
|
/** InvalidEscrowMint: Escrow token account mint mismatch */
|
|
182
|
-
export const OPTION_PROGRAM_ERROR__INVALID_ESCROW_MINT =
|
|
186
|
+
export const OPTION_PROGRAM_ERROR__INVALID_ESCROW_MINT = 0x17c4; // 6084
|
|
183
187
|
/** AccountFrozen: Token account is frozen and cannot be burned */
|
|
184
|
-
export const OPTION_PROGRAM_ERROR__ACCOUNT_FROZEN =
|
|
188
|
+
export const OPTION_PROGRAM_ERROR__ACCOUNT_FROZEN = 0x17c5; // 6085
|
|
185
189
|
/** InvalidAccount: Invalid account - does not match expected account */
|
|
186
|
-
export const OPTION_PROGRAM_ERROR__INVALID_ACCOUNT =
|
|
190
|
+
export const OPTION_PROGRAM_ERROR__INVALID_ACCOUNT = 0x17c6; // 6086
|
|
187
191
|
/** UnwindRepayAccountsMissing: Unwind repayment accounts are required when active pool loans exist */
|
|
188
|
-
export const OPTION_PROGRAM_ERROR__UNWIND_REPAY_ACCOUNTS_MISSING =
|
|
192
|
+
export const OPTION_PROGRAM_ERROR__UNWIND_REPAY_ACCOUNTS_MISSING = 0x17c7; // 6087
|
|
189
193
|
/** UnwindRepayWalletSourceMissing: Writer repayment account is required for unwind shortfall fallback */
|
|
190
|
-
export const OPTION_PROGRAM_ERROR__UNWIND_REPAY_WALLET_SOURCE_MISSING =
|
|
194
|
+
export const OPTION_PROGRAM_ERROR__UNWIND_REPAY_WALLET_SOURCE_MISSING = 0x17c8; // 6088
|
|
191
195
|
/** UnwindRepayInsufficientTotalFunds: Insufficient total funds to fully repay unwind loans (principal + interest + protocol fees) */
|
|
192
|
-
export const OPTION_PROGRAM_ERROR__UNWIND_REPAY_INSUFFICIENT_TOTAL_FUNDS =
|
|
196
|
+
export const OPTION_PROGRAM_ERROR__UNWIND_REPAY_INSUFFICIENT_TOTAL_FUNDS = 0x17c9; // 6089
|
|
197
|
+
/** InsufficientCollateralVault: Collateral vault has insufficient funds for unwind collateral return */
|
|
198
|
+
export const OPTION_PROGRAM_ERROR__INSUFFICIENT_COLLATERAL_VAULT = 0x17ca; // 6090
|
|
193
199
|
|
|
194
200
|
export type OptionProgramError =
|
|
195
201
|
| typeof OPTION_PROGRAM_ERROR__ACCOUNT_FROZEN
|
|
@@ -208,13 +214,16 @@ export type OptionProgramError =
|
|
|
208
214
|
| typeof OPTION_PROGRAM_ERROR__HEALTH_CALCULATION_FAILED
|
|
209
215
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_BALANCE
|
|
210
216
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_COLLATERAL
|
|
217
|
+
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_COLLATERAL_VAULT
|
|
211
218
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_ESCROW_BALANCE
|
|
212
219
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_LOAN_COLLATERAL
|
|
220
|
+
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_AGGREGATE_LIQUIDITY
|
|
213
221
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_COLLATERAL
|
|
214
222
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_LIQUIDITY
|
|
215
223
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_QUANTITY
|
|
216
224
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_USER_BALANCE
|
|
217
225
|
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_VAULT_LIQUIDITY
|
|
226
|
+
| typeof OPTION_PROGRAM_ERROR__INSUFFICIENT_WRITER_POSITION_LIQUIDITY
|
|
218
227
|
| typeof OPTION_PROGRAM_ERROR__INVALID_ACCOUNT
|
|
219
228
|
| typeof OPTION_PROGRAM_ERROR__INVALID_AUTHORITY
|
|
220
229
|
| typeof OPTION_PROGRAM_ERROR__INVALID_BUYER_AUTHORITY
|
|
@@ -300,13 +309,16 @@ if (process.env.NODE_ENV !== "production") {
|
|
|
300
309
|
[OPTION_PROGRAM_ERROR__HEALTH_CALCULATION_FAILED]: `Health ratio calculation failed`,
|
|
301
310
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_BALANCE]: `Insufficient balance for premium payment`,
|
|
302
311
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_COLLATERAL]: `Insufficient collateral provided`,
|
|
312
|
+
[OPTION_PROGRAM_ERROR__INSUFFICIENT_COLLATERAL_VAULT]: `Collateral vault has insufficient funds for unwind collateral return`,
|
|
303
313
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_ESCROW_BALANCE]: `Insufficient balance in escrow token account`,
|
|
304
314
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_LOAN_COLLATERAL]: `Collateral insufficient for loan`,
|
|
315
|
+
[OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_AGGREGATE_LIQUIDITY]: `Insufficient aggregate pool liquidity for requested quantity`,
|
|
305
316
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_COLLATERAL]: `Insufficient collateral in pool for exercise`,
|
|
306
317
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_POOL_LIQUIDITY]: `Insufficient pool liquidity available`,
|
|
307
318
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_QUANTITY]: `Insufficient quantity available in ask position`,
|
|
308
319
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_USER_BALANCE]: `Insufficient user balance for withdrawal`,
|
|
309
320
|
[OPTION_PROGRAM_ERROR__INSUFFICIENT_VAULT_LIQUIDITY]: `Vault has insufficient liquidity`,
|
|
321
|
+
[OPTION_PROGRAM_ERROR__INSUFFICIENT_WRITER_POSITION_LIQUIDITY]: `Insufficient active writer liquidity to fill requested quantity`,
|
|
310
322
|
[OPTION_PROGRAM_ERROR__INVALID_ACCOUNT]: `Invalid account - does not match expected account`,
|
|
311
323
|
[OPTION_PROGRAM_ERROR__INVALID_AUTHORITY]: `Invalid authority - does not match escrow authority`,
|
|
312
324
|
[OPTION_PROGRAM_ERROR__INVALID_BUYER_AUTHORITY]: `Invalid buyer authority`,
|
|
@@ -55,6 +55,8 @@ export type LiquidateWriterPositionInstruction<
|
|
|
55
55
|
TAccountOptionAccount extends string | AccountMeta<string> = string,
|
|
56
56
|
TAccountCollateralPool extends string | AccountMeta<string> = string,
|
|
57
57
|
TAccountWriterPosition extends string | AccountMeta<string> = string,
|
|
58
|
+
TAccountLongMint extends string | AccountMeta<string> = string,
|
|
59
|
+
TAccountEscrowLongAccount extends string | AccountMeta<string> = string,
|
|
58
60
|
TAccountOmlpVault extends string | AccountMeta<string> = string,
|
|
59
61
|
TAccountUnderlyingMint extends string | AccountMeta<string> = string,
|
|
60
62
|
TAccountMarketData extends string | AccountMeta<string> = string,
|
|
@@ -84,6 +86,12 @@ export type LiquidateWriterPositionInstruction<
|
|
|
84
86
|
TAccountWriterPosition extends string
|
|
85
87
|
? WritableAccount<TAccountWriterPosition>
|
|
86
88
|
: TAccountWriterPosition,
|
|
89
|
+
TAccountLongMint extends string
|
|
90
|
+
? WritableAccount<TAccountLongMint>
|
|
91
|
+
: TAccountLongMint,
|
|
92
|
+
TAccountEscrowLongAccount extends string
|
|
93
|
+
? WritableAccount<TAccountEscrowLongAccount>
|
|
94
|
+
: TAccountEscrowLongAccount,
|
|
87
95
|
TAccountOmlpVault extends string
|
|
88
96
|
? WritableAccount<TAccountOmlpVault>
|
|
89
97
|
: TAccountOmlpVault,
|
|
@@ -156,6 +164,8 @@ export type LiquidateWriterPositionAsyncInput<
|
|
|
156
164
|
TAccountOptionAccount extends string = string,
|
|
157
165
|
TAccountCollateralPool extends string = string,
|
|
158
166
|
TAccountWriterPosition extends string = string,
|
|
167
|
+
TAccountLongMint extends string = string,
|
|
168
|
+
TAccountEscrowLongAccount extends string = string,
|
|
159
169
|
TAccountOmlpVault extends string = string,
|
|
160
170
|
TAccountUnderlyingMint extends string = string,
|
|
161
171
|
TAccountMarketData extends string = string,
|
|
@@ -175,6 +185,10 @@ export type LiquidateWriterPositionAsyncInput<
|
|
|
175
185
|
collateralPool?: Address<TAccountCollateralPool>;
|
|
176
186
|
/** Writer's position to liquidate */
|
|
177
187
|
writerPosition: Address<TAccountWriterPosition>;
|
|
188
|
+
/** LONG token mint (for burning unsold tokens) */
|
|
189
|
+
longMint: Address<TAccountLongMint>;
|
|
190
|
+
/** Pool's LONG escrow account (holds unsold LONG tokens to burn) */
|
|
191
|
+
escrowLongAccount: Address<TAccountEscrowLongAccount>;
|
|
178
192
|
/** OMLP Vault (for repayment tracking) */
|
|
179
193
|
omlpVault: Address<TAccountOmlpVault>;
|
|
180
194
|
/** Underlying token mint (for decimal handling) */
|
|
@@ -200,6 +214,8 @@ export async function getLiquidateWriterPositionInstructionAsync<
|
|
|
200
214
|
TAccountOptionAccount extends string,
|
|
201
215
|
TAccountCollateralPool extends string,
|
|
202
216
|
TAccountWriterPosition extends string,
|
|
217
|
+
TAccountLongMint extends string,
|
|
218
|
+
TAccountEscrowLongAccount extends string,
|
|
203
219
|
TAccountOmlpVault extends string,
|
|
204
220
|
TAccountUnderlyingMint extends string,
|
|
205
221
|
TAccountMarketData extends string,
|
|
@@ -217,6 +233,8 @@ export async function getLiquidateWriterPositionInstructionAsync<
|
|
|
217
233
|
TAccountOptionAccount,
|
|
218
234
|
TAccountCollateralPool,
|
|
219
235
|
TAccountWriterPosition,
|
|
236
|
+
TAccountLongMint,
|
|
237
|
+
TAccountEscrowLongAccount,
|
|
220
238
|
TAccountOmlpVault,
|
|
221
239
|
TAccountUnderlyingMint,
|
|
222
240
|
TAccountMarketData,
|
|
@@ -236,6 +254,8 @@ export async function getLiquidateWriterPositionInstructionAsync<
|
|
|
236
254
|
TAccountOptionAccount,
|
|
237
255
|
TAccountCollateralPool,
|
|
238
256
|
TAccountWriterPosition,
|
|
257
|
+
TAccountLongMint,
|
|
258
|
+
TAccountEscrowLongAccount,
|
|
239
259
|
TAccountOmlpVault,
|
|
240
260
|
TAccountUnderlyingMint,
|
|
241
261
|
TAccountMarketData,
|
|
@@ -258,6 +278,11 @@ export async function getLiquidateWriterPositionInstructionAsync<
|
|
|
258
278
|
optionAccount: { value: input.optionAccount ?? null, isWritable: true },
|
|
259
279
|
collateralPool: { value: input.collateralPool ?? null, isWritable: true },
|
|
260
280
|
writerPosition: { value: input.writerPosition ?? null, isWritable: true },
|
|
281
|
+
longMint: { value: input.longMint ?? null, isWritable: true },
|
|
282
|
+
escrowLongAccount: {
|
|
283
|
+
value: input.escrowLongAccount ?? null,
|
|
284
|
+
isWritable: true,
|
|
285
|
+
},
|
|
261
286
|
omlpVault: { value: input.omlpVault ?? null, isWritable: true },
|
|
262
287
|
underlyingMint: { value: input.underlyingMint ?? null, isWritable: false },
|
|
263
288
|
marketData: { value: input.marketData ?? null, isWritable: false },
|
|
@@ -308,6 +333,8 @@ export async function getLiquidateWriterPositionInstructionAsync<
|
|
|
308
333
|
getAccountMeta(accounts.optionAccount),
|
|
309
334
|
getAccountMeta(accounts.collateralPool),
|
|
310
335
|
getAccountMeta(accounts.writerPosition),
|
|
336
|
+
getAccountMeta(accounts.longMint),
|
|
337
|
+
getAccountMeta(accounts.escrowLongAccount),
|
|
311
338
|
getAccountMeta(accounts.omlpVault),
|
|
312
339
|
getAccountMeta(accounts.underlyingMint),
|
|
313
340
|
getAccountMeta(accounts.marketData),
|
|
@@ -327,6 +354,8 @@ export async function getLiquidateWriterPositionInstructionAsync<
|
|
|
327
354
|
TAccountOptionAccount,
|
|
328
355
|
TAccountCollateralPool,
|
|
329
356
|
TAccountWriterPosition,
|
|
357
|
+
TAccountLongMint,
|
|
358
|
+
TAccountEscrowLongAccount,
|
|
330
359
|
TAccountOmlpVault,
|
|
331
360
|
TAccountUnderlyingMint,
|
|
332
361
|
TAccountMarketData,
|
|
@@ -345,6 +374,8 @@ export type LiquidateWriterPositionInput<
|
|
|
345
374
|
TAccountOptionAccount extends string = string,
|
|
346
375
|
TAccountCollateralPool extends string = string,
|
|
347
376
|
TAccountWriterPosition extends string = string,
|
|
377
|
+
TAccountLongMint extends string = string,
|
|
378
|
+
TAccountEscrowLongAccount extends string = string,
|
|
348
379
|
TAccountOmlpVault extends string = string,
|
|
349
380
|
TAccountUnderlyingMint extends string = string,
|
|
350
381
|
TAccountMarketData extends string = string,
|
|
@@ -364,6 +395,10 @@ export type LiquidateWriterPositionInput<
|
|
|
364
395
|
collateralPool: Address<TAccountCollateralPool>;
|
|
365
396
|
/** Writer's position to liquidate */
|
|
366
397
|
writerPosition: Address<TAccountWriterPosition>;
|
|
398
|
+
/** LONG token mint (for burning unsold tokens) */
|
|
399
|
+
longMint: Address<TAccountLongMint>;
|
|
400
|
+
/** Pool's LONG escrow account (holds unsold LONG tokens to burn) */
|
|
401
|
+
escrowLongAccount: Address<TAccountEscrowLongAccount>;
|
|
367
402
|
/** OMLP Vault (for repayment tracking) */
|
|
368
403
|
omlpVault: Address<TAccountOmlpVault>;
|
|
369
404
|
/** Underlying token mint (for decimal handling) */
|
|
@@ -389,6 +424,8 @@ export function getLiquidateWriterPositionInstruction<
|
|
|
389
424
|
TAccountOptionAccount extends string,
|
|
390
425
|
TAccountCollateralPool extends string,
|
|
391
426
|
TAccountWriterPosition extends string,
|
|
427
|
+
TAccountLongMint extends string,
|
|
428
|
+
TAccountEscrowLongAccount extends string,
|
|
392
429
|
TAccountOmlpVault extends string,
|
|
393
430
|
TAccountUnderlyingMint extends string,
|
|
394
431
|
TAccountMarketData extends string,
|
|
@@ -406,6 +443,8 @@ export function getLiquidateWriterPositionInstruction<
|
|
|
406
443
|
TAccountOptionAccount,
|
|
407
444
|
TAccountCollateralPool,
|
|
408
445
|
TAccountWriterPosition,
|
|
446
|
+
TAccountLongMint,
|
|
447
|
+
TAccountEscrowLongAccount,
|
|
409
448
|
TAccountOmlpVault,
|
|
410
449
|
TAccountUnderlyingMint,
|
|
411
450
|
TAccountMarketData,
|
|
@@ -424,6 +463,8 @@ export function getLiquidateWriterPositionInstruction<
|
|
|
424
463
|
TAccountOptionAccount,
|
|
425
464
|
TAccountCollateralPool,
|
|
426
465
|
TAccountWriterPosition,
|
|
466
|
+
TAccountLongMint,
|
|
467
|
+
TAccountEscrowLongAccount,
|
|
427
468
|
TAccountOmlpVault,
|
|
428
469
|
TAccountUnderlyingMint,
|
|
429
470
|
TAccountMarketData,
|
|
@@ -445,6 +486,11 @@ export function getLiquidateWriterPositionInstruction<
|
|
|
445
486
|
optionAccount: { value: input.optionAccount ?? null, isWritable: true },
|
|
446
487
|
collateralPool: { value: input.collateralPool ?? null, isWritable: true },
|
|
447
488
|
writerPosition: { value: input.writerPosition ?? null, isWritable: true },
|
|
489
|
+
longMint: { value: input.longMint ?? null, isWritable: true },
|
|
490
|
+
escrowLongAccount: {
|
|
491
|
+
value: input.escrowLongAccount ?? null,
|
|
492
|
+
isWritable: true,
|
|
493
|
+
},
|
|
448
494
|
omlpVault: { value: input.omlpVault ?? null, isWritable: true },
|
|
449
495
|
underlyingMint: { value: input.underlyingMint ?? null, isWritable: false },
|
|
450
496
|
marketData: { value: input.marketData ?? null, isWritable: false },
|
|
@@ -481,6 +527,8 @@ export function getLiquidateWriterPositionInstruction<
|
|
|
481
527
|
getAccountMeta(accounts.optionAccount),
|
|
482
528
|
getAccountMeta(accounts.collateralPool),
|
|
483
529
|
getAccountMeta(accounts.writerPosition),
|
|
530
|
+
getAccountMeta(accounts.longMint),
|
|
531
|
+
getAccountMeta(accounts.escrowLongAccount),
|
|
484
532
|
getAccountMeta(accounts.omlpVault),
|
|
485
533
|
getAccountMeta(accounts.underlyingMint),
|
|
486
534
|
getAccountMeta(accounts.marketData),
|
|
@@ -500,6 +548,8 @@ export function getLiquidateWriterPositionInstruction<
|
|
|
500
548
|
TAccountOptionAccount,
|
|
501
549
|
TAccountCollateralPool,
|
|
502
550
|
TAccountWriterPosition,
|
|
551
|
+
TAccountLongMint,
|
|
552
|
+
TAccountEscrowLongAccount,
|
|
503
553
|
TAccountOmlpVault,
|
|
504
554
|
TAccountUnderlyingMint,
|
|
505
555
|
TAccountMarketData,
|
|
@@ -527,24 +577,28 @@ export type ParsedLiquidateWriterPositionInstruction<
|
|
|
527
577
|
collateralPool: TAccountMetas[2];
|
|
528
578
|
/** Writer's position to liquidate */
|
|
529
579
|
writerPosition: TAccountMetas[3];
|
|
580
|
+
/** LONG token mint (for burning unsold tokens) */
|
|
581
|
+
longMint: TAccountMetas[4];
|
|
582
|
+
/** Pool's LONG escrow account (holds unsold LONG tokens to burn) */
|
|
583
|
+
escrowLongAccount: TAccountMetas[5];
|
|
530
584
|
/** OMLP Vault (for repayment tracking) */
|
|
531
|
-
omlpVault: TAccountMetas[
|
|
585
|
+
omlpVault: TAccountMetas[6];
|
|
532
586
|
/** Underlying token mint (for decimal handling) */
|
|
533
|
-
underlyingMint: TAccountMetas[
|
|
587
|
+
underlyingMint: TAccountMetas[7];
|
|
534
588
|
/** Market data account (provides pyth_feed_id for price check) */
|
|
535
|
-
marketData: TAccountMetas[
|
|
589
|
+
marketData: TAccountMetas[8];
|
|
536
590
|
/** Pyth price update account for current underlying price */
|
|
537
|
-
priceUpdate: TAccountMetas[
|
|
591
|
+
priceUpdate: TAccountMetas[9];
|
|
538
592
|
/** Collateral vault (source of repayments) */
|
|
539
|
-
collateralVault: TAccountMetas[
|
|
593
|
+
collateralVault: TAccountMetas[10];
|
|
540
594
|
/** OMLP vault token account (receives loan repayments) */
|
|
541
|
-
omlpVaultTokenAccount: TAccountMetas[
|
|
595
|
+
omlpVaultTokenAccount: TAccountMetas[11];
|
|
542
596
|
/** Protocol fee wallet (receives liquidation fees) */
|
|
543
|
-
feeWallet: TAccountMetas[
|
|
597
|
+
feeWallet: TAccountMetas[12];
|
|
544
598
|
/** Liquidator/keeper (permissionless - anyone can call) */
|
|
545
|
-
liquidator: TAccountMetas[
|
|
546
|
-
tokenProgram: TAccountMetas[
|
|
547
|
-
systemProgram: TAccountMetas[
|
|
599
|
+
liquidator: TAccountMetas[13];
|
|
600
|
+
tokenProgram: TAccountMetas[14];
|
|
601
|
+
systemProgram: TAccountMetas[15];
|
|
548
602
|
};
|
|
549
603
|
data: LiquidateWriterPositionInstructionData;
|
|
550
604
|
};
|
|
@@ -557,7 +611,7 @@ export function parseLiquidateWriterPositionInstruction<
|
|
|
557
611
|
InstructionWithAccounts<TAccountMetas> &
|
|
558
612
|
InstructionWithData<ReadonlyUint8Array>,
|
|
559
613
|
): ParsedLiquidateWriterPositionInstruction<TProgram, TAccountMetas> {
|
|
560
|
-
if (instruction.accounts.length <
|
|
614
|
+
if (instruction.accounts.length < 16) {
|
|
561
615
|
// TODO: Coded error.
|
|
562
616
|
throw new Error("Not enough accounts");
|
|
563
617
|
}
|
|
@@ -574,6 +628,8 @@ export function parseLiquidateWriterPositionInstruction<
|
|
|
574
628
|
optionAccount: getNextAccount(),
|
|
575
629
|
collateralPool: getNextAccount(),
|
|
576
630
|
writerPosition: getNextAccount(),
|
|
631
|
+
longMint: getNextAccount(),
|
|
632
|
+
escrowLongAccount: getNextAccount(),
|
|
577
633
|
omlpVault: getNextAccount(),
|
|
578
634
|
underlyingMint: getNextAccount(),
|
|
579
635
|
marketData: getNextAccount(),
|
|
@@ -88,7 +88,7 @@ import {
|
|
|
88
88
|
} from "../instructions";
|
|
89
89
|
|
|
90
90
|
export const OPTION_PROGRAM_PROGRAM_ADDRESS =
|
|
91
|
-
"
|
|
91
|
+
"BUszj34jkTxGik2AuVC4oQ3oKNSbkUaZsyNT3DSV8Qgm" as Address<"BUszj34jkTxGik2AuVC4oQ3oKNSbkUaZsyNT3DSV8Qgm">;
|
|
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 = "
|
|
682
|
+
TProgram extends string = "BUszj34jkTxGik2AuVC4oQ3oKNSbkUaZsyNT3DSV8Qgm",
|
|
683
683
|
> =
|
|
684
684
|
| ({
|
|
685
685
|
instructionType: OptionProgramInstruction.AcceptAdmin;
|
package/long/builders.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type { OptionType } from "../generated/types";
|
|
|
20
20
|
import { getCreateAssociatedTokenIdempotentInstructionWithAddress, NATIVE_MINT } from "../wsol/instructions";
|
|
21
21
|
import { fetchOptionPool } from "../accounts/fetchers";
|
|
22
22
|
import { getBuyFromPoolRemainingAccounts } from "./remaining-accounts";
|
|
23
|
+
import { fetchWriterPositionsForPool } from "../accounts/list";
|
|
23
24
|
|
|
24
25
|
export interface BuildBuyFromPoolParams {
|
|
25
26
|
optionPool: AddressLike;
|
|
@@ -266,6 +267,24 @@ export async function buildBuyFromPoolMarketOrderTransactionWithDerivation(
|
|
|
266
267
|
"Option pool must exist; ensure rpc is provided and pool is initialized."
|
|
267
268
|
);
|
|
268
269
|
|
|
270
|
+
// Build-time coverage assertion: verify active writer liquidity >= requested quantity
|
|
271
|
+
// This catches data staleness between preflight and build
|
|
272
|
+
const quantity = BigInt(params.quantity);
|
|
273
|
+
const writerPositions = await fetchWriterPositionsForPool(
|
|
274
|
+
params.rpc,
|
|
275
|
+
resolved.optionPool,
|
|
276
|
+
params.programId
|
|
277
|
+
);
|
|
278
|
+
const activeUnsoldTotal = writerPositions
|
|
279
|
+
.filter((p) => !p.data.isSettled && !p.data.isLiquidated && p.data.unsoldQty > 0n)
|
|
280
|
+
.reduce((sum, p) => sum + p.data.unsoldQty, 0n);
|
|
281
|
+
|
|
282
|
+
invariant(
|
|
283
|
+
activeUnsoldTotal >= quantity,
|
|
284
|
+
`Insufficient active writer liquidity: available=${activeUnsoldTotal}, requested=${quantity}. ` +
|
|
285
|
+
`This may indicate data staleness - please refresh and retry.`
|
|
286
|
+
);
|
|
287
|
+
|
|
269
288
|
const slippageBuffer = normalizeMarketOrderSlippageBuffer(
|
|
270
289
|
params,
|
|
271
290
|
refetchedPool.underlyingMint
|
package/long/preflight.ts
CHANGED
|
@@ -63,9 +63,13 @@ export async function preflightBuyFromPoolMarketOrder(
|
|
|
63
63
|
"Option pool must exist; ensure rpc is provided and pool is initialized."
|
|
64
64
|
);
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
// Filter out inactive positions (settled, liquidated, or zero unsold)
|
|
67
|
+
const activeWriterPositions = writerPositions.filter(
|
|
68
|
+
({ data }) => !data.isSettled && !data.isLiquidated && toBigInt(data.unsoldQty) > 0n
|
|
68
69
|
);
|
|
70
|
+
|
|
71
|
+
// Use active positions for coverage calculation
|
|
72
|
+
const availableWriterPositions = activeWriterPositions;
|
|
69
73
|
const remainingUnsoldAggregate = availableWriterPositions.reduce(
|
|
70
74
|
(acc, { data }) => acc + toBigInt(data.unsoldQty),
|
|
71
75
|
0n
|
|
@@ -19,7 +19,20 @@ export async function getBuyFromPoolRemainingAccounts(
|
|
|
19
19
|
optionPool,
|
|
20
20
|
programId ?? PROGRAM_ID
|
|
21
21
|
);
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
// Filter out inactive positions (settled, liquidated, or zero unsold)
|
|
24
|
+
const activePositions = positions.filter((p) => {
|
|
25
|
+
const isActive = !p.data.isSettled && !p.data.isLiquidated && p.data.unsoldQty > 0;
|
|
26
|
+
if (!isActive) {
|
|
27
|
+
console.log(
|
|
28
|
+
`Filtering out inactive writer position: ${p.address}, ` +
|
|
29
|
+
`isSettled=${p.data.isSettled}, isLiquidated=${p.data.isLiquidated}, unsoldQty=${p.data.unsoldQty}`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return isActive;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const sorted = [...activePositions].sort((a, b) => {
|
|
23
36
|
const aQty = a.data.unsoldQty;
|
|
24
37
|
const bQty = b.data.unsoldQty;
|
|
25
38
|
return aQty < bQty ? -1 : aQty > bQty ? 1 : 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@epicentral/sos-sdk",
|
|
3
|
-
"version": "0.4.0-alpha.
|
|
3
|
+
"version": "0.4.0-alpha.3",
|
|
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/preflight.ts
CHANGED
|
@@ -63,10 +63,21 @@ export interface UnwindPreflightSummary {
|
|
|
63
63
|
totalInterest: bigint;
|
|
64
64
|
totalProtocolFees: bigint;
|
|
65
65
|
totalOwed: bigint;
|
|
66
|
+
/** Proportional obligations for partial unwind (based on unwind ratio) */
|
|
67
|
+
proportionalPrincipal: bigint;
|
|
68
|
+
proportionalInterest: bigint;
|
|
69
|
+
proportionalProtocolFees: bigint;
|
|
70
|
+
proportionalTotalOwed: bigint;
|
|
71
|
+
/** Collateral return calculation */
|
|
72
|
+
proportionalCollateralShare: bigint;
|
|
73
|
+
returnableCollateral: bigint;
|
|
66
74
|
collateralVaultAvailable: bigint;
|
|
67
75
|
walletFallbackAvailable: bigint;
|
|
68
76
|
walletFallbackRequired: bigint;
|
|
69
77
|
shortfall: bigint;
|
|
78
|
+
/** For top-up UX: explicit shortfall fields */
|
|
79
|
+
collateralVaultShortfall: bigint;
|
|
80
|
+
needsWalletTopUp: boolean;
|
|
70
81
|
}
|
|
71
82
|
|
|
72
83
|
export interface UnwindPreflightResult {
|
|
@@ -139,10 +150,18 @@ export async function preflightUnwindWriterUnsold(
|
|
|
139
150
|
totalInterest: 0n,
|
|
140
151
|
totalProtocolFees: 0n,
|
|
141
152
|
totalOwed: 0n,
|
|
153
|
+
proportionalPrincipal: 0n,
|
|
154
|
+
proportionalInterest: 0n,
|
|
155
|
+
proportionalProtocolFees: 0n,
|
|
156
|
+
proportionalTotalOwed: 0n,
|
|
157
|
+
proportionalCollateralShare: 0n,
|
|
158
|
+
returnableCollateral: 0n,
|
|
142
159
|
collateralVaultAvailable: 0n,
|
|
143
160
|
walletFallbackAvailable: 0n,
|
|
144
161
|
walletFallbackRequired: 0n,
|
|
145
162
|
shortfall: 0n,
|
|
163
|
+
collateralVaultShortfall: 0n,
|
|
164
|
+
needsWalletTopUp: false,
|
|
146
165
|
},
|
|
147
166
|
};
|
|
148
167
|
}
|
|
@@ -161,10 +180,18 @@ export async function preflightUnwindWriterUnsold(
|
|
|
161
180
|
totalInterest: 0n,
|
|
162
181
|
totalProtocolFees: 0n,
|
|
163
182
|
totalOwed: 0n,
|
|
183
|
+
proportionalPrincipal: 0n,
|
|
184
|
+
proportionalInterest: 0n,
|
|
185
|
+
proportionalProtocolFees: 0n,
|
|
186
|
+
proportionalTotalOwed: 0n,
|
|
187
|
+
proportionalCollateralShare: 0n,
|
|
188
|
+
returnableCollateral: 0n,
|
|
164
189
|
collateralVaultAvailable: 0n,
|
|
165
190
|
walletFallbackAvailable: 0n,
|
|
166
191
|
walletFallbackRequired: 0n,
|
|
167
192
|
shortfall: 0n,
|
|
193
|
+
collateralVaultShortfall: 0n,
|
|
194
|
+
needsWalletTopUp: false,
|
|
168
195
|
},
|
|
169
196
|
};
|
|
170
197
|
}
|
|
@@ -220,10 +247,35 @@ export async function preflightUnwindWriterUnsold(
|
|
|
220
247
|
fetchTokenAmount(params.rpc, writerRepaymentAddress),
|
|
221
248
|
]);
|
|
222
249
|
|
|
250
|
+
// Calculate proportional obligations for partial unwinds
|
|
251
|
+
const writtenQty = toBigInt(writerPosition.writtenQty);
|
|
252
|
+
const unwindRatio = writtenQty > 0n ? (unwindQty * 1_000_000n) / writtenQty : 0n; // Basis points precision
|
|
253
|
+
const unwindRatioDecimal = Number(unwindRatio) / 1_000_000; // Convert to decimal
|
|
254
|
+
|
|
255
|
+
// Proportional obligations (for partial unwind logic)
|
|
256
|
+
const proportionalPrincipal = writtenQty > 0n ? (totals.principal * unwindQty) / writtenQty : 0n;
|
|
257
|
+
const proportionalInterest = writtenQty > 0n ? (totals.interest * unwindQty) / writtenQty : 0n;
|
|
258
|
+
const proportionalProtocolFees = writtenQty > 0n ? (totals.fees * unwindQty) / writtenQty : 0n;
|
|
259
|
+
const proportionalTotalOwed = proportionalPrincipal + proportionalInterest + proportionalProtocolFees;
|
|
260
|
+
|
|
261
|
+
// Collateral return calculation (proportional share minus proportional obligations)
|
|
262
|
+
const collateralDeposited = toBigInt(writerPosition.collateralDeposited);
|
|
263
|
+
const proportionalCollateralShare = writtenQty > 0n ? (collateralDeposited * unwindQty) / writtenQty : 0n;
|
|
264
|
+
const returnableCollateral = proportionalCollateralShare > proportionalTotalOwed
|
|
265
|
+
? proportionalCollateralShare - proportionalTotalOwed
|
|
266
|
+
: 0n;
|
|
267
|
+
|
|
268
|
+
// Calculate shortfall against proportional obligations
|
|
223
269
|
const walletFallbackRequired =
|
|
224
|
-
|
|
270
|
+
proportionalTotalOwed > collateralVaultAvailable ? proportionalTotalOwed - collateralVaultAvailable : 0n;
|
|
225
271
|
const totalAvailable = collateralVaultAvailable + walletFallbackAvailable;
|
|
226
|
-
const shortfall =
|
|
272
|
+
const shortfall = proportionalTotalOwed > totalAvailable ? proportionalTotalOwed - totalAvailable : 0n;
|
|
273
|
+
|
|
274
|
+
// For top-up UX: explicit collateral vault shortfall
|
|
275
|
+
const collateralVaultShortfall = returnableCollateral > collateralVaultAvailable
|
|
276
|
+
? returnableCollateral - collateralVaultAvailable
|
|
277
|
+
: 0n;
|
|
278
|
+
const needsWalletTopUp = collateralVaultShortfall > 0n && walletFallbackAvailable < collateralVaultShortfall;
|
|
227
279
|
|
|
228
280
|
return {
|
|
229
281
|
canUnwind: true,
|
|
@@ -239,10 +291,18 @@ export async function preflightUnwindWriterUnsold(
|
|
|
239
291
|
totalInterest: totals.interest,
|
|
240
292
|
totalProtocolFees: totals.fees,
|
|
241
293
|
totalOwed: totals.owed,
|
|
294
|
+
proportionalPrincipal,
|
|
295
|
+
proportionalInterest,
|
|
296
|
+
proportionalProtocolFees,
|
|
297
|
+
proportionalTotalOwed,
|
|
298
|
+
proportionalCollateralShare,
|
|
299
|
+
returnableCollateral,
|
|
242
300
|
collateralVaultAvailable,
|
|
243
301
|
walletFallbackAvailable,
|
|
244
302
|
walletFallbackRequired,
|
|
245
303
|
shortfall,
|
|
304
|
+
collateralVaultShortfall,
|
|
305
|
+
needsWalletTopUp,
|
|
246
306
|
},
|
|
247
307
|
};
|
|
248
308
|
}
|