@strkfarm/sdk 1.0.37 → 1.0.39
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 +38 -588
- package/dist/cli.mjs +37 -587
- package/dist/index.browser.global.js +962 -329
- package/dist/index.browser.mjs +962 -325
- package/dist/index.d.ts +28 -54
- package/dist/index.js +985 -322
- package/dist/index.mjs +990 -323
- package/package.json +3 -3
- package/src/dataTypes/_bignumber.ts +49 -47
- package/src/global.ts +1 -33
- package/src/interfaces/common.ts +112 -98
- package/src/interfaces/lending.ts +1 -2
- package/src/modules/avnu.ts +1 -1
- package/src/modules/erc20.ts +6 -0
- package/src/modules/harvests.ts +1 -1
- package/src/modules/pragma.ts +1 -1
- package/src/modules/pricer-from-api.ts +3 -3
- package/src/modules/pricer.ts +2 -1
- package/src/modules/zkLend.ts +2 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/notifs/telegram.ts +1 -1
- package/src/strategies/ekubo-cl-vault.tsx +1503 -920
- package/src/strategies/vesu-rebalance.tsx +943 -611
- package/src/utils/index.ts +2 -0
- package/src/utils/logger.browser.ts +20 -0
- package/src/utils/logger.node.ts +35 -0
- package/src/utils/logger.ts +1 -0
- package/src/utils/store.ts +1 -1
package/dist/index.browser.mjs
CHANGED
|
@@ -4,6 +4,14 @@ import axios2 from "axios";
|
|
|
4
4
|
// src/global.ts
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
|
|
7
|
+
// src/utils/logger.browser.ts
|
|
8
|
+
var logger = {
|
|
9
|
+
...console,
|
|
10
|
+
verbose(message) {
|
|
11
|
+
console.log(`[VERBOSE] ${message}`);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
7
15
|
// src/dataTypes/_bignumber.ts
|
|
8
16
|
import BigNumber from "bignumber.js";
|
|
9
17
|
var _Web3Number = class extends BigNumber {
|
|
@@ -20,6 +28,7 @@ var _Web3Number = class extends BigNumber {
|
|
|
20
28
|
}
|
|
21
29
|
dividedBy(value) {
|
|
22
30
|
const _value = this.getStandardString(value);
|
|
31
|
+
console.log("dividedBy", _value);
|
|
23
32
|
return this.construct(this.div(_value).toString(), this.decimals);
|
|
24
33
|
}
|
|
25
34
|
plus(value) {
|
|
@@ -33,13 +42,15 @@ var _Web3Number = class extends BigNumber {
|
|
|
33
42
|
construct(value, decimals) {
|
|
34
43
|
return new this.constructor(value, decimals);
|
|
35
44
|
}
|
|
36
|
-
toString(
|
|
37
|
-
return super.
|
|
45
|
+
toString() {
|
|
46
|
+
return super.toString();
|
|
38
47
|
}
|
|
39
48
|
toJSON() {
|
|
49
|
+
logger.verbose(`converting to json with decimals`);
|
|
40
50
|
return this.toString();
|
|
41
51
|
}
|
|
42
52
|
valueOf() {
|
|
53
|
+
logger.verbose(`converting to valueOf with decimals`);
|
|
43
54
|
return this.toString();
|
|
44
55
|
}
|
|
45
56
|
maxToFixedDecimals() {
|
|
@@ -95,12 +106,6 @@ var ContractAddr = class _ContractAddr {
|
|
|
95
106
|
};
|
|
96
107
|
|
|
97
108
|
// src/global.ts
|
|
98
|
-
var logger = {
|
|
99
|
-
...console,
|
|
100
|
-
verbose(message) {
|
|
101
|
-
console.log(`[VERBOSE] ${message}`);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
109
|
var FatalError = class extends Error {
|
|
105
110
|
constructor(message, err) {
|
|
106
111
|
super(message);
|
|
@@ -686,7 +691,7 @@ var PricerFromApi = class extends PricerBase {
|
|
|
686
691
|
} catch (e) {
|
|
687
692
|
logger.warn("getPriceFromMyAPI error", JSON.stringify(e.message || e));
|
|
688
693
|
}
|
|
689
|
-
logger.
|
|
694
|
+
logger.info("getPrice coinbase", tokenSymbol);
|
|
690
695
|
let retry = 0;
|
|
691
696
|
const MAX_RETRIES = 5;
|
|
692
697
|
for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
|
|
@@ -1867,6 +1872,11 @@ var ERC20 = class {
|
|
|
1867
1872
|
const balance = await contract.call("balanceOf", [address.toString()]);
|
|
1868
1873
|
return Web3Number.fromWei(balance.toString(), tokenDecimals);
|
|
1869
1874
|
}
|
|
1875
|
+
async allowance(token, owner, spender, tokenDecimals) {
|
|
1876
|
+
const contract = this.contract(token);
|
|
1877
|
+
const allowance = await contract.call("allowance", [owner.toString(), spender.toString()]);
|
|
1878
|
+
return Web3Number.fromWei(allowance.toString(), tokenDecimals);
|
|
1879
|
+
}
|
|
1870
1880
|
};
|
|
1871
1881
|
|
|
1872
1882
|
// src/modules/avnu.ts
|
|
@@ -2006,7 +2016,9 @@ var getRiskColor = (risk) => {
|
|
|
2006
2016
|
};
|
|
2007
2017
|
var getNoRiskTags = (risks) => {
|
|
2008
2018
|
const noRisks1 = risks.filter((risk) => risk.value === 0).map((risk) => risk.type);
|
|
2009
|
-
const noRisks2 = Object.values(RiskType).filter(
|
|
2019
|
+
const noRisks2 = Object.values(RiskType).filter(
|
|
2020
|
+
(risk) => !risks.map((risk2) => risk2.type).includes(risk)
|
|
2021
|
+
);
|
|
2010
2022
|
const mergedUnique = [.../* @__PURE__ */ new Set([...noRisks1, ...noRisks2])];
|
|
2011
2023
|
return mergedUnique.map((risk) => `No ${risk}`);
|
|
2012
2024
|
};
|
|
@@ -12698,6 +12710,7 @@ var vesu_pools_default = {
|
|
|
12698
12710
|
};
|
|
12699
12711
|
|
|
12700
12712
|
// src/strategies/vesu-rebalance.tsx
|
|
12713
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12701
12714
|
var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
12702
12715
|
// 10000 bps = 100%
|
|
12703
12716
|
/**
|
|
@@ -12711,10 +12724,17 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12711
12724
|
super(config);
|
|
12712
12725
|
this.BASE_WEIGHT = 1e4;
|
|
12713
12726
|
this.pricer = pricer;
|
|
12714
|
-
assert(
|
|
12727
|
+
assert(
|
|
12728
|
+
metadata.depositTokens.length === 1,
|
|
12729
|
+
"VesuRebalance only supports 1 deposit token"
|
|
12730
|
+
);
|
|
12715
12731
|
this.metadata = metadata;
|
|
12716
12732
|
this.address = metadata.address;
|
|
12717
|
-
this.contract = new Contract5(
|
|
12733
|
+
this.contract = new Contract5(
|
|
12734
|
+
vesu_rebalance_abi_default,
|
|
12735
|
+
this.address.address,
|
|
12736
|
+
this.config.provider
|
|
12737
|
+
);
|
|
12718
12738
|
}
|
|
12719
12739
|
/**
|
|
12720
12740
|
* Creates a deposit call to the strategy contract.
|
|
@@ -12723,10 +12743,23 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12723
12743
|
* @returns Populated contract call for deposit
|
|
12724
12744
|
*/
|
|
12725
12745
|
async depositCall(amountInfo, receiver) {
|
|
12726
|
-
assert(
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12746
|
+
assert(
|
|
12747
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
12748
|
+
"Deposit token mismatch"
|
|
12749
|
+
);
|
|
12750
|
+
const assetContract = new Contract5(
|
|
12751
|
+
vesu_rebalance_abi_default,
|
|
12752
|
+
this.asset().address.address,
|
|
12753
|
+
this.config.provider
|
|
12754
|
+
);
|
|
12755
|
+
const call1 = assetContract.populate("approve", [
|
|
12756
|
+
this.address.address,
|
|
12757
|
+
uint2563.bnToUint256(amountInfo.amount.toWei())
|
|
12758
|
+
]);
|
|
12759
|
+
const call2 = this.contract.populate("deposit", [
|
|
12760
|
+
uint2563.bnToUint256(amountInfo.amount.toWei()),
|
|
12761
|
+
receiver.address
|
|
12762
|
+
]);
|
|
12730
12763
|
return [call1, call2];
|
|
12731
12764
|
}
|
|
12732
12765
|
/**
|
|
@@ -12737,7 +12770,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12737
12770
|
* @returns Populated contract call for withdrawal
|
|
12738
12771
|
*/
|
|
12739
12772
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
12740
|
-
return [
|
|
12773
|
+
return [
|
|
12774
|
+
this.contract.populate("withdraw", [
|
|
12775
|
+
uint2563.bnToUint256(amountInfo.amount.toWei()),
|
|
12776
|
+
receiver.address,
|
|
12777
|
+
owner.address
|
|
12778
|
+
])
|
|
12779
|
+
];
|
|
12741
12780
|
}
|
|
12742
12781
|
/**
|
|
12743
12782
|
* Returns the underlying asset token of the strategy.
|
|
@@ -12760,9 +12799,16 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12760
12799
|
*/
|
|
12761
12800
|
async getUserTVL(user) {
|
|
12762
12801
|
const shares = await this.contract.balanceOf(user.address);
|
|
12763
|
-
const assets = await this.contract.convert_to_assets(
|
|
12764
|
-
|
|
12765
|
-
|
|
12802
|
+
const assets = await this.contract.convert_to_assets(
|
|
12803
|
+
uint2563.bnToUint256(shares)
|
|
12804
|
+
);
|
|
12805
|
+
const amount = Web3Number.fromWei(
|
|
12806
|
+
assets.toString(),
|
|
12807
|
+
this.metadata.depositTokens[0].decimals
|
|
12808
|
+
);
|
|
12809
|
+
let price = await this.pricer.getPrice(
|
|
12810
|
+
this.metadata.depositTokens[0].symbol
|
|
12811
|
+
);
|
|
12766
12812
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12767
12813
|
return {
|
|
12768
12814
|
tokenInfo: this.asset(),
|
|
@@ -12776,8 +12822,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12776
12822
|
*/
|
|
12777
12823
|
async getTVL() {
|
|
12778
12824
|
const assets = await this.contract.total_assets();
|
|
12779
|
-
const amount = Web3Number.fromWei(
|
|
12780
|
-
|
|
12825
|
+
const amount = Web3Number.fromWei(
|
|
12826
|
+
assets.toString(),
|
|
12827
|
+
this.metadata.depositTokens[0].decimals
|
|
12828
|
+
);
|
|
12829
|
+
let price = await this.pricer.getPrice(
|
|
12830
|
+
this.metadata.depositTokens[0].symbol
|
|
12831
|
+
);
|
|
12781
12832
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12782
12833
|
return {
|
|
12783
12834
|
tokenInfo: this.asset(),
|
|
@@ -12803,51 +12854,104 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12803
12854
|
return pools;
|
|
12804
12855
|
}
|
|
12805
12856
|
async getPoolInfo(p, pools, vesuPositions, totalAssets, isErrorPositionsAPI, isErrorPoolsAPI) {
|
|
12806
|
-
const vesuPosition = vesuPositions.find(
|
|
12857
|
+
const vesuPosition = vesuPositions.find(
|
|
12858
|
+
(d) => d.pool.id.toString() === num3.getDecimalString(p.pool_id.address.toString())
|
|
12859
|
+
);
|
|
12807
12860
|
const _pool = pools.find((d) => {
|
|
12808
|
-
logger.verbose(
|
|
12861
|
+
logger.verbose(
|
|
12862
|
+
`pool check: ${d.id == num3.getDecimalString(p.pool_id.address.toString())}, id: ${d.id}, pool_id: ${num3.getDecimalString(
|
|
12863
|
+
p.pool_id.address.toString()
|
|
12864
|
+
)}`
|
|
12865
|
+
);
|
|
12809
12866
|
return d.id == num3.getDecimalString(p.pool_id.address.toString());
|
|
12810
12867
|
});
|
|
12811
12868
|
logger.verbose(`pool: ${JSON.stringify(_pool)}`);
|
|
12812
12869
|
logger.verbose(typeof _pool);
|
|
12813
12870
|
logger.verbose(`name: ${_pool?.name}`);
|
|
12814
12871
|
const name = _pool?.name;
|
|
12815
|
-
logger.verbose(
|
|
12816
|
-
|
|
12872
|
+
logger.verbose(
|
|
12873
|
+
`name2: ${name}, ${!name ? true : false}, ${name?.length}, ${typeof name}`
|
|
12874
|
+
);
|
|
12875
|
+
const assetInfo = _pool?.assets.find(
|
|
12876
|
+
(d) => this.asset().address.eqString(d.address)
|
|
12877
|
+
);
|
|
12817
12878
|
if (!name) {
|
|
12818
12879
|
logger.verbose(`Pool not found`);
|
|
12819
12880
|
throw new Error(`Pool name ${p.pool_id.address.toString()} not found`);
|
|
12820
12881
|
}
|
|
12821
12882
|
if (!assetInfo) {
|
|
12822
|
-
throw new Error(
|
|
12883
|
+
throw new Error(
|
|
12884
|
+
`Asset ${this.asset().address.toString()} not found in pool ${p.pool_id.address.toString()}`
|
|
12885
|
+
);
|
|
12823
12886
|
}
|
|
12824
|
-
let vTokenContract = new Contract5(
|
|
12887
|
+
let vTokenContract = new Contract5(
|
|
12888
|
+
vesu_rebalance_abi_default,
|
|
12889
|
+
p.v_token.address,
|
|
12890
|
+
this.config.provider
|
|
12891
|
+
);
|
|
12825
12892
|
const bal = await vTokenContract.balanceOf(this.address.address);
|
|
12826
|
-
const assets = await vTokenContract.convert_to_assets(
|
|
12893
|
+
const assets = await vTokenContract.convert_to_assets(
|
|
12894
|
+
uint2563.bnToUint256(bal.toString())
|
|
12895
|
+
);
|
|
12827
12896
|
logger.verbose(`Collateral: ${JSON.stringify(vesuPosition?.collateral)}`);
|
|
12828
12897
|
logger.verbose(`supplyApy: ${JSON.stringify(assetInfo?.stats.supplyApy)}`);
|
|
12829
|
-
logger.verbose(
|
|
12830
|
-
|
|
12831
|
-
|
|
12898
|
+
logger.verbose(
|
|
12899
|
+
`defiSpringSupplyApr: ${JSON.stringify(
|
|
12900
|
+
assetInfo?.stats.defiSpringSupplyApr
|
|
12901
|
+
)}`
|
|
12902
|
+
);
|
|
12903
|
+
logger.verbose(
|
|
12904
|
+
`currentUtilization: ${JSON.stringify(
|
|
12905
|
+
assetInfo?.stats.currentUtilization
|
|
12906
|
+
)}`
|
|
12907
|
+
);
|
|
12908
|
+
logger.verbose(
|
|
12909
|
+
`maxUtilization: ${JSON.stringify(assetInfo?.config.maxUtilization)}`
|
|
12910
|
+
);
|
|
12832
12911
|
const item = {
|
|
12833
12912
|
pool_id: p.pool_id,
|
|
12834
12913
|
pool_name: _pool?.name,
|
|
12835
12914
|
max_weight: p.max_weight,
|
|
12836
|
-
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12915
|
+
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12916
|
+
Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)
|
|
12917
|
+
),
|
|
12837
12918
|
v_token: p.v_token,
|
|
12838
12919
|
amount: Web3Number.fromWei(assets.toString(), this.decimals()),
|
|
12839
|
-
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
12920
|
+
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
12921
|
+
vesuPosition.collateral.usdPrice.value,
|
|
12922
|
+
vesuPosition.collateral.usdPrice.decimals
|
|
12923
|
+
),
|
|
12840
12924
|
APY: isErrorPoolsAPI || !assetInfo ? {
|
|
12841
12925
|
baseApy: 0,
|
|
12842
12926
|
defiSpringApy: 0,
|
|
12843
12927
|
netApy: 0
|
|
12844
12928
|
} : {
|
|
12845
|
-
baseApy: Number(
|
|
12846
|
-
|
|
12929
|
+
baseApy: Number(
|
|
12930
|
+
Web3Number.fromWei(
|
|
12931
|
+
assetInfo.stats.supplyApy.value,
|
|
12932
|
+
assetInfo.stats.supplyApy.decimals
|
|
12933
|
+
).toFixed(6)
|
|
12934
|
+
),
|
|
12935
|
+
defiSpringApy: assetInfo.stats.defiSpringSupplyApr ? Number(
|
|
12936
|
+
Web3Number.fromWei(
|
|
12937
|
+
assetInfo.stats.defiSpringSupplyApr.value,
|
|
12938
|
+
assetInfo.stats.defiSpringSupplyApr.decimals
|
|
12939
|
+
).toFixed(6)
|
|
12940
|
+
) : 0,
|
|
12847
12941
|
netApy: 0
|
|
12848
12942
|
},
|
|
12849
|
-
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12850
|
-
|
|
12943
|
+
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12944
|
+
Web3Number.fromWei(
|
|
12945
|
+
assetInfo.stats.currentUtilization.value,
|
|
12946
|
+
assetInfo.stats.currentUtilization.decimals
|
|
12947
|
+
).toFixed(6)
|
|
12948
|
+
),
|
|
12949
|
+
maxUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12950
|
+
Web3Number.fromWei(
|
|
12951
|
+
assetInfo.config.maxUtilization.value,
|
|
12952
|
+
assetInfo.config.maxUtilization.decimals
|
|
12953
|
+
).toFixed(6)
|
|
12954
|
+
)
|
|
12851
12955
|
};
|
|
12852
12956
|
item.APY.netApy = item.APY.baseApy + item.APY.defiSpringApy;
|
|
12853
12957
|
return item;
|
|
@@ -12857,7 +12961,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12857
12961
|
* 1. Contract's allowed pools
|
|
12858
12962
|
* 2. Vesu positions API for current positions
|
|
12859
12963
|
* 3. Vesu pools API for APY and utilization data
|
|
12860
|
-
*
|
|
12964
|
+
*
|
|
12861
12965
|
* @returns {Promise<{
|
|
12862
12966
|
* data: Array<PoolInfoFull>,
|
|
12863
12967
|
* isErrorPositionsAPI: boolean
|
|
@@ -12874,15 +12978,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12874
12978
|
let isErrorPositionsAPI = false;
|
|
12875
12979
|
let vesuPositions = [];
|
|
12876
12980
|
try {
|
|
12877
|
-
const data2 = await getAPIUsingHeadlessBrowser(
|
|
12981
|
+
const data2 = await getAPIUsingHeadlessBrowser(
|
|
12982
|
+
`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`
|
|
12983
|
+
);
|
|
12878
12984
|
vesuPositions = data2.data;
|
|
12879
12985
|
} catch (e) {
|
|
12880
|
-
console.error(
|
|
12986
|
+
console.error(
|
|
12987
|
+
`${_VesuRebalance.name}: Error fetching positions for ${this.address.address}`,
|
|
12988
|
+
e
|
|
12989
|
+
);
|
|
12881
12990
|
isErrorPositionsAPI = true;
|
|
12882
12991
|
}
|
|
12883
12992
|
let { pools, isErrorPoolsAPI } = await this.getVesuPools();
|
|
12884
12993
|
const totalAssets = (await this.getTVL()).amount;
|
|
12885
|
-
const info = allowedPools.map(
|
|
12994
|
+
const info = allowedPools.map(
|
|
12995
|
+
(p) => this.getPoolInfo(
|
|
12996
|
+
p,
|
|
12997
|
+
pools,
|
|
12998
|
+
vesuPositions,
|
|
12999
|
+
totalAssets,
|
|
13000
|
+
isErrorPositionsAPI,
|
|
13001
|
+
isErrorPoolsAPI
|
|
13002
|
+
)
|
|
13003
|
+
);
|
|
12886
13004
|
const data = await Promise.all(info);
|
|
12887
13005
|
return {
|
|
12888
13006
|
data,
|
|
@@ -12895,18 +13013,25 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12895
13013
|
let isErrorPoolsAPI = false;
|
|
12896
13014
|
let pools = [];
|
|
12897
13015
|
try {
|
|
12898
|
-
const data = await getAPIUsingHeadlessBrowser(
|
|
13016
|
+
const data = await getAPIUsingHeadlessBrowser(
|
|
13017
|
+
"https://api.vesu.xyz/pools"
|
|
13018
|
+
);
|
|
12899
13019
|
pools = data.data;
|
|
12900
13020
|
for (const pool of vesu_pools_default.data) {
|
|
12901
13021
|
const found = pools.find((d) => d.id === pool.id);
|
|
12902
13022
|
if (!found) {
|
|
12903
13023
|
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
12904
|
-
logger.verbose(
|
|
13024
|
+
logger.verbose(
|
|
13025
|
+
`VesuRebalance: Pool ${pool.id} not found in Vesu API, using hardcoded data`
|
|
13026
|
+
);
|
|
12905
13027
|
throw new Error("pool not found [sanity check]");
|
|
12906
13028
|
}
|
|
12907
13029
|
}
|
|
12908
13030
|
} catch (e) {
|
|
12909
|
-
logger.error(
|
|
13031
|
+
logger.error(
|
|
13032
|
+
`${_VesuRebalance.name}: Error fetching pools for ${this.address.address}, retry ${retry}`,
|
|
13033
|
+
e
|
|
13034
|
+
);
|
|
12910
13035
|
isErrorPoolsAPI = true;
|
|
12911
13036
|
if (retry < 10) {
|
|
12912
13037
|
await new Promise((resolve) => setTimeout(resolve, 5e3 * (retry + 1)));
|
|
@@ -12944,8 +13069,8 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12944
13069
|
* 3. For each pool that needs more funds:
|
|
12945
13070
|
* - Takes funds from lowest APY pools that are over their target
|
|
12946
13071
|
* 4. Validates that total assets remain constant
|
|
12947
|
-
*
|
|
12948
|
-
* @returns {Promise<{
|
|
13072
|
+
*
|
|
13073
|
+
* @returns {Promise<{
|
|
12949
13074
|
* changes: Change[],
|
|
12950
13075
|
* finalPools: PoolInfoFull[],
|
|
12951
13076
|
* isAnyPoolOverMaxWeight: boolean
|
|
@@ -12961,27 +13086,38 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12961
13086
|
_pools = _pools2;
|
|
12962
13087
|
}
|
|
12963
13088
|
const feeDeductions = await this.getFee(_pools);
|
|
12964
|
-
logger.verbose(
|
|
13089
|
+
logger.verbose(
|
|
13090
|
+
`VesuRebalance: feeDeductions: ${JSON.stringify(feeDeductions)}`
|
|
13091
|
+
);
|
|
12965
13092
|
const pools = _pools.map((p) => {
|
|
12966
13093
|
const fee = feeDeductions.find((f) => p.v_token.eq(f.vToken))?.fee || Web3Number.fromWei("0", this.decimals());
|
|
12967
|
-
logger.verbose(
|
|
13094
|
+
logger.verbose(
|
|
13095
|
+
`FeeAdjustment: ${p.pool_id} => ${fee.toString()}, amt: ${p.amount.toString()}`
|
|
13096
|
+
);
|
|
12968
13097
|
return {
|
|
12969
13098
|
...p,
|
|
12970
13099
|
amount: p.amount.minus(fee)
|
|
12971
13100
|
};
|
|
12972
13101
|
});
|
|
12973
13102
|
let totalAssets = (await this.getTVL()).amount;
|
|
12974
|
-
if (totalAssets.eq(0))
|
|
12975
|
-
|
|
12976
|
-
|
|
12977
|
-
|
|
13103
|
+
if (totalAssets.eq(0))
|
|
13104
|
+
return {
|
|
13105
|
+
changes: [],
|
|
13106
|
+
finalPools: []
|
|
13107
|
+
};
|
|
12978
13108
|
feeDeductions.forEach((f) => {
|
|
12979
13109
|
totalAssets = totalAssets.minus(f.fee);
|
|
12980
13110
|
});
|
|
12981
|
-
const sumPools = pools.reduce(
|
|
13111
|
+
const sumPools = pools.reduce(
|
|
13112
|
+
(acc, curr) => acc.plus(curr.amount.toString()),
|
|
13113
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13114
|
+
);
|
|
12982
13115
|
logger.verbose(`Sum of pools: ${sumPools.toString()}`);
|
|
12983
13116
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
12984
|
-
assert(
|
|
13117
|
+
assert(
|
|
13118
|
+
sumPools.lte(totalAssets.multipliedBy(1.00001).toString()),
|
|
13119
|
+
"Sum of pools.amount must be less than or equal to totalAssets"
|
|
13120
|
+
);
|
|
12985
13121
|
const sortedPools = [...pools].sort((a, b) => b.APY.netApy - a.APY.netApy);
|
|
12986
13122
|
const targetAmounts = {};
|
|
12987
13123
|
let remainingAssets = totalAssets;
|
|
@@ -13003,7 +13139,10 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13003
13139
|
assert(remainingAssets.lt(1e-5), "Remaining assets must be 0");
|
|
13004
13140
|
const changes = sortedPools.map((pool) => {
|
|
13005
13141
|
const target = targetAmounts[pool.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
13006
|
-
const change = Web3Number.fromWei(
|
|
13142
|
+
const change = Web3Number.fromWei(
|
|
13143
|
+
target.minus(pool.amount.toString()).toWei(),
|
|
13144
|
+
this.decimals()
|
|
13145
|
+
);
|
|
13007
13146
|
return {
|
|
13008
13147
|
pool_id: pool.pool_id,
|
|
13009
13148
|
changeAmt: change,
|
|
@@ -13012,14 +13151,21 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13012
13151
|
};
|
|
13013
13152
|
});
|
|
13014
13153
|
logger.verbose(`Changes: ${JSON.stringify(changes)}`);
|
|
13015
|
-
const sumChanges = changes.reduce(
|
|
13016
|
-
|
|
13154
|
+
const sumChanges = changes.reduce(
|
|
13155
|
+
(sum, c) => sum.plus(c.changeAmt.toString()),
|
|
13156
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13157
|
+
);
|
|
13158
|
+
const sumFinal = changes.reduce(
|
|
13159
|
+
(sum, c) => sum.plus(c.finalAmt.toString()),
|
|
13160
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13161
|
+
);
|
|
13017
13162
|
const hasChanges = changes.some((c) => !c.changeAmt.eq(0));
|
|
13018
13163
|
logger.verbose(`Sum of changes: ${sumChanges.toString()}`);
|
|
13019
13164
|
if (!sumChanges.eq(0)) throw new Error("Sum of changes must be zero");
|
|
13020
13165
|
logger.verbose(`Sum of final: ${sumFinal.toString()}`);
|
|
13021
13166
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13022
|
-
if (!sumFinal.eq(totalAssets.toString()))
|
|
13167
|
+
if (!sumFinal.eq(totalAssets.toString()))
|
|
13168
|
+
throw new Error("Sum of final amounts must equal total assets");
|
|
13023
13169
|
if (!hasChanges) throw new Error("No changes required");
|
|
13024
13170
|
const finalPools = pools.map((p) => {
|
|
13025
13171
|
const target = targetAmounts[p.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
@@ -13047,9 +13193,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13047
13193
|
if (p.changeAmt.eq(0)) return null;
|
|
13048
13194
|
actions.push({
|
|
13049
13195
|
pool_id: p.pool_id.address,
|
|
13050
|
-
feature: new CairoCustomEnum(
|
|
13196
|
+
feature: new CairoCustomEnum(
|
|
13197
|
+
p.isDeposit ? { DEPOSIT: {} } : { WITHDRAW: {} }
|
|
13198
|
+
),
|
|
13051
13199
|
token: this.asset().address.address,
|
|
13052
|
-
amount: uint2563.bnToUint256(
|
|
13200
|
+
amount: uint2563.bnToUint256(
|
|
13201
|
+
p.changeAmt.multipliedBy(p.isDeposit ? 1 : -1).toWei()
|
|
13202
|
+
)
|
|
13053
13203
|
});
|
|
13054
13204
|
});
|
|
13055
13205
|
if (actions.length === 0) return null;
|
|
@@ -13062,18 +13212,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13062
13212
|
const netYield = await this.netAPYGivenPools(pools);
|
|
13063
13213
|
const baseFlow = {
|
|
13064
13214
|
title: "Your Deposit",
|
|
13065
|
-
subItems: [
|
|
13215
|
+
subItems: [
|
|
13216
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
13217
|
+
{
|
|
13218
|
+
key: `Performance Fee`,
|
|
13219
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
13220
|
+
}
|
|
13221
|
+
],
|
|
13066
13222
|
linkedFlows: [],
|
|
13067
13223
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
13068
13224
|
};
|
|
13069
13225
|
let _pools = [...pools];
|
|
13070
|
-
_pools = _pools.sort(
|
|
13226
|
+
_pools = _pools.sort(
|
|
13227
|
+
(a, b) => Number(b.amount.toString()) - Number(a.amount.toString())
|
|
13228
|
+
);
|
|
13071
13229
|
_pools.forEach((p) => {
|
|
13072
13230
|
const flow = {
|
|
13073
13231
|
title: `Pool name: ${p.pool_name}`,
|
|
13074
13232
|
subItems: [
|
|
13075
13233
|
{ key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%` },
|
|
13076
|
-
{
|
|
13234
|
+
{
|
|
13235
|
+
key: "Weight",
|
|
13236
|
+
value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%`
|
|
13237
|
+
}
|
|
13077
13238
|
],
|
|
13078
13239
|
linkedFlows: [],
|
|
13079
13240
|
style: p.amount.greaterThan(0) ? { backgroundColor: "#35484f" /* Blue */.valueOf() } : { color: "gray" }
|
|
@@ -13105,7 +13266,12 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13105
13266
|
harvest.actualReward.toWei(),
|
|
13106
13267
|
this.address.address
|
|
13107
13268
|
);
|
|
13108
|
-
swapInfo = await avnu.getSwapInfo(
|
|
13269
|
+
swapInfo = await avnu.getSwapInfo(
|
|
13270
|
+
quote,
|
|
13271
|
+
this.address.address,
|
|
13272
|
+
0,
|
|
13273
|
+
this.address.address
|
|
13274
|
+
);
|
|
13109
13275
|
}
|
|
13110
13276
|
return [
|
|
13111
13277
|
this.contract.populate("harvest", [
|
|
@@ -13126,16 +13292,27 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13126
13292
|
* @returns {Promise<Array<{ vToken: ContractAddr, fee: Web3Number }>>} Array of fees deducted in different vTokens
|
|
13127
13293
|
*/
|
|
13128
13294
|
async getFee(allowedPools) {
|
|
13129
|
-
const assets = Web3Number.fromWei(
|
|
13130
|
-
|
|
13131
|
-
|
|
13295
|
+
const assets = Web3Number.fromWei(
|
|
13296
|
+
(await this.contract.total_assets()).toString(),
|
|
13297
|
+
this.asset().decimals
|
|
13298
|
+
);
|
|
13299
|
+
const totalSupply = Web3Number.fromWei(
|
|
13300
|
+
(await this.contract.total_supply()).toString(),
|
|
13301
|
+
this.asset().decimals
|
|
13302
|
+
);
|
|
13303
|
+
const prevIndex = Web3Number.fromWei(
|
|
13304
|
+
(await this.contract.get_previous_index()).toString(),
|
|
13305
|
+
18
|
|
13306
|
+
);
|
|
13132
13307
|
const currIndex = new Web3Number(1, 18).multipliedBy(assets).dividedBy(totalSupply);
|
|
13133
13308
|
logger.verbose(`Previous index: ${prevIndex.toString()}`);
|
|
13134
13309
|
logger.verbose(`Assets: ${assets.toString()}`);
|
|
13135
13310
|
logger.verbose(`Total supply: ${totalSupply.toString()}`);
|
|
13136
13311
|
logger.verbose(`Current index: ${currIndex.toNumber()}`);
|
|
13137
13312
|
if (currIndex.lt(prevIndex)) {
|
|
13138
|
-
logger.verbose(
|
|
13313
|
+
logger.verbose(
|
|
13314
|
+
`getFee::Current index is less than previous index, no fees to be deducted`
|
|
13315
|
+
);
|
|
13139
13316
|
return [];
|
|
13140
13317
|
}
|
|
13141
13318
|
const indexDiff = currIndex.minus(prevIndex);
|
|
@@ -13148,7 +13325,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13148
13325
|
return [];
|
|
13149
13326
|
}
|
|
13150
13327
|
const fees = [];
|
|
13151
|
-
let remainingFee = fee.plus(
|
|
13328
|
+
let remainingFee = fee.plus(
|
|
13329
|
+
Web3Number.fromWei("100", this.asset().decimals)
|
|
13330
|
+
);
|
|
13152
13331
|
for (const pool of allowedPools) {
|
|
13153
13332
|
const vToken = pool.v_token;
|
|
13154
13333
|
const balance = pool.amount;
|
|
@@ -13157,7 +13336,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13157
13336
|
break;
|
|
13158
13337
|
} else {
|
|
13159
13338
|
fees.push({ vToken, fee: Web3Number.fromWei(balance.toString(), 18) });
|
|
13160
|
-
remainingFee = remainingFee.minus(
|
|
13339
|
+
remainingFee = remainingFee.minus(
|
|
13340
|
+
Web3Number.fromWei(balance.toString(), 18)
|
|
13341
|
+
);
|
|
13161
13342
|
}
|
|
13162
13343
|
}
|
|
13163
13344
|
logger.verbose(`Fees: ${JSON.stringify(fees)}`);
|
|
@@ -13165,101 +13346,179 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13165
13346
|
}
|
|
13166
13347
|
};
|
|
13167
13348
|
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.";
|
|
13168
|
-
var _protocol = {
|
|
13349
|
+
var _protocol = {
|
|
13350
|
+
name: "Vesu",
|
|
13351
|
+
logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png"
|
|
13352
|
+
};
|
|
13169
13353
|
var _riskFactor = [
|
|
13170
13354
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
13171
13355
|
{ type: "Counterparty Risk" /* COUNTERPARTY_RISK */, value: 1, weight: 50 },
|
|
13172
13356
|
{ type: "Oracle Risk" /* ORACLE_RISK */, value: 0.5, weight: 25 }
|
|
13173
13357
|
];
|
|
13174
13358
|
var AUDIT_URL = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
13175
|
-
var
|
|
13176
|
-
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
type: "ERC4626",
|
|
13180
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
13181
|
-
protocols: [_protocol],
|
|
13182
|
-
auditUrl: AUDIT_URL,
|
|
13183
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13184
|
-
risk: {
|
|
13185
|
-
riskFactor: _riskFactor,
|
|
13186
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13187
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13359
|
+
var faqs = [
|
|
13360
|
+
{
|
|
13361
|
+
question: "What is the Vesu Rebalancing Strategy?",
|
|
13362
|
+
answer: "The Vesu Rebalancing Strategy is an automated investment strategy that diversifies your holdings across multiple Vesu pools. It optimizes yield by rebalancing assets based on pool performance while adhering to risk constraints."
|
|
13188
13363
|
},
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
}, {
|
|
13193
|
-
name: "Vesu Fusion ETH",
|
|
13194
|
-
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13195
|
-
address: ContractAddr.from("0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"),
|
|
13196
|
-
type: "ERC4626",
|
|
13197
|
-
auditUrl: AUDIT_URL,
|
|
13198
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "ETH")],
|
|
13199
|
-
protocols: [_protocol],
|
|
13200
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13201
|
-
risk: {
|
|
13202
|
-
riskFactor: _riskFactor,
|
|
13203
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13204
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13364
|
+
{
|
|
13365
|
+
question: "Will I earn Vesu points?",
|
|
13366
|
+
answer: "Yes, of course! You will earn Vesu points for your deposits."
|
|
13205
13367
|
},
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
},
|
|
13210
|
-
|
|
13211
|
-
|
|
13212
|
-
|
|
13213
|
-
|
|
13214
|
-
|
|
13215
|
-
|
|
13216
|
-
|
|
13217
|
-
|
|
13218
|
-
|
|
13219
|
-
|
|
13220
|
-
|
|
13221
|
-
|
|
13368
|
+
{
|
|
13369
|
+
question: "How does the strategy optimize yield?",
|
|
13370
|
+
answer: "The strategy calculates the weighted average APY across all pools and reallocates assets to maximize returns. It prioritizes high-performing pools while ensuring compliance with maximum weight constraints."
|
|
13371
|
+
},
|
|
13372
|
+
{
|
|
13373
|
+
question: "What are the risks associated with this strategy?",
|
|
13374
|
+
answer: "The strategy involves usual DeFi risks such as smart contract vulnerabilities, counterparty risks, and oracle inaccuracies. However, we try our best to reduce these risks through audits and careful pool selection."
|
|
13375
|
+
},
|
|
13376
|
+
{
|
|
13377
|
+
question: "How are fees calculated and deducted?",
|
|
13378
|
+
answer: "Fees are calculated based on the performance of the strategy and deducted proportionally from the total assets. We charge a 10% performance fee and is already accounted in the APY shown."
|
|
13379
|
+
},
|
|
13380
|
+
{
|
|
13381
|
+
question: "What happens if a pool exceeds its maximum weight?",
|
|
13382
|
+
answer: "If a pool exceeds its maximum weight, the strategy rebalances by withdrawing excess funds and reallocating them to other pools with available capacity."
|
|
13383
|
+
},
|
|
13384
|
+
{
|
|
13385
|
+
question: "Can I withdraw my assets at any time?",
|
|
13386
|
+
answer: "Yes, you can withdraw your assets at any time. In rare circumstances, if debt utilisation is high for certain pools on Vesu, it may not be possible to withdraw until markets restore balance."
|
|
13222
13387
|
},
|
|
13223
|
-
|
|
13224
|
-
|
|
13388
|
+
{
|
|
13389
|
+
question: "What happens to my Defi Spring STRK rewards?",
|
|
13390
|
+
answer: "STRK rewards are automatically harvested and reinvested into the strategy every week to maximize compounding returns."
|
|
13391
|
+
},
|
|
13392
|
+
{
|
|
13393
|
+
question: "Is the strategy audited?",
|
|
13394
|
+
answer: /* @__PURE__ */ jsxs("div", { children: [
|
|
13395
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
13396
|
+
/* @__PURE__ */ jsx("a", { href: "https://docs.strkfarm.com/p/strategies/vesu-fusion-rebalancing-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
13397
|
+
"."
|
|
13398
|
+
] })
|
|
13225
13399
|
}
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13400
|
+
];
|
|
13401
|
+
var VesuRebalanceStrategies = [
|
|
13402
|
+
{
|
|
13403
|
+
name: "Vesu Fusion STRK",
|
|
13404
|
+
description: _description.replace("{{TOKEN}}", "STRK"),
|
|
13405
|
+
address: ContractAddr.from(
|
|
13406
|
+
"0x7fb5bcb8525954a60fde4e8fb8220477696ce7117ef264775a1770e23571929"
|
|
13407
|
+
),
|
|
13408
|
+
launchBlock: 0,
|
|
13409
|
+
type: "ERC4626",
|
|
13410
|
+
depositTokens: [
|
|
13411
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
13412
|
+
],
|
|
13413
|
+
protocols: [_protocol],
|
|
13414
|
+
auditUrl: AUDIT_URL,
|
|
13415
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13416
|
+
risk: {
|
|
13417
|
+
riskFactor: _riskFactor,
|
|
13418
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13419
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13420
|
+
},
|
|
13421
|
+
additionalInfo: {
|
|
13422
|
+
feeBps: 1e3
|
|
13423
|
+
},
|
|
13424
|
+
faqs
|
|
13239
13425
|
},
|
|
13240
|
-
|
|
13241
|
-
|
|
13426
|
+
{
|
|
13427
|
+
name: "Vesu Fusion ETH",
|
|
13428
|
+
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13429
|
+
address: ContractAddr.from(
|
|
13430
|
+
"0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"
|
|
13431
|
+
),
|
|
13432
|
+
launchBlock: 0,
|
|
13433
|
+
type: "ERC4626",
|
|
13434
|
+
auditUrl: AUDIT_URL,
|
|
13435
|
+
depositTokens: [
|
|
13436
|
+
Global.getDefaultTokens().find((t) => t.symbol === "ETH")
|
|
13437
|
+
],
|
|
13438
|
+
protocols: [_protocol],
|
|
13439
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13440
|
+
risk: {
|
|
13441
|
+
riskFactor: _riskFactor,
|
|
13442
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13443
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13444
|
+
},
|
|
13445
|
+
additionalInfo: {
|
|
13446
|
+
feeBps: 1e3
|
|
13447
|
+
},
|
|
13448
|
+
faqs
|
|
13449
|
+
},
|
|
13450
|
+
{
|
|
13451
|
+
name: "Vesu Fusion USDC",
|
|
13452
|
+
description: _description.replace("{{TOKEN}}", "USDC"),
|
|
13453
|
+
address: ContractAddr.from(
|
|
13454
|
+
"0xa858c97e9454f407d1bd7c57472fc8d8d8449a777c822b41d18e387816f29c"
|
|
13455
|
+
),
|
|
13456
|
+
launchBlock: 0,
|
|
13457
|
+
type: "ERC4626",
|
|
13458
|
+
auditUrl: AUDIT_URL,
|
|
13459
|
+
depositTokens: [
|
|
13460
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC")
|
|
13461
|
+
],
|
|
13462
|
+
protocols: [_protocol],
|
|
13463
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13464
|
+
risk: {
|
|
13465
|
+
riskFactor: _riskFactor,
|
|
13466
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13467
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13468
|
+
},
|
|
13469
|
+
additionalInfo: {
|
|
13470
|
+
feeBps: 1e3
|
|
13471
|
+
},
|
|
13472
|
+
faqs
|
|
13473
|
+
},
|
|
13474
|
+
{
|
|
13475
|
+
name: "Vesu Fusion USDT",
|
|
13476
|
+
description: _description.replace("{{TOKEN}}", "USDT"),
|
|
13477
|
+
address: ContractAddr.from(
|
|
13478
|
+
"0x115e94e722cfc4c77a2f15c4aefb0928c1c0029e5a57570df24c650cb7cec2c"
|
|
13479
|
+
),
|
|
13480
|
+
launchBlock: 0,
|
|
13481
|
+
type: "ERC4626",
|
|
13482
|
+
depositTokens: [
|
|
13483
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
13484
|
+
],
|
|
13485
|
+
auditUrl: AUDIT_URL,
|
|
13486
|
+
protocols: [_protocol],
|
|
13487
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13488
|
+
risk: {
|
|
13489
|
+
riskFactor: _riskFactor,
|
|
13490
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13491
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13492
|
+
},
|
|
13493
|
+
additionalInfo: {
|
|
13494
|
+
feeBps: 1e3
|
|
13495
|
+
},
|
|
13496
|
+
faqs
|
|
13497
|
+
// }, {
|
|
13498
|
+
// name: 'Vesu Fusion WBTC',
|
|
13499
|
+
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13500
|
+
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13501
|
+
// type: 'ERC4626',
|
|
13502
|
+
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13503
|
+
// auditUrl: AUDIT_URL,
|
|
13504
|
+
// protocols: [_protocol],
|
|
13505
|
+
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13506
|
+
// risk: {
|
|
13507
|
+
// riskFactor: _riskFactor,
|
|
13508
|
+
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13509
|
+
// },
|
|
13510
|
+
// additionalInfo: {
|
|
13511
|
+
// feeBps: 1000,
|
|
13512
|
+
// },
|
|
13242
13513
|
}
|
|
13243
|
-
|
|
13244
|
-
// name: 'Vesu Fusion WBTC',
|
|
13245
|
-
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13246
|
-
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13247
|
-
// type: 'ERC4626',
|
|
13248
|
-
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13249
|
-
// auditUrl: AUDIT_URL,
|
|
13250
|
-
// protocols: [_protocol],
|
|
13251
|
-
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13252
|
-
// risk: {
|
|
13253
|
-
// riskFactor: _riskFactor,
|
|
13254
|
-
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13255
|
-
// },
|
|
13256
|
-
// additionalInfo: {
|
|
13257
|
-
// feeBps: 1000,
|
|
13258
|
-
// },
|
|
13259
|
-
}];
|
|
13514
|
+
];
|
|
13260
13515
|
|
|
13261
13516
|
// src/strategies/ekubo-cl-vault.tsx
|
|
13262
|
-
import {
|
|
13517
|
+
import {
|
|
13518
|
+
Contract as Contract6,
|
|
13519
|
+
num as num4,
|
|
13520
|
+
uint256 as uint2564
|
|
13521
|
+
} from "starknet";
|
|
13263
13522
|
|
|
13264
13523
|
// src/data/cl-vault.abi.json
|
|
13265
13524
|
var cl_vault_abi_default = [
|
|
@@ -18161,7 +18420,7 @@ var erc4626_abi_default = [
|
|
|
18161
18420
|
];
|
|
18162
18421
|
|
|
18163
18422
|
// src/strategies/ekubo-cl-vault.tsx
|
|
18164
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18423
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
18165
18424
|
var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
18166
18425
|
/**
|
|
18167
18426
|
* Creates a new VesuRebalance strategy instance.
|
|
@@ -18174,15 +18433,38 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18174
18433
|
super(config);
|
|
18175
18434
|
this.BASE_WEIGHT = 1e4;
|
|
18176
18435
|
this.pricer = pricer;
|
|
18177
|
-
assert(
|
|
18436
|
+
assert(
|
|
18437
|
+
metadata.depositTokens.length === 2,
|
|
18438
|
+
"EkuboCL only supports 2 deposit token"
|
|
18439
|
+
);
|
|
18178
18440
|
this.metadata = metadata;
|
|
18179
18441
|
this.address = metadata.address;
|
|
18180
|
-
this.contract = new Contract6(
|
|
18181
|
-
|
|
18442
|
+
this.contract = new Contract6(
|
|
18443
|
+
cl_vault_abi_default,
|
|
18444
|
+
this.address.address,
|
|
18445
|
+
this.config.provider
|
|
18446
|
+
);
|
|
18447
|
+
if (this.metadata.additionalInfo.lstContract) {
|
|
18448
|
+
this.lstContract = new Contract6(
|
|
18449
|
+
erc4626_abi_default,
|
|
18450
|
+
this.metadata.additionalInfo.lstContract.address,
|
|
18451
|
+
this.config.provider
|
|
18452
|
+
);
|
|
18453
|
+
} else {
|
|
18454
|
+
this.lstContract = null;
|
|
18455
|
+
}
|
|
18182
18456
|
const EKUBO_POSITION = "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
|
|
18183
|
-
this.ekuboPositionsContract = new Contract6(
|
|
18457
|
+
this.ekuboPositionsContract = new Contract6(
|
|
18458
|
+
ekubo_positions_abi_default,
|
|
18459
|
+
EKUBO_POSITION,
|
|
18460
|
+
this.config.provider
|
|
18461
|
+
);
|
|
18184
18462
|
const EKUBO_MATH = "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
|
|
18185
|
-
this.ekuboMathContract = new Contract6(
|
|
18463
|
+
this.ekuboMathContract = new Contract6(
|
|
18464
|
+
ekubo_math_abi_default,
|
|
18465
|
+
EKUBO_MATH,
|
|
18466
|
+
this.config.provider
|
|
18467
|
+
);
|
|
18186
18468
|
this.avnu = new AvnuWrapper();
|
|
18187
18469
|
}
|
|
18188
18470
|
async matchInputAmounts(amountInfo) {
|
|
@@ -18207,25 +18489,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18207
18489
|
/** Returns minimum amounts give given two amounts based on what can be added for liq */
|
|
18208
18490
|
async getMinDepositAmounts(amountInfo) {
|
|
18209
18491
|
const shares = await this.tokensToShares(amountInfo);
|
|
18210
|
-
const { amount0, amount1 } = await this.contract.call(
|
|
18492
|
+
const { amount0, amount1 } = await this.contract.call(
|
|
18493
|
+
"convert_to_assets",
|
|
18494
|
+
[uint2564.bnToUint256(shares.toWei())]
|
|
18495
|
+
);
|
|
18211
18496
|
return {
|
|
18212
18497
|
token0: {
|
|
18213
18498
|
tokenInfo: amountInfo.token0.tokenInfo,
|
|
18214
|
-
amount: Web3Number.fromWei(
|
|
18499
|
+
amount: Web3Number.fromWei(
|
|
18500
|
+
amount0.toString(),
|
|
18501
|
+
amountInfo.token0.tokenInfo.decimals
|
|
18502
|
+
)
|
|
18215
18503
|
},
|
|
18216
18504
|
token1: {
|
|
18217
18505
|
tokenInfo: amountInfo.token1.tokenInfo,
|
|
18218
|
-
amount: Web3Number.fromWei(
|
|
18506
|
+
amount: Web3Number.fromWei(
|
|
18507
|
+
amount1.toString(),
|
|
18508
|
+
amountInfo.token1.tokenInfo.decimals
|
|
18509
|
+
)
|
|
18219
18510
|
}
|
|
18220
18511
|
};
|
|
18221
18512
|
}
|
|
18222
18513
|
async depositCall(amountInfo, receiver) {
|
|
18223
18514
|
const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
|
|
18224
|
-
const token0Contract = new Contract6(
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18228
|
-
|
|
18515
|
+
const token0Contract = new Contract6(
|
|
18516
|
+
erc4626_abi_default,
|
|
18517
|
+
amountInfo.token0.tokenInfo.address.address,
|
|
18518
|
+
this.config.provider
|
|
18519
|
+
);
|
|
18520
|
+
const token1Contract = new Contract6(
|
|
18521
|
+
erc4626_abi_default,
|
|
18522
|
+
amountInfo.token1.tokenInfo.address.address,
|
|
18523
|
+
this.config.provider
|
|
18524
|
+
);
|
|
18525
|
+
const call1 = token0Contract.populate("approve", [
|
|
18526
|
+
this.address.address,
|
|
18527
|
+
uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei())
|
|
18528
|
+
]);
|
|
18529
|
+
const call2 = token1Contract.populate("approve", [
|
|
18530
|
+
this.address.address,
|
|
18531
|
+
uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei())
|
|
18532
|
+
]);
|
|
18533
|
+
const call3 = this.contract.populate("deposit", [
|
|
18534
|
+
uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei()),
|
|
18535
|
+
uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei()),
|
|
18536
|
+
receiver.address
|
|
18537
|
+
]);
|
|
18229
18538
|
const calls = [];
|
|
18230
18539
|
if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
|
|
18231
18540
|
if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
|
|
@@ -18240,25 +18549,29 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18240
18549
|
}
|
|
18241
18550
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
18242
18551
|
const shares = await this.tokensToShares(amountInfo);
|
|
18243
|
-
logger.verbose(
|
|
18244
|
-
|
|
18245
|
-
|
|
18246
|
-
|
|
18247
|
-
|
|
18552
|
+
logger.verbose(
|
|
18553
|
+
`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`
|
|
18554
|
+
);
|
|
18555
|
+
return [
|
|
18556
|
+
this.contract.populate("withdraw", [
|
|
18557
|
+
uint2564.bnToUint256(shares.toWei()),
|
|
18558
|
+
receiver.address
|
|
18559
|
+
])
|
|
18560
|
+
];
|
|
18248
18561
|
}
|
|
18249
18562
|
rebalanceCall(newBounds, swapParams) {
|
|
18250
|
-
return [
|
|
18251
|
-
|
|
18252
|
-
|
|
18253
|
-
|
|
18254
|
-
|
|
18255
|
-
|
|
18256
|
-
|
|
18563
|
+
return [
|
|
18564
|
+
this.contract.populate("rebalance", [
|
|
18565
|
+
{
|
|
18566
|
+
lower: _EkuboCLVault.tickToi129(Number(newBounds.lowerTick)),
|
|
18567
|
+
upper: _EkuboCLVault.tickToi129(Number(newBounds.upperTick))
|
|
18568
|
+
},
|
|
18569
|
+
swapParams
|
|
18570
|
+
])
|
|
18571
|
+
];
|
|
18257
18572
|
}
|
|
18258
18573
|
handleUnusedCall(swapParams) {
|
|
18259
|
-
return [this.contract.populate("handle_unused", [
|
|
18260
|
-
swapParams
|
|
18261
|
-
])];
|
|
18574
|
+
return [this.contract.populate("handle_unused", [swapParams])];
|
|
18262
18575
|
}
|
|
18263
18576
|
handleFeesCall() {
|
|
18264
18577
|
return [this.contract.populate("handle_fees", [])];
|
|
@@ -18273,16 +18586,20 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18273
18586
|
const priceNow = await this.getCurrentPrice(blockIdentifier);
|
|
18274
18587
|
let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
18275
18588
|
const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
18276
|
-
const blockBefore = blockNow - sinceBlocks;
|
|
18277
|
-
const adjustedSupplyNow = supplyNow.minus(
|
|
18278
|
-
|
|
18589
|
+
const blockBefore = Math.max(blockNow - sinceBlocks, this.metadata.launchBlock);
|
|
18590
|
+
const adjustedSupplyNow = supplyNow.minus(
|
|
18591
|
+
await this.getHarvestRewardShares(blockBefore, blockNow)
|
|
18592
|
+
);
|
|
18593
|
+
let blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
18594
|
+
blockBefore
|
|
18595
|
+
);
|
|
18279
18596
|
const tvlBefore = await this._getTVL(blockBefore);
|
|
18280
18597
|
const supplyBefore = await this.totalSupply(blockBefore);
|
|
18281
18598
|
const priceBefore = await this.getCurrentPrice(blockBefore);
|
|
18282
18599
|
const tvlInToken0Now = tvlNow.amount0.multipliedBy(priceNow.price).plus(tvlNow.amount1);
|
|
18283
|
-
const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow);
|
|
18600
|
+
const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow.toString());
|
|
18284
18601
|
const tvlInToken0Bf = tvlBefore.amount0.multipliedBy(priceBefore.price).plus(tvlBefore.amount1);
|
|
18285
|
-
const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore);
|
|
18602
|
+
const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore.toString());
|
|
18286
18603
|
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
18287
18604
|
logger.verbose(`tvlInToken0Now: ${tvlInToken0Now.toString()}`);
|
|
18288
18605
|
logger.verbose(`tvlInToken0Bf: ${tvlInToken0Bf.toString()}`);
|
|
@@ -18293,7 +18610,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18293
18610
|
logger.verbose(`Supply before: ${supplyBefore.toString()}`);
|
|
18294
18611
|
logger.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
|
|
18295
18612
|
logger.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
|
|
18296
|
-
const apyForGivenBlocks = Number(
|
|
18613
|
+
const apyForGivenBlocks = Number(
|
|
18614
|
+
tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)
|
|
18615
|
+
) / 1e4;
|
|
18297
18616
|
return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
|
|
18298
18617
|
}
|
|
18299
18618
|
async getHarvestRewardShares(fromBlock, toBlock) {
|
|
@@ -18311,25 +18630,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18311
18630
|
} else {
|
|
18312
18631
|
shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
|
|
18313
18632
|
}
|
|
18314
|
-
logger.verbose(
|
|
18633
|
+
logger.verbose(
|
|
18634
|
+
`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`
|
|
18635
|
+
);
|
|
18315
18636
|
}
|
|
18316
18637
|
return shares;
|
|
18317
18638
|
}
|
|
18318
18639
|
async balanceOf(user, blockIdentifier = "pending") {
|
|
18319
|
-
let bal = await this.contract.call("balance_of", [user.address]
|
|
18640
|
+
let bal = await this.contract.call("balance_of", [user.address], {
|
|
18641
|
+
blockIdentifier
|
|
18642
|
+
});
|
|
18320
18643
|
return Web3Number.fromWei(bal.toString(), 18);
|
|
18321
18644
|
}
|
|
18322
18645
|
async getUserTVL(user, blockIdentifier = "pending") {
|
|
18323
18646
|
let bal = await this.balanceOf(user, blockIdentifier);
|
|
18324
|
-
const assets = await this.contract.call(
|
|
18325
|
-
|
|
18326
|
-
|
|
18647
|
+
const assets = await this.contract.call(
|
|
18648
|
+
"convert_to_assets",
|
|
18649
|
+
[uint2564.bnToUint256(bal.toWei())],
|
|
18650
|
+
{
|
|
18651
|
+
blockIdentifier
|
|
18652
|
+
}
|
|
18653
|
+
);
|
|
18327
18654
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18328
18655
|
this.assertValidDepositTokens(poolKey);
|
|
18329
18656
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18330
18657
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18331
|
-
const amount0 = Web3Number.fromWei(
|
|
18332
|
-
|
|
18658
|
+
const amount0 = Web3Number.fromWei(
|
|
18659
|
+
assets.amount0.toString(),
|
|
18660
|
+
token0Info.decimals
|
|
18661
|
+
);
|
|
18662
|
+
const amount1 = Web3Number.fromWei(
|
|
18663
|
+
assets.amount1.toString(),
|
|
18664
|
+
token1Info.decimals
|
|
18665
|
+
);
|
|
18333
18666
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18334
18667
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18335
18668
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
@@ -18353,7 +18686,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18353
18686
|
blockIdentifier
|
|
18354
18687
|
});
|
|
18355
18688
|
const bounds = await this.getCurrentBounds(blockIdentifier);
|
|
18356
|
-
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18689
|
+
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18690
|
+
Web3Number.fromWei(result.toString(), 18),
|
|
18691
|
+
bounds,
|
|
18692
|
+
blockIdentifier
|
|
18693
|
+
);
|
|
18357
18694
|
return { amount0, amount1 };
|
|
18358
18695
|
}
|
|
18359
18696
|
async totalSupply(blockIdentifier = "pending") {
|
|
@@ -18363,8 +18700,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18363
18700
|
return Web3Number.fromWei(res.toString(), 18);
|
|
18364
18701
|
}
|
|
18365
18702
|
assertValidDepositTokens(poolKey) {
|
|
18366
|
-
assert(
|
|
18367
|
-
|
|
18703
|
+
assert(
|
|
18704
|
+
poolKey.token0.eq(this.metadata.depositTokens[0].address),
|
|
18705
|
+
"Expected token0 in depositTokens[0]"
|
|
18706
|
+
);
|
|
18707
|
+
assert(
|
|
18708
|
+
poolKey.token1.eq(this.metadata.depositTokens[1].address),
|
|
18709
|
+
"Expected token1 in depositTokens[1]"
|
|
18710
|
+
);
|
|
18368
18711
|
}
|
|
18369
18712
|
async getTVL(blockIdentifier = "pending") {
|
|
18370
18713
|
const { amount0, amount1 } = await this._getTVL(blockIdentifier);
|
|
@@ -18394,26 +18737,35 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18394
18737
|
const nftID = await this.getCurrentNFTID();
|
|
18395
18738
|
const poolKey = await this.getPoolKey();
|
|
18396
18739
|
const currentBounds = await this.getCurrentBounds();
|
|
18397
|
-
const result = await this.ekuboPositionsContract.call(
|
|
18398
|
-
|
|
18399
|
-
|
|
18400
|
-
|
|
18401
|
-
|
|
18402
|
-
|
|
18403
|
-
|
|
18404
|
-
|
|
18405
|
-
|
|
18406
|
-
|
|
18407
|
-
|
|
18408
|
-
|
|
18409
|
-
|
|
18410
|
-
|
|
18740
|
+
const result = await this.ekuboPositionsContract.call(
|
|
18741
|
+
"get_token_info",
|
|
18742
|
+
[
|
|
18743
|
+
nftID,
|
|
18744
|
+
{
|
|
18745
|
+
token0: poolKey.token0.address,
|
|
18746
|
+
token1: poolKey.token1.address,
|
|
18747
|
+
fee: poolKey.fee,
|
|
18748
|
+
tick_spacing: poolKey.tick_spacing,
|
|
18749
|
+
extension: poolKey.extension
|
|
18750
|
+
},
|
|
18751
|
+
{
|
|
18752
|
+
lower: _EkuboCLVault.tickToi129(Number(currentBounds.lowerTick)),
|
|
18753
|
+
upper: _EkuboCLVault.tickToi129(Number(currentBounds.upperTick))
|
|
18754
|
+
}
|
|
18755
|
+
]
|
|
18756
|
+
);
|
|
18411
18757
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18412
18758
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18413
18759
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18414
18760
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18415
|
-
const token0Web3 = Web3Number.fromWei(
|
|
18416
|
-
|
|
18761
|
+
const token0Web3 = Web3Number.fromWei(
|
|
18762
|
+
result.fees0.toString(),
|
|
18763
|
+
token0Info.decimals
|
|
18764
|
+
);
|
|
18765
|
+
const token1Web3 = Web3Number.fromWei(
|
|
18766
|
+
result.fees1.toString(),
|
|
18767
|
+
token1Info.decimals
|
|
18768
|
+
);
|
|
18417
18769
|
const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
|
|
18418
18770
|
const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
|
|
18419
18771
|
return {
|
|
@@ -18435,31 +18787,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18435
18787
|
return Number(result.salt.toString());
|
|
18436
18788
|
}
|
|
18437
18789
|
async truePrice() {
|
|
18438
|
-
|
|
18439
|
-
|
|
18440
|
-
|
|
18790
|
+
if (this.metadata.additionalInfo.truePrice) {
|
|
18791
|
+
return this.metadata.additionalInfo.truePrice;
|
|
18792
|
+
} else if (this.lstContract) {
|
|
18793
|
+
const result = await this.lstContract.call("convert_to_assets", [
|
|
18794
|
+
uint2564.bnToUint256(BigInt(1e18).toString())
|
|
18795
|
+
]);
|
|
18796
|
+
const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
|
|
18797
|
+
return truePrice;
|
|
18798
|
+
}
|
|
18799
|
+
throw new Error("No true price available");
|
|
18441
18800
|
}
|
|
18442
18801
|
async getCurrentPrice(blockIdentifier = "pending") {
|
|
18443
18802
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18444
18803
|
return this._getCurrentPrice(poolKey, blockIdentifier);
|
|
18445
18804
|
}
|
|
18446
18805
|
async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
|
|
18447
|
-
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18806
|
+
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18807
|
+
"get_pool_price",
|
|
18808
|
+
[
|
|
18809
|
+
{
|
|
18810
|
+
token0: poolKey.token0.address,
|
|
18811
|
+
token1: poolKey.token1.address,
|
|
18812
|
+
fee: poolKey.fee,
|
|
18813
|
+
tick_spacing: poolKey.tick_spacing,
|
|
18814
|
+
extension: poolKey.extension
|
|
18815
|
+
}
|
|
18816
|
+
],
|
|
18448
18817
|
{
|
|
18449
|
-
|
|
18450
|
-
token1: poolKey.token1.address,
|
|
18451
|
-
fee: poolKey.fee,
|
|
18452
|
-
tick_spacing: poolKey.tick_spacing,
|
|
18453
|
-
extension: poolKey.extension
|
|
18818
|
+
blockIdentifier
|
|
18454
18819
|
}
|
|
18455
|
-
|
|
18456
|
-
|
|
18457
|
-
|
|
18458
|
-
|
|
18459
|
-
console.log(
|
|
18820
|
+
);
|
|
18821
|
+
const sqrtRatio = _EkuboCLVault.div2Power128(
|
|
18822
|
+
BigInt(priceInfo.sqrt_ratio.toString())
|
|
18823
|
+
);
|
|
18824
|
+
console.log(
|
|
18825
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`
|
|
18826
|
+
);
|
|
18460
18827
|
const price = sqrtRatio * sqrtRatio;
|
|
18461
|
-
const tick = _EkuboCLVault.priceToTick(
|
|
18462
|
-
|
|
18828
|
+
const tick = _EkuboCLVault.priceToTick(
|
|
18829
|
+
price,
|
|
18830
|
+
true,
|
|
18831
|
+
Number(poolKey.tick_spacing)
|
|
18832
|
+
);
|
|
18833
|
+
console.log(
|
|
18834
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`
|
|
18835
|
+
);
|
|
18463
18836
|
return {
|
|
18464
18837
|
price,
|
|
18465
18838
|
tick: tick.mag * (tick.sign == 0 ? 1 : -1),
|
|
@@ -18499,7 +18872,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18499
18872
|
};
|
|
18500
18873
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18501
18874
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18502
|
-
assert(
|
|
18875
|
+
assert(
|
|
18876
|
+
token0Info.decimals == token1Info.decimals,
|
|
18877
|
+
"Tested only for equal decimals"
|
|
18878
|
+
);
|
|
18503
18879
|
this.poolKey = poolKey;
|
|
18504
18880
|
return poolKey;
|
|
18505
18881
|
}
|
|
@@ -18523,11 +18899,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18523
18899
|
async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
|
|
18524
18900
|
assert(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
|
|
18525
18901
|
const sampleLiq = 1e20;
|
|
18526
|
-
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18527
|
-
|
|
18902
|
+
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18903
|
+
Web3Number.fromWei(sampleLiq.toString(), 18),
|
|
18904
|
+
bounds
|
|
18905
|
+
);
|
|
18906
|
+
logger.verbose(
|
|
18907
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`
|
|
18908
|
+
);
|
|
18528
18909
|
assert(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
|
|
18529
18910
|
const price = await (await this.getCurrentPrice()).price;
|
|
18530
|
-
logger.verbose(
|
|
18911
|
+
logger.verbose(
|
|
18912
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`
|
|
18913
|
+
);
|
|
18531
18914
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18532
18915
|
if (sampleAmount1.eq(0)) {
|
|
18533
18916
|
return {
|
|
@@ -18557,12 +18940,22 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18557
18940
|
};
|
|
18558
18941
|
}
|
|
18559
18942
|
}
|
|
18560
|
-
assert(
|
|
18943
|
+
assert(
|
|
18944
|
+
sampleAmount0.decimals == sampleAmount1.decimals,
|
|
18945
|
+
"Sample amounts have different decimals"
|
|
18946
|
+
);
|
|
18561
18947
|
const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
|
|
18562
18948
|
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
18563
|
-
logger.verbose(
|
|
18949
|
+
logger.verbose(
|
|
18950
|
+
`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
|
|
18951
|
+
);
|
|
18564
18952
|
if (justUseInputAmount)
|
|
18565
|
-
return this._solveExpectedAmountsEq(
|
|
18953
|
+
return this._solveExpectedAmountsEq(
|
|
18954
|
+
amount0,
|
|
18955
|
+
amount1,
|
|
18956
|
+
ratioWeb3Number,
|
|
18957
|
+
price
|
|
18958
|
+
);
|
|
18566
18959
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18567
18960
|
const _amount1 = amount0.dividedBy(ratioWeb3Number);
|
|
18568
18961
|
return {
|
|
@@ -18578,7 +18971,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18578
18971
|
ratio
|
|
18579
18972
|
};
|
|
18580
18973
|
} else {
|
|
18581
|
-
throw new Error(
|
|
18974
|
+
throw new Error(
|
|
18975
|
+
"Both amounts are non-zero, cannot compute expected amounts"
|
|
18976
|
+
);
|
|
18582
18977
|
}
|
|
18583
18978
|
}
|
|
18584
18979
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
@@ -18595,34 +18990,65 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18595
18990
|
const erc20Mod = new ERC20(this.config);
|
|
18596
18991
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18597
18992
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18598
|
-
const token0Bal1 = await erc20Mod.balanceOf(
|
|
18599
|
-
|
|
18600
|
-
|
|
18993
|
+
const token0Bal1 = await erc20Mod.balanceOf(
|
|
18994
|
+
poolKey.token0,
|
|
18995
|
+
this.address.address,
|
|
18996
|
+
token0Info.decimals
|
|
18997
|
+
);
|
|
18998
|
+
const token1Bal1 = await erc20Mod.balanceOf(
|
|
18999
|
+
poolKey.token1,
|
|
19000
|
+
this.address.address,
|
|
19001
|
+
token1Info.decimals
|
|
19002
|
+
);
|
|
19003
|
+
logger.verbose(
|
|
19004
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`
|
|
19005
|
+
);
|
|
18601
19006
|
const token0Price = await this.pricer.getPrice(token0Info.symbol);
|
|
18602
19007
|
const token1Price = await this.pricer.getPrice(token1Info.symbol);
|
|
18603
19008
|
const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
|
|
18604
19009
|
const token1PriceUsd = token1Price.price * Number(token1Bal1.toFixed(13));
|
|
18605
19010
|
if (token0PriceUsd > 1 && token1PriceUsd > 1) {
|
|
18606
|
-
throw new Error(
|
|
19011
|
+
throw new Error(
|
|
19012
|
+
"Both tokens are non-zero and above $1, call handle_fees first"
|
|
19013
|
+
);
|
|
18607
19014
|
}
|
|
18608
19015
|
let token0Bal = token0Bal1;
|
|
18609
19016
|
let token1Bal = token1Bal1;
|
|
18610
19017
|
if (considerRebalance) {
|
|
18611
|
-
logger.verbose(
|
|
19018
|
+
logger.verbose(
|
|
19019
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`
|
|
19020
|
+
);
|
|
18612
19021
|
const tvl = await this.getTVL();
|
|
18613
19022
|
token0Bal = token0Bal.plus(tvl.token0.amount.toString());
|
|
18614
19023
|
token1Bal = token1Bal.plus(tvl.token1.amount.toString());
|
|
18615
19024
|
} else {
|
|
18616
|
-
logger.verbose(
|
|
19025
|
+
logger.verbose(
|
|
19026
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: false`
|
|
19027
|
+
);
|
|
18617
19028
|
}
|
|
18618
|
-
logger.verbose(
|
|
19029
|
+
logger.verbose(
|
|
19030
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
19031
|
+
);
|
|
18619
19032
|
const newBounds = await this.getNewBounds();
|
|
18620
|
-
logger.verbose(
|
|
18621
|
-
|
|
19033
|
+
logger.verbose(
|
|
19034
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`
|
|
19035
|
+
);
|
|
19036
|
+
return await this.getSwapInfoGivenAmounts(
|
|
19037
|
+
poolKey,
|
|
19038
|
+
token0Bal,
|
|
19039
|
+
token1Bal,
|
|
19040
|
+
newBounds
|
|
19041
|
+
);
|
|
18622
19042
|
}
|
|
18623
19043
|
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
|
|
18624
|
-
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
18625
|
-
|
|
19044
|
+
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
19045
|
+
token0Bal,
|
|
19046
|
+
token1Bal,
|
|
19047
|
+
bounds
|
|
19048
|
+
);
|
|
19049
|
+
logger.verbose(
|
|
19050
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19051
|
+
);
|
|
18626
19052
|
let retry = 0;
|
|
18627
19053
|
const maxRetry = 10;
|
|
18628
19054
|
while (retry < maxRetry) {
|
|
@@ -18639,9 +19065,15 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18639
19065
|
const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
|
|
18640
19066
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
18641
19067
|
const expectedRatio = expectedAmounts.ratio;
|
|
18642
|
-
logger.verbose(
|
|
18643
|
-
|
|
18644
|
-
|
|
19068
|
+
logger.verbose(
|
|
19069
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`
|
|
19070
|
+
);
|
|
19071
|
+
logger.verbose(
|
|
19072
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`
|
|
19073
|
+
);
|
|
19074
|
+
logger.verbose(
|
|
19075
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`
|
|
19076
|
+
);
|
|
18645
19077
|
if (amountToSell.eq(0)) {
|
|
18646
19078
|
return {
|
|
18647
19079
|
token_from_address: tokenToSell.address,
|
|
@@ -18655,23 +19087,62 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18655
19087
|
routes: []
|
|
18656
19088
|
};
|
|
18657
19089
|
}
|
|
18658
|
-
const quote = await this.avnu.getQuotes(
|
|
19090
|
+
const quote = await this.avnu.getQuotes(
|
|
19091
|
+
tokenToSell.address,
|
|
19092
|
+
tokenToBuy.address,
|
|
19093
|
+
amountToSell.toWei(),
|
|
19094
|
+
this.address.address
|
|
19095
|
+
);
|
|
18659
19096
|
if (remainingSellAmount.eq(0)) {
|
|
18660
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18661
|
-
|
|
19097
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19098
|
+
quote.buyAmount.toString(),
|
|
19099
|
+
tokenToBuyInfo.decimals
|
|
19100
|
+
).multipliedBy(0.9999);
|
|
19101
|
+
return await this.avnu.getSwapInfo(
|
|
19102
|
+
quote,
|
|
19103
|
+
this.address.address,
|
|
19104
|
+
0,
|
|
19105
|
+
this.address.address,
|
|
19106
|
+
minAmountOut.toWei()
|
|
19107
|
+
);
|
|
18662
19108
|
}
|
|
18663
|
-
const amountOut = Web3Number.fromWei(
|
|
19109
|
+
const amountOut = Web3Number.fromWei(
|
|
19110
|
+
quote.buyAmount.toString(),
|
|
19111
|
+
tokenToBuyInfo.decimals
|
|
19112
|
+
);
|
|
18664
19113
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
18665
19114
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
18666
|
-
logger.verbose(
|
|
18667
|
-
|
|
18668
|
-
|
|
19115
|
+
logger.verbose(
|
|
19116
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
19117
|
+
);
|
|
19118
|
+
logger.verbose(
|
|
19119
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swapPrice: ${swapPrice.toString()}`
|
|
19120
|
+
);
|
|
19121
|
+
logger.verbose(
|
|
19122
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newRatio: ${newRatio.toString()}`
|
|
19123
|
+
);
|
|
18669
19124
|
if (Number(newRatio.toString()) > expectedRatio * 1.0000001 || Number(newRatio.toString()) < expectedRatio * 0.9999999) {
|
|
18670
|
-
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
18671
|
-
|
|
19125
|
+
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
19126
|
+
token0Bal,
|
|
19127
|
+
token1Bal,
|
|
19128
|
+
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
19129
|
+
Number(swapPrice.toString())
|
|
19130
|
+
);
|
|
19131
|
+
logger.verbose(
|
|
19132
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19133
|
+
);
|
|
18672
19134
|
} else {
|
|
18673
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18674
|
-
|
|
19135
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19136
|
+
quote.buyAmount.toString(),
|
|
19137
|
+
tokenToBuyInfo.decimals
|
|
19138
|
+
).multipliedBy(0.9999);
|
|
19139
|
+
return await this.avnu.getSwapInfo(
|
|
19140
|
+
quote,
|
|
19141
|
+
this.address.address,
|
|
19142
|
+
0,
|
|
19143
|
+
this.address.address,
|
|
19144
|
+
minAmountOut.toWei()
|
|
19145
|
+
);
|
|
18675
19146
|
}
|
|
18676
19147
|
retry++;
|
|
18677
19148
|
}
|
|
@@ -18680,8 +19151,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18680
19151
|
/**
|
|
18681
19152
|
* Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
|
|
18682
19153
|
* Uses binary search approach to find optimal swap amount.
|
|
18683
|
-
*
|
|
18684
|
-
* @param newBounds - The new tick bounds to rebalance to
|
|
19154
|
+
*
|
|
19155
|
+
* @param newBounds - The new tick bounds to rebalance to
|
|
18685
19156
|
* @param swapInfo - Initial swap parameters for rebalancing
|
|
18686
19157
|
* @param acc - Account to estimate gas fees with
|
|
18687
19158
|
* @param retry - Current retry attempt number (default 0)
|
|
@@ -18708,7 +19179,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18708
19179
|
logger.error(`Rebalance failed after ${MAX_RETRIES} retries`);
|
|
18709
19180
|
throw err;
|
|
18710
19181
|
}
|
|
18711
|
-
logger.error(
|
|
19182
|
+
logger.error(
|
|
19183
|
+
`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`
|
|
19184
|
+
);
|
|
18712
19185
|
const newSwapInfo = { ...swapInfo };
|
|
18713
19186
|
const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
|
|
18714
19187
|
logger.verbose(`Current amount: ${currentAmount.toString()}`);
|
|
@@ -18800,23 +19273,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18800
19273
|
const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
|
|
18801
19274
|
const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
|
|
18802
19275
|
const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
|
|
18803
|
-
logger.verbose(
|
|
18804
|
-
|
|
18805
|
-
|
|
19276
|
+
logger.verbose(
|
|
19277
|
+
`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`
|
|
19278
|
+
);
|
|
19279
|
+
const result = await this.ekuboMathContract.call(
|
|
19280
|
+
"liquidity_delta_to_amount_delta",
|
|
19281
|
+
[
|
|
19282
|
+
uint2564.bnToUint256(currentPrice.sqrtRatio),
|
|
19283
|
+
{
|
|
19284
|
+
mag: liquidity.toWei(),
|
|
19285
|
+
sign: 0
|
|
19286
|
+
},
|
|
19287
|
+
uint2564.bnToUint256(
|
|
19288
|
+
_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()
|
|
19289
|
+
),
|
|
19290
|
+
uint2564.bnToUint256(
|
|
19291
|
+
_EkuboCLVault.priceToSqrtRatio(upperPrice).toString()
|
|
19292
|
+
)
|
|
19293
|
+
],
|
|
18806
19294
|
{
|
|
18807
|
-
|
|
18808
|
-
|
|
18809
|
-
|
|
18810
|
-
uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
|
|
18811
|
-
uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
|
|
18812
|
-
], {
|
|
18813
|
-
blockIdentifier
|
|
18814
|
-
});
|
|
19295
|
+
blockIdentifier
|
|
19296
|
+
}
|
|
19297
|
+
);
|
|
18815
19298
|
const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
|
|
18816
19299
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18817
19300
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18818
|
-
const amount0 = Web3Number.fromWei(
|
|
18819
|
-
|
|
19301
|
+
const amount0 = Web3Number.fromWei(
|
|
19302
|
+
_EkuboCLVault.i129ToNumber(result.amount0).toString(),
|
|
19303
|
+
token0Info.decimals
|
|
19304
|
+
);
|
|
19305
|
+
const amount1 = Web3Number.fromWei(
|
|
19306
|
+
_EkuboCLVault.i129ToNumber(result.amount1).toString(),
|
|
19307
|
+
token1Info.decimals
|
|
19308
|
+
);
|
|
18820
19309
|
return {
|
|
18821
19310
|
amount0,
|
|
18822
19311
|
amount1
|
|
@@ -18824,7 +19313,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18824
19313
|
}
|
|
18825
19314
|
async harvest(acc) {
|
|
18826
19315
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
18827
|
-
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19316
|
+
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19317
|
+
this.address
|
|
19318
|
+
);
|
|
18828
19319
|
const poolKey = await this.getPoolKey();
|
|
18829
19320
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18830
19321
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -18834,18 +19325,38 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18834
19325
|
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
18835
19326
|
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
18836
19327
|
const isToken1 = claim.token.eq(poolKey.token1);
|
|
18837
|
-
logger.verbose(
|
|
19328
|
+
logger.verbose(
|
|
19329
|
+
`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
|
|
19330
|
+
);
|
|
18838
19331
|
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
18839
19332
|
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
18840
|
-
logger.verbose(
|
|
18841
|
-
|
|
19333
|
+
logger.verbose(
|
|
19334
|
+
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
|
|
19335
|
+
);
|
|
19336
|
+
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
19337
|
+
poolKey,
|
|
19338
|
+
token0Amt,
|
|
19339
|
+
token1Amt,
|
|
19340
|
+
bounds
|
|
19341
|
+
);
|
|
18842
19342
|
swapInfo.token_to_address = token0Info.address.address;
|
|
18843
|
-
logger.verbose(
|
|
18844
|
-
|
|
19343
|
+
logger.verbose(
|
|
19344
|
+
`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
19345
|
+
);
|
|
19346
|
+
logger.verbose(
|
|
19347
|
+
`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
19348
|
+
);
|
|
18845
19349
|
const harvestEstimateCall = async (swapInfo1) => {
|
|
18846
|
-
const swap1Amount = Web3Number.fromWei(
|
|
19350
|
+
const swap1Amount = Web3Number.fromWei(
|
|
19351
|
+
uint2564.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
19352
|
+
18
|
|
19353
|
+
// cause its always STRK?
|
|
19354
|
+
);
|
|
18847
19355
|
const remainingAmount = postFeeAmount.minus(swap1Amount);
|
|
18848
|
-
const swapInfo2 = {
|
|
19356
|
+
const swapInfo2 = {
|
|
19357
|
+
...swapInfo,
|
|
19358
|
+
token_from_amount: uint2564.bnToUint256(remainingAmount.toWei())
|
|
19359
|
+
};
|
|
18849
19360
|
swapInfo2.token_to_address = token1Info.address.address;
|
|
18850
19361
|
const calldata = [
|
|
18851
19362
|
claim.rewardsContract.address,
|
|
@@ -18858,11 +19369,23 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18858
19369
|
swapInfo,
|
|
18859
19370
|
swapInfo2
|
|
18860
19371
|
];
|
|
18861
|
-
logger.verbose(
|
|
19372
|
+
logger.verbose(
|
|
19373
|
+
`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
19374
|
+
calldata
|
|
19375
|
+
)}`
|
|
19376
|
+
);
|
|
18862
19377
|
return [this.contract.populate("harvest", calldata)];
|
|
18863
19378
|
};
|
|
18864
|
-
const _callsFinal = await this.rebalanceIter(
|
|
18865
|
-
|
|
19379
|
+
const _callsFinal = await this.rebalanceIter(
|
|
19380
|
+
swapInfo,
|
|
19381
|
+
acc,
|
|
19382
|
+
harvestEstimateCall
|
|
19383
|
+
);
|
|
19384
|
+
logger.verbose(
|
|
19385
|
+
`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|
|
19386
|
+
_callsFinal
|
|
19387
|
+
)}`
|
|
19388
|
+
);
|
|
18866
19389
|
calls.push(..._callsFinal);
|
|
18867
19390
|
}
|
|
18868
19391
|
return calls;
|
|
@@ -18872,24 +19395,37 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18872
19395
|
const poolKey = await this.getPoolKey();
|
|
18873
19396
|
const linkedFlow = {
|
|
18874
19397
|
title: this.metadata.name,
|
|
18875
|
-
subItems: [
|
|
19398
|
+
subItems: [
|
|
19399
|
+
{
|
|
19400
|
+
key: "Pool",
|
|
19401
|
+
value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing`
|
|
19402
|
+
}
|
|
19403
|
+
],
|
|
18876
19404
|
linkedFlows: [],
|
|
18877
19405
|
style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
|
|
18878
19406
|
};
|
|
18879
19407
|
const baseFlow = {
|
|
18880
19408
|
id: "base",
|
|
18881
19409
|
title: "Your Deposit",
|
|
18882
|
-
subItems: [
|
|
19410
|
+
subItems: [
|
|
19411
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
19412
|
+
{
|
|
19413
|
+
key: `Performance Fee`,
|
|
19414
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
19415
|
+
}
|
|
19416
|
+
],
|
|
18883
19417
|
linkedFlows: [linkedFlow],
|
|
18884
19418
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
18885
19419
|
};
|
|
18886
19420
|
const rebalanceFlow = {
|
|
18887
19421
|
id: "rebalance",
|
|
18888
19422
|
title: "Automated Rebalance",
|
|
18889
|
-
subItems: [
|
|
18890
|
-
|
|
18891
|
-
|
|
18892
|
-
|
|
19423
|
+
subItems: [
|
|
19424
|
+
{
|
|
19425
|
+
key: "Range selection",
|
|
19426
|
+
value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
|
|
19427
|
+
}
|
|
19428
|
+
],
|
|
18893
19429
|
linkedFlows: [linkedFlow],
|
|
18894
19430
|
style: { backgroundColor: "purple" /* Green */.valueOf() }
|
|
18895
19431
|
};
|
|
@@ -18897,43 +19433,145 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18897
19433
|
}
|
|
18898
19434
|
};
|
|
18899
19435
|
var _description2 = "Deploys your {{POOL_NAME}} into an Ekubo liquidity pool, automatically rebalancing positions around the current price to optimize yield and reduce the need for manual adjustments. Trading fees and DeFi Spring rewards are automatically compounded back into the strategy. In return, you receive an ERC-20 token representing your share of the strategy. The APY is calculated based on 7-day historical performance.";
|
|
18900
|
-
var _protocol2 = {
|
|
19436
|
+
var _protocol2 = {
|
|
19437
|
+
name: "Ekubo",
|
|
19438
|
+
logo: "https://app.ekubo.org/favicon.ico"
|
|
19439
|
+
};
|
|
18901
19440
|
var _riskFactor2 = [
|
|
18902
19441
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
18903
19442
|
{ type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
|
|
18904
19443
|
];
|
|
19444
|
+
var _riskFactorStable = [
|
|
19445
|
+
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 }
|
|
19446
|
+
];
|
|
18905
19447
|
var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
18906
|
-
var
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
19448
|
+
var faqs2 = [
|
|
19449
|
+
{
|
|
19450
|
+
question: "What is the Ekubo CL Vault strategy?",
|
|
19451
|
+
answer: "The Ekubo CL Vault strategy deploys your assets into an Ekubo liquidity pool, automatically rebalancing positions around the current price to optimize yield and reduce manual adjustments."
|
|
19452
|
+
},
|
|
19453
|
+
{
|
|
19454
|
+
question: "How are trading fees and rewards handled?",
|
|
19455
|
+
answer: "Trading fees and DeFi Spring rewards are automatically compounded back into the strategy, increasing your overall returns."
|
|
19456
|
+
},
|
|
19457
|
+
{
|
|
19458
|
+
question: "What happens during withdrawal?",
|
|
19459
|
+
answer: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices."
|
|
19460
|
+
},
|
|
19461
|
+
{
|
|
19462
|
+
question: "Is the strategy audited?",
|
|
19463
|
+
answer: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19464
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
19465
|
+
/* @__PURE__ */ jsx2("a", { href: "https://docs.strkfarm.com/p/ekubo-cl-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
19466
|
+
"."
|
|
18913
19467
|
] })
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
|
|
18918
|
-
|
|
18919
|
-
|
|
18920
|
-
|
|
18921
|
-
|
|
18922
|
-
|
|
18923
|
-
|
|
18924
|
-
|
|
18925
|
-
|
|
19468
|
+
}
|
|
19469
|
+
];
|
|
19470
|
+
var EkuboCLVaultStrategies = [
|
|
19471
|
+
{
|
|
19472
|
+
name: "Ekubo xSTRK/STRK",
|
|
19473
|
+
description: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19474
|
+
/* @__PURE__ */ jsx2("p", { children: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK") }),
|
|
19475
|
+
/* @__PURE__ */ jsxs2(
|
|
19476
|
+
"ul",
|
|
19477
|
+
{
|
|
19478
|
+
style: {
|
|
19479
|
+
marginLeft: "20px",
|
|
19480
|
+
listStyle: "circle",
|
|
19481
|
+
fontSize: "12px"
|
|
19482
|
+
},
|
|
19483
|
+
children: [
|
|
19484
|
+
/* @__PURE__ */ jsx2("li", { style: { marginTop: "10px" }, children: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices." }),
|
|
19485
|
+
/* @__PURE__ */ jsx2("li", { style: { marginTop: "10px" }, children: "Sometimes you might see a negative APY \u2014 this is usually not a big deal. It happens when xSTRK's price drops on DEXes, but things typically bounce back within a few days or a week." })
|
|
19486
|
+
]
|
|
19487
|
+
}
|
|
19488
|
+
)
|
|
19489
|
+
] }),
|
|
19490
|
+
address: ContractAddr.from(
|
|
19491
|
+
"0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"
|
|
19492
|
+
),
|
|
19493
|
+
launchBlock: 1209881,
|
|
19494
|
+
type: "Other",
|
|
19495
|
+
// must be same order as poolKey token0 and token1
|
|
19496
|
+
depositTokens: [
|
|
19497
|
+
Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"),
|
|
19498
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
19499
|
+
],
|
|
19500
|
+
protocols: [_protocol2],
|
|
19501
|
+
auditUrl: AUDIT_URL2,
|
|
19502
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
19503
|
+
risk: {
|
|
19504
|
+
riskFactor: _riskFactor2,
|
|
19505
|
+
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19506
|
+
notARisks: getNoRiskTags(_riskFactor2)
|
|
19507
|
+
},
|
|
19508
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19509
|
+
additionalInfo: {
|
|
19510
|
+
newBounds: {
|
|
19511
|
+
lower: -1,
|
|
19512
|
+
upper: 1
|
|
19513
|
+
},
|
|
19514
|
+
lstContract: ContractAddr.from(
|
|
19515
|
+
"0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
|
|
19516
|
+
),
|
|
19517
|
+
feeBps: 1e3
|
|
19518
|
+
},
|
|
19519
|
+
faqs: [
|
|
19520
|
+
...faqs2,
|
|
19521
|
+
{
|
|
19522
|
+
question: "Why might I see a negative APY?",
|
|
19523
|
+
answer: "A negative APY can occur when xSTRK's price drops on DEXes. This is usually temporary and tends to recover within a few days or a week."
|
|
19524
|
+
}
|
|
19525
|
+
]
|
|
18926
19526
|
},
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
18930
|
-
|
|
18931
|
-
|
|
19527
|
+
{
|
|
19528
|
+
name: "Ekubo USDC/USDT",
|
|
19529
|
+
description: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19530
|
+
/* @__PURE__ */ jsx2("p", { children: _description2.replace("{{POOL_NAME}}", "USDC/USDT") }),
|
|
19531
|
+
/* @__PURE__ */ jsx2(
|
|
19532
|
+
"ul",
|
|
19533
|
+
{
|
|
19534
|
+
style: {
|
|
19535
|
+
marginLeft: "20px",
|
|
19536
|
+
listStyle: "circle",
|
|
19537
|
+
fontSize: "12px"
|
|
19538
|
+
},
|
|
19539
|
+
children: /* @__PURE__ */ jsx2("li", { style: { marginTop: "10px" }, children: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices." })
|
|
19540
|
+
}
|
|
19541
|
+
)
|
|
19542
|
+
] }),
|
|
19543
|
+
address: ContractAddr.from(
|
|
19544
|
+
"0xd647ed735f0db52f2a5502b6e06ed21dc4284a43a36af4b60d3c80fbc56c91"
|
|
19545
|
+
),
|
|
19546
|
+
launchBlock: 1385576,
|
|
19547
|
+
type: "Other",
|
|
19548
|
+
// must be same order as poolKey token0 and token1
|
|
19549
|
+
depositTokens: [
|
|
19550
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC"),
|
|
19551
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
19552
|
+
],
|
|
19553
|
+
protocols: [_protocol2],
|
|
19554
|
+
auditUrl: AUDIT_URL2,
|
|
19555
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
19556
|
+
risk: {
|
|
19557
|
+
riskFactor: _riskFactorStable,
|
|
19558
|
+
netRisk: _riskFactorStable.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactorStable.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19559
|
+
notARisks: getNoRiskTags(_riskFactorStable)
|
|
18932
19560
|
},
|
|
18933
|
-
|
|
18934
|
-
|
|
19561
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19562
|
+
additionalInfo: {
|
|
19563
|
+
newBounds: {
|
|
19564
|
+
lower: -1,
|
|
19565
|
+
upper: 1
|
|
19566
|
+
},
|
|
19567
|
+
truePrice: 1,
|
|
19568
|
+
feeBps: 1e3
|
|
19569
|
+
},
|
|
19570
|
+
faqs: [
|
|
19571
|
+
...faqs2
|
|
19572
|
+
]
|
|
18935
19573
|
}
|
|
18936
|
-
|
|
19574
|
+
];
|
|
18937
19575
|
export {
|
|
18938
19576
|
AutoCompounderSTRK,
|
|
18939
19577
|
AvnuWrapper,
|
|
@@ -18960,6 +19598,5 @@ export {
|
|
|
18960
19598
|
getMainnetConfig,
|
|
18961
19599
|
getNoRiskTags,
|
|
18962
19600
|
getRiskColor,
|
|
18963
|
-
getRiskExplaination
|
|
18964
|
-
logger
|
|
19601
|
+
getRiskExplaination
|
|
18965
19602
|
};
|