@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.mjs CHANGED
@@ -6,90 +6,10 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/modules/pricer.ts
9
- import axios from "axios";
10
-
11
- // src/data/tokens.json
12
- var tokens_default = [
13
- {
14
- name: "Ether",
15
- symbol: "ETH",
16
- address: "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
17
- decimals: 18,
18
- pricerKey: "ETH-USDT"
19
- },
20
- {
21
- name: "USD Coin",
22
- symbol: "USDC",
23
- address: "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8",
24
- decimals: 6,
25
- pricerKey: "USDC-USDT"
26
- },
27
- {
28
- name: "Wrapped BTC",
29
- symbol: "WBTC",
30
- address: "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
31
- decimals: 8,
32
- pricerKey: "WBTC-USDT"
33
- },
34
- {
35
- name: "Tether USD",
36
- symbol: "USDT",
37
- address: "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
38
- decimals: 6,
39
- pricerKey: "USDT-USDT"
40
- },
41
- {
42
- name: "Dai Stablecoin",
43
- symbol: "DAIv0",
44
- address: "",
45
- decimals: 18,
46
- pricerKey: "DAI-USDT"
47
- },
48
- {
49
- name: "Starknet Wrapped Staked Ether",
50
- symbol: "wstETH",
51
- address: "0x042b8f0484674ca266ac5d08e4ac6a3fe65bd3129795def2dca5c34ecc5f96d2",
52
- decimals: 18,
53
- pricerKey: "wstETH-USDT"
54
- },
55
- {
56
- name: "Starknet Token",
57
- symbol: "STRK",
58
- address: "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
59
- decimals: 18,
60
- pricerKey: "STRK-USDT"
61
- },
62
- {
63
- name: "zkLend Token",
64
- symbol: "ZEND",
65
- address: "",
66
- decimals: 18,
67
- pricerKey: "ZEND-USDT"
68
- },
69
- {
70
- name: "Dai Stablecoin",
71
- symbol: "DAI",
72
- address: "",
73
- decimals: 18,
74
- pricerKey: "DAI-USDT"
75
- },
76
- {
77
- name: "Ekubo Protocol",
78
- symbol: "EKUBO",
79
- address: "",
80
- decimals: 18,
81
- pricerKey: "DAI-USDT"
82
- },
83
- {
84
- name: "kSTRK token",
85
- symbol: "kSTRK",
86
- address: "",
87
- decimals: 18,
88
- pricerKey: "DAI-USDT"
89
- }
90
- ];
9
+ import axios2 from "axios";
91
10
 
92
11
  // src/global.ts
