@strkfarm/sdk 2.0.0-dev.34 → 2.0.0-dev.36
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 +16173 -24081
- package/dist/index.browser.mjs +8572 -16661
- package/dist/index.d.ts +600 -2666
- package/dist/index.js +8660 -16784
- package/dist/index.mjs +8585 -16674
- package/package.json +3 -3
- package/src/data/redeem-request-nft.abi.json +752 -0
- 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 -6
- package/src/strategies/registry.ts +28 -5
- package/src/strategies/sensei.ts +29 -13
- package/src/strategies/svk-strategy.ts +29 -4
- package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1057 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +2 -0
- package/src/strategies/universal-adapters/avnu-adapter.ts +19 -10
- package/src/strategies/universal-adapters/index.ts +1 -2
- package/src/strategies/universal-adapters/svk-troves-adapter.ts +160 -13
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +91 -42
- 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 -835
- package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +0 -200
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +0 -2233
- 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
|
@@ -2,6 +2,8 @@ import { ContractAddr } from "@/dataTypes";
|
|
|
2
2
|
|
|
3
3
|
// Zellic audited
|
|
4
4
|
export const SIMPLE_SANITIZER = ContractAddr.from('0x5a2e3ceb3da368b983a8717898427ab7b6daf04014b70f321e777f9aad940b4');
|
|
5
|
+
// Above SIMPLE_SANITIZER was not exposing the request_redeem method
|
|
6
|
+
export const SVK_SIMPLE_SANITIZER = ContractAddr.from('0x03dcde04343257c3ce14574676cb9c5b2eda16e332c1b8caf5dc4c95ac568d2f')
|
|
5
7
|
export const EXTENDED_SANITIZER = ContractAddr.from('0x65891708362b24dcf4c40c8e218cce6e82d1d6b3a3404c9ab00a48f08e2c110');
|
|
6
8
|
export const AVNU_LEGACY_SANITIZER = ContractAddr.from('0x0656fBE853f116DD53956176a553eDe8fE65632252f8aceB50C1B9B6c8237309');
|
|
7
9
|
// Without flashloan options
|
|
@@ -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,9 +98,10 @@ 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,
|
|
104
|
+
// NOTE : this is a direction flag that we are swapping in this direction
|
|
97
105
|
usdcToBtc: boolean,
|
|
98
106
|
): Promise<ManageCall[]> {
|
|
99
107
|
const vaultAllocator = ContractAddr.from(this.config.vaultAllocator.address);
|
|
@@ -121,13 +129,14 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
121
129
|
};
|
|
122
130
|
logger.verbose(
|
|
123
131
|
`${AvnuAdapter.name}::buildSwapCalls stored price info: ` +
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
`${fromAmt} ${fromToken.symbol} → ${toAmt} ${toToken.symbol}, ` +
|
|
133
|
+
`effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`,
|
|
126
134
|
);
|
|
127
135
|
|
|
128
136
|
const getCalldata = await this.avnuWrapper.getSwapCallData(
|
|
129
137
|
quote,
|
|
130
138
|
vaultAllocator.address,
|
|
139
|
+
params.minAmount,
|
|
131
140
|
);
|
|
132
141
|
const swapCallData = getCalldata[0];
|
|
133
142
|
const approveAmount = uint256.bnToUint256(params.amount.toWei());
|
|
@@ -259,7 +268,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
259
268
|
return [];
|
|
260
269
|
}
|
|
261
270
|
|
|
262
|
-
async getDepositCall(params:
|
|
271
|
+
async getDepositCall(params: AvnuDepositParams): Promise<ManageCall[]> {
|
|
263
272
|
try {
|
|
264
273
|
const fromToken = this.config.supportedPositions[0].asset; //usdc
|
|
265
274
|
const toToken = this.config.supportedPositions[1].asset; //wbtc
|
|
@@ -277,7 +286,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
277
286
|
}
|
|
278
287
|
|
|
279
288
|
//Swap wbtc to usdc
|
|
280
|
-
async getWithdrawCall(params:
|
|
289
|
+
async getWithdrawCall(params: AvnuWithdrawParams): Promise<ManageCall[]> {
|
|
281
290
|
try {
|
|
282
291
|
const toToken = this.config.supportedPositions[0].asset;
|
|
283
292
|
const fromToken = this.config.supportedPositions[1].asset;
|
|
@@ -321,7 +330,7 @@ export class AvnuAdapter extends BaseAdapter<DepositParams, WithdrawParams> {
|
|
|
321
330
|
if (attempt === retries - 1) {
|
|
322
331
|
throw err;
|
|
323
332
|
}
|
|
324
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
333
|
+
await new Promise((resolve) => setTimeout(resolve, MAX_AVNU_RETRY_DELAY));
|
|
325
334
|
}
|
|
326
335
|
}
|
|
327
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";
|
|
@@ -14,11 +14,12 @@ import {
|
|
|
14
14
|
WithdrawParams,
|
|
15
15
|
PositionAmount,
|
|
16
16
|
} from "./baseAdapter";
|
|
17
|
-
import { SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
|
|
17
|
+
import { SIMPLE_SANITIZER, SVK_SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
|
|
18
18
|
import { hash, uint256, Contract } from "starknet";
|
|
19
19
|
import { logger } from "@/utils";
|
|
20
20
|
// Troves SVK universal vault ABI: ERC-4626 + SVK views (e.g. due_assets_from_owner).
|
|
21
21
|
import universalVaultAbi from "@/data/universal-vault.abi.json";
|
|
22
|
+
import redeemRequestNftAbi from "@/data/redeem-request-nft.abi.json";
|
|
22
23
|
|
|
23
24
|
/** Public Troves strategies feed (APY + metadata). Override in config for tests. */
|
|
24
25
|
export const DEFAULT_TROVES_STRATEGIES_API = "https://app.troves.fi/api/strategies";
|
|
@@ -40,6 +41,11 @@ export interface SvkTrovesAdapterConfig extends BaseAdapterConfig {
|
|
|
40
41
|
positionOwner?: ContractAddr;
|
|
41
42
|
/** Optional APY endpoint (defaults to {@link DEFAULT_TROVES_STRATEGIES_API}). */
|
|
42
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;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
type TrovesStrategiesApiPayload = {
|
|
@@ -164,13 +170,21 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
|
|
|
164
170
|
const liquid = Web3Number.fromWei(liquidAssetsRaw.toString(), decimals);
|
|
165
171
|
|
|
166
172
|
let pending = Web3Number.fromWei("0", decimals);
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
`${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,
|
|
173
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
|
+
}
|
|
174
188
|
}
|
|
175
189
|
|
|
176
190
|
const total = liquid.plus(pending);
|
|
@@ -189,6 +203,131 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
|
|
|
189
203
|
}
|
|
190
204
|
}
|
|
191
205
|
|
|
206
|
+
async getPendingAssetsFromOwner(
|
|
207
|
+
owner: ContractAddr = this._positionOwner(),
|
|
208
|
+
decimals: number = this.config.baseToken.decimals,
|
|
209
|
+
): Promise<Web3Number> {
|
|
210
|
+
const vault = new Contract({
|
|
211
|
+
abi: universalVaultAbi as never,
|
|
212
|
+
address: this.config.strategyVault.address,
|
|
213
|
+
providerOrAccount: this.config.networkConfig.provider,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const dueRaw: any = await vault.call("due_assets_from_owner", [owner.address]);
|
|
218
|
+
return Web3Number.fromWei(dueRaw.toString(), decimals);
|
|
219
|
+
} catch (e) {
|
|
220
|
+
logger.warn(
|
|
221
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwner: due_assets_from_owner failed (treating as 0): ${(e as Error).message}`,
|
|
222
|
+
);
|
|
223
|
+
return Web3Number.fromWei("0", decimals);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get pending assets from owner by scanning redeem request NFTs.
|
|
229
|
+
* This method iterates backwards through NFT IDs to find all pending redemptions for a specific owner.
|
|
230
|
+
*
|
|
231
|
+
* @param redeemRequestNFT - The redeem request NFT contract address
|
|
232
|
+
* @param owner - The owner address to check for pending redemptions (defaults to positionOwner)
|
|
233
|
+
* @param decimals - Token decimals for conversion (defaults to baseToken decimals)
|
|
234
|
+
* @returns Total pending assets from all NFTs owned by the specified address
|
|
235
|
+
*/
|
|
236
|
+
async getPendingAssetsFromOwnerNFTMethod(
|
|
237
|
+
redeemRequestNFT: ContractAddr,
|
|
238
|
+
owner: ContractAddr = this._positionOwner(),
|
|
239
|
+
decimals: number = this.config.baseToken.decimals,
|
|
240
|
+
): Promise<Web3Number> {
|
|
241
|
+
try {
|
|
242
|
+
const nftContract = new Contract({
|
|
243
|
+
abi: redeemRequestNftAbi as never,
|
|
244
|
+
address: redeemRequestNFT.address,
|
|
245
|
+
providerOrAccount: this.config.networkConfig.provider,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Step 1: Get the latest NFT ID (the one that is not yet used)
|
|
249
|
+
const idLenRaw: any = await nftContract.id_len();
|
|
250
|
+
const latestId = BigInt(idLenRaw.toString());
|
|
251
|
+
|
|
252
|
+
if (latestId === 0n) {
|
|
253
|
+
logger.info(
|
|
254
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: No NFTs minted yet`,
|
|
255
|
+
);
|
|
256
|
+
return Web3Number.fromWei("0", decimals);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Step 2: Start from (latestId - 1) and loop backwards to find all pending NFTs
|
|
260
|
+
const matchingIds: bigint[] = [];
|
|
261
|
+
let currentId = latestId - 1n;
|
|
262
|
+
|
|
263
|
+
while (currentId >= 0n) {
|
|
264
|
+
try {
|
|
265
|
+
// Convert currentId to u256 format
|
|
266
|
+
const idU256 = uint256.bnToUint256(currentId);
|
|
267
|
+
const ownerRaw: any = await nftContract.owner_of(idU256);
|
|
268
|
+
|
|
269
|
+
// Step 3: Filter IDs that have the owner matching our parameter
|
|
270
|
+
// Use ContractAddr.from to normalize addresses before comparison
|
|
271
|
+
const nftOwnerAddr = ContractAddr.from(ownerRaw.toString());
|
|
272
|
+
if (nftOwnerAddr.eq(owner)) {
|
|
273
|
+
matchingIds.push(currentId);
|
|
274
|
+
logger.debug(
|
|
275
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Found matching NFT ID ${currentId} for owner ${owner.address}`,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
currentId--;
|
|
280
|
+
} catch (e) {
|
|
281
|
+
// When owner_of throws an error, it means we've reached the last pending NFT
|
|
282
|
+
logger.info(
|
|
283
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Reached last pending NFT at id ${currentId}`,
|
|
284
|
+
);
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (matchingIds.length === 0) {
|
|
290
|
+
logger.info(
|
|
291
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: No matching NFTs found for owner ${owner.address}`,
|
|
292
|
+
);
|
|
293
|
+
return Web3Number.fromWei("0", decimals);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Step 4: For each matching ID, call id_to_info and sum up all nominal values
|
|
297
|
+
let totalNominal = 0n;
|
|
298
|
+
|
|
299
|
+
for (const nftId of matchingIds) {
|
|
300
|
+
try {
|
|
301
|
+
const idU256 = uint256.bnToUint256(nftId);
|
|
302
|
+
const infoRaw: any = await nftContract.id_to_info(idU256);
|
|
303
|
+
|
|
304
|
+
// The response is a struct with { epoch: u256, nominal: u256 }
|
|
305
|
+
const nominal = BigInt(infoRaw.nominal.toString());
|
|
306
|
+
totalNominal += nominal;
|
|
307
|
+
|
|
308
|
+
logger.debug(
|
|
309
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: NFT ID ${nftId} has nominal ${nominal}`,
|
|
310
|
+
);
|
|
311
|
+
} catch (e) {
|
|
312
|
+
logger.warn(
|
|
313
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Failed to get info for NFT ID ${nftId}: ${(e as Error).message}`,
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
logger.info(
|
|
319
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: Found ${matchingIds.length} NFTs with total nominal ${totalNominal}`,
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
return Web3Number.fromWei(totalNominal.toString(), decimals);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
logger.error(
|
|
325
|
+
`${SvkTrovesAdapter.name}::getPendingAssetsFromOwnerNFTMethod: ${(error as Error).message}`,
|
|
326
|
+
);
|
|
327
|
+
return Web3Number.fromWei("0", decimals);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
192
331
|
async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
|
|
193
332
|
const baseToken = this.config.baseToken;
|
|
194
333
|
if (!amount) {
|
|
@@ -270,9 +409,9 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
|
|
|
270
409
|
return [
|
|
271
410
|
{
|
|
272
411
|
target: strategyVault,
|
|
273
|
-
method: "
|
|
412
|
+
method: "request_redeem",
|
|
274
413
|
packedArguments: [recv.toBigInt(), owner.toBigInt()],
|
|
275
|
-
sanitizer:
|
|
414
|
+
sanitizer: SVK_SIMPLE_SANITIZER,
|
|
276
415
|
id: this._withdrawCallProofReadableId(),
|
|
277
416
|
},
|
|
278
417
|
];
|
|
@@ -340,16 +479,24 @@ export class SvkTrovesAdapter extends BaseAdapter<DepositParams, WithdrawParams>
|
|
|
340
479
|
const recv = this.config.vaultAllocator;
|
|
341
480
|
const owner = this.config.vaultAllocator;
|
|
342
481
|
|
|
482
|
+
const vault = new Contract({
|
|
483
|
+
abi: universalVaultAbi as never,
|
|
484
|
+
address: strategyVault.address,
|
|
485
|
+
providerOrAccount: this.config.networkConfig.provider,
|
|
486
|
+
});
|
|
487
|
+
const sharesRaw: any = await vault.convert_to_shares(uint256Amount);
|
|
488
|
+
const sharesU256 = uint256.bnToUint256(sharesRaw.toString());
|
|
489
|
+
|
|
343
490
|
return [
|
|
344
491
|
{
|
|
345
492
|
proofReadableId: this._withdrawCallProofReadableId(),
|
|
346
|
-
sanitizer:
|
|
493
|
+
sanitizer: SVK_SIMPLE_SANITIZER,
|
|
347
494
|
call: {
|
|
348
495
|
contractAddress: strategyVault,
|
|
349
|
-
selector: hash.getSelectorFromName("
|
|
496
|
+
selector: hash.getSelectorFromName("request_redeem"),
|
|
350
497
|
calldata: [
|
|
351
|
-
toBigInt(
|
|
352
|
-
toBigInt(
|
|
498
|
+
toBigInt(sharesU256.low.toString()),
|
|
499
|
+
toBigInt(sharesU256.high.toString()),
|
|
353
500
|
recv.toBigInt(),
|
|
354
501
|
owner.toBigInt(),
|
|
355
502
|
],
|
|
@@ -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
|
}
|
|
@@ -198,9 +202,19 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
198
202
|
if (!helperOutput || helperOutput.lessThan(0)) {
|
|
199
203
|
throw new Error(`Failed to calculate default deposit debt delta: ${helperOutput?.toNumber()}`);
|
|
200
204
|
}
|
|
205
|
+
// Fixed :
|
|
206
|
+
// getMaxDebtAmount returns the TOTAL target debt at the new collateral level.
|
|
207
|
+
// Subtract current debt to get the incremental borrow delta.
|
|
201
208
|
const normalizedDebtAmount = this._normalizeDebtAmountFromHelper(
|
|
202
209
|
helperOutput,
|
|
203
|
-
);
|
|
210
|
+
).minus(state.currentDebt);
|
|
211
|
+
if (normalizedDebtAmount.lessThan(0)) {
|
|
212
|
+
logger.warn(`VesuModifyPositionAdapter: deposit debt delta is negative (${normalizedDebtAmount.toNumber()}), clamping to zero`);
|
|
213
|
+
return {
|
|
214
|
+
collateral: this._toSigned(collateralToAdd, false),
|
|
215
|
+
debt: this._toSigned(Web3Number.fromWei(0, this.config.debt.decimals), false),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
204
218
|
return {
|
|
205
219
|
collateral: this._toSigned(collateralToAdd, false),
|
|
206
220
|
debt: this._toSigned(normalizedDebtAmount, false),
|
|
@@ -221,8 +235,8 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
221
235
|
state.debtPrice,
|
|
222
236
|
this.config.debt,
|
|
223
237
|
);
|
|
224
|
-
if (!helperOutput || helperOutput.
|
|
225
|
-
throw new Error(`Failed to calculate
|
|
238
|
+
if (!helperOutput || helperOutput.lessThan(0)) {
|
|
239
|
+
throw new Error(`Failed to calculate max debt amount for withdraw: ${helperOutput?.toNumber()}`);
|
|
226
240
|
}
|
|
227
241
|
const normalizedDebtAmount = this._normalizeDebtAmountFromHelper(
|
|
228
242
|
helperOutput,
|
|
@@ -262,21 +276,21 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
262
276
|
const call = contract.populate("modify_position", {
|
|
263
277
|
params: isV2
|
|
264
278
|
? {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
279
|
+
collateral_asset: this.config.collateral.address.toBigInt(),
|
|
280
|
+
debt_asset: this.config.debt.address.toBigInt(),
|
|
281
|
+
user: this.config.vaultAllocator.toBigInt(),
|
|
282
|
+
collateral: this._amountStruct(collateralDelta),
|
|
283
|
+
debt: this._amountStruct(debtDelta),
|
|
284
|
+
}
|
|
271
285
|
: {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
286
|
+
pool_id: this.config.poolId.toBigInt(),
|
|
287
|
+
collateral_asset: this.config.collateral.address.toBigInt(),
|
|
288
|
+
debt_asset: this.config.debt.address.toBigInt(),
|
|
289
|
+
user: this.config.vaultAllocator.toBigInt(),
|
|
290
|
+
collateral: this._amountStruct(collateralDelta),
|
|
291
|
+
debt: this._amountStruct(debtDelta),
|
|
292
|
+
data: [0],
|
|
293
|
+
},
|
|
280
294
|
});
|
|
281
295
|
return {
|
|
282
296
|
proofReadableId,
|
|
@@ -320,11 +334,26 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
320
334
|
}
|
|
321
335
|
|
|
322
336
|
async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
|
|
323
|
-
|
|
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
|
+
};
|
|
324
346
|
}
|
|
325
347
|
|
|
326
348
|
async maxWithdraw(): Promise<PositionInfo> {
|
|
327
|
-
|
|
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
|
+
};
|
|
328
357
|
}
|
|
329
358
|
|
|
330
359
|
protected _getDepositLeaf(): {
|
|
@@ -337,18 +366,18 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
337
366
|
const { addr, isV2 } = getVesuSingletonAddress(this.config.poolId);
|
|
338
367
|
const modifyPackedArguments = isV2
|
|
339
368
|
? [
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
369
|
+
this.config.collateral.address.toBigInt(),
|
|
370
|
+
this.config.debt.address.toBigInt(),
|
|
371
|
+
this.config.vaultAllocator.toBigInt(),
|
|
372
|
+
]
|
|
344
373
|
: [
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
374
|
+
this.config.poolId.toBigInt(),
|
|
375
|
+
this.config.collateral.address.toBigInt(),
|
|
376
|
+
this.config.debt.address.toBigInt(),
|
|
377
|
+
this.config.vaultAllocator.toBigInt(),
|
|
378
|
+
1n,
|
|
379
|
+
0n,
|
|
380
|
+
];
|
|
352
381
|
|
|
353
382
|
return [
|
|
354
383
|
{
|
|
@@ -378,18 +407,18 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
378
407
|
const { addr, isV2 } = getVesuSingletonAddress(this.config.poolId);
|
|
379
408
|
const modifyPackedArguments = isV2
|
|
380
409
|
? [
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
410
|
+
this.config.collateral.address.toBigInt(),
|
|
411
|
+
this.config.debt.address.toBigInt(),
|
|
412
|
+
this.config.vaultAllocator.toBigInt(),
|
|
413
|
+
]
|
|
385
414
|
: [
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
415
|
+
this.config.poolId.toBigInt(),
|
|
416
|
+
this.config.collateral.address.toBigInt(),
|
|
417
|
+
this.config.debt.address.toBigInt(),
|
|
418
|
+
this.config.vaultAllocator.toBigInt(),
|
|
419
|
+
1n,
|
|
420
|
+
0n,
|
|
421
|
+
];
|
|
393
422
|
|
|
394
423
|
return [
|
|
395
424
|
{
|
|
@@ -473,4 +502,24 @@ export class VesuModifyPositionAdapter extends BaseAdapter<
|
|
|
473
502
|
this._prepareVesuAdapter();
|
|
474
503
|
return this._vesuAdapter.getHealthFactor();
|
|
475
504
|
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Simulates a deposit of `depositAmount` collateral and returns how much
|
|
508
|
+
* debt (STRK) would be incrementally borrowed to reach the target LTV.
|
|
509
|
+
* Used upstream to size the AVNU swap call in the same transaction batch.
|
|
510
|
+
*/
|
|
511
|
+
async getExpectedDepositDebtDelta(depositAmount: Web3Number): Promise<Web3Number> {
|
|
512
|
+
const defaults = await this._buildDefaultDepositDeltas({ amount: depositAmount });
|
|
513
|
+
return defaults.debt.amount;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Simulates a withdrawal of `withdrawAmount` collateral and returns the
|
|
518
|
+
* incremental debt delta needed to keep the target health factor.
|
|
519
|
+
* Positive means borrow, negative means repay.
|
|
520
|
+
*/
|
|
521
|
+
async getExpectedWithdrawDebtDelta(withdrawAmount: Web3Number): Promise<Web3Number> {
|
|
522
|
+
const defaults = await this._buildDefaultWithdrawDeltas({ amount: withdrawAmount });
|
|
523
|
+
return defaults.debt.amount;
|
|
524
|
+
}
|
|
476
525
|
}
|