@epicentral/sos-sdk 0.1.0 → 0.2.0

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/.env.example ADDED
@@ -0,0 +1 @@
1
+ NPM_TOKEN=EnterNpmTokenHere
package/README.md CHANGED
@@ -1,54 +1,50 @@
1
- # SDK (Frontend-First)
1
+ # @epicentral/sos-sdk
2
2
 
3
- This SDK is organized by feature and wraps the Codama-generated client for the `option_program`.
4
- It is Kit-native and uses `@solana/kit` types (`Address`, `Instruction`, `Rpc`) across the public API.
5
- The package entrypoint is `@epicentral/sos-sdk`.
3
+ Solana Option Standard SDK. A frontend-first SDK for native options trading on Solana, built by Epicentral Labs.
4
+
5
+ Uses `@solana/kit` types (`Address`, `Instruction`, `Rpc`) across the public API.
6
6
 
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- pnpm add @solana/kit decimal.js
10
+ pnpm add @epicentral/sos-sdk @solana/kit decimal.js
11
11
  ```
12
12
 
13
- In this repository, the SDK source lives at `epicentral/sos-sdk` and is packaged as
14
- `@epicentral/sos-sdk`.
15
-
16
- ## Structure
17
-
18
- - `client/` shared program constants and address helpers for the option program.
19
- - `accounts/` PDA derivation and account fetch helpers.
20
- - `long/` LONG buy/close/exercise/quote builders.
21
- - `short/` SHORT mint/unwind/sync/settle plus premium/pool/loan builders.
22
- - `omlp/` lender deposit/withdraw instruction builders.
23
- - `shared/` common amount, error, and remaining account helpers.
24
- - `generated/` Codama-generated client (bundled; do not edit).
13
+ ## Overview
25
14
 
26
- ## Updating the bundled client
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.
27
20
 
28
- The SDK bundles the program client in `generated/`. From the **option-program** repo root:
21
+ Each flow exposes `build*Instruction` for single instruction composition and `build*Transaction` for full-flow `Instruction[]` construction.
29
22
 
30
- 1. Regenerate the client: `yarn generate:client`.
31
- 2. Sync into the SDK and copy to the standalone repo: `yarn sync:sdk`.
23
+ ## Usage
32
24
 
33
- Or run the full pipeline: `yarn generate:client:with-sdk`. The script copies the client into `epicentral/sos-sdk/generated` and then copies the entire SDK to the standalone repo (default `../sos-sdk`) so you can commit from there. Override the standalone path with `STANDALONE_SDK_PATH=/path/to/sos-sdk yarn sync:sdk`.
25
+ ### Build + send (recommended)
34
26
 
35
- ## Usage model
36
-
37
- Each flow exposes:
38
-
39
- - `build*Instruction(params)` for single instruction composition.
40
- - `build*Transaction(params)` for one-flow `Instruction[]` construction.
41
- - optional domain services for multi-step flows (no send/confirm).
27
+ ```ts
28
+ import {
29
+ buildBuyFromPoolTransaction,
30
+ sendBuiltTransaction,
31
+ } from "@epicentral/sos-sdk";
42
32
 
43
- ## Core examples
33
+ const built = await buildBuyFromPoolTransaction(params);
34
+ const signature = await sendBuiltTransaction({
35
+ rpc,
36
+ rpcSubscriptions,
37
+ feePayer: walletSigner,
38
+ instructions: built.instructions,
39
+ });
40
+ ```
44
41
 
45
- ### Build + send (app-owned)
42
+ ### Build + send (manual)
46
43
 
47
44
  ```ts
