@strkfarm/sdk 1.0.9 → 1.0.10

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,86 @@ 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 = {};
136
137
  /**
137
138
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
138
139
  */
139
140
  this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
140
- // backup oracle
141
+ this.EKUBO_API = "https://mainnet-api.ekubo.org/quote/{{AMOUNT}}/{{TOKEN_SYMBOL}}/USDC";
142
+ // e.g. ETH/USDC
143
+ // backup oracle001
141
144
  this.client = new CoinMarketCap(process.env.COINMARKETCAP_KEY);
142
145
  this.config = config;
143
- this.tokens = tokens;
146
+ this.tokens = tokens2;
144
147
  }
145
148
  isReady() {
146
149
  const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
@@ -224,7 +227,7 @@ var Pricer = class {
224
227
  });
225
228
  if (this.isReady() && this.config.heartbeatUrl) {
226
229
  console.log(`sending beat`);
227
- axios.get(this.config.heartbeatUrl).catch((err) => {
230
+ axios2.get(this.config.heartbeatUrl).catch((err) => {
228
231
  console.error("Pricer: Heartbeat err", err);
229
232
  });
230
233
  }
@@ -238,14 +241,15 @@ var Pricer = class {
238
241
  return await this._getPriceCoinMarketCap(token);
239
242
  } catch (error) {
240
243
  }
244
+ try {
245
+ return await this._getPriceEkubo(token);
246
+ } catch (error) {
247
+ }
241
248
  throw new FatalError(`Price not found for ${token.name}`);
242
249
  }
243
250
  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);
251
+ const url = this.PRICE_API.replace("{{PRICER_KEY}}", `${token.symbol}-USD`);
252
+ const result = await axios2.get(url);
249
253
  const data = result.data;
250
254
  return Number(data.data.amount);
251
255
  }
@@ -253,6 +257,20 @@ var Pricer = class {
253
257
  const result = await this.client.getQuotes({ symbol: token.symbol });
254
258
  return result.data[token.symbol].quote.USD.price;
255
259
  }
260
+ async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
261
+ const url = this.EKUBO_API.replace("{{TOKEN_SYMBOL}}", token.symbol).replace("{{AMOUNT}}", amountIn.toWei());
262
+ const result = await axios2.get(url);
263
+ const data = result.data;
264
+ const outputUSDC = Number(Web3Number.fromWei(data.total, 6).toFixed(6));
265
+ logger.verbose(`Ekubo: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
266
+ if (outputUSDC === 0 && retry < 3) {
267
+ const amountIn2 = new Web3Number(100, token.decimals);
268
+ return await this._getPriceEkubo(token, amountIn2, retry + 1);
269
+ }
270
+ const usdcPrice = (await this.getPrice("USDC")).price;
271
+ logger.verbose(`USDC Price: ${usdcPrice}`);
272
+ return outputUSDC * usdcPrice;
273
+ }
256
274
  };
257
275
 
258
276
  // src/modules/pragma.ts
@@ -374,43 +392,7 @@ var Pragma = class {
374
392
  };
375
393
 
376
394
  // 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 });
395
+ import axios3 from "axios";
414
396
 
415
397
  // src/interfaces/lending.ts
416
398
  var MarginType = /* @__PURE__ */ ((MarginType2) => {
@@ -454,7 +436,7 @@ var _ZkLend = class _ZkLend extends ILending {
454
436
  async init() {
455
437
  try {
456
438
  logger.verbose(`Initialising ${this.metadata.name}`);
457
- const result = await axios2.get(_ZkLend.POOLS_URL);
439
+ const result = await axios3.get(_ZkLend.POOLS_URL);
458
440
  const data = result.data;
459
441
  const savedTokens = await Global.getTokens();
460
442
  data.forEach((pool) => {
@@ -544,7 +526,7 @@ var _ZkLend = class _ZkLend extends ILending {
544
526
  */
545
527
  async getPositions(user) {
546
528
  const url = this.POSITION_URL.replace("{{USER_ADDR}}", user.address);
547
- const result = await axios2.get(url);
529
+ const result = await axios3.get(url);
548
530
  const data = result.data;
549
531
  const lendingPosition = [];
550
532
  logger.verbose(`${this.metadata.name}:: Positions: ${JSON.stringify(data)}`);
@@ -613,34 +595,6 @@ var Initializable = class {
613
595
  }
614
596
  };
615
597
 
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
598
  // src/strategies/autoCompounderStrk.ts
645
599
  import { Contract as Contract2, uint256 } from "starknet";
646
600
  var AutoCompounderSTRK = class {
@@ -908,8 +862,8 @@ var Store = class _Store {
908
862
  // src/node/pricer-redis.ts
909
863
  import { createClient } from "redis";
910
864
  var PricerRedis = class extends Pricer {
911
- constructor(config, tokens) {
912
- super(config, tokens);
865
+ constructor(config, tokens2) {
866
+ super(config, tokens2);
913
867
  this.redisClient = null;
914
868
  }
915
869
  /** 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.10",
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",
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 {
@@ -19,8 +20,9 @@ export class Pricer {
19
20
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
20
21
  */
21
22
  protected PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
22
-
23
- // backup oracle
23
+ protected EKUBO_API = 'https://mainnet-api.ekubo.org/quote/{{AMOUNT}}/{{TOKEN_SYMBOL}}/USDC'; // e.g. ETH/USDC
24
+
25
+ // backup oracle001
24
26
  protected client = new CoinMarketCap(process.env.COINMARKETCAP_KEY!);
25
27
 
26
28
  constructor(config: IConfig, tokens: TokenInfo[]) {
@@ -136,14 +138,17 @@ export class Pricer {
136
138
  // do nothing, try next
137
139
  }
138
140
 
141
+ try {
142
+ return await this._getPriceEkubo(token);
143
+ } catch (error) {
144
+ // do nothing, try next
145
+ }
146
+
139
147
  throw new FatalError(`Price not found for ${token.name}`);
140
148
  }
141
149
 
142
150
  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);
151
+ const url = this.PRICE_API.replace("{{PRICER_KEY}}", `${token.symbol}-USD`);
147
152
  const result = await axios.get(url);
148
153
  const data: any = result.data;
149
154
  return Number(data.data.amount);
@@ -153,4 +158,24 @@ export class Pricer {
153
158
  const result = await this.client.getQuotes({symbol: token.symbol});
154
159
  return result.data[token.symbol].quote.USD.price as number
155
160
  }
161
+
162
+ async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
163
+ const url = this.EKUBO_API.replace("{{TOKEN_SYMBOL}}", token.symbol).replace("{{AMOUNT}}", amountIn.toWei());
164
+ const result = await axios.get(url);
165
+ const data: any = result.data;
166
+ const outputUSDC = Number(Web3Number.fromWei(data.total, 6).toFixed(6));
167
+ logger.verbose(`Ekubo: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
168
+ if (outputUSDC === 0 && retry < 3) {
169
+ // try again with a higher amount
170
+ const amountIn = new Web3Number(100, token.decimals); // 100 unit of token
171
+ return await this._getPriceEkubo(token, amountIn, retry + 1);
172
+ }
173
+
174
+ // if usdc depegs, it will not longer be 1 USD
175
+ // so we need to get the price of USDC in USD
176
+ // and then convert the outputUSDC to USD
177
+ const usdcPrice = (await this.getPrice('USDC')).price;
178
+ logger.verbose(`USDC Price: ${usdcPrice}`);
179
+ return outputUSDC * usdcPrice;
180
+ }
156
181
  }
