@suilend/sdk 1.1.86 → 1.1.87

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/strategies.js CHANGED
@@ -12,16 +12,95 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getStrategyUnclaimedRewardsAmount = exports.fetchStrategyGlobalTvlAmountUsdMap = exports.getStrategyExposure = exports.getStrategyTvlAmount = exports.getStrategyBorrowedAmount = exports.getStrategyDepositedAmount = exports.getStrategySimulatedObligation = exports.getStrategyDefaultCurrencyReserve = exports.getStrategyBorrowReserve = exports.getStrategyDepositReserves = exports.STRATEGY_TYPE_EXPOSURE_MAP = exports.getStrategyLstRedeemFee = exports.getStrategyLstMintFee = exports.fetchStrategyLstMap = exports.getStrategySuiReserve = exports.hasStrategyPosition = exports.addOrInsertStrategyDeposit = exports.LST_DECIMALS = exports.STRATEGY_E = void 0;
15
+ exports.strategyAdjustTx = exports.strategyDepositAdjustWithdrawTx = exports.strategyMaxWithdrawTx = exports.strategyWithdrawTx = exports.strategyDepositAndLoopToExposureTx = exports.strategyDepositTx = exports.strategyUnloopToExposureTx = exports.strategyLoopToExposureTx = exports.getStrategyLiquidationPrice = exports.getStrategyHealthPercent = exports.getStrategyAprPercent = exports.getStrategyUnclaimedRewardsAmount = exports.fetchStrategyGlobalTvlAmountUsdMap = exports.strategySimulateDepositAndLoopToExposure = exports.strategySimulateDeposit = exports.strategySimulateLoopToExposure = exports.getStrategyStepMaxWithdrawnAmount = exports.getStrategyStepMaxBorrowedAmount = exports.getStrategyExposure = exports.getStrategyTvlAmount = exports.getStrategyBorrowedAmount = exports.getStrategyDepositedAmount = exports.getStrategySimulatedObligation = exports.getStrategyDefaultCurrencyReserve = exports.getStrategyBorrowReserve = exports.getStrategyDepositReserves = exports.STRATEGY_TYPE_EXPOSURE_MAP = exports.getStrategyLstRedeemFee = exports.getStrategyLstMintFee = exports.fetchStrategyLstMap = exports.getStrategySuiReserve = exports.hasStrategyPosition = exports.addOrInsertStrategyDeposit = exports.getReserveSafeDepositLimit = exports.STRATEGY_TYPE_FLASH_LOAN_OBJ_MAP = exports.StrategyFlashLoanProvider = exports.LST_DECIMALS = exports.STRATEGY_E = void 0;
16
+ const transactions_1 = require("@mysten/sui/transactions");
16
17
  const utils_1 = require("@mysten/sui/utils");
17
18
  const bignumber_js_1 = __importDefault(require("bignumber.js"));
19
+ const bn_js_1 = require("bn.js");
18
20
  const lodash_1 = require("lodash");
19
21
  const springsui_sdk_1 = require("@suilend/springsui-sdk");
20
22
  const sui_fe_1 = require("@suilend/sui-fe");
23
+ const lib_1 = require("./lib");
21
24
  const liquidityMining_1 = require("./lib/liquidityMining");
22
25
  const strategyOwnerCap_1 = require("./lib/strategyOwnerCap");
26
+ const mmt_1 = require("./mmt");
27
+ const utils_2 = require("./utils");
23
28
  exports.STRATEGY_E = 10 ** -7;
24
29
  exports.LST_DECIMALS = 9;
30
+ var StrategyFlashLoanProvider;
31
+ (function (StrategyFlashLoanProvider) {
32
+ StrategyFlashLoanProvider["MMT"] = "mmt";
33
+ })(StrategyFlashLoanProvider || (exports.StrategyFlashLoanProvider = StrategyFlashLoanProvider = {}));
34
+ exports.STRATEGY_TYPE_FLASH_LOAN_OBJ_MAP = {
35
+ [strategyOwnerCap_1.StrategyType.sSUI_SUI_LOOPING]: {
36
+ provider: StrategyFlashLoanProvider.MMT,
37
+ poolId: "0x9c92c5b8e9d83e485fb4c86804ac8b920bb0beaace5e61a5b0239218f627f8e9", // xSUI-SUI 0.01% https://app.mmt.finance/liquidity/0x9c92c5b8e9d83e485fb4c86804ac8b920bb0beaace5e61a5b0239218f627f8e9
38
+ coinTypeA: "0x2b6602099970374cf58a2a1b9d96f005fccceb81e92eb059873baf420eb6c717::x_sui::X_SUI",
39
+ coinTypeB: sui_fe_1.NORMALIZED_SUI_COINTYPE,
40
+ borrowA: false,
41
+ feePercent: 0.01,
42
+ },
43
+ [strategyOwnerCap_1.StrategyType.stratSUI_SUI_LOOPING]: {
44
+ provider: StrategyFlashLoanProvider.MMT,
45
+ poolId: "0x9c92c5b8e9d83e485fb4c86804ac8b920bb0beaace5e61a5b0239218f627f8e9", // xSUI-SUI 0.01% https://app.mmt.finance/liquidity/0x9c92c5b8e9d83e485fb4c86804ac8b920bb0beaace5e61a5b0239218f627f8e9
46
+ coinTypeA: "0x2b6602099970374cf58a2a1b9d96f005fccceb81e92eb059873baf420eb6c717::x_sui::X_SUI",
47
+ coinTypeB: sui_fe_1.NORMALIZED_SUI_COINTYPE,
48
+ borrowA: false,
49
+ feePercent: 0.01,
50
+ },
51
+ [strategyOwnerCap_1.StrategyType.USDC_sSUI_SUI_LOOPING]: {
52
+ provider: StrategyFlashLoanProvider.MMT,
53
+ poolId: "0x737ec6a4d3ed0c7e6cc18d8ba04e7ffd4806b726c97efd89867597368c4d06a9", // suiUSDT-USDC 0.001% https://app.mmt.finance/liquidity/0x737ec6a4d3ed0c7e6cc18d8ba04e7ffd4806b726c97efd89867597368c4d06a9
54
+ coinTypeA: sui_fe_1.NORMALIZED_suiUSDT_COINTYPE,
55
+ coinTypeB: sui_fe_1.NORMALIZED_USDC_COINTYPE,
56
+ borrowA: false,
57
+ feePercent: 0.001,
58
+ },
59
+ [strategyOwnerCap_1.StrategyType.AUSD_sSUI_SUI_LOOPING]: {
60
+ provider: StrategyFlashLoanProvider.MMT,
61
+ poolId: "0x900f25b27d2b1686886277d763223988d802f3b6152d02872c382d4dce05e25b", // AUSD-USDC 0.01% https://app.mmt.finance/liquidity/0x900f25b27d2b1686886277d763223988d802f3b6152d02872c382d4dce05e25b
62
+ coinTypeA: sui_fe_1.NORMALIZED_AUSD_COINTYPE,
63
+ coinTypeB: sui_fe_1.NORMALIZED_USDC_COINTYPE,
64
+ borrowA: true,
65
+ feePercent: 0.01,
66
+ },
67
+ [strategyOwnerCap_1.StrategyType.xBTC_sSUI_SUI_LOOPING]: {
68
+ provider: StrategyFlashLoanProvider.MMT,
69
+ poolId: "0x57a662791cea065610455797dfd2751a3c10d929455d3ea88154a2b40cf6614e", // xBTC-wBTC 0.01% https://app.mmt.finance/liquidity/0x57a662791cea065610455797dfd2751a3c10d929455d3ea88154a2b40cf6614e
70
+ coinTypeA: sui_fe_1.NORMALIZED_xBTC_COINTYPE,
71
+ coinTypeB: sui_fe_1.NORMALIZED_wBTC_COINTYPE,
72
+ borrowA: true,
73
+ feePercent: 0.01,
74
+ },
75
+ [strategyOwnerCap_1.StrategyType.xBTC_wBTC_LOOPING]: {
76
+ provider: StrategyFlashLoanProvider.MMT,
77
+ poolId: "0x57a662791cea065610455797dfd2751a3c10d929455d3ea88154a2b40cf6614e", // xBTC-wBTC 0.01% https://app.mmt.finance/liquidity/0x57a662791cea065610455797dfd2751a3c10d929455d3ea88154a2b40cf6614e
78
+ coinTypeA: sui_fe_1.NORMALIZED_xBTC_COINTYPE,
79
+ coinTypeB: sui_fe_1.NORMALIZED_wBTC_COINTYPE,
80
+ borrowA: true,
81
+ feePercent: 0.01,
82
+ },
83
+ [strategyOwnerCap_1.StrategyType.suiUSDT_sSUI_SUI_LOOPING]: {
84
+ provider: StrategyFlashLoanProvider.MMT,
85
+ poolId: "0x737ec6a4d3ed0c7e6cc18d8ba04e7ffd4806b726c97efd89867597368c4d06a9", // suiUSDT-USDC 0.001% https://app.mmt.finance/liquidity/0x737ec6a4d3ed0c7e6cc18d8ba04e7ffd4806b726c97efd89867597368c4d06a9
86
+ coinTypeA: sui_fe_1.NORMALIZED_suiUSDT_COINTYPE,
87
+ coinTypeB: sui_fe_1.NORMALIZED_USDC_COINTYPE,
88
+ borrowA: true,
89
+ feePercent: 0.001,
90
+ },
91
+ };
92
+ const getReserveSafeDepositLimit = (reserve) => {
93
+ // Calculate safe deposit limit (subtract 10 mins of deposit APR from cap)
94
+ const tenMinsDepositAprPercent = reserve.depositAprPercent
95
+ .div(sui_fe_1.MS_PER_YEAR)
96
+ .times(10 * 60 * 1000);
97
+ const safeDepositLimit = reserve.config.depositLimit.minus(reserve.depositedAmount.times(tenMinsDepositAprPercent.div(100)));
98
+ const safeDepositLimitUsd = reserve.config.depositLimitUsd.minus(reserve.depositedAmount
99
+ .times(reserve.maxPrice)
100
+ .times(tenMinsDepositAprPercent.div(100)));
101
+ return { safeDepositLimit, safeDepositLimitUsd };
102
+ };
103
+ exports.getReserveSafeDepositLimit = getReserveSafeDepositLimit;
25
104
  const addOrInsertStrategyDeposit = (_deposits, deposit) => {
26
105
  const deposits = (0, lodash_1.cloneDeep)(_deposits);
27
106
  const existingDeposit = deposits.find((d) => d.coinType === deposit.coinType);
@@ -336,7 +415,240 @@ lstMap, strategyType, obligation) => {
336
415
  : depositedAmountUsd.div(depositedAmountUsd.minus(borrowedAmountUsd));
337
416
  };
338
417
  exports.getStrategyExposure = getStrategyExposure;
