@suilend/sdk 12.4.0-next.g7a809d2 → 12.4.0-next.gd4b87be

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.4.0-next.g7a809d2","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.4.0-next.gd4b87be","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"}
package/strategies.d.ts CHANGED
@@ -12,6 +12,7 @@ import { StrategyType } from "./lib/strategyOwnerCap";
12
12
  import { ParsedObligation, ParsedReserve } from "./parsers";
13
13
  export declare const STRATEGY_E: number;
14
14
  export declare const STRATEGY_FLASH_REPAY_MAX_RESIDUAL_USD = 0.1;
15
+ export declare const STRATEGY_RATE_LIMITED_ERROR_MESSAGE = "This withdrawal exceeds the lending market's outflow limit for now. Withdraw a smaller amount, or wait and try again.";
15
16
  /**
16
17
  * Below this, a levered position is uneconomic no matter what the slippage
17
18
  * tolerance says. The aggregator's dust regime dominates: routes go multi-hop
@@ -360,7 +361,7 @@ export declare const strategyWithdrawTx: (reserveMap: Record<string, ParsedReser
360
361
  transaction: Transaction;
361
362
  withdrawnCoin?: TransactionArgument;
362
363
  }>;
363
- export declare const strategyMaxWithdrawTx: (reserveMap: Record<string, ParsedReserve>, rewardsMap: RewardsMap, lstMap: StrategyLstMap, strategyType: StrategyType, suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, cetusSdk: CetusSdk, cetusPartnerId: string, _address: string, strategyOwnerCapId: TransactionObjectInput, obligationId: string, _deposits: StrategyDeposit[], _borrowedAmount: BigNumber, withdrawCoinType: string, transaction: Transaction, dryRunTransaction: (transaction: Transaction, setGasBudget?: boolean) => Promise<SuiClientTypes.SimulateTransactionResult<{
364
+ export declare const strategyMaxWithdrawTx: (reserveMap: Record<string, ParsedReserve>, rewardsMap: RewardsMap, lstMap: StrategyLstMap, strategyType: StrategyType, suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, cetusSdk: CetusSdk, cetusPartnerId: string, _address: string, strategyOwnerCapId: TransactionObjectInput, obligationId: string, _deposits: StrategyDeposit[], _borrowedAmount: BigNumber, withdrawCoinType: string, transaction: Transaction, _dryRunTransaction: (transaction: Transaction, setGasBudget?: boolean) => Promise<SuiClientTypes.SimulateTransactionResult<{
364
365
  commandResults: true;
365
366
  }>>) => Promise<{
366
367
  deposits: StrategyDeposit[];
@@ -381,7 +382,7 @@ export declare const strategyDepositAdjustWithdrawTx: (reserveMap: Record<string
381
382
  borrowedAmount: BigNumber;
382
383
  transaction: Transaction;
383
384
  }>;
384
- export declare const strategyAdjustTx: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, cetusSdk: CetusSdk, cetusPartnerId: string, _address: string, strategyOwnerCapId: TransactionObjectInput, obligation: ParsedObligation, _deposits: StrategyDeposit[], _borrowedAmount: BigNumber, targetExposure: BigNumber, transaction: Transaction, dryRunTransaction: (transaction: Transaction, setGasBudget?: boolean) => Promise<SuiClientTypes.SimulateTransactionResult<{
385
+ export declare const strategyAdjustTx: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, suiGrpcClient: SuiGrpcClient, suilendClient: SuilendClient, cetusSdk: CetusSdk, cetusPartnerId: string, _address: string, strategyOwnerCapId: TransactionObjectInput, obligation: ParsedObligation, _deposits: StrategyDeposit[], _borrowedAmount: BigNumber, targetExposure: BigNumber, transaction: Transaction, _dryRunTransaction: (transaction: Transaction, setGasBudget?: boolean) => Promise<SuiClientTypes.SimulateTransactionResult<{
385
386
  commandResults: true;
386
387
  }>>) => Promise<{
387
388
  deposits: StrategyDeposit[];
package/strategies.js CHANGED
@@ -13,6 +13,25 @@ import { MMT_CONTRACT_PACKAGE_ID, MMT_VERSION_OBJECT_ID } from "./mmt.js";
13
13
  import { bisectionMethod, getWeightedBorrowsUsd } from "./utils/index.js";
14
14
  export const STRATEGY_E = 10 ** -7;
15
15
  export const STRATEGY_FLASH_REPAY_MAX_RESIDUAL_USD = 0.1;
16
+ export const STRATEGY_RATE_LIMITED_ERROR_MESSAGE = "This withdrawal exceeds the lending market's outflow limit for now. Withdraw a smaller amount, or wait and try again.";
17
+ const isRateLimitExceeded = (err) => /abort code: 2, in '[^']*::rate_limiter::process_qty'/.test(err instanceof Error ? err.message : String(err));
18
+ // Every step dry-runs and rolls back on failure, so the builders only see that
19
+ // no step landed. Recording why lets them name the market's outflow limit
20
+ // instead of reporting a generic failure.
21
+ const trackRateLimit = (dryRunTransaction) => {
22
+ const state = { exceeded: false };
23
+ const tracked = (async (...args) => {
24
+ try {
25
+ return await dryRunTransaction(...args);
26
+ }
27
+ catch (err) {
28
+ if (isRateLimitExceeded(err))
29
+ state.exceeded = true;
30
+ throw err;
31
+ }
32
+ });
33
+ return { rateLimit: state, dryRunTransaction: tracked };
34
+ };
16
35
  /**
17
36
  * Cetus applies its slippage check against its OWN quote, so a wrong quote yields
18
37
  * a wrong floor. Measured on a mainnet unwind of a sub-dollar position: the
@@ -620,20 +639,32 @@ reserveMap,
620
639
  // Strategy
621
640
  lstMap, strategyType, deposits, borrowedAmount) => {
622
641
  const depositReserves = getStrategyDepositReserves(reserveMap, strategyType);
642
+ const borrowReserve = getStrategyBorrowReserve(reserveMap, strategyType);
623
643
  const depositReserve = (depositReserves.base ?? depositReserves.lst); // Must have LST if no base
624
- const depositedAmount = deposits.find((d) => d.coinType === depositReserve.coinType)
625
- ?.depositedAmount ?? new BigNumber(0);
626
- if (depositedAmount.lte(0))
627
- return new BigNumber(0);
628
- const simulateWith = (newDepositedAmount) => getStrategySimulatedObligation(reserveMap, lstMap, strategyType, addOrInsertStrategyDeposit(deposits, {
644
+ const simulateWith = (additionalAmount) => getStrategySimulatedObligation(reserveMap, lstMap, strategyType, addOrInsertStrategyDeposit(deposits, {
629
645
  coinType: depositReserve.coinType,
630
- depositedAmount: newDepositedAmount.minus(depositedAmount),
646
+ depositedAmount: additionalAmount,
631
647
  }), borrowedAmount);
632
- const withinMaxExposure = bisectionMethod(depositedAmount, depositedAmount.times(2), (n) => getStrategyExposure(reserveMap, lstMap, strategyType, simulateWith(n)).lte(STRATEGY_TYPE_EXPOSURE_MAP[strategyType].max));
633
- const fullHealth = bisectionMethod(depositedAmount, depositedAmount.times(2), (n) => getStrategyHealthPercent(reserveMap, lstMap, strategyType, simulateWith(n)).eq(100));
634
- return BigNumber.max(withinMaxExposure, fullHealth)
635
- .minus(depositedAmount)
636
- .decimalPlaces(depositReserve.token.decimals, BigNumber.ROUND_UP);
648
+ const isRebalanced = (additionalAmount) => {
649
+ const obligation = simulateWith(additionalAmount);
650
+ return (getStrategyHealthPercent(reserveMap, lstMap, strategyType, obligation).eq(100) &&
651
+ getStrategyExposure(reserveMap, lstMap, strategyType, obligation).lte(STRATEGY_TYPE_EXPOSURE_MAP[strategyType].max));
652
+ };
653
+ if (isRebalanced(new BigNumber(0)))
654
+ return new BigNumber(0);
655
+ // Both conditions only get easier as base is added, so a doubled bound
656
+ // that satisfies them brackets the smallest amount that does.
657
+ let upperBound = BigNumber.max(deposits.find((d) => d.coinType === depositReserve.coinType)
658
+ ?.depositedAmount ?? new BigNumber(0), borrowedAmount.times(borrowReserve.price).div(depositReserve.price));
659
+ let upperBoundRebalances = isRebalanced(upperBound);
660
+ for (let i = 0; i < 30 && !upperBoundRebalances; i++) {
661
+ upperBound = upperBound.times(2);
662
+ upperBoundRebalances = isRebalanced(upperBound);
663
+ }
664
+ if (!upperBoundRebalances)
665
+ throw new Error("No deposit brings the position back within its borrow limit");
666
+ const smallest = bisectionMethod(new BigNumber(0), upperBound, isRebalanced);
667
+ return (smallest.gt(0) ? smallest : upperBound).decimalPlaces(depositReserve.token.decimals, BigNumber.ROUND_UP);
637
668
  };
638
669
  // Simulate
639
670
  export const strategySimulateLoopToExposure = (
@@ -2354,7 +2385,8 @@ export const strategyMaxWithdrawTx = async (
2354
2385
  // AppContext
2355
2386
  reserveMap, rewardsMap,
2356
2387
  // Strategy
2357
- lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, withdrawCoinType, transaction, dryRunTransaction) => {
2388
+ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, withdrawCoinType, transaction, _dryRunTransaction) => {
2389
+ const { rateLimit, dryRunTransaction } = trackRateLimit(_dryRunTransaction);
2358
2390
  const strategyInfo = STRATEGY_TYPE_INFO_MAP[strategyType];
2359
2391
  const lst = strategyInfo.depositLstCoinType !== undefined
2360
2392
  ? lstMap[strategyInfo.depositLstCoinType]
@@ -2380,25 +2412,37 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2380
2412
  let borrowedAmount = _borrowedAmount;
2381
2413
  // 1) Unloop to 1x (base+LST: no LST and no borrows, LST: no borrows)
2382
2414
  if (borrowedAmount.gt(0)) {
2383
- // 1.0) Above the borrow limit no stepwise withdraw can land, so rebalance first
2384
- if (!isStrategyWithinBorrowLimit(reserveMap, lstMap, strategyType, deposits, borrowedAmount)) {
2415
+ const unloopToOne = (fromDeposits, fromBorrowedAmount, fromTransaction) => strategyUnloopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, fromDeposits, fromBorrowedAmount, undefined, // Don't pass targetBorrowedAmount
2416
+ new BigNumber(1), // Pass targetExposure
2417
+ fromTransaction, dryRunTransaction);
2418
+ // 1.1) Unloop
2419
+ let unlooped;
2420
+ let plainError;
2421
+ try {
2422
+ unlooped = await unloopToOne(deposits, borrowedAmount, Transaction.from(transaction));
2423
+ }
2424
+ catch (err) {
2425
+ plainError = err;
2426
+ }
2427
+ // 1.1b) Above the borrow limit no stepwise withdraw can land, so rebalance and retry
2428
+ if ((unlooped === undefined || unlooped.borrowedAmount.gt(STRATEGY_E)) &&
2429
+ !isStrategyWithinBorrowLimit(reserveMap, lstMap, strategyType, deposits, borrowedAmount)) {
2385
2430
  try {
2386
- ({ deposits, borrowedAmount, transaction } =
2387
- await strategyRebalanceWithinBorrowLimitTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, Transaction.from(transaction), dryRunTransaction));
2431
+ const rebalanced = await strategyRebalanceWithinBorrowLimitTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, Transaction.from(transaction), dryRunTransaction);
2432
+ unlooped = await unloopToOne(rebalanced.deposits, rebalanced.borrowedAmount, rebalanced.transaction);
2388
2433
  }
2389
2434
  catch (err) {
2390
2435
  console.error(err);
2391
- throw new Error("Failed to repay borrows before withdrawing");
2392
2436
  }
2393
2437
  }
2394
- // 1.1) Unloop
2395
- const { deposits: newDeposits, borrowedAmount: newBorrowedAmount, transaction: newTransaction, } = await strategyUnloopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
2396
- new BigNumber(1), // Pass targetExposure
2397
- transaction, dryRunTransaction);
2438
+ if (unlooped === undefined)
2439
+ throw rateLimit.exceeded
2440
+ ? new Error(STRATEGY_RATE_LIMITED_ERROR_MESSAGE)
2441
+ : plainError;
2398
2442
  // 1.2) Update state
2399
- deposits = newDeposits;
2400
- borrowedAmount = newBorrowedAmount;
2401
- transaction = newTransaction;
2443
+ deposits = unlooped.deposits;
2444
+ borrowedAmount = unlooped.borrowedAmount;
2445
+ transaction = unlooped.transaction;
2402
2446
  console.log(`[strategyMaxWithdraw] unloop.update_state |`, JSON.stringify({
2403
2447
  deposits: deposits.map((d) => ({
2404
2448
  coinType: d.coinType,
@@ -2433,7 +2477,9 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2433
2477
  }
2434
2478
  catch (err) {
2435
2479
  console.error(err);
2436
- throw new Error("Failed to repay borrows before withdrawing");
2480
+ throw new Error(rateLimit.exceeded
2481
+ ? STRATEGY_RATE_LIMITED_ERROR_MESSAGE
2482
+ : "Failed to repay borrows before withdrawing");
2437
2483
  }
2438
2484
  repaidByAdjustRepay = true;
2439
2485
  }
@@ -2449,7 +2495,9 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2449
2495
  borrowedAmount
2450
2496
  .times(borrowReserve.price)
2451
2497
  .gte(STRATEGY_FLASH_REPAY_MAX_RESIDUAL_USD))
2452
- throw new Error("Failed to repay borrows before withdrawing");
2498
+ throw new Error(rateLimit.exceeded
2499
+ ? STRATEGY_RATE_LIMITED_ERROR_MESSAGE
2500
+ : "Failed to repay borrows before withdrawing");
2453
2501
  const flashLoanBorrowedAmount = borrowedAmount
2454
2502
  .times(1.1)
2455
2503
  .decimalPlaces(borrowReserve.token.decimals, BigNumber.ROUND_UP);
@@ -2595,7 +2643,9 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
2595
2643
  }
2596
2644
  catch (err) {
2597
2645
  console.error(err);
2598
- throw new Error("Failed to repay borrows before withdrawing");
2646
+ throw new Error(rateLimit.exceeded
2647
+ ? STRATEGY_RATE_LIMITED_ERROR_MESSAGE
2648
+ : "Failed to repay borrows before withdrawing");
2599
2649
  }
2600
2650
  }
2601
2651
  return { deposits, borrowedAmount, transaction };
@@ -3041,7 +3091,8 @@ export const strategyAdjustTx = async (
3041
3091
  // AppContext
3042
3092
  reserveMap,
3043
3093
  // Strategy
3044
- lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation, _deposits, _borrowedAmount, targetExposure, transaction, dryRunTransaction) => {
3094
+ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation, _deposits, _borrowedAmount, targetExposure, transaction, _dryRunTransaction) => {
3095
+ const { rateLimit, dryRunTransaction } = trackRateLimit(_dryRunTransaction);
3045
3096
  const strategyInfo = STRATEGY_TYPE_INFO_MAP[strategyType];
3046
3097
  const lst = strategyInfo.depositLstCoinType !== undefined
3047
3098
  ? lstMap[strategyInfo.depositLstCoinType]
@@ -3049,7 +3100,7 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
3049
3100
  const depositReserves = getStrategyDepositReserves(reserveMap, strategyType);
3050
3101
  const borrowReserve = getStrategyBorrowReserve(reserveMap, strategyType);
3051
3102
  const defaultCurrencyReserve = getStrategyDefaultCurrencyReserve(reserveMap, strategyType);
3052
- let exposure = getStrategyExposure(reserveMap, lstMap, strategyType, obligation);
3103
+ const exposure = getStrategyExposure(reserveMap, lstMap, strategyType, obligation);
3053
3104
  console.log(`[strategyAdjust] args |`, JSON.stringify({
3054
3105
  _address,
3055
3106
  strategyOwnerCapId,
@@ -3062,31 +3113,56 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
3062
3113
  targetExposure: targetExposure.toFixed(20),
3063
3114
  }, null, 2));
3064
3115
  //
3065
- let deposits = cloneDeep(_deposits);
3066
- let borrowedAmount = _borrowedAmount;
3067
- // 1) Above the borrow limit no stepwise withdraw can land, so rebalance first
3068
- const rebalanced = borrowedAmount.gt(0) && !isStrategyWithinBorrowLimit(reserveMap, lstMap, strategyType, deposits, borrowedAmount);
3069
- if (rebalanced) {
3070
- ({ deposits, borrowedAmount, transaction } =
3071
- await strategyRebalanceWithinBorrowLimitTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, transaction, dryRunTransaction));
3072
- exposure = getStrategyExposure(reserveMap, lstMap, strategyType, getStrategySimulatedObligation(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
3073
- }
3074
- // 2) Loop or unloop to target exposure
3075
- let result;
3076
- if (targetExposure.gt(exposure))
3077
- result = await strategyLoopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
3116
+ const deposits = cloneDeep(_deposits);
3117
+ const borrowedAmount = _borrowedAmount;
3118
+ if (targetExposure.eq(exposure))
3119
+ return { deposits, borrowedAmount, transaction };
3120
+ const loopOrUnloop = (fromDeposits, fromBorrowedAmount, fromExposure, fromTransaction) => targetExposure.gt(fromExposure)
3121
+ ? strategyLoopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, fromDeposits, fromBorrowedAmount, undefined, // Don't pass targetBorrowedAmount
3078
3122
  targetExposure, // Pass targetExposure
3079
- transaction);
3080
- else if (targetExposure.lt(exposure))
3081
- result = await strategyUnloopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
3123
+ fromTransaction)
3124
+ : strategyUnloopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, fromDeposits, fromBorrowedAmount, undefined, // Don't pass targetBorrowedAmount
3082
3125
  targetExposure, // Pass targetExposure
3083
- transaction, dryRunTransaction);
3084
- else
3085
- return { deposits, borrowedAmount, transaction };
3086
- if (result.borrowedAmount.eq(borrowedAmount)) {
3087
- if (rebalanced)
3088
- return { deposits, borrowedAmount, transaction };
3089
- throw new Error("Failed to adjust leverage");
3126
+ fromTransaction, dryRunTransaction);
3127
+ // 1) Loop or unloop to target exposure
3128
+ let plainError;
3129
+ try {
3130
+ const result = await loopOrUnloop(deposits, borrowedAmount, exposure, Transaction.from(transaction));
3131
+ if (!result.borrowedAmount.eq(borrowedAmount))
3132
+ return result;
3090
3133
  }
3091
- return result;
3134
+ catch (err) {
3135
+ plainError = err;
3136
+ }
3137
+ // 2) Above the borrow limit no stepwise withdraw can land, so rebalance and retry
3138
+ if (borrowedAmount.gt(0) &&
3139
+ !isStrategyWithinBorrowLimit(reserveMap, lstMap, strategyType, deposits, borrowedAmount)) {
3140
+ let rebalanced;
3141
+ try {
3142
+ rebalanced = await strategyRebalanceWithinBorrowLimitTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, Transaction.from(transaction), dryRunTransaction);
3143
+ }
3144
+ catch (err) {
3145
+ console.error(err);
3146
+ }
3147
+ if (rebalanced !== undefined) {
3148
+ const rebalancedExposure = getStrategyExposure(reserveMap, lstMap, strategyType, getStrategySimulatedObligation(reserveMap, lstMap, strategyType, rebalanced.deposits, rebalanced.borrowedAmount));
3149
+ if (targetExposure.eq(rebalancedExposure))
3150
+ return rebalanced;
3151
+ try {
3152
+ const result = await loopOrUnloop(rebalanced.deposits, rebalanced.borrowedAmount, rebalancedExposure, Transaction.from(rebalanced.transaction));
3153
+ return result.borrowedAmount.eq(rebalanced.borrowedAmount)
3154
+ ? rebalanced
3155
+ : result;
3156
+ }
3157
+ catch (err) {
3158
+ console.error(err);
3159
+ return rebalanced;
3160
+ }
3161
+ }
3162
+ }
3163
+ if (rateLimit.exceeded)
3164
+ throw new Error(STRATEGY_RATE_LIMITED_ERROR_MESSAGE);
3165
+ if (plainError !== undefined)
3166
+ throw plainError;
3167
+ throw new Error("Failed to adjust leverage");
3092
3168
  };