@strkfarm/sdk 1.0.16 → 1.0.17
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 +18 -5
- package/dist/cli.mjs +18 -11
- package/dist/index.browser.global.js +111 -2348
- package/dist/index.browser.mjs +2582 -0
- package/dist/index.d.ts +82 -42
- package/dist/index.js +117 -19
- package/dist/index.mjs +115 -25
- package/package.json +14 -8
- package/src/global.ts +2 -0
- package/src/interfaces/common.ts +34 -2
- package/src/modules/index.ts +2 -1
- package/src/modules/pricer-from-api.ts +61 -0
- package/src/modules/pricer.ts +7 -19
- package/src/modules/pricerBase.ts +15 -0
- package/src/modules/zkLend.ts +1 -0
- package/src/node/pricer-redis.ts +1 -0
- package/src/strategies/vesu-rebalance.ts +40 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import BigNumber from 'bignumber.js';
|
|
1
2
|
import * as starknet from 'starknet';
|
|
2
3
|
import { RpcProvider, BlockIdentifier, Contract, Account } from 'starknet';
|
|
3
|
-
import BigNumber from 'bignumber.js';
|
|
4
4
|
import * as util from 'util';
|
|
5
5
|
import TelegramBot from 'node-telegram-bot-api';
|
|
6
6
|
|
|
@@ -30,11 +30,25 @@ declare class ContractAddr {
|
|
|
30
30
|
static eqString(a: string, b: string): boolean;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
declare enum RiskType {
|
|
34
|
+
MARKET_RISK = "MARKET_RISK",
|
|
35
|
+
IMPERMANENT_LOSS = "IMPERMANENT_LOSS",
|
|
36
|
+
LIQUIDITY_RISK = "LIQUIDITY_RISK",
|
|
37
|
+
SMART_CONTRACT_RISK = "SMART_CONTRACT_RISK",
|
|
38
|
+
TECHNICAL_RISK = "TECHNICAL_RISK",
|
|
39
|
+
COUNTERPARTY_RISK = "COUNTERPARTY_RISK"
|
|
40
|
+
}
|
|
41
|
+
interface RiskFactor {
|
|
42
|
+
type: RiskType;
|
|
43
|
+
value: number;
|
|
44
|
+
weight: number;
|
|
45
|
+
}
|
|
33
46
|
interface TokenInfo {
|
|
34
47
|
name: string;
|
|
35
48
|
symbol: string;
|
|
36
49
|
address: string;
|
|
37
50
|
decimals: number;
|
|
51
|
+
logo: string;
|
|
38
52
|
coingeckId?: string;
|
|
39
53
|
}
|
|
40
54
|
declare enum Network {
|
|
@@ -52,6 +66,10 @@ interface IProtocol {
|
|
|
52
66
|
name: string;
|
|
53
67
|
logo: string;
|
|
54
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* @property risk.riskFactor.factor - The risk factors that are considered for the strategy.
|
|
71
|
+
* @property risk.riskFactor.factor - The value of the risk factor from 0 to 10, 0 being the lowest and 10 being the highest.
|
|
72
|
+
*/
|
|
55
73
|
interface IStrategyMetadata {
|
|
56
74
|
name: string;
|
|
57
75
|
description: string;
|
|
@@ -59,49 +77,19 @@ interface IStrategyMetadata {
|
|
|
59
77
|
type: 'ERC4626' | 'ERC721' | 'Other';
|
|
60
78
|
depositTokens: TokenInfo[];
|
|
61
79
|
protocols: IProtocol[];
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
timestamp: Date;
|
|
68
|
-
}
|
|
69
|
-
declare abstract class PricerBase {
|
|
70
|
-
getPrice(tokenSymbol: string): Promise<PriceInfo>;
|
|
71
|
-
}
|
|
72
|
-
declare class Pricer implements PricerBase {
|
|
73
|
-
readonly config: IConfig;
|
|
74
|
-
readonly tokens: TokenInfo[];
|
|
75
|
-
protected prices: {
|
|
76
|
-
[key: string]: PriceInfo;
|
|
80
|
+
auditUrl?: string;
|
|
81
|
+
maxTVL: Web3Number;
|
|
82
|
+
risk: {
|
|
83
|
+
riskFactor: RiskFactor[];
|
|
84
|
+
netRisk: number;
|
|
77
85
|
};
|
|
78
|
-
private methodToUse;
|
|
79
|
-
/**
|
|
80
|
-
* TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
|
|
81
|
-
*/
|
|
82
|
-
protected PRICE_API: string;
|
|
83
|
-
protected EKUBO_API: string;
|
|
84
|
-
protected client: any;
|
|
85
|
-
constructor(config: IConfig, tokens: TokenInfo[]);
|
|
86
|
-
isReady(): boolean;
|
|
87
|
-
waitTillReady(): Promise<void>;
|
|
88
|
-
start(): void;
|
|
89
|
-
isStale(timestamp: Date, tokenName: string): boolean;
|
|
90
|
-
assertNotStale(timestamp: Date, tokenName: string): void;
|
|
91
|
-
getPrice(tokenSymbol: string): Promise<PriceInfo>;
|
|
92
|
-
protected _loadPrices(onUpdate?: (tokenSymbol: string) => void): void;
|
|
93
|
-
_getPrice(token: TokenInfo, defaultMethod?: string): Promise<number>;
|
|
94
|
-
_getPriceCoinbase(token: TokenInfo): Promise<number>;
|
|
95
|
-
_getPriceCoinMarketCap(token: TokenInfo): Promise<number>;
|
|
96
|
-
_getPriceEkubo(token: TokenInfo, amountIn?: Web3Number, retry?: number): Promise<number>;
|
|
97
86
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
constructor(provider: RpcProvider);
|
|
103
|
-
getPrice(tokenAddr: string): Promise<number>;
|
|
87
|
+
interface IInvestmentFlow {
|
|
88
|
+
title: string;
|
|
89
|
+
subItems: string[];
|
|
90
|
+
linkedFlows: IInvestmentFlow[];
|
|
104
91
|
}
|
|
92
|
+
declare function getMainnetConfig(rpcUrl?: string, blockIdentifier?: BlockIdentifier): IConfig;
|
|
105
93
|
|
|
106
94
|
interface ILendingMetadata {
|
|
107
95
|
name: string;
|
|
@@ -155,6 +143,48 @@ declare abstract class Initializable {
|
|
|
155
143
|
waitForInitilisation(): Promise<void>;
|
|
156
144
|
}
|
|
157
145
|
|
|
146
|
+
declare abstract class PricerBase {
|
|
147
|
+
readonly config: IConfig;
|
|
148
|
+
readonly tokens: TokenInfo[];
|
|
149
|
+
constructor(config: IConfig, tokens: TokenInfo[]);
|
|
150
|
+
getPrice(tokenSymbol: string): Promise<PriceInfo>;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface PriceInfo {
|
|
154
|
+
price: number;
|
|
155
|
+
timestamp: Date;
|
|
156
|
+
}
|
|
157
|
+
declare class Pricer extends PricerBase {
|
|
158
|
+
protected prices: {
|
|
159
|
+
[key: string]: PriceInfo;
|
|
160
|
+
};
|
|
161
|
+
private methodToUse;
|
|
162
|
+
/**
|
|
163
|
+
* TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
|
|
164
|
+
*/
|
|
165
|
+
protected PRICE_API: string;
|
|
166
|
+
protected EKUBO_API: string;
|
|
167
|
+
constructor(config: IConfig, tokens: TokenInfo[]);
|
|
168
|
+
isReady(): boolean;
|
|
169
|
+
waitTillReady(): Promise<void>;
|
|
170
|
+
start(): void;
|
|
171
|
+
isStale(timestamp: Date, tokenName: string): boolean;
|
|
172
|
+
assertNotStale(timestamp: Date, tokenName: string): void;
|
|
173
|
+
getPrice(tokenSymbol: string): Promise<PriceInfo>;
|
|
174
|
+
protected _loadPrices(onUpdate?: (tokenSymbol: string) => void): void;
|
|
175
|
+
_getPrice(token: TokenInfo, defaultMethod?: string): Promise<number>;
|
|
176
|
+
_getPriceCoinbase(token: TokenInfo): Promise<number>;
|
|
177
|
+
_getPriceCoinMarketCap(token: TokenInfo): Promise<number>;
|
|
178
|
+
_getPriceEkubo(token: TokenInfo, amountIn?: Web3Number, retry?: number): Promise<number>;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
declare class Pragma {
|
|
182
|
+
contractAddr: string;
|
|
183
|
+
readonly contract: Contract;
|
|
184
|
+
constructor(provider: RpcProvider);
|
|
185
|
+
getPrice(tokenAddr: string): Promise<number>;
|
|
186
|
+
}
|
|
187
|
+
|
|
158
188
|
declare class ZkLend extends ILending implements ILending {
|
|
159
189
|
readonly pricer: Pricer;
|
|
160
190
|
static readonly POOLS_URL = "https://app.zklend.com/api/pools";
|
|
@@ -186,6 +216,15 @@ declare class ZkLend extends ILending implements ILending {
|
|
|
186
216
|
getPositions(user: ContractAddr): Promise<ILendingPosition[]>;
|
|
187
217
|
}
|
|
188
218
|
|
|
219
|
+
declare class PricerFromApi extends PricerBase {
|
|
220
|
+
constructor(config: IConfig, tokens: TokenInfo[]);
|
|
221
|
+
getPrice(tokenSymbol: string): Promise<PriceInfo>;
|
|
222
|
+
getPriceFromMyAPI(tokenSymbol: string): Promise<{
|
|
223
|
+
price: number;
|
|
224
|
+
timestamp: Date;
|
|
225
|
+
}>;
|
|
226
|
+
}
|
|
227
|
+
|
|
189
228
|
declare const logger: {
|
|
190
229
|
verbose(message: string): void;
|
|
191
230
|
assert(condition?: boolean, ...data: any[]): void;
|
|
@@ -441,6 +480,7 @@ declare class VesuRebalance {
|
|
|
441
480
|
* @returns Populated contract call for rebalance
|
|
442
481
|
*/
|
|
443
482
|
getRebalanceCall(pools: Awaited<ReturnType<typeof this.getRebalancedPositions>>['changes'], isOverWeightAdjustment: boolean): Promise<starknet.Call | null>;
|
|
483
|
+
getInvestmentFlows(pools: PoolInfoFull[]): Promise<IInvestmentFlow[]>;
|
|
444
484
|
}
|
|
445
485
|
/**
|
|
446
486
|
* Represents the Vesu Rebalance Strategies.
|
|
@@ -554,4 +594,4 @@ declare class PasswordJsonCryptoUtil {
|
|
|
554
594
|
decrypt(encryptedData: string, password: string): any;
|
|
555
595
|
}
|
|
556
596
|
|
|
557
|
-
export { type AccountInfo, type AllAccountsStore, AutoCompounderSTRK, ContractAddr, FatalError, Global, type IConfig, ILending, type ILendingMetadata, type ILendingPosition, type IProtocol, type IStrategyMetadata, Initializable, type LendingToken, MarginType, Network, PasswordJsonCryptoUtil, Pragma, type PriceInfo, Pricer,
|
|
597
|
+
export { type AccountInfo, type AllAccountsStore, AutoCompounderSTRK, ContractAddr, FatalError, Global, type IConfig, type IInvestmentFlow, ILending, type ILendingMetadata, type ILendingPosition, type IProtocol, type IStrategyMetadata, Initializable, type LendingToken, MarginType, Network, PasswordJsonCryptoUtil, Pragma, type PriceInfo, Pricer, PricerFromApi, PricerRedis, type RequiredFields, type RequiredKeys, type RequiredStoreConfig, type RiskFactor, RiskType, Store, type StoreConfig, TelegramNotif, type TokenInfo, VesuRebalance, VesuRebalanceStrategies, Web3Number, ZkLend, assert, getDefaultStoreConfig, getMainnetConfig, logger };
|
package/dist/index.js
CHANGED
|
@@ -41,8 +41,9 @@ __export(src_exports, {
|
|
|
41
41
|
PasswordJsonCryptoUtil: () => PasswordJsonCryptoUtil,
|
|
42
42
|
Pragma: () => Pragma,
|
|
43
43
|
Pricer: () => Pricer,
|
|
44
|
-
|
|
44
|
+
PricerFromApi: () => PricerFromApi,
|
|
45
45
|
PricerRedis: () => PricerRedis,
|
|
46
|
+
RiskType: () => RiskType,
|
|
46
47
|
Store: () => Store,
|
|
47
48
|
TelegramNotif: () => TelegramNotif,
|
|
48
49
|
VesuRebalance: () => VesuRebalance,
|
|
@@ -79,6 +80,7 @@ var FatalError = class extends Error {
|
|
|
79
80
|
var tokens = [{
|
|
80
81
|
name: "Starknet",
|
|
81
82
|
symbol: "STRK",
|
|
83
|
+
logo: "https://assets.coingecko.com/coins/images/26433/small/starknet.png",
|
|
82
84
|
address: "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
|
|
83
85
|
decimals: 18,
|
|
84
86
|
coingeckId: "starknet"
|
|
@@ -111,6 +113,7 @@ var Global = class {
|
|
|
111
113
|
symbol: token.symbol,
|
|
112
114
|
address: token.address,
|
|
113
115
|
decimals: token.decimals,
|
|
116
|
+
logo: token.logoUri,
|
|
114
117
|
coingeckId: token.extensions.coingeckoId
|
|
115
118
|
});
|
|
116
119
|
});
|
|
@@ -190,16 +193,22 @@ var ContractAddr = class _ContractAddr {
|
|
|
190
193
|
}
|
|
191
194
|
};
|
|
192
195
|
|
|
193
|
-
// src/modules/
|
|
194
|
-
var CoinMarketCap = require("coinmarketcap-api");
|
|
196
|
+
// src/modules/pricerBase.ts
|
|
195
197
|
var PricerBase = class {
|
|
198
|
+
constructor(config, tokens2) {
|
|
199
|
+
this.config = config;
|
|
200
|
+
this.tokens = tokens2;
|
|
201
|
+
}
|
|
196
202
|
async getPrice(tokenSymbol) {
|
|
197
203
|
throw new Error("Method not implemented");
|
|
198
204
|
}
|
|
199
205
|
};
|
|
200
|
-
|
|
206
|
+
|
|
207
|
+
// src/modules/pricer.ts
|
|
208
|
+
var Pricer = class extends PricerBase {
|
|
209
|
+
// e.g. ETH/USDC
|
|
201
210
|
constructor(config, tokens2) {
|
|
202
|
-
|
|
211
|
+
super(config, tokens2);
|
|
203
212
|
this.prices = {};
|
|
204
213
|
// code populates this map during runtime to determine which method to use for a given token
|
|
205
214
|
// The method set will be the first one to try after first attempt
|
|
@@ -209,11 +218,6 @@ var Pricer = class {
|
|
|
209
218
|
*/
|
|
210
219
|
this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
|
|
211
220
|
this.EKUBO_API = "https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
|
|
212
|
-
// e.g. ETH/USDC
|
|
213
|
-
// backup oracle001
|
|
214
|
-
this.client = new CoinMarketCap(process.env.COINMARKETCAP_KEY);
|
|
215
|
-
this.config = config;
|
|
216
|
-
this.tokens = tokens2;
|
|
217
221
|
}
|
|
218
222
|
isReady() {
|
|
219
223
|
const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
|
|
@@ -345,10 +349,7 @@ var Pricer = class {
|
|
|
345
349
|
return Number(data.data.amount);
|
|
346
350
|
}
|
|
347
351
|
async _getPriceCoinMarketCap(token) {
|
|
348
|
-
|
|
349
|
-
if (result.data)
|
|
350
|
-
return result.data[token.symbol].quote.USD.price;
|
|
351
|
-
throw new Error(result);
|
|
352
|
+
throw new Error("Not implemented");
|
|
352
353
|
}
|
|
353
354
|
async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
|
|
354
355
|
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
|
|
@@ -542,6 +543,7 @@ var _ZkLend = class _ZkLend extends ILending {
|
|
|
542
543
|
name: pool.token.name,
|
|
543
544
|
symbol: pool.token.symbol,
|
|
544
545
|
address: savedTokenInfo?.address || "",
|
|
546
|
+
logo: "",
|
|
545
547
|
decimals: pool.token.decimals,
|
|
546
548
|
borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
|
|
547
549
|
collareralFactor
|
|
@@ -651,8 +653,72 @@ var _ZkLend = class _ZkLend extends ILending {
|
|
|
651
653
|
_ZkLend.POOLS_URL = "https://app.zklend.com/api/pools";
|
|
652
654
|
var ZkLend = _ZkLend;
|
|
653
655
|
|
|
656
|
+
// src/modules/pricer-from-api.ts
|
|
657
|
+
var import_axios4 = __toESM(require("axios"));
|
|
658
|
+
var PricerFromApi = class extends PricerBase {
|
|
659
|
+
constructor(config, tokens2) {
|
|
660
|
+
super(config, tokens2);
|
|
661
|
+
}
|
|
662
|
+
async getPrice(tokenSymbol) {
|
|
663
|
+
try {
|
|
664
|
+
return await this.getPriceFromMyAPI(tokenSymbol);
|
|
665
|
+
} catch (e) {
|
|
666
|
+
logger.warn("getPriceFromMyAPI error", e);
|
|
667
|
+
}
|
|
668
|
+
logger.log("getPrice coinbase", tokenSymbol);
|
|
669
|
+
let retry = 0;
|
|
670
|
+
const MAX_RETRIES = 5;
|
|
671
|
+
for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
|
|
672
|
+
try {
|
|
673
|
+
const priceInfo = await import_axios4.default.get(
|
|
674
|
+
`https://api.coinbase.com/v2/prices/${tokenSymbol}-USDT/spot`
|
|
675
|
+
);
|
|
676
|
+
if (!priceInfo) {
|
|
677
|
+
throw new Error("Failed to fetch price");
|
|
678
|
+
}
|
|
679
|
+
const data = await priceInfo.data;
|
|
680
|
+
const price = Number(data.data.amount);
|
|
681
|
+
return {
|
|
682
|
+
price,
|
|
683
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
684
|
+
};
|
|
685
|
+
} catch (e) {
|
|
686
|
+
logger.warn("getPrice coinbase error", e, retry);
|
|
687
|
+
await new Promise((resolve) => setTimeout(resolve, retry * 1e3));
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
throw new Error(`Failed to fetch price for ${tokenSymbol}`);
|
|
691
|
+
}
|
|
692
|
+
async getPriceFromMyAPI(tokenSymbol) {
|
|
693
|
+
logger.verbose(`getPrice from redis: ${tokenSymbol}`);
|
|
694
|
+
const endpoint = "https://app.strkfarm.com";
|
|
695
|
+
const url = `${endpoint}/api/price/${tokenSymbol}`;
|
|
696
|
+
const priceInfoRes = await fetch(url);
|
|
697
|
+
const priceInfo = await priceInfoRes.json();
|
|
698
|
+
const now = /* @__PURE__ */ new Date();
|
|
699
|
+
const priceTime = new Date(priceInfo.timestamp);
|
|
700
|
+
if (now.getTime() - priceTime.getTime() > 9e5) {
|
|
701
|
+
throw new Error("Price is stale");
|
|
702
|
+
}
|
|
703
|
+
const price = Number(priceInfo.price);
|
|
704
|
+
return {
|
|
705
|
+
price,
|
|
706
|
+
timestamp: new Date(priceInfo.timestamp)
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
654
711
|
// src/interfaces/common.ts
|
|
655
712
|
var import_starknet3 = require("starknet");
|
|
713
|
+
var RiskType = /* @__PURE__ */ ((RiskType2) => {
|
|
714
|
+
RiskType2["MARKET_RISK"] = "MARKET_RISK";
|
|
715
|
+
RiskType2["IMPERMANENT_LOSS"] = "IMPERMANENT_LOSS";
|
|
716
|
+
RiskType2["LIQUIDITY_RISK"] = "LIQUIDITY_RISK";
|
|
717
|
+
RiskType2["SMART_CONTRACT_RISK"] = "SMART_CONTRACT_RISK";
|
|
718
|
+
RiskType2["TECHNICAL_RISK"] = "TECHNICAL_RISK";
|
|
719
|
+
RiskType2["COUNTERPARTY_RISK"] = "COUNTERPARTY_RISK";
|
|
720
|
+
return RiskType2;
|
|
721
|
+
})(RiskType || {});
|
|
656
722
|
var Network = /* @__PURE__ */ ((Network2) => {
|
|
657
723
|
Network2["mainnet"] = "mainnet";
|
|
658
724
|
Network2["sepolia"] = "sepolia";
|
|
@@ -2234,7 +2300,7 @@ function assert(condition, message) {
|
|
|
2234
2300
|
}
|
|
2235
2301
|
|
|
2236
2302
|
// src/strategies/vesu-rebalance.ts
|
|
2237
|
-
var
|
|
2303
|
+
var import_axios5 = __toESM(require("axios"));
|
|
2238
2304
|
var VesuRebalance = class _VesuRebalance {
|
|
2239
2305
|
// 10000 bps = 100%
|
|
2240
2306
|
/**
|
|
@@ -2341,7 +2407,7 @@ var VesuRebalance = class _VesuRebalance {
|
|
|
2341
2407
|
let isErrorPositionsAPI = false;
|
|
2342
2408
|
let vesuPositions = [];
|
|
2343
2409
|
try {
|
|
2344
|
-
const res = await
|
|
2410
|
+
const res = await import_axios5.default.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
|
|
2345
2411
|
const data2 = await res.data;
|
|
2346
2412
|
vesuPositions = data2.data;
|
|
2347
2413
|
} catch (e) {
|
|
@@ -2351,7 +2417,7 @@ var VesuRebalance = class _VesuRebalance {
|
|
|
2351
2417
|
let isErrorPoolsAPI = false;
|
|
2352
2418
|
let pools = [];
|
|
2353
2419
|
try {
|
|
2354
|
-
const res = await
|
|
2420
|
+
const res = await import_axios5.default.get(`https://api.vesu.xyz/pools`);
|
|
2355
2421
|
const data2 = await res.data;
|
|
2356
2422
|
pools = data2.data;
|
|
2357
2423
|
} catch (e) {
|
|
@@ -2511,16 +2577,47 @@ var VesuRebalance = class _VesuRebalance {
|
|
|
2511
2577
|
}
|
|
2512
2578
|
return this.contract.populate("rebalance", [actions]);
|
|
2513
2579
|
}
|
|
2580
|
+
async getInvestmentFlows(pools) {
|
|
2581
|
+
const netYield = this.netAPYGivenPools(pools);
|
|
2582
|
+
const baseFlow = {
|
|
2583
|
+
title: "Deposit $1000",
|
|
2584
|
+
subItems: [`Net yield: ${(netYield * 100).toFixed(2)}%`],
|
|
2585
|
+
linkedFlows: []
|
|
2586
|
+
};
|
|
2587
|
+
pools.forEach((p) => {
|
|
2588
|
+
if (p.amount.eq(0)) return;
|
|
2589
|
+
const flow = {
|
|
2590
|
+
title: `${p.pool_name} - $${(p.current_weight * 1e3).toFixed(2)}`,
|
|
2591
|
+
subItems: [
|
|
2592
|
+
`APY: ${(p.APY.netApy * 100).toFixed(2)}%`,
|
|
2593
|
+
`Weight: ${(p.current_weight * 100).toFixed(2)}% / ${(p.max_weight * 100).toFixed(2)}%`
|
|
2594
|
+
],
|
|
2595
|
+
linkedFlows: []
|
|
2596
|
+
};
|
|
2597
|
+
baseFlow.linkedFlows.push(flow);
|
|
2598
|
+
});
|
|
2599
|
+
return [baseFlow];
|
|
2600
|
+
}
|
|
2514
2601
|
};
|
|
2515
2602
|
var _description = "Automatically diversify {{TOKEN}} holdings into different Vesu pools while reducing risk and maximizing yield. Defi spring STRK Rewards are auto-compounded as well.";
|
|
2516
2603
|
var _protocol = { name: "Vesu", logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png" };
|
|
2604
|
+
var _riskFactor = [
|
|
2605
|
+
{ type: "SMART_CONTRACT_RISK" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
2606
|
+
{ type: "TECHNICAL_RISK" /* TECHNICAL_RISK */, value: 0.5, weight: 25 },
|
|
2607
|
+
{ type: "COUNTERPARTY_RISK" /* COUNTERPARTY_RISK */, value: 1, weight: 50 }
|
|
2608
|
+
];
|
|
2517
2609
|
var VesuRebalanceStrategies = [{
|
|
2518
2610
|
name: "Vesu STRK",
|
|
2519
2611
|
description: _description.replace("{{TOKEN}}", "STRK"),
|
|
2520
2612
|
address: ContractAddr.from("0xeeb729d554ae486387147b13a9c8871bc7991d454e8b5ff570d4bf94de71e1"),
|
|
2521
2613
|
type: "ERC4626",
|
|
2522
2614
|
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
2523
|
-
protocols: [_protocol]
|
|
2615
|
+
protocols: [_protocol],
|
|
2616
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
2617
|
+
risk: {
|
|
2618
|
+
riskFactor: _riskFactor,
|
|
2619
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / 100
|
|
2620
|
+
}
|
|
2524
2621
|
}];
|
|
2525
2622
|
|
|
2526
2623
|
// src/notifs/telegram.ts
|
|
@@ -2797,8 +2894,9 @@ var Store = class _Store {
|
|
|
2797
2894
|
PasswordJsonCryptoUtil,
|
|
2798
2895
|
Pragma,
|
|
2799
2896
|
Pricer,
|
|
2800
|
-
|
|
2897
|
+
PricerFromApi,
|
|
2801
2898
|
PricerRedis,
|
|
2899
|
+
RiskType,
|
|
2802
2900
|
Store,
|
|
2803
2901
|
TelegramNotif,
|
|
2804
2902
|
VesuRebalance,
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/modules/pricer.ts
|
|
9
2
|
import axios2 from "axios";
|
|
10
3
|
|
|
@@ -28,6 +21,7 @@ var FatalError = class extends Error {
|
|
|
28
21
|
var tokens = [{
|
|
29
22
|
name: "Starknet",
|
|
30
23
|
symbol: "STRK",
|
|
24
|
+
logo: "https://assets.coingecko.com/coins/images/26433/small/starknet.png",
|
|
31
25
|
address: "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
|
|
32
26
|
decimals: 18,
|
|
33
27
|
coingeckId: "starknet"
|
|
@@ -60,6 +54,7 @@ var Global = class {
|
|
|
60
54
|
symbol: token.symbol,
|
|
61
55
|
address: token.address,
|
|
62
56
|
decimals: token.decimals,
|
|
57
|
+
logo: token.logoUri,
|
|
63
58
|
coingeckId: token.extensions.coingeckoId
|
|
64
59
|
});
|
|
65
60
|
});
|
|
@@ -139,16 +134,22 @@ var ContractAddr = class _ContractAddr {
|
|
|
139
134
|
}
|
|
140
135
|
};
|
|
141
136
|
|
|
142
|
-
// src/modules/
|
|
143
|
-
var CoinMarketCap = __require("coinmarketcap-api");
|
|
137
|
+
// src/modules/pricerBase.ts
|
|
144
138
|
var PricerBase = class {
|
|
139
|
+
constructor(config, tokens2) {
|
|
140
|
+
this.config = config;
|
|
141
|
+
this.tokens = tokens2;
|
|
142
|
+
}
|
|
145
143
|
async getPrice(tokenSymbol) {
|
|
146
144
|
throw new Error("Method not implemented");
|
|
147
145
|
}
|
|
148
146
|
};
|
|
149
|
-
|
|
147
|
+
|
|
148
|
+
// src/modules/pricer.ts
|
|
149
|
+
var Pricer = class extends PricerBase {
|
|
150
|
+
// e.g. ETH/USDC
|
|
150
151
|
constructor(config, tokens2) {
|
|
151
|
-
|
|
152
|
+
super(config, tokens2);
|
|
152
153
|
this.prices = {};
|
|
153
154
|
// code populates this map during runtime to determine which method to use for a given token
|
|
154
155
|
// The method set will be the first one to try after first attempt
|
|
@@ -158,11 +159,6 @@ var Pricer = class {
|
|
|
158
159
|
*/
|
|
159
160
|
this.PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
|
|
160
161
|
this.EKUBO_API = "https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";
|
|
161
|
-
// e.g. ETH/USDC
|
|
162
|
-
// backup oracle001
|
|
163
|
-
this.client = new CoinMarketCap(process.env.COINMARKETCAP_KEY);
|
|
164
|
-
this.config = config;
|
|
165
|
-
this.tokens = tokens2;
|
|
166
162
|
}
|
|
167
163
|
isReady() {
|
|
168
164
|
const allPricesExist = Object.keys(this.prices).length === this.tokens.length;
|
|
@@ -294,10 +290,7 @@ var Pricer = class {
|
|
|
294
290
|
return Number(data.data.amount);
|
|
295
291
|
}
|
|
296
292
|
async _getPriceCoinMarketCap(token) {
|
|
297
|
-
|
|
298
|
-
if (result.data)
|
|
299
|
-
return result.data[token.symbol].quote.USD.price;
|
|
300
|
-
throw new Error(result);
|
|
293
|
+
throw new Error("Not implemented");
|
|
301
294
|
}
|
|
302
295
|
async _getPriceEkubo(token, amountIn = new Web3Number(1, token.decimals), retry = 0) {
|
|
303
296
|
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address).replace("{{AMOUNT}}", amountIn.toWei());
|
|
@@ -491,6 +484,7 @@ var _ZkLend = class _ZkLend extends ILending {
|
|
|
491
484
|
name: pool.token.name,
|
|
492
485
|
symbol: pool.token.symbol,
|
|
493
486
|
address: savedTokenInfo?.address || "",
|
|
487
|
+
logo: "",
|
|
494
488
|
decimals: pool.token.decimals,
|
|
495
489
|
borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
|
|
496
490
|
collareralFactor
|
|
@@ -600,8 +594,72 @@ var _ZkLend = class _ZkLend extends ILending {
|
|
|
600
594
|
_ZkLend.POOLS_URL = "https://app.zklend.com/api/pools";
|
|
601
595
|
var ZkLend = _ZkLend;
|
|
602
596
|
|
|
597
|
+
// src/modules/pricer-from-api.ts
|
|
598
|
+
import axios4 from "axios";
|
|
599
|
+
var PricerFromApi = class extends PricerBase {
|
|
600
|
+
constructor(config, tokens2) {
|
|
601
|
+
super(config, tokens2);
|
|
602
|
+
}
|
|
603
|
+
async getPrice(tokenSymbol) {
|
|
604
|
+
try {
|
|
605
|
+
return await this.getPriceFromMyAPI(tokenSymbol);
|
|
606
|
+
} catch (e) {
|
|
607
|
+
logger.warn("getPriceFromMyAPI error", e);
|
|
608
|
+
}
|
|
609
|
+
logger.log("getPrice coinbase", tokenSymbol);
|
|
610
|
+
let retry = 0;
|
|
611
|
+
const MAX_RETRIES = 5;
|
|
612
|
+
for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
|
|
613
|
+
try {
|
|
614
|
+
const priceInfo = await axios4.get(
|
|
615
|
+
`https://api.coinbase.com/v2/prices/${tokenSymbol}-USDT/spot`
|
|
616
|
+
);
|
|
617
|
+
if (!priceInfo) {
|
|
618
|
+
throw new Error("Failed to fetch price");
|
|
619
|
+
}
|
|
620
|
+
const data = await priceInfo.data;
|
|
621
|
+
const price = Number(data.data.amount);
|
|
622
|
+
return {
|
|
623
|
+
price,
|
|
624
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
625
|
+
};
|
|
626
|
+
} catch (e) {
|
|
627
|
+
logger.warn("getPrice coinbase error", e, retry);
|
|
628
|
+
await new Promise((resolve) => setTimeout(resolve, retry * 1e3));
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
throw new Error(`Failed to fetch price for ${tokenSymbol}`);
|
|
632
|
+
}
|
|
633
|
+
async getPriceFromMyAPI(tokenSymbol) {
|
|
634
|
+
logger.verbose(`getPrice from redis: ${tokenSymbol}`);
|
|
635
|
+
const endpoint = "https://app.strkfarm.com";
|
|
636
|
+
const url = `${endpoint}/api/price/${tokenSymbol}`;
|
|
637
|
+
const priceInfoRes = await fetch(url);
|
|
638
|
+
const priceInfo = await priceInfoRes.json();
|
|
639
|
+
const now = /* @__PURE__ */ new Date();
|
|
640
|
+
const priceTime = new Date(priceInfo.timestamp);
|
|
641
|
+
if (now.getTime() - priceTime.getTime() > 9e5) {
|
|
642
|
+
throw new Error("Price is stale");
|
|
643
|
+
}
|
|
644
|
+
const price = Number(priceInfo.price);
|
|
645
|
+
return {
|
|
646
|
+
price,
|
|
647
|
+
timestamp: new Date(priceInfo.timestamp)
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
|
|
603
652
|
// src/interfaces/common.ts
|
|
604
653
|
import { RpcProvider as RpcProvider2 } from "starknet";
|
|
654
|
+
var RiskType = /* @__PURE__ */ ((RiskType2) => {
|
|
655
|
+
RiskType2["MARKET_RISK"] = "MARKET_RISK";
|
|
656
|
+
RiskType2["IMPERMANENT_LOSS"] = "IMPERMANENT_LOSS";
|
|
657
|
+
RiskType2["LIQUIDITY_RISK"] = "LIQUIDITY_RISK";
|
|
658
|
+
RiskType2["SMART_CONTRACT_RISK"] = "SMART_CONTRACT_RISK";
|
|
659
|
+
RiskType2["TECHNICAL_RISK"] = "TECHNICAL_RISK";
|
|
660
|
+
RiskType2["COUNTERPARTY_RISK"] = "COUNTERPARTY_RISK";
|
|
661
|
+
return RiskType2;
|
|
662
|
+
})(RiskType || {});
|
|
605
663
|
var Network = /* @__PURE__ */ ((Network2) => {
|
|
606
664
|
Network2["mainnet"] = "mainnet";
|
|
607
665
|
Network2["sepolia"] = "sepolia";
|
|
@@ -2183,7 +2241,7 @@ function assert(condition, message) {
|
|
|
2183
2241
|
}
|
|
2184
2242
|
|
|
2185
2243
|
// src/strategies/vesu-rebalance.ts
|
|
2186
|
-
import
|
|
2244
|
+
import axios5 from "axios";
|
|
2187
2245
|
var VesuRebalance = class _VesuRebalance {
|
|
2188
2246
|
// 10000 bps = 100%
|
|
2189
2247
|
/**
|
|
@@ -2290,7 +2348,7 @@ var VesuRebalance = class _VesuRebalance {
|
|
|
2290
2348
|
let isErrorPositionsAPI = false;
|
|
2291
2349
|
let vesuPositions = [];
|
|
2292
2350
|
try {
|
|
2293
|
-
const res = await
|
|
2351
|
+
const res = await axios5.get(`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`);
|
|
2294
2352
|
const data2 = await res.data;
|
|
2295
2353
|
vesuPositions = data2.data;
|
|
2296
2354
|
} catch (e) {
|
|
@@ -2300,7 +2358,7 @@ var VesuRebalance = class _VesuRebalance {
|
|
|
2300
2358
|
let isErrorPoolsAPI = false;
|
|
2301
2359
|
let pools = [];
|
|
2302
2360
|
try {
|
|
2303
|
-
const res = await
|
|
2361
|
+
const res = await axios5.get(`https://api.vesu.xyz/pools`);
|
|
2304
2362
|
const data2 = await res.data;
|
|
2305
2363
|
pools = data2.data;
|
|
2306
2364
|
} catch (e) {
|
|
@@ -2460,16 +2518,47 @@ var VesuRebalance = class _VesuRebalance {
|
|
|
2460
2518
|
}
|
|
2461
2519
|
return this.contract.populate("rebalance", [actions]);
|
|
2462
2520
|
}
|
|
2521
|
+
async getInvestmentFlows(pools) {
|
|
2522
|
+
const netYield = this.netAPYGivenPools(pools);
|
|
2523
|
+
const baseFlow = {
|
|
2524
|
+
title: "Deposit $1000",
|
|
2525
|
+
subItems: [`Net yield: ${(netYield * 100).toFixed(2)}%`],
|
|
2526
|
+
linkedFlows: []
|
|
2527
|
+
};
|
|
2528
|
+
pools.forEach((p) => {
|
|
2529
|
+
if (p.amount.eq(0)) return;
|
|
2530
|
+
const flow = {
|
|
2531
|
+
title: `${p.pool_name} - $${(p.current_weight * 1e3).toFixed(2)}`,
|
|
2532
|
+
subItems: [
|
|
2533
|
+
`APY: ${(p.APY.netApy * 100).toFixed(2)}%`,
|
|
2534
|
+
`Weight: ${(p.current_weight * 100).toFixed(2)}% / ${(p.max_weight * 100).toFixed(2)}%`
|
|
2535
|
+
],
|
|
2536
|
+
linkedFlows: []
|
|
2537
|
+
};
|
|
2538
|
+
baseFlow.linkedFlows.push(flow);
|
|
2539
|
+
});
|
|
2540
|
+
return [baseFlow];
|
|
2541
|
+
}
|
|
2463
2542
|
};
|
|
2464
2543
|
var _description = "Automatically diversify {{TOKEN}} holdings into different Vesu pools while reducing risk and maximizing yield. Defi spring STRK Rewards are auto-compounded as well.";
|
|
2465
2544
|
var _protocol = { name: "Vesu", logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png" };
|
|
2545
|
+
var _riskFactor = [
|
|
2546
|
+
{ type: "SMART_CONTRACT_RISK" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
2547
|
+
{ type: "TECHNICAL_RISK" /* TECHNICAL_RISK */, value: 0.5, weight: 25 },
|
|
2548
|
+
{ type: "COUNTERPARTY_RISK" /* COUNTERPARTY_RISK */, value: 1, weight: 50 }
|
|
2549
|
+
];
|
|
2466
2550
|
var VesuRebalanceStrategies = [{
|
|
2467
2551
|
name: "Vesu STRK",
|
|
2468
2552
|
description: _description.replace("{{TOKEN}}", "STRK"),
|
|
2469
2553
|
address: ContractAddr.from("0xeeb729d554ae486387147b13a9c8871bc7991d454e8b5ff570d4bf94de71e1"),
|
|
2470
2554
|
type: "ERC4626",
|
|
2471
2555
|
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
2472
|
-
protocols: [_protocol]
|
|
2556
|
+
protocols: [_protocol],
|
|
2557
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
2558
|
+
risk: {
|
|
2559
|
+
riskFactor: _riskFactor,
|
|
2560
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / 100
|
|
2561
|
+
}
|
|
2473
2562
|
}];
|
|
2474
2563
|
|
|
2475
2564
|
// src/notifs/telegram.ts
|
|
@@ -2745,8 +2834,9 @@ export {
|
|
|
2745
2834
|
PasswordJsonCryptoUtil,
|
|
2746
2835
|
Pragma,
|
|
2747
2836
|
Pricer,
|
|
2748
|
-
|
|
2837
|
+
PricerFromApi,
|
|
2749
2838
|
PricerRedis,
|
|
2839
|
+
RiskType,
|
|
2750
2840
|
Store,
|
|
2751
2841
|
TelegramNotif,
|
|
2752
2842
|
VesuRebalance,
|