@elmntl/jlpd-sdk 0.13.6 → 0.13.7
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.
|
@@ -60,6 +60,14 @@ export interface FluidTick {
|
|
|
60
60
|
* program — byte-identical to the on-chain `derive_fluid_tick_pda` (golden-vector tested).
|
|
61
61
|
*/
|
|
62
62
|
export declare function deriveFluidTickPda(vaultId: number, tick: number): PublicKey;
|
|
63
|
+
/**
|
|
64
|
+
* Net RAW debt of a decoded position: tick-implied minus dust — the `net_debt_raw` the deployed
|
|
65
|
+
* partial-repay acceptance predicate is evaluated against (hedge_repay.rs). Supply-only positions
|
|
66
|
+
* read at the COLD tick and therefore return 0. Dust exceeding the tick-implied debt means the
|
|
67
|
+
* decoded (tick, collateral, dust) triple is inconsistent — a STRUCTURAL error the caller must
|
|
68
|
+
* treat as terminal, never as "no debt".
|
|
69
|
+
*/
|
|
70
|
+
export declare function fluidNetDebtRaw(position: FluidPosition): bigint;
|
|
63
71
|
/**
|
|
64
72
|
* The Tick account settle_yield's per-slot triple must carry for `position`: the canonical
|
|
65
73
|
* `Tick(vaultId, position.tick)` for an ACTIVE debt position, or the `Tick(vaultId, MIN_TICK)`
|
|
@@ -20,6 +20,7 @@ exports.FLUID_ZERO_TICK_SCALED_RATIO = exports.JUP_LEND_EARN_MAX_SANE_RATE = exp
|
|
|
20
20
|
exports.getRatioAtTick = getRatioAtTick;
|
|
21
21
|
exports.decodeFluidPosition = decodeFluidPosition;
|
|
22
22
|
exports.deriveFluidTickPda = deriveFluidTickPda;
|
|
23
|
+
exports.fluidNetDebtRaw = fluidNetDebtRaw;
|
|
23
24
|
exports.settleTickPdaForPosition = settleTickPdaForPosition;
|
|
24
25
|
exports.decodeFluidTick = decodeFluidTick;
|
|
25
26
|
exports.requireFluidPositionLiquidationCurrent = requireFluidPositionLiquidationCurrent;
|
|
@@ -296,6 +297,21 @@ function deriveFluidTickPda(vaultId, tick) {
|
|
|
296
297
|
normalizedLe.writeInt32LE(tick - FLUID_MIN_TICK);
|
|
297
298
|
return web3_js_1.PublicKey.findProgramAddressSync([TICK_SEED, vaultIdLe, normalizedLe], constants_1.JUP_LEND_BORROW_PROGRAM_ID)[0];
|
|
298
299
|
}
|
|
300
|
+
/**
|
|
301
|
+
* Net RAW debt of a decoded position: tick-implied minus dust — the `net_debt_raw` the deployed
|
|
302
|
+
* partial-repay acceptance predicate is evaluated against (hedge_repay.rs). Supply-only positions
|
|
303
|
+
* read at the COLD tick and therefore return 0. Dust exceeding the tick-implied debt means the
|
|
304
|
+
* decoded (tick, collateral, dust) triple is inconsistent — a STRUCTURAL error the caller must
|
|
305
|
+
* treat as terminal, never as "no debt".
|
|
306
|
+
*/
|
|
307
|
+
function fluidNetDebtRaw(position) {
|
|
308
|
+
const tick = position.isSupplyOnly ? FLUID_COLD_TICK : position.tick;
|
|
309
|
+
const tickDebt = tickImpliedDebtRaw(tick, position.collateralRaw);
|
|
310
|
+
if (position.dustDebtRaw > tickDebt) {
|
|
311
|
+
throw new Error(`fluidNetDebtRaw: dust ${position.dustDebtRaw} exceeds tick-implied debt ${tickDebt} — inconsistent position`);
|
|
312
|
+
}
|
|
313
|
+
return tickDebt - position.dustDebtRaw;
|
|
314
|
+
}
|
|
299
315
|
/**
|
|
300
316
|
* The Tick account settle_yield's per-slot triple must carry for `position`: the canonical
|
|
301
317
|
* `Tick(vaultId, position.tick)` for an ACTIVE debt position, or the `Tick(vaultId, MIN_TICK)`
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { PublicKey } from "@solana/web3.js";
|
|
11
11
|
import type { SolanaConnection } from "../common/connection";
|
|
12
12
|
import { type HedgeState, type HedgePositionSlot } from "./hedge-state";
|
|
13
|
+
import { type FluidPosition } from "./fluid-view";
|
|
13
14
|
/**
|
|
14
15
|
* Native decimals for each known hedge debt mint — mirrors `utils/oracle.rs`'s
|
|
15
16
|
* `TOKEN_DECIMALS_{BTC,ETH,SOL,JUPUSD}` constants (the four mints
|
|
@@ -62,6 +63,43 @@ export declare function fluidPartialRepayCreditRaw(xNative: bigint, debtDecimals
|
|
|
62
63
|
* minimum position debt). `netRawDebt` = tick-implied − dust (fluid-view's `netDebtRaw` basis).
|
|
63
64
|
*/
|
|
64
65
|
export declare function isPartialRepayAcceptable(xNative: bigint, netRawDebt: bigint, debtDecimals: number, borrowExPrice: bigint): boolean;
|
|
66
|
+
export interface FluidPartialRepayVerdict {
|
|
67
|
+
accepted: boolean;
|
|
68
|
+
/** Human reason when refused; null when accepted. */
|
|
69
|
+
reason: string | null;
|
|
70
|
+
creditRaw: bigint | null;
|
|
71
|
+
netRaw: bigint;
|
|
72
|
+
minDebtRaw: bigint;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The DEPLOYED acceptance predicate for a partial repay of `x`, evaluated from a decoded
|
|
76
|
+
* position, extended with the driver's balance precondition (Fluid pulls `x + 1` on payback):
|
|
77
|
+
*
|
|
78
|
+
* x ≥ MIN_OPERATE ∧ C(x) ≥ 1 ∧ netRaw ≥ C(x) + minDebtRaw ∧ debtAtaBalance ≥ x + 1
|
|
79
|
+
*
|
|
80
|
+
* An EXACT payoff must use the repay-all sentinel — the residual bound enforces that. Throws only
|
|
81
|
+
* on an INCONSISTENT position (dust > tick-implied), which is structural and terminal.
|
|
82
|
+
*/
|
|
83
|
+
export declare function evaluateFluidPartialRepay(args: {
|
|
84
|
+
xNative: bigint;
|
|
85
|
+
position: FluidPosition;
|
|
86
|
+
borrowExPrice: bigint;
|
|
87
|
+
debtDecimals: number;
|
|
88
|
+
debtAtaBalance: bigint;
|
|
89
|
+
}): FluidPartialRepayVerdict;
|
|
90
|
+
/**
|
|
91
|
+
* Largest acceptable partial repay ≤ `desiredNative` given the position AND the debt-ATA balance
|
|
92
|
+
* (Fluid's `x + 1` over-pull). Built on `maxPartialRepayNative`'s invert-and-verify, then
|
|
93
|
+
* re-verified through the full predicate so the ATA bound participates. Null = no acceptable
|
|
94
|
+
* partial exists — the caller escalates to repay-all (if fundable) or reports top-up-required.
|
|
95
|
+
*/
|
|
96
|
+
export declare function maxAcceptedFluidPartialRepay(args: {
|
|
97
|
+
desiredNative: bigint;
|
|
98
|
+
position: FluidPosition;
|
|
99
|
+
borrowExPrice: bigint;
|
|
100
|
+
debtDecimals: number;
|
|
101
|
+
debtAtaBalance: bigint;
|
|
102
|
+
}): bigint | null;
|
|
65
103
|
/**
|
|
66
104
|
* Largest partial repay (native) the on-chain acceptance admits for `netRawDebt` at the given
|
|
67
105
|
* stored price — the driver's sizing cap (anything larger reverts; anything at/below is safe at
|
|
@@ -18,6 +18,8 @@ exports.clampRepayAmount = clampRepayAmount;
|
|
|
18
18
|
exports.fluidMinDebtRaw = fluidMinDebtRaw;
|
|
19
19
|
exports.fluidPartialRepayCreditRaw = fluidPartialRepayCreditRaw;
|
|
20
20
|
exports.isPartialRepayAcceptable = isPartialRepayAcceptable;
|
|
21
|
+
exports.evaluateFluidPartialRepay = evaluateFluidPartialRepay;
|
|
22
|
+
exports.maxAcceptedFluidPartialRepay = maxAcceptedFluidPartialRepay;
|
|
21
23
|
exports.maxPartialRepayNative = maxPartialRepayNative;
|
|
22
24
|
exports.fetchHedgeSlotDerived = fetchHedgeSlotDerived;
|
|
23
25
|
exports.fetchAllHedgeSlotsDerived = fetchAllHedgeSlotsDerived;
|
|
@@ -157,6 +159,70 @@ function isPartialRepayAcceptable(xNative, netRawDebt, debtDecimals, borrowExPri
|
|
|
157
159
|
}
|
|
158
160
|
return credit >= 1n && credit + fluidMinDebtRaw(debtDecimals) <= netRawDebt;
|
|
159
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* The DEPLOYED acceptance predicate for a partial repay of `x`, evaluated from a decoded
|
|
164
|
+
* position, extended with the driver's balance precondition (Fluid pulls `x + 1` on payback):
|
|
165
|
+
*
|
|
166
|
+
* x ≥ MIN_OPERATE ∧ C(x) ≥ 1 ∧ netRaw ≥ C(x) + minDebtRaw ∧ debtAtaBalance ≥ x + 1
|
|
167
|
+
*
|
|
168
|
+
* An EXACT payoff must use the repay-all sentinel — the residual bound enforces that. Throws only
|
|
169
|
+
* on an INCONSISTENT position (dust > tick-implied), which is structural and terminal.
|
|
170
|
+
*/
|
|
171
|
+
function evaluateFluidPartialRepay(args) {
|
|
172
|
+
const netRaw = (0, fluid_view_1.fluidNetDebtRaw)(args.position);
|
|
173
|
+
const minDebtRaw = fluidMinDebtRaw(args.debtDecimals);
|
|
174
|
+
const base = { netRaw, minDebtRaw };
|
|
175
|
+
if (args.xNative < exports.FLUID_MIN_OPERATE_NATIVE) {
|
|
176
|
+
return { accepted: false, reason: `x ${args.xNative} < Fluid minimum ${exports.FLUID_MIN_OPERATE_NATIVE}`, creditRaw: null, ...base };
|
|
177
|
+
}
|
|
178
|
+
if (args.debtAtaBalance < args.xNative + 1n) {
|
|
179
|
+
return {
|
|
180
|
+
accepted: false,
|
|
181
|
+
reason: `debt ATA ${args.debtAtaBalance} < x+1 (${args.xNative + 1n}) — Fluid over-pulls one native unit`,
|
|
182
|
+
creditRaw: null,
|
|
183
|
+
...base,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
let creditRaw;
|
|
187
|
+
try {
|
|
188
|
+
creditRaw = fluidPartialRepayCreditRaw(args.xNative, args.debtDecimals, args.borrowExPrice);
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
return { accepted: false, reason: e instanceof Error ? e.message : String(e), creditRaw: null, ...base };
|
|
192
|
+
}
|
|
193
|
+
if (creditRaw < 1n || netRaw < creditRaw + minDebtRaw) {
|
|
194
|
+
return {
|
|
195
|
+
accepted: false,
|
|
196
|
+
reason: `residual bound: net ${netRaw} < credit ${creditRaw} + min ${minDebtRaw}`,
|
|
197
|
+
creditRaw,
|
|
198
|
+
...base,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
return { accepted: true, reason: null, creditRaw, ...base };
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Largest acceptable partial repay ≤ `desiredNative` given the position AND the debt-ATA balance
|
|
205
|
+
* (Fluid's `x + 1` over-pull). Built on `maxPartialRepayNative`'s invert-and-verify, then
|
|
206
|
+
* re-verified through the full predicate so the ATA bound participates. Null = no acceptable
|
|
207
|
+
* partial exists — the caller escalates to repay-all (if fundable) or reports top-up-required.
|
|
208
|
+
*/
|
|
209
|
+
function maxAcceptedFluidPartialRepay(args) {
|
|
210
|
+
const netRaw = (0, fluid_view_1.fluidNetDebtRaw)(args.position);
|
|
211
|
+
let x = maxPartialRepayNative(netRaw, args.debtDecimals, args.borrowExPrice);
|
|
212
|
+
if (x <= 0n)
|
|
213
|
+
return null;
|
|
214
|
+
if (x > args.desiredNative)
|
|
215
|
+
x = args.desiredNative;
|
|
216
|
+
if (args.debtAtaBalance < 2n)
|
|
217
|
+
return null;
|
|
218
|
+
if (x > args.debtAtaBalance - 1n)
|
|
219
|
+
x = args.debtAtaBalance - 1n;
|
|
220
|
+
for (let i = 0; i < 4 && x >= exports.FLUID_MIN_OPERATE_NATIVE; i++, x -= 1n) {
|
|
221
|
+
if (evaluateFluidPartialRepay({ ...args, xNative: x }).accepted)
|
|
222
|
+
return x;
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
160
226
|
/**
|
|
161
227
|
* Largest partial repay (native) the on-chain acceptance admits for `netRawDebt` at the given
|
|
162
228
|
* stored price — the driver's sizing cap (anything larger reverts; anything at/below is safe at
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elmntl/jlpd-sdk",
|
|
3
|
-
"version": "0.13.
|
|
4
|
-
"description": "SDK for JLP.D (JLP Deconstructed) by Elemental
|
|
3
|
+
"version": "0.13.7",
|
|
4
|
+
"description": "SDK for JLP.D (JLP Deconstructed) by Elemental — p-stv-core, jlpd-strategy, and elemental-lend clients",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"solana",
|
|
@@ -70,4 +70,4 @@
|
|
|
70
70
|
"publishConfig": {
|
|
71
71
|
"access": "public"
|
|
72
72
|
}
|
|
73
|
-
}
|
|
73
|
+
}
|