@strkfarm/sdk 2.0.0-dev.9 → 2.0.0-staging.2

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.
Files changed (64) hide show
  1. package/dist/index.browser.global.js +111371 -93151
  2. package/dist/index.browser.mjs +27815 -32690
  3. package/dist/index.d.ts +1095 -2011
  4. package/dist/index.js +27425 -32309
  5. package/dist/index.mjs +27590 -32452
  6. package/package.json +6 -5
  7. package/src/data/ekubo-price-fethcer.abi.json +265 -0
  8. package/src/data/universal-vault.abi.json +20 -135
  9. package/src/dataTypes/address.ts +0 -7
  10. package/src/dataTypes/index.ts +3 -2
  11. package/src/dataTypes/mynumber.ts +141 -0
  12. package/src/global.ts +296 -288
  13. package/src/index.browser.ts +6 -5
  14. package/src/interfaces/common.tsx +324 -184
  15. package/src/modules/apollo-client-config.ts +28 -0
  16. package/src/modules/avnu.ts +4 -17
  17. package/src/modules/ekubo-pricer.ts +79 -0
  18. package/src/modules/ekubo-quoter.ts +11 -88
  19. package/src/modules/erc20.ts +21 -67
  20. package/src/modules/harvests.ts +26 -15
  21. package/src/modules/index.ts +11 -13
  22. package/src/modules/lst-apr.ts +0 -36
  23. package/src/modules/pragma.ts +23 -8
  24. package/src/modules/pricer-from-api.ts +150 -14
  25. package/src/modules/pricer.ts +2 -1
  26. package/src/modules/pricerBase.ts +2 -1
  27. package/src/node/deployer.ts +36 -1
  28. package/src/node/pricer-redis.ts +2 -1
  29. package/src/strategies/autoCompounderStrk.ts +1 -1
  30. package/src/strategies/base-strategy.ts +5 -22
  31. package/src/strategies/ekubo-cl-vault.tsx +2904 -2175
  32. package/src/strategies/factory.ts +165 -0
  33. package/src/strategies/index.ts +10 -11
  34. package/src/strategies/registry.ts +268 -0
  35. package/src/strategies/sensei.ts +416 -292
  36. package/src/strategies/universal-adapters/adapter-utils.ts +1 -5
  37. package/src/strategies/universal-adapters/baseAdapter.ts +153 -181
  38. package/src/strategies/universal-adapters/common-adapter.ts +77 -98
  39. package/src/strategies/universal-adapters/index.ts +1 -5
  40. package/src/strategies/universal-adapters/vesu-adapter.ts +218 -220
  41. package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +51 -58
  42. package/src/strategies/universal-lst-muliplier-strategy.tsx +1952 -992
  43. package/src/strategies/universal-strategy.tsx +1713 -1150
  44. package/src/strategies/vesu-rebalance.tsx +1189 -986
  45. package/src/utils/health-factor-math.ts +5 -11
  46. package/src/utils/index.ts +8 -9
  47. package/src/utils/strategy-utils.ts +57 -0
  48. package/src/data/extended-deposit.abi.json +0 -3613
  49. package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
  50. package/src/modules/ExtendedWrapperSDk/types.ts +0 -311
  51. package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -395
  52. package/src/modules/midas.ts +0 -159
  53. package/src/modules/token-market-data.ts +0 -202
  54. package/src/strategies/svk-strategy.ts +0 -247
  55. package/src/strategies/universal-adapters/adapter-optimizer.ts +0 -65
  56. package/src/strategies/universal-adapters/avnu-adapter.ts +0 -413
  57. package/src/strategies/universal-adapters/extended-adapter.ts +0 -972
  58. package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
  59. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +0 -1306
  60. package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -34
  61. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -77
  62. package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -49
  63. package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -370
  64. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1379
