@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
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
|
+
import { IConfig, IProtocol, TokenInfo } from "@/interfaces";
|
|
3
|
+
import { TokenMarketData } from "@/modules";
|
|
4
|
+
import { logger } from "@/utils";
|
|
5
|
+
import { HealthFactorMath } from "@/utils/health-factor-math";
|
|
6
|
+
import { num } from "starknet";
|
|
7
|
+
import {
|
|
8
|
+
APYType,
|
|
9
|
+
PositionAPY,
|
|
10
|
+
PositionAmount,
|
|
11
|
+
PositionInfo,
|
|
12
|
+
SupportedPosition,
|
|
13
|
+
} from "./baseAdapter";
|
|
14
|
+
import { VesuAdapter } from "./vesu-adapter";
|
|
15
|
+
|
|
16
|
+
export type VesuPositionCommonContext = {
|
|
17
|
+
adapterName: string;
|
|
18
|
+
protocol: IProtocol;
|
|
19
|
+
poolId: ContractAddr;
|
|
20
|
+
collateral: TokenInfo;
|
|
21
|
+
debt: TokenInfo;
|
|
22
|
+
networkConfig: IConfig;
|
|
23
|
+
pricer: {
|
|
24
|
+
getPrice: (symbol: string) => Promise<{ price: number }>;
|
|
25
|
+
};
|
|
26
|
+
vesuAdapter: VesuAdapter;
|
|
27
|
+
tokenMarketData: TokenMarketData;
|
|
28
|
+
targetHealthFactor: number;
|
|
29
|
+
getCache: <T>(key: string) => T | null | undefined;
|
|
30
|
+
setCache: <T>(key: string, value: T, ttlMs: number) => void;
|
|
31
|
+
getUSDValue: (asset: TokenInfo, amount: Web3Number) => Promise<number>;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export async function getVesuCommonAPY(
|
|
35
|
+
ctx: VesuPositionCommonContext,
|
|
36
|
+
supportedPosition: SupportedPosition,
|
|
37
|
+
): Promise<PositionAPY> {
|
|
38
|
+
const cacheKey = `apy_${ctx.poolId.address}_${supportedPosition.asset.symbol}`;
|
|
39
|
+
const cacheData = ctx.getCache<PositionAPY>(cacheKey);
|
|
40
|
+
if (cacheData) {
|
|
41
|
+
return cacheData;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const allVesuPools = await VesuAdapter.getVesuPools();
|
|
46
|
+
const asset = supportedPosition.asset;
|
|
47
|
+
const pool = allVesuPools.pools.find((p) =>
|
|
48
|
+
ctx.poolId.eqString(num.getHexString(p.id)),
|
|
49
|
+
);
|
|
50
|
+
if (!pool) {
|
|
51
|
+
logger.warn(`${ctx.adapterName}: Pool not found for token ${asset.symbol}`);
|
|
52
|
+
return { apy: 0, type: APYType.BASE };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const assetStats = pool.assets.find(
|
|
56
|
+
(a: any) => a.symbol.toLowerCase() === asset.symbol.toLowerCase(),
|
|
57
|
+
)?.stats;
|
|
58
|
+
if (!assetStats) {
|
|
59
|
+
logger.warn(`${ctx.adapterName}: Asset stats not found for token ${asset.symbol}`);
|
|
60
|
+
return { apy: 0, type: APYType.BASE };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let apy = 0;
|
|
64
|
+
if (supportedPosition.isDebt) {
|
|
65
|
+
apy = Number(assetStats.borrowApr?.value || 0) / 1e18;
|
|
66
|
+
} else {
|
|
67
|
+
const isAssetBTC = asset.symbol.toLowerCase().includes("btc");
|
|
68
|
+
const baseAPY =
|
|
69
|
+
Number(
|
|
70
|
+
isAssetBTC
|
|
71
|
+
? assetStats.btcFiSupplyApr?.value + assetStats.supplyApy?.value
|
|
72
|
+
: assetStats.supplyApy?.value || 0,
|
|
73
|
+
) / 1e18;
|
|
74
|
+
const rewardAPY = Number(assetStats.defiSpringSupplyApr?.value || "0") / 1e18;
|
|
75
|
+
apy = baseAPY + rewardAPY;
|
|
76
|
+
if (ctx.tokenMarketData.isAPYSupported(asset)) {
|
|
77
|
+
apy += await ctx.tokenMarketData.getAPY(asset);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = { apy, type: APYType.BASE };
|
|
82
|
+
ctx.setCache(cacheKey, result, 300000);
|
|
83
|
+
return result;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
logger.error(`${ctx.adapterName}: Error getting APY for ${supportedPosition.asset.symbol}:`, error);
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function getVesuCommonPosition(
|
|
91
|
+
ctx: VesuPositionCommonContext,
|
|
92
|
+
supportedPosition: SupportedPosition,
|
|
93
|
+
): Promise<PositionAmount> {
|
|
94
|
+
const cacheKey = `position_${ctx.poolId.address}_${supportedPosition.asset.symbol}`;
|
|
95
|
+
const cacheData = ctx.getCache<PositionAmount>(cacheKey);
|
|
96
|
+
if (cacheData) {
|
|
97
|
+
return cacheData;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
ctx.vesuAdapter.networkConfig = ctx.networkConfig;
|
|
102
|
+
ctx.vesuAdapter.pricer = ctx.pricer as any;
|
|
103
|
+
const positions = await ctx.vesuAdapter.getPositions(ctx.networkConfig);
|
|
104
|
+
let position = positions.find((p) => p.token.address.eq(supportedPosition.asset.address));
|
|
105
|
+
if (!position) {
|
|
106
|
+
logger.warn(`${ctx.adapterName}: Position not found for token ${supportedPosition.asset.symbol}`);
|
|
107
|
+
return {
|
|
108
|
+
amount: new Web3Number("0", supportedPosition.asset.decimals),
|
|
109
|
+
remarks: "Position not found",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (supportedPosition.isDebt) {
|
|
113
|
+
position.amount = position.amount.multipliedBy(-1);
|
|
114
|
+
position.usdValue = position.usdValue * -1;
|
|
115
|
+
}
|
|
116
|
+
const result = { amount: position.amount, remarks: supportedPosition.isDebt ? "Vesu debt" : "Vesu collateral" };
|
|
117
|
+
ctx.setCache(cacheKey, result, 60000);
|
|
118
|
+
return result;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
logger.error(`${ctx.adapterName}: Error getting position for ${supportedPosition.asset.symbol}:`, error);
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export async function getVesuCommonMaxBorrowableAPY(
|
|
126
|
+
ctx: VesuPositionCommonContext,
|
|
127
|
+
): Promise<number> {
|
|
128
|
+
const collateralAPY = await getVesuCommonAPY(ctx, {
|
|
129
|
+
asset: ctx.collateral,
|
|
130
|
+
isDebt: false,
|
|
131
|
+
});
|
|
132
|
+
return collateralAPY.apy * 0.8;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export async function getVesuCommonMaxDeposit(
|
|
136
|
+
ctx: VesuPositionCommonContext,
|
|
137
|
+
amount?: Web3Number,
|
|
138
|
+
): Promise<PositionInfo> {
|
|
139
|
+
const collateral = ctx.collateral;
|
|
140
|
+
const debt = ctx.debt;
|
|
141
|
+
try {
|
|
142
|
+
ctx.vesuAdapter.networkConfig = ctx.networkConfig;
|
|
143
|
+
ctx.vesuAdapter.pricer = ctx.pricer as any;
|
|
144
|
+
|
|
145
|
+
const positions = await ctx.vesuAdapter.getPositions(ctx.networkConfig);
|
|
146
|
+
const collateralPosition = positions.find((p) => p.token.address.eq(collateral.address));
|
|
147
|
+
const debtPosition = positions.find((p) => p.token.address.eq(debt.address));
|
|
148
|
+
if (!collateralPosition || !debtPosition) {
|
|
149
|
+
throw new Error("Could not find current positions");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const maxBorrowableAPY = await getVesuCommonMaxBorrowableAPY(ctx);
|
|
153
|
+
const maxBorrowableResult = await ctx.vesuAdapter.getMaxBorrowableByInterestRate(
|
|
154
|
+
ctx.networkConfig,
|
|
155
|
+
debt,
|
|
156
|
+
maxBorrowableAPY,
|
|
157
|
+
);
|
|
158
|
+
const maxBorrowable = maxBorrowableResult.maxDebtToHave;
|
|
159
|
+
const debtCap = await ctx.vesuAdapter.getDebtCap(ctx.networkConfig);
|
|
160
|
+
const actualMaxBorrowable = maxBorrowable.minimum(debtCap);
|
|
161
|
+
const maxLTV = await ctx.vesuAdapter.getLTVConfig(ctx.networkConfig);
|
|
162
|
+
|
|
163
|
+
const collateralPrice = await ctx.pricer.getPrice(collateral.symbol);
|
|
164
|
+
if (collateralPrice.price === 0) {
|
|
165
|
+
throw new Error("Collateral price is 0");
|
|
166
|
+
}
|
|
167
|
+
const debtPrice = await ctx.pricer.getPrice(debt.symbol);
|
|
168
|
+
if (debtPrice.price === 0) {
|
|
169
|
+
throw new Error("Debt price is 0");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const maxCollateralFromDebt = HealthFactorMath.getMinCollateralRequiredOnLooping(
|
|
173
|
+
actualMaxBorrowable,
|
|
174
|
+
debtPrice.price,
|
|
175
|
+
ctx.targetHealthFactor,
|
|
176
|
+
maxLTV,
|
|
177
|
+
collateralPrice.price,
|
|
178
|
+
collateral,
|
|
179
|
+
);
|
|
180
|
+
const maxDepositAmount = amount ? amount.minimum(maxCollateralFromDebt) : maxCollateralFromDebt;
|
|
181
|
+
const usdValue = await ctx.getUSDValue(collateral, maxDepositAmount);
|
|
182
|
+
const apys = await Promise.all([
|
|
183
|
+
getVesuCommonAPY(ctx, { asset: collateral, isDebt: false }),
|
|
184
|
+
getVesuCommonAPY(ctx, { asset: debt, isDebt: true }),
|
|
185
|
+
]);
|
|
186
|
+
|
|
187
|
+
const borrowAmountUSD = actualMaxBorrowable.multipliedBy(debtPrice.price);
|
|
188
|
+
const netCollateralUSD = usdValue + borrowAmountUSD.toNumber();
|
|
189
|
+
const netAPY =
|
|
190
|
+
(apys[0].apy * netCollateralUSD + apys[1].apy * borrowAmountUSD.toNumber()) /
|
|
191
|
+
usdValue;
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
tokenInfo: collateral,
|
|
195
|
+
amount: maxDepositAmount,
|
|
196
|
+
usdValue,
|
|
197
|
+
remarks: "Max deposit based on available debt capacity",
|
|
198
|
+
apy: { apy: netAPY, type: APYType.BASE },
|
|
199
|
+
protocol: ctx.protocol,
|
|
200
|
+
};
|
|
201
|
+
} catch (error) {
|
|
202
|
+
logger.error(`${ctx.adapterName}: Error calculating max deposit:`, error);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function getVesuCommonMaxWithdraw(
|
|
208
|
+
ctx: VesuPositionCommonContext,
|
|
209
|
+
): Promise<PositionInfo> {
|
|
210
|
+
const collateral = ctx.collateral;
|
|
211
|
+
const debt = ctx.debt;
|
|
212
|
+
try {
|
|
213
|
+
ctx.vesuAdapter.networkConfig = ctx.networkConfig;
|
|
214
|
+
ctx.vesuAdapter.pricer = ctx.pricer as any;
|
|
215
|
+
const positions = await ctx.vesuAdapter.getPositions(ctx.networkConfig);
|
|
216
|
+
const collateralPosition = positions.find((p) => p.token.address.eq(collateral.address));
|
|
217
|
+
const debtPosition = positions.find((p) => p.token.address.eq(debt.address));
|
|
218
|
+
if (!collateralPosition || !debtPosition) {
|
|
219
|
+
throw new Error("Could not find current positions");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const collateralPrice = collateralPosition.usdValue / collateralPosition.amount.toNumber();
|
|
223
|
+
const debtInCollateral = debtPosition.usdValue / collateralPrice;
|
|
224
|
+
const maxWithdrawable = collateralPosition.amount.minus(debtInCollateral);
|
|
225
|
+
const result = maxWithdrawable.greaterThan(0)
|
|
226
|
+
? maxWithdrawable
|
|
227
|
+
: new Web3Number("0", collateral.decimals);
|
|
228
|
+
const usdValue = await ctx.getUSDValue(collateral, result);
|
|
229
|
+
const debtUSD = debtPosition.usdValue;
|
|
230
|
+
const apys = await Promise.all([
|
|
231
|
+
getVesuCommonAPY(ctx, { asset: collateral, isDebt: false }),
|
|
232
|
+
getVesuCommonAPY(ctx, { asset: debt, isDebt: true }),
|
|
233
|
+
]);
|
|
234
|
+
const netAPY =
|
|
235
|
+
usdValue - debtUSD > 0
|
|
236
|
+
? (apys[0].apy * usdValue + apys[1].apy * debtUSD) / (usdValue - debtUSD)
|
|
237
|
+
: 0;
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
tokenInfo: collateral,
|
|
241
|
+
amount: result,
|
|
242
|
+
usdValue,
|
|
243
|
+
remarks: "Max withdraw based on health factor",
|
|
244
|
+
apy: { apy: netAPY, type: APYType.BASE },
|
|
245
|
+
protocol: ctx.protocol,
|
|
246
|
+
};
|
|
247
|
+
} catch (error) {
|
|
248
|
+
logger.error(`${ctx.adapterName}: Error calculating max withdraw:`, error);
|
|
249
|
+
throw error;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -24,6 +24,18 @@ export class VesuSupplyOnlyAdapter extends BaseAdapter<DepositParams, WithdrawPa
|
|
|
24
24
|
this.tokenMarketData = new TokenMarketData(this.config.pricer, this.config.networkConfig);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
private _depositApproveProofReadableId(): string {
|
|
28
|
+
return `appr_dep_vtkn_${this.config.vTokenContract.shortString()}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private _depositCallProofReadableId(): string {
|
|
32
|
+
return `deposit_vtoken_${this.config.vTokenContract.shortString()}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private _withdrawCallProofReadableId(): string {
|
|
36
|
+
return `withdraw_vtoken_${this.config.vTokenContract.shortString()}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
27
39
|
protected async getAPY(supportedPosition: SupportedPosition): Promise<PositionAPY> {
|
|
28
40
|
const CACHE_KEY = `apy_${this.config.vTokenContract.address}`;
|
|
29
41
|
const cacheData = this.getCache<PositionAPY>(CACHE_KEY);
|
|
@@ -193,7 +205,7 @@ export class VesuSupplyOnlyAdapter extends BaseAdapter<DepositParams, WithdrawPa
|
|
|
193
205
|
vTokenContract.toBigInt(), // spender
|
|
194
206
|
],
|
|
195
207
|
sanitizer: SIMPLE_SANITIZER,
|
|
196
|
-
id:
|
|
208
|
+
id: this._depositApproveProofReadableId()
|
|
197
209
|
},
|
|
198
210
|
// Deposit step
|
|
199
211
|
{
|
|
@@ -203,7 +215,7 @@ export class VesuSupplyOnlyAdapter extends BaseAdapter<DepositParams, WithdrawPa
|
|
|
203
215
|
this.config.vaultAllocator.toBigInt(),
|
|
204
216
|
],
|
|
205
217
|
sanitizer: SIMPLE_SANITIZER,
|
|
206
|
-
id:
|
|
218
|
+
id: this._depositCallProofReadableId()
|
|
207
219
|
}
|
|
208
220
|
];
|
|
209
221
|
}
|
|
@@ -227,7 +239,7 @@ export class VesuSupplyOnlyAdapter extends BaseAdapter<DepositParams, WithdrawPa
|
|
|
227
239
|
this.config.vaultAllocator.toBigInt(),
|
|
228
240
|
],
|
|
229
241
|
sanitizer: SIMPLE_SANITIZER,
|
|
230
|
-
id:
|
|
242
|
+
id: this._withdrawCallProofReadableId()
|
|
231
243
|
}
|
|
232
244
|
];
|
|
233
245
|
}
|
|
@@ -272,6 +284,7 @@ export class VesuSupplyOnlyAdapter extends BaseAdapter<DepositParams, WithdrawPa
|
|
|
272
284
|
return [
|
|
273
285
|
// Approval call
|
|
274
286
|
{
|
|
287
|
+
proofReadableId: this._depositApproveProofReadableId(),
|
|
275
288
|
sanitizer: SIMPLE_SANITIZER,
|
|
276
289
|
call: {
|
|
277
290
|
contractAddress: baseToken.address,
|
|
@@ -285,6 +298,7 @@ export class VesuSupplyOnlyAdapter extends BaseAdapter<DepositParams, WithdrawPa
|
|
|
285
298
|
},
|
|
286
299
|
// Deposit call
|
|
287
300
|
{
|
|
301
|
+
proofReadableId: this._depositCallProofReadableId(),
|
|
288
302
|
sanitizer: SIMPLE_SANITIZER,
|
|
289
303
|
call: {
|
|
290
304
|
contractAddress: vTokenContract,
|
|
@@ -308,6 +322,7 @@ export class VesuSupplyOnlyAdapter extends BaseAdapter<DepositParams, WithdrawPa
|
|
|
308
322
|
return [
|
|
309
323
|
// Withdraw call
|
|
310
324
|
{
|
|
325
|
+
proofReadableId: this._withdrawCallProofReadableId(),
|
|
311
326
|
sanitizer: SIMPLE_SANITIZER,
|
|
312
327
|
call: {
|
|
313
328
|
contractAddress: vTokenContract,
|