@strkfarm/sdk 2.0.0-dev.26 → 2.0.0-dev.28

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 (70) hide show
  1. package/dist/cli.js +190 -36
  2. package/dist/cli.mjs +188 -34
  3. package/dist/index.browser.global.js +79130 -49354
  4. package/dist/index.browser.mjs +18039 -11431
  5. package/dist/index.d.ts +2869 -898
  6. package/dist/index.js +19036 -12207
  7. package/dist/index.mjs +18942 -12158
  8. package/package.json +1 -1
  9. package/src/data/avnu.abi.json +840 -0
  10. package/src/data/ekubo-price-fethcer.abi.json +265 -0
  11. package/src/dataTypes/_bignumber.ts +13 -4
  12. package/src/dataTypes/index.ts +3 -2
  13. package/src/dataTypes/mynumber.ts +141 -0
  14. package/src/global.ts +76 -41
  15. package/src/index.browser.ts +2 -1
  16. package/src/interfaces/common.tsx +167 -2
  17. package/src/modules/ExtendedWrapperSDk/types.ts +26 -4
  18. package/src/modules/ExtendedWrapperSDk/wrapper.ts +110 -67
  19. package/src/modules/apollo-client-config.ts +28 -0
  20. package/src/modules/avnu.ts +4 -4
  21. package/src/modules/ekubo-pricer.ts +79 -0
  22. package/src/modules/ekubo-quoter.ts +46 -30
  23. package/src/modules/erc20.ts +17 -0
  24. package/src/modules/harvests.ts +43 -29
  25. package/src/modules/pragma.ts +23 -8
  26. package/src/modules/pricer-from-api.ts +156 -15
  27. package/src/modules/pricer-lst.ts +1 -1
  28. package/src/modules/pricer.ts +40 -4
  29. package/src/modules/pricerBase.ts +2 -1
  30. package/src/node/deployer.ts +36 -1
  31. package/src/node/pricer-redis.ts +2 -1
  32. package/src/strategies/base-strategy.ts +78 -10
  33. package/src/strategies/ekubo-cl-vault.tsx +906 -347
  34. package/src/strategies/factory.ts +159 -0
  35. package/src/strategies/index.ts +6 -1
  36. package/src/strategies/registry.ts +239 -0
  37. package/src/strategies/sensei.ts +335 -7
  38. package/src/strategies/svk-strategy.ts +97 -27
  39. package/src/strategies/types.ts +4 -0
  40. package/src/strategies/universal-adapters/adapter-utils.ts +2 -1
  41. package/src/strategies/universal-adapters/avnu-adapter.ts +177 -268
  42. package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
  43. package/src/strategies/universal-adapters/common-adapter.ts +206 -203
  44. package/src/strategies/universal-adapters/extended-adapter.ts +155 -336
  45. package/src/strategies/universal-adapters/index.ts +9 -8
  46. package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
  47. package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +200 -0
  48. package/src/strategies/universal-adapters/vesu-adapter.ts +110 -75
  49. package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +476 -0
  50. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +762 -844
  51. package/src/strategies/universal-adapters/vesu-position-common.ts +251 -0
  52. package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
  53. package/src/strategies/universal-lst-muliplier-strategy.tsx +396 -204
  54. package/src/strategies/universal-strategy.tsx +1426 -1178
  55. package/src/strategies/vesu-extended-strategy/services/executionService.ts +2251 -0
  56. package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +2941 -0
  57. package/src/strategies/vesu-extended-strategy/services/operationService.ts +12 -1
  58. package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +52 -0
  59. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +1 -0
  60. package/src/strategies/vesu-extended-strategy/utils/constants.ts +3 -1
  61. package/src/strategies/vesu-extended-strategy/utils/helper.ts +158 -124
  62. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +377 -1781
  63. package/src/strategies/vesu-rebalance.tsx +255 -152
  64. package/src/utils/health-factor-math.ts +4 -1
  65. package/src/utils/index.ts +2 -1
  66. package/src/utils/logger.browser.ts +22 -4
  67. package/src/utils/logger.node.ts +259 -24
  68. package/src/utils/starknet-call-parser.ts +1036 -0
  69. package/src/utils/strategy-utils.ts +61 -0
  70. package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
@@ -5,6 +5,8 @@ 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 { AvnuWrapper } from "./avnu";
9
+ import { BlockIdentifier } from "starknet";
8
10
 