@@ -0,0 +1,79 @@
1
+ import { Contract, RpcProvider, BlockIdentifier } from "starknet";
2
+ import EkuboPricerAbi from '@/data/ekubo-price-fethcer.abi.json';
3
+ import { PricerBase } from "./pricerBase";
4
+ import { IConfig, TokenInfo } from "@/interfaces";
5
+ import { PriceInfo } from "./pricer";
6
+
7
+ export class EkuboPricer extends PricerBase {
8
+ EKUBO_PRICE_FETCHER_ADDRESS = '0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1';
9
+ readonly contract: Contract;
10
+ private readonly USDC_ADDRESS = '0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8';
11
+ private readonly USDC_DECIMALS = 6;
12
+
13
+ constructor(config: IConfig, tokens: TokenInfo[]) {
14
+ super(config, tokens);
15
+ this.contract = new Contract({
16
+ abi: EkuboPricerAbi,
17
+ address: this.EKUBO_PRICE_FETCHER_ADDRESS,
18
+ providerOrAccount: config.provider as RpcProvider
19
+ });
20
+ }
21
+
22
+ private div2Power128(num: bigint): number {
23
+ return Number((num * BigInt(1e18)) / BigInt(2 ** 128)) / 1e18;
24
+ }
25
+
26
+ async getPrice(tokenAddr: string, blockIdentifier: BlockIdentifier = 'latest'): Promise<PriceInfo> {
27
+ if (!tokenAddr) {
28
+ throw new Error(`EkuboPricer:getPrice - no token`);
29
+ }
30
+
31
+ // get_prices arguments in order:
32
+ // - quote_token: USDC address (quote token for price calculation)
33
+ // - base_tokens: array containing the base token address/addresses
34
+ // - period: time period in seconds for TWAP (3600 = 1 hour)
35
+ // - min_token: minimum token amount threshold (min liquidity) in 6 Decimals = 1000000)
36
+ const result: any = await this.contract.call(
37
+ 'get_prices',
38
+ [this.USDC_ADDRESS, [tokenAddr], 3600, 1000000],
39
+ { blockIdentifier }
40
+ );
41
+
42
+ if (!result || result.length === 0) {
43
+ throw new Error(`EkuboPricer: No price result returned for ${tokenAddr}`);
44
+ }
45
+
46
+ const priceResult = result[0];
47
+
48
+ if (!priceResult?.variant?.Price) {
49
+ const variant = priceResult?.variant ? Object.keys(priceResult.variant)[0] : 'Unknown';
50
+ throw new Error(`EkuboPricer: Price fetch failed with variant: ${variant}`);
51
+ }
52
+
53
+ const rawPrice = typeof priceResult.variant.Price === 'string'
54
+ ? BigInt(priceResult.variant.Price)
55
+ : priceResult.variant.Price;
56
+
57
+ // Get token info to determine decimals from configured tokens
58
+ const tokenInfo = this.tokens.find(t =>
59
+ t.address.address.toLowerCase() === tokenAddr.toLowerCase()
60
+ );
61
+
62
+ if (!tokenInfo) {
63
+ throw new Error(`Token ${tokenAddr} not found in global tokens`);
64
+ }
65
+
66
+ // Convert from x128 format
67
+ const priceAfterX128 = this.div2Power128(rawPrice);
68
+
69
+ // Adjust for token decimals
70
+ const decimalAdjustment = 10 ** (tokenInfo.decimals - this.USDC_DECIMALS);
71
+ const price = priceAfterX128 * decimalAdjustment;
72
+
73
+ return {
74
+ price,
75
+ timestamp: new Date()
76
+ };
77
+ }
78
+ }
79
+
@@ -2,12 +2,6 @@ import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { IConfig, TokenInfo } from "@/interfaces/common";
3
3
  import { Swap } from "@/strategies";
4
4
  import axios from "axios";
