@strkfarm/sdk 1.0.16 → 1.0.17

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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
+ import BigNumber from 'bignumber.js';
1
2
  import * as starknet from 'starknet';
2
3
  import { RpcProvider, BlockIdentifier, Contract, Account } from 'starknet';
3
- import BigNumber from 'bignumber.js';
4
4
  import * as util from 'util';
5
5
  import TelegramBot from 'node-telegram-bot-api';
6
6
 
@@ -30,11 +30,25 @@ declare class ContractAddr {
30
30
  static eqString(a: string, b: string): boolean;
31
31
  }
32
32
 
33
+ declare enum RiskType {
34
+ MARKET_RISK = "MARKET_RISK",
35
+ IMPERMANENT_LOSS = "IMPERMANENT_LOSS",
36
+ LIQUIDITY_RISK = "LIQUIDITY_RISK",
37
+ SMART_CONTRACT_RISK = "SMART_CONTRACT_RISK",
38
+ TECHNICAL_RISK = "TECHNICAL_RISK",
39
+ COUNTERPARTY_RISK = "COUNTERPARTY_RISK"
40
+ }
41
+ interface RiskFactor {
42
+ type: RiskType;
43
+ value: number;
44
+ weight: number;
45
+ }
33
46
  interface TokenInfo {
34
47
  name: string;
35
48
  symbol: string;
36
49
  address: string;
37
50
  decimals: number;
51
+ logo: string;
38
52
  coingeckId?: string;
39
53
  }
