@strkfarm/sdk 2.0.0-dev.50 → 2.0.0-dev.52
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 +121 -40
- package/dist/index.browser.mjs +121 -40
- package/dist/index.d.ts +7 -4
- package/dist/index.js +121 -40
- package/dist/index.mjs +121 -40
- 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/src/strategies/universal-lst-muliplier-strategy.tsx +5 -3
|
@@ -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
|
+
}
|
package/src/modules/pricer.ts
CHANGED
|
@@ -8,6 +8,11 @@ import { logger } from "@/utils/logger";
|
|
|
8
8
|
import { AvnuWrapper } from "./avnu";
|
|
9
9
|
import { BlockIdentifier } from "starknet";
|
|
10
10
|
import { PricerAvnuApi } from "./pricer-avnu-api";
|
|
11
|
+
import {
|
|
12
|
+
convertQuoteAmountToUsd,
|
|
13
|
+
PRICER_USDC_ADDRESS,
|
|
14
|
+
resolveQuoteTokenConfig,
|
|
15
|
+
} from "./pricer-quote-utils";
|
|
11
16
|
|
|
12
17
|
export interface PriceInfo {
|
|
13
18
|
price: number,
|
|
@@ -41,7 +46,7 @@ export class Pricer extends PricerBase {
|
|
|
41
46
|
*/
|
|
42
47
|
// ! switch to USDC (new) later
|
|
43
48
|
protected PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
|
|
44
|
-
protected EKUBO_API =
|
|
49
|
+
protected EKUBO_API = `https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/${PRICER_USDC_ADDRESS}`; // e.g. ETH/USDC
|
|
45
50
|
|
|
46
51
|
constructor(config: IConfig, tokens: TokenInfo[], refreshInterval = 30000, staleTime = 60000) {
|
|
47
52
|
super(config, tokens);
|
|
@@ -242,48 +247,80 @@ export class Pricer extends PricerBase {
|
|
|
242
247
|
throw new Error("Not implemented");
|
|
243
248
|
}
|
|
244
249
|
|
|
250
|
+
protected async _resolveQuoteTokenUsdPrice(symbol: string): Promise<number> {
|
|
251
|
+
const cached = this.prices[symbol];
|
|
252
|
+
if (cached && !this.isStale(cached.timestamp, symbol)) {
|
|
253
|
+
return cached.price;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const quoteToken = this.tokens.find((t) => t.symbol === symbol);
|
|
257
|
+
if (!quoteToken) {
|
|
258
|
+
throw new FatalError(`Quote token ${symbol} not found in pricer tokens`);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return await this._getPrice(quoteToken);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
protected async _convertDexQuoteToUsd(
|
|
265
|
+
token: TokenInfo,
|
|
266
|
+
quoteAmount: number,
|
|
267
|
+
): Promise<number> {
|
|
268
|
+
const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
|
|
269
|
+
const usdPrice = await convertQuoteAmountToUsd(
|
|
270
|
+
token.symbol,
|
|
271
|
+
quoteAmount,
|
|
272
|
+
quoteConfig,
|
|
273
|
+
this._resolveQuoteTokenUsdPrice.bind(this),
|
|
274
|
+
);
|
|
275
|
+
logger.verbose(
|
|
276
|
+
`${token.symbol}: ${quoteAmount} ${quoteConfig.symbol} -> $${usdPrice}`,
|
|
277
|
+
);
|
|
278
|
+
return usdPrice;
|
|
279
|
+
}
|
|
280
|
+
|
|
245
281
|
async _getAvnuPrice(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
|
|
246
|
-
|
|
247
|
-
|
|
282
|
+
const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
|
|
283
|
+
logger.verbose(`Getting price of ${token.symbol} using Avnu, amountIn: ${amountIn.toWei()}`);
|
|
284
|
+
|
|
248
285
|
const avnuWrapper = new AvnuWrapper();
|
|
249
|
-
const
|
|
250
|
-
|
|
286
|
+
const quote = await avnuWrapper.getQuotes(
|
|
287
|
+
token.address.toString(),
|
|
288
|
+
quoteConfig.address,
|
|
289
|
+
amountIn.toWei(),
|
|
290
|
+
"0x1",
|
|
291
|
+
);
|
|
251
292
|
const multiplier = 1 / amountIn.toNumber();
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
293
|
+
const outputQuote = Number(
|
|
294
|
+
Web3Number.fromWei(quote.buyAmount.toString(), quoteConfig.decimals).toFixed(6),
|
|
295
|
+
) * multiplier;
|
|
296
|
+
logger.verbose(`Avnu: ${token.symbol} -> ${quoteConfig.symbol}: ${outputQuote}, retry: ${retry}`);
|
|
297
|
+
if (outputQuote === 0 && retry < 3) {
|
|
298
|
+
const retryAmountIn = new Web3Number(100, token.decimals);
|
|
299
|
+
return await this._getAvnuPrice(token, retryAmountIn, retry + 1);
|
|
258
300
|
}
|
|
259
301
|
|
|
260
|
-
|
|
261
|
-
// so we need to get the price of USDC in USD
|
|
262
|
-
// and then convert the outputUSDC to USD
|
|
263
|
-
const usdcPrice = 1; // (await this.getPrice('USDC')).price;
|
|
264
|
-
logger.verbose(`USDC Price: ${usdcPrice}`);
|
|
265
|
-
return outputUSDC * usdcPrice;
|
|
302
|
+
return await this._convertDexQuoteToUsd(token, outputQuote);
|
|
266
303
|
}
|
|
267
304
|
|
|
268
305
|
async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
|
|
306
|
+
const quoteConfig = resolveQuoteTokenConfig(token, this.tokens);
|
|
269
307
|
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
270
|
-
const url = this.EKUBO_API
|
|
308
|
+
const url = this.EKUBO_API
|
|
309
|
+
.replace("{{TOKEN_ADDRESS}}", token.address.toString())
|
|
310
|
+
.replace("{{AMOUNT}}", amountIn.toWei())
|
|
311
|
+
.replace(PRICER_USDC_ADDRESS, quoteConfig.address);
|
|
271
312
|
const result = await axios.get(url);
|
|
272
313
|
const data: any = result.data;
|
|
273
314
|
const multiplier = 1 / amountIn.toNumber();
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
315
|
+
const outputQuote = Number(
|
|
316
|
+
Web3Number.fromWei(data.total_calculated, quoteConfig.decimals).toFixed(6),
|
|
317
|
+
) * multiplier;
|
|
318
|
+
logger.verbose(`Ekubo: ${token.symbol} -> ${quoteConfig.symbol}: ${outputQuote}, retry: ${retry}`);
|
|
319
|
+
if (outputQuote === 0 && retry < 3) {
|
|
320
|
+
const retryAmountIn = new Web3Number(100, token.decimals);
|
|
321
|
+
return await this._getPriceEkubo(token, retryAmountIn, retry + 1);
|
|
280
322
|
}
|
|
281
323
|
|
|
282
|
-
|
|
283
|
-
// so we need to get the price of USDC in USD
|
|
284
|
-
// and then convert the outputUSDC to USD
|
|
285
|
-
const usdcPrice = 1; // (await this.getPrice('USDC')).price;
|
|
286
|
-
logger.verbose(`USDC Price: ${usdcPrice}`);
|
|
287
|
-
return outputUSDC * usdcPrice;
|
|
324
|
+
return await this._convertDexQuoteToUsd(token, outputQuote);
|
|
288
325
|
}
|
|
289
326
|
}
|
|
@@ -158,16 +158,18 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
158
158
|
)
|
|
159
159
|
: new Web3Number(1, underlyingTokenInfo.decimals);
|
|
160
160
|
|
|
161
|
+
// we consider LST as sell token bcz, generally, our risk is during LST less.
|
|
162
|
+
// hence, we price based on the sell price rather than buy price (which is generally higher)
|
|
161
163
|
const quote = await avnuModule.getQuotes(
|
|
162
|
-
underlyingTokenInfo.address.address,
|
|
163
164
|
lstTokenInfo.address.address,
|
|
165
|
+
underlyingTokenInfo.address.address,
|
|
164
166
|
sellAmount.toWei(),
|
|
165
167
|
this.metadata.additionalInfo.vaultAllocator.address,
|
|
166
168
|
);
|
|
167
169
|
|
|
168
|
-
const underlyingAmountNumber =
|
|
170
|
+
const underlyingAmountNumber = Web3Number.fromWei(quote.buyAmount.toString(), underlyingTokenInfo.decimals).toNumber();
|
|
169
171
|
const lstAmountNumber = Web3Number.fromWei(
|
|
170
|
-
quote.
|
|
172
|
+
quote.sellAmount.toString(),
|
|
171
173
|
lstTokenInfo.decimals,
|
|
172
174
|
).toNumber();
|
|
173
175
|
|