@xoxno/sdk-js 1.0.217 → 1.0.219
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/dist/cjs/sdk/stellar/__tests__/auth-policy.test.d.ts +1 -0
- package/dist/cjs/sdk/stellar/__tests__/math/account.test.d.ts +10 -0
- package/dist/cjs/sdk/stellar/__tests__/math/parity.test.d.ts +14 -0
- package/dist/cjs/sdk/stellar/__tests__/math/scaled-rates.test.d.ts +6 -0
- package/dist/cjs/sdk/stellar/__tests__/route-verify.test.d.ts +1 -0
- package/dist/cjs/sdk/stellar/auth-policy.d.ts +106 -0
- package/dist/cjs/sdk/stellar/contracts.d.ts +6 -0
- package/dist/cjs/sdk/stellar/index.d.ts +3 -0
- package/dist/cjs/sdk/stellar/math/account.d.ts +183 -0
- package/dist/cjs/sdk/stellar/math/index.d.ts +12 -0
- package/dist/cjs/sdk/stellar/math/limits.d.ts +80 -0
- package/dist/cjs/sdk/stellar/math/projection.d.ts +53 -0
- package/dist/cjs/sdk/stellar/math/rates.d.ts +34 -0
- package/dist/cjs/sdk/stellar/math/scaled.d.ts +99 -0
- package/dist/cjs/sdk/stellar/route-verify.d.ts +100 -0
- package/dist/cjs/sdk/stellar/scval-encode.d.ts +10 -0
- package/dist/cjs/sdk/stellar/swap.d.ts +26 -6
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/sdk/stellar/__tests__/auth-policy.test.d.ts +1 -0
- package/dist/sdk/stellar/__tests__/math/account.test.d.ts +10 -0
- package/dist/sdk/stellar/__tests__/math/parity.test.d.ts +14 -0
- package/dist/sdk/stellar/__tests__/math/scaled-rates.test.d.ts +6 -0
- package/dist/sdk/stellar/__tests__/route-verify.test.d.ts +1 -0
- package/dist/sdk/stellar/auth-policy.d.ts +106 -0
- package/dist/sdk/stellar/contracts.d.ts +6 -0
- package/dist/sdk/stellar/index.cjs +1 -1
- package/dist/sdk/stellar/index.d.ts +3 -0
- package/dist/sdk/stellar/index.esm.js +1 -1
- package/dist/sdk/stellar/math/account.d.ts +183 -0
- package/dist/sdk/stellar/math/index.d.ts +12 -0
- package/dist/sdk/stellar/math/limits.d.ts +80 -0
- package/dist/sdk/stellar/math/projection.d.ts +53 -0
- package/dist/sdk/stellar/math/rates.d.ts +34 -0
- package/dist/sdk/stellar/math/scaled.d.ts +99 -0
- package/dist/sdk/stellar/route-verify.d.ts +100 -0
- package/dist/sdk/stellar/scval-encode.d.ts +10 -0
- package/dist/sdk/stellar/swap.d.ts +26 -6
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixture-driven tests for `computeAccountRisk`, the limit helpers and the
|
|
3
|
+
* projections. Expected health factors are worked by hand from
|
|
4
|
+
* `rs-lending-xlm/docs/reference/formulas.md` §Valuation and health:
|
|
5
|
+
* value_usd = round(round(round(shares·index/RAY)/1e9)·price/WAD)
|
|
6
|
+
* ltv_collateral = Σ floor(floor-value · min(ltv, threshold) / BPS)
|
|
7
|
+
* weighted_collateral = Σ floor(floor-value · threshold / BPS)
|
|
8
|
+
* debt = Σ ceil-value; HF = floor(weighted_collateral · WAD / debt)
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parity between the SDK math and the xoxno-ui mirrors it replaces. The UI
|
|
3
|
+
* functions are re-implemented inline below as the oracle, ported verbatim
|
|
4
|
+
* (BigNumber integer ops → BigInt, floats kept as floats) from:
|
|
5
|
+
* - xoxno-ui/src/modules/lending/stellar-scaled-math.ts
|
|
6
|
+
* (`unscaleSupplyFloor`, `unscaleBorrowCeil`)
|
|
7
|
+
* - xoxno-ui/src/modules/lending/utils.ts
|
|
8
|
+
* (`getBorrowApy`, `getSupplyApy`, `getNewCollateral` + `getHealthPercentage`,
|
|
9
|
+
* `calculateMaxBorrow` cap headroom)
|
|
10
|
+
* - xoxno-ui/src/modules/lending/stellar-limits.ts
|
|
11
|
+
* (`resolveMaxUtilization`, `utilCapBorrowUsd`, `utilCapWithdrawUsd`)
|
|
12
|
+
* Inputs are random but deterministic (mulberry32, fixed seed).
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-signing allowlist for the Soroban authorization tree of a PREPARED
|
|
3
|
+
* transaction.
|
|
4
|
+
*
|
|
5
|
+
* Simulation records every `require_auth` that code on the call stack makes
|
|
6
|
+
* for the caller — including one made by a rogue pool or token inside a swap
|
|
7
|
+
* hop, e.g. `any_token.transfer(caller, attacker, x)`. The wallet signature
|
|
8
|
+
* covers the whole tree, so whatever `prepareTransaction` returns is what the
|
|
9
|
+
* user authorizes. Each policy below is the set of sub-invocations the
|
|
10
|
+
* contracts legitimately request from the caller, with the source that proves
|
|
11
|
+
* it (paths are in the `rs-lending-xlm` repository):
|
|
12
|
+
*
|
|
13
|
+
* | Entrypoint | Caller sub-invocations | Source |
|
|
14
|
+
* |---|---|---|
|
|
15
|
+
* | controller `supply` | `asset.transfer(caller, pool, <= amount)` per asset | `contracts/controller/src/positions/supply.rs:118` |
|
|
16
|
+
* | controller `repay` | `asset.transfer(caller, pool, <= amount)` per asset | `contracts/controller/src/positions/debt.rs:144` |
|
|
17
|
+
* | controller `liquidate` | `asset.transfer(liquidator, pool, <= payment)` per asset | `contracts/controller/src/positions/liquidation/apply.rs:50` |
|
|
18
|
+
* | controller `multiply` | `asset.transfer(caller, controller, <= initial_payment)` when present | `contracts/controller/src/strategies/multiply.rs:169` |
|
|
19
|
+
* | controller `migrate_from_blend` | `blend_pool.submit(caller, controller, controller, [withdraw / withdraw_collateral / repay])`, at most twice | `contracts/controller/src/external/blend.rs:46-97` |
|
|
20
|
+
* | controller `borrow`, `withdraw`, `swap_debt`, `swap_collateral`, `repay_debt_with_collateral`, `flash_loan`, `flash_position`, any other | none | the controller funds these from its own custody (`strategies/legs.rs:50`, `strategies/flash_position.rs:285`) |
|
|
21
|
+
* | governance, any | none | proposer auth only |
|
|
22
|
+
* | router `execute_strategy` | `token_in.transfer(sender, router, <= total_in)` | `contracts/swap-aggregator/src/execute/mod.rs:81` |
|
|
23
|
+
*/
|
|
24
|
+
import { xdr } from '@stellar/stellar-sdk';
|
|
25
|
+
import { type StellarNetwork } from './contracts.js';
|
|
26
|
+
export type StellarAuthPolicyErrorCode = 'MALFORMED_TRANSACTION' | 'INVOCATION_CHANGED' | 'UNSUPPORTED_CONTRACT' | 'CALLER_MISMATCH' | 'DUPLICATE_ENTRY' | 'UNEXPECTED_ROOT' | 'UNEXPECTED_CALL' | 'NESTED_INVOCATION' | 'UNEXPECTED_TOKEN' | 'UNEXPECTED_SENDER' | 'UNEXPECTED_RECIPIENT' | 'AMOUNT_EXCEEDED';
|
|
27
|
+
/** The invocation that broke the policy, in readable form. */
|
|
28
|
+
export interface StellarAuthOffender {
|
|
29
|
+
contract: string;
|
|
30
|
+
fn: string;
|
|
31
|
+
args: string[];
|
|
32
|
+
}
|
|
33
|
+
/** Thrown when the authorization tree holds anything the policy does not allow. */
|
|
34
|
+
export declare class StellarAuthPolicyError extends Error {
|
|
35
|
+
readonly code: StellarAuthPolicyErrorCode;
|
|
36
|
+
readonly offender?: StellarAuthOffender;
|
|
37
|
+
constructor(code: StellarAuthPolicyErrorCode, message: string, offender?: StellarAuthOffender);
|
|
38
|
+
}
|
|
39
|
+
/** Total the caller may send to `to` in `token` across all sub-invocations. */
|
|
40
|
+
export interface StellarAuthTransferAllowance {
|
|
41
|
+
token: string;
|
|
42
|
+
to: string;
|
|
43
|
+
maxAmount: bigint;
|
|
44
|
+
}
|
|
45
|
+
/** A non-transfer sub-invocation the caller legitimately authorizes. */
|
|
46
|
+
export interface StellarAuthCallAllowance {
|
|
47
|
+
contract: string;
|
|
48
|
+
fn: string;
|
|
49
|
+
maxCount: number;
|
|
50
|
+
/** Returns why `args` are not acceptable, or `undefined` when they are. */
|
|
51
|
+
reject: (args: xdr.ScVal[]) => string | undefined;
|
|
52
|
+
}
|
|
53
|
+
export interface StellarAuthPolicy {
|
|
54
|
+
caller: string;
|
|
55
|
+
root: {
|
|
56
|
+
contract: string;
|
|
57
|
+
fn: string;
|
|
58
|
+
};
|
|
59
|
+
transfers: StellarAuthTransferAllowance[];
|
|
60
|
+
calls?: StellarAuthCallAllowance[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Enforce `policy` on the authorization entries of a prepared transaction.
|
|
64
|
+
* Entries that belong to other addresses are ignored. For the caller: at most
|
|
65
|
+
* one entry, the expected root, and only allowlisted leaf sub-invocations.
|
|
66
|
+
* @param sourceAccount - Account that source-account credentials stand for
|
|
67
|
+
* (the operation source, else the transaction source). When omitted, such
|
|
68
|
+
* entries are treated as the caller's.
|
|
69
|
+
* @throws StellarAuthPolicyError
|
|
70
|
+
*/
|
|
71
|
+
export declare function assertStellarAuthEntries(entries: readonly xdr.SorobanAuthorizationEntry[], policy: StellarAuthPolicy, sourceAccount?: string): void;
|
|
72
|
+
/** Contract addresses a policy is derived against. */
|
|
73
|
+
export interface StellarAuthDeployment {
|
|
74
|
+
lendingController: string;
|
|
75
|
+
lendingPool: string;
|
|
76
|
+
aggregatorRouter: string;
|
|
77
|
+
governance: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Derive the policy for one contract invocation from its own arguments. The
|
|
81
|
+
* arguments are what the application built from the user's intent, so the
|
|
82
|
+
* allowed transfers are bounded by exactly what the user asked to pay.
|
|
83
|
+
* @throws StellarAuthPolicyError for a contract outside `deployment`.
|
|
84
|
+
*/
|
|
85
|
+
export declare function stellarAuthPolicyForInvocation(caller: string, invocation: xdr.InvokeContractArgs, deployment: StellarAuthDeployment): StellarAuthPolicy;
|
|
86
|
+
export interface StellarPreparedTxAuthOptions {
|
|
87
|
+
network: StellarNetwork;
|
|
88
|
+
/** The wallet address that will sign. */
|
|
89
|
+
caller: string;
|
|
90
|
+
/**
|
|
91
|
+
* The unsigned builder output that was sent to `prepareTransaction`. When
|
|
92
|
+
* given, the prepared invocation must be byte-identical to it.
|
|
93
|
+
*/
|
|
94
|
+
builtXdr?: string;
|
|
95
|
+
/** Replaces the derived policy (other contracts, custom flash receivers). */
|
|
96
|
+
policy?: StellarAuthPolicy;
|
|
97
|
+
/** Overrides manifest addresses for a non-standard deployment. */
|
|
98
|
+
deployment?: Partial<StellarAuthDeployment>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check a prepared transaction (base64 XDR, as `prepareTransaction` returns
|
|
102
|
+
* it) before it is passed to a wallet. Takes XDR strings so no live SDK object
|
|
103
|
+
* crosses the consumer's bundle boundary. Never sign when this throws.
|
|
104
|
+
* @throws StellarAuthPolicyError | StellarRouteVerificationError
|
|
105
|
+
*/
|
|
106
|
+
export declare function assertStellarPreparedTxAuth(preparedXdr: string, opts: StellarPreparedTxAuthOptions): void;
|
|
@@ -20,6 +20,8 @@ export interface StellarDeployment {
|
|
|
20
20
|
readonly aquariusApiUrl: string;
|
|
21
21
|
readonly aquariusRouter: string;
|
|
22
22
|
readonly lendingController: string;
|
|
23
|
+
/** The controller's single liquidity pool: the recipient of supply, repay and liquidation transfers. */
|
|
24
|
+
readonly lendingPool: string;
|
|
23
25
|
readonly aggregatorRouter: string;
|
|
24
26
|
readonly governance: string;
|
|
25
27
|
readonly priceAggregator: string;
|
|
@@ -41,6 +43,7 @@ export declare const STELLAR_NETWORKS: {
|
|
|
41
43
|
readonly aquariusApiUrl: "https://amm-api.aqua.network";
|
|
42
44
|
readonly aquariusRouter: "CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK";
|
|
43
45
|
readonly lendingController: "CAUCMIN5KSXEVZ7NMXR3LZATGD5EFIEUI5XWTFLYRO2R5OTXI22WE5JX";
|
|
46
|
+
readonly lendingPool: "CBXRNDQMAJFG4VUKMKFEMFS75UUXE2SPSNV4LEEFCSNBCN66PYRWBKXO";
|
|
44
47
|
readonly aggregatorRouter: "CCVENFSVCBYDHVOACFZXMNNYVOZ3LKXPZYU5LUI4N7KTXOKRVYD7F3TR";
|
|
45
48
|
readonly governance: "CC44PEQW7HSEPKAZ5ZRPH2JS5M5KVJXCUBLJ2ZX4E3WCDKMNFILHC2AD";
|
|
46
49
|
readonly priceAggregator: "CBGUF2G2Q7HCVCWYISDXBHPVBGMYNXA7PG2VET66YBZX6IKOOV27NSMV";
|
|
@@ -56,6 +59,7 @@ export declare const STELLAR_NETWORKS: {
|
|
|
56
59
|
readonly aquariusApiUrl: "https://amm-api-testnet.aqua.network";
|
|
57
60
|
readonly aquariusRouter: "CBCFTQSPDBAIZ6R6PJQKSQWKNKWH2QIV3I4J72SHWBIK3ADRRAM5A6GD";
|
|
58
61
|
readonly lendingController: "CCXRWJ6SIU2WPFEGLFGJVITPL57QAYIMIO6OAM2NBGNDQSSCK2FFV3F3";
|
|
62
|
+
readonly lendingPool: "CBSGF6QOQAMPFBEVSYPEQHSZRIHJ6RCGUPCRDMUX36DEKRWFAO2PZB5A";
|
|
59
63
|
readonly aggregatorRouter: "CDNTWMWW2WGYTKIZTJYNGNVQQZI4KTC5BQRZ3275KESRX5T4O3AYECL5";
|
|
60
64
|
readonly governance: "CDS33JDOYH3F3FL4QUQ6DV4WKHML2AKHF4LADTZ57FRUAEBTE7NMY5FQ";
|
|
61
65
|
readonly priceAggregator: "CAALOOTIDXCX7D7FMQIBSSAJLPKOM3GMXS4UUSTDIG42JCRRJHOPUHOP";
|
|
@@ -74,6 +78,7 @@ export declare const stellarNetworks: {
|
|
|
74
78
|
readonly aquariusApiUrl: "https://amm-api.aqua.network";
|
|
75
79
|
readonly aquariusRouter: "CBQDHNBFBZYE4MKPWBSJOPIYLW4SFSXAXUTSXJN76GNKYVYPCKWC6QUK";
|
|
76
80
|
readonly lendingController: "CAUCMIN5KSXEVZ7NMXR3LZATGD5EFIEUI5XWTFLYRO2R5OTXI22WE5JX";
|
|
81
|
+
readonly lendingPool: "CBXRNDQMAJFG4VUKMKFEMFS75UUXE2SPSNV4LEEFCSNBCN66PYRWBKXO";
|
|
77
82
|
readonly aggregatorRouter: "CCVENFSVCBYDHVOACFZXMNNYVOZ3LKXPZYU5LUI4N7KTXOKRVYD7F3TR";
|
|
78
83
|
readonly governance: "CC44PEQW7HSEPKAZ5ZRPH2JS5M5KVJXCUBLJ2ZX4E3WCDKMNFILHC2AD";
|
|
79
84
|
readonly priceAggregator: "CBGUF2G2Q7HCVCWYISDXBHPVBGMYNXA7PG2VET66YBZX6IKOOV27NSMV";
|
|
@@ -89,6 +94,7 @@ export declare const stellarNetworks: {
|
|
|
89
94
|
readonly aquariusApiUrl: "https://amm-api-testnet.aqua.network";
|
|
90
95
|
readonly aquariusRouter: "CBCFTQSPDBAIZ6R6PJQKSQWKNKWH2QIV3I4J72SHWBIK3ADRRAM5A6GD";
|
|
91
96
|
readonly lendingController: "CCXRWJ6SIU2WPFEGLFGJVITPL57QAYIMIO6OAM2NBGNDQSSCK2FFV3F3";
|
|
97
|
+
readonly lendingPool: "CBSGF6QOQAMPFBEVSYPEQHSZRIHJ6RCGUPCRDMUX36DEKRWFAO2PZB5A";
|
|
92
98
|
readonly aggregatorRouter: "CDNTWMWW2WGYTKIZTJYNGNVQQZI4KTC5BQRZ3275KESRX5T4O3AYECL5";
|
|
93
99
|
readonly governance: "CDS33JDOYH3F3FL4QUQ6DV4WKHML2AKHF4LADTZ57FRUAEBTE7NMY5FQ";
|
|
94
100
|
readonly priceAggregator: "CAALOOTIDXCX7D7FMQIBSSAJLPKOM3GMXS4UUSTDIG42JCRRJHOPUHOP";
|
|
@@ -14,9 +14,12 @@ export * from './governance.js';
|
|
|
14
14
|
export * from './events/index.js';
|
|
15
15
|
export * from './quote.js';
|
|
16
16
|
export * from './swap.js';
|
|
17
|
+
export * from './route-verify.js';
|
|
18
|
+
export * from './auth-policy.js';
|
|
17
19
|
export * from './prepare.js';
|
|
18
20
|
export * from './position-mode.js';
|
|
19
21
|
export * from './repay-swap.js';
|
|
20
22
|
export * from './cctp.js';
|
|
23
|
+
export * from './math/index.js';
|
|
21
24
|
export type { StellarStrategySwapInput, StellarStrategySwapPathInput, StellarStrategySwapHopInput, } from './scval-encode.js';
|
|
22
25
|
export { mapSorobanError } from '../../utils/soroban-errors.js';
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Account valuation and risk — the client mirror of
|
|
3
|
+
* `rs-lending-xlm/contracts/controller/src/risk/totals.rs`
|
|
4
|
+
* (`calculate_account_risk_totals`) and the post-action gate in
|
|
5
|
+
* `contracts/controller/src/risk/validation.rs` (`require_post_pool_risk_gates`),
|
|
6
|
+
* as specified in `docs/reference/formulas.md` §Valuation and health.
|
|
7
|
+
*
|
|
8
|
+
* Inputs are the generated API DTOs; all integer math runs in `BigInt` on the
|
|
9
|
+
* raw string fields. Outputs carry both the raw WAD strings and display numbers.
|
|
10
|
+
*/
|
|
11
|
+
import type { AccountPositionDto, ReserveDto, StellarLendingLiveStateDto, StellarMarketIndexByHub } from '../lending-api-types.js';
|
|
12
|
+
/**
|
|
13
|
+
* `common/src/constants/shared.rs::DEFAULT_MIN_BORROW_COLLATERAL_USD_WAD` —
|
|
14
|
+
* the floor `get_min_borrow_collateral_usd` returns until governance changes
|
|
15
|
+
* it. Used only when `liveState.minBorrowCollateralUsdWad` is absent or null.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_MIN_BORROW_COLLATERAL_USD_WAD: bigint;
|
|
18
|
+
export type PositionSide = 'supply' | 'borrow';
|
|
19
|
+
/** One valued hub-asset leg of an account. */
|
|
20
|
+
export type PositionValue = {
|
|
21
|
+
side: PositionSide;
|
|
22
|
+
hubId: number;
|
|
23
|
+
asset: string;
|
|
24
|
+
decimals: number;
|
|
25
|
+
/** Scaled shares (RAY) the valuation started from. */
|
|
26
|
+
scaledRay: string;
|
|
27
|
+
/** Index (RAY) applied to the shares. */
|
|
28
|
+
indexRay: string;
|
|
29
|
+
/** USD price per whole token (WAD) applied. */
|
|
30
|
+
usdPriceWad: string;
|
|
31
|
+
usdPrice: number;
|
|
32
|
+
/** Token base units: supply = `unscale_supply_floor`, borrow = `unscale_borrow_ceil`. */
|
|
33
|
+
amountBase: string;
|
|
34
|
+
amountShort: number;
|
|
35
|
+
/** USD (WAD): supply = half-up `position_value` (feeds `total_collateral`); borrow = `position_value_ceil` (risk debt). */
|
|
36
|
+
usdWad: string;
|
|
37
|
+
usd: number;
|
|
38
|
+
/** USD (WAD) the risk gates weight: supply = `position_value_floor`; borrow = same as `usdWad`. */
|
|
39
|
+
gateUsdWad: string;
|
|
40
|
+
entryLtvBps: number;
|
|
41
|
+
entryLiquidationThresholdBps: number;
|
|
42
|
+
entryLiquidationBonusBps: number;
|
|
43
|
+
entryLiquidationFeesBps: number;
|
|
44
|
+
/** Supplies: `floor(gateUsd * min(entryLtv, entryThreshold) / BPS)`; borrows: "0". */
|
|
45
|
+
ltvWeightedUsdWad: string;
|
|
46
|
+
/** Supplies: `floor(gateUsd * entryThreshold / BPS)`; borrows: "0". */
|
|
47
|
+
liquidationWeightedUsdWad: string;
|
|
48
|
+
/** Both the index and the price came from an accepted live-state row. */
|
|
49
|
+
liveDataUsed: boolean;
|
|
50
|
+
};
|
|
51
|
+
export type AccountRisk = {
|
|
52
|
+
accountId: string;
|
|
53
|
+
owner: string;
|
|
54
|
+
spokeId: number;
|
|
55
|
+
/** `AccountPositionDto.positionMode`: 0 normal, 1 multiply, 2 long, 3 short. */
|
|
56
|
+
mode: number;
|
|
57
|
+
supplies: PositionValue[];
|
|
58
|
+
borrows: PositionValue[];
|
|
59
|
+
/** Unweighted collateral, half-up (`AccountRiskTotals.total_collateral`). */
|
|
60
|
+
totalCollateralUsdWad: string;
|
|
61
|
+
totalCollateralUsd: number;
|
|
62
|
+
/** Floor-valued collateral weighted by stored `min(LTV, threshold)` (`ltv_collateral`). */
|
|
63
|
+
ltvWeightedCollateralUsdWad: string;
|
|
64
|
+
ltvWeightedCollateralUsd: number;
|
|
65
|
+
/** Floor-valued collateral weighted by stored liquidation threshold (`weighted_collateral`). */
|
|
66
|
+
liquidationWeightedCollateralUsdWad: string;
|
|
67
|
+
liquidationWeightedCollateralUsd: number;
|
|
68
|
+
/** Ceil-valued debt (`total_debt`). */
|
|
69
|
+
totalDebtUsdWad: string;
|
|
70
|
+
totalDebtUsd: number;
|
|
71
|
+
/** Contract convention `floor(weighted_collateral * WAD / debt)`; null when debt-free (the contract returns `i128::MAX`). */
|
|
72
|
+
healthFactorWad: string | null;
|
|
73
|
+
healthFactor: number | null;
|
|
74
|
+
/** UI convention: `debt / threshold-weighted collateral × 100`; higher is riskier, 0 without collateral. */
|
|
75
|
+
healthPercent: number;
|
|
76
|
+
/** Debt the LTV gate admits: `ltv_collateral` (`require_post_pool_risk_gates`). */
|
|
77
|
+
borrowLimitUsdWad: string;
|
|
78
|
+
borrowLimitUsd: number;
|
|
79
|
+
/** `max(0, borrowLimit - debt)`; 0 when `ltv_collateral` is below the min-collateral floor. */
|
|
80
|
+
availableBorrowUsdWad: string;
|
|
81
|
+
availableBorrowUsd: number;
|
|
82
|
+
/** Floor applied while debt remains (`get_min_borrow_collateral_usd`). */
|
|
83
|
+
minBorrowCollateralUsdWad: string;
|
|
84
|
+
minBorrowCollateralUsd: number;
|
|
85
|
+
/** Debt > 0 and health factor < 1 WAD. */
|
|
86
|
+
isLiquidatable: boolean;
|
|
87
|
+
/** `liveState` was supplied and every position was valued from an accepted live row. */
|
|
88
|
+
liveDataValid: boolean;
|
|
89
|
+
};
|
|
90
|
+
export type ReserveInput = Record<string, ReserveDto> | ReserveDto[];
|
|
91
|
+
/** Key used by `StellarLendingContextDto.reserveDetailsByKey`. */
|
|
92
|
+
export declare const reserveKey: (spokeId: number, hubId: number, asset: string) => string;
|
|
93
|
+
/** Find the reserve backing a `(spokeId, hubId, asset)` coordinate; throws when missing. */
|
|
94
|
+
export declare function resolveReserve(reserves: ReserveInput, spokeId: number, hubId: number, asset: string): ReserveDto;
|
|
95
|
+
export type LiveSnapshot = {
|
|
96
|
+
/** Accepted index rows keyed `${hubId}:${asset}`. */
|
|
97
|
+
indexes: Map<string, StellarMarketIndexByHub>;
|
|
98
|
+
/** Accepted USD prices (WAD) keyed by asset. */
|
|
99
|
+
prices: Map<string, bigint>;
|
|
100
|
+
rejectedIndexKeys: Set<string>;
|
|
101
|
+
rejectedAssets: Set<string>;
|
|
102
|
+
};
|
|
103
|
+
export declare const liveIndexKey: (hubId: number, asset: string) => string;
|
|
104
|
+
/** Build the divergence-guarded snapshot from `liveState.indexes`. */
|
|
105
|
+
export declare function buildLiveSnapshot(liveState: StellarLendingLiveStateDto | undefined): LiveSnapshot;
|
|
106
|
+
type PositionSeed = {
|
|
107
|
+
side: PositionSide;
|
|
108
|
+
hubId: number;
|
|
109
|
+
asset: string;
|
|
110
|
+
decimals: number;
|
|
111
|
+
scaledRay: bigint;
|
|
112
|
+
indexRay: bigint;
|
|
113
|
+
priceWad: bigint;
|
|
114
|
+
entryLtvBps: number;
|
|
115
|
+
entryLiquidationThresholdBps: number;
|
|
116
|
+
entryLiquidationBonusBps: number;
|
|
117
|
+
entryLiquidationFeesBps: number;
|
|
118
|
+
liveDataUsed: boolean;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Value scaled shares exactly as `calculate_account_risk_totals` does:
|
|
122
|
+
* supply → `position_value` (half-up display/total) and
|
|
123
|
+
* `position_value_floor` (gate); borrow → `position_value_ceil`.
|
|
124
|
+
*/
|
|
125
|
+
export declare function valueScaledPosition(seed: PositionSeed): PositionValue;
|
|
126
|
+
/**
|
|
127
|
+
* Shares minted when `amountBase` enters a leg — `scaling.rs`
|
|
128
|
+
* `calculate_scaled_supply` (floor) / `calculate_scaled_borrow` (ceil), the
|
|
129
|
+
* only entry conversions the pool performs (`ops/supply.rs`, `ops/borrow.rs`).
|
|
130
|
+
*/
|
|
131
|
+
export declare function mintShares(side: PositionSide, amountBase: bigint, decimals: number, indexRay: bigint): bigint;
|
|
132
|
+
/**
|
|
133
|
+
* Shares burned by a partial exit of `amountBase` — `formulas.md` rounding
|
|
134
|
+
* table: "Partial withdrawal burn: ceil", "Partial repayment burn: floor".
|
|
135
|
+
* Full closes (withdraw ≥ half-up balance, repay ≥ ceil debt) burn every
|
|
136
|
+
* share instead; callers handle that boundary.
|
|
137
|
+
*/
|
|
138
|
+
export declare function burnShares(side: PositionSide, amountBase: bigint, decimals: number, indexRay: bigint): bigint;
|
|
139
|
+
/**
|
|
140
|
+
* Value a fresh leg entered with `amountBase`: shares come from
|
|
141
|
+
* {@link mintShares} and the valuation runs on those shares exactly as
|
|
142
|
+
* {@link valueScaledPosition} does (`position_value_*` never sees the base
|
|
143
|
+
* amount). `amountBase` on the result is therefore the share-derived balance,
|
|
144
|
+
* which can differ from the input by rounding when the index is not `RAY`.
|
|
145
|
+
*/
|
|
146
|
+
export declare function valueBasePosition(seed: Omit<PositionSeed, 'scaledRay'>, amountBase: bigint): PositionValue;
|
|
147
|
+
export type AccountIdentity = Pick<AccountRisk, 'accountId' | 'owner' | 'spokeId' | 'mode'>;
|
|
148
|
+
/**
|
|
149
|
+
* Fold valued positions into the contract's risk totals and the derived
|
|
150
|
+
* limits. `formulas.md`: `health_factor_wad = floor(weighted_collateral_wad *
|
|
151
|
+
* WAD / debt_wad)`; the post-action gate requires `debt <= ltv_collateral`,
|
|
152
|
+
* `HF >= 1 WAD` and `ltv_collateral >= floor` while debt remains.
|
|
153
|
+
*/
|
|
154
|
+
export declare function aggregateAccountRisk(identity: AccountIdentity, supplies: PositionValue[], borrows: PositionValue[], minBorrowCollateralUsdWad: bigint, liveDataValid: boolean): AccountRisk;
|
|
155
|
+
export type ComputeAccountRiskInput = {
|
|
156
|
+
/** Every row of one account (same `accountId`), as `/accounts/{id}/positions` returns them. */
|
|
157
|
+
positions: AccountPositionDto[];
|
|
158
|
+
/** `context.reserveDetailsByKey` (keyed `${spokeId}:${hubId}:${asset}`) or a reserve array. */
|
|
159
|
+
reserves: ReserveInput;
|
|
160
|
+
/** `/live-state`: live indexes, prices and the min-collateral floor. Optional; positions then fall back to their own applied indexes and the reserve price. */
|
|
161
|
+
liveState?: StellarLendingLiveStateDto;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Value one account exactly as the controller's risk gates do.
|
|
165
|
+
*
|
|
166
|
+
* - Index: live-state row for `(hubId, asset)` when it passed the divergence
|
|
167
|
+
* guard, else the position's `liveSupplyIndexRay` / `liveBorrowIndexRay`,
|
|
168
|
+
* then the reserve's `live*IndexRay`, and last the position's stored
|
|
169
|
+
* `supplyIndexRay` / `borrowIndexRay` (undocumented in the API schema; the
|
|
170
|
+
* documented fields say "stored index on fallback", so it is treated as the
|
|
171
|
+
* stalest snapshot). The contract accrues to the current index before every
|
|
172
|
+
* gate, so fresher candidates win.
|
|
173
|
+
* - Price: live-state `usdPrice` (WAD) when accepted, else `reserve.usdPrice`
|
|
174
|
+
* converted to WAD.
|
|
175
|
+
* - Weights: the position's own `entry*Bps`, as the contract stores them.
|
|
176
|
+
* - Min-collateral floor: `liveState.minBorrowCollateralUsdWad`, defaulting
|
|
177
|
+
* to 5 USD (`DEFAULT_MIN_BORROW_COLLATERAL_USD_WAD`) when absent or null.
|
|
178
|
+
*
|
|
179
|
+
* Throws when `positions` is empty, mixes account ids, or names a reserve
|
|
180
|
+
* missing from `reserves`.
|
|
181
|
+
*/
|
|
182
|
+
export declare function computeAccountRisk(input: ComputeAccountRiskInput): AccountRisk;
|
|
183
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stellar lending math: contract-exact share conversion, rate curve, account
|
|
3
|
+
* risk, per-action limits and projections in native `BigInt`. Ground truth is
|
|
4
|
+
* `rs-lending-xlm` (`common/src/rates/*.rs`, `contracts/controller/src/risk/*`,
|
|
5
|
+
* `docs/reference/formulas.md`); each function names the source it mirrors.
|
|
6
|
+
* @module Stellar lending math
|
|
7
|
+
*/
|
|
8
|
+
export * from './scaled.js';
|
|
9
|
+
export * from './rates.js';
|
|
10
|
+
export * from './account.js';
|
|
11
|
+
export * from './limits.js';
|
|
12
|
+
export * from './projection.js';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Largest valid amount per action, in token base units, with the reason that
|
|
3
|
+
* binds. Mirrors the gates the contract enforces rather than capping:
|
|
4
|
+
*
|
|
5
|
+
* - risk gate: `contracts/controller/src/risk/validation.rs`
|
|
6
|
+
* `require_post_pool_risk_gates` (debt ≤ `min(LTV, threshold)`-weighted
|
|
7
|
+
* collateral, HF ≥ 1, `ltv_collateral ≥ min_borrow_collateral_usd`);
|
|
8
|
+
* - listing flags: `positions/mod.rs::enforce_spoke_asset_flags`
|
|
9
|
+
* (`paused` blocks every verb, `frozen` blocks entry only);
|
|
10
|
+
* - spoke caps: `formulas.md` §Caps (native units, no unlimited sentinel);
|
|
11
|
+
* - pool cash and the 200 BPS liquidation buffer:
|
|
12
|
+
* `contracts/pool/src/guards.rs::require_liquidation_buffer`;
|
|
13
|
+
* - hub utilization ceiling: `guards.rs::require_utilization_below_max`
|
|
14
|
+
* (skipped when `max_utilization >= RAY`; repay and liquidation are exempt).
|
|
15
|
+
*
|
|
16
|
+
* Hub totals and pool cash come from the `*Short` display numbers on
|
|
17
|
+
* `ReserveDto`, so cap / liquidity / utilization candidates are exact only to
|
|
18
|
+
* the double precision of those fields; the risk-gate candidate is exact.
|
|
19
|
+
* xoxno-ui parity: `stellar-limits.ts::computeStellarLimits`,
|
|
20
|
+
* `utils.ts::calculateMaxBorrow / calculateMaxSupply / calculateMaxRemove`.
|
|
21
|
+
*/
|
|
22
|
+
import type { ReserveDto, StellarLendingLiveStateDto } from '../lending-api-types.js';
|
|
23
|
+
import type { AccountRisk } from './account.js';
|
|
24
|
+
export type LimitReason = 'healthFactor' | 'ltv' | 'cap' | 'liquidity' | 'utilizationCap' | 'minCollateral' | 'paused' | 'frozen' | null;
|
|
25
|
+
export type Limit = {
|
|
26
|
+
/** Token base units. For a full-close withdraw this is the floor payout; sign `"0"` (the builder's withdraw-all sentinel) instead. */
|
|
27
|
+
amountBase: bigint;
|
|
28
|
+
amountShort: number;
|
|
29
|
+
/** The amount closes the whole position (withdraw: pays the floor balance; repay: burns every debt share and refunds excess). */
|
|
30
|
+
isFullClose: boolean;
|
|
31
|
+
/** Which gate produced `amountBase`; null when only the position size bounds it. */
|
|
32
|
+
reason: LimitReason;
|
|
33
|
+
/** Hub utilization (0..1) after the action at `amountBase`; null when not applicable. */
|
|
34
|
+
projectedUtilization: number | null;
|
|
35
|
+
};
|
|
36
|
+
/** `common/src/constants/pool.rs::LIQUIDATION_BUFFER_BPS`. */
|
|
37
|
+
export declare const LIQUIDATION_BUFFER_BPS = 200n;
|
|
38
|
+
/**
|
|
39
|
+
* Largest borrow of `reserve` the account can open now. Candidates, all in
|
|
40
|
+
* base units:
|
|
41
|
+
*
|
|
42
|
+
* - LTV gate: the contract mints `calculate_scaled_borrow` (ceil) shares,
|
|
43
|
+
* merges them into the existing debt leg and requires
|
|
44
|
+
* `ltv_collateral >= total_debt` with the merged leg valued by
|
|
45
|
+
* `position_value_ceil` (`ops/borrow.rs::mint_debt`, `risk/totals.rs`,
|
|
46
|
+
* `risk/validation.rs`). The search runs on exactly that predicate, with
|
|
47
|
+
* the index and price `computeAccountRisk` used for the leg (else the
|
|
48
|
+
* accepted live row, else `reserve.liveBorrowIndexRay` / `reserve.usdPrice`).
|
|
49
|
+
* - min-collateral floor, spoke borrow-cap headroom, pool cash minus the
|
|
50
|
+
* liquidation buffer, and the hub utilization ceiling.
|
|
51
|
+
*
|
|
52
|
+
* A reserve that is not borrowable reports 0 with reason `cap` (same effect
|
|
53
|
+
* as a zero `borrowCap`).
|
|
54
|
+
*/
|
|
55
|
+
export declare function maxBorrow(risk: AccountRisk, reserve: ReserveDto, liveState?: StellarLendingLiveStateDto): Limit;
|
|
56
|
+
/**
|
|
57
|
+
* Largest withdraw of the account's `reserve` supply. Candidates: the risk
|
|
58
|
+
* gate (LTV-weighted collateral must stay ≥ `max(debt, floor)` — reported as
|
|
59
|
+
* `healthFactor`, or `minCollateral` when the floor is the stricter bound),
|
|
60
|
+
* pool cash, and the hub utilization ceiling. `paused` reports 0. When the
|
|
61
|
+
* whole position can go, `amountBase` is the floor payout and `isFullClose`
|
|
62
|
+
* is true — sign `"0"` so the contract takes the withdraw-all path
|
|
63
|
+
* (`resolve_withdrawal` treats any request ≥ the half-up balance as full).
|
|
64
|
+
* The position is priced as `risk` valued it, so `_liveState` is accepted for
|
|
65
|
+
* signature symmetry with {@link maxBorrow} but not consulted.
|
|
66
|
+
*/
|
|
67
|
+
export declare function maxWithdraw(risk: AccountRisk, reserve: ReserveDto, _liveState?: StellarLendingLiveStateDto): Limit;
|
|
68
|
+
/**
|
|
69
|
+
* Largest supply into `reserve`: the spoke supply-cap headroom
|
|
70
|
+
* (`supplyCap - spoke supplied`, always enforced, `0` closes the side).
|
|
71
|
+
* `paused` and `frozen` both block entry.
|
|
72
|
+
*/
|
|
73
|
+
export declare function maxSupply(reserve: ReserveDto): Limit;
|
|
74
|
+
/**
|
|
75
|
+
* Largest repay of the account's `reserve` debt: the ceil-rounded debt
|
|
76
|
+
* (`unscale_borrow_ceil`, which `resolve_repay` treats as a full close and
|
|
77
|
+
* refunds anything above), bounded by `walletBalance` (base units) when
|
|
78
|
+
* given — reported as `liquidity`. `paused` reports 0.
|
|
79
|
+
*/
|
|
80
|
+
export declare function maxRepay(risk: AccountRisk, reserve: ReserveDto, walletBalance?: string | bigint): Limit;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What-if projections: account risk after hypothetical position changes and
|
|
3
|
+
* reserve rates after hypothetical pool flows. Reuses the same aggregation
|
|
4
|
+
* (`aggregateAccountRisk`) and curve (`rates.ts`) as the live computations.
|
|
5
|
+
* xoxno-ui parity: `utils.ts::getProjectedHealthPercentage`,
|
|
6
|
+
* `getProjectedUtilization`, `getApyProjection`.
|
|
7
|
+
*/
|
|
8
|
+
import type { ReserveDto } from '../lending-api-types.js';
|
|
9
|
+
import type { AccountRisk, PositionSide } from './account.js';
|
|
10
|
+
export type PositionDelta = {
|
|
11
|
+
side: PositionSide;
|
|
12
|
+
hubId: number;
|
|
13
|
+
asset: string;
|
|
14
|
+
/** Signed token base units: positive supplies/borrows, negative withdraws/repays. */
|
|
15
|
+
amountBase: bigint | string;
|
|
16
|
+
/** Required when the account has no such position yet: decimals, `usdPrice` and the `collateralFactorBps` / `liquidationThresholdBps` a new position would snapshot. */
|
|
17
|
+
reserve?: ReserveDto;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Re-value the account after applying `deltas`, moving shares the way the
|
|
21
|
+
* pool does. Existing legs change in shares (`mintShares` on entry,
|
|
22
|
+
* `burnShares` on a partial exit; a withdraw ≥ the half-up balance or a
|
|
23
|
+
* repay ≥ the ceil debt burns every share) and are revalued with
|
|
24
|
+
* `valueScaledPosition`; a new leg is minted from `amountBase`
|
|
25
|
+
* (`valueBasePosition`) and takes its index, price and entry weights from
|
|
26
|
+
* `delta.reserve` (no live price: pass the reserve you would supply into).
|
|
27
|
+
* A leg driven to zero is dropped. The min-collateral floor and
|
|
28
|
+
* `liveDataValid` carry over from `risk`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function projectAccountRisk(risk: AccountRisk, deltas: PositionDelta[]): AccountRisk;
|
|
31
|
+
export type ReserveApyProjection = {
|
|
32
|
+
/** Hub utilization after the flows, 0..1. */
|
|
33
|
+
utilization: number;
|
|
34
|
+
utilizationRay: string;
|
|
35
|
+
/** Annual RAY rates from the curve. */
|
|
36
|
+
borrowAprRay: string;
|
|
37
|
+
supplyAprRay: string;
|
|
38
|
+
/** Simple annual rates as fractions (`0.05` = 5%). */
|
|
39
|
+
borrowApr: number;
|
|
40
|
+
supplyApr: number;
|
|
41
|
+
/** Continuously compounded `e^apr - 1`, fractions. */
|
|
42
|
+
borrowApy: number;
|
|
43
|
+
supplyApy: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Hub rates after adding `supplyDeltaBase` / `borrowDeltaBase` (signed base
|
|
47
|
+
* units) to the reserve's hub-pool totals. Utilization is
|
|
48
|
+
* `curve.rs::utilization` over the projected totals; rates follow
|
|
49
|
+
* `annualBorrowRateRay` / `depositRateRay`. Whether `ReserveDto.supplyApy`
|
|
50
|
+
* / `borrowApy` are simple or compounded is not pinned by the API contract,
|
|
51
|
+
* so both `*Apr` and `*Apy` are returned.
|
|
52
|
+
*/
|
|
53
|
+
export declare function projectReserveApy(reserve: ReserveDto, supplyDeltaBase?: bigint | string, borrowDeltaBase?: bigint | string): ReserveApyProjection;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interest-rate curve mirror of `rs-lending-xlm/common/src/rates/curve.rs`.
|
|
3
|
+
* Annual RAY in, annual RAY out; `Ray::mul` / `Ray::div` are half-up
|
|
4
|
+
* (`fp.rs`), matching the pool's view getters.
|
|
5
|
+
*/
|
|
6
|
+
import type { ReserveIrmCurveDto } from '../lending-api-types.js';
|
|
7
|
+
/**
|
|
8
|
+
* `curve.rs::utilization` — `borrowed / supplied` half-up in RAY; zero when
|
|
9
|
+
* nothing is supplied. The contract feeds it half-up-valued debt and supply
|
|
10
|
+
* (`formulas.md` §Utilization and annual rates).
|
|
11
|
+
*/
|
|
12
|
+
export declare function utilizationRay(borrowedRay: bigint, suppliedRay: bigint): bigint;
|
|
13
|
+
/**
|
|
14
|
+
* `curve.rs::calculate_annual_borrow_rate` — three-segment piecewise-linear
|
|
15
|
+
* curve. Utilization is clamped to one RAY; below `mid` the rate ramps from
|
|
16
|
+
* `base` by `slope1`, between `mid` and `optimal` by `slope2`, above
|
|
17
|
+
* `optimal` by `slope3`; the result is capped at `maxBorrowRate`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function annualBorrowRateRay(utilization: bigint, irm: ReserveIrmCurveDto): bigint;
|
|
20
|
+
/**
|
|
21
|
+
* `curve.rs::calculate_deposit_rate` — supplier rate after the reserve factor:
|
|
22
|
+
* `half_up(half_up(u * borrowRate / RAY) * (BPS - rf) / BPS)`. Zero at zero
|
|
23
|
+
* utilization or a reserve factor outside `0..BPS`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function depositRateRay(utilization: bigint, borrowRateRay: bigint, reserveFactorBps: number): bigint;
|
|
26
|
+
/** Annual RAY rate → simple APR as a fraction (`0.05` = 5%). */
|
|
27
|
+
export declare const rayAprToNumber: (rateRay: bigint) => number;
|
|
28
|
+
/**
|
|
29
|
+
* Annual RAY rate → continuously compounded APY fraction, `e^apr − 1`. The
|
|
30
|
+
* pool compounds per millisecond with an eighth-order Taylor series
|
|
31
|
+
* (`compound.rs`), which the continuous exponential approximates to well
|
|
32
|
+
* within display precision.
|
|
33
|
+
*/
|
|
34
|
+
export declare const rayAprToApy: (rateRay: bigint) => number;
|