48
45
  import {
49
46
  appendTransactionMessageInstructions,
50
47
  createTransactionMessage,
51
- getSignatureFromTransaction,
52
48
  pipe,
53
49
  sendAndConfirmTransactionFactory,
54
50
  setTransactionMessageFeePayerSigner,
@@ -71,64 +67,41 @@ const signedTx = await signTransactionMessageWithSigners(txMessage);
71
67
  await sendAndConfirmTransactionFactory({ rpc, rpcSubscriptions })(signedTx, {
72
68
  commitment: "confirmed",
73
69
  });
74
-
75
- const signature = getSignatureFromTransaction(signedTx);
76
70
  ```
77
71
 
78
- ### Build + send (SDK helper)
79
-
80
- ```ts
81
- import {
82
- buildBuyFromPoolTransaction,
83
- sendBuiltTransaction,
84
- } from "@epicentral/sos-sdk";
85
-
86
- const built = await buildBuyFromPoolTransaction(params);
87
- const signature = await sendBuiltTransaction({
88
- rpc,
89
- rpcSubscriptions,
90
- feePayer: walletSigner,
91
- instructions: built.instructions,
92
- });
93
- ```
94
-
95
- ### Open LONG / close LONG
72
+ ### LONG
96
73
 
97
74
  ```ts
98
75
  import {
99
76
  buildBuyFromPoolTransaction,
100
77
  buildCloseLongToPoolTransaction,
78
+ buildOptionExerciseTransaction,
101
79
  } from "@epicentral/sos-sdk";
102
80
 
81
+ // Open LONG
103
82
  const openLong = await buildBuyFromPoolTransaction(openLongParams);
104
- const closeLong = await buildCloseLongToPoolTransaction(closeLongParams);
105
- ```
106
-
107
- ### Exercise LONG
108
83
 
109
- ```ts
110
- import { buildOptionExerciseTransaction } from "@epicentral/sos-sdk";
84
+ // Close LONG
85
+ const closeLong = await buildCloseLongToPoolTransaction(closeLongParams);
111
86
 
112
- const exercise = buildOptionExerciseTransaction({
113
- optionAccount,
114
- positionAccount,
115
- marketData,
116
- underlyingMint,
117
- priceUpdate,
118
- buyerPaymentAccount,
119
- makerCollateralAccount,
120
- escrowState,
121
- escrowTokenAccount,
122
- escrowAuthority,
123
- buyer,
124
- });
87
+ // Exercise LONG
88
+ const exercise = buildOptionExerciseTransaction({ optionAccount, positionAccount, /* ... */ });
125
89
  ```
126
90
 
127
- ### Open SHORT (option mint)
91
+ ### SHORT
128
92
 
129
93
  ```ts
130
- import { buildOptionMintTransaction, OptionType } from "@epicentral/sos-sdk";
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";
131
103
 
104
+ // Mint (open SHORT)
132
105
  const built = await buildOptionMintTransaction({
133
106
  optionType: OptionType.Call,
134
107
  strikePrice,
@@ -142,53 +115,25 @@ const built = await buildOptionMintTransaction({
142
115
  makerCollateralAccount,
143
116
  underlyingMint,
144
117
  });
145
- ```
146
-
147
- ### Unwind / Sync / Settle SHORT
148
-
149
- ```ts
150
- import {
151
- buildSettleMakerCollateralTransaction,
152
- buildSyncWriterPositionTransaction,
153
- buildUnwindWriterUnsoldTransaction,
154
- } from "@epicentral/sos-sdk";
155
118
 
119
+ // Unwind / Sync / Settle
156
120
  const unwind = await buildUnwindWriterUnsoldTransaction(unwindParams);
157
121
  const sync = buildSyncWriterPositionTransaction(syncParams);
158
122
  const settle = await buildSettleMakerCollateralTransaction(settleParams);
159
- ```
160
-
161
- ### Claim premium / close option
162
-
163
- ```ts
164
- import {
165
- buildClaimPremiumTransaction,
166
- buildCloseOptionTransaction,
167
- } from "@epicentral/sos-sdk";
168
-
169
- const claim = await buildClaimPremiumTransaction({
170
- optionPool,
171
- makerPaymentAccount,
172
- premiumVault,
173
- maker,
174
- });
175
123
 
176
- const close = buildCloseOptionTransaction({
177
- optionAccount,
178
- optionMint,
179
- makerOptionAccount,
180
- maker,
181
- });
124
+ // Claim premium / close option
125
+ const claim = await buildClaimPremiumTransaction({ optionPool, makerPaymentAccount, premiumVault, maker });
126
+ const close = buildCloseOptionTransaction({ optionAccount, optionMint, makerOptionAccount, maker });
182
127
  ```
183
128
 
184
- ### Pool liquidity / borrow / repay
129
+ ### Pool
185
130
 
186
131
  ```ts
187
132
  import {
188
- buildBorrowFromPoolTransaction,
189
133
  buildDepositToPoolTransaction,
190
- buildRepayPoolLoanTransaction,
191
134
  buildWithdrawFromPoolTransaction,
135
+ buildBorrowFromPoolTransaction,
136
+ buildRepayPoolLoanTransaction,
192
137
  } from "@epicentral/sos-sdk";
193
138
 
194
139
  const deposit = await buildDepositToPoolTransaction(depositToPoolParams);
@@ -197,12 +142,7 @@ const borrow = await buildBorrowFromPoolTransaction(borrowFromPoolParams);
197
142
  const repay = await buildRepayPoolLoanTransaction(repayPoolLoanParams);
198
143
  ```
199
144
 
200
- Repayment source behavior:
201
-
202
- - `buildRepayPoolLoanTransaction`: principal is repaid from `escrowTokenAccount`; accrued interest + protocol fees are repaid from `makerTokenAccount`.
203
- - `buildRepayPoolLoanFromCollateralTransaction`: full repayment is sourced from `collateralVault`.
204
-
205
- ### OMLP deposit/withdraw
145
+ ### OMLP
206
146
 
207
147
  ```ts
208
148
  import {
@@ -210,16 +150,23 @@ import {
210
150
  buildWithdrawFromPositionTransaction,
211
151
  } from "@epicentral/sos-sdk";
212
152
 
213
- const deposit = await buildDepositToPositionTransaction(
214
- { vault, lenderTokenAccount, vaultTokenAccount, lender, amount }
215
- );
216
-
217
- const withdraw = await buildWithdrawFromPositionTransaction(
218
- { vault, vaultTokenAccount, lenderTokenAccount, lender, amount }
219
- );
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,
166
+ });
220
167
  ```
221
168
 
222
- ### Replace Anchor reads with SDK fetchers
169
+ ### Fetch accounts
223
170
 
224
171
  ```ts
225
172
  import { fetchOptionAccount, fetchOptionPool, fetchVault } from "@epicentral/sos-sdk";
@@ -228,12 +175,3 @@ const option = await fetchOptionAccount(rpc, optionAddress);
228
175
  const pool = await fetchOptionPool(rpc, optionPoolAddress);
229
176
  const vault = await fetchVault(rpc, vaultAddress);
230
177
  ```
231
-
232
- ## Migration notes
233
-
234
- - This SDK is the only frontend integration surface for this repository.
235
- - Legacy `frontend/constants`, `frontend/services`, and `frontend/utils` modules were removed.
236
- - Replace Anchor `program.account.*` reads with SDK fetchers.
237
- - Replace Anchor `program.methods.*` writes with SDK builders.
238
- - App code should own message building, signing, send, and confirmation via `@solana/kit`.
239
- - `long/service.ts` and `short/service.ts` were removed in favor of direct builder calls.
@@ -0,0 +1,104 @@
1
+ import type { Address } from "@solana/kit";
2
+ import { OptionType } from "../generated/types";
3
+ import { toAddress } from "../client/program";
4
+ import type { AddressLike, KitRpc } from "../client/types";
5
+ import {
6
+ deriveCollateralPoolPda,
7
+ deriveLongMintPda,
8
+ deriveMarketDataPda,
9
+ deriveMintAuthorityPda,
10
+ deriveOptionAccountPda,
11
+ deriveOptionPoolPda,
12
+ deriveShortMintPda,
13
+ } from "./pdas";
14
+ import { fetchCollateralPool, fetchOptionAccount, fetchOptionPool } from "./fetchers";
15
+
16
+ export interface ResolveOptionAccountsParams {
17
+ underlyingAsset: AddressLike;
18
+ optionType: OptionType;
19
+ strikePrice: number;
20
+ expirationDate: bigint | number;
21
+ programId?: AddressLike;
22
+ rpc?: KitRpc;
23
+ }
24
+
25
+ export interface ResolvedOptionAccounts {
26
+ optionAccount: Address;
27
+ longMint: Address;
28
+ shortMint: Address;
29
+ optionPool: Address;
30
+ marketData: Address;
31
+ collateralPool: Address;
32
+ mintAuthority: Address;
33
+ underlyingMint?: Address;
34
+ escrowLongAccount?: Address;
35
+ premiumVault?: Address;
36
+ collateralVault?: Address;
37
+ optionPoolData?: Awaited<ReturnType<typeof fetchOptionPool>>;
38
+ optionAccountData?: Awaited<ReturnType<typeof fetchOptionAccount>>;
39
+ collateralPoolData?: Awaited<ReturnType<typeof fetchCollateralPool>>;
40
+ }
41
+
42
+ /**
43
+ * Resolves all derived and optionally fetched accounts for an option.
44
+ * Given option identity (underlyingAsset, optionType, strikePrice, expirationDate),
45
+ * returns PDAs and, when rpc is provided, fetches OptionPool and CollateralPool
46
+ * to expose escrowLongAccount, premiumVault, collateralVault, underlyingMint.
47
+ */
48
+ export async function resolveOptionAccounts(
49
+ params: ResolveOptionAccountsParams
50
+ ): Promise<ResolvedOptionAccounts> {
51
+ const programId = params.programId;
52
+
53
+ const [optionAccount] = await deriveOptionAccountPda({
54
+ underlyingAsset: params.underlyingAsset,
55
+ optionType: params.optionType,
56
+ strikePrice: params.strikePrice,
57
+ expirationDate: params.expirationDate,
58
+ programId,
59
+ });
60
+
61
+ const [longMint] = await deriveLongMintPda(optionAccount, programId);
62
+ const [shortMint] = await deriveShortMintPda(optionAccount, programId);
63
+ const [optionPool] = await deriveOptionPoolPda(optionAccount, programId);
64
+ const [marketData] = await deriveMarketDataPda(params.underlyingAsset, programId);
65
+ const [collateralPool] = await deriveCollateralPoolPda(optionAccount, programId);
66
+ const [mintAuthority] = await deriveMintAuthorityPda(programId);
67
+
68
+ const result: ResolvedOptionAccounts = {
69
+ optionAccount,
70
+ longMint,
71
+ shortMint,
72
+ optionPool,
73
+ marketData,
74
+ collateralPool,
75
+ mintAuthority,
76
+ };
77
+
78
+ if (params.rpc) {
79
+ const [optionPoolFetched, optionAccountFetched, collateralPoolFetched] =
80
+ await Promise.all([
81
+ fetchOptionPool(params.rpc, optionPool),
82
+ fetchOptionAccount(params.rpc, optionAccount),
83
+ fetchCollateralPool(params.rpc, collateralPool),
84
+ ]);
85
+
86
+ if (optionPoolFetched) {
87
+ result.optionPoolData = optionPoolFetched;
88
+ result.escrowLongAccount = optionPoolFetched.escrowLongAccount;
89
+ result.premiumVault = optionPoolFetched.premiumVault;
90
+ result.underlyingMint = optionPoolFetched.underlyingMint;
91
+ }
92
+
93
+ if (optionAccountFetched) {
94
+ result.optionAccountData = optionAccountFetched;
95
+ }
96
+
97
+ if (collateralPoolFetched) {
98
+ result.collateralPoolData = collateralPoolFetched;
99
+ result.collateralVault = collateralPoolFetched.collateralVault;
100
+ }
101
+ }
102
+
103
+ return result;
104
+ }
package/index.ts CHANGED
@@ -6,6 +6,7 @@ export { OptionType } from "./generated/types";
6
6
  export * from "./accounts/pdas";
7
7
  export * from "./accounts/fetchers";
8
8
  export * from "./accounts/list";
9
+ export * from "./accounts/resolve-option";
9
10
 
10
11
  export * from "./shared/amounts";
11
12
  export * from "./shared/errors";
package/long/builders.ts CHANGED
@@ -4,13 +4,19 @@ import {
4
4
  } from "../generated/instructions";
5
5
  import type { Instruction } from "@solana/kit";
6
6
  import { toAddress } from "../client/program";
7
- import type { AddressLike, BuiltTransaction } from "../client/types";
7
+ import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
8
+ import { resolveOptionAccounts } from "../accounts/resolve-option";
9
+ import {
10
+ deriveAssociatedTokenAddress,
11
+ deriveBuyerPositionPda,
12
+ } from "../accounts/pdas";
8
13
  import { assertPositiveAmount } from "../shared/amounts";
9
14
  import { invariant } from "../shared/errors";
10
15
  import {
11
16
  appendRemainingAccounts,
12
17
  type RemainingAccountInput,
13
18
  } from "../shared/remaining-accounts";
19
+ import type { OptionType } from "../generated/types";
14
20
 
15
21
  export interface BuildBuyFromPoolParams {
16
22
  optionPool: AddressLike;
@@ -86,6 +92,74 @@ export async function buildBuyFromPoolTransaction(
86
92
  return { instructions: [instruction] };
87
93
  }
88
94
 
95
+ export interface BuildBuyFromPoolTransactionWithDerivationParams {
96
+ underlyingAsset: AddressLike;
97
+ optionType: OptionType;
98
+ strikePrice: number;
99
+ expirationDate: bigint | number;
100
+ buyer: AddressLike;
101
+ buyerPaymentAccount: AddressLike;
102
+ priceUpdate: AddressLike;
103
+ quantity: bigint | number;
104
+ premiumAmount: bigint | number;
105
+ rpc: KitRpc;
106
+ programId?: AddressLike;
107
+ buyerPosition?: AddressLike;
108
+ buyerOptionAccount?: AddressLike;
109
+ remainingAccounts?: RemainingAccountInput[];
110
+ }
111
+
112
+ export async function buildBuyFromPoolTransactionWithDerivation(
113
+ params: BuildBuyFromPoolTransactionWithDerivationParams
114
+ ): Promise<BuiltTransaction> {
115
+ const resolved = await resolveOptionAccounts({
116
+ underlyingAsset: params.underlyingAsset,
117
+ optionType: params.optionType,
118
+ strikePrice: params.strikePrice,
119
+ expirationDate: params.expirationDate,
120
+ programId: params.programId,
121
+ rpc: params.rpc,
122
+ });
123
+
124
+ invariant(
125
+ !!resolved.escrowLongAccount &&
126
+ !!resolved.premiumVault &&
127
+ !!resolved.underlyingMint,
128
+ "Option pool must exist; ensure rpc is provided and pool is initialized."
129
+ );
130
+
131
+ const [buyerPosition, buyerOptionAccount] = await Promise.all([
132
+ params.buyerPosition
133
+ ? Promise.resolve(params.buyerPosition)
134
+ : deriveBuyerPositionPda(
135
+ params.buyer,
136
+ resolved.optionAccount,
137
+ params.programId
138
+ ).then(([addr]) => addr),
139
+ params.buyerOptionAccount
140
+ ? Promise.resolve(params.buyerOptionAccount)
141
+ : deriveAssociatedTokenAddress(params.buyer, resolved.longMint),
142
+ ]);
143
+
144
+ return buildBuyFromPoolTransaction({
145
+ optionPool: resolved.optionPool,
146
+ optionAccount: resolved.optionAccount,
147
+ longMint: resolved.longMint,
148
+ underlyingMint: resolved.underlyingMint!,
149
+ marketData: resolved.marketData,
150
+ priceUpdate: params.priceUpdate,
151
+ buyer: params.buyer,
152
+ buyerPaymentAccount: params.buyerPaymentAccount,
153
+ escrowLongAccount: resolved.escrowLongAccount!,
154
+ premiumVault: resolved.premiumVault!,
155
+ quantity: params.quantity,
156
+ premiumAmount: params.premiumAmount,
157
+ buyerPosition,
158
+ buyerOptionAccount,
159
+ remainingAccounts: params.remainingAccounts,
160
+ });
161
+ }
162
+
89
163
  export async function buildCloseLongToPoolInstruction(
90
164
  params: BuildCloseLongToPoolParams
91
165
  ): Promise<Instruction<string>> {
@@ -124,3 +198,71 @@ export async function buildCloseLongToPoolTransaction(
124
198
  const instruction = await buildCloseLongToPoolInstruction(params);
125
199
  return { instructions: [instruction] };
126
200
  }
201
+
202
+ export interface BuildCloseLongToPoolTransactionWithDerivationParams {
203
+ underlyingAsset: AddressLike;
204
+ optionType: OptionType;
205
+ strikePrice: number;
206
+ expirationDate: bigint | number;
207
+ buyer: AddressLike;
208
+ buyerLongAccount: AddressLike;
209
+ buyerPayoutAccount: AddressLike;
210
+ priceUpdate: AddressLike;
211
+ quantity: bigint | number;
212
+ minPayoutAmount: bigint | number;
213
+ rpc: KitRpc;
214
+ programId?: AddressLike;
215
+ buyerPosition?: AddressLike;
216
+ omlpVault?: AddressLike;
217
+ remainingAccounts?: RemainingAccountInput[];
218
+ }
219
+
220
+ export async function buildCloseLongToPoolTransactionWithDerivation(
221
+ params: BuildCloseLongToPoolTransactionWithDerivationParams
222
+ ): Promise<BuiltTransaction> {
223
+ const resolved = await resolveOptionAccounts({
224
+ underlyingAsset: params.underlyingAsset,
225
+ optionType: params.optionType,
226
+ strikePrice: params.strikePrice,
227
+ expirationDate: params.expirationDate,
228
+ programId: params.programId,
229
+ rpc: params.rpc,
230
+ });
231
+
232
+ invariant(
233
+ !!resolved.escrowLongAccount &&
234
+ !!resolved.premiumVault &&
235
+ !!resolved.collateralVault &&
236
+ !!resolved.underlyingMint,
237
+ "Option pool and collateral pool must exist; ensure rpc is provided and pools are initialized."
238
+ );
239
+
240
+ const buyerPosition = params.buyerPosition
241
+ ? params.buyerPosition
242
+ : (await deriveBuyerPositionPda(
243
+ params.buyer,
244
+ resolved.optionAccount,
245
+ params.programId
246
+ ))[0];
247
+
248
+ return buildCloseLongToPoolTransaction({
249
+ optionPool: resolved.optionPool,
250
+ optionAccount: resolved.optionAccount,
251
+ collateralPool: resolved.collateralPool,
252
+ underlyingMint: resolved.underlyingMint!,
253
+ longMint: resolved.longMint,
254
+ escrowLongAccount: resolved.escrowLongAccount!,
255
+ premiumVault: resolved.premiumVault!,
256
+ marketData: resolved.marketData,
257
+ priceUpdate: params.priceUpdate,
258
+ buyer: params.buyer,
259
+ buyerLongAccount: params.buyerLongAccount,
260
+ buyerPayoutAccount: params.buyerPayoutAccount,
261
+ collateralVault: resolved.collateralVault!,
262
+ quantity: params.quantity,
263
+ minPayoutAmount: params.minPayoutAmount,
264
+ buyerPosition,
265
+ omlpVault: params.omlpVault,
266
+ remainingAccounts: params.remainingAccounts,
267
+ });
268
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicentral/sos-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
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",
@@ -11,12 +11,17 @@
11
11
  ".": "./index.ts",
12
12
  "./*": "./*"
13
13
  },
14
- "scripts": {
15
- "typecheck": "tsc --project tsconfig.json --noEmit"
14
+ "devDependencies": {
15
+ "dotenv-cli": "^8.0.0"
16
16
  },
17
17
  "dependencies": {
18
+ "@solana-program/address-lookup-table": "^0.11.0",
19
+ "@solana-program/compute-budget": "^0.13.0",
18
20
  "@solana/kit": "^6.1.0",
19
21
  "bs58": "^6.0.0",
20
22
  "decimal.js": "^10.4.3"
23
+ },
24
+ "scripts": {
25
+ "typecheck": "tsc --project tsconfig.json --noEmit"
21
26
  }
22
- }
27
+ }
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  appendTransactionMessageInstructions,
3
+ compressTransactionMessageUsingAddressLookupTables,
3
4
  createTransactionMessage,
4
5
  getSignatureFromTransaction,
5
6
  pipe,
@@ -7,22 +8,34 @@ import {
7
8
  setTransactionMessageFeePayerSigner,
8
9
  setTransactionMessageLifetimeUsingBlockhash,
9
10
  signTransactionMessageWithSigners,
11
+ type AddressesByLookupTableAddress,
10
12
  type RpcSubscriptions,
11
13
  type SolanaRpcSubscriptionsApi,
12
14
  type TransactionSigner,
13
15
  } from "@solana/kit";
14
- import type { BuiltTransaction, KitRpc } from "../client/types";
16
+ import { fetchAddressLookupTable } from "@solana-program/address-lookup-table";
17
+ import {
18
+ getSetComputeUnitLimitInstruction,
19
+ getSetComputeUnitPriceInstruction,
20
+ } from "@solana-program/compute-budget";
21
+ import type { Instruction } from "@solana/kit";
22
+ import { toAddress } from "../client/program";
23
+ import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
15
24
 
16
25
  export interface SendBuiltTransactionParams extends BuiltTransaction {
17
26
  rpc: KitRpc;
18
27
  rpcSubscriptions: RpcSubscriptions<SolanaRpcSubscriptionsApi>;
19
28
  feePayer: TransactionSigner<string>;
20
29
  commitment?: "processed" | "confirmed" | "finalized";
30
+ computeUnitLimit?: number;
31
+ computeUnitPriceMicroLamports?: number;
32
+ addressLookupTableAddresses?: AddressLike[];
21
33
  }
22
34
 
23
35
  /**
24
36
  * Sends a built SDK transaction with common Solana Kit defaults.
25
37
  * The caller still controls the RPC clients and fee-payer signer.
38
+ * Supports optional compute budget (limit + priority fee) and ALT compression.
26
39
  */
27
40
  export async function sendBuiltTransaction(
28
41
  params: SendBuiltTransactionParams
@@ -30,13 +43,44 @@ export async function sendBuiltTransaction(
30
43
  const commitment = params.commitment ?? "confirmed";
31
44
  const { value: latestBlockhash } = await params.rpc.getLatestBlockhash().send();
32
45
 
33
- const txMessage = pipe(
46
+ const computeBudgetInstructions: Instruction<string>[] = [];
47
+ if (params.computeUnitLimit !== undefined) {
48
+ computeBudgetInstructions.push(
49
+ getSetComputeUnitLimitInstruction({ units: params.computeUnitLimit })
50
+ );
51
+ }
52
+ if (params.computeUnitPriceMicroLamports !== undefined) {
53
+ computeBudgetInstructions.push(
54
+ getSetComputeUnitPriceInstruction({
55
+ microLamports: params.computeUnitPriceMicroLamports,
56
+ })
57
+ );
58
+ }
59
+ const allInstructions = [...computeBudgetInstructions, ...params.instructions];
60
+
61
+ let txMessage = pipe(
34
62
  createTransactionMessage({ version: 0 }),
35
63
  (tx) => setTransactionMessageFeePayerSigner(params.feePayer, tx),
36
64
  (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx),
37
- (tx) => appendTransactionMessageInstructions(params.instructions, tx)
65
+ (tx) => appendTransactionMessageInstructions(allInstructions, tx)
38
66
  );
39
67
 
68
+ if (
69
+ params.addressLookupTableAddresses &&
70
+ params.addressLookupTableAddresses.length > 0
71
+ ) {
72
+ const addressesByAddressLookupTable: AddressesByLookupTableAddress = {};
73
+ for (const altAddress of params.addressLookupTableAddresses) {
74
+ const resolvedAddress = toAddress(altAddress);
75
+ const { data } = await fetchAddressLookupTable(params.rpc, resolvedAddress);
76
+ addressesByAddressLookupTable[resolvedAddress] = data.addresses;
77
+ }
78
+ txMessage = compressTransactionMessageUsingAddressLookupTables(
79
+ txMessage,
80
+ addressesByAddressLookupTable
81
+ );
82
+ }
83
+
40
84
  const signedTx = await signTransactionMessageWithSigners(txMessage);
41
85
  const sendAndConfirm = sendAndConfirmTransactionFactory({
42
86
  rpc: params.rpc,
package/short/builders.ts CHANGED
@@ -7,8 +7,14 @@ import {
7
7
  } from "../generated";
8
8
  import type { Instruction } from "@solana/kit";
9
9
  import { toAddress } from "../client/program";
10
- import type { AddressLike, BuiltTransaction } from "../client/types";
11
- import { deriveMakerCollateralSharePda, deriveMetadataPda } from "../accounts/pdas";
10
+ import type { AddressLike, BuiltTransaction, KitRpc } from "../client/types";
11
+ import { resolveOptionAccounts } from "../accounts/resolve-option";
12
+ import {
13
+ deriveAssociatedTokenAddress,
14
+ deriveMakerCollateralSharePda,
15
+ deriveMetadataPda,
16
+ deriveWriterPositionPda,
17
+ } from "../accounts/pdas";
12
18
  import { assertNonNegativeAmount, assertPositiveAmount } from "../shared/amounts";
13
19
  import { invariant } from "../shared/errors";
14
20
  import {
@@ -184,6 +190,99 @@ export async function buildOptionMintTransaction(
184
190
  return { instructions: [instruction] };
185
191
  }
186
192
 
193
+ export interface BuildOptionMintTransactionWithDerivationParams {
194
+ underlyingAsset: AddressLike;
195
+ optionType: OptionType;
196
+ strikePrice: number;
197
+ expirationDate: bigint | number;
198
+ quantity: bigint | number;
199
+ underlyingMint: AddressLike;
200
+ underlyingSymbol: string;
201
+ makerCollateralAmount: bigint | number;
202
+ borrowedAmount: bigint | number;
203
+ maker: AddressLike;
204
+ makerCollateralAccount: AddressLike;
205
+ rpc: KitRpc;
206
+ programId?: AddressLike;
207
+ vault?: AddressLike;
208
+ vaultTokenAccount?: AddressLike;
209
+ escrowState?: AddressLike;
210
+ escrowAuthority?: AddressLike;
211
+ escrowTokenAccount?: AddressLike;
212
+ poolLoan?: AddressLike;
213
+ liquidityRouter?: AddressLike;
214
+ remainingAccounts?: RemainingAccountInput[];
215
+ }
216
+
217
+ export async function buildOptionMintTransactionWithDerivation(
218
+ params: BuildOptionMintTransactionWithDerivationParams
219
+ ): Promise<BuiltTransaction> {
220
+ const borrowedAmount = BigInt(params.borrowedAmount);
221
+ if (borrowedAmount > 0n) {
222
+ invariant(!!params.vault, "vault is required when borrowedAmount > 0");
223
+ invariant(
224
+ !!params.vaultTokenAccount,
225
+ "vaultTokenAccount is required when borrowedAmount > 0"
226
+ );
227
+ invariant(!!params.escrowState, "escrowState is required when borrowedAmount > 0");
228
+ invariant(
229
+ !!params.escrowAuthority,
230
+ "escrowAuthority is required when borrowedAmount > 0"
231
+ );
232
+ invariant(
233
+ !!params.escrowTokenAccount,
234
+ "escrowTokenAccount is required when borrowedAmount > 0"
235
+ );
236
+ invariant(!!params.poolLoan, "poolLoan is required when borrowedAmount > 0");
237
+ }
238
+
239
+ const resolved = await resolveOptionAccounts({
240
+ underlyingAsset: params.underlyingAsset,
241
+ optionType: params.optionType,
242
+ strikePrice: params.strikePrice,
243
+ expirationDate: params.expirationDate,
244
+ programId: params.programId,
245
+ rpc: params.rpc,
246
+ });
247
+
248
+ invariant(
249
+ !!resolved.escrowLongAccount && !!resolved.premiumVault && !!resolved.collateralVault,
250
+ "Option pool and collateral pool must exist; ensure rpc is provided and pools are initialized."
251
+ );
252
+
253
+ const underlyingMint = resolved.underlyingMint ?? params.underlyingMint;
254
+ const [makerLongAccount, makerShortAccount] = await Promise.all([
255
+ deriveAssociatedTokenAddress(params.maker, resolved.longMint),
256
+ deriveAssociatedTokenAddress(params.maker, resolved.shortMint),
257
+ ]);
258
+
259
+ return buildOptionMintTransaction({
260
+ ...params,
261
+ underlyingAsset: params.underlyingAsset,
262
+ underlyingMint,
263
+ optionAccount: resolved.optionAccount,
264
+ longMint: resolved.longMint,
265
+ shortMint: resolved.shortMint,
266
+ mintAuthority: resolved.mintAuthority,
267
+ makerLongAccount,
268
+ makerShortAccount,
269
+ marketData: resolved.marketData,
270
+ optionPool: resolved.optionPool,
271
+ escrowLongAccount: resolved.escrowLongAccount,
272
+ premiumVault: resolved.premiumVault,
273
+ collateralPool: resolved.collateralPool,
274
+ collateralVault: resolved.collateralVault,
275
+ vault: params.vault,
276
+ vaultTokenAccount: params.vaultTokenAccount,
277
+ escrowState: params.escrowState,
278
+ escrowAuthority: params.escrowAuthority,
279
+ escrowTokenAccount: params.escrowTokenAccount,
280
+ poolLoan: params.poolLoan,
281
+ liquidityRouter: params.liquidityRouter,
282
+ remainingAccounts: params.remainingAccounts,
283
+ });
284
+ }
285
+
187
286
  export async function buildUnwindWriterUnsoldInstruction(
188
287
  params: BuildUnwindWriterUnsoldParams
189
288
  ): Promise<Instruction<string>> {
@@ -216,6 +315,63 @@ export async function buildUnwindWriterUnsoldTransaction(
216
315
  return { instructions: [instruction] };
217
316
  }
218
317
 
318
+ export interface BuildUnwindWriterUnsoldTransactionWithDerivationParams {
319
+ underlyingAsset: AddressLike;
320
+ optionType: OptionType;
321
+ strikePrice: number;
322
+ expirationDate: bigint | number;
323
+ writer: AddressLike;
324
+ unwindQty: bigint | number;
325
+ rpc: KitRpc;
326
+ programId?: AddressLike;
327
+ omlpVault?: AddressLike;
328
+ feeWallet?: AddressLike;
329
+ remainingAccounts?: RemainingAccountInput[];
330
+ }
331
+
332
+ export async function buildUnwindWriterUnsoldTransactionWithDerivation(
333
+ params: BuildUnwindWriterUnsoldTransactionWithDerivationParams
334
+ ): Promise<BuiltTransaction> {
335
+ const resolved = await resolveOptionAccounts({
336
+ underlyingAsset: params.underlyingAsset,
337
+ optionType: params.optionType,
338
+ strikePrice: params.strikePrice,
339
+ expirationDate: params.expirationDate,
340
+ programId: params.programId,
341
+ rpc: params.rpc,
342
+ });
343
+
344
+ invariant(
345
+ !!resolved.escrowLongAccount && !!resolved.collateralVault && !!resolved.underlyingMint,
346
+ "Option pool and collateral pool must exist; ensure rpc is provided and pools are initialized."
347
+ );
348
+
349
+ const [writerShortAccount, writerCollateralAccount, writerPosition] =
350
+ await Promise.all([
351
+ deriveAssociatedTokenAddress(params.writer, resolved.shortMint),
352
+ deriveAssociatedTokenAddress(params.writer, resolved.underlyingMint),
353
+ deriveWriterPositionPda(resolved.optionPool, params.writer, params.programId),
354
+ ]);
355
+
356
+ return buildUnwindWriterUnsoldTransaction({
357
+ optionPool: resolved.optionPool,
358
+ optionAccount: resolved.optionAccount,
359
+ longMint: resolved.longMint,
360
+ shortMint: resolved.shortMint,
361
+ escrowLongAccount: resolved.escrowLongAccount!,
362
+ writerShortAccount,
363
+ collateralVault: resolved.collateralVault!,
364
+ writerCollateralAccount,
365
+ writer: params.writer,
366
+ unwindQty: params.unwindQty,
367
+ collateralPool: resolved.collateralPool,
368
+ writerPosition: writerPosition[0],
369
+ omlpVault: params.omlpVault,
370
+ feeWallet: params.feeWallet,
371
+ remainingAccounts: params.remainingAccounts,
372
+ });
373
+ }
374
+
219
375
  export function buildSyncWriterPositionInstruction(
220
376
  params: BuildSyncWriterPositionParams
221
377
  ): Instruction<string> {
package/tsconfig.json CHANGED
@@ -5,7 +5,8 @@
5
5
  "moduleResolution": "node",
6
6
  "strict": true,
7
7
  "declaration": true,
8
- "outDir": "dist"
8
+ "outDir": "dist",
9
+ "skipLibCheck": true
9
10
  },
10
11
  "include": ["**/*.ts"],
11
12
  "exclude": ["dist", "node_modules"]