@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
@@ -1,21 +1,59 @@
1
- import { PriceInfo } from "./pricer";
1
+ import { PriceInfo } from "./pricer";
2
2
  import axios from "axios";
3
3
  import { IConfig, TokenInfo } from "@/interfaces";
4
4
  import { PricerBase } from "./pricerBase";
5
5
  import { logger } from "@/utils/logger";
6
+ import { createApolloClient } from "./apollo-client-config";
7
+ import { gql } from "@apollo/client";
8
+ import { ContractAddr } from "@/dataTypes";
9
+ import { Pragma } from "./pragma";
10
+ import { EkuboPricer } from "./ekubo-pricer";
11
+ import { BlockIdentifier } from "starknet";
6
12
 
7
13
  export class PricerFromApi extends PricerBase {
14
+ private apolloClient: ReturnType<typeof createApolloClient>;
15
+ private pragma: Pragma;
16
+ private ekuboPricer: EkuboPricer;
17
+
18
+ // Tokens supported by Pragma and GraphQL for historical prices (standardized addresses)
19
+ // Note: xSTRK is not supported by Pragma RPC, so it will use Ekubo pricer instead
20
+ private readonly PRAGMA_SUPPORTED_TOKENS = [
21
+ ContractAddr.from('0x3fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac').address, // WBTC
22
+ ContractAddr.from('0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8').address, // USDC
23
+ ContractAddr.from('0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d').address, // STRK
24
+ // xSTRK removed - not supported by Pragma RPC, will use Ekubo pricer
25
+ // ContractAddr.from('0x28d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a').address, // xSTRK
26
+ ContractAddr.from('0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8').address, // USDT
27
+ ContractAddr.from('0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7').address, // ETH
28
+ ];
29
+
8
30
  constructor(config: IConfig, tokens: TokenInfo[]) {
9
31
  super(config, tokens);
32
+ this.apolloClient = createApolloClient(config);
33
+ this.pragma = new Pragma(config, tokens);
34
+ this.ekuboPricer = new EkuboPricer(config, tokens);
10
35
  }
11
36
 
12
- async getPrice(tokenSymbol: string): Promise<PriceInfo> {
37
+ async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier): Promise<PriceInfo> {
38
+ // If blockNumber is provided, fetch historical price from GraphQL
39
+ if (blockNumber !== undefined) {
40
+ try {
41
+ return await this.getHistoricalPrice(tokenSymbol, blockNumber);
42
+ } catch (e: any) {
43
+ logger.error(`Historical price fetch failed for ${tokenSymbol}: ${e.message}`);
44
+ throw new Error('Server Error in fetching historical price')
45
+ // Fall back to current price if historical price is not available
46
+ }
47
+ }
48
+
49
+ // Otherwise, fetch current price
13
50
  try {
14
51
  return await this.getPriceFromMyAPI(tokenSymbol);
15
52
  } catch (e: any) {
16
- logger.warn('getPriceFromMyAPI error', e);
53
+ logger.error(`API price fetch failed for ${tokenSymbol}: ${e.message}`);
17
54
  }
18
- logger.info('getPrice coinbase', tokenSymbol);
55
+
56
+ logger.info(`Using Coinbase price for ${tokenSymbol}`);
19
57
  let retry = 0;
20
58
  const MAX_RETRIES = 5;
21
59
  for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
@@ -41,22 +79,120 @@ export class PricerFromApi extends PricerBase {
41
79
  }
42
80
 
43
81
  async getPriceFromMyAPI(tokenSymbol: string) {
44
- logger.verbose(`getPrice from api: ${tokenSymbol}`);
45
82
  const endpoint = 'https://proxy.api.troves.fi'
46
83
  const url = `${endpoint}/api/price/${tokenSymbol}`;
47
84
  const priceInfoRes = await fetch(url);
48
85
  const priceInfo = await priceInfoRes.json();
49
- const now = new Date();
50
- const priceTime = new Date(priceInfo.timestamp);
51
- // if (now.getTime() - priceTime.getTime() > 900000) {
52
- // // 15 mins
53
- // logger.verbose(`Price is stale: ${tokenSymbol}, timestamp: ${priceInfo.timestamp}, price: ${priceInfo.price}`);
54
- // throw new Error('Price is stale');
55
- // }
56
86
  const price = Number(priceInfo.price);
57
87
  return {
58
88
  price,
59
89
  timestamp: new Date(priceInfo.timestamp)
60
90
  }
61
- }
62
- }
91
+ }
92
+
93
+ /**
94
+ * Fetches historical price for a token at a specific block number
95
+ * @param tokenSymbol - The token symbol to get price for
96
+ * @param blockNumber - The block number to query
97
+ * @returns PriceInfo with price at the closest block <= blockNumber
98
+ */
99
+ async getHistoricalPrice(tokenSymbol: string, blockNumber: BlockIdentifier): Promise<PriceInfo> {
100
+ const tokenInfo = this.tokens.find(t => t.symbol === tokenSymbol);
101
+ if (!tokenInfo) {
102
+ throw new Error(`Token ${tokenSymbol} not found in configured tokens`);
103
+ }
104
+
105
+ // If blockNumber is not a number, fall back to current price
106
+ if (typeof blockNumber !== 'number') {
107
+ logger.info(`Non-numeric blockIdentifier '${blockNumber}' provided, fetching current price for ${tokenSymbol}`);
108
+ return await this.getPriceFromMyAPI(tokenSymbol);
109
+ }
110
+
111
+ const standardizedAddress = ContractAddr.from(tokenInfo.address.address).address;
112
+ const isPragmaSupported = this.PRAGMA_SUPPORTED_TOKENS.includes(standardizedAddress);
113
+
114
+ // Step 1: Try GraphQL first for ALL tokens
115
+ let data;
116
+ try {
117
+ const result = await this.apolloClient.query({
118
+ query: gql`
119
+ query GetHistoricalPrice($asset: String!, $blockNumber: Int!) {
120
+ findFirstPrices(
121
+ where: {
122
+ asset: { equals: $asset },
123
+ block_number: { lte: $blockNumber }
124
+ }
125
+ orderBy: { block_number: desc }
126
+ ) {
127
+ price
128
+ timestamp
129
+ block_number
130
+ }
131
+ }
132
+ `,
133
+ variables: {
134
+ asset: standardizedAddress,
135
+ blockNumber: blockNumber,
136
+ },
137
+ });
138
+ data = result.data;
139
+ } catch (graphqlError: any) {
140
+ logger.error(`GraphQL query failed for ${tokenSymbol}: ${graphqlError.message}`);
141
+ }
142
+
143
+ // Step 2: If GraphQL has data, check block difference and use it if acceptable
144
+ if (data?.findFirstPrices) {
145
+ const priceData = data.findFirstPrices;
146
+ const blockDifference = blockNumber - priceData.block_number;
147
+ const MAX_BLOCK_DIFFERENCE = 6800; // ~6 hours worth of blocks
148
+
149
+ if (blockDifference <= MAX_BLOCK_DIFFERENCE) {
150
+ logger.info(`Using GraphQL price for ${tokenSymbol} at block ${priceData.block_number}`);
151
+ return {
152
+ price: Number(priceData.price),
153
+ timestamp: new Date(priceData.timestamp * 1000),
154
+ };
155
+ }
156
+
157
+ logger.info(
158
+ `Block difference ${blockDifference} exceeds limit for ${tokenSymbol}. ` +
159
+ `Will try fallback sources at block ${blockNumber}`
160
+ );
161
+ } else {
162
+ logger.info(`No GraphQL data for ${tokenSymbol} at block ${blockNumber}, trying fallback sources`);
163
+ }
164
+
165
+ // Step 3: Fallback to Pragma (for supported tokens) or Ekubo (for non-supported tokens)
166
+ if (isPragmaSupported) {
167
+ logger.info(`Attempting Pragma RPC fetch for ${tokenSymbol} at block ${blockNumber}`);
168
+ try {
169
+ const priceInfo = await this.pragma.getPrice(tokenInfo.address.address, blockNumber);
170
+ logger.info(`Using Pragma price for ${tokenSymbol}: ${priceInfo.price}`);
171
+ return {
172
+ price: priceInfo.price,
173
+ timestamp: priceInfo.timestamp,
174
+ };
175
+ } catch (error: any) {
176
+ logger.error(`Pragma RPC failed for ${tokenSymbol} at block ${blockNumber}: ${error.message}`);
177
+ throw new Error(`Server Error at Pragma token ${tokenSymbol} at block ${blockNumber}`);
178
+ }
179
+ } else {
180
+ // For non-Pragma tokens, use Ekubo pricer
181
+ logger.info(`Attempting Ekubo price fetch for ${tokenSymbol} at block ${blockNumber}`);
182
+ try {
183
+ const priceInfo = await this.ekuboPricer.getPrice(
184
+ tokenInfo.address.address,
185
+ blockNumber
186
+ );
187
+ logger.info(`Using Ekubo price for ${tokenSymbol}: ${priceInfo.price}`);
188
+ return {
189
+ price: priceInfo.price,
190
+ timestamp: priceInfo.timestamp,
191
+ };
192
+ } catch (error: any) {
193
+ logger.error(`Ekubo RPC failed for ${tokenSymbol} at block ${blockNumber}: ${error.message}`);
194
+ throw new Error(`Server Error at Ekubo token ${tokenSymbol} at block ${blockNumber}`);
195
+ }
196
+ }
197
+ }
198
+ }
@@ -5,6 +5,7 @@ import { IConfig } from "@/interfaces/common";
5
5
  import { Web3Number } from "@/dataTypes";
