@strkfarm/sdk 2.0.0-dev.2 → 2.0.0-dev.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.browser.global.js +2006 -1062
- package/dist/index.browser.mjs +1845 -911
- package/dist/index.d.ts +144 -37
- package/dist/index.js +1853 -915
- package/dist/index.mjs +1845 -911
- package/package.json +1 -1
- package/src/modules/ExtendedWrapperSDk/types.ts +1 -1
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +39 -8
- package/src/modules/ekubo-quoter.ts +0 -12
- package/src/strategies/index.ts +2 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +17 -9
- package/src/strategies/universal-adapters/extended-adapter.ts +500 -146
- package/src/strategies/universal-adapters/index.ts +2 -1
- package/src/strategies/universal-adapters/vesu-adapter.ts +6 -6
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +778 -396
- package/src/strategies/universal-lst-muliplier-strategy.tsx +2 -1
- package/src/strategies/universal-strategy.tsx +5 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +25 -16
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +36 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +3 -6
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +50 -16
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +746 -305
|
@@ -1,15 +1,43 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
2
|
import { IConfig, Protocols, TokenInfo } from "@/interfaces";
|
|
3
3
|
import { PricerBase } from "@/modules/pricerBase";
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
BaseAdapter,
|
|
6
|
+
BaseAdapterConfig,
|
|
7
|
+
SupportedPosition,
|
|
8
|
+
PositionInfo,
|
|
9
|
+
PositionAPY,
|
|
10
|
+
APYType,
|
|
11
|
+
ManageCall,
|
|
12
|
+
AdapterLeafType,
|
|
13
|
+
GenerateCallFn,
|
|
14
|
+
DepositParams,
|
|
15
|
+
WithdrawParams,
|
|
16
|
+
PositionAmount,
|
|
17
|
+
} from "./baseAdapter";
|
|
18
|
+
import {
|
|
19
|
+
SIMPLE_SANITIZER,
|
|
20
|
+
SIMPLE_SANITIZER_V2,
|
|
21
|
+
toBigInt,
|
|
22
|
+
VESU_SINGLETON,
|
|
23
|
+
VESU_V2_MODIFY_POSITION_SANITIZER,
|
|
24
|
+
} from "./adapter-utils";
|
|
6
25
|
import { hash, uint256, Contract, CairoCustomEnum, num } from "starknet";
|
|
7
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
VesuAdapter,
|
|
28
|
+
VesuMultiplyCallParams,
|
|
29
|
+
VesuModifyDelegationCallParams,
|
|
30
|
+
getVesuSingletonAddress,
|
|
31
|
+
VesuPools,
|
|
32
|
+
Swap,
|
|
33
|
+
IncreaseLeverParams,
|
|
34
|
+
DecreaseLeverParams,
|
|
35
|
+
} from "./vesu-adapter";
|
|
8
36
|
import { logger } from "@/utils";
|
|
9
37
|
import { WALLET_ADDRESS } from "../vesu-extended-strategy/utils/constants";
|
|
10
|
-
import VesuMultiplyAbi from
|
|
11
|
-
import VesuSingletonAbi from
|
|
12
|
-
import VesuPoolV2Abi from
|
|
38
|
+
import VesuMultiplyAbi from "@/data/vesu-multiple.abi.json";
|
|
39
|
+
import VesuSingletonAbi from "../../data/vesu-singleton.abi.json";
|
|
40
|
+
import VesuPoolV2Abi from "@/data/vesu-pool-v2.abi.json";
|
|
13
41
|
import { EkuboQuoter, TokenMarketData } from "@/modules";
|
|
14
42
|
import { calculateDebtReductionAmountForWithdrawal } from "../vesu-extended-strategy/utils/helper";
|
|
15
43
|
import { HealthFactorMath } from "@/utils/health-factor-math";
|
|
@@ -22,13 +50,17 @@ export interface VesuMultiplyAdapterConfig extends BaseAdapterConfig {
|
|
|
22
50
|
targetHealthFactor: number;
|
|
23
51
|
minHealthFactor: number;
|
|
24
52
|
quoteAmountToFetchPrice: Web3Number;
|
|
53
|
+
minimumVesuMovementAmount: number;
|
|
25
54
|
}
|
|
26
55
|
|
|
27
|
-
export class VesuMultiplyAdapter extends BaseAdapter<
|
|
56
|
+
export class VesuMultiplyAdapter extends BaseAdapter<
|
|
57
|
+
DepositParams,
|
|
58
|
+
WithdrawParams
|
|
59
|
+
> {
|
|
28
60
|
readonly config: VesuMultiplyAdapterConfig;
|
|
29
61
|
readonly vesuAdapter: VesuAdapter;
|
|
30
62
|
readonly tokenMarketData: TokenMarketData;
|
|
31
|
-
|
|
63
|
+
readonly minimumVesuMovementAmount: number;
|
|
32
64
|
constructor(config: VesuMultiplyAdapterConfig) {
|
|
33
65
|
super(config, VesuMultiplyAdapter.name, Protocols.VESU);
|
|
34
66
|
this.config = config;
|
|
@@ -37,15 +69,28 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
37
69
|
collateral: config.collateral,
|
|
38
70
|
debt: config.debt,
|
|
39
71
|
vaultAllocator: config.vaultAllocator,
|
|
40
|
-
id:
|
|
72
|
+
id: "",
|
|
41
73
|
});
|
|
42
|
-
this.
|
|
74
|
+
this.minimumVesuMovementAmount = config.minimumVesuMovementAmount ?? 5; //5 usdc
|
|
75
|
+
this.tokenMarketData = new TokenMarketData(
|
|
76
|
+
this.config.pricer,
|
|
77
|
+
this.config.networkConfig
|
|
78
|
+
);
|
|
43
79
|
}
|
|
44
80
|
|
|
45
|
-
protected async getAPY(
|
|
81
|
+
protected async getAPY(
|
|
82
|
+
supportedPosition: SupportedPosition
|
|
83
|
+
): Promise<PositionAPY> {
|
|
46
84
|
const CACHE_KEY = `apy_${this.config.poolId.address}_${supportedPosition.asset.symbol}`;
|
|
47
85
|
const cacheData = this.getCache<PositionAPY>(CACHE_KEY);
|
|
48
|
-
console.log(
|
|
86
|
+
console.log(
|
|
87
|
+
`${VesuMultiplyAdapter.name}::getAPY cacheData: ${JSON.stringify(
|
|
88
|
+
cacheData
|
|
89
|
+
)}`,
|
|
90
|
+
this.vesuAdapter.config.poolId.shortString(),
|
|
91
|
+
this.vesuAdapter.config.collateral.symbol,
|
|
92
|
+
this.vesuAdapter.config.debt.symbol
|
|
93
|
+
);
|
|
49
94
|
if (cacheData) {
|
|
50
95
|
return cacheData;
|
|
51
96
|
}
|
|
@@ -53,24 +98,30 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
53
98
|
// Get Vesu pools to find APY for the asset
|
|
54
99
|
const allVesuPools = await VesuAdapter.getVesuPools();
|
|
55
100
|
const asset = supportedPosition.asset;
|
|
56
|
-
const pool = allVesuPools.pools.find(p =>
|
|
101
|
+
const pool = allVesuPools.pools.find((p) =>
|
|
102
|
+
this.vesuAdapter.config.poolId.eqString(num.getHexString(p.id))
|
|
103
|
+
);
|
|
57
104
|
if (!pool) {
|
|
58
|
-
logger.warn(
|
|
105
|
+
logger.warn(
|
|
106
|
+
`VesuMultiplyAdapter: Pool not found for token ${asset.symbol}`
|
|
107
|
+
);
|
|
59
108
|
return {
|
|
60
109
|
apy: 0,
|
|
61
|
-
type: APYType.BASE
|
|
110
|
+
type: APYType.BASE,
|
|
62
111
|
};
|
|
63
112
|
}
|
|
64
113
|
// Find the asset stats for our token
|
|
65
|
-
const assetStats = pool.assets.find(
|
|
66
|
-
a.symbol.toLowerCase() === asset.symbol.toLowerCase()
|
|
114
|
+
const assetStats = pool.assets.find(
|
|
115
|
+
(a: any) => a.symbol.toLowerCase() === asset.symbol.toLowerCase()
|
|
67
116
|
)?.stats;
|
|
68
117
|
|
|
69
118
|
if (!assetStats) {
|
|
70
|
-
logger.warn(
|
|
119
|
+
logger.warn(
|
|
120
|
+
`VesuMultiplyAdapter: Asset stats not found for token ${asset.symbol}`
|
|
121
|
+
);
|
|
71
122
|
return {
|
|
72
123
|
apy: 0,
|
|
73
|
-
type: APYType.BASE
|
|
124
|
+
type: APYType.BASE,
|
|
74
125
|
};
|
|
75
126
|
}
|
|
76
127
|
// Get appropriate APY based on position type
|
|
@@ -79,15 +130,21 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
79
130
|
// For debt positions, use borrow APY
|
|
80
131
|
apy = Number(assetStats.borrowApr?.value || 0) / 1e18;
|
|
81
132
|
|
|
82
|
-
// todo
|
|
133
|
+
// todo
|
|
83
134
|
// Account for rewards on debt token
|
|
84
135
|
} else {
|
|
85
136
|
// For collateral positions, use supply APY
|
|
86
137
|
const isAssetBTC = asset.symbol.toLowerCase().includes("btc");
|
|
87
|
-
const baseAPY =
|
|
138
|
+
const baseAPY =
|
|
139
|
+
Number(
|
|
140
|
+
isAssetBTC
|
|
141
|
+
? assetStats.btcFiSupplyApr?.value + assetStats.supplyApy?.value
|
|
142
|
+
: assetStats.supplyApy?.value || 0
|
|
143
|
+
) / 1e18;
|
|
88
144
|
|
|
89
145
|
// account for reward yield (like STRK rewards)
|
|
90
|
-
const rewardAPY =
|
|
146
|
+
const rewardAPY =
|
|
147
|
+
Number(assetStats.defiSpringSupplyApr?.value || "0") / 1e18;
|
|
91
148
|
|
|
92
149
|
// account for base yield of LST
|
|
93
150
|
const isSupported = this.tokenMarketData.isAPYSupported(asset);
|
|
@@ -99,13 +156,16 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
99
156
|
}
|
|
100
157
|
const result = {
|
|
101
158
|
apy,
|
|
102
|
-
type: supportedPosition.isDebt ? APYType.BASE : APYType.BASE
|
|
159
|
+
type: supportedPosition.isDebt ? APYType.BASE : APYType.BASE,
|
|
103
160
|
};
|
|
104
161
|
|
|
105
162
|
this.setCache(CACHE_KEY, result, 300000); // Cache for 5 minutes
|
|
106
163
|
return result;
|
|
107
164
|
} catch (error) {
|
|
108
|
-
logger.error(
|
|
165
|
+
logger.error(
|
|
166
|
+
`VesuMultiplyAdapter: Error getting APY for ${supportedPosition.asset.symbol}:`,
|
|
167
|
+
error
|
|
168
|
+
);
|
|
109
169
|
// return {
|
|
110
170
|
// apy: 0,
|
|
111
171
|
// type: APYType.BASE
|
|
@@ -114,7 +174,9 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
114
174
|
}
|
|
115
175
|
}
|
|
116
176
|
|
|
117
|
-
protected async getPosition(
|
|
177
|
+
protected async getPosition(
|
|
178
|
+
supportedPosition: SupportedPosition
|
|
179
|
+
): Promise<PositionAmount> {
|
|
118
180
|
const CACHE_KEY = `position_${this.config.poolId.address}_${supportedPosition.asset.symbol}`;
|
|
119
181
|
const cacheData = this.getCache<PositionAmount>(CACHE_KEY);
|
|
120
182
|
if (cacheData) {
|
|
@@ -125,19 +187,23 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
125
187
|
// Use VesuAdapter to get positions
|
|
126
188
|
this.vesuAdapter.networkConfig = this.config.networkConfig;
|
|
127
189
|
this.vesuAdapter.pricer = this.config.pricer;
|
|
128
|
-
|
|
129
|
-
const positions = await this.vesuAdapter.getPositions(
|
|
130
|
-
|
|
190
|
+
|
|
191
|
+
const positions = await this.vesuAdapter.getPositions(
|
|
192
|
+
this.config.networkConfig
|
|
193
|
+
);
|
|
194
|
+
|
|
131
195
|
// Find the position for our asset
|
|
132
|
-
let position = positions.find(p =>
|
|
196
|
+
let position = positions.find((p) =>
|
|
133
197
|
p.token.address.eq(supportedPosition.asset.address)
|
|
134
198
|
);
|
|
135
199
|
|
|
136
200
|
if (!position) {
|
|
137
|
-
logger.warn(
|
|
201
|
+
logger.warn(
|
|
202
|
+
`VesuMultiplyAdapter: Position not found for token ${supportedPosition.asset.symbol}`
|
|
203
|
+
);
|
|
138
204
|
return {
|
|
139
|
-
amount: new Web3Number(
|
|
140
|
-
remarks: "Position not found"
|
|
205
|
+
amount: new Web3Number("0", supportedPosition.asset.decimals),
|
|
206
|
+
remarks: "Position not found",
|
|
141
207
|
};
|
|
142
208
|
}
|
|
143
209
|
|
|
@@ -149,7 +215,10 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
149
215
|
this.setCache(CACHE_KEY, position, 60000); // Cache for 1 minute
|
|
150
216
|
return position;
|
|
151
217
|
} catch (error) {
|
|
152
|
-
logger.error(
|
|
218
|
+
logger.error(
|
|
219
|
+
`VesuMultiplyAdapter: Error getting position for ${supportedPosition.asset.symbol}:`,
|
|
220
|
+
error
|
|
221
|
+
);
|
|
153
222
|
// return new Web3Number('0', supportedPosition.asset.decimals);
|
|
154
223
|
throw error;
|
|
155
224
|
}
|
|
@@ -157,7 +226,10 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
157
226
|
|
|
158
227
|
async maxBorrowableAPY(): Promise<number> {
|
|
159
228
|
// get collateral APY
|
|
160
|
-
const collateralAPY = await this.getAPY({
|
|
229
|
+
const collateralAPY = await this.getAPY({
|
|
230
|
+
asset: this.config.collateral,
|
|
231
|
+
isDebt: false,
|
|
232
|
+
});
|
|
161
233
|
const apy = collateralAPY.apy * 0.8;
|
|
162
234
|
return apy;
|
|
163
235
|
}
|
|
@@ -170,58 +242,92 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
170
242
|
// Get current positions
|
|
171
243
|
this.vesuAdapter.networkConfig = this.config.networkConfig;
|
|
172
244
|
this.vesuAdapter.pricer = this.config.pricer;
|
|
173
|
-
|
|
174
|
-
const positions = await this.vesuAdapter.getPositions(
|
|
175
|
-
|
|
176
|
-
|
|
245
|
+
|
|
246
|
+
const positions = await this.vesuAdapter.getPositions(
|
|
247
|
+
this.config.networkConfig
|
|
248
|
+
);
|
|
249
|
+
const collateralPosition = positions.find((p) =>
|
|
250
|
+
p.token.address.eq(collateral.address)
|
|
251
|
+
);
|
|
252
|
+
const debtPosition = positions.find((p) =>
|
|
253
|
+
p.token.address.eq(debt.address)
|
|
254
|
+
);
|
|
177
255
|
|
|
178
256
|
if (!collateralPosition || !debtPosition) {
|
|
179
|
-
throw new Error(
|
|
257
|
+
throw new Error("Could not find current positions");
|
|
180
258
|
}
|
|
181
259
|
|
|
182
260
|
// Calculate max borrowable amount
|
|
183
261
|
const maxBorrowableAPY = await this.maxBorrowableAPY();
|
|
184
|
-
const maxBorrowable =
|
|
185
|
-
this.
|
|
186
|
-
|
|
187
|
-
|
|
262
|
+
const maxBorrowable =
|
|
263
|
+
await this.vesuAdapter.getMaxBorrowableByInterestRate(
|
|
264
|
+
this.config.networkConfig,
|
|
265
|
+
debt,
|
|
266
|
+
maxBorrowableAPY
|
|
267
|
+
);
|
|
268
|
+
logger.verbose(
|
|
269
|
+
`VesuMultiplyAdapter: Max borrowable: ${maxBorrowable.toNumber()}`
|
|
270
|
+
);
|
|
271
|
+
const debtCap = await this.vesuAdapter.getDebtCap(
|
|
272
|
+
this.config.networkConfig
|
|
188
273
|
);
|
|
189
|
-
logger.verbose(`VesuMultiplyAdapter: Max borrowable: ${maxBorrowable.toNumber()}`);
|
|
190
|
-
const debtCap = await this.vesuAdapter.getDebtCap(this.config.networkConfig);
|
|
191
274
|
logger.verbose(`VesuMultiplyAdapter: Debt cap: ${debtCap.toNumber()}`);
|
|
192
275
|
const actualMaxBorrowable = maxBorrowable.minimum(debtCap);
|
|
193
|
-
logger.verbose(
|
|
276
|
+
logger.verbose(
|
|
277
|
+
`VesuMultiplyAdapter: Actual max borrowable: ${actualMaxBorrowable.toNumber()}`
|
|
278
|
+
);
|
|
194
279
|
|
|
195
280
|
// Calculate max collateral that can be deposited based on LTV
|
|
196
|
-
const maxLTV = await this.vesuAdapter.getLTVConfig(
|
|
197
|
-
|
|
281
|
+
const maxLTV = await this.vesuAdapter.getLTVConfig(
|
|
282
|
+
this.config.networkConfig
|
|
283
|
+
);
|
|
284
|
+
const collateralPrice = await this.config.pricer.getPrice(
|
|
285
|
+
collateral.symbol
|
|
286
|
+
);
|
|
198
287
|
if (collateralPrice.price === 0) {
|
|
199
|
-
throw new Error(
|
|
288
|
+
throw new Error("Collateral price is 0");
|
|
200
289
|
}
|
|
201
290
|
const debtPrice = await this.config.pricer.getPrice(debt.symbol);
|
|
202
291
|
if (debtPrice.price === 0) {
|
|
203
|
-
throw new Error(
|
|
292
|
+
throw new Error("Debt price is 0");
|
|
204
293
|
}
|
|
205
|
-
const maxCollateralFromDebt =
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
294
|
+
const maxCollateralFromDebt =
|
|
295
|
+
HealthFactorMath.getMinCollateralRequiredOnLooping(
|
|
296
|
+
actualMaxBorrowable,
|
|
297
|
+
debtPrice.price,
|
|
298
|
+
this.config.targetHealthFactor,
|
|
299
|
+
maxLTV,
|
|
300
|
+
collateralPrice.price,
|
|
301
|
+
collateral
|
|
302
|
+
);
|
|
213
303
|
|
|
214
|
-
const maxDepositAmount = amount
|
|
304
|
+
const maxDepositAmount = amount
|
|
305
|
+
? amount.minimum(maxCollateralFromDebt)
|
|
306
|
+
: maxCollateralFromDebt;
|
|
215
307
|
const usdValue = await this.getUSDValue(collateral, maxDepositAmount);
|
|
216
|
-
logger.verbose(
|
|
217
|
-
|
|
218
|
-
|
|
308
|
+
logger.verbose(
|
|
309
|
+
`VesuMultiplyAdapter: Max deposit::USD value: ${usdValue}, amount: ${maxDepositAmount.toNumber()}`
|
|
310
|
+
);
|
|
311
|
+
const apys = await Promise.all([
|
|
312
|
+
this.getAPY({ asset: collateral, isDebt: false }),
|
|
313
|
+
this.getAPY({ asset: debt, isDebt: true }),
|
|
314
|
+
]);
|
|
315
|
+
logger.verbose(
|
|
316
|
+
`VesuMultiplyAdapter: Apys: ${apys[0].apy}, ${apys[1].apy}`
|
|
317
|
+
);
|
|
219
318
|
|
|
220
319
|
const borrowAmountUSD = actualMaxBorrowable.multipliedBy(debtPrice.price);
|
|
221
|
-
logger.verbose(
|
|
320
|
+
logger.verbose(
|
|
321
|
+
`VesuMultiplyAdapter: Borrow amount: ${actualMaxBorrowable.toNumber()}, borrow amount USD: ${borrowAmountUSD.toNumber()}`
|
|
322
|
+
);
|
|
222
323
|
const netCollateralUSD = usdValue + borrowAmountUSD.toNumber();
|
|
223
|
-
const netAPY =
|
|
224
|
-
|
|
324
|
+
const netAPY =
|
|
325
|
+
(apys[0].apy * netCollateralUSD +
|
|
326
|
+
apys[1].apy * borrowAmountUSD.toNumber()) /
|
|
327
|
+
usdValue;
|
|
328
|
+
logger.verbose(
|
|
329
|
+
`VesuMultiplyAdapter: Max deposit amount: ${maxDepositAmount.toNumber()}, netAPY: ${netAPY}`
|
|
330
|
+
);
|
|
225
331
|
return {
|
|
226
332
|
tokenInfo: collateral,
|
|
227
333
|
amount: maxDepositAmount,
|
|
@@ -229,12 +335,15 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
229
335
|
remarks: "Max deposit based on available debt capacity",
|
|
230
336
|
apy: {
|
|
231
337
|
apy: netAPY,
|
|
232
|
-
type: APYType.BASE
|
|
338
|
+
type: APYType.BASE,
|
|
233
339
|
},
|
|
234
|
-
protocol: this.protocol
|
|
340
|
+
protocol: this.protocol,
|
|
235
341
|
};
|
|
236
342
|
} catch (error) {
|
|
237
|
-
logger.error(
|
|
343
|
+
logger.error(
|
|
344
|
+
`VesuMultiplyAdapter: Error calculating max deposit:`,
|
|
345
|
+
error
|
|
346
|
+
);
|
|
238
347
|
throw error;
|
|
239
348
|
}
|
|
240
349
|
}
|
|
@@ -247,28 +356,50 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
247
356
|
// Calculate how much can be withdrawn without affecting health factor too much
|
|
248
357
|
this.vesuAdapter.networkConfig = this.config.networkConfig;
|
|
249
358
|
this.vesuAdapter.pricer = this.config.pricer;
|
|
250
|
-
|
|
251
|
-
const positions = await this.vesuAdapter.getPositions(
|
|
252
|
-
|
|
253
|
-
|
|
359
|
+
|
|
360
|
+
const positions = await this.vesuAdapter.getPositions(
|
|
361
|
+
this.config.networkConfig
|
|
362
|
+
);
|
|
363
|
+
const collateralPosition = positions.find((p) =>
|
|
364
|
+
p.token.address.eq(collateral.address)
|
|
365
|
+
);
|
|
366
|
+
const debtPosition = positions.find((p) =>
|
|
367
|
+
p.token.address.eq(this.config.debt.address)
|
|
368
|
+
);
|
|
254
369
|
|
|
255
370
|
if (!collateralPosition || !debtPosition) {
|
|
256
|
-
throw new Error(
|
|
371
|
+
throw new Error("Could not find current positions");
|
|
257
372
|
}
|
|
258
373
|
|
|
259
374
|
// Calculate max withdrawable (conservative approach)
|
|
260
|
-
const collateralPrice =
|
|
375
|
+
const collateralPrice =
|
|
376
|
+
collateralPosition.usdValue / collateralPosition.amount.toNumber();
|
|
261
377
|
const debtInCollateral = debtPosition.usdValue / collateralPrice;
|
|
262
378
|
const maxWithdrawable = collateralPosition.amount.minus(debtInCollateral);
|
|
263
379
|
|
|
264
|
-
const result = maxWithdrawable.greaterThan(0)
|
|
380
|
+
const result = maxWithdrawable.greaterThan(0)
|
|
381
|
+
? maxWithdrawable
|
|
382
|
+
: new Web3Number("0", collateral.decimals);
|
|
265
383
|
const usdValue = await this.getUSDValue(collateral, result);
|
|
266
384
|
const debtUSD = debtPosition.usdValue;
|
|
267
|
-
logger.verbose(
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
385
|
+
logger.verbose(
|
|
386
|
+
`VesuMultiplyAdapter: Debt USD: ${debtUSD}, collateral USD: ${usdValue}`
|
|
387
|
+
);
|
|
388
|
+
const apys = await Promise.all([
|
|
389
|
+
this.getAPY({ asset: collateral, isDebt: false }),
|
|
390
|
+
this.getAPY({ asset: debt, isDebt: true }),
|
|
391
|
+
]);
|
|
392
|
+
logger.verbose(
|
|
393
|
+
`VesuMultiplyAdapter: Apys: ${apys[0].apy}, ${apys[1].apy}`
|
|
394
|
+
);
|
|
395
|
+
const netAPY =
|
|
396
|
+
usdValue - debtUSD > 0
|
|
397
|
+
? (apys[0].apy * usdValue + apys[1].apy * debtUSD) /
|
|
398
|
+
(usdValue - debtUSD)
|
|
399
|
+
: 0;
|
|
400
|
+
logger.verbose(
|
|
401
|
+
`VesuMultiplyAdapter: Max withdraw amount: ${result.toNumber()}, netAPY: ${netAPY}`
|
|
402
|
+
);
|
|
272
403
|
return {
|
|
273
404
|
tokenInfo: collateral,
|
|
274
405
|
amount: result,
|
|
@@ -276,58 +407,71 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
276
407
|
remarks: "Max withdraw based on health factor",
|
|
277
408
|
apy: {
|
|
278
409
|
apy: netAPY,
|
|
279
|
-
type: APYType.BASE
|
|
410
|
+
type: APYType.BASE,
|
|
280
411
|
},
|
|
281
|
-
protocol: this.protocol
|
|
412
|
+
protocol: this.protocol,
|
|
282
413
|
};
|
|
283
414
|
} catch (error) {
|
|
284
|
-
logger.error(
|
|
415
|
+
logger.error(
|
|
416
|
+
`VesuMultiplyAdapter: Error calculating max withdraw:`,
|
|
417
|
+
error
|
|
418
|
+
);
|
|
285
419
|
throw error;
|
|
286
420
|
}
|
|
287
421
|
}
|
|
288
422
|
|
|
289
423
|
protected _getDepositLeaf(): {
|
|
290
|
-
target: ContractAddr
|
|
291
|
-
method: string
|
|
292
|
-
packedArguments: bigint[]
|
|
293
|
-
sanitizer: ContractAddr
|
|
294
|
-
id: string
|
|
424
|
+
target: ContractAddr;
|
|
425
|
+
method: string;
|
|
426
|
+
packedArguments: bigint[];
|
|
427
|
+
sanitizer: ContractAddr;
|
|
428
|
+
id: string;
|
|
295
429
|
}[] {
|
|
296
430
|
const collateral = this.config.collateral;
|
|
297
431
|
const debt = this.config.debt;
|
|
298
|
-
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
299
|
-
|
|
432
|
+
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
433
|
+
this.config.poolId
|
|
434
|
+
);
|
|
435
|
+
const vesuMultiply = isV2
|
|
436
|
+
? this.vesuAdapter.VESU_MULTIPLY
|
|
437
|
+
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
300
438
|
|
|
301
439
|
return [
|
|
302
440
|
// Approval step for collateral
|
|
303
441
|
{
|
|
304
442
|
target: collateral.address,
|
|
305
|
-
method:
|
|
443
|
+
method: "approve",
|
|
306
444
|
packedArguments: [
|
|
307
445
|
vesuMultiply.toBigInt(), // spender
|
|
308
446
|
],
|
|
309
447
|
sanitizer: SIMPLE_SANITIZER,
|
|
310
448
|
// amc = approve multiply collateral
|
|
311
|
-
id: `amc_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
449
|
+
id: `amc_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
450
|
+
debt.symbol
|
|
451
|
+
}`,
|
|
312
452
|
},
|
|
313
453
|
// Switch delegation on
|
|
314
454
|
{
|
|
315
455
|
target: vesuSingleton,
|
|
316
|
-
method:
|
|
317
|
-
packedArguments: isV2
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
456
|
+
method: "modify_delegation",
|
|
457
|
+
packedArguments: isV2
|
|
458
|
+
? [
|
|
459
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
460
|
+
]
|
|
461
|
+
: [
|
|
462
|
+
this.config.poolId.toBigInt(),
|
|
463
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
464
|
+
],
|
|
323
465
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
324
466
|
// sd1 = switch delegation on
|
|
325
|
-
id: `sd1_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
467
|
+
id: `sd1_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
468
|
+
debt.symbol
|
|
469
|
+
}`,
|
|
326
470
|
},
|
|
327
471
|
// Vesu multiply call
|
|
328
472
|
{
|
|
329
473
|
target: vesuMultiply,
|
|
330
|
-
method:
|
|
474
|
+
method: "modify_lever",
|
|
331
475
|
packedArguments: [
|
|
332
476
|
this.config.poolId.toBigInt(),
|
|
333
477
|
collateral.address.toBigInt(),
|
|
@@ -336,34 +480,44 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
336
480
|
],
|
|
337
481
|
sanitizer: SIMPLE_SANITIZER_V2,
|
|
338
482
|
// vm = vesu multiply
|
|
339
|
-
id: `vm_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
483
|
+
id: `vm_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
484
|
+
debt.symbol
|
|
485
|
+
}`,
|
|
340
486
|
},
|
|
341
487
|
// Switch delegation off
|
|
342
488
|
{
|
|
343
489
|
target: vesuSingleton,
|
|
344
|
-
method:
|
|
345
|
-
packedArguments: isV2
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
490
|
+
method: "modify_delegation",
|
|
491
|
+
packedArguments: isV2
|
|
492
|
+
? [
|
|
493
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
494
|
+
]
|
|
495
|
+
: [
|
|
496
|
+
this.config.poolId.toBigInt(),
|
|
497
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
498
|
+
],
|
|
351
499
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
352
500
|
// sd2 = switch delegation off
|
|
353
|
-
id: `sd2_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
354
|
-
|
|
501
|
+
id: `sd2_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
502
|
+
debt.symbol
|
|
503
|
+
}`,
|
|
504
|
+
},
|
|
355
505
|
];
|
|
356
506
|
}
|
|
357
507
|
|
|
358
508
|
protected _getWithdrawLeaf(): {
|
|
359
|
-
target: ContractAddr
|
|
360
|
-
method: string
|
|
361
|
-
packedArguments: bigint[]
|
|
362
|
-
sanitizer: ContractAddr
|
|
363
|
-
id: string
|
|
509
|
+
target: ContractAddr;
|
|
510
|
+
method: string;
|
|
511
|
+
packedArguments: bigint[];
|
|
512
|
+
sanitizer: ContractAddr;
|
|
513
|
+
id: string;
|
|
364
514
|
}[] {
|
|
365
|
-
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
366
|
-
|
|
515
|
+
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
516
|
+
this.config.poolId
|
|
517
|
+
);
|
|
518
|
+
const vesuMultiply = isV2
|
|
519
|
+
? this.vesuAdapter.VESU_MULTIPLY
|
|
520
|
+
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
367
521
|
const collateral = this.config.collateral;
|
|
368
522
|
const debt = this.config.debt;
|
|
369
523
|
|
|
@@ -371,21 +525,25 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
371
525
|
// Switch delegation on
|
|
372
526
|
{
|
|
373
527
|
target: vesuSingleton,
|
|
374
|
-
method:
|
|
375
|
-
packedArguments: isV2
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
528
|
+
method: "modify_delegation",
|
|
529
|
+
packedArguments: isV2
|
|
530
|
+
? [
|
|
531
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
532
|
+
]
|
|
533
|
+
: [
|
|
534
|
+
this.config.poolId.toBigInt(),
|
|
535
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
536
|
+
],
|
|
381
537
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
382
538
|
// sdow = switch delegation on withdraw
|
|
383
|
-
id: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
539
|
+
id: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
540
|
+
debt.symbol
|
|
541
|
+
}`,
|
|
384
542
|
},
|
|
385
543
|
// Vesu multiply call
|
|
386
544
|
{
|
|
387
545
|
target: vesuMultiply,
|
|
388
|
-
method:
|
|
546
|
+
method: "modify_lever",
|
|
389
547
|
packedArguments: [
|
|
390
548
|
this.config.poolId.toBigInt(),
|
|
391
549
|
this.config.collateral.address.toBigInt(),
|
|
@@ -394,59 +552,85 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
394
552
|
],
|
|
395
553
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
396
554
|
// vmw = vesu multiply withdraw
|
|
397
|
-
id: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
555
|
+
id: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
556
|
+
debt.symbol
|
|
557
|
+
}`,
|
|
398
558
|
},
|
|
399
559
|
// Switch delegation off
|
|
400
560
|
{
|
|
401
561
|
target: vesuSingleton,
|
|
402
|
-
method:
|
|
403
|
-
packedArguments: isV2
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
562
|
+
method: "modify_delegation",
|
|
563
|
+
packedArguments: isV2
|
|
564
|
+
? [
|
|
565
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
566
|
+
]
|
|
567
|
+
: [
|
|
568
|
+
this.config.poolId.toBigInt(),
|
|
569
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
570
|
+
],
|
|
409
571
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
410
572
|
// sdofw = switch delegation off withdraw
|
|
411
|
-
id: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
412
|
-
|
|
573
|
+
id: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
574
|
+
debt.symbol
|
|
575
|
+
}`,
|
|
576
|
+
},
|
|
413
577
|
];
|
|
414
578
|
}
|
|
415
579
|
|
|
416
580
|
getDepositAdapter(): AdapterLeafType<DepositParams> {
|
|
417
581
|
const leafConfigs = this._getDepositLeaf();
|
|
418
|
-
const leaves = leafConfigs.map(config => {
|
|
582
|
+
const leaves = leafConfigs.map((config) => {
|
|
419
583
|
const { target, method, packedArguments, sanitizer, id } = config;
|
|
420
|
-
const leaf = this.constructSimpleLeafData(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
584
|
+
const leaf = this.constructSimpleLeafData(
|
|
585
|
+
{
|
|
586
|
+
id: id,
|
|
587
|
+
target,
|
|
588
|
+
method,
|
|
589
|
+
packedArguments,
|
|
590
|
+
},
|
|
591
|
+
sanitizer
|
|
592
|
+
);
|
|
426
593
|
return leaf;
|
|
427
594
|
});
|
|
428
|
-
return {
|
|
595
|
+
return {
|
|
596
|
+
leaves,
|
|
597
|
+
callConstructor: this.getDepositCall.bind(
|
|
598
|
+
this
|
|
599
|
+
) as unknown as GenerateCallFn<DepositParams>,
|
|
600
|
+
};
|
|
429
601
|
}
|
|
430
602
|
|
|
431
603
|
getWithdrawAdapter(): AdapterLeafType<WithdrawParams> {
|
|
432
604
|
const leafConfigs = this._getWithdrawLeaf();
|
|
433
|
-
const leaves = leafConfigs.map(config => {
|
|
605
|
+
const leaves = leafConfigs.map((config) => {
|
|
434
606
|
const { target, method, packedArguments, sanitizer, id } = config;
|
|
435
|
-
const leaf = this.constructSimpleLeafData(
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
607
|
+
const leaf = this.constructSimpleLeafData(
|
|
608
|
+
{
|
|
609
|
+
id: id,
|
|
610
|
+
target,
|
|
611
|
+
method,
|
|
612
|
+
packedArguments,
|
|
613
|
+
},
|
|
614
|
+
sanitizer
|
|
615
|
+
);
|
|
441
616
|
return leaf;
|
|
442
617
|
});
|
|
443
|
-
return {
|
|
618
|
+
return {
|
|
619
|
+
leaves,
|
|
620
|
+
callConstructor: this.getWithdrawCall.bind(
|
|
621
|
+
this
|
|
622
|
+
) as unknown as GenerateCallFn<WithdrawParams>,
|
|
623
|
+
};
|
|
444
624
|
}
|
|
445
625
|
|
|
446
626
|
async getDepositCall(params: DepositParams): Promise<ManageCall[]> {
|
|
447
627
|
const collateral = this.config.collateral;
|
|
448
|
-
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
449
|
-
|
|
628
|
+
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
629
|
+
this.config.poolId
|
|
630
|
+
);
|
|
631
|
+
const vesuMultiply = isV2
|
|
632
|
+
? this.vesuAdapter.VESU_MULTIPLY
|
|
633
|
+
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
450
634
|
|
|
451
635
|
const uint256MarginAmount = uint256.bnToUint256(params.amount.toWei());
|
|
452
636
|
|
|
@@ -456,61 +640,69 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
456
640
|
sanitizer: SIMPLE_SANITIZER,
|
|
457
641
|
call: {
|
|
458
642
|
contractAddress: collateral.address,
|
|
459
|
-
selector: hash.getSelectorFromName(
|
|
643
|
+
selector: hash.getSelectorFromName("approve"),
|
|
460
644
|
calldata: [
|
|
461
645
|
vesuMultiply.toBigInt(), // spender
|
|
462
646
|
toBigInt(uint256MarginAmount.low.toString()), // amount low
|
|
463
647
|
toBigInt(uint256MarginAmount.high.toString()), // amount high
|
|
464
|
-
]
|
|
465
|
-
}
|
|
648
|
+
],
|
|
649
|
+
},
|
|
466
650
|
},
|
|
467
651
|
// Switch delegation on
|
|
468
652
|
{
|
|
469
653
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
470
654
|
call: {
|
|
471
655
|
contractAddress: vesuSingleton,
|
|
472
|
-
selector: hash.getSelectorFromName(
|
|
473
|
-
calldata: isV2
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
656
|
+
selector: hash.getSelectorFromName("modify_delegation"),
|
|
657
|
+
calldata: isV2
|
|
658
|
+
? [
|
|
659
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
660
|
+
BigInt(1), // delegation: true
|
|
661
|
+
]
|
|
662
|
+
: [
|
|
663
|
+
this.config.poolId.toBigInt(),
|
|
664
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
665
|
+
BigInt(1), // delegation: true
|
|
666
|
+
],
|
|
667
|
+
},
|
|
482
668
|
},
|
|
483
669
|
// Vesu multiply call
|
|
484
670
|
{
|
|
485
671
|
sanitizer: SIMPLE_SANITIZER_V2,
|
|
486
672
|
call: {
|
|
487
673
|
contractAddress: vesuMultiply,
|
|
488
|
-
selector: hash.getSelectorFromName(
|
|
489
|
-
calldata: await this.getMultiplyCallCalldata(params, true)
|
|
490
|
-
}
|
|
674
|
+
selector: hash.getSelectorFromName("modify_lever"),
|
|
675
|
+
calldata: await this.getMultiplyCallCalldata(params, true),
|
|
676
|
+
},
|
|
491
677
|
},
|
|
492
678
|
// Switch delegation off
|
|
493
679
|
{
|
|
494
680
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
495
681
|
call: {
|
|
496
682
|
contractAddress: vesuSingleton,
|
|
497
|
-
selector: hash.getSelectorFromName(
|
|
498
|
-
calldata: isV2
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
683
|
+
selector: hash.getSelectorFromName("modify_delegation"),
|
|
684
|
+
calldata: isV2
|
|
685
|
+
? [
|
|
686
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
687
|
+
BigInt(0), // delegation: false
|
|
688
|
+
]
|
|
689
|
+
: [
|
|
690
|
+
this.config.poolId.toBigInt(),
|
|
691
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
692
|
+
BigInt(0), // delegation: false
|
|
693
|
+
],
|
|
694
|
+
},
|
|
695
|
+
},
|
|
508
696
|
];
|
|
509
697
|
}
|
|
510
698
|
|
|
511
699
|
async getWithdrawCall(params: WithdrawParams): Promise<ManageCall[]> {
|
|
512
|
-
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
513
|
-
|
|
700
|
+
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
701
|
+
this.config.poolId
|
|
702
|
+
);
|
|
703
|
+
const vesuMultiply = isV2
|
|
704
|
+
? this.vesuAdapter.VESU_MULTIPLY
|
|
705
|
+
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
514
706
|
|
|
515
707
|
return [
|
|
516
708
|
// Switch delegation on
|
|
@@ -518,118 +710,191 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
518
710
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
519
711
|
call: {
|
|
520
712
|
contractAddress: vesuSingleton,
|
|
521
|
-
selector: hash.getSelectorFromName(
|
|
522
|
-
calldata: isV2
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
713
|
+
selector: hash.getSelectorFromName("modify_delegation"),
|
|
714
|
+
calldata: isV2
|
|
715
|
+
? [
|
|
716
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
717
|
+
BigInt(1), // delegation: true
|
|
718
|
+
]
|
|
719
|
+
: [
|
|
720
|
+
this.config.poolId.toBigInt(),
|
|
721
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
722
|
+
BigInt(1), // delegation: true
|
|
723
|
+
],
|
|
724
|
+
},
|
|
531
725
|
},
|
|
532
726
|
// Vesu multiply call
|
|
533
727
|
{
|
|
534
728
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
535
729
|
call: {
|
|
536
730
|
contractAddress: vesuMultiply,
|
|
537
|
-
selector: hash.getSelectorFromName(
|
|
538
|
-
calldata: await this.getWithdrawalCalldata(params)
|
|
539
|
-
}
|
|
731
|
+
selector: hash.getSelectorFromName("modify_lever"),
|
|
732
|
+
calldata: await this.getWithdrawalCalldata(params),
|
|
733
|
+
},
|
|
540
734
|
},
|
|
541
735
|
// Switch delegation off
|
|
542
736
|
{
|
|
543
737
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
544
738
|
call: {
|
|
545
739
|
contractAddress: vesuSingleton,
|
|
546
|
-
selector: hash.getSelectorFromName(
|
|
547
|
-
calldata: isV2
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
740
|
+
selector: hash.getSelectorFromName("modify_delegation"),
|
|
741
|
+
calldata: isV2
|
|
742
|
+
? [
|
|
743
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
744
|
+
BigInt(0), // delegation: false
|
|
745
|
+
]
|
|
746
|
+
: [
|
|
747
|
+
this.config.poolId.toBigInt(),
|
|
748
|
+
vesuMultiply.toBigInt(), // delegatee
|
|
749
|
+
BigInt(0), // delegation: false
|
|
750
|
+
],
|
|
751
|
+
},
|
|
752
|
+
},
|
|
557
753
|
];
|
|
558
754
|
}
|
|
559
755
|
|
|
560
|
-
private async getMultiplyCallCalldata(
|
|
561
|
-
|
|
756
|
+
private async getMultiplyCallCalldata(
|
|
757
|
+
params: DepositParams | WithdrawParams,
|
|
758
|
+
isDeposit: boolean
|
|
759
|
+
): Promise<bigint[]> {
|
|
760
|
+
logger.verbose(
|
|
761
|
+
`${
|
|
762
|
+
VesuMultiplyAdapter.name
|
|
763
|
+
}::getMultiplyCallCalldata params: ${JSON.stringify(
|
|
764
|
+
params
|
|
765
|
+
)}, isDeposit: ${isDeposit}, collateral: ${
|
|
766
|
+
this.config.collateral.symbol
|
|
767
|
+
}, debt: ${this.config.debt.symbol}`
|
|
768
|
+
);
|
|
562
769
|
const { isV2 } = getVesuSingletonAddress(this.config.poolId);
|
|
563
|
-
const vesuMultiply = isV2
|
|
564
|
-
|
|
770
|
+
const vesuMultiply = isV2
|
|
771
|
+
? this.vesuAdapter.VESU_MULTIPLY
|
|
772
|
+
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
773
|
+
|
|
565
774
|
// Create a temporary contract instance to populate the call
|
|
566
775
|
const multiplyContract = new Contract({
|
|
567
|
-
abi: VesuMultiplyAbi,
|
|
568
|
-
address: vesuMultiply.address,
|
|
569
|
-
providerOrAccount: this.config.networkConfig.provider
|
|
776
|
+
abi: VesuMultiplyAbi,
|
|
777
|
+
address: vesuMultiply.address,
|
|
778
|
+
providerOrAccount: this.config.networkConfig.provider,
|
|
570
779
|
});
|
|
571
780
|
|
|
572
781
|
// Configure swaps based on the operation
|
|
573
782
|
let leverSwap: Swap[] = [];
|
|
574
783
|
let leverSwapLimitAmount = Web3Number.fromWei(0, this.config.debt.decimals);
|
|
575
784
|
|
|
576
|
-
const existingPositions = await this.vesuAdapter.getPositions(
|
|
577
|
-
|
|
785
|
+
const existingPositions = await this.vesuAdapter.getPositions(
|
|
786
|
+
this.config.networkConfig
|
|
787
|
+
);
|
|
788
|
+
const collateralisation = await this.vesuAdapter.getCollateralization(
|
|
789
|
+
this.config.networkConfig
|
|
790
|
+
);
|
|
578
791
|
const existingCollateralInfo = existingPositions[0];
|
|
579
792
|
const existingDebtInfo = existingPositions[1];
|
|
580
793
|
const isDexPriceRequired = existingDebtInfo.token.symbol !== "USDC";
|
|
581
|
-
logger.debug(`${
|
|
582
|
-
|
|
794
|
+
logger.debug(`${
|
|
795
|
+
VesuMultiplyAdapter.name
|
|
796
|
+
}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(
|
|
797
|
+
existingCollateralInfo
|
|
798
|
+
)},
|
|
799
|
+
existingDebtInfo: ${JSON.stringify(
|
|
800
|
+
existingDebtInfo
|
|
801
|
+
)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
583
802
|
|
|
584
803
|
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
585
|
-
// Price 1 is ok as fallback bcz that would relatively price the
|
|
804
|
+
// Price 1 is ok as fallback bcz that would relatively price the
|
|
586
805
|
// collateral and debt as equal.
|
|
587
|
-
const collateralPrice =
|
|
588
|
-
collateralisation[0].usdValue
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
806
|
+
const collateralPrice =
|
|
807
|
+
collateralisation[0].usdValue > 0
|
|
808
|
+
? collateralisation[0].usdValue /
|
|
809
|
+
existingCollateralInfo.amount.toNumber()
|
|
810
|
+
: (await this.config.pricer.getPrice(this.config.collateral.symbol))
|
|
811
|
+
.price;
|
|
812
|
+
const debtPrice =
|
|
813
|
+
collateralisation[1].usdValue > 0
|
|
814
|
+
? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
|
|
815
|
+
: (await this.config.pricer.getPrice(this.config.debt.symbol)).price;
|
|
816
|
+
logger.debug(
|
|
817
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`
|
|
818
|
+
);
|
|
819
|
+
|
|
820
|
+
const legLTV = await this.vesuAdapter.getLTVConfig(
|
|
821
|
+
this.config.networkConfig
|
|
822
|
+
);
|
|
823
|
+
const ekuboQuoter = new EkuboQuoter(
|
|
824
|
+
this.config.networkConfig,
|
|
825
|
+
this.config.pricer
|
|
826
|
+
);
|
|
827
|
+
const dexPrice = isDexPriceRequired
|
|
828
|
+
? await ekuboQuoter.getDexPrice(
|
|
829
|
+
this.config.collateral,
|
|
830
|
+
this.config.debt,
|
|
831
|
+
this.config.quoteAmountToFetchPrice
|
|
832
|
+
)
|
|
833
|
+
: 1;
|
|
834
|
+
logger.verbose(
|
|
835
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall dexPrice: ${dexPrice}, ltv: ${legLTV}`
|
|
836
|
+
);
|
|
599
837
|
|
|
600
838
|
// compute optimal amount of collateral and debt post addition/removal
|
|
601
839
|
// target hf = collateral * collateralPrice * ltv / debt * debtPrice
|
|
602
|
-
// assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
840
|
+
// assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
603
841
|
// target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
604
842
|
// => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
605
843
|
// => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
606
844
|
// => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
607
845
|
|
|
608
|
-
const addedCollateral = params.amount
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
logger.verbose(
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
846
|
+
const addedCollateral = params.amount.multipliedBy(isDeposit ? 1 : -1);
|
|
847
|
+
logger.verbose(
|
|
848
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall addedCollateral: ${addedCollateral}`
|
|
849
|
+
);
|
|
850
|
+
const numeratorPart1 = existingCollateralInfo.amount
|
|
851
|
+
.plus(addedCollateral)
|
|
852
|
+
.multipliedBy(collateralPrice)
|
|
853
|
+
.multipliedBy(legLTV);
|
|
854
|
+
logger.verbose(
|
|
855
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}`
|
|
856
|
+
);
|
|
857
|
+
const numeratorPart2 = existingDebtInfo.amount
|
|
858
|
+
.multipliedBy(debtPrice)
|
|
859
|
+
.multipliedBy(this.config.targetHealthFactor);
|
|
860
|
+
logger.verbose(
|
|
861
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall numeratorPart2: ${numeratorPart2}`
|
|
862
|
+
);
|
|
863
|
+
const denominatorPart = this.config.targetHealthFactor - legLTV / dexPrice; // TODO Write reason for this. this dexPrice is some custom thing. this dexPrice is probably exchange rate (1 xWBTC in WBTC terms)
|
|
864
|
+
logger.verbose(
|
|
865
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall denominatorPart: ${denominatorPart}`
|
|
866
|
+
);
|
|
867
|
+
const x_debt_usd = numeratorPart1
|
|
868
|
+
.minus(numeratorPart2)
|
|
869
|
+
.dividedBy(denominatorPart);
|
|
870
|
+
logger.verbose(
|
|
871
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall x_debt_usd: ${x_debt_usd}`
|
|
872
|
+
);
|
|
873
|
+
logger.debug(
|
|
874
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall numeratorPart1: ${numeratorPart1}, numeratorPart2: ${numeratorPart2}, denominatorPart: ${denominatorPart}`
|
|
875
|
+
);
|
|
620
876
|
|
|
621
877
|
// both in underlying
|
|
622
878
|
// debtAmount in debt units
|
|
623
|
-
let debtAmount = new Web3Number(
|
|
879
|
+
let debtAmount = new Web3Number(
|
|
880
|
+
x_debt_usd.dividedBy(debtPrice).toFixed(this.config.debt.decimals),
|
|
881
|
+
this.config.debt.decimals
|
|
882
|
+
);
|
|
624
883
|
const marginAmount = addedCollateral;
|
|
625
884
|
const collateralToken = this.config.collateral;
|
|
626
885
|
const debtToken = this.config.debt;
|
|
627
|
-
const debtAmountInCollateralUnits = new Web3Number(
|
|
628
|
-
|
|
886
|
+
const debtAmountInCollateralUnits = new Web3Number(
|
|
887
|
+
debtAmount
|
|
888
|
+
.multipliedBy(debtPrice)
|
|
889
|
+
.dividedBy(collateralPrice)
|
|
890
|
+
.multipliedBy(10 ** collateralToken.decimals)
|
|
891
|
+
.toFixed(0),
|
|
892
|
+
collateralToken.decimals
|
|
893
|
+
);
|
|
894
|
+
|
|
629
895
|
// increase multiply lever or not
|
|
630
896
|
const isIncrease = debtAmount.greaterThanOrEqualTo(0);
|
|
631
897
|
|
|
632
|
-
|
|
633
898
|
// due to directional limitations in multiply contract
|
|
634
899
|
if (isIncrease && debtAmount.lessThan(0)) {
|
|
635
900
|
// we are increasing lever but math says reduce debt
|
|
@@ -640,23 +905,37 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
640
905
|
// - so just set debt 0
|
|
641
906
|
debtAmount = Web3Number.fromWei(0, this.config.debt.decimals);
|
|
642
907
|
}
|
|
643
|
-
logger.verbose(
|
|
908
|
+
logger.verbose(
|
|
909
|
+
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`
|
|
910
|
+
);
|
|
644
911
|
if (!debtAmount.isZero()) {
|
|
645
912
|
// Get swap quote for leverage operation
|
|
646
913
|
// Determine swap direction based on operation type
|
|
647
|
-
|
|
648
|
-
|
|
914
|
+
|
|
915
|
+
try {
|
|
649
916
|
const swapQuote = await ekuboQuoter.getQuote(
|
|
650
917
|
collateralToken.address.address,
|
|
651
918
|
debtToken.address.address,
|
|
652
|
-
debtAmountInCollateralUnits.multipliedBy(-1)// negative for exact amount out
|
|
919
|
+
debtAmountInCollateralUnits.multipliedBy(-1) // negative for exact amount out
|
|
653
920
|
);
|
|
654
|
-
|
|
921
|
+
|
|
655
922
|
// todo add better slip checks
|
|
656
923
|
// Check price impact
|
|
657
|
-
if (swapQuote.price_impact < 0.01) {
|
|
924
|
+
if (swapQuote.price_impact < 0.01) {
|
|
925
|
+
// 1% max price impact
|
|
658
926
|
// from and toToken param position reversed, to fetch the required quote and keep things generalised
|
|
659
|
-
|
|
927
|
+
|
|
928
|
+
leverSwap = debtAmount.isNegative()
|
|
929
|
+
? ekuboQuoter.getVesuMultiplyQuote(
|
|
930
|
+
swapQuote,
|
|
931
|
+
collateralToken,
|
|
932
|
+
debtToken
|
|
933
|
+
)
|
|
934
|
+
: ekuboQuoter.getVesuMultiplyQuote(
|
|
935
|
+
swapQuote,
|
|
936
|
+
debtToken,
|
|
937
|
+
collateralToken
|
|
938
|
+
);
|
|
660
939
|
//console.log("leverSwap", leverSwap[-1].token_amount);
|
|
661
940
|
//console.log(JSON.stringify(leverSwap));
|
|
662
941
|
// Calculate limit amount with slippage protection
|
|
@@ -672,215 +951,309 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
672
951
|
// For decrease: maximum amount of collateral used
|
|
673
952
|
// from collateral token to debt token
|
|
674
953
|
//leverSwapLimitAmount = await ekuboQuoter.getSwapLimitAmount(collateralToken, debtToken, debtAmountInCollateralUnits.multipliedBy(-1), MAX_SLIPPAGE);
|
|
675
|
-
leverSwapLimitAmount = debtAmount
|
|
954
|
+
leverSwapLimitAmount = debtAmount
|
|
955
|
+
.abs()
|
|
956
|
+
.multipliedBy(1 - MAX_SLIPPAGE);
|
|
676
957
|
//console.log("anotherleverSwapLimitAmount", anotherleverSwapLimitAmount, leverSwapLimitAmount);
|
|
677
958
|
} else {
|
|
678
|
-
leverSwapLimitAmount = Web3Number.fromWei(
|
|
959
|
+
leverSwapLimitAmount = Web3Number.fromWei(
|
|
960
|
+
0,
|
|
961
|
+
this.config.debt.decimals
|
|
962
|
+
);
|
|
679
963
|
}
|
|
680
964
|
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
681
965
|
//console.log("leverSwapLimitAmount", leverSwapLimitAmount);
|
|
682
966
|
} else {
|
|
683
|
-
throw new Error(
|
|
967
|
+
throw new Error(
|
|
968
|
+
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
|
|
969
|
+
);
|
|
684
970
|
}
|
|
685
971
|
} catch (error) {
|
|
686
|
-
throw new Error(
|
|
972
|
+
throw new Error(
|
|
973
|
+
`VesuMultiplyAdapter: Failed to get swap quote: ${error}`
|
|
974
|
+
);
|
|
687
975
|
}
|
|
688
976
|
}
|
|
689
|
-
|
|
690
|
-
const multiplyParams = await this.getLeverParams(
|
|
691
|
-
|
|
692
|
-
|
|
977
|
+
|
|
978
|
+
const multiplyParams = await this.getLeverParams(
|
|
979
|
+
isIncrease,
|
|
980
|
+
params,
|
|
981
|
+
leverSwap,
|
|
982
|
+
leverSwapLimitAmount
|
|
983
|
+
);
|
|
984
|
+
const call = multiplyContract.populate("modify_lever", {
|
|
985
|
+
modify_lever_params: this.formatMultiplyParams(
|
|
986
|
+
isIncrease,
|
|
987
|
+
multiplyParams
|
|
988
|
+
),
|
|
693
989
|
});
|
|
694
990
|
|
|
695
991
|
return call.calldata as bigint[];
|
|
696
992
|
}
|
|
697
993
|
|
|
698
|
-
private async getLeverParams(
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
994
|
+
private async getLeverParams(
|
|
995
|
+
isIncrease: boolean,
|
|
996
|
+
params: DepositParams | WithdrawParams,
|
|
997
|
+
leverSwap: Swap[],
|
|
998
|
+
leverSwapLimitAmount: Web3Number
|
|
999
|
+
): Promise<IncreaseLeverParams | DecreaseLeverParams> {
|
|
1000
|
+
const multiplyParams: IncreaseLeverParams | DecreaseLeverParams = isIncrease
|
|
1001
|
+
? {
|
|
1002
|
+
user: this.config.vaultAllocator,
|
|
1003
|
+
pool_id: this.config.poolId,
|
|
1004
|
+
collateral_asset: this.config.collateral.address,
|
|
1005
|
+
debt_asset: this.config.debt.address,
|
|
1006
|
+
recipient: this.config.vaultAllocator,
|
|
1007
|
+
add_margin: params.amount, // multiplied by collateral decimals in format
|
|
1008
|
+
margin_swap: [],
|
|
1009
|
+
margin_swap_limit_amount: Web3Number.fromWei(
|
|
1010
|
+
0,
|
|
1011
|
+
this.config.collateral.decimals
|
|
1012
|
+
),
|
|
1013
|
+
lever_swap: leverSwap,
|
|
1014
|
+
lever_swap_limit_amount: leverSwapLimitAmount,
|
|
1015
|
+
}
|
|
1016
|
+
: {
|
|
1017
|
+
user: this.config.vaultAllocator,
|
|
1018
|
+
pool_id: this.config.poolId,
|
|
1019
|
+
collateral_asset: this.config.collateral.address,
|
|
1020
|
+
debt_asset: this.config.debt.address,
|
|
1021
|
+
recipient: this.config.vaultAllocator,
|
|
1022
|
+
sub_margin: params.amount,
|
|
1023
|
+
lever_swap: leverSwap,
|
|
1024
|
+
lever_swap_limit_amount: leverSwapLimitAmount,
|
|
1025
|
+
lever_swap_weights: [],
|
|
1026
|
+
withdraw_swap: [],
|
|
1027
|
+
withdraw_swap_limit_amount: Web3Number.fromWei(
|
|
1028
|
+
0,
|
|
1029
|
+
this.config.collateral.decimals
|
|
1030
|
+
),
|
|
1031
|
+
withdraw_swap_weights: [],
|
|
1032
|
+
close_position: false,
|
|
1033
|
+
};
|
|
725
1034
|
return multiplyParams;
|
|
726
1035
|
}
|
|
727
1036
|
|
|
728
|
-
private async getWithdrawalCalldata(
|
|
1037
|
+
private async getWithdrawalCalldata(
|
|
1038
|
+
params: WithdrawParams
|
|
1039
|
+
): Promise<bigint[]> {
|
|
729
1040
|
//params.amount must be in btc here
|
|
730
1041
|
const { isV2 } = getVesuSingletonAddress(this.config.poolId);
|
|
731
|
-
const vesuMultiply = isV2
|
|
1042
|
+
const vesuMultiply = isV2
|
|
1043
|
+
? this.vesuAdapter.VESU_MULTIPLY
|
|
1044
|
+
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
732
1045
|
const multiplyContract = new Contract({
|
|
733
|
-
abi: VesuMultiplyAbi,
|
|
734
|
-
address: vesuMultiply.address,
|
|
735
|
-
providerOrAccount: this.config.networkConfig.provider
|
|
1046
|
+
abi: VesuMultiplyAbi,
|
|
1047
|
+
address: vesuMultiply.address,
|
|
1048
|
+
providerOrAccount: this.config.networkConfig.provider,
|
|
736
1049
|
});
|
|
737
1050
|
let leverSwap: Swap[] = [];
|
|
738
1051
|
let leverSwapLimitAmount = Web3Number.fromWei(0, this.config.debt.decimals);
|
|
739
|
-
const existingPositions = await this.vesuAdapter.getPositions(
|
|
1052
|
+
const existingPositions = await this.vesuAdapter.getPositions(
|
|
1053
|
+
this.config.networkConfig
|
|
1054
|
+
);
|
|
740
1055
|
const existingCollateralInfo = existingPositions[0];
|
|
741
1056
|
const existingDebtInfo = existingPositions[1];
|
|
742
1057
|
const collateralToken = this.config.collateral;
|
|
743
1058
|
const debtToken = this.config.debt;
|
|
744
|
-
const collateralPrice = await this.config.pricer.getPrice(
|
|
1059
|
+
const collateralPrice = await this.config.pricer.getPrice(
|
|
1060
|
+
collateralToken.symbol
|
|
1061
|
+
);
|
|
745
1062
|
const debtPrice = await this.config.pricer.getPrice(debtToken.symbol);
|
|
746
1063
|
// the debt amount is negative as we are reducing debt to withdraw
|
|
747
1064
|
const { deltadebtAmountUnits: debtAmountToRepay } =
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1065
|
+
calculateDebtReductionAmountForWithdrawal(
|
|
1066
|
+
existingDebtInfo.amount,
|
|
1067
|
+
existingCollateralInfo.amount,
|
|
1068
|
+
MAX_LIQUIDATION_RATIO,
|
|
1069
|
+
params.amount,
|
|
1070
|
+
collateralPrice.price,
|
|
1071
|
+
debtPrice.price,
|
|
1072
|
+
debtToken.decimals
|
|
1073
|
+
);
|
|
757
1074
|
//console.log("debtAmountToRepay", debtAmountToRepay);
|
|
758
|
-
if(!debtAmountToRepay) {
|
|
1075
|
+
if (!debtAmountToRepay) {
|
|
759
1076
|
throw new Error("error calculating debt amount to repay");
|
|
760
1077
|
}
|
|
761
|
-
const ekuboQuoter = new EkuboQuoter(
|
|
762
|
-
|
|
1078
|
+
const ekuboQuoter = new EkuboQuoter(
|
|
1079
|
+
this.config.networkConfig,
|
|
1080
|
+
this.config.pricer
|
|
1081
|
+
);
|
|
1082
|
+
const debtInDebtUnits = new Web3Number(
|
|
1083
|
+
debtAmountToRepay,
|
|
1084
|
+
debtToken.decimals
|
|
1085
|
+
)
|
|
1086
|
+
.dividedBy(debtPrice.price)
|
|
1087
|
+
.multipliedBy(10 ** debtToken.decimals);
|
|
763
1088
|
const swapQuote = await ekuboQuoter.getQuote(
|
|
764
1089
|
debtToken.address.address,
|
|
765
1090
|
collateralToken.address.address,
|
|
766
1091
|
debtInDebtUnits
|
|
767
|
-
)
|
|
1092
|
+
);
|
|
768
1093
|
const MAX_SLIPPAGE = 0.002; // 0.2% slippag
|
|
769
|
-
if(swapQuote.price_impact < 0.0025) {
|
|
770
|
-
leverSwap = ekuboQuoter.getVesuMultiplyQuote(
|
|
1094
|
+
if (swapQuote.price_impact < 0.0025) {
|
|
1095
|
+
leverSwap = ekuboQuoter.getVesuMultiplyQuote(
|
|
1096
|
+
swapQuote,
|
|
1097
|
+
collateralToken,
|
|
1098
|
+
debtToken
|
|
1099
|
+
);
|
|
771
1100
|
} else {
|
|
772
|
-
logger.error(
|
|
1101
|
+
logger.error(
|
|
1102
|
+
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
|
|
1103
|
+
);
|
|
773
1104
|
}
|
|
774
1105
|
|
|
775
|
-
leverSwapLimitAmount = new Web3Number(debtAmountToRepay, debtToken.decimals)
|
|
1106
|
+
leverSwapLimitAmount = new Web3Number(debtAmountToRepay, debtToken.decimals)
|
|
1107
|
+
.abs()
|
|
1108
|
+
.multipliedBy(1 + MAX_SLIPPAGE);
|
|
776
1109
|
//leverSwapLimitAmount = await ekuboQuoter.getSwapLimitAmount(debtToken, collateralToken, debtInCollateralUnits, MAX_SLIPPAGE);
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
1110
|
+
const multiplyParams = await this.getLeverParams(
|
|
1111
|
+
false,
|
|
1112
|
+
params,
|
|
1113
|
+
leverSwap,
|
|
1114
|
+
leverSwapLimitAmount
|
|
1115
|
+
);
|
|
1116
|
+
const call = multiplyContract.populate("modify_lever", {
|
|
1117
|
+
modify_lever_params: this.formatMultiplyParams(false, multiplyParams),
|
|
780
1118
|
});
|
|
781
1119
|
return call.calldata as bigint[];
|
|
782
1120
|
}
|
|
783
1121
|
|
|
784
|
-
formatMultiplyParams(
|
|
1122
|
+
formatMultiplyParams(
|
|
1123
|
+
isIncrease: boolean,
|
|
1124
|
+
params: IncreaseLeverParams | DecreaseLeverParams
|
|
1125
|
+
) {
|
|
785
1126
|
if (isIncrease) {
|
|
786
1127
|
const _params = params as IncreaseLeverParams;
|
|
787
1128
|
return {
|
|
788
|
-
action: new CairoCustomEnum({
|
|
1129
|
+
action: new CairoCustomEnum({
|
|
1130
|
+
IncreaseLever: {
|
|
1131
|
+
pool_id: _params.pool_id.toBigInt(),
|
|
1132
|
+
collateral_asset: _params.collateral_asset.toBigInt(),
|
|
1133
|
+
debt_asset: _params.debt_asset.toBigInt(),
|
|
1134
|
+
user: _params.user.toBigInt(),
|
|
1135
|
+
add_margin: BigInt(_params.add_margin.toWei()),
|
|
1136
|
+
margin_swap: _params.margin_swap.map((swap) => ({
|
|
1137
|
+
route: swap.route.map((route) => ({
|
|
1138
|
+
pool_key: {
|
|
1139
|
+
token0: route.pool_key.token0.toBigInt(),
|
|
1140
|
+
token1: route.pool_key.token1.toBigInt(),
|
|
1141
|
+
fee: route.pool_key.fee,
|
|
1142
|
+
tick_spacing: route.pool_key.tick_spacing,
|
|
1143
|
+
extension: BigInt(
|
|
1144
|
+
num.hexToDecimalString(route.pool_key.extension)
|
|
1145
|
+
),
|
|
1146
|
+
},
|
|
1147
|
+
sqrt_ratio_limit: uint256.bnToUint256(
|
|
1148
|
+
route.sqrt_ratio_limit.toWei()
|
|
1149
|
+
),
|
|
1150
|
+
skip_ahead: BigInt(100),
|
|
1151
|
+
})),
|
|
1152
|
+
token_amount: {
|
|
1153
|
+
token: swap.token_amount.token.toBigInt(),
|
|
1154
|
+
amount: swap.token_amount.amount.toI129(),
|
|
1155
|
+
},
|
|
1156
|
+
})),
|
|
1157
|
+
margin_swap_limit_amount: BigInt(
|
|
1158
|
+
_params.margin_swap_limit_amount.toWei()
|
|
1159
|
+
),
|
|
1160
|
+
lever_swap: _params.lever_swap.map((swap) => ({
|
|
1161
|
+
route: swap.route.map((route) => ({
|
|
1162
|
+
pool_key: {
|
|
1163
|
+
token0: route.pool_key.token0.toBigInt(),
|
|
1164
|
+
token1: route.pool_key.token1.toBigInt(),
|
|
1165
|
+
fee: route.pool_key.fee,
|
|
1166
|
+
tick_spacing: route.pool_key.tick_spacing,
|
|
1167
|
+
extension: BigInt(
|
|
1168
|
+
num.hexToDecimalString(route.pool_key.extension)
|
|
1169
|
+
),
|
|
1170
|
+
},
|
|
1171
|
+
sqrt_ratio_limit: uint256.bnToUint256(
|
|
1172
|
+
route.sqrt_ratio_limit.toWei()
|
|
1173
|
+
),
|
|
1174
|
+
skip_ahead: BigInt(0),
|
|
1175
|
+
})),
|
|
1176
|
+
token_amount: {
|
|
1177
|
+
token: swap.token_amount.token.toBigInt(),
|
|
1178
|
+
amount: swap.token_amount.amount.toI129(),
|
|
1179
|
+
},
|
|
1180
|
+
})),
|
|
1181
|
+
lever_swap_limit_amount: BigInt(
|
|
1182
|
+
_params.lever_swap_limit_amount.toWei()
|
|
1183
|
+
),
|
|
1184
|
+
},
|
|
1185
|
+
}),
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
const _params = params as DecreaseLeverParams;
|
|
1190
|
+
return {
|
|
1191
|
+
action: new CairoCustomEnum({
|
|
1192
|
+
DecreaseLever: {
|
|
789
1193
|
pool_id: _params.pool_id.toBigInt(),
|
|
790
1194
|
collateral_asset: _params.collateral_asset.toBigInt(),
|
|
791
1195
|
debt_asset: _params.debt_asset.toBigInt(),
|
|
792
1196
|
user: _params.user.toBigInt(),
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1197
|
+
sub_margin: BigInt(_params.sub_margin.toWei()),
|
|
1198
|
+
recipient: _params.recipient.toBigInt(),
|
|
1199
|
+
lever_swap: _params.lever_swap.map((swap) => ({
|
|
1200
|
+
route: swap.route.map((route) => ({
|
|
796
1201
|
pool_key: {
|
|
797
1202
|
token0: route.pool_key.token0.toBigInt(),
|
|
798
1203
|
token1: route.pool_key.token1.toBigInt(),
|
|
799
1204
|
fee: route.pool_key.fee,
|
|
800
1205
|
tick_spacing: route.pool_key.tick_spacing,
|
|
801
|
-
extension:
|
|
1206
|
+
extension: ContractAddr.from(
|
|
1207
|
+
route.pool_key.extension
|
|
1208
|
+
).toBigInt(),
|
|
802
1209
|
},
|
|
803
|
-
sqrt_ratio_limit: uint256.bnToUint256(
|
|
804
|
-
|
|
1210
|
+
sqrt_ratio_limit: uint256.bnToUint256(
|
|
1211
|
+
route.sqrt_ratio_limit.toWei()
|
|
1212
|
+
),
|
|
1213
|
+
skip_ahead: BigInt(route.skip_ahead.toWei()),
|
|
805
1214
|
})),
|
|
806
1215
|
token_amount: {
|
|
807
1216
|
token: swap.token_amount.token.toBigInt(),
|
|
808
|
-
amount: swap.token_amount.amount.toI129()
|
|
809
|
-
}
|
|
1217
|
+
amount: swap.token_amount.amount.toI129(),
|
|
1218
|
+
},
|
|
810
1219
|
})),
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
1220
|
+
lever_swap_limit_amount: BigInt(
|
|
1221
|
+
_params.lever_swap_limit_amount.toWei()
|
|
1222
|
+
),
|
|
1223
|
+
lever_swap_weights: _params.lever_swap_weights.map((weight) =>
|
|
1224
|
+
BigInt(weight.toWei())
|
|
1225
|
+
),
|
|
1226
|
+
withdraw_swap: _params.withdraw_swap.map((swap) => ({
|
|
1227
|
+
route: swap.route.map((route) => ({
|
|
814
1228
|
pool_key: {
|
|
815
1229
|
token0: route.pool_key.token0.toBigInt(),
|
|
816
1230
|
token1: route.pool_key.token1.toBigInt(),
|
|
817
1231
|
fee: route.pool_key.fee,
|
|
818
1232
|
tick_spacing: route.pool_key.tick_spacing,
|
|
819
|
-
extension:
|
|
1233
|
+
extension: ContractAddr.from(
|
|
1234
|
+
route.pool_key.extension
|
|
1235
|
+
).toBigInt(),
|
|
820
1236
|
},
|
|
821
|
-
sqrt_ratio_limit: uint256.bnToUint256(
|
|
822
|
-
|
|
1237
|
+
sqrt_ratio_limit: uint256.bnToUint256(
|
|
1238
|
+
route.sqrt_ratio_limit.toWei()
|
|
1239
|
+
),
|
|
1240
|
+
skip_ahead: BigInt(route.skip_ahead.toWei()),
|
|
823
1241
|
})),
|
|
824
1242
|
token_amount: {
|
|
825
1243
|
token: swap.token_amount.token.toBigInt(),
|
|
826
|
-
amount: swap.token_amount.amount.toI129()
|
|
827
|
-
}
|
|
828
|
-
})),
|
|
829
|
-
lever_swap_limit_amount: BigInt(_params.lever_swap_limit_amount.toWei()),
|
|
830
|
-
} }),
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
const _params = params as DecreaseLeverParams;
|
|
835
|
-
return {
|
|
836
|
-
action: new CairoCustomEnum({ DecreaseLever: {
|
|
837
|
-
pool_id: _params.pool_id.toBigInt(),
|
|
838
|
-
collateral_asset: _params.collateral_asset.toBigInt(),
|
|
839
|
-
debt_asset: _params.debt_asset.toBigInt(),
|
|
840
|
-
user: _params.user.toBigInt(),
|
|
841
|
-
sub_margin: BigInt(_params.sub_margin.toWei()),
|
|
842
|
-
recipient: _params.recipient.toBigInt(),
|
|
843
|
-
lever_swap: _params.lever_swap.map(swap => ({
|
|
844
|
-
route: swap.route.map(route => ({
|
|
845
|
-
pool_key: {
|
|
846
|
-
token0: route.pool_key.token0.toBigInt(),
|
|
847
|
-
token1: route.pool_key.token1.toBigInt(),
|
|
848
|
-
fee: route.pool_key.fee,
|
|
849
|
-
tick_spacing: route.pool_key.tick_spacing,
|
|
850
|
-
extension: ContractAddr.from(route.pool_key.extension).toBigInt(),
|
|
851
|
-
},
|
|
852
|
-
sqrt_ratio_limit: uint256.bnToUint256(route.sqrt_ratio_limit.toWei()),
|
|
853
|
-
skip_ahead: BigInt(route.skip_ahead.toWei())
|
|
854
|
-
})),
|
|
855
|
-
token_amount: {
|
|
856
|
-
token: swap.token_amount.token.toBigInt(),
|
|
857
|
-
amount: swap.token_amount.amount.toI129()
|
|
858
|
-
}
|
|
859
|
-
})),
|
|
860
|
-
lever_swap_limit_amount: BigInt(_params.lever_swap_limit_amount.toWei()),
|
|
861
|
-
lever_swap_weights: _params.lever_swap_weights.map(weight => BigInt(weight.toWei())),
|
|
862
|
-
withdraw_swap: _params.withdraw_swap.map(swap => ({
|
|
863
|
-
route: swap.route.map(route => ({
|
|
864
|
-
pool_key: {
|
|
865
|
-
token0: route.pool_key.token0.toBigInt(),
|
|
866
|
-
token1: route.pool_key.token1.toBigInt(),
|
|
867
|
-
fee: route.pool_key.fee,
|
|
868
|
-
tick_spacing: route.pool_key.tick_spacing,
|
|
869
|
-
extension: ContractAddr.from(route.pool_key.extension).toBigInt(),
|
|
1244
|
+
amount: swap.token_amount.amount.toI129(),
|
|
870
1245
|
},
|
|
871
|
-
sqrt_ratio_limit: uint256.bnToUint256(route.sqrt_ratio_limit.toWei()),
|
|
872
|
-
skip_ahead: BigInt(route.skip_ahead.toWei())
|
|
873
1246
|
})),
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
}
|
|
883
|
-
}
|
|
1247
|
+
withdraw_swap_limit_amount: BigInt(
|
|
1248
|
+
_params.withdraw_swap_limit_amount.toWei()
|
|
1249
|
+
),
|
|
1250
|
+
withdraw_swap_weights: _params.withdraw_swap_weights.map((weight) =>
|
|
1251
|
+
BigInt(weight.toWei())
|
|
1252
|
+
),
|
|
1253
|
+
close_position: _params.close_position,
|
|
1254
|
+
},
|
|
1255
|
+
}),
|
|
1256
|
+
};
|
|
884
1257
|
}
|
|
885
1258
|
|
|
886
1259
|
async getHealthFactor(): Promise<number> {
|
|
@@ -890,15 +1263,19 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
890
1263
|
|
|
891
1264
|
async getNetAPY(): Promise<number> {
|
|
892
1265
|
const positions = await this.getPositions();
|
|
893
|
-
logger.verbose(
|
|
894
|
-
|
|
1266
|
+
logger.verbose(
|
|
1267
|
+
`${this.name}::getNetAPY: positions: ${JSON.stringify(positions)}`
|
|
1268
|
+
);
|
|
1269
|
+
const allZero = positions.every((p) => p.usdValue === 0);
|
|
895
1270
|
|
|
896
1271
|
// in case of zero positions, apy will come zero/NaN
|
|
897
1272
|
// bcz of net 0 zero weights
|
|
898
1273
|
if (allZero) {
|
|
899
1274
|
// use approx dummy usd values to compute netAPY
|
|
900
1275
|
const collateralUSD = 1000;
|
|
901
|
-
const maxLTV = await this.vesuAdapter.getLTVConfig(
|
|
1276
|
+
const maxLTV = await this.vesuAdapter.getLTVConfig(
|
|
1277
|
+
this.config.networkConfig
|
|
1278
|
+
);
|
|
902
1279
|
const targetHF = this.config.targetHealthFactor;
|
|
903
1280
|
const maxDebt = HealthFactorMath.getMaxDebtAmountOnLooping(
|
|
904
1281
|
new Web3Number(collateralUSD, this.config.collateral.decimals),
|
|
@@ -907,17 +1284,22 @@ export class VesuMultiplyAdapter extends BaseAdapter<DepositParams, WithdrawPara
|
|
|
907
1284
|
targetHF,
|
|
908
1285
|
1, // assume price 1 for simplicity
|
|
909
1286
|
this.config.debt
|
|
910
|
-
)
|
|
1287
|
+
);
|
|
911
1288
|
|
|
912
1289
|
// debt is also added to collateral bcz, we assume debt is swapped to collateral
|
|
913
1290
|
const debtUSD = maxDebt.multipliedBy(1); // assume price 1 for simplicity
|
|
914
|
-
const netAPY =
|
|
1291
|
+
const netAPY =
|
|
1292
|
+
(positions[0].apy.apy * (collateralUSD + debtUSD.toNumber()) +
|
|
1293
|
+
positions[1].apy.apy * debtUSD.toNumber()) /
|
|
1294
|
+
collateralUSD;
|
|
915
1295
|
return netAPY;
|
|
916
1296
|
}
|
|
917
1297
|
|
|
918
1298
|
// Return true APY
|
|
919
1299
|
const netAmount = positions.reduce((acc, curr) => acc + curr.usdValue, 0);
|
|
920
|
-
const netAPY =
|
|
1300
|
+
const netAPY =
|
|
1301
|
+
positions.reduce((acc, curr) => acc + curr.apy.apy * curr.usdValue, 0) /
|
|
1302
|
+
netAmount;
|
|
921
1303
|
logger.verbose(`${this.name}::getNetAPY: netAPY: ${netAPY}`);
|
|
922
1304
|
return netAPY;
|
|
923
1305
|
}
|