@strkfarm/sdk 1.0.36 → 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 +15678 -7543
- 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 -2
- 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/src/node/headless.node.ts +0 -36
package/dist/index.js
CHANGED
|
@@ -2095,7 +2095,9 @@ var getRiskColor = (risk) => {
|
|
|
2095
2095
|
};
|
|
2096
2096
|
var getNoRiskTags = (risks) => {
|
|
2097
2097
|
const noRisks1 = risks.filter((risk) => risk.value === 0).map((risk) => risk.type);
|
|
2098
|
-
const noRisks2 = Object.values(RiskType).filter(
|
|
2098
|
+
const noRisks2 = Object.values(RiskType).filter(
|
|
2099
|
+
(risk) => !risks.map((risk2) => risk2.type).includes(risk)
|
|
2100
|
+
);
|
|
2099
2101
|
const mergedUnique = [.../* @__PURE__ */ new Set([...noRisks1, ...noRisks2])];
|
|
2100
2102
|
return mergedUnique.map((risk) => `No ${risk}`);
|
|
2101
2103
|
};
|
|
@@ -12787,6 +12789,7 @@ var vesu_pools_default = {
|
|
|
12787
12789
|
};
|
|
12788
12790
|
|
|
12789
12791
|
// src/strategies/vesu-rebalance.tsx
|
|
12792
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
12790
12793
|
var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
12791
12794
|
// 10000 bps = 100%
|
|
12792
12795
|
/**
|
|
@@ -12800,10 +12803,17 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12800
12803
|
super(config);
|
|
12801
12804
|
this.BASE_WEIGHT = 1e4;
|
|
12802
12805
|
this.pricer = pricer;
|
|
12803
|
-
assert(
|
|
12806
|
+
assert(
|
|
12807
|
+
metadata.depositTokens.length === 1,
|
|
12808
|
+
"VesuRebalance only supports 1 deposit token"
|
|
12809
|
+
);
|
|
12804
12810
|
this.metadata = metadata;
|
|
12805
12811
|
this.address = metadata.address;
|
|
12806
|
-
this.contract = new import_starknet8.Contract(
|
|
12812
|
+
this.contract = new import_starknet8.Contract(
|
|
12813
|
+
vesu_rebalance_abi_default,
|
|
12814
|
+
this.address.address,
|
|
12815
|
+
this.config.provider
|
|
12816
|
+
);
|
|
12807
12817
|
}
|
|
12808
12818
|
/**
|
|
12809
12819
|
* Creates a deposit call to the strategy contract.
|
|
@@ -12812,10 +12822,23 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12812
12822
|
* @returns Populated contract call for deposit
|
|
12813
12823
|
*/
|
|
12814
12824
|
async depositCall(amountInfo, receiver) {
|
|
12815
|
-
assert(
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12825
|
+
assert(
|
|
12826
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
12827
|
+
"Deposit token mismatch"
|
|
12828
|
+
);
|
|
12829
|
+
const assetContract = new import_starknet8.Contract(
|
|
12830
|
+
vesu_rebalance_abi_default,
|
|
12831
|
+
this.asset().address.address,
|
|
12832
|
+
this.config.provider
|
|
12833
|
+
);
|
|
12834
|
+
const call1 = assetContract.populate("approve", [
|
|
12835
|
+
this.address.address,
|
|
12836
|
+
import_starknet8.uint256.bnToUint256(amountInfo.amount.toWei())
|
|
12837
|
+
]);
|
|
12838
|
+
const call2 = this.contract.populate("deposit", [
|
|
12839
|
+
import_starknet8.uint256.bnToUint256(amountInfo.amount.toWei()),
|
|
12840
|
+
receiver.address
|
|
12841
|
+
]);
|
|
12819
12842
|
return [call1, call2];
|
|
12820
12843
|
}
|
|
12821
12844
|
/**
|
|
@@ -12826,7 +12849,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12826
12849
|
* @returns Populated contract call for withdrawal
|
|
12827
12850
|
*/
|
|
12828
12851
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
12829
|
-
return [
|
|
12852
|
+
return [
|
|
12853
|
+
this.contract.populate("withdraw", [
|
|
12854
|
+
import_starknet8.uint256.bnToUint256(amountInfo.amount.toWei()),
|
|
12855
|
+
receiver.address,
|
|
12856
|
+
owner.address
|
|
12857
|
+
])
|
|
12858
|
+
];
|
|
12830
12859
|
}
|
|
12831
12860
|
/**
|
|
12832
12861
|
* Returns the underlying asset token of the strategy.
|
|
@@ -12849,9 +12878,16 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12849
12878
|
*/
|
|
12850
12879
|
async getUserTVL(user) {
|
|
12851
12880
|
const shares = await this.contract.balanceOf(user.address);
|
|
12852
|
-
const assets = await this.contract.convert_to_assets(
|
|
12853
|
-
|
|
12854
|
-
|
|
12881
|
+
const assets = await this.contract.convert_to_assets(
|
|
12882
|
+
import_starknet8.uint256.bnToUint256(shares)
|
|
12883
|
+
);
|
|
12884
|
+
const amount = Web3Number.fromWei(
|
|
12885
|
+
assets.toString(),
|
|
12886
|
+
this.metadata.depositTokens[0].decimals
|
|
12887
|
+
);
|
|
12888
|
+
let price = await this.pricer.getPrice(
|
|
12889
|
+
this.metadata.depositTokens[0].symbol
|
|
12890
|
+
);
|
|
12855
12891
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12856
12892
|
return {
|
|
12857
12893
|
tokenInfo: this.asset(),
|
|
@@ -12865,8 +12901,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12865
12901
|
*/
|
|
12866
12902
|
async getTVL() {
|
|
12867
12903
|
const assets = await this.contract.total_assets();
|
|
12868
|
-
const amount = Web3Number.fromWei(
|
|
12869
|
-
|
|
12904
|
+
const amount = Web3Number.fromWei(
|
|
12905
|
+
assets.toString(),
|
|
12906
|
+
this.metadata.depositTokens[0].decimals
|
|
12907
|
+
);
|
|
12908
|
+
let price = await this.pricer.getPrice(
|
|
12909
|
+
this.metadata.depositTokens[0].symbol
|
|
12910
|
+
);
|
|
12870
12911
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12871
12912
|
return {
|
|
12872
12913
|
tokenInfo: this.asset(),
|
|
@@ -12892,51 +12933,104 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12892
12933
|
return pools;
|
|
12893
12934
|
}
|
|
12894
12935
|
async getPoolInfo(p, pools, vesuPositions, totalAssets, isErrorPositionsAPI, isErrorPoolsAPI) {
|
|
12895
|
-
const vesuPosition = vesuPositions.find(
|
|
12936
|
+
const vesuPosition = vesuPositions.find(
|
|
12937
|
+
(d) => d.pool.id.toString() === import_starknet8.num.getDecimalString(p.pool_id.address.toString())
|
|
12938
|
+
);
|
|
12896
12939
|
const _pool = pools.find((d) => {
|
|
12897
|
-
logger.verbose(
|
|
12940
|
+
logger.verbose(
|
|
12941
|
+
`pool check: ${d.id == import_starknet8.num.getDecimalString(p.pool_id.address.toString())}, id: ${d.id}, pool_id: ${import_starknet8.num.getDecimalString(
|
|
12942
|
+
p.pool_id.address.toString()
|
|
12943
|
+
)}`
|
|
12944
|
+
);
|
|
12898
12945
|
return d.id == import_starknet8.num.getDecimalString(p.pool_id.address.toString());
|
|
12899
12946
|
});
|
|
12900
12947
|
logger.verbose(`pool: ${JSON.stringify(_pool)}`);
|
|
12901
12948
|
logger.verbose(typeof _pool);
|
|
12902
12949
|
logger.verbose(`name: ${_pool?.name}`);
|
|
12903
12950
|
const name = _pool?.name;
|
|
12904
|
-
logger.verbose(
|
|
12905
|
-
|
|
12951
|
+
logger.verbose(
|
|
12952
|
+
`name2: ${name}, ${!name ? true : false}, ${name?.length}, ${typeof name}`
|
|
12953
|
+
);
|
|
12954
|
+
const assetInfo = _pool?.assets.find(
|
|
12955
|
+
(d) => this.asset().address.eqString(d.address)
|
|
12956
|
+
);
|
|
12906
12957
|
if (!name) {
|
|
12907
12958
|
logger.verbose(`Pool not found`);
|
|
12908
12959
|
throw new Error(`Pool name ${p.pool_id.address.toString()} not found`);
|
|
12909
12960
|
}
|
|
12910
12961
|
if (!assetInfo) {
|
|
12911
|
-
throw new Error(
|
|
12962
|
+
throw new Error(
|
|
12963
|
+
`Asset ${this.asset().address.toString()} not found in pool ${p.pool_id.address.toString()}`
|
|
12964
|
+
);
|
|
12912
12965
|
}
|
|
12913
|
-
let vTokenContract = new import_starknet8.Contract(
|
|
12966
|
+
let vTokenContract = new import_starknet8.Contract(
|
|
12967
|
+
vesu_rebalance_abi_default,
|
|
12968
|
+
p.v_token.address,
|
|
12969
|
+
this.config.provider
|
|
12970
|
+
);
|
|
12914
12971
|
const bal = await vTokenContract.balanceOf(this.address.address);
|
|
12915
|
-
const assets = await vTokenContract.convert_to_assets(
|
|
12972
|
+
const assets = await vTokenContract.convert_to_assets(
|
|
12973
|
+
import_starknet8.uint256.bnToUint256(bal.toString())
|
|
12974
|
+
);
|
|
12916
12975
|
logger.verbose(`Collateral: ${JSON.stringify(vesuPosition?.collateral)}`);
|
|
12917
12976
|
logger.verbose(`supplyApy: ${JSON.stringify(assetInfo?.stats.supplyApy)}`);
|
|
12918
|
-
logger.verbose(
|
|
12919
|
-
|
|
12920
|
-
|
|
12977
|
+
logger.verbose(
|
|
12978
|
+
`defiSpringSupplyApr: ${JSON.stringify(
|
|
12979
|
+
assetInfo?.stats.defiSpringSupplyApr
|
|
12980
|
+
)}`
|
|
12981
|
+
);
|
|
12982
|
+
logger.verbose(
|
|
12983
|
+
`currentUtilization: ${JSON.stringify(
|
|
12984
|
+
assetInfo?.stats.currentUtilization
|
|
12985
|
+
)}`
|
|
12986
|
+
);
|
|
12987
|
+
logger.verbose(
|
|
12988
|
+
`maxUtilization: ${JSON.stringify(assetInfo?.config.maxUtilization)}`
|
|
12989
|
+
);
|
|
12921
12990
|
const item = {
|
|
12922
12991
|
pool_id: p.pool_id,
|
|
12923
12992
|
pool_name: _pool?.name,
|
|
12924
12993
|
max_weight: p.max_weight,
|
|
12925
|
-
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12994
|
+
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12995
|
+
Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)
|
|
12996
|
+
),
|
|
12926
12997
|
v_token: p.v_token,
|
|
12927
12998
|
amount: Web3Number.fromWei(assets.toString(), this.decimals()),
|
|
12928
|
-
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
12999
|
+
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
13000
|
+
vesuPosition.collateral.usdPrice.value,
|
|
13001
|
+
vesuPosition.collateral.usdPrice.decimals
|
|
13002
|
+
),
|
|
12929
13003
|
APY: isErrorPoolsAPI || !assetInfo ? {
|
|
12930
13004
|
baseApy: 0,
|
|
12931
13005
|
defiSpringApy: 0,
|
|
12932
13006
|
netApy: 0
|
|
12933
13007
|
} : {
|
|
12934
|
-
baseApy: Number(
|
|
12935
|
-
|
|
13008
|
+
baseApy: Number(
|
|
13009
|
+
Web3Number.fromWei(
|
|
13010
|
+
assetInfo.stats.supplyApy.value,
|
|
13011
|
+
assetInfo.stats.supplyApy.decimals
|
|
13012
|
+
).toFixed(6)
|
|
13013
|
+
),
|
|
13014
|
+
defiSpringApy: assetInfo.stats.defiSpringSupplyApr ? Number(
|
|
13015
|
+
Web3Number.fromWei(
|
|
13016
|
+
assetInfo.stats.defiSpringSupplyApr.value,
|
|
13017
|
+
assetInfo.stats.defiSpringSupplyApr.decimals
|
|
13018
|
+
).toFixed(6)
|
|
13019
|
+
) : 0,
|
|
12936
13020
|
netApy: 0
|
|
12937
13021
|
},
|
|
12938
|
-
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12939
|
-
|
|
13022
|
+
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
13023
|
+
Web3Number.fromWei(
|
|
13024
|
+
assetInfo.stats.currentUtilization.value,
|
|
13025
|
+
assetInfo.stats.currentUtilization.decimals
|
|
13026
|
+
).toFixed(6)
|
|
13027
|
+
),
|
|
13028
|
+
maxUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
13029
|
+
Web3Number.fromWei(
|
|
13030
|
+
assetInfo.config.maxUtilization.value,
|
|
13031
|
+
assetInfo.config.maxUtilization.decimals
|
|
13032
|
+
).toFixed(6)
|
|
13033
|
+
)
|
|
12940
13034
|
};
|
|
12941
13035
|
item.APY.netApy = item.APY.baseApy + item.APY.defiSpringApy;
|
|
12942
13036
|
return item;
|
|
@@ -12946,7 +13040,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12946
13040
|
* 1. Contract's allowed pools
|
|
12947
13041
|
* 2. Vesu positions API for current positions
|
|
12948
13042
|
* 3. Vesu pools API for APY and utilization data
|
|
12949
|
-
*
|
|
13043
|
+
*
|
|
12950
13044
|
* @returns {Promise<{
|
|
12951
13045
|
* data: Array<PoolInfoFull>,
|
|
12952
13046
|
* isErrorPositionsAPI: boolean
|
|
@@ -12963,15 +13057,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12963
13057
|
let isErrorPositionsAPI = false;
|
|
12964
13058
|
let vesuPositions = [];
|
|
12965
13059
|
try {
|
|
12966
|
-
const data2 = await getAPIUsingHeadlessBrowser(
|
|
13060
|
+
const data2 = await getAPIUsingHeadlessBrowser(
|
|
13061
|
+
`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`
|
|
13062
|
+
);
|
|
12967
13063
|
vesuPositions = data2.data;
|
|
12968
13064
|
} catch (e) {
|
|
12969
|
-
console.error(
|
|
13065
|
+
console.error(
|
|
13066
|
+
`${_VesuRebalance.name}: Error fetching positions for ${this.address.address}`,
|
|
13067
|
+
e
|
|
13068
|
+
);
|
|
12970
13069
|
isErrorPositionsAPI = true;
|
|
12971
13070
|
}
|
|
12972
13071
|
let { pools, isErrorPoolsAPI } = await this.getVesuPools();
|
|
12973
13072
|
const totalAssets = (await this.getTVL()).amount;
|
|
12974
|
-
const info = allowedPools.map(
|
|
13073
|
+
const info = allowedPools.map(
|
|
13074
|
+
(p) => this.getPoolInfo(
|
|
13075
|
+
p,
|
|
13076
|
+
pools,
|
|
13077
|
+
vesuPositions,
|
|
13078
|
+
totalAssets,
|
|
13079
|
+
isErrorPositionsAPI,
|
|
13080
|
+
isErrorPoolsAPI
|
|
13081
|
+
)
|
|
13082
|
+
);
|
|
12975
13083
|
const data = await Promise.all(info);
|
|
12976
13084
|
return {
|
|
12977
13085
|
data,
|
|
@@ -12984,18 +13092,25 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12984
13092
|
let isErrorPoolsAPI = false;
|
|
12985
13093
|
let pools = [];
|
|
12986
13094
|
try {
|
|
12987
|
-
const data = await getAPIUsingHeadlessBrowser(
|
|
13095
|
+
const data = await getAPIUsingHeadlessBrowser(
|
|
13096
|
+
"https://api.vesu.xyz/pools"
|
|
13097
|
+
);
|
|
12988
13098
|
pools = data.data;
|
|
12989
13099
|
for (const pool of vesu_pools_default.data) {
|
|
12990
13100
|
const found = pools.find((d) => d.id === pool.id);
|
|
12991
13101
|
if (!found) {
|
|
12992
13102
|
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
12993
|
-
logger.verbose(
|
|
13103
|
+
logger.verbose(
|
|
13104
|
+
`VesuRebalance: Pool ${pool.id} not found in Vesu API, using hardcoded data`
|
|
13105
|
+
);
|
|
12994
13106
|
throw new Error("pool not found [sanity check]");
|
|
12995
13107
|
}
|
|
12996
13108
|
}
|
|
12997
13109
|
} catch (e) {
|
|
12998
|
-
logger.error(
|
|
13110
|
+
logger.error(
|
|
13111
|
+
`${_VesuRebalance.name}: Error fetching pools for ${this.address.address}, retry ${retry}`,
|
|
13112
|
+
e
|
|
13113
|
+
);
|
|
12999
13114
|
isErrorPoolsAPI = true;
|
|
13000
13115
|
if (retry < 10) {
|
|
13001
13116
|
await new Promise((resolve) => setTimeout(resolve, 5e3 * (retry + 1)));
|
|
@@ -13033,8 +13148,8 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13033
13148
|
* 3. For each pool that needs more funds:
|
|
13034
13149
|
* - Takes funds from lowest APY pools that are over their target
|
|
13035
13150
|
* 4. Validates that total assets remain constant
|
|
13036
|
-
*
|
|
13037
|
-
* @returns {Promise<{
|
|
13151
|
+
*
|
|
13152
|
+
* @returns {Promise<{
|
|
13038
13153
|
* changes: Change[],
|
|
13039
13154
|
* finalPools: PoolInfoFull[],
|
|
13040
13155
|
* isAnyPoolOverMaxWeight: boolean
|
|
@@ -13050,27 +13165,38 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13050
13165
|
_pools = _pools2;
|
|
13051
13166
|
}
|
|
13052
13167
|
const feeDeductions = await this.getFee(_pools);
|
|
13053
|
-
logger.verbose(
|
|
13168
|
+
logger.verbose(
|
|
13169
|
+
`VesuRebalance: feeDeductions: ${JSON.stringify(feeDeductions)}`
|
|
13170
|
+
);
|
|
13054
13171
|
const pools = _pools.map((p) => {
|
|
13055
13172
|
const fee = feeDeductions.find((f) => p.v_token.eq(f.vToken))?.fee || Web3Number.fromWei("0", this.decimals());
|
|
13056
|
-
logger.verbose(
|
|
13173
|
+
logger.verbose(
|
|
13174
|
+
`FeeAdjustment: ${p.pool_id} => ${fee.toString()}, amt: ${p.amount.toString()}`
|
|
13175
|
+
);
|
|
13057
13176
|
return {
|
|
13058
13177
|
...p,
|
|
13059
13178
|
amount: p.amount.minus(fee)
|
|
13060
13179
|
};
|
|
13061
13180
|
});
|
|
13062
13181
|
let totalAssets = (await this.getTVL()).amount;
|
|
13063
|
-
if (totalAssets.eq(0))
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13182
|
+
if (totalAssets.eq(0))
|
|
13183
|
+
return {
|
|
13184
|
+
changes: [],
|
|
13185
|
+
finalPools: []
|
|
13186
|
+
};
|
|
13067
13187
|
feeDeductions.forEach((f) => {
|
|
13068
13188
|
totalAssets = totalAssets.minus(f.fee);
|
|
13069
13189
|
});
|
|
13070
|
-
const sumPools = pools.reduce(
|
|
13190
|
+
const sumPools = pools.reduce(
|
|
13191
|
+
(acc, curr) => acc.plus(curr.amount.toString()),
|
|
13192
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13193
|
+
);
|
|
13071
13194
|
logger.verbose(`Sum of pools: ${sumPools.toString()}`);
|
|
13072
13195
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13073
|
-
assert(
|
|
13196
|
+
assert(
|
|
13197
|
+
sumPools.lte(totalAssets.multipliedBy(1.00001).toString()),
|
|
13198
|
+
"Sum of pools.amount must be less than or equal to totalAssets"
|
|
13199
|
+
);
|
|
13074
13200
|
const sortedPools = [...pools].sort((a, b) => b.APY.netApy - a.APY.netApy);
|
|
13075
13201
|
const targetAmounts = {};
|
|
13076
13202
|
let remainingAssets = totalAssets;
|
|
@@ -13092,7 +13218,10 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13092
13218
|
assert(remainingAssets.lt(1e-5), "Remaining assets must be 0");
|
|
13093
13219
|
const changes = sortedPools.map((pool) => {
|
|
13094
13220
|
const target = targetAmounts[pool.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
13095
|
-
const change = Web3Number.fromWei(
|
|
13221
|
+
const change = Web3Number.fromWei(
|
|
13222
|
+
target.minus(pool.amount.toString()).toWei(),
|
|
13223
|
+
this.decimals()
|
|
13224
|
+
);
|
|
13096
13225
|
return {
|
|
13097
13226
|
pool_id: pool.pool_id,
|
|
13098
13227
|
changeAmt: change,
|
|
@@ -13101,14 +13230,21 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13101
13230
|
};
|
|
13102
13231
|
});
|
|
13103
13232
|
logger.verbose(`Changes: ${JSON.stringify(changes)}`);
|
|
13104
|
-
const sumChanges = changes.reduce(
|
|
13105
|
-
|
|
13233
|
+
const sumChanges = changes.reduce(
|
|
13234
|
+
(sum, c) => sum.plus(c.changeAmt.toString()),
|
|
13235
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13236
|
+
);
|
|
13237
|
+
const sumFinal = changes.reduce(
|
|
13238
|
+
(sum, c) => sum.plus(c.finalAmt.toString()),
|
|
13239
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13240
|
+
);
|
|
13106
13241
|
const hasChanges = changes.some((c) => !c.changeAmt.eq(0));
|
|
13107
13242
|
logger.verbose(`Sum of changes: ${sumChanges.toString()}`);
|
|
13108
13243
|
if (!sumChanges.eq(0)) throw new Error("Sum of changes must be zero");
|
|
13109
13244
|
logger.verbose(`Sum of final: ${sumFinal.toString()}`);
|
|
13110
13245
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13111
|
-
if (!sumFinal.eq(totalAssets.toString()))
|
|
13246
|
+
if (!sumFinal.eq(totalAssets.toString()))
|
|
13247
|
+
throw new Error("Sum of final amounts must equal total assets");
|
|
13112
13248
|
if (!hasChanges) throw new Error("No changes required");
|
|
13113
13249
|
const finalPools = pools.map((p) => {
|
|
13114
13250
|
const target = targetAmounts[p.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
@@ -13136,9 +13272,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13136
13272
|
if (p.changeAmt.eq(0)) return null;
|
|
13137
13273
|
actions.push({
|
|
13138
13274
|
pool_id: p.pool_id.address,
|
|
13139
|
-
feature: new import_starknet8.CairoCustomEnum(
|
|
13275
|
+
feature: new import_starknet8.CairoCustomEnum(
|
|
13276
|
+
p.isDeposit ? { DEPOSIT: {} } : { WITHDRAW: {} }
|
|
13277
|
+
),
|
|
13140
13278
|
token: this.asset().address.address,
|
|
13141
|
-
amount: import_starknet8.uint256.bnToUint256(
|
|
13279
|
+
amount: import_starknet8.uint256.bnToUint256(
|
|
13280
|
+
p.changeAmt.multipliedBy(p.isDeposit ? 1 : -1).toWei()
|
|
13281
|
+
)
|
|
13142
13282
|
});
|
|
13143
13283
|
});
|
|
13144
13284
|
if (actions.length === 0) return null;
|
|
@@ -13151,18 +13291,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13151
13291
|
const netYield = await this.netAPYGivenPools(pools);
|
|
13152
13292
|
const baseFlow = {
|
|
13153
13293
|
title: "Your Deposit",
|
|
13154
|
-
subItems: [
|
|
13294
|
+
subItems: [
|
|
13295
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
13296
|
+
{
|
|
13297
|
+
key: `Performance Fee`,
|
|
13298
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
13299
|
+
}
|
|
13300
|
+
],
|
|
13155
13301
|
linkedFlows: [],
|
|
13156
13302
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
13157
13303
|
};
|
|
13158
13304
|
let _pools = [...pools];
|
|
13159
|
-
_pools = _pools.sort(
|
|
13305
|
+
_pools = _pools.sort(
|
|
13306
|
+
(a, b) => Number(b.amount.toString()) - Number(a.amount.toString())
|
|
13307
|
+
);
|
|
13160
13308
|
_pools.forEach((p) => {
|
|
13161
13309
|
const flow = {
|
|
13162
13310
|
title: `Pool name: ${p.pool_name}`,
|
|
13163
13311
|
subItems: [
|
|
13164
13312
|
{ key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%` },
|
|
13165
|
-
{
|
|
13313
|
+
{
|
|
13314
|
+
key: "Weight",
|
|
13315
|
+
value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%`
|
|
13316
|
+
}
|
|
13166
13317
|
],
|
|
13167
13318
|
linkedFlows: [],
|
|
13168
13319
|
style: p.amount.greaterThan(0) ? { backgroundColor: "#35484f" /* Blue */.valueOf() } : { color: "gray" }
|
|
@@ -13194,7 +13345,12 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13194
13345
|
harvest.actualReward.toWei(),
|
|
13195
13346
|
this.address.address
|
|
13196
13347
|
);
|
|
13197
|
-
swapInfo = await avnu.getSwapInfo(
|
|
13348
|
+
swapInfo = await avnu.getSwapInfo(
|
|
13349
|
+
quote,
|
|
13350
|
+
this.address.address,
|
|
13351
|
+
0,
|
|
13352
|
+
this.address.address
|
|
13353
|
+
);
|
|
13198
13354
|
}
|
|
13199
13355
|
return [
|
|
13200
13356
|
this.contract.populate("harvest", [
|
|
@@ -13215,16 +13371,27 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13215
13371
|
* @returns {Promise<Array<{ vToken: ContractAddr, fee: Web3Number }>>} Array of fees deducted in different vTokens
|
|
13216
13372
|
*/
|
|
13217
13373
|
async getFee(allowedPools) {
|
|
13218
|
-
const assets = Web3Number.fromWei(
|
|
13219
|
-
|
|
13220
|
-
|
|
13374
|
+
const assets = Web3Number.fromWei(
|
|
13375
|
+
(await this.contract.total_assets()).toString(),
|
|
13376
|
+
this.asset().decimals
|
|
13377
|
+
);
|
|
13378
|
+
const totalSupply = Web3Number.fromWei(
|
|
13379
|
+
(await this.contract.total_supply()).toString(),
|
|
13380
|
+
this.asset().decimals
|
|
13381
|
+
);
|
|
13382
|
+
const prevIndex = Web3Number.fromWei(
|
|
13383
|
+
(await this.contract.get_previous_index()).toString(),
|
|
13384
|
+
18
|
|
13385
|
+
);
|
|
13221
13386
|
const currIndex = new Web3Number(1, 18).multipliedBy(assets).dividedBy(totalSupply);
|
|
13222
13387
|
logger.verbose(`Previous index: ${prevIndex.toString()}`);
|
|
13223
13388
|
logger.verbose(`Assets: ${assets.toString()}`);
|
|
13224
13389
|
logger.verbose(`Total supply: ${totalSupply.toString()}`);
|
|
13225
13390
|
logger.verbose(`Current index: ${currIndex.toNumber()}`);
|
|
13226
13391
|
if (currIndex.lt(prevIndex)) {
|
|
13227
|
-
logger.verbose(
|
|
13392
|
+
logger.verbose(
|
|
13393
|
+
`getFee::Current index is less than previous index, no fees to be deducted`
|
|
13394
|
+
);
|
|
13228
13395
|
return [];
|
|
13229
13396
|
}
|
|
13230
13397
|
const indexDiff = currIndex.minus(prevIndex);
|
|
@@ -13237,7 +13404,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13237
13404
|
return [];
|
|
13238
13405
|
}
|
|
13239
13406
|
const fees = [];
|
|
13240
|
-
let remainingFee = fee.plus(
|
|
13407
|
+
let remainingFee = fee.plus(
|
|
13408
|
+
Web3Number.fromWei("100", this.asset().decimals)
|
|
13409
|
+
);
|
|
13241
13410
|
for (const pool of allowedPools) {
|
|
13242
13411
|
const vToken = pool.v_token;
|
|
13243
13412
|
const balance = pool.amount;
|
|
@@ -13246,7 +13415,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13246
13415
|
break;
|
|
13247
13416
|
} else {
|
|
13248
13417
|
fees.push({ vToken, fee: Web3Number.fromWei(balance.toString(), 18) });
|
|
13249
|
-
remainingFee = remainingFee.minus(
|
|
13418
|
+
remainingFee = remainingFee.minus(
|
|
13419
|
+
Web3Number.fromWei(balance.toString(), 18)
|
|
13420
|
+
);
|
|
13250
13421
|
}
|
|
13251
13422
|
}
|
|
13252
13423
|
logger.verbose(`Fees: ${JSON.stringify(fees)}`);
|
|
@@ -13254,98 +13425,168 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13254
13425
|
}
|
|
13255
13426
|
};
|
|
13256
13427
|
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.";
|
|
13257
|
-
var _protocol = {
|
|
13428
|
+
var _protocol = {
|
|
13429
|
+
name: "Vesu",
|
|
13430
|
+
logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png"
|
|
13431
|
+
};
|
|
13258
13432
|
var _riskFactor = [
|
|
13259
13433
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
13260
13434
|
{ type: "Counterparty Risk" /* COUNTERPARTY_RISK */, value: 1, weight: 50 },
|
|
13261
13435
|
{ type: "Oracle Risk" /* ORACLE_RISK */, value: 0.5, weight: 25 }
|
|
13262
13436
|
];
|
|
13263
13437
|
var AUDIT_URL = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
13264
|
-
var
|
|
13265
|
-
|
|
13266
|
-
|
|
13267
|
-
|
|
13268
|
-
type: "ERC4626",
|
|
13269
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
13270
|
-
protocols: [_protocol],
|
|
13271
|
-
auditUrl: AUDIT_URL,
|
|
13272
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13273
|
-
risk: {
|
|
13274
|
-
riskFactor: _riskFactor,
|
|
13275
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13276
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13438
|
+
var faqs = [
|
|
13439
|
+
{
|
|
13440
|
+
question: "What is the Vesu Rebalancing Strategy?",
|
|
13441
|
+
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."
|
|
13277
13442
|
},
|
|
13278
|
-
|
|
13279
|
-
|
|
13280
|
-
|
|
13281
|
-
}, {
|
|
13282
|
-
name: "Vesu Fusion ETH",
|
|
13283
|
-
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13284
|
-
address: ContractAddr.from("0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"),
|
|
13285
|
-
type: "ERC4626",
|
|
13286
|
-
auditUrl: AUDIT_URL,
|
|
13287
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "ETH")],
|
|
13288
|
-
protocols: [_protocol],
|
|
13289
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13290
|
-
risk: {
|
|
13291
|
-
riskFactor: _riskFactor,
|
|
13292
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13293
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13443
|
+
{
|
|
13444
|
+
question: "Will I earn Vesu points?",
|
|
13445
|
+
answer: "Yes, of course! You will earn Vesu points for your deposits."
|
|
13294
13446
|
},
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
|
|
13298
|
-
}, {
|
|
13299
|
-
name: "Vesu Fusion USDC",
|
|
13300
|
-
description: _description.replace("{{TOKEN}}", "USDC"),
|
|
13301
|
-
address: ContractAddr.from("0xa858c97e9454f407d1bd7c57472fc8d8d8449a777c822b41d18e387816f29c"),
|
|
13302
|
-
type: "ERC4626",
|
|
13303
|
-
auditUrl: AUDIT_URL,
|
|
13304
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "USDC")],
|
|
13305
|
-
protocols: [_protocol],
|
|
13306
|
-
maxTVL: Web3Number.fromWei("0", 6),
|
|
13307
|
-
risk: {
|
|
13308
|
-
riskFactor: _riskFactor,
|
|
13309
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13310
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13447
|
+
{
|
|
13448
|
+
question: "How does the strategy optimize yield?",
|
|
13449
|
+
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."
|
|
13311
13450
|
},
|
|
13312
|
-
|
|
13313
|
-
|
|
13451
|
+
{
|
|
13452
|
+
question: "What are the risks associated with this strategy?",
|
|
13453
|
+
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."
|
|
13454
|
+
},
|
|
13455
|
+
{
|
|
13456
|
+
question: "How are fees calculated and deducted?",
|
|
13457
|
+
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."
|
|
13458
|
+
},
|
|
13459
|
+
{
|
|
13460
|
+
question: "What happens if a pool exceeds its maximum weight?",
|
|
13461
|
+
answer: "If a pool exceeds its maximum weight, the strategy rebalances by withdrawing excess funds and reallocating them to other pools with available capacity."
|
|
13462
|
+
},
|
|
13463
|
+
{
|
|
13464
|
+
question: "Can I withdraw my assets at any time?",
|
|
13465
|
+
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."
|
|
13466
|
+
},
|
|
13467
|
+
{
|
|
13468
|
+
question: "What happens to my Defi Spring STRK rewards?",
|
|
13469
|
+
answer: "STRK rewards are automatically harvested and reinvested into the strategy every week to maximize compounding returns."
|
|
13470
|
+
},
|
|
13471
|
+
{
|
|
13472
|
+
question: "Is the strategy audited?",
|
|
13473
|
+
answer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
13474
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
13475
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: "https://docs.strkfarm.com/p/strategies/vesu-fusion-rebalancing-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
13476
|
+
"."
|
|
13477
|
+
] })
|
|
13314
13478
|
}
|
|
13315
|
-
|
|
13316
|
-
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
|
|
13327
|
-
|
|
13479
|
+
];
|
|
13480
|
+
var VesuRebalanceStrategies = [
|
|
13481
|
+
{
|
|
13482
|
+
name: "Vesu Fusion STRK",
|
|
13483
|
+
description: _description.replace("{{TOKEN}}", "STRK"),
|
|
13484
|
+
address: ContractAddr.from(
|
|
13485
|
+
"0x7fb5bcb8525954a60fde4e8fb8220477696ce7117ef264775a1770e23571929"
|
|
13486
|
+
),
|
|
13487
|
+
type: "ERC4626",
|
|
13488
|
+
depositTokens: [
|
|
13489
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
13490
|
+
],
|
|
13491
|
+
protocols: [_protocol],
|
|
13492
|
+
auditUrl: AUDIT_URL,
|
|
13493
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
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
|
+
{
|
|
13505
|
+
name: "Vesu Fusion ETH",
|
|
13506
|
+
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13507
|
+
address: ContractAddr.from(
|
|
13508
|
+
"0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"
|
|
13509
|
+
),
|
|
13510
|
+
type: "ERC4626",
|
|
13511
|
+
auditUrl: AUDIT_URL,
|
|
13512
|
+
depositTokens: [
|
|
13513
|
+
Global.getDefaultTokens().find((t) => t.symbol === "ETH")
|
|
13514
|
+
],
|
|
13515
|
+
protocols: [_protocol],
|
|
13516
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13517
|
+
risk: {
|
|
13518
|
+
riskFactor: _riskFactor,
|
|
13519
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13520
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13521
|
+
},
|
|
13522
|
+
additionalInfo: {
|
|
13523
|
+
feeBps: 1e3
|
|
13524
|
+
},
|
|
13525
|
+
faqs
|
|
13328
13526
|
},
|
|
13329
|
-
|
|
13330
|
-
|
|
13527
|
+
{
|
|
13528
|
+
name: "Vesu Fusion USDC",
|
|
13529
|
+
description: _description.replace("{{TOKEN}}", "USDC"),
|
|
13530
|
+
address: ContractAddr.from(
|
|
13531
|
+
"0xa858c97e9454f407d1bd7c57472fc8d8d8449a777c822b41d18e387816f29c"
|
|
13532
|
+
),
|
|
13533
|
+
type: "ERC4626",
|
|
13534
|
+
auditUrl: AUDIT_URL,
|
|
13535
|
+
depositTokens: [
|
|
13536
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC")
|
|
13537
|
+
],
|
|
13538
|
+
protocols: [_protocol],
|
|
13539
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13540
|
+
risk: {
|
|
13541
|
+
riskFactor: _riskFactor,
|
|
13542
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13543
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13544
|
+
},
|
|
13545
|
+
additionalInfo: {
|
|
13546
|
+
feeBps: 1e3
|
|
13547
|
+
},
|
|
13548
|
+
faqs
|
|
13549
|
+
},
|
|
13550
|
+
{
|
|
13551
|
+
name: "Vesu Fusion USDT",
|
|
13552
|
+
description: _description.replace("{{TOKEN}}", "USDT"),
|
|
13553
|
+
address: ContractAddr.from(
|
|
13554
|
+
"0x115e94e722cfc4c77a2f15c4aefb0928c1c0029e5a57570df24c650cb7cec2c"
|
|
13555
|
+
),
|
|
13556
|
+
type: "ERC4626",
|
|
13557
|
+
depositTokens: [
|
|
13558
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
13559
|
+
],
|
|
13560
|
+
auditUrl: AUDIT_URL,
|
|
13561
|
+
protocols: [_protocol],
|
|
13562
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13563
|
+
risk: {
|
|
13564
|
+
riskFactor: _riskFactor,
|
|
13565
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13566
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13567
|
+
},
|
|
13568
|
+
additionalInfo: {
|
|
13569
|
+
feeBps: 1e3
|
|
13570
|
+
},
|
|
13571
|
+
faqs
|
|
13572
|
+
// }, {
|
|
13573
|
+
// name: 'Vesu Fusion WBTC',
|
|
13574
|
+
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13575
|
+
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13576
|
+
// type: 'ERC4626',
|
|
13577
|
+
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13578
|
+
// auditUrl: AUDIT_URL,
|
|
13579
|
+
// protocols: [_protocol],
|
|
13580
|
+
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13581
|
+
// risk: {
|
|
13582
|
+
// riskFactor: _riskFactor,
|
|
13583
|
+
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13584
|
+
// },
|
|
13585
|
+
// additionalInfo: {
|
|
13586
|
+
// feeBps: 1000,
|
|
13587
|
+
// },
|
|
13331
13588
|
}
|
|
13332
|
-
|
|
13333
|
-
// name: 'Vesu Fusion WBTC',
|
|
13334
|
-
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13335
|
-
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13336
|
-
// type: 'ERC4626',
|
|
13337
|
-
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13338
|
-
// auditUrl: AUDIT_URL,
|
|
13339
|
-
// protocols: [_protocol],
|
|
13340
|
-
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13341
|
-
// risk: {
|
|
13342
|
-
// riskFactor: _riskFactor,
|
|
13343
|
-
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13344
|
-
// },
|
|
13345
|
-
// additionalInfo: {
|
|
13346
|
-
// feeBps: 1000,
|
|
13347
|
-
// },
|
|
13348
|
-
}];
|
|
13589
|
+
];
|
|
13349
13590
|
|
|
13350
13591
|
// src/strategies/ekubo-cl-vault.tsx
|
|
13351
13592
|
var import_starknet9 = require("starknet");
|
|
@@ -18250,7 +18491,7 @@ var erc4626_abi_default = [
|
|
|
18250
18491
|
];
|
|
18251
18492
|
|
|
18252
18493
|
// src/strategies/ekubo-cl-vault.tsx
|
|
18253
|
-
var
|
|
18494
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
18254
18495
|
var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
18255
18496
|
/**
|
|
18256
18497
|
* Creates a new VesuRebalance strategy instance.
|
|
@@ -18263,15 +18504,34 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18263
18504
|
super(config);
|
|
18264
18505
|
this.BASE_WEIGHT = 1e4;
|
|
18265
18506
|
this.pricer = pricer;
|
|
18266
|
-
assert(
|
|
18507
|
+
assert(
|
|
18508
|
+
metadata.depositTokens.length === 2,
|
|
18509
|
+
"EkuboCL only supports 2 deposit token"
|
|
18510
|
+
);
|
|
18267
18511
|
this.metadata = metadata;
|
|
18268
18512
|
this.address = metadata.address;
|
|
18269
|
-
this.contract = new import_starknet9.Contract(
|
|
18270
|
-
|
|
18513
|
+
this.contract = new import_starknet9.Contract(
|
|
18514
|
+
cl_vault_abi_default,
|
|
18515
|
+
this.address.address,
|
|
18516
|
+
this.config.provider
|
|
18517
|
+
);
|
|
18518
|
+
this.lstContract = new import_starknet9.Contract(
|
|
18519
|
+
erc4626_abi_default,
|
|
18520
|
+
this.metadata.additionalInfo.lstContract.address,
|
|
18521
|
+
this.config.provider
|
|
18522
|
+
);
|
|
18271
18523
|
const EKUBO_POSITION = "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
|
|
18272
|
-
this.ekuboPositionsContract = new import_starknet9.Contract(
|
|
18524
|
+
this.ekuboPositionsContract = new import_starknet9.Contract(
|
|
18525
|
+
ekubo_positions_abi_default,
|
|
18526
|
+
EKUBO_POSITION,
|
|
18527
|
+
this.config.provider
|
|
18528
|
+
);
|
|
18273
18529
|
const EKUBO_MATH = "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
|
|
18274
|
-
this.ekuboMathContract = new import_starknet9.Contract(
|
|
18530
|
+
this.ekuboMathContract = new import_starknet9.Contract(
|
|
18531
|
+
ekubo_math_abi_default,
|
|
18532
|
+
EKUBO_MATH,
|
|
18533
|
+
this.config.provider
|
|
18534
|
+
);
|
|
18275
18535
|
this.avnu = new AvnuWrapper();
|
|
18276
18536
|
}
|
|
18277
18537
|
async matchInputAmounts(amountInfo) {
|
|
@@ -18296,25 +18556,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18296
18556
|
/** Returns minimum amounts give given two amounts based on what can be added for liq */
|
|
18297
18557
|
async getMinDepositAmounts(amountInfo) {
|
|
18298
18558
|
const shares = await this.tokensToShares(amountInfo);
|
|
18299
|
-
const { amount0, amount1 } = await this.contract.call(
|
|
18559
|
+
const { amount0, amount1 } = await this.contract.call(
|
|
18560
|
+
"convert_to_assets",
|
|
18561
|
+
[import_starknet9.uint256.bnToUint256(shares.toWei())]
|
|
18562
|
+
);
|
|
18300
18563
|
return {
|
|
18301
18564
|
token0: {
|
|
18302
18565
|
tokenInfo: amountInfo.token0.tokenInfo,
|
|
18303
|
-
amount: Web3Number.fromWei(
|
|
18566
|
+
amount: Web3Number.fromWei(
|
|
18567
|
+
amount0.toString(),
|
|
18568
|
+
amountInfo.token0.tokenInfo.decimals
|
|
18569
|
+
)
|
|
18304
18570
|
},
|
|
18305
18571
|
token1: {
|
|
18306
18572
|
tokenInfo: amountInfo.token1.tokenInfo,
|
|
18307
|
-
amount: Web3Number.fromWei(
|
|
18573
|
+
amount: Web3Number.fromWei(
|
|
18574
|
+
amount1.toString(),
|
|
18575
|
+
amountInfo.token1.tokenInfo.decimals
|
|
18576
|
+
)
|
|
18308
18577
|
}
|
|
18309
18578
|
};
|
|
18310
18579
|
}
|
|
18311
18580
|
async depositCall(amountInfo, receiver) {
|
|
18312
18581
|
const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
|
|
18313
|
-
const token0Contract = new import_starknet9.Contract(
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
18317
|
-
|
|
18582
|
+
const token0Contract = new import_starknet9.Contract(
|
|
18583
|
+
erc4626_abi_default,
|
|
18584
|
+
amountInfo.token0.tokenInfo.address.address,
|
|
18585
|
+
this.config.provider
|
|
18586
|
+
);
|
|
18587
|
+
const token1Contract = new import_starknet9.Contract(
|
|
18588
|
+
erc4626_abi_default,
|
|
18589
|
+
amountInfo.token1.tokenInfo.address.address,
|
|
18590
|
+
this.config.provider
|
|
18591
|
+
);
|
|
18592
|
+
const call1 = token0Contract.populate("approve", [
|
|
18593
|
+
this.address.address,
|
|
18594
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token0.amount.toWei())
|
|
18595
|
+
]);
|
|
18596
|
+
const call2 = token1Contract.populate("approve", [
|
|
18597
|
+
this.address.address,
|
|
18598
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token1.amount.toWei())
|
|
18599
|
+
]);
|
|
18600
|
+
const call3 = this.contract.populate("deposit", [
|
|
18601
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token0.amount.toWei()),
|
|
18602
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token1.amount.toWei()),
|
|
18603
|
+
receiver.address
|
|
18604
|
+
]);
|
|
18318
18605
|
const calls = [];
|
|
18319
18606
|
if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
|
|
18320
18607
|
if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
|
|
@@ -18329,25 +18616,29 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18329
18616
|
}
|
|
18330
18617
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
18331
18618
|
const shares = await this.tokensToShares(amountInfo);
|
|
18332
|
-
logger.verbose(
|
|
18333
|
-
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18619
|
+
logger.verbose(
|
|
18620
|
+
`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`
|
|
18621
|
+
);
|
|
18622
|
+
return [
|
|
18623
|
+
this.contract.populate("withdraw", [
|
|
18624
|
+
import_starknet9.uint256.bnToUint256(shares.toWei()),
|
|
18625
|
+
receiver.address
|
|
18626
|
+
])
|
|
18627
|
+
];
|
|
18337
18628
|
}
|
|
18338
18629
|
rebalanceCall(newBounds, swapParams) {
|
|
18339
|
-
return [
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18630
|
+
return [
|
|
18631
|
+
this.contract.populate("rebalance", [
|
|
18632
|
+
{
|
|
18633
|
+
lower: _EkuboCLVault.tickToi129(Number(newBounds.lowerTick)),
|
|
18634
|
+
upper: _EkuboCLVault.tickToi129(Number(newBounds.upperTick))
|
|
18635
|
+
},
|
|
18636
|
+
swapParams
|
|
18637
|
+
])
|
|
18638
|
+
];
|
|
18346
18639
|
}
|
|
18347
18640
|
handleUnusedCall(swapParams) {
|
|
18348
|
-
return [this.contract.populate("handle_unused", [
|
|
18349
|
-
swapParams
|
|
18350
|
-
])];
|
|
18641
|
+
return [this.contract.populate("handle_unused", [swapParams])];
|
|
18351
18642
|
}
|
|
18352
18643
|
handleFeesCall() {
|
|
18353
18644
|
return [this.contract.populate("handle_fees", [])];
|
|
@@ -18363,8 +18654,12 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18363
18654
|
let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
18364
18655
|
const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
18365
18656
|
const blockBefore = blockNow - sinceBlocks;
|
|
18366
|
-
const adjustedSupplyNow = supplyNow.minus(
|
|
18367
|
-
|
|
18657
|
+
const adjustedSupplyNow = supplyNow.minus(
|
|
18658
|
+
await this.getHarvestRewardShares(blockBefore, blockNow)
|
|
18659
|
+
);
|
|
18660
|
+
let blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
18661
|
+
blockBefore
|
|
18662
|
+
);
|
|
18368
18663
|
const tvlBefore = await this._getTVL(blockBefore);
|
|
18369
18664
|
const supplyBefore = await this.totalSupply(blockBefore);
|
|
18370
18665
|
const priceBefore = await this.getCurrentPrice(blockBefore);
|
|
@@ -18382,7 +18677,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18382
18677
|
logger.verbose(`Supply before: ${supplyBefore.toString()}`);
|
|
18383
18678
|
logger.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
|
|
18384
18679
|
logger.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
|
|
18385
|
-
const apyForGivenBlocks = Number(
|
|
18680
|
+
const apyForGivenBlocks = Number(
|
|
18681
|
+
tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)
|
|
18682
|
+
) / 1e4;
|
|
18386
18683
|
return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
|
|
18387
18684
|
}
|
|
18388
18685
|
async getHarvestRewardShares(fromBlock, toBlock) {
|
|
@@ -18400,7 +18697,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18400
18697
|
} else {
|
|
18401
18698
|
shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
|
|
18402
18699
|
}
|
|
18403
|
-
logger.verbose(
|
|
18700
|
+
logger.verbose(
|
|
18701
|
+
`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`
|
|
18702
|
+
);
|
|
18404
18703
|
}
|
|
18405
18704
|
return shares;
|
|
18406
18705
|
}
|
|
@@ -18410,15 +18709,25 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18410
18709
|
}
|
|
18411
18710
|
async getUserTVL(user, blockIdentifier = "pending") {
|
|
18412
18711
|
let bal = await this.balanceOf(user, blockIdentifier);
|
|
18413
|
-
const assets = await this.contract.call(
|
|
18414
|
-
|
|
18415
|
-
|
|
18712
|
+
const assets = await this.contract.call(
|
|
18713
|
+
"convert_to_assets",
|
|
18714
|
+
[import_starknet9.uint256.bnToUint256(bal.toWei())],
|
|
18715
|
+
{
|
|
18716
|
+
blockIdentifier
|
|
18717
|
+
}
|
|
18718
|
+
);
|
|
18416
18719
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18417
18720
|
this.assertValidDepositTokens(poolKey);
|
|
18418
18721
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18419
18722
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18420
|
-
const amount0 = Web3Number.fromWei(
|
|
18421
|
-
|
|
18723
|
+
const amount0 = Web3Number.fromWei(
|
|
18724
|
+
assets.amount0.toString(),
|
|
18725
|
+
token0Info.decimals
|
|
18726
|
+
);
|
|
18727
|
+
const amount1 = Web3Number.fromWei(
|
|
18728
|
+
assets.amount1.toString(),
|
|
18729
|
+
token1Info.decimals
|
|
18730
|
+
);
|
|
18422
18731
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18423
18732
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18424
18733
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
@@ -18442,7 +18751,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18442
18751
|
blockIdentifier
|
|
18443
18752
|
});
|
|
18444
18753
|
const bounds = await this.getCurrentBounds(blockIdentifier);
|
|
18445
|
-
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18754
|
+
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18755
|
+
Web3Number.fromWei(result.toString(), 18),
|
|
18756
|
+
bounds,
|
|
18757
|
+
blockIdentifier
|
|
18758
|
+
);
|
|
18446
18759
|
return { amount0, amount1 };
|
|
18447
18760
|
}
|
|
18448
18761
|
async totalSupply(blockIdentifier = "pending") {
|
|
@@ -18452,8 +18765,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18452
18765
|
return Web3Number.fromWei(res.toString(), 18);
|
|
18453
18766
|
}
|
|
18454
18767
|
assertValidDepositTokens(poolKey) {
|
|
18455
|
-
assert(
|
|
18456
|
-
|
|
18768
|
+
assert(
|
|
18769
|
+
poolKey.token0.eq(this.metadata.depositTokens[0].address),
|
|
18770
|
+
"Expected token0 in depositTokens[0]"
|
|
18771
|
+
);
|
|
18772
|
+
assert(
|
|
18773
|
+
poolKey.token1.eq(this.metadata.depositTokens[1].address),
|
|
18774
|
+
"Expected token1 in depositTokens[1]"
|
|
18775
|
+
);
|
|
18457
18776
|
}
|
|
18458
18777
|
async getTVL(blockIdentifier = "pending") {
|
|
18459
18778
|
const { amount0, amount1 } = await this._getTVL(blockIdentifier);
|
|
@@ -18483,26 +18802,35 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18483
18802
|
const nftID = await this.getCurrentNFTID();
|
|
18484
18803
|
const poolKey = await this.getPoolKey();
|
|
18485
18804
|
const currentBounds = await this.getCurrentBounds();
|
|
18486
|
-
const result = await this.ekuboPositionsContract.call(
|
|
18487
|
-
|
|
18488
|
-
|
|
18489
|
-
|
|
18490
|
-
|
|
18491
|
-
|
|
18492
|
-
|
|
18493
|
-
|
|
18494
|
-
|
|
18495
|
-
|
|
18496
|
-
|
|
18497
|
-
|
|
18498
|
-
|
|
18499
|
-
|
|
18805
|
+
const result = await this.ekuboPositionsContract.call(
|
|
18806
|
+
"get_token_info",
|
|
18807
|
+
[
|
|
18808
|
+
nftID,
|
|
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
|
+
{
|
|
18817
|
+
lower: _EkuboCLVault.tickToi129(Number(currentBounds.lowerTick)),
|
|
18818
|
+
upper: _EkuboCLVault.tickToi129(Number(currentBounds.upperTick))
|
|
18819
|
+
}
|
|
18820
|
+
]
|
|
18821
|
+
);
|
|
18500
18822
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18501
18823
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18502
18824
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18503
18825
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18504
|
-
const token0Web3 = Web3Number.fromWei(
|
|
18505
|
-
|
|
18826
|
+
const token0Web3 = Web3Number.fromWei(
|
|
18827
|
+
result.fees0.toString(),
|
|
18828
|
+
token0Info.decimals
|
|
18829
|
+
);
|
|
18830
|
+
const token1Web3 = Web3Number.fromWei(
|
|
18831
|
+
result.fees1.toString(),
|
|
18832
|
+
token1Info.decimals
|
|
18833
|
+
);
|
|
18506
18834
|
const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
|
|
18507
18835
|
const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
|
|
18508
18836
|
return {
|
|
@@ -18524,7 +18852,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18524
18852
|
return Number(result.salt.toString());
|
|
18525
18853
|
}
|
|
18526
18854
|
async truePrice() {
|
|
18527
|
-
const result = await this.lstContract.call("convert_to_assets", [
|
|
18855
|
+
const result = await this.lstContract.call("convert_to_assets", [
|
|
18856
|
+
import_starknet9.uint256.bnToUint256(BigInt(1e18).toString())
|
|
18857
|
+
]);
|
|
18528
18858
|
const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
|
|
18529
18859
|
return truePrice;
|
|
18530
18860
|
}
|
|
@@ -18533,22 +18863,36 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18533
18863
|
return this._getCurrentPrice(poolKey, blockIdentifier);
|
|
18534
18864
|
}
|
|
18535
18865
|
async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
|
|
18536
|
-
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18866
|
+
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18867
|
+
"get_pool_price",
|
|
18868
|
+
[
|
|
18869
|
+
{
|
|
18870
|
+
token0: poolKey.token0.address,
|
|
18871
|
+
token1: poolKey.token1.address,
|
|
18872
|
+
fee: poolKey.fee,
|
|
18873
|
+
tick_spacing: poolKey.tick_spacing,
|
|
18874
|
+
extension: poolKey.extension
|
|
18875
|
+
}
|
|
18876
|
+
],
|
|
18537
18877
|
{
|
|
18538
|
-
|
|
18539
|
-
token1: poolKey.token1.address,
|
|
18540
|
-
fee: poolKey.fee,
|
|
18541
|
-
tick_spacing: poolKey.tick_spacing,
|
|
18542
|
-
extension: poolKey.extension
|
|
18878
|
+
blockIdentifier
|
|
18543
18879
|
}
|
|
18544
|
-
|
|
18545
|
-
|
|
18546
|
-
|
|
18547
|
-
|
|
18548
|
-
console.log(
|
|
18880
|
+
);
|
|
18881
|
+
const sqrtRatio = _EkuboCLVault.div2Power128(
|
|
18882
|
+
BigInt(priceInfo.sqrt_ratio.toString())
|
|
18883
|
+
);
|
|
18884
|
+
console.log(
|
|
18885
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`
|
|
18886
|
+
);
|
|
18549
18887
|
const price = sqrtRatio * sqrtRatio;
|
|
18550
|
-
const tick = _EkuboCLVault.priceToTick(
|
|
18551
|
-
|
|
18888
|
+
const tick = _EkuboCLVault.priceToTick(
|
|
18889
|
+
price,
|
|
18890
|
+
true,
|
|
18891
|
+
Number(poolKey.tick_spacing)
|
|
18892
|
+
);
|
|
18893
|
+
console.log(
|
|
18894
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`
|
|
18895
|
+
);
|
|
18552
18896
|
return {
|
|
18553
18897
|
price,
|
|
18554
18898
|
tick: tick.mag * (tick.sign == 0 ? 1 : -1),
|
|
@@ -18588,7 +18932,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18588
18932
|
};
|
|
18589
18933
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18590
18934
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18591
|
-
assert(
|
|
18935
|
+
assert(
|
|
18936
|
+
token0Info.decimals == token1Info.decimals,
|
|
18937
|
+
"Tested only for equal decimals"
|
|
18938
|
+
);
|
|
18592
18939
|
this.poolKey = poolKey;
|
|
18593
18940
|
return poolKey;
|
|
18594
18941
|
}
|
|
@@ -18612,11 +18959,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18612
18959
|
async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
|
|
18613
18960
|
assert(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
|
|
18614
18961
|
const sampleLiq = 1e20;
|
|
18615
|
-
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18616
|
-
|
|
18962
|
+
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18963
|
+
Web3Number.fromWei(sampleLiq.toString(), 18),
|
|
18964
|
+
bounds
|
|
18965
|
+
);
|
|
18966
|
+
logger.verbose(
|
|
18967
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`
|
|
18968
|
+
);
|
|
18617
18969
|
assert(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
|
|
18618
18970
|
const price = await (await this.getCurrentPrice()).price;
|
|
18619
|
-
logger.verbose(
|
|
18971
|
+
logger.verbose(
|
|
18972
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`
|
|
18973
|
+
);
|
|
18620
18974
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18621
18975
|
if (sampleAmount1.eq(0)) {
|
|
18622
18976
|
return {
|
|
@@ -18646,12 +19000,22 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18646
19000
|
};
|
|
18647
19001
|
}
|
|
18648
19002
|
}
|
|
18649
|
-
assert(
|
|
19003
|
+
assert(
|
|
19004
|
+
sampleAmount0.decimals == sampleAmount1.decimals,
|
|
19005
|
+
"Sample amounts have different decimals"
|
|
19006
|
+
);
|
|
18650
19007
|
const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
|
|
18651
19008
|
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
18652
|
-
logger.verbose(
|
|
19009
|
+
logger.verbose(
|
|
19010
|
+
`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
|
|
19011
|
+
);
|
|
18653
19012
|
if (justUseInputAmount)
|
|
18654
|
-
return this._solveExpectedAmountsEq(
|
|
19013
|
+
return this._solveExpectedAmountsEq(
|
|
19014
|
+
amount0,
|
|
19015
|
+
amount1,
|
|
19016
|
+
ratioWeb3Number,
|
|
19017
|
+
price
|
|
19018
|
+
);
|
|
18655
19019
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18656
19020
|
const _amount1 = amount0.dividedBy(ratioWeb3Number);
|
|
18657
19021
|
return {
|
|
@@ -18667,7 +19031,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18667
19031
|
ratio
|
|
18668
19032
|
};
|
|
18669
19033
|
} else {
|
|
18670
|
-
throw new Error(
|
|
19034
|
+
throw new Error(
|
|
19035
|
+
"Both amounts are non-zero, cannot compute expected amounts"
|
|
19036
|
+
);
|
|
18671
19037
|
}
|
|
18672
19038
|
}
|
|
18673
19039
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
@@ -18684,34 +19050,65 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18684
19050
|
const erc20Mod = new ERC20(this.config);
|
|
18685
19051
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18686
19052
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18687
|
-
const token0Bal1 = await erc20Mod.balanceOf(
|
|
18688
|
-
|
|
18689
|
-
|
|
19053
|
+
const token0Bal1 = await erc20Mod.balanceOf(
|
|
19054
|
+
poolKey.token0,
|
|
19055
|
+
this.address.address,
|
|
19056
|
+
token0Info.decimals
|
|
19057
|
+
);
|
|
19058
|
+
const token1Bal1 = await erc20Mod.balanceOf(
|
|
19059
|
+
poolKey.token1,
|
|
19060
|
+
this.address.address,
|
|
19061
|
+
token1Info.decimals
|
|
19062
|
+
);
|
|
19063
|
+
logger.verbose(
|
|
19064
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`
|
|
19065
|
+
);
|
|
18690
19066
|
const token0Price = await this.pricer.getPrice(token0Info.symbol);
|
|
18691
19067
|
const token1Price = await this.pricer.getPrice(token1Info.symbol);
|
|
18692
19068
|
const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
|
|
18693
19069
|
const token1PriceUsd = token1Price.price * Number(token1Bal1.toFixed(13));
|
|
18694
19070
|
if (token0PriceUsd > 1 && token1PriceUsd > 1) {
|
|
18695
|
-
throw new Error(
|
|
19071
|
+
throw new Error(
|
|
19072
|
+
"Both tokens are non-zero and above $1, call handle_fees first"
|
|
19073
|
+
);
|
|
18696
19074
|
}
|
|
18697
19075
|
let token0Bal = token0Bal1;
|
|
18698
19076
|
let token1Bal = token1Bal1;
|
|
18699
19077
|
if (considerRebalance) {
|
|
18700
|
-
logger.verbose(
|
|
19078
|
+
logger.verbose(
|
|
19079
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`
|
|
19080
|
+
);
|
|
18701
19081
|
const tvl = await this.getTVL();
|
|
18702
19082
|
token0Bal = token0Bal.plus(tvl.token0.amount.toString());
|
|
18703
19083
|
token1Bal = token1Bal.plus(tvl.token1.amount.toString());
|
|
18704
19084
|
} else {
|
|
18705
|
-
logger.verbose(
|
|
19085
|
+
logger.verbose(
|
|
19086
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: false`
|
|
19087
|
+
);
|
|
18706
19088
|
}
|
|
18707
|
-
logger.verbose(
|
|
19089
|
+
logger.verbose(
|
|
19090
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
19091
|
+
);
|
|
18708
19092
|
const newBounds = await this.getNewBounds();
|
|
18709
|
-
logger.verbose(
|
|
18710
|
-
|
|
19093
|
+
logger.verbose(
|
|
19094
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`
|
|
19095
|
+
);
|
|
19096
|
+
return await this.getSwapInfoGivenAmounts(
|
|
19097
|
+
poolKey,
|
|
19098
|
+
token0Bal,
|
|
19099
|
+
token1Bal,
|
|
19100
|
+
newBounds
|
|
19101
|
+
);
|
|
18711
19102
|
}
|
|
18712
19103
|
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
|
|
18713
|
-
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
18714
|
-
|
|
19104
|
+
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
19105
|
+
token0Bal,
|
|
19106
|
+
token1Bal,
|
|
19107
|
+
bounds
|
|
19108
|
+
);
|
|
19109
|
+
logger.verbose(
|
|
19110
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19111
|
+
);
|
|
18715
19112
|
let retry = 0;
|
|
18716
19113
|
const maxRetry = 10;
|
|
18717
19114
|
while (retry < maxRetry) {
|
|
@@ -18728,9 +19125,15 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18728
19125
|
const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
|
|
18729
19126
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
18730
19127
|
const expectedRatio = expectedAmounts.ratio;
|
|
18731
|
-
logger.verbose(
|
|
18732
|
-
|
|
18733
|
-
|
|
19128
|
+
logger.verbose(
|
|
19129
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`
|
|
19130
|
+
);
|
|
19131
|
+
logger.verbose(
|
|
19132
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`
|
|
19133
|
+
);
|
|
19134
|
+
logger.verbose(
|
|
19135
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`
|
|
19136
|
+
);
|
|
18734
19137
|
if (amountToSell.eq(0)) {
|
|
18735
19138
|
return {
|
|
18736
19139
|
token_from_address: tokenToSell.address,
|
|
@@ -18744,23 +19147,62 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18744
19147
|
routes: []
|
|
18745
19148
|
};
|
|
18746
19149
|
}
|
|
18747
|
-
const quote = await this.avnu.getQuotes(
|
|
19150
|
+
const quote = await this.avnu.getQuotes(
|
|
19151
|
+
tokenToSell.address,
|
|
19152
|
+
tokenToBuy.address,
|
|
19153
|
+
amountToSell.toWei(),
|
|
19154
|
+
this.address.address
|
|
19155
|
+
);
|
|
18748
19156
|
if (remainingSellAmount.eq(0)) {
|
|
18749
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18750
|
-
|
|
19157
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19158
|
+
quote.buyAmount.toString(),
|
|
19159
|
+
tokenToBuyInfo.decimals
|
|
19160
|
+
).multipliedBy(0.9999);
|
|
19161
|
+
return await this.avnu.getSwapInfo(
|
|
19162
|
+
quote,
|
|
19163
|
+
this.address.address,
|
|
19164
|
+
0,
|
|
19165
|
+
this.address.address,
|
|
19166
|
+
minAmountOut.toWei()
|
|
19167
|
+
);
|
|
18751
19168
|
}
|
|
18752
|
-
const amountOut = Web3Number.fromWei(
|
|
19169
|
+
const amountOut = Web3Number.fromWei(
|
|
19170
|
+
quote.buyAmount.toString(),
|
|
19171
|
+
tokenToBuyInfo.decimals
|
|
19172
|
+
);
|
|
18753
19173
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
18754
19174
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
18755
|
-
logger.verbose(
|
|
18756
|
-
|
|
18757
|
-
|
|
19175
|
+
logger.verbose(
|
|
19176
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
19177
|
+
);
|
|
19178
|
+
logger.verbose(
|
|
19179
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swapPrice: ${swapPrice.toString()}`
|
|
19180
|
+
);
|
|
19181
|
+
logger.verbose(
|
|
19182
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newRatio: ${newRatio.toString()}`
|
|
19183
|
+
);
|
|
18758
19184
|
if (Number(newRatio.toString()) > expectedRatio * 1.0000001 || Number(newRatio.toString()) < expectedRatio * 0.9999999) {
|
|
18759
|
-
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
18760
|
-
|
|
19185
|
+
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
19186
|
+
token0Bal,
|
|
19187
|
+
token1Bal,
|
|
19188
|
+
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
19189
|
+
Number(swapPrice.toString())
|
|
19190
|
+
);
|
|
19191
|
+
logger.verbose(
|
|
19192
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19193
|
+
);
|
|
18761
19194
|
} else {
|
|
18762
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18763
|
-
|
|
19195
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19196
|
+
quote.buyAmount.toString(),
|
|
19197
|
+
tokenToBuyInfo.decimals
|
|
19198
|
+
).multipliedBy(0.9999);
|
|
19199
|
+
return await this.avnu.getSwapInfo(
|
|
19200
|
+
quote,
|
|
19201
|
+
this.address.address,
|
|
19202
|
+
0,
|
|
19203
|
+
this.address.address,
|
|
19204
|
+
minAmountOut.toWei()
|
|
19205
|
+
);
|
|
18764
19206
|
}
|
|
18765
19207
|
retry++;
|
|
18766
19208
|
}
|
|
@@ -18769,8 +19211,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18769
19211
|
/**
|
|
18770
19212
|
* Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
|
|
18771
19213
|
* Uses binary search approach to find optimal swap amount.
|
|
18772
|
-
*
|
|
18773
|
-
* @param newBounds - The new tick bounds to rebalance to
|
|
19214
|
+
*
|
|
19215
|
+
* @param newBounds - The new tick bounds to rebalance to
|
|
18774
19216
|
* @param swapInfo - Initial swap parameters for rebalancing
|
|
18775
19217
|
* @param acc - Account to estimate gas fees with
|
|
18776
19218
|
* @param retry - Current retry attempt number (default 0)
|
|
@@ -18797,7 +19239,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18797
19239
|
logger.error(`Rebalance failed after ${MAX_RETRIES} retries`);
|
|
18798
19240
|
throw err;
|
|
18799
19241
|
}
|
|
18800
|
-
logger.error(
|
|
19242
|
+
logger.error(
|
|
19243
|
+
`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`
|
|
19244
|
+
);
|
|
18801
19245
|
const newSwapInfo = { ...swapInfo };
|
|
18802
19246
|
const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
|
|
18803
19247
|
logger.verbose(`Current amount: ${currentAmount.toString()}`);
|
|
@@ -18889,23 +19333,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18889
19333
|
const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
|
|
18890
19334
|
const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
|
|
18891
19335
|
const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
|
|
18892
|
-
logger.verbose(
|
|
18893
|
-
|
|
18894
|
-
|
|
19336
|
+
logger.verbose(
|
|
19337
|
+
`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`
|
|
19338
|
+
);
|
|
19339
|
+
const result = await this.ekuboMathContract.call(
|
|
19340
|
+
"liquidity_delta_to_amount_delta",
|
|
19341
|
+
[
|
|
19342
|
+
import_starknet9.uint256.bnToUint256(currentPrice.sqrtRatio),
|
|
19343
|
+
{
|
|
19344
|
+
mag: liquidity.toWei(),
|
|
19345
|
+
sign: 0
|
|
19346
|
+
},
|
|
19347
|
+
import_starknet9.uint256.bnToUint256(
|
|
19348
|
+
_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()
|
|
19349
|
+
),
|
|
19350
|
+
import_starknet9.uint256.bnToUint256(
|
|
19351
|
+
_EkuboCLVault.priceToSqrtRatio(upperPrice).toString()
|
|
19352
|
+
)
|
|
19353
|
+
],
|
|
18895
19354
|
{
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
|
|
18899
|
-
import_starknet9.uint256.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
|
|
18900
|
-
import_starknet9.uint256.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
|
|
18901
|
-
], {
|
|
18902
|
-
blockIdentifier
|
|
18903
|
-
});
|
|
19355
|
+
blockIdentifier
|
|
19356
|
+
}
|
|
19357
|
+
);
|
|
18904
19358
|
const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
|
|
18905
19359
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18906
19360
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18907
|
-
const amount0 = Web3Number.fromWei(
|
|
18908
|
-
|
|
19361
|
+
const amount0 = Web3Number.fromWei(
|
|
19362
|
+
_EkuboCLVault.i129ToNumber(result.amount0).toString(),
|
|
19363
|
+
token0Info.decimals
|
|
19364
|
+
);
|
|
19365
|
+
const amount1 = Web3Number.fromWei(
|
|
19366
|
+
_EkuboCLVault.i129ToNumber(result.amount1).toString(),
|
|
19367
|
+
token1Info.decimals
|
|
19368
|
+
);
|
|
18909
19369
|
return {
|
|
18910
19370
|
amount0,
|
|
18911
19371
|
amount1
|
|
@@ -18913,7 +19373,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18913
19373
|
}
|
|
18914
19374
|
async harvest(acc) {
|
|
18915
19375
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
18916
|
-
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19376
|
+
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19377
|
+
this.address
|
|
19378
|
+
);
|
|
18917
19379
|
const poolKey = await this.getPoolKey();
|
|
18918
19380
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18919
19381
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -18923,18 +19385,37 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18923
19385
|
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
18924
19386
|
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
18925
19387
|
const isToken1 = claim.token.eq(poolKey.token1);
|
|
18926
|
-
logger.verbose(
|
|
19388
|
+
logger.verbose(
|
|
19389
|
+
`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
|
|
19390
|
+
);
|
|
18927
19391
|
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
18928
19392
|
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
18929
|
-
logger.verbose(
|
|
18930
|
-
|
|
19393
|
+
logger.verbose(
|
|
19394
|
+
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
|
|
19395
|
+
);
|
|
19396
|
+
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
19397
|
+
poolKey,
|
|
19398
|
+
token0Amt,
|
|
19399
|
+
token1Amt,
|
|
19400
|
+
bounds
|
|
19401
|
+
);
|
|
18931
19402
|
swapInfo.token_to_address = token0Info.address.address;
|
|
18932
|
-
logger.verbose(
|
|
18933
|
-
|
|
19403
|
+
logger.verbose(
|
|
19404
|
+
`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
19405
|
+
);
|
|
19406
|
+
logger.verbose(
|
|
19407
|
+
`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
19408
|
+
);
|
|
18934
19409
|
const harvestEstimateCall = async (swapInfo1) => {
|
|
18935
|
-
const swap1Amount = Web3Number.fromWei(
|
|
19410
|
+
const swap1Amount = Web3Number.fromWei(
|
|
19411
|
+
import_starknet9.uint256.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
19412
|
+
18
|
|
19413
|
+
);
|
|
18936
19414
|
const remainingAmount = postFeeAmount.minus(swap1Amount);
|
|
18937
|
-
const swapInfo2 = {
|
|
19415
|
+
const swapInfo2 = {
|
|
19416
|
+
...swapInfo,
|
|
19417
|
+
token_from_amount: import_starknet9.uint256.bnToUint256(remainingAmount.toWei())
|
|
19418
|
+
};
|
|
18938
19419
|
swapInfo2.token_to_address = token1Info.address.address;
|
|
18939
19420
|
const calldata = [
|
|
18940
19421
|
claim.rewardsContract.address,
|
|
@@ -18947,11 +19428,23 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18947
19428
|
swapInfo,
|
|
18948
19429
|
swapInfo2
|
|
18949
19430
|
];
|
|
18950
|
-
logger.verbose(
|
|
19431
|
+
logger.verbose(
|
|
19432
|
+
`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
19433
|
+
calldata
|
|
19434
|
+
)}`
|
|
19435
|
+
);
|
|
18951
19436
|
return [this.contract.populate("harvest", calldata)];
|
|
18952
19437
|
};
|
|
18953
|
-
const _callsFinal = await this.rebalanceIter(
|
|
18954
|
-
|
|
19438
|
+
const _callsFinal = await this.rebalanceIter(
|
|
19439
|
+
swapInfo,
|
|
19440
|
+
acc,
|
|
19441
|
+
harvestEstimateCall
|
|
19442
|
+
);
|
|
19443
|
+
logger.verbose(
|
|
19444
|
+
`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|
|
19445
|
+
_callsFinal
|
|
19446
|
+
)}`
|
|
19447
|
+
);
|
|
18955
19448
|
calls.push(..._callsFinal);
|
|
18956
19449
|
}
|
|
18957
19450
|
return calls;
|
|
@@ -18961,24 +19454,37 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18961
19454
|
const poolKey = await this.getPoolKey();
|
|
18962
19455
|
const linkedFlow = {
|
|
18963
19456
|
title: this.metadata.name,
|
|
18964
|
-
subItems: [
|
|
19457
|
+
subItems: [
|
|
19458
|
+
{
|
|
19459
|
+
key: "Pool",
|
|
19460
|
+
value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing`
|
|
19461
|
+
}
|
|
19462
|
+
],
|
|
18965
19463
|
linkedFlows: [],
|
|
18966
19464
|
style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
|
|
18967
19465
|
};
|
|
18968
19466
|
const baseFlow = {
|
|
18969
19467
|
id: "base",
|
|
18970
19468
|
title: "Your Deposit",
|
|
18971
|
-
subItems: [
|
|
19469
|
+
subItems: [
|
|
19470
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
19471
|
+
{
|
|
19472
|
+
key: `Performance Fee`,
|
|
19473
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
19474
|
+
}
|
|
19475
|
+
],
|
|
18972
19476
|
linkedFlows: [linkedFlow],
|
|
18973
19477
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
18974
19478
|
};
|
|
18975
19479
|
const rebalanceFlow = {
|
|
18976
19480
|
id: "rebalance",
|
|
18977
19481
|
title: "Automated Rebalance",
|
|
18978
|
-
subItems: [
|
|
18979
|
-
|
|
18980
|
-
|
|
18981
|
-
|
|
19482
|
+
subItems: [
|
|
19483
|
+
{
|
|
19484
|
+
key: "Range selection",
|
|
19485
|
+
value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
|
|
19486
|
+
}
|
|
19487
|
+
],
|
|
18982
19488
|
linkedFlows: [linkedFlow],
|
|
18983
19489
|
style: { backgroundColor: "purple" /* Green */.valueOf() }
|
|
18984
19490
|
};
|
|
@@ -18986,43 +19492,94 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18986
19492
|
}
|
|
18987
19493
|
};
|
|
18988
19494
|
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.";
|
|
18989
|
-
var _protocol2 = {
|
|
19495
|
+
var _protocol2 = {
|
|
19496
|
+
name: "Ekubo",
|
|
19497
|
+
logo: "https://app.ekubo.org/favicon.ico"
|
|
19498
|
+
};
|
|
18990
19499
|
var _riskFactor2 = [
|
|
18991
19500
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
18992
19501
|
{ type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
|
|
18993
19502
|
];
|
|
18994
19503
|
var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
18995
|
-
var
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
|
|
19000
|
-
|
|
19001
|
-
|
|
19002
|
-
|
|
19003
|
-
] }),
|
|
19004
|
-
address: ContractAddr.from("0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"),
|
|
19005
|
-
type: "Other",
|
|
19006
|
-
// must be same order as poolKey token0 and token1
|
|
19007
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"), Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
19008
|
-
protocols: [_protocol2],
|
|
19009
|
-
auditUrl: AUDIT_URL2,
|
|
19010
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
19011
|
-
risk: {
|
|
19012
|
-
riskFactor: _riskFactor2,
|
|
19013
|
-
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19014
|
-
notARisks: getNoRiskTags(_riskFactor2)
|
|
19504
|
+
var faqs2 = [
|
|
19505
|
+
{
|
|
19506
|
+
question: "What is the Ekubo CL Vault strategy?",
|
|
19507
|
+
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."
|
|
19508
|
+
},
|
|
19509
|
+
{
|
|
19510
|
+
question: "How are trading fees and rewards handled?",
|
|
19511
|
+
answer: "Trading fees and DeFi Spring rewards are automatically compounded back into the strategy, increasing your overall returns."
|
|
19015
19512
|
},
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
|
|
19513
|
+
{
|
|
19514
|
+
question: "What happens during withdrawal?",
|
|
19515
|
+
answer: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices."
|
|
19516
|
+
},
|
|
19517
|
+
{
|
|
19518
|
+
question: "Is the strategy audited?",
|
|
19519
|
+
answer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
19520
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
19521
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: "https://docs.strkfarm.com/p/ekubo-cl-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
19522
|
+
"."
|
|
19523
|
+
] })
|
|
19524
|
+
}
|
|
19525
|
+
];
|
|
19526
|
+
var EkuboCLVaultStrategies = [
|
|
19527
|
+
{
|
|
19528
|
+
name: "Ekubo xSTRK/STRK",
|
|
19529
|
+
description: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
19530
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK") }),
|
|
19531
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
19532
|
+
"ul",
|
|
19533
|
+
{
|
|
19534
|
+
style: {
|
|
19535
|
+
marginLeft: "20px",
|
|
19536
|
+
listStyle: "circle",
|
|
19537
|
+
fontSize: "12px"
|
|
19538
|
+
},
|
|
19539
|
+
children: [
|
|
19540
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { style: { marginTop: "10px" }, children: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices." }),
|
|
19541
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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." })
|
|
19542
|
+
]
|
|
19543
|
+
}
|
|
19544
|
+
)
|
|
19545
|
+
] }),
|
|
19546
|
+
address: ContractAddr.from(
|
|
19547
|
+
"0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"
|
|
19548
|
+
),
|
|
19549
|
+
type: "Other",
|
|
19550
|
+
// must be same order as poolKey token0 and token1
|
|
19551
|
+
depositTokens: [
|
|
19552
|
+
Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"),
|
|
19553
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
19554
|
+
],
|
|
19555
|
+
protocols: [_protocol2],
|
|
19556
|
+
auditUrl: AUDIT_URL2,
|
|
19557
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
19558
|
+
risk: {
|
|
19559
|
+
riskFactor: _riskFactor2,
|
|
19560
|
+
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19561
|
+
notARisks: getNoRiskTags(_riskFactor2)
|
|
19021
19562
|
},
|
|
19022
|
-
|
|
19023
|
-
|
|
19563
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19564
|
+
additionalInfo: {
|
|
19565
|
+
newBounds: {
|
|
19566
|
+
lower: -1,
|
|
19567
|
+
upper: 1
|
|
19568
|
+
},
|
|
19569
|
+
lstContract: ContractAddr.from(
|
|
19570
|
+
"0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
|
|
19571
|
+
),
|
|
19572
|
+
feeBps: 1e3
|
|
19573
|
+
},
|
|
19574
|
+
faqs: [
|
|
19575
|
+
...faqs2,
|
|
19576
|
+
{
|
|
19577
|
+
question: "Why might I see a negative APY?",
|
|
19578
|
+
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."
|
|
19579
|
+
}
|
|
19580
|
+
]
|
|
19024
19581
|
}
|
|
19025
|
-
|
|
19582
|
+
];
|
|
19026
19583
|
|
|
19027
19584
|
// src/notifs/telegram.ts
|
|
19028
19585
|
var import_node_telegram_bot_api = __toESM(require("node-telegram-bot-api"));
|