@strkfarm/sdk 1.0.17 → 1.0.19
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 +276 -90
- package/dist/cli.mjs +270 -84
- package/dist/index.browser.global.js +27175 -18059
- package/dist/index.browser.mjs +8078 -1370
- package/dist/index.d.ts +211 -25
- package/dist/index.js +7773 -1035
- package/dist/index.mjs +8146 -1417
- package/package.json +4 -1
- package/src/data/cl-vault.abi.json +1434 -0
- package/src/data/ekubo-math.abi.json +333 -0
- package/src/data/ekubo-positions.abi.json +1594 -0
- package/src/data/erc20.abi.json +1122 -0
- package/src/data/erc4626.abi.json +1530 -0
- package/src/dataTypes/_bignumber.ts +53 -0
- package/src/dataTypes/address.ts +4 -0
- package/src/dataTypes/bignumber.browser.ts +8 -0
- package/src/dataTypes/bignumber.node.ts +22 -0
- package/src/dataTypes/bignumber.ts +1 -55
- package/src/dataTypes/index.ts +1 -1
- package/src/global.ts +54 -4
- package/src/interfaces/common.ts +64 -10
- package/src/interfaces/lending.ts +1 -1
- package/src/modules/avnu.ts +93 -0
- package/src/modules/erc20.ts +23 -0
- package/src/modules/index.ts +2 -0
- package/src/modules/pricer.ts +1 -1
- package/src/modules/zkLend.ts +2 -2
- package/src/node/headless.browser.ts +9 -0
- package/src/node/headless.node.ts +36 -0
- package/src/node/headless.ts +1 -0
- package/src/node/index.ts +2 -1
- package/src/strategies/base-strategy.ts +47 -0
- package/src/strategies/ekubo-cl-vault.ts +535 -0
- package/src/strategies/index.ts +2 -1
- package/src/strategies/vesu-rebalance.ts +123 -35
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
|
-
import { IConfig, IInvestmentFlow, IProtocol, IStrategyMetadata, RiskFactor, RiskType } from "@/interfaces";
|
|
2
|
+
import { FlowChartColors, IConfig, IInvestmentFlow, IProtocol, IStrategyMetadata, RiskFactor, RiskType } from "@/interfaces";
|
|
3
3
|
import { Pricer } from "@/modules";
|
|
4
4
|
import { CairoCustomEnum, Contract, num, uint256 } from "starknet";
|
|
5
5
|
import VesuRebalanceAbi from '@/data/vesu-rebalance.abi.json';
|
|
@@ -7,6 +7,8 @@ import { Global } from "@/global";
|
|
|
7
7
|
import { assert } from "@/utils";
|
|
8
8
|
import axios from "axios";
|
|
9
9
|
import { PricerBase } from "@/modules/pricerBase";
|
|
10
|
+
import { BaseStrategy, SingleActionAmount, SingleTokenInfo } from "./base-strategy";
|
|
11
|
+
import { getAPIUsingHeadlessBrowser } from "@/node/headless";
|
|
10
12
|
|
|
11
13
|
interface PoolProps {
|
|
12
14
|
pool_id: ContractAddr;
|
|
@@ -21,6 +23,10 @@ interface Change {
|
|
|
21
23
|
isDeposit: boolean;
|
|
22
24
|
}
|
|
23
25
|
|
|
26
|
+
interface VesuRebalanceSettings {
|
|
27
|
+
feeBps: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
24
30
|
interface PoolInfoFull {
|
|
25
31
|
pool_id: ContractAddr;
|
|
26
32
|
pool_name: string | undefined;
|
|
@@ -42,15 +48,13 @@ interface PoolInfoFull {
|
|
|
42
48
|
* This class implements an automated rebalancing strategy for Vesu pools,
|
|
43
49
|
* managing deposits and withdrawals while optimizing yield through STRK rewards.
|
|
44
50
|
*/
|
|
45
|
-
export class VesuRebalance {
|
|
46
|
-
/** Configuration object for the strategy */
|
|
47
|
-
readonly config: IConfig;
|
|
51
|
+
export class VesuRebalance extends BaseStrategy<SingleTokenInfo, SingleActionAmount> {
|
|
48
52
|
/** Contract address of the strategy */
|
|
49
53
|
readonly address: ContractAddr;
|
|
50
54
|
/** Pricer instance for token price calculations */
|
|
51
55
|
readonly pricer: PricerBase;
|
|
52
56
|
/** Metadata containing strategy information */
|
|
53
|
-
readonly metadata: IStrategyMetadata
|
|
57
|
+
readonly metadata: IStrategyMetadata<VesuRebalanceSettings>;
|
|
54
58
|
/** Contract instance for interacting with the strategy */
|
|
55
59
|
readonly contract: Contract;
|
|
56
60
|
readonly BASE_WEIGHT = 10000; // 10000 bps = 100%
|
|
@@ -62,8 +66,8 @@ export class VesuRebalance {
|
|
|
62
66
|
* @param metadata - Strategy metadata including deposit tokens and address
|
|
63
67
|
* @throws {Error} If more than one deposit token is specified
|
|
64
68
|
*/
|
|
65
|
-
constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata) {
|
|
66
|
-
|
|
69
|
+
constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<VesuRebalanceSettings>) {
|
|
70
|
+
super(config);
|
|
67
71
|
this.pricer = pricer;
|
|
68
72
|
|
|
69
73
|
assert(metadata.depositTokens.length === 1, 'VesuRebalance only supports 1 deposit token');
|
|
@@ -79,12 +83,13 @@ export class VesuRebalance {
|
|
|
79
83
|
* @param receiver - Address that will receive the strategy tokens
|
|
80
84
|
* @returns Populated contract call for deposit
|
|
81
85
|
*/
|
|
82
|
-
depositCall(
|
|
86
|
+
depositCall(amountInfo: SingleActionAmount, receiver: ContractAddr) {
|
|
83
87
|
// Technically its not erc4626 abi, but we just need approve call
|
|
84
88
|
// so, its ok to use it
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
const
|
|
89
|
+
assert(amountInfo.tokenInfo.address.eq(this.asset().address), 'Deposit token mismatch');
|
|
90
|
+
const assetContract = new Contract(VesuRebalanceAbi, this.asset().address.address, this.config.provider);
|
|
91
|
+
const call1 = assetContract.populate('approve', [this.address.address, uint256.bnToUint256(amountInfo.amount.toWei())]);
|
|
92
|
+
const call2 = this.contract.populate('deposit', [uint256.bnToUint256(amountInfo.amount.toWei()), receiver.address]);
|
|
88
93
|
return [call1, call2];
|
|
89
94
|
}
|
|
90
95
|
|
|
@@ -95,8 +100,8 @@ export class VesuRebalance {
|
|
|
95
100
|
* @param owner - Address that owns the strategy tokens
|
|
96
101
|
* @returns Populated contract call for withdrawal
|
|
97
102
|
*/
|
|
98
|
-
withdrawCall(
|
|
99
|
-
return [this.contract.populate('withdraw', [uint256.bnToUint256(
|
|
103
|
+
withdrawCall(amountInfo: SingleActionAmount, receiver: ContractAddr, owner: ContractAddr) {
|
|
104
|
+
return [this.contract.populate('withdraw', [uint256.bnToUint256(amountInfo.amount.toWei()), receiver.address, owner.address])];
|
|
100
105
|
}
|
|
101
106
|
|
|
102
107
|
/**
|
|
@@ -127,6 +132,7 @@ export class VesuRebalance {
|
|
|
127
132
|
let price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
|
|
128
133
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
129
134
|
return {
|
|
135
|
+
tokenInfo: this.asset(),
|
|
130
136
|
amount,
|
|
131
137
|
usdValue
|
|
132
138
|
}
|
|
@@ -142,11 +148,30 @@ export class VesuRebalance {
|
|
|
142
148
|
let price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
|
|
143
149
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
144
150
|
return {
|
|
151
|
+
tokenInfo: this.asset(),
|
|
145
152
|
amount,
|
|
146
153
|
usdValue
|
|
147
154
|
}
|
|
148
155
|
}
|
|
149
156
|
|
|
157
|
+
static async getAllPossibleVerifiedPools(asset: ContractAddr) {
|
|
158
|
+
const data = await getAPIUsingHeadlessBrowser('https://api.vesu.xyz/pools');
|
|
159
|
+
const verifiedPools =data.data.filter((d: any) => d.isVerified);
|
|
160
|
+
const pools = verifiedPools.map((p: any) => {
|
|
161
|
+
const hasMyAsset = p.assets.find((a: any) => asset.eqString(a.address));
|
|
162
|
+
if (hasMyAsset) {
|
|
163
|
+
return {
|
|
164
|
+
pool_id: ContractAddr.from(p.id),
|
|
165
|
+
max_weight: 10000,
|
|
166
|
+
v_token: ContractAddr.from(hasMyAsset.vToken.address),
|
|
167
|
+
name: p.name,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}).filter((p: PoolProps | null) => p !== null);
|
|
172
|
+
return pools;
|
|
173
|
+
}
|
|
174
|
+
|
|
150
175
|
/**
|
|
151
176
|
* Retrieves the list of allowed pools and their detailed information from multiple sources:
|
|
152
177
|
* 1. Contract's allowed pools
|
|
@@ -217,11 +242,10 @@ export class VesuRebalance {
|
|
|
217
242
|
let isErrorPositionsAPI = false;
|
|
218
243
|
let vesuPositions: any[] = [];
|
|
219
244
|
try {
|
|
220
|
-
const
|
|
221
|
-
const data = await res.data;
|
|
245
|
+
const data = await getAPIUsingHeadlessBrowser(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`)
|
|
222
246
|
vesuPositions = data.data;
|
|
223
247
|
} catch (e) {
|
|
224
|
-
console.error(`${VesuRebalance.name}: Error fetching
|
|
248
|
+
console.error(`${VesuRebalance.name}: Error fetching positions for ${this.address.address}`, e);
|
|
225
249
|
isErrorPositionsAPI = true;
|
|
226
250
|
}
|
|
227
251
|
|
|
@@ -229,8 +253,7 @@ export class VesuRebalance {
|
|
|
229
253
|
let isErrorPoolsAPI = false;
|
|
230
254
|
let pools: any[] = [];
|
|
231
255
|
try {
|
|
232
|
-
const
|
|
233
|
-
const data = await res.data;
|
|
256
|
+
const data = await getAPIUsingHeadlessBrowser('https://api.vesu.xyz/pools');
|
|
234
257
|
pools = data.data;
|
|
235
258
|
} catch (e) {
|
|
236
259
|
console.error(`${VesuRebalance.name}: Error fetching pools for ${this.address.address}`, e);
|
|
@@ -242,13 +265,13 @@ export class VesuRebalance {
|
|
|
242
265
|
const info = allowedPools.map(async (p) => {
|
|
243
266
|
const vesuPosition = vesuPositions.find((d: any) => d.pool.id.toString() === num.getDecimalString(p.pool_id.address.toString()));
|
|
244
267
|
const pool = pools.find((d: any) => d.id == num.getDecimalString(p.pool_id.address));
|
|
245
|
-
const assetInfo = pool?.assets.find((d: any) =>
|
|
268
|
+
const assetInfo = pool?.assets.find((d: any) => this.asset().address.eqString(d.address));
|
|
246
269
|
let vTokenContract = new Contract(VesuRebalanceAbi, p.v_token.address, this.config.provider);
|
|
247
270
|
const bal = await vTokenContract.balanceOf(this.address.address);
|
|
248
271
|
const assets = await vTokenContract.convert_to_assets(uint256.bnToUint256(bal.toString()));
|
|
249
272
|
const item = {
|
|
250
273
|
pool_id: p.pool_id,
|
|
251
|
-
pool_name:
|
|
274
|
+
pool_name: pool.name,
|
|
252
275
|
max_weight: p.max_weight,
|
|
253
276
|
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)),
|
|
254
277
|
v_token: p.v_token,
|
|
@@ -297,7 +320,7 @@ export class VesuRebalance {
|
|
|
297
320
|
return acc + (curr.APY.netApy * weight);
|
|
298
321
|
}, 0);
|
|
299
322
|
|
|
300
|
-
return weightedApy;
|
|
323
|
+
return weightedApy * (1 - (this.metadata.additionalInfo.feeBps / 10000));
|
|
301
324
|
}
|
|
302
325
|
|
|
303
326
|
/**
|
|
@@ -418,24 +441,26 @@ export class VesuRebalance {
|
|
|
418
441
|
const netYield = this.netAPYGivenPools(pools);
|
|
419
442
|
|
|
420
443
|
const baseFlow: IInvestmentFlow = {
|
|
421
|
-
title: "Deposit
|
|
422
|
-
subItems: [`Net yield:
|
|
444
|
+
title: "Your Deposit",
|
|
445
|
+
subItems: [{key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%`}],
|
|
423
446
|
linkedFlows: [],
|
|
447
|
+
style: {backgroundColor: FlowChartColors.Purple.valueOf()},
|
|
424
448
|
};
|
|
425
449
|
|
|
426
|
-
|
|
427
|
-
|
|
450
|
+
let _pools = [...pools];
|
|
451
|
+
_pools = _pools.sort((a, b) => Number(b.amount.toString()) - Number(a.amount.toString()));
|
|
452
|
+
_pools.forEach((p) => {
|
|
428
453
|
const flow: IInvestmentFlow = {
|
|
429
|
-
title:
|
|
454
|
+
title: `Pool name: ${p.pool_name}`,
|
|
430
455
|
subItems: [
|
|
431
|
-
`APY:
|
|
432
|
-
|
|
456
|
+
{key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%`},
|
|
457
|
+
{key: 'Weight', value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%`}
|
|
433
458
|
],
|
|
434
459
|
linkedFlows: [],
|
|
460
|
+
style: p.amount.greaterThan(0) ? {backgroundColor: FlowChartColors.Blue.valueOf()} : {color: 'gray'},
|
|
435
461
|
};
|
|
436
462
|
baseFlow.linkedFlows.push(flow);
|
|
437
463
|
});
|
|
438
|
-
|
|
439
464
|
return [baseFlow];
|
|
440
465
|
}
|
|
441
466
|
}
|
|
@@ -445,22 +470,85 @@ const _protocol: IProtocol = {name: 'Vesu', logo: 'https://static-assets-8zct.on
|
|
|
445
470
|
// need to fine tune better
|
|
446
471
|
const _riskFactor: RiskFactor[] = [
|
|
447
472
|
{type: RiskType.SMART_CONTRACT_RISK, value: 0.5, weight: 25},
|
|
448
|
-
{type: RiskType.TECHNICAL_RISK, value: 0.5, weight: 25},
|
|
449
473
|
{type: RiskType.COUNTERPARTY_RISK, value: 1, weight: 50},
|
|
474
|
+
{type: RiskType.ORACLE_RISK, value: 0.5, weight: 25},
|
|
450
475
|
]
|
|
451
476
|
/**
|
|
452
477
|
* Represents the Vesu Rebalance Strategies.
|
|
453
478
|
*/
|
|
454
|
-
export const VesuRebalanceStrategies: IStrategyMetadata[] = [{
|
|
455
|
-
name: 'Vesu STRK',
|
|
479
|
+
export const VesuRebalanceStrategies: IStrategyMetadata<VesuRebalanceSettings>[] = [{
|
|
480
|
+
name: 'Vesu Fusion STRK',
|
|
456
481
|
description: _description.replace('{{TOKEN}}', 'STRK'),
|
|
457
|
-
address: ContractAddr.from('
|
|
482
|
+
address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
458
483
|
type: 'ERC4626',
|
|
459
484
|
depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'STRK')!],
|
|
460
485
|
protocols: [_protocol],
|
|
461
486
|
maxTVL: Web3Number.fromWei('0', 18),
|
|
462
487
|
risk: {
|
|
463
488
|
riskFactor: _riskFactor,
|
|
464
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
|
|
465
|
-
}
|
|
489
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
490
|
+
},
|
|
491
|
+
additionalInfo: {
|
|
492
|
+
feeBps: 1000,
|
|
493
|
+
},
|
|
494
|
+
}, {
|
|
495
|
+
name: 'Vesu Fusion ETH',
|
|
496
|
+
description: _description.replace('{{TOKEN}}', 'ETH'),
|
|
497
|
+
address: ContractAddr.from('0x26ea414fdf74ace1df5bc5ac72cbac670d438ef19b31edf9d59f98718fc0ab2'),
|
|
498
|
+
type: 'ERC4626',
|
|
499
|
+
depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'ETH')!],
|
|
500
|
+
protocols: [_protocol],
|
|
501
|
+
maxTVL: Web3Number.fromWei('0', 18),
|
|
502
|
+
risk: {
|
|
503
|
+
riskFactor: _riskFactor,
|
|
504
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
505
|
+
},
|
|
506
|
+
additionalInfo: {
|
|
507
|
+
feeBps: 1000,
|
|
508
|
+
},
|
|
509
|
+
}, {
|
|
510
|
+
name: 'Vesu Fusion USDC',
|
|
511
|
+
description: _description.replace('{{TOKEN}}', 'USDC'),
|
|
512
|
+
address: ContractAddr.from('0x3a69adeb993cddb266962d9c995e3d0641dab627df22b825fa31bda460c3c14'),
|
|
513
|
+
type: 'ERC4626',
|
|
514
|
+
depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'USDC')!],
|
|
515
|
+
protocols: [_protocol],
|
|
516
|
+
maxTVL: Web3Number.fromWei('0', 6),
|
|
517
|
+
risk: {
|
|
518
|
+
riskFactor: _riskFactor,
|
|
519
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
520
|
+
},
|
|
521
|
+
additionalInfo: {
|
|
522
|
+
feeBps: 1000,
|
|
523
|
+
},
|
|
524
|
+
// }, {
|
|
525
|
+
// name: 'Vesu Fusion USDT',
|
|
526
|
+
// description: _description.replace('{{TOKEN}}', 'USDT'),
|
|
527
|
+
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
528
|
+
// type: 'ERC4626',
|
|
529
|
+
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'USDT')!],
|
|
530
|
+
// protocols: [_protocol],
|
|
531
|
+
// maxTVL: Web3Number.fromWei('0', 6),
|
|
532
|
+
// risk: {
|
|
533
|
+
// riskFactor: _riskFactor,
|
|
534
|
+
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
535
|
+
// },
|
|
536
|
+
// additionalInfo: {
|
|
537
|
+
// feeBps: 1000,
|
|
538
|
+
// },
|
|
539
|
+
// }, {
|
|
540
|
+
// name: 'Vesu Fusion WBTC',
|
|
541
|
+
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
542
|
+
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
543
|
+
// type: 'ERC4626',
|
|
544
|
+
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
545
|
+
// protocols: [_protocol],
|
|
546
|
+
// maxTVL: Web3Number.fromWei('0', 8),
|
|
547
|
+
// risk: {
|
|
548
|
+
// riskFactor: _riskFactor,
|
|
549
|
+
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
550
|
+
// },
|
|
551
|
+
// additionalInfo: {
|
|
552
|
+
// feeBps: 1000,
|
|
553
|
+
// },
|
|
466
554
|
}]
|