40
54
  declare enum Network {
@@ -52,6 +66,10 @@ interface IProtocol {
52
66
  name: string;
53
67
  logo: string;
54
68
  }
69
+ /**
70
+ * @property risk.riskFactor.factor - The risk factors that are considered for the strategy.
71
+ * @property risk.riskFactor.factor - The value of the risk factor from 0 to 10, 0 being the lowest and 10 being the highest.
72
+ */
55
73
  interface IStrategyMetadata {
56
74
  name: string;
57
75
  description: string;
@@ -59,49 +77,19 @@ interface IStrategyMetadata {
59
77
  type: 'ERC4626' | 'ERC721' | 'Other';
60
78
  depositTokens: TokenInfo[];
61
79
  protocols: IProtocol[];
62
- }
63
- declare function getMainnetConfig(rpcUrl?: string, blockIdentifier?: BlockIdentifier): IConfig;
64
-
65
- interface PriceInfo {
66
- price: number;
67
- timestamp: Date;
68
- }
69
- declare abstract class PricerBase {
70
- getPrice(tokenSymbol: string): Promise<PriceInfo>;
71
- }
72
- declare class Pricer implements PricerBase {
73
- readonly config: IConfig;
74
- readonly tokens: TokenInfo[];
75
- protected prices: {
76
- [key: string]: PriceInfo;
80
+ auditUrl?: string;
81
+ maxTVL: Web3Number;
82
+ risk: {
83
+ riskFactor: RiskFactor[];
84
+ netRisk: number;
77
85
  };
78
- private methodToUse;
79
- /**
80
- * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
81
- */
82
- protected PRICE_API: string;
83
- protected EKUBO_API: string;
84
- protected client: any;
85
- constructor(config: IConfig, tokens: TokenInfo[]);
86
- isReady(): boolean;
87
- waitTillReady(): Promise<void>;
88
- start(): void;
89
- isStale(timestamp: Date, tokenName: string): boolean;
90
- assertNotStale(timestamp: Date, tokenName: string): void;
91
- getPrice(tokenSymbol: string): Promise<PriceInfo>;
92
- protected _loadPrices(onUpdate?: (tokenSymbol: string) => void): void;
93
- _getPrice(token: TokenInfo, defaultMethod?: string): Promise<number>;
94
- _getPriceCoinbase(token: TokenInfo): Promise<number>;
95
- _getPriceCoinMarketCap(token: TokenInfo): Promise<number>;
96
- _getPriceEkubo(token: TokenInfo, amountIn?: Web3Number, retry?: number): Promise<number>;
97
86
  }
98
-
99
- declare class Pragma {
100
- contractAddr: string;
101
- readonly contract: Contract;
102
- constructor(provider: RpcProvider);
103
- getPrice(tokenAddr: string): Promise<number>;
87
+ interface IInvestmentFlow {
88
+ title: string;
89
+ subItems: string[];
90
+ linkedFlows: IInvestmentFlow[];
104
91
  }
92
+ declare function getMainnetConfig(rpcUrl?: string, blockIdentifier?: BlockIdentifier): IConfig;
105
93
 
106
94
  interface ILendingMetadata {
107
95
  name: string;
@@ -155,6 +143,48 @@ declare abstract class Initializable {
155
143
  waitForInitilisation(): Promise<void>;
156
144
  }
157
145
 
146
+ declare abstract class PricerBase {
147
+ readonly config: IConfig;
148
+ readonly tokens: TokenInfo[];
149
+ constructor(config: IConfig, tokens: TokenInfo[]);
150
+ getPrice(tokenSymbol: string): Promise<PriceInfo>;
151
+ }
152
+
153
+ interface PriceInfo {
154
+ price: number;
155
+ timestamp: Date;
156
+ }
157
+ declare class Pricer extends PricerBase {
158
+ protected prices: {
159
+ [key: string]: PriceInfo;
160
+ };
161
+ private methodToUse;
162
+ /**
163
+ * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
164
+ */
165
+ protected PRICE_API: string;
166
+ protected EKUBO_API: string;
167
+ constructor(config: IConfig, tokens: TokenInfo[]);
168
+ isReady(): boolean;
169
+ waitTillReady(): Promise<void>;
170
+ start(): void;
171
+ isStale(timestamp: Date, tokenName: string): boolean;
172
+ assertNotStale(timestamp: Date, tokenName: string): void;
173
+ getPrice(tokenSymbol: string): Promise<PriceInfo>;
174
+ protected _loadPrices(onUpdate?: (tokenSymbol: string) => void): void;
175
+ _getPrice(token: TokenInfo, defaultMethod?: string): Promise<number>;
176
+ _getPriceCoinbase(token: TokenInfo): Promise<number>;
177
+ _getPriceCoinMarketCap(token: TokenInfo): Promise<number>;
178
+ _getPriceEkubo(token: TokenInfo, amountIn?: Web3Number, retry?: number): Promise<number>;
179
+ }
180
+
181
+ declare class Pragma {
182
+ contractAddr: string;
183
+ readonly contract: Contract;
184
+ constructor(provider: RpcProvider);
185
+ getPrice(tokenAddr: string): Promise<number>;
186
+ }
187
+
158
188
  declare class ZkLend extends ILending implements ILending {
159
189
  readonly pricer: Pricer;
160
190
  static readonly POOLS_URL = "https://app.zklend.com/api/pools";
@@ -186,6 +216,15 @@ declare class ZkLend extends ILending implements ILending {
186
216
  getPositions(user: ContractAddr): Promise<ILendingPosition[]>;
187
217
  }
188
218
 
219
+ declare class PricerFromApi extends PricerBase {
220
+ constructor(config: IConfig, tokens: TokenInfo[]);
221
+ getPrice(tokenSymbol: string): Promise<PriceInfo>;
222
+ getPriceFromMyAPI(tokenSymbol: string): Promise<{
223
+ price: number;
224
+ timestamp: Date;
225
+ }>;
226
+ }
227
+
189
228
  declare const logger: {
190
229
  verbose(message: string): void;
191
230
  assert(condition?: boolean, ...data: any[]): void;
@@ -441,6 +480,7 @@ declare class VesuRebalance {
441
480
  * @returns Populated contract call for rebalance
442
481
  */
443
482
  getRebalanceCall(pools: Awaited<ReturnType<typeof this.getRebalancedPositions>>['changes'], isOverWeightAdjustment: boolean): Promise<starknet.Call | null>;
483
+ getInvestmentFlows(pools: PoolInfoFull[]): Promise<IInvestmentFlow[]>;
444
484
  }
445
485
  /**
446
486
  * Represents the Vesu Rebalance Strategies.
@@ -554,4 +594,4 @@ declare class PasswordJsonCryptoUtil {
554
594
  decrypt(encryptedData: string, password: string): any;
555
595
  }
556
596
 
557
- export { type AccountInfo, type AllAccountsStore, AutoCompounderSTRK, ContractAddr, FatalError, Global, type IConfig, ILending, type ILendingMetadata, type ILendingPosition, type IProtocol, type IStrategyMetadata, Initializable, type LendingToken, MarginType, Network, PasswordJsonCryptoUtil, Pragma, type PriceInfo, Pricer, PricerBase, PricerRedis, type RequiredFields, type RequiredKeys, type RequiredStoreConfig, Store, type StoreConfig, TelegramNotif, type TokenInfo, VesuRebalance, VesuRebalanceStrategies, Web3Number, ZkLend, assert, getDefaultStoreConfig, getMainnetConfig, logger };
597
+ export { type AccountInfo, type AllAccountsStore, AutoCompounderSTRK, ContractAddr, FatalError, Global, type IConfig, type IInvestmentFlow, ILending, type ILendingMetadata, type ILendingPosition, type IProtocol, type IStrategyMetadata, Initializable, type LendingToken, MarginType, Network, PasswordJsonCryptoUtil, Pragma, type PriceInfo, Pricer, PricerFromApi, PricerRedis, type RequiredFields, type RequiredKeys, type RequiredStoreConfig, type RiskFactor, RiskType, Store, type StoreConfig, TelegramNotif, type TokenInfo, VesuRebalance, VesuRebalanceStrategies, Web3Number, ZkLend, assert, getDefaultStoreConfig, getMainnetConfig, logger };
package/dist/index.js CHANGED
@@ -41,8 +41,9 @@ __export(src_exports, {
41
41
  PasswordJsonCryptoUtil: () => PasswordJsonCryptoUtil,
42
42
  Pragma: () => Pragma,
43
43
  Pricer: () => Pricer,
44
- PricerBase: () => PricerBase,
44
+ PricerFromApi: () => PricerFromApi,
45
45
  PricerRedis: () => PricerRedis,
46
+ RiskType: () => RiskType,
46
47
  Store: () => Store,
47
48
  TelegramNotif: () => TelegramNotif,
48
49
  VesuRebalance: () => VesuRebalance,
@@ -79,6 +80,7 @@ var FatalError = class extends Error {
79
80
  var tokens = [{
80
81
  name: "Starknet",
81
82
  symbol: "STRK",
83
+ logo: "https://assets.coingecko.com/coins/images/26433/small/starknet.png",
82
84
  address: "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
83
85
  decimals: 18,
84
86
  coingeckId: "starknet"
@@ -111,6 +113,7 @@ var Global = class {
111
113
  symbol: token.symbol,
112
114
  address: token.address,
113
115
  decimals: token.decimals,
116
+ logo: token.logoUri,
114
117
  coingeckId: token.extensions.coingeckoId
115
118
  });
116
119
  });
@@ -190,16 +193,22 @@ var ContractAddr = class _ContractAddr {
190
193
  }
191
194
  };
192
195
 
193
- // src/modules/pricer.ts
194
- var CoinMarketCap = require("coinmarketcap-api");
196
+ // src/modules/pricerBase.ts
195
197
  var PricerBase = class {
198
+ constructor(config, tokens2) {
199
+ this.config = config;
200
+ this.tokens = tokens2;
201
+ }
196
202
  async getPrice(tokenSymbol) {
197
203
  throw new Error("Method not implemented");
198
204
  }
199
205
  };
200
- var Pricer = class {
206
+
207
+ // src/modules/pricer.ts
208
+ var Pricer = class extends PricerBase {
209
+ // e.g. ETH/USDC
201
210
  constructor(config, tokens2) {
202
- this.tokens = [];
211
+ super(config, tokens2);
203
212
  this.prices = {};
204
213
  // code populates this map during runtime to determine which method to use for a given token
205
214
  // The method set will be the first one to try after first attempt
@@ -209,11 +218,6 @@ var Pricer = class {
209
218
  */
210
219
  this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
211
220
  this.EKUBO_API = "https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
212
- // e.g. ETH/USDC
213
- // backup oracle001
214
- this.client = new CoinMarketCap(process.env.COINMARKETCAP_KEY);
215
- this.config = config;
216
- this.tokens = tokens2;
217
221
  }
218
222
  isReady() {
219
223
  const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
@@ -345,10 +349,7 @@ var Pricer = class {
345
349
  return Number(data.data.amount);
346
350
  }
347
351
  async _getPriceCoinMarketCap(token) {
348
- const result = await this.client.getQuotes({ symbol: token.symbol });
349
- if (result.data)
350
- return result.data[token.symbol].quote.USD.price;
351
- throw new Error(result);
352
+ throw new Error("Not implemented");
352
353
  }
353
354
  async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
354
355
  const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
@@ -542,6 +543,7 @@ var _ZkLend = class _ZkLend extends ILending {
542
543
  name: pool.token.name,
543
544
  symbol: pool.token.symbol,
544
545
  address: savedTokenInfo?.address || "",
546
+ logo: "",
545
547
  decimals: pool.token.decimals,
546
548
  borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
547
549
  collareralFactor
@@ -651,8 +653,72 @@ var _ZkLend = class _ZkLend extends ILending {
651
653
  _ZkLend.POOLS_URL = "https://app.zklend.com/api/pools";
652
654
  var ZkLend = _ZkLend;
653
655
 
656
+ // src/modules/pricer-from-api.ts
657
+ var import_axios4 = __toESM(require("axios"));
658
+ var PricerFromApi = class extends PricerBase {
659
+ constructor(config, tokens2) {
660
+ super(config, tokens2);
661
+ }
662
+ async getPrice(tokenSymbol) {
663
+ try {
664
+ return await this.getPriceFromMyAPI(tokenSymbol);
665
+ } catch (e) {
666
+ logger.warn("getPriceFromMyAPI error", e);
667
+ }
668
+ logger.log("getPrice coinbase", tokenSymbol);
669
+ let retry = 0;
670
+ const MAX_RETRIES = 5;
671
+ for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
672
+ try {
673
+ const priceInfo = await import_axios4.default.get(
674
+ `https://api.coinbase.com/v2/prices/${tokenSymbol}-USDT/spot`
675
+ );
676
+ if (!priceInfo) {
677
+ throw new Error("Failed to fetch price");
678
+ }
679
+ const data = await priceInfo.data;
680
+ const price = Number(data.data.amount);
681
+ return {
682
+ price,
683
+ timestamp: /* @__PURE__ */ new Date()
684
+ };
685
+ } catch (e) {
686
+ logger.warn("getPrice coinbase error", e, retry);
687
+ await new Promise((resolve) => setTimeout(resolve, retry * 1e3));
688
+ }
689
+ }
690
+ throw new Error(`Failed to fetch price for ${tokenSymbol}`);
691
+ }
692
+ async getPriceFromMyAPI(tokenSymbol) {
693
+ logger.verbose(`getPrice from redis: ${tokenSymbol}`);
694
+ const endpoint = "https://app.strkfarm.com";
695
+ const url = `${endpoint}/api/price/${tokenSymbol}`;
696
+ const priceInfoRes = await fetch(url);
697
+ const priceInfo = await priceInfoRes.json();
698
+ const now = /* @__PURE__ */ new Date();
699
+ const priceTime = new Date(priceInfo.timestamp);
700
+ if (now.getTime() - priceTime.getTime() > 9e5) {
701
+ throw new Error("Price is stale");
702
+ }
703
+ const price = Number(priceInfo.price);
704
+ return {
705
+ price,
706
+ timestamp: new Date(priceInfo.timestamp)
707
+ };
708
+ }
709
+ };
710
+
654
711
  // src/interfaces/common.ts
655
712
  var import_starknet3 = require("starknet");
713
+ var RiskType = /* @__PURE__ */ ((RiskType2) => {
714
+ RiskType2["MARKET_RISK"] = "MARKET_RISK";
715
+ RiskType2["IMPERMANENT_LOSS"] = "IMPERMANENT_LOSS";
716
+ RiskType2["LIQUIDITY_RISK"] = "LIQUIDITY_RISK";
717
+ RiskType2["SMART_CONTRACT_RISK"] = "SMART_CONTRACT_RISK";
718
+ RiskType2["TECHNICAL_RISK"] = "TECHNICAL_RISK";
719
+ RiskType2["COUNTERPARTY_RISK"] = "COUNTERPARTY_RISK";
720
+ return RiskType2;
721
+ })(RiskType || {});
656
722
  var Network = /* @__PURE__ */ ((Network2) => {
657
723
  Network2["mainnet"] = "mainnet";
658
724
  Network2["sepolia"] = "sepolia";
@@ -2234,7 +2300,7 @@ function assert(condition, message) {
2234
2300
  }
2235
2301
 
2236
2302
  // src/strategies/vesu-rebalance.ts
2237
- var import_axios4 = __toESM(require("axios"));
2303
+ var import_axios5 = __toESM(require("axios"));
2238
2304
  var VesuRebalance = class _VesuRebalance {
2239
2305
  // 10000 bps = 100%
2240
2306
  /**
@@ -2341,7 +2407,7 @@ var VesuRebalance = class _VesuRebalance {
2341
2407
  let isErrorPositionsAPI = false;
2342
2408
  let vesuPositions = [];
2343
2409
  try {
2344
- const res = await import_axios4.default.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
2410
+ const res = await import_axios5.default.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
2345
2411
  const data2 = await res.data;
2346
2412
  vesuPositions = data2.data;
2347
2413
  } catch (e) {
@@ -2351,7 +2417,7 @@ var VesuRebalance = class _VesuRebalance {
2351
2417
  let isErrorPoolsAPI = false;
2352
2418
  let pools = [];
2353
2419
  try {
2354
- const res = await import_axios4.default.get(`https://api.vesu.xyz/pools`);
2420
+ const res = await import_axios5.default.get(`https://api.vesu.xyz/pools`);
2355
2421
  const data2 = await res.data;
2356
2422
  pools = data2.data;
2357
2423
  } catch (e) {
@@ -2511,16 +2577,47 @@ var VesuRebalance = class _VesuRebalance {
2511
2577
  }
2512
2578
  return this.contract.populate("rebalance", [actions]);
2513
2579
  }
2580
+ async getInvestmentFlows(pools) {
2581
+ const netYield = this.netAPYGivenPools(pools);
2582
+ const baseFlow = {
2583
+ title: "Deposit $1000",
2584
+ subItems: [`Net yield: ${(netYield * 100).toFixed(2)}%`],
2585
+ linkedFlows: []
2586
+ };
2587
+ pools.forEach((p) => {
2588
+ if (p.amount.eq(0)) return;
2589
+ const flow = {
2590
+ title: `${p.pool_name} - $${(p.current_weight * 1e3).toFixed(2)}`,
2591
+ subItems: [
2592
+ `APY: ${(p.APY.netApy * 100).toFixed(2)}%`,
2593
+ `Weight: ${(p.current_weight * 100).toFixed(2)}% / ${(p.max_weight * 100).toFixed(2)}%`
2594
+ ],
2595
+ linkedFlows: []
2596
+ };
2597
+ baseFlow.linkedFlows.push(flow);
2598
+ });
2599
+ return [baseFlow];
2600
+ }
2514
2601
  };
2515
2602
  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.";
2516
2603
  var _protocol = { name: "Vesu", logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png" };
2604
+ var _riskFactor = [
2605
+ { type: "SMART_CONTRACT_RISK" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
2606
+ { type: "TECHNICAL_RISK" /* TECHNICAL_RISK */, value: 0.5, weight: 25 },
2607
+ { type: "COUNTERPARTY_RISK" /* COUNTERPARTY_RISK */, value: 1, weight: 50 }
2608
+ ];
2517
2609
  var VesuRebalanceStrategies = [{
2518
2610
  name: "Vesu STRK",
2519
2611
  description: _description.replace("{{TOKEN}}", "STRK"),
2520
2612
  address: ContractAddr.from("0xeeb729d554ae486387147b13a9c8871bc7991d454e8b5ff570d4bf94de71e1"),
2521
2613
  type: "ERC4626",
2522
2614
  depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
2523
- protocols: [_protocol]
2615
+ protocols: [_protocol],
2616
+ maxTVL: Web3Number.fromWei("0", 18),
2617
+ risk: {
2618
+ riskFactor: _riskFactor,
2619
+ netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / 100
2620
+ }
2524
2621
  }];
2525
2622
 
2526
2623
  // src/notifs/telegram.ts
@@ -2797,8 +2894,9 @@ var Store = class _Store {
2797
2894
  PasswordJsonCryptoUtil,
2798
2895
  Pragma,
2799
2896
  Pricer,
2800
- PricerBase,
2897
+ PricerFromApi,
2801
2898
  PricerRedis,
2899
+ RiskType,
2802
2900
  Store,
2803
2901
  TelegramNotif,
2804
2902
  VesuRebalance,
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,8 +594,72 @@ 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";
@@ -2183,7 +2241,7 @@ function assert(condition, message) {
2183
2241
  }
2184
2242
 
2185
2243
  // src/strategies/vesu-rebalance.ts
2186
- import axios4 from "axios";
2244
+ import axios5 from "axios";
2187
2245
  var VesuRebalance = class _VesuRebalance {
2188
2246
  // 10000 bps = 100%
2189
2247
  /**
@@ -2290,7 +2348,7 @@ var VesuRebalance = class _VesuRebalance {
2290
2348
  let isErrorPositionsAPI = false;
2291
2349
  let vesuPositions = [];
2292
2350
  try {
2293
- const res = await axios4.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
2351
+ const res = await axios5.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
2294
2352
  const data2 = await res.data;
2295
2353
  vesuPositions = data2.data;
2296
2354
  } catch (e) {
@@ -2300,7 +2358,7 @@ var VesuRebalance = class _VesuRebalance {
2300
2358
  let isErrorPoolsAPI = false;
2301
2359
  let pools = [];
2302
2360
  try {
2303
- const res = await axios4.get(`https://api.vesu.xyz/pools`);
2361
+ const res = await axios5.get(`https://api.vesu.xyz/pools`);
2304
2362
  const data2 = await res.data;
2305
2363
  pools = data2.data;
2306
2364
  } catch (e) {
@@ -2460,16 +2518,47 @@ var VesuRebalance = class _VesuRebalance {
2460
2518
  }
2461
2519
  return this.contract.populate("rebalance", [actions]);
2462
2520
  }
2521
+ async getInvestmentFlows(pools) {
2522
+ const netYield = this.netAPYGivenPools(pools);
2523
+ const baseFlow = {
2524
+ title: "Deposit $1000",
2525
+ subItems: [`Net yield: ${(netYield * 100).toFixed(2)}%`],
2526
+ linkedFlows: []
2527
+ };
2528
+ pools.forEach((p) => {
2529
+ if (p.amount.eq(0)) return;
2530
+ const flow = {
2531
+ title: `${p.pool_name} - $${(p.current_weight * 1e3).toFixed(2)}`,
2532
+ subItems: [
2533
+ `APY: ${(p.APY.netApy * 100).toFixed(2)}%`,
2534
+ `Weight: ${(p.current_weight * 100).toFixed(2)}% / ${(p.max_weight * 100).toFixed(2)}%`
2535
+ ],
2536
+ linkedFlows: []
2537
+ };
2538
+ baseFlow.linkedFlows.push(flow);
2539
+ });
2540
+ return [baseFlow];
2541
+ }
2463
2542
  };
2464
2543
  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
2544
  var _protocol = { name: "Vesu", logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png" };
2545
+ var _riskFactor = [
2546
+ { type: "SMART_CONTRACT_RISK" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
2547
+ { type: "TECHNICAL_RISK" /* TECHNICAL_RISK */, value: 0.5, weight: 25 },
2548
+ { type: "COUNTERPARTY_RISK" /* COUNTERPARTY_RISK */, value: 1, weight: 50 }
2549
+ ];
2466
2550
  var VesuRebalanceStrategies = [{
2467
2551
  name: "Vesu STRK",
2468
2552
  description: _description.replace("{{TOKEN}}", "STRK"),
2469
2553
  address: ContractAddr.from("0xeeb729d554ae486387147b13a9c8871bc7991d454e8b5ff570d4bf94de71e1"),
2470
2554
  type: "ERC4626",
2471
2555
  depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
2472
- protocols: [_protocol]
2556
+ protocols: [_protocol],
2557
+ maxTVL: Web3Number.fromWei("0", 18),
2558
+ risk: {
2559
+ riskFactor: _riskFactor,
2560
+ netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / 100
2561
+ }
2473
2562
  }];
2474
2563
 
2475
2564
  // src/notifs/telegram.ts
@@ -2745,8 +2834,9 @@ export {
2745
2834
  PasswordJsonCryptoUtil,
2746
2835
  Pragma,
2747
2836
  Pricer,
2748
- PricerBase,
2837
+ PricerFromApi,
2749
2838
  PricerRedis,
2839
+ RiskType,
2750
2840
  Store,
2751
2841
  TelegramNotif,
2752
2842
  VesuRebalance,