@strkfarm/sdk 1.0.16 → 1.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,10 +1,3 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
1
  // src/modules/pricer.ts
9
2
  import axios2 from "axios";
10
3
 
@@ -28,6 +21,7 @@ var FatalError = class extends Error {
28
21
  var tokens = [{
29
22
  name: "Starknet",
30
23
  symbol: "STRK",
24
+ logo: "https://assets.coingecko.com/coins/images/26433/small/starknet.png",
31
25
  address: "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
32
26
  decimals: 18,
33
27
  coingeckId: "starknet"
@@ -60,6 +54,7 @@ var Global = class {
60
54
  symbol: token.symbol,
61
55
  address: token.address,
62
56
  decimals: token.decimals,
57
+ logo: token.logoUri,
63
58
  coingeckId: token.extensions.coingeckoId
64
59
  });
65
60
  });
@@ -139,16 +134,22 @@ var ContractAddr = class _ContractAddr {
139
134
  }
140
135
  };
141
136
 
142
- // src/modules/pricer.ts
143
- var CoinMarketCap = __require("coinmarketcap-api");
137
+ // src/modules/pricerBase.ts
144
138
  var PricerBase = class {
139
+ constructor(config, tokens2) {
140
+ this.config = config;
141
+ this.tokens = tokens2;
142
+ }
145
143
  async getPrice(tokenSymbol) {
146
144
  throw new Error("Method not implemented");
147
145
  }
148
146
  };
