@strkfarm/sdk 2.0.0-dev.9 → 2.0.0-staging.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/index.browser.global.js +111371 -93151
- package/dist/index.browser.mjs +27815 -32690
- package/dist/index.d.ts +1095 -2011
- package/dist/index.js +27425 -32309
- package/dist/index.mjs +27590 -32452
- package/package.json +6 -5
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/data/universal-vault.abi.json +20 -135
- package/src/dataTypes/address.ts +0 -7
- package/src/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +296 -288
- package/src/index.browser.ts +6 -5
- package/src/interfaces/common.tsx +324 -184
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +4 -17
- package/src/modules/ekubo-pricer.ts +79 -0
- package/src/modules/ekubo-quoter.ts +11 -88
- package/src/modules/erc20.ts +21 -67
- package/src/modules/harvests.ts +26 -15
- package/src/modules/index.ts +11 -13
- package/src/modules/lst-apr.ts +0 -36
- 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/deployer.ts +36 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/strategies/autoCompounderStrk.ts +1 -1
- package/src/strategies/base-strategy.ts +5 -22
- package/src/strategies/ekubo-cl-vault.tsx +2904 -2175
- package/src/strategies/factory.ts +165 -0
- package/src/strategies/index.ts +10 -11
- package/src/strategies/registry.ts +268 -0
- package/src/strategies/sensei.ts +416 -292
- package/src/strategies/universal-adapters/adapter-utils.ts +1 -5
- package/src/strategies/universal-adapters/baseAdapter.ts +153 -181
- package/src/strategies/universal-adapters/common-adapter.ts +77 -98
- package/src/strategies/universal-adapters/index.ts +1 -5
- package/src/strategies/universal-adapters/vesu-adapter.ts +218 -220
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +51 -58
- package/src/strategies/universal-lst-muliplier-strategy.tsx +1952 -992
- package/src/strategies/universal-strategy.tsx +1713 -1150
- package/src/strategies/vesu-rebalance.tsx +1189 -986
- package/src/utils/health-factor-math.ts +5 -11
- package/src/utils/index.ts +8 -9
- package/src/utils/strategy-utils.ts +57 -0
- package/src/data/extended-deposit.abi.json +0 -3613
- package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
- package/src/modules/ExtendedWrapperSDk/types.ts +0 -311
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -395
- package/src/modules/midas.ts +0 -159
- package/src/modules/token-market-data.ts +0 -202
- package/src/strategies/svk-strategy.ts +0 -247
- package/src/strategies/universal-adapters/adapter-optimizer.ts +0 -65
- package/src/strategies/universal-adapters/avnu-adapter.ts +0 -413
- package/src/strategies/universal-adapters/extended-adapter.ts +0 -972
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +0 -1306
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -34
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -77
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -49
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -370
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1379
|
@@ -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
|
+
|
|
@@ -2,12 +2,6 @@ import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
|
2
2
|
import { IConfig, TokenInfo } from "@/interfaces/common";
|
|
3
3
|
import { Swap } from "@/strategies";
|
|
4
4
|
import axios from "axios";
|
|
5
|
-
import { logger } from "@/utils";
|
|
6
|
-
import { uint256 } from "starknet";
|
|
7
|
-
import { Contract } from "starknet";
|
|
8
|
-
import ERC4626Abi from "@/data/erc4626.abi.json";
|
|
9
|
-
import { TokenMarketData } from "./token-market-data";
|
|
10
|
-
import { PricerBase } from "./pricerBase";
|
|
11
5
|
|
|
12
6
|
export interface EkuboRouteNode {
|
|
13
7
|
pool_key: {
|
|
@@ -34,12 +28,9 @@ export interface EkuboQuote {
|
|
|
34
28
|
|
|
35
29
|
|
|
36
30
|
export class EkuboQuoter {
|
|
37
|
-
ENDPOINT = 'https://
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
constructor(private readonly config: IConfig, pricer: PricerBase) {
|
|
41
|
-
this.tokenMarketData = new TokenMarketData(pricer, config);
|
|
42
|
-
}
|
|
31
|
+
ENDPOINT = 'https://quoter-mainnet-api.ekubo.org/{{AMOUNT}}/{{TOKEN_FROM_ADDRESS}}/{{TOKEN_TO_ADDRESS}}'; // e.g. ETH/USDC'
|
|
32
|
+
|
|
33
|
+
constructor(private readonly config: IConfig) {}
|
|
43
34
|
|
|
44
35
|
/**
|
|
45
36
|
*
|
|
@@ -49,17 +40,16 @@ export class EkuboQuoter {
|
|
|
49
40
|
* @returns
|
|
50
41
|
*/
|
|
51
42
|
async getQuote(fromToken: string, toToken: string, amount: Web3Number, retry = 0): Promise<EkuboQuote> {
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
let _fromToken = amount.gt(0) ? fromToken : toToken;
|
|
44
|
+
let _toToken = amount.gt(0) ? toToken : fromToken;
|
|
54
45
|
|
|
55
46
|
try {
|
|
56
|
-
const
|
|
57
|
-
console.log(
|
|
58
|
-
|
|
59
|
-
return quote.data as EkuboQuote;
|
|
47
|
+
const quote = await axios.get(this.ENDPOINT.replace("{{AMOUNT}}", amount.toWei()).replace("{{TOKEN_FROM_ADDRESS}}", _fromToken).replace("{{TOKEN_TO_ADDRESS}}", _toToken));
|
|
48
|
+
console.log(`Ekubo quote from ${_fromToken} to ${_toToken} for ${amount.toString()}: ${JSON.stringify(quote.data)}`);
|
|
49
|
+
return quote.data as EkuboQuote;
|
|
60
50
|
} catch (error: any) {
|
|
61
|
-
|
|
62
|
-
if (retry <
|
|
51
|
+
console.error(error.message, 'dassf', error.data);
|
|
52
|
+
if (retry < 10) {
|
|
63
53
|
await new Promise((resolve) => setTimeout(resolve, (retry + 1) * 5000));
|
|
64
54
|
return await this.getQuote(fromToken, toToken, amount, retry + 1);
|
|
65
55
|
}
|
|
@@ -67,72 +57,6 @@ export class EkuboQuoter {
|
|
|
67
57
|
}
|
|
68
58
|
}
|
|
69
59
|
|
|
70
|
-
async getDexPrice(baseToken: TokenInfo, quoteToken: TokenInfo, amount: Web3Number) {
|
|
71
|
-
const lstTokenInfo = baseToken;
|
|
72
|
-
const lstUnderlyingTokenInfo = quoteToken;
|
|
73
|
-
const quote = await this.getQuote(
|
|
74
|
-
lstTokenInfo.address.address,
|
|
75
|
-
lstUnderlyingTokenInfo.address.address,
|
|
76
|
-
amount
|
|
77
|
-
);
|
|
78
|
-
// in Underlying
|
|
79
|
-
const outputAmount = Web3Number.fromWei(quote.total_calculated, lstUnderlyingTokenInfo.decimals);
|
|
80
|
-
const price = outputAmount.toNumber() / amount.toNumber();
|
|
81
|
-
logger.verbose(`${EkuboQuoter.name}:: LST Dex Price: ${price}`);
|
|
82
|
-
return price;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async getLSTTrueExchangeRate(baseToken: TokenInfo, quoteToken: TokenInfo, amount: Web3Number) {
|
|
86
|
-
const lstTokenInfo = baseToken;
|
|
87
|
-
const lstABI = new Contract({
|
|
88
|
-
abi: ERC4626Abi,
|
|
89
|
-
address: lstTokenInfo.address.address,
|
|
90
|
-
providerOrAccount: this.config.provider
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const price: any = await lstABI.call('convert_to_assets', [uint256.bnToUint256((new Web3Number(1, lstTokenInfo.decimals)).toWei())]);
|
|
94
|
-
const exchangeRate = Number(uint256.uint256ToBN(price).toString()) / Math.pow(10, lstTokenInfo.decimals);
|
|
95
|
-
logger.verbose(`${EkuboQuoter.name}:: LST true Exchange Rate: ${exchangeRate}`);
|
|
96
|
-
return exchangeRate;
|
|
97
|
-
}
|
|
98
|
-
// debt collateral
|
|
99
|
-
async getSwapLimitAmount(fromToken: TokenInfo, toToken: TokenInfo, amount: Web3Number, max_slippage: number = 0.002): Promise<Web3Number> {
|
|
100
|
-
const isExactAmountIn = amount.greaterThanOrEqualTo(0);
|
|
101
|
-
logger.verbose(`${EkuboQuoter.name}::getSwapLimitAmount isExactAmountIn: ${isExactAmountIn}, fromToken: ${fromToken.symbol}, toToken: ${toToken.symbol}, amount: ${amount}`);
|
|
102
|
-
const isYieldToken = this.tokenMarketData.isAPYSupported(toToken);
|
|
103
|
-
console.log("isYieldToken", isYieldToken);
|
|
104
|
-
|
|
105
|
-
// if LST, get true exchange rate else use dex price
|
|
106
|
-
// wbtc
|
|
107
|
-
const baseToken = isExactAmountIn ? toToken : fromToken; // fromToken -> wbtc,
|
|
108
|
-
const quoteToken = isExactAmountIn ? fromToken : toToken; // toToken -> usdc,
|
|
109
|
-
// need dex price of from token in toToken
|
|
110
|
-
// from baseToken to underlying token
|
|
111
|
-
// for withdraw, usdc to btc with amount negative
|
|
112
|
-
const dexPrice = await this.getDexPrice(baseToken, quoteToken, amount);
|
|
113
|
-
const trueExchangeRate = isYieldToken ? await this.tokenMarketData.getTruePrice(baseToken) : dexPrice;
|
|
114
|
-
console.log("trueExchangeRate", trueExchangeRate);
|
|
115
|
-
if (isExactAmountIn) {
|
|
116
|
-
let minLSTReceived = amount.dividedBy(dexPrice).multipliedBy(1 - max_slippage); // used for increase
|
|
117
|
-
console.log("minLSTReceived", minLSTReceived);
|
|
118
|
-
const minLSTReceivedAsPerTruePrice = amount.dividedBy(trueExchangeRate); // execution output to be <= True LST price
|
|
119
|
-
if (minLSTReceived < minLSTReceivedAsPerTruePrice) {
|
|
120
|
-
minLSTReceived = minLSTReceivedAsPerTruePrice; // the execution shouldn't be bad than True price logi
|
|
121
|
-
}
|
|
122
|
-
logger.verbose(`${EkuboQuoter.name}::getModifyLeverCall minLSTReceivedAsPerTruePrice: ${minLSTReceivedAsPerTruePrice}, minLSTReceived: ${minLSTReceived}`);
|
|
123
|
-
return minLSTReceived;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
let maxUsedCollateral = amount.abs().dividedBy(dexPrice).multipliedBy(1 + max_slippage); // +ve for exact amount out, used for decrease
|
|
127
|
-
const maxUsedCollateralInLST = amount.abs().dividedBy(trueExchangeRate).multipliedBy(1.005); // 0.5% slippage, worst case based on true price
|
|
128
|
-
logger.verbose(`${EkuboQuoter.name}::getModifyLeverCall maxUsedCollateralInLST: ${maxUsedCollateralInLST}, maxUsedCollateral: ${maxUsedCollateral}`);
|
|
129
|
-
if (maxUsedCollateralInLST > maxUsedCollateral) {
|
|
130
|
-
maxUsedCollateral = maxUsedCollateralInLST;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return maxUsedCollateral;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
60
|
/**
|
|
137
61
|
* Formats Ekubo response for Vesu multiple use
|
|
138
62
|
* @param quote
|
|
@@ -141,8 +65,7 @@ export class EkuboQuoter {
|
|
|
141
65
|
*/
|
|
142
66
|
getVesuMultiplyQuote(quote: EkuboQuote, fromTokenInfo: TokenInfo, toTokenInfo: TokenInfo): Swap[] {
|
|
143
67
|
return quote.splits.map(split => {
|
|
144
|
-
|
|
145
|
-
const isNegativeAmount = BigInt(split.amount_specified) <= 0n;
|
|
68
|
+
const isNegativeAmount = BigInt(split.amount_specified) < 0n;
|
|
146
69
|
const token = isNegativeAmount ? toTokenInfo : fromTokenInfo;
|
|
147
70
|
return {
|
|
148
71
|
route: split.route.map(_route => ({
|
package/src/modules/erc20.ts
CHANGED
|
@@ -1,75 +1,29 @@
|
|
|
1
1
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
2
2
|
import { IConfig } from "@/interfaces";
|
|
3
|
-
import { Contract
|
|
4
|
-
import ERC20Abi from
|
|
3
|
+
import { Contract } 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
|
-
|
|
17
|
-
address: _addr,
|
|
18
|
-
providerOrAccount: this.config.provider,
|
|
19
|
-
});
|
|
20
|
-
}
|
|
13
|
+
contract(addr: string | ContractAddr) {
|
|
14
|
+
const _addr = typeof addr === 'string' ? addr : addr.address;
|
|
15
|
+
return new Contract({abi: ERC20Abi, address: _addr, providerOrAccount: this.config.provider});
|
|
16
|
+
}
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
}
|
|
18
|
+
async balanceOf(token: string | ContractAddr, address: string | ContractAddr, tokenDecimals: number) {
|
|
19
|
+
const contract = this.contract(token);
|
|
20
|
+
const balance = await contract.call('balanceOf', [address.toString()]);
|
|
21
|
+
return Web3Number.fromWei(balance.toString(), tokenDecimals);
|
|
22
|
+
}
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
}
|
|
24
|
+
async allowance(token: string | ContractAddr, owner: string | ContractAddr, spender: string | ContractAddr, tokenDecimals: number) {
|
|
25
|
+
const contract = this.contract(token);
|
|
26
|
+
const allowance = await contract.call('allowance', [owner.toString(), spender.toString()]);
|
|
27
|
+
return Web3Number.fromWei(allowance.toString(), tokenDecimals);
|
|
28
|
+
}
|
|
29
|
+
}
|
package/src/modules/harvests.ts
CHANGED
|
@@ -34,23 +34,30 @@ export class Harvests {
|
|
|
34
34
|
const unClaimed: HarvestInfo[] = [];
|
|
35
35
|
|
|
36
36
|
// use the latest one
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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;
|
|
37
|
+
const sortedRewards = rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
|
|
38
|
+
if (sortedRewards.length == 0) {
|
|
39
|
+
logger.verbose(`${Harvests.name}: no rewards found`);
|
|
40
|
+
return [];
|
|
51
41
|
}
|
|
52
42
|
|
|
53
|
-
|
|
43
|
+
const cls = await this.config.provider.getClassAt(sortedRewards[0].rewardsContract.address);
|
|
44
|
+
|
|
45
|
+
for (const reward of sortedRewards) {
|
|
46
|
+
const contract = new Contract({abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider});
|
|
47
|
+
const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
|
|
48
|
+
logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}, claim id: ${reward.claim.id}, address: ${reward.rewardsContract.address}`);
|
|
49
|
+
if (isClaimed) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
// rewards contract must have enough balance to claim
|
|
53
|
+
const bal = await (new ERC20(this.config)).balanceOf(reward.token, reward.rewardsContract.address, 18);
|
|
54
|
+
if (bal.lessThan(reward.claim.amount)) {
|
|
55
|
+
logger.verbose(`${Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
unClaimed.push(reward); // to ensure older harvest is first
|
|
60
|
+
}
|
|
54
61
|
return unClaimed;
|
|
55
62
|
}
|
|
56
63
|
}
|
|
@@ -101,6 +108,10 @@ export class VesuHarvests extends Harvests {
|
|
|
101
108
|
logger.verbose(`${VesuHarvests.name}: claimed_amount: ${claimed_amount.toString()}`);
|
|
102
109
|
|
|
103
110
|
const data = _data.data['defiSpring'];
|
|
111
|
+
if (!data) {
|
|
112
|
+
logger.verbose(`${VesuHarvests.name}: no defiSpring data found`);
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
104
115
|
|
|
105
116
|
// get the actual reward
|
|
106
117
|
const actualReward = Web3Number.fromWei(data.amount, 18).minus(claimed_amount);
|
package/src/modules/index.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
6
|
-
export * from
|
|
7
|
-
export * from
|
|
8
|
-
export * from
|
|
9
|
-
export * from
|
|
10
|
-
export * from
|
|
11
|
-
export * from
|
|
12
|
-
export * from './midas';
|
|
13
|
-
export * from './ExtendedWrapperSDk';
|
|
1
|
+
export * from "./pricerBase";
|
|
2
|
+
export * from "./pricer";
|
|
3
|
+
export * from "./pragma";
|
|
4
|
+
export * from "./zkLend";
|
|
5
|
+
export * from "./pricer-from-api";
|
|
6
|
+
export * from "./erc20";
|
|
7
|
+
export * from "./avnu";
|
|
8
|
+
export * from "./ekubo-quoter";
|
|
9
|
+
export * from "./ekubo-pricer";
|
|
10
|
+
export * from "./pricer-lst";
|
|
11
|
+
export * from "./lst-apr";
|
package/src/modules/lst-apr.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { ContractAddr } from "@/dataTypes";
|
|
2
|
-
import { Global } from "@/global";
|
|
3
|
-
import { TokenInfo } from "@/interfaces";
|
|
4
2
|
import { logger } from "@/utils";
|
|
5
3
|
|
|
6
4
|
export interface LSTStats {
|
|
@@ -21,19 +19,6 @@ export class LSTAPRService {
|
|
|
21
19
|
private static cacheTimestamp: number = 0;
|
|
22
20
|
private static readonly CACHE_DURATION = 5 * 60 * 1000; // 5 minutes
|
|
23
21
|
|
|
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
|
-
}
|
|
37
22
|
/**
|
|
38
23
|
* Fetches LST stats from Endur API with caching
|
|
39
24
|
* @returns Promise<LSTStats[]> Array of LST statistics
|
|
@@ -153,24 +138,3 @@ export class LSTAPRService {
|
|
|
153
138
|
logger.verbose(`LSTAPRService: Cache cleared`);
|
|
154
139
|
}
|
|
155
140
|
}
|
|
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
|
-
});
|
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
|
}
|