@strkfarm/sdk 1.0.18 → 1.0.20

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.
@@ -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
- this.config = config;
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(assets: Web3Number, receiver: ContractAddr) {
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
- const assetContract = new Contract(VesuRebalanceAbi, this.metadata.depositTokens[0].address, this.config.provider);
86
- const call1 = assetContract.populate('approve', [this.address.address, uint256.bnToUint256(assets.toWei())]);
87
- const call2 = this.contract.populate('deposit', [uint256.bnToUint256(assets.toWei()), receiver.address]);
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(assets: Web3Number, receiver: ContractAddr, owner: ContractAddr) {
99
- return [this.contract.populate('withdraw', [uint256.bnToUint256(assets.toWei()), receiver.address, owner.address])];
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 res = await axios.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`)
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 pools for ${this.address.address}`, e);
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 res = await axios.get(`https://api.vesu.xyz/pools`);
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,7 +265,7 @@ 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) => ContractAddr.from(this.asset().address).eqString(d.address));
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()));
@@ -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
  /**
@@ -447,22 +470,89 @@ const _protocol: IProtocol = {name: 'Vesu', logo: 'https://static-assets-8zct.on
447
470
  // need to fine tune better
448
471
  const _riskFactor: RiskFactor[] = [
449
472
  {type: RiskType.SMART_CONTRACT_RISK, value: 0.5, weight: 25},
450
- {type: RiskType.TECHNICAL_RISK, value: 0.5, weight: 25},
451
473
  {type: RiskType.COUNTERPARTY_RISK, value: 1, weight: 50},
474
+ {type: RiskType.ORACLE_RISK, value: 0.5, weight: 25},
452
475
  ]
476
+ const AUDIT_URL = 'https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf';
453
477
  /**
454
478
  * Represents the Vesu Rebalance Strategies.
455
479
  */
456
- export const VesuRebalanceStrategies: IStrategyMetadata[] = [{
457
- name: 'Vesu STRK',
480
+ export const VesuRebalanceStrategies: IStrategyMetadata<VesuRebalanceSettings>[] = [{
481
+ name: 'Vesu Fusion STRK',
458
482
  description: _description.replace('{{TOKEN}}', 'STRK'),
459
- address: ContractAddr.from('0xeeb729d554ae486387147b13a9c8871bc7991d454e8b5ff570d4bf94de71e1'),
483
+ address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
460
484
  type: 'ERC4626',
461
485
  depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'STRK')!],
462
486
  protocols: [_protocol],
487
+ auditUrl: AUDIT_URL,
463
488
  maxTVL: Web3Number.fromWei('0', 18),
464
489
  risk: {
465
490
  riskFactor: _riskFactor,
466
- netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / 100,
467
- }
491
+ netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
492
+ },
493
+ additionalInfo: {
494
+ feeBps: 1000,
495
+ },
496
+ }, {
497
+ name: 'Vesu Fusion ETH',
498
+ description: _description.replace('{{TOKEN}}', 'ETH'),
499
+ address: ContractAddr.from('0x26ea414fdf74ace1df5bc5ac72cbac670d438ef19b31edf9d59f98718fc0ab2'),
500
+ type: 'ERC4626',
501
+ auditUrl: AUDIT_URL,
502
+ depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'ETH')!],
503
+ protocols: [_protocol],
504
+ maxTVL: Web3Number.fromWei('0', 18),
505
+ risk: {
506
+ riskFactor: _riskFactor,
507
+ netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
508
+ },
509
+ additionalInfo: {
510
+ feeBps: 1000,
511
+ },
512
+ }, {
513
+ name: 'Vesu Fusion USDC',
514
+ description: _description.replace('{{TOKEN}}', 'USDC'),
515
+ address: ContractAddr.from('0x3a69adeb993cddb266962d9c995e3d0641dab627df22b825fa31bda460c3c14'),
516
+ type: 'ERC4626',
517
+ auditUrl: AUDIT_URL,
518
+ depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'USDC')!],
519
+ protocols: [_protocol],
520
+ maxTVL: Web3Number.fromWei('0', 6),
521
+ risk: {
522
+ riskFactor: _riskFactor,
523
+ netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
524
+ },
525
+ additionalInfo: {
526
+ feeBps: 1000,
527
+ },
528
+ // }, {
529
+ // name: 'Vesu Fusion USDT',
530
+ // description: _description.replace('{{TOKEN}}', 'USDT'),
531
+ // address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
532
+ // type: 'ERC4626',
533
+ // depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'USDT')!],
534
+ // protocols: [_protocol],
535
+ // maxTVL: Web3Number.fromWei('0', 6),
536
+ // risk: {
537
+ // riskFactor: _riskFactor,
538
+ // netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
539
+ // },
540
+ // additionalInfo: {
541
+ // feeBps: 1000,
542
+ // },
543
+ // }, {
544
+ // name: 'Vesu Fusion WBTC',
545
+ // description: _description.replace('{{TOKEN}}', 'WBTC'),
546
+ // address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
547
+ // type: 'ERC4626',
548
+ // depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
549
+ // protocols: [_protocol],
550
+ // maxTVL: Web3Number.fromWei('0', 8),
551
+ // risk: {
552
+ // riskFactor: _riskFactor,
553
+ // netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
554
+ // },
555
+ // additionalInfo: {
556
+ // feeBps: 1000,
557
+ // },
468
558
  }]