@epicentral/sos-sdk 0.9.0-beta
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 +1 -0
- package/AGENTS.md +7 -0
- package/LICENSE +21 -0
- package/README.md +568 -0
- package/accounts/fetchers.ts +196 -0
- package/accounts/list.ts +184 -0
- package/accounts/pdas.ts +325 -0
- package/accounts/resolve-option.ts +104 -0
- package/client/lookup-table.ts +114 -0
- package/client/program.ts +13 -0
- package/client/types.ts +9 -0
- package/generated/accounts/collateralPool.ts +217 -0
- package/generated/accounts/config.ts +156 -0
- package/generated/accounts/escrowState.ts +183 -0
- package/generated/accounts/index.ts +20 -0
- package/generated/accounts/lenderPosition.ts +211 -0
- package/generated/accounts/makerCollateralShare.ts +229 -0
- package/generated/accounts/marketDataAccount.ts +176 -0
- package/generated/accounts/optionAccount.ts +247 -0
- package/generated/accounts/optionPool.ts +285 -0
- package/generated/accounts/poolLoan.ts +232 -0
- package/generated/accounts/positionAccount.ts +201 -0
- package/generated/accounts/vault.ts +366 -0
- package/generated/accounts/writerPosition.ts +327 -0
- package/generated/errors/index.ts +9 -0
- package/generated/errors/optionProgram.ts +476 -0
- package/generated/index.ts +13 -0
- package/generated/instructions/acceptAdmin.ts +230 -0
- package/generated/instructions/autoExerciseAllExpired.ts +685 -0
- package/generated/instructions/autoExerciseExpired.ts +754 -0
- package/generated/instructions/borrowFromPool.ts +619 -0
- package/generated/instructions/buyFromPool.ts +761 -0
- package/generated/instructions/closeLongToPool.ts +762 -0
- package/generated/instructions/closeOption.ts +235 -0
- package/generated/instructions/createEscrowV2.ts +518 -0
- package/generated/instructions/depositCollateral.ts +624 -0
- package/generated/instructions/depositToPosition.ts +429 -0
- package/generated/instructions/index.ts +47 -0
- package/generated/instructions/initCollateralPool.ts +513 -0
- package/generated/instructions/initConfig.ts +279 -0
- package/generated/instructions/initOptionPool.ts +587 -0
- package/generated/instructions/initializeMarketData.ts +359 -0
- package/generated/instructions/liquidateWriterPosition.ts +750 -0
- package/generated/instructions/liquidateWriterPositionRescue.ts +623 -0
- package/generated/instructions/omlpCreateVault.ts +553 -0
- package/generated/instructions/omlpUpdateFeeWallet.ts +473 -0
- package/generated/instructions/omlpUpdateInterestModel.ts +322 -0
- package/generated/instructions/omlpUpdateLiquidationThreshold.ts +304 -0
- package/generated/instructions/omlpUpdateMaintenanceBuffer.ts +304 -0
- package/generated/instructions/omlpUpdateMaxBorrowCap.ts +304 -0
- package/generated/instructions/omlpUpdateMaxLeverage.ts +304 -0
- package/generated/instructions/omlpUpdateProtocolFee.ts +304 -0
- package/generated/instructions/omlpUpdateSupplyLimit.ts +304 -0
- package/generated/instructions/optionExercise.ts +617 -0
- package/generated/instructions/optionMint.ts +1373 -0
- package/generated/instructions/optionValidate.ts +302 -0
- package/generated/instructions/repayPoolLoan.ts +558 -0
- package/generated/instructions/repayPoolLoanFromCollateral.ts +514 -0
- package/generated/instructions/repayPoolLoanFromWallet.ts +542 -0
- package/generated/instructions/settleMakerCollateral.ts +509 -0
- package/generated/instructions/syncWriterPosition.ts +206 -0
- package/generated/instructions/transferAdmin.ts +245 -0
- package/generated/instructions/unwindWriterUnsold.ts +764 -0
- package/generated/instructions/updateImpliedVolatility.ts +226 -0
- package/generated/instructions/updateMarketData.ts +315 -0
- package/generated/instructions/withdrawFromPosition.ts +405 -0
- package/generated/instructions/writeToPool.ts +619 -0
- package/generated/programs/index.ts +9 -0
- package/generated/programs/optionProgram.ts +1144 -0
- package/generated/shared/index.ts +164 -0
- package/generated/types/impliedVolatilityUpdated.ts +73 -0
- package/generated/types/index.ts +28 -0
- package/generated/types/liquidationExecuted.ts +73 -0
- package/generated/types/liquidationRescueEvent.ts +82 -0
- package/generated/types/marketDataInitialized.ts +61 -0
- package/generated/types/marketDataUpdated.ts +69 -0
- package/generated/types/optionClosed.ts +56 -0
- package/generated/types/optionExercised.ts +62 -0
- package/generated/types/optionExpired.ts +49 -0
- package/generated/types/optionMinted.ts +78 -0
- package/generated/types/optionType.ts +38 -0
- package/generated/types/optionValidated.ts +82 -0
- package/generated/types/poolLoanCreated.ts +74 -0
- package/generated/types/poolLoanRepaid.ts +74 -0
- package/generated/types/positionDeposited.ts +73 -0
- package/generated/types/positionWithdrawn.ts +81 -0
- package/generated/types/protocolFeeUpdated.ts +69 -0
- package/generated/types/vaultCreated.ts +60 -0
- package/generated/types/vaultFeeWalletUpdated.ts +67 -0
- package/generated/types/vaultInterestModelUpdated.ts +77 -0
- package/generated/types/vaultLiquidationThresholdUpdated.ts +69 -0
- package/index.ts +68 -0
- package/long/builders.ts +690 -0
- package/long/exercise.ts +123 -0
- package/long/preflight.ts +214 -0
- package/long/quotes.ts +48 -0
- package/long/remaining-accounts.ts +111 -0
- package/omlp/builders.ts +94 -0
- package/omlp/service.ts +136 -0
- package/oracle/switchboard.ts +315 -0
- package/package.json +34 -0
- package/shared/amounts.ts +53 -0
- package/shared/balances.ts +57 -0
- package/shared/errors.ts +12 -0
- package/shared/remaining-accounts.ts +41 -0
- package/shared/trade-config.ts +27 -0
- package/shared/transactions.ts +121 -0
- package/short/builders.ts +874 -0
- package/short/close-option.ts +34 -0
- package/short/pool.ts +189 -0
- package/short/preflight.ts +619 -0
- package/tsconfig.json +13 -0
- package/wsol/instructions.ts +247 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getCollateralPoolDecoder,
|
|
3
|
+
getLenderPositionDecoder,
|
|
4
|
+
getMarketDataAccountDecoder,
|
|
5
|
+
getOptionAccountDecoder,
|
|
6
|
+
getOptionPoolDecoder,
|
|
7
|
+
getPoolLoanDecoder,
|
|
8
|
+
getPositionAccountDecoder,
|
|
9
|
+
getVaultDecoder,
|
|
10
|
+
getWriterPositionDecoder,
|
|
11
|
+
type CollateralPool,
|
|
12
|
+
type LenderPosition,
|
|
13
|
+
type MarketDataAccount,
|
|
14
|
+
type OptionAccount,
|
|
15
|
+
type OptionPool,
|
|
16
|
+
type PoolLoan,
|
|
17
|
+
type PositionAccount,
|
|
18
|
+
type Vault,
|
|
19
|
+
type WriterPosition,
|
|
20
|
+
} from "../generated/accounts";
|
|
21
|
+
import type { Address } from "@solana/kit";
|
|
22
|
+
import { toAddress } from "../client/program";
|
|
23
|
+
import type { AddressLike, KitRpc } from "../client/types";
|
|
24
|
+
|
|
25
|
+
async function fetchRawAccount(
|
|
26
|
+
rpc: KitRpc,
|
|
27
|
+
address: AddressLike
|
|
28
|
+
): Promise<Uint8Array | null> {
|
|
29
|
+
const response = await rpc.getAccountInfo(toAddress(address), { encoding: "base64" }).send();
|
|
30
|
+
const accountInfo = response.value;
|
|
31
|
+
if (!accountInfo) return null;
|
|
32
|
+
const [data] = accountInfo.data;
|
|
33
|
+
const binary = atob(data);
|
|
34
|
+
const bytes = new Uint8Array(binary.length);
|
|
35
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
36
|
+
return bytes;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function decodeAccount<T>(
|
|
40
|
+
rpc: KitRpc,
|
|
41
|
+
address: AddressLike,
|
|
42
|
+
decoder: { decode: (value: Uint8Array) => T }
|
|
43
|
+
): Promise<T | null> {
|
|
44
|
+
const data = await fetchRawAccount(rpc, address);
|
|
45
|
+
if (!data) return null;
|
|
46
|
+
try {
|
|
47
|
+
return decoder.decode(data);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
// #region agent log
|
|
50
|
+
try {
|
|
51
|
+
fetch("http://127.0.0.1:7586/ingest/4a07cb33-954d-4b27-b1a3-08f1423b9d05", {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json", "X-Debug-Session-Id": "af65cf" },
|
|
54
|
+
body: JSON.stringify({
|
|
55
|
+
sessionId: "af65cf",
|
|
56
|
+
location: "sos-sdk/accounts/fetchers.ts:decodeAccount",
|
|
57
|
+
message: "decode_throw",
|
|
58
|
+
data: {
|
|
59
|
+
address: String(address),
|
|
60
|
+
dataLen: data.length,
|
|
61
|
+
firstDiscHex: Array.from(data.slice(0, 8))
|
|
62
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
63
|
+
.join(""),
|
|
64
|
+
err: (e as Error)?.message ?? String(e),
|
|
65
|
+
},
|
|
66
|
+
timestamp: Date.now(),
|
|
67
|
+
hypothesisId: "H2-i64-account-size-mismatch",
|
|
68
|
+
}),
|
|
69
|
+
}).catch(() => {});
|
|
70
|
+
} catch {}
|
|
71
|
+
// #endregion
|
|
72
|
+
throw e;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function fetchOptionAccount(
|
|
77
|
+
rpc: KitRpc,
|
|
78
|
+
optionAccount: AddressLike
|
|
79
|
+
): Promise<OptionAccount | null> {
|
|
80
|
+
return decodeAccount(rpc, optionAccount, getOptionAccountDecoder());
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function fetchOptionPool(
|
|
84
|
+
rpc: KitRpc,
|
|
85
|
+
optionPool: AddressLike
|
|
86
|
+
): Promise<OptionPool | null> {
|
|
87
|
+
return decodeAccount(rpc, optionPool, getOptionPoolDecoder());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function fetchCollateralPool(
|
|
91
|
+
rpc: KitRpc,
|
|
92
|
+
collateralPool: AddressLike
|
|
93
|
+
): Promise<CollateralPool | null> {
|
|
94
|
+
return decodeAccount(rpc, collateralPool, getCollateralPoolDecoder());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export async function fetchWriterPosition(
|
|
98
|
+
rpc: KitRpc,
|
|
99
|
+
writerPosition: AddressLike
|
|
100
|
+
): Promise<WriterPosition | null> {
|
|
101
|
+
return decodeAccount(rpc, writerPosition, getWriterPositionDecoder());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function fetchLenderPosition(
|
|
105
|
+
rpc: KitRpc,
|
|
106
|
+
lenderPosition: AddressLike
|
|
107
|
+
): Promise<LenderPosition | null> {
|
|
108
|
+
return decodeAccount(rpc, lenderPosition, getLenderPositionDecoder());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function fetchBuyerPosition(
|
|
112
|
+
rpc: KitRpc,
|
|
113
|
+
buyerPosition: AddressLike
|
|
114
|
+
): Promise<PositionAccount | null> {
|
|
115
|
+
return decodeAccount(rpc, buyerPosition, getPositionAccountDecoder());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function fetchVault(
|
|
119
|
+
rpc: KitRpc,
|
|
120
|
+
vault: AddressLike
|
|
121
|
+
): Promise<Vault | null> {
|
|
122
|
+
return decodeAccount(rpc, vault, getVaultDecoder());
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function fetchMarketDataAccount(
|
|
126
|
+
rpc: KitRpc,
|
|
127
|
+
marketData: AddressLike
|
|
128
|
+
): Promise<MarketDataAccount | null> {
|
|
129
|
+
return decodeAccount(rpc, marketData, getMarketDataAccountDecoder());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function fetchPoolLoan(
|
|
133
|
+
rpc: KitRpc,
|
|
134
|
+
poolLoan: AddressLike
|
|
135
|
+
): Promise<PoolLoan | null> {
|
|
136
|
+
return decodeAccount(rpc, poolLoan, getPoolLoanDecoder());
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function accountExists(
|
|
140
|
+
rpc: KitRpc,
|
|
141
|
+
address: AddressLike
|
|
142
|
+
): Promise<boolean> {
|
|
143
|
+
const response = await rpc.getAccountInfo(toAddress(address), { encoding: "base64" }).send();
|
|
144
|
+
return response.value !== null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function fetchManyAccounts(
|
|
148
|
+
rpc: KitRpc,
|
|
149
|
+
addresses: AddressLike[]
|
|
150
|
+
): Promise<Array<{ address: Address; exists: boolean }>> {
|
|
151
|
+
const keys = addresses.map((value) => toAddress(value));
|
|
152
|
+
const response = await rpc.getMultipleAccounts(keys, { encoding: "base64" }).send();
|
|
153
|
+
const infos = response.value;
|
|
154
|
+
return keys.map((key, index) => ({
|
|
155
|
+
address: key,
|
|
156
|
+
exists: infos[index] !== null,
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const optionDecoder = getOptionAccountDecoder();
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Batch-fetches option accounts via getMultipleAccounts (one RPC round-trip).
|
|
164
|
+
* Returns a Map from address to OptionAccount or null if missing/invalid.
|
|
165
|
+
*/
|
|
166
|
+
export async function fetchOptionAccounts(
|
|
167
|
+
rpc: KitRpc,
|
|
168
|
+
addresses: AddressLike[]
|
|
169
|
+
): Promise<Map<Address, OptionAccount | null>> {
|
|
170
|
+
if (addresses.length === 0) return new Map();
|
|
171
|
+
const keys = addresses.map((value) => toAddress(value));
|
|
172
|
+
const response = await rpc.getMultipleAccounts(keys, { encoding: "base64" }).send();
|
|
173
|
+
const infos = response.value;
|
|
174
|
+
const result = new Map<Address, OptionAccount | null>();
|
|
175
|
+
for (let i = 0; i < keys.length; i++) {
|
|
176
|
+
const accountInfo = infos[i];
|
|
177
|
+
if (!accountInfo) {
|
|
178
|
+
result.set(keys[i], null);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
const [b64] = accountInfo.data;
|
|
182
|
+
if (!b64) {
|
|
183
|
+
result.set(keys[i], null);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const binary = atob(b64);
|
|
187
|
+
const data = new Uint8Array(binary.length);
|
|
188
|
+
for (let j = 0; j < binary.length; j++) data[j] = binary.charCodeAt(j);
|
|
189
|
+
try {
|
|
190
|
+
result.set(keys[i], optionDecoder.decode(data));
|
|
191
|
+
} catch {
|
|
192
|
+
result.set(keys[i], null);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
package/accounts/list.ts
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import bs58 from "bs58";
|
|
2
|
+
import type { Address } from "@solana/kit";
|
|
3
|
+
import {
|
|
4
|
+
OPTION_POOL_DISCRIMINATOR,
|
|
5
|
+
POOL_LOAN_DISCRIMINATOR,
|
|
6
|
+
POSITION_ACCOUNT_DISCRIMINATOR,
|
|
7
|
+
VAULT_DISCRIMINATOR,
|
|
8
|
+
WRITER_POSITION_DISCRIMINATOR,
|
|
9
|
+
getOptionPoolDecoder,
|
|
10
|
+
getOptionPoolSize,
|
|
11
|
+
getPoolLoanDecoder,
|
|
12
|
+
getPoolLoanSize,
|
|
13
|
+
getPositionAccountDecoder,
|
|
14
|
+
getPositionAccountSize,
|
|
15
|
+
getVaultDecoder,
|
|
16
|
+
getVaultSize,
|
|
17
|
+
getWriterPositionDecoder,
|
|
18
|
+
getWriterPositionSize,
|
|
19
|
+
type OptionPool,
|
|
20
|
+
type PoolLoan,
|
|
21
|
+
type PositionAccount,
|
|
22
|
+
type Vault,
|
|
23
|
+
type WriterPosition,
|
|
24
|
+
} from "../generated/accounts";
|
|
25
|
+
import { PROGRAM_ID, toAddress } from "../client/program";
|
|
26
|
+
import type { AddressLike, KitRpc } from "../client/types";
|
|
27
|
+
|
|
28
|
+
const DISCRIMINATOR_OFFSET = 0n;
|
|
29
|
+
const OWNER_OFFSET = 8n;
|
|
30
|
+
/** WriterPosition layout: discriminator(8) + writer_authority(32) + option_pool(32). Used for getProgramAccounts memcmp. */
|
|
31
|
+
const WRITER_POSITION_OPTION_POOL_OFFSET = 40n;
|
|
32
|
+
const ACTIVE_POOL_LOAN_STATUS = 1;
|
|
33
|
+
|
|
34
|
+
type ListedAccount<T> = {
|
|
35
|
+
address: Address;
|
|
36
|
+
data: T;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type ProgramAccountResponse = {
|
|
40
|
+
pubkey: Address;
|
|
41
|
+
account: {
|
|
42
|
+
data: [string, string] | string;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function decodeBase64Data(data: string): Uint8Array {
|
|
47
|
+
const binary = atob(data);
|
|
48
|
+
const bytes = new Uint8Array(binary.length);
|
|
49
|
+
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
50
|
+
return bytes;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function discriminatorFilter(discriminator: Uint8Array) {
|
|
54
|
+
return {
|
|
55
|
+
memcmp: {
|
|
56
|
+
offset: DISCRIMINATOR_OFFSET,
|
|
57
|
+
encoding: "base58",
|
|
58
|
+
bytes: bs58.encode(discriminator),
|
|
59
|
+
},
|
|
60
|
+
} as const;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function ownerFilter(owner: AddressLike) {
|
|
64
|
+
return {
|
|
65
|
+
memcmp: {
|
|
66
|
+
offset: OWNER_OFFSET,
|
|
67
|
+
encoding: "base58",
|
|
68
|
+
bytes: toAddress(owner),
|
|
69
|
+
},
|
|
70
|
+
} as const;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function optionPoolFilter(optionPool: AddressLike) {
|
|
74
|
+
return {
|
|
75
|
+
memcmp: {
|
|
76
|
+
offset: WRITER_POSITION_OPTION_POOL_OFFSET,
|
|
77
|
+
encoding: "base58",
|
|
78
|
+
bytes: toAddress(optionPool),
|
|
79
|
+
},
|
|
80
|
+
} as const;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function fetchAndDecodeProgramAccounts<T>(
|
|
84
|
+
rpc: KitRpc,
|
|
85
|
+
decoder: { decode: (value: Uint8Array) => T },
|
|
86
|
+
filters: ReadonlyArray<unknown>,
|
|
87
|
+
programAddress: AddressLike = PROGRAM_ID
|
|
88
|
+
): Promise<Array<ListedAccount<T>>> {
|
|
89
|
+
const response = await rpc
|
|
90
|
+
.getProgramAccounts(toAddress(programAddress), {
|
|
91
|
+
encoding: "base64",
|
|
92
|
+
filters: filters as never,
|
|
93
|
+
})
|
|
94
|
+
.send();
|
|
95
|
+
|
|
96
|
+
const rawAccounts = Array.isArray(response)
|
|
97
|
+
? (response as Array<ProgramAccountResponse>)
|
|
98
|
+
: (response as { value: Array<ProgramAccountResponse> }).value;
|
|
99
|
+
|
|
100
|
+
return rawAccounts.map(({ pubkey, account }) => {
|
|
101
|
+
const base64Data =
|
|
102
|
+
Array.isArray(account.data) ? account.data[0] : account.data;
|
|
103
|
+
return {
|
|
104
|
+
address: pubkey,
|
|
105
|
+
data: decoder.decode(decodeBase64Data(base64Data)),
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function fetchWriterPositionsByWriter(
|
|
111
|
+
rpc: KitRpc,
|
|
112
|
+
writer: AddressLike
|
|
113
|
+
): Promise<Array<ListedAccount<WriterPosition>>> {
|
|
114
|
+
return fetchAndDecodeProgramAccounts(rpc, getWriterPositionDecoder(), [
|
|
115
|
+
discriminatorFilter(WRITER_POSITION_DISCRIMINATOR),
|
|
116
|
+
ownerFilter(writer),
|
|
117
|
+
{ dataSize: BigInt(getWriterPositionSize()) },
|
|
118
|
+
]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Fetches all WriterPosition accounts for a single option pool.
|
|
123
|
+
* WriterPosition layout: option_pool at offset 40, 32 bytes (memcmp filter).
|
|
124
|
+
*/
|
|
125
|
+
export async function fetchWriterPositionsForPool(
|
|
126
|
+
rpc: KitRpc,
|
|
127
|
+
optionPool: AddressLike,
|
|
128
|
+
programId?: AddressLike
|
|
129
|
+
): Promise<Array<ListedAccount<WriterPosition>>> {
|
|
130
|
+
return fetchAndDecodeProgramAccounts(
|
|
131
|
+
rpc,
|
|
132
|
+
getWriterPositionDecoder(),
|
|
133
|
+
[
|
|
134
|
+
discriminatorFilter(WRITER_POSITION_DISCRIMINATOR),
|
|
135
|
+
optionPoolFilter(optionPool),
|
|
136
|
+
{ dataSize: BigInt(getWriterPositionSize()) },
|
|
137
|
+
],
|
|
138
|
+
programId ?? PROGRAM_ID
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function fetchPositionAccountsByBuyer(
|
|
143
|
+
rpc: KitRpc,
|
|
144
|
+
buyer: AddressLike
|
|
145
|
+
): Promise<Array<ListedAccount<PositionAccount>>> {
|
|
146
|
+
return fetchAndDecodeProgramAccounts(rpc, getPositionAccountDecoder(), [
|
|
147
|
+
discriminatorFilter(POSITION_ACCOUNT_DISCRIMINATOR),
|
|
148
|
+
ownerFilter(buyer),
|
|
149
|
+
{ dataSize: BigInt(getPositionAccountSize()) },
|
|
150
|
+
]);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function fetchPoolLoansByMaker(
|
|
154
|
+
rpc: KitRpc,
|
|
155
|
+
maker: AddressLike
|
|
156
|
+
): Promise<Array<ListedAccount<PoolLoan>>> {
|
|
157
|
+
const decoded = await fetchAndDecodeProgramAccounts(rpc, getPoolLoanDecoder(), [
|
|
158
|
+
discriminatorFilter(POOL_LOAN_DISCRIMINATOR),
|
|
159
|
+
ownerFilter(maker),
|
|
160
|
+
{ dataSize: BigInt(getPoolLoanSize()) },
|
|
161
|
+
]);
|
|
162
|
+
return decoded.filter(
|
|
163
|
+
(item: { address: Address; data: PoolLoan }) =>
|
|
164
|
+
item.data.status === ACTIVE_POOL_LOAN_STATUS
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function fetchAllOptionPools(
|
|
169
|
+
rpc: KitRpc
|
|
170
|
+
): Promise<Array<ListedAccount<OptionPool>>> {
|
|
171
|
+
return fetchAndDecodeProgramAccounts(rpc, getOptionPoolDecoder(), [
|
|
172
|
+
discriminatorFilter(OPTION_POOL_DISCRIMINATOR),
|
|
173
|
+
{ dataSize: BigInt(getOptionPoolSize()) },
|
|
174
|
+
]);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export async function fetchAllVaults(
|
|
178
|
+
rpc: KitRpc
|
|
179
|
+
): Promise<Array<ListedAccount<Vault>>> {
|
|
180
|
+
return fetchAndDecodeProgramAccounts(rpc, getVaultDecoder(), [
|
|
181
|
+
discriminatorFilter(VAULT_DISCRIMINATOR),
|
|
182
|
+
{ dataSize: BigInt(getVaultSize()) },
|
|
183
|
+
]);
|
|
184
|
+
}
|
package/accounts/pdas.ts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { address, getAddressEncoder, getProgramDerivedAddress, type Address } from "@solana/kit";
|
|
2
|
+
import { OptionType } from "../generated/types";
|
|
3
|
+
import { PROGRAM_ID, toAddress } from "../client/program";
|
|
4
|
+
import type { AddressLike } from "../client/types";
|
|
5
|
+
|
|
6
|
+
const METADATA_PROGRAM_ADDRESS = address("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s");
|
|
7
|
+
const ASSOCIATED_TOKEN_PROGRAM_ADDRESS = address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL");
|
|
8
|
+
const TOKEN_PROGRAM_ADDRESS = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
9
|
+
|
|
10
|
+
function f64ToLeBytes(value: number): Uint8Array {
|
|
11
|
+
const bytes = new Uint8Array(8);
|
|
12
|
+
const view = new DataView(bytes.buffer);
|
|
13
|
+
view.setFloat64(0, value, true);
|
|
14
|
+
return bytes;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function i64ToLeBytes(value: bigint | number): Uint8Array {
|
|
18
|
+
const bytes = new Uint8Array(8);
|
|
19
|
+
const view = new DataView(bytes.buffer);
|
|
20
|
+
view.setBigInt64(0, BigInt(value), true);
|
|
21
|
+
return bytes;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function u64ToLeBytes(value: bigint | number): Uint8Array {
|
|
25
|
+
const bytes = new Uint8Array(8);
|
|
26
|
+
const view = new DataView(bytes.buffer);
|
|
27
|
+
view.setBigUint64(0, BigInt(value), true);
|
|
28
|
+
return bytes;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function optionTypeToU8(optionType: OptionType): number {
|
|
32
|
+
return optionType === OptionType.Call ? 0 : 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function deriveOptionAccountPda(params: {
|
|
36
|
+
underlyingAsset: AddressLike;
|
|
37
|
+
optionType: OptionType;
|
|
38
|
+
strikePrice: number;
|
|
39
|
+
expirationDate: bigint | number;
|
|
40
|
+
programId?: AddressLike;
|
|
41
|
+
}): Promise<readonly [Address, number]> {
|
|
42
|
+
const addressEncoder = getAddressEncoder();
|
|
43
|
+
const programAddress = params.programId ? toAddress(params.programId) : PROGRAM_ID;
|
|
44
|
+
return getProgramDerivedAddress({
|
|
45
|
+
programAddress,
|
|
46
|
+
seeds: [
|
|
47
|
+
new TextEncoder().encode("option"),
|
|
48
|
+
addressEncoder.encode(toAddress(params.underlyingAsset)),
|
|
49
|
+
Uint8Array.of(optionTypeToU8(params.optionType)),
|
|
50
|
+
f64ToLeBytes(params.strikePrice),
|
|
51
|
+
i64ToLeBytes(params.expirationDate),
|
|
52
|
+
],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function deriveLongMintPda(
|
|
57
|
+
optionAccount: AddressLike,
|
|
58
|
+
programId: AddressLike = PROGRAM_ID
|
|
59
|
+
): Promise<readonly [Address, number]> {
|
|
60
|
+
const addressEncoder = getAddressEncoder();
|
|
61
|
+
return getProgramDerivedAddress({
|
|
62
|
+
programAddress: toAddress(programId),
|
|
63
|
+
seeds: [
|
|
64
|
+
new TextEncoder().encode("long_mint"),
|
|
65
|
+
addressEncoder.encode(toAddress(optionAccount)),
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function deriveShortMintPda(
|
|
71
|
+
optionAccount: AddressLike,
|
|
72
|
+
programId: AddressLike = PROGRAM_ID
|
|
73
|
+
): Promise<readonly [Address, number]> {
|
|
74
|
+
const addressEncoder = getAddressEncoder();
|
|
75
|
+
return getProgramDerivedAddress({
|
|
76
|
+
programAddress: toAddress(programId),
|
|
77
|
+
seeds: [
|
|
78
|
+
new TextEncoder().encode("short_mint"),
|
|
79
|
+
addressEncoder.encode(toAddress(optionAccount)),
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function deriveMintAuthorityPda(
|
|
85
|
+
programId: AddressLike = PROGRAM_ID
|
|
86
|
+
): Promise<readonly [Address, number]> {
|
|
87
|
+
return getProgramDerivedAddress({
|
|
88
|
+
programAddress: toAddress(programId),
|
|
89
|
+
seeds: [new TextEncoder().encode("mint_authority")],
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function deriveConfigPda(
|
|
94
|
+
programId: AddressLike = PROGRAM_ID
|
|
95
|
+
): Promise<readonly [Address, number]> {
|
|
96
|
+
return getProgramDerivedAddress({
|
|
97
|
+
programAddress: toAddress(programId),
|
|
98
|
+
seeds: [new TextEncoder().encode("config")],
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function deriveOptionPoolPda(
|
|
103
|
+
optionAccount: AddressLike,
|
|
104
|
+
programId: AddressLike = PROGRAM_ID
|
|
105
|
+
): Promise<readonly [Address, number]> {
|
|
106
|
+
const addressEncoder = getAddressEncoder();
|
|
107
|
+
return getProgramDerivedAddress({
|
|
108
|
+
programAddress: toAddress(programId),
|
|
109
|
+
seeds: [
|
|
110
|
+
new TextEncoder().encode("option_pool"),
|
|
111
|
+
addressEncoder.encode(toAddress(optionAccount)),
|
|
112
|
+
],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function deriveCollateralPoolPda(
|
|
117
|
+
optionAccount: AddressLike,
|
|
118
|
+
programId: AddressLike = PROGRAM_ID
|
|
119
|
+
): Promise<readonly [Address, number]> {
|
|
120
|
+
const addressEncoder = getAddressEncoder();
|
|
121
|
+
return getProgramDerivedAddress({
|
|
122
|
+
programAddress: toAddress(programId),
|
|
123
|
+
seeds: [
|
|
124
|
+
new TextEncoder().encode("collateral_pool"),
|
|
125
|
+
addressEncoder.encode(toAddress(optionAccount)),
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function deriveWriterPositionPda(
|
|
131
|
+
optionPool: AddressLike,
|
|
132
|
+
writer: AddressLike,
|
|
133
|
+
programId: AddressLike = PROGRAM_ID
|
|
134
|
+
): Promise<readonly [Address, number]> {
|
|
135
|
+
const addressEncoder = getAddressEncoder();
|
|
136
|
+
return getProgramDerivedAddress({
|
|
137
|
+
programAddress: toAddress(programId),
|
|
138
|
+
seeds: [
|
|
139
|
+
new TextEncoder().encode("writer_position"),
|
|
140
|
+
addressEncoder.encode(toAddress(optionPool)),
|
|
141
|
+
addressEncoder.encode(toAddress(writer)),
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function deriveMakerCollateralSharePda(
|
|
147
|
+
collateralPool: AddressLike,
|
|
148
|
+
maker: AddressLike,
|
|
149
|
+
programId: AddressLike = PROGRAM_ID
|
|
150
|
+
): Promise<readonly [Address, number]> {
|
|
151
|
+
const addressEncoder = getAddressEncoder();
|
|
152
|
+
return getProgramDerivedAddress({
|
|
153
|
+
programAddress: toAddress(programId),
|
|
154
|
+
seeds: [
|
|
155
|
+
new TextEncoder().encode("maker_collateral_share"),
|
|
156
|
+
addressEncoder.encode(toAddress(collateralPool)),
|
|
157
|
+
addressEncoder.encode(toAddress(maker)),
|
|
158
|
+
],
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function deriveBuyerPositionPda(
|
|
163
|
+
buyer: AddressLike,
|
|
164
|
+
optionAccount: AddressLike,
|
|
165
|
+
programId: AddressLike = PROGRAM_ID
|
|
166
|
+
): Promise<readonly [Address, number]> {
|
|
167
|
+
const addressEncoder = getAddressEncoder();
|
|
168
|
+
return getProgramDerivedAddress({
|
|
169
|
+
programAddress: toAddress(programId),
|
|
170
|
+
seeds: [
|
|
171
|
+
new TextEncoder().encode("position"),
|
|
172
|
+
addressEncoder.encode(toAddress(buyer)),
|
|
173
|
+
addressEncoder.encode(toAddress(optionAccount)),
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function deriveMetadataPda(mint: AddressLike): Promise<readonly [Address, number]> {
|
|
179
|
+
const addressEncoder = getAddressEncoder();
|
|
180
|
+
return getProgramDerivedAddress({
|
|
181
|
+
programAddress: METADATA_PROGRAM_ADDRESS,
|
|
182
|
+
seeds: [
|
|
183
|
+
new TextEncoder().encode("metadata"),
|
|
184
|
+
addressEncoder.encode(METADATA_PROGRAM_ADDRESS),
|
|
185
|
+
addressEncoder.encode(toAddress(mint)),
|
|
186
|
+
],
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function deriveMarketDataPda(
|
|
191
|
+
underlyingAsset: AddressLike,
|
|
192
|
+
programId: AddressLike = PROGRAM_ID
|
|
193
|
+
): Promise<readonly [Address, number]> {
|
|
194
|
+
const addressEncoder = getAddressEncoder();
|
|
195
|
+
return getProgramDerivedAddress({
|
|
196
|
+
programAddress: toAddress(programId),
|
|
197
|
+
seeds: [
|
|
198
|
+
new TextEncoder().encode("market_data"),
|
|
199
|
+
addressEncoder.encode(toAddress(underlyingAsset)),
|
|
200
|
+
],
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function deriveVaultPda(
|
|
205
|
+
mint: AddressLike,
|
|
206
|
+
programId: AddressLike = PROGRAM_ID
|
|
207
|
+
): Promise<readonly [Address, number]> {
|
|
208
|
+
const addressEncoder = getAddressEncoder();
|
|
209
|
+
return getProgramDerivedAddress({
|
|
210
|
+
programAddress: toAddress(programId),
|
|
211
|
+
seeds: [new TextEncoder().encode("vault"), addressEncoder.encode(toAddress(mint))],
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export async function deriveLenderPositionPda(
|
|
216
|
+
vault: AddressLike,
|
|
217
|
+
lender: AddressLike,
|
|
218
|
+
programId: AddressLike = PROGRAM_ID
|
|
219
|
+
): Promise<readonly [Address, number]> {
|
|
220
|
+
const addressEncoder = getAddressEncoder();
|
|
221
|
+
return getProgramDerivedAddress({
|
|
222
|
+
programAddress: toAddress(programId),
|
|
223
|
+
seeds: [
|
|
224
|
+
new TextEncoder().encode("lender_position"),
|
|
225
|
+
addressEncoder.encode(toAddress(vault)),
|
|
226
|
+
addressEncoder.encode(toAddress(lender)),
|
|
227
|
+
],
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export async function deriveEscrowStatePda(
|
|
232
|
+
maker: AddressLike,
|
|
233
|
+
collateralMint: AddressLike,
|
|
234
|
+
programId: AddressLike = PROGRAM_ID
|
|
235
|
+
): Promise<readonly [Address, number]> {
|
|
236
|
+
const addressEncoder = getAddressEncoder();
|
|
237
|
+
return getProgramDerivedAddress({
|
|
238
|
+
programAddress: toAddress(programId),
|
|
239
|
+
seeds: [
|
|
240
|
+
new TextEncoder().encode("escrow_v2"),
|
|
241
|
+
addressEncoder.encode(toAddress(maker)),
|
|
242
|
+
addressEncoder.encode(toAddress(collateralMint)),
|
|
243
|
+
],
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export async function deriveEscrowAuthorityPda(
|
|
248
|
+
escrowState: AddressLike,
|
|
249
|
+
programId: AddressLike = PROGRAM_ID
|
|
250
|
+
): Promise<readonly [Address, number]> {
|
|
251
|
+
const addressEncoder = getAddressEncoder();
|
|
252
|
+
return getProgramDerivedAddress({
|
|
253
|
+
programAddress: toAddress(programId),
|
|
254
|
+
seeds: [
|
|
255
|
+
new TextEncoder().encode("escrow_authority_v2"),
|
|
256
|
+
addressEncoder.encode(toAddress(escrowState)),
|
|
257
|
+
],
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Derives the PoolLoan PDA using the canonical seeds: vault, maker, nonce.
|
|
263
|
+
* Matches the program's derivation in omlp_context.rs (BorrowFromPool).
|
|
264
|
+
*
|
|
265
|
+
* @param vault - OMLP vault PDA (from deriveVaultPda)
|
|
266
|
+
* @param maker - Writer/borrower pubkey
|
|
267
|
+
* @param nonce - Loan nonce (u64)
|
|
268
|
+
* @param programId - Optional program ID
|
|
269
|
+
*/
|
|
270
|
+
export async function derivePoolLoanPdaFromVault(
|
|
271
|
+
vault: AddressLike,
|
|
272
|
+
maker: AddressLike,
|
|
273
|
+
nonce: bigint | number,
|
|
274
|
+
programId: AddressLike = PROGRAM_ID
|
|
275
|
+
): Promise<readonly [Address, number]> {
|
|
276
|
+
const addressEncoder = getAddressEncoder();
|
|
277
|
+
return getProgramDerivedAddress({
|
|
278
|
+
programAddress: toAddress(programId),
|
|
279
|
+
seeds: [
|
|
280
|
+
new TextEncoder().encode("pool_loan"),
|
|
281
|
+
addressEncoder.encode(toAddress(vault)),
|
|
282
|
+
addressEncoder.encode(toAddress(maker)),
|
|
283
|
+
u64ToLeBytes(nonce),
|
|
284
|
+
],
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Derives the PoolLoan PDA using writer position and nonce.
|
|
290
|
+
*
|
|
291
|
+
* @deprecated This derivation does not match the program. Use {@link derivePoolLoanPdaFromVault}
|
|
292
|
+
* with (vault, maker, nonce) instead. Program seeds are: pool_loan, vault, maker, nonce (u64 le).
|
|
293
|
+
*/
|
|
294
|
+
export async function derivePoolLoanPda(
|
|
295
|
+
writerPosition: AddressLike,
|
|
296
|
+
nonce: bigint | number,
|
|
297
|
+
programId: AddressLike = PROGRAM_ID
|
|
298
|
+
): Promise<readonly [Address, number]> {
|
|
299
|
+
const addressEncoder = getAddressEncoder();
|
|
300
|
+
return getProgramDerivedAddress({
|
|
301
|
+
programAddress: toAddress(programId),
|
|
302
|
+
seeds: [
|
|
303
|
+
new TextEncoder().encode("pool_loan"),
|
|
304
|
+
addressEncoder.encode(toAddress(writerPosition)),
|
|
305
|
+
u64ToLeBytes(nonce),
|
|
306
|
+
],
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export async function deriveAssociatedTokenAddress(
|
|
311
|
+
owner: AddressLike,
|
|
312
|
+
mint: AddressLike,
|
|
313
|
+
_allowOwnerOffCurve = false
|
|
314
|
+
): Promise<Address> {
|
|
315
|
+
const addressEncoder = getAddressEncoder();
|
|
316
|
+
const [associatedTokenAddress] = await getProgramDerivedAddress({
|
|
317
|
+
programAddress: ASSOCIATED_TOKEN_PROGRAM_ADDRESS,
|
|
318
|
+
seeds: [
|
|
319
|
+
addressEncoder.encode(toAddress(owner)),
|
|
320
|
+
addressEncoder.encode(TOKEN_PROGRAM_ADDRESS),
|
|
321
|
+
addressEncoder.encode(toAddress(mint)),
|
|
322
|
+
],
|
|
323
|
+
});
|
|
324
|
+
return associatedTokenAddress;
|
|
325
|
+
}
|