@suilend/sdk 12.3.0-next.gcbeb44e → 12.4.0-next.g7a809d2
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/_generated/suilend/lending-market/functions.d.ts +1 -0
- package/_generated/suilend/lending-market/functions.js +11 -0
- package/_generated/suilend/reserve-config/functions.d.ts +5 -0
- package/_generated/suilend/reserve-config/functions.js +15 -0
- package/client.d.ts +25 -4
- package/client.js +140 -28
- package/lib/initialize.d.ts +8 -0
- package/package.json +1 -1
- package/parsers/lendingMarket.d.ts +1 -0
- package/parsers/obligation.d.ts +2 -0
- package/parsers/reserve.d.ts +2 -0
- package/parsers/reserve.js +4 -2
- package/strategies.d.ts +4 -1
- package/strategies.js +99 -19
|
@@ -58,6 +58,7 @@ export interface ClosePoolRewardArgs {
|
|
|
58
58
|
}
|
|
59
59
|
export declare function closePoolReward(tx: Transaction, typeArgs: [string, string], args: ClosePoolRewardArgs): import("@mysten/sui/transactions").TransactionResult;
|
|
60
60
|
export declare function reserve(tx: Transaction, typeArgs: [string, string], lendingMarket: TransactionObjectInput): import("@mysten/sui/transactions").TransactionResult;
|
|
61
|
+
export declare function reserveConfigBuilder(tx: Transaction, typeArgs: [string, string], lendingMarket: TransactionObjectInput): import("@mysten/sui/transactions").TransactionResult;
|
|
61
62
|
export interface CompoundInterestArgs {
|
|
62
63
|
lendingMarket: TransactionObjectInput;
|
|
63
64
|
reserveArrayIndex: bigint | TransactionArgument;
|
|
@@ -107,6 +107,17 @@ export function reserve(tx, typeArgs, lendingMarket) {
|
|
|
107
107
|
arguments: [obj(tx, lendingMarket)],
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
+
// lending_market::reserve_config_builder has no published package to
|
|
111
|
+
// regenerate a binding from yet, so this is hand-written to match the shape
|
|
112
|
+
// codegen already produces for `reserve` above. Replace it with the real
|
|
113
|
+
// generated output once this package publishes and the generator runs again.
|
|
114
|
+
export function reserveConfigBuilder(tx, typeArgs, lendingMarket) {
|
|
115
|
+
return tx.moveCall({
|
|
116
|
+
target: `${PUBLISHED_AT}::lending_market::reserve_config_builder`,
|
|
117
|
+
typeArguments: typeArgs,
|
|
118
|
+
arguments: [obj(tx, lendingMarket)],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
110
121
|
export function compoundInterest(tx, typeArg, args) {
|
|
111
122
|
return tx.moveCall({
|
|
112
123
|
target: `${PUBLISHED_AT}::lending_market::compound_interest`,
|
|
@@ -13,6 +13,11 @@ export interface SetSpreadFeeBpsArgs {
|
|
|
13
13
|
spreadFeeBps: bigint | TransactionArgument;
|
|
14
14
|
}
|
|
15
15
|
export declare function setSpreadFeeBps(tx: Transaction, args: SetSpreadFeeBpsArgs): import("@mysten/sui/transactions").TransactionResult;
|
|
16
|
+
export interface SetConfidenceMultiplierBuilderArgs {
|
|
17
|
+
builder: TransactionObjectInput;
|
|
18
|
+
confidenceMultiplierBps: bigint | TransactionArgument;
|
|
19
|
+
}
|
|
20
|
+
export declare function setConfidenceMultiplierBuilder(tx: Transaction, args: SetConfidenceMultiplierBuilderArgs): import("@mysten/sui/transactions").TransactionResult;
|
|
16
21
|
export declare function spreadFee(tx: Transaction, config: TransactionObjectInput): import("@mysten/sui/transactions").TransactionResult;
|
|
17
22
|
export declare function borrowFee(tx: Transaction, config: TransactionObjectInput): import("@mysten/sui/transactions").TransactionResult;
|
|
18
23
|
export declare function borrowLimit(tx: Transaction, config: TransactionObjectInput): import("@mysten/sui/transactions").TransactionResult;
|
|
@@ -29,6 +29,21 @@ export function setSpreadFeeBps(tx, args) {
|
|
|
29
29
|
arguments: [obj(tx, args.builder), pure(tx, args.spreadFeeBps, `u64`)],
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
+
// reserve_config::set_confidence_multiplier_builder is published (v25), but
|
|
33
|
+
// this SDK's PUBLISHED_AT/PKG_V* constants haven't been bumped to v25 yet, so
|
|
34
|
+
// there's nothing to regenerate a binding from until that separate regen
|
|
35
|
+
// pass happens. Hand-written to match the shape codegen already produces for
|
|
36
|
+
// every other u64 builder setter in this file (e.g. setSpreadFeeBps above).
|
|
37
|
+
// Replace it with the real generated output once that regen lands.
|
|
38
|
+
export function setConfidenceMultiplierBuilder(tx, args) {
|
|
39
|
+
return tx.moveCall({
|
|
40
|
+
target: `${PUBLISHED_AT}::reserve_config::set_confidence_multiplier_builder`,
|
|
41
|
+
arguments: [
|
|
42
|
+
obj(tx, args.builder),
|
|
43
|
+
pure(tx, args.confidenceMultiplierBps, `u64`),
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
}
|
|
32
47
|
export function spreadFee(tx, config) {
|
|
33
48
|
return tx.moveCall({
|
|
34
49
|
target: `${PUBLISHED_AT}::reserve_config::spread_fee`,
|
package/client.d.ts
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { SuiClientTypes } from "@mysten/sui/client";
|
|
2
2
|
import { SuiGrpcClient } from "@mysten/sui/grpc";
|
|
3
|
-
import { Transaction, TransactionObjectArgument, TransactionObjectInput } from "@mysten/sui/transactions";
|
|
3
|
+
import { Transaction, TransactionArgument, TransactionObjectArgument, TransactionObjectInput } from "@mysten/sui/transactions";
|
|
4
4
|
import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
|
|
5
|
+
import BigNumber from "bignumber.js";
|
|
5
6
|
import { LendingMarket, ObligationOwnerCap } from "./_generated/suilend/lending-market/structs";
|
|
6
7
|
import { Obligation } from "./_generated/suilend/obligation/structs";
|
|
7
8
|
import { NewConfigArgs as CreateRateLimiterConfigArgs } from "./_generated/suilend/rate-limiter/functions";
|
|
8
9
|
import { CreateReserveConfigArgs } from "./_generated/suilend/reserve-config/functions";
|
|
9
10
|
import { type PriceUpdateDataSource } from "./lib/pyth";
|
|
10
11
|
import { Side } from "./lib/types";
|
|
12
|
+
/**
|
|
13
|
+
* Signed Pyth update data the caller already fetched for `priceIds`, posted
|
|
14
|
+
* unconditionally instead of the on-chain staleness check deciding.
|
|
15
|
+
*/
|
|
16
|
+
export interface PriceUpdate {
|
|
17
|
+
priceIds: string[];
|
|
18
|
+
updateData: Buffer[];
|
|
19
|
+
}
|
|
11
20
|
export declare function needsPriceRefresh(stalenessSeconds: number): boolean;
|
|
12
21
|
export declare const ADMIN_ADDRESS: string;
|
|
13
22
|
export declare const LENDING_MARKET_REGISTRY_ID: string;
|
|
@@ -41,6 +50,9 @@ export type ClaimRewardsReward = {
|
|
|
41
50
|
rewardCoinType: string;
|
|
42
51
|
side: Side;
|
|
43
52
|
};
|
|
53
|
+
export type PatchReserveConfigArgs = Partial<CreateReserveConfigArgs> & {
|
|
54
|
+
confidenceMultiplierBps?: bigint | TransactionArgument;
|
|
55
|
+
};
|
|
44
56
|
export declare class SuilendClient {
|
|
45
57
|
lendingMarket: LendingMarket<string>;
|
|
46
58
|
suiGrpcClient: SuiGrpcClient;
|
|
@@ -71,6 +83,8 @@ export declare class SuilendClient {
|
|
|
71
83
|
receivers: string[];
|
|
72
84
|
weights: string[];
|
|
73
85
|
}>;
|
|
86
|
+
/** A reserve's confidence multiplier `k`, in bps; pass `parsedReserve.config.additionalFields.id`. 0 when unset. */
|
|
87
|
+
static getReserveConfidenceMultiplierBps(suiGrpcClient: SuiGrpcClient, additionalFieldsBagId: string): Promise<BigNumber>;
|
|
74
88
|
static createNewLendingMarket(registryId: string, lendingMarketType: string, transaction: Transaction): {
|
|
75
89
|
NestedResult: [number, number];
|
|
76
90
|
$kind: "NestedResult";
|
|
@@ -94,12 +108,19 @@ export declare class SuilendClient {
|
|
|
94
108
|
claimRewardsAndSendToUser(ownerId: string, obligationOwnerCap: TransactionObjectInput, rewards: ClaimRewardsReward[], transaction: Transaction): Transaction;
|
|
95
109
|
claimRewardsAndDeposit(ownerId: string, obligationOwnerCap: TransactionObjectInput, rewards: ClaimRewardsReward[], transaction: Transaction): Transaction;
|
|
96
110
|
findReserveArrayIndex(coinType: string): bigint;
|
|
111
|
+
private submitReserveConfigUpdate;
|
|
97
112
|
updateReserveConfig(lendingMarketOwnerCapId: string, transaction: Transaction, coinType: string, createReserveConfigArgs: CreateReserveConfigArgs): import("@mysten/sui/transactions").TransactionResult;
|
|
113
|
+
/**
|
|
114
|
+
* Updates a subset of a reserve's config fields, leaving every unspecified
|
|
115
|
+
* field -- including `additionalFields` entries like the confidence
|
|
116
|
+
* multiplier -- unchanged from the reserve's current on-chain value.
|
|
117
|
+
*/
|
|
118
|
+
patchReserveConfig(lendingMarketOwnerCapId: string, transaction: Transaction, coinType: string, overrides: PatchReserveConfigArgs): import("@mysten/sui/transactions").TransactionResult;
|
|
98
119
|
newObligationOwnerCap(transaction: Transaction, lendingMarketOwnerCapId: string, destinationAddress: string, obligationId: string): void;
|
|
99
120
|
updateRateLimiterConfig(lendingMarketOwnerCapId: string, transaction: Transaction, newRateLimiterConfigArgs: CreateRateLimiterConfigArgs): import("@mysten/sui/transactions").TransactionResult;
|
|
100
121
|
changeReservePriceFeed(lendingMarketOwnerCapId: string, coinType: string, pythPriceId: string, transaction: Transaction): Promise<import("@mysten/sui/transactions").TransactionResult>;
|
|
101
122
|
createObligation(transaction: Transaction): import("@mysten/sui/transactions").TransactionResult;
|
|
102
|
-
refreshAll(transaction: Transaction, obligation?: Obligation<string>, coinTypes?: string[]): Promise<void>;
|
|
123
|
+
refreshAll(transaction: Transaction, obligation?: Obligation<string>, coinTypes?: string[], priceUpdate?: PriceUpdate): Promise<void>;
|
|
103
124
|
refreshReservePrices(transaction: Transaction, priceInfoObjectId: string, reserveArrayIndex: bigint): Promise<void>;
|
|
104
125
|
deposit(sendCoin: TransactionObjectInput, coinType: string, obligationOwnerCap: TransactionObjectInput, transaction: Transaction): void;
|
|
105
126
|
depositIntoObligation(ownerId: string, coinType: string, value: string, transaction: Transaction, obligationOwnerCap: TransactionObjectInput): Promise<void>;
|
|
@@ -111,8 +132,8 @@ export declare class SuilendClient {
|
|
|
111
132
|
borrowAndSendToUser(ownerId: string, obligationOwnerCap: TransactionObjectInput, obligationId: string, coinType: string, value: string, transaction: Transaction): Promise<void>;
|
|
112
133
|
repay(obligationId: string, coinType: string, coin: TransactionObjectInput, transaction: Transaction): import("@mysten/sui/transactions").TransactionResult;
|
|
113
134
|
repayIntoObligation(ownerId: string, obligationId: string, coinType: string, value: string, transaction: Transaction): Promise<import("@mysten/sui/transactions").TransactionResult>;
|
|
114
|
-
liquidateAndRedeem(transaction: Transaction, obligation: Obligation<string>, repayCoinType: string, withdrawCoinType: string, repayCoinId: TransactionObjectInput): Promise<import("@mysten/sui/transactions").TransactionResult>;
|
|
115
|
-
liquidate(transaction: Transaction, obligation: Obligation<string>, repayCoinType: string, withdrawCoinType: string, repayCoinId: TransactionObjectInput): Promise<import("@mysten/sui/transactions").TransactionResult>;
|
|
135
|
+
liquidateAndRedeem(transaction: Transaction, obligation: Obligation<string>, repayCoinType: string, withdrawCoinType: string, repayCoinId: TransactionObjectInput, priceUpdate?: PriceUpdate): Promise<import("@mysten/sui/transactions").TransactionResult>;
|
|
136
|
+
liquidate(transaction: Transaction, obligation: Obligation<string>, repayCoinType: string, withdrawCoinType: string, repayCoinId: TransactionObjectInput, priceUpdate?: PriceUpdate): Promise<import("@mysten/sui/transactions").TransactionResult>;
|
|
116
137
|
migrate(transaction: Transaction, lendingMarketOwnerCapId: string): import("@mysten/sui/transactions").TransactionResult;
|
|
117
138
|
claimFees(transaction: Transaction, coinType: string): import("@mysten/sui/transactions").TransactionResult;
|
|
118
139
|
setFeeReceiversAndWeights(transaction: Transaction, lendingMarketOwnerCapId: string, receivers: string[], weights: bigint[]): import("@mysten/sui/transactions").TransactionResult;
|
package/client.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { bcs } from "@mysten/sui/bcs";
|
|
1
2
|
import { SUI_CLOCK_OBJECT_ID, SUI_SYSTEM_STATE_OBJECT_ID, normalizeStructTag, normalizeSuiObjectId, toHex, } from "@mysten/sui/utils";
|
|
2
3
|
import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
|
|
4
|
+
import BigNumber from "bignumber.js";
|
|
3
5
|
import { extractCTokenCoinType, getAllCoins, getSpendableCoin, } from "@suilend/sui-core";
|
|
4
6
|
import { phantom } from "./_generated/_framework/reified.js";
|
|
5
7
|
import { PKG_V10, setPublishedAt } from "./_generated/suilend/index.js";
|
|
6
8
|
import { PACKAGE_ID, PUBLISHED_AT } from "./_generated/suilend/index.js";
|
|
7
|
-
import { addPoolReward, borrowRequest, cancelPoolReward, claimFees, claimRewards, claimRewardsAndDeposit, closePoolReward, depositCtokensIntoObligation, depositLiquidityAndMintCtokens, fulfillLiquidityRequest, liquidate, migrate, newObligationOwnerCap, rebalanceStaker, redeemCtokensAndWithdrawLiquidityRequest, repay, setFeeReceivers, unstakeSuiFromStaker, updateRateLimiterConfig, updateReserveConfig, withdrawCtokens, } from "./_generated/suilend/lending-market/functions.js";
|
|
9
|
+
import { addPoolReward, borrowRequest, cancelPoolReward, claimFees, claimRewards, claimRewardsAndDeposit, closePoolReward, depositCtokensIntoObligation, depositLiquidityAndMintCtokens, fulfillLiquidityRequest, liquidate, migrate, newObligationOwnerCap, rebalanceStaker, redeemCtokensAndWithdrawLiquidityRequest, repay, reserveConfigBuilder, setFeeReceivers, unstakeSuiFromStaker, updateRateLimiterConfig, updateReserveConfig, withdrawCtokens, } from "./_generated/suilend/lending-market/functions.js";
|
|
8
10
|
import { LendingMarket, ObligationOwnerCap, } from "./_generated/suilend/lending-market/structs.js";
|
|
9
11
|
import { createLendingMarket } from "./_generated/suilend/lending-market-registry/functions.js";
|
|
10
12
|
import { Obligation } from "./_generated/suilend/obligation/structs.js";
|
|
11
13
|
import { newConfig as createRateLimiterConfig, } from "./_generated/suilend/rate-limiter/functions.js";
|
|
12
|
-
import { createReserveConfig, } from "./_generated/suilend/reserve-config/functions.js";
|
|
14
|
+
import { build as buildReserveConfig, createReserveConfig, setBorrowFeeBps, setBorrowLimit, setBorrowLimitUsd, setBorrowWeightBps, setCloseAttributedBorrowLimitUsd, setCloseLtvPct, setConfidenceMultiplierBuilder, setDepositLimit, setDepositLimitUsd, setInterestRateAprs, setInterestRateUtils, setIsolated, setLiquidationBonusBps, setMaxCloseLtvPct, setMaxLiquidationBonusBps, setOpenAttributedBorrowLimitUsd, setOpenLtvPct, setProtocolLiquidationFeeBps, setSpreadFeeBps, } from "./_generated/suilend/reserve-config/functions.js";
|
|
13
15
|
import { addReserveProCompatible, addReserveV2ProCompatible, changeReservePriceFeedProCompatible, priceInfoPublishTime, refreshReservePriceProCompatible, } from "./lib/proCompatible.js";
|
|
14
16
|
import { resolvePriceUpdateDataSource, } from "./lib/pyth.js";
|
|
15
17
|
import { Side } from "./lib/types.js";
|
|
@@ -28,6 +30,7 @@ const isSui = (coinType) => normalizeStructTag(coinType) === NORMALIZED_SUI_COIN
|
|
|
28
30
|
// refresh would come from a package the contract does not accept.
|
|
29
31
|
const WORMHOLE_STATE_ID = "0xdbca52b9fb4f712e25f61f974586d93ac541bcf8389564f0323bb07215168b5c";
|
|
30
32
|
const PYTH_STATE_ID = "0x03719fae774ddab3cfcaa53bbc046f0cbe21410019b6280811bf3f9f4b05839d";
|
|
33
|
+
const normalizePriceIdentifier = (id) => id.toLowerCase().replace(/^0x/, "");
|
|
31
34
|
// Mirrors suilend::oracles's MAX_FORWARD_SECONDS (move/contracts/suilend/sources/oracles.move);
|
|
32
35
|
// `refresh_reserve_price` now rejects a forward-dated price the same as a stale one.
|
|
33
36
|
const MAX_FORWARD_SECONDS = 60;
|
|
@@ -153,6 +156,87 @@ export async function listAllOwnedObjects(suiGrpcClient, owner, type, include) {
|
|
|
153
156
|
cache.set(key, promise);
|
|
154
157
|
return promise;
|
|
155
158
|
}
|
|
159
|
+
// One entry per CreateReserveConfigArgs field, each just closing over its
|
|
160
|
+
// generated setter -- lets patchReserveConfig apply only the caller's
|
|
161
|
+
// specified overrides with one loop instead of one copy-pasted
|
|
162
|
+
// `if (overrides.x !== undefined) setX(...)` per field.
|
|
163
|
+
const CREATE_RESERVE_CONFIG_FIELD_SETTERS = {
|
|
164
|
+
openLtvPct: (tx, builder, openLtvPct) => setOpenLtvPct(tx, { builder, openLtvPct }),
|
|
165
|
+
closeLtvPct: (tx, builder, closeLtvPct) => setCloseLtvPct(tx, { builder, closeLtvPct }),
|
|
166
|
+
maxCloseLtvPct: (tx, builder, maxCloseLtvPct) => setMaxCloseLtvPct(tx, { builder, maxCloseLtvPct }),
|
|
167
|
+
borrowWeightBps: (tx, builder, borrowWeightBps) => setBorrowWeightBps(tx, { builder, borrowWeightBps }),
|
|
168
|
+
depositLimit: (tx, builder, depositLimit) => setDepositLimit(tx, { builder, depositLimit }),
|
|
169
|
+
borrowLimit: (tx, builder, borrowLimit) => setBorrowLimit(tx, { builder, borrowLimit }),
|
|
170
|
+
liquidationBonusBps: (tx, builder, liquidationBonusBps) => setLiquidationBonusBps(tx, { builder, liquidationBonusBps }),
|
|
171
|
+
maxLiquidationBonusBps: (tx, builder, maxLiquidationBonusBps) => setMaxLiquidationBonusBps(tx, { builder, maxLiquidationBonusBps }),
|
|
172
|
+
depositLimitUsd: (tx, builder, depositLimitUsd) => setDepositLimitUsd(tx, { builder, depositLimitUsd }),
|
|
173
|
+
borrowLimitUsd: (tx, builder, borrowLimitUsd) => setBorrowLimitUsd(tx, { builder, borrowLimitUsd }),
|
|
174
|
+
borrowFeeBps: (tx, builder, borrowFeeBps) => setBorrowFeeBps(tx, { builder, borrowFeeBps }),
|
|
175
|
+
spreadFeeBps: (tx, builder, spreadFeeBps) => setSpreadFeeBps(tx, { builder, spreadFeeBps }),
|
|
176
|
+
protocolLiquidationFeeBps: (tx, builder, protocolLiquidationFeeBps) => setProtocolLiquidationFeeBps(tx, { builder, protocolLiquidationFeeBps }),
|
|
177
|
+
interestRateUtils: (tx, builder, interestRateUtils) => setInterestRateUtils(tx, { builder, interestRateUtils }),
|
|
178
|
+
interestRateAprs: (tx, builder, interestRateAprs) => setInterestRateAprs(tx, { builder, interestRateAprs }),
|
|
179
|
+
isolated: (tx, builder, isolated) => setIsolated(tx, { builder, isolated }),
|
|
180
|
+
openAttributedBorrowLimitUsd: (tx, builder, openAttributedBorrowLimitUsd) => setOpenAttributedBorrowLimitUsd(tx, {
|
|
181
|
+
builder,
|
|
182
|
+
openAttributedBorrowLimitUsd,
|
|
183
|
+
}),
|
|
184
|
+
closeAttributedBorrowLimitUsd: (tx, builder, closeAttributedBorrowLimitUsd) => setCloseAttributedBorrowLimitUsd(tx, {
|
|
185
|
+
builder,
|
|
186
|
+
closeAttributedBorrowLimitUsd,
|
|
187
|
+
}),
|
|
188
|
+
};
|
|
189
|
+
// A generic type parameter, unlike indexing CREATE_RESERVE_CONFIG_FIELD_SETTERS
|
|
190
|
+
// with the union `keyof CreateReserveConfigArgs` directly at the call site,
|
|
191
|
+
// keeps TS from expanding the call into one union branch per field (which
|
|
192
|
+
// blows up into "union type too complex to represent").
|
|
193
|
+
function applyCreateReserveConfigFieldOverride(tx, builder, overrides, key) {
|
|
194
|
+
const value = overrides[key];
|
|
195
|
+
if (value === undefined)
|
|
196
|
+
return;
|
|
197
|
+
// TS can't verify this call is sound (calling a mapped type indexed by a
|
|
198
|
+
// generic key produces a union of all 18 setter signatures, which blows up
|
|
199
|
+
// into "union type too complex to represent" rather than typechecking) --
|
|
200
|
+
// but it is: `value`'s type and CREATE_RESERVE_CONFIG_FIELD_SETTERS[key]'s
|
|
201
|
+
// parameter type are both CreateReserveConfigArgs[K] for the same K.
|
|
202
|
+
CREATE_RESERVE_CONFIG_FIELD_SETTERS[key](tx, builder, value);
|
|
203
|
+
}
|
|
204
|
+
// Only safe for a key with a fixed, known type tag; see findBagEntryByTypeSuffix
|
|
205
|
+
// for one that can legitimately be absent.
|
|
206
|
+
async function resolveDynamicFieldId(suiGrpcClient, parentId, nameType) {
|
|
207
|
+
const { dynamicField } = await suiGrpcClient.getDynamicField({
|
|
208
|
+
parentId,
|
|
209
|
+
name: { type: nameType, bcs: new Uint8Array([0]) },
|
|
210
|
+
});
|
|
211
|
+
return dynamicField.fieldId;
|
|
212
|
+
}
|
|
213
|
+
// For a key that's always present (getFeeReceivers), both calls' errors can
|
|
214
|
+
// propagate through this combined helper unguarded.
|
|
215
|
+
async function fetchDynamicFieldJson(suiGrpcClient, parentId, nameType) {
|
|
216
|
+
const fieldId = await resolveDynamicFieldId(suiGrpcClient, parentId, nameType);
|
|
217
|
+
const { object } = await suiGrpcClient.getObject({
|
|
218
|
+
objectId: fieldId,
|
|
219
|
+
include: { json: true },
|
|
220
|
+
});
|
|
221
|
+
return object.json;
|
|
222
|
+
}
|
|
223
|
+
// Scans by type-tag suffix, ignoring the package address: codegen doesn't
|
|
224
|
+
// track a struct's originally-defining address, only the current PUBLISHED_AT.
|
|
225
|
+
async function findBagEntryByTypeSuffix(suiGrpcClient, parentId, typeSuffix) {
|
|
226
|
+
let cursor = null;
|
|
227
|
+
while (true) {
|
|
228
|
+
const response = await suiGrpcClient.listDynamicFields({ parentId, cursor });
|
|
229
|
+
const match = response.dynamicFields.find((entry) => entry.name.type.endsWith(typeSuffix));
|
|
230
|
+
if (match)
|
|
231
|
+
return match;
|
|
232
|
+
if (!response.hasNextPage)
|
|
233
|
+
return undefined;
|
|
234
|
+
if (!response.cursor) {
|
|
235
|
+
throw new Error(`listDynamicFields for ${parentId} reported hasNextPage=true with no cursor to continue`);
|
|
236
|
+
}
|
|
237
|
+
cursor = response.cursor;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
156
240
|
export class SuilendClient {
|
|
157
241
|
lendingMarket;
|
|
158
242
|
suiGrpcClient;
|
|
@@ -249,22 +333,22 @@ export class SuilendClient {
|
|
|
249
333
|
});
|
|
250
334
|
}
|
|
251
335
|
static async getFeeReceivers(suiGrpcClient, lendingMarketId) {
|
|
252
|
-
const
|
|
253
|
-
parentId: lendingMarketId,
|
|
254
|
-
name: {
|
|
255
|
-
type: `${PKG_V10}::lending_market::FeeReceiversKey`,
|
|
256
|
-
bcs: new Uint8Array([0]),
|
|
257
|
-
},
|
|
258
|
-
});
|
|
259
|
-
const { object } = await suiGrpcClient.getObject({
|
|
260
|
-
objectId: dynamicField.fieldId,
|
|
261
|
-
include: { json: true },
|
|
262
|
-
});
|
|
263
|
-
const json = object.json;
|
|
336
|
+
const json = await fetchDynamicFieldJson(suiGrpcClient, lendingMarketId, `${PKG_V10}::lending_market::FeeReceiversKey`);
|
|
264
337
|
const receivers = json?.value?.receivers;
|
|
265
338
|
const weights = json?.value?.weights;
|
|
266
339
|
return { receivers, weights };
|
|
267
340
|
}
|
|
341
|
+
/** A reserve's confidence multiplier `k`, in bps; pass `parsedReserve.config.additionalFields.id`. 0 when unset. */
|
|
342
|
+
static async getReserveConfidenceMultiplierBps(suiGrpcClient, additionalFieldsBagId) {
|
|
343
|
+
const match = await findBagEntryByTypeSuffix(suiGrpcClient, additionalFieldsBagId, "::reserve_config::ConfidenceMultiplierKey");
|
|
344
|
+
if (!match)
|
|
345
|
+
return new BigNumber(0);
|
|
346
|
+
const { dynamicField } = await suiGrpcClient.getDynamicField({
|
|
347
|
+
parentId: additionalFieldsBagId,
|
|
348
|
+
name: match.name,
|
|
349
|
+
});
|
|
350
|
+
return new BigNumber(bcs.u64().parse(dynamicField.value.bcs));
|
|
351
|
+
}
|
|
268
352
|
static createNewLendingMarket(registryId, lendingMarketType, transaction) {
|
|
269
353
|
const [ownerCap, lendingMarket] = createLendingMarket(transaction, lendingMarketType, transaction.object(registryId));
|
|
270
354
|
transaction.moveCall({
|
|
@@ -429,8 +513,7 @@ export class SuilendClient {
|
|
|
429
513
|
const arrayIndex = this.lendingMarket.reserves.findIndex((r) => normalizeStructTag(r.coinType.name) === normalizeStructTag(coinType));
|
|
430
514
|
return BigInt(arrayIndex);
|
|
431
515
|
}
|
|
432
|
-
|
|
433
|
-
const [config] = createReserveConfig(transaction, createReserveConfigArgs);
|
|
516
|
+
submitReserveConfigUpdate(lendingMarketOwnerCapId, transaction, coinType, config) {
|
|
434
517
|
return updateReserveConfig(transaction, [this.lendingMarket.$typeArgs[0], coinType], {
|
|
435
518
|
lendingMarketOwnerCap: transaction.object(lendingMarketOwnerCapId),
|
|
436
519
|
lendingMarket: transaction.object(this.lendingMarket.id),
|
|
@@ -438,6 +521,28 @@ export class SuilendClient {
|
|
|
438
521
|
config: transaction.object(config),
|
|
439
522
|
});
|
|
440
523
|
}
|
|
524
|
+
updateReserveConfig(lendingMarketOwnerCapId, transaction, coinType, createReserveConfigArgs) {
|
|
525
|
+
const [config] = createReserveConfig(transaction, createReserveConfigArgs);
|
|
526
|
+
return this.submitReserveConfigUpdate(lendingMarketOwnerCapId, transaction, coinType, config);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Updates a subset of a reserve's config fields, leaving every unspecified
|
|
530
|
+
* field -- including `additionalFields` entries like the confidence
|
|
531
|
+
* multiplier -- unchanged from the reserve's current on-chain value.
|
|
532
|
+
*/
|
|
533
|
+
patchReserveConfig(lendingMarketOwnerCapId, transaction, coinType, overrides) {
|
|
534
|
+
const [builder] = reserveConfigBuilder(transaction, [this.lendingMarket.$typeArgs[0], coinType], transaction.object(this.lendingMarket.id));
|
|
535
|
+
for (const key of Object.keys(CREATE_RESERVE_CONFIG_FIELD_SETTERS)) {
|
|
536
|
+
applyCreateReserveConfigFieldOverride(transaction, builder, overrides, key);
|
|
537
|
+
}
|
|
538
|
+
if (overrides.confidenceMultiplierBps !== undefined)
|
|
539
|
+
setConfidenceMultiplierBuilder(transaction, {
|
|
540
|
+
builder,
|
|
541
|
+
confidenceMultiplierBps: overrides.confidenceMultiplierBps,
|
|
542
|
+
});
|
|
543
|
+
const [config] = buildReserveConfig(transaction, builder);
|
|
544
|
+
return this.submitReserveConfigUpdate(lendingMarketOwnerCapId, transaction, coinType, config);
|
|
545
|
+
}
|
|
441
546
|
newObligationOwnerCap(transaction, lendingMarketOwnerCapId, destinationAddress, obligationId) {
|
|
442
547
|
const [obligationOwnerCap] = newObligationOwnerCap(transaction, this.lendingMarket.$typeArgs[0], {
|
|
443
548
|
lendingMarketOwnerCap: transaction.object(lendingMarketOwnerCapId),
|
|
@@ -475,7 +580,7 @@ export class SuilendClient {
|
|
|
475
580
|
typeArguments: this.lendingMarket.$typeArgs,
|
|
476
581
|
});
|
|
477
582
|
}
|
|
478
|
-
async refreshAll(transaction, obligation, coinTypes) {
|
|
583
|
+
async refreshAll(transaction, obligation, coinTypes, priceUpdate) {
|
|
479
584
|
const reserveArrayIndexToPriceId = new Map();
|
|
480
585
|
if (obligation) {
|
|
481
586
|
obligation.deposits.forEach((deposit) => {
|
|
@@ -505,8 +610,14 @@ export class SuilendClient {
|
|
|
505
610
|
const priceInfoObjectId = await this.pythClient.getPriceFeedObjectId(priceIdentifiers[i]);
|
|
506
611
|
priceInfoObjectIds[i] = priceInfoObjectId;
|
|
507
612
|
})()));
|
|
508
|
-
const
|
|
509
|
-
|
|
613
|
+
const suppliedPriceIdentifiers = new Set(priceUpdate?.priceIds.map(normalizePriceIdentifier));
|
|
614
|
+
if (priceUpdate?.priceIds.length) {
|
|
615
|
+
await this.pythClient.updatePriceFeeds(transaction, priceUpdate.updateData, priceUpdate.priceIds);
|
|
616
|
+
}
|
|
617
|
+
const stalePriceIdentifiers = new Set();
|
|
618
|
+
await Promise.all(Object.entries(priceInfoObjectIds)
|
|
619
|
+
.filter(([i]) => !suppliedPriceIdentifiers.has(normalizePriceIdentifier(priceIdentifiers[+i])))
|
|
620
|
+
.map(([i, priceInfoObjectId]) => (async () => {
|
|
510
621
|
// Read the field directly: the generated parser's type check is
|
|
511
622
|
// bound to one Pyth package address and would throw post-cutover.
|
|
512
623
|
const { object } = await this.suiGrpcClient.getObject({
|
|
@@ -516,15 +627,16 @@ export class SuilendClient {
|
|
|
516
627
|
const publishTime = priceInfoPublishTime(object.json, priceInfoObjectId);
|
|
517
628
|
const stalenessSeconds = Date.now() / 1000 - Number(publishTime);
|
|
518
629
|
if (needsPriceRefresh(stalenessSeconds)) {
|
|
519
|
-
stalePriceIdentifiers.
|
|
630
|
+
stalePriceIdentifiers.add(priceIdentifiers[+i]);
|
|
520
631
|
}
|
|
521
632
|
})()));
|
|
522
|
-
if (stalePriceIdentifiers.
|
|
523
|
-
const
|
|
524
|
-
await this.
|
|
633
|
+
if (stalePriceIdentifiers.size > 0) {
|
|
634
|
+
const staleIds = [...stalePriceIdentifiers];
|
|
635
|
+
const stalePriceUpdateData = await this.pythConnection.getPriceFeedsUpdateData(staleIds);
|
|
636
|
+
await this.pythClient.updatePriceFeeds(transaction, stalePriceUpdateData, staleIds);
|
|
525
637
|
}
|
|
526
638
|
for (let i = 0; i < reserveArrayIndexes.length; i++) {
|
|
527
|
-
this.refreshReservePrices(transaction, priceInfoObjectIds[i], reserveArrayIndexes[i]);
|
|
639
|
+
await this.refreshReservePrices(transaction, priceInfoObjectIds[i], reserveArrayIndexes[i]);
|
|
528
640
|
}
|
|
529
641
|
}
|
|
530
642
|
async refreshReservePrices(transaction, priceInfoObjectId, reserveArrayIndex) {
|
|
@@ -670,8 +782,8 @@ export class SuilendClient {
|
|
|
670
782
|
transaction.transferObjects([sendCoin], transaction.pure.address(ownerId));
|
|
671
783
|
return result;
|
|
672
784
|
}
|
|
673
|
-
async liquidateAndRedeem(transaction, obligation, repayCoinType, withdrawCoinType, repayCoinId) {
|
|
674
|
-
const [ctokens, exemption] = await this.liquidate(transaction, obligation, repayCoinType, withdrawCoinType, repayCoinId);
|
|
785
|
+
async liquidateAndRedeem(transaction, obligation, repayCoinType, withdrawCoinType, repayCoinId, priceUpdate) {
|
|
786
|
+
const [ctokens, exemption] = await this.liquidate(transaction, obligation, repayCoinType, withdrawCoinType, repayCoinId, priceUpdate);
|
|
675
787
|
const [optionalExemption] = transaction.moveCall({
|
|
676
788
|
target: `0x1::option::some`,
|
|
677
789
|
typeArguments: [
|
|
@@ -681,8 +793,8 @@ export class SuilendClient {
|
|
|
681
793
|
});
|
|
682
794
|
return this.redeem(ctokens, withdrawCoinType, optionalExemption, transaction);
|
|
683
795
|
}
|
|
684
|
-
async liquidate(transaction, obligation, repayCoinType, withdrawCoinType, repayCoinId) {
|
|
685
|
-
await this.refreshAll(transaction, obligation);
|
|
796
|
+
async liquidate(transaction, obligation, repayCoinType, withdrawCoinType, repayCoinId, priceUpdate) {
|
|
797
|
+
await this.refreshAll(transaction, obligation, undefined, priceUpdate);
|
|
686
798
|
return liquidate(transaction, [this.lendingMarket.$typeArgs[0], repayCoinType, withdrawCoinType], {
|
|
687
799
|
lendingMarket: transaction.object(this.lendingMarket.id),
|
|
688
800
|
obligationId: obligation.id,
|
package/lib/initialize.d.ts
CHANGED
|
@@ -167,6 +167,7 @@ export declare const initializeSuilendAll: (suiGrpcClient: SuiGrpcClient, market
|
|
|
167
167
|
isolated: boolean;
|
|
168
168
|
openAttributedBorrowLimitUsd: number;
|
|
169
169
|
closeAttributedBorrowLimitUsd: number;
|
|
170
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
170
171
|
interestRate: {
|
|
171
172
|
id: string;
|
|
172
173
|
utilPercent: BigNumber;
|
|
@@ -306,6 +307,7 @@ export declare const initializeSuilendAll: (suiGrpcClient: SuiGrpcClient, market
|
|
|
306
307
|
isolated: boolean;
|
|
307
308
|
openAttributedBorrowLimitUsd: number;
|
|
308
309
|
closeAttributedBorrowLimitUsd: number;
|
|
310
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
309
311
|
interestRate: {
|
|
310
312
|
id: string;
|
|
311
313
|
utilPercent: BigNumber;
|
|
@@ -423,6 +425,7 @@ export declare const initializeSuilend: (suiGrpcClient: SuiGrpcClient, suilendCl
|
|
|
423
425
|
isolated: boolean;
|
|
424
426
|
openAttributedBorrowLimitUsd: number;
|
|
425
427
|
closeAttributedBorrowLimitUsd: number;
|
|
428
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
426
429
|
interestRate: {
|
|
427
430
|
id: string;
|
|
428
431
|
utilPercent: BigNumber;
|
|
@@ -562,6 +565,7 @@ export declare const initializeSuilend: (suiGrpcClient: SuiGrpcClient, suilendCl
|
|
|
562
565
|
isolated: boolean;
|
|
563
566
|
openAttributedBorrowLimitUsd: number;
|
|
564
567
|
closeAttributedBorrowLimitUsd: number;
|
|
568
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
565
569
|
interestRate: {
|
|
566
570
|
id: string;
|
|
567
571
|
utilPercent: BigNumber;
|
|
@@ -708,6 +712,7 @@ export declare const initializeObligations: (suiGrpcClient: SuiGrpcClient, suile
|
|
|
708
712
|
isolated: boolean;
|
|
709
713
|
openAttributedBorrowLimitUsd: number;
|
|
710
714
|
closeAttributedBorrowLimitUsd: number;
|
|
715
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
711
716
|
interestRate: {
|
|
712
717
|
id: string;
|
|
713
718
|
utilPercent: BigNumber;
|
|
@@ -825,6 +830,7 @@ export declare const initializeObligations: (suiGrpcClient: SuiGrpcClient, suile
|
|
|
825
830
|
isolated: boolean;
|
|
826
831
|
openAttributedBorrowLimitUsd: number;
|
|
827
832
|
closeAttributedBorrowLimitUsd: number;
|
|
833
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
828
834
|
interestRate: {
|
|
829
835
|
id: string;
|
|
830
836
|
utilPercent: BigNumber;
|
|
@@ -966,6 +972,7 @@ export declare const initializeObligations: (suiGrpcClient: SuiGrpcClient, suile
|
|
|
966
972
|
isolated: boolean;
|
|
967
973
|
openAttributedBorrowLimitUsd: number;
|
|
968
974
|
closeAttributedBorrowLimitUsd: number;
|
|
975
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
969
976
|
interestRate: {
|
|
970
977
|
id: string;
|
|
971
978
|
utilPercent: BigNumber;
|
|
@@ -1083,6 +1090,7 @@ export declare const initializeObligations: (suiGrpcClient: SuiGrpcClient, suile
|
|
|
1083
1090
|
isolated: boolean;
|
|
1084
1091
|
openAttributedBorrowLimitUsd: number;
|
|
1085
1092
|
closeAttributedBorrowLimitUsd: number;
|
|
1093
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
1086
1094
|
interestRate: {
|
|
1087
1095
|
id: string;
|
|
1088
1096
|
utilPercent: BigNumber;
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"@suilend/sdk","version":"12.
|
|
1
|
+
{"name":"@suilend/sdk","version":"12.4.0-next.g7a809d2","private":false,"description":"A TypeScript SDK for interacting with the Suilend program","author":"Suilend","license":"MIT","main":"./index.js","exports":{".":"./index.js","./client":"./client.js","./mmt":"./mmt.js","./strategies":"./strategies.js","./api/events":"./api/events.js","./api":"./api/index.js","./lib/constants":"./lib/constants.js","./lib":"./lib/index.js","./lib/initialize":"./lib/initialize.js","./lib/liquidityMining":"./lib/liquidityMining.js","./lib/proCompatible":"./lib/proCompatible.js","./lib/pyth":"./lib/pyth.js","./lib/strategyOwnerCap":"./lib/strategyOwnerCap.js","./lib/transactions":"./lib/transactions.js","./lib/types":"./lib/types.js","./margin":"./margin/index.js","./parsers/apiReserveAssetDataEvent":"./parsers/apiReserveAssetDataEvent.js","./parsers":"./parsers/index.js","./parsers/lendingMarket":"./parsers/lendingMarket.js","./parsers/obligation":"./parsers/obligation.js","./parsers/rateLimiter":"./parsers/rateLimiter.js","./parsers/reserve":"./parsers/reserve.js","./swap":"./swap/index.js","./swap/quote":"./swap/quote.js","./swap/transaction":"./swap/transaction.js","./utils/events":"./utils/events.js","./utils/feedId":"./utils/feedId.js","./utils":"./utils/index.js","./utils/obligation":"./utils/obligation.js","./utils/simulate":"./utils/simulate.js","./_generated/_framework/reified":"./_generated/_framework/reified.js","./_generated/_framework/util":"./_generated/_framework/util.js","./_generated/_framework/vector":"./_generated/_framework/vector.js","./_generated/suilend":"./_generated/suilend/index.js","./margin/margin/admin_cap":"./margin/margin/admin_cap.js","./margin/margin/market":"./margin/margin/market.js","./margin/margin/permissions":"./margin/margin/permissions.js","./margin/margin/position":"./margin/margin/position.js","./margin/margin/router":"./margin/margin/router.js","./margin/margin/version":"./margin/margin/version.js","./margin/utils":"./margin/utils/index.js","./_generated/suilend/cell/structs":"./_generated/suilend/cell/structs.js","./_generated/suilend/decimal/structs":"./_generated/suilend/decimal/structs.js","./_generated/suilend/lending-market/functions":"./_generated/suilend/lending-market/functions.js","./_generated/suilend/lending-market/structs":"./_generated/suilend/lending-market/structs.js","./_generated/suilend/lending-market-registry/functions":"./_generated/suilend/lending-market-registry/functions.js","./_generated/suilend/liquidity-mining/structs":"./_generated/suilend/liquidity-mining/structs.js","./_generated/suilend/obligation/structs":"./_generated/suilend/obligation/structs.js","./_generated/suilend/rate-limiter/functions":"./_generated/suilend/rate-limiter/functions.js","./_generated/suilend/rate-limiter/structs":"./_generated/suilend/rate-limiter/structs.js","./_generated/suilend/reserve/structs":"./_generated/suilend/reserve/structs.js","./_generated/suilend/reserve-config/functions":"./_generated/suilend/reserve-config/functions.js","./_generated/suilend/reserve-config/structs":"./_generated/suilend/reserve-config/structs.js","./_generated/_dependencies/source/0x1":"./_generated/_dependencies/source/0x1/index.js","./_generated/_dependencies/source/0x2":"./_generated/_dependencies/source/0x2/index.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/index.js","./margin/margin/deps/std/type_name":"./margin/margin/deps/std/type_name.js","./margin/margin/deps/sui/vec_set":"./margin/margin/deps/sui/vec_set.js","./margin/margin/deps/suilend/lending_market":"./margin/margin/deps/suilend/lending_market.js","./_generated/_dependencies/source/0x1/ascii/structs":"./_generated/_dependencies/source/0x1/ascii/structs.js","./_generated/_dependencies/source/0x1/option/structs":"./_generated/_dependencies/source/0x1/option/structs.js","./_generated/_dependencies/source/0x1/type-name/structs":"./_generated/_dependencies/source/0x1/type-name/structs.js","./_generated/_dependencies/source/0x2/bag/structs":"./_generated/_dependencies/source/0x2/bag/structs.js","./_generated/_dependencies/source/0x2/balance/structs":"./_generated/_dependencies/source/0x2/balance/structs.js","./_generated/_dependencies/source/0x2/object/structs":"./_generated/_dependencies/source/0x2/object/structs.js","./_generated/_dependencies/source/0x2/object-table/structs":"./_generated/_dependencies/source/0x2/object-table/structs.js","./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs":"./_generated/_dependencies/source/0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e/price-identifier/structs.js"},"types":"./index.d.ts","scripts":{"build":"rm -rf ./dist && tsc && node ./fix-esm-imports.js","typecheck":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","lint:ci":"yarn run typecheck","prettier":"prettier --write src/ tests/","release":"yarn run build && node ./release.js && cd ./dist && npm publish --access public"},"repository":{"type":"git","url":"git+https://github.com/fireflyprotocol/lending-mono.git","directory":"ts/sdks/sdk"},"dependencies":{"@bluefin-exchange/bluefin7k-aggregator-sdk":"7.6.3","@cetusprotocol/aggregator-sdk":"1.7.2","@flowx-finance/sdk":"2.1.0","@pythnetwork/hermes-client":"3.1.0","@pythnetwork/pyth-sui-js":"4.0.0","@suilend/springsui-sdk":"4.1.0","bignumber.js":"11.1.5","bn.js":"5.2.5","crypto-js":"4.2.0","lodash":"4.18.1","p-limit":"7.3.2","uuid":"14.0.2"},"devDependencies":{"@mysten/bcs":"2.1.1","@mysten/sui":"2.29.0","@suilend/sui-core":"2.1.0","@tsconfig/recommended":"1.0.13","@types/bn.js":"5.2.0","@types/lodash":"4.17.25","@types/node":"26.5.1","fast-check":"4.10.0","prettier":"3.9.6","ts-node":"10.9.2","typescript":"6.0.3","vite":"8.3.0","vitest":"5.0.0"},"peerDependencies":{"@mysten/bcs":"^2.1.1","@mysten/sui":">=2.22.1 <3","@suilend/sui-core":"^2.0.1"},"type":"module"}
|
|
@@ -25,6 +25,7 @@ export declare const parseLendingMarket: (lendingMarket: LendingMarket<string>,
|
|
|
25
25
|
isolated: boolean;
|
|
26
26
|
openAttributedBorrowLimitUsd: number;
|
|
27
27
|
closeAttributedBorrowLimitUsd: number;
|
|
28
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
28
29
|
interestRate: {
|
|
29
30
|
id: string;
|
|
30
31
|
utilPercent: BigNumber;
|
package/parsers/obligation.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export declare const parseObligation: (obligation: Obligation<string>, parsedRes
|
|
|
45
45
|
isolated: boolean;
|
|
46
46
|
openAttributedBorrowLimitUsd: number;
|
|
47
47
|
closeAttributedBorrowLimitUsd: number;
|
|
48
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
48
49
|
interestRate: {
|
|
49
50
|
id: string;
|
|
50
51
|
utilPercent: BigNumber;
|
|
@@ -162,6 +163,7 @@ export declare const parseObligation: (obligation: Obligation<string>, parsedRes
|
|
|
162
163
|
isolated: boolean;
|
|
163
164
|
openAttributedBorrowLimitUsd: number;
|
|
164
165
|
closeAttributedBorrowLimitUsd: number;
|
|
166
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
165
167
|
interestRate: {
|
|
166
168
|
id: string;
|
|
167
169
|
utilPercent: BigNumber;
|
package/parsers/reserve.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export declare const parseReserve: (reserve: Reserve<string>, coinMetadataMap: R
|
|
|
25
25
|
isolated: boolean;
|
|
26
26
|
openAttributedBorrowLimitUsd: number;
|
|
27
27
|
closeAttributedBorrowLimitUsd: number;
|
|
28
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
28
29
|
interestRate: {
|
|
29
30
|
id: string;
|
|
30
31
|
utilPercent: BigNumber;
|
|
@@ -147,6 +148,7 @@ export declare const parseReserveConfig: (reserve: Reserve<string>) => {
|
|
|
147
148
|
isolated: boolean;
|
|
148
149
|
openAttributedBorrowLimitUsd: number;
|
|
149
150
|
closeAttributedBorrowLimitUsd: number;
|
|
151
|
+
additionalFields: import("../_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
150
152
|
interestRate: {
|
|
151
153
|
id: string;
|
|
152
154
|
utilPercent: BigNumber;
|
package/parsers/reserve.js
CHANGED
|
@@ -122,7 +122,7 @@ export const parseReserveConfig = (reserve) => {
|
|
|
122
122
|
const isolated = config.isolated;
|
|
123
123
|
const openAttributedBorrowLimitUsd = Number(config.openAttributedBorrowLimitUsd.toString());
|
|
124
124
|
const closeAttributedBorrowLimitUsd = Number(config.closeAttributedBorrowLimitUsd.toString());
|
|
125
|
-
|
|
125
|
+
const additionalFields = config.additionalFields;
|
|
126
126
|
const interestRate = config.interestRateUtils.map((util, index) => ({
|
|
127
127
|
id: uuidv4(),
|
|
128
128
|
utilPercent: new BigNumber(util.toString()),
|
|
@@ -146,10 +146,12 @@ export const parseReserveConfig = (reserve) => {
|
|
|
146
146
|
isolated,
|
|
147
147
|
openAttributedBorrowLimitUsd,
|
|
148
148
|
closeAttributedBorrowLimitUsd,
|
|
149
|
-
|
|
149
|
+
additionalFields,
|
|
150
150
|
interestRate,
|
|
151
151
|
};
|
|
152
152
|
};
|
|
153
|
+
// additionalFields is a Bag; read the confidence multiplier via
|
|
154
|
+
// SuilendClient.getReserveConfidenceMultiplierBps(suiGrpcClient, additionalFields.id).
|
|
153
155
|
export const parsePoolRewardManager = (poolRewardManager, coinMetadataMap) => {
|
|
154
156
|
const $typeName = poolRewardManager.$typeName;
|
|
155
157
|
const id = poolRewardManager.id;
|
package/strategies.d.ts
CHANGED
|
@@ -73,6 +73,7 @@ export declare const getStrategySuiReserve: (reserveMap: Record<string, ParsedRe
|
|
|
73
73
|
isolated: boolean;
|
|
74
74
|
openAttributedBorrowLimitUsd: number;
|
|
75
75
|
closeAttributedBorrowLimitUsd: number;
|
|
76
|
+
additionalFields: import("./_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
76
77
|
interestRate: {
|
|
77
78
|
id: string;
|
|
78
79
|
utilPercent: BigNumber;
|
|
@@ -202,6 +203,7 @@ export declare const getStrategyDefaultCurrencyReserve: (reserveMap: Record<stri
|
|
|
202
203
|
isolated: boolean;
|
|
203
204
|
openAttributedBorrowLimitUsd: number;
|
|
204
205
|
closeAttributedBorrowLimitUsd: number;
|
|
206
|
+
additionalFields: import("./_generated/_dependencies/source/0x2/bag/structs").Bag;
|
|
205
207
|
interestRate: {
|
|
206
208
|
id: string;
|
|
207
209
|
utilPercent: BigNumber;
|
|
@@ -297,7 +299,8 @@ export declare const getStrategyBorrowedAmount: (reserveMap: Record<string, Pars
|
|
|
297
299
|
export declare const getStrategyTvlAmount: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, obligation?: ParsedObligation) => BigNumber;
|
|
298
300
|
export declare const getStrategyExposure: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, obligation?: ParsedObligation) => BigNumber;
|
|
299
301
|
export declare const getStrategyStepMaxBorrowedAmount: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, deposits: StrategyDeposit[], borrowedAmount: BigNumber) => BigNumber;
|
|
300
|
-
export declare const getStrategyStepMaxWithdrawnAmount: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, deposits: StrategyDeposit[], borrowedAmount: BigNumber, withdrawCoinType: string) => BigNumber;
|
|
302
|
+
export declare const getStrategyStepMaxWithdrawnAmount: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, deposits: StrategyDeposit[], borrowedAmount: BigNumber, withdrawCoinType: string, headroomBuffer?: number) => BigNumber;
|
|
303
|
+
export declare const getStrategyRebalanceDepositAmount: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, deposits: StrategyDeposit[], borrowedAmount: BigNumber) => BigNumber;
|
|
301
304
|
export declare const strategySimulateLoopToExposure: (reserveMap: Record<string, ParsedReserve>, lstMap: StrategyLstMap, strategyType: StrategyType, _deposits: StrategyDeposit[], _borrowedAmount: BigNumber, _targetBorrowedAmount: BigNumber | undefined, _targetExposure: BigNumber | undefined) => {
|
|
302
305
|
deposits: StrategyDeposit[];
|
|
303
306
|
borrowedAmount: BigNumber;
|
package/strategies.js
CHANGED
|
@@ -10,7 +10,7 @@ import { getNetAprPercent } from "./lib/index.js";
|
|
|
10
10
|
import { getRewardsMap } from "./lib/liquidityMining.js";
|
|
11
11
|
import { STRATEGY_TYPE_INFO_MAP, StrategyType, strategyBorrow, strategyClaimRewardsAndMergeCoins, strategyDeposit, strategyWithdraw, } from "./lib/strategyOwnerCap.js";
|
|
12
12
|
import { MMT_CONTRACT_PACKAGE_ID, MMT_VERSION_OBJECT_ID } from "./mmt.js";
|
|
13
|
-
import { getWeightedBorrowsUsd } from "./utils/index.js";
|
|
13
|
+
import { bisectionMethod, getWeightedBorrowsUsd } from "./utils/index.js";
|
|
14
14
|
export const STRATEGY_E = 10 ** -7;
|
|
15
15
|
export const STRATEGY_FLASH_REPAY_MAX_RESIDUAL_USD = 0.1;
|
|
16
16
|
/**
|
|
@@ -591,7 +591,7 @@ export const getStrategyStepMaxWithdrawnAmount = (
|
|
|
591
591
|
// AppContext
|
|
592
592
|
reserveMap,
|
|
593
593
|
// Strategy
|
|
594
|
-
lstMap, strategyType, deposits, borrowedAmount, withdrawCoinType) => {
|
|
594
|
+
lstMap, strategyType, deposits, borrowedAmount, withdrawCoinType, headroomBuffer = 1) => {
|
|
595
595
|
const depositReserves = getStrategyDepositReserves(reserveMap, strategyType);
|
|
596
596
|
const borrowReserve = getStrategyBorrowReserve(reserveMap, strategyType);
|
|
597
597
|
const defaultCurrencyReserve = getStrategyDefaultCurrencyReserve(reserveMap, strategyType);
|
|
@@ -608,10 +608,33 @@ lstMap, strategyType, deposits, borrowedAmount, withdrawCoinType) => {
|
|
|
608
608
|
.minus(obligation.maxPriceWeightedBorrowsUsd)
|
|
609
609
|
.div(withdrawReserve.minPrice)
|
|
610
610
|
.div(withdrawReserve.config.openLtvPct / 100)
|
|
611
|
+
.decimalPlaces(withdrawReserve.token.decimals, BigNumber.ROUND_DOWN)
|
|
612
|
+
.times(headroomBuffer)
|
|
611
613
|
: MAX_U64, // Infinity
|
|
612
614
|
deposits.find((deposit) => deposit.coinType === withdrawReserve.coinType)
|
|
613
615
|
?.depositedAmount ?? new BigNumber(0)).decimalPlaces(withdrawReserve.token.decimals, BigNumber.ROUND_DOWN);
|
|
614
616
|
};
|
|
617
|
+
export const getStrategyRebalanceDepositAmount = (
|
|
618
|
+
// AppContext
|
|
619
|
+
reserveMap,
|
|
620
|
+
// Strategy
|
|
621
|
+
lstMap, strategyType, deposits, borrowedAmount) => {
|
|
622
|
+
const depositReserves = getStrategyDepositReserves(reserveMap, strategyType);
|
|
623
|
+
const depositReserve = (depositReserves.base ?? depositReserves.lst); // Must have LST if no base
|
|
624
|
+
const depositedAmount = deposits.find((d) => d.coinType === depositReserve.coinType)
|
|
625
|
+
?.depositedAmount ?? new BigNumber(0);
|
|
626
|
+
if (depositedAmount.lte(0))
|
|
627
|
+
return new BigNumber(0);
|
|
628
|
+
const simulateWith = (newDepositedAmount) => getStrategySimulatedObligation(reserveMap, lstMap, strategyType, addOrInsertStrategyDeposit(deposits, {
|
|
629
|
+
coinType: depositReserve.coinType,
|
|
630
|
+
depositedAmount: newDepositedAmount.minus(depositedAmount),
|
|
631
|
+
}), borrowedAmount);
|
|
632
|
+
const withinMaxExposure = bisectionMethod(depositedAmount, depositedAmount.times(2), (n) => getStrategyExposure(reserveMap, lstMap, strategyType, simulateWith(n)).lte(STRATEGY_TYPE_EXPOSURE_MAP[strategyType].max));
|
|
633
|
+
const fullHealth = bisectionMethod(depositedAmount, depositedAmount.times(2), (n) => getStrategyHealthPercent(reserveMap, lstMap, strategyType, simulateWith(n)).eq(100));
|
|
634
|
+
return BigNumber.max(withinMaxExposure, fullHealth)
|
|
635
|
+
.minus(depositedAmount)
|
|
636
|
+
.decimalPlaces(depositReserve.token.decimals, BigNumber.ROUND_UP);
|
|
637
|
+
};
|
|
615
638
|
// Simulate
|
|
616
639
|
export const strategySimulateLoopToExposure = (
|
|
617
640
|
// AppContext
|
|
@@ -1236,7 +1259,7 @@ transaction, dryRunTransaction) => {
|
|
|
1236
1259
|
_targetExposure: _targetExposure?.toFixed(20),
|
|
1237
1260
|
}, null, 2));
|
|
1238
1261
|
const depositReserve = (depositReserves.base ?? depositReserves.lst); // Must have LST if no base
|
|
1239
|
-
|
|
1262
|
+
let loopingDepositReserve = (depositReserves.lst ?? depositReserves.base); // Must have base if no LST
|
|
1240
1263
|
//
|
|
1241
1264
|
let deposits = cloneDeep(_deposits);
|
|
1242
1265
|
let borrowedAmount = _borrowedAmount;
|
|
@@ -1426,7 +1449,9 @@ transaction, dryRunTransaction) => {
|
|
|
1426
1449
|
fullRepaymentAmount: fullRepaymentAmount.toFixed(20),
|
|
1427
1450
|
}));
|
|
1428
1451
|
// 1) MAX withdraw LST
|
|
1429
|
-
if (depositReserves.lst !== undefined
|
|
1452
|
+
if (depositReserves.lst !== undefined &&
|
|
1453
|
+
(deposits.find((d) => d.coinType === depositReserves.lst.coinType)
|
|
1454
|
+
?.depositedAmount ?? new BigNumber(0)).gt(0)) {
|
|
1430
1455
|
// 1.1) MAX withdraw
|
|
1431
1456
|
const [withdrawnMaxLstCoin] = strategyWithdraw(strategyType, depositReserves.lst.coinType, strategyOwnerCapId, suilendClient.findReserveArrayIndex(depositReserves.lst.coinType), BigInt(MAX_U64.toString()), transaction);
|
|
1432
1457
|
// 1.2) Update state
|
|
@@ -1585,9 +1610,10 @@ transaction, dryRunTransaction) => {
|
|
|
1585
1610
|
if (pendingBorrowedAmount.lt(0))
|
|
1586
1611
|
break; // Fully repaid already
|
|
1587
1612
|
if (depositReserve.coinType === depositReserves.base?.coinType) {
|
|
1588
|
-
const
|
|
1613
|
+
const lstDepositedAmount = deposits.find((d) => d.coinType === depositReserves.lst.coinType)
|
|
1614
|
+
?.depositedAmount ?? new BigNumber(0);
|
|
1589
1615
|
// Ran out of LST
|
|
1590
|
-
if (
|
|
1616
|
+
if (lstDepositedAmount.lte(STRATEGY_E)) {
|
|
1591
1617
|
// 1. MAX withdraws LST (transferred to user as SUI)
|
|
1592
1618
|
// 2. Withdraws base to cover borrows
|
|
1593
1619
|
// - Leftover transferred to user as borrow coinType, e.g. SUI or suiWBTC
|
|
@@ -1643,12 +1669,17 @@ transaction, dryRunTransaction) => {
|
|
|
1643
1669
|
else {
|
|
1644
1670
|
if (pendingBorrowedAmount.lte(STRATEGY_E))
|
|
1645
1671
|
break;
|
|
1672
|
+
// The rest of the debt is backed by base, so keep unwinding from it.
|
|
1673
|
+
if (depositReserves.base !== undefined &&
|
|
1674
|
+
(deposits.find((d) => d.coinType === depositReserves.lst.coinType)
|
|
1675
|
+
?.depositedAmount ?? new BigNumber(0)).lte(STRATEGY_E)) {
|
|
1676
|
+
loopingDepositReserve = depositReserves.base;
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1646
1679
|
}
|
|
1647
1680
|
// 1) Withdraw LST
|
|
1648
1681
|
// 1.1) Max
|
|
1649
|
-
const stepMaxWithdrawnAmount = getStrategyStepMaxWithdrawnAmount(reserveMap, lstMap, strategyType, deposits, borrowedAmount, loopingDepositReserve.coinType)
|
|
1650
|
-
.times(0.9) // 10% buffer
|
|
1651
|
-
.decimalPlaces(loopingDepositReserve.token.decimals, BigNumber.ROUND_DOWN);
|
|
1682
|
+
const stepMaxWithdrawnAmount = getStrategyStepMaxWithdrawnAmount(reserveMap, lstMap, strategyType, deposits, borrowedAmount, loopingDepositReserve.coinType, 0.9);
|
|
1652
1683
|
const stepMaxRepaidAmount = new BigNumber(new BigNumber(stepMaxWithdrawnAmount.times(lst?.lstToSuiExchangeRate ?? 1)).minus(getStrategyLstRedeemFee(lstMap, loopingDepositReserve.coinType, stepMaxWithdrawnAmount))).decimalPlaces(borrowReserve.token.decimals, BigNumber.ROUND_DOWN);
|
|
1653
1684
|
console.log(`[unloopStrategyToExposure] ${i} withdraw_lst.max |`, JSON.stringify({
|
|
1654
1685
|
stepMaxWithdrawnAmount: stepMaxWithdrawnAmount.toFixed(20),
|
|
@@ -1785,9 +1816,7 @@ transaction, dryRunTransaction) => {
|
|
|
1785
1816
|
}
|
|
1786
1817
|
// 1) Withdraw base
|
|
1787
1818
|
// 1.1) Max
|
|
1788
|
-
const stepMaxWithdrawnAmount = getStrategyStepMaxWithdrawnAmount(reserveMap, lstMap, strategyType, deposits, borrowedAmount, loopingDepositReserve.coinType)
|
|
1789
|
-
.times(0.9) // 10% buffer
|
|
1790
|
-
.decimalPlaces(loopingDepositReserve.token.decimals, BigNumber.ROUND_DOWN);
|
|
1819
|
+
const stepMaxWithdrawnAmount = getStrategyStepMaxWithdrawnAmount(reserveMap, lstMap, strategyType, deposits, borrowedAmount, loopingDepositReserve.coinType, 0.9);
|
|
1791
1820
|
const stepMaxRepaidAmount = new BigNumber(stepMaxWithdrawnAmount.times(baseToBorrowExchangeRate)).decimalPlaces(borrowReserve.token.decimals, BigNumber.ROUND_DOWN);
|
|
1792
1821
|
console.log(`[unloopStrategyToExposure] ${i} withdraw_base.max |`, JSON.stringify({
|
|
1793
1822
|
stepMaxWithdrawnAmount: stepMaxWithdrawnAmount.toFixed(20),
|
|
@@ -2351,6 +2380,17 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2351
2380
|
let borrowedAmount = _borrowedAmount;
|
|
2352
2381
|
// 1) Unloop to 1x (base+LST: no LST and no borrows, LST: no borrows)
|
|
2353
2382
|
if (borrowedAmount.gt(0)) {
|
|
2383
|
+
// 1.0) Above the borrow limit no stepwise withdraw can land, so rebalance first
|
|
2384
|
+
if (!isStrategyWithinBorrowLimit(reserveMap, lstMap, strategyType, deposits, borrowedAmount)) {
|
|
2385
|
+
try {
|
|
2386
|
+
({ deposits, borrowedAmount, transaction } =
|
|
2387
|
+
await strategyRebalanceWithinBorrowLimitTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, Transaction.from(transaction), dryRunTransaction));
|
|
2388
|
+
}
|
|
2389
|
+
catch (err) {
|
|
2390
|
+
console.error(err);
|
|
2391
|
+
throw new Error("Failed to repay borrows before withdrawing");
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2354
2394
|
// 1.1) Unloop
|
|
2355
2395
|
const { deposits: newDeposits, borrowedAmount: newBorrowedAmount, transaction: newTransaction, } = await strategyUnloopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
|
|
2356
2396
|
new BigNumber(1), // Pass targetExposure
|
|
@@ -2373,6 +2413,30 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2373
2413
|
// rather than build a transaction that is certain to abort on the health check —
|
|
2374
2414
|
// the caller can surface this instead of asking the user to sign it.
|
|
2375
2415
|
let flashLoan;
|
|
2416
|
+
let repaidByAdjustRepay = false;
|
|
2417
|
+
if (borrowedAmount.gt(STRATEGY_E) &&
|
|
2418
|
+
depositReserves.base !== undefined &&
|
|
2419
|
+
depositReserves.lst !== undefined) {
|
|
2420
|
+
const baseFlashLoanAmount = borrowedAmount
|
|
2421
|
+
.times(1.01)
|
|
2422
|
+
.decimalPlaces(borrowReserve.token.decimals, BigNumber.ROUND_UP)
|
|
2423
|
+
.times(borrowReserve.price)
|
|
2424
|
+
.div(depositReserves.base.price)
|
|
2425
|
+
.times(1.03)
|
|
2426
|
+
.decimalPlaces(depositReserves.base.token.decimals, BigNumber.ROUND_UP);
|
|
2427
|
+
console.log(`[strategyMaxWithdraw] adjust_repay |`, JSON.stringify({
|
|
2428
|
+
borrowedAmount: borrowedAmount.toFixed(20),
|
|
2429
|
+
baseFlashLoanAmount: baseFlashLoanAmount.toFixed(20),
|
|
2430
|
+
}));
|
|
2431
|
+
try {
|
|
2432
|
+
({ deposits, borrowedAmount, transaction } = await strategyAdjustRepayTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, baseFlashLoanAmount, Transaction.from(transaction), dryRunTransaction));
|
|
2433
|
+
}
|
|
2434
|
+
catch (err) {
|
|
2435
|
+
console.error(err);
|
|
2436
|
+
throw new Error("Failed to repay borrows before withdrawing");
|
|
2437
|
+
}
|
|
2438
|
+
repaidByAdjustRepay = true;
|
|
2439
|
+
}
|
|
2376
2440
|
if (borrowedAmount.gt(STRATEGY_E)) {
|
|
2377
2441
|
const flashLoanObj = STRATEGY_TYPE_FLASH_LOAN_OBJ_MAP[strategyType];
|
|
2378
2442
|
if (flashLoanObj.provider !== StrategyFlashLoanProvider.MMT ||
|
|
@@ -2525,7 +2589,7 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2525
2589
|
transaction = transactionBeforeClaim;
|
|
2526
2590
|
}
|
|
2527
2591
|
}
|
|
2528
|
-
if (flashLoan !== undefined || borrowedAmount.gt(0)) {
|
|
2592
|
+
if (flashLoan !== undefined || repaidByAdjustRepay || borrowedAmount.gt(0)) {
|
|
2529
2593
|
try {
|
|
2530
2594
|
await dryRunTransaction(transaction);
|
|
2531
2595
|
}
|
|
@@ -2971,6 +3035,8 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2971
3035
|
}
|
|
2972
3036
|
return { deposits, borrowedAmount, transaction };
|
|
2973
3037
|
};
|
|
3038
|
+
const isStrategyWithinBorrowLimit = (reserveMap, lstMap, strategyType, deposits, borrowedAmount) => getStrategyHealthPercent(reserveMap, lstMap, strategyType, getStrategySimulatedObligation(reserveMap, lstMap, strategyType, deposits, borrowedAmount)).eq(100);
|
|
3039
|
+
const strategyRebalanceWithinBorrowLimitTx = (reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, transaction, dryRunTransaction) => strategyDepositAdjustWithdrawTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligationId, deposits, borrowedAmount, getStrategyRebalanceDepositAmount(reserveMap, lstMap, strategyType, deposits, borrowedAmount), transaction, dryRunTransaction);
|
|
2974
3040
|
export const strategyAdjustTx = async (
|
|
2975
3041
|
// AppContext
|
|
2976
3042
|
reserveMap,
|
|
@@ -2983,7 +3049,7 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2983
3049
|
const depositReserves = getStrategyDepositReserves(reserveMap, strategyType);
|
|
2984
3050
|
const borrowReserve = getStrategyBorrowReserve(reserveMap, strategyType);
|
|
2985
3051
|
const defaultCurrencyReserve = getStrategyDefaultCurrencyReserve(reserveMap, strategyType);
|
|
2986
|
-
|
|
3052
|
+
let exposure = getStrategyExposure(reserveMap, lstMap, strategyType, obligation);
|
|
2987
3053
|
console.log(`[strategyAdjust] args |`, JSON.stringify({
|
|
2988
3054
|
_address,
|
|
2989
3055
|
strategyOwnerCapId,
|
|
@@ -2996,17 +3062,31 @@ lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _a
|
|
|
2996
3062
|
targetExposure: targetExposure.toFixed(20),
|
|
2997
3063
|
}, null, 2));
|
|
2998
3064
|
//
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
// 1)
|
|
3065
|
+
let deposits = cloneDeep(_deposits);
|
|
3066
|
+
let borrowedAmount = _borrowedAmount;
|
|
3067
|
+
// 1) Above the borrow limit no stepwise withdraw can land, so rebalance first
|
|
3068
|
+
const rebalanced = borrowedAmount.gt(0) && !isStrategyWithinBorrowLimit(reserveMap, lstMap, strategyType, deposits, borrowedAmount);
|
|
3069
|
+
if (rebalanced) {
|
|
3070
|
+
({ deposits, borrowedAmount, transaction } =
|
|
3071
|
+
await strategyRebalanceWithinBorrowLimitTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, transaction, dryRunTransaction));
|
|
3072
|
+
exposure = getStrategyExposure(reserveMap, lstMap, strategyType, getStrategySimulatedObligation(reserveMap, lstMap, strategyType, deposits, borrowedAmount));
|
|
3073
|
+
}
|
|
3074
|
+
// 2) Loop or unloop to target exposure
|
|
3075
|
+
let result;
|
|
3002
3076
|
if (targetExposure.gt(exposure))
|
|
3003
|
-
|
|
3077
|
+
result = await strategyLoopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
|
|
3004
3078
|
targetExposure, // Pass targetExposure
|
|
3005
3079
|
transaction);
|
|
3006
3080
|
else if (targetExposure.lt(exposure))
|
|
3007
|
-
|
|
3081
|
+
result = await strategyUnloopToExposureTx(reserveMap, lstMap, strategyType, suiGrpcClient, suilendClient, cetusSdk, cetusPartnerId, _address, strategyOwnerCapId, obligation.id, deposits, borrowedAmount, undefined, // Don't pass targetBorrowedAmount
|
|
3008
3082
|
targetExposure, // Pass targetExposure
|
|
3009
3083
|
transaction, dryRunTransaction);
|
|
3010
3084
|
else
|
|
3011
3085
|
return { deposits, borrowedAmount, transaction };
|
|
3086
|
+
if (result.borrowedAmount.eq(borrowedAmount)) {
|
|
3087
|
+
if (rebalanced)
|
|
3088
|
+
return { deposits, borrowedAmount, transaction };
|
|
3089
|
+
throw new Error("Failed to adjust leverage");
|
|
3090
|
+
}
|
|
3091
|
+
return result;
|
|
3012
3092
|
};
|