418
+ const getStrategyStepMaxBorrowedAmount = (
419
+ // AppContext
420
+ reserveMap,
421
+ // Strategy
422
+ lstMap, strategyType, deposits, borrowedAmount) => {
423
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
424
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
425
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
426
+ //
427
+ const obligation = (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount);
428
+ const borrowFeePercent = borrowReserve.config.borrowFeeBps / 100;
429
+ // "Borrows cannot exceed borrow limit"
430
+ return !obligation ||
431
+ obligation.maxPriceWeightedBorrowsUsd.gt(obligation.minPriceBorrowLimitUsd)
432
+ ? new bignumber_js_1.default(0)
433
+ : obligation.minPriceBorrowLimitUsd
434
+ .minus(obligation.maxPriceWeightedBorrowsUsd)
435
+ .div(borrowReserve.maxPrice.times(borrowReserve.config.borrowWeightBps.div(10000)))
436
+ .div(1 + borrowFeePercent / 100);
437
+ };
438
+ exports.getStrategyStepMaxBorrowedAmount = getStrategyStepMaxBorrowedAmount;
439
+ const getStrategyStepMaxWithdrawnAmount = (
440
+ // AppContext
441
+ reserveMap,
442
+ // Strategy
443
+ lstMap, strategyType, deposits, borrowedAmount, withdrawCoinType) => {
444
+ var _a, _b;
445
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
446
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
447
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
448
+ const withdrawReserve = reserveMap[withdrawCoinType];
449
+ //
450
+ const obligation = (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount);
451
+ return bignumber_js_1.default.min(
452
+ // "Withdraw is unhealthy"
453
+ !obligation ||
454
+ obligation.maxPriceWeightedBorrowsUsd.gt(obligation.minPriceBorrowLimitUsd)
455
+ ? new bignumber_js_1.default(0)
456
+ : withdrawReserve.config.openLtvPct > 0
457
+ ? obligation.minPriceBorrowLimitUsd
458
+ .minus(obligation.maxPriceWeightedBorrowsUsd)
459
+ .div(withdrawReserve.minPrice)
460
+ .div(withdrawReserve.config.openLtvPct / 100)
461
+ : sui_fe_1.MAX_U64, // Infinity
462
+ (_b = (_a = deposits.find((deposit) => deposit.coinType === withdrawReserve.coinType)) === null || _a === void 0 ? void 0 : _a.depositedAmount) !== null && _b !== void 0 ? _b : new bignumber_js_1.default(0)).decimalPlaces(withdrawReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
463
+ };
464
+ exports.getStrategyStepMaxWithdrawnAmount = getStrategyStepMaxWithdrawnAmount;
339
465
  // Simulate
466
+ const strategySimulateLoopToExposure = (
467
+ // AppContext
468
+ reserveMap,
469
+ // Strategy
470
+ lstMap, strategyType, _deposits, _borrowedAmount, _targetBorrowedAmount, _targetExposure) => {
471
+ var _a, _b, _c, _d, _e, _f, _g;
472
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
473
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
474
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
475
+ const loopingDepositReserve = ((_a = depositReserves.lst) !== null && _a !== void 0 ? _a : depositReserves.base); // Must have base if no LST
476
+ //
477
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
478
+ let borrowedAmount = _borrowedAmount;
479
+ const tvlAmountUsd = (0, exports.getStrategyTvlAmount)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)).times(defaultCurrencyReserve.price);
480
+ const targetBorrowedAmount = _targetBorrowedAmount !== null && _targetBorrowedAmount !== void 0 ? _targetBorrowedAmount : tvlAmountUsd
481
+ .times(_targetExposure.minus(1))
482
+ .div(borrowReserve.price)
483
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
484
+ // Base+LST or LST only
485
+ if (loopingDepositReserve.coinType === ((_b = depositReserves.lst) === null || _b === void 0 ? void 0 : _b.coinType)) {
486
+ for (let i = 0; i < 30; i++) {
487
+ const exposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
488
+ const pendingBorrowedAmount = targetBorrowedAmount.minus(borrowedAmount);
489
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E))
490
+ break;
491
+ // 1) Borrow SUI
492
+ // 1.1) Max
493
+ const stepMaxBorrowedAmount = (0, exports.getStrategyStepMaxBorrowedAmount)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)
494
+ .times(0.9) // 10% buffer
495
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
496
+ const stepMaxDepositedAmount = new bignumber_js_1.default(stepMaxBorrowedAmount.minus((0, exports.getStrategyLstMintFee)(lstMap, loopingDepositReserve.coinType, stepMaxBorrowedAmount)))
497
+ .times((_d = (_c = lstMap === null || lstMap === void 0 ? void 0 : lstMap[depositReserves.lst.coinType]) === null || _c === void 0 ? void 0 : _c.suiToLstExchangeRate) !== null && _d !== void 0 ? _d : new bignumber_js_1.default(1))
498
+ .decimalPlaces(exports.LST_DECIMALS, bignumber_js_1.default.ROUND_DOWN);
499
+ // 1.2) Borrow
500
+ const stepBorrowedAmount = bignumber_js_1.default.min(pendingBorrowedAmount, stepMaxBorrowedAmount).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
501
+ const isMaxBorrow = stepBorrowedAmount.eq(stepMaxBorrowedAmount);
502
+ // 1.3) Update state
503
+ borrowedAmount = borrowedAmount.plus(stepBorrowedAmount);
504
+ // 2) Deposit LST
505
+ // 2.1) Stake SUI for LST
506
+ // 2.2) Deposit
507
+ const stepDepositedAmount = new bignumber_js_1.default(stepBorrowedAmount.minus((0, exports.getStrategyLstMintFee)(lstMap, loopingDepositReserve.coinType, stepBorrowedAmount)))
508
+ .times((_f = (_e = lstMap === null || lstMap === void 0 ? void 0 : lstMap[depositReserves.lst.coinType]) === null || _e === void 0 ? void 0 : _e.suiToLstExchangeRate) !== null && _f !== void 0 ? _f : new bignumber_js_1.default(1))
509
+ .decimalPlaces(exports.LST_DECIMALS, bignumber_js_1.default.ROUND_DOWN);
510
+ const isMaxDeposit = stepDepositedAmount.eq(stepMaxDepositedAmount);
511
+ // 2.3) Update state
512
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
513
+ coinType: loopingDepositReserve.coinType,
514
+ depositedAmount: stepDepositedAmount,
515
+ });
516
+ }
517
+ }
518
+ // Base only
519
+ else if (loopingDepositReserve.coinType === ((_g = depositReserves.base) === null || _g === void 0 ? void 0 : _g.coinType)) {
520
+ const borrowToBaseExchangeRate = new bignumber_js_1.default(1); // Assume 1:1 exchange rate
521
+ for (let i = 0; i < 30; i++) {
522
+ const exposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
523
+ const pendingBorrowedAmount = targetBorrowedAmount.minus(borrowedAmount);
524
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E))
525
+ break;
526
+ // 1) Borrow
527
+ // 1.1) Max
528
+ const stepMaxBorrowedAmount = (0, exports.getStrategyStepMaxBorrowedAmount)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)
529
+ .times(0.9) // 10% buffer
530
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
531
+ const stepMaxDepositedAmount = stepMaxBorrowedAmount
532
+ .times(borrowToBaseExchangeRate)
533
+ .decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
534
+ // 1.2) Borrow
535
+ const stepBorrowedAmount = bignumber_js_1.default.min(pendingBorrowedAmount, stepMaxBorrowedAmount).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
536
+ const isMaxBorrow = stepBorrowedAmount.eq(stepMaxBorrowedAmount);
537
+ // 1.3) Update state
538
+ borrowedAmount = borrowedAmount.plus(stepBorrowedAmount);
539
+ // 2) Deposit base
540
+ // 2.1) Swap borrows for base
541
+ // 2.2) Deposit
542
+ const stepDepositedAmount = stepBorrowedAmount
543
+ .times(borrowToBaseExchangeRate)
544
+ .decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
545
+ const isMaxDeposit = stepDepositedAmount.eq(stepMaxDepositedAmount);
546
+ // 2.3) Update state
547
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
548
+ coinType: loopingDepositReserve.coinType,
549
+ depositedAmount: stepDepositedAmount,
550
+ });
551
+ }
552
+ }
553
+ else {
554
+ throw new Error("No LST or base reserve found"); // Should not happen
555
+ }
556
+ return {
557
+ deposits,
558
+ borrowedAmount,
559
+ obligation: (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount),
560
+ };
561
+ };
562
+ exports.strategySimulateLoopToExposure = strategySimulateLoopToExposure;
563
+ const strategySimulateDeposit = (
564
+ // AppContext
565
+ reserveMap,
566
+ // Strategy
567
+ lstMap, strategyType, _deposits, _borrowedAmount, deposit) => {
568
+ var _a, _b, _c, _d;
569
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
570
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
571
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
572
+ const depositReserve = ((_a = depositReserves.base) !== null && _a !== void 0 ? _a : depositReserves.lst); // Must have LST if no base
573
+ //
574
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
575
+ const borrowedAmount = _borrowedAmount;
576
+ // 1) Deposit
577
+ // 1.1) SUI
578
+ if ((0, sui_fe_1.isSui)(deposit.coinType)) {
579
+ if (depositReserves.lst === undefined)
580
+ throw new Error("LST reserve not found");
581
+ const suiToLstExchangeRate = (_c = (_b = lstMap === null || lstMap === void 0 ? void 0 : lstMap[depositReserves.lst.coinType]) === null || _b === void 0 ? void 0 : _b.suiToLstExchangeRate) !== null && _c !== void 0 ? _c : new bignumber_js_1.default(1);
582
+ const suiAmount = deposit.depositedAmount;
583
+ const lstAmount = new bignumber_js_1.default(suiAmount
584
+ .minus((0, exports.getStrategyLstMintFee)(lstMap, depositReserves.lst.coinType, suiAmount))
585
+ .times(suiToLstExchangeRate)).decimalPlaces(exports.LST_DECIMALS, bignumber_js_1.default.ROUND_DOWN);
586
+ // 1.1.1) Split coins
587
+ // 1.1.2) Stake SUI for LST
588
+ // 1.1.3) Deposit LST (1x exposure)
589
+ // 1.1.4) Update state
590
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
591
+ coinType: depositReserves.lst.coinType,
592
+ depositedAmount: lstAmount,
593
+ });
594
+ }
595
+ // 1.2) LST
596
+ else if (deposit.coinType === ((_d = depositReserves.lst) === null || _d === void 0 ? void 0 : _d.coinType)) {
597
+ // 1.2.1) Split coins
598
+ // 1.2.2) Deposit LST (1x exposure)
599
+ // 1.2.3) Update state
600
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, deposit);
601
+ }
602
+ // 1.3) Other
603
+ else {
604
+ // 1.3.1) Split coins
605
+ // 1.3.2) Deposit other (1x exposure)
606
+ // 1.3.3) Update state
607
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, deposit);
608
+ }
609
+ return {
610
+ deposits,
611
+ borrowedAmount,
612
+ obligation: (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount),
613
+ };
614
+ };
615
+ exports.strategySimulateDeposit = strategySimulateDeposit;
616
+ const strategySimulateDepositAndLoopToExposure = (
617
+ // AppContext
618
+ reserveMap,
619
+ // Strategy
620
+ lstMap, strategyType, _deposits, _borrowedAmount, deposit, targetExposure) => {
621
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
622
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
623
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
624
+ //
625
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
626
+ let borrowedAmount = _borrowedAmount;
627
+ let obligation = (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount);
628
+ // 1) Deposit (1x exposure)
629
+ // 1.1) Deposit
630
+ const { deposits: newDeposits, borrowedAmount: newBorrowedAmount, obligation: newObligation, } = (0, exports.strategySimulateDeposit)(reserveMap, lstMap, strategyType, deposits, borrowedAmount, deposit);
631
+ // 1.2) Update state
632
+ deposits = newDeposits;
633
+ borrowedAmount = newBorrowedAmount;
634
+ obligation = newObligation;
635
+ if (targetExposure.gt(1)) {
636
+ // 2) Loop to target exposure
637
+ // 2.1) Loop
638
+ const { deposits: newDeposits2, borrowedAmount: newBorrowedAmount2, obligation: newObligation2, } = (0, exports.strategySimulateLoopToExposure)(reserveMap, lstMap, strategyType, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
639
+ targetExposure);
640
+ // 2.2) Update state
641
+ deposits = newDeposits2;
642
+ borrowedAmount = newBorrowedAmount2;
643
+ obligation = newObligation2;
644
+ }
645
+ return {
646
+ deposits,
647
+ borrowedAmount,
648
+ obligation,
649
+ };
650
+ };
651
+ exports.strategySimulateDepositAndLoopToExposure = strategySimulateDepositAndLoopToExposure;
340
652
  // Stats
341
653
  // Stats - Global TVL
