@strkfarm/sdk 2.0.0-dev.4 → 2.0.0-dev.41

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 (78) hide show
  1. package/dist/cli.js +190 -36
  2. package/dist/cli.mjs +188 -34
  3. package/dist/index.browser.global.js +116250 -90801
  4. package/dist/index.browser.mjs +13050 -10957
  5. package/dist/index.d.ts +2232 -1933
  6. package/dist/index.js +13380 -11084
  7. package/dist/index.mjs +13280 -11007
  8. package/package.json +6 -7
  9. package/src/data/avnu.abi.json +840 -0
  10. package/src/data/ekubo-price-fethcer.abi.json +265 -0
  11. package/src/data/redeem-request-nft.abi.json +752 -0
  12. package/src/data/universal-vault.abi.json +8 -7
  13. package/src/dataTypes/_bignumber.ts +13 -4
  14. package/src/dataTypes/bignumber.browser.ts +10 -1
  15. package/src/dataTypes/bignumber.node.ts +10 -1
  16. package/src/dataTypes/index.ts +3 -2
  17. package/src/dataTypes/mynumber.ts +141 -0
  18. package/src/global.ts +93 -36
  19. package/src/index.browser.ts +2 -1
  20. package/src/interfaces/common.tsx +218 -5
  21. package/src/modules/apollo-client-config.ts +28 -0
  22. package/src/modules/avnu.ts +21 -12
  23. package/src/modules/ekubo-pricer.ts +79 -0
  24. package/src/modules/ekubo-quoter.ts +48 -30
  25. package/src/modules/erc20.ts +17 -0
  26. package/src/modules/harvests.ts +43 -29
  27. package/src/modules/index.ts +2 -1
  28. package/src/modules/pragma.ts +23 -8
  29. package/src/modules/pricer-avnu-api.ts +114 -0
  30. package/src/modules/pricer-from-api.ts +156 -15
  31. package/src/modules/pricer-lst.ts +1 -1
  32. package/src/modules/pricer.ts +94 -40
  33. package/src/modules/pricerBase.ts +2 -1
  34. package/src/node/deployer.ts +36 -1
  35. package/src/node/pricer-redis.ts +3 -1
  36. package/src/strategies/base-strategy.ts +168 -16
  37. package/src/strategies/constants.ts +8 -3
  38. package/src/strategies/ekubo-cl-vault.tsx +1047 -351
  39. package/src/strategies/factory.ts +199 -0
  40. package/src/strategies/index.ts +5 -3
  41. package/src/strategies/registry.ts +262 -0
  42. package/src/strategies/sensei.ts +353 -9
  43. package/src/strategies/svk-strategy.ts +283 -31
  44. package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1262 -0
  45. package/src/strategies/types.ts +4 -0
  46. package/src/strategies/universal-adapters/adapter-utils.ts +4 -1
  47. package/src/strategies/universal-adapters/avnu-adapter.ts +196 -272
  48. package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
  49. package/src/strategies/universal-adapters/common-adapter.ts +206 -203
  50. package/src/strategies/universal-adapters/index.ts +10 -8
  51. package/src/strategies/universal-adapters/svk-troves-adapter.ts +511 -0
  52. package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
  53. package/src/strategies/universal-adapters/vesu-adapter.ts +120 -82
  54. package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +525 -0
  55. package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +1098 -712
  56. package/src/strategies/universal-adapters/vesu-position-common.ts +258 -0
  57. package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
  58. package/src/strategies/universal-lst-muliplier-strategy.tsx +631 -414
  59. package/src/strategies/universal-strategy.tsx +1331 -1173
  60. package/src/strategies/vesu-rebalance.tsx +252 -152
  61. package/src/strategies/yoloVault.ts +1087 -0
  62. package/src/utils/cacheClass.ts +11 -2
  63. package/src/utils/health-factor-math.ts +33 -1
  64. package/src/utils/index.ts +3 -1
  65. package/src/utils/logger.browser.ts +22 -4
  66. package/src/utils/logger.node.ts +259 -24
  67. package/src/utils/starknet-call-parser.ts +1036 -0
  68. package/src/utils/strategy-utils.ts +61 -0
  69. package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
  70. package/src/modules/ExtendedWrapperSDk/types.ts +0 -311
  71. package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -395
  72. package/src/strategies/universal-adapters/extended-adapter.ts +0 -661
  73. package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
  74. package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -34
  75. package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -77
  76. package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -49
  77. package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -372
  78. package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1140
