@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.
- package/accounts/fetchers.ts +38 -0
- package/accounts/list.ts +13 -2
- package/index.ts +1 -0
- package/long/exercise.ts +7 -2
- package/package.json +1 -1
- package/shared/balances.ts +57 -0
- package/short/claim-premium.ts +0 -37
package/accounts/fetchers.ts
CHANGED
|
@@ -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
|
|
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
|
|
42
|
-
*
|
|
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
|
@@ -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
|
+
}
|
package/short/claim-premium.ts
DELETED
|
@@ -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
|
-
}
|