@strkfarm/sdk 2.0.0-dev.3 → 2.0.0-dev.30
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 +78478 -45620
- package/dist/index.browser.mjs +19583 -9901
- package/dist/index.d.ts +3763 -1424
- package/dist/index.js +20980 -11063
- package/dist/index.mjs +20948 -11087
- 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 +28 -5
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +275 -59
- 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 +48 -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 +7 -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 +180 -265
- 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 +490 -316
- package/src/strategies/universal-adapters/index.ts +11 -8
- 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 +120 -82
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +476 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +1067 -704
- 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 +397 -204
- package/src/strategies/universal-strategy.tsx +1426 -1173
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +2233 -0
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +4087 -0
- package/src/strategies/vesu-extended-strategy/services/ltv-imbalance-rebalance-math.ts +783 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +38 -16
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +88 -0
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +1 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +5 -6
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +259 -103
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +688 -817
- package/src/strategies/vesu-rebalance.tsx +255 -152
- package/src/utils/cacheClass.ts +11 -2
- 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,25 +3,43 @@ 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
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
ExtendedSVKVesuStateManager,
|
|
19
|
+
StateManagerConfig,
|
|
20
|
+
} from "./services/extended-vesu-state-manager";
|
|
21
|
+
import { ExecutionService, ExecutionConfig } from "./services/executionService";
|
|
12
22
|
import { logger } from "@/utils";
|
|
13
23
|
import { AUDIT_URL } from "../universal-lst-muliplier-strategy";
|
|
14
24
|
import { getNoRiskTags } from "@/interfaces";
|
|
15
25
|
import { _riskFactor } from "../universal-lst-muliplier-strategy";
|
|
16
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
LIMIT_BALANCE,
|
|
28
|
+
USDC_TOKEN_DECIMALS,
|
|
29
|
+
WALLET_ADDRESS,
|
|
30
|
+
WBTC_TOKEN_DECIMALS,
|
|
31
|
+
} from "./utils/constants";
|
|
32
|
+
import { ExecutionCallback } from "./types/transaction-metadata";
|
|
17
33
|
import { PricerBase } from "@/modules/pricerBase";
|
|
18
34
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
19
35
|
import { Global } from "@/global";
|
|
20
36
|
import { ERC20 } from "@/modules";
|
|
21
|
-
import { Balance, OrderSide } from "@/modules/ExtendedWrapperSDk";
|
|
22
37
|
import { Protocols } from "@/interfaces";
|
|
23
38
|
import { MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP } from "./utils/constants";
|
|
24
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
getInvestmentSteps,
|
|
41
|
+
getFAQs,
|
|
42
|
+
} from "../universal-lst-muliplier-strategy";
|
|
25
43
|
import { getContractDetails } from "../universal-strategy";
|
|
26
44
|
import { highlightTextWithLinks } from "@/interfaces";
|
|
27
45
|
import { PositionInfo } from "../universal-adapters";
|
|
@@ -31,12 +49,11 @@ import { VesuPools } from "../universal-adapters";
|
|
|
31
49
|
import {
|
|
32
50
|
BaseAdapterConfig,
|
|
33
51
|
CommonAdapter,
|
|
34
|
-
|
|
52
|
+
TokenTransferAdapter,
|
|
53
|
+
VesuModifyPositionAdapter,
|
|
35
54
|
VesuMultiplyAdapter,
|
|
36
55
|
} from "../universal-adapters";
|
|
37
|
-
import { Operations } from "./services/operationService";
|
|
38
56
|
import {
|
|
39
|
-
AVNU_EXCHANGE,
|
|
40
57
|
AVNU_QUOTE_URL,
|
|
41
58
|
AVNU_MIDDLEWARE,
|
|
42
59
|
EXTENDED_CONTRACT,
|
|
@@ -45,18 +62,10 @@ import { PricerFromApi } from "@/modules";
|
|
|
45
62
|
import { ExtendedAdapter } from "../universal-adapters/extended-adapter";
|
|
46
63
|
import { SVKStrategy } from "../svk-strategy";
|
|
47
64
|
import { AvnuAdapter } from "../universal-adapters/avnu-adapter";
|
|
48
|
-
import {
|
|
49
|
-
calculateAmountDistribution,
|
|
50
|
-
calculateAmountDistributionForWithdrawal,
|
|
51
|
-
calculateVesuLeverage,
|
|
52
|
-
calculateVesUPositionSizeGivenExtended
|
|
53
|
-
} from "./utils/helper";
|
|
54
65
|
import { SingleTokenInfo } from "../base-strategy";
|
|
55
|
-
import {
|
|
66
|
+
import { VesuConfig } from "./utils/config.runtime";
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
export interface VesuExtendedStrategySettings
|
|
59
|
-
extends UniversalStrategySettings {
|
|
68
|
+
export interface VesuExtendedStrategySettings extends UniversalStrategySettings {
|
|
60
69
|
underlyingToken: TokenInfo;
|
|
61
70
|
borrowable_assets: TokenInfo[];
|
|
62
71
|
targetHealthFactor: number;
|
|
@@ -64,36 +73,80 @@ export interface VesuExtendedStrategySettings
|
|
|
64
73
|
minHealthFactor: number;
|
|
65
74
|
aumOracle: ContractAddr;
|
|
66
75
|
minimumWBTCDifferenceForAvnuSwap: number;
|
|
76
|
+
walletAddress: string;
|
|
67
77
|
}
|
|
68
78
|
|
|
69
79
|
export class VesuExtendedMultiplierStrategy<
|
|
70
|
-
S extends VesuExtendedStrategySettings
|
|
71
|
-
>
|
|
72
|
-
|
|
73
|
-
|
|
80
|
+
S extends VesuExtendedStrategySettings,
|
|
81
|
+
> extends SVKStrategy<S> {
|
|
82
|
+
public wbtcToken: TokenInfo;
|
|
83
|
+
public usdcToken: TokenInfo;
|
|
84
|
+
public readonly stateManager: ExtendedSVKVesuStateManager;
|
|
74
85
|
|
|
75
86
|
constructor(
|
|
76
87
|
config: IConfig,
|
|
77
88
|
pricer: PricerBase,
|
|
78
|
-
metadata: IStrategyMetadata<S
|
|
89
|
+
metadata: IStrategyMetadata<S>,
|
|
79
90
|
) {
|
|
80
91
|
super(config, pricer, metadata);
|
|
81
92
|
this.metadata.additionalInfo.adapters.forEach((adapter) => {
|
|
82
93
|
adapter.adapter.config.networkConfig = this.config;
|
|
83
94
|
adapter.adapter.config.pricer = this.pricer;
|
|
84
|
-
if ((adapter.adapter as
|
|
85
|
-
(adapter.adapter as
|
|
95
|
+
if ((adapter.adapter as any)._vesuAdapter) {
|
|
96
|
+
(adapter.adapter as any)._vesuAdapter.networkConfig =
|
|
86
97
|
this.config;
|
|
87
|
-
(adapter.adapter as
|
|
98
|
+
(adapter.adapter as any)._vesuAdapter.pricer =
|
|
88
99
|
this.pricer;
|
|
89
100
|
}
|
|
90
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);
|
|
91
140
|
}
|
|
92
141
|
|
|
93
142
|
getTag() {
|
|
94
143
|
return `${VesuExtendedMultiplierStrategy.name}:${this.metadata.name}`;
|
|
95
144
|
}
|
|
96
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
|
+
*/
|
|
97
150
|
async getAssetPrices() {
|
|
98
151
|
const wbtcToken = Global.getDefaultTokens().find(
|
|
99
152
|
(token) => token.symbol === "WBTC"
|
|
@@ -103,767 +156,157 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
103
156
|
)!;
|
|
104
157
|
const collateralPrice = await this.pricer.getPrice(wbtcToken.symbol);
|
|
105
158
|
const debtPrice = await this.pricer.getPrice(usdcToken.symbol);
|
|
106
|
-
return {
|
|
107
|
-
collateralPrice,
|
|
108
|
-
debtPrice,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async getUnusedBalanceUSDCE(): Promise<SingleTokenInfo> {
|
|
113
|
-
const usdceToken = Global.getDefaultTokens().find(
|
|
114
|
-
(token) => token.symbol === "USDCe"
|
|
115
|
-
)!;
|
|
116
|
-
const balance = await new ERC20(this.config).balanceOf(
|
|
117
|
-
usdceToken.address,
|
|
118
|
-
WALLET_ADDRESS,
|
|
119
|
-
usdceToken.decimals
|
|
120
|
-
);
|
|
121
|
-
const price = await this.pricer.getPrice(usdceToken.symbol);
|
|
122
|
-
const usdValue =
|
|
123
|
-
Number(balance.toFixed(usdceToken.decimals)) * price.price;
|
|
124
|
-
return {
|
|
125
|
-
tokenInfo: usdceToken,
|
|
126
|
-
amount: balance,
|
|
127
|
-
usdValue,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
159
|
|
|
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
|
+
}
|
|
131
170
|
|
|
132
|
-
|
|
133
|
-
const collateralToken = this.metadata.additionalInfo.borrowable_assets[0]!;
|
|
134
|
-
const balance = await new ERC20(this.config).balanceOf(
|
|
135
|
-
collateralToken.address,
|
|
136
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
137
|
-
collateralToken.decimals
|
|
138
|
-
);
|
|
139
|
-
const price = await this.pricer.getPrice(collateralToken.symbol);
|
|
140
|
-
const usdValue =
|
|
141
|
-
Number(balance.toFixed(collateralToken.decimals)) * price.price;
|
|
142
|
-
return {
|
|
143
|
-
tokenInfo: collateralToken,
|
|
144
|
-
amount: balance,
|
|
145
|
-
usdValue,
|
|
146
|
-
};
|
|
171
|
+
return { collateralPrice, debtPrice };
|
|
147
172
|
}
|
|
148
173
|
|
|
149
|
-
async
|
|
174
|
+
async getVesuMultiplyAdapter(): Promise<VesuMultiplyAdapter> {
|
|
150
175
|
const vesuAdapter = this.metadata.additionalInfo.adapters.find(
|
|
151
|
-
(adapter) => adapter.adapter.name === VesuMultiplyAdapter.name
|
|
176
|
+
(adapter) => adapter.adapter.name === VesuMultiplyAdapter.name,
|
|
152
177
|
);
|
|
153
178
|
if (!vesuAdapter) {
|
|
154
|
-
|
|
155
|
-
|
|
179
|
+
throw new Error(
|
|
180
|
+
`${this.getTag()} Vesu adapter not configured in metadata.`
|
|
181
|
+
);
|
|
156
182
|
}
|
|
157
183
|
return vesuAdapter.adapter as VesuMultiplyAdapter;
|
|
158
184
|
}
|
|
159
185
|
|
|
160
|
-
async
|
|
161
|
-
const
|
|
162
|
-
(adapter) => adapter.adapter.name ===
|
|
186
|
+
async getVesuModifyPositionAdapter(): Promise<VesuModifyPositionAdapter> {
|
|
187
|
+
const vesuModifyPositionAdapter = this.metadata.additionalInfo.adapters.find(
|
|
188
|
+
(adapter) => adapter.adapter.name === VesuModifyPositionAdapter.name,
|
|
163
189
|
);
|
|
164
|
-
if (!
|
|
165
|
-
|
|
166
|
-
|
|
190
|
+
if (!vesuModifyPositionAdapter) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
`${this.getTag()} Vesu modify position adapter not configured in metadata.`,
|
|
193
|
+
);
|
|
167
194
|
}
|
|
168
|
-
return
|
|
195
|
+
return vesuModifyPositionAdapter.adapter as VesuModifyPositionAdapter;
|
|
169
196
|
}
|
|
170
197
|
|
|
171
|
-
async
|
|
172
|
-
const
|
|
173
|
-
(adapter) => adapter.adapter.name ===
|
|
198
|
+
async getUsdcTransferAdapter(): Promise<TokenTransferAdapter> {
|
|
199
|
+
const usdcTransferAdapter = this.metadata.additionalInfo.adapters.find(
|
|
200
|
+
(adapter) => adapter.adapter.name === TokenTransferAdapter.name,
|
|
174
201
|
);
|
|
175
|
-
if (!
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
return extendedAdapter.adapter as ExtendedAdapter;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async moveAssetsToVaultAllocator(amount: Web3Number, extendedAdapter: ExtendedAdapter): Promise<Call[]> {
|
|
183
|
-
try {
|
|
184
|
-
const usdceToken = Global.getDefaultTokens().find(
|
|
185
|
-
(token) => token.symbol === "USDCe"
|
|
186
|
-
)!;
|
|
187
|
-
const approveCall = new ERC20(this.config).approve(
|
|
188
|
-
usdceToken.address,
|
|
189
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
190
|
-
amount
|
|
191
|
-
);
|
|
192
|
-
const transferCall = new ERC20(this.config).transfer(
|
|
193
|
-
usdceToken.address,
|
|
194
|
-
this.metadata.additionalInfo.vaultAllocator,
|
|
195
|
-
amount
|
|
196
|
-
);
|
|
197
|
-
const proofsInfo = extendedAdapter.getProofsForFromLegacySwap(
|
|
198
|
-
this.getMerkleTree()
|
|
199
|
-
);
|
|
200
|
-
const proofGroups = proofsInfo.proofs;
|
|
201
|
-
const call = this.getManageCall(
|
|
202
|
-
proofGroups,
|
|
203
|
-
await proofsInfo.callConstructor({ amount: amount })
|
|
202
|
+
if (!usdcTransferAdapter) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`${this.getTag()} Usdc transfer adapter not configured in metadata.`
|
|
204
205
|
);
|
|
205
|
-
return [approveCall, transferCall, call];
|
|
206
|
-
} catch (err) {
|
|
207
|
-
logger.error(`error moving assets to vault allocator: ${err}`);
|
|
208
|
-
return [];
|
|
209
206
|
}
|
|
207
|
+
return usdcTransferAdapter.adapter as TokenTransferAdapter;
|
|
210
208
|
}
|
|
211
209
|
|
|
212
|
-
async
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
vesuLeverage: number;
|
|
220
|
-
}> {
|
|
221
|
-
try {
|
|
222
|
-
const vesuAdapter = await this.getVesuAdapter();
|
|
223
|
-
const extendedAdapter = await this.getExtendedAdapter();
|
|
224
|
-
if (!vesuAdapter || !extendedAdapter || !extendedAdapter.client) {
|
|
225
|
-
logger.error(
|
|
226
|
-
`vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
|
|
227
|
-
);
|
|
228
|
-
return {
|
|
229
|
-
shouldInvest: false,
|
|
230
|
-
vesuAmount: new Web3Number(0, 0),
|
|
231
|
-
extendedAmount: new Web3Number(0, 0),
|
|
232
|
-
extendedLeverage: 0,
|
|
233
|
-
collateralPrice: 0,
|
|
234
|
-
debtPrice: 0,
|
|
235
|
-
vesuLeverage: 0,
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
const balance = await this.getUnusedBalance();
|
|
239
|
-
const usdcBalanceOnExtended = await extendedAdapter.getExtendedDepositAmount();
|
|
240
|
-
/** The LIMIT_BALANCE is the bffer amount to keep in the investing Cycle */
|
|
241
|
-
const amountToInvest = balance.amount.plus(usdcBalanceOnExtended?.availableForWithdrawal ?? 0).minus(LIMIT_BALANCE);
|
|
242
|
-
|
|
243
|
-
if (amountToInvest.lessThan(0)) {
|
|
244
|
-
return {
|
|
245
|
-
shouldInvest: false,
|
|
246
|
-
vesuAmount: new Web3Number(0, 0),
|
|
247
|
-
extendedAmount: new Web3Number(0, 0),
|
|
248
|
-
extendedLeverage: 0,
|
|
249
|
-
collateralPrice: 0,
|
|
250
|
-
debtPrice: 0,
|
|
251
|
-
vesuLeverage: 0,
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const extendedPositon = await extendedAdapter.getAllOpenPositions();
|
|
256
|
-
if (!extendedPositon) {
|
|
257
|
-
logger.error("error getting extended position to decide move assets");
|
|
258
|
-
return {
|
|
259
|
-
shouldInvest: false,
|
|
260
|
-
vesuAmount: new Web3Number(0, 0),
|
|
261
|
-
extendedAmount: new Web3Number(0, 0),
|
|
262
|
-
extendedLeverage: 0,
|
|
263
|
-
collateralPrice: 0,
|
|
264
|
-
debtPrice: 0,
|
|
265
|
-
vesuLeverage: 0,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
const { collateralTokenAmount } =
|
|
269
|
-
await vesuAdapter.vesuAdapter.getAssetPrices();
|
|
270
|
-
|
|
271
|
-
const {
|
|
272
|
-
collateralPrice,
|
|
273
|
-
debtPrice
|
|
274
|
-
} = await this.getAssetPrices();
|
|
275
|
-
const { vesu_amount, extended_amount, extended_leverage, vesu_leverage } =
|
|
276
|
-
await calculateAmountDistribution(
|
|
277
|
-
amountToInvest.toNumber(),
|
|
278
|
-
extendedAdapter.client,
|
|
279
|
-
extendedAdapter.config.extendedMarketName,
|
|
280
|
-
collateralPrice.price,
|
|
281
|
-
debtPrice.price,
|
|
282
|
-
collateralTokenAmount,
|
|
283
|
-
extendedPositon
|
|
284
|
-
);
|
|
285
|
-
if (
|
|
286
|
-
!vesu_amount ||
|
|
287
|
-
!extended_amount ||
|
|
288
|
-
!extended_leverage ||
|
|
289
|
-
!vesu_leverage
|
|
290
|
-
) {
|
|
291
|
-
logger.error(
|
|
292
|
-
`Not enough amount to invest: vesu_amount=${vesu_amount}, extended_amount=${extended_amount}`
|
|
293
|
-
);
|
|
294
|
-
return {
|
|
295
|
-
shouldInvest: false,
|
|
296
|
-
vesuAmount: new Web3Number(0, 0),
|
|
297
|
-
extendedAmount: new Web3Number(0, 0),
|
|
298
|
-
extendedLeverage: 0,
|
|
299
|
-
collateralPrice: 0,
|
|
300
|
-
debtPrice: 0,
|
|
301
|
-
vesuLeverage: 0,
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
return {
|
|
305
|
-
shouldInvest: true,
|
|
306
|
-
vesuAmount: vesu_amount,
|
|
307
|
-
extendedAmount: extended_amount,
|
|
308
|
-
extendedLeverage: extended_leverage,
|
|
309
|
-
vesuLeverage: vesu_leverage,
|
|
310
|
-
collateralPrice: collateralPrice.price,
|
|
311
|
-
debtPrice: debtPrice.price,
|
|
312
|
-
};
|
|
313
|
-
} catch (err) {
|
|
314
|
-
logger.error(`error deciding invest: ${err}`);
|
|
315
|
-
return {
|
|
316
|
-
shouldInvest: false,
|
|
317
|
-
vesuAmount: new Web3Number(0, 0),
|
|
318
|
-
extendedAmount: new Web3Number(0, 0),
|
|
319
|
-
extendedLeverage: 0,
|
|
320
|
-
collateralPrice: 0,
|
|
321
|
-
debtPrice: 0,
|
|
322
|
-
vesuLeverage: 0,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
async shouldMoveAssets(extendedAmount: Web3Number, vesuAmount: Web3Number): Promise<Call[]> {
|
|
328
|
-
try {
|
|
329
|
-
const vesuAdapter = await this.getVesuAdapter();
|
|
330
|
-
const extendedAdapter = await this.getExtendedAdapter();
|
|
331
|
-
let calls: Call[] = [];
|
|
332
|
-
if (!vesuAdapter || !extendedAdapter || !extendedAdapter.client) {
|
|
333
|
-
logger.error(
|
|
334
|
-
`vesu or extended adapter not found: vesuAdapter=${vesuAdapter}, extendedAdapter=${extendedAdapter}`
|
|
335
|
-
);
|
|
336
|
-
return calls;
|
|
337
|
-
}
|
|
338
|
-
if (extendedAmount.lessThan(0)) {
|
|
339
|
-
try {
|
|
340
|
-
const { calls: extendedCalls, status: extendedStatus } = await this.moveAssets(
|
|
341
|
-
{
|
|
342
|
-
to: Protocols.VAULT.name,
|
|
343
|
-
from: Protocols.EXTENDED.name,
|
|
344
|
-
amount: extendedAmount.abs(),
|
|
345
|
-
},
|
|
346
|
-
extendedAdapter,
|
|
347
|
-
vesuAdapter
|
|
348
|
-
);
|
|
349
|
-
//If withdrawal succesfull, then do further
|
|
350
|
-
if (extendedStatus) {
|
|
351
|
-
calls.push(...extendedCalls);
|
|
352
|
-
} else {
|
|
353
|
-
return [];
|
|
354
|
-
}
|
|
355
|
-
} catch (err) {
|
|
356
|
-
logger.error(`Failed moving assets to vault: ${err}`);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (vesuAmount.lessThan(0)) {
|
|
361
|
-
try {
|
|
362
|
-
const { calls: vesuCalls, status: vesuStatus } = await this.moveAssets(
|
|
363
|
-
{
|
|
364
|
-
to: Protocols.EXTENDED.name,
|
|
365
|
-
from: Protocols.VESU.name,
|
|
366
|
-
amount: vesuAmount.abs(),
|
|
367
|
-
},
|
|
368
|
-
extendedAdapter,
|
|
369
|
-
vesuAdapter
|
|
370
|
-
);
|
|
371
|
-
calls.push(...vesuCalls);
|
|
372
|
-
if (!vesuStatus) {
|
|
373
|
-
return [];
|
|
374
|
-
}
|
|
375
|
-
} catch (err) {
|
|
376
|
-
logger.error(`Failed moving assets to vault: ${err}`);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
381
|
-
if (!extendedHoldings) {
|
|
382
|
-
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
383
|
-
return calls;
|
|
384
|
-
}
|
|
385
|
-
const usdcAmountInWallet = (await this.getUnusedBalance()).amount;
|
|
386
|
-
const usdcAmountOnExtended = parseFloat(
|
|
387
|
-
extendedHoldings.availableForTrade
|
|
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.`
|
|
388
217
|
);
|
|
389
|
-
if (extendedAmount.plus(BUFFER_USDC_IN_WITHDRAWAL).minus(usdcAmountOnExtended).greaterThan(0)) {
|
|
390
|
-
//move assets to extended
|
|
391
|
-
try {
|
|
392
|
-
const { calls: extendedCalls } = await this.moveAssets(
|
|
393
|
-
{
|
|
394
|
-
to: Protocols.EXTENDED.name,
|
|
395
|
-
from: Protocols.VAULT.name,
|
|
396
|
-
amount: extendedAmount.plus(BUFFER_USDC_IN_WITHDRAWAL).minus(usdcAmountOnExtended),
|
|
397
|
-
},
|
|
398
|
-
extendedAdapter,
|
|
399
|
-
vesuAdapter
|
|
400
|
-
);
|
|
401
|
-
calls.push(...extendedCalls);
|
|
402
|
-
} catch (err) {
|
|
403
|
-
logger.error(`Failed moving assets to extended: ${err}`);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
if (vesuAmount.minus(usdcAmountInWallet).greaterThan(0)) {
|
|
407
|
-
//move assets to vesu
|
|
408
|
-
try {
|
|
409
|
-
const { calls: vesuCalls, status: vesuStatus } = await this.moveAssets(
|
|
410
|
-
{
|
|
411
|
-
to: Protocols.VAULT.name,
|
|
412
|
-
from: Protocols.EXTENDED.name,
|
|
413
|
-
amount: vesuAmount.minus(usdcAmountInWallet),
|
|
414
|
-
},
|
|
415
|
-
extendedAdapter,
|
|
416
|
-
vesuAdapter
|
|
417
|
-
);
|
|
418
|
-
if (!vesuStatus) {
|
|
419
|
-
return [];
|
|
420
|
-
}
|
|
421
|
-
calls.push(...vesuCalls);
|
|
422
|
-
} catch (err) {
|
|
423
|
-
logger.error(`Failed moving assets to vault: ${err}`);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return calls;
|
|
427
|
-
} catch (err) {
|
|
428
|
-
logger.error(`Failed moving assets to vesu: ${err}`);
|
|
429
|
-
return [];
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
async moveAssets(
|
|
434
|
-
params: {
|
|
435
|
-
amount: Web3Number;
|
|
436
|
-
from: string;
|
|
437
|
-
to: string;
|
|
438
|
-
},
|
|
439
|
-
extendedAdapter: ExtendedAdapter,
|
|
440
|
-
vesuAdapter: VesuMultiplyAdapter
|
|
441
|
-
): Promise<{
|
|
442
|
-
calls: Call[];
|
|
443
|
-
status: boolean;
|
|
444
|
-
}> {
|
|
445
|
-
try {
|
|
446
|
-
const avnuAdapter = await this.getAvnuAdapter();
|
|
447
|
-
if (!avnuAdapter) {
|
|
448
|
-
logger.error(`avnu adapter not found: ${avnuAdapter}`);
|
|
449
|
-
return {
|
|
450
|
-
calls: [],
|
|
451
|
-
status: false
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
logger.info(`moveAssets params, ${JSON.stringify(params)}`);
|
|
455
|
-
const collateralToken = vesuAdapter.config.supportedPositions[0].asset;
|
|
456
|
-
const {
|
|
457
|
-
collateralPrice,
|
|
458
|
-
} = await this.getAssetPrices();
|
|
459
|
-
|
|
460
|
-
if (params.to === Protocols.EXTENDED.name && params.from === Protocols.VAULT.name) {
|
|
461
|
-
const proofsInfo = extendedAdapter.getProofs(
|
|
462
|
-
true,
|
|
463
|
-
this.getMerkleTree()
|
|
464
|
-
);
|
|
465
|
-
const calls = [];
|
|
466
|
-
const proofGroups = proofsInfo.proofs;
|
|
467
|
-
const call = this.getManageCall(
|
|
468
|
-
proofGroups,
|
|
469
|
-
await proofsInfo.callConstructor({ amount: params.amount })
|
|
470
|
-
);
|
|
471
|
-
calls.push(call);
|
|
472
|
-
return {
|
|
473
|
-
calls: [call],
|
|
474
|
-
status: true
|
|
475
|
-
};
|
|
476
|
-
} else if (params.to === Protocols.VAULT.name && params.from === Protocols.EXTENDED.name) {
|
|
477
|
-
const extendedLeverage = calculateExtendedLevergae();
|
|
478
|
-
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
479
|
-
if (!extendedHoldings) {
|
|
480
|
-
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
481
|
-
return {
|
|
482
|
-
calls: [],
|
|
483
|
-
status: false
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
const extendedHoldingAmount = new Web3Number(
|
|
487
|
-
extendedHoldings.availableForWithdrawal,
|
|
488
|
-
USDC_TOKEN_DECIMALS
|
|
489
|
-
);
|
|
490
|
-
logger.info(`${VesuExtendedMultiplierStrategy.name}::moveAssets extendedHoldingAmount: ${extendedHoldingAmount.toNumber()}`);
|
|
491
|
-
if (params.amount.abs().greaterThan(extendedHoldingAmount)) {
|
|
492
|
-
const leftAmountAfterWithdrawalAmountInAccount = params.amount.abs().minus(extendedHoldingAmount);
|
|
493
|
-
logger.info(`${VesuExtendedMultiplierStrategy.name}::moveAssets leftAmountAfterWithdrawalAmountInAccount: ${leftAmountAfterWithdrawalAmountInAccount.toNumber()}`);
|
|
494
|
-
const btcAmount = leftAmountAfterWithdrawalAmountInAccount.dividedBy(collateralPrice.price);
|
|
495
|
-
const openLongPosition = btcAmount.multipliedBy(3).greaterThan(MINIMUM_EXTENDED_POSITION_SIZE) ? await extendedAdapter.createOrder(
|
|
496
|
-
extendedLeverage.toString(),
|
|
497
|
-
btcAmount.toNumber(),
|
|
498
|
-
OrderSide.BUY
|
|
499
|
-
) : await extendedAdapter.createOrder(
|
|
500
|
-
extendedLeverage.toString(),
|
|
501
|
-
0.000035, // just in case amount falls short then we need to create a withdrawal
|
|
502
|
-
OrderSide.BUY
|
|
503
|
-
)
|
|
504
|
-
if (!openLongPosition) {
|
|
505
|
-
logger.error(`error opening long position: ${openLongPosition}`);
|
|
506
|
-
return {
|
|
507
|
-
calls: [],
|
|
508
|
-
status: false
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
512
|
-
}
|
|
513
|
-
const withdrawalFromExtended =
|
|
514
|
-
await extendedAdapter.withdrawFromExtended(params.amount);
|
|
515
|
-
if (withdrawalFromExtended) {
|
|
516
|
-
/**
|
|
517
|
-
* We need to move assets from my wallet back to vault contract
|
|
518
|
-
*/
|
|
519
|
-
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
520
|
-
logger.info(`extendedHoldings after withdrawal ${extendedHoldings}`);
|
|
521
|
-
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
522
|
-
const calls = await this.moveAssetsToVaultAllocator(params.amount, extendedAdapter);
|
|
523
|
-
if (calls.length > 0) {
|
|
524
|
-
return {
|
|
525
|
-
calls: calls,
|
|
526
|
-
status: true
|
|
527
|
-
};
|
|
528
|
-
}
|
|
529
|
-
} else {
|
|
530
|
-
logger.error("withdrawal from extended failed");
|
|
531
|
-
return {
|
|
532
|
-
calls: [],
|
|
533
|
-
status: false
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
} else if (params.to === Protocols.VAULT.name && params.from === Protocols.VESU.name) {
|
|
537
|
-
//withdraw from vesu
|
|
538
|
-
const vesuAmountInBTC = new Web3Number(
|
|
539
|
-
params.amount.dividedBy(collateralPrice.price).toNumber(),
|
|
540
|
-
collateralToken.decimals
|
|
541
|
-
);
|
|
542
|
-
const proofsInfo = vesuAdapter.getProofs(false, this.getMerkleTree());
|
|
543
|
-
const calls = [];
|
|
544
|
-
const proofGroups = proofsInfo.proofs;
|
|
545
|
-
const call = this.getManageCall(
|
|
546
|
-
proofGroups,
|
|
547
|
-
await proofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
548
|
-
);
|
|
549
|
-
calls.push(call);
|
|
550
|
-
const swapProofsInfo = avnuAdapter.getProofs(false, this.getMerkleTree());
|
|
551
|
-
const swapProofGroups = swapProofsInfo.proofs;
|
|
552
|
-
const swapCall = this.getManageCall(
|
|
553
|
-
swapProofGroups,
|
|
554
|
-
await swapProofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
555
|
-
);
|
|
556
|
-
calls.push(swapCall);
|
|
557
|
-
return {
|
|
558
|
-
calls: calls,
|
|
559
|
-
status: true
|
|
560
|
-
};
|
|
561
|
-
} else if (params.to === Protocols.EXTENDED.name && params.from === Protocols.VESU.name) {
|
|
562
|
-
const vesuAmountInBTC = new Web3Number(
|
|
563
|
-
params.amount.dividedBy(collateralPrice.price).toNumber(),
|
|
564
|
-
collateralToken.decimals
|
|
565
|
-
);
|
|
566
|
-
const proofsInfo = vesuAdapter.getProofs(false, this.getMerkleTree());
|
|
567
|
-
const calls = [];
|
|
568
|
-
const proofGroups = proofsInfo.proofs;
|
|
569
|
-
const call = this.getManageCall(
|
|
570
|
-
proofGroups,
|
|
571
|
-
await proofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
572
|
-
);
|
|
573
|
-
calls.push(call);
|
|
574
|
-
const swapProofsInfo = avnuAdapter.getProofs(false, this.getMerkleTree());
|
|
575
|
-
const swapProofGroups = swapProofsInfo.proofs;
|
|
576
|
-
const swapCall = this.getManageCall(
|
|
577
|
-
swapProofGroups,
|
|
578
|
-
await swapProofsInfo.callConstructor({ amount: vesuAmountInBTC })
|
|
579
|
-
);
|
|
580
|
-
calls.push(swapCall);
|
|
581
|
-
const proofsInfoDeposit = extendedAdapter.getProofs(
|
|
582
|
-
true,
|
|
583
|
-
this.getMerkleTree()
|
|
584
|
-
);
|
|
585
|
-
//Deposit Amount would still be in usdc
|
|
586
|
-
const proofGroupsDeposit = proofsInfoDeposit.proofs;
|
|
587
|
-
const callDeposit = this.getManageCall(
|
|
588
|
-
proofGroupsDeposit,
|
|
589
|
-
await proofsInfoDeposit.callConstructor({ amount: params.amount })
|
|
590
|
-
);
|
|
591
|
-
calls.push(callDeposit);
|
|
592
|
-
return {
|
|
593
|
-
calls: calls,
|
|
594
|
-
status: true
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
return {
|
|
598
|
-
calls: [],
|
|
599
|
-
status: false
|
|
600
|
-
};
|
|
601
|
-
} catch (err) {
|
|
602
|
-
logger.error(`error moving assets: ${err}`);
|
|
603
|
-
return {
|
|
604
|
-
calls: [],
|
|
605
|
-
status: false
|
|
606
|
-
};
|
|
607
218
|
}
|
|
219
|
+
return avnuAdapter.adapter as AvnuAdapter;
|
|
608
220
|
}
|
|
609
221
|
|
|
610
|
-
async
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
const avnuAdapter = await this.getAvnuAdapter();
|
|
618
|
-
if (
|
|
619
|
-
!vesuAdapter ||
|
|
620
|
-
!extendedAdapter ||
|
|
621
|
-
!extendedAdapter.client ||
|
|
622
|
-
!avnuAdapter
|
|
623
|
-
) {
|
|
624
|
-
logger.error(
|
|
625
|
-
"vesu or extended adapter not found",
|
|
626
|
-
vesuAdapter,
|
|
627
|
-
extendedAdapter
|
|
628
|
-
);
|
|
629
|
-
return {
|
|
630
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
631
|
-
calls: [],
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
const extendedLeverage = calculateExtendedLevergae();
|
|
635
|
-
const isPriceDifferenceBetweenAvnuAndExtended = await this.checkPriceDifferenceBetweenAvnuAndExtended(extendedAdapter, vesuAdapter, avnuAdapter);
|
|
636
|
-
if (!isPriceDifferenceBetweenAvnuAndExtended) {
|
|
637
|
-
logger.error("price difference between avnu and extended doesn't fit the range");
|
|
638
|
-
return {
|
|
639
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
640
|
-
calls: [],
|
|
641
|
-
};
|
|
642
|
-
}
|
|
643
|
-
const position = await extendedAdapter.getAllOpenPositions();
|
|
644
|
-
if (!position) {
|
|
645
|
-
logger.error("error getting extended position", position);
|
|
646
|
-
return {
|
|
647
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
648
|
-
calls: [],
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
const extendedPositionValue = position.length > 0 ? parseFloat(position[0].value) : 0;
|
|
652
|
-
const BUFFER_AMOUNT_IN_AVAILABLE_FOR_TRADE = BUFFER_USDC_IN_WITHDRAWAL;
|
|
653
|
-
const extendedHoldings = await extendedAdapter.getExtendedDepositAmount();
|
|
654
|
-
if (!extendedHoldings) {
|
|
655
|
-
logger.error(`error getting extended holdings: ${extendedHoldings}`);
|
|
656
|
-
return {
|
|
657
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
658
|
-
calls: [],
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
const extendedHoldingAmount = new Web3Number(
|
|
662
|
-
extendedHoldings.availableForTrade,
|
|
663
|
-
USDC_TOKEN_DECIMALS
|
|
664
|
-
);
|
|
665
|
-
const {
|
|
666
|
-
collateralTokenAmount,
|
|
667
|
-
} = await vesuAdapter.vesuAdapter.getAssetPrices();
|
|
668
|
-
const { collateralPrice } = await this.getAssetPrices();
|
|
669
|
-
const { vesuAmountInBTC, extendedAmountInBTC } = calculateVesUPositionSizeGivenExtended(
|
|
670
|
-
extendedPositionValue,
|
|
671
|
-
extendedHoldingAmount.minus(BUFFER_AMOUNT_IN_AVAILABLE_FOR_TRADE),
|
|
672
|
-
collateralTokenAmount,
|
|
673
|
-
collateralPrice.price
|
|
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.`
|
|
674
229
|
);
|
|
675
|
-
logger.info(`vesuAmountInBTC ${vesuAmountInBTC}, extendedAmountInBTC ${extendedAmountInBTC}`);
|
|
676
|
-
|
|
677
|
-
let calls: Call[] = [];
|
|
678
|
-
if (vesuAmountInBTC.greaterThan(MINIMUM_EXTENDED_POSITION_SIZE)) {
|
|
679
|
-
const proofsInfo = vesuAdapter.getProofs(true, this.getMerkleTree());
|
|
680
|
-
const proofGroups = proofsInfo.proofs;
|
|
681
|
-
const call = this.getManageCall(
|
|
682
|
-
proofGroups,
|
|
683
|
-
await proofsInfo.callConstructor({
|
|
684
|
-
amount: vesuAmountInBTC,
|
|
685
|
-
})
|
|
686
|
-
);
|
|
687
|
-
const { amount: wbtcAmountInVaultAllocator } = await this.getUnusedBalanceWBTC();
|
|
688
|
-
if (wbtcAmountInVaultAllocator.lessThan(vesuAmountInBTC)) {
|
|
689
|
-
const swapProofsInfo = avnuAdapter.getProofs(true, this.getMerkleTree());
|
|
690
|
-
const swapProofGroups = swapProofsInfo.proofs;
|
|
691
|
-
const swapCall = this.getManageCall(
|
|
692
|
-
swapProofGroups,
|
|
693
|
-
await swapProofsInfo.callConstructor({
|
|
694
|
-
amount: vesuAmountInBTC,
|
|
695
|
-
})
|
|
696
|
-
);
|
|
697
|
-
calls.push(swapCall);
|
|
698
|
-
}
|
|
699
|
-
calls.push(call);
|
|
700
|
-
}
|
|
701
|
-
const shortPosition = extendedAmountInBTC.multipliedBy(3).abs().greaterThan(MINIMUM_EXTENDED_POSITION_SIZE) ? await extendedAdapter.createOrder(
|
|
702
|
-
extendedLeverage.toString(),
|
|
703
|
-
extendedAmountInBTC.toNumber(),
|
|
704
|
-
OrderSide.SELL
|
|
705
|
-
) : null;
|
|
706
|
-
if (!shortPosition && extendedAmountInBTC.multipliedBy(3).abs().greaterThan(MINIMUM_EXTENDED_POSITION_SIZE)) {
|
|
707
|
-
logger.error(`error creating short position thus no position to be opened on vesu: ${shortPosition}`);
|
|
708
|
-
return {
|
|
709
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
710
|
-
calls: [],
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
return {
|
|
714
|
-
extendedAmountInBTC: extendedAmountInBTC,
|
|
715
|
-
calls: calls,
|
|
716
|
-
};
|
|
717
|
-
} catch (err) {
|
|
718
|
-
logger.error(`error handling deposit: ${err}`);
|
|
719
|
-
return {
|
|
720
|
-
extendedAmountInBTC: new Web3Number(0, 0),
|
|
721
|
-
calls: [],
|
|
722
|
-
};;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
async checkPriceDifferenceBetweenAvnuAndExtended(extendedAdapter: ExtendedAdapter, vesuAdapter: VesuMultiplyAdapter, avnuAdapter: AvnuAdapter): Promise<boolean> {
|
|
727
|
-
const {
|
|
728
|
-
ask, bid
|
|
729
|
-
} = await extendedAdapter.fetchOrderBookBTCUSDC();
|
|
730
|
-
const price = ask.plus(bid).dividedBy(2);
|
|
731
|
-
const btcToken = vesuAdapter.config.supportedPositions[0].asset;
|
|
732
|
-
const btcPriceAvnu = await avnuAdapter.getPriceOfToken(btcToken.address.toString());
|
|
733
|
-
if (!btcPriceAvnu) {
|
|
734
|
-
logger.error(`error getting btc price avnu: ${btcPriceAvnu}`);
|
|
735
|
-
return false;
|
|
736
230
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
231
|
+
if (!extendedAdapter.adapter || !(extendedAdapter.adapter as ExtendedAdapter).client) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`${this.getTag()} Extended adapter client not initialized.`
|
|
234
|
+
);
|
|
740
235
|
}
|
|
741
|
-
|
|
742
|
-
return false;
|
|
236
|
+
return extendedAdapter.adapter as ExtendedAdapter;
|
|
743
237
|
}
|
|
744
238
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
)
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
collateralTokenAmount,
|
|
794
|
-
extendedPositon
|
|
795
|
-
);
|
|
796
|
-
if (!amountDistributionForWithdrawal) {
|
|
797
|
-
status = false;
|
|
798
|
-
logger.error(
|
|
799
|
-
`error calculating amount distribution for withdrawal: ${amountDistributionForWithdrawal}`
|
|
800
|
-
);
|
|
801
|
-
return {
|
|
802
|
-
calls: calls,
|
|
803
|
-
status: status
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
const { vesu_amount, extended_amount } = amountDistributionForWithdrawal;
|
|
239
|
+
/**
|
|
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%)
|
|
247
|
+
*/
|
|
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
|
+
};
|
|
807
287
|
|
|
808
|
-
|
|
809
|
-
const { calls: vesuCalls, status: vesuStatus } = await this.moveAssets(
|
|
810
|
-
{
|
|
811
|
-
amount: vesu_amount,
|
|
812
|
-
from: Protocols.VESU.name,
|
|
813
|
-
to: Protocols.VAULT.name,
|
|
814
|
-
},
|
|
815
|
-
extendedAdapter,
|
|
816
|
-
vesuAdapter
|
|
817
|
-
);
|
|
818
|
-
status = vesuStatus;
|
|
819
|
-
calls.push(...vesuCalls);
|
|
820
|
-
}
|
|
821
|
-
if (status && extended_amount.greaterThan(0)) {
|
|
822
|
-
const { calls: extendedCalls, status: extendedStatus } = await this.moveAssets(
|
|
823
|
-
{
|
|
824
|
-
amount: extended_amount,
|
|
825
|
-
from: Protocols.EXTENDED.name,
|
|
826
|
-
to: Protocols.VAULT.name,
|
|
827
|
-
},
|
|
828
|
-
extendedAdapter,
|
|
829
|
-
vesuAdapter
|
|
830
|
-
);
|
|
831
|
-
status = extendedStatus;
|
|
832
|
-
if (status) {
|
|
833
|
-
calls.push(...extendedCalls);
|
|
834
|
-
} else {
|
|
835
|
-
logger.error("error moving assets to vault: extendedStatus: ${extendedStatus}");
|
|
836
|
-
return {
|
|
837
|
-
calls: [],
|
|
838
|
-
status: status
|
|
839
|
-
};
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
const withdrawCall = await this.getBringLiquidityCall({
|
|
843
|
-
amount: amount
|
|
844
|
-
})
|
|
845
|
-
logger.info("withdraw call", withdrawCall);
|
|
846
|
-
calls.push(withdrawCall);
|
|
847
|
-
return {
|
|
848
|
-
calls: calls,
|
|
849
|
-
status: status
|
|
850
|
-
};
|
|
851
|
-
} catch (err) {
|
|
852
|
-
logger.error(`error handling withdrawal: ${err}`);
|
|
853
|
-
return {
|
|
854
|
-
calls: [],
|
|
855
|
-
status: false
|
|
856
|
-
};
|
|
857
|
-
}
|
|
288
|
+
return new ExecutionService(executionConfig);
|
|
858
289
|
}
|
|
859
290
|
|
|
860
|
-
|
|
291
|
+
/**
|
|
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.
|
|
296
|
+
*/
|
|
297
|
+
async getAUM(): Promise<{
|
|
298
|
+
net: SingleTokenInfo;
|
|
299
|
+
prevAum: Web3Number;
|
|
300
|
+
splits: PositionInfo[];
|
|
301
|
+
}> {
|
|
861
302
|
const allPositions: PositionInfo[] = [];
|
|
862
303
|
for (let adapter of this.metadata.additionalInfo.adapters) {
|
|
863
|
-
|
|
864
|
-
|
|
304
|
+
let positions = await adapter.adapter.getPositions();
|
|
305
|
+
if (positions && positions.length > 0) {
|
|
306
|
+
const filteredPositions = positions;
|
|
307
|
+
allPositions.push(...filteredPositions);
|
|
308
|
+
}
|
|
865
309
|
}
|
|
866
|
-
|
|
867
310
|
const assetPrice = await this.pricer.getPrice(this.asset().symbol);
|
|
868
311
|
let netAUM = new Web3Number(0, this.asset().decimals);
|
|
869
312
|
for (let position of allPositions) {
|
|
@@ -874,6 +317,16 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
874
317
|
}
|
|
875
318
|
}
|
|
876
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
|
+
|
|
877
330
|
const prevAum = await this.getPrevAUM();
|
|
878
331
|
const realAUM: PositionInfo = {
|
|
879
332
|
tokenInfo: this.asset(),
|
|
@@ -881,49 +334,249 @@ export class VesuExtendedMultiplierStrategy<
|
|
|
881
334
|
usdValue: netAUM.toNumber() * assetPrice.price,
|
|
882
335
|
apy: { apy: netAUM.toNumber() * assetPrice.price, type: APYType.BASE },
|
|
883
336
|
remarks: AUMTypes.FINALISED,
|
|
884
|
-
protocol: Protocols.NONE // just placeholder
|
|
337
|
+
protocol: Protocols.NONE, // just placeholder
|
|
885
338
|
};
|
|
886
339
|
|
|
887
340
|
const estimatedAUMDelta: PositionInfo = {
|
|
888
341
|
tokenInfo: this.asset(),
|
|
889
|
-
amount: Web3Number.fromWei(
|
|
342
|
+
amount: Web3Number.fromWei("0", this.asset().decimals),
|
|
890
343
|
usdValue: 0,
|
|
891
344
|
apy: { apy: 0, type: APYType.BASE },
|
|
892
345
|
remarks: AUMTypes.DEFISPRING,
|
|
893
|
-
protocol: Protocols.NONE // just placeholder
|
|
346
|
+
protocol: Protocols.NONE, // just placeholder
|
|
894
347
|
};
|
|
895
348
|
|
|
896
349
|
return {
|
|
897
350
|
net: {
|
|
898
351
|
tokenInfo: this.asset(),
|
|
899
352
|
amount: netAUM,
|
|
900
|
-
usdValue: netAUM.toNumber() * assetPrice.price
|
|
901
|
-
},
|
|
353
|
+
usdValue: netAUM.toNumber() * assetPrice.price,
|
|
354
|
+
},
|
|
355
|
+
prevAum: prevAum,
|
|
356
|
+
splits: [realAUM, estimatedAUMDelta],
|
|
902
357
|
};
|
|
903
358
|
}
|
|
904
359
|
|
|
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
|
+
*/
|
|
367
|
+
async getMaxBorrowableAmount(): Promise<Web3Number> {
|
|
368
|
+
const vesuAdapter = await this.getVesuModifyPositionAdapter();
|
|
369
|
+
const extendedAdapter = await this.getExtendedAdapter();
|
|
370
|
+
const extendedFundingRate = new Web3Number(
|
|
371
|
+
(await extendedAdapter.getNetAPY()).toFixed(4),
|
|
372
|
+
0,
|
|
373
|
+
);
|
|
374
|
+
const extendedPositions = await extendedAdapter.getAllOpenPositions();
|
|
375
|
+
if (!extendedPositions || extendedPositions.length === 0) {
|
|
376
|
+
logger.warn(`${this.getTag()} getMaxBorrowableAmount: no extended positions found`);
|
|
377
|
+
return new Web3Number(0, 0);
|
|
378
|
+
}
|
|
379
|
+
const extendePositionSizeUSD = new Web3Number(
|
|
380
|
+
extendedPositions[0].value || 0,
|
|
381
|
+
0,
|
|
382
|
+
);
|
|
383
|
+
const vesuPositions = await vesuAdapter.getPositions();
|
|
384
|
+
const vesuSupplyApy = vesuPositions[0].apy.apy;
|
|
385
|
+
const vesuCollateralSizeUSD = new Web3Number(
|
|
386
|
+
vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
387
|
+
USDC_TOKEN_DECIMALS,
|
|
388
|
+
);
|
|
389
|
+
const vesuDebtSizeUSD = new Web3Number(
|
|
390
|
+
vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
391
|
+
USDC_TOKEN_DECIMALS,
|
|
392
|
+
);
|
|
393
|
+
const num1 = extendePositionSizeUSD.multipliedBy(extendedFundingRate);
|
|
394
|
+
const num2 = vesuCollateralSizeUSD.multipliedBy(vesuSupplyApy);
|
|
395
|
+
const num3 = vesuDebtSizeUSD.abs();
|
|
396
|
+
const maxBorrowApy = num1.plus(num2).minus(0.1).dividedBy(num3);
|
|
397
|
+
const vesuMaxBorrowableResult =
|
|
398
|
+
await vesuAdapter._vesuAdapter.getMaxBorrowableByInterestRate(
|
|
399
|
+
this.config,
|
|
400
|
+
vesuAdapter.config.debt,
|
|
401
|
+
maxBorrowApy.toNumber(),
|
|
402
|
+
);
|
|
403
|
+
return new Web3Number(
|
|
404
|
+
vesuMaxBorrowableResult.maxDebtToHave.toFixed(USDC_TOKEN_DECIMALS),
|
|
405
|
+
USDC_TOKEN_DECIMALS,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
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
|
+
*/
|
|
414
|
+
async getVesuHealthFactors(): Promise<number[]> {
|
|
415
|
+
const vesuAdapter = await this.getVesuModifyPositionAdapter();
|
|
416
|
+
const extendedAdapter = await this.getExtendedAdapter();
|
|
417
|
+
const vesuPositions = await vesuAdapter.getPositions();
|
|
418
|
+
const vesuCollateralSizeUSD = new Web3Number(
|
|
419
|
+
vesuPositions[0].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
420
|
+
0,
|
|
421
|
+
);
|
|
422
|
+
const vesuDebtSizeUSD = new Web3Number(
|
|
423
|
+
vesuPositions[1].usdValue.toFixed(USDC_TOKEN_DECIMALS),
|
|
424
|
+
0,
|
|
425
|
+
);
|
|
426
|
+
const actualLtv = vesuDebtSizeUSD.dividedBy(vesuCollateralSizeUSD).abs();
|
|
427
|
+
logger.debug(`${this.getTag()} getVesuHealthFactors: actualLtv=${actualLtv.toNumber()}`);
|
|
428
|
+
const maxLtv = new Web3Number(
|
|
429
|
+
await vesuAdapter._vesuAdapter.getLTVConfig(this.config),
|
|
430
|
+
4,
|
|
431
|
+
);
|
|
432
|
+
const healthFactor = new Web3Number(
|
|
433
|
+
maxLtv.dividedBy(actualLtv).toFixed(4),
|
|
434
|
+
4,
|
|
435
|
+
);
|
|
436
|
+
logger.debug(`${this.getTag()} getVesuHealthFactors: healthFactor=${healthFactor.toNumber()}`);
|
|
437
|
+
const extendedBalance = await extendedAdapter.getExtendedDepositAmount();
|
|
438
|
+
if (!extendedBalance) {
|
|
439
|
+
return [0, 0];
|
|
440
|
+
}
|
|
441
|
+
const extendedLeverage = new Web3Number(
|
|
442
|
+
(Number(extendedBalance.marginRatio) * 100).toFixed(4),
|
|
443
|
+
4,
|
|
444
|
+
);
|
|
445
|
+
logger.debug(`${this.getTag()} getVesuHealthFactors: extendedLeverage=${extendedLeverage.toNumber()}`);
|
|
446
|
+
return [healthFactor.toNumber(), extendedLeverage.toNumber()];
|
|
447
|
+
}
|
|
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
|
+
*/
|
|
455
|
+
async netAPY(): Promise<{
|
|
456
|
+
net: number;
|
|
457
|
+
splits: { apy: number; id: string }[];
|
|
458
|
+
}> {
|
|
459
|
+
const allPositions: PositionInfo[] = [];
|
|
460
|
+
for (let adapter of this.metadata.additionalInfo.adapters) {
|
|
461
|
+
if (adapter.adapter.name !== ExtendedAdapter.name) {
|
|
462
|
+
let positions = await adapter.adapter.getPositions();
|
|
463
|
+
if (positions.length > 0) {
|
|
464
|
+
allPositions.push(...positions);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const extendedAdapter = await this.getExtendedAdapter();
|
|
469
|
+
let vesuPositions = allPositions.filter(
|
|
470
|
+
(item) => item.protocol === Protocols.VESU,
|
|
471
|
+
);
|
|
472
|
+
vesuPositions.map((item) => {
|
|
473
|
+
item.apy.apy = item.apy.apy * 0.1;
|
|
474
|
+
});
|
|
475
|
+
const extendedPositions = await extendedAdapter.getAllOpenPositions();
|
|
476
|
+
if (!extendedPositions || !this.usdcToken) {
|
|
477
|
+
return {
|
|
478
|
+
net: 0,
|
|
479
|
+
splits: [],
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
const extendedPosition = extendedPositions[0] || 0;
|
|
483
|
+
const extendedEquity =
|
|
484
|
+
(await extendedAdapter.getExtendedDepositAmount())?.equity || 0;
|
|
485
|
+
const extendedApy = await extendedAdapter.getNetAPY();
|
|
486
|
+
const totalHoldingsUSDValue =
|
|
487
|
+
allPositions.reduce((acc, curr) => acc + curr.usdValue, 0) +
|
|
488
|
+
Number(extendedEquity);
|
|
489
|
+
const extendedPositionSizeMultipliedByApy =
|
|
490
|
+
Number(extendedPosition.value) * extendedApy;
|
|
491
|
+
let weightedAPYs =
|
|
492
|
+
allPositions.reduce(
|
|
493
|
+
(acc, curr) => acc + curr.apy.apy * curr.usdValue,
|
|
494
|
+
0,
|
|
495
|
+
) + extendedPositionSizeMultipliedByApy;
|
|
496
|
+
const netAPY = weightedAPYs / totalHoldingsUSDValue;
|
|
497
|
+
logger.debug(
|
|
498
|
+
`${this.getTag()} netAPY: holdingsUsd=${totalHoldingsUSDValue}, weightedApy=${weightedAPYs}, net=${netAPY}`,
|
|
499
|
+
);
|
|
500
|
+
allPositions.push({
|
|
501
|
+
tokenInfo: this.usdcToken,
|
|
502
|
+
amount: new Web3Number(extendedPosition.size, 0),
|
|
503
|
+
usdValue: Number(extendedEquity),
|
|
504
|
+
apy: { apy: extendedApy, type: APYType.BASE },
|
|
505
|
+
remarks: AUMTypes.FINALISED,
|
|
506
|
+
protocol: Protocols.EXTENDED,
|
|
507
|
+
});
|
|
508
|
+
return {
|
|
509
|
+
net: netAPY,
|
|
510
|
+
splits: allPositions.map((p) => ({
|
|
511
|
+
apy: p.apy.apy,
|
|
512
|
+
id: p.remarks ?? "",
|
|
513
|
+
})),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Fetches the operator wallet's current holdings for USDC and WBTC,
|
|
519
|
+
* returning each token's balance and USD value.
|
|
520
|
+
*/
|
|
521
|
+
async getWalletHoldings(): Promise<
|
|
522
|
+
{
|
|
523
|
+
tokenInfo: TokenInfo;
|
|
524
|
+
amount: Web3Number;
|
|
525
|
+
usdValue: number;
|
|
526
|
+
}[]
|
|
527
|
+
> {
|
|
528
|
+
if (!this.wbtcToken || !this.usdcToken) {
|
|
529
|
+
return [];
|
|
530
|
+
}
|
|
531
|
+
const walletAddress = this.metadata.additionalInfo.walletAddress;
|
|
532
|
+
const usdcWalletBalance = await new ERC20(this.config).balanceOf(
|
|
533
|
+
this.usdcToken.address,
|
|
534
|
+
walletAddress,
|
|
535
|
+
this.usdcToken.decimals,
|
|
536
|
+
);
|
|
537
|
+
const price = await this.pricer.getPrice(this.usdcToken.symbol);
|
|
538
|
+
const usdcUsdValue =
|
|
539
|
+
Number(usdcWalletBalance.toFixed(this.usdcToken.decimals)) * price.price;
|
|
540
|
+
return [
|
|
541
|
+
{
|
|
542
|
+
tokenInfo: this.usdcToken,
|
|
543
|
+
amount: usdcWalletBalance,
|
|
544
|
+
usdValue: usdcUsdValue,
|
|
545
|
+
}
|
|
546
|
+
];
|
|
547
|
+
}
|
|
905
548
|
}
|
|
906
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
|
+
*/
|
|
907
555
|
function getLooperSettings(
|
|
908
|
-
|
|
556
|
+
collateralSymbol: string,
|
|
909
557
|
underlyingSymbol: string,
|
|
910
558
|
vaultSettings: VesuExtendedStrategySettings,
|
|
911
559
|
pool1: ContractAddr,
|
|
912
|
-
|
|
913
|
-
|
|
560
|
+
extendedBackendReadUrl: string,
|
|
561
|
+
extendedBackendWriteUrl: string,
|
|
914
562
|
vaultIdExtended: number,
|
|
563
|
+
minimumExtendedMovementAmount: number,
|
|
564
|
+
minimumVesuMovementAmount: number,
|
|
565
|
+
minimumExtendedRetriesDelayForOrderStatus: number,
|
|
566
|
+
minimumExtendedPriceDifferenceForSwapOpen: number,
|
|
567
|
+
maximumExtendedPriceDifferenceForSwapClosing: number,
|
|
915
568
|
) {
|
|
916
569
|
vaultSettings.leafAdapters = [];
|
|
917
570
|
|
|
918
571
|
const wbtcToken = Global.getDefaultTokens().find(
|
|
919
|
-
(token) => token.symbol ===
|
|
572
|
+
(token) => token.symbol === collateralSymbol,
|
|
920
573
|
)!;
|
|
921
574
|
const usdcToken = Global.getDefaultTokens().find(
|
|
922
|
-
(token) => token.symbol === underlyingSymbol
|
|
575
|
+
(token) => token.symbol === underlyingSymbol,
|
|
923
576
|
)!;
|
|
924
577
|
|
|
925
578
|
const baseAdapterConfig: BaseAdapterConfig = {
|
|
926
|
-
baseToken:
|
|
579
|
+
baseToken: usdcToken,
|
|
927
580
|
supportedPositions: [
|
|
928
581
|
{ asset: usdcToken, isDebt: true },
|
|
929
582
|
{ asset: wbtcToken, isDebt: false },
|
|
@@ -940,29 +593,36 @@ function getLooperSettings(
|
|
|
940
593
|
avnuContract: AVNU_MIDDLEWARE,
|
|
941
594
|
slippage: 0.01,
|
|
942
595
|
baseUrl: AVNU_QUOTE_URL,
|
|
596
|
+
minimumExtendedPriceDifferenceForSwapOpen:
|
|
597
|
+
minimumExtendedPriceDifferenceForSwapOpen,
|
|
598
|
+
maximumExtendedPriceDifferenceForSwapClosing:
|
|
599
|
+
maximumExtendedPriceDifferenceForSwapClosing,
|
|
943
600
|
});
|
|
944
601
|
|
|
945
602
|
const extendedAdapter = new ExtendedAdapter({
|
|
946
603
|
...baseAdapterConfig,
|
|
947
604
|
supportedPositions: [
|
|
948
|
-
{ asset: usdcToken, isDebt:
|
|
605
|
+
{ asset: usdcToken, isDebt: false },
|
|
949
606
|
],
|
|
950
607
|
vaultIdExtended: vaultIdExtended,
|
|
951
608
|
extendedContract: EXTENDED_CONTRACT,
|
|
952
|
-
|
|
953
|
-
|
|
609
|
+
extendedBackendWriteUrl: extendedBackendWriteUrl,
|
|
610
|
+
extendedBackendReadUrl: extendedBackendReadUrl,
|
|
954
611
|
extendedTimeout: 30000,
|
|
955
612
|
extendedRetries: 3,
|
|
956
613
|
extendedBaseUrl: "https://api.starknet.extended.exchange",
|
|
957
614
|
extendedMarketName: "BTC-USD",
|
|
958
615
|
extendedPrecision: 5,
|
|
959
616
|
avnuAdapter: avnuAdapter,
|
|
617
|
+
retryDelayForOrderStatus: minimumExtendedRetriesDelayForOrderStatus ?? 3000,
|
|
618
|
+
minimumExtendedMovementAmount: minimumExtendedMovementAmount ?? 5, //5 usdcs
|
|
960
619
|
});
|
|
961
620
|
|
|
962
621
|
const vesuMultiplyAdapter = new VesuMultiplyAdapter({
|
|
963
622
|
poolId: pool1,
|
|
964
623
|
collateral: wbtcToken,
|
|
965
624
|
debt: usdcToken,
|
|
625
|
+
marginToken: usdcToken,
|
|
966
626
|
targetHealthFactor: vaultSettings.targetHealthFactor,
|
|
967
627
|
minHealthFactor: vaultSettings.minHealthFactor,
|
|
968
628
|
quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
|
|
@@ -971,10 +631,31 @@ function getLooperSettings(
|
|
|
971
631
|
{ asset: wbtcToken, isDebt: false },
|
|
972
632
|
{ asset: usdcToken, isDebt: true },
|
|
973
633
|
],
|
|
634
|
+
minimumVesuMovementAmount: minimumVesuMovementAmount ?? 5, //5 usdc
|
|
635
|
+
});
|
|
636
|
+
|
|
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
|
+
],
|
|
974
650
|
});
|
|
975
651
|
|
|
976
|
-
|
|
652
|
+
// Transfers USDC between the vault allocator (fromAddress) and the operator wallet (toAddress)
|
|
653
|
+
const usdcTransferAdapter = new TokenTransferAdapter({
|
|
977
654
|
...baseAdapterConfig,
|
|
655
|
+
baseToken: usdcToken,
|
|
656
|
+
supportedPositions: [{ asset: usdcToken, isDebt: false }],
|
|
657
|
+
fromAddress: vaultSettings.vaultAllocator,
|
|
658
|
+
toAddress: ContractAddr.from(vaultSettings.walletAddress),
|
|
978
659
|
});
|
|
979
660
|
|
|
980
661
|
vaultSettings.adapters.push({
|
|
@@ -983,8 +664,13 @@ function getLooperSettings(
|
|
|
983
664
|
});
|
|
984
665
|
|
|
985
666
|
vaultSettings.adapters.push({
|
|
986
|
-
id: `${
|
|
987
|
-
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,
|
|
988
674
|
});
|
|
989
675
|
|
|
990
676
|
vaultSettings.adapters.push({
|
|
@@ -1002,44 +688,43 @@ function getLooperSettings(
|
|
|
1002
688
|
vaultAddress: vaultSettings.vaultAddress,
|
|
1003
689
|
vaultAllocator: vaultSettings.vaultAllocator,
|
|
1004
690
|
manager: vaultSettings.manager,
|
|
1005
|
-
asset:
|
|
691
|
+
asset: usdcToken.address,
|
|
1006
692
|
});
|
|
1007
693
|
|
|
1008
694
|
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getDepositLeaf());
|
|
1009
|
-
vaultSettings.leafAdapters.push(() =>
|
|
1010
|
-
|
|
1011
|
-
);
|
|
695
|
+
vaultSettings.leafAdapters.push(() => vesuMultiplyAdapter.getWithdrawLeaf());
|
|
696
|
+
vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getDepositLeaf());
|
|
697
|
+
vaultSettings.leafAdapters.push(() => vesuModifyPositionAdapter.getWithdrawLeaf());
|
|
1012
698
|
vaultSettings.leafAdapters.push(() => extendedAdapter.getDepositLeaf());
|
|
1013
|
-
vaultSettings.leafAdapters.push(() => extendedAdapter.getSwapFromLegacyLeaf());
|
|
1014
699
|
vaultSettings.leafAdapters.push(() => avnuAdapter.getDepositLeaf());
|
|
1015
700
|
vaultSettings.leafAdapters.push(() => avnuAdapter.getWithdrawLeaf());
|
|
1016
|
-
|
|
701
|
+
vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getDepositLeaf());
|
|
702
|
+
vaultSettings.leafAdapters.push(() => usdcTransferAdapter.getWithdrawLeaf());
|
|
1017
703
|
vaultSettings.leafAdapters.push(
|
|
1018
704
|
commonAdapter
|
|
1019
705
|
.getApproveAdapter(
|
|
1020
706
|
usdcToken.address,
|
|
1021
707
|
vaultSettings.vaultAddress,
|
|
1022
|
-
UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY
|
|
708
|
+
UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY,
|
|
1023
709
|
)
|
|
1024
|
-
.bind(commonAdapter)
|
|
710
|
+
.bind(commonAdapter),
|
|
1025
711
|
);
|
|
1026
712
|
|
|
1027
713
|
vaultSettings.leafAdapters.push(
|
|
1028
714
|
commonAdapter
|
|
1029
715
|
.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY)
|
|
1030
|
-
.bind(commonAdapter)
|
|
716
|
+
.bind(commonAdapter),
|
|
1031
717
|
);
|
|
1032
718
|
return vaultSettings;
|
|
1033
719
|
}
|
|
1034
720
|
|
|
1035
|
-
|
|
1036
721
|
function getDescription(tokenSymbol: string, underlyingSymbol: string) {
|
|
1037
722
|
return VaultDescription(tokenSymbol, underlyingSymbol);
|
|
1038
723
|
}
|
|
1039
724
|
|
|
1040
725
|
export default function VaultDescription(
|
|
1041
726
|
lstSymbol: string,
|
|
1042
|
-
underlyingSymbol: string
|
|
727
|
+
underlyingSymbol: string,
|
|
1043
728
|
) {
|
|
1044
729
|
const containerStyle = {
|
|
1045
730
|
maxWidth: "800px",
|
|
@@ -1052,26 +737,64 @@ export default function VaultDescription(
|
|
|
1052
737
|
|
|
1053
738
|
return (
|
|
1054
739
|
<div style={containerStyle}>
|
|
1055
|
-
<h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
|
|
740
|
+
<h1 style={{ fontSize: "18px", marginBottom: "10px" }}>
|
|
741
|
+
Liquidation risk managed leverged {lstSymbol} Vault
|
|
742
|
+
</h1>
|
|
1056
743
|
<p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
|
|
1057
|
-
This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault,
|
|
1058
|
-
|
|
1059
|
-
|
|
744
|
+
This Levered Endur {lstSymbol} vault is a tokenized leveraged Vault,
|
|
745
|
+
auto-compounding strategy that takes upto 5x leverage on {lstSymbol} by
|
|
746
|
+
borrow {underlyingSymbol}. Borrowed amount is swapped to {lstSymbol} to
|
|
747
|
+
create leverage. Depositors receive vault shares that represent a
|
|
748
|
+
proportional claim on the underlying assets and accrued yield.
|
|
1060
749
|
</p>
|
|
1061
750
|
|
|
1062
751
|
<p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
|
|
1063
|
-
This vault uses Vesu for lending and borrowing. The oracle used by this
|
|
1064
|
-
{" "}
|
|
752
|
+
This vault uses Vesu for lending and borrowing. The oracle used by this
|
|
753
|
+
pool is a{" "}
|
|
754
|
+
{highlightTextWithLinks("conversion rate oracle", [
|
|
755
|
+
{
|
|
756
|
+
highlight: "conversion rate oracle",
|
|
757
|
+
link: "https://docs.pragma.build/starknet/development#conversion-rate",
|
|
758
|
+
},
|
|
759
|
+
])}{" "}
|
|
760
|
+
which is resilient to liquidity issues and price volatility, hence
|
|
761
|
+
reducing the risk of liquidation. However, overtime, if left
|
|
762
|
+
un-monitored, debt can increase enough to trigger a liquidation. But no
|
|
763
|
+
worries, our continuous monitoring systems look for situations with
|
|
764
|
+
reduced health factor and balance collateral/debt to bring it back to
|
|
765
|
+
safe levels. With Troves, you can have a peaceful sleep.
|
|
1065
766
|
</p>
|
|
1066
767
|
|
|
1067
|
-
<div
|
|
768
|
+
<div
|
|
769
|
+
style={{
|
|
770
|
+
backgroundColor: "#222",
|
|
771
|
+
padding: "10px",
|
|
772
|
+
borderRadius: "8px",
|
|
773
|
+
marginBottom: "20px",
|
|
774
|
+
border: "1px solid #444",
|
|
775
|
+
}}
|
|
776
|
+
>
|
|
1068
777
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
1069
|
-
<strong>Withdrawals:</strong> Requests can take up to
|
|
778
|
+
<strong>Withdrawals:</strong> Requests can take up to{" "}
|
|
779
|
+
<strong>1-2 hours</strong> to process as the vault unwinds and settles
|
|
780
|
+
routing.
|
|
1070
781
|
</p>
|
|
1071
782
|
</div>
|
|
1072
|
-
<div
|
|
783
|
+
<div
|
|
784
|
+
style={{
|
|
785
|
+
backgroundColor: "#222",
|
|
786
|
+
padding: "10px",
|
|
787
|
+
borderRadius: "8px",
|
|
788
|
+
marginBottom: "20px",
|
|
789
|
+
border: "1px solid #444",
|
|
790
|
+
}}
|
|
791
|
+
>
|
|
1073
792
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
1074
|
-
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are
|
|
793
|
+
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are
|
|
794
|
+
gradually increased over time. Until caps are raised, deposited Tokens
|
|
795
|
+
remain in the vault, generating a shared net return for all
|
|
796
|
+
depositors. There is no additional fee taken by Troves on Yield
|
|
797
|
+
token's APY, its only on added gain.
|
|
1075
798
|
</p>
|
|
1076
799
|
</div>
|
|
1077
800
|
{/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
@@ -1084,57 +807,205 @@ export default function VaultDescription(
|
|
|
1084
807
|
}
|
|
1085
808
|
|
|
1086
809
|
const re7UsdcPrimeDevansh: VesuExtendedStrategySettings = {
|
|
1087
|
-
vaultAddress: ContractAddr.from(
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
810
|
+
vaultAddress: ContractAddr.from(
|
|
811
|
+
"0x772d6cf5038c18ff5ab89f8945017bbf4d2c6959891339975c70a4f74ac6c8e",
|
|
812
|
+
),
|
|
813
|
+
manager: ContractAddr.from(
|
|
814
|
+
"0x3340c9d7231838e2dccff72b9004f1598a74e65c74b954f07fe1ea19d04a625",
|
|
815
|
+
),
|
|
816
|
+
vaultAllocator: ContractAddr.from(
|
|
817
|
+
"0x537353b35eee5ca2d9a45eb646977baddd4e89ce870a231dcada79884117292",
|
|
818
|
+
),
|
|
819
|
+
redeemRequestNFT: ContractAddr.from(
|
|
820
|
+
"0x6117d1a8c72c0457948083757e1a17ee8c0833b969d5c959b629e5f8feb56ec",
|
|
821
|
+
),
|
|
822
|
+
aumOracle: ContractAddr.from(
|
|
823
|
+
"0x6d7d68045bf5e0b5a4cec43241549851cb9645f7a73a20894152165dbe7083a",
|
|
824
|
+
),
|
|
1092
825
|
leafAdapters: [],
|
|
1093
826
|
adapters: [],
|
|
1094
827
|
targetHealthFactor: 1.4,
|
|
1095
828
|
minHealthFactor: 1.05,
|
|
1096
829
|
underlyingToken: Global.getDefaultTokens().find(
|
|
1097
|
-
(token) => token.symbol === "USDC"
|
|
830
|
+
(token) => token.symbol === "USDC",
|
|
1098
831
|
)!,
|
|
1099
832
|
quoteAmountToFetchPrice: new Web3Number(
|
|
1100
833
|
"0.001",
|
|
1101
|
-
Global.getDefaultTokens().find((token) => token.symbol === "WBTC")
|
|
834
|
+
Global.getDefaultTokens().find((token) => token.symbol === "WBTC")!
|
|
835
|
+
.decimals,
|
|
1102
836
|
),
|
|
1103
|
-
borrowable_assets: [
|
|
837
|
+
borrowable_assets: [
|
|
838
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC")!,
|
|
839
|
+
],
|
|
1104
840
|
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
export const VesuExtendedTestStrategies = (extendedBackendUrl: string, extendedApiKey: string, vaultIdExtended: number): IStrategyMetadata<VesuExtendedStrategySettings>[] => {
|
|
1108
|
-
return [
|
|
1109
|
-
getStrategySettingsVesuExtended('WBTC', 'USDC', re7UsdcPrimeDevansh, false, false, extendedBackendUrl, extendedApiKey, vaultIdExtended),
|
|
1110
|
-
]
|
|
1111
|
-
}
|
|
1112
|
-
|
|
841
|
+
walletAddress: '0x024b563C1C7d41B32BF4EFB9F38828508a65Be2d6e25268E9f63F22C5e9E51c5',
|
|
842
|
+
};
|
|
1113
843
|
|
|
844
|
+
const pureUsdc: VesuExtendedStrategySettings = {
|
|
845
|
+
vaultAddress: ContractAddr.from(
|
|
846
|
+
"0x745c972db65bdee10022fd875dd328c7f40a90849135b6a0f875a40f3c632ae",
|
|
847
|
+
),
|
|
848
|
+
manager: ContractAddr.from(
|
|
849
|
+
"0x364e0894edefb616ec090f57f5c0274517fcd98ab276ae1f021c5e962fa1deb",
|
|
850
|
+
),
|
|
851
|
+
vaultAllocator: ContractAddr.from(
|
|
852
|
+
"0x6fceed28e03a96091877568893df0dd89b9bb80fec30da2b742dacbd5526179",
|
|
853
|
+
),
|
|
854
|
+
redeemRequestNFT: ContractAddr.from(
|
|
855
|
+
"0x501c2b87728e22c6dfcebe4c0b2b3a9fba5845606e4d59fa7bf591badcbb42",
|
|
856
|
+
),
|
|
857
|
+
aumOracle: ContractAddr.from(
|
|
858
|
+
"0x6ccd95f5765242695d3c75e1440b1d0b30efac8babb864ce15729977b97cb82",
|
|
859
|
+
),
|
|
860
|
+
leafAdapters: [],
|
|
861
|
+
adapters: [],
|
|
862
|
+
targetHealthFactor: 1.4,
|
|
863
|
+
minHealthFactor: 1.35,
|
|
864
|
+
underlyingToken: Global.getDefaultTokens().find(
|
|
865
|
+
(token) => token.symbol === "USDC",
|
|
866
|
+
)!,
|
|
867
|
+
quoteAmountToFetchPrice: new Web3Number(
|
|
868
|
+
"0.001",
|
|
869
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC")!
|
|
870
|
+
.decimals,
|
|
871
|
+
),
|
|
872
|
+
borrowable_assets: [
|
|
873
|
+
Global.getDefaultTokens().find((token) => token.symbol === "USDC")!,
|
|
874
|
+
],
|
|
875
|
+
minimumWBTCDifferenceForAvnuSwap: MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP,
|
|
876
|
+
walletAddress: '0x058571C23da5FEdd4e36003FAE3fE2fA9782f2692E552f081839142B10770D0B',
|
|
877
|
+
};
|
|
1114
878
|
|
|
1115
|
-
|
|
879
|
+
export const VesuExtendedTestStrategies = (
|
|
880
|
+
extendedBackendReadUrl: string,
|
|
881
|
+
extendedBackendWriteUrl: string,
|
|
882
|
+
vaultIdExtended: number,
|
|
883
|
+
minimumExtendedMovementAmount: number,
|
|
884
|
+
minimumVesuMovementAmount: number,
|
|
885
|
+
minimumExtendedRetriesDelayForOrderStatus: number,
|
|
886
|
+
minimumExtendedPriceDifferenceForSwapOpen: number,
|
|
887
|
+
maximumExtendedPriceDifferenceForSwapClosing: number,
|
|
888
|
+
): IStrategyMetadata<VesuExtendedStrategySettings>[] => {
|
|
889
|
+
return [
|
|
890
|
+
getStrategySettingsVesuExtended(
|
|
891
|
+
"WBTC",
|
|
892
|
+
"USDC",
|
|
893
|
+
re7UsdcPrimeDevansh,
|
|
894
|
+
false,
|
|
895
|
+
false,
|
|
896
|
+
extendedBackendReadUrl,
|
|
897
|
+
extendedBackendWriteUrl,
|
|
898
|
+
vaultIdExtended,
|
|
899
|
+
minimumExtendedMovementAmount,
|
|
900
|
+
minimumVesuMovementAmount,
|
|
901
|
+
minimumExtendedRetriesDelayForOrderStatus,
|
|
902
|
+
minimumExtendedPriceDifferenceForSwapOpen,
|
|
903
|
+
maximumExtendedPriceDifferenceForSwapClosing,
|
|
904
|
+
),
|
|
905
|
+
getStrategySettingsVesuExtended(
|
|
906
|
+
"WBTC",
|
|
907
|
+
"USDC",
|
|
908
|
+
pureUsdc,
|
|
909
|
+
false,
|
|
910
|
+
false,
|
|
911
|
+
extendedBackendReadUrl,
|
|
912
|
+
extendedBackendWriteUrl,
|
|
913
|
+
vaultIdExtended,
|
|
914
|
+
minimumExtendedMovementAmount,
|
|
915
|
+
minimumVesuMovementAmount,
|
|
916
|
+
minimumExtendedRetriesDelayForOrderStatus,
|
|
917
|
+
minimumExtendedPriceDifferenceForSwapOpen,
|
|
918
|
+
maximumExtendedPriceDifferenceForSwapClosing,
|
|
919
|
+
),
|
|
920
|
+
];
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* Constructs a complete IStrategyMetadata object for a Vesu-Extended strategy,
|
|
925
|
+
* including adapter wiring, risk configuration, FAQ, and UI description.
|
|
926
|
+
*/
|
|
927
|
+
function getStrategySettingsVesuExtended(
|
|
928
|
+
collateralSymbol: string,
|
|
929
|
+
underlyingSymbol: string,
|
|
930
|
+
addresses: VesuExtendedStrategySettings,
|
|
931
|
+
isPreview: boolean = false,
|
|
932
|
+
isLST: boolean,
|
|
933
|
+
extendedBackendReadUrl: string,
|
|
934
|
+
extendedBackendWriteUrl: string,
|
|
935
|
+
vaultIdExtended: number,
|
|
936
|
+
minimumExtendedMovementAmount: number,
|
|
937
|
+
minimumVesuMovementAmount: number,
|
|
938
|
+
minimumExtendedRetriesDelayForOrderStatus: number,
|
|
939
|
+
minimumExtendedPriceDifferenceForSwapOpen: number,
|
|
940
|
+
maximumExtendedPriceDifferenceForSwapClosing: number,
|
|
941
|
+
): IStrategyMetadata<VesuExtendedStrategySettings> {
|
|
1116
942
|
return {
|
|
943
|
+
id: `extended_${underlyingSymbol.toLowerCase()}_test`,
|
|
1117
944
|
name: `Extended Test ${underlyingSymbol}`,
|
|
1118
|
-
description: getDescription(
|
|
945
|
+
description: getDescription(collateralSymbol, underlyingSymbol),
|
|
1119
946
|
address: addresses.vaultAddress,
|
|
1120
947
|
launchBlock: 0,
|
|
1121
|
-
type:
|
|
1122
|
-
|
|
1123
|
-
|
|
948
|
+
type: "Other",
|
|
949
|
+
vaultType: {
|
|
950
|
+
type: VaultType.DELTA_NEUTRAL,
|
|
951
|
+
description: "Delta Neutral strategy using extended position on Vesu"
|
|
952
|
+
},
|
|
953
|
+
depositTokens: [
|
|
954
|
+
Global.getDefaultTokens().find(
|
|
955
|
+
(token) => token.symbol === 'WBTC',
|
|
956
|
+
)!,
|
|
957
|
+
],
|
|
958
|
+
additionalInfo: getLooperSettings(
|
|
959
|
+
collateralSymbol,
|
|
960
|
+
underlyingSymbol,
|
|
961
|
+
addresses,
|
|
962
|
+
VesuPools.Re7USDCPrime,
|
|
963
|
+
extendedBackendReadUrl,
|
|
964
|
+
extendedBackendWriteUrl,
|
|
965
|
+
vaultIdExtended,
|
|
966
|
+
minimumExtendedMovementAmount,
|
|
967
|
+
minimumVesuMovementAmount,
|
|
968
|
+
minimumExtendedRetriesDelayForOrderStatus,
|
|
969
|
+
minimumExtendedPriceDifferenceForSwapOpen,
|
|
970
|
+
maximumExtendedPriceDifferenceForSwapClosing,
|
|
971
|
+
),
|
|
1124
972
|
risk: {
|
|
1125
973
|
riskFactor: _riskFactor,
|
|
1126
974
|
netRisk:
|
|
1127
975
|
_riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
|
|
1128
976
|
_riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
1129
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
977
|
+
notARisks: getNoRiskTags(_riskFactor),
|
|
1130
978
|
},
|
|
1131
979
|
auditUrl: AUDIT_URL,
|
|
1132
980
|
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
1133
|
-
maxTVL: Web3Number.fromWei(0, 18),
|
|
1134
981
|
contractDetails: getContractDetails(addresses),
|
|
1135
|
-
faqs: getFAQs(
|
|
1136
|
-
investmentSteps: getInvestmentSteps(
|
|
982
|
+
faqs: getFAQs(collateralSymbol, underlyingSymbol, isLST),
|
|
983
|
+
investmentSteps: getInvestmentSteps(collateralSymbol, underlyingSymbol),
|
|
1137
984
|
isPreview: isPreview,
|
|
1138
|
-
apyMethodology: isLST
|
|
1139
|
-
|
|
1140
|
-
|
|
985
|
+
apyMethodology: isLST
|
|
986
|
+
? "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."
|
|
987
|
+
: "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.",
|
|
988
|
+
security: {
|
|
989
|
+
auditStatus: AuditStatus.NOT_AUDITED,
|
|
990
|
+
sourceCode: {
|
|
991
|
+
type: SourceCodeType.OPEN_SOURCE,
|
|
992
|
+
contractLink: AUDIT_URL,
|
|
993
|
+
},
|
|
994
|
+
accessControl: {
|
|
995
|
+
type: AccessControlType.MULTISIG_ACCOUNT,
|
|
996
|
+
addresses: [addresses.vaultAddress],
|
|
997
|
+
timeLock: "None",
|
|
998
|
+
},
|
|
999
|
+
},
|
|
1000
|
+
redemptionInfo: {
|
|
1001
|
+
instantWithdrawalVault: InstantWithdrawalVault.NO,
|
|
1002
|
+
redemptionsInfo: [{
|
|
1003
|
+
title: "Up to $500k",
|
|
1004
|
+
description: "1-24 hours",
|
|
1005
|
+
}],
|
|
1006
|
+
alerts: [],
|
|
1007
|
+
},
|
|
1008
|
+
usualTimeToEarnings: null,
|
|
1009
|
+
usualTimeToEarningsDescription: null,
|
|
1010
|
+
};
|
|
1011
|
+
}
|