@strkfarm/sdk 2.0.0-staging.4 → 2.0.0-staging.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.
- package/dist/cli.js +10 -6
- package/dist/cli.mjs +10 -6
- package/dist/index.browser.global.js +29277 -26655
- package/dist/index.browser.mjs +4037 -1224
- package/dist/index.d.ts +287 -45
- package/dist/index.js +4316 -1489
- package/dist/index.mjs +4256 -1437
- package/package.json +4 -4
- package/src/data/yoloVault.abi.json +1109 -0
- package/src/dataTypes/_bignumber.ts +5 -0
- package/src/dataTypes/bignumber.browser.ts +5 -0
- package/src/dataTypes/bignumber.node.ts +5 -0
- package/src/global.ts +27 -0
- package/src/interfaces/common.tsx +68 -25
- package/src/modules/avnu.ts +1 -1
- package/src/modules/erc20.ts +18 -2
- package/src/strategies/base-strategy.ts +216 -6
- package/src/strategies/constants.ts +2 -2
- package/src/strategies/ekubo-cl-vault.tsx +210 -105
- package/src/strategies/factory.ts +21 -1
- package/src/strategies/index.ts +2 -0
- package/src/strategies/registry.ts +15 -30
- package/src/strategies/sensei.ts +156 -11
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +48 -27
- package/src/strategies/universal-lst-muliplier-strategy.tsx +1473 -574
- package/src/strategies/universal-strategy.tsx +141 -69
- package/src/strategies/vesu-rebalance.tsx +27 -11
- package/src/strategies/yoloVault.ts +747 -0
- package/src/utils/logger.node.ts +11 -4
- package/src/utils/strategy-utils.ts +6 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from "@/utils/logger";
|
|
2
2
|
import BigNumber from "bignumber.js";
|
|
3
|
+
import { uint256 } from "starknet";
|
|
3
4
|
|
|
4
5
|
export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
|
|
5
6
|
decimals: number;
|
|
@@ -107,6 +108,10 @@ export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
|
|
|
107
108
|
sign: sign,
|
|
108
109
|
}
|
|
109
110
|
}
|
|
111
|
+
|
|
112
|
+
toUint256() {
|
|
113
|
+
return uint256.bnToUint256(this.toWei());
|
|
114
|
+
}
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
BigNumber.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Uint256, uint256 } from "starknet";
|
|
1
2
|
import { _Web3Number } from "./_bignumber";
|
|
2
3
|
|
|
3
4
|
export class Web3Number extends _Web3Number<Web3Number> {
|
|
@@ -5,4 +6,8 @@ export class Web3Number extends _Web3Number<Web3Number> {
|
|
|
5
6
|
const bn = (new Web3Number(weiNumber, decimals)).dividedBy(10 ** decimals)
|
|
6
7
|
return new Web3Number(bn.toString(), decimals);
|
|
7
8
|
}
|
|
9
|
+
|
|
10
|
+
static fromUint256(uint256Value: Uint256): Web3Number {
|
|
11
|
+
return this.fromWei(uint256.uint256ToBN(uint256Value).toString(), 18);
|
|
12
|
+
}
|
|
8
13
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import util from 'util';
|
|
2
2
|
import { _Web3Number } from "./_bignumber";
|
|
3
|
+
import { Uint256, uint256 } from 'starknet';
|
|
3
4
|
|
|
4
5
|
export class Web3Number extends _Web3Number<Web3Number> {
|
|
5
6
|
|
|
@@ -8,6 +9,10 @@ export class Web3Number extends _Web3Number<Web3Number> {
|
|
|
8
9
|
return new Web3Number(bn.toString(), decimals);
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
static fromUint256(uint256Value: Uint256): Web3Number {
|
|
13
|
+
return this.fromWei(uint256.uint256ToBN(uint256Value).toString(), 18);
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
[util.inspect.custom](depth: any, opts: any): string {
|
|
12
17
|
return this.toString();
|
|
13
18
|
}
|
package/src/global.ts
CHANGED
|
@@ -165,6 +165,33 @@ const defaultTokens: TokenInfo[] = [{
|
|
|
165
165
|
coingeckId: undefined,
|
|
166
166
|
displayDecimals: 2,
|
|
167
167
|
priceCheckAmount: 100,
|
|
168
|
+
}, {
|
|
169
|
+
name: "fyeWBTC",
|
|
170
|
+
symbol: "fyeWBTC",
|
|
171
|
+
logo: 'https://assets.strkfarm.com/integrations/tokens/wbtc.svg',
|
|
172
|
+
address: ContractAddr.from('0x04dd39de0a588f5e1c7a8377e1bef2c49caaee49a11433429d2c48f587b3a492'),
|
|
173
|
+
decimals: 8,
|
|
174
|
+
coingeckId: undefined,
|
|
175
|
+
displayDecimals: 6,
|
|
176
|
+
priceCheckAmount: 0.001, // 112000 * 0.0001 = $110.2
|
|
177
|
+
}, {
|
|
178
|
+
name: "fyETH",
|
|
179
|
+
symbol: "fyETH",
|
|
180
|
+
logo: 'https://assets.strkfarm.com/integrations/tokens/eth.svg',
|
|
181
|
+
address: ContractAddr.from('0x050707bC3b8730022F10530C2c6f6b9467644129C50C2868Ad0036c5e4E9e616'),
|
|
182
|
+
decimals: 18,
|
|
183
|
+
coingeckId: undefined,
|
|
184
|
+
displayDecimals: 4,
|
|
185
|
+
priceCheckAmount: 0.1,
|
|
186
|
+
}, {
|
|
187
|
+
name: "fyeUSDC",
|
|
188
|
+
symbol: "fyeUSDC",
|
|
189
|
+
logo: 'https://assets.strkfarm.com/integrations/tokens/usdc.svg',
|
|
190
|
+
address: ContractAddr.from('0x07fdcec0cef01294c9c3d52415215949805c77bae8003702a7928fd6d2c36bc1'),
|
|
191
|
+
decimals: 6,
|
|
192
|
+
coingeckId: undefined,
|
|
193
|
+
displayDecimals: 2,
|
|
194
|
+
priceCheckAmount: 100,
|
|
168
195
|
}]
|
|
169
196
|
const tokens: TokenInfo[] = defaultTokens;
|
|
170
197
|
|
|
@@ -55,21 +55,24 @@ export interface IProtocol {
|
|
|
55
55
|
logo: string;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
BTC = "btc",
|
|
62
|
-
META_VAULTS = "meta-vaults",
|
|
58
|
+
export interface ICurator {
|
|
59
|
+
name: string;
|
|
60
|
+
logo: string;
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
export enum StrategyTag {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
META_VAULT = "Meta Vaults",
|
|
65
|
+
LEVERED = "Maxx",
|
|
66
|
+
AUTOMATED_LP = "Ekubo",
|
|
67
|
+
BTC = "BTC"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export enum VaultType {
|
|
71
|
+
LOOPING = "Looping",
|
|
72
|
+
META_VAULT = "Meta Vault",
|
|
73
|
+
DELTA_NEUTRAL = "Delta Neutral",
|
|
74
|
+
AUTOMATED_LP = "Automated LP",
|
|
75
|
+
TVA = "Troves Value Averaging",
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
// Security metadata enums
|
|
@@ -86,6 +89,7 @@ export enum SourceCodeType {
|
|
|
86
89
|
export enum AccessControlType {
|
|
87
90
|
MULTISIG_ACCOUNT = "Multisig Account",
|
|
88
91
|
STANDARD_ACCOUNT = "Standard Account",
|
|
92
|
+
ROLE_BASED_ACCESS = "Role Based Access",
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
export enum InstantWithdrawalVault {
|
|
@@ -102,7 +106,7 @@ export interface SourceCodeInfo {
|
|
|
102
106
|
export interface AccessControlInfo {
|
|
103
107
|
type: AccessControlType;
|
|
104
108
|
addresses: ContractAddr[];
|
|
105
|
-
timeLock
|
|
109
|
+
timeLock?: string;
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
export interface SecurityMetadata {
|
|
@@ -111,16 +115,13 @@ export interface SecurityMetadata {
|
|
|
111
115
|
accessControl: AccessControlInfo;
|
|
112
116
|
}
|
|
113
117
|
|
|
114
|
-
// Redemption metadata interfaces
|
|
115
|
-
export interface RedemptionExpectedTime {
|
|
116
|
-
upto1M: string;
|
|
117
|
-
upto10M: string;
|
|
118
|
-
above10M: string;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
118
|
export interface RedemptionInfo {
|
|
122
119
|
instantWithdrawalVault: InstantWithdrawalVault;
|
|
123
|
-
|
|
120
|
+
redemptionsInfo: {
|
|
121
|
+
title: string; // e.g. Upto $1M
|
|
122
|
+
description: string; // e.g. "1-2 hours"
|
|
123
|
+
}[],
|
|
124
|
+
alerts: StrategyAlert[];
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
export enum FlowChartColors {
|
|
@@ -138,7 +139,8 @@ export enum StrategyLiveStatus {
|
|
|
138
139
|
ACTIVE = "Active",
|
|
139
140
|
NEW = "New",
|
|
140
141
|
COMING_SOON = "Coming Soon",
|
|
141
|
-
|
|
142
|
+
DEPRECATED = "Deprecated", // active but not recommended
|
|
143
|
+
RETIRED = "Retired", // not active anymore
|
|
142
144
|
HOT = "Hot & New 🔥"
|
|
143
145
|
}
|
|
144
146
|
|
|
@@ -165,6 +167,13 @@ export interface StrategySettings {
|
|
|
165
167
|
tags?: StrategyTag[];
|
|
166
168
|
}
|
|
167
169
|
|
|
170
|
+
export interface StrategyApyHistoryUIConfig {
|
|
171
|
+
// Defaults to true in UI if omitted.
|
|
172
|
+
showApyHistory?: boolean;
|
|
173
|
+
// Optional message shown when APY history is hidden.
|
|
174
|
+
noApyHistoryMessage?: string;
|
|
175
|
+
}
|
|
176
|
+
|
|
168
177
|
/**
|
|
169
178
|
* @property risk.riskFactor.factor - The risk factors that are considered for the strategy.
|
|
170
179
|
* @property risk.riskFactor.factor - The value of the risk factor from 0 to 10, 0 being the lowest and 10 being the highest.
|
|
@@ -178,6 +187,10 @@ export interface IStrategyMetadata<T> {
|
|
|
178
187
|
address: ContractAddr;
|
|
179
188
|
launchBlock: number;
|
|
180
189
|
type: "ERC4626" | "ERC721" | "Other";
|
|
190
|
+
vaultType: {
|
|
191
|
+
type: VaultType;
|
|
192
|
+
description: string;
|
|
193
|
+
};
|
|
181
194
|
depositTokens: TokenInfo[];
|
|
182
195
|
protocols: IProtocol[];
|
|
183
196
|
auditUrl?: string;
|
|
@@ -187,6 +200,7 @@ export interface IStrategyMetadata<T> {
|
|
|
187
200
|
notARisks: RiskType[];
|
|
188
201
|
};
|
|
189
202
|
apyMethodology?: string;
|
|
203
|
+
realizedApyMethodology?: string;
|
|
190
204
|
additionalInfo: T;
|
|
191
205
|
contractDetails: {
|
|
192
206
|
address: ContractAddr;
|
|
@@ -197,13 +211,20 @@ export interface IStrategyMetadata<T> {
|
|
|
197
211
|
points?: {multiplier: number, logo: string, toolTip?: string}[];
|
|
198
212
|
docs?: string;
|
|
199
213
|
investmentSteps: string[];
|
|
200
|
-
curator?:
|
|
214
|
+
curator?: ICurator,
|
|
201
215
|
isPreview?: boolean;
|
|
202
|
-
category: StrategyCategory;
|
|
203
216
|
tags?: StrategyTag[];
|
|
204
217
|
security: SecurityMetadata;
|
|
205
218
|
redemptionInfo: RedemptionInfo;
|
|
219
|
+
usualTimeToEarnings: null | string; // e.g. "2 weeks" // some strats grow like step functions
|
|
220
|
+
usualTimeToEarningsDescription: null | string; // e.g. "LSTs price on DEX goes up roughly every 2 weeks"
|
|
221
|
+
discontinuationInfo?: {
|
|
222
|
+
date?: Date;
|
|
223
|
+
reason?: React.ReactNode | string;
|
|
224
|
+
info?: React.ReactNode | string;
|
|
225
|
+
};
|
|
206
226
|
settings?: StrategySettings;
|
|
227
|
+
apyHistoryUIConfig?: StrategyApyHistoryUIConfig;
|
|
207
228
|
// Legacy field for multi-step strategies (deprecated, use investmentFlows instead)
|
|
208
229
|
actions?: Array<{
|
|
209
230
|
name?: string;
|
|
@@ -242,6 +263,23 @@ export function getMainnetConfig(
|
|
|
242
263
|
};
|
|
243
264
|
}
|
|
244
265
|
|
|
266
|
+
export const getStrategyTagDesciption = (tag: StrategyTag): string => {
|
|
267
|
+
switch (tag) {
|
|
268
|
+
case StrategyTag.META_VAULT:
|
|
269
|
+
return "A meta vault is a vault that auto allocates funds to multiple vaults based on optimal yield opportunities";
|
|
270
|
+
case StrategyTag.LEVERED:
|
|
271
|
+
return "Looping vaults on Endur LSTs with leveraged borrowing of STRK or BTC to increase yield (2-4x higher yield than simply staking)";
|
|
272
|
+
case StrategyTag.AUTOMATED_LP:
|
|
273
|
+
return "Automated LP vaults on Ekubo that rebalance position automatically, ensuring you earn fees efficiently";
|
|
274
|
+
case StrategyTag.BTC:
|
|
275
|
+
return "BTC linked vaults";
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export const getAllStrategyTags = (): StrategyTag[] => {
|
|
280
|
+
return Object.values(StrategyTag);
|
|
281
|
+
}
|
|
282
|
+
|
|
245
283
|
export const getRiskExplaination = (riskType: RiskType) => {
|
|
246
284
|
switch (riskType) {
|
|
247
285
|
case RiskType.MARKET_RISK:
|
|
@@ -314,7 +352,7 @@ export function highlightTextWithLinks(
|
|
|
314
352
|
{parts.map((part, i) => {
|
|
315
353
|
const match = highlights.find(m => m.highlight.toLowerCase() === part.toLowerCase());
|
|
316
354
|
return match ? (
|
|
317
|
-
<a key={i} href={match.link} target="_blank" style={{ color: '
|
|
355
|
+
<a key={i} href={match.link} target="_blank" style={{ color: 'white', background: 'rgba(255, 255, 255, 0.04)' }}>
|
|
318
356
|
{part}
|
|
319
357
|
</a>
|
|
320
358
|
) : (
|
|
@@ -375,4 +413,9 @@ export const Protocols = {
|
|
|
375
413
|
VESU: VesuProtocol,
|
|
376
414
|
ENDUR: EndurProtocol,
|
|
377
415
|
EXTENDED: ExtendedProtocol
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export const UnwrapLabsCurator: ICurator = {
|
|
419
|
+
name: "Unwrap Labs",
|
|
420
|
+
logo: "https://assets.troves.fi/integrations/unwraplabs/white.png"
|
|
378
421
|
}
|
package/src/modules/avnu.ts
CHANGED
|
@@ -37,7 +37,7 @@ export class AvnuWrapper {
|
|
|
37
37
|
excludeSources = ['Haiko(Solvers)']
|
|
38
38
|
): Promise<Quote> {
|
|
39
39
|
const MAX_RETRY = 5;
|
|
40
|
-
|
|
40
|
+
logger.verbose(`${AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
|
|
41
41
|
const params: any = {
|
|
42
42
|
sellTokenAddress: fromToken,
|
|
43
43
|
buyTokenAddress: toToken,
|
package/src/modules/erc20.ts
CHANGED
|
@@ -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
|
}
|
|
@@ -23,18 +23,76 @@ 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
|
+
|
|
29
|
+
export interface NetAPYSplit {
|
|
30
|
+
apy: number;
|
|
31
|
+
id: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface NetAPYDetails {
|
|
35
|
+
net: number;
|
|
36
|
+
splits: NetAPYSplit[];
|
|
37
|
+
}
|
|
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
|
+
kind?: "metric" | "apy";
|
|
48
|
+
apyMethod?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface UserPositionCardsInput {
|
|
52
|
+
user: ContractAddr;
|
|
53
|
+
investmentFlows?: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>;
|
|
54
|
+
usualTimeToEarnings?: string | null;
|
|
55
|
+
usualTimeToEarningsDescription?: string | null;
|
|
56
|
+
apy?: number | string | null;
|
|
57
|
+
apyMethod?: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
26
60
|
interface CacheData {
|
|
27
61
|
timestamp: number;
|
|
28
62
|
ttl: number;
|
|
29
63
|
data: any;
|
|
30
64
|
}
|
|
31
|
-
export class BaseStrategy<
|
|
32
|
-
|
|
33
|
-
|
|
65
|
+
export class BaseStrategy<
|
|
66
|
+
TVLInfo,
|
|
67
|
+
DepositActionInfo,
|
|
68
|
+
WithdrawActionInfo = DepositActionInfo,
|
|
69
|
+
> extends CacheClass {
|
|
70
|
+
readonly config: IConfig;
|
|
71
|
+
readonly cache: Map<string, CacheData> = new Map();
|
|
72
|
+
private readonly _depositInputMode: InputModeFromAction<DepositActionInfo>;
|
|
73
|
+
private readonly _withdrawInputMode: InputModeFromAction<WithdrawActionInfo>;
|
|
34
74
|
|
|
35
|
-
constructor(
|
|
75
|
+
constructor(
|
|
76
|
+
config: IConfig,
|
|
77
|
+
inputModes?: {
|
|
78
|
+
depositInputMode?: InputModeFromAction<DepositActionInfo>;
|
|
79
|
+
withdrawInputMode?: InputModeFromAction<WithdrawActionInfo>;
|
|
80
|
+
}
|
|
81
|
+
) {
|
|
36
82
|
super();
|
|
37
83
|
this.config = config;
|
|
84
|
+
this._depositInputMode = (inputModes?.depositInputMode ??
|
|
85
|
+
("single" as InputModeFromAction<DepositActionInfo>));
|
|
86
|
+
this._withdrawInputMode = (inputModes?.withdrawInputMode ??
|
|
87
|
+
("single" as InputModeFromAction<WithdrawActionInfo>));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
depositInputMode(): InputModeFromAction<DepositActionInfo> {
|
|
91
|
+
return this._depositInputMode;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
withdrawInputMode(): InputModeFromAction<WithdrawActionInfo> {
|
|
95
|
+
return this._withdrawInputMode;
|
|
38
96
|
}
|
|
39
97
|
|
|
40
98
|
async getUserTVL(user: ContractAddr, blockIdentifier?: BlockIdentifier): Promise<TVLInfo> {
|
|
@@ -45,11 +103,11 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
|
|
|
45
103
|
throw new Error("Not implemented");
|
|
46
104
|
}
|
|
47
105
|
|
|
48
|
-
async depositCall(amountInfo:
|
|
106
|
+
async depositCall(amountInfo: DepositActionInfo, receiver: ContractAddr): Promise<Call[]> {
|
|
49
107
|
throw new Error("Not implemented");
|
|
50
108
|
}
|
|
51
109
|
|
|
52
|
-
async withdrawCall(amountInfo:
|
|
110
|
+
async withdrawCall(amountInfo: WithdrawActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
|
|
53
111
|
throw new Error("Not implemented");
|
|
54
112
|
}
|
|
55
113
|
|
|
@@ -57,7 +115,159 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
|
|
|
57
115
|
throw new Error("Not implemented");
|
|
58
116
|
}
|
|
59
117
|
|
|
118
|
+
async netAPY(
|
|
119
|
+
blockIdentifier?: BlockIdentifier,
|
|
120
|
+
sinceBlocks?: number,
|
|
121
|
+
timeperiod?: "24h" | "7d" | "30d" | "3m"
|
|
122
|
+
): Promise<number | string | NetAPYDetails> {
|
|
123
|
+
throw new Error("Not implemented");
|
|
124
|
+
}
|
|
125
|
+
|
|
60
126
|
async getPendingRewards(): Promise<HarvestInfo[]> {
|
|
61
127
|
return [];
|
|
62
128
|
}
|
|
129
|
+
|
|
130
|
+
async getUserRealizedAPY(
|
|
131
|
+
blockIdentifier?: BlockIdentifier,
|
|
132
|
+
sinceBlocks?: number
|
|
133
|
+
): Promise<number> {
|
|
134
|
+
throw new Error("Not implemented");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async getUserPositionCards(_input: UserPositionCardsInput): Promise<UserPositionCard[]> {
|
|
138
|
+
throw new Error("Not implemented");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected formatTokenAmountForCard(amount: Web3Number, tokenInfo: TokenInfo): string {
|
|
142
|
+
const displayDecimals = tokenInfo.displayDecimals ?? 2;
|
|
143
|
+
const fixed = Number(amount.toFixed(displayDecimals));
|
|
144
|
+
const normalized = Number.isFinite(fixed) ? fixed : 0;
|
|
145
|
+
return `${normalized.toLocaleString("en-US", {
|
|
146
|
+
maximumFractionDigits: displayDecimals,
|
|
147
|
+
minimumFractionDigits: 0,
|
|
148
|
+
})} ${tokenInfo.symbol}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
protected formatPercentForCard(value: number): string {
|
|
152
|
+
if (!Number.isFinite(value)) return "N/A";
|
|
153
|
+
return `${(value * 100).toFixed(2)}%`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
protected resolveApyMethod(input?: UserPositionCardsInput): string | undefined {
|
|
157
|
+
const fromInput = input?.apyMethod?.trim();
|
|
158
|
+
if (fromInput) return fromInput;
|
|
159
|
+
const metadataApyMethod = (this as any)?.metadata?.apyMethodology;
|
|
160
|
+
if (typeof metadataApyMethod === "string" && metadataApyMethod.trim().length > 0) {
|
|
161
|
+
return metadataApyMethod;
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
protected normalizeApyDisplayValue(
|
|
167
|
+
apy: number | string | NetAPYDetails | null | undefined
|
|
168
|
+
): string {
|
|
169
|
+
if (typeof apy === "number") {
|
|
170
|
+
return this.formatPercentForCard(apy);
|
|
171
|
+
}
|
|
172
|
+
if (typeof apy === "string") {
|
|
173
|
+
return apy.trim().length > 0 ? apy : "N/A";
|
|
174
|
+
}
|
|
175
|
+
if (apy && typeof apy === "object" && "net" in apy) {
|
|
176
|
+
return this.formatPercentForCard(Number(apy.net));
|
|
177
|
+
}
|
|
178
|
+
return "N/A";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected async resolveApyValueForCard(input?: UserPositionCardsInput): Promise<string> {
|
|
182
|
+
if (input && input.apy !== undefined && input.apy !== null) {
|
|
183
|
+
return this.normalizeApyDisplayValue(input.apy);
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const netApy = await this.netAPY();
|
|
187
|
+
return this.normalizeApyDisplayValue(netApy as number | string | NetAPYDetails);
|
|
188
|
+
} catch {
|
|
189
|
+
return "N/A";
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
protected async createApyCard(input?: UserPositionCardsInput): Promise<UserPositionCard> {
|
|
194
|
+
const apyMethod = this.resolveApyMethod(input);
|
|
195
|
+
return {
|
|
196
|
+
title: "APY",
|
|
197
|
+
value: await this.resolveApyValueForCard(input),
|
|
198
|
+
tooltip: apyMethod,
|
|
199
|
+
kind: "apy",
|
|
200
|
+
apyMethod,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
protected formatUSDForCard(value: number): string {
|
|
205
|
+
if (!Number.isFinite(value)) return "$0.00";
|
|
206
|
+
return new Intl.NumberFormat("en-US", {
|
|
207
|
+
style: "currency",
|
|
208
|
+
currency: "USD",
|
|
209
|
+
maximumFractionDigits: 2,
|
|
210
|
+
}).format(value);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
protected getSubValueColorFromSignedNumber(value: number): UserPositionCardSubValueColor {
|
|
214
|
+
if (!Number.isFinite(value)) return "default";
|
|
215
|
+
if (value > 0) return "positive";
|
|
216
|
+
if (value < 0) return "negative";
|
|
217
|
+
return "default";
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Calculate lifetime earnings for a user based on provided data from client
|
|
222
|
+
* Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
|
|
223
|
+
*
|
|
224
|
+
* @param userTVL - The user's current TVL (SingleTokenInfo with amount, usdValue, tokenInfo)
|
|
225
|
+
* @param investmentFlows - Array of investment flow transactions from client
|
|
226
|
+
* @returns Object containing lifetime earnings, current value, and total deposits/withdrawals
|
|
227
|
+
*/
|
|
228
|
+
getLifetimeEarnings(
|
|
229
|
+
userTVL: SingleTokenInfo,
|
|
230
|
+
investmentFlows: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>
|
|
231
|
+
): {
|
|
232
|
+
tokenInfo: SingleTokenInfo;
|
|
233
|
+
lifetimeEarnings: Web3Number;
|
|
234
|
+
currentValue: Web3Number;
|
|
235
|
+
totalDeposits: Web3Number;
|
|
236
|
+
totalWithdrawals: Web3Number;
|
|
237
|
+
} {
|
|
238
|
+
// Get token decimals from userTVL
|
|
239
|
+
const tokenDecimals = userTVL.tokenInfo.decimals;
|
|
240
|
+
|
|
241
|
+
// Initialize totals
|
|
242
|
+
let totalDeposits = Web3Number.fromWei("0", tokenDecimals);
|
|
243
|
+
let totalWithdrawals = Web3Number.fromWei("0", tokenDecimals);
|
|
244
|
+
|
|
245
|
+
// Process investment flows
|
|
246
|
+
for (const flow of investmentFlows) {
|
|
247
|
+
const amount = Web3Number.fromWei(flow.amount, tokenDecimals);
|
|
248
|
+
|
|
249
|
+
if (flow.type === 'deposit') {
|
|
250
|
+
totalDeposits = totalDeposits.plus(amount);
|
|
251
|
+
} else if (flow.type === 'withdraw' || flow.type === 'redeem') {
|
|
252
|
+
totalWithdrawals = totalWithdrawals.plus(amount);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Calculate lifetime earnings: current value + withdrawals - deposits
|
|
257
|
+
const lifetimeEarnings = userTVL.amount
|
|
258
|
+
.plus(totalWithdrawals)
|
|
259
|
+
.minus(totalDeposits);
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
tokenInfo: {
|
|
263
|
+
tokenInfo: userTVL.tokenInfo,
|
|
264
|
+
amount: lifetimeEarnings,
|
|
265
|
+
usdValue: 0, // Lifetime earnings are not converted to USD
|
|
266
|
+
},
|
|
267
|
+
lifetimeEarnings,
|
|
268
|
+
currentValue: userTVL.amount,
|
|
269
|
+
totalDeposits,
|
|
270
|
+
totalWithdrawals,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
63
273
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ContractAddr } from "@/dataTypes";
|
|
2
2
|
|
|
3
|
-
export const
|
|
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",
|