@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/client.d.ts +1 -1
- package/client.js +15 -7
- package/lib/initialize.js +5 -0
- package/lib/liquidityMining.d.ts +2 -2
- package/lib/liquidityMining.js +3 -3
- package/mmt.d.ts +2 -0
- package/mmt.js +5 -0
- package/package.json +1 -1
- package/strategies.d.ts +86 -1
- package/strategies.js +1788 -1
- package/swap/quote.js +0 -35
- package/swap/transaction.js +0 -35
- package/utils/index.d.ts +10 -0
- package/utils/index.js +34 -1
- package/utils/obligation.d.ts +2 -0
- package/utils/obligation.js +8 -0
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;
|