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