@@ -1,79 +0,0 @@
1
- [
2
- {
3
- "name": "Ether",
4
- "symbol": "ETH",
5
- "address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
6
- "decimals": 18,
7
- "pricerKey": "ETH-USDT"
8
- },
9
- {
10
- "name": "USD Coin",
11
- "symbol": "USDC",
12
- "address": "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8",
13
- "decimals": 6,
14
- "pricerKey": "USDC-USDT"
15
- },
16
- {
17
- "name": "Wrapped BTC",
18
- "symbol": "WBTC",
19
- "address": "0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac",
20
- "decimals": 8,
21
- "pricerKey": "WBTC-USDT"
22
- },
23
- {
24
- "name": "Tether USD",
25
- "symbol": "USDT",
26
- "address": "0x068f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
27
- "decimals": 6,
28
- "pricerKey": "USDT-USDT"
29
- },
30
- {
31
- "name": "Dai Stablecoin",
32
- "symbol": "DAIv0",
33
- "address": "",
34
- "decimals": 18,
35
- "pricerKey": "DAI-USDT"
36
- },
37
- {
38
- "name": "Starknet Wrapped Staked Ether",
39
- "symbol": "wstETH",
40
- "address": "0x042b8f0484674ca266ac5d08e4ac6a3fe65bd3129795def2dca5c34ecc5f96d2",
41
- "decimals": 18,
42
- "pricerKey": "wstETH-USDT"
43
- },
44
- {
45
- "name": "Starknet Token",
46
- "symbol": "STRK",
47
- "address": "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
48
- "decimals": 18,
49
- "pricerKey": "STRK-USDT"
50
- },
51
- {
52
- "name": "zkLend Token",
53
- "symbol": "ZEND",
54
- "address": "",
55
- "decimals": 18,
56
- "pricerKey": "ZEND-USDT"
57
- },
58
- {
59
- "name": "Dai Stablecoin",
60
- "symbol": "DAI",
61
- "address": "",
62
- "decimals": 18,
63
- "pricerKey": "DAI-USDT"
64
- },
65
- {
66
- "name": "Ekubo Protocol",
67
- "symbol": "EKUBO",
68
- "address": "",
69
- "decimals": 18,
70
- "pricerKey": "DAI-USDT"
71
- },
72
- {
73
- "name": "kSTRK token",
74
- "symbol": "kSTRK",
75
- "address": "",
76
- "decimals": 18,
77
- "pricerKey": "DAI-USDT"
78
- }
79
- ]