@strkfarm/sdk 2.0.0-dev.2 → 2.0.0-dev.21
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/index.browser.global.js +2006 -1062
- package/dist/index.browser.mjs +1845 -911
- package/dist/index.d.ts +144 -37
- package/dist/index.js +1853 -915
- package/dist/index.mjs +1845 -911
- package/package.json +1 -1
- package/src/modules/ExtendedWrapperSDk/types.ts +1 -1
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +39 -8
- package/src/modules/ekubo-quoter.ts +0 -12
- package/src/strategies/index.ts +2 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +17 -9
- package/src/strategies/universal-adapters/extended-adapter.ts +500 -146
- package/src/strategies/universal-adapters/index.ts +2 -1
- package/src/strategies/universal-adapters/vesu-adapter.ts +6 -6
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +778 -396
- package/src/strategies/universal-lst-muliplier-strategy.tsx +2 -1
- package/src/strategies/universal-strategy.tsx +5 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +25 -16
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +36 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +3 -6
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +50 -16
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +746 -305
|
@@ -13,7 +13,8 @@ import { logger } from "@/utils";
|
|
|
13
13
|
import { AUDIT_URL } from "../universal-lst-muliplier-strategy";
|
|
14
14
|
import { getNoRiskTags } from "@/interfaces";
|
|
15
15
|
import { _riskFactor } from "../universal-lst-muliplier-strategy";
|
|
16
|
-
import { BUFFER_USDC_IN_WITHDRAWAL,
|
|
16
|
+
import { BUFFER_USDC_IN_WITHDRAWAL, LIMIT_BALANCE, LIMIT_BALANCE_VALUE, MINIMUM_EXTENDED_POSITION_SIZE, USDC_TOKEN_DECIMALS, WALLET_ADDRESS, WBTC_TOKEN_DECIMALS } from "./utils/constants";
|
|
17
|
+
import { CycleType } from "./types/transaction-metadata";
|
|
17
18
|
import { PricerBase } from "@/modules/pricerBase";
|
|
18
19
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
19
20
|
import { Global } from "@/global";
|
|
@@ -53,6 +54,8 @@ import {
|
|
|
53
54
|
} from "./utils/helper";
|
|
54
55
|
import { SingleTokenInfo } from "../base-strategy";
|
|
55
56
|
import { Call } from "starknet";
|
|
57
|
+
import { PositionTypeAvnuExtended } from "../universal-strategy";
|
|
58
|
+
import { TransactionMetadata, TransactionResult } from "./types/transaction-metadata";
|
|
56
59
|
|
|
57
60
|
|
|
58
61
|
export interface VesuExtendedStrategySettings
|
|
@@ -64,6 +67,7 @@ export interface VesuExtendedStrategySettings
|
|
|
64
67
|
minHealthFactor: number;
|
|
65
68
|
aumOracle: ContractAddr;
|
|
66
69
|
minimumWBTCDifferenceForAvnuSwap: number;
|
|
70
|
+
walletAddress: string;
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
export class VesuExtendedMultiplierStrategy<
|
|
@@ -71,7 +75,6 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
71
75
|
>
|
|
72
76
|
extends SVKStrategy<S>
|
|
73
77
|
implements Operations {
|
|
74
|
-
|
|
75
78
|
constructor(
|
|
76
79
|
config: IConfig,
|
|
77
80
|
pricer: PricerBase,
|
|
@@ -108,7 +111,7 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
108
111
|
debtPrice,
|
|
109
112
|
};
|
|
110
113
|
}
|
|
111
|
-
|
|
114
|
+
|
|
112
115
|
async getUnusedBalanceUSDCE(): Promise<SingleTokenInfo> {
|
|
113
116
|
const usdceToken = Global.getDefaultTokens().find(
|
|
114
117
|
(token) => token.symbol === "USDCe"
|
|
@@ -179,20 +182,40 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
179
182
|
return extendedAdapter.adapter as ExtendedAdapter;
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
async moveAssetsToVaultAllocator(amount: Web3Number, extendedAdapter: ExtendedAdapter): Promise<
|
|
185
|
+
async moveAssetsToVaultAllocator(amount: Web3Number, extendedAdapter: ExtendedAdapter): Promise<{
|
|
186
|
+
calls: Call[],
|
|
187
|
+
status: boolean,
|
|
188
|
+
}> {
|
|
183
189
|
try {
|
|
184
190
|
const usdceToken = Global.getDefaultTokens().find(
|
|
185
191
|
(token) => token.symbol === "USDCe"
|
|
186
192
|
)!;
|
|
193
|
+
const walletBalance = await new ERC20(this.config).balanceOf(
|
|
194
|
+
usdceToken.address,
|
|
195
|
+
WALLET_ADDRESS,
|
|
196
|
+
usdceToken.decimals
|
|
197
|
+
);
|
|
198
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::moveAssetsToVaultAllocator walletBalance: ${walletBalance}`);
|
|
199
|
+
const amountToBeTransferred = amount.minimum(walletBalance);
|
|
200
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::moveAssetsToVaultAllocator amountToBeTransferred: ${amountToBeTransferred.toNumber()}`);
|
|
201
|
+
|
|
202
|
+
if (amountToBeTransferred.lessThan(0)) {
|
|
203
|
+
logger.error(`${VesuExtendedMultiplierStrategy.name}::moveAssetsToVaultAllocator amountToBeTransferred is less than 0: ${amountToBeTransferred.toNumber()}`);
|
|
204
|
+
return {
|
|
205
|
+
calls: [],
|
|
206
|
+
status: false,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
187
210
|
const approveCall = new ERC20(this.config).approve(
|
|
188
211
|
usdceToken.address,
|
|
189
212
|
this.metadata.additionalInfo.vaultAllocator,
|
|
190
|
-
|
|
213
|
+
amountToBeTransferred
|
|
191
214
|
);
|
|
192
215
|
const transferCall = new ERC20(this.config).transfer(
|
|
193
216
|
usdceToken.address,
|
|
194
217
|
this.metadata.additionalInfo.vaultAllocator,
|
|
195
|
-
|
|
218
|
+
amountToBeTransferred
|
|
196
219
|
);
|
|
197
220
|
const proofsInfo = extendedAdapter.getProofsForFromLegacySwap(
|
|
198
221
|
this.getMerkleTree()
|
|
@@ -200,12 +223,18 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
200
223
|
const proofGroups = proofsInfo.proofs;
|
|
201
224
|
const call = this.getManageCall(
|
|
202
225
|
proofGroups,
|
|
203
|
-
await proofsInfo.callConstructor({ amount:
|
|
226
|
+
await proofsInfo.callConstructor({ amount: amountToBeTransferred })
|
|
204
227
|
);
|
|
205
|
-
return
|
|
228
|
+
return {
|
|
229
|
+
calls: [approveCall, transferCall, call],
|
|
230
|
+
status: true,
|
|
231
|
+
};
|
|
206
232
|
} catch (err) {
|
|
207
233
|
logger.error(`error moving assets to vault allocator: ${err}`);
|
|
208
|
-
return
|
|
234
|
+
return {
|
|
235
|
+
calls: [],
|
|
236
|
+
status: false,
|
|
237
|
+
};
|
|
209
238
|
}
|
|
210
239
|
}
|
|
211
240
|
|
|
@@ -219,11 +248,42 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
219
248
|
vesuLeverage: number;
|
|
220
249
|
}> {
|
|
221
250
|
try {
|
|
251
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest starting`);
|
|
222
252
|
const vesuAdapter = await this.getVesuAdapter();
|
|
223
253
|
const extendedAdapter = await this.getExtendedAdapter();
|
|
224
|
-
|
|
254
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest adapters fetched: vesuAdapter=${!!vesuAdapter}, extendedAdapter=${!!extendedAdapter}, extendedAdapter.client=${!!extendedAdapter?.client}`);
|
|
255
|
+
|
|
256
|
+
if (!vesuAdapter) {
|
|
225
257
|
logger.error(
|
|
226
|
-
`
|
|
258
|
+
`Vesu adapter not configured in metadata. This is a configuration issue, not a temporary failure.`
|
|
259
|
+
);
|
|
260
|
+
return {
|
|
261
|
+
shouldInvest: false,
|
|
262
|
+
vesuAmount: new Web3Number(0, 0),
|
|
263
|
+
extendedAmount: new Web3Number(0, 0),
|
|
264
|
+
extendedLeverage: 0,
|
|
265
|
+
collateralPrice: 0,
|
|
266
|
+
debtPrice: 0,
|
|
267
|
+
vesuLeverage: 0,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (!extendedAdapter) {
|
|
271
|
+
logger.error(
|
|
272
|
+
`Extended adapter not configured in metadata. This is a configuration issue, not a temporary failure.`
|
|
273
|
+
);
|
|
274
|
+
return {
|
|
275
|
+
shouldInvest: false,
|
|
276
|
+
vesuAmount: new Web3Number(0, 0),
|
|
277
|
+
extendedAmount: new Web3Number(0, 0),
|
|
278
|
+
extendedLeverage: 0,
|
|
279
|
+
collateralPrice: 0,
|
|
280
|
+
debtPrice: 0,
|
|
281
|
+
vesuLeverage: 0,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (!extendedAdapter.client) {
|
|
285
|
+
logger.error(
|
|
286
|
+
`Extended adapter client not initialized. This may be a temporary initialization failure - check network connectivity and API availability.`
|
|
227
287
|
);
|
|
228
288
|
return {
|
|
229
289
|
shouldInvest: false,
|
|
@@ -235,12 +295,67 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
235
295
|
vesuLeverage: 0,
|
|
236
296
|
};
|
|
237
297
|
}
|
|
298
|
+
|
|
299
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest calling getUnusedBalance`);
|
|
238
300
|
const balance = await this.getUnusedBalance();
|
|
301
|
+
|
|
302
|
+
if (!Number.isFinite(balance.usdValue) || balance.usdValue < 0) {
|
|
303
|
+
logger.error(
|
|
304
|
+
`Invalid balance.usdValue: ${balance.usdValue}. Expected a finite, non-negative number.`
|
|
305
|
+
);
|
|
306
|
+
return {
|
|
307
|
+
shouldInvest: false,
|
|
308
|
+
vesuAmount: new Web3Number(0, 0),
|
|
309
|
+
extendedAmount: new Web3Number(0, 0),
|
|
310
|
+
extendedLeverage: 0,
|
|
311
|
+
collateralPrice: 0,
|
|
312
|
+
debtPrice: 0,
|
|
313
|
+
vesuLeverage: 0,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest balance: ${balance.usdValue}`);
|
|
239
317
|
const usdcBalanceOnExtended = await extendedAdapter.getExtendedDepositAmount();
|
|
318
|
+
|
|
319
|
+
if (usdcBalanceOnExtended) {
|
|
320
|
+
const availableForWithdrawal = parseFloat(usdcBalanceOnExtended.availableForWithdrawal);
|
|
321
|
+
if (!Number.isFinite(availableForWithdrawal) || availableForWithdrawal < 0) {
|
|
322
|
+
logger.error(
|
|
323
|
+
`Invalid usdcBalanceOnExtended.availableForWithdrawal: ${usdcBalanceOnExtended.availableForWithdrawal}. Expected a finite, non-negative number.`
|
|
324
|
+
);
|
|
325
|
+
return {
|
|
326
|
+
shouldInvest: false,
|
|
327
|
+
vesuAmount: new Web3Number(0, 0),
|
|
328
|
+
extendedAmount: new Web3Number(0, 0),
|
|
329
|
+
extendedLeverage: 0,
|
|
330
|
+
collateralPrice: 0,
|
|
331
|
+
debtPrice: 0,
|
|
332
|
+
vesuLeverage: 0,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
240
337
|
/** The LIMIT_BALANCE is the bffer amount to keep in the investing Cycle */
|
|
241
|
-
const amountToInvest = balance.
|
|
338
|
+
const amountToInvest = new Web3Number(balance.usdValue, USDC_TOKEN_DECIMALS).plus(usdcBalanceOnExtended?.availableForWithdrawal ?? 0).multipliedBy(1 - LIMIT_BALANCE);
|
|
339
|
+
|
|
242
340
|
|
|
243
|
-
|
|
341
|
+
const amountToInvestNumber = amountToInvest.toNumber();
|
|
342
|
+
if (!Number.isFinite(amountToInvestNumber)) {
|
|
343
|
+
logger.error(
|
|
344
|
+
`Invalid amountToInvest calculation result: ${amountToInvestNumber}. Calculation may have produced NaN or Infinity.`
|
|
345
|
+
);
|
|
346
|
+
return {
|
|
347
|
+
shouldInvest: false,
|
|
348
|
+
vesuAmount: new Web3Number(0, 0),
|
|
349
|
+
extendedAmount: new Web3Number(0, 0),
|
|
350
|
+
extendedLeverage: 0,
|
|
351
|
+
collateralPrice: 0,
|
|
352
|
+
debtPrice: 0,
|
|
353
|
+
vesuLeverage: 0,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest amountToInvest: ${amountToInvestNumber}`);
|
|
358
|
+
if (amountToInvest.lessThan(LIMIT_BALANCE_VALUE)) {
|
|
244
359
|
return {
|
|
245
360
|
shouldInvest: false,
|
|
246
361
|
vesuAmount: new Web3Number(0, 0),
|
|
@@ -272,6 +387,37 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
272
387
|
collateralPrice,
|
|
273
388
|
debtPrice
|
|
274
389
|
} = await this.getAssetPrices();
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
if (!Number.isFinite(collateralPrice.price) || collateralPrice.price <= 0) {
|
|
393
|
+
logger.error(
|
|
394
|
+
`Invalid collateralPrice: ${collateralPrice.price}. Expected a finite, positive number.`
|
|
395
|
+
);
|
|
396
|
+
return {
|
|
397
|
+
shouldInvest: false,
|
|
398
|
+
vesuAmount: new Web3Number(0, 0),
|
|
399
|
+
extendedAmount: new Web3Number(0, 0),
|
|
400
|
+
extendedLeverage: 0,
|
|
401
|
+
collateralPrice: 0,
|
|
402
|
+
debtPrice: 0,
|
|
403
|
+
vesuLeverage: 0,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
if (!Number.isFinite(debtPrice.price) || debtPrice.price <= 0) {
|
|
407
|
+
logger.error(
|
|
408
|
+
`Invalid debtPrice: ${debtPrice.price}. Expected a finite, positive number.`
|
|
409
|
+
);
|
|
410
|
+
return {
|
|
411
|
+
shouldInvest: false,
|
|
412
|
+
vesuAmount: new Web3Number(0, 0),
|
|
413
|
+
extendedAmount: new Web3Number(0, 0),
|
|
414
|
+
extendedLeverage: 0,
|
|
415
|
+
collateralPrice: 0,
|
|
416
|
+
debtPrice: 0,
|
|
417
|
+
vesuLeverage: 0,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
275
421
|
const { vesu_amount, extended_amount, extended_leverage, vesu_leverage } =
|
|
276
422
|
await calculateAmountDistribution(
|
|
277
423
|
amountToInvest.toNumber(),
|
|
@@ -301,6 +447,7 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
301
447
|
vesuLeverage: 0,
|
|
302
448
|
};
|
|
303
449
|
}
|
|
450
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldInvest vesu_amount: ${vesu_amount.toNumber()}, extended_amount: ${extended_amount.toNumber()}`);
|
|
304
451
|
return {
|
|
305
452
|
shouldInvest: true,
|
|
306
453
|
vesuAmount: vesu_amount,
|
|
@@ -324,110 +471,258 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
324
471
|
}
|
|
325
472
|
}
|
|
326
473
|
|
|
327
|
-
async shouldMoveAssets(extendedAmount: Web3Number, vesuAmount: Web3Number): Promise<
|
|
474
|
+
async shouldMoveAssets(extendedAmount: Web3Number, vesuAmount: Web3Number): Promise<TransactionResult[]> {
|
|
328
475
|
try {
|
|
329
476
|
const vesuAdapter = await this.getVesuAdapter();
|
|
330
477
|
const extendedAdapter = await this.getExtendedAdapter();
|
|
331
|
-
let calls: Call[] = [];
|
|
332
478
|
if (!vesuAdapter || !extendedAdapter || !extendedAdapter.client) {
|
|
333
479
|
logger.error(
|
|
334
480
|
`vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
|
|
335
481
|
);
|
|
336
|
-
return
|
|
482
|
+
return [];
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
486
|
+
if (!extendedHoldings) {
|
|
487
|
+
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
488
|
+
return [];
|
|
337
489
|
}
|
|
338
|
-
|
|
490
|
+
const usdcAmountInWallet = (await this.getUnusedBalance()).amount;
|
|
491
|
+
const usdcAmountOnExtendedAvailableForWithdrawal = parseFloat(
|
|
492
|
+
extendedHoldings.availableForWithdrawal
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldMoveAssets calculating movements - Extended current: ${usdcAmountOnExtendedAvailableForWithdrawal}, Wallet: ${usdcAmountInWallet.toNumber()}, Target Extended: ${extendedAmount.toNumber()}, Target Vesu: ${vesuAmount.toNumber()}`);
|
|
496
|
+
|
|
497
|
+
let totalExtendedWithdrawal = new Web3Number(0, USDC_TOKEN_DECIMALS);
|
|
498
|
+
let totalExtendedDeposit = new Web3Number(0, USDC_TOKEN_DECIMALS);
|
|
499
|
+
|
|
500
|
+
if (extendedAmount.isNegative() && extendedAmount.abs().greaterThan(extendedAdapter.minimumExtendedMovementAmount)) {
|
|
501
|
+
totalExtendedWithdrawal = totalExtendedWithdrawal.plus(extendedAmount.abs());
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Calculate remaining Extended difference (target vs current)
|
|
505
|
+
// If extendedAmount was negative, we've already accounted for that withdrawal
|
|
506
|
+
// So we calculate based on what Extended will be after that withdrawal
|
|
507
|
+
const extendedTargetAmount = extendedAmount.abs(); // Use absolute value as target
|
|
508
|
+
let projectedExtendedBalance = usdcAmountOnExtendedAvailableForWithdrawal;
|
|
509
|
+
|
|
510
|
+
if (extendedAmount.isNegative()) {
|
|
511
|
+
projectedExtendedBalance = projectedExtendedBalance - extendedAmount.abs().toNumber();
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const extendedAmountDifference = extendedTargetAmount.minus(projectedExtendedBalance);
|
|
515
|
+
const extendedAmountDifferenceAbs = extendedAmountDifference.abs();
|
|
516
|
+
|
|
517
|
+
// Track additional Extended movements
|
|
518
|
+
if (extendedAmountDifference.lessThan(0)) {
|
|
519
|
+
totalExtendedWithdrawal = totalExtendedWithdrawal.plus(extendedAmountDifferenceAbs);
|
|
520
|
+
} else if (extendedAmountDifference.greaterThan(0)) {
|
|
521
|
+
totalExtendedDeposit = totalExtendedDeposit.plus(extendedAmountDifference);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const vesuTargetAmount = vesuAmount.abs();
|
|
525
|
+
const projectedWalletBalance = usdcAmountInWallet
|
|
526
|
+
.plus(totalExtendedWithdrawal)
|
|
527
|
+
.minus(totalExtendedDeposit);
|
|
528
|
+
|
|
529
|
+
let vesuAmountDifference = vesuTargetAmount.minus(projectedWalletBalance);
|
|
530
|
+
const vesuAmountDifferenceAbs = vesuAmountDifference.abs();
|
|
531
|
+
|
|
532
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::shouldMoveAssets calculated movements - Extended withdrawal: ${totalExtendedWithdrawal.toNumber()}, Extended deposit: ${totalExtendedDeposit.toNumber()}, Extended diff: ${extendedAmountDifference.toNumber()}, Projected wallet: ${projectedWalletBalance.toNumber()}, Vesu diff: ${vesuAmountDifference.toNumber()}`);
|
|
533
|
+
let calls: Call[] = [];
|
|
534
|
+
let transactionResults: TransactionResult[] = [];
|
|
535
|
+
|
|
536
|
+
// Handle negative extendedAmount (initial withdrawal needed)
|
|
537
|
+
if (extendedAmount.isNegative() && extendedAmount.abs().greaterThan(extendedAdapter.minimumExtendedMovementAmount)) {
|
|
339
538
|
try {
|
|
340
|
-
const {calls: extendedCalls, status: extendedStatus} = await this.moveAssets(
|
|
539
|
+
const { calls: extendedCalls, status: extendedStatus, transactionMetadata: extendedTransactionMetadata } = await this.moveAssets(
|
|
341
540
|
{
|
|
342
541
|
to: Protocols.VAULT.name,
|
|
343
542
|
from: Protocols.EXTENDED.name,
|
|
344
543
|
amount: extendedAmount.abs(),
|
|
544
|
+
cycleType: CycleType.INVESTMENT
|
|
345
545
|
},
|
|
346
546
|
extendedAdapter,
|
|
347
547
|
vesuAdapter
|
|
348
548
|
);
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
549
|
+
if (extendedStatus) {
|
|
550
|
+
transactionResults.push({
|
|
551
|
+
status: extendedStatus,
|
|
552
|
+
calls: extendedCalls,
|
|
553
|
+
transactionMetadata: {
|
|
554
|
+
...extendedTransactionMetadata,
|
|
555
|
+
transactionType: "DEPOSIT",
|
|
556
|
+
},
|
|
557
|
+
})
|
|
558
|
+
} else {
|
|
559
|
+
return [this.createTransactionResult([], false, { from: Protocols.EXTENDED.name, to: Protocols.VAULT.name, amount: extendedAmount.abs() }, "NONE", CycleType.INVESTMENT)];
|
|
354
560
|
}
|
|
355
561
|
} catch (err) {
|
|
356
562
|
logger.error(`Failed moving assets to vault: ${err}`);
|
|
563
|
+
return [this.createTransactionResult([], false, { from: Protocols.EXTENDED.name, to: Protocols.VAULT.name, amount: extendedAmount.abs() }, "NONE", CycleType.INVESTMENT)];
|
|
357
564
|
}
|
|
358
565
|
}
|
|
359
566
|
|
|
360
|
-
if (vesuAmount.
|
|
567
|
+
if (vesuAmount.isNegative() && vesuAmount.abs().greaterThan(vesuAdapter.minimumVesuMovementAmount)) {
|
|
361
568
|
try {
|
|
362
|
-
const {calls: vesuCalls, status: vesuStatus} = await this.moveAssets(
|
|
569
|
+
const { calls: vesuCalls, status: vesuStatus, transactionMetadata: vesuTransactionMetadata } = await this.moveAssets(
|
|
363
570
|
{
|
|
364
571
|
to: Protocols.EXTENDED.name,
|
|
365
572
|
from: Protocols.VESU.name,
|
|
366
573
|
amount: vesuAmount.abs(),
|
|
574
|
+
cycleType: CycleType.INVESTMENT
|
|
367
575
|
},
|
|
368
576
|
extendedAdapter,
|
|
369
577
|
vesuAdapter
|
|
370
578
|
);
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
return [];
|
|
579
|
+
if (!vesuStatus) {
|
|
580
|
+
return [this.createTransactionResult([], false, { from: Protocols.VESU.name, to: Protocols.EXTENDED.name, amount: vesuAmount.abs() }, "NONE", CycleType.INVESTMENT)];
|
|
374
581
|
}
|
|
582
|
+
transactionResults.push({
|
|
583
|
+
status: vesuStatus,
|
|
584
|
+
calls: vesuCalls,
|
|
585
|
+
transactionMetadata: {
|
|
586
|
+
...vesuTransactionMetadata,
|
|
587
|
+
transactionType: "DEPOSIT",
|
|
588
|
+
}
|
|
589
|
+
})
|
|
375
590
|
} catch (err) {
|
|
376
|
-
logger.error(`Failed moving assets to vault: ${err}`);
|
|
591
|
+
logger.error(`Failed moving assets to extended via vault allocator: ${err}`);
|
|
592
|
+
return [this.createTransactionResult([], false, { from: Protocols.VESU.name, to: Protocols.EXTENDED.name, amount: vesuAmount.abs() }, "NONE", CycleType.INVESTMENT)];
|
|
377
593
|
}
|
|
378
594
|
}
|
|
379
595
|
|
|
380
|
-
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
596
|
+
// Handle Extended adjustments based on calculated difference
|
|
597
|
+
if (extendedAmountDifferenceAbs.greaterThan(extendedAdapter.minimumExtendedMovementAmount)) {
|
|
598
|
+
if (extendedAmountDifference.greaterThan(0)) {
|
|
599
|
+
try {
|
|
600
|
+
const { calls: extendedCalls, status: extendedStatus, transactionMetadata: extendedTransactionMetadata } = await this.moveAssets(
|
|
601
|
+
{
|
|
602
|
+
to: Protocols.EXTENDED.name,
|
|
603
|
+
from: Protocols.VAULT.name,
|
|
604
|
+
amount: extendedAmountDifference,
|
|
605
|
+
cycleType: CycleType.INVESTMENT
|
|
606
|
+
},
|
|
607
|
+
extendedAdapter,
|
|
608
|
+
vesuAdapter
|
|
609
|
+
);
|
|
610
|
+
if (extendedStatus) {
|
|
611
|
+
transactionResults.push({
|
|
612
|
+
status: extendedStatus,
|
|
613
|
+
calls: extendedCalls,
|
|
614
|
+
transactionMetadata: extendedTransactionMetadata
|
|
615
|
+
})
|
|
616
|
+
} else {
|
|
617
|
+
logger.error(`Failed to move assets to extended - operation returned false status`);
|
|
618
|
+
return [this.createTransactionResult([], false, { from: Protocols.VAULT.name, to: Protocols.EXTENDED.name, amount: extendedAmountDifference }, "NONE", CycleType.INVESTMENT)];
|
|
619
|
+
}
|
|
620
|
+
} catch (err) {
|
|
621
|
+
logger.error(`Failed moving assets to extended: ${err}`);
|
|
622
|
+
return [this.createTransactionResult([], false, { from: Protocols.VAULT.name, to: Protocols.EXTENDED.name, amount: extendedAmountDifference }, "NONE", CycleType.INVESTMENT)];
|
|
623
|
+
}
|
|
624
|
+
} else if (extendedAmountDifference.lessThan(0)) {
|
|
625
|
+
try {
|
|
626
|
+
const { calls: extendedCalls, status: extendedStatus, transactionMetadata: extendedTransactionMetadata } = await this.moveAssets(
|
|
627
|
+
{
|
|
628
|
+
to: Protocols.VAULT.name,
|
|
629
|
+
from: Protocols.EXTENDED.name,
|
|
630
|
+
amount: extendedAmountDifferenceAbs,
|
|
631
|
+
cycleType: CycleType.INVESTMENT
|
|
632
|
+
},
|
|
633
|
+
extendedAdapter,
|
|
634
|
+
vesuAdapter
|
|
635
|
+
);
|
|
636
|
+
if (extendedStatus) {
|
|
637
|
+
transactionResults.push({
|
|
638
|
+
status: extendedStatus,
|
|
639
|
+
calls: extendedCalls,
|
|
640
|
+
transactionMetadata: {
|
|
641
|
+
...extendedTransactionMetadata,
|
|
642
|
+
transactionType: "DEPOSIT",
|
|
643
|
+
}
|
|
644
|
+
})
|
|
645
|
+
} else {
|
|
646
|
+
logger.error(`Failed to withdraw from extended - operation returned false status`);
|
|
647
|
+
return [this.createTransactionResult([], false, { from: Protocols.EXTENDED.name, to: Protocols.VAULT.name, amount: extendedAmountDifferenceAbs }, "NONE", CycleType.INVESTMENT)];
|
|
648
|
+
}
|
|
649
|
+
} catch (err) {
|
|
650
|
+
logger.error(`Failed moving assets from extended to vault: ${err}`);
|
|
651
|
+
return [this.createTransactionResult([], false, { from: Protocols.EXTENDED.name, to: Protocols.VAULT.name, amount: extendedAmountDifferenceAbs }, "NONE", CycleType.INVESTMENT)];
|
|
652
|
+
}
|
|
404
653
|
}
|
|
405
654
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
from: Protocols.EXTENDED.name,
|
|
413
|
-
amount: vesuAmount.minus(usdcAmountInWallet),
|
|
414
|
-
},
|
|
415
|
-
extendedAdapter,
|
|
416
|
-
vesuAdapter
|
|
655
|
+
|
|
656
|
+
// Handle Vesu adjustments based on calculated difference (already adjusted for Extended movements)
|
|
657
|
+
if (vesuAmountDifferenceAbs.greaterThan(vesuAdapter.minimumVesuMovementAmount)) {
|
|
658
|
+
if (vesuAmountDifference.lessThanOrEqualTo(0)) {
|
|
659
|
+
logger.warn(
|
|
660
|
+
`Vesu amount difference is negative or zero: ${vesuAmountDifference.toNumber()}. Skipping operation.`
|
|
417
661
|
);
|
|
418
|
-
|
|
419
|
-
|
|
662
|
+
} else {
|
|
663
|
+
// Move assets from Extended to Vault (which will then go to Vesu)
|
|
664
|
+
try {
|
|
665
|
+
const { calls: vesuCalls, status: vesuStatus, transactionMetadata: vesuTransactionMetadata } = await this.moveAssets(
|
|
666
|
+
{
|
|
667
|
+
to: Protocols.VAULT.name,
|
|
668
|
+
from: Protocols.EXTENDED.name,
|
|
669
|
+
amount: vesuAmountDifference,
|
|
670
|
+
cycleType: CycleType.INVESTMENT
|
|
671
|
+
},
|
|
672
|
+
extendedAdapter,
|
|
673
|
+
vesuAdapter
|
|
674
|
+
);
|
|
675
|
+
if (!vesuStatus) {
|
|
676
|
+
logger.error(`Failed to move assets to vesu - operation returned false status`);
|
|
677
|
+
return [this.createTransactionResult([], false, { from: Protocols.EXTENDED.name, to: Protocols.VAULT.name, amount: vesuAmountDifference }, "NONE", CycleType.INVESTMENT)];
|
|
678
|
+
}
|
|
679
|
+
transactionResults.push({
|
|
680
|
+
status: vesuStatus,
|
|
681
|
+
calls: vesuCalls,
|
|
682
|
+
transactionMetadata: {
|
|
683
|
+
...vesuTransactionMetadata,
|
|
684
|
+
transactionType: "DEPOSIT",
|
|
685
|
+
}
|
|
686
|
+
})
|
|
687
|
+
} catch (err) {
|
|
688
|
+
logger.error(`Failed moving assets to vault: ${err}`);
|
|
689
|
+
return [this.createTransactionResult([], false, { from: Protocols.EXTENDED.name, to: Protocols.VAULT.name, amount: vesuAmountDifference }, "NONE", CycleType.INVESTMENT)];
|
|
420
690
|
}
|
|
421
|
-
calls.push(...vesuCalls);
|
|
422
|
-
} catch (err) {
|
|
423
|
-
logger.error(`Failed moving assets to vault: ${err}`);
|
|
424
691
|
}
|
|
425
692
|
}
|
|
426
|
-
|
|
693
|
+
|
|
694
|
+
return transactionResults;
|
|
427
695
|
} catch (err) {
|
|
428
696
|
logger.error(`Failed moving assets to vesu: ${err}`);
|
|
429
|
-
return [];
|
|
697
|
+
return [this.createTransactionResult([], false, { from: Protocols.EXTENDED.name, to: Protocols.VAULT.name, amount: new Web3Number(0, USDC_TOKEN_DECIMALS) }, "NONE", CycleType.INVESTMENT)];
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
/**
|
|
702
|
+
* Helper method to create transaction result with metadata
|
|
703
|
+
*/
|
|
704
|
+
private createTransactionResult(
|
|
705
|
+
calls: Call[],
|
|
706
|
+
status: boolean,
|
|
707
|
+
params: { from: string; to: string; amount: Web3Number },
|
|
708
|
+
transactionType: 'DEPOSIT' | 'WITHDRAWAL' | 'NONE',
|
|
709
|
+
cycleType: CycleType
|
|
710
|
+
): TransactionResult {
|
|
711
|
+
if (status) {
|
|
712
|
+
return {
|
|
713
|
+
calls,
|
|
714
|
+
status: status,
|
|
715
|
+
transactionMetadata: {
|
|
716
|
+
protocolFrom: params.from,
|
|
717
|
+
protocolTo: params.to,
|
|
718
|
+
transactionType: transactionType,
|
|
719
|
+
usdAmount: params.amount.abs().toFixed(),
|
|
720
|
+
status: 'PENDING',
|
|
721
|
+
cycleType: cycleType
|
|
722
|
+
}
|
|
723
|
+
};
|
|
430
724
|
}
|
|
725
|
+
return { calls: [], status: false, transactionMetadata: { protocolFrom: '', protocolTo: '', transactionType: 'DEPOSIT', usdAmount: '0', status: 'FAILED', cycleType: cycleType } };
|
|
431
726
|
}
|
|
432
727
|
|
|
433
728
|
async moveAssets(
|
|
@@ -435,21 +730,43 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
435
730
|
amount: Web3Number;
|
|
436
731
|
from: string;
|
|
437
732
|
to: string;
|
|
733
|
+
cycleType: CycleType;
|
|
438
734
|
},
|
|
439
735
|
extendedAdapter: ExtendedAdapter,
|
|
440
|
-
vesuAdapter: VesuMultiplyAdapter
|
|
441
|
-
): Promise<{
|
|
442
|
-
calls: Call[];
|
|
443
|
-
status: boolean;
|
|
444
|
-
}> {
|
|
736
|
+
vesuAdapter: VesuMultiplyAdapter,
|
|
737
|
+
): Promise<TransactionResult> {
|
|
445
738
|
try {
|
|
739
|
+
// Validate amount is positive before starting operations
|
|
740
|
+
if (params.amount.lessThanOrEqualTo(0)) {
|
|
741
|
+
logger.error(
|
|
742
|
+
`Invalid amount for moveAssets: ${params.amount.toNumber()}. Amount must be positive.`
|
|
743
|
+
);
|
|
744
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Check minimum movement amounts before starting operations
|
|
748
|
+
const amountAbs = params.amount.abs();
|
|
749
|
+
if (params.from === Protocols.EXTENDED.name || params.to === Protocols.EXTENDED.name) {
|
|
750
|
+
if (amountAbs.lessThanOrEqualTo(extendedAdapter.minimumExtendedMovementAmount)) {
|
|
751
|
+
logger.warn(
|
|
752
|
+
`Amount ${amountAbs.toNumber()} is below minimum Extended movement amount ${extendedAdapter.minimumExtendedMovementAmount}. Skipping operation.`
|
|
753
|
+
);
|
|
754
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
if (params.from === Protocols.VESU.name || params.to === Protocols.VESU.name) {
|
|
758
|
+
if (amountAbs.lessThanOrEqualTo(vesuAdapter.minimumVesuMovementAmount)) {
|
|
759
|
+
logger.warn(
|
|
760
|
+
`Amount ${amountAbs.toNumber()} is below minimum Vesu movement amount ${vesuAdapter.minimumVesuMovementAmount}. Skipping operation.`
|
|
761
|
+
);
|
|
762
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
|
|
446
766
|
const avnuAdapter = await this.getAvnuAdapter();
|
|
447
767
|
if (!avnuAdapter) {
|
|
448
768
|
logger.error(`avnu adapter not found: ${avnuAdapter}`);
|
|
449
|
-
return
|
|
450
|
-
calls: [],
|
|
451
|
-
status: false
|
|
452
|
-
};
|
|
769
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
453
770
|
}
|
|
454
771
|
logger.info(`moveAssets params, ${JSON.stringify(params)}`);
|
|
455
772
|
const collateralToken = vesuAdapter.config.supportedPositions[0].asset;
|
|
@@ -469,67 +786,84 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
469
786
|
await proofsInfo.callConstructor({ amount: params.amount })
|
|
470
787
|
);
|
|
471
788
|
calls.push(call);
|
|
472
|
-
return
|
|
473
|
-
calls: [call],
|
|
474
|
-
status: true
|
|
475
|
-
};
|
|
789
|
+
return this.createTransactionResult(calls, true, params, "DEPOSIT", params.cycleType);
|
|
476
790
|
} else if (params.to === Protocols.VAULT.name && params.from === Protocols.EXTENDED.name) {
|
|
477
791
|
const extendedLeverage = calculateExtendedLevergae();
|
|
478
792
|
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
479
793
|
if (!extendedHoldings) {
|
|
480
794
|
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
481
|
-
return
|
|
482
|
-
calls: [],
|
|
483
|
-
status: false
|
|
484
|
-
};
|
|
795
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
485
796
|
}
|
|
486
797
|
const extendedHoldingAmount = new Web3Number(
|
|
487
798
|
extendedHoldings.availableForWithdrawal,
|
|
488
799
|
USDC_TOKEN_DECIMALS
|
|
489
800
|
);
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
btcAmount.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
801
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::moveAssets extendedHoldingAmount: ${extendedHoldingAmount.toNumber()}`);
|
|
802
|
+
if (params.amount.abs().greaterThan(extendedHoldingAmount)) {
|
|
803
|
+
const leftAmountAfterWithdrawalAmountInAccount = params.amount.abs().minus(extendedHoldingAmount);
|
|
804
|
+
logger.info(`${VesuExtendedMultiplierStrategy.name}::moveAssets leftAmountAfterWithdrawalAmountInAccount: ${leftAmountAfterWithdrawalAmountInAccount.toNumber()}`);
|
|
805
|
+
const btcAmount = leftAmountAfterWithdrawalAmountInAccount.dividedBy(collateralPrice.price);
|
|
806
|
+
const openLongPosition = btcAmount.multipliedBy(3).greaterThan(MINIMUM_EXTENDED_POSITION_SIZE) ? await extendedAdapter.createOrder(
|
|
807
|
+
extendedLeverage.toString(),
|
|
808
|
+
btcAmount.toNumber(),
|
|
809
|
+
OrderSide.BUY
|
|
810
|
+
) : await extendedAdapter.createOrder(
|
|
811
|
+
extendedLeverage.toString(),
|
|
812
|
+
0.000034, // just in case amount falls short then we need to create a withdrawal
|
|
813
|
+
OrderSide.BUY
|
|
814
|
+
)
|
|
815
|
+
if (!openLongPosition) {
|
|
816
|
+
logger.error(`error opening long position: ${openLongPosition}`);
|
|
817
|
+
}
|
|
818
|
+
const updatedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
819
|
+
if (!updatedHoldings || new Web3Number(updatedHoldings.availableForWithdrawal, USDC_TOKEN_DECIMALS).lessThan(params.amount.abs())) {
|
|
820
|
+
logger.error(`Insufficient balance after opening position. Available: ${updatedHoldings?.availableForWithdrawal}, Needed: ${params.amount.abs()}`);
|
|
821
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
822
|
+
}
|
|
504
823
|
}
|
|
505
|
-
|
|
506
|
-
|
|
824
|
+
const {
|
|
825
|
+
status: withdrawalFromExtendedStatus,
|
|
826
|
+
receivedTxnHash: withdrawalFromExtendedTxnHash,
|
|
827
|
+
} =
|
|
507
828
|
await extendedAdapter.withdrawFromExtended(params.amount);
|
|
508
|
-
|
|
829
|
+
/**
|
|
830
|
+
* This logic needs fixing
|
|
831
|
+
*/
|
|
832
|
+
logger.info(`withdrawalFromExtendedStatus: ${withdrawalFromExtendedStatus}, withdrawalFromExtendedTxnHash: ${withdrawalFromExtendedTxnHash}`);
|
|
833
|
+
if (withdrawalFromExtendedStatus && withdrawalFromExtendedTxnHash) {
|
|
509
834
|
/**
|
|
510
835
|
* We need to move assets from my wallet back to vault contract
|
|
511
836
|
*/
|
|
512
837
|
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
513
|
-
logger.info(`extendedHoldings after withdrawal ${extendedHoldings}`);
|
|
514
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
515
|
-
const calls = await this.moveAssetsToVaultAllocator(params.amount, extendedAdapter);
|
|
516
|
-
if (calls.length > 0) {
|
|
517
|
-
return
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
838
|
+
logger.info(`extendedHoldings after withdrawal ${extendedHoldings?.availableForWithdrawal}`);
|
|
839
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
840
|
+
const { calls, status } = await this.moveAssetsToVaultAllocator(params.amount, extendedAdapter);
|
|
841
|
+
if (calls.length > 0 && status) {
|
|
842
|
+
return this.createTransactionResult(calls, true, params, "WITHDRAWAL", params.cycleType);
|
|
843
|
+
} else {
|
|
844
|
+
/**
|
|
845
|
+
* This is a fallback scenario, where the funds were withdrawn from extended, but didn't get transferred to the wallet
|
|
846
|
+
* We need to return a successful transaction result, but with no calls
|
|
847
|
+
* Db update will be handled by the risk engine for this specific case
|
|
848
|
+
*/
|
|
849
|
+
return this.createTransactionResult([], true, params, "WITHDRAWAL", params.cycleType);
|
|
521
850
|
}
|
|
851
|
+
} else if (withdrawalFromExtendedStatus && !withdrawalFromExtendedTxnHash) {
|
|
852
|
+
logger.error("withdrawal from extended successful, but funds didn't get transferred to the wallet");
|
|
853
|
+
return this.createTransactionResult([], true, params, "WITHDRAWAL", params.cycleType);
|
|
522
854
|
} else {
|
|
523
855
|
logger.error("withdrawal from extended failed");
|
|
524
|
-
return
|
|
525
|
-
calls: [],
|
|
526
|
-
status: false
|
|
527
|
-
};
|
|
856
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
528
857
|
}
|
|
529
858
|
} else if (params.to === Protocols.VAULT.name && params.from === Protocols.VESU.name) {
|
|
859
|
+
const isPriceDifferenceBetweenAvnuAndExtended = await this.checkPriceDifferenceBetweenAvnuAndExtended(extendedAdapter, vesuAdapter, avnuAdapter, PositionTypeAvnuExtended.CLOSE);
|
|
860
|
+
if (!isPriceDifferenceBetweenAvnuAndExtended) {
|
|
861
|
+
logger.warn(`price difference between avnu and extended doesn't fit the range for close position, ${avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing}`);
|
|
862
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
863
|
+
}
|
|
530
864
|
//withdraw from vesu
|
|
531
865
|
const vesuAmountInBTC = new Web3Number(
|
|
532
|
-
params.amount.dividedBy(collateralPrice.price).
|
|
866
|
+
params.amount.dividedBy(collateralPrice.price).toFixed(WBTC_TOKEN_DECIMALS),
|
|
533
867
|
collateralToken.decimals
|
|
534
868
|
);
|
|
535
869
|
const proofsInfo = vesuAdapter.getProofs(false, this.getMerkleTree());
|
|
@@ -547,11 +881,13 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
547
881
|
await swapProofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
548
882
|
);
|
|
549
883
|
calls.push(swapCall);
|
|
550
|
-
return
|
|
551
|
-
calls: calls,
|
|
552
|
-
status: true
|
|
553
|
-
};
|
|
884
|
+
return this.createTransactionResult(calls, true, params, "WITHDRAWAL", params.cycleType);
|
|
554
885
|
} else if (params.to === Protocols.EXTENDED.name && params.from === Protocols.VESU.name) {
|
|
886
|
+
const isPriceDifferenceBetweenAvnuAndExtended = await this.checkPriceDifferenceBetweenAvnuAndExtended(extendedAdapter, vesuAdapter, avnuAdapter, PositionTypeAvnuExtended.CLOSE);
|
|
887
|
+
if (!isPriceDifferenceBetweenAvnuAndExtended) {
|
|
888
|
+
logger.warn(`price difference between avnu and extended doesn't fit the range for close position, ${avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing}`);
|
|
889
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
890
|
+
}
|
|
555
891
|
const vesuAmountInBTC = new Web3Number(
|
|
556
892
|
params.amount.dividedBy(collateralPrice.price).toNumber(),
|
|
557
893
|
collateralToken.decimals
|
|
@@ -582,163 +918,77 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
582
918
|
await proofsInfoDeposit.callConstructor({ amount: params.amount })
|
|
583
919
|
);
|
|
584
920
|
calls.push(callDeposit);
|
|
585
|
-
return
|
|
586
|
-
calls: calls,
|
|
587
|
-
status: true
|
|
588
|
-
};
|
|
921
|
+
return this.createTransactionResult(calls, true, params, "DEPOSIT", params.cycleType);
|
|
589
922
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
status: false
|
|
593
|
-
};
|
|
923
|
+
logger.error(`Unsupported assets movement: ${params.from} to ${params.to}`);
|
|
924
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
594
925
|
} catch (err) {
|
|
595
926
|
logger.error(`error moving assets: ${err}`);
|
|
596
|
-
return
|
|
597
|
-
calls: [],
|
|
598
|
-
status: false
|
|
599
|
-
};
|
|
927
|
+
return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
600
928
|
}
|
|
601
929
|
}
|
|
602
930
|
|
|
603
|
-
async handleDeposit(): Promise<{
|
|
604
|
-
extendedAmountInBTC: Web3Number,
|
|
605
|
-
calls: Call[]
|
|
606
|
-
}> {
|
|
931
|
+
async handleDeposit(): Promise<TransactionResult> {
|
|
607
932
|
try {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
!vesuAdapter ||
|
|
613
|
-
!extendedAdapter ||
|
|
614
|
-
!extendedAdapter.client ||
|
|
615
|
-
!avnuAdapter
|
|
616
|
-
) {
|
|
617
|
-
logger.error(
|
|
618
|
-
"vesu or extended adapter not found",
|
|
619
|
-
vesuAdapter,
|
|
620
|
-
extendedAdapter
|
|
621
|
-
);
|
|
622
|
-
return {
|
|
623
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
624
|
-
calls: [],
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
const extendedLeverage = calculateExtendedLevergae();
|
|
628
|
-
const isPriceDifferenceBetweenAvnuAndExtended = await this.checkPriceDifferenceBetweenAvnuAndExtended(extendedAdapter, vesuAdapter, avnuAdapter);
|
|
629
|
-
if (!isPriceDifferenceBetweenAvnuAndExtended) {
|
|
630
|
-
logger.error("price difference between avnu and extended doesn't fit the range");
|
|
631
|
-
return {
|
|
632
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
633
|
-
calls: [],
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
const position = await extendedAdapter.getAllOpenPositions();
|
|
637
|
-
if (!position) {
|
|
638
|
-
logger.error("error getting extended position", position);
|
|
639
|
-
return {
|
|
640
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
641
|
-
calls: [],
|
|
642
|
-
};
|
|
643
|
-
}
|
|
644
|
-
const extendedPositionValue = position.length > 0 ? parseFloat(position[0].value) : 0;
|
|
645
|
-
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
646
|
-
if (!extendedHoldings) {
|
|
647
|
-
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
648
|
-
return {
|
|
649
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
650
|
-
calls: [],
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
const extendedHoldingAmount = new Web3Number(
|
|
654
|
-
extendedHoldings.availableForWithdrawal,
|
|
655
|
-
USDC_TOKEN_DECIMALS
|
|
656
|
-
);
|
|
657
|
-
const {
|
|
658
|
-
collateralTokenAmount,
|
|
659
|
-
} = await vesuAdapter.vesuAdapter.getAssetPrices();
|
|
660
|
-
const { collateralPrice } = await this.getAssetPrices();
|
|
661
|
-
const { vesuAmountInBTC, extendedAmountInBTC } = calculateVesUPositionSizeGivenExtended(
|
|
662
|
-
extendedPositionValue,
|
|
663
|
-
extendedHoldingAmount,
|
|
664
|
-
collateralTokenAmount,
|
|
665
|
-
collateralPrice.price
|
|
666
|
-
);
|
|
667
|
-
logger.info(`vesuAmountInBTC ${vesuAmountInBTC}, extendedAmountInBTC ${extendedAmountInBTC}`);
|
|
668
|
-
|
|
669
|
-
let calls: Call[] = [];
|
|
670
|
-
if (vesuAmountInBTC.greaterThan(MINIMUM_EXTENDED_POSITION_SIZE)) {
|
|
671
|
-
const proofsInfo = vesuAdapter.getProofs(true, this.getMerkleTree());
|
|
672
|
-
const proofGroups = proofsInfo.proofs;
|
|
673
|
-
const call = this.getManageCall(
|
|
674
|
-
proofGroups,
|
|
675
|
-
await proofsInfo.callConstructor({
|
|
676
|
-
amount: vesuAmountInBTC,
|
|
677
|
-
})
|
|
678
|
-
);
|
|
679
|
-
const { amount: wbtcAmountInVaultAllocator } = await this.getUnusedBalanceWBTC();
|
|
680
|
-
if (wbtcAmountInVaultAllocator.lessThan(vesuAmountInBTC)) {
|
|
681
|
-
logger.info(`WBTC amount in vault allocator is less than vesu amount required in WBTC thus swapping, wbtcAmountInVaultAllocator: ${wbtcAmountInVaultAllocator}, vesuAmountInBTC: ${vesuAmountInBTC}`);
|
|
682
|
-
const swapProofsInfo = avnuAdapter.getProofs(true, this.getMerkleTree());
|
|
683
|
-
const swapProofGroups = swapProofsInfo.proofs;
|
|
684
|
-
const swapCall = this.getManageCall(
|
|
685
|
-
swapProofGroups,
|
|
686
|
-
await swapProofsInfo.callConstructor({
|
|
687
|
-
amount: vesuAmountInBTC,
|
|
688
|
-
})
|
|
689
|
-
);
|
|
690
|
-
calls.push(swapCall);
|
|
691
|
-
}
|
|
692
|
-
calls.push(call);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
const shortPosition = extendedAmountInBTC.multipliedBy(3).abs().greaterThan(MINIMUM_EXTENDED_POSITION_SIZE) ? await extendedAdapter.createOrder(
|
|
696
|
-
extendedLeverage.toString(),
|
|
697
|
-
extendedAmountInBTC.toNumber(),
|
|
698
|
-
OrderSide.SELL
|
|
699
|
-
) : null;
|
|
700
|
-
if (!shortPosition && extendedAmountInBTC.multipliedBy(3).abs().greaterThan(MINIMUM_EXTENDED_POSITION_SIZE)) {
|
|
701
|
-
logger.error(`error creating short position thus no position to be opened on vesu: ${shortPosition}`);
|
|
702
|
-
return {
|
|
703
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
704
|
-
calls: [],
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
return {
|
|
708
|
-
extendedAmountInBTC: extendedAmountInBTC,
|
|
709
|
-
calls: calls,
|
|
710
|
-
};
|
|
933
|
+
/**
|
|
934
|
+
* Just a demo function, not used in the risk engine
|
|
935
|
+
*/
|
|
936
|
+
return this.createTransactionResult([], false, { from: Protocols.VAULT.name, to: Protocols.VAULT.name, amount: new Web3Number(0, 0) }, "NONE", CycleType.INVESTMENT);
|
|
711
937
|
} catch (err) {
|
|
712
938
|
logger.error(`error handling deposit: ${err}`);
|
|
713
|
-
return {
|
|
714
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
715
|
-
calls: [],
|
|
716
|
-
};;
|
|
939
|
+
return this.createTransactionResult([], false, { from: Protocols.VAULT.name, to: Protocols.VAULT.name, amount: new Web3Number(0, 0) }, "NONE", CycleType.INVESTMENT);
|
|
717
940
|
}
|
|
718
941
|
}
|
|
719
942
|
|
|
720
|
-
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Check if the price difference between avnu and extended is within the acceptable range to enhance the position size or close the position
|
|
946
|
+
* @param extendedAdapter - the extended adapter
|
|
947
|
+
* @param vesuAdapter - the vesu adapter
|
|
948
|
+
* @param avnuAdapter - the avnu adapter
|
|
949
|
+
* @param positionType - the position type (open or close)
|
|
950
|
+
* @returns true if the price difference is within the acceptable range, false otherwise
|
|
951
|
+
*/
|
|
952
|
+
async checkPriceDifferenceBetweenAvnuAndExtended(extendedAdapter: ExtendedAdapter, vesuAdapter: VesuMultiplyAdapter, avnuAdapter: AvnuAdapter, positionType: PositionTypeAvnuExtended): Promise<boolean> {
|
|
721
953
|
const {
|
|
722
954
|
ask, bid
|
|
723
955
|
} = await extendedAdapter.fetchOrderBookBTCUSDC();
|
|
724
956
|
const price = ask.plus(bid).dividedBy(2);
|
|
725
957
|
const btcToken = vesuAdapter.config.supportedPositions[0].asset;
|
|
726
958
|
const btcPriceAvnu = await avnuAdapter.getPriceOfToken(btcToken.address.toString());
|
|
959
|
+
|
|
727
960
|
if (!btcPriceAvnu) {
|
|
728
961
|
logger.error(`error getting btc price avnu: ${btcPriceAvnu}`);
|
|
729
962
|
return false;
|
|
730
963
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
964
|
+
logger.info(`price: ${price}`);
|
|
965
|
+
logger.info(`btcPriceAvnu: ${btcPriceAvnu}`);
|
|
966
|
+
const priceDifference = new Web3Number(price.minus(btcPriceAvnu).toFixed(2), 0);
|
|
967
|
+
logger.info(`priceDifference: ${priceDifference}`);
|
|
968
|
+
if (priceDifference.isNegative()) {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
if (positionType === PositionTypeAvnuExtended.OPEN) {
|
|
972
|
+
logger.info(`price difference between avnu and extended for open position: ${priceDifference.toNumber()}, minimumExtendedPriceDifferenceForSwapOpen: ${avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen}`);
|
|
973
|
+
const result = priceDifference.greaterThanOrEqualTo(avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen); // 500 for now
|
|
974
|
+
logger.info(`result: ${result}`);
|
|
975
|
+
return result;
|
|
976
|
+
} else {
|
|
977
|
+
logger.info(`price difference between avnu and extended for close position: ${priceDifference.toNumber()}, maximumExtendedPriceDifferenceForSwapClosing: ${avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing}`);
|
|
978
|
+
const result = priceDifference.lessThanOrEqualTo(avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing); // 1000 for now
|
|
979
|
+
logger.info(`result: ${result}`);
|
|
980
|
+
return result;
|
|
734
981
|
}
|
|
735
|
-
logger.error(`price difference between avnu and extended doesn't fit the range, priceDifference: ${priceDifference}`);
|
|
736
|
-
return false;
|
|
737
982
|
}
|
|
738
983
|
|
|
739
|
-
|
|
984
|
+
/**
|
|
985
|
+
* Handle the withdrawal of assets from the vault
|
|
986
|
+
* @param amount - the amount to withdraw in USDC
|
|
987
|
+
* @returns the calls to be executed and the status of the calls generated along with the metadata for the calls
|
|
988
|
+
*/
|
|
989
|
+
async handleWithdraw(amount: Web3Number): Promise<TransactionResult[]> {
|
|
740
990
|
try {
|
|
741
|
-
const usdcBalanceVaultAllocator = await this.getUnusedBalance()
|
|
991
|
+
const usdcBalanceVaultAllocator = await this.getUnusedBalance();
|
|
742
992
|
const usdcBalanceDifference = amount.plus(BUFFER_USDC_IN_WITHDRAWAL).minus(usdcBalanceVaultAllocator.usdValue);
|
|
743
993
|
logger.info(`usdcBalanceDifference, ${usdcBalanceDifference.toNumber()}`);
|
|
744
994
|
let calls: Call[] = [];
|
|
@@ -747,12 +997,8 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
747
997
|
const withdrawCall = await this.getBringLiquidityCall({
|
|
748
998
|
amount: usdcBalanceVaultAllocator.amount
|
|
749
999
|
})
|
|
750
|
-
logger.info("withdraw call", withdrawCall);
|
|
751
1000
|
calls.push(withdrawCall);
|
|
752
|
-
return {
|
|
753
|
-
calls:calls,
|
|
754
|
-
status:true
|
|
755
|
-
};
|
|
1001
|
+
return [this.createTransactionResult(calls, true, { from: Protocols.VAULT.name, to: Protocols.NONE.name, amount: amount }, "WITHDRAWAL", CycleType.WITHDRAWAL)];
|
|
756
1002
|
}
|
|
757
1003
|
const vesuAdapter = await this.getVesuAdapter();
|
|
758
1004
|
const extendedAdapter = await this.getExtendedAdapter();
|
|
@@ -761,24 +1007,19 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
761
1007
|
logger.error(
|
|
762
1008
|
`vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
|
|
763
1009
|
);
|
|
764
|
-
return {
|
|
765
|
-
calls:calls,
|
|
766
|
-
status:status
|
|
767
|
-
};
|
|
1010
|
+
return [this.createTransactionResult(calls, status, { from: Protocols.VAULT.name, to: Protocols.NONE.name, amount: amount }, "NONE", CycleType.WITHDRAWAL)];
|
|
768
1011
|
}
|
|
1012
|
+
let transactionResults: TransactionResult[] = [];
|
|
769
1013
|
const { collateralTokenAmount } =
|
|
770
1014
|
await vesuAdapter.vesuAdapter.getAssetPrices();
|
|
771
1015
|
const {
|
|
772
1016
|
collateralPrice
|
|
773
1017
|
} = await this.getAssetPrices();
|
|
774
1018
|
const extendedPositon = await extendedAdapter.getAllOpenPositions();
|
|
775
|
-
if(!extendedPositon) {
|
|
1019
|
+
if (!extendedPositon) {
|
|
776
1020
|
status = false;
|
|
777
1021
|
logger.error("error getting extended position", extendedPositon);
|
|
778
|
-
return {
|
|
779
|
-
calls:calls,
|
|
780
|
-
status:status
|
|
781
|
-
}
|
|
1022
|
+
return [this.createTransactionResult(calls, status, { from: Protocols.VAULT.name, to: Protocols.NONE.name, amount: amount }, "NONE", CycleType.WITHDRAWAL)];
|
|
782
1023
|
}
|
|
783
1024
|
const amountDistributionForWithdrawal =
|
|
784
1025
|
await calculateAmountDistributionForWithdrawal(
|
|
@@ -792,62 +1033,71 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
792
1033
|
logger.error(
|
|
793
1034
|
`error calculating amount distribution for withdrawal: ${amountDistributionForWithdrawal}`
|
|
794
1035
|
);
|
|
795
|
-
return {
|
|
796
|
-
calls:calls,
|
|
797
|
-
status:status
|
|
798
|
-
};
|
|
1036
|
+
return [this.createTransactionResult(calls, status, { from: Protocols.VAULT.name, to: Protocols.NONE.name, amount: amount }, "NONE", CycleType.WITHDRAWAL)];
|
|
799
1037
|
}
|
|
800
1038
|
const { vesu_amount, extended_amount } = amountDistributionForWithdrawal;
|
|
801
|
-
|
|
1039
|
+
|
|
802
1040
|
if (status && vesu_amount.greaterThan(0)) {
|
|
803
|
-
const {calls: vesuCalls, status: vesuStatus} = await this.moveAssets(
|
|
1041
|
+
const { calls: vesuCalls, status: vesuStatus, transactionMetadata: vesuTransactionMetadata } = await this.moveAssets(
|
|
804
1042
|
{
|
|
805
1043
|
amount: vesu_amount,
|
|
806
1044
|
from: Protocols.VESU.name,
|
|
807
1045
|
to: Protocols.VAULT.name,
|
|
1046
|
+
cycleType: CycleType.WITHDRAWAL
|
|
808
1047
|
},
|
|
809
1048
|
extendedAdapter,
|
|
810
1049
|
vesuAdapter
|
|
811
1050
|
);
|
|
812
1051
|
status = vesuStatus;
|
|
813
|
-
|
|
1052
|
+
transactionResults.push({
|
|
1053
|
+
status: vesuStatus,
|
|
1054
|
+
calls: vesuCalls,
|
|
1055
|
+
transactionMetadata: vesuTransactionMetadata
|
|
1056
|
+
})
|
|
814
1057
|
}
|
|
815
1058
|
if (status && extended_amount.greaterThan(0)) {
|
|
816
|
-
const {calls: extendedCalls, status: extendedStatus} = await this.moveAssets(
|
|
1059
|
+
const { calls: extendedCalls, status: extendedStatus, transactionMetadata: extendedTransactionMetadata } = await this.moveAssets(
|
|
817
1060
|
{
|
|
818
1061
|
amount: extended_amount,
|
|
819
1062
|
from: Protocols.EXTENDED.name,
|
|
820
1063
|
to: Protocols.VAULT.name,
|
|
1064
|
+
cycleType: CycleType.WITHDRAWAL
|
|
821
1065
|
},
|
|
822
1066
|
extendedAdapter,
|
|
823
1067
|
vesuAdapter
|
|
824
1068
|
);
|
|
825
1069
|
status = extendedStatus;
|
|
826
|
-
if(status) {
|
|
827
|
-
|
|
828
|
-
|
|
1070
|
+
if (status) {
|
|
1071
|
+
transactionResults.push({
|
|
1072
|
+
status: extendedStatus,
|
|
1073
|
+
calls: extendedCalls,
|
|
1074
|
+
transactionMetadata: extendedTransactionMetadata
|
|
1075
|
+
})
|
|
1076
|
+
} else {
|
|
829
1077
|
logger.error("error moving assets to vault: extendedStatus: ${extendedStatus}");
|
|
830
|
-
return {
|
|
831
|
-
calls: [],
|
|
832
|
-
status: status
|
|
833
|
-
};
|
|
1078
|
+
return [this.createTransactionResult([], status, { from: Protocols.VAULT.name, to: Protocols.NONE.name, amount: amount }, "NONE", CycleType.WITHDRAWAL)];
|
|
834
1079
|
}
|
|
835
1080
|
}
|
|
836
1081
|
const withdrawCall = await this.getBringLiquidityCall({
|
|
837
1082
|
amount: amount
|
|
838
1083
|
})
|
|
839
1084
|
logger.info("withdraw call", withdrawCall);
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
calls:
|
|
843
|
-
|
|
844
|
-
|
|
1085
|
+
transactionResults.push({
|
|
1086
|
+
status: status,
|
|
1087
|
+
calls: [withdrawCall],
|
|
1088
|
+
transactionMetadata: {
|
|
1089
|
+
protocolFrom: Protocols.VAULT.name,
|
|
1090
|
+
protocolTo: Protocols.NONE.name,
|
|
1091
|
+
transactionType: "WITHDRAWAL",
|
|
1092
|
+
usdAmount: amount.toFixed(),
|
|
1093
|
+
status: 'PENDING',
|
|
1094
|
+
cycleType: CycleType.WITHDRAWAL
|
|
1095
|
+
}
|
|
1096
|
+
})
|
|
1097
|
+
return transactionResults;
|
|
845
1098
|
} catch (err) {
|
|
846
1099
|
logger.error(`error handling withdrawal: ${err}`);
|
|
847
|
-
return {
|
|
848
|
-
calls: [],
|
|
849
|
-
status: false
|
|
850
|
-
};
|
|
1100
|
+
return [this.createTransactionResult([], false, { from: Protocols.VAULT.name, to: Protocols.NONE.name, amount: amount }, "NONE", CycleType.WITHDRAWAL)];
|
|
851
1101
|
}
|
|
852
1102
|
}
|
|
853
1103
|
|
|
@@ -896,6 +1146,187 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
896
1146
|
};
|
|
897
1147
|
}
|
|
898
1148
|
|
|
1149
|
+
async processTransactionDataFromSDK(txnData: TransactionResult<any>[]): Promise<{ callsToBeExecutedFinal: Call[], txnMetadata: TransactionMetadata[] } | null> {
|
|
1150
|
+
try {
|
|
1151
|
+
const txnsToBeExecuted = txnData.filter(txn => {
|
|
1152
|
+
return txn.transactionMetadata.transactionType !== 'NONE' && txn.transactionMetadata.protocolFrom !== "" && txn.transactionMetadata.protocolTo !== "";
|
|
1153
|
+
})
|
|
1154
|
+
const callsToBeExecutedFinal = txnsToBeExecuted.flatMap(txn => txn.calls);
|
|
1155
|
+
const txnMetadata = txnsToBeExecuted.map(txn => txn.transactionMetadata);
|
|
1156
|
+
return { callsToBeExecutedFinal, txnMetadata };
|
|
1157
|
+
} catch (err) {
|
|
1158
|
+
logger.error(`error processing transaction data from SDK: ${err}`);
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
async processTransactionMetadata(txnMetadata: TransactionMetadata[], extendedIntentFulfilled: boolean): Promise<TransactionMetadata[] | null> {
|
|
1164
|
+
try {
|
|
1165
|
+
const txnMetadataNew = txnMetadata.map(txn => {
|
|
1166
|
+
const isExtendedProtocol = txn.protocolFrom === Protocols.EXTENDED.name || txn.protocolTo === Protocols.EXTENDED.name;
|
|
1167
|
+
// Only update status for extended protocol transactions since thsoe only cause delays
|
|
1168
|
+
if (isExtendedProtocol) {
|
|
1169
|
+
txn.status = extendedIntentFulfilled ? 'COMPLETED' : 'PENDING';
|
|
1170
|
+
} else {
|
|
1171
|
+
txn.status = 'COMPLETED';
|
|
1172
|
+
}
|
|
1173
|
+
return txn;
|
|
1174
|
+
})
|
|
1175
|
+
return txnMetadataNew;
|
|
1176
|
+
} catch (err) {
|
|
1177
|
+
logger.error(`error processing transaction data from SDK: ${err}`);
|
|
1178
|
+
return null;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
async getMaxBorrowableAmount(): Promise<Web3Number> {
|
|
1183
|
+
const vesuAdapter = await this.getVesuAdapter();
|
|
1184
|
+
const extendedAdapter = await this.getExtendedAdapter();
|
|
1185
|
+
if (!vesuAdapter || !extendedAdapter) {
|
|
1186
|
+
return new Web3Number(0, 0);
|
|
1187
|
+
}
|
|
1188
|
+
const extendedFundingRate = new Web3Number((await extendedAdapter.getNetAPY()).toFixed(4), 0);
|
|
1189
|
+
const extendedPositions = await extendedAdapter.getAllOpenPositions();
|
|
1190
|
+
if (!extendedPositions || extendedPositions.length === 0) {
|
|
1191
|
+
logger.info(`no extended positions found`);
|
|
1192
|
+
return new Web3Number(0, 0);
|
|
1193
|
+
}
|
|
1194
|
+
const extendePositionSizeUSD = new Web3Number(extendedPositions[0].value || 0, 0);
|
|
1195
|
+
const vesuPositions = await vesuAdapter.getPositions();
|
|
1196
|
+
const vesuSupplyApy = vesuPositions[0].apy.apy;
|
|
1197
|
+
const vesuCollateralSizeUSD = new Web3Number(vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
|
|
1198
|
+
const vesuDebtSizeUSD = new Web3Number(vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS)
|
|
1199
|
+
const num1 = extendePositionSizeUSD.multipliedBy(extendedFundingRate);
|
|
1200
|
+
const num2 = vesuCollateralSizeUSD.multipliedBy(vesuSupplyApy);
|
|
1201
|
+
const num3 = vesuDebtSizeUSD.abs()
|
|
1202
|
+
const maxBorrowApy = num1.plus(num2).minus(0.1).dividedBy(num3);
|
|
1203
|
+
const vesuMaxBorrowableAmount = await vesuAdapter.vesuAdapter.getMaxBorrowableByInterestRate(this.config, vesuAdapter.config.debt, maxBorrowApy.toNumber());
|
|
1204
|
+
return new Web3Number(vesuMaxBorrowableAmount.toFixed(USDC_TOKEN_DECIMALS), USDC_TOKEN_DECIMALS);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
async getVesuHealthFactors(): Promise<number[]> {
|
|
1208
|
+
const vesuAdapter = await this.getVesuAdapter();
|
|
1209
|
+
const extendedAdapter = await this.getExtendedAdapter();
|
|
1210
|
+
if (!vesuAdapter || !extendedAdapter) {
|
|
1211
|
+
return [0, 0];
|
|
1212
|
+
}
|
|
1213
|
+
const vesuPositions = await vesuAdapter.getPositions();
|
|
1214
|
+
const vesuCollateralSizeUSD = new Web3Number(vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS), 0);
|
|
1215
|
+
const vesuDebtSizeUSD = new Web3Number(vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS), 0);
|
|
1216
|
+
const actualLtv = vesuDebtSizeUSD.dividedBy(vesuCollateralSizeUSD).abs();
|
|
1217
|
+
logger.info(`actualLtv: ${actualLtv.toNumber()}`);
|
|
1218
|
+
const maxLtv = new Web3Number(await vesuAdapter.vesuAdapter.getLTVConfig(this.config), 4);
|
|
1219
|
+
const healthFactor = new Web3Number(maxLtv.dividedBy(actualLtv).toFixed(4), 4);
|
|
1220
|
+
logger.info(`healthFactor: ${healthFactor.toNumber()}`);
|
|
1221
|
+
const extendedBalance = await extendedAdapter.getExtendedDepositAmount();
|
|
1222
|
+
if (!extendedBalance) {
|
|
1223
|
+
return [0, 0];
|
|
1224
|
+
}
|
|
1225
|
+
const extendedLeverage = new Web3Number((Number(extendedBalance.marginRatio) * 100).toFixed(4), 4);
|
|
1226
|
+
logger.info(`extendedLeverage: ${extendedLeverage.toNumber()}`);
|
|
1227
|
+
return [healthFactor.toNumber(), extendedLeverage.toNumber()];
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
async netAPY(): Promise<{ net: number; splits: { apy: number; id: string; }[]; }> {
|
|
1231
|
+
const allPositions: PositionInfo[] = [];
|
|
1232
|
+
for (let adapter of this.metadata.additionalInfo.adapters) {
|
|
1233
|
+
if(adapter.adapter.name !== ExtendedAdapter.name){
|
|
1234
|
+
let positions = await adapter.adapter.getPositions();
|
|
1235
|
+
if(positions.length > 0){
|
|
1236
|
+
allPositions.push(...positions);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
const extendedAdapter =await this.getExtendedAdapter()
|
|
1241
|
+
if(!extendedAdapter){
|
|
1242
|
+
return {
|
|
1243
|
+
net: 0,
|
|
1244
|
+
splits: []
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
let vesuPositions = allPositions.filter((item) => item.protocol === Protocols.VESU);
|
|
1248
|
+
vesuPositions.map((item) =>{
|
|
1249
|
+
item.apy.apy = item.apy.apy * 0.1;
|
|
1250
|
+
})
|
|
1251
|
+
const extendedPositions = await extendedAdapter.getAllOpenPositions();
|
|
1252
|
+
const usdcToken = Global.getDefaultTokens().find(token => token.symbol === "USDC");
|
|
1253
|
+
if(!extendedPositions || !usdcToken){
|
|
1254
|
+
return {
|
|
1255
|
+
net: 0,
|
|
1256
|
+
splits: []
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
const extendedPosition = extendedPositions[0] || 0;
|
|
1260
|
+
const extendedEquity = (await extendedAdapter.getExtendedDepositAmount())?.equity || 0;
|
|
1261
|
+
const extendedApy = await extendedAdapter.getNetAPY();
|
|
1262
|
+
const totalHoldingsUSDValue = allPositions.reduce((acc, curr) => acc + curr.usdValue, 0) + Number(extendedEquity);
|
|
1263
|
+
console.log(totalHoldingsUSDValue)
|
|
1264
|
+
const extendedPositionSizeMultipliedByApy = Number(extendedPosition.value) * extendedApy;
|
|
1265
|
+
let weightedAPYs = allPositions.reduce((acc, curr) => acc + curr.apy.apy * curr.usdValue, 0) + extendedPositionSizeMultipliedByApy;
|
|
1266
|
+
console.log(weightedAPYs)
|
|
1267
|
+
const netAPY = weightedAPYs / totalHoldingsUSDValue;
|
|
1268
|
+
console.log(netAPY)
|
|
1269
|
+
allPositions.push({
|
|
1270
|
+
tokenInfo: usdcToken,
|
|
1271
|
+
amount: new Web3Number(extendedPosition.size, 0),
|
|
1272
|
+
usdValue: Number(extendedEquity),
|
|
1273
|
+
apy: { apy: extendedApy, type: APYType.BASE },
|
|
1274
|
+
remarks: AUMTypes.FINALISED,
|
|
1275
|
+
protocol: Protocols.EXTENDED
|
|
1276
|
+
})
|
|
1277
|
+
return {
|
|
1278
|
+
net: netAPY,
|
|
1279
|
+
splits: allPositions.map(p => ({apy: p.apy.apy, id: p.remarks ?? ''}))
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
async getWalletHoldings(): Promise<{
|
|
1284
|
+
tokenInfo: TokenInfo,
|
|
1285
|
+
amount: Web3Number,
|
|
1286
|
+
usdValue: number
|
|
1287
|
+
}[]> {
|
|
1288
|
+
const usdceToken = Global.getDefaultTokens().find(token => token.symbol === "USDCe");
|
|
1289
|
+
const wbtcToken = Global.getDefaultTokens().find(token => token.symbol === "WBTC");
|
|
1290
|
+
const usdcToken = Global.getDefaultTokens().find(token => token.symbol === "USDC");
|
|
1291
|
+
if (!usdceToken || !wbtcToken || !usdcToken) {
|
|
1292
|
+
return [];
|
|
1293
|
+
}
|
|
1294
|
+
const walletAddress = this.metadata.additionalInfo.walletAddress;
|
|
1295
|
+
const usdceWalletBalance = await new ERC20(this.config).balanceOf(
|
|
1296
|
+
usdceToken.address,
|
|
1297
|
+
walletAddress,
|
|
1298
|
+
usdceToken.decimals
|
|
1299
|
+
);
|
|
1300
|
+
const usdcWalletBalance = await new ERC20(this.config).balanceOf(
|
|
1301
|
+
usdcToken.address,
|
|
1302
|
+
walletAddress,
|
|
1303
|
+
usdcToken.decimals
|
|
1304
|
+
);
|
|
1305
|
+
const wbtcWalletBalance = await new ERC20(this.config).balanceOf(
|
|
1306
|
+
wbtcToken.address,
|
|
1307
|
+
walletAddress,
|
|
1308
|
+
wbtcToken.decimals
|
|
1309
|
+
);
|
|
1310
|
+
const price = await this.pricer.getPrice(usdceToken.symbol);
|
|
1311
|
+
const wbtcPrice = await this.pricer.getPrice(wbtcToken.symbol);
|
|
1312
|
+
const usdceUsdValue = Number(usdceWalletBalance.toFixed(usdceToken.decimals)) * price.price;
|
|
1313
|
+
const usdcUsdValue = Number(usdcWalletBalance.toFixed(usdcToken.decimals)) * price.price;
|
|
1314
|
+
const wbtcUsdValue = Number(wbtcWalletBalance.toFixed(wbtcToken.decimals)) * wbtcPrice.price;
|
|
1315
|
+
return [{
|
|
1316
|
+
tokenInfo: usdceToken,
|
|
1317
|
+
amount: usdceWalletBalance,
|
|
1318
|
+
usdValue: usdceUsdValue
|
|
1319
|
+
},
|
|
1320
|
+
{
|
|
1321
|
+
tokenInfo: usdcToken,
|
|
1322
|
+
amount: usdcWalletBalance,
|
|
1323
|
+
usdValue: usdcUsdValue
|
|
1324
|
+
}, {
|
|
1325
|
+
tokenInfo: wbtcToken,
|
|
1326
|
+
amount: wbtcWalletBalance,
|
|
1327
|
+
usdValue: wbtcUsdValue
|
|
1328
|
+
}];
|
|
1329
|
+
}
|
|
899
1330
|
}
|
|
900
1331
|
|
|
901
1332
|
function getLooperSettings(
|
|
@@ -903,9 +1334,14 @@ function getLooperSettings(
|
|
|
903
1334
|
underlyingSymbol: string,
|
|
904
1335
|
vaultSettings: VesuExtendedStrategySettings,
|
|
905
1336
|
pool1: ContractAddr,
|
|
906
|
-
|
|
907
|
-
|
|
1337
|
+
extendedBackendReadUrl: string,
|
|
1338
|
+
extendedBackendWriteUrl: string,
|
|
908
1339
|
vaultIdExtended: number,
|
|
1340
|
+
minimumExtendedMovementAmount: number,
|
|
1341
|
+
minimumVesuMovementAmount: number,
|
|
1342
|
+
minimumExtendedRetriesDelayForOrderStatus: number,
|
|
1343
|
+
minimumExtendedPriceDifferenceForSwapOpen: number,
|
|
1344
|
+
maximumExtendedPriceDifferenceForSwapClosing: number,
|
|
909
1345
|
) {
|
|
910
1346
|
vaultSettings.leafAdapters = [];
|
|
911
1347
|
|
|
@@ -934,6 +1370,8 @@ function getLooperSettings(
|
|
|
934
1370
|
avnuContract: AVNU_MIDDLEWARE,
|
|
935
1371
|
slippage: 0.01,
|
|
936
1372
|
baseUrl: AVNU_QUOTE_URL,
|
|
1373
|
+
minimumExtendedPriceDifferenceForSwapOpen: minimumExtendedPriceDifferenceForSwapOpen,
|
|
1374
|
+
maximumExtendedPriceDifferenceForSwapClosing: maximumExtendedPriceDifferenceForSwapClosing,
|
|
937
1375
|
});
|
|
938
1376
|
|
|
939
1377
|
const extendedAdapter = new ExtendedAdapter({
|
|
@@ -943,14 +1381,16 @@ function getLooperSettings(
|
|
|
943
1381
|
],
|
|
944
1382
|
vaultIdExtended: vaultIdExtended,
|
|
945
1383
|
extendedContract: EXTENDED_CONTRACT,
|
|
946
|
-
|
|
947
|
-
|
|
1384
|
+
extendedBackendWriteUrl: extendedBackendWriteUrl,
|
|
1385
|
+
extendedBackendReadUrl: extendedBackendReadUrl,
|
|
948
1386
|
extendedTimeout: 30000,
|
|
949
1387
|
extendedRetries: 3,
|
|
950
1388
|
extendedBaseUrl: "https://api.starknet.extended.exchange",
|
|
951
1389
|
extendedMarketName: "BTC-USD",
|
|
952
1390
|
extendedPrecision: 5,
|
|
953
1391
|
avnuAdapter: avnuAdapter,
|
|
1392
|
+
retryDelayForOrderStatus: minimumExtendedRetriesDelayForOrderStatus ?? 3000,
|
|
1393
|
+
minimumExtendedMovementAmount: minimumExtendedMovementAmount ?? 5, //5 usdcs
|
|
954
1394
|
});
|
|
955
1395
|
|
|
956
1396
|
const vesuMultiplyAdapter = new VesuMultiplyAdapter({
|
|
@@ -965,6 +1405,7 @@ function getLooperSettings(
|
|
|
965
1405
|
{ asset: wbtcToken, isDebt: false },
|
|
966
1406
|
{ asset: usdcToken, isDebt: true },
|
|
967
1407
|
],
|
|
1408
|
+
minimumVesuMovementAmount: minimumVesuMovementAmount ?? 5, //5 usdc
|
|
968
1409
|
});
|
|
969
1410
|
|
|
970
1411
|
const unusedBalanceAdapter = new UnusedBalanceAdapter({
|
|
@@ -1007,7 +1448,6 @@ function getLooperSettings(
|
|
|
1007
1448
|
vaultSettings.leafAdapters.push(() => extendedAdapter.getSwapFromLegacyLeaf());
|
|
1008
1449
|
vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
|
|
1009
1450
|
vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
|
|
1010
|
-
// Doubt here, should this be usdcToken.address, or wbtcToken.address?
|
|
1011
1451
|
vaultSettings.leafAdapters.push(
|
|
1012
1452
|
commonAdapter
|
|
1013
1453
|
.getApproveAdapter(
|
|
@@ -1078,11 +1518,11 @@ export default function VaultDescription(
|
|
|
1078
1518
|
}
|
|
1079
1519
|
|
|
1080
1520
|
const re7UsdcPrimeDevansh: VesuExtendedStrategySettings = {
|
|
1081
|
-
vaultAddress: ContractAddr.from("
|
|
1082
|
-
manager: ContractAddr.from("
|
|
1083
|
-
vaultAllocator: ContractAddr.from("
|
|
1084
|
-
redeemRequestNFT: ContractAddr.from("
|
|
1085
|
-
aumOracle: ContractAddr.from("
|
|
1521
|
+
vaultAddress: ContractAddr.from("0x058905be22d6a81792df79425dc9641cf3e1b77f36748631b7d7e5d713a32b55"),
|
|
1522
|
+
manager: ContractAddr.from("0x02648d703783feb2d967cf0520314cb5aa800d69a9426f3e3b317395af44de16"),
|
|
1523
|
+
vaultAllocator: ContractAddr.from("0x07d533c838eab6a4d854dd3aea96a55993fccd35821921970d00bde946b63b6f"),
|
|
1524
|
+
redeemRequestNFT: ContractAddr.from("0x01ef91f08fb99729c00f82fc6e0ece37917bcc43952596c19996259dc8adbbba"),
|
|
1525
|
+
aumOracle: ContractAddr.from("0x030b6acfec162f5d6e72b8a4d2798aedce78fb39de78a8f549f7cd277ae8bc8d"),
|
|
1086
1526
|
leafAdapters: [],
|
|
1087
1527
|
adapters: [],
|
|
1088
1528
|
targetHealthFactor: 1.4,
|
|
@@ -1096,17 +1536,18 @@ const re7UsdcPrimeDevansh: VesuExtendedStrategySettings = {
|
|
|
1096
1536
|
),
|
|
1097
1537
|
borrowable_assets: [Global.getDefaultTokens().find(token => token.symbol === "WBTC")!],
|
|
1098
1538
|
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
1539
|
+
walletAddress: WALLET_ADDRESS,
|
|
1099
1540
|
}
|
|
1100
1541
|
|
|
1101
|
-
export const VesuExtendedTestStrategies = (
|
|
1542
|
+
export const VesuExtendedTestStrategies = (extendedBackendReadUrl: string, extendedBackendWriteUrl: string, vaultIdExtended: number, minimumExtendedMovementAmount: number, minimumVesuMovementAmount: number, minimumExtendedRetriesDelayForOrderStatus: number, minimumExtendedPriceDifferenceForSwapOpen: number, maximumExtendedPriceDifferenceForSwapClosing: number): IStrategyMetadata<VesuExtendedStrategySettings>[] => {
|
|
1102
1543
|
return [
|
|
1103
|
-
getStrategySettingsVesuExtended('WBTC', 'USDC', re7UsdcPrimeDevansh, false, false,
|
|
1544
|
+
getStrategySettingsVesuExtended('WBTC', 'USDC', re7UsdcPrimeDevansh, false, false, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing),
|
|
1104
1545
|
]
|
|
1105
1546
|
}
|
|
1106
1547
|
|
|
1107
1548
|
|
|
1108
1549
|
|
|
1109
|
-
function getStrategySettingsVesuExtended(lstSymbol: string, underlyingSymbol: string, addresses: VesuExtendedStrategySettings, isPreview: boolean = false, isLST: boolean,
|
|
1550
|
+
function getStrategySettingsVesuExtended(lstSymbol: string, underlyingSymbol: string, addresses: VesuExtendedStrategySettings, isPreview: boolean = false, isLST: boolean, extendedBackendReadUrl: string, extendedBackendWriteUrl: string, vaultIdExtended: number, minimumExtendedMovementAmount: number, minimumVesuMovementAmount: number, minimumExtendedRetriesDelayForOrderStatus: number, minimumExtendedPriceDifferenceForSwapOpen: number, maximumExtendedPriceDifferenceForSwapClosing: number): IStrategyMetadata<VesuExtendedStrategySettings> {
|
|
1110
1551
|
return {
|
|
1111
1552
|
name: `Extended Test ${underlyingSymbol}`,
|
|
1112
1553
|
description: getDescription(lstSymbol, underlyingSymbol),
|
|
@@ -1114,7 +1555,7 @@ function getStrategySettingsVesuExtended(lstSymbol: string, underlyingSymbol: st
|
|
|
1114
1555
|
launchBlock: 0,
|
|
1115
1556
|
type: 'Other',
|
|
1116
1557
|
depositTokens: [Global.getDefaultTokens().find(token => token.symbol === underlyingSymbol)!],
|
|
1117
|
-
additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, VesuPools.Re7USDCPrime,
|
|
1558
|
+
additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, addresses, VesuPools.Re7USDCPrime, extendedBackendReadUrl, extendedBackendWriteUrl, vaultIdExtended, minimumExtendedMovementAmount, minimumVesuMovementAmount, minimumExtendedRetriesDelayForOrderStatus, minimumExtendedPriceDifferenceForSwapOpen, maximumExtendedPriceDifferenceForSwapClosing),
|
|
1118
1559
|
risk: {
|
|
1119
1560
|
riskFactor: _riskFactor,
|
|
1120
1561
|
netRisk:
|