12
+ import axios from "axios";
93
13
  var logger = {
94
14
  ...console,
95
15
  verbose(message) {
@@ -105,6 +25,7 @@ var FatalError = class extends Error {
105
25
  this.name = "FatalError";
106
26
  }
107
27
  };
28
+ var tokens = [];
108
29
  var Global = class {
109
30
  static fatalError(message, err) {
110
31
  logger.error(message);
@@ -118,7 +39,23 @@ var Global = class {
118
39
  console.error(err);
119
40
  }
120
41
  static async getTokens() {
121
- return tokens_default;
42
+ if (tokens.length) return tokens;
43
+ const data = await axios.get("https://starknet.api.avnu.fi/v1/starknet/tokens");
44
+ const tokensData = data.data.content;
45
+ tokensData.forEach((token) => {
46
+ if (!token.tags.includes("AVNU") || !token.tags.includes("Verified")) {
47
+ return;
48
+ }
49
+ tokens.push({
50
+ name: token.name,
51
+ symbol: token.symbol,
52
+ address: token.address,
53
+ decimals: token.decimals,
54
+ coingeckId: token.extensions.coingeckoId
55
+ });
56
+ });
57
+ console.log(tokens);
58
+ return tokens;
122
59
  }
123
60
  static assert(condition, message) {
124
61
  if (!condition) {
@@ -127,20 +64,89 @@ var Global = class {
127
64
  }
128
65
  };
129
66
 
67
+ // src/dataTypes/bignumber.ts
68
+ import BigNumber from "bignumber.js";
69
+ var Web3Number = class _Web3Number extends BigNumber {
70
+ constructor(value, decimals) {
71
+ super(value);
72
+ this.decimals = decimals;
73
+ }
74
+ static fromWei(weiNumber, decimals) {
75
+ const bn = new _Web3Number(weiNumber, decimals).dividedBy(10 ** decimals);
76
+ return new _Web3Number(bn.toString(), decimals);
77
+ }
78
+ toWei() {
79
+ return this.mul(10 ** this.decimals).toFixed(0);
80
+ }
81
+ multipliedBy(value) {
82
+ return new _Web3Number(this.mul(value).toString(), this.decimals);
83
+ }
84
+ dividedBy(value) {
85
+ return new _Web3Number(this.div(value).toString(), this.decimals);
86
+ }
87
+ plus(value) {
88
+ return new _Web3Number(this.add(value).toString(), this.decimals);
89
+ }
90
+ minus(n, base) {
91
+ return new _Web3Number(super.minus(n, base).toString(), this.decimals);
92
+ }
93
+ toString(base) {
94
+ return super.toString(base);
95
+ }
96
+ // [customInspectSymbol](depth: any, inspectOptions: any, inspect: any) {
97
+ // return this.toString();
98
+ // }
99
+ };
100
+ BigNumber.config({ DECIMAL_PLACES: 18 });
101
+ Web3Number.config({ DECIMAL_PLACES: 18 });
102
+
103
+ // src/dataTypes/address.ts
104
+ import { num } from "starknet";
105
+ var ContractAddr = class _ContractAddr {
106
+ constructor(address) {
107
+ this.address = _ContractAddr.standardise(address);
108
+ }
109
+ static from(address) {
110
+ return new _ContractAddr(address);
111
+ }
112
+ eq(other) {
113
+ return this.address === other.address;
114
+ }
115
+ eqString(other) {
116
+ return this.address === _ContractAddr.standardise(other);
117
+ }
118
+ static standardise(address) {
119
+ let _a = address;
120
+ if (!address) {
121
+ _a = "0";
122
+ }
123
+ const a = num.getHexString(num.getDecimalString(_a.toString()));
124
+ return a;
125
+ }
126
+ static eqString(a, b) {
127
+ return _ContractAddr.standardise(a) === _ContractAddr.standardise(b);
128
+ }
129
+ };
130
+
130
131
  // src/modules/pricer.ts
131
132
  var CoinMarketCap = __require("coinmarketcap-api");
132
133
  var Pricer = class {
133
- constructor(config, tokens) {
134
+ constructor(config, tokens2) {
134
135
  this.tokens = [];
135
136
  this.prices = {};
137
+ // code populates this map during runtime to determine which method to use for a given token
138
+ // The method set will be the first one to try after first attempt
139
+ this.methodToUse = {};
136
140
  /**
137
141
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
138
142
  */
139
143
  this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
140
- // backup oracle
144
+ this.EKUBO_API = "https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
145
+ // e.g. ETH/USDC
146
+ // backup oracle001
141
147
  this.client = new CoinMarketCap(process.env.COINMARKETCAP_KEY);
142
148
  this.config = config;
143
- this.tokens = tokens;
149
+ this.tokens = tokens2;
144
150
  }
145
151
  isReady() {
146
152
  const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
@@ -224,34 +230,72 @@ var Pricer = class {
224
230
  });
225
231
  if (this.isReady() && this.config.heartbeatUrl) {
226
232
  console.log(`sending beat`);
227
- axios.get(this.config.heartbeatUrl).catch((err) => {
233
+ axios2.get(this.config.heartbeatUrl).catch((err) => {
228
234
  console.error("Pricer: Heartbeat err", err);
229
235
  });
230
236
  }
231
237
  }
232
- async _getPrice(token) {
233
- try {
234
- return await this._getPriceCoinbase(token);
235
- } catch (error) {
238
+ async _getPrice(token, defaultMethod = "all") {
239
+ const methodToUse = this.methodToUse[token.symbol] || defaultMethod;
240
+ logger.info(`Fetching price of ${token.symbol} using ${methodToUse}`);
241
+ switch (methodToUse) {
242
+ case "Coinbase":
243
+ try {
244
+ const result = await this._getPriceCoinbase(token);
245
+ this.methodToUse[token.symbol] = "Coinbase";
246
+ return result;
247
+ } catch (error) {
248
+ console.warn(`Coinbase: price err: message [${token.symbol}]: `, error.message);
249
+ }
250
+ case "Coinmarketcap":
251
+ try {
252
+ const result = await this._getPriceCoinMarketCap(token);
253
+ this.methodToUse[token.symbol] = "Coinmarketcap";
254
+ return result;
255
+ } catch (error) {
256
+ console.warn(`CoinMarketCap: price err [${token.symbol}]: `, Object.keys(error));
257
+ console.warn(`CoinMarketCap: price err [${token.symbol}]: `, error.message);
258
+ }
259
+ case "Ekubo":
260
+ try {
261
+ const result = await this._getPriceEkubo(token);
262
+ this.methodToUse[token.symbol] = "Ekubo";
263
+ return result;
264
+ } catch (error) {
265
+ console.warn(`Ekubo: price err [${token.symbol}]: `, error.message);
266
+ console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
267
+ }
236
268
  }
237
- try {
238
- return await this._getPriceCoinMarketCap(token);
239
- } catch (error) {
269
+ if (defaultMethod == "all") {
270
+ return await this._getPrice(token, "Coinbase");
240
271
  }
241
- throw new FatalError(`Price not found for ${token.name}`);
272
+ throw new FatalError(`Price not found for ${token.symbol}`);
242
273
  }
243
274
  async _getPriceCoinbase(token) {
244
- if (!token.pricerKey) {
245
- throw new FatalError(`Pricer key not found for ${token.name}`);
246
- }
247
- const url = this.PRICE_API.replace("{{PRICER_KEY}}", token.pricerKey);
248
- const result = await axios.get(url);
275
+ const url = this.PRICE_API.replace("{{PRICER_KEY}}", `${token.symbol}-USD`);
276
+ const result = await axios2.get(url);
249
277
  const data = result.data;
250
278
  return Number(data.data.amount);
251
279
  }
252
280
  async _getPriceCoinMarketCap(token) {
253
281
  const result = await this.client.getQuotes({ symbol: token.symbol });
254
- return result.data[token.symbol].quote.USD.price;
282
+ if (result.data)
283
+ return result.data[token.symbol].quote.USD.price;
284
+ throw new Error(result);
285
+ }
286
+ async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
287
+ const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
288
+ const result = await axios2.get(url);
289
+ const data = result.data;
290
+ const outputUSDC = Number(Web3Number.fromWei(data.total_calculated, 6).toFixed(6));
291
+ logger.verbose(`Ekubo: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
292
+ if (outputUSDC === 0 && retry < 3) {
293
+ const amountIn2 = new Web3Number(100, token.decimals);
294
+ return await this._getPriceEkubo(token, amountIn2, retry + 1);
295
+ }
296
+ const usdcPrice = (await this.getPrice("USDC")).price;
297
+ logger.verbose(`USDC Price: ${usdcPrice}`);
298
+ return outputUSDC * usdcPrice;
255
299
  }
256
300
  };
257
301
 
@@ -374,43 +418,7 @@ var Pragma = class {
374
418
  };
375
419
 
376
420
  // src/modules/zkLend.ts
377
- import axios2 from "axios";
378
-
379
- // src/dataTypes/bignumber.ts
380
- import BigNumber from "bignumber.js";
381
- var Web3Number = class _Web3Number extends BigNumber {
382
- constructor(value, decimals) {
383
- super(value);
384
- this.decimals = decimals;
385
- }
386
- static fromWei(weiNumber, decimals) {
387
- const bn = new _Web3Number(weiNumber, decimals).dividedBy(10 ** decimals);
388
- return new _Web3Number(bn.toString(), decimals);
389
- }
390
- toWei() {
391
- return this.mul(10 ** this.decimals).toFixed(0);
392
- }
393
- multipliedBy(value) {
394
- return new _Web3Number(this.mul(value).toString(), this.decimals);
395
- }
396
- dividedBy(value) {
397
- return new _Web3Number(this.div(value).toString(), this.decimals);
398
- }
399
- plus(value) {
400
- return new _Web3Number(this.add(value).toString(), this.decimals);
401
- }
402
- minus(n, base) {
403
- return new _Web3Number(super.minus(n, base).toString(), this.decimals);
404
- }
405
- toString(base) {
406
- return super.toString(base);
407
- }
408
- // [customInspectSymbol](depth: any, inspectOptions: any, inspect: any) {
409
- // return this.toString();
410
- // }
411
- };
412
- BigNumber.config({ DECIMAL_PLACES: 18 });
413
- Web3Number.config({ DECIMAL_PLACES: 18 });
421
+ import axios3 from "axios";
414
422
 
415
423
  // src/interfaces/lending.ts
416
424
  var MarginType = /* @__PURE__ */ ((MarginType2) => {
@@ -454,7 +462,7 @@ var _ZkLend = class _ZkLend extends ILending {
454
462
  async init() {
455
463
  try {
456
464
  logger.verbose(`Initialising ${this.metadata.name}`);
457
- const result = await axios2.get(_ZkLend.POOLS_URL);
465
+ const result = await axios3.get(_ZkLend.POOLS_URL);
458
466
  const data = result.data;
459
467
  const savedTokens = await Global.getTokens();
460
468
  data.forEach((pool) => {
@@ -544,7 +552,7 @@ var _ZkLend = class _ZkLend extends ILending {
544
552
  */
545
553
  async getPositions(user) {
546
554
  const url = this.POSITION_URL.replace("{{USER_ADDR}}", user.address);
547
- const result = await axios2.get(url);
555
+ const result = await axios3.get(url);
548
556
  const data = result.data;
549
557
  const lendingPosition = [];
550
558
  logger.verbose(`${this.metadata.name}:: Positions: ${JSON.stringify(data)}`);
@@ -613,34 +621,6 @@ var Initializable = class {
613
621
  }
614
622
  };
615
623
 
616
- // src/dataTypes/address.ts
617
- import { num } from "starknet";
618
- var ContractAddr = class _ContractAddr {
619
- constructor(address) {
620
- this.address = _ContractAddr.standardise(address);
621
- }
622
- static from(address) {
623
- return new _ContractAddr(address);
624
- }
625
- eq(other) {
626
- return this.address === other.address;
627
- }
628
- eqString(other) {
629
- return this.address === _ContractAddr.standardise(other);
630
- }
631
- static standardise(address) {
632
- let _a = address;
633
- if (!address) {
634
- _a = "0";
635
- }
636
- const a = num.getHexString(num.getDecimalString(_a.toString()));
637
- return a;
638
- }
639
- static eqString(a, b) {
640
- return _ContractAddr.standardise(a) === _ContractAddr.standardise(b);
641
- }
642
- };
643
-
644
624
  // src/strategies/autoCompounderStrk.ts
645
625
  import { Contract as Contract2, uint256 } from "starknet";
646
626
  var AutoCompounderSTRK = class {
@@ -908,8 +888,8 @@ var Store = class _Store {
908
888
  // src/node/pricer-redis.ts
909
889
  import { createClient } from "redis";
910
890
  var PricerRedis = class extends Pricer {
911
- constructor(config, tokens) {
912
- super(config, tokens);
891
+ constructor(config, tokens2) {
892
+ super(config, tokens2);
913
893
  this.redisClient = null;
914
894
  }
915
895
  /** Reads prices from Pricer._loadPrices and uses a callback to set prices in redis */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
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
6
  "main": "dist/index.js",
@@ -53,6 +53,7 @@
53
53
  "inquirer": "^10.1.2",
54
54
  "node-telegram-bot-api": "^0.66.0",
55
55
  "redis": "^4.7.0",
56
+ "stacktrace-js": "^2.0.2",
56
57
  "starknet": "^6.11.0",
57
58
  "winston": "^3.13.0"
58
59
  }
package/src/global.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import axios from 'axios';
1
2
  import { TokenInfo } from './interfaces';
2
- import TOKENS from '@/data/tokens.json';
3
3
 
4
4
  const colors = {
5
5
  error: 'red',
@@ -44,6 +44,9 @@ export class FatalError extends Error {
44
44
  this.name = "FatalError";
45
45
  }
46
46
  }
47
+
48
+ const tokens: TokenInfo[] = [];
49
+
47
50
  /** Contains globally useful functions.
48
51
  * - fatalError: Things to do when a fatal error occurs
49
52
  */
@@ -62,7 +65,46 @@ export class Global {
62
65
  }
63
66
 
64
67
  static async getTokens(): Promise<TokenInfo[]> {
65
- return TOKENS;
68
+ if (tokens.length) return tokens;
69
+
70
+ // fetch from avnu API
71
+ const data = await axios.get('https://starknet.api.avnu.fi/v1/starknet/tokens');
72
+ const tokensData = data.data.content;
73
+
74
+ // Array of the following is returned
75
+ // {
76
+ // "name": "USD Coin",
77
+ // "address": "0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8",
78
+ // "symbol": "USDC",
79
+ // "decimals": 6,
80
+ // "logoUri": "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
81
+ // "lastDailyVolumeUsd": 2964287916.82621,
82
+ // "extensions": {
83
+ // "coingeckoId": "usd-coin"
84
+ // },
85
+ // "tags": [
86
+ // "AVNU",
87
+ // "Verified"
88
+ // ]
89
+ // }
90
+
91
+ tokensData.forEach((token: any) => {
92
+ // if tags do not contain Avnu and verified, ignore
93
+ // This would exclude meme coins for now
94
+ if (!token.tags.includes('AVNU') || !token.tags.includes('Verified')) {
95
+ return;
96
+ }
97
+
98
+ tokens.push({
99
+ name: token.name,
100
+ symbol: token.symbol,
101
+ address: token.address,
102
+ decimals: token.decimals,
103
+ coingeckId: token.extensions.coingeckoId,
104
+ });
105
+ });
106
+ console.log(tokens);
107
+ return tokens;
66
108
  }
67
109
 
68
110
  static assert(condition: any, message: string) {
@@ -5,7 +5,7 @@ export interface TokenInfo {
5
5
  symbol: string,
6
6
  address: string,
7
7
  decimals: number,
8
- pricerKey?: string
8
+ coingeckId?: string,
9
9
  }
10
10
 
11
11
  export enum Network {
@@ -2,6 +2,7 @@ import axios from "axios";
2
2
  import { FatalError, Global, logger } from "@/global";
3
3
  import { TokenInfo } from "@/interfaces/common";
4
4
  import { IConfig } from "@/interfaces/common";
5
+ import { Web3Number } from "@/dataTypes";
5
6
  const CoinMarketCap = require('coinmarketcap-api')
6
7
 
7
8
  export interface PriceInfo {
@@ -15,14 +16,19 @@ export class Pricer {
15
16
  [key: string]: PriceInfo
16
17
  } = {}
17
18
 
19
+ // code populates this map during runtime to determine which method to use for a given token
20
+ // The method set will be the first one to try after first attempt
21
+ private methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap'} = {};
22
+
18
23
  /**
19
24
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
20
25
  */
21
26
  protected PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
22
-
23
- // backup oracle
27
+ protected EKUBO_API = 'https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8'; // e.g. ETH/USDC
28
+
29
+ // backup oracle001
24
30
  protected client = new CoinMarketCap(process.env.COINMARKETCAP_KEY!);
25
-
31
+
26
32
  constructor(config: IConfig, tokens: TokenInfo[]) {
27
33
  this.config = config;
28
34
  this.tokens = tokens;
@@ -123,34 +129,81 @@ export class Pricer {
123
129
  }
124
130
  }
125
131
 
126
- async _getPrice(token: TokenInfo) {
127
- try {
128
- return await this._getPriceCoinbase(token);
129
- } catch (error) {
130
- // do nothing, try next
132
+ async _getPrice(token: TokenInfo, defaultMethod = 'all'): Promise<number> {
133
+ const methodToUse: string = this.methodToUse[token.symbol] || defaultMethod; // default start with coinbase
134
+ logger.info(`Fetching price of ${token.symbol} using ${methodToUse}`);
135
+ switch (methodToUse) {
136
+ case 'Coinbase':
137
+ try {
138
+ const result = await this._getPriceCoinbase(token);
139
+ this.methodToUse[token.symbol] = 'Coinbase';
140
+ return result;
141
+ } catch (error: any) {
142
+ console.warn(`Coinbase: price err: message [${token.symbol}]: `, error.message);
143
+ // do nothing, try next
144
+ }
145
+ case 'Coinmarketcap':
146
+ try {
147
+ const result = await this._getPriceCoinMarketCap(token);
148
+ this.methodToUse[token.symbol] = 'Coinmarketcap';
149
+ return result;
150
+ } catch (error: any) {
151
+ console.warn(`CoinMarketCap: price err [${token.symbol}]: `, Object.keys(error));
152
+ console.warn(`CoinMarketCap: price err [${token.symbol}]: `, error.message);
153
+ }
154
+ case 'Ekubo':
155
+ try {
156
+ const result = await this._getPriceEkubo(token);
157
+ this.methodToUse[token.symbol] = 'Ekubo';
158
+ return result;
159
+ } catch (error: any) {
160
+ console.warn(`Ekubo: price err [${token.symbol}]: `, error.message);
161
+ console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
162
+ // do nothing, try next
163
+ }
131
164
  }
132
165
 
133
- try {
134
- return await this._getPriceCoinMarketCap(token);
135
- } catch (error) {
136
- // do nothing, try next
166
+ // if methodToUse is the default one, pass Coinbase to try all from start
167
+ if (defaultMethod == 'all') {
168
+ // try again with coinbase
169
+ return await this._getPrice(token, 'Coinbase');
137
170
  }
138
171
 
139
- throw new FatalError(`Price not found for ${token.name}`);
172
+ throw new FatalError(`Price not found for ${token.symbol}`);
140
173
  }
141
174
 
142
175
  async _getPriceCoinbase(token: TokenInfo) {
143
- if (!token.pricerKey) {
144
- throw new FatalError(`Pricer key not found for ${token.name}`);
145
- }
146
- const url = this.PRICE_API.replace("{{PRICER_KEY}}", token.pricerKey);
147
- const result = await axios.get(url);
176
+ const url = this.PRICE_API.replace("{{PRICER_KEY}}", `${token.symbol}-USD`);
177
+ const result = await axios.get(url)
148
178
  const data: any = result.data;
149
179
  return Number(data.data.amount);
150
180
  }
151
181
 
152
182
  async _getPriceCoinMarketCap(token: TokenInfo): Promise<number> {
153
183
  const result = await this.client.getQuotes({symbol: token.symbol});
154
- return result.data[token.symbol].quote.USD.price as number
184
+ if (result.data)
185
+ return result.data[token.symbol].quote.USD.price as number
186
+
187
+ throw new Error(result);
188
+ }
189
+
190
+ async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
191
+ const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
192
+ const result = await axios.get(url);
193
+ const data: any = result.data;
194
+ const outputUSDC = Number(Web3Number.fromWei(data.total_calculated, 6).toFixed(6));
195
+ logger.verbose(`Ekubo: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
196
+ if (outputUSDC === 0 && retry < 3) {
197
+ // try again with a higher amount
198
+ const amountIn = new Web3Number(100, token.decimals); // 100 unit of token
199
+ return await this._getPriceEkubo(token, amountIn, retry + 1);
200
+ }
201
+
202
+ // if usdc depegs, it will not longer be 1 USD
203
+ // so we need to get the price of USDC in USD
204
+ // and then convert the outputUSDC to USD
205
+ const usdcPrice = (await this.getPrice('USDC')).price;
206
+ logger.verbose(`USDC Price: ${usdcPrice}`);
207
+ return outputUSDC * usdcPrice;
155
208
  }
156
209
  }