6
6
  import { PricerBase } from "./pricerBase";
7
7
  import { logger } from "@/utils/logger";
8
+ import { BlockIdentifier } from "starknet";
8
9
 
9
10
  export interface PriceInfo {
10
11
  price: number,
@@ -81,7 +82,7 @@ export class Pricer extends PricerBase {
81
82
  Global.assert(!this.isStale(timestamp, tokenName), `Price of ${tokenName} is stale`);
82
83
 
83
84
  }
84
- async getPrice(tokenSymbol: string) {
85
+ async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier) {
85
86
  Global.assert(this.prices[tokenSymbol], `Price of ${tokenSymbol} not found`);
86
87
  this.assertNotStale(this.prices[tokenSymbol].timestamp, tokenSymbol);
87
88
  return this.prices[tokenSymbol];
@@ -1,5 +1,6 @@
1
1
  import { IConfig, TokenInfo } from "@/interfaces";
2
2
  import { PriceInfo } from "./pricer";
3
+ import { BlockIdentifier } from "starknet";
3
4
 
4
5
  export abstract class PricerBase {
5
6
  readonly config: IConfig;
@@ -9,7 +10,7 @@ export abstract class PricerBase {
9
10
  this.tokens = tokens;
10
11
  }
11
12
 
12
- async getPrice(tokenSymbol: string): Promise<PriceInfo> {
13
+ async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier): Promise<PriceInfo> {
13
14
  throw new Error('Method not implemented');
14
15
  }
15
16
  }
@@ -4,6 +4,7 @@ import { readFileSync, existsSync, writeFileSync } from 'fs'
4
4
  import { IConfig } from '../interfaces';
5
5
  import { Store, getDefaultStoreConfig } from '../utils/store';
6
6
  import { add } from 'winston';
7
+ import { logger } from '@/utils';
7
8
 
8
9
  function getContracts() {
9
10
  const PATH = './contracts.json'
@@ -207,13 +208,47 @@ async function executeTransactions(
207
208
  return tx;
208
209
  }
209
210
 
211
+ async function myWaitForTransaction(
212
+ transaction_hash: string,
213
+ provider: RpcProvider,
214
+ retry = 0
215
+ ) {
216
+ const MAX_RETRIES = 60;
217
+ logger.verbose(`Waiting for transaction: ${transaction_hash}, retry: ${retry}`);
218
+ try {
219
+ const status = await provider.getTransactionStatus(transaction_hash);
220
+ logger.verbose(`Transaction status: ${JSON.stringify(status.execution_status)}`);
221
+ if (status.execution_status == TransactionExecutionStatus.SUCCEEDED) {
222
+ return true;
223
+ }
224
+ if (status.execution_status == TransactionExecutionStatus.REVERTED) {
225
+ throw new Error(`Transaction reverted: ${transaction_hash}`);
226
+ }
227
+ if (retry > MAX_RETRIES) {
228
+ throw new Error(`Transaction not found: ${transaction_hash}`);
229
+ }
230
+ await new Promise(resolve => setTimeout(resolve, 1000));
231
+ return myWaitForTransaction(transaction_hash, provider, retry + 1);
232
+ } catch (error) {
233
+ if (error instanceof Error && error.message.includes('Transaction reverted')) {
234
+ throw new Error(`Transaction reverted: ${transaction_hash}`);
235
+ }
236
+ if (retry > MAX_RETRIES) {
237
+ throw error;
238
+ }
239
+ await new Promise(resolve => setTimeout(resolve, 1000));
240
+ return myWaitForTransaction(transaction_hash, provider, retry + 1);
241
+ }
242
+ }
243
+
210
244
  const Deployer = {
211
245
  getAccount,
212
246
  myDeclare,
213
247
  deployContract,
214
248
  prepareMultiDeployContracts,
215
249
  executeDeployCalls,
216
- executeTransactions
250
+ executeTransactions,
251
+ myWaitForTransaction
217
252
  }
218
253
 
219
254
  export default Deployer;
@@ -4,6 +4,7 @@ import { PriceInfo, Pricer } from '@/modules/pricer';
4
4
  import { createClient } from 'redis';
5
5
  import type { RedisClientType } from 'redis'
6
6
  import { logger } from "@/utils/logger";
7
+ import { BlockIdentifier } from "starknet";
7
8
 
8
9
  export class PricerRedis extends Pricer {
9
10
  private redisClient: RedisClientType | null = null;
@@ -51,7 +52,7 @@ export class PricerRedis extends Pricer {
51
52
  }
52
53
 
53
54
  /** Returns price from redis */
54
- async getPrice(tokenSymbol: string) {
55
+ async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier) {
55
56
  const STALE_TIME = 60000;
56
57
  if (!this.redisClient) {
57
58
  throw new FatalError(`Redis client not initialised`);
@@ -48,7 +48,7 @@ export class AutoCompounderSTRK {
48
48
 
49
49
  /** Returns shares of user */
50
50
  async balanceOf(user: ContractAddr) {
51
- const result = await this.contract!.balance_of(user.address);
51
+ const result = await this.contract!.balanceOf(user.address);
52
52
  return Web3Number.fromWei(result.toString(), this.metadata.decimals);
53
53
  }
54
54
 
@@ -1,8 +1,8 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { IConfig, TokenInfo, VaultPosition } from "@/interfaces";
3
+ import { HarvestInfo } from "@/modules/harvests";
3
4
  import { CacheClass } from "@/utils/cacheClass";
4
- import { Call } from "starknet";
5
- import { PositionInfo } from "./universal-adapters";
5
+ import { Call, BlockIdentifier } from "starknet";
6
6
 
7
7
  export interface SingleActionAmount {
8
8
  tokenInfo: TokenInfo,
@@ -13,11 +13,6 @@ export interface SingleTokenInfo extends SingleActionAmount {
13
13
  usdValue: number
14
14
  };
15
15
 
16
- export interface APYInfo {
17
- net: number;
18
- splits: { apy: number; id: string; }[];
19
- };
20
-
21
16
  export interface DualActionAmount {
22
17
  token0: SingleActionAmount,
23
18
  token1: SingleActionAmount
@@ -42,7 +37,7 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
42
37
  this.config = config;
43
38
  }
44
39
 
45
- async getUserTVL(user: ContractAddr): Promise<TVLInfo> {
40
+ async getUserTVL(user: ContractAddr, blockIdentifier?: BlockIdentifier): Promise<TVLInfo> {
46
41
  throw new Error("Not implemented");
47
42
  }
48
43
 
@@ -62,19 +57,7 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
62
57
  throw new Error("Not implemented");
63
58
  }
64
59
 
65
- async getUnusedBalance(): Promise<SingleTokenInfo> {
66
- throw new Error("Not implemented");
67
- }
68
-
69
- async netAPY(): Promise<APYInfo> {
70
- throw new Error("Not implemented");
71
- }
72
-
73
- async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
74
- throw new Error("Not implemented");
75
- }
76
-
77
- async getHealthFactors(): Promise<number[]> {
78
- throw new Error("Not implemented");
60
+ async getPendingRewards(): Promise<HarvestInfo[]> {
61
+ return [];
79
62
  }
80
63
  }