@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.browser.global.js +118 -37
- package/dist/index.browser.mjs +118 -37
- package/dist/index.d.ts +7 -4
- package/dist/index.js +118 -37
- package/dist/index.mjs +118 -37
- package/package.json +1 -1
- package/src/global.ts +2 -1
- package/src/interfaces/common.tsx +1 -0
- package/src/modules/ekubo-pricer.ts +45 -26
- package/src/modules/pricer-from-api.ts +4 -1
- package/src/modules/pricer-quote-utils.ts +54 -0
- package/src/modules/pricer.ts +67 -30
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 =
|
|
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
|
-
|
|
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
|
|
4514
|
-
|
|
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
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
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
|
-
|
|
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
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
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 -
|
|
5163
|
-
const
|
|
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 =
|
|
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
|
-
|
|
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
|
|
4344
|
-
|
|
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
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
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
|
-
|
|
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
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
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 -
|
|
4993
|
-
const
|
|
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
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(
|
|
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:
|
|
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
|
-
[
|
|
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 -
|
|
72
|
-
const
|
|
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
|
+
}
|