@percolatorct/sdk 0.3.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.
@@ -0,0 +1,2 @@
1
+ export * from "./trading.js";
2
+ export * from "./warmup.js";
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Coin-margined perpetual trade math utilities.
3
+ *
4
+ * On-chain PnL formula:
5
+ * mark_pnl = (oracle - entry) * abs_pos / oracle (longs)
6
+ * mark_pnl = (entry - oracle) * abs_pos / oracle (shorts)
7
+ *
8
+ * All prices are in e6 format (1 USD = 1_000_000).
9
+ * All token amounts are in native units (e.g. lamports).
10
+ */
11
+ /**
12
+ * Compute mark-to-market PnL for an open position.
13
+ */
14
+ export declare function computeMarkPnl(positionSize: bigint, entryPrice: bigint, oraclePrice: bigint): bigint;
15
+ /**
16
+ * Compute liquidation price given entry, capital, position and maintenance margin.
17
+ * Uses pure BigInt arithmetic for precision (no Number() truncation).
18
+ */
19
+ export declare function computeLiqPrice(entryPrice: bigint, capital: bigint, positionSize: bigint, maintenanceMarginBps: bigint): bigint;
20
+ /**
21
+ * Compute estimated liquidation price BEFORE opening a trade.
22
+ * Accounts for trading fees reducing effective capital.
23
+ */
24
+ export declare function computePreTradeLiqPrice(oracleE6: bigint, margin: bigint, posSize: bigint, maintBps: bigint, feeBps: bigint, direction: "long" | "short"): bigint;
25
+ /**
26
+ * Compute trading fee from notional value and fee rate in bps.
27
+ */
28
+ export declare function computeTradingFee(notional: bigint, tradingFeeBps: bigint): bigint;
29
+ /**
30
+ * Dynamic fee tier configuration.
31
+ */
32
+ export interface FeeTierConfig {
33
+ /** Base trading fee (Tier 1) in bps */
34
+ baseBps: bigint;
35
+ /** Tier 2 fee in bps (0 = disabled) */
36
+ tier2Bps: bigint;
37
+ /** Tier 3 fee in bps (0 = disabled) */
38
+ tier3Bps: bigint;
39
+ /** Notional threshold to enter Tier 2 (0 = tiered fees disabled) */
40
+ tier2Threshold: bigint;
41
+ /** Notional threshold to enter Tier 3 */
42
+ tier3Threshold: bigint;
43
+ }
44
+ /**
45
+ * Compute the effective fee rate in bps using the tiered fee schedule.
46
+ *
47
+ * Mirrors on-chain `compute_dynamic_fee_bps` logic:
48
+ * - notional < tier2Threshold → baseBps (Tier 1)
49
+ * - notional < tier3Threshold → tier2Bps (Tier 2)
50
+ * - notional >= tier3Threshold → tier3Bps (Tier 3)
51
+ *
52
+ * If tier2Threshold == 0, tiered fees are disabled (flat baseBps).
53
+ */
54
+ export declare function computeDynamicFeeBps(notional: bigint, config: FeeTierConfig): bigint;
55
+ /**
56
+ * Compute the dynamic trading fee for a given notional and tier config.
57
+ *
58
+ * Uses ceiling division to match on-chain behavior (prevents fee evasion
59
+ * via micro-trades).
60
+ */
61
+ export declare function computeDynamicTradingFee(notional: bigint, config: FeeTierConfig): bigint;
62
+ /**
63
+ * Fee split configuration.
64
+ */
65
+ export interface FeeSplitConfig {
66
+ /** LP vault share in bps (0–10_000) */
67
+ lpBps: bigint;
68
+ /** Protocol treasury share in bps */
69
+ protocolBps: bigint;
70
+ /** Market creator share in bps */
71
+ creatorBps: bigint;
72
+ }
73
+ /**
74
+ * Compute fee split for a total fee amount.
75
+ *
76
+ * Returns [lpShare, protocolShare, creatorShare].
77
+ * If all split params are 0, 100% goes to LP (legacy behavior).
78
+ * Creator gets the rounding remainder to ensure total is preserved.
79
+ */
80
+ export declare function computeFeeSplit(totalFee: bigint, config: FeeSplitConfig): [bigint, bigint, bigint];
81
+ /**
82
+ * Compute PnL as a percentage of capital.
83
+ *
84
+ * Uses BigInt scaling to avoid precision loss from Number(bigint) conversion.
85
+ * Number(bigint) silently truncates values above 2^53, which can produce
86
+ * incorrect percentages for large positions (e.g., tokens with 9 decimals
87
+ * where capital > ~9M tokens in native units exceeds MAX_SAFE_INTEGER).
88
+ */
89
+ export declare function computePnlPercent(pnlTokens: bigint, capital: bigint): number;
90
+ /**
91
+ * Estimate entry price including fee impact (slippage approximation).
92
+ */
93
+ export declare function computeEstimatedEntryPrice(oracleE6: bigint, tradingFeeBps: bigint, direction: "long" | "short"): bigint;
94
+ /**
95
+ * Convert per-slot funding rate (bps) to annualized percentage.
96
+ */
97
+ export declare function computeFundingRateAnnualized(fundingRateBpsPerSlot: bigint): number;
98
+ /**
99
+ * Compute margin required for a given notional and initial margin bps.
100
+ */
101
+ export declare function computeRequiredMargin(notional: bigint, initialMarginBps: bigint): bigint;
102
+ /**
103
+ * Compute maximum leverage from initial margin bps.
104
+ *
105
+ * @throws Error if initialMarginBps is zero (infinite leverage is undefined)
106
+ */
107
+ export declare function computeMaxLeverage(initialMarginBps: bigint): number;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Warmup leverage cap utilities.
3
+ *
4
+ * During the market warmup period, capital is released linearly over
5
+ * `warmupPeriodSlots` slots, which constrains the effective leverage
6
+ * and maximum position size available to traders.
7
+ */
8
+ /**
9
+ * Compute unlocked capital during the warmup period.
10
+ *
11
+ * Capital is released linearly over `warmupPeriodSlots` slots starting from
12
+ * `warmupStartedAtSlot`. Before warmup starts (startSlot === 0) or if the
13
+ * warmup period is 0, all capital is considered unlocked.
14
+ *
15
+ * @param totalCapital - Total deposited capital (native units).
16
+ * @param currentSlot - The current on-chain slot.
17
+ * @param warmupStartSlot - Slot at which warmup started (0 = not started).
18
+ * @param warmupPeriodSlots - Total slots in the warmup period.
19
+ * @returns The amount of capital currently unlocked.
20
+ */
21
+ export declare function computeWarmupUnlockedCapital(totalCapital: bigint, currentSlot: bigint, warmupStartSlot: bigint, warmupPeriodSlots: bigint): bigint;
22
+ /**
23
+ * Compute the effective maximum leverage during the warmup period.
24
+ *
25
+ * During warmup, only unlocked capital can be used as margin. The effective
26
+ * leverage relative to *total* capital is therefore capped at:
27
+ *
28
+ * effectiveMaxLeverage = maxLeverage × (unlockedCapital / totalCapital)
29
+ *
30
+ * This returns a floored integer value (leverage is always a whole number
31
+ * in the UI), with a minimum of 1x if any capital is unlocked.
32
+ *
33
+ * @param initialMarginBps - Initial margin requirement in basis points.
34
+ * @param totalCapital - Total deposited capital (native units).
35
+ * @param currentSlot - The current on-chain slot.
36
+ * @param warmupStartSlot - Slot at which warmup started (0 = not started).
37
+ * @param warmupPeriodSlots - Total slots in the warmup period.
38
+ * @returns The effective maximum leverage (integer, ≥ 1).
39
+ */
40
+ export declare function computeWarmupLeverageCap(initialMarginBps: bigint, totalCapital: bigint, currentSlot: bigint, warmupStartSlot: bigint, warmupPeriodSlots: bigint): number;
41
+ /**
42
+ * Compute the maximum position size allowed during warmup.
43
+ *
44
+ * This is the unlocked capital multiplied by the base max leverage.
45
+ * Unlike `computeWarmupLeverageCap` (which gives effective leverage
46
+ * relative to total capital), this gives the absolute notional cap.
47
+ *
48
+ * @param initialMarginBps - Initial margin requirement in basis points.
49
+ * @param totalCapital - Total deposited capital (native units).
50
+ * @param currentSlot - The current on-chain slot.
51
+ * @param warmupStartSlot - Slot at which warmup started (0 = not started).
52
+ * @param warmupPeriodSlots - Total slots in the warmup period.
53
+ * @returns Maximum position size in native units.
54
+ */
55
+ export declare function computeWarmupMaxPositionSize(initialMarginBps: bigint, totalCapital: bigint, currentSlot: bigint, warmupStartSlot: bigint, warmupPeriodSlots: bigint): bigint;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Smart Price Router — automatic oracle selection for any token.
3
+ *
4
+ * Given a token mint, discovers all available price sources (DexScreener, Pyth, Jupiter),
5
+ * ranks them by liquidity/reliability, and returns the best oracle config.
6
+ */
7
+ export type PriceSourceType = "pyth" | "dex" | "jupiter";
8
+ export interface PriceSource {
9
+ type: PriceSourceType;
10
+ /** Pool address (dex), Pyth feed ID (pyth), or mint (jupiter) */
11
+ address: string;
12
+ /** DEX id for dex sources */
13
+ dexId?: string;
14
+ /** Pair label e.g. "SOL / USDC" */
15
+ pairLabel?: string;
16
+ /** USD liquidity depth — higher is better */
17
+ liquidity: number;
18
+ /** Latest spot price in USD */
19
+ price: number;
20
+ /** Confidence score 0-100 (composite of liquidity, staleness, reliability) */
21
+ confidence: number;
22
+ }
23
+ export interface PriceRouterResult {
24
+ mint: string;
25
+ bestSource: PriceSource | null;
26
+ allSources: PriceSource[];
27
+ /** ISO timestamp of resolution */
28
+ resolvedAt: string;
29
+ }
30
+ /** Options for {@link resolvePrice}. */
31
+ export interface ResolvePriceOptions {
32
+ timeoutMs?: number;
33
+ }
34
+ export declare const PYTH_SOLANA_FEEDS: Record<string, {
35
+ symbol: string;
36
+ mint: string;
37
+ }>;
38
+ export declare function resolvePrice(mint: string, signal?: AbortSignal, options?: ResolvePriceOptions): Promise<PriceRouterResult>;
@@ -0,0 +1 @@
1
+ export * from "./tx.js";
@@ -0,0 +1,31 @@
1
+ import { Connection, PublicKey, TransactionInstruction, Keypair, Commitment, AccountMeta } from "@solana/web3.js";
2
+ export interface BuildIxParams {
3
+ programId: PublicKey;
4
+ keys: AccountMeta[];
5
+ data: Uint8Array | Buffer;
6
+ }
7
+ /**
8
+ * Build a transaction instruction.
9
+ */
10
+ export declare function buildIx(params: BuildIxParams): TransactionInstruction;
11
+ export interface TxResult {
12
+ signature: string;
13
+ slot: number;
14
+ err: string | null;
15
+ hint?: string;
16
+ logs: string[];
17
+ unitsConsumed?: number;
18
+ }
19
+ export interface SimulateOrSendParams {
20
+ connection: Connection;
21
+ ix: TransactionInstruction;
22
+ signers: Keypair[];
23
+ simulate: boolean;
24
+ commitment?: Commitment;
25
+ computeUnitLimit?: number;
26
+ }
27
+ export declare function simulateOrSend(params: SimulateOrSendParams): Promise<TxResult>;
28
+ /**
29
+ * Format transaction result for output.
30
+ */
31
+ export declare function formatResult(result: TxResult, jsonMode: boolean): string;
@@ -0,0 +1,305 @@
1
+ /**
2
+ * @module adl
3
+ * Percolator ADL (Auto-Deleveraging) client utilities.
4
+ *
5
+ * PERC-8278 / PERC-8312 / PERC-305: ADL is triggered when `pnl_pos_tot > max_pnl_cap`
6
+ * on a market (PnL cap exceeded) AND the insurance fund is fully depleted (balance == 0).
7
+ * The most profitable positions on the dominant side are deleveraged first.
8
+ *
9
+ * **Note on caller permissions:** `ExecuteAdl` (tag 50) requires the caller to be the
10
+ * market admin/keeper key (`header.admin`). It is NOT permissionless despite the
11
+ * instruction being structurally available to any signer.
12
+ *
13
+ * API surface:
14
+ * - fetchAdlRankedPositions() — fetch slab + rank all open positions by PnL%
15
+ * - rankAdlPositions() — pure (no-RPC) variant for already-fetched slab bytes
16
+ * - isAdlTriggered() — check if slab's pnl_pos_tot exceeds max_pnl_cap
17
+ * - buildAdlInstruction() — build a single ExecuteAdl TransactionInstruction
18
+ * - buildAdlTransaction() — fetch + rank + pick top target + return instruction
19
+ * - parseAdlEvent() — decode AdlEvent from transaction log lines
20
+ * - fetchAdlRankings() — call /api/adl/rankings HTTP endpoint
21
+ * - AdlRankedPosition — position record with adl_rank and computed pnlPct
22
+ * - AdlRankingResult — full ranking with trigger status
23
+ * - AdlEvent — decoded on-chain AdlEvent log entry (tag 0xAD1E_0001)
24
+ * - AdlApiRanking — single ranked position from /api/adl/rankings
25
+ * - AdlApiResult — full result from /api/adl/rankings
26
+ * - AdlSide — "long" | "short"
27
+ */
28
+ import { Connection, PublicKey, TransactionInstruction } from "@solana/web3.js";
29
+ /** Position side derived from positionSize sign. */
30
+ export type AdlSide = "long" | "short";
31
+ /**
32
+ * A ranked open position for ADL purposes.
33
+ * Positions are ranked descending by `pnlPct` — rank 0 is the most profitable
34
+ * and will be deleveraged first.
35
+ */
36
+ export interface AdlRankedPosition {
37
+ /** Account index in the slab (used as `targetIdx` in ExecuteAdl). */
38
+ idx: number;
39
+ /** Owner public key. */
40
+ owner: PublicKey;
41
+ /** Raw position size (i128 — negative = short, positive = long). */
42
+ positionSize: bigint;
43
+ /** Realised + mark-to-market PnL in lamports (i128 from slab). */
44
+ pnl: bigint;
45
+ /** Capital at entry in lamports (u128). */
46
+ capital: bigint;
47
+ /**
48
+ * PnL as a fraction of capital, expressed as basis points (scaled × 10_000).
49
+ * pnlPct = pnl * 10_000 / capital.
50
+ * Higher = more profitable = deleveraged first.
51
+ */
52
+ pnlPct: bigint;
53
+ /** Long or short. */
54
+ side: AdlSide;
55
+ /**
56
+ * ADL rank among positions on the same side (0 = highest PnL%, deleveraged first).
57
+ * `-1` if position size is zero (inactive).
58
+ */
59
+ adlRank: number;
60
+ }
61
+ /**
62
+ * Result of `fetchAdlRankedPositions`.
63
+ */
64
+ export interface AdlRankingResult {
65
+ /** All open (non-zero) user positions, sorted descending by PnLPct, ranked. */
66
+ ranked: AdlRankedPosition[];
67
+ /**
68
+ * Longs ranked separately (adlRank within this subset).
69
+ * Rank 0 = most profitable long = first to be deleveraged on a net-long market.
70
+ */
71
+ longs: AdlRankedPosition[];
72
+ /**
73
+ * Shorts ranked separately (adlRank within this subset).
74
+ * Rank 0 = most profitable short (most negative pnlPct magnitude — i.e., highest
75
+ * unrealised gain for the short-side holder).
76
+ */
77
+ shorts: AdlRankedPosition[];
78
+ /** Whether ADL is currently triggered (pnlPosTot > maxPnlCap). */
79
+ isTriggered: boolean;
80
+ /** pnl_pos_tot from engine state. */
81
+ pnlPosTot: bigint;
82
+ /** max_pnl_cap from market config. */
83
+ maxPnlCap: bigint;
84
+ }
85
+ /**
86
+ * Check whether ADL is currently triggered on a slab.
87
+ *
88
+ * ADL triggers when pnl_pos_tot > max_pnl_cap (max_pnl_cap must be > 0).
89
+ *
90
+ * @param slabData - Raw slab account bytes.
91
+ * @returns true if ADL is triggered.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * const data = await fetchSlab(connection, slabKey);
96
+ * if (isAdlTriggered(data)) {
97
+ * const ranking = await fetchAdlRankedPositions(connection, slabKey);
98
+ * }
99
+ * ```
100
+ */
101
+ export declare function isAdlTriggered(slabData: Uint8Array): boolean;
102
+ /**
103
+ * Fetch a slab and rank all open user positions by PnL% for ADL targeting.
104
+ *
105
+ * Positions are ranked separately per side:
106
+ * - Longs: rank 0 = highest positive PnL% (most profitable long)
107
+ * - Shorts: rank 0 = highest negative PnL% by abs value (most profitable short)
108
+ *
109
+ * Rank ordering matches the on-chain ADL engine in percolator-prog (PERC-8273):
110
+ * the position at rank 0 of the dominant side is deleveraged first.
111
+ *
112
+ * @param connection - Solana connection.
113
+ * @param slab - Slab (market) public key.
114
+ * @returns AdlRankingResult with ranked longs, ranked shorts, and trigger status.
115
+ *
116
+ * @example
117
+ * ```ts
118
+ * const { ranked, longs, isTriggered } = await fetchAdlRankedPositions(connection, slabKey);
119
+ * if (isTriggered && longs.length > 0) {
120
+ * const target = longs[0]; // highest PnL long
121
+ * const ix = buildAdlInstruction(caller, slabKey, oracleKey, programId, target.idx);
122
+ * }
123
+ * ```
124
+ */
125
+ export declare function fetchAdlRankedPositions(connection: Connection, slab: PublicKey): Promise<AdlRankingResult>;
126
+ /**
127
+ * Pure (no-RPC) variant — rank positions from already-fetched slab bytes.
128
+ * Useful when you already have the slab data (e.g., from a subscription).
129
+ */
130
+ export declare function rankAdlPositions(slabData: Uint8Array): AdlRankingResult;
131
+ /**
132
+ * Build a single `ExecuteAdl` TransactionInstruction (tag 50, PERC-305).
133
+ *
134
+ * Does NOT fetch the slab or check trigger status — use `fetchAdlRankedPositions`
135
+ * first to determine the correct `targetIdx`.
136
+ *
137
+ * **Caller requirement:** The on-chain handler requires the caller to be the market
138
+ * admin/keeper authority (`header.admin`). Passing any other signer will result in
139
+ * `EngineUnauthorized`.
140
+ *
141
+ * @param caller - Signer — must be the market keeper/admin authority.
142
+ * @param slab - Slab (market) public key.
143
+ * @param oracle - Primary oracle public key for this market.
144
+ * @param programId - Percolator program ID.
145
+ * @param targetIdx - Account index to deleverage (from `AdlRankedPosition.idx`).
146
+ * @param backupOracles - Optional additional oracle accounts (non-Hyperp markets).
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * import { fetchAdlRankedPositions, buildAdlInstruction } from "@percolator/sdk";
151
+ *
152
+ * const { longs, isTriggered } = await fetchAdlRankedPositions(connection, slabKey);
153
+ * if (isTriggered && longs.length > 0) {
154
+ * const ix = buildAdlInstruction(
155
+ * caller.publicKey, slabKey, oracleKey, PROGRAM_ID, longs[0].idx
156
+ * );
157
+ * await sendAndConfirmTransaction(connection, new Transaction().add(ix), [caller]);
158
+ * }
159
+ * ```
160
+ */
161
+ export declare function buildAdlInstruction(caller: PublicKey, slab: PublicKey, oracle: PublicKey, programId: PublicKey, targetIdx: number, backupOracles?: PublicKey[]): TransactionInstruction;
162
+ /**
163
+ * Convenience builder: fetch slab, rank positions, pick the highest-ranked
164
+ * target on the given side, and return a ready-to-send `TransactionInstruction`.
165
+ *
166
+ * Returns `null` when ADL is not triggered or no eligible positions exist.
167
+ *
168
+ * @param connection - Solana connection.
169
+ * @param caller - Signer — must be the market keeper/admin authority.
170
+ * @param slab - Slab (market) public key.
171
+ * @param oracle - Primary oracle public key.
172
+ * @param programId - Percolator program ID.
173
+ * @param preferSide - Optional: target "long" or "short" side only.
174
+ * If omitted, picks the overall top-ranked position.
175
+ * @param backupOracles - Optional extra oracle accounts.
176
+ *
177
+ * @example
178
+ * ```ts
179
+ * const ix = await buildAdlTransaction(
180
+ * connection, caller.publicKey, slabKey, oracleKey, PROGRAM_ID
181
+ * );
182
+ * if (ix) {
183
+ * await sendAndConfirmTransaction(connection, new Transaction().add(ix), [caller]);
184
+ * }
185
+ * ```
186
+ */
187
+ export declare function buildAdlTransaction(connection: Connection, caller: PublicKey, slab: PublicKey, oracle: PublicKey, programId: PublicKey, preferSide?: AdlSide, backupOracles?: PublicKey[]): Promise<TransactionInstruction | null>;
188
+ /**
189
+ * Decoded on-chain AdlEvent emitted by the `ExecuteAdl` instruction handler.
190
+ *
191
+ * The on-chain handler emits via `sol_log_64(0xAD1E_0001, target_idx, price, closed_lo, closed_hi)`.
192
+ * `sol_log_64` prints 5 decimal u64 values separated by spaces on a single "Program log:" line.
193
+ *
194
+ * Fields:
195
+ * - `tag` — always `0xAD1E_0001` (2970353665n)
196
+ * - `targetIdx` — slab account index that was deleveraged
197
+ * - `price` — oracle price used (in market price units, e.g. e6)
198
+ * - `closedAbs` — absolute size of the position closed (i128, reassembled from lo+hi u64 parts)
199
+ *
200
+ * @example
201
+ * ```ts
202
+ * const logs = tx.meta?.logMessages ?? [];
203
+ * const event = parseAdlEvent(logs);
204
+ * if (event) {
205
+ * console.log("ADL closed position", event.targetIdx, "size", event.closedAbs);
206
+ * }
207
+ * ```
208
+ */
209
+ export interface AdlEvent {
210
+ /** Tag discriminator — always 0xAD1E_0001n (2970353665). */
211
+ tag: bigint;
212
+ /** Slab account index that was deleveraged. */
213
+ targetIdx: number;
214
+ /** Oracle price used for the deleverage (market-native units, e.g. lamports/e6). */
215
+ price: bigint;
216
+ /**
217
+ * Absolute position size closed (reassembled from lo+hi u64).
218
+ * This is the i128 absolute value — always non-negative.
219
+ */
220
+ closedAbs: bigint;
221
+ }
222
+ /**
223
+ * Parse the AdlEvent from a transaction's log messages.
224
+ *
225
+ * Searches for a "Program log: <a> <b> <c> <d> <e>" line where the first
226
+ * decimal value equals `0xAD1E_0001` (2970353665). Returns `null` if not found.
227
+ *
228
+ * @param logs - Array of log message strings (from `tx.meta.logMessages`).
229
+ * @returns Decoded `AdlEvent` or `null` if the log is not present.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * const event = parseAdlEvent(tx.meta?.logMessages ?? []);
234
+ * if (event) {
235
+ * console.log(`ADL: idx=${event.targetIdx} price=${event.price} closed=${event.closedAbs}`);
236
+ * }
237
+ * ```
238
+ */
239
+ export declare function parseAdlEvent(logs: string[]): AdlEvent | null;
240
+ /**
241
+ * A single ranked position as returned by the /api/adl/rankings endpoint.
242
+ */
243
+ export interface AdlApiRanking {
244
+ /** 1-based rank (1 = highest PnL%, first to be deleveraged). */
245
+ rank: number;
246
+ /** Slab account index. Pass as `targetIdx` to `buildAdlInstruction`. */
247
+ idx: number;
248
+ /** Absolute PnL (lamports) as a decimal string. */
249
+ pnlAbs: string;
250
+ /** Capital at entry (lamports) as a decimal string. */
251
+ capital: string;
252
+ /** PnL as millionths of capital (pnl * 1_000_000 / capital). */
253
+ pnlPctMillionths: string;
254
+ }
255
+ /**
256
+ * Full result from the /api/adl/rankings endpoint.
257
+ */
258
+ export interface AdlApiResult {
259
+ slabAddress: string;
260
+ /** pnl_pos_tot from slab engine state (decimal string). */
261
+ pnlPosTot: string;
262
+ /** max_pnl_cap from market config (decimal string, "0" if unconfigured). */
263
+ maxPnlCap: string;
264
+ /** Insurance fund balance (decimal string). */
265
+ insuranceFundBalance: string;
266
+ /** Insurance fund lifetime fee revenue (decimal string). */
267
+ insuranceFundFeeRevenue: string;
268
+ /** Insurance utilization in basis points (0–10000). */
269
+ insuranceUtilizationBps: number;
270
+ /** true if pnlPosTot > maxPnlCap. */
271
+ capExceeded: boolean;
272
+ /** true if insurance fund is fully depleted (balance == 0). */
273
+ insuranceDepleted: boolean;
274
+ /** true if utilization BPS exceeds the configured ADL threshold. */
275
+ utilizationTriggered: boolean;
276
+ /** true if ADL is needed (capExceeded or utilizationTriggered). */
277
+ adlNeeded: boolean;
278
+ /** Excess PnL above cap (decimal string). */
279
+ excess: string;
280
+ /** Ranked positions (empty if adlNeeded=false). */
281
+ rankings: AdlApiRanking[];
282
+ }
283
+ /**
284
+ * Fetch ADL rankings from the Percolator API.
285
+ *
286
+ * Calls `GET <apiBase>/api/adl/rankings?slab=<address>` and returns the
287
+ * parsed result. Use this from the frontend or keeper to determine ADL
288
+ * trigger status and pick the target index.
289
+ *
290
+ * @param apiBase - Base URL of the Percolator API (e.g. `https://api.percolator.io`).
291
+ * @param slab - Slab (market) public key or base58 address string.
292
+ * @param fetchFn - Optional custom fetch implementation (defaults to global `fetch`).
293
+ * @returns Parsed `AdlApiResult`.
294
+ * @throws On HTTP error or JSON parse failure.
295
+ *
296
+ * @example
297
+ * ```ts
298
+ * const result = await fetchAdlRankings("https://api.percolator.io", slabKey);
299
+ * if (result.adlNeeded && result.rankings.length > 0) {
300
+ * const target = result.rankings[0]; // rank 1 = highest PnL%
301
+ * const ix = buildAdlInstruction(caller, slabKey, oracleKey, PROGRAM_ID, target.idx);
302
+ * }
303
+ * ```
304
+ */
305
+ export declare function fetchAdlRankings(apiBase: string, slab: PublicKey | string, fetchFn?: typeof fetch): Promise<AdlApiResult>;
@@ -0,0 +1,18 @@
1
+ import { Connection, PublicKey } from "@solana/web3.js";
2
+ import { Account } from "@solana/spl-token";
3
+ /**
4
+ * Get the associated token address for an owner and mint.
5
+ * Supports both standard SPL Token and Token2022 via optional tokenProgramId.
6
+ */
7
+ export declare function getAta(owner: PublicKey, mint: PublicKey, allowOwnerOffCurve?: boolean, tokenProgramId?: PublicKey): Promise<PublicKey>;
8
+ /**
9
+ * Synchronous version of getAta.
10
+ * Supports both standard SPL Token and Token2022 via optional tokenProgramId.
11
+ */
12
+ export declare function getAtaSync(owner: PublicKey, mint: PublicKey, allowOwnerOffCurve?: boolean, tokenProgramId?: PublicKey): PublicKey;
13
+ /**
14
+ * Fetch token account info.
15
+ * Supports both standard SPL Token and Token2022 via optional tokenProgramId.
16
+ * Throws if account doesn't exist.
17
+ */
18
+ export declare function fetchTokenAccount(connection: Connection, address: PublicKey, tokenProgramId?: PublicKey): Promise<Account>;
@@ -0,0 +1,49 @@
1
+ import { PublicKey } from "@solana/web3.js";
2
+ export type DexType = "pumpswap" | "raydium-clmm" | "meteora-dlmm";
3
+ export interface DexPoolInfo {
4
+ dexType: DexType;
5
+ poolAddress: PublicKey;
6
+ baseMint: PublicKey;
7
+ quoteMint: PublicKey;
8
+ baseVault?: PublicKey;
9
+ quoteVault?: PublicKey;
10
+ }
11
+ /**
12
+ * Detect DEX type from the program that owns the pool account.
13
+ *
14
+ * @param ownerProgramId - The program ID that owns the pool account
15
+ * @returns The detected DEX type, or `null` if the owner is not a supported DEX program
16
+ *
17
+ * Supported DEX programs:
18
+ * - PumpSwap (constant-product AMM)
19
+ * - Raydium CLMM (concentrated liquidity)
20
+ * - Meteora DLMM (discretized liquidity)
21
+ */
22
+ export declare function detectDexType(ownerProgramId: PublicKey): DexType | null;
23
+ /**
24
+ * Parse a DEX pool account into a {@link DexPoolInfo} struct.
25
+ *
26
+ * @param dexType - The type of DEX (pumpswap, raydium-clmm, or meteora-dlmm)
27
+ * @param poolAddress - The on-chain address of the pool account
28
+ * @param data - Raw account data bytes
29
+ * @returns Parsed pool info including mints and (for PumpSwap) vault addresses
30
+ * @throws Error if data is too short for the given DEX type
31
+ */
32
+ export declare function parseDexPool(dexType: DexType, poolAddress: PublicKey, data: Uint8Array): DexPoolInfo;
33
+ /**
34
+ * Compute the spot price from a DEX pool in e6 format (i.e., 1.0 = 1_000_000).
35
+ *
36
+ * **SECURITY NOTE:** DEX spot prices have no staleness or confidence checks and are
37
+ * vulnerable to flash-loan manipulation within a single transaction. For high-value
38
+ * markets, prefer Pyth or Chainlink oracles.
39
+ *
40
+ * @param dexType - The type of DEX
41
+ * @param data - Raw pool account data
42
+ * @param vaultData - For PumpSwap only: base and quote vault account data
43
+ * @returns Price in e6 format (quote per base token)
44
+ * @throws Error if data is too short or computation fails
45
+ */
46
+ export declare function computeDexSpotPriceE6(dexType: DexType, data: Uint8Array, vaultData?: {
47
+ base: Uint8Array;
48
+ quote: Uint8Array;
49
+ }): bigint;