@epicentral/sos-sdk 0.2.4 → 0.2.6

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.
@@ -129,3 +129,41 @@ export async function fetchManyAccounts(
129
129
  exists: infos[index] !== null,
130
130
  }));
131
131
  }
132
+
133
+ const optionDecoder = getOptionAccountDecoder();
134
+
135
+ /**
136
+ * Batch-fetches option accounts via getMultipleAccounts (one RPC round-trip).
137
+ * Returns a Map from address to OptionAccount or null if missing/invalid.
138
+ */
139
+ export async function fetchOptionAccounts(
140
+ rpc: KitRpc,
141
+ addresses: AddressLike[]
142
+ ): Promise<Map<Address, OptionAccount | null>> {
143
+ if (addresses.length === 0) return new Map();
144
+ const keys = addresses.map((value) => toAddress(value));
145
+ const response = await rpc.getMultipleAccounts(keys, { encoding: "base64" }).send();
146
+ const infos = response.value;
147
+ const result = new Map<Address, OptionAccount | null>();
148
+ for (let i = 0; i < keys.length; i++) {
149
+ const accountInfo = infos[i];
150
+ if (!accountInfo) {
151
+ result.set(keys[i], null);
152
+ continue;
153
+ }
154
+ const [b64] = accountInfo.data;
155
+ if (!b64) {
156
+ result.set(keys[i], null);
157
+ continue;
158
+ }
159
+ const binary = atob(b64);
160
+ const data = new Uint8Array(binary.length);
161
+ for (let j = 0; j < binary.length; j++) data[j] = binary.charCodeAt(j);
162
+ try {
163
+ result.set(keys[i], optionDecoder.decode(data));
164
+ } catch {
165
+ result.set(keys[i], null);
166
+ }
167
+ }
168
+ return result;
169
+ }
package/accounts/list.ts CHANGED
@@ -7,10 +7,15 @@ import {
7
7
  VAULT_DISCRIMINATOR,
8
8
  WRITER_POSITION_DISCRIMINATOR,
9
9
  getOptionPoolDecoder,
10
+ getOptionPoolSize,
10
11
  getPoolLoanDecoder,
12
+ getPoolLoanSize,
11
13
  getPositionAccountDecoder,
14
+ getPositionAccountSize,
12
15
  getVaultDecoder,
16
+ getVaultSize,
13
17
  getWriterPositionDecoder,
18
+ getWriterPositionSize,
14
19
  type OptionPool,
15
20
  type PoolLoan,
16
21
  type PositionAccount,
@@ -32,7 +37,7 @@ type ListedAccount<T> = {
32
37
  type ProgramAccountResponse = {
33
38
  pubkey: Address;
34
39
  account: {
35
- data: [string, string];
40
+ data: [string, string] | string;
36
41
  };
37
42
  };
38
43
 
@@ -80,7 +85,8 @@ async function fetchAndDecodeProgramAccounts<T>(
80
85
  : (response as { value: Array<ProgramAccountResponse> }).value;
81
86
 
82
87
  return rawAccounts.map(({ pubkey, account }) => {
83
- const [base64Data] = account.data;
88
+ const base64Data =
89
+ Array.isArray(account.data) ? account.data[0] : account.data;
84
90
  return {
85
91
  address: pubkey,
86
92
  data: decoder.decode(decodeBase64Data(base64Data)),
@@ -95,6 +101,7 @@ export async function fetchWriterPositionsByWriter(
95
101
  return fetchAndDecodeProgramAccounts(rpc, getWriterPositionDecoder(), [
96
102
  discriminatorFilter(WRITER_POSITION_DISCRIMINATOR),
97
103
  ownerFilter(writer),
104
+ { dataSize: BigInt(getWriterPositionSize()) },
98
105
  ]);
99
106
  }
100
107
 
@@ -105,6 +112,7 @@ export async function fetchPositionAccountsByBuyer(
105
112
  return fetchAndDecodeProgramAccounts(rpc, getPositionAccountDecoder(), [
106
113
  discriminatorFilter(POSITION_ACCOUNT_DISCRIMINATOR),
107
114
  ownerFilter(buyer),
115
+ { dataSize: BigInt(getPositionAccountSize()) },
108
116
  ]);
109
117
  }
110
118
 
@@ -115,6 +123,7 @@ export async function fetchPoolLoansByMaker(
115
123
  const decoded = await fetchAndDecodeProgramAccounts(rpc, getPoolLoanDecoder(), [
116
124
  discriminatorFilter(POOL_LOAN_DISCRIMINATOR),
117
125
  ownerFilter(maker),
126
+ { dataSize: BigInt(getPoolLoanSize()) },
118
127
  ]);
119
128
  return decoded.filter(
120
129
  (item: { address: Address; data: PoolLoan }) =>
@@ -127,6 +136,7 @@ export async function fetchAllOptionPools(
127
136
  ): Promise<Array<ListedAccount<OptionPool>>> {
128
137
  return fetchAndDecodeProgramAccounts(rpc, getOptionPoolDecoder(), [
129
138
  discriminatorFilter(OPTION_POOL_DISCRIMINATOR),
139
+ { dataSize: BigInt(getOptionPoolSize()) },
130
140
  ]);
131
141
  }
132
142
 
@@ -135,5 +145,6 @@ export async function fetchAllVaults(
135
145
  ): Promise<Array<ListedAccount<Vault>>> {
136
146
  return fetchAndDecodeProgramAccounts(rpc, getVaultDecoder(), [
137
147
  discriminatorFilter(VAULT_DISCRIMINATOR),
148
+ { dataSize: BigInt(getVaultSize()) },
138
149
  ]);
139
150
  }
package/index.ts CHANGED
@@ -9,6 +9,7 @@ export * from "./accounts/list";
9
9
  export * from "./accounts/resolve-option";
10
10
 
11
11
  export * from "./shared/amounts";
12
+ export * from "./shared/balances";
12
13
  export * from "./shared/errors";
13
14
  export * from "./shared/remaining-accounts";
14
15
  export * from "./shared/transactions";
package/long/exercise.ts CHANGED
@@ -18,6 +18,10 @@ export interface BuildOptionExerciseParams {
18
18
  tokenProgram?: AddressLike;
19
19
  }
20
20
 
21
+ /**
22
+ * Legacy escrow-based option exercise. Prefer pool flows: close_long_to_pool and auto_exercise_expired.
23
+ * @deprecated Use buildCloseLongToPoolTransaction for closing longs and rely on auto_exercise_expired for expiration.
24
+ */
21
25
  export function buildOptionExerciseInstruction(
22
26
  params: BuildOptionExerciseParams
23
27
  ): Instruction<string> {
@@ -38,8 +42,9 @@ export function buildOptionExerciseInstruction(
38
42
  }
39
43
 
40
44
  /**
41
- * Builds an option exercise transaction instruction set.
42
- * Use `priceUpdate` from a fresh Pyth receiver update for accurate exercise pricing.
45
+ * Builds an option exercise transaction (escrow/ask-based flow).
46
+ * Prefer pool-based flows: buildCloseLongToPoolTransaction for closing longs and auto_exercise_expired for expired ITM.
47
+ * @deprecated Use buildCloseLongToPoolTransaction and auto_exercise_expired; do not use for new flows.
43
48
  */
44
49
  export function buildOptionExerciseTransaction(
45
50
  params: BuildOptionExerciseParams
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicentral/sos-sdk",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "private": false,
5
5
  "description": "Solana Option Standard SDK. The frontend-first SDK for Native Options Trading on Solana. Created by Epicentral Labs.",
6
6
  "type": "module",
@@ -0,0 +1,57 @@
1
+ import type { Address } from "@solana/kit";
2
+ import { deriveAssociatedTokenAddress } from "../accounts/pdas";
3
+ import { toAddress } from "../client/program";
4
+ import type { AddressLike, KitRpc } from "../client/types";
5
+ import { NATIVE_MINT } from "../wsol/instructions";
6
+
7
+ /** SPL Token account data: amount field offset (u64 LE). */
8
+ const TOKEN_ACCOUNT_AMOUNT_OFFSET = 64;
9
+
10
+ function decodeTokenAccountAmount(data: Uint8Array): bigint {
11
+ if (data.length < TOKEN_ACCOUNT_AMOUNT_OFFSET + 8) return BigInt(0);
12
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
13
+ return view.getBigUint64(TOKEN_ACCOUNT_AMOUNT_OFFSET, true);
14
+ }
15
+
16
+ async function fetchTokenAccountBalance(rpc: KitRpc, ata: Address): Promise<bigint> {
17
+ const response = await rpc.getAccountInfo(ata, { encoding: "base64" }).send();
18
+ const accountInfo = response.value;
19
+ if (!accountInfo) return BigInt(0);
20
+ const [b64] = accountInfo.data;
21
+ if (!b64) return BigInt(0);
22
+ const binary = atob(b64);
23
+ const data = new Uint8Array(binary.length);
24
+ for (let i = 0; i < binary.length; i++) data[i] = binary.charCodeAt(i);
25
+ return decodeTokenAccountAmount(data);
26
+ }
27
+
28
+ /**
29
+ * Returns the SPL token balance for an owner and mint in base units (smallest units).
30
+ * Derives the associated token account; returns 0n if the ATA does not exist or has no data.
31
+ */
32
+ export async function getTokenBalance(
33
+ owner: AddressLike,
34
+ mint: AddressLike,
35
+ rpc: KitRpc
36
+ ): Promise<bigint> {
37
+ const ata = await deriveAssociatedTokenAddress(owner, mint);
38
+ return fetchTokenAccountBalance(rpc, ata);
39
+ }
40
+
41
+ /**
42
+ * Returns native SOL balance (lamports), wrapped SOL (WSOL) balance (base units), and total (native + wrapped).
43
+ * Use for SOL pools when the UI should show combined "total SOL".
44
+ */
45
+ export async function getCombinedSOLBalance(
46
+ owner: AddressLike,
47
+ rpc: KitRpc
48
+ ): Promise<{ native: bigint; wrapped: bigint; total: bigint }> {
49
+ const ownerAddress = toAddress(owner);
50
+ const [nativeResponse, wrappedBalance] = await Promise.all([
51
+ rpc.getBalance(ownerAddress).send(),
52
+ getTokenBalance(ownerAddress, NATIVE_MINT, rpc),
53
+ ]);
54
+ const native = nativeResponse.value;
55
+ const total = native + wrappedBalance;
56
+ return { native, wrapped: wrappedBalance, total };
57
+ }
@@ -1,37 +0,0 @@
1
- import { getClaimPremiumInstructionAsync } from "../generated/instructions";
2
- import type { Instruction } from "@solana/kit";
3
- import { toAddress } from "../client/program";
4
- import type { AddressLike, BuiltTransaction } from "../client/types";
5
-
6
- export interface BuildClaimPremiumParams {
7
- optionPool: AddressLike;
8
- makerPaymentAccount: AddressLike;
9
- premiumVault: AddressLike;
10
- maker: AddressLike;
11
- makerPoolShare?: AddressLike;
12
- tokenProgram?: AddressLike;
13
- }
14
-
15
- export async function buildClaimPremiumInstruction(
16
- params: BuildClaimPremiumParams
17
- ): Promise<Instruction<string>> {
18
- return getClaimPremiumInstructionAsync({
19
- optionPool: toAddress(params.optionPool),
20
- makerPoolShare: params.makerPoolShare ? toAddress(params.makerPoolShare) : undefined,
21
- makerPaymentAccount: toAddress(params.makerPaymentAccount),
22
- premiumVault: toAddress(params.premiumVault),
23
- maker: toAddress(params.maker) as any,
24
- tokenProgram: params.tokenProgram ? toAddress(params.tokenProgram) : undefined,
25
- });
26
- }
27
-
28
- /**
29
- * Builds a premium claim transaction for a maker's pool share.
30
- * `makerPoolShare` is optional and can be derived by the generated instruction helper.
31
- */
32
- export async function buildClaimPremiumTransaction(
33
- params: BuildClaimPremiumParams
34
- ): Promise<BuiltTransaction> {
35
- const instruction = await buildClaimPremiumInstruction(params);
36
- return { instructions: [instruction] };
37
- }