@strkfarm/sdk 2.0.0-dev.35 → 2.0.0-dev.37
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/dist/cli.js +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/index.browser.global.js +19596 -28786
- package/dist/index.browser.mjs +8559 -17931
- package/dist/index.d.ts +578 -2746
- package/dist/index.js +8649 -18059
- package/dist/index.mjs +8577 -17949
- package/package.json +3 -3
- package/src/data/universal-vault.abi.json +8 -7
- package/src/dataTypes/bignumber.browser.ts +5 -1
- package/src/dataTypes/bignumber.node.ts +5 -0
- package/src/global.ts +21 -1
- package/src/interfaces/common.tsx +39 -4
- package/src/modules/avnu.ts +19 -10
- package/src/modules/index.ts +1 -1
- package/src/strategies/base-strategy.ts +92 -8
- package/src/strategies/constants.ts +8 -3
- package/src/strategies/ekubo-cl-vault.tsx +150 -16
- package/src/strategies/factory.ts +21 -1
- package/src/strategies/index.ts +2 -7
- package/src/strategies/registry.ts +28 -5
- package/src/strategies/sensei.ts +29 -13
- package/src/strategies/svk-strategy.ts +26 -2
- package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1223 -0
- package/src/strategies/universal-adapters/avnu-adapter.ts +16 -8
- package/src/strategies/universal-adapters/index.ts +1 -2
- package/src/strategies/universal-adapters/svk-troves-adapter.ts +19 -6
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +22 -3
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +75 -52
- package/src/strategies/universal-adapters/vesu-position-common.ts +38 -31
- package/src/strategies/universal-lst-muliplier-strategy.tsx +222 -269
- package/src/strategies/universal-strategy.tsx +166 -105
- package/src/strategies/vesu-rebalance.tsx +3 -6
- package/src/strategies/yoloVault.ts +1084 -0
- package/src/utils/health-factor-math.ts +29 -0
- package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
- package/src/modules/ExtendedWrapperSDk/types.ts +0 -334
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -611
- package/src/strategies/universal-adapters/extended-adapter.ts +0 -860
- package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +0 -200
- package/src/strategies/usdc-boosted-strategy.tsx +0 -693
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +0 -2234
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +0 -4254
- package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +0 -783
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -56
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +0 -88
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -78
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -48
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -528
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1014
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
} from "./baseAdapter";
|
|
8
8
|
import { toBigInt } from "./adapter-utils";
|
|
9
9
|
import { Protocols } from "@/interfaces";
|
|
10
|
-
import { MAX_DELAY } from "../vesu-extended-strategy/utils/constants";
|
|
11
10
|
import { SupportedPosition } from "./baseAdapter";
|
|
12
11
|
import { PositionAPY, APYType, PositionAmount } from "./baseAdapter";
|
|
13
12
|
import { Web3Number } from "@/dataTypes";
|
|
@@ -15,17 +14,25 @@ import { PositionInfo } from "./baseAdapter";
|
|
|
15
14
|
import { ManageCall } from "./baseAdapter";
|
|
16
15
|
import { ContractAddr } from "@/dataTypes";
|
|
17
16
|
import { AVNU_EXCHANGE } from "./adapter-utils";
|
|
18
|
-
import { MAX_RETRIES } from "../vesu-extended-strategy/utils/constants";
|
|
19
17
|
import { Quote } from "@avnu/avnu-sdk";
|
|
20
18
|
import { hash, uint256 } from "starknet";
|
|
21
19
|
import { AvnuWrapper } from "@/modules/avnu";
|
|
22
20
|
import axios from "axios";
|
|
23
21
|
import { SIMPLE_SANITIZER } from "./adapter-utils";
|
|
24
|
-
import { returnFormattedAmount } from "../vesu-extended-strategy/utils/helper";
|
|
25
22
|
import { assert, logger } from "@/utils";
|
|
26
23
|
import { Global } from "@/global";
|
|
27
24
|
import { TokenInfo } from "@/interfaces";
|
|
28
25
|
import { ERC20 } from "@/modules";
|
|
26
|
+
import { MAX_AVNU_RETRY_DELAY } from "../constants";
|
|
27
|
+
|
|
28
|
+
export interface AvnuDepositParams extends DepositParams {
|
|
29
|
+
minAmount?: Web3Number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AvnuWithdrawParams extends WithdrawParams {
|
|
33
|
+
minAmount?: Web3Number;
|
|
34
|
+
}
|
|
35
|
+
|
|
29
36
|
export interface AvnuAdapterConfig extends BaseAdapterConfig {
|
|
30
37
|
baseUrl: string;
|
|
31
38
|
avnuContract: ContractAddr; //0x04270219d365d6b017231b52e92b3fb5d7c8378b05e9abc97724537a80e93b0f
|
|
@@ -34,7 +41,7 @@ export interface AvnuAdapterConfig extends BaseAdapterConfig {
|
|
|
34
41
|
maximumExtendedPriceDifferenceForSwapClosing: number;
|
|
35
42
|
}
|
|
36
43
|
|
|
37
|
-
export class AvnuAdapter extends BaseAdapter<
|
|
44
|
+
export class AvnuAdapter extends BaseAdapter<AvnuDepositParams, AvnuWithdrawParams> {
|
|
38
45
|
readonly config: AvnuAdapterConfig;
|
|
39
46
|
protected avnuWrapper: AvnuWrapper;
|
|
40
47
|
lastSwapPriceInfo: SwapPriceInfo | null = null;
|
|
@@ -91,7 +98,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
private async buildSwapCalls(
|
|
94
|
-
params:
|
|
101
|
+
params: AvnuDepositParams | AvnuWithdrawParams,
|
|
95
102
|
fromToken: TokenInfo,
|
|
96
103
|
toToken: TokenInfo,
|
|
97
104
|
// NOTE : this is a direction flag that we are swapping in this direction
|
|
@@ -129,6 +136,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
129
136
|
const getCalldata = await this.avnuWrapper.getSwapCallData(
|
|
130
137
|
quote,
|
|
131
138
|
vaultAllocator.address,
|
|
139
|
+
params.minAmount,
|
|
132
140
|
);
|
|
133
141
|
const swapCallData = getCalldata[0];
|
|
134
142
|
const approveAmount = uint256.bnToUint256(params.amount.toWei());
|
|
@@ -260,7 +268,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
260
268
|
return [];
|
|
261
269
|
}
|
|
262
270
|
|
|
263
|
-
async getDepositCall(params:
|
|
271
|
+
async getDepositCall(params: AvnuDepositParams): Promise<ManageCall[]> {
|
|
264
272
|
try {
|
|
265
273
|
const fromToken = this.config.supportedPositions[0].asset; //usdc
|
|
266
274
|
const toToken = this.config.supportedPositions[1].asset; //wbtc
|
|
@@ -278,7 +286,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
278
286
|
}
|
|
279
287
|
|
|
280
288
|
//Swap wbtc to usdc
|
|
281
|
-
async getWithdrawCall(params:
|
|
289
|
+
async getWithdrawCall(params: AvnuWithdrawParams): Promise<ManageCall[]> {
|
|
282
290
|
try {
|
|
283
291
|
const toToken = this.config.supportedPositions[0].asset;
|
|
284
292
|
const fromToken = this.config.supportedPositions[1].asset;
|
|
@@ -322,7 +330,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
322
330
|
if (attempt === retries - 1) {
|
|
323
331
|
throw err;
|
|
324
332
|
}
|
|
325
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
333
|
+
await new Promise((resolve) => setTimeout(resolve, MAX_AVNU_RETRY_DELAY));
|
|
326
334
|
}
|
|
327
335
|
}
|
|
328
336
|
throw new Error("Failed to fetch quote after retries");
|
|
@@ -4,8 +4,7 @@ export * from "./vesu-adapter";
|
|
|
4
4
|
export * from "./vesu-supply-only-adapter";
|
|
5
5
|
export * from "./vesu-multiply-adapter";
|
|
6
6
|
export * from "./vesu-modify-position-adapter";
|
|
7
|
-
export * from "./extended-adapter";
|
|
8
7
|
export * from "./adapter-utils";
|
|
9
8
|
export * from "./token-transfer-adapter";
|
|
10
9
|
export * from "./avnu-adapter";
|
|
11
|
-
export * from "./svk-troves-adapter";
|
|
10
|
+
export * from "./svk-troves-adapter";
|
|
@@ -41,6 +41,11 @@ export interface SvkTrovesAdapterConfig extends BaseAdapterConfig {
|
|
|
41
41
|
positionOwner?: ContractAddr;
|
|
42
42
|
/** Optional APY endpoint (defaults to {@link DEFAULT_TROVES_STRATEGIES_API}). */
|
|
43
43
|
trovesStrategiesApiUrl?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Optional redeem request NFT contract address. When provided, pending assets are calculated
|
|
46
|
+
* using `getPendingAssetsFromOwnerNFTMethod` instead of `due_assets_from_owner`.
|
|
47
|
+
*/
|
|
48
|
+
redeemRequestNFT?: ContractAddr;
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
type TrovesStrategiesApiPayload = {
|
|
@@ -165,13 +170,21 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
|
|
|
165
170
|
const liquid = Web3Number.fromWei(liquidAssetsRaw.toString(), decimals);
|
|
166
171
|
|
|
167
172
|
let pending = Web3Number.fromWei("0", decimals);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
`${SvkTrovesAdapter.name}::getPosition: due_assets_from_owner failed (treating as 0): ${(e as Error).message}`,
|
|
173
|
+
if (this.config.redeemRequestNFT) {
|
|
174
|
+
pending = await this.getPendingAssetsFromOwnerNFTMethod(
|
|
175
|
+
this.config.redeemRequestNFT,
|
|
176
|
+
owner,
|
|
177
|
+
decimals,
|
|
174
178
|
);
|
|
179
|
+
} else {
|
|
180
|
+
try {
|
|
181
|
+
const dueRaw: any = await vault.call("due_assets_from_owner", [owner.address]);
|
|
182
|
+
pending = Web3Number.fromWei(dueRaw.toString(), decimals);
|
|
183
|
+
} catch (e) {
|
|
184
|
+
logger.warn(
|
|
185
|
+
`${SvkTrovesAdapter.name}::getPosition: due_assets_from_owner failed (treating as 0): ${(e as Error).message}`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
175
188
|
}
|
|
176
189
|
|
|
177
190
|
const total = liquid.plus(pending);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
2
|
import { IProtocol, Protocols, TokenInfo } from "@/interfaces";
|
|
3
3
|
import {
|
|
4
|
+
APYType,
|
|
4
5
|
BaseAdapter,
|
|
5
6
|
BaseAdapterConfig,
|
|
6
7
|
ManageCall,
|
|
@@ -86,7 +87,10 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
86
87
|
|
|
87
88
|
private _getTargetHealthFactor(): number {
|
|
88
89
|
if (this.config.targetLtv <= 0) {
|
|
89
|
-
|
|
90
|
+
throw new Error(`Target LTV is less than or equal to 0: ${this.config.targetLtv}`);
|
|
91
|
+
}
|
|
92
|
+
if (this.config.targetLtv >= this._getEffectiveMaxLtv(this.config.maxLtv)) {
|
|
93
|
+
throw new Error(`Target LTV is greater than or equal to max LTV: ${this.config.targetLtv} >= ${this._getEffectiveMaxLtv(this.config.maxLtv)}`);
|
|
90
94
|
}
|
|
91
95
|
return this.config.maxLtv / this.config.targetLtv;
|
|
92
96
|
}
|
|
@@ -330,11 +334,26 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
330
334
|
}
|
|
331
335
|
|
|
332
336
|
async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
|
|
333
|
-
|
|
337
|
+
const { collateralPosition, debtPosition: debtPosition } = await getVesuCommonMaxDeposit(this._getPositionCommonContext(), amount);
|
|
338
|
+
// since this is not multiply adapter, max allow collateral is directly max allowed deposit
|
|
339
|
+
const netAPY =
|
|
340
|
+
(collateralPosition.apy.apy * collateralPosition.usdValue - debtPosition.apy.apy * debtPosition.usdValue) /
|
|
341
|
+
(collateralPosition.usdValue - debtPosition.usdValue);
|
|
342
|
+
return {
|
|
343
|
+
...collateralPosition,
|
|
344
|
+
apy: { apy: netAPY, type: APYType.BASE },
|
|
345
|
+
};
|
|
334
346
|
}
|
|
335
347
|
|
|
336
348
|
async maxWithdraw(): Promise<PositionInfo> {
|
|
337
|
-
|
|
349
|
+
const { collateralPosition, debtPosition } = await getVesuCommonMaxWithdraw(this._getPositionCommonContext());
|
|
350
|
+
const netAPY =
|
|
351
|
+
(collateralPosition.apy.apy * collateralPosition.usdValue - debtPosition.apy.apy * debtPosition.usdValue) /
|
|
352
|
+
(collateralPosition.usdValue - debtPosition.usdValue);
|
|
353
|
+
return {
|
|
354
|
+
...collateralPosition,
|
|
355
|
+
apy: { apy: netAPY, type: APYType.BASE },
|
|
356
|
+
};
|
|
338
357
|
}
|
|
339
358
|
|
|
340
359
|
protected _getDepositLeaf(): {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
SIMPLE_SANITIZER,
|
|
18
18
|
SIMPLE_SANITIZER_V2,
|
|
19
|
+
SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
19
20
|
toBigInt,
|
|
20
21
|
} from "./adapter-utils";
|
|
21
22
|
import { hash, uint256, Contract, CairoCustomEnum, num } from "starknet";
|
|
@@ -29,9 +30,7 @@ import {
|
|
|
29
30
|
import { assert, logger } from "@/utils";
|
|
30
31
|
import VesuMultiplyAbi from "@/data/vesu-multiple.abi.json";
|
|
31
32
|
import { EkuboQuote, EkuboQuoter, TokenMarketData } from "@/modules";
|
|
32
|
-
import { calculateDebtReductionAmountForWithdrawal } from "../vesu-extended-strategy/utils/helper";
|
|
33
33
|
import { HealthFactorMath } from "@/utils/health-factor-math";
|
|
34
|
-
import { MAX_LIQUIDATION_RATIO } from "../vesu-extended-strategy/utils/constants";
|
|
35
34
|
import {
|
|
36
35
|
VesuPositionCommonContext,
|
|
37
36
|
getVesuCommonAPY,
|
|
@@ -139,7 +138,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
139
138
|
|
|
140
139
|
const delegationOn: ManageCall = {
|
|
141
140
|
proofReadableId: proofReadableIds.delegationOn,
|
|
142
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
141
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
143
142
|
call: {
|
|
144
143
|
contractAddress: vesuSingleton,
|
|
145
144
|
selector: hash.getSelectorFromName("modify_delegation"),
|
|
@@ -151,7 +150,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
151
150
|
|
|
152
151
|
const modifyLever: ManageCall = {
|
|
153
152
|
proofReadableId: proofReadableIds.modifyLever,
|
|
154
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
153
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
155
154
|
call: {
|
|
156
155
|
contractAddress: vesuMultiply,
|
|
157
156
|
selector: hash.getSelectorFromName("modify_lever"),
|
|
@@ -161,7 +160,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
161
160
|
|
|
162
161
|
const delegationOff: ManageCall = {
|
|
163
162
|
proofReadableId: proofReadableIds.delegationOff,
|
|
164
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
163
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
165
164
|
call: {
|
|
166
165
|
contractAddress: vesuSingleton,
|
|
167
166
|
selector: hash.getSelectorFromName("modify_delegation"),
|
|
@@ -178,7 +177,8 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
178
177
|
const collateral = this.config.collateral;
|
|
179
178
|
const debt = this.config.debt;
|
|
180
179
|
return {
|
|
181
|
-
|
|
180
|
+
approve_collateral: `amc_${this.config.poolId.shortString()}_${collateral.symbol}`,
|
|
181
|
+
approve_margin: `amc_${this.config.poolId.shortString()}_${this.config.marginToken.symbol}`,
|
|
182
182
|
delegationOn: `sd1_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
183
183
|
modifyLever: `vm_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
184
184
|
delegationOff: `sd2_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
@@ -321,7 +321,13 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
321
321
|
const numeratorPart2 = existingDebt
|
|
322
322
|
.multipliedBy(debtPrice)
|
|
323
323
|
.multipliedBy(this.config.targetHealthFactor);
|
|
324
|
+
if (dexPrice === 0) {
|
|
325
|
+
throw new Error("_computeTargetDebtDelta::dexPrice is 0");
|
|
326
|
+
}
|
|
324
327
|
const denominatorPart = this.config.targetHealthFactor - ltv / dexPrice;
|
|
328
|
+
if (denominatorPart === 0) {
|
|
329
|
+
throw new Error("_computeTargetDebtDelta::denominatorPart is 0");
|
|
330
|
+
}
|
|
325
331
|
const x_debt_usd = numeratorPart1
|
|
326
332
|
.minus(numeratorPart2)
|
|
327
333
|
.dividedBy(denominatorPart);
|
|
@@ -377,30 +383,42 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
377
383
|
return 0;
|
|
378
384
|
}
|
|
379
385
|
|
|
380
|
-
|
|
381
|
-
//
|
|
382
|
-
|
|
386
|
+
private _computeMax(collateralPosition: PositionInfo, debtPosition: PositionInfo): PositionInfo {
|
|
387
|
+
// in multiply, most of the debt also goes as collateral. hence, net addable is collateral - debt.
|
|
388
|
+
if (collateralPosition.usdValue <= 0 || collateralPosition.usdValue <= debtPosition.usdValue) {
|
|
389
|
+
return {
|
|
390
|
+
tokenInfo: this.config.collateral,
|
|
391
|
+
amount: new Web3Number(0, this.config.collateral.decimals),
|
|
392
|
+
usdValue: 0,
|
|
393
|
+
remarks: "Max deposit",
|
|
394
|
+
apy: { apy: 0, type: APYType.BASE },
|
|
395
|
+
protocol: this.protocol,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
const maxCollateralUsd = collateralPosition.usdValue - debtPosition.usdValue;
|
|
399
|
+
const collateralPrice = collateralPosition.usdValue / collateralPosition.amount.toNumber();
|
|
400
|
+
const collateralAmount = new Web3Number(maxCollateralUsd / collateralPrice, this.config.collateral.decimals);
|
|
401
|
+
const netAPY =
|
|
402
|
+
(collateralPosition.apy.apy * collateralPosition.usdValue - debtPosition.apy.apy * debtPosition.usdValue) /
|
|
403
|
+
(maxCollateralUsd);
|
|
383
404
|
return {
|
|
384
|
-
tokenInfo: this.config.
|
|
385
|
-
amount:
|
|
386
|
-
usdValue:
|
|
405
|
+
tokenInfo: this.config.collateral,
|
|
406
|
+
amount: collateralAmount,
|
|
407
|
+
usdValue: maxCollateralUsd,
|
|
387
408
|
remarks: "Max deposit",
|
|
388
|
-
apy: { apy:
|
|
409
|
+
apy: { apy: netAPY, type: APYType.BASE },
|
|
389
410
|
protocol: this.protocol,
|
|
390
|
-
}
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
|
|
415
|
+
const { collateralPosition, debtPosition: debtPosition } = await getVesuCommonMaxDeposit(this._getPositionCommonContext(), amount);
|
|
416
|
+
return this._computeMax(collateralPosition, debtPosition);
|
|
391
417
|
}
|
|
392
418
|
|
|
393
419
|
async maxWithdraw(): Promise<PositionInfo> {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return {
|
|
397
|
-
tokenInfo: this.config.baseToken,
|
|
398
|
-
amount: new Web3Number(0, this.config.baseToken.decimals),
|
|
399
|
-
usdValue: 0,
|
|
400
|
-
remarks: "Max withdraw",
|
|
401
|
-
apy: { apy: 0, type: APYType.BASE },
|
|
402
|
-
protocol: this.protocol,
|
|
403
|
-
}
|
|
420
|
+
const { collateralPosition, debtPosition } = await getVesuCommonMaxWithdraw(this._getPositionCommonContext());
|
|
421
|
+
return this._computeMax(collateralPosition, debtPosition);
|
|
404
422
|
}
|
|
405
423
|
|
|
406
424
|
// ─── Leaf Configuration ────────────────────────────────────────────────────
|
|
@@ -415,20 +433,27 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
415
433
|
const collateral = this.config.collateral;
|
|
416
434
|
const debt = this.config.debt;
|
|
417
435
|
const { vesuMultiply, vesuSingleton, isV2 } = this._getMultiplyContract();
|
|
418
|
-
const token = debt;
|
|
419
|
-
const idSuffix = `${collateral.symbol}_${debt.symbol}`;
|
|
420
436
|
const idApprovePrefix = "amc";
|
|
421
437
|
const idDelegOnPrefix = "sd1";
|
|
422
438
|
const idMultiplyPrefix = "vm";
|
|
423
439
|
const idDelegOffPrefix = "sd2";
|
|
424
440
|
|
|
425
441
|
return [
|
|
442
|
+
// allow approval of margin token and collateral to vesu multiply contract
|
|
443
|
+
// depending on strategy, respective token needs to be approved by implementer
|
|
426
444
|
{
|
|
427
|
-
target:
|
|
445
|
+
target: this.config.marginToken.address,
|
|
428
446
|
method: "approve",
|
|
429
447
|
packedArguments: [vesuMultiply.toBigInt()],
|
|
430
448
|
sanitizer: SIMPLE_SANITIZER,
|
|
431
|
-
id: `${idApprovePrefix}_${this.config.poolId.shortString()}_${
|
|
449
|
+
id: `${idApprovePrefix}_${this.config.poolId.shortString()}_${this.config.marginToken.symbol}`,
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
target: collateral.address,
|
|
453
|
+
method: "approve",
|
|
454
|
+
packedArguments: [vesuMultiply.toBigInt()],
|
|
455
|
+
sanitizer: SIMPLE_SANITIZER,
|
|
456
|
+
id: `${idApprovePrefix}_${this.config.poolId.shortString()}_${collateral.symbol}`,
|
|
432
457
|
},
|
|
433
458
|
{
|
|
434
459
|
target: vesuSingleton,
|
|
@@ -436,7 +461,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
436
461
|
packedArguments: isV2
|
|
437
462
|
? [vesuMultiply.toBigInt()]
|
|
438
463
|
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
439
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
464
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
440
465
|
id: `${idDelegOnPrefix}_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
441
466
|
},
|
|
442
467
|
{
|
|
@@ -457,7 +482,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
457
482
|
packedArguments: isV2
|
|
458
483
|
? [vesuMultiply.toBigInt()]
|
|
459
484
|
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
460
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
485
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
461
486
|
id: `${idDelegOffPrefix}_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
462
487
|
},
|
|
463
488
|
];
|
|
@@ -481,7 +506,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
481
506
|
packedArguments: isV2
|
|
482
507
|
? [vesuMultiply.toBigInt()]
|
|
483
508
|
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
484
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
509
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
485
510
|
id: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
486
511
|
},
|
|
487
512
|
{
|
|
@@ -493,7 +518,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
493
518
|
this.config.debt.address.toBigInt(),
|
|
494
519
|
this.config.vaultAllocator.toBigInt(),
|
|
495
520
|
],
|
|
496
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
521
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
497
522
|
id: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
498
523
|
},
|
|
499
524
|
{
|
|
@@ -502,7 +527,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
502
527
|
packedArguments: isV2
|
|
503
528
|
? [vesuMultiply.toBigInt()]
|
|
504
529
|
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
505
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 :
|
|
530
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER_VESU_V1_DELEGATIONS,
|
|
506
531
|
id: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
507
532
|
},
|
|
508
533
|
];
|
|
@@ -557,7 +582,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
557
582
|
const uint256Amount = uint256.bnToUint256(approveAmount.toWei());
|
|
558
583
|
|
|
559
584
|
const approveCall: ManageCall = {
|
|
560
|
-
proofReadableId: proofReadableIds.
|
|
585
|
+
proofReadableId: approveToken.address.eq(this.config.collateral.address) ? proofReadableIds.approve_collateral : proofReadableIds.approve_margin,
|
|
561
586
|
sanitizer: SIMPLE_SANITIZER,
|
|
562
587
|
call: {
|
|
563
588
|
contractAddress: approveToken.address,
|
|
@@ -699,11 +724,6 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
699
724
|
let leverSwap: Swap[] = [];
|
|
700
725
|
let leverSwapLimitAmount = Web3Number.fromWei(0, debtToken.decimals);
|
|
701
726
|
|
|
702
|
-
const isIncrease = debtAmount.greaterThanOrEqualTo(0);
|
|
703
|
-
if (!isIncrease && debtAmount.greaterThan(0)) {
|
|
704
|
-
debtAmount = Web3Number.fromWei(0, this.config.debt.decimals);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
727
|
if (!debtAmount.isZero() && debtAmount.greaterThan(0)) {
|
|
708
728
|
try {
|
|
709
729
|
let swapQuote: EkuboQuote;
|
|
@@ -914,10 +934,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
914
934
|
throw new Error(`VesuMultiplyAdapter: Lever swap price impact too high (${leverSwapQuote.price_impact})`);
|
|
915
935
|
}
|
|
916
936
|
|
|
917
|
-
leverCollateralUsed =
|
|
918
|
-
leverSwapQuote.total_calculated,
|
|
919
|
-
collateralToken.decimals
|
|
920
|
-
).abs();
|
|
937
|
+
leverCollateralUsed = collateralToSwap;
|
|
921
938
|
}
|
|
922
939
|
|
|
923
940
|
let withdrawSwap: Swap[] = [];
|
|
@@ -962,6 +979,8 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
962
979
|
estimatedOutput.decimals = params.outputToken.decimals;
|
|
963
980
|
withdrawSwapLimitAmount = estimatedOutput
|
|
964
981
|
.multipliedBy(1 - this.maxSlippage);
|
|
982
|
+
} else {
|
|
983
|
+
throw new Error(`VesuMultiplyAdapter: Withdraw swap price impact too high (${withdrawQuote.price_impact})`);
|
|
965
984
|
}
|
|
966
985
|
}
|
|
967
986
|
}
|
|
@@ -1029,24 +1048,27 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
1029
1048
|
);
|
|
1030
1049
|
const debtPrice = await this.config.pricer.getPrice(debtToken.symbol);
|
|
1031
1050
|
|
|
1051
|
+
const maxLTV = await this._vesuAdapter.getLTVConfig(
|
|
1052
|
+
this.config.networkConfig
|
|
1053
|
+
);
|
|
1054
|
+
|
|
1032
1055
|
const { deltadebtAmountUnits: debtAmountToRepay } =
|
|
1033
|
-
calculateDebtReductionAmountForWithdrawal(
|
|
1056
|
+
HealthFactorMath.calculateDebtReductionAmountForWithdrawal(
|
|
1034
1057
|
existingDebtInfo.amount,
|
|
1035
1058
|
existingCollateralInfo.amount,
|
|
1036
|
-
|
|
1059
|
+
maxLTV,
|
|
1060
|
+
this.config.targetHealthFactor,
|
|
1037
1061
|
params.amount,
|
|
1038
1062
|
collateralPrice.price,
|
|
1039
1063
|
debtPrice.price,
|
|
1040
|
-
|
|
1064
|
+
collateralToken,
|
|
1065
|
+
debtToken
|
|
1041
1066
|
);
|
|
1042
1067
|
if (!debtAmountToRepay) {
|
|
1043
1068
|
throw new Error("error calculating debt amount to repay");
|
|
1044
1069
|
}
|
|
1045
1070
|
|
|
1046
|
-
const debtToRepayAbs =
|
|
1047
|
-
debtAmountToRepay,
|
|
1048
|
-
debtToken.decimals
|
|
1049
|
-
).abs();
|
|
1071
|
+
const debtToRepayAbs = debtAmountToRepay.abs();
|
|
1050
1072
|
const existingDebtAbs = existingDebtInfo.amount.abs();
|
|
1051
1073
|
|
|
1052
1074
|
const shouldCloseByDebt = debtToRepayAbs.greaterThanOrEqualTo(existingDebtAbs);
|
|
@@ -1155,7 +1177,7 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
1155
1177
|
sqrt_ratio_limit: uint256.bnToUint256(
|
|
1156
1178
|
route.sqrt_ratio_limit.toWei()
|
|
1157
1179
|
),
|
|
1158
|
-
skip_ahead: BigInt(
|
|
1180
|
+
skip_ahead: BigInt(100),
|
|
1159
1181
|
})),
|
|
1160
1182
|
token_amount: {
|
|
1161
1183
|
token: swap.token_amount.token.toBigInt(),
|
|
@@ -1254,12 +1276,13 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
1254
1276
|
);
|
|
1255
1277
|
const allZero = positions.every((p) => p.usdValue === 0);
|
|
1256
1278
|
|
|
1257
|
-
if (allZero) {
|
|
1279
|
+
if (allZero && positions.length == 2) {
|
|
1258
1280
|
const collateralUSD = 1000;
|
|
1259
1281
|
const maxLTV = await this._vesuAdapter.getLTVConfig(
|
|
1260
1282
|
this.config.networkConfig
|
|
1261
1283
|
);
|
|
1262
1284
|
const targetHF = this.config.targetHealthFactor;
|
|
1285
|
+
// though we are passing in token terms, but by simulating with price 1, usd terms == token terms
|
|
1263
1286
|
const maxDebt = HealthFactorMath.getMaxDebtAmountOnLooping(
|
|
1264
1287
|
new Web3Number(collateralUSD, this.config.collateral.decimals),
|
|
1265
1288
|
1,
|
|
@@ -113,7 +113,7 @@ export async function getVesuCommonPosition(
|
|
|
113
113
|
position.amount = position.amount.multipliedBy(-1);
|
|
114
114
|
position.usdValue = position.usdValue * -1;
|
|
115
115
|
}
|
|
116
|
-
const result = { amount: position.amount, remarks:
|
|
116
|
+
const result = { amount: position.amount, remarks: position.remarks };
|
|
117
117
|
ctx.setCache(cacheKey, result, 60000);
|
|
118
118
|
return result;
|
|
119
119
|
} catch (error) {
|
|
@@ -132,10 +132,11 @@ export async function getVesuCommonMaxBorrowableAPY(
|
|
|
132
132
|
return collateralAPY.apy * 0.8;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/// @dev this function is used to calculate the max deposit amount based on the available debt capacity
|
|
135
136
|
export async function getVesuCommonMaxDeposit(
|
|
136
137
|
ctx: VesuPositionCommonContext,
|
|
137
138
|
amount?: Web3Number,
|
|
138
|
-
): Promise<PositionInfo> {
|
|
139
|
+
): Promise<{collateralPosition: PositionInfo, debtPosition: PositionInfo}> {
|
|
139
140
|
const collateral = ctx.collateral;
|
|
140
141
|
const debt = ctx.debt;
|
|
141
142
|
try {
|
|
@@ -169,15 +170,20 @@ export async function getVesuCommonMaxDeposit(
|
|
|
169
170
|
throw new Error("Debt price is 0");
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
|
|
173
|
-
|
|
173
|
+
/**
|
|
174
|
+
* We use existing collateral and debt also in the calculations.
|
|
175
|
+
* This will ensure any new collateral requirement also considers low ltv possibilities + new debt being created.
|
|
176
|
+
*/
|
|
177
|
+
const maxCollateralFromDebt = HealthFactorMath.getCollateralRequired(
|
|
178
|
+
actualMaxBorrowable.plus(debtPosition.amount), // net new debt
|
|
174
179
|
debtPrice.price,
|
|
175
180
|
ctx.targetHealthFactor,
|
|
176
181
|
maxLTV,
|
|
177
182
|
collateralPrice.price,
|
|
178
183
|
collateral,
|
|
179
184
|
);
|
|
180
|
-
const
|
|
185
|
+
const maxNewCollateral = maxCollateralFromDebt.minus(collateralPosition.amount).minimum(0); // net new collateral
|
|
186
|
+
const maxDepositAmount = amount ? amount.minimum(maxNewCollateral) : maxNewCollateral;
|
|
181
187
|
const usdValue = await ctx.getUSDValue(collateral, maxDepositAmount);
|
|
182
188
|
const apys = await Promise.all([
|
|
183
189
|
getVesuCommonAPY(ctx, { asset: collateral, isDebt: false }),
|
|
@@ -185,19 +191,22 @@ export async function getVesuCommonMaxDeposit(
|
|
|
185
191
|
]);
|
|
186
192
|
|
|
187
193
|
const borrowAmountUSD = actualMaxBorrowable.multipliedBy(debtPrice.price);
|
|
188
|
-
const netCollateralUSD = usdValue + borrowAmountUSD.toNumber();
|
|
189
|
-
const netAPY =
|
|
190
|
-
(apys[0].apy * netCollateralUSD + apys[1].apy * borrowAmountUSD.toNumber()) /
|
|
191
|
-
usdValue;
|
|
192
194
|
|
|
193
|
-
return {
|
|
195
|
+
return {collateralPosition: {
|
|
194
196
|
tokenInfo: collateral,
|
|
195
197
|
amount: maxDepositAmount,
|
|
196
198
|
usdValue,
|
|
197
199
|
remarks: "Max deposit based on available debt capacity",
|
|
198
|
-
apy: { apy:
|
|
200
|
+
apy: { apy: apys[0].apy, type: APYType.BASE },
|
|
199
201
|
protocol: ctx.protocol,
|
|
200
|
-
}
|
|
202
|
+
}, debtPosition: {
|
|
203
|
+
tokenInfo: debt,
|
|
204
|
+
amount: actualMaxBorrowable,
|
|
205
|
+
usdValue: borrowAmountUSD.toNumber(),
|
|
206
|
+
remarks: "Max borrow based on available debt capacity",
|
|
207
|
+
apy: { apy: apys[1].apy, type: APYType.BASE },
|
|
208
|
+
protocol: ctx.protocol,
|
|
209
|
+
}};
|
|
201
210
|
} catch (error) {
|
|
202
211
|
logger.error(`${ctx.adapterName}: Error calculating max deposit:`, error);
|
|
203
212
|
throw error;
|
|
@@ -206,7 +215,7 @@ export async function getVesuCommonMaxDeposit(
|
|
|
206
215
|
|
|
207
216
|
export async function getVesuCommonMaxWithdraw(
|
|
208
217
|
ctx: VesuPositionCommonContext,
|
|
209
|
-
): Promise<PositionInfo> {
|
|
218
|
+
): Promise<{collateralPosition: PositionInfo, debtPosition: PositionInfo}> {
|
|
210
219
|
const collateral = ctx.collateral;
|
|
211
220
|
const debt = ctx.debt;
|
|
212
221
|
try {
|
|
@@ -219,30 +228,28 @@ export async function getVesuCommonMaxWithdraw(
|
|
|
219
228
|
throw new Error("Could not find current positions");
|
|
220
229
|
}
|
|
221
230
|
|
|
222
|
-
const collateralPrice = collateralPosition.usdValue / collateralPosition.amount.toNumber();
|
|
223
|
-
const debtInCollateral = debtPosition.usdValue / collateralPrice;
|
|
224
|
-
const maxWithdrawable = collateralPosition.amount.minus(debtInCollateral);
|
|
225
|
-
const result = maxWithdrawable.greaterThan(0)
|
|
226
|
-
? maxWithdrawable
|
|
227
|
-
: new Web3Number("0", collateral.decimals);
|
|
228
|
-
const usdValue = await ctx.getUSDValue(collateral, result);
|
|
229
|
-
const debtUSD = debtPosition.usdValue;
|
|
230
231
|
const apys = await Promise.all([
|
|
231
232
|
getVesuCommonAPY(ctx, { asset: collateral, isDebt: false }),
|
|
232
233
|
getVesuCommonAPY(ctx, { asset: debt, isDebt: true }),
|
|
233
234
|
]);
|
|
234
|
-
const netAPY =
|
|
235
|
-
usdValue - debtUSD > 0
|
|
236
|
-
? (apys[0].apy * usdValue + apys[1].apy * debtUSD) / (usdValue - debtUSD)
|
|
237
|
-
: 0;
|
|
238
235
|
|
|
239
236
|
return {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
237
|
+
collateralPosition: {
|
|
238
|
+
tokenInfo: collateral,
|
|
239
|
+
amount: collateralPosition.amount,
|
|
240
|
+
usdValue: collateralPosition.usdValue,
|
|
241
|
+
remarks: "Max withdraw based on health factor",
|
|
242
|
+
apy: { apy: apys[0].apy, type: APYType.BASE },
|
|
243
|
+
protocol: ctx.protocol,
|
|
244
|
+
},
|
|
245
|
+
debtPosition: {
|
|
246
|
+
tokenInfo: debt,
|
|
247
|
+
amount: debtPosition.amount,
|
|
248
|
+
usdValue: debtPosition.usdValue,
|
|
249
|
+
remarks: "Max withdraw based on health factor",
|
|
250
|
+
apy: { apy: apys[1].apy, type: APYType.BASE },
|
|
251
|
+
protocol: ctx.protocol,
|
|
252
|
+
},
|
|
246
253
|
};
|
|
247
254
|
} catch (error) {
|
|
248
255
|
logger.error(`${ctx.adapterName}: Error calculating max withdraw:`, error);
|