@@ -5,12 +5,25 @@ 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";
10
+ import { PricerAvnuApi } from "./pricer-avnu-api";
8
11
 
9
12
  export interface PriceInfo {
10
13
  price: number,
11
14
  timestamp: Date
12
15
  }
13
16
 
17
+ type PriceMethod = 'AvnuApi' | 'Coinbase' | 'Coinmarketcap' | 'Ekubo' | 'Avnu';
18
+
19
+ const PRICE_METHOD_PRIORITY: PriceMethod[] = [
20
+ 'AvnuApi',
21
+ 'Coinbase',
22
+ 'Coinmarketcap',
23
+ 'Ekubo',
24
+ 'Avnu',
25
+ ];
26
+
14
27
  export class Pricer extends PricerBase {
15
28
  protected prices: {
16
29
  [key: string]: PriceInfo
@@ -19,20 +32,24 @@ export class Pricer extends PricerBase {
19
32
  refreshInterval = 30000;
20
33
  staleTime = 60000;
21
34
 
35
+ protected readonly avnuApiPricer: PricerAvnuApi;
36
+
22
37
  // code populates this map during runtime to determine which method to use for a given token
23
38
  // The method set will be the first one to try after first attempt
24
- protected methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap'} = {};
39
+ protected methodToUse: {[tokenSymbol: string]: PriceMethod} = {};
25
40
 
26
41
  /**
27
42
  * TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
28
43
  */
44
+ // ! switch to USDC (new) later
29
45
  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
46
+ protected EKUBO_API = 'https://prod-api-quoter.ekubo.org/23448594291968334/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb'; // e.g. ETH/USDC
31
47
 
32
48
  constructor(config: IConfig, tokens: TokenInfo[], refreshInterval = 30000, staleTime = 60000) {
33
49
  super(config, tokens);
34
50
  this.refreshInterval = refreshInterval;
35
51
  this.staleTime = staleTime;
52
+ this.avnuApiPricer = new PricerAvnuApi(config, tokens);
36
53
  }
37
54
 
38
55
  isReady() {
@@ -66,6 +83,7 @@ export class Pricer extends PricerBase {
66
83
  }
67
84
 
68
85
  start() {
86
+ this.avnuApiPricer.start();
69
87
  this._loadPrices();
70
88
  setInterval(() => {
71
89
  this._loadPrices();
@@ -81,7 +99,7 @@ export class Pricer extends PricerBase {
81
99
  Global.assert(!this.isStale(timestamp, tokenName), `Price of ${tokenName} is stale`);
82
100
 
83
101
  }
84
- async getPrice(tokenSymbol: string) {
102
+ async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier) {
85
103
  Global.assert(this.prices[tokenSymbol], `Price of ${tokenSymbol} not found`);
86
104
  this.assertNotStale(this.prices[tokenSymbol].timestamp, tokenSymbol);
87
105
  return this.prices[tokenSymbol];
@@ -93,7 +111,10 @@ export class Pricer extends PricerBase {
93
111
  let retry = 0;
94
112
  while (retry < MAX_RETRIES) {
95
113
  try {
96
- if (token.symbol === 'USDT') {
114
+ if (token.dontPrice) {
115
+ return;
116
+ }
117
+ if (token.symbol === 'USDT' || token.symbol === 'USDC') {
97
118
  this.prices[token.symbol] = {
98
119
  price: 1,
99
120
  timestamp: new Date()
@@ -142,49 +163,58 @@ export class Pricer extends PricerBase {
142
163
  }
143
164
  }
144
165
 
145
- async _getPrice(token: TokenInfo, defaultMethod = 'all'): Promise<number> {
146
- const methodToUse: string = this.methodToUse[token.symbol] || defaultMethod; // default start with coinbase
147
- logger.verbose(`Fetching price of ${token.symbol} using ${methodToUse}`);
148
- switch (methodToUse) {
149
- case 'Coinbase':
150
- // try {
151
- // const result = await this._getPriceCoinbase(token);
152
- // this.methodToUse[token.symbol] = 'Coinbase';
153
- // return result;
154
- // } catch (error: any) {
155
- // console.warn(`Coinbase: price err: message [${token.symbol}]: `, error.message);
156
- // // do nothing, try next
157
- // }
158
- case 'Coinmarketcap':
159
- try {
160
- const result = await this._getPriceCoinMarketCap(token);
161
- this.methodToUse[token.symbol] = 'Coinmarketcap';
162
- return result;
163
- } catch (error: any) {
164
- console.warn(`CoinMarketCap: price err [${token.symbol}]: `, Object.keys(error));
165
- console.warn(`CoinMarketCap: price err [${token.symbol}]: `, error.message);
166
- }
167
- case 'Ekubo':
168
- try {
169
- const result = await this._getPriceEkubo(token, new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals));
170
- this.methodToUse[token.symbol] = 'Ekubo';
171
- return result;
172
- } catch (error: any) {
173
- console.warn(`Ekubo: price err [${token.symbol}]: `, error.message);
174
- console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
175
- // do nothing, try next
176
- }
166
+ async _getPrice(token: TokenInfo): Promise<number> {
167
+ const pinned = this.methodToUse[token.symbol];
168
+ if (pinned) {
169
+ logger.verbose(`Fetching price of ${token.symbol} using pinned ${pinned}`);
170
+ try {
171
+ return await this._tryPriceMethod(token, pinned);
172
+ } catch (error: any) {
173
+ console.warn(`${pinned}: pinned price failed [${token.symbol}]: `, error.message);
174
+ delete this.methodToUse[token.symbol];
175
+ }
177
176
  }
178
177
 
179
- // if methodToUse is the default one, pass Coinbase to try all from start
180
- if (defaultMethod == 'all') {
181
- // try again with coinbase
182
- return await this._getPrice(token, 'Coinbase');
178
+ for (const method of PRICE_METHOD_PRIORITY) {
179
+ logger.verbose(`Fetching price of ${token.symbol} using ${method}`);
180
+ try {
181
+ const result = await this._tryPriceMethod(token, method);
182
+ this.methodToUse[token.symbol] = method;
183
+ return result;
184
+ } catch (error: any) {
185
+ console.warn(`${method}: price err [${token.symbol}]: `, error.message);
186
+ }
183
187
  }
184
188
 
185
189
  throw new FatalError(`Price not found for ${token.symbol}`);
186
190
  }
187
191
 
192
+ protected async _tryPriceMethod(token: TokenInfo, method: PriceMethod): Promise<number> {
193
+ switch (method) {
194
+ case 'AvnuApi':
195
+ return await this._getPriceAvnuApi(token);
196
+ case 'Coinbase':
197
+ return await this._getPriceCoinbase(token);
198
+ case 'Coinmarketcap':
199
+ return await this._getPriceCoinMarketCap(token);
200
+ case 'Ekubo':
201
+ return await this._getPriceEkubo(
202
+ token,
203
+ new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals),
204
+ );
205
+ case 'Avnu':
206
+ return await this._getAvnuPrice(
207
+ token,
208
+ new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals),
209
+ );
210
+ }
211
+ }
212
+
213
+ async _getPriceAvnuApi(token: TokenInfo): Promise<number> {
214
+ const priceInfo = await this.avnuApiPricer.getPrice(token.symbol);
215
+ return priceInfo.price;
216
+ }
217
+
188
218
  async _getPriceCoinbase(token: TokenInfo) {
189
219
  const url = this.PRICE_API.replace("{{PRICER_KEY}}", `${token.symbol}-USD`);
190
220
  const result = await axios.get(url)
@@ -200,7 +230,31 @@ export class Pricer extends PricerBase {
200
230
  throw new Error("Not implemented");
201
231
  }
202
232
 
233
+ async _getAvnuPrice(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
234
+ logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
235
+
236
+ const avnuWrapper = new AvnuWrapper();
237
+ const usdcAddress = '0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb';
238
+ const quote = await avnuWrapper.getQuotes(token.address.toString(), usdcAddress, amountIn.toWei(), '0x1');
239
+ const multiplier = 1 / amountIn.toNumber();
240
+ const outputUSDC = Number(Web3Number.fromWei(quote.buyAmount.toString(), 6).toFixed(6)) * multiplier;
241
+ logger.verbose(`Avnu: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
242
+ if (outputUSDC === 0 && retry < 3) {
243
+ // try again with a higher amount
244
+ const amountIn = new Web3Number(100, token.decimals); // 100 unit of token
245
+ return await this._getAvnuPrice(token, amountIn, retry + 1);
246
+ }
247
+
248
+ // if usdc depegs, it will not longer be 1 USD
249
+ // so we need to get the price of USDC in USD
250
+ // and then convert the outputUSDC to USD
251
+ const usdcPrice = 1; // (await this.getPrice('USDC')).price;
252
+ logger.verbose(`USDC Price: ${usdcPrice}`);
253
+ return outputUSDC * usdcPrice;
254
+ }
255
+
203
256
  async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
257
+ logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
204
258
  const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
205
259
  const result = await axios.get(url);
206
260
  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;
@@ -17,6 +18,7 @@ export class PricerRedis extends Pricer {
17
18
  await this.initRedis(redisUrl);
18
19
 
19
20
  logger.info(`Starting Pricer with Redis`);
21
+ this.avnuApiPricer.start();
20
22
  this._loadPrices(this._setRedisPrices.bind(this));
21
23
  setInterval(() => {
22
24
  this._loadPrices(this._setRedisPrices.bind(this));
@@ -51,7 +53,7 @@ export class PricerRedis extends Pricer {
51
53
  }
52
54
 
53
55
  /** Returns price from redis */
54
- async getPrice(tokenSymbol: string) {
56
+ async getPrice(tokenSymbol: string, blockNumber?: BlockIdentifier) {
55
57
  const STALE_TIME = 60000;
56
58
  if (!this.redisClient) {
57
59
  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,33 +29,87 @@ export interface DualTokenInfo {
28
29
  token1: SingleTokenInfo
29
30
  }
30
31
 
32
+ export type StrategyInputMode = "single" | "dual";
33
+ export type InputModeFromAction<T> = T extends DualActionAmount ? "dual" : "single";
34
+
35
+ export interface NetAPYSplit {
36
+ apy: number;
37
+ id: string;
38
+ }
39
+
40
+ export interface NetAPYDetails {
41
+ net: number;
42
+ splits: NetAPYSplit[];
43
+ }
44
+
45
+ export type UserPositionCardSubValueColor = "default" | "positive" | "negative" | "info";
46
+
47
+ export interface UserPositionCard {
48
+ title: string;
49
+ value: string;
50
+ tooltip?: string;
51
+ subValue?: string;
52
+ subValueColor?: UserPositionCardSubValueColor;
53
+ }
54
+
55
+ export interface UserPositionCardsInput {
56
+ user: ContractAddr;
57
+ investmentFlows?: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>;
58
+ usualTimeToEarnings?: string | null;
59
+ usualTimeToEarningsDescription?: string | null;
60
+ }
61
+
31
62
  interface CacheData {
32
63
  timestamp: number;
33
64
  ttl: number;
34
65
  data: any;
35
66
  }
36
- export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
37
- readonly config: IConfig;
38
- readonly cache: Map<string, CacheData> = new Map();
67
+ export class BaseStrategy<
68
+ TVLInfo,
69
+ DepositActionInfo,
70
+ WithdrawActionInfo = DepositActionInfo,
71
+ > extends CacheClass {
72
+ readonly config: IConfig;
73
+ readonly cache: Map<string, CacheData> = new Map();
74
+ private readonly _depositInputMode: InputModeFromAction<DepositActionInfo>;
75
+ private readonly _withdrawInputMode: InputModeFromAction<WithdrawActionInfo>;
39
76
 
40
- constructor(config: IConfig) {
77
+ constructor(
78
+ config: IConfig,
79
+ inputModes?: {
80
+ depositInputMode?: InputModeFromAction<DepositActionInfo>;
81
+ withdrawInputMode?: InputModeFromAction<WithdrawActionInfo>;
82
+ }
83
+ ) {
41
84
  super();
42
85
  this.config = config;
86
+ this._depositInputMode = (inputModes?.depositInputMode ??
87
+ ("single" as InputModeFromAction<DepositActionInfo>));
88
+ this._withdrawInputMode = (inputModes?.withdrawInputMode ??
89
+ ("single" as InputModeFromAction<WithdrawActionInfo>));
90
+ }
91
+
92
+ depositInputMode(): InputModeFromAction<DepositActionInfo> {
93
+ return this._depositInputMode;
94
+ }
95
+
96
+ withdrawInputMode(): InputModeFromAction<WithdrawActionInfo> {
97
+ return this._withdrawInputMode;
43
98
  }
44
99
 
45
- async getUserTVL(user: ContractAddr): Promise<TVLInfo> {
100
+ async getUserTVL(user: ContractAddr, blockIdentifier?: BlockIdentifier): Promise<TVLInfo> {
46
101
  throw new Error("Not implemented");
47
102
  }
48
103
 
49
104
  async getTVL(): Promise<TVLInfo> {
50
105
  throw new Error("Not implemented");
51
- }
106
+ }
52
107
 
53
- async depositCall(amountInfo: ActionInfo, receiver: ContractAddr): Promise<Call[]> {
108
+ async depositCall(amountInfo: DepositActionInfo, receiver: ContractAddr): Promise<Call[]> {
54
109
  throw new Error("Not implemented");
55
110
  }
56
111
 
57
- async withdrawCall(amountInfo: ActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
112
+ async withdrawCall(amountInfo: WithdrawActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
58
113
  throw new Error("Not implemented");
59
114
  }
60
115
 
@@ -62,19 +117,116 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
62
117
  throw new Error("Not implemented");
63
118
  }
64
119
 
65
- async getUnusedBalance(): Promise<SingleTokenInfo> {
120
+ async netAPY(
121
+ blockIdentifier?: BlockIdentifier,
122
+ sinceBlocks?: number,
123
+ timeperiod?: "24h" | "7d" | "30d" | "3m"
124
+ ): Promise<number | string | NetAPYDetails> {
66
125
  throw new Error("Not implemented");
67
126
  }
68
127
 
69
- async netAPY(): Promise<APYInfo> {
70
- throw new Error("Not implemented");
128
+ async getPendingRewards(): Promise<HarvestInfo[]> {
129
+ return [];
71
130
  }
72
131
 
73
- async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
132
+ async getUserRealizedAPY(
133
+ blockIdentifier?: BlockIdentifier,
134
+ sinceBlocks?: number
135
+ ): Promise<number> {
74
136
  throw new Error("Not implemented");
75
137
  }
76
138
 
77
- async getHealthFactors(): Promise<number[]> {
78
- throw new Error("Not implemented");
139
+ async getUserPositionCards(_input: UserPositionCardsInput): Promise<UserPositionCard[]> {
140
+ throw new Error("Not implemented");
79
141
  }
80
- }
142
+
143
+ async getMaxTVL() : Promise<Web3Number> {
144
+ // Can throw an error as well if needed, RN returning 0
145
+ return new Web3Number('0', 18)
146
+ }
147
+
148
+ protected formatTokenAmountForCard(amount: Web3Number, tokenInfo: TokenInfo): string {
149
+ const displayDecimals = tokenInfo.displayDecimals ?? 2;
150
+ const fixed = Number(amount.toFixed(displayDecimals));
151
+ const normalized = Number.isFinite(fixed) ? fixed : 0;
152
+ return `${normalized.toLocaleString("en-US", {
153
+ maximumFractionDigits: displayDecimals,
154
+ minimumFractionDigits: 0,
155
+ })} ${tokenInfo.symbol}`;
156
+ }
157
+
158
+ protected formatPercentForCard(value: number): string {
159
+ if (!Number.isFinite(value)) return "N/A";
160
+ return `${(value * 100).toFixed(2)}%`;
161
+ }
162
+
163
+ protected formatUSDForCard(value: number): string {
164
+ if (!Number.isFinite(value)) return "$0.00";
165
+ return new Intl.NumberFormat("en-US", {
166
+ style: "currency",
167
+ currency: "USD",
168
+ maximumFractionDigits: 2,
169
+ }).format(value);
170
+ }
171
+
172
+ protected getSubValueColorFromSignedNumber(value: number): UserPositionCardSubValueColor {
173
+ if (!Number.isFinite(value)) return "default";
174
+ if (value > 0) return "positive";
175
+ if (value < 0) return "negative";
176
+ return "default";
177
+ }
178
+
179
+ /**
180
+ * Calculate lifetime earnings for a user based on provided data from client
181
+ * Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
182
+ *
183
+ * @param userTVL - The user's current TVL (SingleTokenInfo with amount, usdValue, tokenInfo)
184
+ * @param investmentFlows - Array of investment flow transactions from client
185
+ * @returns Object containing lifetime earnings, current value, and total deposits/withdrawals
186
+ */
187
+ getLifetimeEarnings(
188
+ userTVL: SingleTokenInfo,
189
+ investmentFlows: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>
190
+ ): {
191
+ tokenInfo: SingleTokenInfo;
192
+ lifetimeEarnings: Web3Number;
193
+ currentValue: Web3Number;
194
+ totalDeposits: Web3Number;
195
+ totalWithdrawals: Web3Number;
196
+ } {
197
+ // Get token decimals from userTVL
198
+ const tokenDecimals = userTVL.tokenInfo.decimals;
199
+
200
+ // Initialize totals
201
+ let totalDeposits = Web3Number.fromWei("0", tokenDecimals);
202
+ let totalWithdrawals = Web3Number.fromWei("0", tokenDecimals);
203
+
204
+ // Process investment flows
205
+ for (const flow of investmentFlows) {
206
+ const amount = Web3Number.fromWei(flow.amount, tokenDecimals);
207
+
208
+ if (flow.type === 'deposit') {
209
+ totalDeposits = totalDeposits.plus(amount);
210
+ } else if (flow.type === 'withdraw' || flow.type === 'redeem') {
211
+ totalWithdrawals = totalWithdrawals.plus(amount);
212
+ }
213
+ }
214
+
215
+ // Calculate lifetime earnings: current value + withdrawals - deposits
216
+ const lifetimeEarnings = userTVL.amount
217
+ .plus(totalWithdrawals)
218
+ .minus(totalDeposits);
219
+
220
+ return {
221
+ tokenInfo: {
222
+ tokenInfo: userTVL.tokenInfo,
223
+ amount: lifetimeEarnings,
224
+ usdValue: 0, // Lifetime earnings are not converted to USD
225
+ },
226
+ lifetimeEarnings,
227
+ currentValue: userTVL.amount,
228
+ totalDeposits,
229
+ totalWithdrawals,
230
+ };
231
+ }
232
+ }
@@ -1,12 +1,17 @@
1
1
  import { ContractAddr } from "@/dataTypes";
2
2
 
3
- export const COMMON_CONTRACTS = [{
3
+ export const MY_ACCESS_CONTROL = {
4
4
  address: ContractAddr.from("0x0636a3f51cc37f5729e4da4b1de6a8549a28f3c0d5bf3b17f150971e451ff9c2"),
5
5
  name: "Access Controller",
6
6
  sourceCodeUrl: "https://github.com/strkfarm/strkfarm-contracts/blob/main/src/components/accessControl.cairo",
7
- }];
7
+ };
8
+
9
+ export const COMMON_CONTRACTS = [MY_ACCESS_CONTROL];
8
10
 
9
11
  export const ENDPOINTS = {
10
12
  VESU_BASE: "https://proxy.api.troves.fi/vesu",
11
13
  VESU_BASE_STAGING: "https://proxy.api.troves.fi/vesu-staging"
12
- }
14
+ }
15
+
16
+
17
+ export const MAX_AVNU_RETRY_DELAY = Number(process.env.MAX_AVNU_RETRY_DELAY ?? 100);