@strkfarm/sdk 1.0.9 → 1.0.11

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
@@ -8,7 +8,7 @@ interface TokenInfo {
8
8
  symbol: string;
9
9
  address: string;
10
10
  decimals: number;
11
- pricerKey?: string;
11
+ coingeckId?: string;
12
12
  }
13
13
  declare enum Network {
14
14
  mainnet = "mainnet",
@@ -23,6 +23,32 @@ interface IConfig {
23
23
  }
24
24
  declare function getMainnetConfig(rpcUrl?: string, blockIdentifier?: BlockIdentifier): IConfig;
25
25
 
26
+ declare class Web3Number extends BigNumber {
27
+ decimals: number;
28
+ constructor(value: string | number, decimals: number);
29
+ static fromWei(weiNumber: string | number, decimals: number): Web3Number;
30
+ toWei(): string;
31
+ multipliedBy(value: string | number): Web3Number;
32
+ dividedBy(value: string | number): Web3Number;
33
+ plus(value: string | number): Web3Number;
34
+ minus(n: number | string, base?: number): Web3Number;
35
+ toString(base?: number | undefined): string;
36
+ }
37
+
38
+ /**
39
+ * A simple wrapper around a contract address that is universally comparable
40
+ * - Helps avoid padding issues
41
+ */
42
+ declare class ContractAddr {
43
+ readonly address: string;
44
+ constructor(address: string);
45
+ static from(address: string): ContractAddr;
46
+ eq(other: ContractAddr): boolean;
47
+ eqString(other: string): boolean;
48
+ static standardise(address: string | bigint): string;
49
+ static eqString(a: string, b: string): boolean;
50
+ }
51
+
26
52
  interface PriceInfo {
27
53
  price: number;
28
54
  timestamp: Date;
@@ -33,10 +59,12 @@ declare class Pricer {
33
59
  protected prices: {
34
60
  [key: string]: PriceInfo;
35
61
  };
62
+ private methodToUse;
36
63
  /**
37
64
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
38
65
  */
39
66
  protected PRICE_API: string;
67
+ protected EKUBO_API: string;
40
68
  protected client: any;
41
69
  constructor(config: IConfig, tokens: TokenInfo[]);
42
70
  isReady(): boolean;
@@ -46,9 +74,10 @@ declare class Pricer {
46
74
  assertNotStale(timestamp: Date, tokenName: string): void;
47
75
  getPrice(tokenName: string): Promise<PriceInfo>;
48
76
  protected _loadPrices(onUpdate?: (tokenSymbol: string) => void): void;
49
- _getPrice(token: TokenInfo): Promise<number>;
77
+ _getPrice(token: TokenInfo, defaultMethod?: string): Promise<number>;
50
78
  _getPriceCoinbase(token: TokenInfo): Promise<number>;
51
79
  _getPriceCoinMarketCap(token: TokenInfo): Promise<number>;
80
+ _getPriceEkubo(token: TokenInfo, amountIn?: Web3Number, retry?: number): Promise<number>;
52
81
  }
53
82
 
54
83
  declare class Pragma {
@@ -58,32 +87,6 @@ declare class Pragma {
58
87
  getPrice(tokenAddr: string): Promise<number>;
59
88
  }
60
89
 
61
- /**
62
- * A simple wrapper around a contract address that is universally comparable
63
- * - Helps avoid padding issues
64
- */
65
- declare class ContractAddr {
66
- readonly address: string;
67
- constructor(address: string);
68
- static from(address: string): ContractAddr;
69
- eq(other: ContractAddr): boolean;
70
- eqString(other: string): boolean;
71
- static standardise(address: string | bigint): string;
72
- static eqString(a: string, b: string): boolean;
73
- }
74
-
75
- declare class Web3Number extends BigNumber {
76
- decimals: number;
77
- constructor(value: string | number, decimals: number);
78
- static fromWei(weiNumber: string | number, decimals: number): Web3Number;
79
- toWei(): string;
80
- multipliedBy(value: string | number): Web3Number;
81
- dividedBy(value: string | number): Web3Number;
82
- plus(value: string | number): Web3Number;
83
- minus(n: number | string, base?: number): Web3Number;
84
- toString(base?: number | undefined): string;
85
- }
86
-
87
90
  interface ILendingMetadata {
88
91
  name: string;
89
92
  logo: string;
package/dist/index.js CHANGED
@@ -53,90 +53,10 @@ __export(src_exports, {
53
53
  module.exports = __toCommonJS(src_exports);
54
54
 
55
55
  // src/modules/pricer.ts
56
- var import_axios = __toESM(require("axios"));
57
-
58
- // src/data/tokens.json
59
- var tokens_default = [
60
- {
61
- name: "Ether",
62
- symbol: "ETH",
63
- address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
64
- decimals: 18,
65
- pricerKey: "ETH-USDT"
66
- },
67
- {
68
- name: "USD Coin",
69
- symbol: "USDC",
70
- address: "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8",
71
- decimals: 6,
72
- pricerKey: "USDC-USDT"
73
- },
74
- {
75
- name: "Wrapped BTC",
76
- symbol: "WBTC",
77
- address: "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
78
- decimals: 8,
79
- pricerKey: "WBTC-USDT"
80
- },
81
- {
82
- name: "Tether USD",
83
- symbol: "USDT",
84
- address: "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
85
- decimals: 6,
86
- pricerKey: "USDT-USDT"
87
- },
88
- {
89
- name: "Dai Stablecoin",
90
- symbol: "DAIv0",
91
- address: "",
92
- decimals: 18,
93
- pricerKey: "DAI-USDT"
94
- },
95
- {
96
- name: "Starknet Wrapped Staked Ether",
97
- symbol: "wstETH",
98
- address: "0x042b8f0484674ca266ac5d08e4ac6a3fe65bd3129795def2dca5c34ecc5f96d2",
99
- decimals: 18,
100
- pricerKey: "wstETH-USDT"
101
- },
102
- {
103
- name: "Starknet Token",
104
- symbol: "STRK",
105
- address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
106
- decimals: 18,
107
- pricerKey: "STRK-USDT"
108
- },
109
- {
110
- name: "zkLend Token",
111
- symbol: "ZEND",
112
- address: "",
113
- decimals: 18,
114
- pricerKey: "ZEND-USDT"
115
- },
116
- {
117
- name: "Dai Stablecoin",
118
- symbol: "DAI",
119
- address: "",
120
- decimals: 18,
121
- pricerKey: "DAI-USDT"
122
- },
123
- {
124
- name: "Ekubo Protocol",
125
- symbol: "EKUBO",
126
- address: "",
127
- decimals: 18,
128
- pricerKey: "DAI-USDT"
129
- },
130
- {
131
- name: "kSTRK token",
132
- symbol: "kSTRK",
133
- address: "",
134
- decimals: 18,
135
- pricerKey: "DAI-USDT"
136
- }
137
- ];
56
+ var import_axios2 = __toESM(require("axios"));
138
57
 
139
58
  // src/global.ts
59
+ var import_axios = __toESM(require("axios"));
140
60
  var logger = {
141
61
  ...console,
142
62
  verbose(message) {
@@ -152,6 +72,7 @@ var FatalError = class extends Error {
152
72
  this.name = "FatalError";
153
73
  }
154
74
  };
75
+ var tokens = [];
155
76
  var Global = class {
156
77
  static fatalError(message, err) {
157
78
  logger.error(message);
@@ -165,7 +86,23 @@ var Global = class {
165
86
  console.error(err);
166
87
  }
167
88
  static async getTokens() {
168
- return tokens_default;
89
+ if (tokens.length) return tokens;
90
+ const data = await import_axios.default.get("https://starknet.api.avnu.fi/v1/starknet/tokens");
91
+ const tokensData = data.data.content;
92
+ tokensData.forEach((token) => {
93
+ if (!token.tags.includes("AVNU") || !token.tags.includes("Verified")) {
94
+ return;
95
+ }
96
+ tokens.push({
97
+ name: token.name,
98
+ symbol: token.symbol,
99
+ address: token.address,
100
+ decimals: token.decimals,
101
+ coingeckId: token.extensions.coingeckoId
102
+ });
103
+ });
104
+ console.log(tokens);
105
+ return tokens;
169
106
  }
170
107
  static assert(condition, message) {
171
108
  if (!condition) {
@@ -174,20 +111,89 @@ var Global = class {
174
111
  }
175
112
  };
176
113
 
114
+ // src/dataTypes/bignumber.ts
115
+ var import_bignumber = __toESM(require("bignumber.js"));
116
+ var Web3Number = class _Web3Number extends import_bignumber.default {
117
+ constructor(value, decimals) {
118
+ super(value);
119
+ this.decimals = decimals;
120
+ }
121
+ static fromWei(weiNumber, decimals) {
122
+ const bn = new _Web3Number(weiNumber, decimals).dividedBy(10 ** decimals);
123
+ return new _Web3Number(bn.toString(), decimals);
124
+ }
125
+ toWei() {
126
+ return this.mul(10 ** this.decimals).toFixed(0);
127
+ }
128
+ multipliedBy(value) {
129
+ return new _Web3Number(this.mul(value).toString(), this.decimals);
130
+ }
131
+ dividedBy(value) {
132
+ return new _Web3Number(this.div(value).toString(), this.decimals);
133
+ }
134
+ plus(value) {
135
+ return new _Web3Number(this.add(value).toString(), this.decimals);
136
+ }
137
+ minus(n, base) {
138
+ return new _Web3Number(super.minus(n, base).toString(), this.decimals);
139
+ }
140
+ toString(base) {
141
+ return super.toString(base);
142
+ }
143
+ // [customInspectSymbol](depth: any, inspectOptions: any, inspect: any) {
144
+ // return this.toString();
145
+ // }
146
+ };
147
+ import_bignumber.default.config({ DECIMAL_PLACES: 18 });
148
+ Web3Number.config({ DECIMAL_PLACES: 18 });
149
+
150
+ // src/dataTypes/address.ts
151
+ var import_starknet = require("starknet");
152
+ var ContractAddr = class _ContractAddr {
153
+ constructor(address) {
154
+ this.address = _ContractAddr.standardise(address);
155
+ }
156
+ static from(address) {
157
+ return new _ContractAddr(address);
158
+ }
159
+ eq(other) {
160
+ return this.address === other.address;
161
+ }
162
+ eqString(other) {
163
+ return this.address === _ContractAddr.standardise(other);
164
+ }
165
+ static standardise(address) {
166
+ let _a = address;
167
+ if (!address) {
168
+ _a = "0";
169
+ }
170
+ const a = import_starknet.num.getHexString(import_starknet.num.getDecimalString(_a.toString()));
171
+ return a;
172
+ }
173
+ static eqString(a, b) {
174
+ return _ContractAddr.standardise(a) === _ContractAddr.standardise(b);
175
+ }
176
+ };
177
+
177
178
  // src/modules/pricer.ts
178
179
  var CoinMarketCap = require("coinmarketcap-api");
179
180
  var Pricer = class {
180
- constructor(config, tokens) {
181
+ constructor(config, tokens2) {
181
182
  this.tokens = [];
182
183
  this.prices = {};
184
+ // code populates this map during runtime to determine which method to use for a given token
185
+ // The method set will be the first one to try after first attempt
186
+ this.methodToUse = {};
183
187
  /**
184
188
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
185
189
  */
186
190
  this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
187
- // backup oracle
191
+ this.EKUBO_API = "https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
192
+ // e.g. ETH/USDC
193
+ // backup oracle001
188
194
  this.client = new CoinMarketCap(process.env.COINMARKETCAP_KEY);
189
195
  this.config = config;
190
- this.tokens = tokens;
196
+ this.tokens = tokens2;
191
197
  }
192
198
  isReady() {
193
199
  const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
@@ -271,39 +277,77 @@ var Pricer = class {
271
277
  });
272
278
  if (this.isReady() && this.config.heartbeatUrl) {
273
279
  console.log(`sending beat`);
274
- import_axios.default.get(this.config.heartbeatUrl).catch((err) => {
280
+ import_axios2.default.get(this.config.heartbeatUrl).catch((err) => {
275
281
  console.error("Pricer: Heartbeat err", err);
276
282
  });
277
283
  }
278
284
  }
279
- async _getPrice(token) {
280
- try {
281
- return await this._getPriceCoinbase(token);
282
- } catch (error) {
285
+ async _getPrice(token, defaultMethod = "all") {
286
+ const methodToUse = this.methodToUse[token.symbol] || defaultMethod;
287
+ logger.info(`Fetching price of ${token.symbol} using ${methodToUse}`);
288
+ switch (methodToUse) {
289
+ case "Coinbase":
290
+ try {
291
+ const result = await this._getPriceCoinbase(token);
292
+ this.methodToUse[token.symbol] = "Coinbase";
293
+ return result;
294
+ } catch (error) {
295
+ console.warn(`Coinbase: price err: message [${token.symbol}]: `, error.message);
296
+ }
297
+ case "Coinmarketcap":
298
+ try {
299
+ const result = await this._getPriceCoinMarketCap(token);
300
+ this.methodToUse[token.symbol] = "Coinmarketcap";
301
+ return result;
302
+ } catch (error) {
303
+ console.warn(`CoinMarketCap: price err [${token.symbol}]: `, Object.keys(error));
304
+ console.warn(`CoinMarketCap: price err [${token.symbol}]: `, error.message);
305
+ }
306
+ case "Ekubo":
307
+ try {
308
+ const result = await this._getPriceEkubo(token);
309
+ this.methodToUse[token.symbol] = "Ekubo";
310
+ return result;
311
+ } catch (error) {
312
+ console.warn(`Ekubo: price err [${token.symbol}]: `, error.message);
313
+ console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
314
+ }
283
315
  }
284
- try {
285
- return await this._getPriceCoinMarketCap(token);
286
- } catch (error) {
316
+ if (defaultMethod == "all") {
317
+ return await this._getPrice(token, "Coinbase");
287
318
  }
288
- throw new FatalError(`Price not found for ${token.name}`);
319
+ throw new FatalError(`Price not found for ${token.symbol}`);
289
320
  }
290
321
  async _getPriceCoinbase(token) {
291
- if (!token.pricerKey) {
292
- throw new FatalError(`Pricer key not found for ${token.name}`);
293
- }
294
- const url = this.PRICE_API.replace("{{PRICER_KEY}}", token.pricerKey);
295
- const result = await import_axios.default.get(url);
322
+ const url = this.PRICE_API.replace("{{PRICER_KEY}}", `${token.symbol}-USD`);
323
+ const result = await import_axios2.default.get(url);
296
324
  const data = result.data;
297
325
  return Number(data.data.amount);
298
326
  }
299
327
  async _getPriceCoinMarketCap(token) {
300
328
  const result = await this.client.getQuotes({ symbol: token.symbol });
301
- return result.data[token.symbol].quote.USD.price;
329
+ if (result.data)
330
+ return result.data[token.symbol].quote.USD.price;
331
+ throw new Error(result);
332
+ }
333
+ async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
334
+ const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
335
+ const result = await import_axios2.default.get(url);
336
+ const data = result.data;
337
+ const outputUSDC = Number(Web3Number.fromWei(data.total_calculated, 6).toFixed(6));
338
+ logger.verbose(`Ekubo: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
339
+ if (outputUSDC === 0 && retry < 3) {
340
+ const amountIn2 = new Web3Number(100, token.decimals);
341
+ return await this._getPriceEkubo(token, amountIn2, retry + 1);
342
+ }
343
+ const usdcPrice = (await this.getPrice("USDC")).price;
344
+ logger.verbose(`USDC Price: ${usdcPrice}`);
345
+ return outputUSDC * usdcPrice;
302
346
  }
303
347
  };
304
348
 
305
349
  // src/modules/pragma.ts
306
- var import_starknet = require("starknet");
350
+ var import_starknet2 = require("starknet");
307
351
 
308
352
  // src/data/pragma.abi.json
309
353
  var pragma_abi_default = [
@@ -407,7 +451,7 @@ var pragma_abi_default = [
407
451
  var Pragma = class {
408
452
  constructor(provider) {
409
453
  this.contractAddr = "0x023fb3afbff2c0e3399f896dcf7400acf1a161941cfb386e34a123f228c62832";
410
- this.contract = new import_starknet.Contract(pragma_abi_default, this.contractAddr, provider);
454
+ this.contract = new import_starknet2.Contract(pragma_abi_default, this.contractAddr, provider);
411
455
  }
412
456
  async getPrice(tokenAddr) {
413
457
  if (!tokenAddr) {
@@ -421,43 +465,7 @@ var Pragma = class {
421
465
  };
422
466
 
423
467
  // src/modules/zkLend.ts
424
- var import_axios2 = __toESM(require("axios"));
425
-
426
- // src/dataTypes/bignumber.ts
427
- var import_bignumber = __toESM(require("bignumber.js"));
428
- var Web3Number = class _Web3Number extends import_bignumber.default {
429
- constructor(value, decimals) {
430
- super(value);
431
- this.decimals = decimals;
432
- }
433
- static fromWei(weiNumber, decimals) {
434
- const bn = new _Web3Number(weiNumber, decimals).dividedBy(10 ** decimals);
435
- return new _Web3Number(bn.toString(), decimals);
436
- }
437
- toWei() {
438
- return this.mul(10 ** this.decimals).toFixed(0);
439
- }
440
- multipliedBy(value) {
441
- return new _Web3Number(this.mul(value).toString(), this.decimals);
442
- }
443
- dividedBy(value) {
444
- return new _Web3Number(this.div(value).toString(), this.decimals);
445
- }
446
- plus(value) {
447
- return new _Web3Number(this.add(value).toString(), this.decimals);
448
- }
449
- minus(n, base) {
450
- return new _Web3Number(super.minus(n, base).toString(), this.decimals);
451
- }
452
- toString(base) {
453
- return super.toString(base);
454
- }
455
- // [customInspectSymbol](depth: any, inspectOptions: any, inspect: any) {
456
- // return this.toString();
457
- // }
458
- };
459
- import_bignumber.default.config({ DECIMAL_PLACES: 18 });
460
- Web3Number.config({ DECIMAL_PLACES: 18 });
468
+ var import_axios3 = __toESM(require("axios"));
461
469
 
462
470
  // src/interfaces/lending.ts
463
471
  var MarginType = /* @__PURE__ */ ((MarginType2) => {
@@ -501,7 +509,7 @@ var _ZkLend = class _ZkLend extends ILending {
501
509
  async init() {
502
510
  try {
503
511
  logger.verbose(`Initialising ${this.metadata.name}`);
504
- const result = await import_axios2.default.get(_ZkLend.POOLS_URL);
512
+ const result = await import_axios3.default.get(_ZkLend.POOLS_URL);
505
513
  const data = result.data;
506
514
  const savedTokens = await Global.getTokens();
507
515
  data.forEach((pool) => {
@@ -591,7 +599,7 @@ var _ZkLend = class _ZkLend extends ILending {
591
599
  */
592
600
  async getPositions(user) {
593
601
  const url = this.POSITION_URL.replace("{{USER_ADDR}}", user.address);
594
- const result = await import_axios2.default.get(url);
602
+ const result = await import_axios3.default.get(url);
595
603
  const data = result.data;
596
604
  const lendingPosition = [];
597
605
  logger.verbose(`${this.metadata.name}:: Positions: ${JSON.stringify(data)}`);
@@ -624,7 +632,7 @@ _ZkLend.POOLS_URL = "https://app.zklend.com/api/pools";
624
632
  var ZkLend = _ZkLend;
625
633
 
626
634
  // src/interfaces/common.ts
627
- var import_starknet2 = require("starknet");
635
+ var import_starknet3 = require("starknet");
628
636
  var Network = /* @__PURE__ */ ((Network2) => {
629
637
  Network2["mainnet"] = "mainnet";
630
638
  Network2["sepolia"] = "sepolia";
@@ -633,7 +641,7 @@ var Network = /* @__PURE__ */ ((Network2) => {
633
641
  })(Network || {});
634
642
  function getMainnetConfig(rpcUrl = "https://starknet-mainnet.public.blastapi.io", blockIdentifier = "pending") {
635
643
  return {
636
- provider: new import_starknet2.RpcProvider({
644
+ provider: new import_starknet3.RpcProvider({
637
645
  nodeUrl: rpcUrl,
638
646
  blockIdentifier
639
647
  }),
@@ -660,34 +668,6 @@ var Initializable = class {
660
668
  }
661
669
  };
662
670
 
663
- // src/dataTypes/address.ts
664
- var import_starknet3 = require("starknet");
665
- var ContractAddr = class _ContractAddr {
666
- constructor(address) {
667
- this.address = _ContractAddr.standardise(address);
668
- }
669
- static from(address) {
670
- return new _ContractAddr(address);
671
- }
672
- eq(other) {
673
- return this.address === other.address;
674
- }
675
- eqString(other) {
676
- return this.address === _ContractAddr.standardise(other);
677
- }
678
- static standardise(address) {
679
- let _a = address;
680
- if (!address) {
681
- _a = "0";
682
- }
683
- const a = import_starknet3.num.getHexString(import_starknet3.num.getDecimalString(_a.toString()));
684
- return a;
685
- }
686
- static eqString(a, b) {
687
- return _ContractAddr.standardise(a) === _ContractAddr.standardise(b);
688
- }
689
- };
690
-
691
671
  // src/strategies/autoCompounderStrk.ts
692
672
  var import_starknet4 = require("starknet");
693
673
  var AutoCompounderSTRK = class {
@@ -955,8 +935,8 @@ var Store = class _Store {
955
935
  // src/node/pricer-redis.ts
956
936
  var import_redis = require("redis");
957
937
  var PricerRedis = class extends Pricer {
958
- constructor(config, tokens) {
959
- super(config, tokens);
938
+ constructor(config, tokens2) {
939
+ super(config, tokens2);
960
940
  this.redisClient = null;
961
941
  }
962
942
  /** Reads prices from Pricer._loadPrices and uses a callback to set prices in redis */