@strkfarm/sdk 1.0.37 → 1.0.38
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 +236 -112
- package/dist/cli.mjs +241 -113
- package/dist/index.browser.global.js +869 -312
- package/dist/index.browser.mjs +870 -309
- package/dist/index.d.ts +9 -4
- package/dist/index.js +865 -308
- package/dist/index.mjs +870 -309
- package/package.json +1 -1
- package/src/interfaces/common.ts +111 -98
- package/src/strategies/ekubo-cl-vault.tsx +1434 -923
- package/src/strategies/vesu-rebalance.tsx +937 -610
package/dist/index.mjs
CHANGED
|
@@ -2026,7 +2026,9 @@ var getRiskColor = (risk) => {
|
|
|
2026
2026
|
};
|
|
2027
2027
|
var getNoRiskTags = (risks) => {
|
|
2028
2028
|
const noRisks1 = risks.filter((risk) => risk.value === 0).map((risk) => risk.type);
|
|
2029
|
-
const noRisks2 = Object.values(RiskType).filter(
|
|
2029
|
+
const noRisks2 = Object.values(RiskType).filter(
|
|
2030
|
+
(risk) => !risks.map((risk2) => risk2.type).includes(risk)
|
|
2031
|
+
);
|
|
2030
2032
|
const mergedUnique = [.../* @__PURE__ */ new Set([...noRisks1, ...noRisks2])];
|
|
2031
2033
|
return mergedUnique.map((risk) => `No ${risk}`);
|
|
2032
2034
|
};
|
|
@@ -12718,6 +12720,7 @@ var vesu_pools_default = {
|
|
|
12718
12720
|
};
|
|
12719
12721
|
|
|
12720
12722
|
// src/strategies/vesu-rebalance.tsx
|
|
12723
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12721
12724
|
var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
12722
12725
|
// 10000 bps = 100%
|
|
12723
12726
|
/**
|
|
@@ -12731,10 +12734,17 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12731
12734
|
super(config);
|
|
12732
12735
|
this.BASE_WEIGHT = 1e4;
|
|
12733
12736
|
this.pricer = pricer;
|
|
12734
|
-
assert(
|
|
12737
|
+
assert(
|
|
12738
|
+
metadata.depositTokens.length === 1,
|
|
12739
|
+
"VesuRebalance only supports 1 deposit token"
|
|
12740
|
+
);
|
|
12735
12741
|
this.metadata = metadata;
|
|
12736
12742
|
this.address = metadata.address;
|
|
12737
|
-
this.contract = new Contract5(
|
|
12743
|
+
this.contract = new Contract5(
|
|
12744
|
+
vesu_rebalance_abi_default,
|
|
12745
|
+
this.address.address,
|
|
12746
|
+
this.config.provider
|
|
12747
|
+
);
|
|
12738
12748
|
}
|
|
12739
12749
|
/**
|
|
12740
12750
|
* Creates a deposit call to the strategy contract.
|
|
@@ -12743,10 +12753,23 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12743
12753
|
* @returns Populated contract call for deposit
|
|
12744
12754
|
*/
|
|
12745
12755
|
async depositCall(amountInfo, receiver) {
|
|
12746
|
-
assert(
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12756
|
+
assert(
|
|
12757
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
12758
|
+
"Deposit token mismatch"
|
|
12759
|
+
);
|
|
12760
|
+
const assetContract = new Contract5(
|
|
12761
|
+
vesu_rebalance_abi_default,
|
|
12762
|
+
this.asset().address.address,
|
|
12763
|
+
this.config.provider
|
|
12764
|
+
);
|
|
12765
|
+
const call1 = assetContract.populate("approve", [
|
|
12766
|
+
this.address.address,
|
|
12767
|
+
uint2563.bnToUint256(amountInfo.amount.toWei())
|
|
12768
|
+
]);
|
|
12769
|
+
const call2 = this.contract.populate("deposit", [
|
|
12770
|
+
uint2563.bnToUint256(amountInfo.amount.toWei()),
|
|
12771
|
+
receiver.address
|
|
12772
|
+
]);
|
|
12750
12773
|
return [call1, call2];
|
|
12751
12774
|
}
|
|
12752
12775
|
/**
|
|
@@ -12757,7 +12780,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12757
12780
|
* @returns Populated contract call for withdrawal
|
|
12758
12781
|
*/
|
|
12759
12782
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
12760
|
-
return [
|
|
12783
|
+
return [
|
|
12784
|
+
this.contract.populate("withdraw", [
|
|
12785
|
+
uint2563.bnToUint256(amountInfo.amount.toWei()),
|
|
12786
|
+
receiver.address,
|
|
12787
|
+
owner.address
|
|
12788
|
+
])
|
|
12789
|
+
];
|
|
12761
12790
|
}
|
|
12762
12791
|
/**
|
|
12763
12792
|
* Returns the underlying asset token of the strategy.
|
|
@@ -12780,9 +12809,16 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12780
12809
|
*/
|
|
12781
12810
|
async getUserTVL(user) {
|
|
12782
12811
|
const shares = await this.contract.balanceOf(user.address);
|
|
12783
|
-
const assets = await this.contract.convert_to_assets(
|
|
12784
|
-
|
|
12785
|
-
|
|
12812
|
+
const assets = await this.contract.convert_to_assets(
|
|
12813
|
+
uint2563.bnToUint256(shares)
|
|
12814
|
+
);
|
|
12815
|
+
const amount = Web3Number.fromWei(
|
|
12816
|
+
assets.toString(),
|
|
12817
|
+
this.metadata.depositTokens[0].decimals
|
|
12818
|
+
);
|
|
12819
|
+
let price = await this.pricer.getPrice(
|
|
12820
|
+
this.metadata.depositTokens[0].symbol
|
|
12821
|
+
);
|
|
12786
12822
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12787
12823
|
return {
|
|
12788
12824
|
tokenInfo: this.asset(),
|
|
@@ -12796,8 +12832,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12796
12832
|
*/
|
|
12797
12833
|
async getTVL() {
|
|
12798
12834
|
const assets = await this.contract.total_assets();
|
|
12799
|
-
const amount = Web3Number.fromWei(
|
|
12800
|
-
|
|
12835
|
+
const amount = Web3Number.fromWei(
|
|
12836
|
+
assets.toString(),
|
|
12837
|
+
this.metadata.depositTokens[0].decimals
|
|
12838
|
+
);
|
|
12839
|
+
let price = await this.pricer.getPrice(
|
|
12840
|
+
this.metadata.depositTokens[0].symbol
|
|
12841
|
+
);
|
|
12801
12842
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12802
12843
|
return {
|
|
12803
12844
|
tokenInfo: this.asset(),
|
|
@@ -12823,51 +12864,104 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12823
12864
|
return pools;
|
|
12824
12865
|
}
|
|
12825
12866
|
async getPoolInfo(p, pools, vesuPositions, totalAssets, isErrorPositionsAPI, isErrorPoolsAPI) {
|
|
12826
|
-
const vesuPosition = vesuPositions.find(
|
|
12867
|
+
const vesuPosition = vesuPositions.find(
|
|
12868
|
+
(d) => d.pool.id.toString() === num3.getDecimalString(p.pool_id.address.toString())
|
|
12869
|
+
);
|
|
12827
12870
|
const _pool = pools.find((d) => {
|
|
12828
|
-
logger.verbose(
|
|
12871
|
+
logger.verbose(
|
|
12872
|
+
`pool check: ${d.id == num3.getDecimalString(p.pool_id.address.toString())}, id: ${d.id}, pool_id: ${num3.getDecimalString(
|
|
12873
|
+
p.pool_id.address.toString()
|
|
12874
|
+
)}`
|
|
12875
|
+
);
|
|
12829
12876
|
return d.id == num3.getDecimalString(p.pool_id.address.toString());
|
|
12830
12877
|
});
|
|
12831
12878
|
logger.verbose(`pool: ${JSON.stringify(_pool)}`);
|
|
12832
12879
|
logger.verbose(typeof _pool);
|
|
12833
12880
|
logger.verbose(`name: ${_pool?.name}`);
|
|
12834
12881
|
const name = _pool?.name;
|
|
12835
|
-
logger.verbose(
|
|
12836
|
-
|
|
12882
|
+
logger.verbose(
|
|
12883
|
+
`name2: ${name}, ${!name ? true : false}, ${name?.length}, ${typeof name}`
|
|
12884
|
+
);
|
|
12885
|
+
const assetInfo = _pool?.assets.find(
|
|
12886
|
+
(d) => this.asset().address.eqString(d.address)
|
|
12887
|
+
);
|
|
12837
12888
|
if (!name) {
|
|
12838
12889
|
logger.verbose(`Pool not found`);
|
|
12839
12890
|
throw new Error(`Pool name ${p.pool_id.address.toString()} not found`);
|
|
12840
12891
|
}
|
|
12841
12892
|
if (!assetInfo) {
|
|
12842
|
-
throw new Error(
|
|
12893
|
+
throw new Error(
|
|
12894
|
+
`Asset ${this.asset().address.toString()} not found in pool ${p.pool_id.address.toString()}`
|
|
12895
|
+
);
|
|
12843
12896
|
}
|
|
12844
|
-
let vTokenContract = new Contract5(
|
|
12897
|
+
let vTokenContract = new Contract5(
|
|
12898
|
+
vesu_rebalance_abi_default,
|
|
12899
|
+
p.v_token.address,
|
|
12900
|
+
this.config.provider
|
|
12901
|
+
);
|
|
12845
12902
|
const bal = await vTokenContract.balanceOf(this.address.address);
|
|
12846
|
-
const assets = await vTokenContract.convert_to_assets(
|
|
12903
|
+
const assets = await vTokenContract.convert_to_assets(
|
|
12904
|
+
uint2563.bnToUint256(bal.toString())
|
|
12905
|
+
);
|
|
12847
12906
|
logger.verbose(`Collateral: ${JSON.stringify(vesuPosition?.collateral)}`);
|
|
12848
12907
|
logger.verbose(`supplyApy: ${JSON.stringify(assetInfo?.stats.supplyApy)}`);
|
|
12849
|
-
logger.verbose(
|
|
12850
|
-
|
|
12851
|
-
|
|
12908
|
+
logger.verbose(
|
|
12909
|
+
`defiSpringSupplyApr: ${JSON.stringify(
|
|
12910
|
+
assetInfo?.stats.defiSpringSupplyApr
|
|
12911
|
+
)}`
|
|
12912
|
+
);
|
|
12913
|
+
logger.verbose(
|
|
12914
|
+
`currentUtilization: ${JSON.stringify(
|
|
12915
|
+
assetInfo?.stats.currentUtilization
|
|
12916
|
+
)}`
|
|
12917
|
+
);
|
|
12918
|
+
logger.verbose(
|
|
12919
|
+
`maxUtilization: ${JSON.stringify(assetInfo?.config.maxUtilization)}`
|
|
12920
|
+
);
|
|
12852
12921
|
const item = {
|
|
12853
12922
|
pool_id: p.pool_id,
|
|
12854
12923
|
pool_name: _pool?.name,
|
|
12855
12924
|
max_weight: p.max_weight,
|
|
12856
|
-
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12925
|
+
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12926
|
+
Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)
|
|
12927
|
+
),
|
|
12857
12928
|
v_token: p.v_token,
|
|
12858
12929
|
amount: Web3Number.fromWei(assets.toString(), this.decimals()),
|
|
12859
|
-
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
12930
|
+
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
12931
|
+
vesuPosition.collateral.usdPrice.value,
|
|
12932
|
+
vesuPosition.collateral.usdPrice.decimals
|
|
12933
|
+
),
|
|
12860
12934
|
APY: isErrorPoolsAPI || !assetInfo ? {
|
|
12861
12935
|
baseApy: 0,
|
|
12862
12936
|
defiSpringApy: 0,
|
|
12863
12937
|
netApy: 0
|
|
12864
12938
|
} : {
|
|
12865
|
-
baseApy: Number(
|
|
12866
|
-
|
|
12939
|
+
baseApy: Number(
|
|
12940
|
+
Web3Number.fromWei(
|
|
12941
|
+
assetInfo.stats.supplyApy.value,
|
|
12942
|
+
assetInfo.stats.supplyApy.decimals
|
|
12943
|
+
).toFixed(6)
|
|
12944
|
+
),
|
|
12945
|
+
defiSpringApy: assetInfo.stats.defiSpringSupplyApr ? Number(
|
|
12946
|
+
Web3Number.fromWei(
|
|
12947
|
+
assetInfo.stats.defiSpringSupplyApr.value,
|
|
12948
|
+
assetInfo.stats.defiSpringSupplyApr.decimals
|
|
12949
|
+
).toFixed(6)
|
|
12950
|
+
) : 0,
|
|
12867
12951
|
netApy: 0
|
|
12868
12952
|
},
|
|
12869
|
-
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12870
|
-
|
|
12953
|
+
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12954
|
+
Web3Number.fromWei(
|
|
12955
|
+
assetInfo.stats.currentUtilization.value,
|
|
12956
|
+
assetInfo.stats.currentUtilization.decimals
|
|
12957
|
+
).toFixed(6)
|
|
12958
|
+
),
|
|
12959
|
+
maxUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12960
|
+
Web3Number.fromWei(
|
|
12961
|
+
assetInfo.config.maxUtilization.value,
|
|
12962
|
+
assetInfo.config.maxUtilization.decimals
|
|
12963
|
+
).toFixed(6)
|
|
12964
|
+
)
|
|
12871
12965
|
};
|
|
12872
12966
|
item.APY.netApy = item.APY.baseApy + item.APY.defiSpringApy;
|
|
12873
12967
|
return item;
|
|
@@ -12877,7 +12971,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12877
12971
|
* 1. Contract's allowed pools
|
|
12878
12972
|
* 2. Vesu positions API for current positions
|
|
12879
12973
|
* 3. Vesu pools API for APY and utilization data
|
|
12880
|
-
*
|
|
12974
|
+
*
|
|
12881
12975
|
* @returns {Promise<{
|
|
12882
12976
|
* data: Array<PoolInfoFull>,
|
|
12883
12977
|
* isErrorPositionsAPI: boolean
|
|
@@ -12894,15 +12988,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12894
12988
|
let isErrorPositionsAPI = false;
|
|
12895
12989
|
let vesuPositions = [];
|
|
12896
12990
|
try {
|
|
12897
|
-
const data2 = await getAPIUsingHeadlessBrowser(
|
|
12991
|
+
const data2 = await getAPIUsingHeadlessBrowser(
|
|
12992
|
+
`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`
|
|
12993
|
+
);
|
|
12898
12994
|
vesuPositions = data2.data;
|
|
12899
12995
|
} catch (e) {
|
|
12900
|
-
console.error(
|
|
12996
|
+
console.error(
|
|
12997
|
+
`${_VesuRebalance.name}: Error fetching positions for ${this.address.address}`,
|
|
12998
|
+
e
|
|
12999
|
+
);
|
|
12901
13000
|
isErrorPositionsAPI = true;
|
|
12902
13001
|
}
|
|
12903
13002
|
let { pools, isErrorPoolsAPI } = await this.getVesuPools();
|
|
12904
13003
|
const totalAssets = (await this.getTVL()).amount;
|
|
12905
|
-
const info = allowedPools.map(
|
|
13004
|
+
const info = allowedPools.map(
|
|
13005
|
+
(p) => this.getPoolInfo(
|
|
13006
|
+
p,
|
|
13007
|
+
pools,
|
|
13008
|
+
vesuPositions,
|
|
13009
|
+
totalAssets,
|
|
13010
|
+
isErrorPositionsAPI,
|
|
13011
|
+
isErrorPoolsAPI
|
|
13012
|
+
)
|
|
13013
|
+
);
|
|
12906
13014
|
const data = await Promise.all(info);
|
|
12907
13015
|
return {
|
|
12908
13016
|
data,
|
|
@@ -12915,18 +13023,25 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12915
13023
|
let isErrorPoolsAPI = false;
|
|
12916
13024
|
let pools = [];
|
|
12917
13025
|
try {
|
|
12918
|
-
const data = await getAPIUsingHeadlessBrowser(
|
|
13026
|
+
const data = await getAPIUsingHeadlessBrowser(
|
|
13027
|
+
"https://api.vesu.xyz/pools"
|
|
13028
|
+
);
|
|
12919
13029
|
pools = data.data;
|
|
12920
13030
|
for (const pool of vesu_pools_default.data) {
|
|
12921
13031
|
const found = pools.find((d) => d.id === pool.id);
|
|
12922
13032
|
if (!found) {
|
|
12923
13033
|
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
12924
|
-
logger.verbose(
|
|
13034
|
+
logger.verbose(
|
|
13035
|
+
`VesuRebalance: Pool ${pool.id} not found in Vesu API, using hardcoded data`
|
|
13036
|
+
);
|
|
12925
13037
|
throw new Error("pool not found [sanity check]");
|
|
12926
13038
|
}
|
|
12927
13039
|
}
|
|
12928
13040
|
} catch (e) {
|
|
12929
|
-
logger.error(
|
|
13041
|
+
logger.error(
|
|
13042
|
+
`${_VesuRebalance.name}: Error fetching pools for ${this.address.address}, retry ${retry}`,
|
|
13043
|
+
e
|
|
13044
|
+
);
|
|
12930
13045
|
isErrorPoolsAPI = true;
|
|
12931
13046
|
if (retry < 10) {
|
|
12932
13047
|
await new Promise((resolve) => setTimeout(resolve, 5e3 * (retry + 1)));
|
|
@@ -12964,8 +13079,8 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12964
13079
|
* 3. For each pool that needs more funds:
|
|
12965
13080
|
* - Takes funds from lowest APY pools that are over their target
|
|
12966
13081
|
* 4. Validates that total assets remain constant
|
|
12967
|
-
*
|
|
12968
|
-
* @returns {Promise<{
|
|
13082
|
+
*
|
|
13083
|
+
* @returns {Promise<{
|
|
12969
13084
|
* changes: Change[],
|
|
12970
13085
|
* finalPools: PoolInfoFull[],
|
|
12971
13086
|
* isAnyPoolOverMaxWeight: boolean
|
|
@@ -12981,27 +13096,38 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12981
13096
|
_pools = _pools2;
|
|
12982
13097
|
}
|
|
12983
13098
|
const feeDeductions = await this.getFee(_pools);
|
|
12984
|
-
logger.verbose(
|
|
13099
|
+
logger.verbose(
|
|
13100
|
+
`VesuRebalance: feeDeductions: ${JSON.stringify(feeDeductions)}`
|
|
13101
|
+
);
|
|
12985
13102
|
const pools = _pools.map((p) => {
|
|
12986
13103
|
const fee = feeDeductions.find((f) => p.v_token.eq(f.vToken))?.fee || Web3Number.fromWei("0", this.decimals());
|
|
12987
|
-
logger.verbose(
|
|
13104
|
+
logger.verbose(
|
|
13105
|
+
`FeeAdjustment: ${p.pool_id} => ${fee.toString()}, amt: ${p.amount.toString()}`
|
|
13106
|
+
);
|
|
12988
13107
|
return {
|
|
12989
13108
|
...p,
|
|
12990
13109
|
amount: p.amount.minus(fee)
|
|
12991
13110
|
};
|
|
12992
13111
|
});
|
|
12993
13112
|
let totalAssets = (await this.getTVL()).amount;
|
|
12994
|
-
if (totalAssets.eq(0))
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
13113
|
+
if (totalAssets.eq(0))
|
|
13114
|
+
return {
|
|
13115
|
+
changes: [],
|
|
13116
|
+
finalPools: []
|
|
13117
|
+
};
|
|
12998
13118
|
feeDeductions.forEach((f) => {
|
|
12999
13119
|
totalAssets = totalAssets.minus(f.fee);
|
|
13000
13120
|
});
|
|
13001
|
-
const sumPools = pools.reduce(
|
|
13121
|
+
const sumPools = pools.reduce(
|
|
13122
|
+
(acc, curr) => acc.plus(curr.amount.toString()),
|
|
13123
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13124
|
+
);
|
|
13002
13125
|
logger.verbose(`Sum of pools: ${sumPools.toString()}`);
|
|
13003
13126
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13004
|
-
assert(
|
|
13127
|
+
assert(
|
|
13128
|
+
sumPools.lte(totalAssets.multipliedBy(1.00001).toString()),
|
|
13129
|
+
"Sum of pools.amount must be less than or equal to totalAssets"
|
|
13130
|
+
);
|
|
13005
13131
|
const sortedPools = [...pools].sort((a, b) => b.APY.netApy - a.APY.netApy);
|
|
13006
13132
|
const targetAmounts = {};
|
|
13007
13133
|
let remainingAssets = totalAssets;
|
|
@@ -13023,7 +13149,10 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13023
13149
|
assert(remainingAssets.lt(1e-5), "Remaining assets must be 0");
|
|
13024
13150
|
const changes = sortedPools.map((pool) => {
|
|
13025
13151
|
const target = targetAmounts[pool.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
13026
|
-
const change = Web3Number.fromWei(
|
|
13152
|
+
const change = Web3Number.fromWei(
|
|
13153
|
+
target.minus(pool.amount.toString()).toWei(),
|
|
13154
|
+
this.decimals()
|
|
13155
|
+
);
|
|
13027
13156
|
return {
|
|
13028
13157
|
pool_id: pool.pool_id,
|
|
13029
13158
|
changeAmt: change,
|
|
@@ -13032,14 +13161,21 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13032
13161
|
};
|
|
13033
13162
|
});
|
|
13034
13163
|
logger.verbose(`Changes: ${JSON.stringify(changes)}`);
|
|
13035
|
-
const sumChanges = changes.reduce(
|
|
13036
|
-
|
|
13164
|
+
const sumChanges = changes.reduce(
|
|
13165
|
+
(sum, c) => sum.plus(c.changeAmt.toString()),
|
|
13166
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13167
|
+
);
|
|
13168
|
+
const sumFinal = changes.reduce(
|
|
13169
|
+
(sum, c) => sum.plus(c.finalAmt.toString()),
|
|
13170
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13171
|
+
);
|
|
13037
13172
|
const hasChanges = changes.some((c) => !c.changeAmt.eq(0));
|
|
13038
13173
|
logger.verbose(`Sum of changes: ${sumChanges.toString()}`);
|
|
13039
13174
|
if (!sumChanges.eq(0)) throw new Error("Sum of changes must be zero");
|
|
13040
13175
|
logger.verbose(`Sum of final: ${sumFinal.toString()}`);
|
|
13041
13176
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13042
|
-
if (!sumFinal.eq(totalAssets.toString()))
|
|
13177
|
+
if (!sumFinal.eq(totalAssets.toString()))
|
|
13178
|
+
throw new Error("Sum of final amounts must equal total assets");
|
|
13043
13179
|
if (!hasChanges) throw new Error("No changes required");
|
|
13044
13180
|
const finalPools = pools.map((p) => {
|
|
13045
13181
|
const target = targetAmounts[p.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
@@ -13067,9 +13203,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13067
13203
|
if (p.changeAmt.eq(0)) return null;
|
|
13068
13204
|
actions.push({
|
|
13069
13205
|
pool_id: p.pool_id.address,
|
|
13070
|
-
feature: new CairoCustomEnum(
|
|
13206
|
+
feature: new CairoCustomEnum(
|
|
13207
|
+
p.isDeposit ? { DEPOSIT: {} } : { WITHDRAW: {} }
|
|
13208
|
+
),
|
|
13071
13209
|
token: this.asset().address.address,
|
|
13072
|
-
amount: uint2563.bnToUint256(
|
|
13210
|
+
amount: uint2563.bnToUint256(
|
|
13211
|
+
p.changeAmt.multipliedBy(p.isDeposit ? 1 : -1).toWei()
|
|
13212
|
+
)
|
|
13073
13213
|
});
|
|
13074
13214
|
});
|
|
13075
13215
|
if (actions.length === 0) return null;
|
|
@@ -13082,18 +13222,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13082
13222
|
const netYield = await this.netAPYGivenPools(pools);
|
|
13083
13223
|
const baseFlow = {
|
|
13084
13224
|
title: "Your Deposit",
|
|
13085
|
-
subItems: [
|
|
13225
|
+
subItems: [
|
|
13226
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
13227
|
+
{
|
|
13228
|
+
key: `Performance Fee`,
|
|
13229
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
13230
|
+
}
|
|
13231
|
+
],
|
|
13086
13232
|
linkedFlows: [],
|
|
13087
13233
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
13088
13234
|
};
|
|
13089
13235
|
let _pools = [...pools];
|
|
13090
|
-
_pools = _pools.sort(
|
|
13236
|
+
_pools = _pools.sort(
|
|
13237
|
+
(a, b) => Number(b.amount.toString()) - Number(a.amount.toString())
|
|
13238
|
+
);
|
|
13091
13239
|
_pools.forEach((p) => {
|
|
13092
13240
|
const flow = {
|
|
13093
13241
|
title: `Pool name: ${p.pool_name}`,
|
|
13094
13242
|
subItems: [
|
|
13095
13243
|
{ key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%` },
|
|
13096
|
-
{
|
|
13244
|
+
{
|
|
13245
|
+
key: "Weight",
|
|
13246
|
+
value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%`
|
|
13247
|
+
}
|
|
13097
13248
|
],
|
|
13098
13249
|
linkedFlows: [],
|
|
13099
13250
|
style: p.amount.greaterThan(0) ? { backgroundColor: "#35484f" /* Blue */.valueOf() } : { color: "gray" }
|
|
@@ -13125,7 +13276,12 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13125
13276
|
harvest.actualReward.toWei(),
|
|
13126
13277
|
this.address.address
|
|
13127
13278
|
);
|
|
13128
|
-
swapInfo = await avnu.getSwapInfo(
|
|
13279
|
+
swapInfo = await avnu.getSwapInfo(
|
|
13280
|
+
quote,
|
|
13281
|
+
this.address.address,
|
|
13282
|
+
0,
|
|
13283
|
+
this.address.address
|
|
13284
|
+
);
|
|
13129
13285
|
}
|
|
13130
13286
|
return [
|
|
13131
13287
|
this.contract.populate("harvest", [
|
|
@@ -13146,16 +13302,27 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13146
13302
|
* @returns {Promise<Array<{ vToken: ContractAddr, fee: Web3Number }>>} Array of fees deducted in different vTokens
|
|
13147
13303
|
*/
|
|
13148
13304
|
async getFee(allowedPools) {
|
|
13149
|
-
const assets = Web3Number.fromWei(
|
|
13150
|
-
|
|
13151
|
-
|
|
13305
|
+
const assets = Web3Number.fromWei(
|
|
13306
|
+
(await this.contract.total_assets()).toString(),
|
|
13307
|
+
this.asset().decimals
|
|
13308
|
+
);
|
|
13309
|
+
const totalSupply = Web3Number.fromWei(
|
|
13310
|
+
(await this.contract.total_supply()).toString(),
|
|
13311
|
+
this.asset().decimals
|
|
13312
|
+
);
|
|
13313
|
+
const prevIndex = Web3Number.fromWei(
|
|
13314
|
+
(await this.contract.get_previous_index()).toString(),
|
|
13315
|
+
18
|
|
13316
|
+
);
|
|
13152
13317
|
const currIndex = new Web3Number(1, 18).multipliedBy(assets).dividedBy(totalSupply);
|
|
13153
13318
|
logger.verbose(`Previous index: ${prevIndex.toString()}`);
|
|
13154
13319
|
logger.verbose(`Assets: ${assets.toString()}`);
|
|
13155
13320
|
logger.verbose(`Total supply: ${totalSupply.toString()}`);
|
|
13156
13321
|
logger.verbose(`Current index: ${currIndex.toNumber()}`);
|
|
13157
13322
|
if (currIndex.lt(prevIndex)) {
|
|
13158
|
-
logger.verbose(
|
|
13323
|
+
logger.verbose(
|
|
13324
|
+
`getFee::Current index is less than previous index, no fees to be deducted`
|
|
13325
|
+
);
|
|
13159
13326
|
return [];
|
|
13160
13327
|
}
|
|
13161
13328
|
const indexDiff = currIndex.minus(prevIndex);
|
|
@@ -13168,7 +13335,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13168
13335
|
return [];
|
|
13169
13336
|
}
|
|
13170
13337
|
const fees = [];
|
|
13171
|
-
let remainingFee = fee.plus(
|
|
13338
|
+
let remainingFee = fee.plus(
|
|
13339
|
+
Web3Number.fromWei("100", this.asset().decimals)
|
|
13340
|
+
);
|
|
13172
13341
|
for (const pool of allowedPools) {
|
|
13173
13342
|
const vToken = pool.v_token;
|
|
13174
13343
|
const balance = pool.amount;
|
|
@@ -13177,7 +13346,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13177
13346
|
break;
|
|
13178
13347
|
} else {
|
|
13179
13348
|
fees.push({ vToken, fee: Web3Number.fromWei(balance.toString(), 18) });
|
|
13180
|
-
remainingFee = remainingFee.minus(
|
|
13349
|
+
remainingFee = remainingFee.minus(
|
|
13350
|
+
Web3Number.fromWei(balance.toString(), 18)
|
|
13351
|
+
);
|
|
13181
13352
|
}
|
|
13182
13353
|
}
|
|
13183
13354
|
logger.verbose(`Fees: ${JSON.stringify(fees)}`);
|
|
@@ -13185,101 +13356,175 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13185
13356
|
}
|
|
13186
13357
|
};
|
|
13187
13358
|
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.";
|
|
13188
|
-
var _protocol = {
|
|
13359
|
+
var _protocol = {
|
|
13360
|
+
name: "Vesu",
|
|
13361
|
+
logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png"
|
|
13362
|
+
};
|
|
13189
13363
|
var _riskFactor = [
|
|
13190
13364
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
13191
13365
|
{ type: "Counterparty Risk" /* COUNTERPARTY_RISK */, value: 1, weight: 50 },
|
|
13192
13366
|
{ type: "Oracle Risk" /* ORACLE_RISK */, value: 0.5, weight: 25 }
|
|
13193
13367
|
];
|
|
13194
13368
|
var AUDIT_URL = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
13195
|
-
var
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
type: "ERC4626",
|
|
13200
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
13201
|
-
protocols: [_protocol],
|
|
13202
|
-
auditUrl: AUDIT_URL,
|
|
13203
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13204
|
-
risk: {
|
|
13205
|
-
riskFactor: _riskFactor,
|
|
13206
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13207
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13369
|
+
var faqs = [
|
|
13370
|
+
{
|
|
13371
|
+
question: "What is the Vesu Rebalancing Strategy?",
|
|
13372
|
+
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."
|
|
13208
13373
|
},
|
|
13209
|
-
|
|
13210
|
-
|
|
13211
|
-
|
|
13212
|
-
}, {
|
|
13213
|
-
name: "Vesu Fusion ETH",
|
|
13214
|
-
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13215
|
-
address: ContractAddr.from("0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"),
|
|
13216
|
-
type: "ERC4626",
|
|
13217
|
-
auditUrl: AUDIT_URL,
|
|
13218
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "ETH")],
|
|
13219
|
-
protocols: [_protocol],
|
|
13220
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13221
|
-
risk: {
|
|
13222
|
-
riskFactor: _riskFactor,
|
|
13223
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13224
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13374
|
+
{
|
|
13375
|
+
question: "Will I earn Vesu points?",
|
|
13376
|
+
answer: "Yes, of course! You will earn Vesu points for your deposits."
|
|
13225
13377
|
},
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
}, {
|
|
13230
|
-
name: "Vesu Fusion USDC",
|
|
13231
|
-
description: _description.replace("{{TOKEN}}", "USDC"),
|
|
13232
|
-
address: ContractAddr.from("0xa858c97e9454f407d1bd7c57472fc8d8d8449a777c822b41d18e387816f29c"),
|
|
13233
|
-
type: "ERC4626",
|
|
13234
|
-
auditUrl: AUDIT_URL,
|
|
13235
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "USDC")],
|
|
13236
|
-
protocols: [_protocol],
|
|
13237
|
-
maxTVL: Web3Number.fromWei("0", 6),
|
|
13238
|
-
risk: {
|
|
13239
|
-
riskFactor: _riskFactor,
|
|
13240
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13241
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13378
|
+
{
|
|
13379
|
+
question: "How does the strategy optimize yield?",
|
|
13380
|
+
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."
|
|
13242
13381
|
},
|
|
13243
|
-
|
|
13244
|
-
|
|
13382
|
+
{
|
|
13383
|
+
question: "What are the risks associated with this strategy?",
|
|
13384
|
+
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."
|
|
13385
|
+
},
|
|
13386
|
+
{
|
|
13387
|
+
question: "How are fees calculated and deducted?",
|
|
13388
|
+
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."
|
|
13389
|
+
},
|
|
13390
|
+
{
|
|
13391
|
+
question: "What happens if a pool exceeds its maximum weight?",
|
|
13392
|
+
answer: "If a pool exceeds its maximum weight, the strategy rebalances by withdrawing excess funds and reallocating them to other pools with available capacity."
|
|
13393
|
+
},
|
|
13394
|
+
{
|
|
13395
|
+
question: "Can I withdraw my assets at any time?",
|
|
13396
|
+
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."
|
|
13397
|
+
},
|
|
13398
|
+
{
|
|
13399
|
+
question: "What happens to my Defi Spring STRK rewards?",
|
|
13400
|
+
answer: "STRK rewards are automatically harvested and reinvested into the strategy every week to maximize compounding returns."
|
|
13401
|
+
},
|
|
13402
|
+
{
|
|
13403
|
+
question: "Is the strategy audited?",
|
|
13404
|
+
answer: /* @__PURE__ */ jsxs("div", { children: [
|
|
13405
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
13406
|
+
/* @__PURE__ */ jsx("a", { href: "https://docs.strkfarm.com/p/strategies/vesu-fusion-rebalancing-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
13407
|
+
"."
|
|
13408
|
+
] })
|
|
13245
13409
|
}
|
|
13246
|
-
|
|
13247
|
-
|
|
13248
|
-
|
|
13249
|
-
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13410
|
+
];
|
|
13411
|
+
var VesuRebalanceStrategies = [
|
|
13412
|
+
{
|
|
13413
|
+
name: "Vesu Fusion STRK",
|
|
13414
|
+
description: _description.replace("{{TOKEN}}", "STRK"),
|
|
13415
|
+
address: ContractAddr.from(
|
|
13416
|
+
"0x7fb5bcb8525954a60fde4e8fb8220477696ce7117ef264775a1770e23571929"
|
|
13417
|
+
),
|
|
13418
|
+
type: "ERC4626",
|
|
13419
|
+
depositTokens: [
|
|
13420
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
13421
|
+
],
|
|
13422
|
+
protocols: [_protocol],
|
|
13423
|
+
auditUrl: AUDIT_URL,
|
|
13424
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13425
|
+
risk: {
|
|
13426
|
+
riskFactor: _riskFactor,
|
|
13427
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13428
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13429
|
+
},
|
|
13430
|
+
additionalInfo: {
|
|
13431
|
+
feeBps: 1e3
|
|
13432
|
+
},
|
|
13433
|
+
faqs
|
|
13434
|
+
},
|
|
13435
|
+
{
|
|
13436
|
+
name: "Vesu Fusion ETH",
|
|
13437
|
+
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13438
|
+
address: ContractAddr.from(
|
|
13439
|
+
"0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"
|
|
13440
|
+
),
|
|
13441
|
+
type: "ERC4626",
|
|
13442
|
+
auditUrl: AUDIT_URL,
|
|
13443
|
+
depositTokens: [
|
|
13444
|
+
Global.getDefaultTokens().find((t) => t.symbol === "ETH")
|
|
13445
|
+
],
|
|
13446
|
+
protocols: [_protocol],
|
|
13447
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13448
|
+
risk: {
|
|
13449
|
+
riskFactor: _riskFactor,
|
|
13450
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13451
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13452
|
+
},
|
|
13453
|
+
additionalInfo: {
|
|
13454
|
+
feeBps: 1e3
|
|
13455
|
+
},
|
|
13456
|
+
faqs
|
|
13259
13457
|
},
|
|
13260
|
-
|
|
13261
|
-
|
|
13458
|
+
{
|
|
13459
|
+
name: "Vesu Fusion USDC",
|
|
13460
|
+
description: _description.replace("{{TOKEN}}", "USDC"),
|
|
13461
|
+
address: ContractAddr.from(
|
|
13462
|
+
"0xa858c97e9454f407d1bd7c57472fc8d8d8449a777c822b41d18e387816f29c"
|
|
13463
|
+
),
|
|
13464
|
+
type: "ERC4626",
|
|
13465
|
+
auditUrl: AUDIT_URL,
|
|
13466
|
+
depositTokens: [
|
|
13467
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC")
|
|
13468
|
+
],
|
|
13469
|
+
protocols: [_protocol],
|
|
13470
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13471
|
+
risk: {
|
|
13472
|
+
riskFactor: _riskFactor,
|
|
13473
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13474
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13475
|
+
},
|
|
13476
|
+
additionalInfo: {
|
|
13477
|
+
feeBps: 1e3
|
|
13478
|
+
},
|
|
13479
|
+
faqs
|
|
13480
|
+
},
|
|
13481
|
+
{
|
|
13482
|
+
name: "Vesu Fusion USDT",
|
|
13483
|
+
description: _description.replace("{{TOKEN}}", "USDT"),
|
|
13484
|
+
address: ContractAddr.from(
|
|
13485
|
+
"0x115e94e722cfc4c77a2f15c4aefb0928c1c0029e5a57570df24c650cb7cec2c"
|
|
13486
|
+
),
|
|
13487
|
+
type: "ERC4626",
|
|
13488
|
+
depositTokens: [
|
|
13489
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
13490
|
+
],
|
|
13491
|
+
auditUrl: AUDIT_URL,
|
|
13492
|
+
protocols: [_protocol],
|
|
13493
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13494
|
+
risk: {
|
|
13495
|
+
riskFactor: _riskFactor,
|
|
13496
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13497
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13498
|
+
},
|
|
13499
|
+
additionalInfo: {
|
|
13500
|
+
feeBps: 1e3
|
|
13501
|
+
},
|
|
13502
|
+
faqs
|
|
13503
|
+
// }, {
|
|
13504
|
+
// name: 'Vesu Fusion WBTC',
|
|
13505
|
+
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13506
|
+
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13507
|
+
// type: 'ERC4626',
|
|
13508
|
+
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13509
|
+
// auditUrl: AUDIT_URL,
|
|
13510
|
+
// protocols: [_protocol],
|
|
13511
|
+
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13512
|
+
// risk: {
|
|
13513
|
+
// riskFactor: _riskFactor,
|
|
13514
|
+
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13515
|
+
// },
|
|
13516
|
+
// additionalInfo: {
|
|
13517
|
+
// feeBps: 1000,
|
|
13518
|
+
// },
|
|
13262
13519
|
}
|
|
13263
|
-
|
|
13264
|
-
// name: 'Vesu Fusion WBTC',
|
|
13265
|
-
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13266
|
-
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13267
|
-
// type: 'ERC4626',
|
|
13268
|
-
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13269
|
-
// auditUrl: AUDIT_URL,
|
|
13270
|
-
// protocols: [_protocol],
|
|
13271
|
-
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13272
|
-
// risk: {
|
|
13273
|
-
// riskFactor: _riskFactor,
|
|
13274
|
-
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13275
|
-
// },
|
|
13276
|
-
// additionalInfo: {
|
|
13277
|
-
// feeBps: 1000,
|
|
13278
|
-
// },
|
|
13279
|
-
}];
|
|
13520
|
+
];
|
|
13280
13521
|
|
|
13281
13522
|
// src/strategies/ekubo-cl-vault.tsx
|
|
13282
|
-
import {
|
|
13523
|
+
import {
|
|
13524
|
+
Contract as Contract6,
|
|
13525
|
+
num as num4,
|
|
13526
|
+
uint256 as uint2564
|
|
13527
|
+
} from "starknet";
|
|
13283
13528
|
|
|
13284
13529
|
// src/data/cl-vault.abi.json
|
|
13285
13530
|
var cl_vault_abi_default = [
|
|
@@ -18181,7 +18426,7 @@ var erc4626_abi_default = [
|
|
|
18181
18426
|
];
|
|
18182
18427
|
|
|
18183
18428
|
// src/strategies/ekubo-cl-vault.tsx
|
|
18184
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18429
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
18185
18430
|
var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
18186
18431
|
/**
|
|
18187
18432
|
* Creates a new VesuRebalance strategy instance.
|
|
@@ -18194,15 +18439,34 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18194
18439
|
super(config);
|
|
18195
18440
|
this.BASE_WEIGHT = 1e4;
|
|
18196
18441
|
this.pricer = pricer;
|
|
18197
|
-
assert(
|
|
18442
|
+
assert(
|
|
18443
|
+
metadata.depositTokens.length === 2,
|
|
18444
|
+
"EkuboCL only supports 2 deposit token"
|
|
18445
|
+
);
|
|
18198
18446
|
this.metadata = metadata;
|
|
18199
18447
|
this.address = metadata.address;
|
|
18200
|
-
this.contract = new Contract6(
|
|
18201
|
-
|
|
18448
|
+
this.contract = new Contract6(
|
|
18449
|
+
cl_vault_abi_default,
|
|
18450
|
+
this.address.address,
|
|
18451
|
+
this.config.provider
|
|
18452
|
+
);
|
|
18453
|
+
this.lstContract = new Contract6(
|
|
18454
|
+
erc4626_abi_default,
|
|
18455
|
+
this.metadata.additionalInfo.lstContract.address,
|
|
18456
|
+
this.config.provider
|
|
18457
|
+
);
|
|
18202
18458
|
const EKUBO_POSITION = "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
|
|
18203
|
-
this.ekuboPositionsContract = new Contract6(
|
|
18459
|
+
this.ekuboPositionsContract = new Contract6(
|
|
18460
|
+
ekubo_positions_abi_default,
|
|
18461
|
+
EKUBO_POSITION,
|
|
18462
|
+
this.config.provider
|
|
18463
|
+
);
|
|
18204
18464
|
const EKUBO_MATH = "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
|
|
18205
|
-
this.ekuboMathContract = new Contract6(
|
|
18465
|
+
this.ekuboMathContract = new Contract6(
|
|
18466
|
+
ekubo_math_abi_default,
|
|
18467
|
+
EKUBO_MATH,
|
|
18468
|
+
this.config.provider
|
|
18469
|
+
);
|
|
18206
18470
|
this.avnu = new AvnuWrapper();
|
|
18207
18471
|
}
|
|
18208
18472
|
async matchInputAmounts(amountInfo) {
|
|
@@ -18227,25 +18491,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18227
18491
|
/** Returns minimum amounts give given two amounts based on what can be added for liq */
|
|
18228
18492
|
async getMinDepositAmounts(amountInfo) {
|
|
18229
18493
|
const shares = await this.tokensToShares(amountInfo);
|
|
18230
|
-
const { amount0, amount1 } = await this.contract.call(
|
|
18494
|
+
const { amount0, amount1 } = await this.contract.call(
|
|
18495
|
+
"convert_to_assets",
|
|
18496
|
+
[uint2564.bnToUint256(shares.toWei())]
|
|
18497
|
+
);
|
|
18231
18498
|
return {
|
|
18232
18499
|
token0: {
|
|
18233
18500
|
tokenInfo: amountInfo.token0.tokenInfo,
|
|
18234
|
-
amount: Web3Number.fromWei(
|
|
18501
|
+
amount: Web3Number.fromWei(
|
|
18502
|
+
amount0.toString(),
|
|
18503
|
+
amountInfo.token0.tokenInfo.decimals
|
|
18504
|
+
)
|
|
18235
18505
|
},
|
|
18236
18506
|
token1: {
|
|
18237
18507
|
tokenInfo: amountInfo.token1.tokenInfo,
|
|
18238
|
-
amount: Web3Number.fromWei(
|
|
18508
|
+
amount: Web3Number.fromWei(
|
|
18509
|
+
amount1.toString(),
|
|
18510
|
+
amountInfo.token1.tokenInfo.decimals
|
|
18511
|
+
)
|
|
18239
18512
|
}
|
|
18240
18513
|
};
|
|
18241
18514
|
}
|
|
18242
18515
|
async depositCall(amountInfo, receiver) {
|
|
18243
18516
|
const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
|
|
18244
|
-
const token0Contract = new Contract6(
|
|
18245
|
-
|
|
18246
|
-
|
|
18247
|
-
|
|
18248
|
-
|
|
18517
|
+
const token0Contract = new Contract6(
|
|
18518
|
+
erc4626_abi_default,
|
|
18519
|
+
amountInfo.token0.tokenInfo.address.address,
|
|
18520
|
+
this.config.provider
|
|
18521
|
+
);
|
|
18522
|
+
const token1Contract = new Contract6(
|
|
18523
|
+
erc4626_abi_default,
|
|
18524
|
+
amountInfo.token1.tokenInfo.address.address,
|
|
18525
|
+
this.config.provider
|
|
18526
|
+
);
|
|
18527
|
+
const call1 = token0Contract.populate("approve", [
|
|
18528
|
+
this.address.address,
|
|
18529
|
+
uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei())
|
|
18530
|
+
]);
|
|
18531
|
+
const call2 = token1Contract.populate("approve", [
|
|
18532
|
+
this.address.address,
|
|
18533
|
+
uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei())
|
|
18534
|
+
]);
|
|
18535
|
+
const call3 = this.contract.populate("deposit", [
|
|
18536
|
+
uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei()),
|
|
18537
|
+
uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei()),
|
|
18538
|
+
receiver.address
|
|
18539
|
+
]);
|
|
18249
18540
|
const calls = [];
|
|
18250
18541
|
if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
|
|
18251
18542
|
if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
|
|
@@ -18260,25 +18551,29 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18260
18551
|
}
|
|
18261
18552
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
18262
18553
|
const shares = await this.tokensToShares(amountInfo);
|
|
18263
|
-
logger.verbose(
|
|
18264
|
-
|
|
18265
|
-
|
|
18266
|
-
|
|
18267
|
-
|
|
18554
|
+
logger.verbose(
|
|
18555
|
+
`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`
|
|
18556
|
+
);
|
|
18557
|
+
return [
|
|
18558
|
+
this.contract.populate("withdraw", [
|
|
18559
|
+
uint2564.bnToUint256(shares.toWei()),
|
|
18560
|
+
receiver.address
|
|
18561
|
+
])
|
|
18562
|
+
];
|
|
18268
18563
|
}
|
|
18269
18564
|
rebalanceCall(newBounds, swapParams) {
|
|
18270
|
-
return [
|
|
18271
|
-
|
|
18272
|
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18565
|
+
return [
|
|
18566
|
+
this.contract.populate("rebalance", [
|
|
18567
|
+
{
|
|
18568
|
+
lower: _EkuboCLVault.tickToi129(Number(newBounds.lowerTick)),
|
|
18569
|
+
upper: _EkuboCLVault.tickToi129(Number(newBounds.upperTick))
|
|
18570
|
+
},
|
|
18571
|
+
swapParams
|
|
18572
|
+
])
|
|
18573
|
+
];
|
|
18277
18574
|
}
|
|
18278
18575
|
handleUnusedCall(swapParams) {
|
|
18279
|
-
return [this.contract.populate("handle_unused", [
|
|
18280
|
-
swapParams
|
|
18281
|
-
])];
|
|
18576
|
+
return [this.contract.populate("handle_unused", [swapParams])];
|
|
18282
18577
|
}
|
|
18283
18578
|
handleFeesCall() {
|
|
18284
18579
|
return [this.contract.populate("handle_fees", [])];
|
|
@@ -18294,8 +18589,12 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18294
18589
|
let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
18295
18590
|
const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
18296
18591
|
const blockBefore = blockNow - sinceBlocks;
|
|
18297
|
-
const adjustedSupplyNow = supplyNow.minus(
|
|
18298
|
-
|
|
18592
|
+
const adjustedSupplyNow = supplyNow.minus(
|
|
18593
|
+
await this.getHarvestRewardShares(blockBefore, blockNow)
|
|
18594
|
+
);
|
|
18595
|
+
let blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
18596
|
+
blockBefore
|
|
18597
|
+
);
|
|
18299
18598
|
const tvlBefore = await this._getTVL(blockBefore);
|
|
18300
18599
|
const supplyBefore = await this.totalSupply(blockBefore);
|
|
18301
18600
|
const priceBefore = await this.getCurrentPrice(blockBefore);
|
|
@@ -18313,7 +18612,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18313
18612
|
logger.verbose(`Supply before: ${supplyBefore.toString()}`);
|
|
18314
18613
|
logger.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
|
|
18315
18614
|
logger.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
|
|
18316
|
-
const apyForGivenBlocks = Number(
|
|
18615
|
+
const apyForGivenBlocks = Number(
|
|
18616
|
+
tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)
|
|
18617
|
+
) / 1e4;
|
|
18317
18618
|
return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
|
|
18318
18619
|
}
|
|
18319
18620
|
async getHarvestRewardShares(fromBlock, toBlock) {
|
|
@@ -18331,7 +18632,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18331
18632
|
} else {
|
|
18332
18633
|
shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
|
|
18333
18634
|
}
|
|
18334
|
-
logger.verbose(
|
|
18635
|
+
logger.verbose(
|
|
18636
|
+
`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`
|
|
18637
|
+
);
|
|
18335
18638
|
}
|
|
18336
18639
|
return shares;
|
|
18337
18640
|
}
|
|
@@ -18341,15 +18644,25 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18341
18644
|
}
|
|
18342
18645
|
async getUserTVL(user, blockIdentifier = "pending") {
|
|
18343
18646
|
let bal = await this.balanceOf(user, blockIdentifier);
|
|
18344
|
-
const assets = await this.contract.call(
|
|
18345
|
-
|
|
18346
|
-
|
|
18647
|
+
const assets = await this.contract.call(
|
|
18648
|
+
"convert_to_assets",
|
|
18649
|
+
[uint2564.bnToUint256(bal.toWei())],
|
|
18650
|
+
{
|
|
18651
|
+
blockIdentifier
|
|
18652
|
+
}
|
|
18653
|
+
);
|
|
18347
18654
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18348
18655
|
this.assertValidDepositTokens(poolKey);
|
|
18349
18656
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18350
18657
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18351
|
-
const amount0 = Web3Number.fromWei(
|
|
18352
|
-
|
|
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
|
+
);
|
|
18353
18666
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18354
18667
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18355
18668
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
@@ -18373,7 +18686,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18373
18686
|
blockIdentifier
|
|
18374
18687
|
});
|
|
18375
18688
|
const bounds = await this.getCurrentBounds(blockIdentifier);
|
|
18376
|
-
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18689
|
+
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18690
|
+
Web3Number.fromWei(result.toString(), 18),
|
|
18691
|
+
bounds,
|
|
18692
|
+
blockIdentifier
|
|
18693
|
+
);
|
|
18377
18694
|
return { amount0, amount1 };
|
|
18378
18695
|
}
|
|
18379
18696
|
async totalSupply(blockIdentifier = "pending") {
|
|
@@ -18383,8 +18700,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18383
18700
|
return Web3Number.fromWei(res.toString(), 18);
|
|
18384
18701
|
}
|
|
18385
18702
|
assertValidDepositTokens(poolKey) {
|
|
18386
|
-
assert(
|
|
18387
|
-
|
|
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
|
+
);
|
|
18388
18711
|
}
|
|
18389
18712
|
async getTVL(blockIdentifier = "pending") {
|
|
18390
18713
|
const { amount0, amount1 } = await this._getTVL(blockIdentifier);
|
|
@@ -18414,26 +18737,35 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18414
18737
|
const nftID = await this.getCurrentNFTID();
|
|
18415
18738
|
const poolKey = await this.getPoolKey();
|
|
18416
18739
|
const currentBounds = await this.getCurrentBounds();
|
|
18417
|
-
const result = await this.ekuboPositionsContract.call(
|
|
18418
|
-
|
|
18419
|
-
|
|
18420
|
-
|
|
18421
|
-
|
|
18422
|
-
|
|
18423
|
-
|
|
18424
|
-
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
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
|
+
);
|
|
18431
18757
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18432
18758
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18433
18759
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18434
18760
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18435
|
-
const token0Web3 = Web3Number.fromWei(
|
|
18436
|
-
|
|
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
|
+
);
|
|
18437
18769
|
const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
|
|
18438
18770
|
const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
|
|
18439
18771
|
return {
|
|
@@ -18455,7 +18787,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18455
18787
|
return Number(result.salt.toString());
|
|
18456
18788
|
}
|
|
18457
18789
|
async truePrice() {
|
|
18458
|
-
const result = await this.lstContract.call("convert_to_assets", [
|
|
18790
|
+
const result = await this.lstContract.call("convert_to_assets", [
|
|
18791
|
+
uint2564.bnToUint256(BigInt(1e18).toString())
|
|
18792
|
+
]);
|
|
18459
18793
|
const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
|
|
18460
18794
|
return truePrice;
|
|
18461
18795
|
}
|
|
@@ -18464,22 +18798,36 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18464
18798
|
return this._getCurrentPrice(poolKey, blockIdentifier);
|
|
18465
18799
|
}
|
|
18466
18800
|
async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
|
|
18467
|
-
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18801
|
+
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18802
|
+
"get_pool_price",
|
|
18803
|
+
[
|
|
18804
|
+
{
|
|
18805
|
+
token0: poolKey.token0.address,
|
|
18806
|
+
token1: poolKey.token1.address,
|
|
18807
|
+
fee: poolKey.fee,
|
|
18808
|
+
tick_spacing: poolKey.tick_spacing,
|
|
18809
|
+
extension: poolKey.extension
|
|
18810
|
+
}
|
|
18811
|
+
],
|
|
18468
18812
|
{
|
|
18469
|
-
|
|
18470
|
-
token1: poolKey.token1.address,
|
|
18471
|
-
fee: poolKey.fee,
|
|
18472
|
-
tick_spacing: poolKey.tick_spacing,
|
|
18473
|
-
extension: poolKey.extension
|
|
18813
|
+
blockIdentifier
|
|
18474
18814
|
}
|
|
18475
|
-
|
|
18476
|
-
|
|
18477
|
-
|
|
18478
|
-
|
|
18479
|
-
console.log(
|
|
18815
|
+
);
|
|
18816
|
+
const sqrtRatio = _EkuboCLVault.div2Power128(
|
|
18817
|
+
BigInt(priceInfo.sqrt_ratio.toString())
|
|
18818
|
+
);
|
|
18819
|
+
console.log(
|
|
18820
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`
|
|
18821
|
+
);
|
|
18480
18822
|
const price = sqrtRatio * sqrtRatio;
|
|
18481
|
-
const tick = _EkuboCLVault.priceToTick(
|
|
18482
|
-
|
|
18823
|
+
const tick = _EkuboCLVault.priceToTick(
|
|
18824
|
+
price,
|
|
18825
|
+
true,
|
|
18826
|
+
Number(poolKey.tick_spacing)
|
|
18827
|
+
);
|
|
18828
|
+
console.log(
|
|
18829
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`
|
|
18830
|
+
);
|
|
18483
18831
|
return {
|
|
18484
18832
|
price,
|
|
18485
18833
|
tick: tick.mag * (tick.sign == 0 ? 1 : -1),
|
|
@@ -18519,7 +18867,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18519
18867
|
};
|
|
18520
18868
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18521
18869
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18522
|
-
assert(
|
|
18870
|
+
assert(
|
|
18871
|
+
token0Info.decimals == token1Info.decimals,
|
|
18872
|
+
"Tested only for equal decimals"
|
|
18873
|
+
);
|
|
18523
18874
|
this.poolKey = poolKey;
|
|
18524
18875
|
return poolKey;
|
|
18525
18876
|
}
|
|
@@ -18543,11 +18894,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18543
18894
|
async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
|
|
18544
18895
|
assert(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
|
|
18545
18896
|
const sampleLiq = 1e20;
|
|
18546
|
-
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18547
|
-
|
|
18897
|
+
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18898
|
+
Web3Number.fromWei(sampleLiq.toString(), 18),
|
|
18899
|
+
bounds
|
|
18900
|
+
);
|
|
18901
|
+
logger.verbose(
|
|
18902
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`
|
|
18903
|
+
);
|
|
18548
18904
|
assert(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
|
|
18549
18905
|
const price = await (await this.getCurrentPrice()).price;
|
|
18550
|
-
logger.verbose(
|
|
18906
|
+
logger.verbose(
|
|
18907
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`
|
|
18908
|
+
);
|
|
18551
18909
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18552
18910
|
if (sampleAmount1.eq(0)) {
|
|
18553
18911
|
return {
|
|
@@ -18577,12 +18935,22 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18577
18935
|
};
|
|
18578
18936
|
}
|
|
18579
18937
|
}
|
|
18580
|
-
assert(
|
|
18938
|
+
assert(
|
|
18939
|
+
sampleAmount0.decimals == sampleAmount1.decimals,
|
|
18940
|
+
"Sample amounts have different decimals"
|
|
18941
|
+
);
|
|
18581
18942
|
const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
|
|
18582
18943
|
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
18583
|
-
logger.verbose(
|
|
18944
|
+
logger.verbose(
|
|
18945
|
+
`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
|
|
18946
|
+
);
|
|
18584
18947
|
if (justUseInputAmount)
|
|
18585
|
-
return this._solveExpectedAmountsEq(
|
|
18948
|
+
return this._solveExpectedAmountsEq(
|
|
18949
|
+
amount0,
|
|
18950
|
+
amount1,
|
|
18951
|
+
ratioWeb3Number,
|
|
18952
|
+
price
|
|
18953
|
+
);
|
|
18586
18954
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18587
18955
|
const _amount1 = amount0.dividedBy(ratioWeb3Number);
|
|
18588
18956
|
return {
|
|
@@ -18598,7 +18966,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18598
18966
|
ratio
|
|
18599
18967
|
};
|
|
18600
18968
|
} else {
|
|
18601
|
-
throw new Error(
|
|
18969
|
+
throw new Error(
|
|
18970
|
+
"Both amounts are non-zero, cannot compute expected amounts"
|
|
18971
|
+
);
|
|
18602
18972
|
}
|
|
18603
18973
|
}
|
|
18604
18974
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
@@ -18615,34 +18985,65 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18615
18985
|
const erc20Mod = new ERC20(this.config);
|
|
18616
18986
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18617
18987
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18618
|
-
const token0Bal1 = await erc20Mod.balanceOf(
|
|
18619
|
-
|
|
18620
|
-
|
|
18988
|
+
const token0Bal1 = await erc20Mod.balanceOf(
|
|
18989
|
+
poolKey.token0,
|
|
18990
|
+
this.address.address,
|
|
18991
|
+
token0Info.decimals
|
|
18992
|
+
);
|
|
18993
|
+
const token1Bal1 = await erc20Mod.balanceOf(
|
|
18994
|
+
poolKey.token1,
|
|
18995
|
+
this.address.address,
|
|
18996
|
+
token1Info.decimals
|
|
18997
|
+
);
|
|
18998
|
+
logger.verbose(
|
|
18999
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`
|
|
19000
|
+
);
|
|
18621
19001
|
const token0Price = await this.pricer.getPrice(token0Info.symbol);
|
|
18622
19002
|
const token1Price = await this.pricer.getPrice(token1Info.symbol);
|
|
18623
19003
|
const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
|
|
18624
19004
|
const token1PriceUsd = token1Price.price * Number(token1Bal1.toFixed(13));
|
|
18625
19005
|
if (token0PriceUsd > 1 && token1PriceUsd > 1) {
|
|
18626
|
-
throw new Error(
|
|
19006
|
+
throw new Error(
|
|
19007
|
+
"Both tokens are non-zero and above $1, call handle_fees first"
|
|
19008
|
+
);
|
|
18627
19009
|
}
|
|
18628
19010
|
let token0Bal = token0Bal1;
|
|
18629
19011
|
let token1Bal = token1Bal1;
|
|
18630
19012
|
if (considerRebalance) {
|
|
18631
|
-
logger.verbose(
|
|
19013
|
+
logger.verbose(
|
|
19014
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`
|
|
19015
|
+
);
|
|
18632
19016
|
const tvl = await this.getTVL();
|
|
18633
19017
|
token0Bal = token0Bal.plus(tvl.token0.amount.toString());
|
|
18634
19018
|
token1Bal = token1Bal.plus(tvl.token1.amount.toString());
|
|
18635
19019
|
} else {
|
|
18636
|
-
logger.verbose(
|
|
19020
|
+
logger.verbose(
|
|
19021
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: false`
|
|
19022
|
+
);
|
|
18637
19023
|
}
|
|
18638
|
-
logger.verbose(
|
|
19024
|
+
logger.verbose(
|
|
19025
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
19026
|
+
);
|
|
18639
19027
|
const newBounds = await this.getNewBounds();
|
|
18640
|
-
logger.verbose(
|
|
18641
|
-
|
|
19028
|
+
logger.verbose(
|
|
19029
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`
|
|
19030
|
+
);
|
|
19031
|
+
return await this.getSwapInfoGivenAmounts(
|
|
19032
|
+
poolKey,
|
|
19033
|
+
token0Bal,
|
|
19034
|
+
token1Bal,
|
|
19035
|
+
newBounds
|
|
19036
|
+
);
|
|
18642
19037
|
}
|
|
18643
19038
|
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
|
|
18644
|
-
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
18645
|
-
|
|
19039
|
+
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
19040
|
+
token0Bal,
|
|
19041
|
+
token1Bal,
|
|
19042
|
+
bounds
|
|
19043
|
+
);
|
|
19044
|
+
logger.verbose(
|
|
19045
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19046
|
+
);
|
|
18646
19047
|
let retry = 0;
|
|
18647
19048
|
const maxRetry = 10;
|
|
18648
19049
|
while (retry < maxRetry) {
|
|
@@ -18659,9 +19060,15 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18659
19060
|
const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
|
|
18660
19061
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
18661
19062
|
const expectedRatio = expectedAmounts.ratio;
|
|
18662
|
-
logger.verbose(
|
|
18663
|
-
|
|
18664
|
-
|
|
19063
|
+
logger.verbose(
|
|
19064
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`
|
|
19065
|
+
);
|
|
19066
|
+
logger.verbose(
|
|
19067
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`
|
|
19068
|
+
);
|
|
19069
|
+
logger.verbose(
|
|
19070
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`
|
|
19071
|
+
);
|
|
18665
19072
|
if (amountToSell.eq(0)) {
|
|
18666
19073
|
return {
|
|
18667
19074
|
token_from_address: tokenToSell.address,
|
|
@@ -18675,23 +19082,62 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18675
19082
|
routes: []
|
|
18676
19083
|
};
|
|
18677
19084
|
}
|
|
18678
|
-
const quote = await this.avnu.getQuotes(
|
|
19085
|
+
const quote = await this.avnu.getQuotes(
|
|
19086
|
+
tokenToSell.address,
|
|
19087
|
+
tokenToBuy.address,
|
|
19088
|
+
amountToSell.toWei(),
|
|
19089
|
+
this.address.address
|
|
19090
|
+
);
|
|
18679
19091
|
if (remainingSellAmount.eq(0)) {
|
|
18680
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18681
|
-
|
|
19092
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19093
|
+
quote.buyAmount.toString(),
|
|
19094
|
+
tokenToBuyInfo.decimals
|
|
19095
|
+
).multipliedBy(0.9999);
|
|
19096
|
+
return await this.avnu.getSwapInfo(
|
|
19097
|
+
quote,
|
|
19098
|
+
this.address.address,
|
|
19099
|
+
0,
|
|
19100
|
+
this.address.address,
|
|
19101
|
+
minAmountOut.toWei()
|
|
19102
|
+
);
|
|
18682
19103
|
}
|
|
18683
|
-
const amountOut = Web3Number.fromWei(
|
|
19104
|
+
const amountOut = Web3Number.fromWei(
|
|
19105
|
+
quote.buyAmount.toString(),
|
|
19106
|
+
tokenToBuyInfo.decimals
|
|
19107
|
+
);
|
|
18684
19108
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
18685
19109
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
18686
|
-
logger.verbose(
|
|
18687
|
-
|
|
18688
|
-
|
|
19110
|
+
logger.verbose(
|
|
19111
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
19112
|
+
);
|
|
19113
|
+
logger.verbose(
|
|
19114
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swapPrice: ${swapPrice.toString()}`
|
|
19115
|
+
);
|
|
19116
|
+
logger.verbose(
|
|
19117
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newRatio: ${newRatio.toString()}`
|
|
19118
|
+
);
|
|
18689
19119
|
if (Number(newRatio.toString()) > expectedRatio * 1.0000001 || Number(newRatio.toString()) < expectedRatio * 0.9999999) {
|
|
18690
|
-
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
18691
|
-
|
|
19120
|
+
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
19121
|
+
token0Bal,
|
|
19122
|
+
token1Bal,
|
|
19123
|
+
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
19124
|
+
Number(swapPrice.toString())
|
|
19125
|
+
);
|
|
19126
|
+
logger.verbose(
|
|
19127
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19128
|
+
);
|
|
18692
19129
|
} else {
|
|
18693
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18694
|
-
|
|
19130
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19131
|
+
quote.buyAmount.toString(),
|
|
19132
|
+
tokenToBuyInfo.decimals
|
|
19133
|
+
).multipliedBy(0.9999);
|
|
19134
|
+
return await this.avnu.getSwapInfo(
|
|
19135
|
+
quote,
|
|
19136
|
+
this.address.address,
|
|
19137
|
+
0,
|
|
19138
|
+
this.address.address,
|
|
19139
|
+
minAmountOut.toWei()
|
|
19140
|
+
);
|
|
18695
19141
|
}
|
|
18696
19142
|
retry++;
|
|
18697
19143
|
}
|
|
@@ -18700,8 +19146,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18700
19146
|
/**
|
|
18701
19147
|
* Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
|
|
18702
19148
|
* Uses binary search approach to find optimal swap amount.
|
|
18703
|
-
*
|
|
18704
|
-
* @param newBounds - The new tick bounds to rebalance to
|
|
19149
|
+
*
|
|
19150
|
+
* @param newBounds - The new tick bounds to rebalance to
|
|
18705
19151
|
* @param swapInfo - Initial swap parameters for rebalancing
|
|
18706
19152
|
* @param acc - Account to estimate gas fees with
|
|
18707
19153
|
* @param retry - Current retry attempt number (default 0)
|
|
@@ -18728,7 +19174,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18728
19174
|
logger.error(`Rebalance failed after ${MAX_RETRIES} retries`);
|
|
18729
19175
|
throw err;
|
|
18730
19176
|
}
|
|
18731
|
-
logger.error(
|
|
19177
|
+
logger.error(
|
|
19178
|
+
`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`
|
|
19179
|
+
);
|
|
18732
19180
|
const newSwapInfo = { ...swapInfo };
|
|
18733
19181
|
const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
|
|
18734
19182
|
logger.verbose(`Current amount: ${currentAmount.toString()}`);
|
|
@@ -18820,23 +19268,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18820
19268
|
const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
|
|
18821
19269
|
const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
|
|
18822
19270
|
const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
|
|
18823
|
-
logger.verbose(
|
|
18824
|
-
|
|
18825
|
-
|
|
19271
|
+
logger.verbose(
|
|
19272
|
+
`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`
|
|
19273
|
+
);
|
|
19274
|
+
const result = await this.ekuboMathContract.call(
|
|
19275
|
+
"liquidity_delta_to_amount_delta",
|
|
19276
|
+
[
|
|
19277
|
+
uint2564.bnToUint256(currentPrice.sqrtRatio),
|
|
19278
|
+
{
|
|
19279
|
+
mag: liquidity.toWei(),
|
|
19280
|
+
sign: 0
|
|
19281
|
+
},
|
|
19282
|
+
uint2564.bnToUint256(
|
|
19283
|
+
_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()
|
|
19284
|
+
),
|
|
19285
|
+
uint2564.bnToUint256(
|
|
19286
|
+
_EkuboCLVault.priceToSqrtRatio(upperPrice).toString()
|
|
19287
|
+
)
|
|
19288
|
+
],
|
|
18826
19289
|
{
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
|
|
18831
|
-
uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
|
|
18832
|
-
], {
|
|
18833
|
-
blockIdentifier
|
|
18834
|
-
});
|
|
19290
|
+
blockIdentifier
|
|
19291
|
+
}
|
|
19292
|
+
);
|
|
18835
19293
|
const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
|
|
18836
19294
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18837
19295
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18838
|
-
const amount0 = Web3Number.fromWei(
|
|
18839
|
-
|
|
19296
|
+
const amount0 = Web3Number.fromWei(
|
|
19297
|
+
_EkuboCLVault.i129ToNumber(result.amount0).toString(),
|
|
19298
|
+
token0Info.decimals
|
|
19299
|
+
);
|
|
19300
|
+
const amount1 = Web3Number.fromWei(
|
|
19301
|
+
_EkuboCLVault.i129ToNumber(result.amount1).toString(),
|
|
19302
|
+
token1Info.decimals
|
|
19303
|
+
);
|
|
18840
19304
|
return {
|
|
18841
19305
|
amount0,
|
|
18842
19306
|
amount1
|
|
@@ -18844,7 +19308,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18844
19308
|
}
|
|
18845
19309
|
async harvest(acc) {
|
|
18846
19310
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
18847
|
-
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19311
|
+
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19312
|
+
this.address
|
|
19313
|
+
);
|
|
18848
19314
|
const poolKey = await this.getPoolKey();
|
|
18849
19315
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18850
19316
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -18854,18 +19320,37 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18854
19320
|
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
18855
19321
|
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
18856
19322
|
const isToken1 = claim.token.eq(poolKey.token1);
|
|
18857
|
-
logger.verbose(
|
|
19323
|
+
logger.verbose(
|
|
19324
|
+
`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
|
|
19325
|
+
);
|
|
18858
19326
|
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
18859
19327
|
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
18860
|
-
logger.verbose(
|
|
18861
|
-
|
|
19328
|
+
logger.verbose(
|
|
19329
|
+
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
|
|
19330
|
+
);
|
|
19331
|
+
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
19332
|
+
poolKey,
|
|
19333
|
+
token0Amt,
|
|
19334
|
+
token1Amt,
|
|
19335
|
+
bounds
|
|
19336
|
+
);
|
|
18862
19337
|
swapInfo.token_to_address = token0Info.address.address;
|
|
18863
|
-
logger.verbose(
|
|
18864
|
-
|
|
19338
|
+
logger.verbose(
|
|
19339
|
+
`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
19340
|
+
);
|
|
19341
|
+
logger.verbose(
|
|
19342
|
+
`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
19343
|
+
);
|
|
18865
19344
|
const harvestEstimateCall = async (swapInfo1) => {
|
|
18866
|
-
const swap1Amount = Web3Number.fromWei(
|
|
19345
|
+
const swap1Amount = Web3Number.fromWei(
|
|
19346
|
+
uint2564.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
19347
|
+
18
|
|
19348
|
+
);
|
|
18867
19349
|
const remainingAmount = postFeeAmount.minus(swap1Amount);
|
|
18868
|
-
const swapInfo2 = {
|
|
19350
|
+
const swapInfo2 = {
|
|
19351
|
+
...swapInfo,
|
|
19352
|
+
token_from_amount: uint2564.bnToUint256(remainingAmount.toWei())
|
|
19353
|
+
};
|
|
18869
19354
|
swapInfo2.token_to_address = token1Info.address.address;
|
|
18870
19355
|
const calldata = [
|
|
18871
19356
|
claim.rewardsContract.address,
|
|
@@ -18878,11 +19363,23 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18878
19363
|
swapInfo,
|
|
18879
19364
|
swapInfo2
|
|
18880
19365
|
];
|
|
18881
|
-
logger.verbose(
|
|
19366
|
+
logger.verbose(
|
|
19367
|
+
`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
19368
|
+
calldata
|
|
19369
|
+
)}`
|
|
19370
|
+
);
|
|
18882
19371
|
return [this.contract.populate("harvest", calldata)];
|
|
18883
19372
|
};
|
|
18884
|
-
const _callsFinal = await this.rebalanceIter(
|
|
18885
|
-
|
|
19373
|
+
const _callsFinal = await this.rebalanceIter(
|
|
19374
|
+
swapInfo,
|
|
19375
|
+
acc,
|
|
19376
|
+
harvestEstimateCall
|
|
19377
|
+
);
|
|
19378
|
+
logger.verbose(
|
|
19379
|
+
`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|
|
19380
|
+
_callsFinal
|
|
19381
|
+
)}`
|
|
19382
|
+
);
|
|
18886
19383
|
calls.push(..._callsFinal);
|
|
18887
19384
|
}
|
|
18888
19385
|
return calls;
|
|
@@ -18892,24 +19389,37 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18892
19389
|
const poolKey = await this.getPoolKey();
|
|
18893
19390
|
const linkedFlow = {
|
|
18894
19391
|
title: this.metadata.name,
|
|
18895
|
-
subItems: [
|
|
19392
|
+
subItems: [
|
|
19393
|
+
{
|
|
19394
|
+
key: "Pool",
|
|
19395
|
+
value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing`
|
|
19396
|
+
}
|
|
19397
|
+
],
|
|
18896
19398
|
linkedFlows: [],
|
|
18897
19399
|
style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
|
|
18898
19400
|
};
|
|
18899
19401
|
const baseFlow = {
|
|
18900
19402
|
id: "base",
|
|
18901
19403
|
title: "Your Deposit",
|
|
18902
|
-
subItems: [
|
|
19404
|
+
subItems: [
|
|
19405
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
19406
|
+
{
|
|
19407
|
+
key: `Performance Fee`,
|
|
19408
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
19409
|
+
}
|
|
19410
|
+
],
|
|
18903
19411
|
linkedFlows: [linkedFlow],
|
|
18904
19412
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
18905
19413
|
};
|
|
18906
19414
|
const rebalanceFlow = {
|
|
18907
19415
|
id: "rebalance",
|
|
18908
19416
|
title: "Automated Rebalance",
|
|
18909
|
-
subItems: [
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
19417
|
+
subItems: [
|
|
19418
|
+
{
|
|
19419
|
+
key: "Range selection",
|
|
19420
|
+
value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
|
|
19421
|
+
}
|
|
19422
|
+
],
|
|
18913
19423
|
linkedFlows: [linkedFlow],
|
|
18914
19424
|
style: { backgroundColor: "purple" /* Green */.valueOf() }
|
|
18915
19425
|
};
|
|
@@ -18917,43 +19427,94 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18917
19427
|
}
|
|
18918
19428
|
};
|
|
18919
19429
|
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.";
|
|
18920
|
-
var _protocol2 = {
|
|
19430
|
+
var _protocol2 = {
|
|
19431
|
+
name: "Ekubo",
|
|
19432
|
+
logo: "https://app.ekubo.org/favicon.ico"
|
|
19433
|
+
};
|
|
18921
19434
|
var _riskFactor2 = [
|
|
18922
19435
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
18923
19436
|
{ type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
|
|
18924
19437
|
];
|
|
18925
19438
|
var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
18926
|
-
var
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
18930
|
-
|
|
18931
|
-
|
|
18932
|
-
|
|
18933
|
-
|
|
18934
|
-
] }),
|
|
18935
|
-
address: ContractAddr.from("0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"),
|
|
18936
|
-
type: "Other",
|
|
18937
|
-
// must be same order as poolKey token0 and token1
|
|
18938
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"), Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
18939
|
-
protocols: [_protocol2],
|
|
18940
|
-
auditUrl: AUDIT_URL2,
|
|
18941
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
18942
|
-
risk: {
|
|
18943
|
-
riskFactor: _riskFactor2,
|
|
18944
|
-
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
18945
|
-
notARisks: getNoRiskTags(_riskFactor2)
|
|
19439
|
+
var faqs2 = [
|
|
19440
|
+
{
|
|
19441
|
+
question: "What is the Ekubo CL Vault strategy?",
|
|
19442
|
+
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."
|
|
19443
|
+
},
|
|
19444
|
+
{
|
|
19445
|
+
question: "How are trading fees and rewards handled?",
|
|
19446
|
+
answer: "Trading fees and DeFi Spring rewards are automatically compounded back into the strategy, increasing your overall returns."
|
|
18946
19447
|
},
|
|
18947
|
-
|
|
18948
|
-
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
|
|
19448
|
+
{
|
|
19449
|
+
question: "What happens during withdrawal?",
|
|
19450
|
+
answer: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices."
|
|
19451
|
+
},
|
|
19452
|
+
{
|
|
19453
|
+
question: "Is the strategy audited?",
|
|
19454
|
+
answer: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19455
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
19456
|
+
/* @__PURE__ */ jsx2("a", { href: "https://docs.strkfarm.com/p/ekubo-cl-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
19457
|
+
"."
|
|
19458
|
+
] })
|
|
19459
|
+
}
|
|
19460
|
+
];
|
|
19461
|
+
var EkuboCLVaultStrategies = [
|
|
19462
|
+
{
|
|
19463
|
+
name: "Ekubo xSTRK/STRK",
|
|
19464
|
+
description: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19465
|
+
/* @__PURE__ */ jsx2("p", { children: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK") }),
|
|
19466
|
+
/* @__PURE__ */ jsxs2(
|
|
19467
|
+
"ul",
|
|
19468
|
+
{
|
|
19469
|
+
style: {
|
|
19470
|
+
marginLeft: "20px",
|
|
19471
|
+
listStyle: "circle",
|
|
19472
|
+
fontSize: "12px"
|
|
19473
|
+
},
|
|
19474
|
+
children: [
|
|
19475
|
+
/* @__PURE__ */ jsx2("li", { style: { marginTop: "10px" }, children: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices." }),
|
|
19476
|
+
/* @__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." })
|
|
19477
|
+
]
|
|
19478
|
+
}
|
|
19479
|
+
)
|
|
19480
|
+
] }),
|
|
19481
|
+
address: ContractAddr.from(
|
|
19482
|
+
"0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"
|
|
19483
|
+
),
|
|
19484
|
+
type: "Other",
|
|
19485
|
+
// must be same order as poolKey token0 and token1
|
|
19486
|
+
depositTokens: [
|
|
19487
|
+
Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"),
|
|
19488
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
19489
|
+
],
|
|
19490
|
+
protocols: [_protocol2],
|
|
19491
|
+
auditUrl: AUDIT_URL2,
|
|
19492
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
19493
|
+
risk: {
|
|
19494
|
+
riskFactor: _riskFactor2,
|
|
19495
|
+
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19496
|
+
notARisks: getNoRiskTags(_riskFactor2)
|
|
18952
19497
|
},
|
|
18953
|
-
|
|
18954
|
-
|
|
19498
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19499
|
+
additionalInfo: {
|
|
19500
|
+
newBounds: {
|
|
19501
|
+
lower: -1,
|
|
19502
|
+
upper: 1
|
|
19503
|
+
},
|
|
19504
|
+
lstContract: ContractAddr.from(
|
|
19505
|
+
"0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
|
|
19506
|
+
),
|
|
19507
|
+
feeBps: 1e3
|
|
19508
|
+
},
|
|
19509
|
+
faqs: [
|
|
19510
|
+
...faqs2,
|
|
19511
|
+
{
|
|
19512
|
+
question: "Why might I see a negative APY?",
|
|
19513
|
+
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."
|
|
19514
|
+
}
|
|
19515
|
+
]
|
|
18955
19516
|
}
|
|
18956
|
-
|
|
19517
|
+
];
|
|
18957
19518
|
|
|
18958
19519
|
// src/notifs/telegram.ts
|
|
18959
19520
|
import TelegramBot from "node-telegram-bot-api";
|