@strkfarm/sdk 1.2.0 → 2.0.0-dev-strategy2.1
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/index.browser.global.js +76556 -66640
- package/dist/index.browser.mjs +34235 -24392
- package/dist/index.d.ts +2372 -793
- package/dist/index.js +31967 -22084
- package/dist/index.mjs +25545 -15719
- package/package.json +86 -76
- package/readme.md +56 -1
- package/src/data/extended-deposit.abi.json +3613 -0
- package/src/data/universal-vault.abi.json +135 -20
- package/src/dataTypes/_bignumber.ts +11 -0
- package/src/dataTypes/address.ts +7 -0
- package/src/global.ts +240 -193
- package/src/interfaces/common.tsx +26 -2
- package/src/modules/ExtendedWrapperSDk/index.ts +62 -0
- package/src/modules/ExtendedWrapperSDk/types.ts +311 -0
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +448 -0
- package/src/modules/avnu.ts +17 -4
- package/src/modules/ekubo-quoter.ts +89 -10
- package/src/modules/erc20.ts +67 -21
- package/src/modules/harvests.ts +29 -43
- package/src/modules/index.ts +5 -1
- package/src/modules/lst-apr.ts +36 -0
- package/src/modules/midas.ts +159 -0
- package/src/modules/pricer-from-api.ts +2 -2
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +3 -38
- package/src/modules/token-market-data.ts +202 -0
- package/src/node/deployer.ts +1 -36
- package/src/strategies/autoCompounderStrk.ts +1 -1
- package/src/strategies/base-strategy.ts +20 -3
- package/src/strategies/btc-vesu-extended-strategy/core-strategy.tsx +1486 -0
- package/src/strategies/btc-vesu-extended-strategy/services/operationService.ts +32 -0
- package/src/strategies/btc-vesu-extended-strategy/utils/constants.ts +3 -0
- package/src/strategies/btc-vesu-extended-strategy/utils/helper.ts +396 -0
- package/src/strategies/btc-vesu-extended-strategy/utils/types.ts +5 -0
- package/src/strategies/ekubo-cl-vault.tsx +123 -306
- package/src/strategies/index.ts +7 -1
- package/src/strategies/svk-strategy.ts +247 -0
- package/src/strategies/universal-adapters/adapter-optimizer.ts +65 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +5 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +432 -0
- package/src/strategies/universal-adapters/baseAdapter.ts +181 -153
- package/src/strategies/universal-adapters/common-adapter.ts +98 -77
- package/src/strategies/universal-adapters/extended-adapter.ts +976 -0
- package/src/strategies/universal-adapters/index.ts +7 -1
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +109 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +230 -230
- package/src/strategies/universal-adapters/vesu-borrow-adapter.ts +1247 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +1306 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +58 -51
- package/src/strategies/universal-lst-muliplier-strategy.tsx +716 -844
- package/src/strategies/universal-strategy.tsx +1103 -1181
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +34 -0
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +25 -0
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +77 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +50 -0
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +367 -0
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +1420 -0
- package/src/strategies/vesu-rebalance.tsx +16 -20
- package/src/utils/health-factor-math.ts +11 -5
package/src/modules/erc20.ts
CHANGED
|
@@ -1,29 +1,75 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
2
|
import { IConfig } from "@/interfaces";
|
|
3
|
-
import { Contract } from "starknet";
|
|
4
|
-
import ERC20Abi from
|
|
3
|
+
import { Contract, uint256 } from "starknet";
|
|
4
|
+
import ERC20Abi from "@/data/erc20.abi.json";
|
|
5
5
|
|
|
6
6
|
export class ERC20 {
|
|
7
|
-
|
|
7
|
+
readonly config: IConfig;
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
constructor(config: IConfig) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
contract(addr: string | ContractAddr) {
|
|
14
|
+
const _addr = typeof addr === "string" ? addr : addr.address;
|
|
15
|
+
return new Contract({
|
|
16
|
+
abi: ERC20Abi,
|
|
17
|
+
address: _addr,
|
|
18
|
+
providerOrAccount: this.config.provider,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
17
21
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
async balanceOf(
|
|
23
|
+
token: string | ContractAddr,
|
|
24
|
+
address: string | ContractAddr,
|
|
25
|
+
tokenDecimals: number
|
|
26
|
+
) {
|
|
27
|
+
const contract = this.contract(token);
|
|
28
|
+
const balance = await contract.call("balance_of", [address.toString()]);
|
|
29
|
+
return Web3Number.fromWei(balance.toString(), tokenDecimals);
|
|
30
|
+
}
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
async allowance(
|
|
33
|
+
token: string | ContractAddr,
|
|
34
|
+
owner: string | ContractAddr,
|
|
35
|
+
spender: string | ContractAddr,
|
|
36
|
+
tokenDecimals: number
|
|
37
|
+
) {
|
|
38
|
+
const contract = this.contract(token);
|
|
39
|
+
const allowance = await contract.call("allowance", [
|
|
40
|
+
owner.toString(),
|
|
41
|
+
spender.toString(),
|
|
42
|
+
]);
|
|
43
|
+
return Web3Number.fromWei(allowance.toString(), tokenDecimals);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
transfer(
|
|
47
|
+
token: string | ContractAddr,
|
|
48
|
+
to: string | ContractAddr,
|
|
49
|
+
amount: Web3Number
|
|
50
|
+
) {
|
|
51
|
+
const contract = this.contract(token);
|
|
52
|
+
const amountUint256 = uint256.bnToUint256(amount.toWei());
|
|
53
|
+
const transferCall = contract.populate("transfer", [
|
|
54
|
+
to.toString(),
|
|
55
|
+
amountUint256.low.toString(),
|
|
56
|
+
amountUint256.high.toString(),
|
|
57
|
+
]);
|
|
58
|
+
return transferCall;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
approve(
|
|
62
|
+
token: string | ContractAddr,
|
|
63
|
+
spender: string | ContractAddr,
|
|
64
|
+
amount: Web3Number
|
|
65
|
+
) {
|
|
66
|
+
const contract = this.contract(token);
|
|
67
|
+
const amountUint256 = uint256.bnToUint256(amount.toWei());
|
|
68
|
+
const approveCall = contract.populate("approve", [
|
|
69
|
+
spender.toString(),
|
|
70
|
+
amountUint256.low.toString(),
|
|
71
|
+
amountUint256.high.toString(),
|
|
72
|
+
]);
|
|
73
|
+
return approveCall;
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/modules/harvests.ts
CHANGED
|
@@ -29,36 +29,28 @@ export class Harvests {
|
|
|
29
29
|
|
|
30
30
|
async getUnHarvestedRewards(addr: ContractAddr) {
|
|
31
31
|
const rewards = await this.getHarvests(addr);
|
|
32
|
-
logger.verbose(`${Harvests.name}: getUnHarvestedRewards => rewards length: ${rewards.length}`);
|
|
33
32
|
if (rewards.length == 0) return [];
|
|
34
33
|
|
|
35
34
|
const unClaimed: HarvestInfo[] = [];
|
|
36
35
|
|
|
37
36
|
// use the latest one
|
|
38
|
-
const
|
|
39
|
-
if (sortedRewards.length == 0) {
|
|
40
|
-
logger.verbose(`${Harvests.name}: no rewards found`);
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const cls = await this.config.provider.getClassAt(sortedRewards[0].rewardsContract.address);
|
|
37
|
+
const reward = rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime())[0];
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
unClaimed.push(reward); // to ensure older harvest is first
|
|
39
|
+
const cls = await this.config.provider.getClassAt(reward.rewardsContract.address);
|
|
40
|
+
const contract = new Contract({abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider});
|
|
41
|
+
const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
|
|
42
|
+
logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}`);
|
|
43
|
+
if (isClaimed) {
|
|
44
|
+
return unClaimed;
|
|
45
|
+
}
|
|
46
|
+
// rewards contract must have enough balance to claim
|
|
47
|
+
const bal = await (new ERC20(this.config)).balanceOf(reward.token, reward.rewardsContract.address, 18);
|
|
48
|
+
if (bal.lessThan(reward.claim.amount)) {
|
|
49
|
+
logger.verbose(`${Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
|
|
50
|
+
return unClaimed;
|
|
61
51
|
}
|
|
52
|
+
|
|
53
|
+
unClaimed.unshift(reward); // to ensure older harvest is first
|
|
62
54
|
return unClaimed;
|
|
63
55
|
}
|
|
64
56
|
}
|
|
@@ -67,28 +59,26 @@ const STRK = '0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d'
|
|
|
67
59
|
|
|
68
60
|
export class EkuboHarvests extends Harvests {
|
|
69
61
|
async getHarvests(addr: ContractAddr) {
|
|
70
|
-
|
|
71
|
-
const EKUBO_API = `https://prod-api.ekubo.org/claims/${addr.address}`
|
|
62
|
+
const EKUBO_API = `https://starknet-mainnet-api.ekubo.org/airdrops/${addr.address}?token=${STRK}`
|
|
72
63
|
const resultEkubo = await fetch(EKUBO_API);
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
logger.verbose(`${EkuboHarvests.name}: getHarvests => claims length: ${claims.length}`);
|
|
64
|
+
const items = (await resultEkubo.json());
|
|
65
|
+
|
|
76
66
|
const rewards: HarvestInfo[] = [];
|
|
77
|
-
for (let i=0; i<
|
|
78
|
-
const
|
|
79
|
-
assert(
|
|
67
|
+
for (let i=0; i<items.length; ++i) {
|
|
68
|
+
const info = items[i];
|
|
69
|
+
assert(info.token == STRK, 'expected strk token only')
|
|
80
70
|
rewards.push({
|
|
81
|
-
rewardsContract: ContractAddr.from(
|
|
71
|
+
rewardsContract: ContractAddr.from(info.contract_address),
|
|
82
72
|
token: ContractAddr.from(STRK),
|
|
83
|
-
startDate: new Date(
|
|
84
|
-
endDate: new Date(
|
|
73
|
+
startDate: new Date(info.start_date),
|
|
74
|
+
endDate: new Date(info.end_date),
|
|
85
75
|
claim: {
|
|
86
|
-
id:
|
|
87
|
-
amount: Web3Number.fromWei(
|
|
88
|
-
claimee: ContractAddr.from(
|
|
76
|
+
id: info.claim.id,
|
|
77
|
+
amount: Web3Number.fromWei(info.claim.amount, 18),
|
|
78
|
+
claimee: ContractAddr.from(info.claim.claimee)
|
|
89
79
|
},
|
|
90
|
-
actualReward: Web3Number.fromWei(
|
|
91
|
-
proof:
|
|
80
|
+
actualReward: Web3Number.fromWei(info.claim.amount, 18),
|
|
81
|
+
proof: info.proof
|
|
92
82
|
});
|
|
93
83
|
}
|
|
94
84
|
return rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
|
|
@@ -111,10 +101,6 @@ export class VesuHarvests extends Harvests {
|
|
|
111
101
|
logger.verbose(`${VesuHarvests.name}: claimed_amount: ${claimed_amount.toString()}`);
|
|
112
102
|
|
|
113
103
|
const data = _data.data['defiSpring'];
|
|
114
|
-
if (!data) {
|
|
115
|
-
logger.verbose(`${VesuHarvests.name}: no defiSpring data found`);
|
|
116
|
-
return [];
|
|
117
|
-
}
|
|
118
104
|
|
|
119
105
|
// get the actual reward
|
|
120
106
|
const actualReward = Web3Number.fromWei(data.amount, 18).minus(claimed_amount);
|
package/src/modules/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export * from './token-market-data';
|
|
1
2
|
export * from './pricer';
|
|
2
3
|
export * from './pragma';
|
|
3
4
|
export * from './zkLend';
|
|
@@ -6,4 +7,7 @@ export * from './erc20';
|
|
|
6
7
|
export * from './avnu';
|
|
7
8
|
export * from './ekubo-quoter';
|
|
8
9
|
export * from './pricer-lst';
|
|
9
|
-
export * from './lst-apr';
|
|
10
|
+
export * from './lst-apr';
|
|
11
|
+
export * from './pricerBase';
|
|
12
|
+
export * from './midas';
|
|
13
|
+
export * from './ExtendedWrapperSDk';
|
package/src/modules/lst-apr.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ContractAddr } from "@/dataTypes";
|
|
2
|
+
import { Global } from "@/global";
|
|
3
|
+
import { TokenInfo } from "@/interfaces";
|
|
2
4
|
import { logger } from "@/utils";
|
|
3
5
|
|
|
4
6
|
export interface LSTStats {
|
|
@@ -19,6 +21,19 @@ export class LSTAPRService {
|
|
|
19
21
|
private static cacheTimestamp: number = 0;
|
|
20
22
|
private static readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
21
23
|
|
|
24
|
+
static lstMapping: Record<string, TokenInfo> = {};
|
|
25
|
+
|
|
26
|
+
static isLST(address: ContractAddr): boolean {
|
|
27
|
+
return this.lstMapping[address.address] !== undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
static getUnderlyingFromLST(lstAddress: ContractAddr): TokenInfo {
|
|
31
|
+
const underlying = this.lstMapping[lstAddress.address];
|
|
32
|
+
if (!underlying) {
|
|
33
|
+
throw new Error(`Underlying token not found for ${lstAddress.address}`);
|
|
34
|
+
}
|
|
35
|
+
return underlying;
|
|
36
|
+
}
|
|
22
37
|
/**
|
|
23
38
|
* Fetches LST stats from Endur API with caching
|
|
24
39
|
* @returns Promise<LSTStats[]> Array of LST statistics
|
|
@@ -138,3 +153,24 @@ export class LSTAPRService {
|
|
|
138
153
|
logger.verbose(`LSTAPRService: Cache cleared`);
|
|
139
154
|
}
|
|
140
155
|
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// populate lstMapping
|
|
159
|
+
const lstSymbolMapping: any = {
|
|
160
|
+
xSTRK: "STRK",
|
|
161
|
+
xWBTC: "WBTC",
|
|
162
|
+
xtBTC: "tBTC",
|
|
163
|
+
xsBTC: "solvBTC",
|
|
164
|
+
xLBTC: "LBTC",
|
|
165
|
+
}
|
|
166
|
+
Object.keys(lstSymbolMapping).forEach(key => {
|
|
167
|
+
const lst = Global.getDefaultTokens().find(t => t.symbol === key);
|
|
168
|
+
if (!lst) {
|
|
169
|
+
throw new Error(`LST token not found for ${key}`);
|
|
170
|
+
}
|
|
171
|
+
const underlying = Global.getDefaultTokens().find(t => t.symbol === lstSymbolMapping[key]);
|
|
172
|
+
if (!underlying) {
|
|
173
|
+
throw new Error(`Underlying token not found for ${key}`);
|
|
174
|
+
}
|
|
175
|
+
LSTAPRService.lstMapping[lst.address.address] = underlying;
|
|
176
|
+
});
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { ContractAddr } from '../dataTypes';
|
|
3
|
+
import { logger } from '../utils/logger';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Midas module for interacting with Midas API
|
|
7
|
+
* Provides functions to get APY, price, and TVL data for Midas tokens
|
|
8
|
+
*/
|
|
9
|
+
export class Midas {
|
|
10
|
+
// Static mapping of contract addresses to Midas API symbols
|
|
11
|
+
private static readonly CONTRACT_TO_SYMBOL: Record<string, string> = {
|
|
12
|
+
'0x4e4fb1a9ca7e84bae609b9dc0078ad7719e49187ae7e425bb47d131710eddac': 'mre7btc', // mRe7BTC
|
|
13
|
+
'0x4be8945e61dc3e19ebadd1579a6bd53b262f51ba89e6f8b0c4bc9a7e3c633fc': 'mre7', // mRe7YIELD
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
private static readonly BASE_URL = 'https://api-prod.midas.app/api/data';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check if a contract address is supported by Midas
|
|
20
|
+
* @param contractAddr The contract address to check
|
|
21
|
+
* @returns True if the contract address is supported
|
|
22
|
+
*/
|
|
23
|
+
static isSupported(contractAddr: ContractAddr): boolean {
|
|
24
|
+
return contractAddr.address in Midas.CONTRACT_TO_SYMBOL;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Get the Midas symbol for a given contract address
|
|
29
|
+
* @param contractAddr The contract address to look up
|
|
30
|
+
* @returns The Midas symbol for the contract
|
|
31
|
+
* @throws Error if contract address is not found
|
|
32
|
+
*/
|
|
33
|
+
static getSymbolFromContract(contractAddr: ContractAddr): string {
|
|
34
|
+
const symbol = Midas.CONTRACT_TO_SYMBOL[contractAddr.address];
|
|
35
|
+
if (!symbol) {
|
|
36
|
+
throw new Error(`Contract address ${contractAddr.address} not found in Midas mapping`);
|
|
37
|
+
}
|
|
38
|
+
return symbol;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get APY data for all Midas tokens
|
|
43
|
+
* @returns Object with token symbols as keys and APY values as numbers
|
|
44
|
+
* @throws Error if API request fails
|
|
45
|
+
*/
|
|
46
|
+
static async getAPYs(): Promise<Record<string, number>> {
|
|
47
|
+
try {
|
|
48
|
+
const response = await axios.get(`${Midas.BASE_URL}/apys`);
|
|
49
|
+
return response.data;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
logger.error('Failed to fetch APYs from Midas API:', error);
|
|
52
|
+
throw new Error(`Failed to fetch APYs: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get APY for a specific token by contract address
|
|
58
|
+
* @param contractAddr The contract address of the token
|
|
59
|
+
* @returns The APY value for the token
|
|
60
|
+
* @throws Error if contract address not found or API request fails
|
|
61
|
+
*/
|
|
62
|
+
static async getAPY(contractAddr: ContractAddr): Promise<number> {
|
|
63
|
+
const symbol = Midas.getSymbolFromContract(contractAddr);
|
|
64
|
+
const apys = await Midas.getAPYs();
|
|
65
|
+
|
|
66
|
+
if (!(symbol in apys)) {
|
|
67
|
+
throw new Error(`Symbol ${symbol} not found in APY data`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return apys[symbol];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get price data for a specific token
|
|
75
|
+
* @param contractAddr The contract address of the token
|
|
76
|
+
* @param timestampFrom Optional start timestamp (defaults to 30 days ago)
|
|
77
|
+
* @param timestampTo Optional end timestamp (defaults to now)
|
|
78
|
+
* @param environment Environment (defaults to 'mainnet')
|
|
79
|
+
* @returns The latest price for the token
|
|
80
|
+
* @throws Error if contract address not found or API request fails
|
|
81
|
+
*/
|
|
82
|
+
static async getPrice(
|
|
83
|
+
contractAddr: ContractAddr,
|
|
84
|
+
timestampFrom?: number,
|
|
85
|
+
timestampTo?: number,
|
|
86
|
+
environment: string = 'mainnet'
|
|
87
|
+
): Promise<number> {
|
|
88
|
+
const symbol = Midas.getSymbolFromContract(contractAddr);
|
|
89
|
+
|
|
90
|
+
// Default to 30 days ago if not provided
|
|
91
|
+
const from = timestampFrom ?? Math.floor(Date.now() / 1000) - (30 * 24 * 60 * 60);
|
|
92
|
+
// Default to now if not provided
|
|
93
|
+
const to = timestampTo ?? Math.floor(Date.now() / 1000);
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const response = await axios.get(`${Midas.BASE_URL}/${symbol}/price`, {
|
|
97
|
+
params: {
|
|
98
|
+
timestampFrom: from,
|
|
99
|
+
timestampTo: to,
|
|
100
|
+
environment
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const priceData = response.data;
|
|
105
|
+
if (!Array.isArray(priceData) || priceData.length === 0) {
|
|
106
|
+
throw new Error(`No price data found for ${symbol}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Return the latest price (first item in the array)
|
|
110
|
+
return priceData[0].price;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
logger.error(`Failed to fetch price for ${symbol}:`, error);
|
|
113
|
+
throw new Error(`Failed to fetch price for ${symbol}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Get TVL data for all tokens
|
|
119
|
+
* @param environment Environment (defaults to 'mainnet')
|
|
120
|
+
* @returns Object with TVL data including totalTvl and tokenTvl
|
|
121
|
+
* @throws Error if API request fails
|
|
122
|
+
*/
|
|
123
|
+
static async getTVLData(environment: string = 'mainnet'): Promise<{
|
|
124
|
+
lastUpdatedAt: string;
|
|
125
|
+
totalTvl: number;
|
|
126
|
+
tokenTvl: Record<string, any>;
|
|
127
|
+
}> {
|
|
128
|
+
try {
|
|
129
|
+
const response = await axios.get(`${Midas.BASE_URL}/tvl`, {
|
|
130
|
+
params: { environment }
|
|
131
|
+
});
|
|
132
|
+
return response.data;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
logger.error('Failed to fetch TVL data from Midas API:', error);
|
|
135
|
+
throw new Error(`Failed to fetch TVL data: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get TVL for a specific token by contract address
|
|
141
|
+
* @param contractAddr The contract address of the token
|
|
142
|
+
* @param environment Environment (defaults to 'mainnet')
|
|
143
|
+
* @returns The TVL data for the token (USD and native amounts)
|
|
144
|
+
* @throws Error if contract address not found or API request fails
|
|
145
|
+
*/
|
|
146
|
+
static async getTVL(
|
|
147
|
+
contractAddr: ContractAddr,
|
|
148
|
+
environment: string = 'mainnet'
|
|
149
|
+
): Promise<{ usd: number; native: number } | number> {
|
|
150
|
+
const symbol = Midas.getSymbolFromContract(contractAddr);
|
|
151
|
+
const tvlData = await Midas.getTVLData(environment);
|
|
152
|
+
|
|
153
|
+
if (!(symbol in tvlData.tokenTvl)) {
|
|
154
|
+
throw new Error(`Symbol ${symbol} not found in TVL data`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return tvlData.tokenTvl[symbol];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -13,7 +13,7 @@ export class PricerFromApi extends PricerBase {
|
|
|
13
13
|
try {
|
|
14
14
|
return await this.getPriceFromMyAPI(tokenSymbol);
|
|
15
15
|
} catch (e: any) {
|
|
16
|
-
logger.warn('getPriceFromMyAPI error',
|
|
16
|
+
logger.warn('getPriceFromMyAPI error', e);
|
|
17
17
|
}
|
|
18
18
|
logger.info('getPrice coinbase', tokenSymbol);
|
|
19
19
|
let retry = 0;
|
|
@@ -41,7 +41,7 @@ export class PricerFromApi extends PricerBase {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
async getPriceFromMyAPI(tokenSymbol: string) {
|
|
44
|
-
logger.verbose(`getPrice from
|
|
44
|
+
// logger.verbose(`getPrice from api: ${tokenSymbol}`);
|
|
45
45
|
const endpoint = 'https://proxy.api.troves.fi'
|
|
46
46
|
const url = `${endpoint}/api/price/${tokenSymbol}`;
|
|
47
47
|
const priceInfoRes = await fetch(url);
|
|
@@ -6,7 +6,7 @@ import axios from "axios";
|
|
|
6
6
|
|
|
7
7
|
export class PricerLST extends Pricer {
|
|
8
8
|
private tokenMaps: {lst: TokenInfo, underlying: TokenInfo}[];
|
|
9
|
-
protected EKUBO_API = 'https://
|
|
9
|
+
protected EKUBO_API = 'https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/{{UNDERLYING_ADDRESS}}'; // e.g. xSTRK/STRK
|
|
10
10
|
|
|
11
11
|
constructor(config: IConfig, tokenMaps: {lst: TokenInfo, underlying: TokenInfo}[]) {
|
|
12
12
|
const refreshInterval = 5000;
|
package/src/modules/pricer.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { IConfig } from "@/interfaces/common";
|
|
|
5
5
|
import { Web3Number } from "@/dataTypes";
|
|
6
6
|
import { PricerBase } from "./pricerBase";
|
|
7
7
|
import { logger } from "@/utils/logger";
|
|
8
|
-
import { AvnuWrapper } from "./avnu";
|
|
9
8
|
|
|
10
9
|
export interface PriceInfo {
|
|
11
10
|
price: number,
|
|
@@ -22,14 +21,13 @@ export class Pricer extends PricerBase {
|
|
|
22
21
|
|
|
23
22
|
// code populates this map during runtime to determine which method to use for a given token
|
|
24
23
|
// The method set will be the first one to try after first attempt
|
|
25
|
-
protected methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap'
|
|
24
|
+
protected methodToUse: {[tokenSymbol: string]: 'Ekubo' | 'Coinbase' | 'Coinmarketcap'} = {};
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
27
|
* TOKENA and TOKENB are the two token names to get price of TokenA in terms of TokenB
|
|
29
28
|
*/
|
|
30
|
-
// ! switch to USDC (new) later
|
|
31
29
|
protected PRICE_API = `https://api.coinbase.com/v2/prices/{{PRICER_KEY}}/buy`;
|
|
32
|
-
protected EKUBO_API = 'https://
|
|
30
|
+
protected EKUBO_API = 'https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_ADDRESS}}/0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8'; // e.g. ETH/USDC
|
|
33
31
|
|
|
34
32
|
constructor(config: IConfig, tokens: TokenInfo[], refreshInterval = 30000, staleTime = 60000) {
|
|
35
33
|
super(config, tokens);
|
|
@@ -95,7 +93,7 @@ export class Pricer extends PricerBase {
|
|
|
95
93
|
let retry = 0;
|
|
96
94
|
while (retry < MAX_RETRIES) {
|
|
97
95
|
try {
|
|
98
|
-
if (token.symbol === 'USDT'
|
|
96
|
+
if (token.symbol === 'USDT') {
|
|
99
97
|
this.prices[token.symbol] = {
|
|
100
98
|
price: 1,
|
|
101
99
|
timestamp: new Date()
|
|
@@ -176,15 +174,6 @@ export class Pricer extends PricerBase {
|
|
|
176
174
|
console.warn(`Ekubo: price err [${token.symbol}]: `, Object.keys(error));
|
|
177
175
|
// do nothing, try next
|
|
178
176
|
}
|
|
179
|
-
case 'Avnu':
|
|
180
|
-
try {
|
|
181
|
-
const result = await this._getAvnuPrice(token, new Web3Number(token.priceCheckAmount ? token.priceCheckAmount : 1, token.decimals));
|
|
182
|
-
this.methodToUse[token.symbol] = 'Avnu';
|
|
183
|
-
return result;
|
|
184
|
-
} catch (error: any) {
|
|
185
|
-
console.warn(`Avnu: price err [${token.symbol}]: `, error.message);
|
|
186
|
-
console.warn(`Avnu: price err [${token.symbol}]: `, Object.keys(error));
|
|
187
|
-
}
|
|
188
177
|
}
|
|
189
178
|
|
|
190
179
|
// if methodToUse is the default one, pass Coinbase to try all from start
|
|
@@ -211,31 +200,7 @@ export class Pricer extends PricerBase {
|
|
|
211
200
|
throw new Error("Not implemented");
|
|
212
201
|
}
|
|
213
202
|
|
|
214
|
-
async _getAvnuPrice(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
|
|
215
|
-
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
216
|
-
|
|
217
|
-
const avnuWrapper = new AvnuWrapper();
|
|
218
|
-
const usdcAddress = '0x033068F6539f8e6e6b131e6B2B814e6c34A5224bC66947c47DaB9dFeE93b35fb';
|
|
219
|
-
const quote = await avnuWrapper.getQuotes(token.address.toString(), usdcAddress, amountIn.toWei(), '0x1');
|
|
220
|
-
const multiplier = 1 / amountIn.toNumber();
|
|
221
|
-
const outputUSDC = Number(Web3Number.fromWei(quote.buyAmount.toString(), 6).toFixed(6)) * multiplier;
|
|
222
|
-
logger.verbose(`Avnu: ${token.symbol} -> USDC: ${outputUSDC}, retry: ${retry}`);
|
|
223
|
-
if (outputUSDC === 0 && retry < 3) {
|
|
224
|
-
// try again with a higher amount
|
|
225
|
-
const amountIn = new Web3Number(100, token.decimals); // 100 unit of token
|
|
226
|
-
return await this._getAvnuPrice(token, amountIn, retry + 1);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// if usdc depegs, it will not longer be 1 USD
|
|
230
|
-
// so we need to get the price of USDC in USD
|
|
231
|
-
// and then convert the outputUSDC to USD
|
|
232
|
-
const usdcPrice = 1; // (await this.getPrice('USDC')).price;
|
|
233
|
-
logger.verbose(`USDC Price: ${usdcPrice}`);
|
|
234
|
-
return outputUSDC * usdcPrice;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
203
|
async _getPriceEkubo(token: TokenInfo, amountIn = new Web3Number(1, token.decimals), retry = 0): Promise<number> {
|
|
238
|
-
logger.verbose(`Getting price of ${token.symbol} using Ekubo, amountIn: ${amountIn.toWei()}`);
|
|
239
204
|
const url = this.EKUBO_API.replace("{{TOKEN_ADDRESS}}", token.address.toString()).replace("{{AMOUNT}}", amountIn.toWei());
|
|
240
205
|
const result = await axios.get(url);
|
|
241
206
|
const data: any = result.data;
|