@strkfarm/sdk 2.0.0-staging.6 → 2.0.0-staging.61

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.
@@ -2,7 +2,7 @@ import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { IConfig } from "@/interfaces";
3
3
  import { Contract } from "starknet";
4
4
  import ERC20Abi from '@/data/erc20.abi.json';
5
-
5
+ import { uint256 } from "starknet";
6
6
  export class ERC20 {
7
7
  readonly config: IConfig;
8
8
 
@@ -12,7 +12,7 @@ export class ERC20 {
12
12
 
13
13
  contract(addr: string | ContractAddr) {
14
14
  const _addr = typeof addr === 'string' ? addr : addr.address;
15
- return new Contract({abi: ERC20Abi, address: _addr, providerOrAccount: this.config.provider});
15
+ return new Contract({ abi: ERC20Abi, address: _addr, providerOrAccount: this.config.provider });
16
16
  }
17
17
 
18
18
  async balanceOf(token: string | ContractAddr, address: string | ContractAddr, tokenDecimals: number) {
@@ -26,4 +26,20 @@ export class ERC20 {
26
26
  const allowance = await contract.call('allowance', [owner.toString(), spender.toString()]);
27
27
  return Web3Number.fromWei(allowance.toString(), tokenDecimals);
28
28
  }
29
+
30
+ approve(
31
+ token: string | ContractAddr,
32
+ spender: string | ContractAddr,
33
+ amount: Web3Number
34
+ ) {
35
+ const contract = this.contract(token);
36
+ const amountUint256 = uint256.bnToUint256(amount.toWei());
37
+ const approveCall = contract.populate("approve", [
38
+ spender.toString(),
39
+ amountUint256.low.toString(),
40
+ amountUint256.high.toString(),
41
+ ]);
42
+ return approveCall;
43
+ }
44
+
29
45
  }
@@ -6,4 +6,5 @@ export * from './erc20';
6
6
  export * from './avnu';
7
7
  export * from './ekubo-quoter';
8
8
  export * from './pricer-lst';
9
- export * from './lst-apr';
9
+ export * from './lst-apr';
10
+ export * from './ekubo-pricer'
@@ -23,6 +23,9 @@ export interface DualTokenInfo {
23
23
  token1: SingleTokenInfo
24
24
  }
25
25
 
26
+ export type StrategyInputMode = "single" | "dual";
27
+ export type InputModeFromAction<T> = T extends DualActionAmount ? "dual" : "single";
28
+
26
29
  export interface NetAPYSplit {
27
30
  apy: number;
28
31
  id: string;
@@ -33,18 +36,59 @@ export interface NetAPYDetails {
33
36
  splits: NetAPYSplit[];
34
37
  }
35
38
 
39
+ export type UserPositionCardSubValueColor = "default" | "positive" | "negative" | "info";
40
+
41
+ export interface UserPositionCard {
42
+ title: string;
43
+ value: string;
44
+ tooltip?: string;
45
+ subValue?: string;
46
+ subValueColor?: UserPositionCardSubValueColor;
47
+ }
48
+
49
+ export interface UserPositionCardsInput {
50
+ user: ContractAddr;
51
+ investmentFlows?: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>;
52
+ usualTimeToEarnings?: string | null;
53
+ usualTimeToEarningsDescription?: string | null;
54
+ }
55
+
36
56
  interface CacheData {
37
57
  timestamp: number;
38
58
  ttl: number;
39
59
  data: any;
40
60
  }
41
- export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
42
- readonly config: IConfig;
43
- readonly cache: Map<string, CacheData> = new Map();
61
+ export class BaseStrategy<
62
+ TVLInfo,
63
+ DepositActionInfo,
64
+ WithdrawActionInfo = DepositActionInfo,
65
+ > extends CacheClass {
66
+ readonly config: IConfig;
67
+ readonly cache: Map<string, CacheData> = new Map();
68
+ private readonly _depositInputMode: InputModeFromAction<DepositActionInfo>;
69
+ private readonly _withdrawInputMode: InputModeFromAction<WithdrawActionInfo>;
44
70
 
45
- constructor(config: IConfig) {
71
+ constructor(
72
+ config: IConfig,
73
+ inputModes?: {
74
+ depositInputMode?: InputModeFromAction<DepositActionInfo>;
75
+ withdrawInputMode?: InputModeFromAction<WithdrawActionInfo>;
76
+ }
77
+ ) {
46
78
  super();
47
79
  this.config = config;
80
+ this._depositInputMode = (inputModes?.depositInputMode ??
81
+ ("single" as InputModeFromAction<DepositActionInfo>));
82
+ this._withdrawInputMode = (inputModes?.withdrawInputMode ??
83
+ ("single" as InputModeFromAction<WithdrawActionInfo>));
84
+ }
85
+
86
+ depositInputMode(): InputModeFromAction<DepositActionInfo> {
87
+ return this._depositInputMode;
88
+ }
89
+
90
+ withdrawInputMode(): InputModeFromAction<WithdrawActionInfo> {
91
+ return this._withdrawInputMode;
48
92
  }
49
93
 
50
94
  async getUserTVL(user: ContractAddr, blockIdentifier?: BlockIdentifier): Promise<TVLInfo> {
@@ -55,11 +99,11 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
55
99
  throw new Error("Not implemented");
56
100
  }
57
101
 
58
- async depositCall(amountInfo: ActionInfo, receiver: ContractAddr): Promise<Call[]> {
102
+ async depositCall(amountInfo: DepositActionInfo, receiver: ContractAddr): Promise<Call[]> {
59
103
  throw new Error("Not implemented");
60
104
  }
61
105
 
62
- async withdrawCall(amountInfo: ActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
106
+ async withdrawCall(amountInfo: WithdrawActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
63
107
  throw new Error("Not implemented");
64
108
  }
65
109
 
@@ -71,11 +115,112 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
71
115
  blockIdentifier?: BlockIdentifier,
72
116
  sinceBlocks?: number,
73
117
  timeperiod?: "24h" | "7d" | "30d" | "3m"
74
- ): Promise<number | NetAPYDetails> {
118
+ ): Promise<number | string | NetAPYDetails> {
75
119
  throw new Error("Not implemented");
76
120
  }
77
121
 
78
122
  async getPendingRewards(): Promise<HarvestInfo[]> {
79
123
  return [];
80
124
  }
81
- }
125
+
126
+ async getUserRealizedAPY(
127
+ blockIdentifier?: BlockIdentifier,
128
+ sinceBlocks?: number
129
+ ): Promise<number> {
130
+ throw new Error("Not implemented");
131
+ }
132
+
133
+ async getUserPositionCards(_input: UserPositionCardsInput): Promise<UserPositionCard[]> {
134
+ throw new Error("Not implemented");
135
+ }
136
+
137
+ async getMaxTVL() : Promise<Web3Number> {
138
+ // Can throw an error as well if needed, RN returning 0
139
+ return new Web3Number('0', 18)
140
+ }
141
+
142
+ protected formatTokenAmountForCard(amount: Web3Number, tokenInfo: TokenInfo): string {
143
+ const displayDecimals = tokenInfo.displayDecimals ?? 2;
144
+ const fixed = Number(amount.toFixed(displayDecimals));
145
+ const normalized = Number.isFinite(fixed) ? fixed : 0;
146
+ return `${normalized.toLocaleString("en-US", {
147
+ maximumFractionDigits: displayDecimals,
148
+ minimumFractionDigits: 0,
149
+ })} ${tokenInfo.symbol}`;
150
+ }
151
+
152
+ protected formatPercentForCard(value: number): string {
153
+ if (!Number.isFinite(value)) return "N/A";
154
+ return `${(value * 100).toFixed(2)}%`;
155
+ }
156
+
157
+ protected formatUSDForCard(value: number): string {
158
+ if (!Number.isFinite(value)) return "$0.00";
159
+ return new Intl.NumberFormat("en-US", {
160
+ style: "currency",
161
+ currency: "USD",
162
+ maximumFractionDigits: 2,
163
+ }).format(value);
164
+ }
165
+
166
+ protected getSubValueColorFromSignedNumber(value: number): UserPositionCardSubValueColor {
167
+ if (!Number.isFinite(value)) return "default";
168
+ if (value > 0) return "positive";
169
+ if (value < 0) return "negative";
170
+ return "default";
171
+ }
172
+
173
+ /**
174
+ * Calculate lifetime earnings for a user based on provided data from client
175
+ * Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
176
+ *
177
+ * @param userTVL - The user's current TVL (SingleTokenInfo with amount, usdValue, tokenInfo)
178
+ * @param investmentFlows - Array of investment flow transactions from client
179
+ * @returns Object containing lifetime earnings, current value, and total deposits/withdrawals
180
+ */
181
+ getLifetimeEarnings(
182
+ userTVL: SingleTokenInfo,
183
+ investmentFlows: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>
184
+ ): {
185
+ tokenInfo: SingleTokenInfo;
186
+ lifetimeEarnings: Web3Number;
187
+ currentValue: Web3Number;
188
+ totalDeposits: Web3Number;
189
+ totalWithdrawals: Web3Number;
190
+ } {
191
+ // Get token decimals from userTVL
192
+ const tokenDecimals = userTVL.tokenInfo.decimals;
193
+
194
+ // Initialize totals
195
+ let totalDeposits = Web3Number.fromWei("0", tokenDecimals);
196
+ let totalWithdrawals = Web3Number.fromWei("0", tokenDecimals);
197
+
198
+ // Process investment flows
199
+ for (const flow of investmentFlows) {
200
+ const amount = Web3Number.fromWei(flow.amount, tokenDecimals);
201
+
202
+ if (flow.type === 'deposit') {
203
+ totalDeposits = totalDeposits.plus(amount);
204
+ } else if (flow.type === 'withdraw' || flow.type === 'redeem') {
205
+ totalWithdrawals = totalWithdrawals.plus(amount);
206
+ }
207
+ }
208
+
209
+ // Calculate lifetime earnings: current value + withdrawals - deposits
210
+ const lifetimeEarnings = userTVL.amount
211
+ .plus(totalWithdrawals)
212
+ .minus(totalDeposits);
213
+
214
+ return {
215
+ tokenInfo: {
216
+ tokenInfo: userTVL.tokenInfo,
217
+ amount: lifetimeEarnings,
218
+ usdValue: 0, // Lifetime earnings are not converted to USD
219
+ },
220
+ lifetimeEarnings,
221
+ currentValue: userTVL.amount,
222
+ totalDeposits,
223
+ totalWithdrawals,
224
+ };
225
+ }
226
+ }
@@ -1,10 +1,10 @@
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
8
 
9
9
  export const ENDPOINTS = {
10
10
  VESU_BASE: "https://proxy.api.troves.fi/vesu",