@strkfarm/sdk 2.0.0-dev.26 → 2.0.0-dev.28
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 +190 -36
- package/dist/cli.mjs +188 -34
- package/dist/index.browser.global.js +79130 -49354
- package/dist/index.browser.mjs +18039 -11431
- package/dist/index.d.ts +2869 -898
- package/dist/index.js +19036 -12207
- package/dist/index.mjs +18942 -12158
- package/package.json +1 -1
- package/src/data/avnu.abi.json +840 -0
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/dataTypes/_bignumber.ts +13 -4
- package/src/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +76 -41
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +167 -2
- package/src/modules/ExtendedWrapperSDk/types.ts +26 -4
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +110 -67
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +4 -4
- package/src/modules/ekubo-pricer.ts +79 -0
- package/src/modules/ekubo-quoter.ts +46 -30
- package/src/modules/erc20.ts +17 -0
- package/src/modules/harvests.ts +43 -29
- package/src/modules/pragma.ts +23 -8
- package/src/modules/pricer-from-api.ts +156 -15
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +40 -4
- package/src/modules/pricerBase.ts +2 -1
- package/src/node/deployer.ts +36 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/strategies/base-strategy.ts +78 -10
- package/src/strategies/ekubo-cl-vault.tsx +906 -347
- package/src/strategies/factory.ts +159 -0
- package/src/strategies/index.ts +6 -1
- package/src/strategies/registry.ts +239 -0
- package/src/strategies/sensei.ts +335 -7
- package/src/strategies/svk-strategy.ts +97 -27
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +2 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +177 -268
- package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
- package/src/strategies/universal-adapters/common-adapter.ts +206 -203
- package/src/strategies/universal-adapters/extended-adapter.ts +155 -336
- package/src/strategies/universal-adapters/index.ts +9 -8
- package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
- package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +200 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +110 -75
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +476 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +762 -844
- package/src/strategies/universal-adapters/vesu-position-common.ts +251 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
- package/src/strategies/universal-lst-muliplier-strategy.tsx +396 -204
- package/src/strategies/universal-strategy.tsx +1426 -1178
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +2251 -0
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +2941 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +12 -1
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +52 -0
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +1 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +3 -1
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +158 -124
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +377 -1781
- package/src/strategies/vesu-rebalance.tsx +255 -152
- package/src/utils/health-factor-math.ts +4 -1
- package/src/utils/index.ts +2 -1
- package/src/utils/logger.browser.ts +22 -4
- package/src/utils/logger.node.ts +259 -24
- package/src/utils/starknet-call-parser.ts +1036 -0
- package/src/utils/strategy-utils.ts +61 -0
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
|
@@ -3,37 +3,37 @@ import {
|
|
|
3
3
|
IConfig,
|
|
4
4
|
IStrategyMetadata,
|
|
5
5
|
TokenInfo,
|
|
6
|
+
AuditStatus,
|
|
7
|
+
SourceCodeType,
|
|
8
|
+
AccessControlType,
|
|
9
|
+
InstantWithdrawalVault,
|
|
10
|
+
VaultType,
|
|
11
|
+
VaultPosition,
|
|
6
12
|
} from "@/interfaces";
|
|
7
13
|
import {
|
|
8
14
|
UNIVERSAL_MANAGE_IDS,
|
|
9
15
|
UniversalStrategySettings,
|
|
10
16
|
} from "../universal-strategy";
|
|
11
17
|
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
} from "./utils/helper";
|
|
18
|
+
ExtendedSVKVesuStateManager,
|
|
19
|
+
StateManagerConfig,
|
|
20
|
+
} from "./services/extended-vesu-state-manager";
|
|
21
|
+
import { ExecutionService, ExecutionConfig } from "./services/executionService";
|
|
17
22
|
import { logger } from "@/utils";
|
|
18
23
|
import { AUDIT_URL } from "../universal-lst-muliplier-strategy";
|
|
19
24
|
import { getNoRiskTags } from "@/interfaces";
|
|
20
25
|
import { _riskFactor } from "../universal-lst-muliplier-strategy";
|
|
21
26
|
import {
|
|
22
|
-
BUFFER_USDC_IN_WITHDRAWAL,
|
|
23
27
|
LIMIT_BALANCE,
|
|
24
|
-
LIMIT_BALANCE_VALUE,
|
|
25
|
-
MAX_LTV_BTC_USDC,
|
|
26
|
-
MINIMUM_EXTENDED_POSITION_SIZE,
|
|
27
28
|
USDC_TOKEN_DECIMALS,
|
|
28
29
|
WALLET_ADDRESS,
|
|
29
30
|
WBTC_TOKEN_DECIMALS,
|
|
30
31
|
} from "./utils/constants";
|
|
31
|
-
import {
|
|
32
|
+
import { ExecutionCallback } from "./types/transaction-metadata";
|
|
32
33
|
import { PricerBase } from "@/modules/pricerBase";
|
|
33
34
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
34
35
|
import { Global } from "@/global";
|
|
35
36
|
import { ERC20 } from "@/modules";
|
|
36
|
-
import { Balance, OrderSide } from "@/modules/ExtendedWrapperSDk";
|
|
37
37
|
import { Protocols } from "@/interfaces";
|
|
38
38
|
import { MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP } from "./utils/constants";
|
|
39
39
|
import {
|
|
@@ -49,12 +49,11 @@ import { VesuPools } from "../universal-adapters";
|
|
|
49
49
|
import {
|
|
50
50
|
BaseAdapterConfig,
|
|
51
51
|
CommonAdapter,
|
|
52
|
-
|
|
52
|
+
TokenTransferAdapter,
|
|
53
|
+
VesuModifyPositionAdapter,
|
|
53
54
|
VesuMultiplyAdapter,
|
|
54
55
|
} from "../universal-adapters";
|
|
55
|
-
import { Operations } from "./services/operationService";
|
|
56
56
|
import {
|
|
57
|
-
AVNU_EXCHANGE,
|
|
58
57
|
AVNU_QUOTE_URL,
|
|
59
58
|
AVNU_MIDDLEWARE,
|
|
60
59
|
EXTENDED_CONTRACT,
|
|
@@ -63,22 +62,11 @@ import { PricerFromApi } from "@/modules";
|
|
|
63
62
|
import { ExtendedAdapter } from "../universal-adapters/extended-adapter";
|
|
64
63
|
import { SVKStrategy } from "../svk-strategy";
|
|
65
64
|
import { AvnuAdapter } from "../universal-adapters/avnu-adapter";
|
|
66
|
-
import {
|
|
67
|
-
calculateAmountDistribution,
|
|
68
|
-
calculateAmountDistributionForWithdrawal,
|
|
69
|
-
calculateVesuLeverage,
|
|
70
|
-
calculateVesUPositionSizeGivenExtended,
|
|
71
|
-
} from "./utils/helper";
|
|
72
65
|
import { SingleTokenInfo } from "../base-strategy";
|
|
73
|
-
import {
|
|
74
|
-
import {
|
|
75
|
-
import {
|
|
76
|
-
TransactionMetadata,
|
|
77
|
-
TransactionResult,
|
|
78
|
-
} from "./types/transaction-metadata";
|
|
66
|
+
import { UsdcToUsdceAdapter } from "../universal-adapters/usdc<>usdce-adapter";
|
|
67
|
+
import { VesuConfig } from "./utils/config.runtime";
|
|
79
68
|
|
|
80
|
-
export interface VesuExtendedStrategySettings
|
|
81
|
-
extends UniversalStrategySettings {
|
|
69
|
+
export interface VesuExtendedStrategySettings extends UniversalStrategySettings {
|
|
82
70
|
underlyingToken: TokenInfo;
|
|
83
71
|
borrowable_assets: TokenInfo[];
|
|
84
72
|
targetHealthFactor: number;
|
|
@@ -90,33 +78,81 @@ export interface VesuExtendedStrategySettings
|
|
|
90
78
|
}
|
|
91
79
|
|
|
92
80
|
export class VesuExtendedMultiplierStrategy<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
S extends VesuExtendedStrategySettings,
|
|
82
|
+
> extends SVKStrategy<S> {
|
|
83
|
+
public wbtcToken: TokenInfo;
|
|
84
|
+
public usdcToken: TokenInfo;
|
|
85
|
+
public usdceToken: TokenInfo;
|
|
86
|
+
public readonly stateManager: ExtendedSVKVesuStateManager;
|
|
87
|
+
|
|
98
88
|
constructor(
|
|
99
89
|
config: IConfig,
|
|
100
90
|
pricer: PricerBase,
|
|
101
|
-
metadata: IStrategyMetadata<S
|
|
91
|
+
metadata: IStrategyMetadata<S>,
|
|
102
92
|
) {
|
|
103
93
|
super(config, pricer, metadata);
|
|
104
94
|
this.metadata.additionalInfo.adapters.forEach((adapter) => {
|
|
105
95
|
adapter.adapter.config.networkConfig = this.config;
|
|
106
96
|
adapter.adapter.config.pricer = this.pricer;
|
|
107
|
-
if ((adapter.adapter as
|
|
108
|
-
(adapter.adapter as
|
|
97
|
+
if ((adapter.adapter as any)._vesuAdapter) {
|
|
98
|
+
(adapter.adapter as any)._vesuAdapter.networkConfig =
|
|
109
99
|
this.config;
|
|
110
|
-
(adapter.adapter as
|
|
100
|
+
(adapter.adapter as any)._vesuAdapter.pricer =
|
|
111
101
|
this.pricer;
|
|
112
102
|
}
|
|
113
103
|
});
|
|
104
|
+
// todo check if this can be generalized
|
|
105
|
+
this.wbtcToken = Global.getDefaultTokens().find((token) => token.symbol === "WBTC")!;
|
|
106
|
+
this.usdcToken = this.metadata.additionalInfo.borrowable_assets[0]!;
|
|
107
|
+
this.usdceToken = Global.getDefaultTokens().find(
|
|
108
|
+
(token) => token.symbol === "USDC.e",
|
|
109
|
+
)!;
|
|
110
|
+
|
|
111
|
+
this.stateManager = this._initializeStateManager();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Extracts the required adapters from metadata and constructs the
|
|
116
|
+
* state manager used by shouldInvest / handleWithdraw.
|
|
117
|
+
*/
|
|
118
|
+
private _initializeStateManager(): ExtendedSVKVesuStateManager {
|
|
119
|
+
const vesuAdapters = this.metadata.additionalInfo.adapters
|
|
120
|
+
.filter((a) => a.adapter.name === VesuMultiplyAdapter.name)
|
|
121
|
+
.map((a) => a.adapter as VesuMultiplyAdapter);
|
|
122
|
+
|
|
123
|
+
const extendedAdapterEntry = this.metadata.additionalInfo.adapters.find(
|
|
124
|
+
(a) => a.adapter.name === ExtendedAdapter.name,
|
|
125
|
+
);
|
|
126
|
+
if (!extendedAdapterEntry) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`${this.getTag()} ExtendedAdapter not found in adapters — cannot initialise state manager.`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const stateManagerConfig: StateManagerConfig = {
|
|
133
|
+
pricer: this.pricer,
|
|
134
|
+
networkConfig: this.config,
|
|
135
|
+
vesuAdapters,
|
|
136
|
+
extendedAdapter: extendedAdapterEntry.adapter as ExtendedAdapter,
|
|
137
|
+
vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
|
|
138
|
+
walletAddress: this.metadata.additionalInfo.walletAddress,
|
|
139
|
+
assetToken: Global.getDefaultTokens().find((token) => token.symbol === 'USDC')!, // ! TODO change to asset() latest
|
|
140
|
+
usdceToken: this.usdceToken,
|
|
141
|
+
collateralToken: this.wbtcToken,
|
|
142
|
+
limitBalanceBufferFactor: LIMIT_BALANCE,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
return new ExtendedSVKVesuStateManager(stateManagerConfig);
|
|
114
146
|
}
|
|
115
147
|
|
|
116
148
|
getTag() {
|
|
117
149
|
return `${VesuExtendedMultiplierStrategy.name}:${this.metadata.name}`;
|
|
118
150
|
}
|
|
119
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Fetches current WBTC (collateral) and USDC (debt) prices from the pricer.
|
|
154
|
+
* Validates that both prices are finite and positive, throwing if not.
|
|
155
|
+
*/
|
|
120
156
|
async getAssetPrices() {
|
|
121
157
|
const wbtcToken = Global.getDefaultTokens().find(
|
|
122
158
|
(token) => token.symbol === "WBTC"
|
|
@@ -126,1633 +162,160 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
126
162
|
)!;
|
|
127
163
|
const collateralPrice = await this.pricer.getPrice(wbtcToken.symbol);
|
|
128
164
|
const debtPrice = await this.pricer.getPrice(usdcToken.symbol);
|
|
129
|
-
return {
|
|
130
|
-
collateralPrice,
|
|
131
|
-
debtPrice,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
165
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const usdValue = Number(balance.toFixed(usdceToken.decimals)) * price.price;
|
|
146
|
-
return {
|
|
147
|
-
tokenInfo: usdceToken,
|
|
148
|
-
amount: balance,
|
|
149
|
-
usdValue,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
166
|
+
if (!Number.isFinite(collateralPrice.price) || collateralPrice.price <= 0) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`${this.getTag()} Invalid collateralPrice: ${collateralPrice.price}. Expected a finite, positive number.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (!Number.isFinite(debtPrice.price) || debtPrice.price <= 0) {
|
|
172
|
+
throw new Error(
|
|
173
|
+
`${this.getTag()} Invalid debtPrice: ${debtPrice.price}. Expected a finite, positive number.`
|
|
174
|
+
);
|
|
175
|
+
}
|
|
152
176
|
|
|
153
|
-
|
|
154
|
-
const collateralToken = this.metadata.additionalInfo.borrowable_assets[0]!;
|
|
155
|
-
const balance = await new ERC20(this.config).balanceOf(
|
|
156
|
-
collateralToken.address,
|
|
157
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
158
|
-
collateralToken.decimals
|
|
159
|
-
);
|
|
160
|
-
const price = await this.pricer.getPrice(collateralToken.symbol);
|
|
161
|
-
const usdValue =
|
|
162
|
-
Number(balance.toFixed(collateralToken.decimals)) * price.price;
|
|
163
|
-
return {
|
|
164
|
-
tokenInfo: collateralToken,
|
|
165
|
-
amount: balance,
|
|
166
|
-
usdValue,
|
|
167
|
-
};
|
|
177
|
+
return { collateralPrice, debtPrice };
|
|
168
178
|
}
|
|
169
179
|
|
|
170
|
-
async getVesuAdapter(): Promise<VesuMultiplyAdapter
|
|
180
|
+
async getVesuAdapter(): Promise<VesuMultiplyAdapter> {
|
|
171
181
|
const vesuAdapter = this.metadata.additionalInfo.adapters.find(
|
|
172
|
-
(adapter) => adapter.adapter.name === VesuMultiplyAdapter.name
|
|
182
|
+
(adapter) => adapter.adapter.name === VesuMultiplyAdapter.name,
|
|
173
183
|
);
|
|
174
184
|
if (!vesuAdapter) {
|
|
175
|
-
|
|
176
|
-
|
|
185
|
+
throw new Error(
|
|
186
|
+
`${this.getTag()} Vesu adapter not configured in metadata.`
|
|
187
|
+
);
|
|
177
188
|
}
|
|
178
189
|
return vesuAdapter.adapter as VesuMultiplyAdapter;
|
|
179
190
|
}
|
|
180
191
|
|
|
181
|
-
async
|
|
182
|
-
const
|
|
183
|
-
(adapter) => adapter.adapter.name ===
|
|
192
|
+
async getVesuModifyPositionAdapter(): Promise<VesuModifyPositionAdapter> {
|
|
193
|
+
const vesuModifyPositionAdapter = this.metadata.additionalInfo.adapters.find(
|
|
194
|
+
(adapter) => adapter.adapter.name === VesuModifyPositionAdapter.name,
|
|
184
195
|
);
|
|
185
|
-
if (!
|
|
186
|
-
|
|
187
|
-
|
|
196
|
+
if (!vesuModifyPositionAdapter) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
`${this.getTag()} Vesu modify position adapter not configured in metadata.`,
|
|
199
|
+
);
|
|
188
200
|
}
|
|
189
|
-
return
|
|
201
|
+
return vesuModifyPositionAdapter.adapter as VesuModifyPositionAdapter;
|
|
190
202
|
}
|
|
191
203
|
|
|
192
|
-
async
|
|
193
|
-
const
|
|
194
|
-
(adapter) => adapter.adapter.name ===
|
|
204
|
+
async getUsdceTransferAdapter(): Promise<TokenTransferAdapter> {
|
|
205
|
+
const usdceTransferAdapter = this.metadata.additionalInfo.adapters.find(
|
|
206
|
+
(adapter) => adapter.adapter.name === TokenTransferAdapter.name,
|
|
195
207
|
);
|
|
196
|
-
if (!
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
return extendedAdapter.adapter as ExtendedAdapter;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
async moveAssetsToVaultAllocator(
|
|
204
|
-
amount: Web3Number,
|
|
205
|
-
extendedAdapter: ExtendedAdapter
|
|
206
|
-
): Promise<{
|
|
207
|
-
calls: Call[];
|
|
208
|
-
status: boolean;
|
|
209
|
-
}> {
|
|
210
|
-
try {
|
|
211
|
-
const usdceToken = Global.getDefaultTokens().find(
|
|
212
|
-
(token) => token.symbol === "USDCe"
|
|
213
|
-
)!;
|
|
214
|
-
const walletBalance = await new ERC20(this.config).balanceOf(
|
|
215
|
-
usdceToken.address,
|
|
216
|
-
WALLET_ADDRESS,
|
|
217
|
-
usdceToken.decimals
|
|
218
|
-
);
|
|
219
|
-
logger.info(
|
|
220
|
-
`${VesuExtendedMultiplierStrategy.name}::moveAssetsToVaultAllocator walletBalance: ${walletBalance}`
|
|
221
|
-
);
|
|
222
|
-
const amountToBeTransferred = amount.minimum(walletBalance);
|
|
223
|
-
logger.info(
|
|
224
|
-
`${
|
|
225
|
-
VesuExtendedMultiplierStrategy.name
|
|
226
|
-
}::moveAssetsToVaultAllocator amountToBeTransferred: ${amountToBeTransferred.toNumber()}`
|
|
227
|
-
);
|
|
228
|
-
|
|
229
|
-
if (amountToBeTransferred.lessThan(0)) {
|
|
230
|
-
logger.error(
|
|
231
|
-
`${
|
|
232
|
-
VesuExtendedMultiplierStrategy.name
|
|
233
|
-
}::moveAssetsToVaultAllocator amountToBeTransferred is less than 0: ${amountToBeTransferred.toNumber()}`
|
|
234
|
-
);
|
|
235
|
-
return {
|
|
236
|
-
calls: [],
|
|
237
|
-
status: false,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const approveCall = new ERC20(this.config).approve(
|
|
242
|
-
usdceToken.address,
|
|
243
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
244
|
-
amountToBeTransferred
|
|
245
|
-
);
|
|
246
|
-
const transferCall = new ERC20(this.config).transfer(
|
|
247
|
-
usdceToken.address,
|
|
248
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
249
|
-
amountToBeTransferred
|
|
250
|
-
);
|
|
251
|
-
const proofsInfo = extendedAdapter.getProofsForFromLegacySwap(
|
|
252
|
-
this.getMerkleTree()
|
|
208
|
+
if (!usdceTransferAdapter) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`${this.getTag()} Usdce transfer adapter not configured in metadata.`
|
|
253
211
|
);
|
|
254
|
-
const proofGroups = proofsInfo.proofs;
|
|
255
|
-
const call = this.getManageCall(
|
|
256
|
-
proofGroups,
|
|
257
|
-
await proofsInfo.callConstructor({ amount: amountToBeTransferred })
|
|
258
|
-
);
|
|
259
|
-
return {
|
|
260
|
-
calls: [approveCall, transferCall, call],
|
|
261
|
-
status: true,
|
|
262
|
-
};
|
|
263
|
-
} catch (err) {
|
|
264
|
-
logger.error(`error moving assets to vault allocator: ${err}`);
|
|
265
|
-
return {
|
|
266
|
-
calls: [],
|
|
267
|
-
status: false,
|
|
268
|
-
};
|
|
269
212
|
}
|
|
213
|
+
return usdceTransferAdapter.adapter as TokenTransferAdapter;
|
|
270
214
|
}
|
|
271
215
|
|
|
272
|
-
async
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
vesuLeverage: number;
|
|
280
|
-
debtAmountToBeRepaid: Web3Number;
|
|
281
|
-
}> {
|
|
282
|
-
try {
|
|
283
|
-
logger.info(
|
|
284
|
-
`${VesuExtendedMultiplierStrategy.name}::shouldInvest starting`
|
|
285
|
-
);
|
|
286
|
-
const vesuAdapter = await this.getVesuAdapter();
|
|
287
|
-
const extendedAdapter = await this.getExtendedAdapter();
|
|
288
|
-
logger.info(
|
|
289
|
-
`${
|
|
290
|
-
VesuExtendedMultiplierStrategy.name
|
|
291
|
-
}::shouldInvest adapters fetched: vesuAdapter=${!!vesuAdapter}, extendedAdapter=${!!extendedAdapter}, extendedAdapter.client=${!!extendedAdapter?.client}`
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
if (!vesuAdapter) {
|
|
295
|
-
logger.error(
|
|
296
|
-
`Vesu adapter not configured in metadata. This is a configuration issue, not a temporary failure.`
|
|
297
|
-
);
|
|
298
|
-
return {
|
|
299
|
-
shouldInvest: false,
|
|
300
|
-
vesuAmount: new Web3Number(0, 0),
|
|
301
|
-
extendedAmount: new Web3Number(0, 0),
|
|
302
|
-
extendedLeverage: 0,
|
|
303
|
-
collateralPrice: 0,
|
|
304
|
-
debtPrice: 0,
|
|
305
|
-
vesuLeverage: 0,
|
|
306
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
if (!extendedAdapter) {
|
|
310
|
-
logger.error(
|
|
311
|
-
`Extended adapter not configured in metadata. This is a configuration issue, not a temporary failure.`
|
|
312
|
-
);
|
|
313
|
-
return {
|
|
314
|
-
shouldInvest: false,
|
|
315
|
-
vesuAmount: new Web3Number(0, 0),
|
|
316
|
-
extendedAmount: new Web3Number(0, 0),
|
|
317
|
-
extendedLeverage: 0,
|
|
318
|
-
collateralPrice: 0,
|
|
319
|
-
debtPrice: 0,
|
|
320
|
-
vesuLeverage: 0,
|
|
321
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
if (!extendedAdapter.client) {
|
|
325
|
-
logger.error(
|
|
326
|
-
`Extended adapter client not initialized. This may be a temporary initialization failure - check network connectivity and API availability.`
|
|
327
|
-
);
|
|
328
|
-
return {
|
|
329
|
-
shouldInvest: false,
|
|
330
|
-
vesuAmount: new Web3Number(0, 0),
|
|
331
|
-
extendedAmount: new Web3Number(0, 0),
|
|
332
|
-
extendedLeverage: 0,
|
|
333
|
-
collateralPrice: 0,
|
|
334
|
-
debtPrice: 0,
|
|
335
|
-
vesuLeverage: 0,
|
|
336
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
logger.info(
|
|
341
|
-
`${VesuExtendedMultiplierStrategy.name}::shouldInvest calling getUnusedBalance`
|
|
342
|
-
);
|
|
343
|
-
const balance = await this.getUnusedBalance();
|
|
344
|
-
|
|
345
|
-
if (!Number.isFinite(balance.usdValue) || balance.usdValue < 0) {
|
|
346
|
-
logger.error(
|
|
347
|
-
`Invalid balance.usdValue: ${balance.usdValue}. Expected a finite, non-negative number.`
|
|
348
|
-
);
|
|
349
|
-
return {
|
|
350
|
-
shouldInvest: false,
|
|
351
|
-
vesuAmount: new Web3Number(0, 0),
|
|
352
|
-
extendedAmount: new Web3Number(0, 0),
|
|
353
|
-
extendedLeverage: 0,
|
|
354
|
-
collateralPrice: 0,
|
|
355
|
-
debtPrice: 0,
|
|
356
|
-
vesuLeverage: 0,
|
|
357
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
logger.info(
|
|
361
|
-
`${VesuExtendedMultiplierStrategy.name}::shouldInvest balance: ${balance.usdValue}`
|
|
362
|
-
);
|
|
363
|
-
const usdcBalanceOnExtended =
|
|
364
|
-
await extendedAdapter.getExtendedDepositAmount();
|
|
365
|
-
|
|
366
|
-
if (usdcBalanceOnExtended) {
|
|
367
|
-
const availableForWithdrawal = parseFloat(
|
|
368
|
-
usdcBalanceOnExtended.availableForWithdrawal
|
|
369
|
-
);
|
|
370
|
-
if (
|
|
371
|
-
!Number.isFinite(availableForWithdrawal) ||
|
|
372
|
-
availableForWithdrawal < 0
|
|
373
|
-
) {
|
|
374
|
-
logger.error(
|
|
375
|
-
`Invalid usdcBalanceOnExtended.availableForWithdrawal: ${usdcBalanceOnExtended.availableForWithdrawal}. Expected a finite, non-negative number.`
|
|
376
|
-
);
|
|
377
|
-
return {
|
|
378
|
-
shouldInvest: false,
|
|
379
|
-
vesuAmount: new Web3Number(0, 0),
|
|
380
|
-
extendedAmount: new Web3Number(0, 0),
|
|
381
|
-
extendedLeverage: 0,
|
|
382
|
-
collateralPrice: 0,
|
|
383
|
-
debtPrice: 0,
|
|
384
|
-
vesuLeverage: 0,
|
|
385
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
/** The LIMIT_BALANCE is the bffer amount to keep in the investing Cycle */
|
|
391
|
-
const amountToInvest = new Web3Number(
|
|
392
|
-
balance.usdValue,
|
|
393
|
-
USDC_TOKEN_DECIMALS
|
|
394
|
-
)
|
|
395
|
-
.plus(usdcBalanceOnExtended?.availableForTrade ?? 0)
|
|
396
|
-
.multipliedBy(1 - LIMIT_BALANCE);
|
|
397
|
-
|
|
398
|
-
const amountToInvestNumber = amountToInvest.toNumber();
|
|
399
|
-
if (!Number.isFinite(amountToInvestNumber)) {
|
|
400
|
-
logger.error(
|
|
401
|
-
`Invalid amountToInvest calculation result: ${amountToInvestNumber}. Calculation may have produced NaN or Infinity.`
|
|
402
|
-
);
|
|
403
|
-
return {
|
|
404
|
-
shouldInvest: false,
|
|
405
|
-
vesuAmount: new Web3Number(0, 0),
|
|
406
|
-
extendedAmount: new Web3Number(0, 0),
|
|
407
|
-
extendedLeverage: 0,
|
|
408
|
-
collateralPrice: 0,
|
|
409
|
-
debtPrice: 0,
|
|
410
|
-
vesuLeverage: 0,
|
|
411
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
logger.info(
|
|
416
|
-
`${VesuExtendedMultiplierStrategy.name}::shouldInvest amountToInvest: ${amountToInvestNumber}`
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
if (amountToInvest.lessThan(LIMIT_BALANCE_VALUE)) {
|
|
420
|
-
return {
|
|
421
|
-
shouldInvest: false,
|
|
422
|
-
vesuAmount: new Web3Number(0, 0),
|
|
423
|
-
extendedAmount: new Web3Number(0, 0),
|
|
424
|
-
extendedLeverage: 0,
|
|
425
|
-
collateralPrice: 0,
|
|
426
|
-
debtPrice: 0,
|
|
427
|
-
vesuLeverage: 0,
|
|
428
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const extendedPositon = await extendedAdapter.getAllOpenPositions();
|
|
433
|
-
if (!extendedPositon) {
|
|
434
|
-
logger.error("error getting extended position to decide move assets");
|
|
435
|
-
return {
|
|
436
|
-
shouldInvest: false,
|
|
437
|
-
vesuAmount: new Web3Number(0, 0),
|
|
438
|
-
extendedAmount: new Web3Number(0, 0),
|
|
439
|
-
extendedLeverage: 0,
|
|
440
|
-
collateralPrice: 0,
|
|
441
|
-
debtPrice: 0,
|
|
442
|
-
vesuLeverage: 0,
|
|
443
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
const { collateralTokenAmount, debtTokenAmount } =
|
|
447
|
-
await vesuAdapter.vesuAdapter.getAssetPrices();
|
|
448
|
-
|
|
449
|
-
const { collateralPrice, debtPrice } = await this.getAssetPrices();
|
|
450
|
-
|
|
451
|
-
if (
|
|
452
|
-
!Number.isFinite(collateralPrice.price) ||
|
|
453
|
-
collateralPrice.price <= 0
|
|
454
|
-
) {
|
|
455
|
-
logger.error(
|
|
456
|
-
`Invalid collateralPrice: ${collateralPrice.price}. Expected a finite, positive number.`
|
|
457
|
-
);
|
|
458
|
-
return {
|
|
459
|
-
shouldInvest: false,
|
|
460
|
-
vesuAmount: new Web3Number(0, 0),
|
|
461
|
-
extendedAmount: new Web3Number(0, 0),
|
|
462
|
-
extendedLeverage: 0,
|
|
463
|
-
collateralPrice: 0,
|
|
464
|
-
debtPrice: 0,
|
|
465
|
-
vesuLeverage: 0,
|
|
466
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
467
|
-
};
|
|
468
|
-
}
|
|
469
|
-
if (!Number.isFinite(debtPrice.price) || debtPrice.price <= 0) {
|
|
470
|
-
logger.error(
|
|
471
|
-
`Invalid debtPrice: ${debtPrice.price}. Expected a finite, positive number.`
|
|
472
|
-
);
|
|
473
|
-
return {
|
|
474
|
-
shouldInvest: false,
|
|
475
|
-
vesuAmount: new Web3Number(0, 0),
|
|
476
|
-
extendedAmount: new Web3Number(0, 0),
|
|
477
|
-
extendedLeverage: 0,
|
|
478
|
-
collateralPrice: 0,
|
|
479
|
-
debtPrice: 0,
|
|
480
|
-
vesuLeverage: 0,
|
|
481
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const debtAmountToBeRepaid = calculateDeltaDebtAmount(
|
|
486
|
-
MAX_LTV_BTC_USDC,
|
|
487
|
-
collateralTokenAmount,
|
|
488
|
-
debtTokenAmount,
|
|
489
|
-
collateralPrice.price,
|
|
490
|
-
debtPrice.price,
|
|
491
|
-
this.metadata.additionalInfo.targetHealthFactor
|
|
492
|
-
);
|
|
493
|
-
if (!debtAmountToBeRepaid) {
|
|
494
|
-
logger.error("error calculating debt amount to be repaid");
|
|
495
|
-
return {
|
|
496
|
-
shouldInvest: false,
|
|
497
|
-
vesuAmount: new Web3Number(0, 0),
|
|
498
|
-
extendedAmount: new Web3Number(0, 0),
|
|
499
|
-
extendedLeverage: 0,
|
|
500
|
-
collateralPrice: 0,
|
|
501
|
-
debtPrice: 0,
|
|
502
|
-
vesuLeverage: 0,
|
|
503
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
logger.info(
|
|
507
|
-
`${
|
|
508
|
-
VesuExtendedMultiplierStrategy.name
|
|
509
|
-
}::shouldInvest debtAmountToBeRepaid: ${debtAmountToBeRepaid.toNumber()}`
|
|
510
|
-
);
|
|
511
|
-
/**
|
|
512
|
-
* Since the debt amount is negative, we need to add it to the amount to invest
|
|
513
|
-
* to maintain the ltv
|
|
514
|
-
*/
|
|
515
|
-
const amountToInvestAfterRepayingDebt =
|
|
516
|
-
amountToInvest.plus(debtAmountToBeRepaid);
|
|
517
|
-
const { vesu_amount, extended_amount, extended_leverage, vesu_leverage } =
|
|
518
|
-
await calculateAmountDistribution(
|
|
519
|
-
amountToInvestAfterRepayingDebt.toNumber(),
|
|
520
|
-
extendedAdapter.client,
|
|
521
|
-
extendedAdapter.config.extendedMarketName,
|
|
522
|
-
collateralPrice.price,
|
|
523
|
-
debtPrice.price,
|
|
524
|
-
collateralTokenAmount,
|
|
525
|
-
extendedPositon
|
|
526
|
-
);
|
|
527
|
-
if (
|
|
528
|
-
!vesu_amount ||
|
|
529
|
-
!extended_amount ||
|
|
530
|
-
!extended_leverage ||
|
|
531
|
-
!vesu_leverage
|
|
532
|
-
) {
|
|
533
|
-
logger.error(
|
|
534
|
-
`Not enough amount to invest: vesu_amount=${vesu_amount}, extended_amount=${extended_amount}`
|
|
535
|
-
);
|
|
536
|
-
return {
|
|
537
|
-
shouldInvest: false,
|
|
538
|
-
vesuAmount: new Web3Number(0, 0),
|
|
539
|
-
extendedAmount: new Web3Number(0, 0),
|
|
540
|
-
extendedLeverage: 0,
|
|
541
|
-
collateralPrice: 0,
|
|
542
|
-
debtPrice: 0,
|
|
543
|
-
vesuLeverage: 0,
|
|
544
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
logger.info(
|
|
548
|
-
`${
|
|
549
|
-
VesuExtendedMultiplierStrategy.name
|
|
550
|
-
}::shouldInvest vesu_amount: ${vesu_amount.toNumber()}, extended_amount: ${extended_amount.toNumber()}`
|
|
216
|
+
async getAvnuAdapter(): Promise<AvnuAdapter> {
|
|
217
|
+
const avnuAdapter = this.metadata.additionalInfo.adapters.find(
|
|
218
|
+
(adapter) => adapter.adapter.name === AvnuAdapter.name,
|
|
219
|
+
);
|
|
220
|
+
if (!avnuAdapter) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`${this.getTag()} Avnu adapter not configured in metadata.`
|
|
551
223
|
);
|
|
552
|
-
return {
|
|
553
|
-
shouldInvest: true,
|
|
554
|
-
vesuAmount: vesu_amount,
|
|
555
|
-
extendedAmount: extended_amount,
|
|
556
|
-
extendedLeverage: extended_leverage,
|
|
557
|
-
vesuLeverage: vesu_leverage,
|
|
558
|
-
collateralPrice: collateralPrice.price,
|
|
559
|
-
debtPrice: debtPrice.price,
|
|
560
|
-
debtAmountToBeRepaid: debtAmountToBeRepaid,
|
|
561
|
-
};
|
|
562
|
-
} catch (err) {
|
|
563
|
-
logger.error(`error deciding invest: ${err}`);
|
|
564
|
-
return {
|
|
565
|
-
shouldInvest: false,
|
|
566
|
-
vesuAmount: new Web3Number(0, 0),
|
|
567
|
-
extendedAmount: new Web3Number(0, 0),
|
|
568
|
-
extendedLeverage: 0,
|
|
569
|
-
collateralPrice: 0,
|
|
570
|
-
debtPrice: 0,
|
|
571
|
-
vesuLeverage: 0,
|
|
572
|
-
debtAmountToBeRepaid: new Web3Number(0, 0),
|
|
573
|
-
};
|
|
574
224
|
}
|
|
225
|
+
return avnuAdapter.adapter as AvnuAdapter;
|
|
575
226
|
}
|
|
576
227
|
|
|
577
|
-
async
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (!vesuAdapter || !extendedAdapter || !extendedAdapter.client) {
|
|
585
|
-
logger.error(
|
|
586
|
-
`vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
|
|
587
|
-
);
|
|
588
|
-
return [];
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
592
|
-
if (!extendedHoldings) {
|
|
593
|
-
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
594
|
-
return [];
|
|
595
|
-
}
|
|
596
|
-
const usdcAmountInWallet = (await this.getUnusedBalance()).amount;
|
|
597
|
-
/**
|
|
598
|
-
* Trade is the correct metric, since we can close position for some vesu amount, otherwise underutilisation of funds will be a huge issue
|
|
599
|
-
*/
|
|
600
|
-
const usdcAmountOnExtendedAvailableForTrade = parseFloat(
|
|
601
|
-
extendedHoldings.availableForTrade
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
logger.info(
|
|
605
|
-
`${
|
|
606
|
-
VesuExtendedMultiplierStrategy.name
|
|
607
|
-
}::shouldMoveAssets calculating movements - Extended current: ${usdcAmountOnExtendedAvailableForTrade}, Wallet: ${usdcAmountInWallet.toNumber()}, Target Extended: ${extendedAmount.toNumber()}, Target Vesu: ${vesuAmount.toNumber()}`
|
|
608
|
-
);
|
|
609
|
-
|
|
610
|
-
let totalExtendedWithdrawal = new Web3Number(0, USDC_TOKEN_DECIMALS);
|
|
611
|
-
let totalExtendedDeposit = new Web3Number(0, USDC_TOKEN_DECIMALS);
|
|
612
|
-
|
|
613
|
-
if (
|
|
614
|
-
extendedAmount.isNegative() &&
|
|
615
|
-
extendedAmount
|
|
616
|
-
.abs()
|
|
617
|
-
.greaterThan(extendedAdapter.minimumExtendedMovementAmount)
|
|
618
|
-
) {
|
|
619
|
-
totalExtendedWithdrawal = totalExtendedWithdrawal.plus(
|
|
620
|
-
extendedAmount.abs()
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// Calculate remaining Extended difference (target vs current)
|
|
625
|
-
// If extendedAmount was negative, we've already accounted for that withdrawal
|
|
626
|
-
// So we calculate based on what Extended will be after that withdrawal
|
|
627
|
-
const extendedTargetAmount = extendedAmount.abs(); // Use absolute value as target
|
|
628
|
-
let projectedExtendedBalance = usdcAmountOnExtendedAvailableForTrade;
|
|
629
|
-
|
|
630
|
-
if (extendedAmount.isNegative()) {
|
|
631
|
-
projectedExtendedBalance =
|
|
632
|
-
projectedExtendedBalance - extendedAmount.abs().toNumber();
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const extendedAmountDifference = extendedTargetAmount.minus(
|
|
636
|
-
projectedExtendedBalance
|
|
637
|
-
);
|
|
638
|
-
const extendedAmountDifferenceAbs = extendedAmountDifference.abs();
|
|
639
|
-
|
|
640
|
-
// Track additional Extended movements
|
|
641
|
-
if (extendedAmountDifference.lessThan(0)) {
|
|
642
|
-
totalExtendedWithdrawal = totalExtendedWithdrawal.plus(
|
|
643
|
-
extendedAmountDifferenceAbs
|
|
644
|
-
);
|
|
645
|
-
} else if (extendedAmountDifference.greaterThan(0)) {
|
|
646
|
-
totalExtendedDeposit = totalExtendedDeposit.plus(
|
|
647
|
-
extendedAmountDifference
|
|
648
|
-
);
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
const vesuTargetAmount = vesuAmount.abs();
|
|
652
|
-
const projectedWalletBalance = usdcAmountInWallet
|
|
653
|
-
.plus(totalExtendedWithdrawal)
|
|
654
|
-
.minus(totalExtendedDeposit);
|
|
655
|
-
|
|
656
|
-
let vesuAmountDifference = vesuTargetAmount.minus(projectedWalletBalance);
|
|
657
|
-
const vesuAmountDifferenceAbs = vesuAmountDifference.abs();
|
|
658
|
-
|
|
659
|
-
logger.info(
|
|
660
|
-
`${
|
|
661
|
-
VesuExtendedMultiplierStrategy.name
|
|
662
|
-
}::shouldMoveAssets calculated movements - Extended withdrawal: ${totalExtendedWithdrawal.toNumber()}, Extended deposit: ${totalExtendedDeposit.toNumber()}, Extended diff: ${extendedAmountDifference.toNumber()}, Projected wallet: ${projectedWalletBalance.toNumber()}, Vesu diff: ${vesuAmountDifference.toNumber()}`
|
|
228
|
+
async getExtendedAdapter(): Promise<ExtendedAdapter> {
|
|
229
|
+
const extendedAdapter = this.metadata.additionalInfo.adapters.find(
|
|
230
|
+
(adapter) => adapter.adapter.name === ExtendedAdapter.name,
|
|
231
|
+
);
|
|
232
|
+
if (!extendedAdapter) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`${this.getTag()} Extended adapter not configured in metadata.`
|
|
663
235
|
);
|
|
664
|
-
let transactionResults: TransactionResult[] = [];
|
|
665
|
-
|
|
666
|
-
// Handle negative extendedAmount (initial withdrawal needed)
|
|
667
|
-
if (
|
|
668
|
-
extendedAmount.isNegative() &&
|
|
669
|
-
extendedAmount
|
|
670
|
-
.abs()
|
|
671
|
-
.greaterThan(extendedAdapter.minimumExtendedMovementAmount)
|
|
672
|
-
) {
|
|
673
|
-
try {
|
|
674
|
-
const {
|
|
675
|
-
calls: extendedCalls,
|
|
676
|
-
status: extendedStatus,
|
|
677
|
-
transactionMetadata: extendedTransactionMetadata,
|
|
678
|
-
} = await this.moveAssets(
|
|
679
|
-
{
|
|
680
|
-
to: Protocols.VAULT.name,
|
|
681
|
-
from: Protocols.EXTENDED.name,
|
|
682
|
-
amount: extendedAmount.abs(),
|
|
683
|
-
cycleType: CycleType.INVESTMENT,
|
|
684
|
-
},
|
|
685
|
-
extendedAdapter,
|
|
686
|
-
vesuAdapter
|
|
687
|
-
);
|
|
688
|
-
if (extendedStatus) {
|
|
689
|
-
transactionResults.push({
|
|
690
|
-
status: extendedStatus,
|
|
691
|
-
calls: extendedCalls,
|
|
692
|
-
transactionMetadata: {
|
|
693
|
-
...extendedTransactionMetadata,
|
|
694
|
-
transactionType: "DEPOSIT",
|
|
695
|
-
},
|
|
696
|
-
});
|
|
697
|
-
} else {
|
|
698
|
-
return [
|
|
699
|
-
this.createTransactionResult(
|
|
700
|
-
[],
|
|
701
|
-
false,
|
|
702
|
-
{
|
|
703
|
-
from: Protocols.EXTENDED.name,
|
|
704
|
-
to: Protocols.VAULT.name,
|
|
705
|
-
amount: extendedAmount.abs(),
|
|
706
|
-
},
|
|
707
|
-
"NONE",
|
|
708
|
-
CycleType.INVESTMENT
|
|
709
|
-
),
|
|
710
|
-
];
|
|
711
|
-
}
|
|
712
|
-
} catch (err) {
|
|
713
|
-
logger.error(`Failed moving assets to vault: ${err}`);
|
|
714
|
-
return [
|
|
715
|
-
this.createTransactionResult(
|
|
716
|
-
[],
|
|
717
|
-
false,
|
|
718
|
-
{
|
|
719
|
-
from: Protocols.EXTENDED.name,
|
|
720
|
-
to: Protocols.VAULT.name,
|
|
721
|
-
amount: extendedAmount.abs(),
|
|
722
|
-
},
|
|
723
|
-
"NONE",
|
|
724
|
-
CycleType.INVESTMENT
|
|
725
|
-
),
|
|
726
|
-
];
|
|
727
|
-
}
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
if (
|
|
731
|
-
vesuAmount.isNegative() &&
|
|
732
|
-
vesuAmount.abs().greaterThan(vesuAdapter.minimumVesuMovementAmount)
|
|
733
|
-
) {
|
|
734
|
-
try {
|
|
735
|
-
const {
|
|
736
|
-
calls: vesuCalls,
|
|
737
|
-
status: vesuStatus,
|
|
738
|
-
transactionMetadata: vesuTransactionMetadata,
|
|
739
|
-
} = await this.moveAssets(
|
|
740
|
-
{
|
|
741
|
-
to: Protocols.EXTENDED.name,
|
|
742
|
-
from: Protocols.VESU.name,
|
|
743
|
-
amount: vesuAmount.abs(),
|
|
744
|
-
cycleType: CycleType.INVESTMENT,
|
|
745
|
-
},
|
|
746
|
-
extendedAdapter,
|
|
747
|
-
vesuAdapter
|
|
748
|
-
);
|
|
749
|
-
if (!vesuStatus) {
|
|
750
|
-
return [
|
|
751
|
-
this.createTransactionResult(
|
|
752
|
-
[],
|
|
753
|
-
false,
|
|
754
|
-
{
|
|
755
|
-
from: Protocols.VESU.name,
|
|
756
|
-
to: Protocols.EXTENDED.name,
|
|
757
|
-
amount: vesuAmount.abs(),
|
|
758
|
-
},
|
|
759
|
-
"NONE",
|
|
760
|
-
CycleType.INVESTMENT
|
|
761
|
-
),
|
|
762
|
-
];
|
|
763
|
-
}
|
|
764
|
-
transactionResults.push({
|
|
765
|
-
status: vesuStatus,
|
|
766
|
-
calls: vesuCalls,
|
|
767
|
-
transactionMetadata: {
|
|
768
|
-
...vesuTransactionMetadata,
|
|
769
|
-
transactionType: "DEPOSIT",
|
|
770
|
-
},
|
|
771
|
-
});
|
|
772
|
-
} catch (err) {
|
|
773
|
-
logger.error(
|
|
774
|
-
`Failed moving assets to extended via vault allocator: ${err}`
|
|
775
|
-
);
|
|
776
|
-
return [
|
|
777
|
-
this.createTransactionResult(
|
|
778
|
-
[],
|
|
779
|
-
false,
|
|
780
|
-
{
|
|
781
|
-
from: Protocols.VESU.name,
|
|
782
|
-
to: Protocols.EXTENDED.name,
|
|
783
|
-
amount: vesuAmount.abs(),
|
|
784
|
-
},
|
|
785
|
-
"NONE",
|
|
786
|
-
CycleType.INVESTMENT
|
|
787
|
-
),
|
|
788
|
-
];
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
// Handle Extended adjustments based on calculated difference
|
|
793
|
-
if (
|
|
794
|
-
extendedAmountDifferenceAbs.greaterThan(
|
|
795
|
-
extendedAdapter.minimumExtendedMovementAmount
|
|
796
|
-
)
|
|
797
|
-
) {
|
|
798
|
-
if (extendedAmountDifference.greaterThan(0)) {
|
|
799
|
-
try {
|
|
800
|
-
const {
|
|
801
|
-
calls: extendedCalls,
|
|
802
|
-
status: extendedStatus,
|
|
803
|
-
transactionMetadata: extendedTransactionMetadata,
|
|
804
|
-
} = await this.moveAssets(
|
|
805
|
-
{
|
|
806
|
-
to: Protocols.EXTENDED.name,
|
|
807
|
-
from: Protocols.VAULT.name,
|
|
808
|
-
amount: extendedAmountDifference,
|
|
809
|
-
cycleType: CycleType.INVESTMENT,
|
|
810
|
-
},
|
|
811
|
-
extendedAdapter,
|
|
812
|
-
vesuAdapter
|
|
813
|
-
);
|
|
814
|
-
if (extendedStatus) {
|
|
815
|
-
transactionResults.push({
|
|
816
|
-
status: extendedStatus,
|
|
817
|
-
calls: extendedCalls,
|
|
818
|
-
transactionMetadata: extendedTransactionMetadata,
|
|
819
|
-
});
|
|
820
|
-
} else {
|
|
821
|
-
logger.error(
|
|
822
|
-
`Failed to move assets to extended - operation returned false status`
|
|
823
|
-
);
|
|
824
|
-
return [
|
|
825
|
-
this.createTransactionResult(
|
|
826
|
-
[],
|
|
827
|
-
false,
|
|
828
|
-
{
|
|
829
|
-
from: Protocols.VAULT.name,
|
|
830
|
-
to: Protocols.EXTENDED.name,
|
|
831
|
-
amount: extendedAmountDifference,
|
|
832
|
-
},
|
|
833
|
-
"NONE",
|
|
834
|
-
CycleType.INVESTMENT
|
|
835
|
-
),
|
|
836
|
-
];
|
|
837
|
-
}
|
|
838
|
-
} catch (err) {
|
|
839
|
-
logger.error(`Failed moving assets to extended: ${err}`);
|
|
840
|
-
return [
|
|
841
|
-
this.createTransactionResult(
|
|
842
|
-
[],
|
|
843
|
-
false,
|
|
844
|
-
{
|
|
845
|
-
from: Protocols.VAULT.name,
|
|
846
|
-
to: Protocols.EXTENDED.name,
|
|
847
|
-
amount: extendedAmountDifference,
|
|
848
|
-
},
|
|
849
|
-
"NONE",
|
|
850
|
-
CycleType.INVESTMENT
|
|
851
|
-
),
|
|
852
|
-
];
|
|
853
|
-
}
|
|
854
|
-
} else if (extendedAmountDifference.lessThan(0)) {
|
|
855
|
-
try {
|
|
856
|
-
const {
|
|
857
|
-
calls: extendedCalls,
|
|
858
|
-
status: extendedStatus,
|
|
859
|
-
transactionMetadata: extendedTransactionMetadata,
|
|
860
|
-
} = await this.moveAssets(
|
|
861
|
-
{
|
|
862
|
-
to: Protocols.VAULT.name,
|
|
863
|
-
from: Protocols.EXTENDED.name,
|
|
864
|
-
amount: extendedAmountDifferenceAbs,
|
|
865
|
-
cycleType: CycleType.INVESTMENT,
|
|
866
|
-
},
|
|
867
|
-
extendedAdapter,
|
|
868
|
-
vesuAdapter
|
|
869
|
-
);
|
|
870
|
-
if (extendedStatus) {
|
|
871
|
-
transactionResults.push({
|
|
872
|
-
status: extendedStatus,
|
|
873
|
-
calls: extendedCalls,
|
|
874
|
-
transactionMetadata: {
|
|
875
|
-
...extendedTransactionMetadata,
|
|
876
|
-
transactionType: "DEPOSIT",
|
|
877
|
-
},
|
|
878
|
-
});
|
|
879
|
-
} else {
|
|
880
|
-
logger.error(
|
|
881
|
-
`Failed to withdraw from extended - operation returned false status`
|
|
882
|
-
);
|
|
883
|
-
return [
|
|
884
|
-
this.createTransactionResult(
|
|
885
|
-
[],
|
|
886
|
-
false,
|
|
887
|
-
{
|
|
888
|
-
from: Protocols.EXTENDED.name,
|
|
889
|
-
to: Protocols.VAULT.name,
|
|
890
|
-
amount: extendedAmountDifferenceAbs,
|
|
891
|
-
},
|
|
892
|
-
"NONE",
|
|
893
|
-
CycleType.INVESTMENT
|
|
894
|
-
),
|
|
895
|
-
];
|
|
896
|
-
}
|
|
897
|
-
} catch (err) {
|
|
898
|
-
logger.error(`Failed moving assets from extended to vault: ${err}`);
|
|
899
|
-
return [
|
|
900
|
-
this.createTransactionResult(
|
|
901
|
-
[],
|
|
902
|
-
false,
|
|
903
|
-
{
|
|
904
|
-
from: Protocols.EXTENDED.name,
|
|
905
|
-
to: Protocols.VAULT.name,
|
|
906
|
-
amount: extendedAmountDifferenceAbs,
|
|
907
|
-
},
|
|
908
|
-
"NONE",
|
|
909
|
-
CycleType.INVESTMENT
|
|
910
|
-
),
|
|
911
|
-
];
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
// Handle Vesu adjustments based on calculated difference (already adjusted for Extended movements)
|
|
917
|
-
if (
|
|
918
|
-
vesuAmountDifferenceAbs.greaterThan(
|
|
919
|
-
vesuAdapter.minimumVesuMovementAmount
|
|
920
|
-
)
|
|
921
|
-
) {
|
|
922
|
-
if (vesuAmountDifference.lessThanOrEqualTo(0)) {
|
|
923
|
-
logger.warn(
|
|
924
|
-
`Vesu amount difference is negative or zero: ${vesuAmountDifference.toNumber()}. Skipping operation.`
|
|
925
|
-
);
|
|
926
|
-
} else {
|
|
927
|
-
// Move assets from Extended to Vault (which will then go to Vesu)
|
|
928
|
-
try {
|
|
929
|
-
const {
|
|
930
|
-
calls: vesuCalls,
|
|
931
|
-
status: vesuStatus,
|
|
932
|
-
transactionMetadata: vesuTransactionMetadata,
|
|
933
|
-
} = await this.moveAssets(
|
|
934
|
-
{
|
|
935
|
-
to: Protocols.VAULT.name,
|
|
936
|
-
from: Protocols.EXTENDED.name,
|
|
937
|
-
amount: vesuAmountDifference,
|
|
938
|
-
cycleType: CycleType.INVESTMENT,
|
|
939
|
-
},
|
|
940
|
-
extendedAdapter,
|
|
941
|
-
vesuAdapter
|
|
942
|
-
);
|
|
943
|
-
if (!vesuStatus) {
|
|
944
|
-
logger.error(
|
|
945
|
-
`Failed to move assets to vesu - operation returned false status`
|
|
946
|
-
);
|
|
947
|
-
return [
|
|
948
|
-
this.createTransactionResult(
|
|
949
|
-
[],
|
|
950
|
-
false,
|
|
951
|
-
{
|
|
952
|
-
from: Protocols.EXTENDED.name,
|
|
953
|
-
to: Protocols.VAULT.name,
|
|
954
|
-
amount: vesuAmountDifference,
|
|
955
|
-
},
|
|
956
|
-
"NONE",
|
|
957
|
-
CycleType.INVESTMENT
|
|
958
|
-
),
|
|
959
|
-
];
|
|
960
|
-
}
|
|
961
|
-
transactionResults.push({
|
|
962
|
-
status: vesuStatus,
|
|
963
|
-
calls: vesuCalls,
|
|
964
|
-
transactionMetadata: {
|
|
965
|
-
...vesuTransactionMetadata,
|
|
966
|
-
transactionType: "DEPOSIT",
|
|
967
|
-
},
|
|
968
|
-
});
|
|
969
|
-
} catch (err) {
|
|
970
|
-
logger.error(`Failed moving assets to vault: ${err}`);
|
|
971
|
-
return [
|
|
972
|
-
this.createTransactionResult(
|
|
973
|
-
[],
|
|
974
|
-
false,
|
|
975
|
-
{
|
|
976
|
-
from: Protocols.EXTENDED.name,
|
|
977
|
-
to: Protocols.VAULT.name,
|
|
978
|
-
amount: vesuAmountDifference,
|
|
979
|
-
},
|
|
980
|
-
"NONE",
|
|
981
|
-
CycleType.INVESTMENT
|
|
982
|
-
),
|
|
983
|
-
];
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
return transactionResults;
|
|
988
|
-
} catch (err) {
|
|
989
|
-
logger.error(`Failed moving assets to vesu: ${err}`);
|
|
990
|
-
return [
|
|
991
|
-
this.createTransactionResult(
|
|
992
|
-
[],
|
|
993
|
-
false,
|
|
994
|
-
{
|
|
995
|
-
from: Protocols.EXTENDED.name,
|
|
996
|
-
to: Protocols.VAULT.name,
|
|
997
|
-
amount: new Web3Number(0, USDC_TOKEN_DECIMALS),
|
|
998
|
-
},
|
|
999
|
-
"NONE",
|
|
1000
|
-
CycleType.INVESTMENT
|
|
1001
|
-
),
|
|
1002
|
-
];
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
/**
|
|
1007
|
-
* Helper method to create transaction result with metadata
|
|
1008
|
-
*/
|
|
1009
|
-
private createTransactionResult(
|
|
1010
|
-
calls: Call[],
|
|
1011
|
-
status: boolean,
|
|
1012
|
-
params: { from: string; to: string; amount: Web3Number },
|
|
1013
|
-
transactionType: "DEPOSIT" | "WITHDRAWAL" | "NONE",
|
|
1014
|
-
cycleType: CycleType
|
|
1015
|
-
): TransactionResult {
|
|
1016
|
-
if (status) {
|
|
1017
|
-
return {
|
|
1018
|
-
calls,
|
|
1019
|
-
status: status,
|
|
1020
|
-
transactionMetadata: {
|
|
1021
|
-
protocolFrom: params.from,
|
|
1022
|
-
protocolTo: params.to,
|
|
1023
|
-
transactionType: transactionType,
|
|
1024
|
-
usdAmount: params.amount.abs().toFixed(),
|
|
1025
|
-
status: "PENDING",
|
|
1026
|
-
cycleType: cycleType,
|
|
1027
|
-
},
|
|
1028
|
-
};
|
|
1029
236
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
transactionMetadata: {
|
|
1034
|
-
protocolFrom: "",
|
|
1035
|
-
protocolTo: "",
|
|
1036
|
-
transactionType: "DEPOSIT",
|
|
1037
|
-
usdAmount: "0",
|
|
1038
|
-
status: "FAILED",
|
|
1039
|
-
cycleType: cycleType,
|
|
1040
|
-
},
|
|
1041
|
-
};
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* This method is used to move assets between protocols
|
|
1046
|
-
* @param params - The parameters for the move assets operation
|
|
1047
|
-
* @param extendedAdapter - The extended adapter
|
|
1048
|
-
* @param vesuAdapter - The vesu adapter
|
|
1049
|
-
* @returns The transaction result
|
|
1050
|
-
* If Extended amount is greater than amount of withdrawal from extended, then we need to open a long position
|
|
1051
|
-
* so that the amount of withdrawal from extended is fullfilled
|
|
1052
|
-
*/
|
|
1053
|
-
async moveAssets(
|
|
1054
|
-
params: {
|
|
1055
|
-
amount: Web3Number;
|
|
1056
|
-
from: string;
|
|
1057
|
-
to: string;
|
|
1058
|
-
cycleType: CycleType;
|
|
1059
|
-
},
|
|
1060
|
-
extendedAdapter: ExtendedAdapter,
|
|
1061
|
-
vesuAdapter: VesuMultiplyAdapter
|
|
1062
|
-
): Promise<TransactionResult> {
|
|
1063
|
-
try {
|
|
1064
|
-
// Validate amount is positive before starting operations
|
|
1065
|
-
if (params.amount.lessThanOrEqualTo(0)) {
|
|
1066
|
-
logger.error(
|
|
1067
|
-
`Invalid amount for moveAssets: ${params.amount.toNumber()}. Amount must be positive.`
|
|
1068
|
-
);
|
|
1069
|
-
return this.createTransactionResult(
|
|
1070
|
-
[],
|
|
1071
|
-
false,
|
|
1072
|
-
params,
|
|
1073
|
-
"NONE",
|
|
1074
|
-
params.cycleType
|
|
1075
|
-
);
|
|
1076
|
-
}
|
|
1077
|
-
|
|
1078
|
-
// Check minimum movement amounts before starting operations
|
|
1079
|
-
// const amountAbs = params.amount.abs();
|
|
1080
|
-
// if (params.from === Protocols.EXTENDED.name || params.to === Protocols.EXTENDED.name) {
|
|
1081
|
-
// if (amountAbs.lessThanOrEqualTo(extendedAdapter.minimumExtendedMovementAmount)) {
|
|
1082
|
-
// logger.warn(
|
|
1083
|
-
// `Amount ${amountAbs.toNumber()} is below minimum Extended movement amount ${extendedAdapter.minimumExtendedMovementAmount}. Skipping operation.`
|
|
1084
|
-
// );
|
|
1085
|
-
// return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
1086
|
-
// }
|
|
1087
|
-
// }
|
|
1088
|
-
// if (params.from === Protocols.VESU.name || params.to === Protocols.VESU.name) {
|
|
1089
|
-
// if (amountAbs.lessThanOrEqualTo(vesuAdapter.minimumVesuMovementAmount)) {
|
|
1090
|
-
// logger.warn(
|
|
1091
|
-
// `Amount ${amountAbs.toNumber()} is below minimum Vesu movement amount ${vesuAdapter.minimumVesuMovementAmount}. Skipping operation.`
|
|
1092
|
-
// );
|
|
1093
|
-
// return this.createTransactionResult([], false, params, "NONE", params.cycleType);
|
|
1094
|
-
// }
|
|
1095
|
-
// }
|
|
1096
|
-
|
|
1097
|
-
const avnuAdapter = await this.getAvnuAdapter();
|
|
1098
|
-
if (!avnuAdapter) {
|
|
1099
|
-
logger.error(`avnu adapter not found: ${avnuAdapter}`);
|
|
1100
|
-
return this.createTransactionResult(
|
|
1101
|
-
[],
|
|
1102
|
-
false,
|
|
1103
|
-
params,
|
|
1104
|
-
"NONE",
|
|
1105
|
-
params.cycleType
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
logger.info(`moveAssets params, ${JSON.stringify(params)}`);
|
|
1109
|
-
const collateralToken = vesuAdapter.config.supportedPositions[0].asset;
|
|
1110
|
-
const { collateralPrice } = await this.getAssetPrices();
|
|
1111
|
-
|
|
1112
|
-
if (
|
|
1113
|
-
params.to === Protocols.EXTENDED.name &&
|
|
1114
|
-
params.from === Protocols.VAULT.name
|
|
1115
|
-
) {
|
|
1116
|
-
const proofsInfo = extendedAdapter.getProofs(
|
|
1117
|
-
true,
|
|
1118
|
-
this.getMerkleTree()
|
|
1119
|
-
);
|
|
1120
|
-
const calls = [];
|
|
1121
|
-
const proofGroups = proofsInfo.proofs;
|
|
1122
|
-
const call = this.getManageCall(
|
|
1123
|
-
proofGroups,
|
|
1124
|
-
await proofsInfo.callConstructor({ amount: params.amount })
|
|
1125
|
-
);
|
|
1126
|
-
calls.push(call);
|
|
1127
|
-
return this.createTransactionResult(
|
|
1128
|
-
calls,
|
|
1129
|
-
true,
|
|
1130
|
-
params,
|
|
1131
|
-
"DEPOSIT",
|
|
1132
|
-
params.cycleType
|
|
1133
|
-
);
|
|
1134
|
-
} else if (
|
|
1135
|
-
params.to === Protocols.VAULT.name &&
|
|
1136
|
-
params.from === Protocols.EXTENDED.name
|
|
1137
|
-
) {
|
|
1138
|
-
const extendedLeverage = calculateExtendedLevergae();
|
|
1139
|
-
const extendedHoldings =
|
|
1140
|
-
await extendedAdapter.getExtendedDepositAmount();
|
|
1141
|
-
if (!extendedHoldings) {
|
|
1142
|
-
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
1143
|
-
return this.createTransactionResult(
|
|
1144
|
-
[],
|
|
1145
|
-
false,
|
|
1146
|
-
params,
|
|
1147
|
-
"NONE",
|
|
1148
|
-
params.cycleType
|
|
1149
|
-
);
|
|
1150
|
-
}
|
|
1151
|
-
const extendedHoldingAmount = new Web3Number(
|
|
1152
|
-
extendedHoldings.availableForWithdrawal,
|
|
1153
|
-
USDC_TOKEN_DECIMALS
|
|
1154
|
-
);
|
|
1155
|
-
logger.info(
|
|
1156
|
-
`${
|
|
1157
|
-
VesuExtendedMultiplierStrategy.name
|
|
1158
|
-
}::moveAssets extendedHoldingAmount: ${extendedHoldingAmount.toNumber()}`
|
|
1159
|
-
);
|
|
1160
|
-
const extendedPositions = await extendedAdapter.getAllOpenPositions();
|
|
1161
|
-
if (!extendedPositions) {
|
|
1162
|
-
logger.error(
|
|
1163
|
-
`error getting extended positions: ${extendedPositions} while moving assets from extended to vault`
|
|
1164
|
-
);
|
|
1165
|
-
return this.createTransactionResult(
|
|
1166
|
-
[],
|
|
1167
|
-
false,
|
|
1168
|
-
params,
|
|
1169
|
-
"NONE",
|
|
1170
|
-
params.cycleType
|
|
1171
|
-
);
|
|
1172
|
-
}
|
|
1173
|
-
if (params.amount.abs().greaterThan(extendedHoldingAmount)) {
|
|
1174
|
-
const leftAmountAfterWithdrawalAmountInAccount = new Web3Number(
|
|
1175
|
-
Math.ceil(
|
|
1176
|
-
params.amount.abs().minus(extendedHoldingAmount).toNumber()
|
|
1177
|
-
),
|
|
1178
|
-
USDC_TOKEN_DECIMALS
|
|
1179
|
-
);
|
|
1180
|
-
const positionAmountToClose =
|
|
1181
|
-
await calculatePositionToCloseToWithdrawAmount(
|
|
1182
|
-
extendedHoldings,
|
|
1183
|
-
extendedPositions[0],
|
|
1184
|
-
params.amount
|
|
1185
|
-
);
|
|
1186
|
-
logger.info(
|
|
1187
|
-
`positionAmountToClose: ${positionAmountToClose} this is without leverage`
|
|
1188
|
-
);
|
|
1189
|
-
logger.info(
|
|
1190
|
-
`${
|
|
1191
|
-
VesuExtendedMultiplierStrategy.name
|
|
1192
|
-
}::moveAssets leftAmountAfterWithdrawalAmountInAccount: ${leftAmountAfterWithdrawalAmountInAccount.toNumber()}`
|
|
1193
|
-
);
|
|
1194
|
-
let priceOfBTC;
|
|
1195
|
-
const { ask, bid, status } =
|
|
1196
|
-
await extendedAdapter.fetchOrderBookBTCUSDC();
|
|
1197
|
-
const price = ask.plus(bid).dividedBy(2);
|
|
1198
|
-
if (status) {
|
|
1199
|
-
priceOfBTC = price;
|
|
1200
|
-
} else {
|
|
1201
|
-
logger.error(`error fetching order book btc usdc: ${status}`);
|
|
1202
|
-
priceOfBTC = collateralPrice.price;
|
|
1203
|
-
}
|
|
1204
|
-
const btcAmount = positionAmountToClose.dividedBy(priceOfBTC);
|
|
1205
|
-
/**
|
|
1206
|
-
* If amount for withdrawal is greater than the amount in extended available for withdrawal,
|
|
1207
|
-
* then we need to open a long position depending on the difference between the two
|
|
1208
|
-
*/
|
|
1209
|
-
const openLongPosition = btcAmount
|
|
1210
|
-
.multipliedBy(3)
|
|
1211
|
-
.greaterThan(MINIMUM_EXTENDED_POSITION_SIZE)
|
|
1212
|
-
? await extendedAdapter.createOrder(
|
|
1213
|
-
extendedLeverage.toString(),
|
|
1214
|
-
btcAmount.toNumber(),
|
|
1215
|
-
OrderSide.BUY
|
|
1216
|
-
)
|
|
1217
|
-
: await extendedAdapter.createOrder(
|
|
1218
|
-
extendedLeverage.toString(),
|
|
1219
|
-
0.000034, // just in case amount falls short then we need to create a withdrawal
|
|
1220
|
-
OrderSide.BUY
|
|
1221
|
-
);
|
|
1222
|
-
if (!openLongPosition) {
|
|
1223
|
-
logger.error(`error opening long position: ${openLongPosition}`);
|
|
1224
|
-
}
|
|
1225
|
-
const updatedHoldings =
|
|
1226
|
-
await extendedAdapter.getExtendedDepositAmount();
|
|
1227
|
-
if (
|
|
1228
|
-
!updatedHoldings ||
|
|
1229
|
-
new Web3Number(
|
|
1230
|
-
updatedHoldings.availableForWithdrawal,
|
|
1231
|
-
USDC_TOKEN_DECIMALS
|
|
1232
|
-
).lessThan(params.amount.abs())
|
|
1233
|
-
) {
|
|
1234
|
-
logger.error(
|
|
1235
|
-
`Insufficient balance after opening position. Available: ${
|
|
1236
|
-
updatedHoldings?.availableForWithdrawal
|
|
1237
|
-
}, Needed: ${params.amount.abs()}`
|
|
1238
|
-
);
|
|
1239
|
-
return this.createTransactionResult(
|
|
1240
|
-
[],
|
|
1241
|
-
false,
|
|
1242
|
-
params,
|
|
1243
|
-
"NONE",
|
|
1244
|
-
params.cycleType
|
|
1245
|
-
);
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
const {
|
|
1249
|
-
status: withdrawalFromExtendedStatus,
|
|
1250
|
-
receivedTxnHash: withdrawalFromExtendedTxnHash,
|
|
1251
|
-
} = await extendedAdapter.withdrawFromExtended(params.amount);
|
|
1252
|
-
/**
|
|
1253
|
-
* This logic needs fixing
|
|
1254
|
-
*/
|
|
1255
|
-
logger.info(
|
|
1256
|
-
`withdrawalFromExtendedStatus: ${withdrawalFromExtendedStatus}, withdrawalFromExtendedTxnHash: ${withdrawalFromExtendedTxnHash}`
|
|
1257
|
-
);
|
|
1258
|
-
if (withdrawalFromExtendedStatus && withdrawalFromExtendedTxnHash) {
|
|
1259
|
-
/**
|
|
1260
|
-
* We need to move assets from my wallet back to vault contract
|
|
1261
|
-
*/
|
|
1262
|
-
const extendedHoldings =
|
|
1263
|
-
await extendedAdapter.getExtendedDepositAmount();
|
|
1264
|
-
logger.info(
|
|
1265
|
-
`extendedHoldings after withdrawal ${extendedHoldings?.availableForWithdrawal}`
|
|
1266
|
-
);
|
|
1267
|
-
await new Promise((resolve) => setTimeout(resolve, 5000));
|
|
1268
|
-
const { calls, status } = await this.moveAssetsToVaultAllocator(
|
|
1269
|
-
params.amount,
|
|
1270
|
-
extendedAdapter
|
|
1271
|
-
);
|
|
1272
|
-
if (calls.length > 0 && status) {
|
|
1273
|
-
return this.createTransactionResult(
|
|
1274
|
-
calls,
|
|
1275
|
-
true,
|
|
1276
|
-
params,
|
|
1277
|
-
"WITHDRAWAL",
|
|
1278
|
-
params.cycleType
|
|
1279
|
-
);
|
|
1280
|
-
} else {
|
|
1281
|
-
/**
|
|
1282
|
-
* This is a fallback scenario, where the funds were withdrawn from extended, but didn't get transferred to the wallet
|
|
1283
|
-
* We need to return a successful transaction result, but with no calls
|
|
1284
|
-
* Db update will be handled by the risk engine for this specific case
|
|
1285
|
-
*/
|
|
1286
|
-
return this.createTransactionResult(
|
|
1287
|
-
[],
|
|
1288
|
-
true,
|
|
1289
|
-
params,
|
|
1290
|
-
"WITHDRAWAL",
|
|
1291
|
-
params.cycleType
|
|
1292
|
-
);
|
|
1293
|
-
}
|
|
1294
|
-
} else if (
|
|
1295
|
-
withdrawalFromExtendedStatus &&
|
|
1296
|
-
!withdrawalFromExtendedTxnHash
|
|
1297
|
-
) {
|
|
1298
|
-
logger.error(
|
|
1299
|
-
"withdrawal from extended successful, but funds didn't get transferred to the wallet"
|
|
1300
|
-
);
|
|
1301
|
-
return this.createTransactionResult(
|
|
1302
|
-
[],
|
|
1303
|
-
true,
|
|
1304
|
-
params,
|
|
1305
|
-
"WITHDRAWAL",
|
|
1306
|
-
params.cycleType
|
|
1307
|
-
);
|
|
1308
|
-
} else {
|
|
1309
|
-
logger.error("withdrawal from extended failed");
|
|
1310
|
-
return this.createTransactionResult(
|
|
1311
|
-
[],
|
|
1312
|
-
false,
|
|
1313
|
-
params,
|
|
1314
|
-
"NONE",
|
|
1315
|
-
params.cycleType
|
|
1316
|
-
);
|
|
1317
|
-
}
|
|
1318
|
-
} else if (
|
|
1319
|
-
params.to === Protocols.VAULT.name &&
|
|
1320
|
-
params.from === Protocols.VESU.name
|
|
1321
|
-
) {
|
|
1322
|
-
const isPriceDifferenceBetweenAvnuAndExtended =
|
|
1323
|
-
await this.checkPriceDifferenceBetweenAvnuAndExtended(
|
|
1324
|
-
extendedAdapter,
|
|
1325
|
-
vesuAdapter,
|
|
1326
|
-
avnuAdapter,
|
|
1327
|
-
PositionTypeAvnuExtended.CLOSE
|
|
1328
|
-
);
|
|
1329
|
-
if (
|
|
1330
|
-
!isPriceDifferenceBetweenAvnuAndExtended &&
|
|
1331
|
-
params.cycleType === CycleType.WITHDRAWAL
|
|
1332
|
-
) {
|
|
1333
|
-
logger.warn(
|
|
1334
|
-
`price difference between avnu and extended doesn't fit the range for close position, ${avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing}`
|
|
1335
|
-
);
|
|
1336
|
-
return this.createTransactionResult(
|
|
1337
|
-
[],
|
|
1338
|
-
false,
|
|
1339
|
-
params,
|
|
1340
|
-
"NONE",
|
|
1341
|
-
params.cycleType
|
|
1342
|
-
);
|
|
1343
|
-
}
|
|
1344
|
-
//withdraw from vesu
|
|
1345
|
-
const vesuAmountInBTC = new Web3Number(
|
|
1346
|
-
params.amount
|
|
1347
|
-
.dividedBy(collateralPrice.price)
|
|
1348
|
-
.toFixed(WBTC_TOKEN_DECIMALS),
|
|
1349
|
-
collateralToken.decimals
|
|
1350
|
-
);
|
|
1351
|
-
const proofsInfo = vesuAdapter.getProofs(false, this.getMerkleTree());
|
|
1352
|
-
const calls = [];
|
|
1353
|
-
const proofGroups = proofsInfo.proofs;
|
|
1354
|
-
const call = this.getManageCall(
|
|
1355
|
-
proofGroups,
|
|
1356
|
-
await proofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
1357
|
-
);
|
|
1358
|
-
calls.push(call);
|
|
1359
|
-
const swapProofsInfo = avnuAdapter.getProofs(
|
|
1360
|
-
false,
|
|
1361
|
-
this.getMerkleTree()
|
|
1362
|
-
);
|
|
1363
|
-
const swapProofGroups = swapProofsInfo.proofs;
|
|
1364
|
-
const swapCall = this.getManageCall(
|
|
1365
|
-
swapProofGroups,
|
|
1366
|
-
await swapProofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
1367
|
-
);
|
|
1368
|
-
calls.push(swapCall);
|
|
1369
|
-
return this.createTransactionResult(
|
|
1370
|
-
calls,
|
|
1371
|
-
true,
|
|
1372
|
-
params,
|
|
1373
|
-
"WITHDRAWAL",
|
|
1374
|
-
params.cycleType
|
|
1375
|
-
);
|
|
1376
|
-
} else if (
|
|
1377
|
-
params.to === Protocols.EXTENDED.name &&
|
|
1378
|
-
params.from === Protocols.VESU.name
|
|
1379
|
-
) {
|
|
1380
|
-
const isPriceDifferenceBetweenAvnuAndExtended =
|
|
1381
|
-
await this.checkPriceDifferenceBetweenAvnuAndExtended(
|
|
1382
|
-
extendedAdapter,
|
|
1383
|
-
vesuAdapter,
|
|
1384
|
-
avnuAdapter,
|
|
1385
|
-
PositionTypeAvnuExtended.CLOSE
|
|
1386
|
-
);
|
|
1387
|
-
if (!isPriceDifferenceBetweenAvnuAndExtended) {
|
|
1388
|
-
logger.warn(
|
|
1389
|
-
`price difference between avnu and extended doesn't fit the range for close position, ${avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing}`
|
|
1390
|
-
);
|
|
1391
|
-
return this.createTransactionResult(
|
|
1392
|
-
[],
|
|
1393
|
-
false,
|
|
1394
|
-
params,
|
|
1395
|
-
"NONE",
|
|
1396
|
-
params.cycleType
|
|
1397
|
-
);
|
|
1398
|
-
}
|
|
1399
|
-
const vesuAmountInBTC = new Web3Number(
|
|
1400
|
-
params.amount.dividedBy(collateralPrice.price).toNumber(),
|
|
1401
|
-
collateralToken.decimals
|
|
1402
|
-
);
|
|
1403
|
-
const proofsInfo = vesuAdapter.getProofs(false, this.getMerkleTree());
|
|
1404
|
-
const calls = [];
|
|
1405
|
-
const proofGroups = proofsInfo.proofs;
|
|
1406
|
-
const call = this.getManageCall(
|
|
1407
|
-
proofGroups,
|
|
1408
|
-
await proofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
1409
|
-
);
|
|
1410
|
-
calls.push(call);
|
|
1411
|
-
const swapProofsInfo = avnuAdapter.getProofs(
|
|
1412
|
-
false,
|
|
1413
|
-
this.getMerkleTree()
|
|
1414
|
-
);
|
|
1415
|
-
const swapProofGroups = swapProofsInfo.proofs;
|
|
1416
|
-
const swapCall = this.getManageCall(
|
|
1417
|
-
swapProofGroups,
|
|
1418
|
-
await swapProofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
1419
|
-
);
|
|
1420
|
-
calls.push(swapCall);
|
|
1421
|
-
const proofsInfoDeposit = extendedAdapter.getProofs(
|
|
1422
|
-
true,
|
|
1423
|
-
this.getMerkleTree()
|
|
1424
|
-
);
|
|
1425
|
-
//Deposit Amount would still be in usdc
|
|
1426
|
-
const proofGroupsDeposit = proofsInfoDeposit.proofs;
|
|
1427
|
-
const callDeposit = this.getManageCall(
|
|
1428
|
-
proofGroupsDeposit,
|
|
1429
|
-
await proofsInfoDeposit.callConstructor({ amount: params.amount })
|
|
1430
|
-
);
|
|
1431
|
-
calls.push(callDeposit);
|
|
1432
|
-
return this.createTransactionResult(
|
|
1433
|
-
calls,
|
|
1434
|
-
true,
|
|
1435
|
-
params,
|
|
1436
|
-
"DEPOSIT",
|
|
1437
|
-
params.cycleType
|
|
1438
|
-
);
|
|
1439
|
-
}
|
|
1440
|
-
logger.error(
|
|
1441
|
-
`Unsupported assets movement: ${params.from} to ${params.to}`
|
|
1442
|
-
);
|
|
1443
|
-
return this.createTransactionResult(
|
|
1444
|
-
[],
|
|
1445
|
-
false,
|
|
1446
|
-
params,
|
|
1447
|
-
"NONE",
|
|
1448
|
-
params.cycleType
|
|
1449
|
-
);
|
|
1450
|
-
} catch (err) {
|
|
1451
|
-
logger.error(`error moving assets: ${err}`);
|
|
1452
|
-
return this.createTransactionResult(
|
|
1453
|
-
[],
|
|
1454
|
-
false,
|
|
1455
|
-
params,
|
|
1456
|
-
"NONE",
|
|
1457
|
-
params.cycleType
|
|
237
|
+
if (!extendedAdapter.adapter || !(extendedAdapter.adapter as ExtendedAdapter).client) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
`${this.getTag()} Extended adapter client not initialized.`
|
|
1458
240
|
);
|
|
1459
241
|
}
|
|
242
|
+
return extendedAdapter.adapter as ExtendedAdapter;
|
|
1460
243
|
}
|
|
1461
244
|
|
|
1462
|
-
async
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
false,
|
|
1470
|
-
{
|
|
1471
|
-
from: Protocols.VAULT.name,
|
|
1472
|
-
to: Protocols.VAULT.name,
|
|
1473
|
-
amount: new Web3Number(0, 0),
|
|
1474
|
-
},
|
|
1475
|
-
"NONE",
|
|
1476
|
-
CycleType.INVESTMENT
|
|
1477
|
-
);
|
|
1478
|
-
} catch (err) {
|
|
1479
|
-
logger.error(`error handling deposit: ${err}`);
|
|
1480
|
-
return this.createTransactionResult(
|
|
1481
|
-
[],
|
|
1482
|
-
false,
|
|
1483
|
-
{
|
|
1484
|
-
from: Protocols.VAULT.name,
|
|
1485
|
-
to: Protocols.VAULT.name,
|
|
1486
|
-
amount: new Web3Number(0, 0),
|
|
1487
|
-
},
|
|
1488
|
-
"NONE",
|
|
1489
|
-
CycleType.INVESTMENT
|
|
245
|
+
async getUsdcToUsdceAdapter(): Promise<UsdcToUsdceAdapter> {
|
|
246
|
+
const usdcToUsdceAdapter = this.metadata.additionalInfo.adapters.find(
|
|
247
|
+
(adapter) => adapter.adapter.name === UsdcToUsdceAdapter.name,
|
|
248
|
+
);
|
|
249
|
+
if (!usdcToUsdceAdapter) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`${this.getTag()} UsdcToUsdce adapter not configured in metadata.`
|
|
1490
252
|
);
|
|
1491
253
|
}
|
|
254
|
+
return usdcToUsdceAdapter.adapter as UsdcToUsdceAdapter;
|
|
1492
255
|
}
|
|
1493
256
|
|
|
1494
257
|
/**
|
|
1495
|
-
*
|
|
1496
|
-
*
|
|
1497
|
-
*
|
|
1498
|
-
*
|
|
1499
|
-
* @param
|
|
1500
|
-
* @
|
|
258
|
+
* Creates an ExecutionService wired to this strategy's adapters and config.
|
|
259
|
+
* Use with `stateManager.solve()` to get a SolveResult, then pass it to
|
|
260
|
+
* `executionService.execute(solveResult)` for execution.
|
|
261
|
+
*
|
|
262
|
+
* @param onExecutionEvent - Optional callback for execution lifecycle events (DB persistence, alerts, etc.)
|
|
263
|
+
* @param extendedAcceptableSlippageBps - Slippage for Extended limit orders (default: 10 = 0.1%)
|
|
264
|
+
* @param maxPriceDivergenceBps - Max price divergence between Extended and AVNU (default: 50 = 0.5%)
|
|
1501
265
|
*/
|
|
1502
|
-
async
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
266
|
+
async createExecutionService(opts?: {
|
|
267
|
+
onExecutionEvent?: ExecutionCallback;
|
|
268
|
+
extendedAcceptableSlippageBps?: number;
|
|
269
|
+
maxPriceDivergenceBps?: number;
|
|
270
|
+
}): Promise<ExecutionService> {
|
|
271
|
+
const [
|
|
272
|
+
vesuAdapter,
|
|
273
|
+
vesuModifyPositionAdapter,
|
|
274
|
+
usdceTransferAdapter,
|
|
275
|
+
extendedAdapter,
|
|
276
|
+
avnuAdapter,
|
|
277
|
+
usdcToUsdceAdapter,
|
|
278
|
+
] =
|
|
279
|
+
await Promise.all([
|
|
280
|
+
this.getVesuAdapter(),
|
|
281
|
+
this.getVesuModifyPositionAdapter(),
|
|
282
|
+
this.getUsdceTransferAdapter(),
|
|
283
|
+
this.getExtendedAdapter(),
|
|
284
|
+
this.getAvnuAdapter(),
|
|
285
|
+
this.getUsdcToUsdceAdapter(),
|
|
286
|
+
]);
|
|
287
|
+
|
|
288
|
+
const executionConfig: ExecutionConfig = {
|
|
289
|
+
networkConfig: this.config,
|
|
290
|
+
pricer: this.pricer,
|
|
291
|
+
vesuAdapter,
|
|
292
|
+
vesuModifyPositionAdapter,
|
|
293
|
+
extendedAdapter,
|
|
294
|
+
avnuAdapter,
|
|
295
|
+
usdcToUsdceAdapter,
|
|
296
|
+
vaultAllocator: this.metadata.additionalInfo.vaultAllocator,
|
|
297
|
+
walletAddress: this.metadata.additionalInfo.walletAddress,
|
|
298
|
+
usdceTransferAdapter,
|
|
299
|
+
wbtcToken: this.wbtcToken,
|
|
300
|
+
usdcToken: this.usdcToken,
|
|
301
|
+
usdceToken: this.usdceToken,
|
|
302
|
+
getMerkleTree: () => this.getMerkleTree(),
|
|
303
|
+
getManageCall: (proofs, manageCalls) => this.getManageCall(proofs, manageCalls),
|
|
304
|
+
getBringLiquidityCall: (params) => this.getBringLiquidityCall(params),
|
|
305
|
+
onExecutionEvent: opts?.onExecutionEvent,
|
|
306
|
+
extendedAcceptableSlippageBps: opts?.extendedAcceptableSlippageBps,
|
|
307
|
+
maxPriceDivergenceBps: opts?.maxPriceDivergenceBps,
|
|
308
|
+
};
|
|
1514
309
|
|
|
1515
|
-
|
|
1516
|
-
logger.error(`error getting btc price avnu: ${btcPriceAvnu}`);
|
|
1517
|
-
return false;
|
|
1518
|
-
}
|
|
1519
|
-
logger.info(`price: ${price}`);
|
|
1520
|
-
logger.info(`btcPriceAvnu: ${btcPriceAvnu}`);
|
|
1521
|
-
const priceDifference = new Web3Number(
|
|
1522
|
-
price.minus(btcPriceAvnu).toFixed(2),
|
|
1523
|
-
0
|
|
1524
|
-
);
|
|
1525
|
-
logger.info(`priceDifference: ${priceDifference}`);
|
|
1526
|
-
if (priceDifference.isNegative()) {
|
|
1527
|
-
return false;
|
|
1528
|
-
}
|
|
1529
|
-
if (positionType === PositionTypeAvnuExtended.OPEN) {
|
|
1530
|
-
logger.info(
|
|
1531
|
-
`price difference between avnu and extended for open position: ${priceDifference.toNumber()}, minimumExtendedPriceDifferenceForSwapOpen: ${
|
|
1532
|
-
avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen
|
|
1533
|
-
}`
|
|
1534
|
-
);
|
|
1535
|
-
const result = priceDifference.greaterThanOrEqualTo(
|
|
1536
|
-
avnuAdapter.config.minimumExtendedPriceDifferenceForSwapOpen
|
|
1537
|
-
); // 500 for now
|
|
1538
|
-
logger.info(`result: ${result}`);
|
|
1539
|
-
return result;
|
|
1540
|
-
} else {
|
|
1541
|
-
logger.info(
|
|
1542
|
-
`price difference between avnu and extended for close position: ${priceDifference.toNumber()}, maximumExtendedPriceDifferenceForSwapClosing: ${
|
|
1543
|
-
avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing
|
|
1544
|
-
}`
|
|
1545
|
-
);
|
|
1546
|
-
const result = priceDifference.lessThanOrEqualTo(
|
|
1547
|
-
avnuAdapter.config.maximumExtendedPriceDifferenceForSwapClosing
|
|
1548
|
-
); // 1000 for now
|
|
1549
|
-
logger.info(`result: ${result}`);
|
|
1550
|
-
return result;
|
|
1551
|
-
}
|
|
310
|
+
return new ExecutionService(executionConfig);
|
|
1552
311
|
}
|
|
1553
312
|
|
|
1554
313
|
/**
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
314
|
+
* Calculates the total Assets Under Management across all adapters.
|
|
315
|
+
* Aggregates position values from every adapter, converts to the vault's
|
|
316
|
+
* base asset, and returns the net AUM along with the previous AUM snapshot
|
|
317
|
+
* and per-position breakdowns.
|
|
1558
318
|
*/
|
|
1559
|
-
async handleWithdraw(amount: Web3Number): Promise<TransactionResult[]> {
|
|
1560
|
-
try {
|
|
1561
|
-
const usdcBalanceVaultAllocator = await this.getUnusedBalance();
|
|
1562
|
-
const usdcBalanceDifference = amount
|
|
1563
|
-
.plus(BUFFER_USDC_IN_WITHDRAWAL)
|
|
1564
|
-
.minus(usdcBalanceVaultAllocator.usdValue);
|
|
1565
|
-
logger.info(`usdcBalanceDifference, ${usdcBalanceDifference.toNumber()}`);
|
|
1566
|
-
let calls: Call[] = [];
|
|
1567
|
-
let status: boolean = true;
|
|
1568
|
-
if (usdcBalanceDifference.lessThan(0)) {
|
|
1569
|
-
const withdrawCall = await this.getBringLiquidityCall({
|
|
1570
|
-
amount: usdcBalanceVaultAllocator.amount,
|
|
1571
|
-
});
|
|
1572
|
-
calls.push(withdrawCall);
|
|
1573
|
-
return [
|
|
1574
|
-
this.createTransactionResult(
|
|
1575
|
-
calls,
|
|
1576
|
-
true,
|
|
1577
|
-
{
|
|
1578
|
-
from: Protocols.VAULT.name,
|
|
1579
|
-
to: Protocols.NONE.name,
|
|
1580
|
-
amount: amount,
|
|
1581
|
-
},
|
|
1582
|
-
"WITHDRAWAL",
|
|
1583
|
-
CycleType.WITHDRAWAL
|
|
1584
|
-
),
|
|
1585
|
-
];
|
|
1586
|
-
}
|
|
1587
|
-
const vesuAdapter = await this.getVesuAdapter();
|
|
1588
|
-
const extendedAdapter = await this.getExtendedAdapter();
|
|
1589
|
-
if (!vesuAdapter || !extendedAdapter || !extendedAdapter.client) {
|
|
1590
|
-
status = false;
|
|
1591
|
-
logger.error(
|
|
1592
|
-
`vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
|
|
1593
|
-
);
|
|
1594
|
-
return [
|
|
1595
|
-
this.createTransactionResult(
|
|
1596
|
-
calls,
|
|
1597
|
-
status,
|
|
1598
|
-
{
|
|
1599
|
-
from: Protocols.VAULT.name,
|
|
1600
|
-
to: Protocols.NONE.name,
|
|
1601
|
-
amount: amount,
|
|
1602
|
-
},
|
|
1603
|
-
"NONE",
|
|
1604
|
-
CycleType.WITHDRAWAL
|
|
1605
|
-
),
|
|
1606
|
-
];
|
|
1607
|
-
}
|
|
1608
|
-
let transactionResults: TransactionResult[] = [];
|
|
1609
|
-
const { collateralTokenAmount } =
|
|
1610
|
-
await vesuAdapter.vesuAdapter.getAssetPrices();
|
|
1611
|
-
const { collateralPrice } = await this.getAssetPrices();
|
|
1612
|
-
const extendedPositon = await extendedAdapter.getAllOpenPositions();
|
|
1613
|
-
if (!extendedPositon) {
|
|
1614
|
-
status = false;
|
|
1615
|
-
logger.error("error getting extended position", extendedPositon);
|
|
1616
|
-
return [
|
|
1617
|
-
this.createTransactionResult(
|
|
1618
|
-
calls,
|
|
1619
|
-
status,
|
|
1620
|
-
{
|
|
1621
|
-
from: Protocols.VAULT.name,
|
|
1622
|
-
to: Protocols.NONE.name,
|
|
1623
|
-
amount: amount,
|
|
1624
|
-
},
|
|
1625
|
-
"NONE",
|
|
1626
|
-
CycleType.WITHDRAWAL
|
|
1627
|
-
),
|
|
1628
|
-
];
|
|
1629
|
-
}
|
|
1630
|
-
const amountDistributionForWithdrawal =
|
|
1631
|
-
await calculateAmountDistributionForWithdrawal(
|
|
1632
|
-
usdcBalanceDifference,
|
|
1633
|
-
collateralPrice.price,
|
|
1634
|
-
collateralTokenAmount,
|
|
1635
|
-
extendedPositon
|
|
1636
|
-
);
|
|
1637
|
-
if (!amountDistributionForWithdrawal) {
|
|
1638
|
-
status = false;
|
|
1639
|
-
logger.error(
|
|
1640
|
-
`error calculating amount distribution for withdrawal: ${amountDistributionForWithdrawal}`
|
|
1641
|
-
);
|
|
1642
|
-
return [
|
|
1643
|
-
this.createTransactionResult(
|
|
1644
|
-
calls,
|
|
1645
|
-
status,
|
|
1646
|
-
{
|
|
1647
|
-
from: Protocols.VAULT.name,
|
|
1648
|
-
to: Protocols.NONE.name,
|
|
1649
|
-
amount: amount,
|
|
1650
|
-
},
|
|
1651
|
-
"NONE",
|
|
1652
|
-
CycleType.WITHDRAWAL
|
|
1653
|
-
),
|
|
1654
|
-
];
|
|
1655
|
-
}
|
|
1656
|
-
const { vesu_amount, extended_amount } = amountDistributionForWithdrawal;
|
|
1657
|
-
|
|
1658
|
-
if (status && vesu_amount.greaterThan(0)) {
|
|
1659
|
-
const {
|
|
1660
|
-
calls: vesuCalls,
|
|
1661
|
-
status: vesuStatus,
|
|
1662
|
-
transactionMetadata: vesuTransactionMetadata,
|
|
1663
|
-
} = await this.moveAssets(
|
|
1664
|
-
{
|
|
1665
|
-
amount: vesu_amount,
|
|
1666
|
-
from: Protocols.VESU.name,
|
|
1667
|
-
to: Protocols.VAULT.name,
|
|
1668
|
-
cycleType: CycleType.WITHDRAWAL,
|
|
1669
|
-
},
|
|
1670
|
-
extendedAdapter,
|
|
1671
|
-
vesuAdapter
|
|
1672
|
-
);
|
|
1673
|
-
status = vesuStatus;
|
|
1674
|
-
transactionResults.push({
|
|
1675
|
-
status: vesuStatus,
|
|
1676
|
-
calls: vesuCalls,
|
|
1677
|
-
transactionMetadata: vesuTransactionMetadata,
|
|
1678
|
-
});
|
|
1679
|
-
}
|
|
1680
|
-
if (status && extended_amount.greaterThan(0)) {
|
|
1681
|
-
const {
|
|
1682
|
-
calls: extendedCalls,
|
|
1683
|
-
status: extendedStatus,
|
|
1684
|
-
transactionMetadata: extendedTransactionMetadata,
|
|
1685
|
-
} = await this.moveAssets(
|
|
1686
|
-
{
|
|
1687
|
-
amount: extended_amount,
|
|
1688
|
-
from: Protocols.EXTENDED.name,
|
|
1689
|
-
to: Protocols.VAULT.name,
|
|
1690
|
-
cycleType: CycleType.WITHDRAWAL,
|
|
1691
|
-
},
|
|
1692
|
-
extendedAdapter,
|
|
1693
|
-
vesuAdapter
|
|
1694
|
-
);
|
|
1695
|
-
status = extendedStatus;
|
|
1696
|
-
if (status) {
|
|
1697
|
-
transactionResults.push({
|
|
1698
|
-
status: extendedStatus,
|
|
1699
|
-
calls: extendedCalls,
|
|
1700
|
-
transactionMetadata: extendedTransactionMetadata,
|
|
1701
|
-
});
|
|
1702
|
-
} else {
|
|
1703
|
-
logger.error(
|
|
1704
|
-
"error moving assets to vault: extendedStatus: ${extendedStatus}"
|
|
1705
|
-
);
|
|
1706
|
-
return [
|
|
1707
|
-
this.createTransactionResult(
|
|
1708
|
-
[],
|
|
1709
|
-
status,
|
|
1710
|
-
{
|
|
1711
|
-
from: Protocols.VAULT.name,
|
|
1712
|
-
to: Protocols.NONE.name,
|
|
1713
|
-
amount: amount,
|
|
1714
|
-
},
|
|
1715
|
-
"NONE",
|
|
1716
|
-
CycleType.WITHDRAWAL
|
|
1717
|
-
),
|
|
1718
|
-
];
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
const withdrawCall = await this.getBringLiquidityCall({
|
|
1722
|
-
amount: amount,
|
|
1723
|
-
});
|
|
1724
|
-
logger.info("withdraw call", withdrawCall);
|
|
1725
|
-
transactionResults.push({
|
|
1726
|
-
status: status,
|
|
1727
|
-
calls: [withdrawCall],
|
|
1728
|
-
transactionMetadata: {
|
|
1729
|
-
protocolFrom: Protocols.VAULT.name,
|
|
1730
|
-
protocolTo: Protocols.NONE.name,
|
|
1731
|
-
transactionType: "WITHDRAWAL",
|
|
1732
|
-
usdAmount: amount.toFixed(),
|
|
1733
|
-
status: "PENDING",
|
|
1734
|
-
cycleType: CycleType.WITHDRAWAL,
|
|
1735
|
-
},
|
|
1736
|
-
});
|
|
1737
|
-
return transactionResults;
|
|
1738
|
-
} catch (err) {
|
|
1739
|
-
logger.error(`error handling withdrawal: ${err}`);
|
|
1740
|
-
return [
|
|
1741
|
-
this.createTransactionResult(
|
|
1742
|
-
[],
|
|
1743
|
-
false,
|
|
1744
|
-
{
|
|
1745
|
-
from: Protocols.VAULT.name,
|
|
1746
|
-
to: Protocols.NONE.name,
|
|
1747
|
-
amount: amount,
|
|
1748
|
-
},
|
|
1749
|
-
"NONE",
|
|
1750
|
-
CycleType.WITHDRAWAL
|
|
1751
|
-
),
|
|
1752
|
-
];
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
319
|
async getAUM(): Promise<{
|
|
1757
320
|
net: SingleTokenInfo;
|
|
1758
321
|
prevAum: Web3Number;
|
|
@@ -1760,10 +323,14 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
1760
323
|
}> {
|
|
1761
324
|
const allPositions: PositionInfo[] = [];
|
|
1762
325
|
for (let adapter of this.metadata.additionalInfo.adapters) {
|
|
1763
|
-
|
|
1764
|
-
|
|
326
|
+
let positions = await adapter.adapter.getPositions();
|
|
327
|
+
if (positions && positions.length > 0) {
|
|
328
|
+
const filteredPositions = positions.filter((position) => {
|
|
329
|
+
return position.tokenInfo.address !== this.usdceToken.address;
|
|
330
|
+
});
|
|
331
|
+
allPositions.push(...filteredPositions);
|
|
332
|
+
}
|
|
1765
333
|
}
|
|
1766
|
-
|
|
1767
334
|
const assetPrice = await this.pricer.getPrice(this.asset().symbol);
|
|
1768
335
|
let netAUM = new Web3Number(0, this.asset().decimals);
|
|
1769
336
|
for (let position of allPositions) {
|
|
@@ -1774,6 +341,16 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
1774
341
|
}
|
|
1775
342
|
}
|
|
1776
343
|
|
|
344
|
+
// ! IMO should also include USDC in wallet.
|
|
345
|
+
const walletHoldings = await this.getWalletHoldings();
|
|
346
|
+
for (let holding of walletHoldings) {
|
|
347
|
+
if (holding.tokenInfo.address.eq(this.asset().address)) {
|
|
348
|
+
netAUM = netAUM.plus(holding.amount);
|
|
349
|
+
} else {
|
|
350
|
+
netAUM = netAUM.plus(holding.usdValue / assetPrice.price);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
1777
354
|
const prevAum = await this.getPrevAUM();
|
|
1778
355
|
const realAUM: PositionInfo = {
|
|
1779
356
|
tokenInfo: this.asset(),
|
|
@@ -1804,140 +381,101 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
1804
381
|
};
|
|
1805
382
|
}
|
|
1806
383
|
|
|
1807
|
-
async processTransactionDataFromSDK(
|
|
1808
|
-
txnData: TransactionResult<any>[]
|
|
1809
|
-
): Promise<{
|
|
1810
|
-
callsToBeExecutedFinal: Call[];
|
|
1811
|
-
txnMetadata: TransactionMetadata[];
|
|
1812
|
-
} | null> {
|
|
1813
|
-
try {
|
|
1814
|
-
const txnsToBeExecuted = txnData.filter((txn) => {
|
|
1815
|
-
return (
|
|
1816
|
-
txn.transactionMetadata.transactionType !== "NONE" &&
|
|
1817
|
-
txn.transactionMetadata.protocolFrom !== "" &&
|
|
1818
|
-
txn.transactionMetadata.protocolTo !== ""
|
|
1819
|
-
);
|
|
1820
|
-
});
|
|
1821
|
-
const callsToBeExecutedFinal = txnsToBeExecuted.flatMap(
|
|
1822
|
-
(txn) => txn.calls
|
|
1823
|
-
);
|
|
1824
|
-
const txnMetadata = txnsToBeExecuted.map(
|
|
1825
|
-
(txn) => txn.transactionMetadata
|
|
1826
|
-
);
|
|
1827
|
-
return { callsToBeExecutedFinal, txnMetadata };
|
|
1828
|
-
} catch (err) {
|
|
1829
|
-
logger.error(`error processing transaction data from SDK: ${err}`);
|
|
1830
|
-
return null;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
|
|
1834
|
-
async processTransactionMetadata(
|
|
1835
|
-
txnMetadata: TransactionMetadata[],
|
|
1836
|
-
extendedIntentFulfilled: boolean
|
|
1837
|
-
): Promise<TransactionMetadata[] | null> {
|
|
1838
|
-
try {
|
|
1839
|
-
const txnMetadataNew = txnMetadata.map((txn) => {
|
|
1840
|
-
const isExtendedProtocol =
|
|
1841
|
-
txn.protocolFrom === Protocols.EXTENDED.name ||
|
|
1842
|
-
txn.protocolTo === Protocols.EXTENDED.name;
|
|
1843
|
-
// Only update status for extended protocol transactions since thsoe only cause delays
|
|
1844
|
-
if (isExtendedProtocol) {
|
|
1845
|
-
txn.status = extendedIntentFulfilled ? "COMPLETED" : "PENDING";
|
|
1846
|
-
} else {
|
|
1847
|
-
txn.status = "COMPLETED";
|
|
1848
|
-
}
|
|
1849
|
-
return txn;
|
|
1850
|
-
});
|
|
1851
|
-
return txnMetadataNew;
|
|
1852
|
-
} catch (err) {
|
|
1853
|
-
logger.error(`error processing transaction data from SDK: ${err}`);
|
|
1854
|
-
return null;
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Computes the maximum additional USDC that can be borrowed from Vesu
|
|
387
|
+
* while keeping the strategy profitable. Uses the Extended funding rate
|
|
388
|
+
* and Vesu supply APY to derive a break-even borrow APY, then queries
|
|
389
|
+
* Vesu for the max borrowable amount at that rate.
|
|
390
|
+
*/
|
|
1858
391
|
async getMaxBorrowableAmount(): Promise<Web3Number> {
|
|
1859
|
-
const vesuAdapter = await this.
|
|
392
|
+
const vesuAdapter = await this.getVesuModifyPositionAdapter();
|
|
1860
393
|
const extendedAdapter = await this.getExtendedAdapter();
|
|
1861
|
-
if (!vesuAdapter || !extendedAdapter) {
|
|
1862
|
-
return new Web3Number(0, 0);
|
|
1863
|
-
}
|
|
1864
394
|
const extendedFundingRate = new Web3Number(
|
|
1865
395
|
(await extendedAdapter.getNetAPY()).toFixed(4),
|
|
1866
|
-
0
|
|
396
|
+
0,
|
|
1867
397
|
);
|
|
1868
398
|
const extendedPositions = await extendedAdapter.getAllOpenPositions();
|
|
1869
399
|
if (!extendedPositions || extendedPositions.length === 0) {
|
|
1870
|
-
logger.
|
|
400
|
+
logger.warn(`${this.getTag()} getMaxBorrowableAmount: no extended positions found`);
|
|
1871
401
|
return new Web3Number(0, 0);
|
|
1872
402
|
}
|
|
1873
403
|
const extendePositionSizeUSD = new Web3Number(
|
|
1874
404
|
extendedPositions[0].value || 0,
|
|
1875
|
-
0
|
|
405
|
+
0,
|
|
1876
406
|
);
|
|
1877
407
|
const vesuPositions = await vesuAdapter.getPositions();
|
|
1878
408
|
const vesuSupplyApy = vesuPositions[0].apy.apy;
|
|
1879
409
|
const vesuCollateralSizeUSD = new Web3Number(
|
|
1880
410
|
vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
1881
|
-
USDC_TOKEN_DECIMALS
|
|
411
|
+
USDC_TOKEN_DECIMALS,
|
|
1882
412
|
);
|
|
1883
413
|
const vesuDebtSizeUSD = new Web3Number(
|
|
1884
414
|
vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
1885
|
-
USDC_TOKEN_DECIMALS
|
|
415
|
+
USDC_TOKEN_DECIMALS,
|
|
1886
416
|
);
|
|
1887
417
|
const num1 = extendePositionSizeUSD.multipliedBy(extendedFundingRate);
|
|
1888
418
|
const num2 = vesuCollateralSizeUSD.multipliedBy(vesuSupplyApy);
|
|
1889
419
|
const num3 = vesuDebtSizeUSD.abs();
|
|
1890
420
|
const maxBorrowApy = num1.plus(num2).minus(0.1).dividedBy(num3);
|
|
1891
|
-
const
|
|
1892
|
-
await vesuAdapter.
|
|
421
|
+
const vesuMaxBorrowableResult =
|
|
422
|
+
await vesuAdapter._vesuAdapter.getMaxBorrowableByInterestRate(
|
|
1893
423
|
this.config,
|
|
1894
424
|
vesuAdapter.config.debt,
|
|
1895
|
-
maxBorrowApy.toNumber()
|
|
425
|
+
maxBorrowApy.toNumber(),
|
|
1896
426
|
);
|
|
1897
427
|
return new Web3Number(
|
|
1898
|
-
|
|
1899
|
-
USDC_TOKEN_DECIMALS
|
|
428
|
+
vesuMaxBorrowableResult.maxDebtToHave.toFixed(USDC_TOKEN_DECIMALS),
|
|
429
|
+
USDC_TOKEN_DECIMALS,
|
|
1900
430
|
);
|
|
1901
431
|
}
|
|
1902
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Returns the current health metrics for the strategy:
|
|
435
|
+
* [0] Vesu health factor (maxLTV / actualLTV) — higher is safer.
|
|
436
|
+
* [1] Extended margin ratio (as percentage) — higher means more margin available.
|
|
437
|
+
*/
|
|
1903
438
|
async getVesuHealthFactors(): Promise<number[]> {
|
|
1904
|
-
const vesuAdapter = await this.
|
|
439
|
+
const vesuAdapter = await this.getVesuModifyPositionAdapter();
|
|
1905
440
|
const extendedAdapter = await this.getExtendedAdapter();
|
|
1906
|
-
if (!vesuAdapter || !extendedAdapter) {
|
|
1907
|
-
return [0, 0];
|
|
1908
|
-
}
|
|
1909
441
|
const vesuPositions = await vesuAdapter.getPositions();
|
|
1910
442
|
const vesuCollateralSizeUSD = new Web3Number(
|
|
1911
443
|
vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
1912
|
-
0
|
|
444
|
+
0,
|
|
1913
445
|
);
|
|
1914
446
|
const vesuDebtSizeUSD = new Web3Number(
|
|
1915
447
|
vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
1916
|
-
0
|
|
448
|
+
0,
|
|
1917
449
|
);
|
|
1918
450
|
const actualLtv = vesuDebtSizeUSD.dividedBy(vesuCollateralSizeUSD).abs();
|
|
1919
|
-
logger.
|
|
451
|
+
logger.debug(`${this.getTag()} getVesuHealthFactors: actualLtv=${actualLtv.toNumber()}`);
|
|
1920
452
|
const maxLtv = new Web3Number(
|
|
1921
|
-
await vesuAdapter.
|
|
1922
|
-
4
|
|
453
|
+
await vesuAdapter._vesuAdapter.getLTVConfig(this.config),
|
|
454
|
+
4,
|
|
1923
455
|
);
|
|
1924
456
|
const healthFactor = new Web3Number(
|
|
1925
457
|
maxLtv.dividedBy(actualLtv).toFixed(4),
|
|
1926
|
-
4
|
|
458
|
+
4,
|
|
1927
459
|
);
|
|
1928
|
-
logger.
|
|
460
|
+
logger.debug(`${this.getTag()} getVesuHealthFactors: healthFactor=${healthFactor.toNumber()}`);
|
|
1929
461
|
const extendedBalance = await extendedAdapter.getExtendedDepositAmount();
|
|
1930
462
|
if (!extendedBalance) {
|
|
1931
463
|
return [0, 0];
|
|
1932
464
|
}
|
|
1933
465
|
const extendedLeverage = new Web3Number(
|
|
1934
466
|
(Number(extendedBalance.marginRatio) * 100).toFixed(4),
|
|
1935
|
-
4
|
|
467
|
+
4,
|
|
1936
468
|
);
|
|
1937
|
-
logger.
|
|
469
|
+
logger.debug(`${this.getTag()} getVesuHealthFactors: extendedLeverage=${extendedLeverage.toNumber()}`);
|
|
1938
470
|
return [healthFactor.toNumber(), extendedLeverage.toNumber()];
|
|
1939
471
|
}
|
|
1940
472
|
|
|
473
|
+
/**
|
|
474
|
+
* Calculates the weighted net APY of the strategy across all positions.
|
|
475
|
+
* Combines Vesu supply APY (scaled by 0.1 performance fee) and Extended
|
|
476
|
+
* position APY, weighted by their respective USD values.
|
|
477
|
+
* Also returns per-position APY splits.
|
|
478
|
+
*/
|
|
1941
479
|
async netAPY(): Promise<{
|
|
1942
480
|
net: number;
|
|
1943
481
|
splits: { apy: number; id: string }[];
|
|
@@ -1952,23 +490,14 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
1952
490
|
}
|
|
1953
491
|
}
|
|
1954
492
|
const extendedAdapter = await this.getExtendedAdapter();
|
|
1955
|
-
if (!extendedAdapter) {
|
|
1956
|
-
return {
|
|
1957
|
-
net: 0,
|
|
1958
|
-
splits: [],
|
|
1959
|
-
};
|
|
1960
|
-
}
|
|
1961
493
|
let vesuPositions = allPositions.filter(
|
|
1962
|
-
(item) => item.protocol === Protocols.VESU
|
|
494
|
+
(item) => item.protocol === Protocols.VESU,
|
|
1963
495
|
);
|
|
1964
496
|
vesuPositions.map((item) => {
|
|
1965
497
|
item.apy.apy = item.apy.apy * 0.1;
|
|
1966
498
|
});
|
|
1967
499
|
const extendedPositions = await extendedAdapter.getAllOpenPositions();
|
|
1968
|
-
|
|
1969
|
-
(token) => token.symbol === "USDC"
|
|
1970
|
-
);
|
|
1971
|
-
if (!extendedPositions || !usdcToken) {
|
|
500
|
+
if (!extendedPositions || !this.usdcToken) {
|
|
1972
501
|
return {
|
|
1973
502
|
net: 0,
|
|
1974
503
|
splits: [],
|
|
@@ -1981,19 +510,19 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
1981
510
|
const totalHoldingsUSDValue =
|
|
1982
511
|
allPositions.reduce((acc, curr) => acc + curr.usdValue, 0) +
|
|
1983
512
|
Number(extendedEquity);
|
|
1984
|
-
console.log(totalHoldingsUSDValue);
|
|
1985
513
|
const extendedPositionSizeMultipliedByApy =
|
|
1986
514
|
Number(extendedPosition.value) * extendedApy;
|
|
1987
515
|
let weightedAPYs =
|
|
1988
516
|
allPositions.reduce(
|
|
1989
517
|
(acc, curr) => acc + curr.apy.apy * curr.usdValue,
|
|
1990
|
-
0
|
|
518
|
+
0,
|
|
1991
519
|
) + extendedPositionSizeMultipliedByApy;
|
|
1992
|
-
console.log(weightedAPYs);
|
|
1993
520
|
const netAPY = weightedAPYs / totalHoldingsUSDValue;
|
|
1994
|
-
|
|
521
|
+
logger.debug(
|
|
522
|
+
`${this.getTag()} netAPY: holdingsUsd=${totalHoldingsUSDValue}, weightedApy=${weightedAPYs}, net=${netAPY}`,
|
|
523
|
+
);
|
|
1995
524
|
allPositions.push({
|
|
1996
|
-
tokenInfo: usdcToken,
|
|
525
|
+
tokenInfo: this.usdcToken,
|
|
1997
526
|
amount: new Web3Number(extendedPosition.size, 0),
|
|
1998
527
|
usdValue: Number(extendedEquity),
|
|
1999
528
|
apy: { apy: extendedApy, type: APYType.BASE },
|
|
@@ -2009,6 +538,10 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
2009
538
|
};
|
|
2010
539
|
}
|
|
2011
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Fetches the operator wallet's current holdings for USDC.e, USDC, and WBTC,
|
|
543
|
+
* returning each token's balance and USD value.
|
|
544
|
+
*/
|
|
2012
545
|
async getWalletHoldings(): Promise<
|
|
2013
546
|
{
|
|
2014
547
|
tokenInfo: TokenInfo;
|
|
@@ -2016,62 +549,47 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
2016
549
|
usdValue: number;
|
|
2017
550
|
}[]
|
|
2018
551
|
> {
|
|
2019
|
-
|
|
2020
|
-
(token) => token.symbol === "USDCe"
|
|
2021
|
-
);
|
|
2022
|
-
const wbtcToken = Global.getDefaultTokens().find(
|
|
2023
|
-
(token) => token.symbol === "WBTC"
|
|
2024
|
-
);
|
|
2025
|
-
const usdcToken = Global.getDefaultTokens().find(
|
|
2026
|
-
(token) => token.symbol === "USDC"
|
|
2027
|
-
);
|
|
2028
|
-
if (!usdceToken || !wbtcToken || !usdcToken) {
|
|
552
|
+
if (!this.usdceToken || !this.wbtcToken || !this.usdcToken) {
|
|
2029
553
|
return [];
|
|
2030
554
|
}
|
|
2031
555
|
const walletAddress = this.metadata.additionalInfo.walletAddress;
|
|
2032
556
|
const usdceWalletBalance = await new ERC20(this.config).balanceOf(
|
|
2033
|
-
usdceToken.address,
|
|
557
|
+
this.usdceToken.address,
|
|
2034
558
|
walletAddress,
|
|
2035
|
-
usdceToken.decimals
|
|
559
|
+
this.usdceToken.decimals,
|
|
2036
560
|
);
|
|
2037
561
|
const usdcWalletBalance = await new ERC20(this.config).balanceOf(
|
|
2038
|
-
usdcToken.address,
|
|
2039
|
-
walletAddress,
|
|
2040
|
-
usdcToken.decimals
|
|
2041
|
-
);
|
|
2042
|
-
const wbtcWalletBalance = await new ERC20(this.config).balanceOf(
|
|
2043
|
-
wbtcToken.address,
|
|
562
|
+
this.usdcToken.address,
|
|
2044
563
|
walletAddress,
|
|
2045
|
-
|
|
564
|
+
this.usdcToken.decimals,
|
|
2046
565
|
);
|
|
2047
|
-
const price = await this.pricer.getPrice(usdceToken.symbol);
|
|
2048
|
-
const wbtcPrice = await this.pricer.getPrice(wbtcToken.symbol);
|
|
566
|
+
const price = await this.pricer.getPrice(this.usdceToken.symbol);
|
|
567
|
+
const wbtcPrice = await this.pricer.getPrice(this.wbtcToken.symbol);
|
|
2049
568
|
const usdceUsdValue =
|
|
2050
|
-
Number(usdceWalletBalance.toFixed(usdceToken.decimals)) *
|
|
569
|
+
Number(usdceWalletBalance.toFixed(this.usdceToken.decimals)) *
|
|
570
|
+
price.price;
|
|
2051
571
|
const usdcUsdValue =
|
|
2052
|
-
Number(usdcWalletBalance.toFixed(usdcToken.decimals)) * price.price;
|
|
2053
|
-
const wbtcUsdValue =
|
|
2054
|
-
Number(wbtcWalletBalance.toFixed(wbtcToken.decimals)) * wbtcPrice.price;
|
|
572
|
+
Number(usdcWalletBalance.toFixed(this.usdcToken.decimals)) * price.price;
|
|
2055
573
|
return [
|
|
2056
574
|
{
|
|
2057
|
-
tokenInfo: usdceToken,
|
|
575
|
+
tokenInfo: this.usdceToken,
|
|
2058
576
|
amount: usdceWalletBalance,
|
|
2059
577
|
usdValue: usdceUsdValue,
|
|
2060
578
|
},
|
|
2061
579
|
{
|
|
2062
|
-
tokenInfo: usdcToken,
|
|
580
|
+
tokenInfo: this.usdcToken,
|
|
2063
581
|
amount: usdcWalletBalance,
|
|
2064
582
|
usdValue: usdcUsdValue,
|
|
2065
|
-
}
|
|
2066
|
-
{
|
|
2067
|
-
tokenInfo: wbtcToken,
|
|
2068
|
-
amount: wbtcWalletBalance,
|
|
2069
|
-
usdValue: wbtcUsdValue,
|
|
2070
|
-
},
|
|
583
|
+
}
|
|
2071
584
|
];
|
|
2072
585
|
}
|
|
2073
586
|
}
|
|
2074
587
|
|
|
588
|
+
/**
|
|
589
|
+
* Configures all adapters (Vesu, Extended, Avnu, UsdcToUsdce, TokenTransfer)
|
|
590
|
+
* and registers their leaf adapters on the vault settings. This is the central
|
|
591
|
+
* wiring function that connects the strategy to its underlying protocol adapters.
|
|
592
|
+
*/
|
|
2075
593
|
function getLooperSettings(
|
|
2076
594
|
lstSymbol: string,
|
|
2077
595
|
underlyingSymbol: string,
|
|
@@ -2084,15 +602,18 @@ function getLooperSettings(
|
|
|
2084
602
|
minimumVesuMovementAmount: number,
|
|
2085
603
|
minimumExtendedRetriesDelayForOrderStatus: number,
|
|
2086
604
|
minimumExtendedPriceDifferenceForSwapOpen: number,
|
|
2087
|
-
maximumExtendedPriceDifferenceForSwapClosing: number
|
|
605
|
+
maximumExtendedPriceDifferenceForSwapClosing: number,
|
|
2088
606
|
) {
|
|
2089
607
|
vaultSettings.leafAdapters = [];
|
|
2090
608
|
|
|
2091
609
|
const wbtcToken = Global.getDefaultTokens().find(
|
|
2092
|
-
(token) => token.symbol === lstSymbol
|
|
610
|
+
(token) => token.symbol === lstSymbol,
|
|
2093
611
|
)!;
|
|
2094
612
|
const usdcToken = Global.getDefaultTokens().find(
|
|
2095
|
-
(token) => token.symbol === underlyingSymbol
|
|
613
|
+
(token) => token.symbol === underlyingSymbol,
|
|
614
|
+
)!;
|
|
615
|
+
const usdceToken = Global.getDefaultTokens().find(
|
|
616
|
+
(token) => token.symbol === "USDC.e",
|
|
2096
617
|
)!;
|
|
2097
618
|
|
|
2098
619
|
const baseAdapterConfig: BaseAdapterConfig = {
|
|
@@ -2119,9 +640,19 @@ function getLooperSettings(
|
|
|
2119
640
|
maximumExtendedPriceDifferenceForSwapClosing,
|
|
2120
641
|
});
|
|
2121
642
|
|
|
643
|
+
const usdcToUsdceAdapter = new UsdcToUsdceAdapter({
|
|
644
|
+
...baseAdapterConfig,
|
|
645
|
+
supportedPositions: [
|
|
646
|
+
{ asset: usdcToken, isDebt: true },
|
|
647
|
+
{ asset: usdceToken, isDebt: false },
|
|
648
|
+
],
|
|
649
|
+
});
|
|
650
|
+
|
|
2122
651
|
const extendedAdapter = new ExtendedAdapter({
|
|
2123
652
|
...baseAdapterConfig,
|
|
2124
|
-
supportedPositions: [
|
|
653
|
+
supportedPositions: [
|
|
654
|
+
{ asset: usdceToken, isDebt: false },
|
|
655
|
+
],
|
|
2125
656
|
vaultIdExtended: vaultIdExtended,
|
|
2126
657
|
extendedContract: EXTENDED_CONTRACT,
|
|
2127
658
|
extendedBackendWriteUrl: extendedBackendWriteUrl,
|
|
@@ -2140,6 +671,7 @@ function getLooperSettings(
|
|
|
2140
671
|
poolId: pool1,
|
|
2141
672
|
collateral: wbtcToken,
|
|
2142
673
|
debt: usdcToken,
|
|
674
|
+
marginToken: usdcToken,
|
|
2143
675
|
targetHealthFactor: vaultSettings.targetHealthFactor,
|
|
2144
676
|
minHealthFactor: vaultSettings.minHealthFactor,
|
|
2145
677
|
quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
|
|
@@ -2151,8 +683,28 @@ function getLooperSettings(
|
|
|
2151
683
|
minimumVesuMovementAmount: minimumVesuMovementAmount ?? 5, //5 usdc
|
|
2152
684
|
});
|
|
2153
685
|
|
|
2154
|
-
const
|
|
686
|
+
const vesuModifyPositionMaxLtv = VesuConfig.maxLtv;
|
|
687
|
+
const vesuModifyPositionTargetLtv = VesuConfig.targetLtv;
|
|
688
|
+
const vesuModifyPositionAdapter = new VesuModifyPositionAdapter({
|
|
689
|
+
poolId: pool1,
|
|
690
|
+
collateral: wbtcToken,
|
|
691
|
+
debt: usdcToken,
|
|
692
|
+
targetLtv: vesuModifyPositionTargetLtv,
|
|
693
|
+
maxLtv: vesuModifyPositionMaxLtv,
|
|
2155
694
|
...baseAdapterConfig,
|
|
695
|
+
supportedPositions: [
|
|
696
|
+
{ asset: wbtcToken, isDebt: false },
|
|
697
|
+
{ asset: usdcToken, isDebt: true },
|
|
698
|
+
],
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
// Transfers USDC between the vault allocator (fromAddress) and the operator wallet (toAddress)
|
|
702
|
+
const usdceTransferAdapter = new TokenTransferAdapter({
|
|
703
|
+
...baseAdapterConfig,
|
|
704
|
+
baseToken: usdceToken,
|
|
705
|
+
supportedPositions: [{ asset: usdceToken, isDebt: false }],
|
|
706
|
+
fromAddress: vaultSettings.vaultAllocator,
|
|
707
|
+
toAddress: ContractAddr.from(vaultSettings.walletAddress),
|
|
2156
708
|
});
|
|
2157
709
|
|
|
2158
710
|
vaultSettings.adapters.push({
|
|
@@ -2161,8 +713,18 @@ function getLooperSettings(
|
|
|
2161
713
|
});
|
|
2162
714
|
|
|
2163
715
|
vaultSettings.adapters.push({
|
|
2164
|
-
id: `${
|
|
2165
|
-
adapter:
|
|
716
|
+
id: `${vesuModifyPositionAdapter.name}_${wbtcToken.symbol}_${usdcToken.symbol}`,
|
|
717
|
+
adapter: vesuModifyPositionAdapter,
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
vaultSettings.adapters.push({
|
|
721
|
+
id: `${usdceTransferAdapter.name}_${usdceToken.symbol}`,
|
|
722
|
+
adapter: usdceTransferAdapter,
|
|
723
|
+
});
|
|
724
|
+
|
|
725
|
+
vaultSettings.adapters.push({
|
|
726
|
+
id: `${usdcToUsdceAdapter.name}_${usdceToken.symbol}_${usdcToken.symbol}`,
|
|
727
|
+
adapter: usdcToUsdceAdapter,
|
|
2166
728
|
});
|
|
2167
729
|
|
|
2168
730
|
vaultSettings.adapters.push({
|
|
@@ -2185,26 +747,29 @@ function getLooperSettings(
|
|
|
2185
747
|
|
|
2186
748
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getDepositLeaf());
|
|
2187
749
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getWithdrawLeaf());
|
|
750
|
+
vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getDepositLeaf());
|
|
751
|
+
vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getWithdrawLeaf());
|
|
2188
752
|
vaultSettings.leafAdapters.push(() => extendedAdapter.getDepositLeaf());
|
|
2189
|
-
vaultSettings.leafAdapters.push(() =>
|
|
2190
|
-
|
|
2191
|
-
);
|
|
753
|
+
vaultSettings.leafAdapters.push(() => usdcToUsdceAdapter.getDepositLeaf());
|
|
754
|
+
vaultSettings.leafAdapters.push(() => usdcToUsdceAdapter.getWithdrawLeaf());
|
|
2192
755
|
vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
|
|
2193
756
|
vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
|
|
757
|
+
vaultSettings.leafAdapters.push(() => usdceTransferAdapter.getDepositLeaf());
|
|
758
|
+
vaultSettings.leafAdapters.push(() => usdceTransferAdapter.getWithdrawLeaf());
|
|
2194
759
|
vaultSettings.leafAdapters.push(
|
|
2195
760
|
commonAdapter
|
|
2196
761
|
.getApproveAdapter(
|
|
2197
762
|
usdcToken.address,
|
|
2198
763
|
vaultSettings.vaultAddress,
|
|
2199
|
-
UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY
|
|
764
|
+
UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY,
|
|
2200
765
|
)
|
|
2201
|
-
.bind(commonAdapter)
|
|
766
|
+
.bind(commonAdapter),
|
|
2202
767
|
);
|
|
2203
768
|
|
|
2204
769
|
vaultSettings.leafAdapters.push(
|
|
2205
770
|
commonAdapter
|
|
2206
771
|
.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
|
|
2207
|
-
.bind(commonAdapter)
|
|
772
|
+
.bind(commonAdapter),
|
|
2208
773
|
);
|
|
2209
774
|
return vaultSettings;
|
|
2210
775
|
}
|
|
@@ -2215,7 +780,7 @@ function getDescription(tokenSymbol: string, underlyingSymbol: string) {
|
|
|
2215
780
|
|
|
2216
781
|
export default function VaultDescription(
|
|
2217
782
|
lstSymbol: string,
|
|
2218
|
-
underlyingSymbol: string
|
|
783
|
+
underlyingSymbol: string,
|
|
2219
784
|
) {
|
|
2220
785
|
const containerStyle = {
|
|
2221
786
|
maxWidth: "800px",
|
|
@@ -2299,36 +864,37 @@ export default function VaultDescription(
|
|
|
2299
864
|
|
|
2300
865
|
const re7UsdcPrimeDevansh: VesuExtendedStrategySettings = {
|
|
2301
866
|
vaultAddress: ContractAddr.from(
|
|
2302
|
-
"
|
|
867
|
+
"0x772d6cf5038c18ff5ab89f8945017bbf4d2c6959891339975c70a4f74ac6c8e",
|
|
2303
868
|
),
|
|
2304
869
|
manager: ContractAddr.from(
|
|
2305
|
-
"
|
|
870
|
+
"0x3340c9d7231838e2dccff72b9004f1598a74e65c74b954f07fe1ea19d04a625",
|
|
2306
871
|
),
|
|
2307
872
|
vaultAllocator: ContractAddr.from(
|
|
2308
|
-
"
|
|
873
|
+
"0x537353b35eee5ca2d9a45eb646977baddd4e89ce870a231dcada79884117292",
|
|
2309
874
|
),
|
|
2310
875
|
redeemRequestNFT: ContractAddr.from(
|
|
2311
|
-
"
|
|
876
|
+
"0x6117d1a8c72c0457948083757e1a17ee8c0833b969d5c959b629e5f8feb56ec",
|
|
2312
877
|
),
|
|
2313
878
|
aumOracle: ContractAddr.from(
|
|
2314
|
-
"
|
|
879
|
+
"0x6d7d68045bf5e0b5a4cec43241549851cb9645f7a73a20894152165dbe7083a",
|
|
2315
880
|
),
|
|
2316
881
|
leafAdapters: [],
|
|
2317
882
|
adapters: [],
|
|
2318
883
|
targetHealthFactor: 1.4,
|
|
2319
884
|
minHealthFactor: 1.05,
|
|
2320
885
|
underlyingToken: Global.getDefaultTokens().find(
|
|
2321
|
-
(token) => token.symbol === "USDC"
|
|
886
|
+
(token) => token.symbol === "USDC",
|
|
2322
887
|
)!,
|
|
2323
888
|
quoteAmountToFetchPrice: new Web3Number(
|
|
2324
889
|
"0.001",
|
|
2325
|
-
Global.getDefaultTokens().find((token) => token.symbol === "WBTC")
|
|
890
|
+
Global.getDefaultTokens().find((token) => token.symbol === "WBTC")!
|
|
891
|
+
.decimals,
|
|
2326
892
|
),
|
|
2327
893
|
borrowable_assets: [
|
|
2328
|
-
Global.getDefaultTokens().find((token) => token.symbol === "
|
|
894
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC")!,
|
|
2329
895
|
],
|
|
2330
896
|
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
2331
|
-
walletAddress:
|
|
897
|
+
walletAddress: '0x024b563C1C7d41B32BF4EFB9F38828508a65Be2d6e25268E9f63F22C5e9E51c5',
|
|
2332
898
|
};
|
|
2333
899
|
|
|
2334
900
|
export const VesuExtendedTestStrategies = (
|
|
@@ -2339,7 +905,7 @@ export const VesuExtendedTestStrategies = (
|
|
|
2339
905
|
minimumVesuMovementAmount: number,
|
|
2340
906
|
minimumExtendedRetriesDelayForOrderStatus: number,
|
|
2341
907
|
minimumExtendedPriceDifferenceForSwapOpen: number,
|
|
2342
|
-
maximumExtendedPriceDifferenceForSwapClosing: number
|
|
908
|
+
maximumExtendedPriceDifferenceForSwapClosing: number,
|
|
2343
909
|
): IStrategyMetadata<VesuExtendedStrategySettings>[] => {
|
|
2344
910
|
return [
|
|
2345
911
|
getStrategySettingsVesuExtended(
|
|
@@ -2355,11 +921,15 @@ export const VesuExtendedTestStrategies = (
|
|
|
2355
921
|
minimumVesuMovementAmount,
|
|
2356
922
|
minimumExtendedRetriesDelayForOrderStatus,
|
|
2357
923
|
minimumExtendedPriceDifferenceForSwapOpen,
|
|
2358
|
-
maximumExtendedPriceDifferenceForSwapClosing
|
|
924
|
+
maximumExtendedPriceDifferenceForSwapClosing,
|
|
2359
925
|
),
|
|
2360
926
|
];
|
|
2361
927
|
};
|
|
2362
928
|
|
|
929
|
+
/**
|
|
930
|
+
* Constructs a complete IStrategyMetadata object for a Vesu-Extended strategy,
|
|
931
|
+
* including adapter wiring, risk configuration, FAQ, and UI description.
|
|
932
|
+
*/
|
|
2363
933
|
function getStrategySettingsVesuExtended(
|
|
2364
934
|
lstSymbol: string,
|
|
2365
935
|
underlyingSymbol: string,
|
|
@@ -2373,17 +943,22 @@ function getStrategySettingsVesuExtended(
|
|
|
2373
943
|
minimumVesuMovementAmount: number,
|
|
2374
944
|
minimumExtendedRetriesDelayForOrderStatus: number,
|
|
2375
945
|
minimumExtendedPriceDifferenceForSwapOpen: number,
|
|
2376
|
-
maximumExtendedPriceDifferenceForSwapClosing: number
|
|
946
|
+
maximumExtendedPriceDifferenceForSwapClosing: number,
|
|
2377
947
|
): IStrategyMetadata<VesuExtendedStrategySettings> {
|
|
2378
948
|
return {
|
|
949
|
+
id: `extended_${underlyingSymbol.toLowerCase()}_test`,
|
|
2379
950
|
name: `Extended Test ${underlyingSymbol}`,
|
|
2380
951
|
description: getDescription(lstSymbol, underlyingSymbol),
|
|
2381
952
|
address: addresses.vaultAddress,
|
|
2382
953
|
launchBlock: 0,
|
|
2383
954
|
type: "Other",
|
|
955
|
+
vaultType: {
|
|
956
|
+
type: VaultType.DELTA_NEUTRAL,
|
|
957
|
+
description: "Delta Neutral strategy using extended position on Vesu"
|
|
958
|
+
},
|
|
2384
959
|
depositTokens: [
|
|
2385
960
|
Global.getDefaultTokens().find(
|
|
2386
|
-
(token) => token.symbol ===
|
|
961
|
+
(token) => token.symbol === 'WBTC',
|
|
2387
962
|
)!,
|
|
2388
963
|
],
|
|
2389
964
|
additionalInfo: getLooperSettings(
|
|
@@ -2398,7 +973,7 @@ function getStrategySettingsVesuExtended(
|
|
|
2398
973
|
minimumVesuMovementAmount,
|
|
2399
974
|
minimumExtendedRetriesDelayForOrderStatus,
|
|
2400
975
|
minimumExtendedPriceDifferenceForSwapOpen,
|
|
2401
|
-
maximumExtendedPriceDifferenceForSwapClosing
|
|
976
|
+
maximumExtendedPriceDifferenceForSwapClosing,
|
|
2402
977
|
),
|
|
2403
978
|
risk: {
|
|
2404
979
|
riskFactor: _riskFactor,
|
|
@@ -2409,7 +984,6 @@ function getStrategySettingsVesuExtended(
|
|
|
2409
984
|
},
|
|
2410
985
|
auditUrl: AUDIT_URL,
|
|
2411
986
|
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
2412
|
-
maxTVL: Web3Number.fromWei(0, 18),
|
|
2413
987
|
contractDetails: getContractDetails(addresses),
|
|
2414
988
|
faqs: getFAQs(lstSymbol, underlyingSymbol, isLST),
|
|
2415
989
|
investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
|
|
@@ -2417,5 +991,27 @@ function getStrategySettingsVesuExtended(
|
|
|
2417
991
|
apyMethodology: isLST
|
|
2418
992
|
? "Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown."
|
|
2419
993
|
: "Current annualized APY in terms of base asset of the Yield Token. There is no additional fee taken by Troves on yield token APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.",
|
|
994
|
+
security: {
|
|
995
|
+
auditStatus: AuditStatus.NOT_AUDITED,
|
|
996
|
+
sourceCode: {
|
|
997
|
+
type: SourceCodeType.OPEN_SOURCE,
|
|
998
|
+
contractLink: AUDIT_URL,
|
|
999
|
+
},
|
|
1000
|
+
accessControl: {
|
|
1001
|
+
type: AccessControlType.MULTISIG_ACCOUNT,
|
|
1002
|
+
addresses: [addresses.vaultAddress],
|
|
1003
|
+
timeLock: "None",
|
|
1004
|
+
},
|
|
1005
|
+
},
|
|
1006
|
+
redemptionInfo: {
|
|
1007
|
+
instantWithdrawalVault: InstantWithdrawalVault.NO,
|
|
1008
|
+
redemptionsInfo: [{
|
|
1009
|
+
title: "Up to $500k",
|
|
1010
|
+
description: "1-24 hours",
|
|
1011
|
+
}],
|
|
1012
|
+
alerts: [],
|
|
1013
|
+
},
|
|
1014
|
+
usualTimeToEarnings: null,
|
|
1015
|
+
usualTimeToEarningsDescription: null,
|
|
2420
1016
|
};
|
|
2421
1017
|
}
|