342
654
  const fetchStrategyGlobalTvlAmountUsdMap = () => __awaiter(void 0, void 0, void 0, function* () {
@@ -398,5 +710,1480 @@ exports.getStrategyUnclaimedRewardsAmount = getStrategyUnclaimedRewardsAmount;
398
710
  // Stats - History
399
711
  // Stats - Historical TVL
400
712
  // Stats - APR
713
+ const getStrategyAprPercent = (
714
+ // AppContext
715
+ reserveMap, lstStatsMap,
716
+ // UserContext
717
+ rewardMap,
718
+ // Strategy
719
+ lstMap, strategyType, obligation, exposure) => {
720
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
721
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
722
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
723
+ //
724
+ let _obligation;
725
+ if (!!obligation && (0, exports.hasStrategyPosition)(obligation)) {
726
+ _obligation = obligation;
727
+ }
728
+ else {
729
+ if (exposure === undefined)
730
+ return new bignumber_js_1.default(0); // Not shown in UI
731
+ _obligation = (0, exports.strategySimulateDepositAndLoopToExposure)(reserveMap, lstMap, strategyType, [], new bignumber_js_1.default(0), {
732
+ coinType: defaultCurrencyReserve.coinType,
733
+ depositedAmount: new bignumber_js_1.default(1), // Any number will do
734
+ }, exposure).obligation;
735
+ }
736
+ return (0, lib_1.getNetAprPercent)(_obligation, rewardMap, lstStatsMap, !obligation ||
737
+ !(0, exports.hasStrategyPosition)(obligation) ||
738
+ obligation.deposits.some((d) => !d.userRewardManager));
739
+ };
740
+ exports.getStrategyAprPercent = getStrategyAprPercent;
401
741
  // Stats - Health
742
+ const getStrategyHealthPercent = (
743
+ // AppContext
744
+ reserveMap,
745
+ // Strategy
746
+ lstMap, strategyType, obligation, exposure) => {
747
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
748
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
749
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
750
+ //
751
+ let _obligation;
752
+ if (!!obligation && (0, exports.hasStrategyPosition)(obligation)) {
753
+ _obligation = obligation;
754
+ }
755
+ else {
756
+ if (exposure === undefined)
757
+ return new bignumber_js_1.default(0); // Not shown in UI
758
+ _obligation = (0, exports.strategySimulateDepositAndLoopToExposure)(reserveMap, lstMap, strategyType, [], new bignumber_js_1.default(0), {
759
+ coinType: defaultCurrencyReserve.coinType,
760
+ depositedAmount: new bignumber_js_1.default(1), // Any number will do
761
+ }, exposure).obligation;
762
+ }
763
+ const weightedBorrowsUsd = (0, utils_2.getWeightedBorrowsUsd)(_obligation);
764
+ const borrowLimitUsd = _obligation.minPriceBorrowLimitUsd.times(depositReserves.base !== undefined
765
+ ? 0.99 // 1% buffer
766
+ : 0.999);
767
+ const liquidationThresholdUsd = _obligation.unhealthyBorrowValueUsd;
768
+ if (weightedBorrowsUsd.lt(borrowLimitUsd))
769
+ return new bignumber_js_1.default(100);
770
+ return new bignumber_js_1.default(100).minus(new bignumber_js_1.default(weightedBorrowsUsd.minus(borrowLimitUsd))
771
+ .div(liquidationThresholdUsd.minus(borrowLimitUsd))
772
+ .times(100));
773
+ };
774
+ exports.getStrategyHealthPercent = getStrategyHealthPercent;
402
775
  // Stats - Liquidation price
776
+ const getStrategyLiquidationPrice = (
777
+ // AppContext
778
+ reserveMap,
779
+ // Strategy
780
+ lstMap, strategyType, obligation, exposure) => {
781
+ var _a;
782
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
783
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
784
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
785
+ //
786
+ if (![
787
+ strategyOwnerCap_1.StrategyType.USDC_sSUI_SUI_LOOPING,
788
+ strategyOwnerCap_1.StrategyType.AUSD_sSUI_SUI_LOOPING,
789
+ strategyOwnerCap_1.StrategyType.xBTC_sSUI_SUI_LOOPING,
790
+ strategyOwnerCap_1.StrategyType.suiUSDT_sSUI_SUI_LOOPING,
791
+ ].includes(strategyType))
792
+ return new bignumber_js_1.default(0); // Not shown in UI
793
+ let _obligation;
794
+ if (!!obligation && (0, exports.hasStrategyPosition)(obligation)) {
795
+ _obligation = obligation;
796
+ }
797
+ else {
798
+ if (exposure === undefined)
799
+ return new bignumber_js_1.default(0); // Not shown in UI
800
+ _obligation = (0, exports.strategySimulateDepositAndLoopToExposure)(reserveMap, lstMap, strategyType, [], new bignumber_js_1.default(0), {
801
+ coinType: defaultCurrencyReserve.coinType,
802
+ depositedAmount: new bignumber_js_1.default(1), // Any number will do
803
+ }, exposure).obligation;
804
+ }
805
+ const baseDeposit = _obligation.deposits.find((d) => d.coinType === depositReserves.base.coinType);
806
+ const lstDeposit = _obligation.deposits.find((d) => d.coinType === depositReserves.lst.coinType);
807
+ if (!baseDeposit || baseDeposit.depositedAmount.eq(0))
808
+ return null;
809
+ const borrow = _obligation.borrows[0];
810
+ if (!borrow || borrow.borrowedAmount.eq(0))
811
+ return null;
812
+ const result = new bignumber_js_1.default(baseDeposit.depositedAmount
813
+ .times(depositReserves.base.price)
814
+ .times(+depositReserves.base.config.closeLtvPct / 100)).div(new bignumber_js_1.default(borrow.borrowedAmount.times(borrowReserve.config.borrowWeightBps.div(10000))).minus(((_a = lstDeposit === null || lstDeposit === void 0 ? void 0 : lstDeposit.depositedAmount) !== null && _a !== void 0 ? _a : new bignumber_js_1.default(0)).times(+depositReserves.lst.config.closeLtvPct / 100)));
815
+ return result;
816
+ };
817
+ exports.getStrategyLiquidationPrice = getStrategyLiquidationPrice;
818
+ // --------------------------------
819
+ const strategyLoopToExposureTx = (
820
+ // AppContext
821
+ reserveMap,
822
+ // Strategy
823
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, _targetBorrowedAmount, _targetExposure, // Must be defined if _targetBorrowedAmount is undefined
824
+ transaction) => __awaiter(void 0, void 0, void 0, function* () {
825
+ var _a, _b, _c, _d, _e;
826
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
827
+ const lst = strategyInfo.depositLstCoinType !== undefined
828
+ ? lstMap[strategyInfo.depositLstCoinType]
829
+ : undefined;
830
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
831
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
832
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
833
+ console.log(`[loopStrategyToExposure] args |`, JSON.stringify({
834
+ _address,
835
+ strategyOwnerCapId,
836
+ obligationId,
837
+ _deposits: _deposits.map((d) => ({
838
+ coinType: d.coinType,
839
+ depositedAmount: d.depositedAmount.toFixed(20),
840
+ })),
841
+ _borrowedAmount: _borrowedAmount.toFixed(20),
842
+ _targetBorrowedAmount: _targetBorrowedAmount === null || _targetBorrowedAmount === void 0 ? void 0 : _targetBorrowedAmount.toFixed(20),
843
+ _targetExposure: _targetExposure === null || _targetExposure === void 0 ? void 0 : _targetExposure.toFixed(20),
844
+ }, null, 2));
845
+ const loopingDepositReserve = ((_a = depositReserves.lst) !== null && _a !== void 0 ? _a : depositReserves.base); // Must have base if no LST
846
+ //
847
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
848
+ let borrowedAmount = _borrowedAmount;
849
+ const tvlAmountUsd = (0, exports.getStrategyTvlAmount)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)).times(defaultCurrencyReserve.price);
850
+ const targetBorrowedAmount = _targetBorrowedAmount !== null && _targetBorrowedAmount !== void 0 ? _targetBorrowedAmount : tvlAmountUsd
851
+ .times(_targetExposure.minus(1))
852
+ .div(borrowReserve.price)
853
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
854
+ console.log(`[loopStrategyToExposure] processed_args |`, JSON.stringify({
855
+ tvlAmountUsd: tvlAmountUsd.toFixed(20),
856
+ targetBorrowedAmount: targetBorrowedAmount.toFixed(20),
857
+ }));
858
+ // Base+LST or LST only
859
+ if (loopingDepositReserve.coinType === ((_b = depositReserves.lst) === null || _b === void 0 ? void 0 : _b.coinType)) {
860
+ for (let i = 0; i < 30; i++) {
861
+ const exposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
862
+ const pendingBorrowedAmount = targetBorrowedAmount.minus(borrowedAmount);
863
+ console.log(`[loopStrategyToExposure] ${i} start |`, JSON.stringify({
864
+ deposits: deposits.map((d) => ({
865
+ coinType: d.coinType,
866
+ depositedAmount: d.depositedAmount.toFixed(20),
867
+ })),
868
+ borrowedAmount: borrowedAmount.toFixed(20),
869
+ exposure: exposure.toFixed(20),
870
+ pendingBorrowedAmount: pendingBorrowedAmount.toFixed(20),
871
+ }, null, 2));
872
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E))
873
+ break;
874
+ // 1) Borrow SUI
875
+ // 1.1) Max
876
+ const stepMaxBorrowedAmount = (0, exports.getStrategyStepMaxBorrowedAmount)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)
877
+ .times(0.9) // 10% buffer
878
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
879
+ const stepMaxDepositedAmount = new bignumber_js_1.default(stepMaxBorrowedAmount.minus((0, exports.getStrategyLstMintFee)(lstMap, loopingDepositReserve.coinType, stepMaxBorrowedAmount)))
880
+ .times((_c = lst === null || lst === void 0 ? void 0 : lst.suiToLstExchangeRate) !== null && _c !== void 0 ? _c : 1)
881
+ .decimalPlaces(exports.LST_DECIMALS, bignumber_js_1.default.ROUND_DOWN);
882
+ console.log(`[loopStrategyToExposure] ${i} borrow_sui.max |`, JSON.stringify({
883
+ stepMaxBorrowedAmount: stepMaxBorrowedAmount.toFixed(20),
884
+ stepMaxDepositedAmount: stepMaxDepositedAmount.toFixed(20),
885
+ }, null, 2));
886
+ // 1.2) Borrow
887
+ const stepBorrowedAmount = bignumber_js_1.default.min(pendingBorrowedAmount, stepMaxBorrowedAmount).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
888
+ const isMaxBorrow = stepBorrowedAmount.eq(stepMaxBorrowedAmount);
889
+ console.log(`[loopStrategyToExposure] ${i} borrow_sui.borrow |`, JSON.stringify({
890
+ stepBorrowedAmount: stepBorrowedAmount.toFixed(20),
891
+ isMaxBorrow,
892
+ }, null, 2));
893
+ const [borrowedCoin] = (0, strategyOwnerCap_1.strategyBorrow)(borrowReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(borrowReserve.coinType), BigInt(stepBorrowedAmount
894
+ .times(10 ** borrowReserve.token.decimals)
895
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
896
+ .toString()), transaction);
897
+ // 1.3) Update state
898
+ borrowedAmount = borrowedAmount.plus(stepBorrowedAmount);
899
+ console.log(`[loopStrategyToExposure] ${i} borrow_sui.update_state |`, JSON.stringify({
900
+ deposits: deposits.map((d) => ({
901
+ coinType: d.coinType,
902
+ depositedAmount: d.depositedAmount.toFixed(20),
903
+ })),
904
+ borrowedAmount: borrowedAmount.toFixed(20),
905
+ }, null, 2));
906
+ // 2) Deposit LST
907
+ // 2.1) Stake SUI for LST
908
+ const stepLstCoin = lst.client.mint(transaction, borrowedCoin);
909
+ // 2.2) Deposit
910
+ const stepDepositedAmount = new bignumber_js_1.default(stepBorrowedAmount.minus((0, exports.getStrategyLstMintFee)(lstMap, loopingDepositReserve.coinType, stepBorrowedAmount)))
911
+ .times((_d = lst === null || lst === void 0 ? void 0 : lst.suiToLstExchangeRate) !== null && _d !== void 0 ? _d : 1)
912
+ .decimalPlaces(exports.LST_DECIMALS, bignumber_js_1.default.ROUND_DOWN);
913
+ const isMaxDeposit = stepDepositedAmount.eq(stepMaxDepositedAmount);
914
+ console.log(`[loopStrategyToExposure] ${i} deposit_lst.deposit |`, JSON.stringify({
915
+ stepDepositedAmount: stepDepositedAmount.toFixed(20),
916
+ isMaxDeposit,
917
+ }, null, 2));
918
+ (0, strategyOwnerCap_1.strategyDeposit)(stepLstCoin, loopingDepositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(loopingDepositReserve.coinType), transaction);
919
+ // 2.3) Update state
920
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
921
+ coinType: loopingDepositReserve.coinType,
922
+ depositedAmount: stepDepositedAmount,
923
+ });
924
+ console.log(`[loopStrategyToExposure] ${i} deposit_lst.update_state |`, JSON.stringify({
925
+ deposits: deposits.map((d) => ({
926
+ coinType: d.coinType,
927
+ depositedAmount: d.depositedAmount.toFixed(20),
928
+ })),
929
+ borrowedAmount: borrowedAmount.toFixed(20),
930
+ }, null, 2));
931
+ }
932
+ }
933
+ // Base only
934
+ else if (loopingDepositReserve.coinType === ((_e = depositReserves.base) === null || _e === void 0 ? void 0 : _e.coinType)) {
935
+ const exchangeRateRouters = yield cetusSdk.findRouters({
936
+ from: borrowReserve.coinType,
937
+ target: loopingDepositReserve.coinType,
938
+ amount: new bn_js_1.BN(new bignumber_js_1.default(0.1)
939
+ .times(10 ** borrowReserve.token.decimals)
940
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
941
+ .toString()),
942
+ byAmountIn: true,
943
+ splitCount: 0, // Use direct swap to avoid split algo
944
+ });
945
+ if (!exchangeRateRouters)
946
+ throw new Error("No swap quote found");
947
+ const borrowToBaseExchangeRate = new bignumber_js_1.default(new bignumber_js_1.default(exchangeRateRouters.amountOut.toString()).div(10 ** loopingDepositReserve.token.decimals)).div(new bignumber_js_1.default(exchangeRateRouters.amountIn.toString()).div(10 ** borrowReserve.token.decimals));
948
+ for (let i = 0; i < 30; i++) {
949
+ const exposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
950
+ const pendingBorrowedAmount = targetBorrowedAmount.minus(borrowedAmount);
951
+ console.log(`[loopStrategyToExposure] ${i} start |`, JSON.stringify({
952
+ deposits: deposits.map((d) => ({
953
+ coinType: d.coinType,
954
+ depositedAmount: d.depositedAmount.toFixed(20),
955
+ })),
956
+ borrowedAmount: borrowedAmount.toFixed(20),
957
+ exposure: exposure.toFixed(20),
958
+ pendingBorrowedAmount: pendingBorrowedAmount.toFixed(20),
959
+ }, null, 2));
960
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E))
961
+ break;
962
+ // 1) Borrow
963
+ // 1.1) Max
964
+ const stepMaxBorrowedAmount = (0, exports.getStrategyStepMaxBorrowedAmount)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)
965
+ .times(0.9) // 10% buffer
966
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
967
+ const stepMaxDepositedAmount = new bignumber_js_1.default(stepMaxBorrowedAmount.times(borrowToBaseExchangeRate)).decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
968
+ console.log(`[loopStrategyToExposure] ${i} borrow.max |`, JSON.stringify({
969
+ stepMaxBorrowedAmount: stepMaxBorrowedAmount.toFixed(20),
970
+ stepMaxDepositedAmount: stepMaxDepositedAmount.toFixed(20),
971
+ }, null, 2));
972
+ // 1.2) Borrow
973
+ const stepBorrowedAmount = bignumber_js_1.default.min(pendingBorrowedAmount, stepMaxBorrowedAmount).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
974
+ const isMaxBorrow = stepBorrowedAmount.eq(stepMaxBorrowedAmount);
975
+ console.log(`[loopStrategyToExposure] ${i} borrow.borrow |`, JSON.stringify({
976
+ stepBorrowedAmount: stepBorrowedAmount.toFixed(20),
977
+ isMaxBorrow,
978
+ }, null, 2));
979
+ const [stepBorrowedCoin] = (0, strategyOwnerCap_1.strategyBorrow)(borrowReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(borrowReserve.coinType), BigInt(stepBorrowedAmount
980
+ .times(10 ** borrowReserve.token.decimals)
981
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
982
+ .toString()), transaction);
983
+ // 1.3) Update state
984
+ borrowedAmount = borrowedAmount.plus(stepBorrowedAmount);
985
+ console.log(`[loopStrategyToExposure] ${i} borrow.update_state |`, JSON.stringify({
986
+ deposits: deposits.map((d) => ({
987
+ coinType: d.coinType,
988
+ depositedAmount: d.depositedAmount.toFixed(20),
989
+ })),
990
+ borrowedAmount: borrowedAmount.toFixed(20),
991
+ }, null, 2));
992
+ // 2) Deposit base
993
+ // 2.1) Swap borrows for base
994
+ const routers = yield cetusSdk.findRouters({
995
+ from: borrowReserve.coinType,
996
+ target: loopingDepositReserve.coinType,
997
+ amount: new bn_js_1.BN(stepBorrowedAmount
998
+ .times(10 ** borrowReserve.token.decimals)
999
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1000
+ .toString()), // Estimate for loop 2 onwards (don't know exact out amount, we are not accounting for swap fees, etc)
1001
+ byAmountIn: true,
1002
+ splitCount: 0, // Use direct swap to avoid split algo
1003
+ });
1004
+ if (!routers)
1005
+ throw new Error("No swap quote found");
1006
+ const slippagePercent = 0.1;
1007
+ let stepBaseCoin;
1008
+ try {
1009
+ stepBaseCoin = yield cetusSdk.fixableRouterSwapV3({
1010
+ router: routers,
1011
+ inputCoin: stepBorrowedCoin,
1012
+ slippage: slippagePercent / 100,
1013
+ txb: transaction,
1014
+ partner: cetusPartnerId,
1015
+ });
1016
+ }
1017
+ catch (err) {
1018
+ throw new Error("No swap quote found");
1019
+ }
1020
+ console.log(`[loopStrategyToExposure] ${i} swap_borrows_for_base.swap |`, JSON.stringify({
1021
+ inCoinType: borrowReserve.coinType,
1022
+ outCoinType: loopingDepositReserve.coinType,
1023
+ amountIn: stepBorrowedAmount.toFixed(20),
1024
+ amountOut: new bignumber_js_1.default(routers.amountOut.toString())
1025
+ .div(10 ** loopingDepositReserve.token.decimals)
1026
+ .decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN)
1027
+ .toFixed(20),
1028
+ }, null, 2), routers);
1029
+ // 2.2) Deposit
1030
+ const stepDepositedAmount = new bignumber_js_1.default(new bignumber_js_1.default(routers.amountOut.toString()).div(10 ** loopingDepositReserve.token.decimals)).decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1031
+ const isMaxDeposit = stepDepositedAmount.eq(stepMaxDepositedAmount);
1032
+ console.log(`[loopStrategyToExposure] ${i} deposit.deposit |`, JSON.stringify({
1033
+ stepDepositedAmount: stepDepositedAmount.toFixed(20),
1034
+ isMaxDeposit,
1035
+ }, null, 2));
1036
+ (0, strategyOwnerCap_1.strategyDeposit)(stepBaseCoin, loopingDepositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(loopingDepositReserve.coinType), transaction);
1037
+ // 2.3) Update state
1038
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1039
+ coinType: loopingDepositReserve.coinType,
1040
+ depositedAmount: stepDepositedAmount,
1041
+ });
1042
+ console.log(`[loopStrategyToExposure] ${i} deposit.update_state |`, JSON.stringify({
1043
+ deposits: deposits.map((d) => ({
1044
+ coinType: d.coinType,
1045
+ depositedAmount: d.depositedAmount.toFixed(20),
1046
+ })),
1047
+ borrowedAmount: borrowedAmount.toFixed(20),
1048
+ }, null, 2));
1049
+ }
1050
+ }
1051
+ else {
1052
+ throw new Error("No LST or base reserve found"); // Should not happen
1053
+ }
1054
+ return { deposits, borrowedAmount, transaction };
1055
+ });
1056
+ exports.strategyLoopToExposureTx = strategyLoopToExposureTx;
1057
+ const strategyUnloopToExposureTx = (
1058
+ // AppContext
1059
+ reserveMap,
1060
+ // Strategy
1061
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, _targetBorrowedAmount, _targetExposure, // Must be defined if _targetBorrowedAmount is undefined
1062
+ transaction, dryRunTransaction) => __awaiter(void 0, void 0, void 0, function* () {
1063
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1064
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
1065
+ const lst = strategyInfo.depositLstCoinType !== undefined
1066
+ ? lstMap[strategyInfo.depositLstCoinType]
1067
+ : undefined;
1068
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
1069
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
1070
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
1071
+ console.log(`[unloopStrategyToExposure] args |`, JSON.stringify({
1072
+ _address,
1073
+ strategyOwnerCapId,
1074
+ obligationId,
1075
+ _deposits: _deposits.map((d) => ({
1076
+ coinType: d.coinType,
1077
+ depositedAmount: d.depositedAmount.toFixed(20),
1078
+ })),
1079
+ _borrowedAmount: _borrowedAmount.toFixed(20),
1080
+ _targetBorrowedAmount: _targetBorrowedAmount === null || _targetBorrowedAmount === void 0 ? void 0 : _targetBorrowedAmount.toFixed(20),
1081
+ _targetExposure: _targetExposure === null || _targetExposure === void 0 ? void 0 : _targetExposure.toFixed(20),
1082
+ }, null, 2));
1083
+ const depositReserve = ((_a = depositReserves.base) !== null && _a !== void 0 ? _a : depositReserves.lst); // Must have LST if no base
1084
+ const loopingDepositReserve = ((_b = depositReserves.lst) !== null && _b !== void 0 ? _b : depositReserves.base); // Must have base if no LST
1085
+ //
1086
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
1087
+ let borrowedAmount = _borrowedAmount;
1088
+ const tvlAmountUsd = (0, exports.getStrategyTvlAmount)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)).times(defaultCurrencyReserve.price);
1089
+ const targetBorrowedAmount = _targetBorrowedAmount !== null && _targetBorrowedAmount !== void 0 ? _targetBorrowedAmount : tvlAmountUsd
1090
+ .times(_targetExposure.minus(1))
1091
+ .div(borrowReserve.price)
1092
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1093
+ console.log(`[unloopStrategyToExposure] processed_args |`, JSON.stringify({
1094
+ tvlAmountUsd: tvlAmountUsd.toFixed(20),
1095
+ targetBorrowedAmount: targetBorrowedAmount.toFixed(20),
1096
+ }));
1097
+ if (borrowedAmount.eq(targetBorrowedAmount))
1098
+ return { deposits, borrowedAmount, transaction };
1099
+ const fullyRepayBorrowsUsingLst = (maxWithdrawRemainingLstAndRedepositAsBase) => __awaiter(void 0, void 0, void 0, function* () {
1100
+ var _a, _b, _c, _d, _e, _f, _g;
1101
+ if (depositReserves.lst === undefined)
1102
+ throw new Error("LST reserve not found");
1103
+ const borrowedAmountUsd = borrowedAmount.times(borrowReserve.price);
1104
+ const fullRepaymentAmount = (borrowedAmountUsd.lt(0.02)
1105
+ ? new bignumber_js_1.default(0.02).div(borrowReserve.price) // $0.02 in borrow coinType (still well over E borrows, e.g. E SUI, or E wBTC)
1106
+ : borrowedAmountUsd.lt(1)
1107
+ ? borrowedAmount.times(1.1) // 10% buffer
1108
+ : borrowedAmountUsd.lt(10)
1109
+ ? borrowedAmount.times(1.01) // 1% buffer
1110
+ : borrowedAmount.times(1.001)) // 0.1% buffer
1111
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1112
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] |`, JSON.stringify({
1113
+ borrowedAmount: borrowedAmount.toFixed(20),
1114
+ fullRepaymentAmount: fullRepaymentAmount.toFixed(20),
1115
+ }));
1116
+ // 1) Withdraw LST
1117
+ const lstWithdrawnAmount = fullRepaymentAmount
1118
+ .div(1 - +((_a = lst === null || lst === void 0 ? void 0 : lst.redeemFeePercent) !== null && _a !== void 0 ? _a : 0) / 100) // Potential rounding issue (max 1 MIST)
1119
+ .div((_b = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _b !== void 0 ? _b : 1)
1120
+ .decimalPlaces(depositReserves.lst.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1121
+ if (((_d = (_c = deposits.find((d) => d.coinType === depositReserves.lst.coinType)) === null || _c === void 0 ? void 0 : _c.depositedAmount) !== null && _d !== void 0 ? _d : new bignumber_js_1.default(0)).lt(lstWithdrawnAmount))
1122
+ throw new Error("Not enough LST deposited");
1123
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] withdraw_lst |`, JSON.stringify({
1124
+ lstWithdrawnAmount: lstWithdrawnAmount.toFixed(20),
1125
+ }));
1126
+ // 1.1) Withdraw
1127
+ const [withdrawnLstCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(depositReserves.lst.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.lst.coinType), BigInt(new bignumber_js_1.default(lstWithdrawnAmount
1128
+ .times(10 ** exports.LST_DECIMALS)
1129
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1130
+ .toString())
1131
+ .div(depositReserves.lst.cTokenExchangeRate)
1132
+ .integerValue(bignumber_js_1.default.ROUND_UP)
1133
+ .toString()), transaction);
1134
+ // 1.2) Update state
1135
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1136
+ coinType: depositReserves.lst.coinType,
1137
+ depositedAmount: lstWithdrawnAmount.times(-1),
1138
+ });
1139
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] withdraw_lst.update_state |`, JSON.stringify({
1140
+ deposits: deposits.map((d) => ({
1141
+ coinType: d.coinType,
1142
+ depositedAmount: d.depositedAmount.toFixed(20),
1143
+ })),
1144
+ borrowedAmount: borrowedAmount.toFixed(20),
1145
+ }, null, 2));
1146
+ // 2) Unstake LST for SUI
1147
+ const fullRepaymentCoin = lst.client.redeem(transaction, withdrawnLstCoin);
1148
+ // 3) Repay borrows
1149
+ // 3.1) Repay
1150
+ const repaidAmount = new bignumber_js_1.default(new bignumber_js_1.default(lstWithdrawnAmount.times((_e = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _e !== void 0 ? _e : 1)).minus((0, exports.getStrategyLstRedeemFee)(lstMap, depositReserves.lst.coinType, lstWithdrawnAmount))).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1151
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] repay_borrows.repay |`, JSON.stringify({
1152
+ repaidAmount: repaidAmount.toFixed(20),
1153
+ }, null, 2));
1154
+ try {
1155
+ const txCopy = transactions_1.Transaction.from(transaction);
1156
+ suilendClient.repay(obligationId, borrowReserve.coinType, fullRepaymentCoin, txCopy);
1157
+ txCopy.transferObjects([fullRepaymentCoin], _address); // Transfer remaining SUI to user
1158
+ yield dryRunTransaction(txCopy); // Throws error if fails
1159
+ transaction = txCopy;
1160
+ }
1161
+ catch (err) {
1162
+ // Don't block user if fails
1163
+ console.error(err);
1164
+ transaction.transferObjects([fullRepaymentCoin], _address); // Transfer SUI to user
1165
+ }
1166
+ // 2.3) Update state
1167
+ borrowedAmount = bignumber_js_1.default.max(borrowedAmount.minus(repaidAmount), new bignumber_js_1.default(0));
1168
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] repay_borrows.update_state |`, JSON.stringify({
1169
+ deposits: deposits.map((d) => ({
1170
+ coinType: d.coinType,
1171
+ depositedAmount: d.depositedAmount.toFixed(20),
1172
+ })),
1173
+ borrowedAmount: borrowedAmount.toFixed(20),
1174
+ }, null, 2));
1175
+ // 3) Swap remaining borrow to LST and redeposit (not possible because coin is a mutable reference (?))
1176
+ // Max withdraw remaining LST and redeposit as base:
1177
+ if (maxWithdrawRemainingLstAndRedepositAsBase) {
1178
+ if (depositReserves.base === undefined)
1179
+ throw new Error("Base reserve not found");
1180
+ // 1) MAX withdraw LST
1181
+ const remainingLstWithdrawnAmount = ((_g = (_f = deposits.find((d) => d.coinType === depositReserves.lst.coinType)) === null || _f === void 0 ? void 0 : _f.depositedAmount) !== null && _g !== void 0 ? _g : new bignumber_js_1.default(0)).decimalPlaces(depositReserves.lst.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1182
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] max_withdraw_lst |`, JSON.stringify({
1183
+ remainingLstWithdrawnAmount: remainingLstWithdrawnAmount.toFixed(20),
1184
+ }));
1185
+ // 1.1) MAX Withdraw
1186
+ const [withdrawnRemainingLstCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(depositReserves.lst.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.lst.coinType), BigInt(sui_fe_1.MAX_U64.toString()), transaction);
1187
+ // 1.2) Update state
1188
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1189
+ coinType: depositReserves.lst.coinType,
1190
+ depositedAmount: remainingLstWithdrawnAmount.times(-1), // Should be 0 after this
1191
+ });
1192
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] max_withdraw_lst.update_state |`, JSON.stringify({
1193
+ deposits: deposits.map((d) => ({
1194
+ coinType: d.coinType,
1195
+ depositedAmount: d.depositedAmount.toFixed(20),
1196
+ })),
1197
+ borrowedAmount: borrowedAmount.toFixed(20),
1198
+ }, null, 2));
1199
+ // 2) Swap LST for base and redeposit
1200
+ // 2.1) Get routers
1201
+ const routers = yield cetusSdk.findRouters({
1202
+ from: depositReserves.lst.coinType,
1203
+ target: depositReserves.base.coinType,
1204
+ amount: new bn_js_1.BN(remainingLstWithdrawnAmount
1205
+ .times(10 ** depositReserves.lst.token.decimals)
1206
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1207
+ .toString()),
1208
+ byAmountIn: true,
1209
+ });
1210
+ if (!routers)
1211
+ throw new Error("No swap quote found");
1212
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingLst] swap_lst_for_base.get_routers`, {
1213
+ routers,
1214
+ amountIn: new bignumber_js_1.default(routers.amountIn.toString())
1215
+ .div(10 ** depositReserves.lst.token.decimals)
1216
+ .decimalPlaces(depositReserves.lst.token.decimals, bignumber_js_1.default.ROUND_DOWN)
1217
+ .toFixed(20),
1218
+ amountOut: new bignumber_js_1.default(routers.amountOut.toString())
1219
+ .div(10 ** depositReserves.base.token.decimals)
1220
+ .decimalPlaces(depositReserves.base.token.decimals, bignumber_js_1.default.ROUND_DOWN)
1221
+ .toFixed(20),
1222
+ });
1223
+ // 2.2) Swap
1224
+ let baseCoin;
1225
+ try {
1226
+ baseCoin = yield cetusSdk.fixableRouterSwapV3({
1227
+ router: routers,
1228
+ inputCoin: withdrawnRemainingLstCoin,
1229
+ slippage: 100 / 100,
1230
+ txb: transaction,
1231
+ partner: cetusPartnerId,
1232
+ });
1233
+ }
1234
+ catch (err) {
1235
+ throw new Error("No swap quote found");
1236
+ }
1237
+ // 3) Deposit base
1238
+ (0, strategyOwnerCap_1.strategyDeposit)(baseCoin, depositReserves.base.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.base.coinType), transaction);
1239
+ }
1240
+ });
1241
+ const fullyRepayBorrowsUsingBase = () => __awaiter(void 0, void 0, void 0, function* () {
1242
+ var _a, _b;
1243
+ if (depositReserves.base === undefined)
1244
+ throw new Error("Base reserve not found");
1245
+ const borrowedAmountUsd = borrowedAmount.times(borrowReserve.price);
1246
+ const fullRepaymentAmount = (borrowedAmountUsd.lt(0.02)
1247
+ ? new bignumber_js_1.default(0.02).div(borrowReserve.price) // $0.02 in borrow coinType (still well over E borrows, e.g. E SUI, or E wBTC)
1248
+ : borrowedAmountUsd.lt(1)
1249
+ ? borrowedAmount.times(1.1) // 10% buffer
1250
+ : borrowedAmountUsd.lt(10)
1251
+ ? borrowedAmount.times(1.01) // 1% buffer
1252
+ : borrowedAmount.times(1.001)) // 0.1% buffer
1253
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1254
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingBase] |`, JSON.stringify({
1255
+ borrowedAmount: borrowedAmount.toFixed(20),
1256
+ fullRepaymentAmount: fullRepaymentAmount.toFixed(20),
1257
+ }));
1258
+ // 1) MAX withdraw LST
1259
+ if (depositReserves.lst !== undefined) {
1260
+ // 1.1) MAX withdraw
1261
+ const [withdrawnMaxLstCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(depositReserves.lst.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.lst.coinType), BigInt(sui_fe_1.MAX_U64.toString()), transaction);
1262
+ // 1.2) Update state
1263
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1264
+ coinType: depositReserves.lst.coinType,
1265
+ depositedAmount: ((_b = (_a = deposits.find((d) => d.coinType === depositReserves.lst.coinType)) === null || _a === void 0 ? void 0 : _a.depositedAmount) !== null && _b !== void 0 ? _b : new bignumber_js_1.default(0)).times(-1),
1266
+ });
1267
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingBase] max_withdraw_lst.update_state |`, JSON.stringify({
1268
+ deposits: deposits.map((d) => ({
1269
+ coinType: d.coinType,
1270
+ depositedAmount: d.depositedAmount.toFixed(20),
1271
+ })),
1272
+ borrowedAmount: borrowedAmount.toFixed(20),
1273
+ }, null, 2));
1274
+ // 1.3) Unstake LST for SUI
1275
+ const suiCoin = lst.client.redeem(transaction, withdrawnMaxLstCoin);
1276
+ // 1.4) Transfer SUI to user
1277
+ transaction.transferObjects([suiCoin], _address);
1278
+ }
1279
+ // 2) Withdraw base
1280
+ const baseWithdrawnAmount = new bignumber_js_1.default(fullRepaymentAmount.times(borrowReserve.price))
1281
+ .div(depositReserves.base.price)
1282
+ .times(1.03); // 3% buffer
1283
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingBase] withdraw_base |`, JSON.stringify({
1284
+ baseWithdrawnAmount: baseWithdrawnAmount.toFixed(20),
1285
+ }, null, 2));
1286
+ // 2.1) Withdraw
1287
+ const [withdrawnBaseCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(depositReserves.base.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.base.coinType), BigInt(new bignumber_js_1.default(baseWithdrawnAmount
1288
+ .times(10 ** depositReserves.base.token.decimals)
1289
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1290
+ .toString())
1291
+ .div(depositReserves.base.cTokenExchangeRate)
1292
+ .integerValue(bignumber_js_1.default.ROUND_UP)
1293
+ .toString()), transaction);
1294
+ // 2.2) Update state
1295
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1296
+ coinType: depositReserves.base.coinType,
1297
+ depositedAmount: baseWithdrawnAmount.times(-1),
1298
+ });
1299
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingBase] withdraw_base.update_state |`, JSON.stringify({
1300
+ deposits: deposits.map((d) => ({
1301
+ coinType: d.coinType,
1302
+ depositedAmount: d.depositedAmount.toFixed(20),
1303
+ })),
1304
+ borrowedAmount: borrowedAmount.toFixed(20),
1305
+ }, null, 2));
1306
+ // 3) Swap base for borrow
1307
+ // 3.1) Get routers
1308
+ const routers = yield cetusSdk.findRouters({
1309
+ from: depositReserves.base.coinType,
1310
+ target: borrowReserve.coinType,
1311
+ amount: new bn_js_1.BN(baseWithdrawnAmount
1312
+ .times(10 ** depositReserves.base.token.decimals)
1313
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1314
+ .toString()),
1315
+ byAmountIn: true,
1316
+ });
1317
+ if (!routers)
1318
+ throw new Error("No swap quote found");
1319
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingBase] swap_base_for_borrows.get_routers`, {
1320
+ routers,
1321
+ amountIn: new bignumber_js_1.default(routers.amountIn.toString())
1322
+ .div(10 ** depositReserves.base.token.decimals)
1323
+ .decimalPlaces(depositReserves.base.token.decimals, bignumber_js_1.default.ROUND_DOWN)
1324
+ .toFixed(20),
1325
+ amountOut: new bignumber_js_1.default(routers.amountOut.toString())
1326
+ .div(10 ** borrowReserve.token.decimals)
1327
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN)
1328
+ .toFixed(20),
1329
+ });
1330
+ // 3.2) Swap
1331
+ let borrowCoin;
1332
+ try {
1333
+ borrowCoin = yield cetusSdk.fixableRouterSwapV3({
1334
+ router: routers,
1335
+ inputCoin: withdrawnBaseCoin,
1336
+ slippage: 1 / 100,
1337
+ txb: transaction,
1338
+ partner: cetusPartnerId,
1339
+ });
1340
+ }
1341
+ catch (err) {
1342
+ throw new Error("No swap quote found");
1343
+ }
1344
+ // 4) Repay borrows
1345
+ // 4.1) Repay
1346
+ const repaidAmount = fullRepaymentAmount;
1347
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingBase] repay_borrows.repay |`, JSON.stringify({
1348
+ repaidAmount: repaidAmount.toFixed(20),
1349
+ }, null, 2));
1350
+ try {
1351
+ const txCopy = transactions_1.Transaction.from(transaction);
1352
+ suilendClient.repay(obligationId, borrowReserve.coinType, borrowCoin, txCopy);
1353
+ txCopy.transferObjects([borrowCoin], _address); // Transfer remaining borrow to user
1354
+ yield dryRunTransaction(txCopy); // Throws error if fails
1355
+ transaction = txCopy;
1356
+ }
1357
+ catch (err) {
1358
+ // Don't block user if fails
1359
+ console.error(err);
1360
+ transaction.transferObjects([borrowCoin], _address); // Transfer borrow to user
1361
+ }
1362
+ // 4.2) Update state
1363
+ borrowedAmount = bignumber_js_1.default.max(borrowedAmount.minus(repaidAmount), new bignumber_js_1.default(0));
1364
+ console.log(`[unloopStrategyToExposure.fullyRepayBorrowsUsingBase] repay_borrows.update_state |`, JSON.stringify({
1365
+ deposits: deposits.map((d) => ({
1366
+ coinType: d.coinType,
1367
+ depositedAmount: d.depositedAmount.toFixed(20),
1368
+ })),
1369
+ borrowedAmount: borrowedAmount.toFixed(20),
1370
+ }, null, 2));
1371
+ // 5) Swap remaining borrow to base and redeposit (not possible because coin is a mutable reference (?))
1372
+ });
1373
+ for (let i = 0; i < 30; i++) {
1374
+ const exposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
1375
+ const pendingBorrowedAmount = borrowedAmount.minus(targetBorrowedAmount);
1376
+ console.log(`[unloopStrategyToExposure] ${i} start |`, JSON.stringify({
1377
+ deposits: deposits.map((d) => ({
1378
+ coinType: d.coinType,
1379
+ depositedAmount: d.depositedAmount.toFixed(20),
1380
+ })),
1381
+ borrowedAmount: borrowedAmount.toFixed(20),
1382
+ exposure: exposure.toFixed(20),
1383
+ pendingBorrowedAmount: pendingBorrowedAmount.toFixed(20),
1384
+ }, null, 2));
1385
+ // Base+LST or LST only
1386
+ if (loopingDepositReserve.coinType === ((_c = depositReserves.lst) === null || _c === void 0 ? void 0 : _c.coinType)) {
1387
+ // Target: 1x leverage
1388
+ if (targetBorrowedAmount.eq(0)) {
1389
+ if (pendingBorrowedAmount.lt(0))
1390
+ break; // Fully repaid already
1391
+ if (depositReserve.coinType === ((_d = depositReserves.base) === null || _d === void 0 ? void 0 : _d.coinType)) {
1392
+ const lstDeposit = deposits.find((d) => d.coinType === depositReserves.lst.coinType);
1393
+ // Ran out of LST
1394
+ if (lstDeposit.depositedAmount.lte(exports.STRATEGY_E)) {
1395
+ // 1. MAX withdraws LST (transferred to user as SUI)
1396
+ // 2. Withdraws base to cover borrows
1397
+ // - Leftover transferred to user as borrow coinType, e.g. SUI or wBTC
1398
+ yield fullyRepayBorrowsUsingBase();
1399
+ break;
1400
+ }
1401
+ // Borrows almost fully repaid
1402
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E)) {
1403
+ try {
1404
+ // 1. Withdraws LST to cover borrows
1405
+ // - Leftover transferred to user as borrow coinType, e.g. SUI or wBTC
1406
+ // 2. MAX withdraws remaining LST and redeposits as base
1407
+ yield fullyRepayBorrowsUsingLst(true);
1408
+ break;
1409
+ }
1410
+ catch (err) {
1411
+ console.error(err);
1412
+ }
1413
+ // 1. MAX withdraws LST (transferred to user as SUI)
1414
+ // 2. Withdraws base to cover borrows
1415
+ // - Leftover transferred to user as borrow coinType, e.g. SUI or wBTC
1416
+ yield fullyRepayBorrowsUsingBase();
1417
+ break;
1418
+ }
1419
+ }
1420
+ else {
1421
+ // Borrows almost fully repaid
1422
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E)) {
1423
+ // 1. Withdraws LST to cover borrows
1424
+ // - Leftover transferred to user as borrow coinType, e.g. SUI or wBTC
1425
+ yield fullyRepayBorrowsUsingLst(false);
1426
+ break;
1427
+ }
1428
+ }
1429
+ }
1430
+ else {
1431
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E))
1432
+ break;
1433
+ }
1434
+ // 1) Withdraw LST
1435
+ // 1.1) Max
1436
+ const stepMaxWithdrawnAmount = (0, exports.getStrategyStepMaxWithdrawnAmount)(reserveMap, lstMap, strategyType, deposits, borrowedAmount, loopingDepositReserve.coinType)
1437
+ .times(0.9) // 10% buffer
1438
+ .decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1439
+ const stepMaxRepaidAmount = new bignumber_js_1.default(new bignumber_js_1.default(stepMaxWithdrawnAmount.times((_e = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _e !== void 0 ? _e : 1)).minus((0, exports.getStrategyLstRedeemFee)(lstMap, loopingDepositReserve.coinType, stepMaxWithdrawnAmount))).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1440
+ console.log(`[unloopStrategyToExposure] ${i} withdraw_lst.max |`, JSON.stringify({
1441
+ stepMaxWithdrawnAmount: stepMaxWithdrawnAmount.toFixed(20),
1442
+ stepMaxRepaidAmount: stepMaxRepaidAmount.toFixed(20),
1443
+ }, null, 2));
1444
+ // 1.2) Withdraw
1445
+ const stepWithdrawnAmount = bignumber_js_1.default.min(pendingBorrowedAmount, stepMaxRepaidAmount)
1446
+ .times(1 - +((_f = lst === null || lst === void 0 ? void 0 : lst.redeemFeePercent) !== null && _f !== void 0 ? _f : 0) / 100) // Potential rounding issue (max 1 MIST)
1447
+ .div((_g = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _g !== void 0 ? _g : 1)
1448
+ .decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1449
+ const isMaxWithdraw = stepWithdrawnAmount.eq(stepMaxWithdrawnAmount);
1450
+ console.log(`[unloopStrategyToExposure] ${i} withdraw_lst.withdraw |`, JSON.stringify({
1451
+ stepWithdrawnAmount: stepWithdrawnAmount.toFixed(20),
1452
+ isMaxWithdraw,
1453
+ }, null, 2));
1454
+ const [stepWithdrawnCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(loopingDepositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(loopingDepositReserve.coinType), BigInt(new bignumber_js_1.default(stepWithdrawnAmount
1455
+ .times(10 ** loopingDepositReserve.token.decimals)
1456
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1457
+ .toString())
1458
+ .div(loopingDepositReserve.cTokenExchangeRate)
1459
+ .integerValue(bignumber_js_1.default.ROUND_UP)
1460
+ .toString()), transaction);
1461
+ // 1.3) Update state
1462
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1463
+ coinType: loopingDepositReserve.coinType,
1464
+ depositedAmount: stepWithdrawnAmount.times(-1),
1465
+ });
1466
+ console.log(`[unloopStrategyToExposure] ${i} withdraw_lst.update_state |`, JSON.stringify({
1467
+ deposits: deposits.map((d) => ({
1468
+ coinType: d.coinType,
1469
+ depositedAmount: d.depositedAmount.toFixed(20),
1470
+ })),
1471
+ borrowedAmount: borrowedAmount.toFixed(20),
1472
+ }, null, 2));
1473
+ // 2) Unstake LST for SUI
1474
+ const stepSuiCoin = lst.client.redeem(transaction, stepWithdrawnCoin);
1475
+ // 3) Repay SUI
1476
+ // 3.1) Repay
1477
+ const stepRepaidAmount = new bignumber_js_1.default(new bignumber_js_1.default(stepWithdrawnAmount.times((_h = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _h !== void 0 ? _h : 1)).minus((0, exports.getStrategyLstRedeemFee)(lstMap, loopingDepositReserve.coinType, stepWithdrawnAmount))).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1478
+ const isMaxRepay = stepRepaidAmount.eq(stepMaxRepaidAmount);
1479
+ console.log(`[unloopStrategyToExposure] ${i} repay_sui.repay |`, JSON.stringify({
1480
+ stepRepaidAmount: stepRepaidAmount.toFixed(20),
1481
+ isMaxRepay,
1482
+ }, null, 2));
1483
+ suilendClient.repay(obligationId, borrowReserve.coinType, stepSuiCoin, transaction);
1484
+ transaction.transferObjects([stepSuiCoin], _address);
1485
+ // 3.2) Update state
1486
+ borrowedAmount = borrowedAmount.minus(stepRepaidAmount);
1487
+ console.log(`[unloopStrategyToExposure] ${i} repay_sui.update_state |`, JSON.stringify({
1488
+ deposits: deposits.map((d) => ({
1489
+ coinType: d.coinType,
1490
+ depositedAmount: d.depositedAmount.toFixed(20),
1491
+ })),
1492
+ borrowedAmount: borrowedAmount.toFixed(20),
1493
+ }, null, 2));
1494
+ }
1495
+ // Base only
1496
+ else if (loopingDepositReserve.coinType === ((_j = depositReserves.base) === null || _j === void 0 ? void 0 : _j.coinType)) {
1497
+ const exchangeRateRouters = yield cetusSdk.findRouters({
1498
+ from: loopingDepositReserve.coinType,
1499
+ target: borrowReserve.coinType,
1500
+ amount: new bn_js_1.BN(new bignumber_js_1.default(0.1)
1501
+ .times(10 ** loopingDepositReserve.token.decimals)
1502
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1503
+ .toString()),
1504
+ byAmountIn: true,
1505
+ splitCount: 0, // Use direct swap to avoid split algo
1506
+ });
1507
+ if (!exchangeRateRouters)
1508
+ throw new Error("No swap quote found");
1509
+ const baseToBorrowExchangeRate = new bignumber_js_1.default(new bignumber_js_1.default(exchangeRateRouters.amountOut.toString()).div(10 ** borrowReserve.token.decimals)).div(new bignumber_js_1.default(exchangeRateRouters.amountIn.toString()).div(10 ** loopingDepositReserve.token.decimals));
1510
+ // Target: 1x leverage
1511
+ if (targetBorrowedAmount.eq(0)) {
1512
+ if (pendingBorrowedAmount.lt(0))
1513
+ break; // Fully repaid already
1514
+ // Borrows almost fully repaid
1515
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E)) {
1516
+ // 1. Withdraws base to cover borrows
1517
+ // - Leftover transferred to user as borrow coinType, e.g. SUI or wBTC
1518
+ yield fullyRepayBorrowsUsingBase();
1519
+ break;
1520
+ }
1521
+ }
1522
+ else {
1523
+ if (pendingBorrowedAmount.lte(exports.STRATEGY_E))
1524
+ break;
1525
+ }
1526
+ // 1) Withdraw base
1527
+ // 1.1) Max
1528
+ const stepMaxWithdrawnAmount = (0, exports.getStrategyStepMaxWithdrawnAmount)(reserveMap, lstMap, strategyType, deposits, borrowedAmount, loopingDepositReserve.coinType)
1529
+ .times(0.9) // 10% buffer
1530
+ .decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1531
+ const stepMaxRepaidAmount = new bignumber_js_1.default(stepMaxWithdrawnAmount.times(baseToBorrowExchangeRate)).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1532
+ console.log(`[unloopStrategyToExposure] ${i} withdraw_base.max |`, JSON.stringify({
1533
+ stepMaxWithdrawnAmount: stepMaxWithdrawnAmount.toFixed(20),
1534
+ stepMaxRepaidAmount: stepMaxRepaidAmount.toFixed(20),
1535
+ }, null, 2));
1536
+ // 1.2) Withdraw
1537
+ const stepWithdrawnAmount = bignumber_js_1.default.min(pendingBorrowedAmount, stepMaxRepaidAmount)
1538
+ .div(baseToBorrowExchangeRate)
1539
+ .decimalPlaces(loopingDepositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1540
+ const isMaxWithdraw = stepWithdrawnAmount.eq(stepMaxWithdrawnAmount);
1541
+ console.log(`[unloopStrategyToExposure] ${i} withdraw_base.withdraw |`, JSON.stringify({
1542
+ stepWithdrawnAmount: stepWithdrawnAmount.toFixed(20),
1543
+ isMaxWithdraw,
1544
+ }, null, 2));
1545
+ const [stepWithdrawnCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(loopingDepositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(loopingDepositReserve.coinType), BigInt(new bignumber_js_1.default(stepWithdrawnAmount
1546
+ .times(10 ** loopingDepositReserve.token.decimals)
1547
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1548
+ .toString())
1549
+ .div(loopingDepositReserve.cTokenExchangeRate)
1550
+ .integerValue(bignumber_js_1.default.ROUND_UP)
1551
+ .toString()), transaction);
1552
+ // 1.3) Update state
1553
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1554
+ coinType: loopingDepositReserve.coinType,
1555
+ depositedAmount: stepWithdrawnAmount.times(-1),
1556
+ });
1557
+ console.log(`[unloopStrategyToExposure] ${i} withdraw_base.update_state |`, JSON.stringify({
1558
+ deposits: deposits.map((d) => ({
1559
+ coinType: d.coinType,
1560
+ depositedAmount: d.depositedAmount.toFixed(20),
1561
+ })),
1562
+ borrowedAmount: borrowedAmount.toFixed(20),
1563
+ }, null, 2));
1564
+ // 2) Swap base for borrows
1565
+ const routers = yield cetusSdk.findRouters({
1566
+ from: loopingDepositReserve.coinType,
1567
+ target: borrowReserve.coinType,
1568
+ amount: new bn_js_1.BN(stepWithdrawnAmount
1569
+ .times(10 ** loopingDepositReserve.token.decimals)
1570
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1571
+ .toString()), // Estimate for loop 2 onwards (don't know exact out amount, we are not accounting for swap fees, etc)
1572
+ byAmountIn: true,
1573
+ splitCount: 0, // Use direct swap to avoid split algo
1574
+ });
1575
+ if (!routers)
1576
+ throw new Error("No swap quote found");
1577
+ const slippagePercent = 0.1;
1578
+ let stepBorrowCoin;
1579
+ try {
1580
+ stepBorrowCoin = yield cetusSdk.fixableRouterSwapV3({
1581
+ router: routers,
1582
+ inputCoin: stepWithdrawnCoin,
1583
+ slippage: slippagePercent / 100,
1584
+ txb: transaction,
1585
+ partner: cetusPartnerId,
1586
+ });
1587
+ }
1588
+ catch (err) {
1589
+ throw new Error("No swap quote found");
1590
+ }
1591
+ console.log(`[unloopStrategyToExposure] ${i} swap_base_for_borrows |`, JSON.stringify({
1592
+ inCoinType: loopingDepositReserve.coinType,
1593
+ outCoinType: borrowReserve.coinType,
1594
+ amountIn: stepWithdrawnAmount.toFixed(20),
1595
+ amountOut: new bignumber_js_1.default(routers.amountOut.toString())
1596
+ .div(10 ** borrowReserve.token.decimals)
1597
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN)
1598
+ .toFixed(20),
1599
+ }, null, 2), routers);
1600
+ // 3) Repay borrows
1601
+ // 3.1) Repay
1602
+ const stepRepaidAmount = new bignumber_js_1.default(new bignumber_js_1.default(routers.amountOut.toString()).div(10 ** borrowReserve.token.decimals)).decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1603
+ const isMaxRepay = stepRepaidAmount.eq(stepMaxRepaidAmount);
1604
+ console.log(`[unloopStrategyToExposure] ${i} repay_borrows.repay |`, JSON.stringify({
1605
+ stepRepaidAmount: stepRepaidAmount.toFixed(20),
1606
+ isMaxRepay,
1607
+ }, null, 2));
1608
+ suilendClient.repay(obligationId, borrowReserve.coinType, stepBorrowCoin, transaction);
1609
+ transaction.transferObjects([stepBorrowCoin], _address);
1610
+ // 3.2) Update state
1611
+ borrowedAmount = borrowedAmount.minus(stepRepaidAmount);
1612
+ console.log(`[unloopStrategyToExposure] ${i} repay_borrows.update_state |`, JSON.stringify({
1613
+ deposits: deposits.map((d) => ({
1614
+ coinType: d.coinType,
1615
+ depositedAmount: d.depositedAmount.toFixed(20),
1616
+ })),
1617
+ borrowedAmount: borrowedAmount.toFixed(20),
1618
+ }, null, 2));
1619
+ }
1620
+ else {
1621
+ throw new Error("No LST or base reserve found"); // Should not happen
1622
+ }
1623
+ }
1624
+ return { deposits, borrowedAmount, transaction };
1625
+ });
1626
+ exports.strategyUnloopToExposureTx = strategyUnloopToExposureTx;
1627
+ const strategyDepositTx = (
1628
+ // AppContext
1629
+ reserveMap,
1630
+ // Strategy
1631
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, deposit, transaction, dryRunTransaction) => __awaiter(void 0, void 0, void 0, function* () {
1632
+ var _a, _b, _c;
1633
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
1634
+ const lst = strategyInfo.depositLstCoinType !== undefined
1635
+ ? lstMap[strategyInfo.depositLstCoinType]
1636
+ : undefined;
1637
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
1638
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
1639
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
1640
+ console.log(`[strategyDepositTx] args |`, JSON.stringify({
1641
+ _address,
1642
+ strategyOwnerCapId,
1643
+ obligationId,
1644
+ _deposits: _deposits.map((d) => ({
1645
+ coinType: d.coinType,
1646
+ depositedAmount: d.depositedAmount.toFixed(20),
1647
+ })),
1648
+ _borrowedAmount: _borrowedAmount.toFixed(20),
1649
+ deposit: {
1650
+ coinType: deposit.coinType,
1651
+ depositedAmount: deposit.depositedAmount.toFixed(20),
1652
+ },
1653
+ }, null, 2));
1654
+ const depositReserve = ((_a = depositReserves.base) !== null && _a !== void 0 ? _a : depositReserves.lst); // Must have LST if no base
1655
+ //
1656
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
1657
+ const borrowedAmount = _borrowedAmount;
1658
+ // 1) Deposit
1659
+ // 1.1) SUI
1660
+ if ((0, sui_fe_1.isSui)(deposit.coinType)) {
1661
+ if (depositReserves.lst === undefined)
1662
+ throw new Error("LST reserve not found");
1663
+ const suiAmount = deposit.depositedAmount;
1664
+ const lstAmount = new bignumber_js_1.default(suiAmount
1665
+ .minus((0, exports.getStrategyLstMintFee)(lstMap, depositReserves.lst.coinType, suiAmount))
1666
+ .times((_b = lst === null || lst === void 0 ? void 0 : lst.suiToLstExchangeRate) !== null && _b !== void 0 ? _b : 1)).decimalPlaces(exports.LST_DECIMALS, bignumber_js_1.default.ROUND_DOWN);
1667
+ // 1.1.1) Split coins
1668
+ const suiCoin = transaction.splitCoins(transaction.gas, [
1669
+ suiAmount
1670
+ .times(10 ** utils_1.SUI_DECIMALS)
1671
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1672
+ .toString(),
1673
+ ]);
1674
+ // 1.1.2) Stake SUI for LST
1675
+ const lstCoin = lst.client.mint(transaction, suiCoin);
1676
+ // 1.1.3) Deposit LST (1x exposure)
1677
+ (0, strategyOwnerCap_1.strategyDeposit)(lstCoin, depositReserves.lst.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.lst.coinType), transaction);
1678
+ // 1.1.4) Update state
1679
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1680
+ coinType: depositReserves.lst.coinType,
1681
+ depositedAmount: lstAmount,
1682
+ });
1683
+ }
1684
+ // 1.2) LST
1685
+ else if (deposit.coinType === ((_c = depositReserves.lst) === null || _c === void 0 ? void 0 : _c.coinType)) {
1686
+ // 1.2.1) Split coins
1687
+ const allCoinsLst = yield (0, sui_fe_1.getAllCoins)(suiClient, _address, depositReserves.lst.coinType);
1688
+ const mergeCoinLst = (0, sui_fe_1.mergeAllCoins)(depositReserves.lst.coinType, transaction, allCoinsLst);
1689
+ const lstCoin = transaction.splitCoins(transaction.object(mergeCoinLst.coinObjectId), [
1690
+ BigInt(deposit.depositedAmount
1691
+ .times(10 ** exports.LST_DECIMALS)
1692
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1693
+ .toString()),
1694
+ ]);
1695
+ // 1.2.2) Deposit LST (1x exposure)
1696
+ (0, strategyOwnerCap_1.strategyDeposit)(lstCoin, depositReserves.lst.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.lst.coinType), transaction);
1697
+ // 1.2.3) Update state
1698
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, deposit);
1699
+ // 1.3) Other
1700
+ }
1701
+ else {
1702
+ const otherReserve = reserveMap[deposit.coinType];
1703
+ // 1.3.1) Split coins
1704
+ const allCoinsOther = yield (0, sui_fe_1.getAllCoins)(suiClient, _address, otherReserve.coinType);
1705
+ const mergeCoinOther = (0, sui_fe_1.mergeAllCoins)(otherReserve.coinType, transaction, allCoinsOther);
1706
+ const otherCoin = transaction.splitCoins(transaction.object(mergeCoinOther.coinObjectId), [
1707
+ BigInt(deposit.depositedAmount
1708
+ .times(10 ** otherReserve.token.decimals)
1709
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1710
+ .toString()),
1711
+ ]);
1712
+ // 1.3.2) Deposit other (1x exposure)
1713
+ (0, strategyOwnerCap_1.strategyDeposit)(otherCoin, otherReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(otherReserve.coinType), transaction);
1714
+ // 1.3.3) Update state
1715
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, deposit);
1716
+ }
1717
+ console.log(`[deposit] deposit |`, JSON.stringify({
1718
+ deposits: deposits.map((d) => ({
1719
+ coinType: d.coinType,
1720
+ depositedAmount: d.depositedAmount.toFixed(20),
1721
+ })),
1722
+ borrowedAmount: borrowedAmount.toFixed(20),
1723
+ }, null, 2));
1724
+ return { deposits, borrowedAmount, transaction };
1725
+ });
1726
+ exports.strategyDepositTx = strategyDepositTx;
1727
+ const strategyDepositAndLoopToExposureTx = (
1728
+ // AppContext
1729
+ reserveMap,
1730
+ // Strategy
1731
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, deposit, targetExposure, transaction, dryRunTransaction) => __awaiter(void 0, void 0, void 0, function* () {
1732
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
1733
+ const lst = strategyInfo.depositLstCoinType !== undefined
1734
+ ? lstMap[strategyInfo.depositLstCoinType]
1735
+ : undefined;
1736
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
1737
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
1738
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
1739
+ console.log(`[depositAndLoopToExposure] args |`, JSON.stringify({
1740
+ _address,
1741
+ strategyOwnerCapId,
1742
+ obligationId,
1743
+ _deposits: _deposits.map((d) => ({
1744
+ coinType: d.coinType,
1745
+ depositedAmount: d.depositedAmount.toFixed(20),
1746
+ })),
1747
+ _borrowedAmount: _borrowedAmount.toFixed(20),
1748
+ deposit: {
1749
+ coinType: deposit.coinType,
1750
+ depositedAmount: deposit.depositedAmount.toFixed(20),
1751
+ },
1752
+ targetExposure: targetExposure.toFixed(20),
1753
+ }, null, 2));
1754
+ //
1755
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
1756
+ let borrowedAmount = _borrowedAmount;
1757
+ // 1) Deposit (1x exposure)
1758
+ // 1.1) Deposit
1759
+ const { deposits: newDeposits, borrowedAmount: newBorrowedAmount, transaction: newTransaction, } = yield (0, exports.strategyDepositTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, deposit, transaction, dryRunTransaction);
1760
+ // 1.2) Update state
1761
+ deposits = newDeposits;
1762
+ borrowedAmount = newBorrowedAmount;
1763
+ transaction = newTransaction;
1764
+ if (targetExposure.gt(1)) {
1765
+ // 2) Loop to target exposure
1766
+ // 2.1) Loop
1767
+ const { deposits: newDeposits2, borrowedAmount: newBorrowedAmount2, transaction: newTransaction2, } = yield (0, exports.strategyLoopToExposureTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
1768
+ targetExposure, // Pass targetExposure
1769
+ transaction);
1770
+ // 2.2) Update state
1771
+ deposits = newDeposits2;
1772
+ borrowedAmount = newBorrowedAmount2;
1773
+ transaction = newTransaction2;
1774
+ }
1775
+ return { deposits, borrowedAmount, transaction };
1776
+ });
1777
+ exports.strategyDepositAndLoopToExposureTx = strategyDepositAndLoopToExposureTx;
1778
+ const strategyWithdrawTx = (
1779
+ // AppContext
1780
+ reserveMap,
1781
+ // Strategy
1782
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, withdraw, transaction, dryRunTransaction, returnWithdrawnCoin) => __awaiter(void 0, void 0, void 0, function* () {
1783
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1784
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
1785
+ const lst = strategyInfo.depositLstCoinType !== undefined
1786
+ ? lstMap[strategyInfo.depositLstCoinType]
1787
+ : undefined;
1788
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
1789
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
1790
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
1791
+ console.log(`[strategyWithdraw] args |`, JSON.stringify({
1792
+ _address,
1793
+ strategyOwnerCapId,
1794
+ obligationId,
1795
+ _deposits: _deposits.map((d) => ({
1796
+ coinType: d.coinType,
1797
+ depositedAmount: d.depositedAmount.toFixed(20),
1798
+ })),
1799
+ _borrowedAmount: _borrowedAmount.toFixed(20),
1800
+ withdraw: {
1801
+ coinType: withdraw.coinType,
1802
+ withdrawnAmount: withdraw.withdrawnAmount.toFixed(20),
1803
+ },
1804
+ returnWithdrawnCoin,
1805
+ }, null, 2));
1806
+ const depositReserve = ((_a = depositReserves.base) !== null && _a !== void 0 ? _a : depositReserves.lst); // Must have LST if no base
1807
+ //
1808
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
1809
+ let borrowedAmount = _borrowedAmount;
1810
+ const withdrawnAmount = (depositReserve.coinType === ((_b = depositReserves.base) === null || _b === void 0 ? void 0 : _b.coinType)
1811
+ ? withdraw.withdrawnAmount
1812
+ : (0, sui_fe_1.isSui)(withdraw.coinType)
1813
+ ? withdraw.withdrawnAmount
1814
+ .div(1 - +((_c = lst === null || lst === void 0 ? void 0 : lst.redeemFeePercent) !== null && _c !== void 0 ? _c : 0) / 100) // Potential rounding issue (max 1 MIST)
1815
+ .div((_d = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _d !== void 0 ? _d : 1)
1816
+ : withdraw.withdrawnAmount).decimalPlaces(depositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1817
+ const withdrawnAmountUsd = withdrawnAmount
1818
+ .times(depositReserve.price)
1819
+ .times(depositReserve.coinType === ((_e = depositReserves.base) === null || _e === void 0 ? void 0 : _e.coinType)
1820
+ ? 1
1821
+ : new bignumber_js_1.default((_f = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _f !== void 0 ? _f : 1).times(1 - +((_g = lst === null || lst === void 0 ? void 0 : lst.redeemFeePercent) !== null && _g !== void 0 ? _g : 0) / 100));
1822
+ const exposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
1823
+ const tvlAmountUsd = (0, exports.getStrategyTvlAmount)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)).times(defaultCurrencyReserve.price);
1824
+ const targetTvlAmountUsd = tvlAmountUsd.minus(withdrawnAmountUsd);
1825
+ const targetBorrowedAmount = targetTvlAmountUsd
1826
+ .times(exposure.minus(1))
1827
+ .div(borrowReserve.price)
1828
+ .decimalPlaces(borrowReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
1829
+ console.log(`[withdraw] processed_args |`, JSON.stringify({
1830
+ depositReserve_coinType: depositReserve.coinType,
1831
+ withdrawnAmount: withdrawnAmount.toFixed(20),
1832
+ withdrawnAmountUsd: withdrawnAmountUsd.toFixed(20),
1833
+ exposure: exposure.toFixed(20),
1834
+ tvlAmountUsd: tvlAmountUsd.toFixed(20),
1835
+ targetTvlAmountUsd: targetTvlAmountUsd.toFixed(20),
1836
+ targetBorrowedAmount: targetBorrowedAmount.toFixed(20),
1837
+ }, null, 2));
1838
+ // 1) Unloop to targetBorrowedAmount borrows
1839
+ // 1.1) Unloop
1840
+ if (borrowedAmount.gt(targetBorrowedAmount)) {
1841
+ const { deposits: newDeposits, borrowedAmount: newBorrowedAmount, transaction: newTransaction, } = yield (0, exports.strategyUnloopToExposureTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, targetBorrowedAmount, // Pass targetBorrowedAmount
1842
+ undefined, // Don't pass targetExposure
1843
+ transaction, dryRunTransaction);
1844
+ // 1.2) Update state
1845
+ deposits = newDeposits;
1846
+ borrowedAmount = newBorrowedAmount;
1847
+ transaction = newTransaction;
1848
+ console.log(`[withdraw] unloop.update_state |`, JSON.stringify({
1849
+ deposits: deposits.map((d) => ({
1850
+ coinType: d.coinType,
1851
+ depositedAmount: d.depositedAmount.toFixed(20),
1852
+ })),
1853
+ borrowedAmount: borrowedAmount.toFixed(20),
1854
+ targetBorrowedAmount: targetBorrowedAmount.toFixed(20),
1855
+ }, null, 2));
1856
+ }
1857
+ // 2) Withdraw base or LST
1858
+ // 2.1) Withdraw
1859
+ const [withdrawnCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(depositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserve.coinType), BigInt(new bignumber_js_1.default(withdrawnAmount
1860
+ .times(10 ** depositReserve.token.decimals)
1861
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
1862
+ .toString())
1863
+ .div(depositReserve.cTokenExchangeRate)
1864
+ .integerValue(bignumber_js_1.default.ROUND_UP)
1865
+ .toString()), transaction);
1866
+ // 2.2) Update state
1867
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
1868
+ coinType: depositReserve.coinType,
1869
+ depositedAmount: withdrawnAmount.times(-1),
1870
+ });
1871
+ const newExposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
1872
+ const newTvlAmountUsd = (0, exports.getStrategyTvlAmount)(reserveMap, lstMap, strategyType, (0, exports.getStrategySimulatedObligation)(reserveMap, lstMap, strategyType, deposits, borrowedAmount)).times(defaultCurrencyReserve.price);
1873
+ console.log(`[withdraw] withdraw.update_state |`, JSON.stringify({
1874
+ deposits: deposits.map((d) => ({
1875
+ coinType: d.coinType,
1876
+ depositedAmount: d.depositedAmount.toFixed(20),
1877
+ })),
1878
+ borrowedAmount: borrowedAmount.toFixed(20),
1879
+ exposure: exposure.toFixed(20),
1880
+ newExposure: newExposure.toFixed(20),
1881
+ tvlAmountUsd: tvlAmountUsd.toFixed(20),
1882
+ targetTvlAmountUsd: targetTvlAmountUsd.toFixed(20),
1883
+ newTvlAmountUsd: newTvlAmountUsd.toFixed(20),
1884
+ }, null, 2));
1885
+ // 3) Transfer coin to user, or return coin
1886
+ if (returnWithdrawnCoin)
1887
+ return { deposits, borrowedAmount, transaction, withdrawnCoin };
1888
+ else {
1889
+ if (depositReserve.coinType === ((_h = depositReserves.base) === null || _h === void 0 ? void 0 : _h.coinType)) {
1890
+ // 3.1) Transfer base to user
1891
+ transaction.transferObjects([withdrawnCoin], _address);
1892
+ }
1893
+ else {
1894
+ if ((0, sui_fe_1.isSui)(withdraw.coinType)) {
1895
+ // 3.1) Unstake LST for SUI
1896
+ const suiWithdrawnCoin = lst.client.redeem(transaction, withdrawnCoin);
1897
+ // 3.2) Transfer SUI to user
1898
+ transaction.transferObjects([suiWithdrawnCoin], _address);
1899
+ }
1900
+ else {
1901
+ // 3.1) Transfer LST to user
1902
+ transaction.transferObjects([withdrawnCoin], _address);
1903
+ }
1904
+ }
1905
+ }
1906
+ return { deposits, borrowedAmount, transaction };
1907
+ });
1908
+ exports.strategyWithdrawTx = strategyWithdrawTx;
1909
+ const strategyMaxWithdrawTx = (
1910
+ // AppContext
1911
+ reserveMap, rewardPriceMap, rewardsMap,
1912
+ // Strategy
1913
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, withdrawCoinType, transaction, dryRunTransaction) => __awaiter(void 0, void 0, void 0, function* () {
1914
+ var _a, _b;
1915
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
1916
+ const lst = strategyInfo.depositLstCoinType !== undefined
1917
+ ? lstMap[strategyInfo.depositLstCoinType]
1918
+ : undefined;
1919
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
1920
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
1921
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
1922
+ const hasClaimableRewards = Object.values(rewardsMap).some(({ amount }) => amount.gt(0));
1923
+ console.log(`[strategyMaxWithdraw] args |`, JSON.stringify({
1924
+ _address,
1925
+ strategyOwnerCapId,
1926
+ obligationId,
1927
+ _deposits: _deposits.map((d) => ({
1928
+ coinType: d.coinType,
1929
+ depositedAmount: d.depositedAmount.toFixed(20),
1930
+ })),
1931
+ _borrowedAmount: _borrowedAmount.toFixed(20),
1932
+ withdrawCoinType,
1933
+ }, null, 2));
1934
+ const depositReserve = ((_a = depositReserves.base) !== null && _a !== void 0 ? _a : depositReserves.lst); // Must have LST if no base
1935
+ //
1936
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
1937
+ let borrowedAmount = _borrowedAmount;
1938
+ // 1) Unloop to 1x (base+LST: no LST and no borrows, LST: no borrows)
1939
+ if (borrowedAmount.gt(0)) {
1940
+ // 1.1) Unloop
1941
+ const { deposits: newDeposits, borrowedAmount: newBorrowedAmount, transaction: newTransaction, } = yield (0, exports.strategyUnloopToExposureTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
1942
+ new bignumber_js_1.default(1), // Pass targetExposure
1943
+ transaction, dryRunTransaction);
1944
+ // 1.2) Update state
1945
+ deposits = newDeposits;
1946
+ borrowedAmount = newBorrowedAmount;
1947
+ transaction = newTransaction;
1948
+ console.log(`[strategyMaxWithdraw] unloop.update_state |`, JSON.stringify({
1949
+ deposits: deposits.map((d) => ({
1950
+ coinType: d.coinType,
1951
+ depositedAmount: d.depositedAmount.toFixed(20),
1952
+ })),
1953
+ borrowedAmount: borrowedAmount.toFixed(20),
1954
+ }, null, 2));
1955
+ }
1956
+ // 2) MAX withdraw base or LST
1957
+ const [withdrawnCoin] = (0, strategyOwnerCap_1.strategyWithdraw)(depositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserve.coinType), BigInt(sui_fe_1.MAX_U64.toString()), transaction);
1958
+ // 2.2) Update state
1959
+ deposits = [];
1960
+ console.log(`[strategyMaxWithdraw] max_withdraw.update_state |`, JSON.stringify({
1961
+ deposits: deposits.map((d) => ({
1962
+ coinType: d.coinType,
1963
+ depositedAmount: d.depositedAmount.toFixed(20),
1964
+ })),
1965
+ borrowedAmount: borrowedAmount.toFixed(20),
1966
+ }, null, 2));
1967
+ // 3) Transfer coin to user
1968
+ if (depositReserve.coinType === ((_b = depositReserves.base) === null || _b === void 0 ? void 0 : _b.coinType)) {
1969
+ // 3.1) Transfer base to user
1970
+ transaction.transferObjects([withdrawnCoin], _address);
1971
+ }
1972
+ else {
1973
+ if ((0, sui_fe_1.isSui)(withdrawCoinType)) {
1974
+ // 3.1) Unstake LST for SUI
1975
+ const suiWithdrawnCoin = lst.client.redeem(transaction, withdrawnCoin);
1976
+ // 3.2) Transfer SUI to user
1977
+ transaction.transferObjects([suiWithdrawnCoin], _address);
1978
+ }
1979
+ else {
1980
+ // 3.1) Transfer LST to user
1981
+ transaction.transferObjects([withdrawnCoin], _address);
1982
+ }
1983
+ }
1984
+ // 4) Claim rewards, swap for withdrawCoinType, and transfer to user
1985
+ if (hasClaimableRewards) {
1986
+ try {
1987
+ const txCopy = transactions_1.Transaction.from(transaction);
1988
+ yield (0, strategyOwnerCap_1.strategyClaimRewardsAndSwapForCoinType)(_address, cetusSdk, cetusPartnerId, rewardsMap, rewardPriceMap, reserveMap[withdrawCoinType], strategyOwnerCapId, false, // isDepositing (false = transfer to user)
1989
+ txCopy);
1990
+ yield dryRunTransaction(txCopy); // Throws error if fails
1991
+ transaction = txCopy;
1992
+ }
1993
+ catch (err) {
1994
+ // Don't block user if fails
1995
+ console.error(err);
1996
+ }
1997
+ }
1998
+ return { deposits, borrowedAmount, transaction };
1999
+ });
2000
+ exports.strategyMaxWithdrawTx = strategyMaxWithdrawTx;
2001
+ const strategyDepositAdjustWithdrawTx = (
2002
+ // AppContext
2003
+ reserveMap,
2004
+ // Strategy
2005
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, _deposits, _borrowedAmount, flashLoanBorrowedAmount, transaction, dryRunTransaction) => __awaiter(void 0, void 0, void 0, function* () {
2006
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
2007
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
2008
+ const lst = strategyInfo.depositLstCoinType !== undefined
2009
+ ? lstMap[strategyInfo.depositLstCoinType]
2010
+ : undefined;
2011
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
2012
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
2013
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
2014
+ const depositAdjustWithdrawExposure = exports.STRATEGY_TYPE_EXPOSURE_MAP[strategyType].max;
2015
+ console.log(`[strategyDepositAdjustWithdraw] args |`, JSON.stringify({
2016
+ _address,
2017
+ strategyOwnerCapId,
2018
+ obligationId,
2019
+ _deposits: _deposits.map((d) => ({
2020
+ coinType: d.coinType,
2021
+ depositedAmount: d.depositedAmount.toFixed(20),
2022
+ })),
2023
+ _borrowedAmount: _borrowedAmount.toFixed(20),
2024
+ flashLoanBorrowedAmount: flashLoanBorrowedAmount.toFixed(20),
2025
+ }, null, 2));
2026
+ const depositReserve = ((_a = depositReserves.base) !== null && _a !== void 0 ? _a : depositReserves.lst); // Must have LST if no base
2027
+ //
2028
+ let deposits = (0, lodash_1.cloneDeep)(_deposits);
2029
+ let borrowedAmount = _borrowedAmount;
2030
+ // 1) Flash loan borrow
2031
+ const flashLoanObj = exports.STRATEGY_TYPE_FLASH_LOAN_OBJ_MAP[strategyType];
2032
+ if (depositReserve.coinType === ((_b = depositReserves.lst) === null || _b === void 0 ? void 0 : _b.coinType)) {
2033
+ // TODO: Account for LST mint fees
2034
+ flashLoanBorrowedAmount = flashLoanBorrowedAmount
2035
+ .times((_c = lst === null || lst === void 0 ? void 0 : lst.lstToSuiExchangeRate) !== null && _c !== void 0 ? _c : 1)
2036
+ .decimalPlaces(utils_1.SUI_DECIMALS, bignumber_js_1.default.ROUND_UP);
2037
+ }
2038
+ let borrowedBalanceA, borrowedBalanceB, receipt;
2039
+ if (flashLoanObj.provider === StrategyFlashLoanProvider.MMT) {
2040
+ [borrowedBalanceA, borrowedBalanceB, receipt] = transaction.moveCall({
2041
+ target: `${mmt_1.MMT_CONTRACT_PACKAGE_ID}::trade::flash_loan`,
2042
+ typeArguments: [flashLoanObj.coinTypeA, flashLoanObj.coinTypeB],
2043
+ arguments: [
2044
+ transaction.object(flashLoanObj.poolId),
2045
+ transaction.pure.u64(flashLoanObj.borrowA
2046
+ ? flashLoanBorrowedAmount
2047
+ .times(10 **
2048
+ (depositReserve.coinType === ((_d = depositReserves.lst) === null || _d === void 0 ? void 0 : _d.coinType)
2049
+ ? utils_1.SUI_DECIMALS
2050
+ : depositReserve.token.decimals))
2051
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
2052
+ .toString()
2053
+ : 0),
2054
+ transaction.pure.u64(flashLoanObj.borrowA
2055
+ ? 0
2056
+ : flashLoanBorrowedAmount
2057
+ .times(10 **
2058
+ (depositReserve.coinType === ((_e = depositReserves.lst) === null || _e === void 0 ? void 0 : _e.coinType)
2059
+ ? utils_1.SUI_DECIMALS
2060
+ : depositReserve.token.decimals))
2061
+ .integerValue(bignumber_js_1.default.ROUND_DOWN)
2062
+ .toString()),
2063
+ transaction.object(mmt_1.MMT_VERSION_OBJECT_ID),
2064
+ ],
2065
+ });
2066
+ }
2067
+ else {
2068
+ throw new Error("Invalid flash loan provider");
2069
+ }
2070
+ // 2) Deposit additional (to get back up to 100% health, so the user can then unloop back down to the max leverage shown in the UI)
2071
+ // 2.1) Deposit
2072
+ let depositedAmount = flashLoanBorrowedAmount;
2073
+ if (depositReserve.coinType === ((_f = depositReserves.lst) === null || _f === void 0 ? void 0 : _f.coinType))
2074
+ depositedAmount = new bignumber_js_1.default(depositedAmount.minus((0, exports.getStrategyLstMintFee)(lstMap, depositReserve.coinType, depositedAmount)))
2075
+ .times((_g = lst === null || lst === void 0 ? void 0 : lst.suiToLstExchangeRate) !== null && _g !== void 0 ? _g : 1)
2076
+ .decimalPlaces(depositReserve.token.decimals, bignumber_js_1.default.ROUND_DOWN);
2077
+ let flashLoanBorrowedCoin = transaction.moveCall({
2078
+ target: "0x2::coin::from_balance",
2079
+ typeArguments: [
2080
+ flashLoanObj.borrowA ? flashLoanObj.coinTypeA : flashLoanObj.coinTypeB,
2081
+ ],
2082
+ arguments: [flashLoanObj.borrowA ? borrowedBalanceA : borrowedBalanceB],
2083
+ });
2084
+ if (depositReserve.coinType === ((_h = depositReserves.lst) === null || _h === void 0 ? void 0 : _h.coinType))
2085
+ flashLoanBorrowedCoin = lst.client.mint(transaction, flashLoanBorrowedCoin);
2086
+ (0, strategyOwnerCap_1.strategyDeposit)(flashLoanBorrowedCoin, depositReserve.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserve.coinType), transaction);
2087
+ // 2.2) Update state
2088
+ deposits = (0, exports.addOrInsertStrategyDeposit)(deposits, {
2089
+ coinType: depositReserve.coinType,
2090
+ depositedAmount,
2091
+ });
2092
+ // 3) Unloop to max exposure
2093
+ // 3.1) Unloop
2094
+ const { deposits: newDeposits, borrowedAmount: newBorrowedAmount, transaction: newTransaction, } = yield (0, exports.strategyUnloopToExposureTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
2095
+ depositAdjustWithdrawExposure, // Pass targetExposure
2096
+ transaction, dryRunTransaction);
2097
+ // 3.2) Update state
2098
+ deposits = newDeposits;
2099
+ borrowedAmount = newBorrowedAmount;
2100
+ transaction = newTransaction;
2101
+ // 4) Repay flash loan + fee
2102
+ // 4.1) Withdraw additional + fee
2103
+ let withdrawnAmount = depositedAmount
2104
+ .times(1 + flashLoanObj.feePercent / 100)
2105
+ .decimalPlaces(depositReserve.token.decimals, bignumber_js_1.default.ROUND_UP);
2106
+ if (depositReserve.coinType === ((_j = depositReserves.lst) === null || _j === void 0 ? void 0 : _j.coinType))
2107
+ withdrawnAmount = withdrawnAmount
2108
+ .div(1 - +((_k = lst === null || lst === void 0 ? void 0 : lst.redeemFeePercent) !== null && _k !== void 0 ? _k : 0) / 100) // Potential rounding issue (max 1 MIST)
2109
+ .decimalPlaces(depositReserve.token.decimals, bignumber_js_1.default.ROUND_UP);
2110
+ const { deposits: newDeposits2, borrowedAmount: newBorrowedAmount2, transaction: newTransaction2, withdrawnCoin, } = yield (0, exports.strategyWithdrawTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, {
2111
+ coinType: depositReserve.coinType,
2112
+ withdrawnAmount,
2113
+ }, transaction, dryRunTransaction, true);
2114
+ if (!withdrawnCoin)
2115
+ throw new Error("Withdrawn coin not found");
2116
+ let flashLoanRepayCoin = withdrawnCoin;
2117
+ if (depositReserve.coinType === ((_l = depositReserves.lst) === null || _l === void 0 ? void 0 : _l.coinType))
2118
+ flashLoanRepayCoin = lst.client.redeem(transaction, flashLoanRepayCoin);
2119
+ // 4.2) Repay flash loan
2120
+ const flashLoanRepayBalance = transaction.moveCall({
2121
+ target: "0x2::coin::into_balance",
2122
+ typeArguments: [
2123
+ flashLoanObj.borrowA ? flashLoanObj.coinTypeA : flashLoanObj.coinTypeB,
2124
+ ],
2125
+ arguments: [flashLoanRepayCoin],
2126
+ });
2127
+ if (flashLoanObj.provider === StrategyFlashLoanProvider.MMT) {
2128
+ transaction.moveCall({
2129
+ target: `${mmt_1.MMT_CONTRACT_PACKAGE_ID}::trade::repay_flash_loan`,
2130
+ typeArguments: [flashLoanObj.coinTypeA, flashLoanObj.coinTypeB],
2131
+ arguments: [
2132
+ transaction.object(flashLoanObj.poolId),
2133
+ receipt,
2134
+ flashLoanObj.borrowA ? flashLoanRepayBalance : borrowedBalanceA,
2135
+ flashLoanObj.borrowA ? borrowedBalanceB : flashLoanRepayBalance,
2136
+ transaction.object(mmt_1.MMT_VERSION_OBJECT_ID),
2137
+ ],
2138
+ });
2139
+ }
2140
+ else {
2141
+ throw new Error("Invalid flash loan provider");
2142
+ }
2143
+ // 4.3) Update state
2144
+ deposits = newDeposits2;
2145
+ borrowedAmount = newBorrowedAmount2;
2146
+ transaction = newTransaction2;
2147
+ return { deposits, borrowedAmount, transaction };
2148
+ });
2149
+ exports.strategyDepositAdjustWithdrawTx = strategyDepositAdjustWithdrawTx;
2150
+ const strategyAdjustTx = (
2151
+ // AppContext
2152
+ reserveMap,
2153
+ // Strategy
2154
+ lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation, _deposits, _borrowedAmount, targetExposure, transaction, dryRunTransaction) => __awaiter(void 0, void 0, void 0, function* () {
2155
+ const strategyInfo = strategyOwnerCap_1.STRATEGY_TYPE_INFO_MAP[strategyType];
2156
+ const lst = strategyInfo.depositLstCoinType !== undefined
2157
+ ? lstMap[strategyInfo.depositLstCoinType]
2158
+ : undefined;
2159
+ const depositReserves = (0, exports.getStrategyDepositReserves)(reserveMap, strategyType);
2160
+ const borrowReserve = (0, exports.getStrategyBorrowReserve)(reserveMap, strategyType);
2161
+ const defaultCurrencyReserve = (0, exports.getStrategyDefaultCurrencyReserve)(reserveMap, strategyType);
2162
+ const exposure = (0, exports.getStrategyExposure)(reserveMap, lstMap, strategyType, obligation);
2163
+ console.log(`[strategyAdjust] args |`, JSON.stringify({
2164
+ _address,
2165
+ strategyOwnerCapId,
2166
+ obligationId: obligation.id,
2167
+ _deposits: _deposits.map((d) => ({
2168
+ coinType: d.coinType,
2169
+ depositedAmount: d.depositedAmount.toFixed(20),
2170
+ })),
2171
+ _borrowedAmount: _borrowedAmount.toFixed(20),
2172
+ targetExposure: targetExposure.toFixed(20),
2173
+ }, null, 2));
2174
+ //
2175
+ const deposits = (0, lodash_1.cloneDeep)(_deposits);
2176
+ const borrowedAmount = _borrowedAmount;
2177
+ // 1) Loop or unloop to target exposure
2178
+ if (targetExposure.gt(exposure))
2179
+ return (0, exports.strategyLoopToExposureTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
2180
+ targetExposure, // Pass targetExposure
2181
+ transaction);
2182
+ else if (targetExposure.lt(exposure))
2183
+ return (0, exports.strategyUnloopToExposureTx)(reserveMap, lstMap, strategyType, suiClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
2184
+ targetExposure, // Pass targetExposure
2185
+ transaction, dryRunTransaction);
2186
+ else
2187
+ return { deposits, borrowedAmount, transaction };
2188
+ });
2189
+ exports.strategyAdjustTx = strategyAdjustTx;