9
11
  export interface PriceInfo {
10
12
  price: number,
@@ -21,13 +23,14 @@ export class Pricer extends PricerBase {
21
23
 
22
24
  // code populates this map during runtime to determine which method to use for a given token
23
25
  // The method set will be the first one to try after first attempt
24
- protected methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap'} = {};
26
+ protected methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap' | 'Avnu'} = {};
25
27
 
26
28
  /**
27
29
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
28
30
  */
31
+ // ! switch to USDC (new) later
29
32
  protected PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
30
- protected EKUBO_API = 'https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8'; // e.g. ETH/USDC
33
+ protected EKUBO_API = 'https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb'; // e.g. ETH/USDC
31
34
 
32
35
  constructor(config: IConfig, tokens: TokenInfo[], refreshInterval = 30000, staleTime = 60000) {
33
36
  super(config, tokens);
@@ -81,7 +84,7 @@ export class Pricer extends PricerBase {
81
84
  Global.assert(!this.isStale(timestamp, tokenName), `Price of ${tokenName} is stale`);
82
85
 
83
86
  }
84
- async getPrice(tokenSymbol: string) {
87
+ async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier) {
85
88
  Global.assert(this.prices[tokenSymbol], `Price of ${tokenSymbol} not found`);
86
89
  this.assertNotStale(this.prices[tokenSymbol].timestamp, tokenSymbol);
87
90
  return this.prices[tokenSymbol];
@@ -93,7 +96,7 @@ export class Pricer extends PricerBase {
93
96
  let retry = 0;
94
97
  while (retry < MAX_RETRIES) {
95
98
  try {
96
- if (token.symbol === 'USDT') {
99
+ if (token.symbol === 'USDT' || token.symbol === 'USDC') {
97
100
  this.prices[token.symbol] = {
98
101
  price: 1,
99
102
  timestamp: new Date()
@@ -174,6 +177,15 @@ export class Pricer extends PricerBase {
174
177
  console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
175
178
  // do nothing, try next
176
179
  }
180
+ case 'Avnu':
181
+ try {
182
+ const result = await this._getAvnuPrice(token, new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals));
183
+ this.methodToUse[token.symbol] = 'Avnu';
184
+ return result;
185
+ } catch (error: any) {
186
+ console.warn(`Avnu: price err [${token.symbol}]: `, error.message);
187
+ console.warn(`Avnu: price err [${token.symbol}]: `, Object.keys(error));
188
+ }
177
189
  }
178
190
 
179
191
  // if methodToUse is the default one, pass Coinbase to try all from start
@@ -200,7 +212,31 @@ export class Pricer extends PricerBase {
200
212
  throw new Error("Not implemented");
201
213
  }
202
214
 
215
+ async _getAvnuPrice(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
216
+ logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
217
+
218
+ const avnuWrapper = new AvnuWrapper();
219
+ const usdcAddress = '0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb';
220
+ const quote = await avnuWrapper.getQuotes(token.address.toString(), usdcAddress, amountIn.toWei(), '0x1');
221
+ const multiplier = 1 / amountIn.toNumber();
222
+ const outputUSDC = Number(Web3Number.fromWei(quote.buyAmount.toString(), 6).toFixed(6)) * multiplier;
223
+ logger.verbose(`Avnu: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
224
+ if (outputUSDC === 0 && retry < 3) {
225
+ // try again with a higher amount
226
+ const amountIn = new Web3Number(100, token.decimals); // 100 unit of token
227
+ return await this._getAvnuPrice(token, amountIn, retry + 1);
228
+ }
229
+
230
+ // if usdc depegs, it will not longer be 1 USD
231
+ // so we need to get the price of USDC in USD
232
+ // and then convert the outputUSDC to USD
233
+ const usdcPrice = 1; // (await this.getPrice('USDC')).price;
234
+ logger.verbose(`USDC Price: ${usdcPrice}`);
235
+ return outputUSDC * usdcPrice;
236
+ }
237
+
203
238
  async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
239
+ logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
204
240
  const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
205
241
  const result = await axios.get(url);
206
242
  const data: any = result.data;
@@ -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`);
@@ -1,8 +1,9 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { IConfig, TokenInfo, VaultPosition } from "@/interfaces";
3
3
  import { CacheClass } from "@/utils/cacheClass";
4
- import { Call } from "starknet";
5
4
  import { PositionInfo } from "./universal-adapters";
5
+ import { Call, BlockIdentifier } from "starknet";
6
+ import { HarvestInfo } from "@/modules/harvests";
6
7
 
7
8
  export interface SingleActionAmount {
8
9
  tokenInfo: TokenInfo,
@@ -28,6 +29,16 @@ export interface DualTokenInfo {
28
29
  token1: SingleTokenInfo
29
30
  }
30
31
 
32
+ export interface NetAPYSplit {
33
+ apy: number;
34
+ id: string;
35
+ }
36
+
37
+ export interface NetAPYDetails {
38
+ net: number;
39
+ splits: NetAPYSplit[];
40
+ }
41
+
31
42
  interface CacheData {
32
43
  timestamp: number;
33
44
  ttl: number;
@@ -42,13 +53,13 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
42
53
  this.config = config;
43
54
  }
44
55
 
45
- async getUserTVL(user: ContractAddr): Promise<TVLInfo> {
56
+ async getUserTVL(user: ContractAddr, blockIdentifier?: BlockIdentifier): Promise<TVLInfo> {
46
57
  throw new Error("Not implemented");
47
58
  }
48
59
 
49
60
  async getTVL(): Promise<TVLInfo> {
50
61
  throw new Error("Not implemented");
51
- }
62
+ }
52
63
 
53
64
  async depositCall(amountInfo: ActionInfo, receiver: ContractAddr): Promise<Call[]> {
54
65
  throw new Error("Not implemented");
@@ -62,19 +73,76 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
62
73
  throw new Error("Not implemented");
63
74
  }
64
75
 
65
- async getUnusedBalance(): Promise<SingleTokenInfo> {
76
+ async netAPY(
77
+ blockIdentifier?: BlockIdentifier,
78
+ sinceBlocks?: number,
79
+ timeperiod?: "24h" | "7d" | "30d" | "3m"
80
+ ): Promise<number | NetAPYDetails> {
66
81
  throw new Error("Not implemented");
67
82
  }
68
83
 
69
- async netAPY(): Promise<APYInfo> {
70
- throw new Error("Not implemented");
84
+ async getPendingRewards(): Promise<HarvestInfo[]> {
85
+ return [];
71
86
  }
72
87
 
73
- async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
88
+ async getUserRealizedAPY(
89
+ blockIdentifier?: BlockIdentifier,
90
+ sinceBlocks?: number
91
+ ): Promise<number> {
74
92
  throw new Error("Not implemented");
75
93
  }
76
94
 
77
- async getHealthFactors(): Promise<number[]> {
78
- throw new Error("Not implemented");
95
+ /**
96
+ * Calculate lifetime earnings for a user based on provided data from client
97
+ * Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
98
+ *
99
+ * @param userTVL - The user's current TVL (SingleTokenInfo with amount, usdValue, tokenInfo)
100
+ * @param investmentFlows - Array of investment flow transactions from client
101
+ * @returns Object containing lifetime earnings, current value, and total deposits/withdrawals
102
+ */
103
+ getLifetimeEarnings(
104
+ userTVL: SingleTokenInfo,
105
+ investmentFlows: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>
106
+ ): {
107
+ tokenInfo: SingleTokenInfo;
108
+ lifetimeEarnings: Web3Number;
109
+ currentValue: Web3Number;
110
+ totalDeposits: Web3Number;
111
+ totalWithdrawals: Web3Number;
112
+ } {
113
+ // Get token decimals from userTVL
114
+ const tokenDecimals = userTVL.tokenInfo.decimals;
115
+
116
+ // Initialize totals
117
+ let totalDeposits = Web3Number.fromWei("0", tokenDecimals);
118
+ let totalWithdrawals = Web3Number.fromWei("0", tokenDecimals);
119
+
120
+ // Process investment flows
121
+ for (const flow of investmentFlows) {
122
+ const amount = Web3Number.fromWei(flow.amount, tokenDecimals);
123
+
124
+ if (flow.type === 'deposit') {
125
+ totalDeposits = totalDeposits.plus(amount);
126
+ } else if (flow.type === 'withdraw' || flow.type === 'redeem') {
127
+ totalWithdrawals = totalWithdrawals.plus(amount);
128
+ }
129
+ }
130
+
131
+ // Calculate lifetime earnings: current value + withdrawals - deposits
132
+ const lifetimeEarnings = userTVL.amount
133
+ .plus(totalWithdrawals)
134
+ .minus(totalDeposits);
135
+
136
+ return {
137
+ tokenInfo: {
138
+ tokenInfo: userTVL.tokenInfo,
139
+ amount: lifetimeEarnings,
140
+ usdValue: 0, // Lifetime earnings are not converted to USD
141
+ },
142
+ lifetimeEarnings,
143
+ currentValue: userTVL.amount,
144
+ totalDeposits,
145
+ totalWithdrawals,
146
+ };
79
147
  }
80
- }
148
+ }