@strkfarm/sdk 2.0.0-dev.27 → 2.0.0-dev.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +190 -36
- package/dist/cli.mjs +188 -34
- package/dist/index.browser.global.js +79130 -49357
- package/dist/index.browser.mjs +18039 -11434
- package/dist/index.d.ts +2869 -898
- package/dist/index.js +19036 -12210
- package/dist/index.mjs +18942 -12161
- package/package.json +1 -1
- package/src/data/avnu.abi.json +840 -0
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/dataTypes/_bignumber.ts +13 -4
- package/src/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +76 -41
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +167 -2
- package/src/modules/ExtendedWrapperSDk/types.ts +26 -4
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +110 -67
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +4 -4
- package/src/modules/ekubo-pricer.ts +79 -0
- package/src/modules/ekubo-quoter.ts +46 -30
- package/src/modules/erc20.ts +17 -0
- package/src/modules/harvests.ts +43 -29
- package/src/modules/pragma.ts +23 -8
- package/src/modules/pricer-from-api.ts +156 -15
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +40 -4
- package/src/modules/pricerBase.ts +2 -1
- package/src/node/deployer.ts +36 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/strategies/base-strategy.ts +78 -10
- package/src/strategies/ekubo-cl-vault.tsx +906 -347
- package/src/strategies/factory.ts +159 -0
- package/src/strategies/index.ts +6 -1
- package/src/strategies/registry.ts +239 -0
- package/src/strategies/sensei.ts +335 -7
- package/src/strategies/svk-strategy.ts +97 -27
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +2 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +177 -268
- package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
- package/src/strategies/universal-adapters/common-adapter.ts +206 -203
- package/src/strategies/universal-adapters/extended-adapter.ts +155 -336
- package/src/strategies/universal-adapters/index.ts +9 -8
- package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
- package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +200 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +110 -75
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +476 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +762 -844
- package/src/strategies/universal-adapters/vesu-position-common.ts +251 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
- package/src/strategies/universal-lst-muliplier-strategy.tsx +396 -204
- package/src/strategies/universal-strategy.tsx +1426 -1178
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +2251 -0
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +2941 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +12 -1
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +52 -0
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +1 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +2 -0
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +158 -124
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +377 -1788
- package/src/strategies/vesu-rebalance.tsx +255 -152
- package/src/utils/health-factor-math.ts +4 -1
- package/src/utils/index.ts +2 -1
- package/src/utils/logger.browser.ts +22 -4
- package/src/utils/logger.node.ts +259 -24
- package/src/utils/starknet-call-parser.ts +1036 -0
- package/src/utils/strategy-utils.ts +61 -0
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
|
-
import {
|
|
3
|
-
import { PricerBase } from "@/modules/pricerBase";
|
|
2
|
+
import { Protocols, TokenInfo } from "@/interfaces";
|
|
4
3
|
import {
|
|
5
4
|
BaseAdapter,
|
|
6
5
|
BaseAdapterConfig,
|
|
@@ -11,60 +10,85 @@ import {
|
|
|
11
10
|
ManageCall,
|
|
12
11
|
AdapterLeafType,
|
|
13
12
|
GenerateCallFn,
|
|
14
|
-
DepositParams,
|
|
15
|
-
WithdrawParams,
|
|
16
13
|
PositionAmount,
|
|
14
|
+
SwapPriceInfo,
|
|
17
15
|
} from "./baseAdapter";
|
|
18
16
|
import {
|
|
19
17
|
SIMPLE_SANITIZER,
|
|
20
18
|
SIMPLE_SANITIZER_V2,
|
|
21
19
|
toBigInt,
|
|
22
|
-
VESU_SINGLETON,
|
|
23
|
-
VESU_V2_MODIFY_POSITION_SANITIZER,
|
|
24
20
|
} from "./adapter-utils";
|
|
25
21
|
import { hash, uint256, Contract, CairoCustomEnum, num } from "starknet";
|
|
26
22
|
import {
|
|
27
23
|
VesuAdapter,
|
|
28
|
-
VesuMultiplyCallParams,
|
|
29
|
-
VesuModifyDelegationCallParams,
|
|
30
24
|
getVesuSingletonAddress,
|
|
31
|
-
VesuPools,
|
|
32
25
|
Swap,
|
|
33
26
|
IncreaseLeverParams,
|
|
34
27
|
DecreaseLeverParams,
|
|
35
28
|
} from "./vesu-adapter";
|
|
36
29
|
import { logger } from "@/utils";
|
|
37
|
-
import { WALLET_ADDRESS } from "../vesu-extended-strategy/utils/constants";
|
|
38
30
|
import VesuMultiplyAbi from "@/data/vesu-multiple.abi.json";
|
|
39
|
-
import
|
|
40
|
-
import VesuPoolV2Abi from "@/data/vesu-pool-v2.abi.json";
|
|
41
|
-
import { EkuboQuoter, TokenMarketData } from "@/modules";
|
|
31
|
+
import { EkuboQuote, EkuboQuoter, TokenMarketData } from "@/modules";
|
|
42
32
|
import { calculateDebtReductionAmountForWithdrawal } from "../vesu-extended-strategy/utils/helper";
|
|
43
33
|
import { HealthFactorMath } from "@/utils/health-factor-math";
|
|
44
34
|
import { MAX_LIQUIDATION_RATIO } from "../vesu-extended-strategy/utils/constants";
|
|
35
|
+
import {
|
|
36
|
+
VesuPositionCommonContext,
|
|
37
|
+
getVesuCommonAPY,
|
|
38
|
+
getVesuCommonMaxBorrowableAPY,
|
|
39
|
+
getVesuCommonMaxDeposit,
|
|
40
|
+
getVesuCommonMaxWithdraw,
|
|
41
|
+
getVesuCommonPosition,
|
|
42
|
+
} from "./vesu-position-common";
|
|
43
|
+
|
|
44
|
+
const MIN_REMAINING_DEBT_USD = 11;
|
|
45
|
+
const SCALE_128 = BigInt('1000000000000000000'); // 1e18 (matches vesu::units::SCALE_128)
|
|
46
|
+
const MIN_SQRT_RATIO_LIMIT = BigInt('18446748437148339061');
|
|
47
|
+
const MAX_SQRT_RATIO_LIMIT = BigInt('6277100250585753475930931601400621808602321654880405518632');
|
|
48
|
+
|
|
49
|
+
export interface VesuDepositParams {
|
|
50
|
+
amount: Web3Number;
|
|
51
|
+
marginSwap?: {
|
|
52
|
+
marginToken: TokenInfo;
|
|
53
|
+
};
|
|
54
|
+
leverSwap?: {
|
|
55
|
+
exactOutput?: Web3Number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface VesuWithdrawParams {
|
|
60
|
+
amount: Web3Number;
|
|
61
|
+
withdrawSwap?: {
|
|
62
|
+
outputToken: TokenInfo;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
45
65
|
|
|
46
66
|
export interface VesuMultiplyAdapterConfig extends BaseAdapterConfig {
|
|
47
67
|
poolId: ContractAddr;
|
|
48
68
|
collateral: TokenInfo;
|
|
49
69
|
debt: TokenInfo;
|
|
70
|
+
marginToken: TokenInfo; // in case of using ekubo to do margin swap, this can be diff token, else collateral itself.
|
|
50
71
|
targetHealthFactor: number;
|
|
51
72
|
minHealthFactor: number;
|
|
52
73
|
quoteAmountToFetchPrice: Web3Number;
|
|
53
74
|
minimumVesuMovementAmount: number;
|
|
75
|
+
maxSlippage?: number; // e.g. 0.005 for 0.5%
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
export class VesuMultiplyAdapter extends BaseAdapter<
|
|
57
|
-
|
|
58
|
-
|
|
79
|
+
VesuDepositParams,
|
|
80
|
+
VesuWithdrawParams
|
|
59
81
|
> {
|
|
60
82
|
readonly config: VesuMultiplyAdapterConfig;
|
|
61
|
-
readonly
|
|
83
|
+
readonly _vesuAdapter: VesuAdapter;
|
|
62
84
|
readonly tokenMarketData: TokenMarketData;
|
|
63
85
|
readonly minimumVesuMovementAmount: number;
|
|
86
|
+
lastSwapPriceInfo: SwapPriceInfo | null = null;
|
|
87
|
+
maxSlippage: number = 0.002; // 0.2%
|
|
64
88
|
constructor(config: VesuMultiplyAdapterConfig) {
|
|
65
89
|
super(config, VesuMultiplyAdapter.name, Protocols.VESU);
|
|
66
90
|
this.config = config;
|
|
67
|
-
this.
|
|
91
|
+
this._vesuAdapter = new VesuAdapter({
|
|
68
92
|
poolId: config.poolId,
|
|
69
93
|
collateral: config.collateral,
|
|
70
94
|
debt: config.debt,
|
|
@@ -76,350 +100,302 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
76
100
|
this.config.pricer,
|
|
77
101
|
this.config.networkConfig
|
|
78
102
|
);
|
|
103
|
+
this.config.maxSlippage = config.maxSlippage ?? 0.002; // 0.2%
|
|
104
|
+
this.maxSlippage = config.maxSlippage ?? 0.002; // 0.2%
|
|
79
105
|
}
|
|
80
106
|
|
|
81
|
-
|
|
82
|
-
supportedPosition: SupportedPosition
|
|
83
|
-
): Promise<PositionAPY> {
|
|
84
|
-
const CACHE_KEY = `apy_${this.config.poolId.address}_${supportedPosition.asset.symbol}`;
|
|
85
|
-
const cacheData = this.getCache<PositionAPY>(CACHE_KEY);
|
|
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
|
-
);
|
|
94
|
-
if (cacheData) {
|
|
95
|
-
return cacheData;
|
|
96
|
-
}
|
|
97
|
-
try {
|
|
98
|
-
// Get Vesu pools to find APY for the asset
|
|
99
|
-
const allVesuPools = await VesuAdapter.getVesuPools();
|
|
100
|
-
const asset = supportedPosition.asset;
|
|
101
|
-
const pool = allVesuPools.pools.find((p) =>
|
|
102
|
-
this.vesuAdapter.config.poolId.eqString(num.getHexString(p.id))
|
|
103
|
-
);
|
|
104
|
-
if (!pool) {
|
|
105
|
-
logger.warn(
|
|
106
|
-
`VesuMultiplyAdapter: Pool not found for token ${asset.symbol}`
|
|
107
|
-
);
|
|
108
|
-
return {
|
|
109
|
-
apy: 0,
|
|
110
|
-
type: APYType.BASE,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
// Find the asset stats for our token
|
|
114
|
-
const assetStats = pool.assets.find(
|
|
115
|
-
(a: any) => a.symbol.toLowerCase() === asset.symbol.toLowerCase()
|
|
116
|
-
)?.stats;
|
|
117
|
-
|
|
118
|
-
if (!assetStats) {
|
|
119
|
-
logger.warn(
|
|
120
|
-
`VesuMultiplyAdapter: Asset stats not found for token ${asset.symbol}`
|
|
121
|
-
);
|
|
122
|
-
return {
|
|
123
|
-
apy: 0,
|
|
124
|
-
type: APYType.BASE,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
// Get appropriate APY based on position type
|
|
128
|
-
let apy = 0;
|
|
129
|
-
if (supportedPosition.isDebt) {
|
|
130
|
-
// For debt positions, use borrow APY
|
|
131
|
-
apy = Number(assetStats.borrowApr?.value || 0) / 1e18;
|
|
132
|
-
|
|
133
|
-
// todo
|
|
134
|
-
// Account for rewards on debt token
|
|
135
|
-
} else {
|
|
136
|
-
// For collateral positions, use supply APY
|
|
137
|
-
const isAssetBTC = asset.symbol.toLowerCase().includes("btc");
|
|
138
|
-
const baseAPY =
|
|
139
|
-
Number(
|
|
140
|
-
isAssetBTC
|
|
141
|
-
? assetStats.btcFiSupplyApr?.value + assetStats.supplyApy?.value
|
|
142
|
-
: assetStats.supplyApy?.value || 0
|
|
143
|
-
) / 1e18;
|
|
144
|
-
|
|
145
|
-
// account for reward yield (like STRK rewards)
|
|
146
|
-
const rewardAPY =
|
|
147
|
-
Number(assetStats.defiSpringSupplyApr?.value || "0") / 1e18;
|
|
148
|
-
|
|
149
|
-
// account for base yield of LST
|
|
150
|
-
const isSupported = this.tokenMarketData.isAPYSupported(asset);
|
|
151
|
-
apy = baseAPY + rewardAPY;
|
|
152
|
-
if (isSupported) {
|
|
153
|
-
const tokenAPY = await this.tokenMarketData.getAPY(asset);
|
|
154
|
-
apy += tokenAPY;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
const result = {
|
|
158
|
-
apy,
|
|
159
|
-
type: supportedPosition.isDebt ? APYType.BASE : APYType.BASE,
|
|
160
|
-
};
|
|
107
|
+
// ─── Shared Helpers ──────────────────────────────────────────────────────────
|
|
161
108
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
109
|
+
private _getMultiplyContract(): {
|
|
110
|
+
contract: Contract;
|
|
111
|
+
vesuMultiply: ContractAddr;
|
|
112
|
+
vesuSingleton: ContractAddr;
|
|
113
|
+
isV2: boolean;
|
|
114
|
+
} {
|
|
115
|
+
const { addr: vesuSingleton, isV2 } = getVesuSingletonAddress(
|
|
116
|
+
this.config.poolId
|
|
117
|
+
);
|
|
118
|
+
const vesuMultiply = isV2
|
|
119
|
+
? this._vesuAdapter.VESU_WITHDRAW_SWAP_FIXED_MULTIPLIER
|
|
120
|
+
: this._vesuAdapter.VESU_MULTIPLY_V1;
|
|
121
|
+
const contract = new Contract({
|
|
122
|
+
abi: VesuMultiplyAbi,
|
|
123
|
+
address: vesuMultiply.address,
|
|
124
|
+
providerOrAccount: this.config.networkConfig.provider,
|
|
125
|
+
});
|
|
126
|
+
return { contract, vesuMultiply, vesuSingleton, isV2 };
|
|
175
127
|
}
|
|
176
128
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
129
|
+
private _buildDelegationWrappedCalls(
|
|
130
|
+
preCalls: ManageCall[],
|
|
131
|
+
modifyLeverCalldata: bigint[],
|
|
132
|
+
proofReadableIds: {
|
|
133
|
+
delegationOn: string;
|
|
134
|
+
modifyLever: string;
|
|
135
|
+
delegationOff: string;
|
|
136
|
+
},
|
|
137
|
+
): ManageCall[] {
|
|
138
|
+
const { vesuMultiply, vesuSingleton, isV2 } = this._getMultiplyContract();
|
|
139
|
+
|
|
140
|
+
const delegationOn: ManageCall = {
|
|
141
|
+
proofReadableId: proofReadableIds.delegationOn,
|
|
142
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
143
|
+
call: {
|
|
144
|
+
contractAddress: vesuSingleton,
|
|
145
|
+
selector: hash.getSelectorFromName("modify_delegation"),
|
|
146
|
+
calldata: isV2
|
|
147
|
+
? [vesuMultiply.toBigInt(), BigInt(1)]
|
|
148
|
+
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt(), BigInt(1)],
|
|
149
|
+
},
|
|
150
|
+
};
|
|
185
151
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
152
|
+
const modifyLever: ManageCall = {
|
|
153
|
+
proofReadableId: proofReadableIds.modifyLever,
|
|
154
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
155
|
+
call: {
|
|
156
|
+
contractAddress: vesuMultiply,
|
|
157
|
+
selector: hash.getSelectorFromName("modify_lever"),
|
|
158
|
+
calldata: modifyLeverCalldata,
|
|
159
|
+
},
|
|
160
|
+
};
|
|
190
161
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
162
|
+
const delegationOff: ManageCall = {
|
|
163
|
+
proofReadableId: proofReadableIds.delegationOff,
|
|
164
|
+
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
165
|
+
call: {
|
|
166
|
+
contractAddress: vesuSingleton,
|
|
167
|
+
selector: hash.getSelectorFromName("modify_delegation"),
|
|
168
|
+
calldata: isV2
|
|
169
|
+
? [vesuMultiply.toBigInt(), BigInt(0)]
|
|
170
|
+
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt(), BigInt(0)],
|
|
171
|
+
},
|
|
172
|
+
};
|
|
194
173
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
p.token.address.eq(supportedPosition.asset.address)
|
|
198
|
-
);
|
|
174
|
+
return [...preCalls, delegationOn, modifyLever, delegationOff];
|
|
175
|
+
}
|
|
199
176
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
177
|
+
private _getDepositProofReadableIds() {
|
|
178
|
+
const collateral = this.config.collateral;
|
|
179
|
+
const debt = this.config.debt;
|
|
180
|
+
return {
|
|
181
|
+
approve: `amc_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
182
|
+
delegationOn: `sd1_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
183
|
+
modifyLever: `vm_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
184
|
+
delegationOff: `sd2_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
209
187
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
188
|
+
private _getWithdrawProofReadableIds() {
|
|
189
|
+
const collateral = this.config.collateral;
|
|
190
|
+
const debt = this.config.debt;
|
|
191
|
+
return {
|
|
192
|
+
delegationOn: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
193
|
+
modifyLever: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
194
|
+
delegationOff: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private _buildZeroAmountSwapsWithWeights(
|
|
199
|
+
quote: { splits: any[] },
|
|
200
|
+
token: TokenInfo,
|
|
201
|
+
reverseRoutes = false,
|
|
202
|
+
): { swaps: Swap[]; weights: Web3Number[] } {
|
|
203
|
+
const swaps: Swap[] = quote.splits.map((split) => {
|
|
204
|
+
const routeNodes = split.route.map((_route: any) => ({
|
|
205
|
+
pool_key: {
|
|
206
|
+
token0: ContractAddr.from(_route.pool_key.token0),
|
|
207
|
+
token1: ContractAddr.from(_route.pool_key.token1),
|
|
208
|
+
fee: _route.pool_key.fee,
|
|
209
|
+
tick_spacing: _route.pool_key.tick_spacing.toString(),
|
|
210
|
+
extension: _route.pool_key.extension,
|
|
211
|
+
},
|
|
212
|
+
sqrt_ratio_limit: Web3Number.fromWei(_route.sqrt_ratio_limit, 18),
|
|
213
|
+
skip_ahead: Web3Number.fromWei(_route.skip_ahead, 0),
|
|
214
|
+
}));
|
|
215
|
+
if (reverseRoutes) {
|
|
216
|
+
routeNodes.reverse();
|
|
213
217
|
}
|
|
218
|
+
return {
|
|
219
|
+
route: routeNodes,
|
|
220
|
+
token_amount: {
|
|
221
|
+
token: token.address,
|
|
222
|
+
amount: Web3Number.fromWei(0, token.decimals),
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
});
|
|
214
226
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
227
|
+
const totalSpecifiedBig = quote.splits.reduce(
|
|
228
|
+
(sum: bigint, s: any) => sum + BigInt(Math.abs(Number(s.amount_specified))),
|
|
229
|
+
0n
|
|
230
|
+
);
|
|
231
|
+
const weights: Web3Number[] = [];
|
|
232
|
+
let allocatedWeight = 0n;
|
|
233
|
+
for (let i = 0; i < quote.splits.length; i++) {
|
|
234
|
+
const split = quote.splits[i];
|
|
235
|
+
let weight: bigint;
|
|
236
|
+
if (i === quote.splits.length - 1) {
|
|
237
|
+
weight = SCALE_128 - allocatedWeight;
|
|
238
|
+
} else if (totalSpecifiedBig > 0n) {
|
|
239
|
+
const splitAbs = BigInt(Math.abs(Number(split.amount_specified)));
|
|
240
|
+
weight = (splitAbs * SCALE_128) / totalSpecifiedBig;
|
|
241
|
+
} else {
|
|
242
|
+
weight = SCALE_128;
|
|
243
|
+
}
|
|
244
|
+
allocatedWeight += weight;
|
|
245
|
+
weights.push(Web3Number.fromWei(weight.toString(), 0));
|
|
224
246
|
}
|
|
225
|
-
}
|
|
226
247
|
|
|
227
|
-
|
|
228
|
-
// get collateral APY
|
|
229
|
-
const collateralAPY = await this.getAPY({
|
|
230
|
-
asset: this.config.collateral,
|
|
231
|
-
isDebt: false,
|
|
232
|
-
});
|
|
233
|
-
const apy = collateralAPY.apy * 0.8;
|
|
234
|
-
return apy;
|
|
248
|
+
return { swaps, weights };
|
|
235
249
|
}
|
|
236
250
|
|
|
237
|
-
async
|
|
238
|
-
|
|
239
|
-
|
|
251
|
+
private async _fetchPositionAndPrices(): Promise<{
|
|
252
|
+
existingCollateralInfo: any;
|
|
253
|
+
existingDebtInfo: any;
|
|
254
|
+
collateralisation: any;
|
|
255
|
+
collateralPrice: number;
|
|
256
|
+
debtPrice: number;
|
|
257
|
+
ekuboQuoter: EkuboQuoter;
|
|
258
|
+
}> {
|
|
259
|
+
this._vesuAdapter.networkConfig = this.config.networkConfig;
|
|
260
|
+
this._vesuAdapter.pricer = this.config.pricer;
|
|
261
|
+
|
|
262
|
+
const existingPositions = await this._vesuAdapter.getPositions(
|
|
263
|
+
this.config.networkConfig
|
|
264
|
+
);
|
|
265
|
+
const collateralisation = await this._vesuAdapter.getCollateralization(
|
|
266
|
+
this.config.networkConfig
|
|
267
|
+
);
|
|
268
|
+
const existingCollateralInfo = existingPositions[0];
|
|
269
|
+
const existingDebtInfo = existingPositions[1];
|
|
240
270
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
271
|
+
const collateralPrice =
|
|
272
|
+
collateralisation[0].usdValue > 0
|
|
273
|
+
? collateralisation[0].usdValue /
|
|
274
|
+
existingCollateralInfo.amount.toNumber()
|
|
275
|
+
: (await this.config.pricer.getPrice(this.config.collateral.symbol))
|
|
276
|
+
.price;
|
|
277
|
+
const debtPrice =
|
|
278
|
+
collateralisation[1].usdValue > 0
|
|
279
|
+
? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber()
|
|
280
|
+
: (await this.config.pricer.getPrice(this.config.debt.symbol)).price;
|
|
245
281
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
p.token.address.eq(collateral.address)
|
|
251
|
-
);
|
|
252
|
-
const debtPosition = positions.find((p) =>
|
|
253
|
-
p.token.address.eq(debt.address)
|
|
254
|
-
);
|
|
282
|
+
const ekuboQuoter = new EkuboQuoter(
|
|
283
|
+
this.config.networkConfig,
|
|
284
|
+
this.config.pricer
|
|
285
|
+
);
|
|
255
286
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
287
|
+
return {
|
|
288
|
+
existingCollateralInfo,
|
|
289
|
+
existingDebtInfo,
|
|
290
|
+
collateralisation,
|
|
291
|
+
collateralPrice,
|
|
292
|
+
debtPrice,
|
|
293
|
+
ekuboQuoter,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
259
296
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
297
|
+
private _computeTargetDebtDelta(
|
|
298
|
+
addedCollateral: Web3Number,
|
|
299
|
+
existingCollateral: Web3Number,
|
|
300
|
+
existingDebt: Web3Number,
|
|
301
|
+
collateralPrice: number,
|
|
302
|
+
debtPrice: number,
|
|
303
|
+
ltv: number,
|
|
304
|
+
dexPrice: number
|
|
305
|
+
): Web3Number {
|
|
306
|
+
// target hf = (((collateral + addedCollateral) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
307
|
+
// => X = (((collateral + addedCollateral) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
308
|
+
const numeratorPart1 = existingCollateral
|
|
309
|
+
.plus(addedCollateral)
|
|
310
|
+
.multipliedBy(collateralPrice)
|
|
311
|
+
.multipliedBy(ltv);
|
|
312
|
+
const numeratorPart2 = existingDebt
|
|
313
|
+
.multipliedBy(debtPrice)
|
|
314
|
+
.multipliedBy(this.config.targetHealthFactor);
|
|
315
|
+
const denominatorPart = this.config.targetHealthFactor - ltv / dexPrice;
|
|
316
|
+
const x_debt_usd = numeratorPart1
|
|
317
|
+
.minus(numeratorPart2)
|
|
318
|
+
.dividedBy(denominatorPart);
|
|
279
319
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
collateral.symbol
|
|
286
|
-
);
|
|
287
|
-
if (collateralPrice.price === 0) {
|
|
288
|
-
throw new Error("Collateral price is 0");
|
|
289
|
-
}
|
|
290
|
-
const debtPrice = await this.config.pricer.getPrice(debt.symbol);
|
|
291
|
-
if (debtPrice.price === 0) {
|
|
292
|
-
throw new Error("Debt price is 0");
|
|
293
|
-
}
|
|
294
|
-
const maxCollateralFromDebt =
|
|
295
|
-
HealthFactorMath.getMinCollateralRequiredOnLooping(
|
|
296
|
-
actualMaxBorrowable,
|
|
297
|
-
debtPrice.price,
|
|
298
|
-
this.config.targetHealthFactor,
|
|
299
|
-
maxLTV,
|
|
300
|
-
collateralPrice.price,
|
|
301
|
-
collateral
|
|
302
|
-
);
|
|
320
|
+
return new Web3Number(
|
|
321
|
+
x_debt_usd.dividedBy(debtPrice).toFixed(this.config.debt.decimals),
|
|
322
|
+
this.config.debt.decimals
|
|
323
|
+
);
|
|
324
|
+
}
|
|
303
325
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
)
|
|
326
|
+
private _getPositionCommonContext(): VesuPositionCommonContext {
|
|
327
|
+
return {
|
|
328
|
+
adapterName: VesuMultiplyAdapter.name,
|
|
329
|
+
protocol: this.protocol,
|
|
330
|
+
poolId: this.config.poolId,
|
|
331
|
+
collateral: this.config.collateral,
|
|
332
|
+
debt: this.config.debt,
|
|
333
|
+
networkConfig: this.config.networkConfig,
|
|
334
|
+
pricer: this.config.pricer,
|
|
335
|
+
vesuAdapter: this._vesuAdapter,
|
|
336
|
+
tokenMarketData: this.tokenMarketData,
|
|
337
|
+
targetHealthFactor: this.config.targetHealthFactor,
|
|
338
|
+
getCache: this.getCache.bind(this),
|
|
339
|
+
setCache: this.setCache.bind(this),
|
|
340
|
+
getUSDValue: this.getUSDValue.bind(this),
|
|
341
|
+
};
|
|
342
|
+
}
|
|
318
343
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
`VesuMultiplyAdapter: Max deposit amount: ${maxDepositAmount.toNumber()}, netAPY: ${netAPY}`
|
|
330
|
-
);
|
|
331
|
-
return {
|
|
332
|
-
tokenInfo: collateral,
|
|
333
|
-
amount: maxDepositAmount,
|
|
334
|
-
usdValue,
|
|
335
|
-
remarks: "Max deposit based on available debt capacity",
|
|
336
|
-
apy: {
|
|
337
|
-
apy: netAPY,
|
|
338
|
-
type: APYType.BASE,
|
|
339
|
-
},
|
|
340
|
-
protocol: this.protocol,
|
|
341
|
-
};
|
|
342
|
-
} catch (error) {
|
|
343
|
-
logger.error(
|
|
344
|
-
`VesuMultiplyAdapter: Error calculating max deposit:`,
|
|
345
|
-
error
|
|
346
|
-
);
|
|
347
|
-
throw error;
|
|
344
|
+
// ─── APY / Position / Max Deposit/Withdraw ─────────────────────────────────
|
|
345
|
+
|
|
346
|
+
protected async getAPY(
|
|
347
|
+
supportedPosition: SupportedPosition
|
|
348
|
+
): Promise<PositionAPY> {
|
|
349
|
+
// always add vesu modify position adapter, which shall
|
|
350
|
+
// return correct apy
|
|
351
|
+
return {
|
|
352
|
+
apy: 0,
|
|
353
|
+
type: APYType.BASE,
|
|
348
354
|
}
|
|
349
355
|
}
|
|
350
356
|
|
|
351
|
-
async
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
this.vesuAdapter.pricer = this.config.pricer;
|
|
357
|
+
protected async getPosition(
|
|
358
|
+
supportedPosition: SupportedPosition
|
|
359
|
+
): Promise<PositionAmount | null> {
|
|
360
|
+
// always add vesu modify position adapter, which shall
|
|
361
|
+
// return correct position amount
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
359
364
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
);
|
|
366
|
-
const debtPosition = positions.find((p) =>
|
|
367
|
-
p.token.address.eq(this.config.debt.address)
|
|
368
|
-
);
|
|
365
|
+
async maxBorrowableAPY(): Promise<number> {
|
|
366
|
+
// always add vesu modify position adapter, which shall
|
|
367
|
+
// return correct max borrowable apy
|
|
368
|
+
return 0;
|
|
369
|
+
}
|
|
369
370
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
371
|
+
async maxDeposit(amount?: Web3Number): Promise<PositionInfo> {
|
|
372
|
+
// always add vesu modify position adapter, which shall
|
|
373
|
+
// return correct max deposit
|
|
374
|
+
return {
|
|
375
|
+
tokenInfo: this.config.baseToken,
|
|
376
|
+
amount: new Web3Number(0, this.config.baseToken.decimals),
|
|
377
|
+
usdValue: 0,
|
|
378
|
+
remarks: "Max deposit",
|
|
379
|
+
apy: { apy: 0, type: APYType.BASE },
|
|
380
|
+
protocol: this.protocol,
|
|
381
|
+
}
|
|
382
|
+
}
|
|
373
383
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
const debtUSD = debtPosition.usdValue;
|
|
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
|
-
);
|
|
403
|
-
return {
|
|
404
|
-
tokenInfo: collateral,
|
|
405
|
-
amount: result,
|
|
406
|
-
usdValue,
|
|
407
|
-
remarks: "Max withdraw based on health factor",
|
|
408
|
-
apy: {
|
|
409
|
-
apy: netAPY,
|
|
410
|
-
type: APYType.BASE,
|
|
411
|
-
},
|
|
412
|
-
protocol: this.protocol,
|
|
413
|
-
};
|
|
414
|
-
} catch (error) {
|
|
415
|
-
logger.error(
|
|
416
|
-
`VesuMultiplyAdapter: Error calculating max withdraw:`,
|
|
417
|
-
error
|
|
418
|
-
);
|
|
419
|
-
throw error;
|
|
384
|
+
async maxWithdraw(): Promise<PositionInfo> {
|
|
385
|
+
// always add vesu modify position adapter, which shall
|
|
386
|
+
// return correct max withdraw
|
|
387
|
+
return {
|
|
388
|
+
tokenInfo: this.config.baseToken,
|
|
389
|
+
amount: new Web3Number(0, this.config.baseToken.decimals),
|
|
390
|
+
usdValue: 0,
|
|
391
|
+
remarks: "Max withdraw",
|
|
392
|
+
apy: { apy: 0, type: APYType.BASE },
|
|
393
|
+
protocol: this.protocol,
|
|
420
394
|
}
|
|
421
395
|
}
|
|
422
396
|
|
|
397
|
+
// ─── Leaf Configuration ────────────────────────────────────────────────────
|
|
398
|
+
|
|
423
399
|
protected _getDepositLeaf(): {
|
|
424
400
|
target: ContractAddr;
|
|
425
401
|
method: string;
|
|
@@ -429,46 +405,31 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
429
405
|
}[] {
|
|
430
406
|
const collateral = this.config.collateral;
|
|
431
407
|
const debt = this.config.debt;
|
|
432
|
-
const {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
408
|
+
const { vesuMultiply, vesuSingleton, isV2 } = this._getMultiplyContract();
|
|
409
|
+
const token = debt;
|
|
410
|
+
const idSuffix = `${collateral.symbol}_${debt.symbol}`;
|
|
411
|
+
const idApprovePrefix = "amc";
|
|
412
|
+
const idDelegOnPrefix = "sd1";
|
|
413
|
+
const idMultiplyPrefix = "vm";
|
|
414
|
+
const idDelegOffPrefix = "sd2";
|
|
438
415
|
|
|
439
416
|
return [
|
|
440
|
-
// Approval step for collateral
|
|
441
417
|
{
|
|
442
|
-
target:
|
|
418
|
+
target: token.address,
|
|
443
419
|
method: "approve",
|
|
444
|
-
packedArguments: [
|
|
445
|
-
vesuMultiply.toBigInt(), // spender
|
|
446
|
-
],
|
|
420
|
+
packedArguments: [vesuMultiply.toBigInt()],
|
|
447
421
|
sanitizer: SIMPLE_SANITIZER,
|
|
448
|
-
|
|
449
|
-
id: `amc_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
450
|
-
debt.symbol
|
|
451
|
-
}`,
|
|
422
|
+
id: `${idApprovePrefix}_${this.config.poolId.shortString()}_${idSuffix}`,
|
|
452
423
|
},
|
|
453
|
-
// Switch delegation on
|
|
454
424
|
{
|
|
455
425
|
target: vesuSingleton,
|
|
456
426
|
method: "modify_delegation",
|
|
457
427
|
packedArguments: isV2
|
|
458
|
-
? [
|
|
459
|
-
|
|
460
|
-
]
|
|
461
|
-
: [
|
|
462
|
-
this.config.poolId.toBigInt(),
|
|
463
|
-
vesuMultiply.toBigInt(), // delegatee
|
|
464
|
-
],
|
|
428
|
+
? [vesuMultiply.toBigInt()]
|
|
429
|
+
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
465
430
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
466
|
-
|
|
467
|
-
id: `sd1_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
468
|
-
debt.symbol
|
|
469
|
-
}`,
|
|
431
|
+
id: `${idDelegOnPrefix}_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
470
432
|
},
|
|
471
|
-
// Vesu multiply call
|
|
472
433
|
{
|
|
473
434
|
target: vesuMultiply,
|
|
474
435
|
method: "modify_lever",
|
|
@@ -479,28 +440,16 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
479
440
|
this.config.vaultAllocator.toBigInt(),
|
|
480
441
|
],
|
|
481
442
|
sanitizer: SIMPLE_SANITIZER_V2,
|
|
482
|
-
|
|
483
|
-
id: `vm_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
484
|
-
debt.symbol
|
|
485
|
-
}`,
|
|
443
|
+
id: `${idMultiplyPrefix}_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
486
444
|
},
|
|
487
|
-
// Switch delegation off
|
|
488
445
|
{
|
|
489
446
|
target: vesuSingleton,
|
|
490
447
|
method: "modify_delegation",
|
|
491
448
|
packedArguments: isV2
|
|
492
|
-
? [
|
|
493
|
-
|
|
494
|
-
]
|
|
495
|
-
: [
|
|
496
|
-
this.config.poolId.toBigInt(),
|
|
497
|
-
vesuMultiply.toBigInt(), // delegatee
|
|
498
|
-
],
|
|
449
|
+
? [vesuMultiply.toBigInt()]
|
|
450
|
+
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
499
451
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
500
|
-
|
|
501
|
-
id: `sd2_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
502
|
-
debt.symbol
|
|
503
|
-
}`,
|
|
452
|
+
id: `${idDelegOffPrefix}_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
504
453
|
},
|
|
505
454
|
];
|
|
506
455
|
}
|
|
@@ -512,35 +461,20 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
512
461
|
sanitizer: ContractAddr;
|
|
513
462
|
id: string;
|
|
514
463
|
}[] {
|
|
515
|
-
const {
|
|
516
|
-
this.config.poolId
|
|
517
|
-
);
|
|
518
|
-
const vesuMultiply = isV2
|
|
519
|
-
? this.vesuAdapter.VESU_MULTIPLY
|
|
520
|
-
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
464
|
+
const { vesuMultiply, vesuSingleton, isV2 } = this._getMultiplyContract();
|
|
521
465
|
const collateral = this.config.collateral;
|
|
522
466
|
const debt = this.config.debt;
|
|
523
467
|
|
|
524
468
|
return [
|
|
525
|
-
// Switch delegation on
|
|
526
469
|
{
|
|
527
470
|
target: vesuSingleton,
|
|
528
471
|
method: "modify_delegation",
|
|
529
472
|
packedArguments: isV2
|
|
530
|
-
? [
|
|
531
|
-
|
|
532
|
-
]
|
|
533
|
-
: [
|
|
534
|
-
this.config.poolId.toBigInt(),
|
|
535
|
-
vesuMultiply.toBigInt(), // delegatee
|
|
536
|
-
],
|
|
473
|
+
? [vesuMultiply.toBigInt()]
|
|
474
|
+
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
537
475
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
538
|
-
|
|
539
|
-
id: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
540
|
-
debt.symbol
|
|
541
|
-
}`,
|
|
476
|
+
id: `sdow_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
542
477
|
},
|
|
543
|
-
// Vesu multiply call
|
|
544
478
|
{
|
|
545
479
|
target: vesuMultiply,
|
|
546
480
|
method: "modify_lever",
|
|
@@ -551,418 +485,256 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
551
485
|
this.config.vaultAllocator.toBigInt(),
|
|
552
486
|
],
|
|
553
487
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
554
|
-
|
|
555
|
-
id: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
556
|
-
debt.symbol
|
|
557
|
-
}`,
|
|
488
|
+
id: `vmw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
558
489
|
},
|
|
559
|
-
// Switch delegation off
|
|
560
490
|
{
|
|
561
491
|
target: vesuSingleton,
|
|
562
492
|
method: "modify_delegation",
|
|
563
493
|
packedArguments: isV2
|
|
564
|
-
? [
|
|
565
|
-
|
|
566
|
-
]
|
|
567
|
-
: [
|
|
568
|
-
this.config.poolId.toBigInt(),
|
|
569
|
-
vesuMultiply.toBigInt(), // delegatee
|
|
570
|
-
],
|
|
494
|
+
? [vesuMultiply.toBigInt()]
|
|
495
|
+
: [this.config.poolId.toBigInt(), vesuMultiply.toBigInt()],
|
|
571
496
|
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
572
|
-
|
|
573
|
-
id: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${
|
|
574
|
-
debt.symbol
|
|
575
|
-
}`,
|
|
497
|
+
id: `sdofw_${this.config.poolId.shortString()}_${collateral.symbol}_${debt.symbol}`,
|
|
576
498
|
},
|
|
577
499
|
];
|
|
578
500
|
}
|
|
579
501
|
|
|
580
|
-
|
|
502
|
+
// ─── Leaf Adapters ─────────────────────────────────────────────────────────
|
|
503
|
+
|
|
504
|
+
getDepositAdapter(approveToken?: TokenInfo): AdapterLeafType<VesuDepositParams> {
|
|
505
|
+
throw new Error("getDepositAdapter::Not implemented");
|
|
581
506
|
const leafConfigs = this._getDepositLeaf();
|
|
582
507
|
const leaves = leafConfigs.map((config) => {
|
|
583
508
|
const { target, method, packedArguments, sanitizer, id } = config;
|
|
584
|
-
|
|
585
|
-
{
|
|
586
|
-
id: id,
|
|
587
|
-
target,
|
|
588
|
-
method,
|
|
589
|
-
packedArguments,
|
|
590
|
-
},
|
|
509
|
+
return this.constructSimpleLeafData(
|
|
510
|
+
{ id, target, method, packedArguments },
|
|
591
511
|
sanitizer
|
|
592
512
|
);
|
|
593
|
-
return leaf;
|
|
594
513
|
});
|
|
595
514
|
return {
|
|
596
515
|
leaves,
|
|
597
516
|
callConstructor: this.getDepositCall.bind(
|
|
598
517
|
this
|
|
599
|
-
) as unknown as GenerateCallFn<
|
|
518
|
+
) as unknown as GenerateCallFn<VesuDepositParams>,
|
|
600
519
|
};
|
|
601
520
|
}
|
|
602
521
|
|
|
603
|
-
getWithdrawAdapter(): AdapterLeafType<
|
|
522
|
+
getWithdrawAdapter(): AdapterLeafType<VesuWithdrawParams> {
|
|
523
|
+
throw new Error("getWithdrawAdapter::Dont think this fn is used anywhere");
|
|
604
524
|
const leafConfigs = this._getWithdrawLeaf();
|
|
605
525
|
const leaves = leafConfigs.map((config) => {
|
|
606
526
|
const { target, method, packedArguments, sanitizer, id } = config;
|
|
607
|
-
|
|
608
|
-
{
|
|
609
|
-
id: id,
|
|
610
|
-
target,
|
|
611
|
-
method,
|
|
612
|
-
packedArguments,
|
|
613
|
-
},
|
|
527
|
+
return this.constructSimpleLeafData(
|
|
528
|
+
{ id, target, method, packedArguments },
|
|
614
529
|
sanitizer
|
|
615
530
|
);
|
|
616
|
-
return leaf;
|
|
617
531
|
});
|
|
618
532
|
return {
|
|
619
533
|
leaves,
|
|
620
534
|
callConstructor: this.getWithdrawCall.bind(
|
|
621
535
|
this
|
|
622
|
-
) as unknown as GenerateCallFn<
|
|
536
|
+
) as unknown as GenerateCallFn<VesuWithdrawParams>,
|
|
623
537
|
};
|
|
624
538
|
}
|
|
625
539
|
|
|
626
|
-
|
|
627
|
-
const collateral = this.config.collateral;
|
|
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;
|
|
540
|
+
// ─── Public Call Builders ──────────────────────────────────────────────────
|
|
634
541
|
|
|
635
|
-
|
|
542
|
+
async getDepositCall(params: VesuDepositParams): Promise<ManageCall[]> {
|
|
543
|
+
const { calldata, approveToken, approveAmount } = await this._getIncreaseCalldata(params);
|
|
544
|
+
const { vesuMultiply } = this._getMultiplyContract();
|
|
545
|
+
const proofReadableIds = this._getDepositProofReadableIds();
|
|
636
546
|
|
|
637
|
-
return [
|
|
638
|
-
// Approval call
|
|
639
|
-
{
|
|
640
|
-
sanitizer: SIMPLE_SANITIZER,
|
|
641
|
-
call: {
|
|
642
|
-
contractAddress: collateral.address,
|
|
643
|
-
selector: hash.getSelectorFromName("approve"),
|
|
644
|
-
calldata: [
|
|
645
|
-
vesuMultiply.toBigInt(), // spender
|
|
646
|
-
toBigInt(uint256MarginAmount.low.toString()), // amount low
|
|
647
|
-
toBigInt(uint256MarginAmount.high.toString()), // amount high
|
|
648
|
-
],
|
|
649
|
-
},
|
|
650
|
-
},
|
|
651
|
-
// Switch delegation on
|
|
652
|
-
{
|
|
653
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
654
|
-
call: {
|
|
655
|
-
contractAddress: vesuSingleton,
|
|
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
|
-
},
|
|
668
|
-
},
|
|
669
|
-
// Vesu multiply call
|
|
670
|
-
{
|
|
671
|
-
sanitizer: SIMPLE_SANITIZER_V2,
|
|
672
|
-
call: {
|
|
673
|
-
contractAddress: vesuMultiply,
|
|
674
|
-
selector: hash.getSelectorFromName("modify_lever"),
|
|
675
|
-
calldata: await this.getMultiplyCallCalldata(params, true),
|
|
676
|
-
},
|
|
677
|
-
},
|
|
678
|
-
// Switch delegation off
|
|
679
|
-
{
|
|
680
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
681
|
-
call: {
|
|
682
|
-
contractAddress: vesuSingleton,
|
|
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
|
-
},
|
|
696
|
-
];
|
|
697
|
-
}
|
|
698
547
|
|
|
699
|
-
|
|
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;
|
|
548
|
+
const uint256Amount = uint256.bnToUint256(approveAmount.toWei());
|
|
706
549
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
]
|
|
719
|
-
: [
|
|
720
|
-
this.config.poolId.toBigInt(),
|
|
721
|
-
vesuMultiply.toBigInt(), // delegatee
|
|
722
|
-
BigInt(1), // delegation: true
|
|
723
|
-
],
|
|
724
|
-
},
|
|
725
|
-
},
|
|
726
|
-
// Vesu multiply call
|
|
727
|
-
{
|
|
728
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
729
|
-
call: {
|
|
730
|
-
contractAddress: vesuMultiply,
|
|
731
|
-
selector: hash.getSelectorFromName("modify_lever"),
|
|
732
|
-
calldata: await this.getWithdrawalCalldata(params),
|
|
733
|
-
},
|
|
734
|
-
},
|
|
735
|
-
// Switch delegation off
|
|
736
|
-
{
|
|
737
|
-
sanitizer: isV2 ? SIMPLE_SANITIZER_V2 : SIMPLE_SANITIZER,
|
|
738
|
-
call: {
|
|
739
|
-
contractAddress: vesuSingleton,
|
|
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
|
-
},
|
|
550
|
+
const approveCall: ManageCall = {
|
|
551
|
+
proofReadableId: proofReadableIds.approve,
|
|
552
|
+
sanitizer: SIMPLE_SANITIZER,
|
|
553
|
+
call: {
|
|
554
|
+
contractAddress: approveToken.address,
|
|
555
|
+
selector: hash.getSelectorFromName("approve"),
|
|
556
|
+
calldata: [
|
|
557
|
+
vesuMultiply.toBigInt(),
|
|
558
|
+
toBigInt(uint256Amount.low.toString()),
|
|
559
|
+
toBigInt(uint256Amount.high.toString()),
|
|
560
|
+
],
|
|
752
561
|
},
|
|
753
|
-
|
|
754
|
-
}
|
|
562
|
+
};
|
|
755
563
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
-
);
|
|
769
|
-
const { isV2 } = getVesuSingletonAddress(this.config.poolId);
|
|
770
|
-
const vesuMultiply = isV2
|
|
771
|
-
? this.vesuAdapter.VESU_MULTIPLY
|
|
772
|
-
: this.vesuAdapter.VESU_MULTIPLY_V1;
|
|
564
|
+
return this._buildDelegationWrappedCalls([approveCall], calldata, {
|
|
565
|
+
delegationOn: proofReadableIds.delegationOn,
|
|
566
|
+
modifyLever: proofReadableIds.modifyLever,
|
|
567
|
+
delegationOff: proofReadableIds.delegationOff,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
773
570
|
|
|
774
|
-
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
571
|
+
async getWithdrawCall(params: VesuWithdrawParams): Promise<ManageCall[]> {
|
|
572
|
+
const calldata = await this._getDecreaseCalldata(params);
|
|
573
|
+
const proofReadableIds = this._getWithdrawProofReadableIds();
|
|
574
|
+
return this._buildDelegationWrappedCalls([], calldata, {
|
|
575
|
+
delegationOn: proofReadableIds.delegationOn,
|
|
576
|
+
modifyLever: proofReadableIds.modifyLever,
|
|
577
|
+
delegationOff: proofReadableIds.delegationOff,
|
|
779
578
|
});
|
|
579
|
+
}
|
|
780
580
|
|
|
781
|
-
|
|
782
|
-
let leverSwap: Swap[] = [];
|
|
783
|
-
let leverSwapLimitAmount = Web3Number.fromWei(0, this.config.debt.decimals);
|
|
581
|
+
// ─── Consolidated Calldata Builders ────────────────────────────────────────
|
|
784
582
|
|
|
785
|
-
|
|
786
|
-
|
|
583
|
+
private async _getIncreaseCalldata(
|
|
584
|
+
params: VesuDepositParams
|
|
585
|
+
): Promise<{ calldata: bigint[]; approveToken: TokenInfo; approveAmount: Web3Number }> {
|
|
586
|
+
const collateralToken = this.config.collateral;
|
|
587
|
+
const debtToken = this.config.debt;
|
|
588
|
+
const { contract: multiplyContract } = this._getMultiplyContract();
|
|
589
|
+
|
|
590
|
+
const {
|
|
591
|
+
existingCollateralInfo,
|
|
592
|
+
existingDebtInfo,
|
|
593
|
+
collateralPrice,
|
|
594
|
+
debtPrice,
|
|
595
|
+
ekuboQuoter,
|
|
596
|
+
} = await this._fetchPositionAndPrices();
|
|
597
|
+
|
|
598
|
+
logger.verbose(
|
|
599
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata params: ${JSON.stringify(
|
|
600
|
+
params
|
|
601
|
+
)}, collateral: ${collateralToken.symbol}, debt: ${debtToken.symbol}`
|
|
787
602
|
);
|
|
788
|
-
|
|
789
|
-
|
|
603
|
+
logger.debug(
|
|
604
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata existingCollateralInfo: ${JSON.stringify(
|
|
605
|
+
existingCollateralInfo
|
|
606
|
+
)}, existingDebtInfo: ${JSON.stringify(existingDebtInfo)}`
|
|
790
607
|
);
|
|
791
|
-
const existingCollateralInfo = existingPositions[0];
|
|
792
|
-
const existingDebtInfo = existingPositions[1];
|
|
793
|
-
const isDexPriceRequired = existingDebtInfo.token.symbol !== "USDC";
|
|
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)}`);
|
|
802
|
-
|
|
803
|
-
// - Prices as seen by Vesu contracts, ideal for HF math
|
|
804
|
-
// Price 1 is ok as fallback bcz that would relatively price the
|
|
805
|
-
// collateral and debt as equal.
|
|
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
608
|
logger.debug(
|
|
817
|
-
`${VesuMultiplyAdapter.name}::
|
|
609
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata collateralPrice: ${collateralPrice}, debtPrice: ${debtPrice}`
|
|
818
610
|
);
|
|
819
611
|
|
|
820
|
-
const legLTV = await this.
|
|
612
|
+
const legLTV = await this._vesuAdapter.getLTVConfig(
|
|
821
613
|
this.config.networkConfig
|
|
822
614
|
);
|
|
823
|
-
const
|
|
824
|
-
this.config.networkConfig,
|
|
825
|
-
this.config.pricer
|
|
826
|
-
);
|
|
615
|
+
const isDexPriceRequired = debtToken.symbol !== "USDC";
|
|
827
616
|
const dexPrice = isDexPriceRequired
|
|
828
617
|
? await ekuboQuoter.getDexPrice(
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
618
|
+
collateralToken,
|
|
619
|
+
debtToken,
|
|
620
|
+
this.config.quoteAmountToFetchPrice
|
|
621
|
+
)
|
|
833
622
|
: 1;
|
|
834
623
|
logger.verbose(
|
|
835
|
-
`${VesuMultiplyAdapter.name}::
|
|
624
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata dexPrice: ${dexPrice}, ltv: ${legLTV}`
|
|
836
625
|
);
|
|
837
626
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
// => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
843
|
-
// => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
844
|
-
// => X = (((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)) / (target hf - ltv)
|
|
627
|
+
let marginSwap: Swap[] = [];
|
|
628
|
+
let marginSwapLimitAmount = Web3Number.fromWei(0, collateralToken.decimals);
|
|
629
|
+
let addedCollateral: Web3Number = params.amount;
|
|
630
|
+
let approveAmount: Web3Number = params.amount;
|
|
845
631
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
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
|
-
);
|
|
632
|
+
if (params.marginSwap) {
|
|
633
|
+
const marginToken = params.marginSwap.marginToken;
|
|
634
|
+
const requiredAmount = params.amount;
|
|
876
635
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
636
|
+
const marginSwapQuote = await ekuboQuoter.getQuoteExactOutput(
|
|
637
|
+
marginToken.address.address,
|
|
638
|
+
collateralToken.address.address,
|
|
639
|
+
requiredAmount
|
|
640
|
+
);
|
|
641
|
+
if (marginSwapQuote.price_impact > 0.01) {
|
|
642
|
+
throw new Error(
|
|
643
|
+
`VesuMultiplyAdapter: Margin swap price impact too high (${marginSwapQuote.price_impact})`
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
marginSwap = ekuboQuoter.getVesuMultiplyQuote(
|
|
648
|
+
marginSwapQuote,
|
|
649
|
+
marginToken,
|
|
650
|
+
collateralToken
|
|
651
|
+
);
|
|
652
|
+
|
|
653
|
+
approveAmount = Web3Number
|
|
654
|
+
.fromWei(marginSwapQuote.total_calculated, marginToken.decimals)
|
|
655
|
+
.multipliedBy(1 + this.maxSlippage)
|
|
656
|
+
.abs();
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
let debtAmount = this._computeTargetDebtDelta(
|
|
660
|
+
addedCollateral,
|
|
661
|
+
existingCollateralInfo.amount,
|
|
662
|
+
existingDebtInfo.amount,
|
|
663
|
+
collateralPrice,
|
|
664
|
+
debtPrice,
|
|
665
|
+
legLTV,
|
|
666
|
+
dexPrice
|
|
882
667
|
);
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
const debtAmountInCollateralUnits = new Web3Number(
|
|
887
|
-
debtAmount
|
|
888
|
-
.multipliedBy(debtPrice)
|
|
889
|
-
.dividedBy(collateralPrice)
|
|
890
|
-
.multipliedBy(10 ** collateralToken.decimals)
|
|
891
|
-
.toFixed(0),
|
|
892
|
-
collateralToken.decimals
|
|
668
|
+
|
|
669
|
+
logger.verbose(
|
|
670
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata debtAmount: ${debtAmount}, addedCollateral: ${addedCollateral}`
|
|
893
671
|
);
|
|
672
|
+
let leverSwap: Swap[] = [];
|
|
673
|
+
let leverSwapLimitAmount = Web3Number.fromWei(0, debtToken.decimals);
|
|
894
674
|
|
|
895
|
-
// increase multiply lever or not
|
|
896
675
|
const isIncrease = debtAmount.greaterThanOrEqualTo(0);
|
|
897
|
-
|
|
898
|
-
// due to directional limitations in multiply contract
|
|
899
|
-
if (isIncrease && debtAmount.lessThan(0)) {
|
|
900
|
-
// we are increasing lever but math says reduce debt
|
|
901
|
-
// - this is ok
|
|
902
|
-
} else if (!isIncrease && debtAmount.greaterThan(0)) {
|
|
903
|
-
// we are decreasing level but math says increase debt
|
|
904
|
-
// - such actions must be done with zero margin amount
|
|
905
|
-
// - so just set debt 0
|
|
676
|
+
if (!isIncrease && debtAmount.greaterThan(0)) {
|
|
906
677
|
debtAmount = Web3Number.fromWei(0, this.config.debt.decimals);
|
|
907
678
|
}
|
|
908
|
-
logger.verbose(
|
|
909
|
-
`${VesuMultiplyAdapter.name}::getVesuMultiplyCall debtAmount: ${debtAmount}, marginAmount: ${marginAmount}`
|
|
910
|
-
);
|
|
911
|
-
if (!debtAmount.isZero()) {
|
|
912
|
-
// Get swap quote for leverage operation
|
|
913
|
-
// Determine swap direction based on operation type
|
|
914
679
|
|
|
680
|
+
if (!debtAmount.isZero() && debtAmount.greaterThan(0)) {
|
|
915
681
|
try {
|
|
916
|
-
|
|
917
|
-
|
|
682
|
+
let swapQuote: EkuboQuote;
|
|
683
|
+
const debtAmountInCollateralUnits = new Web3Number(
|
|
684
|
+
debtAmount
|
|
685
|
+
.multipliedBy(debtPrice)
|
|
686
|
+
.dividedBy(collateralPrice)
|
|
687
|
+
.toFixed(6),
|
|
688
|
+
collateralToken.decimals
|
|
689
|
+
);
|
|
690
|
+
if (params.leverSwap?.exactOutput) {
|
|
691
|
+
swapQuote = await ekuboQuoter.getQuoteExactOutput(
|
|
692
|
+
debtToken.address.address,
|
|
693
|
+
collateralToken.address.address,
|
|
694
|
+
params.leverSwap?.exactOutput?.abs()!
|
|
695
|
+
);
|
|
696
|
+
debtAmount = Web3Number.fromWei(swapQuote.total_calculated, debtToken.decimals).abs();
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
swapQuote = await ekuboQuoter.getQuoteExactInput(
|
|
918
700
|
debtToken.address.address,
|
|
919
|
-
|
|
701
|
+
collateralToken.address.address,
|
|
702
|
+
debtAmount.abs()
|
|
920
703
|
);
|
|
704
|
+
const expectedOutputAmount = debtAmount.multipliedBy(debtPrice).dividedBy(collateralPrice);
|
|
705
|
+
expectedOutputAmount.decimals = collateralToken.decimals;
|
|
706
|
+
leverSwapLimitAmount = expectedOutputAmount.multipliedBy(1 - this.maxSlippage);
|
|
921
707
|
|
|
922
|
-
// todo add better slip checks
|
|
923
|
-
// Check price impact
|
|
924
708
|
if (swapQuote.price_impact < 0.01) {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
//leverSwapLimitAmount = await ekuboQuoter.getSwapLimitAmount(collateralToken, debtToken, debtAmountInCollateralUnits.multipliedBy(-1), MAX_SLIPPAGE);
|
|
954
|
-
leverSwapLimitAmount = debtAmount
|
|
955
|
-
.abs()
|
|
956
|
-
.multipliedBy(1 - MAX_SLIPPAGE);
|
|
957
|
-
//console.log("anotherleverSwapLimitAmount", anotherleverSwapLimitAmount, leverSwapLimitAmount);
|
|
958
|
-
} else {
|
|
959
|
-
leverSwapLimitAmount = Web3Number.fromWei(
|
|
960
|
-
0,
|
|
961
|
-
this.config.debt.decimals
|
|
962
|
-
);
|
|
963
|
-
}
|
|
709
|
+
const quoteOutputAmount = Web3Number.fromWei(
|
|
710
|
+
swapQuote.total_calculated,
|
|
711
|
+
collateralToken.decimals
|
|
712
|
+
)
|
|
713
|
+
.abs()
|
|
714
|
+
.toNumber();
|
|
715
|
+
|
|
716
|
+
const inputAmt = debtAmount.abs().toNumber();
|
|
717
|
+
const outputAmt = quoteOutputAmount;
|
|
718
|
+
this.lastSwapPriceInfo = {
|
|
719
|
+
source: "ekubo",
|
|
720
|
+
fromTokenSymbol: debtToken.symbol,
|
|
721
|
+
toTokenSymbol: collateralToken.symbol,
|
|
722
|
+
fromAmount: inputAmt,
|
|
723
|
+
toAmount: outputAmt,
|
|
724
|
+
effectivePrice: outputAmt !== 0 ? inputAmt / outputAmt : 0,
|
|
725
|
+
};
|
|
726
|
+
logger.verbose(
|
|
727
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata stored price info: ` +
|
|
728
|
+
`${inputAmt} ${debtToken.symbol} → ${outputAmt} ${collateralToken.symbol}, ` +
|
|
729
|
+
`effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`
|
|
730
|
+
);
|
|
731
|
+
|
|
732
|
+
leverSwap = ekuboQuoter.getVesuMultiplyQuote(
|
|
733
|
+
swapQuote,
|
|
734
|
+
debtToken,
|
|
735
|
+
collateralToken
|
|
736
|
+
);
|
|
964
737
|
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
965
|
-
//console.log("leverSwapLimitAmount", leverSwapLimitAmount);
|
|
966
738
|
} else {
|
|
967
739
|
throw new Error(
|
|
968
740
|
`VesuMultiplyAdapter: Price impact too high (${swapQuote.price_impact}), skipping swap`
|
|
@@ -975,92 +747,225 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
975
747
|
}
|
|
976
748
|
}
|
|
977
749
|
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
750
|
+
logger.verbose(
|
|
751
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata leverSwapLimitAmount: ${leverSwapLimitAmount.toWei()}`
|
|
752
|
+
);
|
|
753
|
+
logger.verbose(
|
|
754
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata approveAmount: ${approveAmount.toWei()}, marginSwapLimitAmount: ${marginSwapLimitAmount.toWei()}`
|
|
983
755
|
);
|
|
756
|
+
logger.verbose(
|
|
757
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata marginSwap: ${JSON.stringify(marginSwap)}`
|
|
758
|
+
);
|
|
759
|
+
const multiplyParams: IncreaseLeverParams = {
|
|
760
|
+
user: this.config.vaultAllocator,
|
|
761
|
+
pool_id: this.config.poolId,
|
|
762
|
+
collateral_asset: collateralToken.address,
|
|
763
|
+
debt_asset: debtToken.address,
|
|
764
|
+
add_margin: params.marginSwap
|
|
765
|
+
? Web3Number.fromWei(0, collateralToken.decimals)
|
|
766
|
+
: params.amount,
|
|
767
|
+
margin_swap: marginSwap,
|
|
768
|
+
margin_swap_limit_amount: params.marginSwap ? approveAmount : marginSwapLimitAmount,
|
|
769
|
+
lever_swap: leverSwap,
|
|
770
|
+
lever_swap_limit_amount: leverSwapLimitAmount,
|
|
771
|
+
};
|
|
772
|
+
|
|
984
773
|
const call = multiplyContract.populate("modify_lever", {
|
|
985
|
-
modify_lever_params: this.formatMultiplyParams(
|
|
986
|
-
isIncrease,
|
|
987
|
-
multiplyParams
|
|
988
|
-
),
|
|
774
|
+
modify_lever_params: this.formatMultiplyParams(true, multiplyParams),
|
|
989
775
|
});
|
|
776
|
+
logger.debug(
|
|
777
|
+
`${VesuMultiplyAdapter.name}::_getIncreaseCalldata marginSwapCount=${marginSwap.length}, leverSwapCount=${leverSwap.length}`,
|
|
778
|
+
);
|
|
990
779
|
|
|
991
|
-
return
|
|
780
|
+
return {
|
|
781
|
+
calldata: call.calldata as bigint[],
|
|
782
|
+
approveToken: params.marginSwap?.marginToken ?? this.config.collateral,
|
|
783
|
+
approveAmount: approveAmount,
|
|
784
|
+
};
|
|
992
785
|
}
|
|
993
786
|
|
|
994
|
-
private async
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
787
|
+
private async _buildDecreaseLikeCalldata(params: {
|
|
788
|
+
subMargin: Web3Number;
|
|
789
|
+
debtToRepayAbs: Web3Number;
|
|
790
|
+
existingCollateral: Web3Number;
|
|
791
|
+
closePosition: boolean;
|
|
792
|
+
outputToken?: TokenInfo;
|
|
793
|
+
collateralPrice: number;
|
|
794
|
+
debtPrice: number;
|
|
795
|
+
}): Promise<bigint[]> {
|
|
796
|
+
const collateralToken = this.config.collateral;
|
|
797
|
+
const debtToken = this.config.debt;
|
|
798
|
+
const { contract: multiplyContract } = this._getMultiplyContract();
|
|
799
|
+
const ekuboQuoter = new EkuboQuoter(
|
|
800
|
+
this.config.networkConfig,
|
|
801
|
+
this.config.pricer
|
|
802
|
+
);
|
|
803
|
+
|
|
804
|
+
let leverSwap: Swap[] = [];
|
|
805
|
+
let leverSwapWeights: Web3Number[] = [];
|
|
806
|
+
let leverSwapLimitAmount = Web3Number.fromWei(0, collateralToken.decimals);
|
|
807
|
+
let leverCollateralUsed = Web3Number.fromWei(0, collateralToken.decimals);
|
|
808
|
+
|
|
809
|
+
if (params.closePosition) {
|
|
810
|
+
const debtQuote = await ekuboQuoter.getQuoteExactOutput(
|
|
811
|
+
collateralToken.address.address,
|
|
812
|
+
debtToken.address.address,
|
|
813
|
+
params.debtToRepayAbs
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
const built = this._buildZeroAmountSwapsWithWeights(
|
|
817
|
+
debtQuote,
|
|
818
|
+
debtToken,
|
|
819
|
+
true
|
|
820
|
+
);
|
|
821
|
+
leverSwap = built.swaps;
|
|
822
|
+
leverSwapWeights = built.weights;
|
|
823
|
+
leverCollateralUsed = Web3Number.fromWei(
|
|
824
|
+
debtQuote.total_calculated,
|
|
825
|
+
collateralToken.decimals
|
|
826
|
+
).abs();
|
|
827
|
+
leverSwapLimitAmount = leverCollateralUsed.multipliedBy(1 + this.maxSlippage);
|
|
828
|
+
} else {
|
|
829
|
+
if (params.collateralPrice === undefined || params.debtPrice === undefined) {
|
|
830
|
+
throw new Error(
|
|
831
|
+
"VesuMultiplyAdapter: Missing prices for non-close decrease calldata"
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
const collateralToSwap = new Web3Number(
|
|
835
|
+
params.debtToRepayAbs
|
|
836
|
+
.multipliedBy(params.debtPrice)
|
|
837
|
+
.dividedBy(params.collateralPrice)
|
|
838
|
+
.toFixed(collateralToken.decimals),
|
|
839
|
+
collateralToken.decimals
|
|
840
|
+
);
|
|
841
|
+
const leverSwapQuote = await ekuboQuoter.getQuoteExactInput(
|
|
842
|
+
collateralToken.address.address,
|
|
843
|
+
debtToken.address.address,
|
|
844
|
+
collateralToSwap
|
|
845
|
+
);
|
|
846
|
+
if (leverSwapQuote.price_impact < 0.0025) {
|
|
847
|
+
const inputAmt = collateralToSwap.toNumber();
|
|
848
|
+
const outputAmt = Web3Number.fromWei(
|
|
849
|
+
leverSwapQuote.total_calculated,
|
|
850
|
+
debtToken.decimals
|
|
851
|
+
)
|
|
852
|
+
.abs()
|
|
853
|
+
.toNumber();
|
|
854
|
+
this.lastSwapPriceInfo = {
|
|
855
|
+
source: "ekubo",
|
|
856
|
+
fromTokenSymbol: collateralToken.symbol,
|
|
857
|
+
toTokenSymbol: debtToken.symbol,
|
|
858
|
+
fromAmount: inputAmt,
|
|
859
|
+
toAmount: outputAmt,
|
|
860
|
+
effectivePrice: outputAmt !== 0 ? outputAmt / inputAmt : 0,
|
|
1033
861
|
};
|
|
1034
|
-
|
|
862
|
+
logger.verbose(
|
|
863
|
+
`${VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata stored price info: ` +
|
|
864
|
+
`${inputAmt} ${collateralToken.symbol} → ${outputAmt} ${debtToken.symbol}, ` +
|
|
865
|
+
`effectivePrice=${this.lastSwapPriceInfo.effectivePrice}`
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
leverSwap = ekuboQuoter.getVesuMultiplyQuote(
|
|
869
|
+
leverSwapQuote,
|
|
870
|
+
collateralToken,
|
|
871
|
+
debtToken
|
|
872
|
+
);
|
|
873
|
+
leverSwapLimitAmount = collateralToSwap
|
|
874
|
+
.multipliedBy(params.collateralPrice)
|
|
875
|
+
.dividedBy(params.debtPrice)
|
|
876
|
+
.multipliedBy(1 - this.maxSlippage);
|
|
877
|
+
leverSwapLimitAmount.decimals = debtToken.decimals;
|
|
878
|
+
} else {
|
|
879
|
+
throw new Error(`VesuMultiplyAdapter: Lever swap price impact too high (${leverSwapQuote.price_impact})`);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
leverCollateralUsed = Web3Number.fromWei(
|
|
883
|
+
leverSwapQuote.total_calculated,
|
|
884
|
+
collateralToken.decimals
|
|
885
|
+
).abs();
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
let withdrawSwap: Swap[] = [];
|
|
889
|
+
let withdrawSwapLimitAmount = Web3Number.fromWei(
|
|
890
|
+
0,
|
|
891
|
+
params.outputToken?.decimals ?? collateralToken.decimals
|
|
892
|
+
);
|
|
893
|
+
const withdrawSwapWeights: Web3Number[] = [];
|
|
894
|
+
|
|
895
|
+
if (params.outputToken && !params.outputToken.address.eq(collateralToken.address)) {
|
|
896
|
+
const residualCollateral = params.closePosition
|
|
897
|
+
? params.existingCollateral.minus(leverCollateralUsed)
|
|
898
|
+
: params.subMargin;
|
|
899
|
+
const outputTokenPrice = await this.config.pricer.getPrice(params.outputToken.symbol);
|
|
900
|
+
if (residualCollateral.greaterThan(0)) {
|
|
901
|
+
await new Promise((r) => setTimeout(r, 10000));
|
|
902
|
+
const withdrawQuote = await ekuboQuoter.getQuoteExactInput(
|
|
903
|
+
collateralToken.address.address,
|
|
904
|
+
params.outputToken.address.address,
|
|
905
|
+
residualCollateral
|
|
906
|
+
);
|
|
907
|
+
if (withdrawQuote.price_impact < 0.0025) {
|
|
908
|
+
const built = this._buildZeroAmountSwapsWithWeights(
|
|
909
|
+
withdrawQuote,
|
|
910
|
+
collateralToken
|
|
911
|
+
);
|
|
912
|
+
withdrawSwap = built.swaps;
|
|
913
|
+
withdrawSwapWeights.push(...built.weights);
|
|
914
|
+
const estimatedOutput = residualCollateral
|
|
915
|
+
.multipliedBy(params.collateralPrice)
|
|
916
|
+
.dividedBy(outputTokenPrice.price);
|
|
917
|
+
estimatedOutput.decimals = params.outputToken.decimals;
|
|
918
|
+
withdrawSwapLimitAmount = estimatedOutput
|
|
919
|
+
.multipliedBy(1 - this.maxSlippage);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
logger.debug(
|
|
925
|
+
`${VesuMultiplyAdapter.name}::_buildDecreaseLikeCalldata leverSwapCount=${leverSwap.length}, withdrawSwapCount=${withdrawSwap.length}, withdrawSwapLimitAmount=${withdrawSwapLimitAmount.toNumber()}, subMargin=${params.subMargin.toNumber()}`,
|
|
926
|
+
);
|
|
927
|
+
const multiplyParams: DecreaseLeverParams = {
|
|
928
|
+
user: this.config.vaultAllocator,
|
|
929
|
+
pool_id: this.config.poolId,
|
|
930
|
+
collateral_asset: collateralToken.address,
|
|
931
|
+
debt_asset: debtToken.address,
|
|
932
|
+
recipient: this.config.vaultAllocator,
|
|
933
|
+
sub_margin: params.closePosition
|
|
934
|
+
? Web3Number.fromWei(0, collateralToken.decimals)
|
|
935
|
+
: params.subMargin,
|
|
936
|
+
lever_swap: leverSwap,
|
|
937
|
+
lever_swap_limit_amount: leverSwapLimitAmount,
|
|
938
|
+
lever_swap_weights: leverSwapWeights,
|
|
939
|
+
withdraw_swap: withdrawSwap,
|
|
940
|
+
withdraw_swap_limit_amount: withdrawSwapLimitAmount,
|
|
941
|
+
withdraw_swap_weights: withdrawSwapWeights,
|
|
942
|
+
close_position: params.closePosition,
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
const call = multiplyContract.populate("modify_lever", {
|
|
946
|
+
modify_lever_params: this.formatMultiplyParams(false, multiplyParams),
|
|
947
|
+
});
|
|
948
|
+
return call.calldata as bigint[];
|
|
1035
949
|
}
|
|
1036
950
|
|
|
1037
|
-
private async
|
|
1038
|
-
params:
|
|
951
|
+
private async _getDecreaseCalldata(
|
|
952
|
+
params: VesuWithdrawParams
|
|
1039
953
|
): Promise<bigint[]> {
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
const
|
|
1046
|
-
abi: VesuMultiplyAbi,
|
|
1047
|
-
address: vesuMultiply.address,
|
|
1048
|
-
providerOrAccount: this.config.networkConfig.provider,
|
|
1049
|
-
});
|
|
1050
|
-
let leverSwap: Swap[] = [];
|
|
1051
|
-
let leverSwapLimitAmount = Web3Number.fromWei(0, this.config.debt.decimals);
|
|
1052
|
-
const existingPositions = await this.vesuAdapter.getPositions(
|
|
954
|
+
const collateralToken = this.config.collateral;
|
|
955
|
+
const debtToken = this.config.debt;
|
|
956
|
+
|
|
957
|
+
this._vesuAdapter.networkConfig = this.config.networkConfig;
|
|
958
|
+
this._vesuAdapter.pricer = this.config.pricer;
|
|
959
|
+
const existingPositions = await this._vesuAdapter.getPositions(
|
|
1053
960
|
this.config.networkConfig
|
|
1054
961
|
);
|
|
1055
962
|
const existingCollateralInfo = existingPositions[0];
|
|
1056
963
|
const existingDebtInfo = existingPositions[1];
|
|
1057
|
-
const collateralToken = this.config.collateral;
|
|
1058
|
-
const debtToken = this.config.debt;
|
|
1059
964
|
const collateralPrice = await this.config.pricer.getPrice(
|
|
1060
965
|
collateralToken.symbol
|
|
1061
966
|
);
|
|
1062
967
|
const debtPrice = await this.config.pricer.getPrice(debtToken.symbol);
|
|
1063
|
-
|
|
968
|
+
|
|
1064
969
|
const { deltadebtAmountUnits: debtAmountToRepay } =
|
|
1065
970
|
calculateDebtReductionAmountForWithdrawal(
|
|
1066
971
|
existingDebtInfo.amount,
|
|
@@ -1071,54 +976,70 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
1071
976
|
debtPrice.price,
|
|
1072
977
|
debtToken.decimals
|
|
1073
978
|
);
|
|
1074
|
-
//console.log("debtAmountToRepay", debtAmountToRepay);
|
|
1075
979
|
if (!debtAmountToRepay) {
|
|
1076
980
|
throw new Error("error calculating debt amount to repay");
|
|
1077
981
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
this.config.pricer
|
|
1081
|
-
);
|
|
1082
|
-
const debtInDebtUnits = new Web3Number(
|
|
982
|
+
|
|
983
|
+
const debtToRepayAbs = new Web3Number(
|
|
1083
984
|
debtAmountToRepay,
|
|
1084
985
|
debtToken.decimals
|
|
1085
|
-
)
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
const
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
debtToken
|
|
986
|
+
).abs();
|
|
987
|
+
const existingDebtAbs = existingDebtInfo.amount.abs();
|
|
988
|
+
|
|
989
|
+
const shouldCloseByDebt = debtToRepayAbs.greaterThanOrEqualTo(existingDebtAbs);
|
|
990
|
+
const remainingDebtUSD = existingDebtAbs
|
|
991
|
+
.minus(debtToRepayAbs)
|
|
992
|
+
.multipliedBy(debtPrice.price)
|
|
993
|
+
.toNumber();
|
|
994
|
+
const shouldCloseByDust = remainingDebtUSD < MIN_REMAINING_DEBT_USD;
|
|
995
|
+
|
|
996
|
+
if (shouldCloseByDebt) {
|
|
997
|
+
logger.info(
|
|
998
|
+
`${VesuMultiplyAdapter.name}::_getDecreaseCalldata debt to repay (${debtToRepayAbs.toNumber()}) >= existing debt (${existingDebtAbs.toNumber()}), auto-closing position`
|
|
1099
999
|
);
|
|
1100
|
-
} else {
|
|
1101
|
-
logger.
|
|
1102
|
-
|
|
1000
|
+
} else if (shouldCloseByDust) {
|
|
1001
|
+
logger.info(
|
|
1002
|
+
`${VesuMultiplyAdapter.name}::_getDecreaseCalldata remaining debt $${remainingDebtUSD.toFixed(2)} < $${MIN_REMAINING_DEBT_USD}, auto-closing position`
|
|
1103
1003
|
);
|
|
1104
1004
|
}
|
|
1105
1005
|
|
|
1106
|
-
|
|
1107
|
-
.
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
leverSwapLimitAmount
|
|
1115
|
-
);
|
|
1116
|
-
const call = multiplyContract.populate("modify_lever", {
|
|
1117
|
-
modify_lever_params: this.formatMultiplyParams(false, multiplyParams),
|
|
1006
|
+
return this._buildDecreaseLikeCalldata({
|
|
1007
|
+
subMargin: params.amount,
|
|
1008
|
+
debtToRepayAbs: shouldCloseByDebt || shouldCloseByDust ? existingDebtAbs : debtToRepayAbs,
|
|
1009
|
+
existingCollateral: existingCollateralInfo.amount,
|
|
1010
|
+
closePosition: shouldCloseByDebt || shouldCloseByDust,
|
|
1011
|
+
outputToken: params.withdrawSwap?.outputToken,
|
|
1012
|
+
collateralPrice: collateralPrice.price,
|
|
1013
|
+
debtPrice: debtPrice.price,
|
|
1118
1014
|
});
|
|
1119
|
-
return call.calldata as bigint[];
|
|
1120
1015
|
}
|
|
1121
1016
|
|
|
1017
|
+
// private async _getClosePositionCalldata(
|
|
1018
|
+
// outputToken?: TokenInfo
|
|
1019
|
+
// ): Promise<bigint[]> {
|
|
1020
|
+
// this._vesuAdapter.networkConfig = this.config.networkConfig;
|
|
1021
|
+
// this._vesuAdapter.pricer = this.config.pricer;
|
|
1022
|
+
// const existingPositions = await this._vesuAdapter.getPositions(
|
|
1023
|
+
// this.config.networkConfig
|
|
1024
|
+
// );
|
|
1025
|
+
// const existingCollateralInfo = existingPositions[0];
|
|
1026
|
+
// const existingDebtInfo = existingPositions[1];
|
|
1027
|
+
|
|
1028
|
+
// if (existingDebtInfo.amount.isZero()) {
|
|
1029
|
+
// throw new Error("VesuMultiplyAdapter: No debt to close");
|
|
1030
|
+
// }
|
|
1031
|
+
|
|
1032
|
+
// return this._buildDecreaseLikeCalldata({
|
|
1033
|
+
// subMargin: Web3Number.fromWei(0, this.config.collateral.decimals),
|
|
1034
|
+
// debtToRepayAbs: existingDebtInfo.amount.abs(),
|
|
1035
|
+
// existingCollateral: existingCollateralInfo.amount,
|
|
1036
|
+
// closePosition: true,
|
|
1037
|
+
// outputToken,
|
|
1038
|
+
// });
|
|
1039
|
+
// }
|
|
1040
|
+
|
|
1041
|
+
// ─── Param Formatting ─────────────────────────────────────────────────────
|
|
1042
|
+
|
|
1122
1043
|
formatMultiplyParams(
|
|
1123
1044
|
isIncrease: boolean,
|
|
1124
1045
|
params: IncreaseLeverParams | DecreaseLeverParams
|
|
@@ -1256,8 +1177,10 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
1256
1177
|
};
|
|
1257
1178
|
}
|
|
1258
1179
|
|
|
1180
|
+
// ─── Health Factor / Net APY ───────────────────────────────────────────────
|
|
1181
|
+
|
|
1259
1182
|
async getHealthFactor(): Promise<number> {
|
|
1260
|
-
const healthFactor = await this.
|
|
1183
|
+
const healthFactor = await this._vesuAdapter.getHealthFactor();
|
|
1261
1184
|
return healthFactor;
|
|
1262
1185
|
}
|
|
1263
1186
|
|
|
@@ -1268,26 +1191,22 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
1268
1191
|
);
|
|
1269
1192
|
const allZero = positions.every((p) => p.usdValue === 0);
|
|
1270
1193
|
|
|
1271
|
-
// in case of zero positions, apy will come zero/NaN
|
|
1272
|
-
// bcz of net 0 zero weights
|
|
1273
1194
|
if (allZero) {
|
|
1274
|
-
// use approx dummy usd values to compute netAPY
|
|
1275
1195
|
const collateralUSD = 1000;
|
|
1276
|
-
const maxLTV = await this.
|
|
1196
|
+
const maxLTV = await this._vesuAdapter.getLTVConfig(
|
|
1277
1197
|
this.config.networkConfig
|
|
1278
1198
|
);
|
|
1279
1199
|
const targetHF = this.config.targetHealthFactor;
|
|
1280
1200
|
const maxDebt = HealthFactorMath.getMaxDebtAmountOnLooping(
|
|
1281
1201
|
new Web3Number(collateralUSD, this.config.collateral.decimals),
|
|
1282
|
-
1,
|
|
1202
|
+
1,
|
|
1283
1203
|
maxLTV,
|
|
1284
1204
|
targetHF,
|
|
1285
|
-
1,
|
|
1205
|
+
1,
|
|
1286
1206
|
this.config.debt
|
|
1287
1207
|
);
|
|
1288
1208
|
|
|
1289
|
-
|
|
1290
|
-
const debtUSD = maxDebt.multipliedBy(1); // assume price 1 for simplicity
|
|
1209
|
+
const debtUSD = maxDebt.multipliedBy(1);
|
|
1291
1210
|
const netAPY =
|
|
1292
1211
|
(positions[0].apy.apy * (collateralUSD + debtUSD.toNumber()) +
|
|
1293
1212
|
positions[1].apy.apy * debtUSD.toNumber()) /
|
|
@@ -1295,7 +1214,6 @@ export class VesuMultiplyAdapter extends BaseAdapter<
|
|
|
1295
1214
|
return netAPY;
|
|
1296
1215
|
}
|
|
1297
1216
|
|
|
1298
|
-
// Return true APY
|
|
1299
1217
|
const netAmount = positions.reduce((acc, curr) => acc + curr.usdValue, 0);
|
|
1300
1218
|
const netAPY =
|
|
1301
1219
|
positions.reduce((acc, curr) => acc + curr.apy.apy * curr.usdValue, 0) /
|