@suilend/sdk 12.3.0-next.g8719037 → 12.3.0-next.gcbeb44e

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/package.json CHANGED
@@ -1 +1 @@
1
- {"name":"@suilend/sdk","version":"12.3.0-next.g8719037","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/obligation":"./utils/obligation.js","./utils/simulate":"./utils/simulate.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/source/0x1":"./_generated/_dependencies/source/0x1/index.js","./_generated/_dependencies/source/0x2":"./_generated/_dependencies/source/0x2/index.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/index.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js","./_generated/_dependencies/source/0x1/ascii/structs":"./_generated/_dependencies/source/0x1/ascii/structs.js","./_generated/_dependencies/source/0x1/option/structs":"./_generated/_dependencies/source/0x1/option/structs.js","./_generated/_dependencies/source/0x1/type-name/structs":"./_generated/_dependencies/source/0x1/type-name/structs.js","./_generated/_dependencies/source/0x2/bag/structs":"./_generated/_dependencies/source/0x2/bag/structs.js","./_generated/_dependencies/source/0x2/balance/structs":"./_generated/_dependencies/source/0x2/balance/structs.js","./_generated/_dependencies/source/0x2/object/structs":"./_generated/_dependencies/source/0x2/object/structs.js","./_generated/_dependencies/source/0x2/object-table/structs":"./_generated/_dependencies/source/0x2/object-table/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js"},"types":"./index.d.ts","scripts":{"build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"7.6.3","@cetusprotocol/aggregator-sdk":"1.7.2","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.2","uuid":"14.0.2"},"devDependencies":{"@mysten/bcs":"2.1.1","@mysten/sui":"2.29.0","@suilend/sui-core":"2.1.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.25","@types/node":"26.5.1","fast-check":"4.10.0","prettier":"3.9.6","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.0","vitest":"5.0.0"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.22.1 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
1
+ {"name":"@suilend/sdk","version":"12.3.0-next.gcbeb44e","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/obligation":"./utils/obligation.js","./utils/simulate":"./utils/simulate.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/source/0x1":"./_generated/_dependencies/source/0x1/index.js","./_generated/_dependencies/source/0x2":"./_generated/_dependencies/source/0x2/index.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/index.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js","./_generated/_dependencies/source/0x1/ascii/structs":"./_generated/_dependencies/source/0x1/ascii/structs.js","./_generated/_dependencies/source/0x1/option/structs":"./_generated/_dependencies/source/0x1/option/structs.js","./_generated/_dependencies/source/0x1/type-name/structs":"./_generated/_dependencies/source/0x1/type-name/structs.js","./_generated/_dependencies/source/0x2/bag/structs":"./_generated/_dependencies/source/0x2/bag/structs.js","./_generated/_dependencies/source/0x2/balance/structs":"./_generated/_dependencies/source/0x2/balance/structs.js","./_generated/_dependencies/source/0x2/object/structs":"./_generated/_dependencies/source/0x2/object/structs.js","./_generated/_dependencies/source/0x2/object-table/structs":"./_generated/_dependencies/source/0x2/object-table/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js"},"types":"./index.d.ts","scripts":{"build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"7.6.3","@cetusprotocol/aggregator-sdk":"1.7.2","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.2","uuid":"14.0.2"},"devDependencies":{"@mysten/bcs":"2.1.1","@mysten/sui":"2.29.0","@suilend/sui-core":"2.1.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.25","@types/node":"26.5.1","fast-check":"4.10.0","prettier":"3.9.6","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.0","vitest":"5.0.0"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.22.1 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
@@ -197,6 +197,42 @@ export type ObligationHealth = {
197
197
  * All math stays in BigNumber end-to-end — never construct a `Decimal` from
198
198
  * a stored quantity; `numberToDecimal`/`stringToDecimal` round through a JS
199
199
  * `number` (`Math.round`) and are lossy at WAD scale. `decimalToBigNumber` is
200
- * only ever called on a genuine on-chain `Reserve` field below.
200
+ * only ever called on a genuine on-chain `Reserve` field, in
201
+ * `buildReserveHealthTable`.
202
+ *
203
+ * Per-position operation order matches `getCTokenMarketValue` and
204
+ * `compoundBorrowedAmount` exactly, so the result is bit-identical to
205
+ * recomputing every reserve value per position; reordering the divisions
206
+ * would round differently and can flip a verdict at the threshold.
207
+ */
208
+ export declare function computeObligationHealth(deposits: ObligationHealthDepositInput[], borrows: ObligationHealthBorrowInput[], reserveHealth: ReserveHealthTable): ObligationHealth;
209
+ export type ReserveDepositHealthValues = {
210
+ price: BigNumber;
211
+ cTokenRatio: BigNumber;
212
+ mintScale: BigNumber;
213
+ closeLtv: BigNumber;
214
+ };
215
+ export type ReserveBorrowHealthValues = {
216
+ price: BigNumber;
217
+ mintScale: BigNumber;
218
+ cumulativeBorrowRate: BigNumber;
219
+ borrowWeight: BigNumber;
220
+ };
221
+ /**
222
+ * Each lookup throws, as `findReserveOrThrow` does, when no reserve has
223
+ * this coin type. The two sides are separate so a reserve is only read for
224
+ * the fields its positions actually use.
225
+ */
226
+ export type ReserveHealthTable = {
227
+ deposit: (coinType: string) => ReserveDepositHealthValues;
228
+ borrow: (coinType: string) => ReserveBorrowHealthValues;
229
+ };
230
+ /**
231
+ * The per-reserve inputs to `computeObligationHealth`, derived once per
232
+ * reserve and side on first lookup instead of once per position. Build one
233
+ * per pass over a fixed set of priced reserves and drop it after: the values
234
+ * are a snapshot, and a caller that later rewrites a reserve's price in place
235
+ * would otherwise keep reading the old one.
201
236
  */
237
+ export declare function buildReserveHealthTable(reserves: Reserve<string>[]): ReserveHealthTable;
202
238
  export declare function computeObligationHealthFromPositions(deposits: ObligationHealthDepositInput[], borrows: ObligationHealthBorrowInput[], refreshedReserves: Reserve<string>[]): ObligationHealth;
package/utils/simulate.js CHANGED
@@ -507,28 +507,32 @@ export const compoundDebt = (borrow, reserve) => {
507
507
  * All math stays in BigNumber end-to-end — never construct a `Decimal` from
508
508
  * a stored quantity; `numberToDecimal`/`stringToDecimal` round through a JS
509
509
  * `number` (`Math.round`) and are lossy at WAD scale. `decimalToBigNumber` is
510
- * only ever called on a genuine on-chain `Reserve` field below.
510
+ * only ever called on a genuine on-chain `Reserve` field, in
511
+ * `buildReserveHealthTable`.
512
+ *
513
+ * Per-position operation order matches `getCTokenMarketValue` and
514
+ * `compoundBorrowedAmount` exactly, so the result is bit-identical to
515
+ * recomputing every reserve value per position; reordering the divisions
516
+ * would round differently and can flip a verdict at the threshold.
511
517
  */
512
- export function computeObligationHealthFromPositions(deposits, borrows, refreshedReserves) {
518
+ export function computeObligationHealth(deposits, borrows, reserveHealth) {
513
519
  let depositValueUsd = new BigNumber(0);
514
520
  let unhealthyBorrowValueUsd = new BigNumber(0);
515
521
  for (const deposit of deposits) {
516
- const reserve = findReserveOrThrow(refreshedReserves, deposit.coinType);
517
- const config = reserve.config.element;
518
- const marketValue = getCTokenMarketValue(reserve, deposit.depositedCtokenAmount);
522
+ const reserve = reserveHealth.deposit(deposit.coinType);
523
+ const marketValue = reserve.price
524
+ .multipliedBy(deposit.depositedCtokenAmount.multipliedBy(reserve.cTokenRatio))
525
+ .dividedBy(reserve.mintScale);
519
526
  depositValueUsd = depositValueUsd.plus(marketValue);
520
- unhealthyBorrowValueUsd = unhealthyBorrowValueUsd.plus(marketValue.multipliedBy(computeCloseLtv(config)));
527
+ unhealthyBorrowValueUsd = unhealthyBorrowValueUsd.plus(marketValue.multipliedBy(reserve.closeLtv));
521
528
  }
522
529
  let weightedBorrowedValueUsd = new BigNumber(0);
523
530
  for (const borrow of borrows) {
524
- const reserve = findReserveOrThrow(refreshedReserves, borrow.coinType);
525
- const config = reserve.config.element;
526
- const currentBorrowedAmountUnderlying = compoundBorrowedAmount(borrow.borrowedAmountUnderlying, decimalToBigNumber(reserve.cumulativeBorrowRate), borrow.cumulativeBorrowRateAtSnapshot);
527
- const marketValue = currentBorrowedAmountUnderlying
528
- .multipliedBy(decimalToBigNumber(reserve.price))
529
- .dividedBy(new BigNumber(10 ** reserve.mintDecimals));
530
- const borrowWeight = computeBorrowWeight(config);
531
- weightedBorrowedValueUsd = weightedBorrowedValueUsd.plus(marketValue.multipliedBy(borrowWeight));
531
+ const reserve = reserveHealth.borrow(borrow.coinType);
532
+ const marketValue = compoundBorrowedAmount(borrow.borrowedAmountUnderlying, reserve.cumulativeBorrowRate, borrow.cumulativeBorrowRateAtSnapshot)
533
+ .multipliedBy(reserve.price)
534
+ .dividedBy(reserve.mintScale);
535
+ weightedBorrowedValueUsd = weightedBorrowedValueUsd.plus(marketValue.multipliedBy(reserve.borrowWeight));
532
536
  }
533
537
  return {
534
538
  depositedValueUsd: depositValueUsd,
@@ -536,3 +540,46 @@ export function computeObligationHealthFromPositions(deposits, borrows, refreshe
536
540
  weightedBorrowedValueUsd,
537
541
  };
538
542
  }
543
+ const memoizeByCoinType = (derive) => {
544
+ const byCoinType = new Map();
545
+ return (coinType) => {
546
+ let values = byCoinType.get(coinType);
547
+ if (values === undefined) {
548
+ values = derive(coinType);
549
+ byCoinType.set(coinType, values);
550
+ }
551
+ return values;
552
+ };
553
+ };
554
+ /**
555
+ * The per-reserve inputs to `computeObligationHealth`, derived once per
556
+ * reserve and side on first lookup instead of once per position. Build one
557
+ * per pass over a fixed set of priced reserves and drop it after: the values
558
+ * are a snapshot, and a caller that later rewrites a reserve's price in place
559
+ * would otherwise keep reading the old one.
560
+ */
561
+ export function buildReserveHealthTable(reserves) {
562
+ return {
563
+ deposit: memoizeByCoinType((coinType) => {
564
+ const reserve = findReserveOrThrow(reserves, coinType);
565
+ return {
566
+ price: decimalToBigNumber(reserve.price),
567
+ cTokenRatio: cTokenRatio(reserve),
568
+ mintScale: new BigNumber(10 ** reserve.mintDecimals),
569
+ closeLtv: computeCloseLtv(reserve.config.element),
570
+ };
571
+ }),
572
+ borrow: memoizeByCoinType((coinType) => {
573
+ const reserve = findReserveOrThrow(reserves, coinType);
574
+ return {
575
+ price: decimalToBigNumber(reserve.price),
576
+ mintScale: new BigNumber(10 ** reserve.mintDecimals),
577
+ cumulativeBorrowRate: decimalToBigNumber(reserve.cumulativeBorrowRate),
578
+ borrowWeight: computeBorrowWeight(reserve.config.element),
579
+ };
580
+ }),
581
+ };
582
+ }
583
+ export function computeObligationHealthFromPositions(deposits, borrows, refreshedReserves) {
584
+ return computeObligationHealth(deposits, borrows, buildReserveHealthTable(refreshedReserves));
585
+ }