149
- var Pricer = class {
147
+
148
+ // src/modules/pricer.ts
149
+ var Pricer = class extends PricerBase {
150
+ // e.g. ETH/USDC
150
151
  constructor(config, tokens2) {
151
- this.tokens = [];
152
+ super(config, tokens2);
152
153
  this.prices = {};
153
154
  // code populates this map during runtime to determine which method to use for a given token
154
155
  // The method set will be the first one to try after first attempt
@@ -158,11 +159,6 @@ var Pricer = class {
158
159
  */
159
160
  this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
160
161
  this.EKUBO_API = "https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
161
- // e.g. ETH/USDC
162
- // backup oracle001
163
- this.client = new CoinMarketCap(process.env.COINMARKETCAP_KEY);
164
- this.config = config;
165
- this.tokens = tokens2;
166
162
  }
167
163
  isReady() {
168
164
  const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
@@ -294,10 +290,7 @@ var Pricer = class {
294
290
  return Number(data.data.amount);
295
291
  }
296
292
  async _getPriceCoinMarketCap(token) {
297
- const result = await this.client.getQuotes({ symbol: token.symbol });
298
- if (result.data)
299
- return result.data[token.symbol].quote.USD.price;
300
- throw new Error(result);
293
+ throw new Error("Not implemented");
301
294
  }
302
295
  async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
303
296
  const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
@@ -491,6 +484,7 @@ var _ZkLend = class _ZkLend extends ILending {
491
484
  name: pool.token.name,
492
485
  symbol: pool.token.symbol,
493
486
  address: savedTokenInfo?.address || "",
487
+ logo: "",
494
488
  decimals: pool.token.decimals,
495
489
  borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
496
490
  collareralFactor
@@ -600,14 +594,84 @@ var _ZkLend = class _ZkLend extends ILending {
600
594
  _ZkLend.POOLS_URL = "https://app.zklend.com/api/pools";
601
595
  var ZkLend = _ZkLend;
602
596
 
597
+ // src/modules/pricer-from-api.ts
598
+ import axios4 from "axios";
599
+ var PricerFromApi = class extends PricerBase {
600
+ constructor(config, tokens2) {
601
+ super(config, tokens2);
602
+ }
603
+ async getPrice(tokenSymbol) {
604
+ try {
605
+ return await this.getPriceFromMyAPI(tokenSymbol);
606
+ } catch (e) {
607
+ logger.warn("getPriceFromMyAPI error", e);
608
+ }
609
+ logger.log("getPrice coinbase", tokenSymbol);
610
+ let retry = 0;
611
+ const MAX_RETRIES = 5;
612
+ for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
613
+ try {
614
+ const priceInfo = await axios4.get(
615
+ `https://api.coinbase.com/v2/prices/${tokenSymbol}-USDT/spot`
616
+ );
617
+ if (!priceInfo) {
618
+ throw new Error("Failed to fetch price");
619
+ }
620
+ const data = await priceInfo.data;
621
+ const price = Number(data.data.amount);
622
+ return {
623
+ price,
624
+ timestamp: /* @__PURE__ */ new Date()
625
+ };
626
+ } catch (e) {
627
+ logger.warn("getPrice coinbase error", e, retry);
628
+ await new Promise((resolve) => setTimeout(resolve, retry * 1e3));
629
+ }
630
+ }
631
+ throw new Error(`Failed to fetch price for ${tokenSymbol}`);
632
+ }
633
+ async getPriceFromMyAPI(tokenSymbol) {
634
+ logger.verbose(`getPrice from redis: ${tokenSymbol}`);
635
+ const endpoint = "https://app.strkfarm.com";
636
+ const url = `${endpoint}/api/price/${tokenSymbol}`;
637
+ const priceInfoRes = await fetch(url);
638
+ const priceInfo = await priceInfoRes.json();
639
+ const now = /* @__PURE__ */ new Date();
640
+ const priceTime = new Date(priceInfo.timestamp);
641
+ if (now.getTime() - priceTime.getTime() > 9e5) {
642
+ throw new Error("Price is stale");
643
+ }
644
+ const price = Number(priceInfo.price);
645
+ return {
646
+ price,
647
+ timestamp: new Date(priceInfo.timestamp)
648
+ };
649
+ }
650
+ };
651
+
603
652
  // src/interfaces/common.ts
604
653
  import { RpcProvider as RpcProvider2 } from "starknet";
654
+ var RiskType = /* @__PURE__ */ ((RiskType2) => {
655
+ RiskType2["MARKET_RISK"] = "MARKET_RISK";
656
+ RiskType2["IMPERMANENT_LOSS"] = "IMPERMANENT_LOSS";
657
+ RiskType2["LIQUIDITY_RISK"] = "LIQUIDITY_RISK";
658
+ RiskType2["SMART_CONTRACT_RISK"] = "SMART_CONTRACT_RISK";
659
+ RiskType2["TECHNICAL_RISK"] = "TECHNICAL_RISK";
660
+ RiskType2["COUNTERPARTY_RISK"] = "COUNTERPARTY_RISK";
661
+ return RiskType2;
662
+ })(RiskType || {});
605
663
  var Network = /* @__PURE__ */ ((Network2) => {
606
664
  Network2["mainnet"] = "mainnet";
607
665
  Network2["sepolia"] = "sepolia";
608
666
  Network2["devnet"] = "devnet";
609
667
  return Network2;
610
668
  })(Network || {});
669
+ var FlowChartColors = /* @__PURE__ */ ((FlowChartColors2) => {
670
+ FlowChartColors2["Green"] = "purple";
671
+ FlowChartColors2["Blue"] = "#35484f";
672
+ FlowChartColors2["Purple"] = "#6e53dc";
673
+ return FlowChartColors2;
674
+ })(FlowChartColors || {});
611
675
  function getMainnetConfig(rpcUrl = "https://starknet-mainnet.public.blastapi.io", blockIdentifier = "pending") {
612
676
  return {
613
677
  provider: new RpcProvider2({
@@ -2183,7 +2247,7 @@ function assert(condition, message) {
2183
2247
  }
2184
2248
 
2185
2249
  // src/strategies/vesu-rebalance.ts
2186
- import axios4 from "axios";
2250
+ import axios5 from "axios";
2187
2251
  var VesuRebalance = class _VesuRebalance {
2188
2252
  // 10000 bps = 100%
2189
2253
  /**
@@ -2290,7 +2354,7 @@ var VesuRebalance = class _VesuRebalance {
2290
2354
  let isErrorPositionsAPI = false;
2291
2355
  let vesuPositions = [];
2292
2356
  try {
2293
- const res = await axios4.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
2357
+ const res = await axios5.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
2294
2358
  const data2 = await res.data;
2295
2359
  vesuPositions = data2.data;
2296
2360
  } catch (e) {
@@ -2300,7 +2364,7 @@ var VesuRebalance = class _VesuRebalance {
2300
2364
  let isErrorPoolsAPI = false;
2301
2365
  let pools = [];
2302
2366
  try {
2303
- const res = await axios4.get(`https://api.vesu.xyz/pools`);
2367
+ const res = await axios5.get(`https://api.vesu.xyz/pools`);
2304
2368
  const data2 = await res.data;
2305
2369
  pools = data2.data;
2306
2370
  } catch (e) {
@@ -2317,7 +2381,7 @@ var VesuRebalance = class _VesuRebalance {
2317
2381
  const assets = await vTokenContract.convert_to_assets(uint2562.bnToUint256(bal.toString()));
2318
2382
  const item = {
2319
2383
  pool_id: p.pool_id,
2320
- pool_name: vesuPosition?.pool.name,
2384
+ pool_name: pool.name,
2321
2385
  max_weight: p.max_weight,
2322
2386
  current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)),
2323
2387
  v_token: p.v_token,
@@ -2460,16 +2524,50 @@ var VesuRebalance = class _VesuRebalance {
2460
2524
  }
2461
2525
  return this.contract.populate("rebalance", [actions]);
2462
2526
  }
2527
+ async getInvestmentFlows(pools) {
2528
+ const netYield = this.netAPYGivenPools(pools);
2529
+ const baseFlow = {
2530
+ title: "Your Deposit",
2531
+ subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }],
2532
+ linkedFlows: [],
2533
+ style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
2534
+ };
2535
+ let _pools = [...pools];
2536
+ _pools = _pools.sort((a, b) => Number(b.amount.toString()) - Number(a.amount.toString()));
2537
+ _pools.forEach((p) => {
2538
+ const flow = {
2539
+ title: `Pool name: ${p.pool_name}`,
2540
+ subItems: [
2541
+ { key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%` },
2542
+ { key: "Weight", value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%` }
2543
+ ],
2544
+ linkedFlows: [],
2545
+ style: p.amount.greaterThan(0) ? { backgroundColor: "#35484f" /* Blue */.valueOf() } : { color: "gray" }
2546
+ };
2547
+ baseFlow.linkedFlows.push(flow);
2548
+ });
2549
+ return [baseFlow];
2550
+ }
2463
2551
  };
2464
2552
  var _description = "Automatically diversify {{TOKEN}} holdings into different Vesu pools while reducing risk and maximizing yield. Defi spring STRK Rewards are auto-compounded as well.";
2465
2553
  var _protocol = { name: "Vesu", logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png" };
2554
+ var _riskFactor = [
2555
+ { type: "SMART_CONTRACT_RISK" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
2556
+ { type: "TECHNICAL_RISK" /* TECHNICAL_RISK */, value: 0.5, weight: 25 },
2557
+ { type: "COUNTERPARTY_RISK" /* COUNTERPARTY_RISK */, value: 1, weight: 50 }
2558
+ ];
2466
2559
  var VesuRebalanceStrategies = [{
2467
2560
  name: "Vesu STRK",
2468
2561
  description: _description.replace("{{TOKEN}}", "STRK"),
2469
2562
  address: ContractAddr.from("0xeeb729d554ae486387147b13a9c8871bc7991d454e8b5ff570d4bf94de71e1"),
2470
2563
  type: "ERC4626",
2471
2564
  depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
2472
- protocols: [_protocol]
2565
+ protocols: [_protocol],
2566
+ maxTVL: Web3Number.fromWei("0", 18),
2567
+ risk: {
2568
+ riskFactor: _riskFactor,
2569
+ netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / 100
2570
+ }
2473
2571
  }];
2474
2572
 
2475
2573
  // src/notifs/telegram.ts
@@ -2737,6 +2835,7 @@ export {
2737
2835
  AutoCompounderSTRK,
2738
2836
  ContractAddr,
2739
2837
  FatalError,
2838
+ FlowChartColors,
2740
2839
  Global,
2741
2840
  ILending,
2742
2841
  Initializable,
@@ -2745,8 +2844,9 @@ export {
2745
2844
  PasswordJsonCryptoUtil,
2746
2845
  Pragma,
2747
2846
  Pricer,
2748
- PricerBase,
2847
+ PricerFromApi,
2749
2848
  PricerRedis,
2849
+ RiskType,
2750
2850
  Store,
2751
2851
  TelegramNotif,
2752
2852
  VesuRebalance,
package/package.json CHANGED
@@ -1,18 +1,23 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
4
4
  "description": "STRKFarm TS SDK (Meant for our internal use, but feel free to use it)",
5
5
  "typings": "dist/index.d.ts",
6
- "main": "dist/index.js",
7
6
  "types": "dist/index.d.ts",
8
7
  "bin": {
9
8
  "accountsecure": "dist/cli.js"
10
9
  },
11
10
  "exports": {
12
11
  ".": {
13
- "import": "./dist/index.mjs",
14
- "require": "./dist/index.js",
15
- "browser": "./dist/index.global.js",
12
+ "import": {
13
+ "browser": "./dist/index.browser.mjs",
14
+ "default": "./dist/index.mjs"
15
+ },
16
+ "require": {
17
+ "browser": "./dist/index.browser.global.js",
18
+ "default": "./dist/index.js"
19
+ },
20
+ "browser": "./dist/index.browser.global.js",
16
21
  "types": "./dist/index.d.ts"
17
22
  }
18
23
  },
@@ -22,12 +27,13 @@
22
27
  ],
23
28
  "scripts": {
24
29
  "test": "jest",
25
- "build": "tsup --clean && pnpm run build:esm && npm run build:dts && npm run build:iife && npm run build-cli",
30
+ "build": "tsup --clean && pnpm run build:esm && npm run build:dts && npm run build:iife && npm run build-cli && pnpm run build:iife-esm",
26
31
  "build:esm": "tsup --clean false --format esm --platform node",
27
32
  "build-cli:esm": "tsup ./src/cli.ts --clean false --format esm --platform node",
28
33
  "build-cli": "tsup ./src/cli.ts --clean false && pnpm run build-cli:esm",
29
34
  "build:dts": "tsup --clean false --dts-only",
30
- "build:iife": "tsup --clean false --format iife --platform browser"
35
+ "build:iife": "tsup --clean false --format iife --platform browser",
36
+ "build:iife-esm": "tsup --clean false --format iife --format esm --platform browser"
31
37
  },
32
38
  "keywords": [],
33
39
  "author": "",
@@ -48,10 +54,11 @@
48
54
  "bignumber.js": "4.0.4",
49
55
  "browser-assert": "^1.2.1",
50
56
  "chalk": "^4.1.2",
51
- "coinmarketcap-api": "^3.1.1",
52
57
  "commander": "^12.1.0",
58
+ "form-data": "^4.0.2",
53
59
  "inquirer": "^10.1.2",
54
60
  "node-telegram-bot-api": "^0.66.0",
61
+ "proxy-from-env": "^1.1.0",
55
62
  "redis": "^4.7.0",
56
63
  "stacktrace-js": "^2.0.2",
57
64
  "starknet": "^6.11.0",
package/src/global.ts CHANGED
@@ -48,6 +48,7 @@ export class FatalError extends Error {
48
48
  const tokens: TokenInfo[] = [{
49
49
  name: 'Starknet',
50
50
  symbol: 'STRK',
51
+ logo: 'https://assets.coingecko.com/coins/images/26433/small/starknet.png',
51
52
  address: '0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',
52
53
  decimals: 18,
53
54
  coingeckId: 'starknet'
@@ -110,6 +111,7 @@ export class Global {
110
111
  symbol: token.symbol,
111
112
  address: token.address,
112
113
  decimals: token.decimals,
114
+ logo: token.logoUri,
113
115
  coingeckId: token.extensions.coingeckoId,
114
116
  });
115
117
  });
@@ -1,11 +1,27 @@
1
- import { ContractAddr } from "@/dataTypes"
1
+ import { ContractAddr, Web3Number } from "@/dataTypes"
2
2
  import { BlockIdentifier, RpcProvider } from "starknet"
3
3
 
4
+ export enum RiskType {
5
+ MARKET_RISK = 'MARKET_RISK',
6
+ IMPERMANENT_LOSS = 'IMPERMANENT_LOSS',
7
+ LIQUIDITY_RISK = 'LIQUIDITY_RISK',
8
+ SMART_CONTRACT_RISK = 'SMART_CONTRACT_RISK',
9
+ TECHNICAL_RISK = 'TECHNICAL_RISK',
10
+ COUNTERPARTY_RISK = 'COUNTERPARTY_RISK', // e.g. bad debt
11
+ }
12
+
13
+ export interface RiskFactor {
14
+ type: RiskType,
15
+ value: number, // 0 to 5
16
+ weight: number // 0 to 100
17
+ }
18
+
4
19
  export interface TokenInfo {
5
20
  name: string,
6
21
  symbol: string,
7
22
  address: string,
8
23
  decimals: number,
24
+ logo: string,
9
25
  coingeckId?: string,
10
26
  }
11
27
 
@@ -27,13 +43,36 @@ export interface IProtocol {
27
43
  logo: string,
28
44
  }
29
45
 
46
+ export enum FlowChartColors {
47
+ Green = 'purple',
48
+ Blue = '#35484f',
49
+ Purple = '#6e53dc',
50
+ }
51
+
52
+ /**
53
+ * @property risk.riskFactor.factor - The risk factors that are considered for the strategy.
54
+ * @property risk.riskFactor.factor - The value of the risk factor from 0 to 10, 0 being the lowest and 10 being the highest.
55
+ */
30
56
  export interface IStrategyMetadata {
31
57
  name: string,
32
58
  description: string,
33
59
  address: ContractAddr,
34
60
  type: 'ERC4626' | 'ERC721' | 'Other',
35
61
  depositTokens: TokenInfo[],
36
- protocols: IProtocol[]
62
+ protocols: IProtocol[],
63
+ auditUrl?: string,
64
+ maxTVL: Web3Number,
65
+ risk: {
66
+ riskFactor: RiskFactor[],
67
+ netRisk: number
68
+ }
69
+ }
70
+
71
+ export interface IInvestmentFlow {
72
+ title: string,
73
+ subItems: {key: string, value: string}[],
74
+ linkedFlows: IInvestmentFlow[],
75
+ style?: any
37
76
  }
38
77
 
39
78
  export function getMainnetConfig(rpcUrl = "https://starknet-mainnet.public.blastapi.io", blockIdentifier: BlockIdentifier = 'pending'): IConfig {
@@ -1,3 +1,4 @@
1
1
  export * from './pricer';
2
2
  export * from './pragma';
3
- export * from './zkLend';
3
+ export * from './zkLend';
4
+ export * from './pricer-from-api';
@@ -0,0 +1,61 @@
1
+ import { logger } from "@/global";
2
+ import { PriceInfo } from "./pricer";
3
+ import axios from "axios";
4
+ import { IConfig, TokenInfo } from "@/interfaces";
5
+ import { PricerBase } from "./pricerBase";
6
+
7
+ export class PricerFromApi extends PricerBase {
8
+ constructor(config: IConfig, tokens: TokenInfo[]) {
9
+ super(config, tokens);
10
+ }
11
+
12
+ async getPrice(tokenSymbol: string): Promise<PriceInfo> {
13
+ try {
14
+ return await this.getPriceFromMyAPI(tokenSymbol);
15
+ } catch (e) {
16
+ logger.warn('getPriceFromMyAPI error', e);
17
+ }
18
+ logger.log('getPrice coinbase', tokenSymbol);
19
+ let retry = 0;
20
+ const MAX_RETRIES = 5;
21
+ for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
22
+ try {
23
+ const priceInfo = await axios.get(
24
+ `https://api.coinbase.com/v2/prices/${tokenSymbol}-USDT/spot`,
25
+ );
26
+ if (!priceInfo) {
27
+ throw new Error('Failed to fetch price');
28
+ }
29
+ const data = await priceInfo.data;
30
+ const price = Number(data.data.amount);
31
+ return {
32
+ price,
33
+ timestamp: new Date()
34
+ }
35
+ } catch (e) {
36
+ logger.warn('getPrice coinbase error', e, retry);
37
+ await new Promise((resolve) => setTimeout(resolve, retry * 1000));
38
+ }
39
+ }
40
+ throw new Error(`Failed to fetch price for ${tokenSymbol}`);
41
+ }
42
+
43
+ async getPriceFromMyAPI(tokenSymbol: string) {
44
+ logger.verbose(`getPrice from redis: ${tokenSymbol}`);
45
+ const endpoint = 'https://app.strkfarm.com'
46
+ const url = `${endpoint}/api/price/${tokenSymbol}`;
47
+ const priceInfoRes = await fetch(url);
48
+ const priceInfo = await priceInfoRes.json();
49
+ const now = new Date();
50
+ const priceTime = new Date(priceInfo.timestamp);
51
+ if (now.getTime() - priceTime.getTime() > 900000) {
52
+ // 15 mins
53
+ throw new Error('Price is stale');
54
+ }
55
+ const price = Number(priceInfo.price);
56
+ return {
57
+ price,
58
+ timestamp: new Date(priceInfo.timestamp)
59
+ }
60
+ }
61
+ }
@@ -3,22 +3,14 @@ import { FatalError, Global, logger } from "@/global";
3
3
  import { TokenInfo } from "@/interfaces/common";
4
4
  import { IConfig } from "@/interfaces/common";
5
5
  import { Web3Number } from "@/dataTypes";
6
- const CoinMarketCap = require('coinmarketcap-api')
6
+ import { PricerBase } from "./pricerBase";
7
7
 
8
8
  export interface PriceInfo {
9
9
  price: number,
10
10
  timestamp: Date
11
11
  }
12
12
 
13
- export abstract class PricerBase {
14
- async getPrice(tokenSymbol: string): Promise<PriceInfo> {
15
- throw new Error('Method not implemented');
16
- }
17
- }
18
-
19
- export class Pricer implements PricerBase{
20
- readonly config: IConfig;
21
- readonly tokens: TokenInfo[] = [];
13
+ export class Pricer extends PricerBase {
22
14
  protected prices: {
23
15
  [key: string]: PriceInfo
24
16
  } = {}
@@ -33,12 +25,8 @@ export class Pricer implements PricerBase{
33
25
  protected PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
34
26
  protected EKUBO_API = 'https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8'; // e.g. ETH/USDC
35
27
 
36
- // backup oracle001
37
- protected client = new CoinMarketCap(process.env.COINMARKETCAP_KEY!);
38
-
39
28
  constructor(config: IConfig, tokens: TokenInfo[]) {
40
- this.config = config;
41
- this.tokens = tokens;
29
+ super(config, tokens);
42
30
  }
43
31
 
44
32
  isReady() {
@@ -187,11 +175,11 @@ export class Pricer implements PricerBase{
187
175
  }
188
176
 
189
177
  async _getPriceCoinMarketCap(token: TokenInfo): Promise<number> {
190
- const result = await this.client.getQuotes({symbol: token.symbol});
191
- if (result.data)
192
- return result.data[token.symbol].quote.USD.price as number
178
+ // const result = await this.client.getQuotes({symbol: token.symbol});
179
+ // if (result.data)
180
+ // return result.data[token.symbol].quote.USD.price as number
193
181
 
194
- throw new Error(result);
182
+ throw new Error("Not implemented");
195
183
  }
196
184
 
197
185
  async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
@@ -0,0 +1,15 @@
1
+ import { IConfig, TokenInfo } from "@/interfaces";
2
+ import { PriceInfo } from "./pricer";
3
+
4
+ export abstract class PricerBase {
5
+ readonly config: IConfig;
6
+ readonly tokens: TokenInfo[];
7
+ constructor(config: IConfig, tokens: TokenInfo[]) {
8
+ this.config = config;
9
+ this.tokens = tokens;
10
+ }
11
+
12
+ async getPrice(tokenSymbol: string): Promise<PriceInfo> {
13
+ throw new Error('Method not implemented');
14
+ }
15
+ }
@@ -37,6 +37,7 @@ export class ZkLend extends ILending implements ILending {
37
37
  name: pool.token.name,
38
38
  symbol: pool.token.symbol,
39
39
  address: savedTokenInfo?.address || '',
40
+ logo: '',
40
41
  decimals: pool.token.decimals,
41
42
  borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
42
43
  collareralFactor
@@ -6,6 +6,7 @@ import type { RedisClientType } from 'redis'
6
6
 
7
7
  export class PricerRedis extends Pricer {
8
8
  private redisClient: RedisClientType | null = null;
9
+
9
10
  constructor(config: IConfig, tokens: TokenInfo[]) {
10
11
  super(config, tokens)
11
12
  }
@@ -1,11 +1,12 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
- import { IConfig, IProtocol, IStrategyMetadata } from "@/interfaces";
3
- import { Pricer, PricerBase } from "@/modules";
2
+ import { FlowChartColors, IConfig, IInvestmentFlow, IProtocol, IStrategyMetadata, RiskFactor, RiskType } from "@/interfaces";
3
+ import { Pricer } from "@/modules";
4
4
  import { CairoCustomEnum, Contract, num, uint256 } from "starknet";
5
5
  import VesuRebalanceAbi from '@/data/vesu-rebalance.abi.json';
6
6
  import { Global } from "@/global";
7
7
  import { assert } from "@/utils";
8
8
  import axios from "axios";
9
+ import { PricerBase } from "@/modules/pricerBase";
9
10
 
10
11
  interface PoolProps {
11
12
  pool_id: ContractAddr;
@@ -247,7 +248,7 @@ export class VesuRebalance {
247
248
  const assets = await vTokenContract.convert_to_assets(uint256.bnToUint256(bal.toString()));
248
249
  const item = {
249
250
  pool_id: p.pool_id,
250
- pool_name: vesuPosition?.pool.name,
251
+ pool_name: pool.name,
251
252
  max_weight: p.max_weight,
252
253
  current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)),
253
254
  v_token: p.v_token,
@@ -412,10 +413,43 @@ export class VesuRebalance {
412
413
  }
413
414
  return this.contract.populate('rebalance', [actions]);
414
415
  }
416
+
417
+ async getInvestmentFlows(pools: PoolInfoFull[]) {
418
+ const netYield = this.netAPYGivenPools(pools);
419
+
420
+ const baseFlow: IInvestmentFlow = {
421
+ title: "Your Deposit",
422
+ subItems: [{key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%`}],
423
+ linkedFlows: [],
424
+ style: {backgroundColor: FlowChartColors.Purple.valueOf()},
425
+ };
426
+
427
+ let _pools = [...pools];
428
+ _pools = _pools.sort((a, b) => Number(b.amount.toString()) - Number(a.amount.toString()));
429
+ _pools.forEach((p) => {
430
+ const flow: IInvestmentFlow = {
431
+ title: `Pool name: ${p.pool_name}`,
432
+ subItems: [
433
+ {key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%`},
434
+ {key: 'Weight', value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%`}
435
+ ],
436
+ linkedFlows: [],
437
+ style: p.amount.greaterThan(0) ? {backgroundColor: FlowChartColors.Blue.valueOf()} : {color: 'gray'},
438
+ };
439
+ baseFlow.linkedFlows.push(flow);
440
+ });
441
+ return [baseFlow];
442
+ }
415
443
  }
416
444
 
417
445
  const _description = 'Automatically diversify {{TOKEN}} holdings into different Vesu pools while reducing risk and maximizing yield. Defi spring STRK Rewards are auto-compounded as well.'
418
446
  const _protocol: IProtocol = {name: 'Vesu', logo: 'https://static-assets-8zct.onrender.com/integrations/vesu/logo.png'}
447
+ // need to fine tune better
448
+ const _riskFactor: RiskFactor[] = [
449
+ {type: RiskType.SMART_CONTRACT_RISK, value: 0.5, weight: 25},
450
+ {type: RiskType.TECHNICAL_RISK, value: 0.5, weight: 25},
451
+ {type: RiskType.COUNTERPARTY_RISK, value: 1, weight: 50},
452
+ ]
419
453
  /**
420
454
  * Represents the Vesu Rebalance Strategies.
421
455
  */
@@ -425,5 +459,10 @@ export const VesuRebalanceStrategies: IStrategyMetadata[] = [{
425
459
  address: ContractAddr.from('0xeeb729d554ae486387147b13a9c8871bc7991d454e8b5ff570d4bf94de71e1'),
426
460
  type: 'ERC4626',
427
461
  depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'STRK')!],
428
- protocols: [_protocol]
462
+ protocols: [_protocol],
463
+ maxTVL: Web3Number.fromWei('0', 18),
464
+ risk: {
465
+ riskFactor: _riskFactor,
466
+ netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / 100,
467
+ }
429
468
  }]