@xoxno/sdk-js 1.0.223 → 1.0.225
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__/admin.test.d.ts +0 -12
- package/dist/cjs/sdk/stellar/__tests__/builders-hubasset.test.d.ts +0 -11
- package/dist/cjs/sdk/stellar/__tests__/governance.test.d.ts +0 -4
- package/dist/cjs/sdk/stellar/__tests__/lending.test.d.ts +0 -20
- package/dist/cjs/sdk/stellar/__tests__/strategy-program.test.d.ts +0 -8
- package/dist/cjs/sdk/stellar/__tests__/swap-history.test.d.ts +1 -0
- package/dist/cjs/sdk/stellar/index.d.ts +1 -0
- package/dist/cjs/sdk/stellar/swap-history.d.ts +25 -0
- package/dist/cjs/sdk/stellar/swap-index.d.ts +2 -0
- package/dist/cjs/sdk/stellar/xdr-compat.d.ts +11 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/sdk/index.js +198 -0
- package/dist/esm/sdk/stellar/admin.js +385 -0
- package/dist/esm/sdk/stellar/auth-policy.js +310 -0
- package/dist/esm/sdk/stellar/cctp.js +23 -0
- package/dist/esm/sdk/stellar/contracts.js +49 -0
- package/dist/esm/sdk/stellar/events/decode.js +415 -0
- package/dist/esm/sdk/stellar/events/id.js +62 -0
- package/dist/esm/sdk/stellar/events/index.js +2 -0
- package/dist/esm/sdk/stellar/governance.js +403 -0
- package/dist/esm/sdk/stellar/index.js +25 -0
- package/dist/esm/sdk/stellar/lending-api-types.js +3 -0
- package/dist/esm/sdk/stellar/lending-read-types.js +1 -0
- package/dist/esm/sdk/stellar/lending-read.js +341 -0
- package/dist/esm/sdk/stellar/lending.js +358 -0
- package/dist/esm/sdk/stellar/math/account.js +256 -0
- package/dist/esm/sdk/stellar/math/index.js +11 -0
- package/dist/esm/sdk/stellar/math/limits.js +273 -0
- package/dist/esm/sdk/stellar/math/projection.js +130 -0
- package/dist/esm/sdk/stellar/math/rates.js +62 -0
- package/dist/esm/sdk/stellar/math/scaled.js +156 -0
- package/dist/esm/sdk/stellar/position-mode.js +11 -0
- package/dist/esm/sdk/stellar/prepare.js +47 -0
- package/dist/esm/sdk/stellar/quote.js +87 -0
- package/dist/esm/sdk/stellar/repay-swap.js +12 -0
- package/dist/esm/sdk/stellar/route-verify.js +285 -0
- package/dist/esm/sdk/stellar/scval-encode.js +464 -0
- package/dist/esm/sdk/stellar/swap-history.js +46 -0
- package/dist/esm/sdk/stellar/swap-index.js +1 -0
- package/dist/esm/sdk/stellar/swap.js +117 -0
- package/dist/esm/sdk/stellar/xdr-compat.js +17 -0
- package/dist/esm/sdk/stellar-schema.js +129 -0
- package/dist/esm/sdk/swagger.js +1957 -0
- package/dist/esm/sdk/types.js +1 -0
- package/dist/esm/sdk/utils.js +21 -0
- package/dist/esm/utils/api.js +169 -0
- package/dist/esm/utils/const.js +11 -0
- package/dist/esm/utils/errors.js +20 -0
- package/dist/esm/utils/guards.js +29 -0
- package/dist/esm/utils/helpers.js +20 -0
- package/dist/esm/utils/regex.js +14 -0
- package/dist/esm/utils/soroban-errors.js +149 -0
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -2
- package/dist/sdk/stellar/__tests__/admin.test.d.ts +0 -12
- package/dist/sdk/stellar/__tests__/builders-hubasset.test.d.ts +0 -11
- package/dist/sdk/stellar/__tests__/governance.test.d.ts +0 -4
- package/dist/sdk/stellar/__tests__/lending.test.d.ts +0 -20
- package/dist/sdk/stellar/__tests__/strategy-program.test.d.ts +0 -8
- package/dist/sdk/stellar/__tests__/swap-history.test.d.ts +1 -0
- package/dist/sdk/stellar/index.cjs +1 -1
- package/dist/sdk/stellar/index.d.ts +1 -0
- package/dist/sdk/stellar/index.esm.js +1 -1
- package/dist/sdk/stellar/swap-history.d.ts +25 -0
- package/dist/sdk/stellar/swap-index.cjs +2 -0
- package/dist/sdk/stellar/swap-index.d.ts +2 -0
- package/dist/sdk/stellar/swap-index.esm.js +2 -0
- package/dist/sdk/stellar/swap-index.esm.js.LICENSE.txt +8 -0
- package/dist/sdk/stellar/xdr-compat.d.ts +11 -0
- package/package.json +16 -5
- /package/dist/{index.esm.js.LICENSE.txt → sdk/stellar/swap-index.cjs.LICENSE.txt} +0 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stellar lending user-operation transaction builders.
|
|
3
|
+
*
|
|
4
|
+
* Each builder takes a typed Stellar argument object plus shared
|
|
5
|
+
* `StellarLendingBuilderOptions` ({ network, caller, sourceSequence,
|
|
6
|
+
* controllerAddress, ... }) and returns
|
|
7
|
+
* unsigned transaction XDR that must be prepared before wallet signing.
|
|
8
|
+
*
|
|
9
|
+
* i128 values cross the boundary as decimal strings, encoded via
|
|
10
|
+
* `new ScInt(str).toI128()`. Addresses (Stellar `G...` accounts and Soroban
|
|
11
|
+
* `C...` contracts) are encoded via `new Address(str).toScVal()`.
|
|
12
|
+
*
|
|
13
|
+
* Builders are RPC-free: they accept a caller-supplied `sourceSequence` so the
|
|
14
|
+
* returned XDR is deterministic and snapshot-testable. The caller fetches the
|
|
15
|
+
* current sequence via `rpc.Server.getAccount(caller)` and runs
|
|
16
|
+
* `rpc.Server.prepareTransaction` (simulation + Soroban footprint/auth/resource
|
|
17
|
+
* fee) before handing the XDR to the wallet to sign.
|
|
18
|
+
*/ import { Account, BASE_FEE, Contract, scValToNative, TransactionBuilder, xdr } from "@stellar/stellar-sdk";
|
|
19
|
+
import { STELLAR_NETWORK_PASSPHRASE } from "./contracts.js";
|
|
20
|
+
import { addr, asStellarBytes, asStellarStrategySwapBytes, bool, hubAsset, i128, option, seizeMode, tupleAddrAmountVec, tupleHubAssetAmount, tupleHubAssetAmountVec, u32, u64, vec } from "./scval-encode.js";
|
|
21
|
+
// -----------------------------------------------------------------------------
|
|
22
|
+
// Transaction assembly
|
|
23
|
+
// -----------------------------------------------------------------------------
|
|
24
|
+
/**
|
|
25
|
+
* Assemble an unsigned XDR that invokes a single Soroban contract method.
|
|
26
|
+
*
|
|
27
|
+
* Uses a synthetic `Account(caller, sourceSequence)` — no RPC call. The
|
|
28
|
+
* returned XDR still needs preparation (simulation to populate Soroban
|
|
29
|
+
* footprint + auth entries + resource fees) before signing. The UI layer
|
|
30
|
+
* does this via `rpc.Server.prepareTransaction`.
|
|
31
|
+
*/ export function buildTx(opts, method, params) {
|
|
32
|
+
if (!opts.controllerAddress) {
|
|
33
|
+
throw new Error('Stellar lending controllerAddress is required');
|
|
34
|
+
}
|
|
35
|
+
const contract = new Contract(opts.controllerAddress);
|
|
36
|
+
const source = new Account(opts.caller, opts.sourceSequence);
|
|
37
|
+
const tx = new TransactionBuilder(source, {
|
|
38
|
+
fee: opts.fee ?? BASE_FEE,
|
|
39
|
+
networkPassphrase: STELLAR_NETWORK_PASSPHRASE[opts.network]
|
|
40
|
+
}).addOperation(contract.call(method, ...params)).setTimeout(opts.timeoutSeconds ?? 300).build();
|
|
41
|
+
return {
|
|
42
|
+
xdr: tx.toXDR()
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// -----------------------------------------------------------------------------
|
|
46
|
+
// Builders — 11 entry points, 1 : 1 with the multi-hub Stellar controller
|
|
47
|
+
// -----------------------------------------------------------------------------
|
|
48
|
+
function encodeSpokeId(value) {
|
|
49
|
+
if (!Number.isInteger(value) || value < 1 || value > 0xffffffff) {
|
|
50
|
+
throw new Error('Stellar builder: spokeId must be an integer from 1 to 4294967295');
|
|
51
|
+
}
|
|
52
|
+
return u32(value);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* supply(caller, account_id: u64, spoke_id: u32, assets: Vec<(HubAssetKey, i128)>)
|
|
56
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
57
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
58
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
59
|
+
* @category Lending transactions
|
|
60
|
+
*/ export function buildStellarSupplyBatchTx(opts, args) {
|
|
61
|
+
const accountId = args.accountNonce ?? 0;
|
|
62
|
+
return buildTx(opts, 'supply', [
|
|
63
|
+
addr(opts.caller),
|
|
64
|
+
u64(accountId),
|
|
65
|
+
encodeSpokeId(args.spokeId),
|
|
66
|
+
tupleHubAssetAmountVec([
|
|
67
|
+
...args.assets
|
|
68
|
+
])
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
/** Single-asset `supply` — wraps the asset in a 1-element batch.
|
|
72
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
73
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
74
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
75
|
+
* @category Lending transactions
|
|
76
|
+
*/ export function buildStellarSupplyTx(opts, args) {
|
|
77
|
+
return buildStellarSupplyBatchTx(opts, {
|
|
78
|
+
accountNonce: args.accountNonce,
|
|
79
|
+
spokeId: args.spokeId,
|
|
80
|
+
assets: [
|
|
81
|
+
{
|
|
82
|
+
hubId: args.hubId,
|
|
83
|
+
asset: args.asset,
|
|
84
|
+
amount: args.amount
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* borrow(caller, account_id: u64, borrows: Vec<(HubAssetKey, i128)>,
|
|
91
|
+
* to: Option<Address>) — `to` is always sent; absent means the caller
|
|
92
|
+
* receives the funds.
|
|
93
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
94
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
95
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
96
|
+
* @category Lending transactions
|
|
97
|
+
*/ export function buildStellarBorrowBatchTx(opts, args) {
|
|
98
|
+
return buildTx(opts, 'borrow', [
|
|
99
|
+
addr(opts.caller),
|
|
100
|
+
u64(args.accountNonce),
|
|
101
|
+
tupleHubAssetAmountVec([
|
|
102
|
+
...args.borrows
|
|
103
|
+
]),
|
|
104
|
+
option(args.to, addr)
|
|
105
|
+
]);
|
|
106
|
+
}
|
|
107
|
+
/** Single-asset `borrow`.
|
|
108
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
109
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
110
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
111
|
+
* @category Lending transactions
|
|
112
|
+
*/ export function buildStellarBorrowTx(opts, args) {
|
|
113
|
+
return buildStellarBorrowBatchTx(opts, {
|
|
114
|
+
accountNonce: args.accountNonce,
|
|
115
|
+
borrows: [
|
|
116
|
+
{
|
|
117
|
+
hubId: args.hubId,
|
|
118
|
+
asset: args.asset,
|
|
119
|
+
amount: args.amount
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
to: args.to
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* withdraw(caller, account_id: u64, withdrawals: Vec<(HubAssetKey, i128)>,
|
|
127
|
+
* to: Option<Address>) — `to` is always sent; absent means the caller
|
|
128
|
+
* receives the funds.
|
|
129
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
130
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
131
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
132
|
+
* @category Lending transactions
|
|
133
|
+
*/ export function buildStellarWithdrawBatchTx(opts, args) {
|
|
134
|
+
return buildTx(opts, 'withdraw', [
|
|
135
|
+
addr(opts.caller),
|
|
136
|
+
u64(args.accountNonce),
|
|
137
|
+
tupleHubAssetAmountVec([
|
|
138
|
+
...args.withdrawals
|
|
139
|
+
]),
|
|
140
|
+
option(args.to, addr)
|
|
141
|
+
]);
|
|
142
|
+
}
|
|
143
|
+
/** Single-asset `withdraw`. Amount "0" withdraws the full supplied position.
|
|
144
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
145
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
146
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
147
|
+
* @category Lending transactions
|
|
148
|
+
*/ export function buildStellarWithdrawTx(opts, args) {
|
|
149
|
+
return buildStellarWithdrawBatchTx(opts, {
|
|
150
|
+
accountNonce: args.accountNonce,
|
|
151
|
+
withdrawals: [
|
|
152
|
+
{
|
|
153
|
+
hubId: args.hubId,
|
|
154
|
+
asset: args.asset,
|
|
155
|
+
amount: args.amount
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
to: args.to
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* repay(caller, account_id: u64, payments: Vec<(HubAssetKey, i128)>)
|
|
163
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
164
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
165
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
166
|
+
* @category Lending transactions
|
|
167
|
+
*/ export function buildStellarRepayBatchTx(opts, args) {
|
|
168
|
+
return buildTx(opts, 'repay', [
|
|
169
|
+
addr(opts.caller),
|
|
170
|
+
u64(args.accountNonce),
|
|
171
|
+
tupleHubAssetAmountVec([
|
|
172
|
+
...args.payments
|
|
173
|
+
])
|
|
174
|
+
]);
|
|
175
|
+
}
|
|
176
|
+
/** Single-asset `repay`.
|
|
177
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
178
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
179
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
180
|
+
* @category Lending transactions
|
|
181
|
+
*/ export function buildStellarRepayTx(opts, args) {
|
|
182
|
+
return buildStellarRepayBatchTx(opts, {
|
|
183
|
+
accountNonce: args.accountNonce,
|
|
184
|
+
payments: [
|
|
185
|
+
{
|
|
186
|
+
hubId: args.hubId,
|
|
187
|
+
asset: args.asset,
|
|
188
|
+
amount: args.amount
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* liquidate(liquidator, account_id: u64, debt_payments: Vec<(HubAssetKey, i128)>,
|
|
195
|
+
* seize_mode: SeizeMode) -> u64
|
|
196
|
+
*
|
|
197
|
+
* The `u64` return is the account credited with the seized supply shares under
|
|
198
|
+
* `SeizeMode::Credit` (freshly opened when `Credit(0)` was passed), and `0`
|
|
199
|
+
* under `SeizeMode::Transfer`. Read it from the simulation / tx result with
|
|
200
|
+
* `decodeStellarLiquidateReturn`.
|
|
201
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
202
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
203
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
204
|
+
* @category Lending transactions
|
|
205
|
+
*/ export function buildStellarLiquidateTx(opts, args) {
|
|
206
|
+
return buildTx(opts, 'liquidate', [
|
|
207
|
+
addr(opts.caller),
|
|
208
|
+
u64(args.accountNonce),
|
|
209
|
+
tupleHubAssetAmountVec([
|
|
210
|
+
...args.debtPayments
|
|
211
|
+
]),
|
|
212
|
+
seizeMode(args.seizeMode ?? 'Transfer')
|
|
213
|
+
]);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Decode `liquidate`'s `u64` return value from its base64 XDR — the receiving
|
|
217
|
+
* account id, as a decimal string. `'0'` means `SeizeMode::Transfer` (the
|
|
218
|
+
* liquidator was paid in underlying tokens, no account was touched).
|
|
219
|
+
*
|
|
220
|
+
* Takes base64 XDR (as Soroban RPC delivers `returnValue` / `result.retval`)
|
|
221
|
+
* so no live `xdr.ScVal` crosses the consumer boundary — same contract as
|
|
222
|
+
* `decodeStellarLendingEvent`. Hold a parsed ScVal? Pass `toBase64Xdr(scv)`.
|
|
223
|
+
*/ export function decodeStellarLiquidateReturn(returnValueB64) {
|
|
224
|
+
const native = scValToNative(xdr.ScVal.fromXDR(returnValueB64, 'base64'));
|
|
225
|
+
if (typeof native === 'bigint' || typeof native === 'number') {
|
|
226
|
+
return native.toString();
|
|
227
|
+
}
|
|
228
|
+
throw new Error(`Stellar builder: liquidate return value must be a u64, got ${typeof native}`);
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* flash_loan(caller, asset: HubAssetKey, amount: i128, receiver, data: Bytes)
|
|
232
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
233
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
234
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
235
|
+
* @category Lending transactions
|
|
236
|
+
*/ export function buildStellarFlashLoanTx(opts, args) {
|
|
237
|
+
return buildTx(opts, 'flash_loan', [
|
|
238
|
+
addr(opts.caller),
|
|
239
|
+
hubAsset(args.hubId, args.asset),
|
|
240
|
+
i128(args.amount),
|
|
241
|
+
addr(args.receiver),
|
|
242
|
+
asStellarBytes(args.data)
|
|
243
|
+
]);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Build a `migrate_from_blend` controller invocation: atomically moves a Blend
|
|
247
|
+
* V2 position (collateral + supply + debt) into XOXNO at zero flash-loan fee.
|
|
248
|
+
*
|
|
249
|
+
* ABI: `migrate_from_blend(caller, account_id, spoke_id, hub_id, blend_pool,
|
|
250
|
+
* collateral_assets, supply_assets, debt_caps: Vec<(Address, i128)>)`. Pass
|
|
251
|
+
* `accountId = "0"` to open a new account. Each debt cap should slightly exceed
|
|
252
|
+
* the live Blend debt — Blend refunds the excess, reconciled on-chain.
|
|
253
|
+
*
|
|
254
|
+
* Like every builder this emits an unsigned invoke; the nested `submit(from =
|
|
255
|
+
* user)` authorization is materialized by `prepareTransaction` (simulation) and
|
|
256
|
+
* signed by the wallet over the whole envelope. The Blend pool must be approved
|
|
257
|
+
* by the controller. Use an explicit destination hub and spoke; this operation
|
|
258
|
+
* does not consume aggregator route bytes.
|
|
259
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
260
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
261
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
262
|
+
* @category Lending transactions
|
|
263
|
+
*/ export function buildStellarMigrateFromBlendTx(opts, args) {
|
|
264
|
+
return buildTx(opts, 'migrate_from_blend', [
|
|
265
|
+
addr(opts.caller),
|
|
266
|
+
u64(args.accountId),
|
|
267
|
+
encodeSpokeId(args.spokeId),
|
|
268
|
+
u32(args.hubId),
|
|
269
|
+
addr(args.blendPool),
|
|
270
|
+
vec(args.collateralTokens.map(addr)),
|
|
271
|
+
vec(args.supplyTokens.map(addr)),
|
|
272
|
+
tupleAddrAmountVec(args.debtCaps.map((d)=>({
|
|
273
|
+
token: d.token,
|
|
274
|
+
amount: d.cap
|
|
275
|
+
})))
|
|
276
|
+
]);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* multiply(caller, account_id, spoke_id, collateral: HubAssetKey,
|
|
280
|
+
* debt_to_flash_loan: i128, debt: HubAssetKey, mode: PositionMode,
|
|
281
|
+
* swap: Bytes, initial_payment: Option<(HubAssetKey, i128)>,
|
|
282
|
+
* convert_swap: Option<Bytes>) -> u64
|
|
283
|
+
*
|
|
284
|
+
* `mode` is a repr(u32) `PositionMode` → encoded as `scvU32`. The two trailing
|
|
285
|
+
* `Option`s seed an optional initial collateral payment and a secondary swap
|
|
286
|
+
* converting it into the collateral token; both omit to Soroban `Void`.
|
|
287
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
288
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
289
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
290
|
+
* @category Lending transactions
|
|
291
|
+
*/ export function buildStellarMultiplyTx(opts, args) {
|
|
292
|
+
const accountId = args.accountNonce ?? 0;
|
|
293
|
+
return buildTx(opts, 'multiply', [
|
|
294
|
+
addr(opts.caller),
|
|
295
|
+
u64(accountId),
|
|
296
|
+
encodeSpokeId(args.spokeId),
|
|
297
|
+
hubAsset(args.collateral.hubId, args.collateral.asset),
|
|
298
|
+
i128(args.debtToFlashLoan),
|
|
299
|
+
hubAsset(args.debt.hubId, args.debt.asset),
|
|
300
|
+
u32(args.mode),
|
|
301
|
+
asStellarStrategySwapBytes(args.steps),
|
|
302
|
+
option(args.initialPayment, (p)=>tupleHubAssetAmount(p.hubId, p.asset, p.amount)),
|
|
303
|
+
option(args.convertSwap, asStellarStrategySwapBytes)
|
|
304
|
+
]);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* swap_debt(caller, account_id, existing_debt: HubAssetKey, amount: i128,
|
|
308
|
+
* new_debt: HubAssetKey, swap: Bytes)
|
|
309
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
310
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
311
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
312
|
+
* @category Lending transactions
|
|
313
|
+
*/ export function buildStellarSwapDebtTx(opts, args) {
|
|
314
|
+
return buildTx(opts, 'swap_debt', [
|
|
315
|
+
addr(opts.caller),
|
|
316
|
+
u64(args.accountNonce),
|
|
317
|
+
hubAsset(args.existingDebt.hubId, args.existingDebt.asset),
|
|
318
|
+
i128(args.newDebtAmount),
|
|
319
|
+
hubAsset(args.newDebt.hubId, args.newDebt.asset),
|
|
320
|
+
asStellarStrategySwapBytes(args.steps)
|
|
321
|
+
]);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* swap_collateral(caller, account_id, current: HubAssetKey, amount: i128,
|
|
325
|
+
* new: HubAssetKey, swap: Bytes)
|
|
326
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
327
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
328
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
329
|
+
* @category Lending transactions
|
|
330
|
+
*/ export function buildStellarSwapCollateralTx(opts, args) {
|
|
331
|
+
return buildTx(opts, 'swap_collateral', [
|
|
332
|
+
addr(opts.caller),
|
|
333
|
+
u64(args.accountNonce),
|
|
334
|
+
hubAsset(args.current.hubId, args.current.asset),
|
|
335
|
+
i128(args.fromAmount),
|
|
336
|
+
hubAsset(args.newCollateral.hubId, args.newCollateral.asset),
|
|
337
|
+
asStellarStrategySwapBytes(args.steps)
|
|
338
|
+
]);
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* repay_debt_with_collateral(caller, account_id, collateral: HubAssetKey,
|
|
342
|
+
* collateral_amount: i128, debt: HubAssetKey,
|
|
343
|
+
* swap: Bytes, close_position: bool)
|
|
344
|
+
* @param opts - Network, caller, current source sequence and deployment addresses.
|
|
345
|
+
* @param args - Operation arguments; token amounts are decimal base-unit strings.
|
|
346
|
+
* @returns Unsigned XDR; prepare with prepareStellarBuiltTx before wallet signing.
|
|
347
|
+
* @category Lending transactions
|
|
348
|
+
*/ export function buildStellarRepayDebtWithCollateralTx(opts, args) {
|
|
349
|
+
return buildTx(opts, 'repay_debt_with_collateral', [
|
|
350
|
+
addr(opts.caller),
|
|
351
|
+
u64(args.accountNonce),
|
|
352
|
+
hubAsset(args.collateral.hubId, args.collateral.asset),
|
|
353
|
+
i128(args.collateralAmount),
|
|
354
|
+
hubAsset(args.debt.hubId, args.debt.asset),
|
|
355
|
+
asStellarStrategySwapBytes(args.steps),
|
|
356
|
+
bool(args.closePosition)
|
|
357
|
+
]);
|
|
358
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
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
|
+
*/ import { BPS, RAY, WAD, baseToRay, baseToShort, mulDivCeil, mulDivFloor, numberToWad, positionValueWad, toBigInt, unscale, wadToNumber } from "./scaled.js";
|
|
11
|
+
/**
|
|
12
|
+
* `common/src/constants/shared.rs::DEFAULT_MIN_BORROW_COLLATERAL_USD_WAD` —
|
|
13
|
+
* the floor `get_min_borrow_collateral_usd` returns until governance changes
|
|
14
|
+
* it. Used only when `liveState.minBorrowCollateralUsdWad` is absent or null.
|
|
15
|
+
*/ export const DEFAULT_MIN_BORROW_COLLATERAL_USD_WAD = 5n * WAD;
|
|
16
|
+
/** Key used by `StellarLendingContextDto.reserveDetailsByKey`. */ export const reserveKey = (spokeId, hubId, asset)=>`${spokeId}:${hubId}:${asset}`;
|
|
17
|
+
/** Find the reserve backing a `(spokeId, hubId, asset)` coordinate; throws when missing. */ export function resolveReserve(reserves, spokeId, hubId, asset) {
|
|
18
|
+
const list = Array.isArray(reserves) ? reserves : undefined;
|
|
19
|
+
const found = list ? list.find((r)=>r.spokeId === spokeId && r.hubId === hubId && r.asset === asset) : reserves[reserveKey(spokeId, hubId, asset)] ?? Object.values(reserves).find((r)=>r.spokeId === spokeId && r.hubId === hubId && r.asset === asset);
|
|
20
|
+
if (!found) {
|
|
21
|
+
throw new Error(`Stellar math: no reserve for ${reserveKey(spokeId, hubId, asset)}`);
|
|
22
|
+
}
|
|
23
|
+
return found;
|
|
24
|
+
}
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Live-state divergence guard — mirror of xoxno-ui `use-scaled-amount.ts`
|
|
27
|
+
// `buildStellarLiveSnapshot`: exact per-hub-asset indexes, asset-level price
|
|
28
|
+
// consistency, and the controller oracle flags (a row the contract's
|
|
29
|
+
// fail-closed solvency path would reject must not feed valuations either).
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
const POSITIVE_INTEGER = /^0*[1-9]\d*$/;
|
|
32
|
+
const normalizedInteger = (value)=>typeof value === 'string' && POSITIVE_INTEGER.test(value) ? BigInt(value).toString() : null;
|
|
33
|
+
export const liveIndexKey = (hubId, asset)=>`${hubId}:${asset}`;
|
|
34
|
+
/** Build the divergence-guarded snapshot from `liveState.indexes`. */ export function buildLiveSnapshot(liveState) {
|
|
35
|
+
const indexes = new Map();
|
|
36
|
+
const prices = new Map();
|
|
37
|
+
const rejectedIndexKeys = new Set();
|
|
38
|
+
const rejectedAssets = new Set();
|
|
39
|
+
for (const row of liveState?.indexes ?? []){
|
|
40
|
+
const key = liveIndexKey(row.hubId, row.asset);
|
|
41
|
+
const supply = normalizedInteger(row.supplyIndex);
|
|
42
|
+
const borrow = normalizedInteger(row.borrowIndex);
|
|
43
|
+
const price = normalizedInteger(row.usdPrice);
|
|
44
|
+
const existing = indexes.get(key);
|
|
45
|
+
const sameAsExisting = existing && normalizedInteger(existing.supplyIndex) === supply && normalizedInteger(existing.borrowIndex) === borrow && normalizedInteger(existing.usdPrice) === price;
|
|
46
|
+
if (supply == null || borrow == null || price == null || existing && !sameAsExisting) {
|
|
47
|
+
rejectedIndexKeys.add(key);
|
|
48
|
+
} else if (!existing) {
|
|
49
|
+
indexes.set(key, row);
|
|
50
|
+
}
|
|
51
|
+
const oracleRejected = row.valid === false || row.stale === true || row.deviation === true;
|
|
52
|
+
const existingPrice = prices.get(row.asset);
|
|
53
|
+
if (price == null || oracleRejected || existingPrice !== undefined && existingPrice.toString() !== price) {
|
|
54
|
+
rejectedAssets.add(row.asset);
|
|
55
|
+
} else if (existingPrice === undefined) {
|
|
56
|
+
prices.set(row.asset, BigInt(price));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const key of rejectedIndexKeys)indexes.delete(key);
|
|
60
|
+
for (const asset of rejectedAssets)prices.delete(asset);
|
|
61
|
+
return {
|
|
62
|
+
indexes,
|
|
63
|
+
prices,
|
|
64
|
+
rejectedIndexKeys,
|
|
65
|
+
rejectedAssets
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Weight a floor-valued gate USD by a BPS ratio, flooring (`Bps::apply_to_wad_floor`). */ const weightFloor = (gateUsdWad, bps)=>mulDivFloor(gateUsdWad, BigInt(bps), BPS);
|
|
69
|
+
function finishPositionValue(seed, amountBase, usdWad, gateUsdWad) {
|
|
70
|
+
const isSupply = seed.side === 'supply';
|
|
71
|
+
const effectiveLtv = Math.min(seed.entryLtvBps, seed.entryLiquidationThresholdBps);
|
|
72
|
+
return {
|
|
73
|
+
side: seed.side,
|
|
74
|
+
hubId: seed.hubId,
|
|
75
|
+
asset: seed.asset,
|
|
76
|
+
decimals: seed.decimals,
|
|
77
|
+
scaledRay: seed.scaledRay.toString(),
|
|
78
|
+
indexRay: seed.indexRay.toString(),
|
|
79
|
+
usdPriceWad: seed.priceWad.toString(),
|
|
80
|
+
usdPrice: wadToNumber(seed.priceWad),
|
|
81
|
+
amountBase: amountBase.toString(),
|
|
82
|
+
amountShort: baseToShort(amountBase, seed.decimals),
|
|
83
|
+
usdWad: usdWad.toString(),
|
|
84
|
+
usd: wadToNumber(usdWad),
|
|
85
|
+
gateUsdWad: gateUsdWad.toString(),
|
|
86
|
+
entryLtvBps: seed.entryLtvBps,
|
|
87
|
+
entryLiquidationThresholdBps: seed.entryLiquidationThresholdBps,
|
|
88
|
+
entryLiquidationBonusBps: seed.entryLiquidationBonusBps,
|
|
89
|
+
entryLiquidationFeesBps: seed.entryLiquidationFeesBps,
|
|
90
|
+
ltvWeightedUsdWad: isSupply ? weightFloor(gateUsdWad, effectiveLtv).toString() : '0',
|
|
91
|
+
liquidationWeightedUsdWad: isSupply ? weightFloor(gateUsdWad, seed.entryLiquidationThresholdBps).toString() : '0',
|
|
92
|
+
liveDataUsed: seed.liveDataUsed
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Value scaled shares exactly as `calculate_account_risk_totals` does:
|
|
97
|
+
* supply → `position_value` (half-up display/total) and
|
|
98
|
+
* `position_value_floor` (gate); borrow → `position_value_ceil`.
|
|
99
|
+
*/ export function valueScaledPosition(seed) {
|
|
100
|
+
if (seed.side === 'supply') {
|
|
101
|
+
const amountBase = unscale(seed.scaledRay, seed.indexRay, seed.decimals, 'floor');
|
|
102
|
+
const usdWad = positionValueWad(seed.scaledRay, seed.indexRay, seed.priceWad, 'halfUp');
|
|
103
|
+
const gateUsdWad = positionValueWad(seed.scaledRay, seed.indexRay, seed.priceWad, 'floor');
|
|
104
|
+
return finishPositionValue(seed, amountBase, usdWad, gateUsdWad);
|
|
105
|
+
}
|
|
106
|
+
const amountBase = unscale(seed.scaledRay, seed.indexRay, seed.decimals, 'ceil');
|
|
107
|
+
const usdWad = positionValueWad(seed.scaledRay, seed.indexRay, seed.priceWad, 'ceil');
|
|
108
|
+
return finishPositionValue(seed, amountBase, usdWad, usdWad);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Shares minted when `amountBase` enters a leg — `scaling.rs`
|
|
112
|
+
* `calculate_scaled_supply` (floor) / `calculate_scaled_borrow` (ceil), the
|
|
113
|
+
* only entry conversions the pool performs (`ops/supply.rs`, `ops/borrow.rs`).
|
|
114
|
+
*/ export function mintShares(side, amountBase, decimals, indexRay) {
|
|
115
|
+
const amountRay = baseToRay(amountBase, decimals);
|
|
116
|
+
return side === 'supply' ? mulDivFloor(amountRay, RAY, indexRay) : mulDivCeil(amountRay, RAY, indexRay);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Shares burned by a partial exit of `amountBase` — `formulas.md` rounding
|
|
120
|
+
* table: "Partial withdrawal burn: ceil", "Partial repayment burn: floor".
|
|
121
|
+
* Full closes (withdraw ≥ half-up balance, repay ≥ ceil debt) burn every
|
|
122
|
+
* share instead; callers handle that boundary.
|
|
123
|
+
*/ export function burnShares(side, amountBase, decimals, indexRay) {
|
|
124
|
+
const amountRay = baseToRay(amountBase, decimals);
|
|
125
|
+
return side === 'supply' ? mulDivCeil(amountRay, RAY, indexRay) : mulDivFloor(amountRay, RAY, indexRay);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Value a fresh leg entered with `amountBase`: shares come from
|
|
129
|
+
* {@link mintShares} and the valuation runs on those shares exactly as
|
|
130
|
+
* {@link valueScaledPosition} does (`position_value_*` never sees the base
|
|
131
|
+
* amount). `amountBase` on the result is therefore the share-derived balance,
|
|
132
|
+
* which can differ from the input by rounding when the index is not `RAY`.
|
|
133
|
+
*/ export function valueBasePosition(seed, amountBase) {
|
|
134
|
+
return valueScaledPosition({
|
|
135
|
+
...seed,
|
|
136
|
+
scaledRay: mintShares(seed.side, amountBase, seed.decimals, seed.indexRay)
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const firstIndex = (...candidates)=>{
|
|
140
|
+
for (const c of candidates){
|
|
141
|
+
const n = normalizedInteger(c);
|
|
142
|
+
if (n != null) return BigInt(n);
|
|
143
|
+
}
|
|
144
|
+
return RAY;
|
|
145
|
+
};
|
|
146
|
+
function valueDtoPosition(side, pos, reserve, snapshot) {
|
|
147
|
+
const scaledRay = toBigInt(side === 'supply' ? pos.supplyScaledRay : pos.borrowScaledRay, `${side}ScaledRay`);
|
|
148
|
+
if (scaledRay === 0n) return null;
|
|
149
|
+
const liveRow = snapshot.indexes.get(liveIndexKey(pos.hubId, pos.asset));
|
|
150
|
+
const livePrice = snapshot.prices.get(pos.asset);
|
|
151
|
+
const indexRay = side === 'supply' ? firstIndex(liveRow?.supplyIndex, pos.liveSupplyIndexRay, reserve.liveSupplyIndexRay, pos.supplyIndexRay) : firstIndex(liveRow?.borrowIndex, pos.liveBorrowIndexRay, reserve.liveBorrowIndexRay, pos.borrowIndexRay);
|
|
152
|
+
const priceWad = livePrice ?? numberToWad(reserve.usdPrice);
|
|
153
|
+
return valueScaledPosition({
|
|
154
|
+
side,
|
|
155
|
+
hubId: pos.hubId,
|
|
156
|
+
asset: pos.asset,
|
|
157
|
+
decimals: reserve.assetDecimals,
|
|
158
|
+
scaledRay,
|
|
159
|
+
indexRay,
|
|
160
|
+
priceWad,
|
|
161
|
+
entryLtvBps: pos.entryLtvBps,
|
|
162
|
+
entryLiquidationThresholdBps: pos.entryLiquidationThresholdBps,
|
|
163
|
+
entryLiquidationBonusBps: pos.entryLiquidationBonusBps,
|
|
164
|
+
entryLiquidationFeesBps: pos.entryLiquidationFeesBps,
|
|
165
|
+
liveDataUsed: liveRow !== undefined && livePrice !== undefined
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const sumWad = (positions, pick)=>positions.reduce((acc, p)=>acc + BigInt(pick(p)), 0n);
|
|
169
|
+
/**
|
|
170
|
+
* Fold valued positions into the contract's risk totals and the derived
|
|
171
|
+
* limits. `formulas.md`: `health_factor_wad = floor(weighted_collateral_wad *
|
|
172
|
+
* WAD / debt_wad)`; the post-action gate requires `debt <= ltv_collateral`,
|
|
173
|
+
* `HF >= 1 WAD` and `ltv_collateral >= floor` while debt remains.
|
|
174
|
+
*/ export function aggregateAccountRisk(identity, supplies, borrows, minBorrowCollateralUsdWad, liveDataValid) {
|
|
175
|
+
const totalCollateral = sumWad(supplies, (p)=>p.usdWad);
|
|
176
|
+
const ltvCollateral = sumWad(supplies, (p)=>p.ltvWeightedUsdWad);
|
|
177
|
+
const weightedCollateral = sumWad(supplies, (p)=>p.liquidationWeightedUsdWad);
|
|
178
|
+
const totalDebt = sumWad(borrows, (p)=>p.usdWad);
|
|
179
|
+
const healthFactorWad = totalDebt === 0n ? null : mulDivFloor(weightedCollateral, WAD, totalDebt);
|
|
180
|
+
const healthPercent = weightedCollateral === 0n ? 0 : Number(totalDebt) / Number(weightedCollateral) * 100;
|
|
181
|
+
const belowFloor = ltvCollateral < minBorrowCollateralUsdWad;
|
|
182
|
+
const availableBorrow = belowFloor || ltvCollateral <= totalDebt ? 0n : ltvCollateral - totalDebt;
|
|
183
|
+
return {
|
|
184
|
+
...identity,
|
|
185
|
+
supplies,
|
|
186
|
+
borrows,
|
|
187
|
+
totalCollateralUsdWad: totalCollateral.toString(),
|
|
188
|
+
totalCollateralUsd: wadToNumber(totalCollateral),
|
|
189
|
+
ltvWeightedCollateralUsdWad: ltvCollateral.toString(),
|
|
190
|
+
ltvWeightedCollateralUsd: wadToNumber(ltvCollateral),
|
|
191
|
+
liquidationWeightedCollateralUsdWad: weightedCollateral.toString(),
|
|
192
|
+
liquidationWeightedCollateralUsd: wadToNumber(weightedCollateral),
|
|
193
|
+
totalDebtUsdWad: totalDebt.toString(),
|
|
194
|
+
totalDebtUsd: wadToNumber(totalDebt),
|
|
195
|
+
healthFactorWad: healthFactorWad === null ? null : healthFactorWad.toString(),
|
|
196
|
+
healthFactor: healthFactorWad === null ? null : wadToNumber(healthFactorWad),
|
|
197
|
+
healthPercent,
|
|
198
|
+
borrowLimitUsdWad: ltvCollateral.toString(),
|
|
199
|
+
borrowLimitUsd: wadToNumber(ltvCollateral),
|
|
200
|
+
availableBorrowUsdWad: availableBorrow.toString(),
|
|
201
|
+
availableBorrowUsd: wadToNumber(availableBorrow),
|
|
202
|
+
minBorrowCollateralUsdWad: minBorrowCollateralUsdWad.toString(),
|
|
203
|
+
minBorrowCollateralUsd: wadToNumber(minBorrowCollateralUsdWad),
|
|
204
|
+
isLiquidatable: healthFactorWad !== null && healthFactorWad < WAD,
|
|
205
|
+
liveDataValid
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Value one account exactly as the controller's risk gates do.
|
|
210
|
+
*
|
|
211
|
+
* - Index: live-state row for `(hubId, asset)` when it passed the divergence
|
|
212
|
+
* guard, else the position's `liveSupplyIndexRay` / `liveBorrowIndexRay`,
|
|
213
|
+
* then the reserve's `live*IndexRay`, and last the position's stored
|
|
214
|
+
* `supplyIndexRay` / `borrowIndexRay` (undocumented in the API schema; the
|
|
215
|
+
* documented fields say "stored index on fallback", so it is treated as the
|
|
216
|
+
* stalest snapshot). The contract accrues to the current index before every
|
|
217
|
+
* gate, so fresher candidates win.
|
|
218
|
+
* - Price: live-state `usdPrice` (WAD) when accepted, else `reserve.usdPrice`
|
|
219
|
+
* converted to WAD.
|
|
220
|
+
* - Weights: the position's own `entry*Bps`, as the contract stores them.
|
|
221
|
+
* - Min-collateral floor: `liveState.minBorrowCollateralUsdWad`, defaulting
|
|
222
|
+
* to 5 USD (`DEFAULT_MIN_BORROW_COLLATERAL_USD_WAD`) when absent or null.
|
|
223
|
+
*
|
|
224
|
+
* Throws when `positions` is empty, mixes account ids, or names a reserve
|
|
225
|
+
* missing from `reserves`.
|
|
226
|
+
*/ export function computeAccountRisk(input) {
|
|
227
|
+
const { positions, reserves, liveState } = input;
|
|
228
|
+
const first = positions[0];
|
|
229
|
+
if (!first) throw new Error('Stellar math: computeAccountRisk needs at least one position row');
|
|
230
|
+
for (const p of positions){
|
|
231
|
+
if (p.accountId !== first.accountId) {
|
|
232
|
+
throw new Error(`Stellar math: computeAccountRisk received mixed account ids (${first.accountId}, ${p.accountId})`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const snapshot = buildLiveSnapshot(liveState);
|
|
236
|
+
const supplies = [];
|
|
237
|
+
const borrows = [];
|
|
238
|
+
for (const p of positions){
|
|
239
|
+
const reserve = resolveReserve(reserves, p.spokeId, p.hubId, p.asset);
|
|
240
|
+
const supply = valueDtoPosition('supply', p, reserve, snapshot);
|
|
241
|
+
if (supply) supplies.push(supply);
|
|
242
|
+
const borrow = valueDtoPosition('borrow', p, reserve, snapshot);
|
|
243
|
+
if (borrow) borrows.push(borrow);
|
|
244
|
+
}
|
|
245
|
+
const floorWad = liveState?.minBorrowCollateralUsdWad != null ? toBigInt(liveState.minBorrowCollateralUsdWad, 'minBorrowCollateralUsdWad') : DEFAULT_MIN_BORROW_COLLATERAL_USD_WAD;
|
|
246
|
+
const liveDataValid = liveState !== undefined && [
|
|
247
|
+
...supplies,
|
|
248
|
+
...borrows
|
|
249
|
+
].every((p)=>p.liveDataUsed);
|
|
250
|
+
return aggregateAccountRisk({
|
|
251
|
+
accountId: first.accountId,
|
|
252
|
+
owner: first.owner,
|
|
253
|
+
spokeId: first.spokeId,
|
|
254
|
+
mode: first.positionMode
|
|
255
|
+
}, supplies, borrows, floorWad, liveDataValid);
|
|
256
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
*/ export * from "./scaled.js";
|
|
8
|
+
export * from "./rates.js";
|
|
9
|
+
export * from "./account.js";
|
|
10
|
+
export * from "./limits.js";
|
|
11
|
+
export * from "./projection.js";
|