5
- import { logger } from "@/utils";
6
- import { uint256 } from "starknet";
7
- import { Contract } from "starknet";
8
- import ERC4626Abi from "@/data/erc4626.abi.json";
9
- import { TokenMarketData } from "./token-market-data";
10
- import { PricerBase } from "./pricerBase";
11
5
 
12
6
  export interface EkuboRouteNode {
13
7
  pool_key: {
@@ -34,12 +28,9 @@ export interface EkuboQuote {
34
28
 
35
29
 
36
30
  export class EkuboQuoter {
37
- ENDPOINT = 'https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_FROM_ADDRESS}}/{{TOKEN_TO_ADDRESS}}'; // e.g. ETH/USDC'
38
- tokenMarketData: TokenMarketData;
39
-
40
- constructor(private readonly config: IConfig, pricer: PricerBase) {
41
- this.tokenMarketData = new TokenMarketData(pricer, config);
42
- }
31
+ ENDPOINT = 'https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_FROM_ADDRESS}}/{{TOKEN_TO_ADDRESS}}'; // e.g. ETH/USDC'
32
+
33
+ constructor(private readonly config: IConfig) {}
43
34
 
44
35
  /**
45
36
  *
@@ -49,17 +40,16 @@ export class EkuboQuoter {
49
40
  * @returns
50
41
  */
51
42
  async getQuote(fromToken: string, toToken: string, amount: Web3Number, retry = 0): Promise<EkuboQuote> {
52
- // let _fromToken = amount.gt(0) ? fromToken : toToken;
53
- // let _toToken = amount.gt(0) ? toToken : fromToken;
43
+ let _fromToken = amount.gt(0) ? fromToken : toToken;
44
+ let _toToken = amount.gt(0) ? toToken : fromToken;
54
45
 
55
46
  try {
56
- const url = this.ENDPOINT.replace("{{AMOUNT}}", amount.toFixed(0)).replace("{{TOKEN_FROM_ADDRESS}}", fromToken).replace("{{TOKEN_TO_ADDRESS}}", toToken);
57
- console.log("url", url);
58
- const quote = await axios.get(url);
59
- return quote.data as EkuboQuote;
47
+ const quote = await axios.get(this.ENDPOINT.replace("{{AMOUNT}}", amount.toWei()).replace("{{TOKEN_FROM_ADDRESS}}", _fromToken).replace("{{TOKEN_TO_ADDRESS}}", _toToken));
48
+ console.log(`Ekubo quote from ${_fromToken} to ${_toToken} for ${amount.toString()}: ${JSON.stringify(quote.data)}`);
49
+ return quote.data as EkuboQuote;
60
50
  } catch (error: any) {
61
- logger.error(`${error.message} dassf ${error.data}`);
62
- if (retry < 3) {
51
+ console.error(error.message, 'dassf', error.data);
52
+ if (retry < 10) {
63
53
  await new Promise((resolve) => setTimeout(resolve, (retry + 1) * 5000));
64
54
  return await this.getQuote(fromToken, toToken, amount, retry + 1);
65
55
  }
@@ -67,72 +57,6 @@ export class EkuboQuoter {
67
57
  }
68
58
  }
69
59
 
70
- async getDexPrice(baseToken: TokenInfo, quoteToken: TokenInfo, amount: Web3Number) {
71
- const lstTokenInfo = baseToken;
72
- const lstUnderlyingTokenInfo = quoteToken;
73
- const quote = await this.getQuote(
74
- lstTokenInfo.address.address,
75
- lstUnderlyingTokenInfo.address.address,
76
- amount
77
- );
78
- // in Underlying
79
- const outputAmount = Web3Number.fromWei(quote.total_calculated, lstUnderlyingTokenInfo.decimals);
80
- const price = outputAmount.toNumber() / amount.toNumber();
81
- logger.verbose(`${EkuboQuoter.name}:: LST Dex Price: ${price}`);
82
- return price;
83
- }
84
-
85
- async getLSTTrueExchangeRate(baseToken: TokenInfo, quoteToken: TokenInfo, amount: Web3Number) {
86
- const lstTokenInfo = baseToken;
87
- const lstABI = new Contract({
88
- abi: ERC4626Abi,
89
- address: lstTokenInfo.address.address,
90
- providerOrAccount: this.config.provider
91
- });
92
-
93
- const price: any = await lstABI.call('convert_to_assets', [uint256.bnToUint256((new Web3Number(1, lstTokenInfo.decimals)).toWei())]);
94
- const exchangeRate = Number(uint256.uint256ToBN(price).toString()) / Math.pow(10, lstTokenInfo.decimals);
95
- logger.verbose(`${EkuboQuoter.name}:: LST true Exchange Rate: ${exchangeRate}`);
96
- return exchangeRate;
97
- }
98
- // debt collateral
99
- async getSwapLimitAmount(fromToken: TokenInfo, toToken: TokenInfo, amount: Web3Number, max_slippage: number = 0.002): Promise<Web3Number> {
100
- const isExactAmountIn = amount.greaterThanOrEqualTo(0);
101
- logger.verbose(`${EkuboQuoter.name}::getSwapLimitAmount isExactAmountIn: ${isExactAmountIn}, fromToken: ${fromToken.symbol}, toToken: ${toToken.symbol}, amount: ${amount}`);
102
- const isYieldToken = this.tokenMarketData.isAPYSupported(toToken);
103
- console.log("isYieldToken", isYieldToken);
104
-
105
- // if LST, get true exchange rate else use dex price
106
- // wbtc
107
- const baseToken = isExactAmountIn ? toToken : fromToken; // fromToken -> wbtc,
108
- const quoteToken = isExactAmountIn ? fromToken : toToken; // toToken -> usdc,
109
- // need dex price of from token in toToken
110
- // from baseToken to underlying token
111
- // for withdraw, usdc to btc with amount negative
112
- const dexPrice = await this.getDexPrice(baseToken, quoteToken, amount);
113
- const trueExchangeRate = isYieldToken ? await this.tokenMarketData.getTruePrice(baseToken) : dexPrice;
114
- console.log("trueExchangeRate", trueExchangeRate);
115
- if (isExactAmountIn) {
116
- let minLSTReceived = amount.dividedBy(dexPrice).multipliedBy(1 - max_slippage); // used for increase
117
- console.log("minLSTReceived", minLSTReceived);
118
- const minLSTReceivedAsPerTruePrice = amount.dividedBy(trueExchangeRate); // execution output to be <= True LST price
119
- if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
120
- minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
121
- }
122
- logger.verbose(`${EkuboQuoter.name}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`);
123
- return minLSTReceived;
124
- }
125
-
126
- let maxUsedCollateral = amount.abs().dividedBy(dexPrice).multipliedBy(1 + max_slippage); // +ve for exact amount out, used for decrease
127
- const maxUsedCollateralInLST = amount.abs().dividedBy(trueExchangeRate).multipliedBy(1.005); // 0.5% slippage, worst case based on true price
128
- logger.verbose(`${EkuboQuoter.name}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`);
129
- if (maxUsedCollateralInLST > maxUsedCollateral) {
130
- maxUsedCollateral = maxUsedCollateralInLST;
131
- }
132
-
133
- return maxUsedCollateral;
134
- }
135
-
136
60
  /**
137
61
  * Formats Ekubo response for Vesu multiple use
138
62
  * @param quote
@@ -141,8 +65,7 @@ export class EkuboQuoter {
141
65
  */
142
66
  getVesuMultiplyQuote(quote: EkuboQuote, fromTokenInfo: TokenInfo, toTokenInfo: TokenInfo): Swap[] {
143
67
  return quote.splits.map(split => {
144
-
145
- const isNegativeAmount = BigInt(split.amount_specified) <= 0n;
68
+ const isNegativeAmount = BigInt(split.amount_specified) < 0n;
146
69
  const token = isNegativeAmount ? toTokenInfo : fromTokenInfo;
147
70
  return {
148
71
  route: split.route.map(_route => ({
@@ -1,75 +1,29 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { IConfig } from "@/interfaces";
3
- import { Contract, uint256 } from "starknet";
4
- import ERC20Abi from "@/data/erc20.abi.json";
3
+ import { Contract } from "starknet";
4
+ import ERC20Abi from '@/data/erc20.abi.json';
5
5
 
6
6
  export class ERC20 {
7
- readonly config: IConfig;
7
+ readonly config: IConfig;
8
8
 
9
- constructor(config: IConfig) {
10
- this.config = config;
11
- }
9
+ constructor(config: IConfig) {
10
+ this.config = config;
11
+ }
12
12
 
13
- contract(addr: string | ContractAddr) {
14
- const _addr = typeof addr === "string" ? addr : addr.address;
15
- return new Contract({
16
- abi: ERC20Abi,
17
- address: _addr,
18
- providerOrAccount: this.config.provider,
19
- });
20
- }
13
+ contract(addr: string | ContractAddr) {
14
+ const _addr = typeof addr === 'string' ? addr : addr.address;
15
+ return new Contract({abi: ERC20Abi, address: _addr, providerOrAccount: this.config.provider});
16
+ }
21
17
 
22
- async balanceOf(
23
- token: string | ContractAddr,
24
- address: string | ContractAddr,
25
- tokenDecimals: number
26
- ) {
27
- const contract = this.contract(token);
28
- const balance = await contract.call("balance_of", [address.toString()]);
29
- return Web3Number.fromWei(balance.toString(), tokenDecimals);
30
- }
18
+ async balanceOf(token: string | ContractAddr, address: string | ContractAddr, tokenDecimals: number) {
19
+ const contract = this.contract(token);
20
+ const balance = await contract.call('balanceOf', [address.toString()]);
21
+ return Web3Number.fromWei(balance.toString(), tokenDecimals);
22
+ }
31
23
 
32
- async allowance(
33
- token: string | ContractAddr,
34
- owner: string | ContractAddr,
35
- spender: string | ContractAddr,
36
- tokenDecimals: number
37
- ) {
38
- const contract = this.contract(token);
39
- const allowance = await contract.call("allowance", [
40
- owner.toString(),
41
- spender.toString(),
42
- ]);
43
- return Web3Number.fromWei(allowance.toString(), tokenDecimals);
44
- }
45
-
46
- transfer(
47
- token: string | ContractAddr,
48
- to: string | ContractAddr,
49
- amount: Web3Number
50
- ) {
51
- const contract = this.contract(token);
52
- const amountUint256 = uint256.bnToUint256(amount.toWei());
53
- const transferCall = contract.populate("transfer", [
54
- to.toString(),
55
- amountUint256.low.toString(),
56
- amountUint256.high.toString(),
57
- ]);
58
- return transferCall;
59
- }
60
-
61
- approve(
62
- token: string | ContractAddr,
63
- spender: string | ContractAddr,
64
- amount: Web3Number
65
- ) {
66
- const contract = this.contract(token);
67
- const amountUint256 = uint256.bnToUint256(amount.toWei());
68
- const approveCall = contract.populate("approve", [
69
- spender.toString(),
70
- amountUint256.low.toString(),
71
- amountUint256.high.toString(),
72
- ]);
73
- return approveCall;
74
- }
75
- }
24
+ async allowance(token: string | ContractAddr, owner: string | ContractAddr, spender: string | ContractAddr, tokenDecimals: number) {
25
+ const contract = this.contract(token);
26
+ const allowance = await contract.call('allowance', [owner.toString(), spender.toString()]);
27
+ return Web3Number.fromWei(allowance.toString(), tokenDecimals);
28
+ }
29
+ }
@@ -34,23 +34,30 @@ export class Harvests {
34
34
  const unClaimed: HarvestInfo[] = [];
35
35
 
36
36
  // use the latest one
37
- const reward = rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime())[0];
38
-
39
- const cls = await this.config.provider.getClassAt(reward.rewardsContract.address);
40
- const contract = new Contract({abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider});
41
- const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
42
- logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}`);
43
- if (isClaimed) {
44
- return unClaimed;
45
- }
46
- // rewards contract must have enough balance to claim
47
- const bal = await (new ERC20(this.config)).balanceOf(reward.token, reward.rewardsContract.address, 18);
48
- if (bal.lessThan(reward.claim.amount)) {
49
- logger.verbose(`${Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
50
- return unClaimed;
37
+ const sortedRewards = rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
38
+ if (sortedRewards.length == 0) {
39
+ logger.verbose(`${Harvests.name}: no rewards found`);
40
+ return [];
51
41
  }
52
42
 
53
- unClaimed.unshift(reward); // to ensure older harvest is first
43
+ const cls = await this.config.provider.getClassAt(sortedRewards[0].rewardsContract.address);
44
+
45
+ for (const reward of sortedRewards) {
46
+ const contract = new Contract({abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider});
47
+ const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
48
+ logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}, claim id: ${reward.claim.id}, address: ${reward.rewardsContract.address}`);
49
+ if (isClaimed) {
50
+ continue;
51
+ }
52
+ // rewards contract must have enough balance to claim
53
+ const bal = await (new ERC20(this.config)).balanceOf(reward.token, reward.rewardsContract.address, 18);
54
+ if (bal.lessThan(reward.claim.amount)) {
55
+ logger.verbose(`${Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
56
+ continue;
57
+ }
58
+
59
+ unClaimed.push(reward); // to ensure older harvest is first
60
+ }
54
61
  return unClaimed;
55
62
  }
56
63
  }
@@ -101,6 +108,10 @@ export class VesuHarvests extends Harvests {
101
108
  logger.verbose(`${VesuHarvests.name}: claimed_amount: ${claimed_amount.toString()}`);
102
109
 
103
110
  const data = _data.data['defiSpring'];
111
+ if (!data) {
112
+ logger.verbose(`${VesuHarvests.name}: no defiSpring data found`);
113
+ return [];
114
+ }
104
115
 
105
116
  // get the actual reward
106
117
  const actualReward = Web3Number.fromWei(data.amount, 18).minus(claimed_amount);
@@ -1,13 +1,11 @@
1
- export * from './token-market-data';
2
- export * from './pricer';
3
- export * from './pragma';
4
- export * from './zkLend';
5
- export * from './pricer-from-api';
6
- export * from './erc20';
7
- export * from './avnu';
8
- export * from './ekubo-quoter';
9
- export * from './pricer-lst';
10
- export * from './lst-apr';
11
- export * from './pricerBase';
12
- export * from './midas';
13
- export * from './ExtendedWrapperSDk';
1
+ export * from "./pricerBase";
2
+ export * from "./pricer";
3
+ export * from "./pragma";
4
+ export * from "./zkLend";
5
+ export * from "./pricer-from-api";
6
+ export * from "./erc20";
7
+ export * from "./avnu";
8
+ export * from "./ekubo-quoter";
9
+ export * from "./ekubo-pricer";
10
+ export * from "./pricer-lst";
11
+ export * from "./lst-apr";
@@ -1,6 +1,4 @@
1
1
  import { ContractAddr } from "@/dataTypes";
2
- import { Global } from "@/global";
3
- import { TokenInfo } from "@/interfaces";
4
2
  import { logger } from "@/utils";
5
3
 
6
4
  export interface LSTStats {
@@ -21,19 +19,6 @@ export class LSTAPRService {
21
19
  private static cacheTimestamp: number = 0;
22
20
  private static readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
23
21
 
24
- static lstMapping: Record<string, TokenInfo> = {};
25
-
26
- static isLST(address: ContractAddr): boolean {
27
- return this.lstMapping[address.address] !== undefined;
28
- }
29
-
30
- static getUnderlyingFromLST(lstAddress: ContractAddr): TokenInfo {
31
- const underlying = this.lstMapping[lstAddress.address];
32
- if (!underlying) {
33
- throw new Error(`Underlying token not found for ${lstAddress.address}`);
34
- }
35
- return underlying;
36
- }
37
22
  /**
38
23
  * Fetches LST stats from Endur API with caching
39
24
  * @returns Promise<LSTStats[]> Array of LST statistics
@@ -153,24 +138,3 @@ export class LSTAPRService {
153
138
  logger.verbose(`LSTAPRService: Cache cleared`);
154
139
  }
155
140
  }
156
-
157
-
158
- // populate lstMapping
159
- const lstSymbolMapping: any = {
160
- xSTRK: "STRK",
161
- xWBTC: "WBTC",
162
- xtBTC: "tBTC",
163
- xsBTC: "solvBTC",
164
- xLBTC: "LBTC",
165
- }
166
- Object.keys(lstSymbolMapping).forEach(key => {
167
- const lst = Global.getDefaultTokens().find(t => t.symbol === key);
168
- if (!lst) {
169
- throw new Error(`LST token not found for ${key}`);
170
- }
171
- const underlying = Global.getDefaultTokens().find(t => t.symbol === lstSymbolMapping[key]);
172
- if (!underlying) {
173
- throw new Error(`Underlying token not found for ${key}`);
174
- }
175
- LSTAPRService.lstMapping[lst.address.address] = underlying;
176
- });
@@ -1,22 +1,37 @@
1
- import { Contract, RpcProvider } from "starknet";
1
+ import { Contract, RpcProvider, BlockIdentifier } from "starknet";
2
2
  import PragmaAbi from '@/data/pragma.abi.json';
3
3
  import { logger } from "@/utils/logger";
4
+ import { PricerBase } from "./pricerBase";
5
+ import { IConfig, TokenInfo } from "@/interfaces";
6
+ import { PriceInfo } from "./pricer";
4
7
 
5
- export class Pragma {
8
+ export class Pragma extends PricerBase {
6
9
  contractAddr = '0x023fb3afbff2c0e3399f896dcf7400acf1a161941cfb386e34a123f228c62832';
7
10
  readonly contract: Contract;
8
11
 
9
- constructor(provider: RpcProvider) {
10
- this.contract = new Contract({abi: PragmaAbi, address: this.contractAddr, providerOrAccount: provider});
12
+ constructor(config: IConfig, tokens: TokenInfo[]) {
13
+ super(config, tokens);
14
+ this.contract = new Contract({
15
+ abi: PragmaAbi,
16
+ address: this.contractAddr,
17
+ providerOrAccount: config.provider as RpcProvider
18
+ });
11
19
  }
12
20
 
13
- async getPrice(tokenAddr: string) {
21
+ async getPrice(tokenAddr: string, blockIdentifier: BlockIdentifier = 'latest'): Promise<PriceInfo> {
14
22
  if (!tokenAddr) {
15
23
  throw new Error(`Pragma:getPrice - no token`)
16
24
  }
17
- const result: any = await this.contract.call('get_price', [tokenAddr]);
25
+ const result: any = await this.contract.call(
26
+ 'get_price',
27
+ [tokenAddr],
28
+ { blockIdentifier }
29
+ );
18
30
  const price = Number(result.price) / 10**8;
19
- logger.verbose(`Pragma:${tokenAddr}: ${price}`);
20
- return price;
31
+ logger.verbose(`Pragma:${tokenAddr}: ${price} at block ${blockIdentifier}`);
32
+ return {
33
+ price,
34
+ timestamp: new Date()
35
+ };
21
36
  }
22
37
  }