@strkfarm/sdk 2.0.0-dev.50 → 2.0.0-dev.51

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.js CHANGED
@@ -914,7 +914,8 @@ var defaultTokens = [{
914
914
  priceCheckAmount: 5e3,
915
915
  displayDecimals: 2,
916
916
  priceMethod: "Avnu",
917
- indexingType: "lstScript" /* LST_SCRIPT */
917
+ indexingType: "lstScript" /* LST_SCRIPT */,
918
+ intermediateQuoteTokenSymbol: "STRK"
918
919
  }, {
919
920
  name: "ETH",
920
921
  symbol: "ETH",
@@ -4311,6 +4312,42 @@ var PricerAvnuApi = class extends PricerBase {
4311
4312
  }
4312
4313
  };
4313
4314
 
4315
+ // src/modules/pricer-quote-utils.ts
4316
+ var PRICER_USDC_ADDRESS = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
4317
+ var PRICER_USDC_DECIMALS = 6;
4318
+ function resolveQuoteTokenConfig(token, tokens2) {
4319
+ if (token.intermediateQuoteTokenSymbol) {
4320
+ const intermediateToken = tokens2.find(
4321
+ (t) => t.symbol === token.intermediateQuoteTokenSymbol
4322
+ );
4323
+ if (!intermediateToken) {
4324
+ throw new Error(
4325
+ `Intermediate quote token ${token.intermediateQuoteTokenSymbol} not found for ${token.symbol}`
4326
+ );
4327
+ }
4328
+ return {
4329
+ address: intermediateToken.address.toString(),
4330
+ decimals: intermediateToken.decimals,
4331
+ symbol: intermediateToken.symbol,
4332
+ isUsdQuote: false
4333
+ };
4334
+ }
4335
+ return {
4336
+ address: PRICER_USDC_ADDRESS,
4337
+ decimals: PRICER_USDC_DECIMALS,
4338
+ symbol: "USDC",
4339
+ isUsdQuote: true
4340
+ };
4341
+ }
4342
+ async function convertQuoteAmountToUsd(tokenSymbol, quoteAmount, quoteConfig, resolveUsdPrice) {
4343
+ if (quoteConfig.isUsdQuote) {
4344
+ const usdcPrice = await resolveUsdPrice("USDC");
4345
+ return quoteAmount * usdcPrice;
4346
+ }
4347
+ const intermediateUsdPrice = await resolveUsdPrice(quoteConfig.symbol);
4348
+ return quoteAmount * intermediateUsdPrice;
4349
+ }
4350
+
4314
4351
  // src/modules/pricer.ts
4315
4352
  var PRICE_METHOD_PRIORITY = [
4316
4353
  "AvnuApi",
@@ -4334,7 +4371,7 @@ var Pricer = class extends PricerBase {
4334
4371
  */
4335
4372
  // ! switch to USDC (new) later
4336
4373
  this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
4337
- this.EKUBO_API = "https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
4374
+ this.EKUBO_API = `https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/${PRICER_USDC_ADDRESS}`;
4338
4375
  this.refreshInterval = refreshInterval;
4339
4376
  this.staleTime = staleTime;
4340
4377
  this.avnuApiPricer = new PricerAvnuApi(config, tokens2);
@@ -4507,37 +4544,67 @@ var Pricer = class extends PricerBase {
4507
4544
  async _getPriceCoinMarketCap(token) {
4508
4545
  throw new Error("Not implemented");
4509
4546
  }
4547
+ async _resolveQuoteTokenUsdPrice(symbol) {
4548
+ const cached = this.prices[symbol];
4549
+ if (cached && !this.isStale(cached.timestamp, symbol)) {
4550
+ return cached.price;
4551
+ }
4552
+ const quoteToken = this.tokens.find((t) => t.symbol === symbol);
4553
+ if (!quoteToken) {
4554
+ throw new FatalError(`Quote token ${symbol} not found in pricer tokens`);
4555
+ }
4556
+ return await this._getPrice(quoteToken);
4557
+ }
4558
+ async _convertDexQuoteToUsd(token, quoteAmount) {
4559
+ const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
4560
+ const usdPrice = await convertQuoteAmountToUsd(
4561
+ token.symbol,
4562
+ quoteAmount,
4563
+ quoteConfig,
4564
+ this._resolveQuoteTokenUsdPrice.bind(this)
4565
+ );
4566
+ logger.verbose(
4567
+ `${token.symbol}: ${quoteAmount} ${quoteConfig.symbol} -> $${usdPrice}`
4568
+ );
4569
+ return usdPrice;
4570
+ }
4510
4571
  async _getAvnuPrice(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
4511
- logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
4572
+ const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
4573
+ logger.verbose(`Getting price of ${token.symbol} using Avnu, amountIn: ${amountIn.toWei()}`);
4512
4574
  const avnuWrapper = new AvnuWrapper();
4513
- const usdcAddress = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
4514
- const quote = await avnuWrapper.getQuotes(token.address.toString(), usdcAddress, amountIn.toWei(), "0x1");
4575
+ const quote = await avnuWrapper.getQuotes(
4576
+ token.address.toString(),
4577
+ quoteConfig.address,
4578
+ amountIn.toWei(),
4579
+ "0x1"
4580
+ );
4515
4581
  const multiplier = 1 / amountIn.toNumber();
4516
- const outputUSDC = Number(Web3Number.fromWei(quote.buyAmount.toString(), 6).toFixed(6)) * multiplier;
4517
- logger.verbose(`Avnu: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
4518
- if (outputUSDC === 0 && retry < 3) {
4519
- const amountIn2 = new Web3Number(100, token.decimals);
4520
- return await this._getAvnuPrice(token, amountIn2, retry + 1);
4582
+ const outputQuote = Number(
4583
+ Web3Number.fromWei(quote.buyAmount.toString(), quoteConfig.decimals).toFixed(6)
4584
+ ) * multiplier;
4585
+ logger.verbose(`Avnu: ${token.symbol} -> ${quoteConfig.symbol}: ${outputQuote}, retry: ${retry}`);
4586
+ if (outputQuote === 0 && retry < 3) {
4587
+ const retryAmountIn = new Web3Number(100, token.decimals);
4588
+ return await this._getAvnuPrice(token, retryAmountIn, retry + 1);
4521
4589
  }
4522
- const usdcPrice = 1;
4523
- logger.verbose(`USDC Price: ${usdcPrice}`);
4524
- return outputUSDC * usdcPrice;
4590
+ return await this._convertDexQuoteToUsd(token, outputQuote);
4525
4591
  }
4526
4592
  async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
4593
+ const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
4527
4594
  logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
4528
- const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
4595
+ const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei()).replace(PRICER_USDC_ADDRESS, quoteConfig.address);
4529
4596
  const result = await import_axios4.default.get(url);
4530
4597
  const data = result.data;
4531
4598
  const multiplier = 1 / amountIn.toNumber();
4532
- const outputUSDC = Number(Web3Number.fromWei(data.total_calculated, 6).toFixed(6)) * multiplier;
4533
- logger.verbose(`Ekubo: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
4534
- if (outputUSDC === 0 && retry < 3) {
4535
- const amountIn2 = new Web3Number(100, token.decimals);
4536
- return await this._getPriceEkubo(token, amountIn2, retry + 1);
4599
+ const outputQuote = Number(
4600
+ Web3Number.fromWei(data.total_calculated, quoteConfig.decimals).toFixed(6)
4601
+ ) * multiplier;
4602
+ logger.verbose(`Ekubo: ${token.symbol} -> ${quoteConfig.symbol}: ${outputQuote}, retry: ${retry}`);
4603
+ if (outputQuote === 0 && retry < 3) {
4604
+ const retryAmountIn = new Web3Number(100, token.decimals);
4605
+ return await this._getPriceEkubo(token, retryAmountIn, retry + 1);
4537
4606
  }
4538
- const usdcPrice = 1;
4539
- logger.verbose(`USDC Price: ${usdcPrice}`);
4540
- return outputUSDC * usdcPrice;
4607
+ return await this._convertDexQuoteToUsd(token, outputQuote);
4541
4608
  }
4542
4609
  };
4543
4610
 
@@ -5119,12 +5186,10 @@ var ekubo_price_fethcer_abi_default = [
5119
5186
 
5120
5187
  // src/modules/ekubo-pricer.ts
5121
5188
  var EkuboPricer = class extends PricerBase {
5122
- constructor(config, tokens2) {
5189
+ constructor(config, tokens2, resolveUsdPrice) {
5123
5190
  super(config, tokens2);
5191
+ this.resolveUsdPrice = resolveUsdPrice;
5124
5192
  this.EKUBO_PRICE_FETCHER_ADDRESS = "0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1";
5125
- // Updating to new USDC_ADDRESS
5126
- this.USDC_ADDRESS = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
5127
- this.USDC_DECIMALS = 6;
5128
5193
  this.contract = new import_starknet13.Contract({
5129
5194
  abi: ekubo_price_fethcer_abi_default,
5130
5195
  address: this.EKUBO_PRICE_FETCHER_ADDRESS,
@@ -5138,9 +5203,16 @@ var EkuboPricer = class extends PricerBase {
5138
5203
  if (!tokenAddr) {
5139
5204
  throw new Error(`EkuboPricer:getPrice - no token`);
5140
5205
  }
5206
+ const tokenInfo = this.tokens.find(
5207
+ (t) => t.address.eqString(tokenAddr)
5208
+ );
5209
+ if (!tokenInfo) {
5210
+ throw new Error(`Token ${tokenAddr} not found in global tokens`);
5211
+ }
5212
+ const quoteConfig = resolveQuoteTokenConfig(tokenInfo, this.tokens);
5141
5213
  const result = await this.contract.call(
5142
5214
  "get_prices",
5143
- [this.USDC_ADDRESS, [tokenAddr], 3600, 1e6],
5215
+ [quoteConfig.address, [tokenAddr], 3600, 1e6],
5144
5216
  { blockIdentifier }
5145
5217
  );
5146
5218
  if (!result || result.length === 0) {
@@ -5152,17 +5224,23 @@ var EkuboPricer = class extends PricerBase {
5152
5224
  throw new Error(`EkuboPricer: Price fetch failed with variant: ${variant}`);
5153
5225
  }
5154
5226
  const rawPrice = typeof priceResult.variant.Price === "string" ? BigInt(priceResult.variant.Price) : priceResult.variant.Price;
5155
- const tokenInfo = this.tokens.find(
5156
- (t) => t.address.address.toLowerCase() === tokenAddr.toLowerCase()
5157
- );
5158
- if (!tokenInfo) {
5159
- throw new Error(`Token ${tokenAddr} not found in global tokens`);
5160
- }
5161
5227
  const priceAfterX128 = this.div2Power128(rawPrice);
5162
- const decimalAdjustment = 10 ** (tokenInfo.decimals - this.USDC_DECIMALS);
5163
- const price = priceAfterX128 * decimalAdjustment;
5228
+ const decimalAdjustment = 10 ** (tokenInfo.decimals - quoteConfig.decimals);
5229
+ const priceInQuote = priceAfterX128 * decimalAdjustment;
5230
+ if (!quoteConfig.isUsdQuote) {
5231
+ if (!this.resolveUsdPrice) {
5232
+ throw new Error(
5233
+ `EkuboPricer: USD price resolver required for ${tokenInfo.symbol} via ${quoteConfig.symbol}`
5234
+ );
5235
+ }
5236
+ const quoteUsdPrice = await this.resolveUsdPrice(quoteConfig.symbol);
5237
+ return {
5238
+ price: priceInQuote * quoteUsdPrice,
5239
+ timestamp: /* @__PURE__ */ new Date()
5240
+ };
5241
+ }
5164
5242
  return {
5165
- price,
5243
+ price: priceInQuote,
5166
5244
  timestamp: /* @__PURE__ */ new Date()
5167
5245
  };
5168
5246
  }
@@ -5190,7 +5268,10 @@ var PricerFromApi = class extends PricerBase {
5190
5268
  ];
5191
5269
  this.apolloClient = createApolloClient(config);
5192
5270
  this.pragma = new Pragma(config, tokens2);
5193
- this.ekuboPricer = new EkuboPricer(config, tokens2);
5271
+ this.ekuboPricer = new EkuboPricer(config, tokens2, async (symbol) => {
5272
+ const priceInfo = await this.getPriceFromMyAPI(symbol);
5273
+ return priceInfo.price;
5274
+ });
5194
5275
  }
5195
5276
  async getPrice(tokenSymbol, blockNumber) {
5196
5277
  const tokenInfo = this.tokens.find((t) => t.symbol === tokenSymbol);
package/dist/index.mjs CHANGED
@@ -744,7 +744,8 @@ var defaultTokens = [{
744
744
  priceCheckAmount: 5e3,
745
745
  displayDecimals: 2,
746
746
  priceMethod: "Avnu",
747
- indexingType: "lstScript" /* LST_SCRIPT */
747
+ indexingType: "lstScript" /* LST_SCRIPT */,
748
+ intermediateQuoteTokenSymbol: "STRK"
748
749
  }, {
749
750
  name: "ETH",
750
751
  symbol: "ETH",
@@ -4141,6 +4142,42 @@ var PricerAvnuApi = class extends PricerBase {
4141
4142
  }
4142
4143
  };
4143
4144
 
4145
+ // src/modules/pricer-quote-utils.ts
4146
+ var PRICER_USDC_ADDRESS = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
4147
+ var PRICER_USDC_DECIMALS = 6;
4148
+ function resolveQuoteTokenConfig(token, tokens2) {
4149
+ if (token.intermediateQuoteTokenSymbol) {
4150
+ const intermediateToken = tokens2.find(
4151
+ (t) => t.symbol === token.intermediateQuoteTokenSymbol
4152
+ );
4153
+ if (!intermediateToken) {
4154
+ throw new Error(
4155
+ `Intermediate quote token ${token.intermediateQuoteTokenSymbol} not found for ${token.symbol}`
4156
+ );
4157
+ }
4158
+ return {
4159
+ address: intermediateToken.address.toString(),
4160
+ decimals: intermediateToken.decimals,
4161
+ symbol: intermediateToken.symbol,
4162
+ isUsdQuote: false
4163
+ };
4164
+ }
4165
+ return {
4166
+ address: PRICER_USDC_ADDRESS,
4167
+ decimals: PRICER_USDC_DECIMALS,
4168
+ symbol: "USDC",
4169
+ isUsdQuote: true
4170
+ };
4171
+ }
4172
+ async function convertQuoteAmountToUsd(tokenSymbol, quoteAmount, quoteConfig, resolveUsdPrice) {
4173
+ if (quoteConfig.isUsdQuote) {
4174
+ const usdcPrice = await resolveUsdPrice("USDC");
4175
+ return quoteAmount * usdcPrice;
4176
+ }
4177
+ const intermediateUsdPrice = await resolveUsdPrice(quoteConfig.symbol);
4178
+ return quoteAmount * intermediateUsdPrice;
4179
+ }
4180
+
4144
4181
  // src/modules/pricer.ts
4145
4182
  var PRICE_METHOD_PRIORITY = [
4146
4183
  "AvnuApi",
@@ -4164,7 +4201,7 @@ var Pricer = class extends PricerBase {
4164
4201
  */
4165
4202
  // ! switch to USDC (new) later
4166
4203
  this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
4167
- this.EKUBO_API = "https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
4204
+ this.EKUBO_API = `https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/${PRICER_USDC_ADDRESS}`;
4168
4205
  this.refreshInterval = refreshInterval;
4169
4206
  this.staleTime = staleTime;
4170
4207
  this.avnuApiPricer = new PricerAvnuApi(config, tokens2);
@@ -4337,37 +4374,67 @@ var Pricer = class extends PricerBase {
4337
4374
  async _getPriceCoinMarketCap(token) {
4338
4375
  throw new Error("Not implemented");
4339
4376
  }
4377
+ async _resolveQuoteTokenUsdPrice(symbol) {
4378
+ const cached = this.prices[symbol];
4379
+ if (cached && !this.isStale(cached.timestamp, symbol)) {
4380
+ return cached.price;
4381
+ }
4382
+ const quoteToken = this.tokens.find((t) => t.symbol === symbol);
4383
+ if (!quoteToken) {
4384
+ throw new FatalError(`Quote token ${symbol} not found in pricer tokens`);
4385
+ }
4386
+ return await this._getPrice(quoteToken);
4387
+ }
4388
+ async _convertDexQuoteToUsd(token, quoteAmount) {
4389
+ const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
4390
+ const usdPrice = await convertQuoteAmountToUsd(
4391
+ token.symbol,
4392
+ quoteAmount,
4393
+ quoteConfig,
4394
+ this._resolveQuoteTokenUsdPrice.bind(this)
4395
+ );
4396
+ logger.verbose(
4397
+ `${token.symbol}: ${quoteAmount} ${quoteConfig.symbol} -> $${usdPrice}`
4398
+ );
4399
+ return usdPrice;
4400
+ }
4340
4401
  async _getAvnuPrice(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
4341
- logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
4402
+ const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
4403
+ logger.verbose(`Getting price of ${token.symbol} using Avnu, amountIn: ${amountIn.toWei()}`);
4342
4404
  const avnuWrapper = new AvnuWrapper();
4343
- const usdcAddress = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
4344
- const quote = await avnuWrapper.getQuotes(token.address.toString(), usdcAddress, amountIn.toWei(), "0x1");
4405
+ const quote = await avnuWrapper.getQuotes(
4406
+ token.address.toString(),
4407
+ quoteConfig.address,
4408
+ amountIn.toWei(),
4409
+ "0x1"
4410
+ );
4345
4411
  const multiplier = 1 / amountIn.toNumber();
4346
- const outputUSDC = Number(Web3Number.fromWei(quote.buyAmount.toString(), 6).toFixed(6)) * multiplier;
4347
- logger.verbose(`Avnu: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
4348
- if (outputUSDC === 0 && retry < 3) {
4349
- const amountIn2 = new Web3Number(100, token.decimals);
4350
- return await this._getAvnuPrice(token, amountIn2, retry + 1);
4412
+ const outputQuote = Number(
4413
+ Web3Number.fromWei(quote.buyAmount.toString(), quoteConfig.decimals).toFixed(6)
4414
+ ) * multiplier;
4415
+ logger.verbose(`Avnu: ${token.symbol} -> ${quoteConfig.symbol}: ${outputQuote}, retry: ${retry}`);
4416
+ if (outputQuote === 0 && retry < 3) {
4417
+ const retryAmountIn = new Web3Number(100, token.decimals);
4418
+ return await this._getAvnuPrice(token, retryAmountIn, retry + 1);
4351
4419
  }
4352
- const usdcPrice = 1;
4353
- logger.verbose(`USDC Price: ${usdcPrice}`);
4354
- return outputUSDC * usdcPrice;
4420
+ return await this._convertDexQuoteToUsd(token, outputQuote);
4355
4421
  }
4356
4422
  async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
4423
+ const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
4357
4424
  logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
4358
- const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
4425
+ const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei()).replace(PRICER_USDC_ADDRESS, quoteConfig.address);
4359
4426
  const result = await axios4.get(url);
4360
4427
  const data = result.data;
4361
4428
  const multiplier = 1 / amountIn.toNumber();
4362
- const outputUSDC = Number(Web3Number.fromWei(data.total_calculated, 6).toFixed(6)) * multiplier;
4363
- logger.verbose(`Ekubo: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
4364
- if (outputUSDC === 0 && retry < 3) {
4365
- const amountIn2 = new Web3Number(100, token.decimals);
4366
- return await this._getPriceEkubo(token, amountIn2, retry + 1);
4429
+ const outputQuote = Number(
4430
+ Web3Number.fromWei(data.total_calculated, quoteConfig.decimals).toFixed(6)
4431
+ ) * multiplier;
4432
+ logger.verbose(`Ekubo: ${token.symbol} -> ${quoteConfig.symbol}: ${outputQuote}, retry: ${retry}`);
4433
+ if (outputQuote === 0 && retry < 3) {
4434
+ const retryAmountIn = new Web3Number(100, token.decimals);
4435
+ return await this._getPriceEkubo(token, retryAmountIn, retry + 1);
4367
4436
  }
4368
- const usdcPrice = 1;
4369
- logger.verbose(`USDC Price: ${usdcPrice}`);
4370
- return outputUSDC * usdcPrice;
4437
+ return await this._convertDexQuoteToUsd(token, outputQuote);
4371
4438
  }
4372
4439
  };
4373
4440
 
@@ -4949,12 +5016,10 @@ var ekubo_price_fethcer_abi_default = [
4949
5016
 
4950
5017
  // src/modules/ekubo-pricer.ts
4951
5018
  var EkuboPricer = class extends PricerBase {
4952
- constructor(config, tokens2) {
5019
+ constructor(config, tokens2, resolveUsdPrice) {
4953
5020
  super(config, tokens2);
5021
+ this.resolveUsdPrice = resolveUsdPrice;
4954
5022
  this.EKUBO_PRICE_FETCHER_ADDRESS = "0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1";
4955
- // Updating to new USDC_ADDRESS
4956
- this.USDC_ADDRESS = "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
4957
- this.USDC_DECIMALS = 6;
4958
5023
  this.contract = new Contract3({
4959
5024
  abi: ekubo_price_fethcer_abi_default,
4960
5025
  address: this.EKUBO_PRICE_FETCHER_ADDRESS,
@@ -4968,9 +5033,16 @@ var EkuboPricer = class extends PricerBase {
4968
5033
  if (!tokenAddr) {
4969
5034
  throw new Error(`EkuboPricer:getPrice - no token`);
4970
5035
  }
5036
+ const tokenInfo = this.tokens.find(
5037
+ (t) => t.address.eqString(tokenAddr)
5038
+ );
5039
+ if (!tokenInfo) {
5040
+ throw new Error(`Token ${tokenAddr} not found in global tokens`);
5041
+ }
5042
+ const quoteConfig = resolveQuoteTokenConfig(tokenInfo, this.tokens);
4971
5043
  const result = await this.contract.call(
4972
5044
  "get_prices",
4973
- [this.USDC_ADDRESS, [tokenAddr], 3600, 1e6],
5045
+ [quoteConfig.address, [tokenAddr], 3600, 1e6],
4974
5046
  { blockIdentifier }
4975
5047
  );
4976
5048
  if (!result || result.length === 0) {
@@ -4982,17 +5054,23 @@ var EkuboPricer = class extends PricerBase {
4982
5054
  throw new Error(`EkuboPricer: Price fetch failed with variant: ${variant}`);
4983
5055
  }
4984
5056
  const rawPrice = typeof priceResult.variant.Price === "string" ? BigInt(priceResult.variant.Price) : priceResult.variant.Price;
4985
- const tokenInfo = this.tokens.find(
4986
- (t) => t.address.address.toLowerCase() === tokenAddr.toLowerCase()
4987
- );
4988
- if (!tokenInfo) {
4989
- throw new Error(`Token ${tokenAddr} not found in global tokens`);
4990
- }
4991
5057
  const priceAfterX128 = this.div2Power128(rawPrice);
4992
- const decimalAdjustment = 10 ** (tokenInfo.decimals - this.USDC_DECIMALS);
4993
- const price = priceAfterX128 * decimalAdjustment;
5058
+ const decimalAdjustment = 10 ** (tokenInfo.decimals - quoteConfig.decimals);
5059
+ const priceInQuote = priceAfterX128 * decimalAdjustment;
5060
+ if (!quoteConfig.isUsdQuote) {
5061
+ if (!this.resolveUsdPrice) {
5062
+ throw new Error(
5063
+ `EkuboPricer: USD price resolver required for ${tokenInfo.symbol} via ${quoteConfig.symbol}`
5064
+ );
5065
+ }
5066
+ const quoteUsdPrice = await this.resolveUsdPrice(quoteConfig.symbol);
5067
+ return {
5068
+ price: priceInQuote * quoteUsdPrice,
5069
+ timestamp: /* @__PURE__ */ new Date()
5070
+ };
5071
+ }
4994
5072
  return {
4995
- price,
5073
+ price: priceInQuote,
4996
5074
  timestamp: /* @__PURE__ */ new Date()
4997
5075
  };
4998
5076
  }
@@ -5020,7 +5098,10 @@ var PricerFromApi = class extends PricerBase {
5020
5098
  ];
5021
5099
  this.apolloClient = createApolloClient(config);
5022
5100
  this.pragma = new Pragma(config, tokens2);
5023
- this.ekuboPricer = new EkuboPricer(config, tokens2);
5101
+ this.ekuboPricer = new EkuboPricer(config, tokens2, async (symbol) => {
5102
+ const priceInfo = await this.getPriceFromMyAPI(symbol);
5103
+ return priceInfo.price;
5104
+ });
5024
5105
  }
5025
5106
  async getPrice(tokenSymbol, blockNumber) {
5026
5107
  const tokenInfo = this.tokens.find((t) => t.symbol === tokenSymbol);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "2.0.0-dev.50",
3
+ "version": "2.0.0-dev.51",
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
  "types": "dist/index.d.ts",
package/src/global.ts CHANGED
@@ -36,6 +36,7 @@ const defaultTokens: TokenInfo[] = [{
36
36
  displayDecimals: 2,
37
37
  priceMethod: 'Avnu',
38
38
  indexingType: TokenIndexingType.LST_SCRIPT,
39
+ intermediateQuoteTokenSymbol: 'STRK',
39
40
  }, {
40
41
  name: 'ETH',
41
42
  symbol: 'ETH',
@@ -249,7 +250,7 @@ const tokens: TokenInfo[] = defaultTokens;
249
250
  * - fatalError: Things to do when a fatal error occurs
250
251
  */
251
252
  export class Global {
252
- static cache: Record<string, {value: any, ttl: number, timestamp: number}> = {};
253
+ static cache: Record<string, { value: any, ttl: number, timestamp: number }> = {};
253
254
 
254
255
  static fatalError(message: string, err?: Error) {
255
256
  logger.error(message);
@@ -46,6 +46,7 @@ export interface TokenInfo {
46
46
  priceMethod?: PriceMethod; // preferred price source; tried first, then falls back to other methods
47
47
  dontPrice?: boolean; // a flag that skips pricer to check these tokens.
48
48
  indexingType: TokenIndexingType;
49
+ intermediateQuoteTokenSymbol?: string; // for tokens like xSTRK, directly quoting in USDC can lead to higher slippages on avnu, instead can quote in STRK and then convert STRK to USDC
49
50
  }
50
51
 
51
52
  export enum Network {
@@ -3,19 +3,25 @@ import EkuboPricerAbi from '@/data/ekubo-price-fethcer.abi.json';
3
3
  import { PricerBase } from "./pricerBase";
4
4
  import { IConfig, TokenInfo } from "@/interfaces";
5
5
  import { PriceInfo } from "./pricer";
6
+ import {
7
+ resolveQuoteTokenConfig,
8
+ } from "./pricer-quote-utils";
9
+
10
+ export type UsdPriceResolver = (symbol: string) => Promise<number>;
6
11
 
7
12
  export class EkuboPricer extends PricerBase {
8
13
  EKUBO_PRICE_FETCHER_ADDRESS = '0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1';
9
14
  readonly contract: Contract;
10
- // Updating to new USDC_ADDRESS
11
- private readonly USDC_ADDRESS = '0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb';
12
- private readonly USDC_DECIMALS = 6;
13
15
 
14
- constructor(config: IConfig, tokens: TokenInfo[]) {
16
+ constructor(
17
+ config: IConfig,
18
+ tokens: TokenInfo[],
19
+ private readonly resolveUsdPrice?: UsdPriceResolver,
20
+ ) {
15
21
  super(config, tokens);
16
22
  this.contract = new Contract({
17
- abi: EkuboPricerAbi,
18
- address: this.EKUBO_PRICE_FETCHER_ADDRESS,
23
+ abi: EkuboPricerAbi,
24
+ address: this.EKUBO_PRICE_FETCHER_ADDRESS,
19
25
  providerOrAccount: config.provider as RpcProvider
20
26
  });
21
27
  }
@@ -29,14 +35,24 @@ export class EkuboPricer extends PricerBase {
29
35
  throw new Error(`EkuboPricer:getPrice - no token`);
30
36
  }
31
37
 
38
+ const tokenInfo = this.tokens.find(t =>
39
+ t.address.eqString(tokenAddr)
40
+ );
41
+
42
+ if (!tokenInfo) {
43
+ throw new Error(`Token ${tokenAddr} not found in global tokens`);
44
+ }
45
+
46
+ const quoteConfig = resolveQuoteTokenConfig(tokenInfo, this.tokens);
47
+
32
48
  // get_prices arguments in order:
33
- // - quote_token: USDC address (quote token for price calculation)
49
+ // - quote_token: quote token address for price calculation
34
50
  // - base_tokens: array containing the base token address/addresses
35
51
  // - period: time period in seconds for TWAP (3600 = 1 hour)
36
52
  // - min_token: minimum token amount threshold (min liquidity) in 6 Decimals = 1000000)
37
53
  const result: any = await this.contract.call(
38
54
  'get_prices',
39
- [this.USDC_ADDRESS, [tokenAddr], 3600, 1000000],
55
+ [quoteConfig.address, [tokenAddr], 3600, 1000000],
40
56
  { blockIdentifier }
41
57
  );
42
58
 
@@ -45,36 +61,39 @@ export class EkuboPricer extends PricerBase {
45
61
  }
46
62
 
47
63
  const priceResult = result[0];
48
-
64
+
49
65
  if (!priceResult?.variant?.Price) {
50
66
  const variant = priceResult?.variant ? Object.keys(priceResult.variant)[0] : 'Unknown';
51
67
  throw new Error(`EkuboPricer: Price fetch failed with variant: ${variant}`);
52
68
  }
53
69
 
54
- const rawPrice = typeof priceResult.variant.Price === 'string'
55
- ? BigInt(priceResult.variant.Price)
70
+ const rawPrice = typeof priceResult.variant.Price === 'string'
71
+ ? BigInt(priceResult.variant.Price)
56
72
  : priceResult.variant.Price;
57
73
 
58
- // Get token info to determine decimals from configured tokens
59
- const tokenInfo = this.tokens.find(t =>
60
- t.address.address.toLowerCase() === tokenAddr.toLowerCase()
61
- );
62
-
63
- if (!tokenInfo) {
64
- throw new Error(`Token ${tokenAddr} not found in global tokens`);
65
- }
66
-
67
74
  // Convert from x128 format
68
75
  const priceAfterX128 = this.div2Power128(rawPrice);
69
-
70
- // Adjust for token decimals
71
- const decimalAdjustment = 10 ** (tokenInfo.decimals - this.USDC_DECIMALS);
72
- const price = priceAfterX128 * decimalAdjustment;
76
+
77
+ // Adjust for token decimals relative to quote token decimals
78
+ const decimalAdjustment = 10 ** (tokenInfo.decimals - quoteConfig.decimals);
79
+ const priceInQuote = priceAfterX128 * decimalAdjustment;
80
+
81
+ if (!quoteConfig.isUsdQuote) {
82
+ if (!this.resolveUsdPrice) {
83
+ throw new Error(
84
+ `EkuboPricer: USD price resolver required for ${tokenInfo.symbol} via ${quoteConfig.symbol}`,
85
+ );
86
+ }
87
+ const quoteUsdPrice = await this.resolveUsdPrice(quoteConfig.symbol);
88
+ return {
89
+ price: priceInQuote * quoteUsdPrice,
90
+ timestamp: new Date(),
91
+ };
92
+ }
73
93
 
74
94
  return {
75
- price,
95
+ price: priceInQuote,
76
96
  timestamp: new Date()
77
97
  };
78
98
  }
79
99
  }
80
-
@@ -31,7 +31,10 @@ export class PricerFromApi extends PricerBase {
31
31
  super(config, tokens);
32
32
  this.apolloClient = createApolloClient(config);
33
33
  this.pragma = new Pragma(config, tokens);
34
- this.ekuboPricer = new EkuboPricer(config, tokens);
34
+ this.ekuboPricer = new EkuboPricer(config, tokens, async (symbol) => {
35
+ const priceInfo = await this.getPriceFromMyAPI(symbol);
36
+ return priceInfo.price;
37
+ });
35
38
  }
36
39
 
37
40
  async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier): Promise<PriceInfo> {
@@ -0,0 +1,54 @@
1
+ import { TokenInfo } from "@/interfaces/common";
2
+
3
+ export const PRICER_USDC_ADDRESS =
4
+ "0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb";
5
+ export const PRICER_USDC_DECIMALS = 6;
6
+
7
+ export interface QuoteTokenConfig {
8
+ address: string;
9
+ decimals: number;
10
+ symbol: string;
11
+ isUsdQuote: boolean;
12
+ }
13
+
14
+ export function resolveQuoteTokenConfig(token: TokenInfo, tokens: TokenInfo[]): QuoteTokenConfig {
15
+ if (token.intermediateQuoteTokenSymbol) {
16
+ const intermediateToken = tokens.find(
17
+ (t) => t.symbol === token.intermediateQuoteTokenSymbol,
18
+ );
19
+ if (!intermediateToken) {
20
+ throw new Error(
21
+ `Intermediate quote token ${token.intermediateQuoteTokenSymbol} not found for ${token.symbol}`,
22
+ );
23
+ }
24
+
25
+ return {
26
+ address: intermediateToken.address.toString(),
27
+ decimals: intermediateToken.decimals,
28
+ symbol: intermediateToken.symbol,
29
+ isUsdQuote: false,
30
+ };
31
+ }
32
+
33
+ return {
34
+ address: PRICER_USDC_ADDRESS,
35
+ decimals: PRICER_USDC_DECIMALS,
36
+ symbol: "USDC",
37
+ isUsdQuote: true,
38
+ };
39
+ }
40
+
41
+ export async function convertQuoteAmountToUsd(
42
+ tokenSymbol: string,
43
+ quoteAmount: number,
44
+ quoteConfig: QuoteTokenConfig,
45
+ resolveUsdPrice: (symbol: string) => Promise<number>,
46
+ ): Promise<number> {
47
+ if (quoteConfig.isUsdQuote) {
48
+ const usdcPrice = await resolveUsdPrice("USDC");
49
+ return quoteAmount * usdcPrice;
50
+ }
51
+
52
+ const intermediateUsdPrice = await resolveUsdPrice(quoteConfig.symbol);
53
+ return quoteAmount * intermediateUsdPrice;
54
+ }