@strkfarm/sdk 1.2.1 → 2.0.0-dca.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.
- package/dist/cli.js +9 -5
- package/dist/cli.mjs +9 -5
- package/dist/index.browser.global.js +67035 -40458
- package/dist/index.browser.mjs +5218 -1908
- package/dist/index.d.ts +478 -33
- package/dist/index.js +5500 -2157
- package/dist/index.mjs +5441 -2129
- package/package.json +4 -1
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/data/yoloVault.abi.json +777 -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/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +42 -0
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +168 -2
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +1 -1
- package/src/modules/ekubo-pricer.ts +79 -0
- package/src/modules/erc20.ts +18 -2
- package/src/modules/pragma.ts +23 -8
- package/src/modules/pricer-from-api.ts +150 -14
- package/src/modules/pricer.ts +2 -1
- package/src/modules/pricerBase.ts +2 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/strategies/base-strategy.ts +81 -2
- package/src/strategies/ekubo-cl-vault.tsx +686 -316
- package/src/strategies/factory.ts +159 -0
- package/src/strategies/index.ts +5 -1
- package/src/strategies/registry.ts +239 -0
- package/src/strategies/sensei.ts +361 -13
- 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 +1396 -463
- package/src/strategies/universal-strategy.tsx +287 -129
- package/src/strategies/vesu-rebalance.tsx +242 -146
- package/src/strategies/yoloVault.ts +463 -0
- package/src/utils/index.ts +1 -1
- package/src/utils/logger.node.ts +11 -4
- package/src/utils/strategy-utils.ts +61 -0
|
@@ -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/dataTypes/index.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "@/dataTypes/bignumber";
|
|
2
|
+
export * from "./address";
|
|
3
|
+
export * from "./mynumber";
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { ethers } from "ethers";
|
|
3
|
+
|
|
4
|
+
const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom");
|
|
5
|
+
|
|
6
|
+
BigNumber.config({
|
|
7
|
+
DECIMAL_PLACES: 18
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export class MyNumber {
|
|
11
|
+
bigNumber: BigNumber;
|
|
12
|
+
decimals: number;
|
|
13
|
+
|
|
14
|
+
constructor(bigNumber: string, decimals: number) {
|
|
15
|
+
this.bigNumber = new BigNumber(bigNumber);
|
|
16
|
+
this.decimals = decimals;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static fromEther(num: string, decimals: number) {
|
|
20
|
+
try {
|
|
21
|
+
return new MyNumber(
|
|
22
|
+
Number(
|
|
23
|
+
ethers.parseUnits(Number(num).toFixed(10), decimals)
|
|
24
|
+
).toFixed(6),
|
|
25
|
+
decimals
|
|
26
|
+
);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
console.error("fromEther", e, num, decimals);
|
|
29
|
+
throw e;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static fromZero() {
|
|
34
|
+
return new MyNumber("0", 0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
toString() {
|
|
38
|
+
return this.bigNumber.toFixed();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
toEtherStr() {
|
|
42
|
+
return ethers.formatUnits(this.bigNumber.toFixed(), this.decimals);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
toFixedStr(decimals: number) {
|
|
46
|
+
return Number(this.toEtherStr()).toFixed(decimals);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
toEtherToFixedDecimals(decimals: number) {
|
|
50
|
+
// rounding down
|
|
51
|
+
if (this.bigNumber.isNaN()) {
|
|
52
|
+
return "NaN";
|
|
53
|
+
}
|
|
54
|
+
return (
|
|
55
|
+
Math.floor(parseFloat(this.toEtherStr()) * 10 ** decimals) /
|
|
56
|
+
10 ** decimals
|
|
57
|
+
).toFixed(decimals);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
isZero() {
|
|
61
|
+
return this.bigNumber.eq("0");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
*
|
|
66
|
+
* @param amountEther in token terms without decimal e.g. 1 for 1 STRK
|
|
67
|
+
* @param command BigNumber compare funds. e.g. gte, gt, lt
|
|
68
|
+
* @returns
|
|
69
|
+
* @dev Add more commands as needed
|
|
70
|
+
*/
|
|
71
|
+
compare(amountEther: string, command: "gte" | "gt" | "lt") {
|
|
72
|
+
const fullNum = new BigNumber(
|
|
73
|
+
ethers.parseUnits(amountEther, this.decimals).toString()
|
|
74
|
+
);
|
|
75
|
+
return this.bigNumber[command](fullNum);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
operate(command: "div" | "plus" | "mul", value: string | number) {
|
|
79
|
+
const bn = new BigNumber(Number(value).toFixed(6));
|
|
80
|
+
return new MyNumber(
|
|
81
|
+
this.bigNumber[command](bn).toFixed(0),
|
|
82
|
+
this.decimals
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
subtract(value: MyNumber) {
|
|
87
|
+
const bn = this.bigNumber.minus(value.bigNumber);
|
|
88
|
+
return new MyNumber(bn.toString(), this.decimals);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static min(a: MyNumber, b: MyNumber) {
|
|
92
|
+
if (a.decimals !== b.decimals) {
|
|
93
|
+
const diff = Math.abs(a.decimals - b.decimals);
|
|
94
|
+
if (a.decimals > b.decimals) {
|
|
95
|
+
b = new MyNumber(
|
|
96
|
+
b.bigNumber.times(10 ** diff).toString(),
|
|
97
|
+
a.decimals
|
|
98
|
+
);
|
|
99
|
+
} else {
|
|
100
|
+
a = new MyNumber(
|
|
101
|
+
a.bigNumber.times(10 ** diff).toString(),
|
|
102
|
+
b.decimals
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const bn = BigNumber.min(a.bigNumber, b.bigNumber);
|
|
107
|
+
return new MyNumber(
|
|
108
|
+
bn.toString(),
|
|
109
|
+
a.decimals > b.decimals ? a.decimals : b.decimals
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static max(a: MyNumber, b: MyNumber) {
|
|
114
|
+
if (a.decimals !== b.decimals) {
|
|
115
|
+
const diff = Math.abs(a.decimals - b.decimals);
|
|
116
|
+
if (a.decimals > b.decimals) {
|
|
117
|
+
b = new MyNumber(
|
|
118
|
+
b.bigNumber.times(10 ** diff).toString(),
|
|
119
|
+
a.decimals
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
a = new MyNumber(
|
|
123
|
+
a.bigNumber.times(10 ** diff).toString(),
|
|
124
|
+
b.decimals
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const bn = BigNumber.max(a.bigNumber, b.bigNumber);
|
|
129
|
+
return new MyNumber(
|
|
130
|
+
bn.toString(),
|
|
131
|
+
a.decimals > b.decimals ? a.decimals : b.decimals
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[customInspectSymbol](depth: any, inspectOptions: any, inspect: any) {
|
|
136
|
+
return JSON.stringify({
|
|
137
|
+
raw: this.toString(),
|
|
138
|
+
decimals: this.decimals
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
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
|
|
|
@@ -255,6 +282,21 @@ export class Global {
|
|
|
255
282
|
return token;
|
|
256
283
|
}
|
|
257
284
|
|
|
285
|
+
static async getTokenInfoFromName(tokenName: string) {
|
|
286
|
+
// if tokens are not loaded, load them
|
|
287
|
+
if (tokens.length == defaultTokens.length) {
|
|
288
|
+
await Global.getTokens();
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const token = tokens.find(
|
|
292
|
+
(token) => token.name.toLowerCase() === tokenName.toLowerCase()
|
|
293
|
+
);
|
|
294
|
+
if (!token) {
|
|
295
|
+
throw new FatalError(`Token not found: ${tokenName}`);
|
|
296
|
+
}
|
|
297
|
+
return token;
|
|
298
|
+
}
|
|
299
|
+
|
|
258
300
|
static setGlobalCache(key: string, data: any, ttl: number = 60000) {
|
|
259
301
|
Global.cache[key] = {
|
|
260
302
|
value: data,
|
package/src/index.browser.ts
CHANGED
|
@@ -55,6 +55,69 @@ export interface IProtocol {
|
|
|
55
55
|
logo: string;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
export enum StrategyTag {
|
|
59
|
+
META_VAULT = "Meta Vaults",
|
|
60
|
+
LEVERED = "Maxx",
|
|
61
|
+
AUTOMATED_LP = "Ekubo",
|
|
62
|
+
BTC = "BTC"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export enum VaultType {
|
|
66
|
+
LOOPING = "Looping",
|
|
67
|
+
META_VAULT = "Meta Vault",
|
|
68
|
+
DELTA_NEUTRAL = "Delta Neutral",
|
|
69
|
+
AUTOMATED_LP = "Automated LP",
|
|
70
|
+
Other = "Other",
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Security metadata enums
|
|
74
|
+
export enum AuditStatus {
|
|
75
|
+
AUDITED = "Audited",
|
|
76
|
+
NOT_AUDITED = "Not Audited",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export enum SourceCodeType {
|
|
80
|
+
OPEN_SOURCE = "Open Source",
|
|
81
|
+
CLOSED_SOURCE = "Closed Source",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export enum AccessControlType {
|
|
85
|
+
MULTISIG_ACCOUNT = "Multisig Account",
|
|
86
|
+
STANDARD_ACCOUNT = "Standard Account",
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export enum InstantWithdrawalVault {
|
|
90
|
+
YES = "Yes",
|
|
91
|
+
NO = "No",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Security metadata interfaces
|
|
95
|
+
export interface SourceCodeInfo {
|
|
96
|
+
type: SourceCodeType;
|
|
97
|
+
contractLink: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface AccessControlInfo {
|
|
101
|
+
type: AccessControlType;
|
|
102
|
+
addresses: ContractAddr[];
|
|
103
|
+
timeLock: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface SecurityMetadata {
|
|
107
|
+
auditStatus: AuditStatus;
|
|
108
|
+
sourceCode: SourceCodeInfo;
|
|
109
|
+
accessControl: AccessControlInfo;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface RedemptionInfo {
|
|
113
|
+
instantWithdrawalVault: InstantWithdrawalVault;
|
|
114
|
+
redemptionsInfo: {
|
|
115
|
+
title: string; // e.g. Upto $1M
|
|
116
|
+
description: string; // e.g. "1-2 hours"
|
|
117
|
+
}[],
|
|
118
|
+
alerts: StrategyAlert[];
|
|
119
|
+
}
|
|
120
|
+
|
|
58
121
|
export enum FlowChartColors {
|
|
59
122
|
Green = "purple",
|
|
60
123
|
Blue = "#35484f",
|
|
@@ -66,26 +129,65 @@ export interface FAQ {
|
|
|
66
129
|
answer: string | React.ReactNode;
|
|
67
130
|
}
|
|
68
131
|
|
|
132
|
+
export enum StrategyLiveStatus {
|
|
133
|
+
ACTIVE = "Active",
|
|
134
|
+
NEW = "New",
|
|
135
|
+
COMING_SOON = "Coming Soon",
|
|
136
|
+
DEPRECATED = "Deprecated", // active but not recommended
|
|
137
|
+
RETIRED = "Retired", // not active anymore
|
|
138
|
+
HOT = "Hot & New 🔥"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface StrategyAlert {
|
|
142
|
+
type: "warning" | "info";
|
|
143
|
+
text: string | React.ReactNode;
|
|
144
|
+
tab: "all" | "deposit" | "withdraw";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface StrategySettings {
|
|
148
|
+
maxTVL?: Web3Number;
|
|
149
|
+
liveStatus?: StrategyLiveStatus;
|
|
150
|
+
isPaused?: boolean;
|
|
151
|
+
isInMaintenance?: boolean;
|
|
152
|
+
isAudited: boolean;
|
|
153
|
+
isInstantWithdrawal?: boolean;
|
|
154
|
+
hideHarvestInfo?: boolean;
|
|
155
|
+
is_promoted?: boolean;
|
|
156
|
+
isTransactionHistDisabled?: boolean;
|
|
157
|
+
quoteToken: TokenInfo;
|
|
158
|
+
hideNetEarnings?: boolean;
|
|
159
|
+
showWithdrawalWarningModal?: boolean;
|
|
160
|
+
alerts?: StrategyAlert[];
|
|
161
|
+
tags?: StrategyTag[];
|
|
162
|
+
}
|
|
163
|
+
|
|
69
164
|
/**
|
|
70
165
|
* @property risk.riskFactor.factor - The risk factors that are considered for the strategy.
|
|
71
166
|
* @property risk.riskFactor.factor - The value of the risk factor from 0 to 10, 0 being the lowest and 10 being the highest.
|
|
167
|
+
* @property security - Security-related metadata including audit status, source code information, and access control details.
|
|
168
|
+
* @property redemptionInfo - Redemption information including instant withdrawal availability and expected redemption times.
|
|
72
169
|
*/
|
|
73
170
|
export interface IStrategyMetadata<T> {
|
|
171
|
+
id: string;
|
|
74
172
|
name: string;
|
|
75
173
|
description: string | React.ReactNode;
|
|
76
174
|
address: ContractAddr;
|
|
77
175
|
launchBlock: number;
|
|
78
176
|
type: "ERC4626" | "ERC721" | "Other";
|
|
177
|
+
vaultType: {
|
|
178
|
+
type: VaultType;
|
|
179
|
+
description: string;
|
|
180
|
+
};
|
|
79
181
|
depositTokens: TokenInfo[];
|
|
80
182
|
protocols: IProtocol[];
|
|
81
183
|
auditUrl?: string;
|
|
82
|
-
maxTVL: Web3Number;
|
|
83
184
|
risk: {
|
|
84
185
|
riskFactor: RiskFactor[];
|
|
85
186
|
netRisk: number;
|
|
86
187
|
notARisks: RiskType[];
|
|
87
188
|
};
|
|
88
189
|
apyMethodology?: string;
|
|
190
|
+
realizedAPYMethodology?: string;
|
|
89
191
|
additionalInfo: T;
|
|
90
192
|
contractDetails: {
|
|
91
193
|
address: ContractAddr;
|
|
@@ -97,7 +199,30 @@ export interface IStrategyMetadata<T> {
|
|
|
97
199
|
docs?: string;
|
|
98
200
|
investmentSteps: string[];
|
|
99
201
|
curator?: { name: string, logo: string },
|
|
100
|
-
isPreview?: boolean
|
|
202
|
+
isPreview?: boolean;
|
|
203
|
+
tags?: StrategyTag[];
|
|
204
|
+
security: SecurityMetadata;
|
|
205
|
+
redemptionInfo: RedemptionInfo;
|
|
206
|
+
usualTimeToEarnings: null | string; // e.g. "2 weeks" // some strats grow like step functions
|
|
207
|
+
usualTimeToEarningsDescription: null | string; // e.g. "LSTs price on DEX goes up roughly every 2 weeks"
|
|
208
|
+
discontinuationInfo?: {
|
|
209
|
+
date?: Date;
|
|
210
|
+
reason?: React.ReactNode | string;
|
|
211
|
+
info?: React.ReactNode | string;
|
|
212
|
+
};
|
|
213
|
+
settings?: StrategySettings;
|
|
214
|
+
// Legacy field for multi-step strategies (deprecated, use investmentFlows instead)
|
|
215
|
+
actions?: Array<{
|
|
216
|
+
name?: string;
|
|
217
|
+
pool?: {
|
|
218
|
+
protocol?: { name: string; logo: string };
|
|
219
|
+
pool?: { name: string; logos?: string[] };
|
|
220
|
+
apr?: number;
|
|
221
|
+
borrow?: { apr?: number };
|
|
222
|
+
};
|
|
223
|
+
amount?: string | number;
|
|
224
|
+
isDeposit?: boolean;
|
|
225
|
+
}>;
|
|
101
226
|
}
|
|
102
227
|
|
|
103
228
|
export interface IInvestmentFlow {
|
|
@@ -124,6 +249,23 @@ export function getMainnetConfig(
|
|
|
124
249
|
};
|
|
125
250
|
}
|
|
126
251
|
|
|
252
|
+
export const getStrategyTagDesciption = (tag: StrategyTag): string => {
|
|
253
|
+
switch (tag) {
|
|
254
|
+
case StrategyTag.META_VAULT:
|
|
255
|
+
return "A meta vault is a vault that auto allocates funds to multiple vaults based on optimal yield opportunities";
|
|
256
|
+
case StrategyTag.LEVERED:
|
|
257
|
+
return "Looping vaults on Endur LSTs with leveraged borrowing of STRK or BTC to increase yield (2-4x higher yield than simply staking)";
|
|
258
|
+
case StrategyTag.AUTOMATED_LP:
|
|
259
|
+
return "Automated LP vaults on Ekubo that rebalance position automatically, ensuring you earn fees efficiently";
|
|
260
|
+
case StrategyTag.BTC:
|
|
261
|
+
return "BTC linked vaults";
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export const getAllStrategyTags = (): StrategyTag[] => {
|
|
266
|
+
return Object.values(StrategyTag);
|
|
267
|
+
}
|
|
268
|
+
|
|
127
269
|
export const getRiskExplaination = (riskType: RiskType) => {
|
|
128
270
|
switch (riskType) {
|
|
129
271
|
case RiskType.MARKET_RISK:
|
|
@@ -214,6 +356,30 @@ export interface VaultPosition {
|
|
|
214
356
|
remarks: string
|
|
215
357
|
}
|
|
216
358
|
|
|
359
|
+
export interface AmountInfo {
|
|
360
|
+
amount: Web3Number;
|
|
361
|
+
usdValue: number;
|
|
362
|
+
tokenInfo: TokenInfo;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export interface AmountsInfo {
|
|
366
|
+
usdValue: number;
|
|
367
|
+
amounts: AmountInfo[];
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Strategy capabilities interface
|
|
372
|
+
* Describes what optional methods a strategy instance supports
|
|
373
|
+
*/
|
|
374
|
+
export interface StrategyCapabilities {
|
|
375
|
+
hasMatchInputAmounts: boolean;
|
|
376
|
+
hasNetAPY: boolean;
|
|
377
|
+
hasGetInvestmentFlows: boolean;
|
|
378
|
+
hasGetPendingRewards: boolean;
|
|
379
|
+
hasHarvest: boolean;
|
|
380
|
+
hasRebalance: boolean;
|
|
381
|
+
}
|
|
382
|
+
|
|
217
383
|
const VesuProtocol: IProtocol = {
|
|
218
384
|
name: "Vesu",
|
|
219
385
|
logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ApolloClient, InMemoryCache } from '@apollo/client';
|
|
2
|
+
import { IConfig } from '@/interfaces';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates an Apollo Client instance configured for the appropriate environment
|
|
6
|
+
* @param config - The application config containing network and stage information
|
|
7
|
+
* @returns Configured Apollo Client instance
|
|
8
|
+
*/
|
|
9
|
+
export function createApolloClient(config: IConfig) {
|
|
10
|
+
// Determine the URI based on the environment
|
|
11
|
+
const uri = config.stage === 'production'
|
|
12
|
+
? 'https://api.troves.fi/'
|
|
13
|
+
: 'http://localhost:4000';
|
|
14
|
+
|
|
15
|
+
return new ApolloClient({
|
|
16
|
+
uri,
|
|
17
|
+
cache: new InMemoryCache(),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Default client for backward compatibility
|
|
22
|
+
const apolloClient = new ApolloClient({
|
|
23
|
+
uri: 'https://api.troves.fi/',
|
|
24
|
+
cache: new InMemoryCache(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default apolloClient;
|
|
28
|
+
|
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,
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Contract, RpcProvider, BlockIdentifier } from "starknet";
|
|
2
|
+
import EkuboPricerAbi from '@/data/ekubo-price-fethcer.abi.json';
|
|
3
|
+
import { PricerBase } from "./pricerBase";
|
|
4
|
+
import { IConfig, TokenInfo } from "@/interfaces";
|
|
5
|
+
import { PriceInfo } from "./pricer";
|
|
6
|
+
|
|
7
|
+
export class EkuboPricer extends PricerBase {
|
|
8
|
+
EKUBO_PRICE_FETCHER_ADDRESS = '0x04946fb4ad5237d97bbb1256eba2080c4fe1de156da6a7f83e3b4823bb6d7da1';
|
|
9
|
+
readonly contract: Contract;
|
|
10
|
+
private readonly USDC_ADDRESS = '0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8';
|
|
11
|
+
private readonly USDC_DECIMALS = 6;
|
|
12
|
+
|
|
13
|
+
constructor(config: IConfig, tokens: TokenInfo[]) {
|
|
14
|
+
super(config, tokens);
|
|
15
|
+
this.contract = new Contract({
|
|
16
|
+
abi: EkuboPricerAbi,
|
|
17
|
+
address: this.EKUBO_PRICE_FETCHER_ADDRESS,
|
|
18
|
+
providerOrAccount: config.provider as RpcProvider
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private div2Power128(num: bigint): number {
|
|
23
|
+
return Number((num * BigInt(1e18)) / BigInt(2 ** 128)) / 1e18;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getPrice(tokenAddr: string, blockIdentifier: BlockIdentifier = 'latest'): Promise<PriceInfo> {
|
|
27
|
+
if (!tokenAddr) {
|
|
28
|
+
throw new Error(`EkuboPricer:getPrice - no token`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// get_prices arguments in order:
|
|
32
|
+
// - quote_token: USDC address (quote token for price calculation)
|
|
33
|
+
// - base_tokens: array containing the base token address/addresses
|
|
34
|
+
// - period: time period in seconds for TWAP (3600 = 1 hour)
|
|
35
|
+
// - min_token: minimum token amount threshold (min liquidity) in 6 Decimals = 1000000)
|
|
36
|
+
const result: any = await this.contract.call(
|
|
37
|
+
'get_prices',
|
|
38
|
+
[this.USDC_ADDRESS, [tokenAddr], 3600, 1000000],
|
|
39
|
+
{ blockIdentifier }
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (!result || result.length === 0) {
|
|
43
|
+
throw new Error(`EkuboPricer: No price result returned for ${tokenAddr}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const priceResult = result[0];
|
|
47
|
+
|
|
48
|
+
if (!priceResult?.variant?.Price) {
|
|
49
|
+
const variant = priceResult?.variant ? Object.keys(priceResult.variant)[0] : 'Unknown';
|
|
50
|
+
throw new Error(`EkuboPricer: Price fetch failed with variant: ${variant}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const rawPrice = typeof priceResult.variant.Price === 'string'
|
|
54
|
+
? BigInt(priceResult.variant.Price)
|
|
55
|
+
: priceResult.variant.Price;
|
|
56
|
+
|
|
57
|
+
// Get token info to determine decimals from configured tokens
|
|
58
|
+
const tokenInfo = this.tokens.find(t =>
|
|
59
|
+
t.address.address.toLowerCase() === tokenAddr.toLowerCase()
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (!tokenInfo) {
|
|
63
|
+
throw new Error(`Token ${tokenAddr} not found in global tokens`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Convert from x128 format
|
|
67
|
+
const priceAfterX128 = this.div2Power128(rawPrice);
|
|
68
|
+
|
|
69
|
+
// Adjust for token decimals
|
|
70
|
+
const decimalAdjustment = 10 ** (tokenInfo.decimals - this.USDC_DECIMALS);
|
|
71
|
+
const price = priceAfterX128 * decimalAdjustment;
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
price,
|
|
75
|
+
timestamp: new Date()
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
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
|
}
|
package/src/modules/pragma.ts
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
|
-
import { Contract, RpcProvider } from "starknet";
|
|
1
|
+
import { Contract, RpcProvider, BlockIdentifier } from "starknet";
|
|
2
2
|
import PragmaAbi from '@/data/pragma.abi.json';
|
|
3
3
|
import { logger } from "@/utils/logger";
|
|
4
|
+
import { PricerBase } from "./pricerBase";
|
|
5
|
+
import { IConfig, TokenInfo } from "@/interfaces";
|
|
6
|
+
import { PriceInfo } from "./pricer";
|
|
4
7
|
|
|
5
|
-
export class Pragma {
|
|
8
|
+
export class Pragma extends PricerBase {
|
|
6
9
|
contractAddr = '0x023fb3afbff2c0e3399f896dcf7400acf1a161941cfb386e34a123f228c62832';
|
|
7
10
|
readonly contract: Contract;
|
|
8
11
|
|
|
9
|
-
constructor(
|
|
10
|
-
|
|
12
|
+
constructor(config: IConfig, tokens: TokenInfo[]) {
|
|
13
|
+
super(config, tokens);
|
|
14
|
+
this.contract = new Contract({
|
|
15
|
+
abi: PragmaAbi,
|
|
16
|
+
address: this.contractAddr,
|
|
17
|
+
providerOrAccount: config.provider as RpcProvider
|
|
18
|
+
});
|
|
11
19
|
}
|
|
12
20
|
|
|
13
|
-
async getPrice(tokenAddr: string) {
|
|
21
|
+
async getPrice(tokenAddr: string, blockIdentifier: BlockIdentifier = 'latest'): Promise<PriceInfo> {
|
|
14
22
|
if (!tokenAddr) {
|
|
15
23
|
throw new Error(`Pragma:getPrice - no token`)
|
|
16
24
|
}
|
|
17
|
-
const result: any = await this.contract.call(
|
|
25
|
+
const result: any = await this.contract.call(
|
|
26
|
+
'get_price',
|
|
27
|
+
[tokenAddr],
|
|
28
|
+
{ blockIdentifier }
|
|
29
|
+
);
|
|
18
30
|
const price = Number(result.price) / 10**8;
|
|
19
|
-
logger.verbose(`Pragma:${tokenAddr}: ${price}`);
|
|
20
|
-
return
|
|
31
|
+
logger.verbose(`Pragma:${tokenAddr}: ${price} at block ${blockIdentifier}`);
|
|
32
|
+
return {
|
|
33
|
+
price,
|
|
34
|
+
timestamp: new Date()
|
|
35
|
+
};
|
|
21
36
|
}
|
|
22
37
|
}
|