@strkfarm/sdk 1.0.37 → 1.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +38 -588
- package/dist/cli.mjs +37 -587
- package/dist/index.browser.global.js +962 -329
- package/dist/index.browser.mjs +962 -325
- package/dist/index.d.ts +28 -54
- package/dist/index.js +985 -322
- package/dist/index.mjs +990 -323
- package/package.json +3 -3
- package/src/dataTypes/_bignumber.ts +49 -47
- package/src/global.ts +1 -33
- package/src/interfaces/common.ts +112 -98
- package/src/interfaces/lending.ts +1 -2
- package/src/modules/avnu.ts +1 -1
- package/src/modules/erc20.ts +6 -0
- package/src/modules/harvests.ts +1 -1
- package/src/modules/pragma.ts +1 -1
- package/src/modules/pricer-from-api.ts +3 -3
- package/src/modules/pricer.ts +2 -1
- package/src/modules/zkLend.ts +2 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/notifs/telegram.ts +1 -1
- package/src/strategies/ekubo-cl-vault.tsx +1503 -920
- package/src/strategies/vesu-rebalance.tsx +943 -611
- package/src/utils/index.ts +2 -0
- package/src/utils/logger.browser.ts +20 -0
- package/src/utils/logger.node.ts +35 -0
- package/src/utils/logger.ts +1 -0
- package/src/utils/store.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -76,6 +76,43 @@ var import_axios = __toESM(require("axios"));
|
|
|
76
76
|
// src/dataTypes/bignumber.node.ts
|
|
77
77
|
var import_util = __toESM(require("util"));
|
|
78
78
|
|
|
79
|
+
// src/utils/logger.node.ts
|
|
80
|
+
var import_winston = __toESM(require("winston"));
|
|
81
|
+
var colors = {
|
|
82
|
+
error: "red",
|
|
83
|
+
warn: "yellow",
|
|
84
|
+
info: "blue",
|
|
85
|
+
verbose: "white",
|
|
86
|
+
debug: "white"
|
|
87
|
+
};
|
|
88
|
+
import_winston.default.addColors(colors);
|
|
89
|
+
var logger = import_winston.default.createLogger({
|
|
90
|
+
level: "verbose",
|
|
91
|
+
// Set the minimum logging level
|
|
92
|
+
format: import_winston.format.combine(
|
|
93
|
+
import_winston.format.colorize({ all: true }),
|
|
94
|
+
// Apply custom colors
|
|
95
|
+
import_winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
96
|
+
// Add timestamp to log messages
|
|
97
|
+
import_winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
|
98
|
+
let msg = `${timestamp} ${level}: ${message}`;
|
|
99
|
+
if (meta && meta[Symbol.for("splat")]) {
|
|
100
|
+
for (const arg of meta[Symbol.for("splat")]) {
|
|
101
|
+
if (arg instanceof Error) {
|
|
102
|
+
msg += `
|
|
103
|
+
${arg.stack}`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return msg;
|
|
108
|
+
})
|
|
109
|
+
),
|
|
110
|
+
transports: [
|
|
111
|
+
new import_winston.default.transports.Console()
|
|
112
|
+
// Output logs to the console
|
|
113
|
+
]
|
|
114
|
+
});
|
|
115
|
+
|
|
79
116
|
// src/dataTypes/_bignumber.ts
|
|
80
117
|
var import_bignumber = __toESM(require("bignumber.js"));
|
|
81
118
|
var _Web3Number = class extends import_bignumber.default {
|
|
@@ -92,6 +129,7 @@ var _Web3Number = class extends import_bignumber.default {
|
|
|
92
129
|
}
|
|
93
130
|
dividedBy(value) {
|
|
94
131
|
const _value = this.getStandardString(value);
|
|
132
|
+
console.log("dividedBy", _value);
|
|
95
133
|
return this.construct(this.div(_value).toString(), this.decimals);
|
|
96
134
|
}
|
|
97
135
|
plus(value) {
|
|
@@ -105,13 +143,15 @@ var _Web3Number = class extends import_bignumber.default {
|
|
|
105
143
|
construct(value, decimals) {
|
|
106
144
|
return new this.constructor(value, decimals);
|
|
107
145
|
}
|
|
108
|
-
toString(
|
|
109
|
-
return super.
|
|
146
|
+
toString() {
|
|
147
|
+
return super.toString();
|
|
110
148
|
}
|
|
111
149
|
toJSON() {
|
|
150
|
+
logger.verbose(`converting to json with decimals`);
|
|
112
151
|
return this.toString();
|
|
113
152
|
}
|
|
114
153
|
valueOf() {
|
|
154
|
+
logger.verbose(`converting to valueOf with decimals`);
|
|
115
155
|
return this.toString();
|
|
116
156
|
}
|
|
117
157
|
maxToFixedDecimals() {
|
|
@@ -176,12 +216,6 @@ var ContractAddr = class _ContractAddr {
|
|
|
176
216
|
};
|
|
177
217
|
|
|
178
218
|
// src/global.ts
|
|
179
|
-
var logger = {
|
|
180
|
-
...console,
|
|
181
|
-
verbose(message) {
|
|
182
|
-
console.log(`[VERBOSE] ${message}`);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
219
|
var FatalError = class extends Error {
|
|
186
220
|
constructor(message, err) {
|
|
187
221
|
super(message);
|
|
@@ -775,7 +809,7 @@ var PricerFromApi = class extends PricerBase {
|
|
|
775
809
|
} catch (e) {
|
|
776
810
|
logger.warn("getPriceFromMyAPI error", JSON.stringify(e.message || e));
|
|
777
811
|
}
|
|
778
|
-
logger.
|
|
812
|
+
logger.info("getPrice coinbase", tokenSymbol);
|
|
779
813
|
let retry = 0;
|
|
780
814
|
const MAX_RETRIES = 5;
|
|
781
815
|
for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
|
|
@@ -1956,6 +1990,11 @@ var ERC20 = class {
|
|
|
1956
1990
|
const balance = await contract.call("balanceOf", [address.toString()]);
|
|
1957
1991
|
return Web3Number.fromWei(balance.toString(), tokenDecimals);
|
|
1958
1992
|
}
|
|
1993
|
+
async allowance(token, owner, spender, tokenDecimals) {
|
|
1994
|
+
const contract = this.contract(token);
|
|
1995
|
+
const allowance = await contract.call("allowance", [owner.toString(), spender.toString()]);
|
|
1996
|
+
return Web3Number.fromWei(allowance.toString(), tokenDecimals);
|
|
1997
|
+
}
|
|
1959
1998
|
};
|
|
1960
1999
|
|
|
1961
2000
|
// src/modules/avnu.ts
|
|
@@ -2095,7 +2134,9 @@ var getRiskColor = (risk) => {
|
|
|
2095
2134
|
};
|
|
2096
2135
|
var getNoRiskTags = (risks) => {
|
|
2097
2136
|
const noRisks1 = risks.filter((risk) => risk.value === 0).map((risk) => risk.type);
|
|
2098
|
-
const noRisks2 = Object.values(RiskType).filter(
|
|
2137
|
+
const noRisks2 = Object.values(RiskType).filter(
|
|
2138
|
+
(risk) => !risks.map((risk2) => risk2.type).includes(risk)
|
|
2139
|
+
);
|
|
2099
2140
|
const mergedUnique = [.../* @__PURE__ */ new Set([...noRisks1, ...noRisks2])];
|
|
2100
2141
|
return mergedUnique.map((risk) => `No ${risk}`);
|
|
2101
2142
|
};
|
|
@@ -12787,6 +12828,7 @@ var vesu_pools_default = {
|
|
|
12787
12828
|
};
|
|
12788
12829
|
|
|
12789
12830
|
// src/strategies/vesu-rebalance.tsx
|
|
12831
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
12790
12832
|
var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
12791
12833
|
// 10000 bps = 100%
|
|
12792
12834
|
/**
|
|
@@ -12800,10 +12842,17 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12800
12842
|
super(config);
|
|
12801
12843
|
this.BASE_WEIGHT = 1e4;
|
|
12802
12844
|
this.pricer = pricer;
|
|
12803
|
-
assert(
|
|
12845
|
+
assert(
|
|
12846
|
+
metadata.depositTokens.length === 1,
|
|
12847
|
+
"VesuRebalance only supports 1 deposit token"
|
|
12848
|
+
);
|
|
12804
12849
|
this.metadata = metadata;
|
|
12805
12850
|
this.address = metadata.address;
|
|
12806
|
-
this.contract = new import_starknet8.Contract(
|
|
12851
|
+
this.contract = new import_starknet8.Contract(
|
|
12852
|
+
vesu_rebalance_abi_default,
|
|
12853
|
+
this.address.address,
|
|
12854
|
+
this.config.provider
|
|
12855
|
+
);
|
|
12807
12856
|
}
|
|
12808
12857
|
/**
|
|
12809
12858
|
* Creates a deposit call to the strategy contract.
|
|
@@ -12812,10 +12861,23 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12812
12861
|
* @returns Populated contract call for deposit
|
|
12813
12862
|
*/
|
|
12814
12863
|
async depositCall(amountInfo, receiver) {
|
|
12815
|
-
assert(
|
|
12816
|
-
|
|
12817
|
-
|
|
12818
|
-
|
|
12864
|
+
assert(
|
|
12865
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
12866
|
+
"Deposit token mismatch"
|
|
12867
|
+
);
|
|
12868
|
+
const assetContract = new import_starknet8.Contract(
|
|
12869
|
+
vesu_rebalance_abi_default,
|
|
12870
|
+
this.asset().address.address,
|
|
12871
|
+
this.config.provider
|
|
12872
|
+
);
|
|
12873
|
+
const call1 = assetContract.populate("approve", [
|
|
12874
|
+
this.address.address,
|
|
12875
|
+
import_starknet8.uint256.bnToUint256(amountInfo.amount.toWei())
|
|
12876
|
+
]);
|
|
12877
|
+
const call2 = this.contract.populate("deposit", [
|
|
12878
|
+
import_starknet8.uint256.bnToUint256(amountInfo.amount.toWei()),
|
|
12879
|
+
receiver.address
|
|
12880
|
+
]);
|
|
12819
12881
|
return [call1, call2];
|
|
12820
12882
|
}
|
|
12821
12883
|
/**
|
|
@@ -12826,7 +12888,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12826
12888
|
* @returns Populated contract call for withdrawal
|
|
12827
12889
|
*/
|
|
12828
12890
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
12829
|
-
return [
|
|
12891
|
+
return [
|
|
12892
|
+
this.contract.populate("withdraw", [
|
|
12893
|
+
import_starknet8.uint256.bnToUint256(amountInfo.amount.toWei()),
|
|
12894
|
+
receiver.address,
|
|
12895
|
+
owner.address
|
|
12896
|
+
])
|
|
12897
|
+
];
|
|
12830
12898
|
}
|
|
12831
12899
|
/**
|
|
12832
12900
|
* Returns the underlying asset token of the strategy.
|
|
@@ -12849,9 +12917,16 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12849
12917
|
*/
|
|
12850
12918
|
async getUserTVL(user) {
|
|
12851
12919
|
const shares = await this.contract.balanceOf(user.address);
|
|
12852
|
-
const assets = await this.contract.convert_to_assets(
|
|
12853
|
-
|
|
12854
|
-
|
|
12920
|
+
const assets = await this.contract.convert_to_assets(
|
|
12921
|
+
import_starknet8.uint256.bnToUint256(shares)
|
|
12922
|
+
);
|
|
12923
|
+
const amount = Web3Number.fromWei(
|
|
12924
|
+
assets.toString(),
|
|
12925
|
+
this.metadata.depositTokens[0].decimals
|
|
12926
|
+
);
|
|
12927
|
+
let price = await this.pricer.getPrice(
|
|
12928
|
+
this.metadata.depositTokens[0].symbol
|
|
12929
|
+
);
|
|
12855
12930
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12856
12931
|
return {
|
|
12857
12932
|
tokenInfo: this.asset(),
|
|
@@ -12865,8 +12940,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12865
12940
|
*/
|
|
12866
12941
|
async getTVL() {
|
|
12867
12942
|
const assets = await this.contract.total_assets();
|
|
12868
|
-
const amount = Web3Number.fromWei(
|
|
12869
|
-
|
|
12943
|
+
const amount = Web3Number.fromWei(
|
|
12944
|
+
assets.toString(),
|
|
12945
|
+
this.metadata.depositTokens[0].decimals
|
|
12946
|
+
);
|
|
12947
|
+
let price = await this.pricer.getPrice(
|
|
12948
|
+
this.metadata.depositTokens[0].symbol
|
|
12949
|
+
);
|
|
12870
12950
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12871
12951
|
return {
|
|
12872
12952
|
tokenInfo: this.asset(),
|
|
@@ -12892,51 +12972,104 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12892
12972
|
return pools;
|
|
12893
12973
|
}
|
|
12894
12974
|
async getPoolInfo(p, pools, vesuPositions, totalAssets, isErrorPositionsAPI, isErrorPoolsAPI) {
|
|
12895
|
-
const vesuPosition = vesuPositions.find(
|
|
12975
|
+
const vesuPosition = vesuPositions.find(
|
|
12976
|
+
(d) => d.pool.id.toString() === import_starknet8.num.getDecimalString(p.pool_id.address.toString())
|
|
12977
|
+
);
|
|
12896
12978
|
const _pool = pools.find((d) => {
|
|
12897
|
-
logger.verbose(
|
|
12979
|
+
logger.verbose(
|
|
12980
|
+
`pool check: ${d.id == import_starknet8.num.getDecimalString(p.pool_id.address.toString())}, id: ${d.id}, pool_id: ${import_starknet8.num.getDecimalString(
|
|
12981
|
+
p.pool_id.address.toString()
|
|
12982
|
+
)}`
|
|
12983
|
+
);
|
|
12898
12984
|
return d.id == import_starknet8.num.getDecimalString(p.pool_id.address.toString());
|
|
12899
12985
|
});
|
|
12900
12986
|
logger.verbose(`pool: ${JSON.stringify(_pool)}`);
|
|
12901
12987
|
logger.verbose(typeof _pool);
|
|
12902
12988
|
logger.verbose(`name: ${_pool?.name}`);
|
|
12903
12989
|
const name = _pool?.name;
|
|
12904
|
-
logger.verbose(
|
|
12905
|
-
|
|
12990
|
+
logger.verbose(
|
|
12991
|
+
`name2: ${name}, ${!name ? true : false}, ${name?.length}, ${typeof name}`
|
|
12992
|
+
);
|
|
12993
|
+
const assetInfo = _pool?.assets.find(
|
|
12994
|
+
(d) => this.asset().address.eqString(d.address)
|
|
12995
|
+
);
|
|
12906
12996
|
if (!name) {
|
|
12907
12997
|
logger.verbose(`Pool not found`);
|
|
12908
12998
|
throw new Error(`Pool name ${p.pool_id.address.toString()} not found`);
|
|
12909
12999
|
}
|
|
12910
13000
|
if (!assetInfo) {
|
|
12911
|
-
throw new Error(
|
|
13001
|
+
throw new Error(
|
|
13002
|
+
`Asset ${this.asset().address.toString()} not found in pool ${p.pool_id.address.toString()}`
|
|
13003
|
+
);
|
|
12912
13004
|
}
|
|
12913
|
-
let vTokenContract = new import_starknet8.Contract(
|
|
13005
|
+
let vTokenContract = new import_starknet8.Contract(
|
|
13006
|
+
vesu_rebalance_abi_default,
|
|
13007
|
+
p.v_token.address,
|
|
13008
|
+
this.config.provider
|
|
13009
|
+
);
|
|
12914
13010
|
const bal = await vTokenContract.balanceOf(this.address.address);
|
|
12915
|
-
const assets = await vTokenContract.convert_to_assets(
|
|
13011
|
+
const assets = await vTokenContract.convert_to_assets(
|
|
13012
|
+
import_starknet8.uint256.bnToUint256(bal.toString())
|
|
13013
|
+
);
|
|
12916
13014
|
logger.verbose(`Collateral: ${JSON.stringify(vesuPosition?.collateral)}`);
|
|
12917
13015
|
logger.verbose(`supplyApy: ${JSON.stringify(assetInfo?.stats.supplyApy)}`);
|
|
12918
|
-
logger.verbose(
|
|
12919
|
-
|
|
12920
|
-
|
|
13016
|
+
logger.verbose(
|
|
13017
|
+
`defiSpringSupplyApr: ${JSON.stringify(
|
|
13018
|
+
assetInfo?.stats.defiSpringSupplyApr
|
|
13019
|
+
)}`
|
|
13020
|
+
);
|
|
13021
|
+
logger.verbose(
|
|
13022
|
+
`currentUtilization: ${JSON.stringify(
|
|
13023
|
+
assetInfo?.stats.currentUtilization
|
|
13024
|
+
)}`
|
|
13025
|
+
);
|
|
13026
|
+
logger.verbose(
|
|
13027
|
+
`maxUtilization: ${JSON.stringify(assetInfo?.config.maxUtilization)}`
|
|
13028
|
+
);
|
|
12921
13029
|
const item = {
|
|
12922
13030
|
pool_id: p.pool_id,
|
|
12923
13031
|
pool_name: _pool?.name,
|
|
12924
13032
|
max_weight: p.max_weight,
|
|
12925
|
-
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
13033
|
+
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
13034
|
+
Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)
|
|
13035
|
+
),
|
|
12926
13036
|
v_token: p.v_token,
|
|
12927
13037
|
amount: Web3Number.fromWei(assets.toString(), this.decimals()),
|
|
12928
|
-
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
13038
|
+
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
13039
|
+
vesuPosition.collateral.usdPrice.value,
|
|
13040
|
+
vesuPosition.collateral.usdPrice.decimals
|
|
13041
|
+
),
|
|
12929
13042
|
APY: isErrorPoolsAPI || !assetInfo ? {
|
|
12930
13043
|
baseApy: 0,
|
|
12931
13044
|
defiSpringApy: 0,
|
|
12932
13045
|
netApy: 0
|
|
12933
13046
|
} : {
|
|
12934
|
-
baseApy: Number(
|
|
12935
|
-
|
|
13047
|
+
baseApy: Number(
|
|
13048
|
+
Web3Number.fromWei(
|
|
13049
|
+
assetInfo.stats.supplyApy.value,
|
|
13050
|
+
assetInfo.stats.supplyApy.decimals
|
|
13051
|
+
).toFixed(6)
|
|
13052
|
+
),
|
|
13053
|
+
defiSpringApy: assetInfo.stats.defiSpringSupplyApr ? Number(
|
|
13054
|
+
Web3Number.fromWei(
|
|
13055
|
+
assetInfo.stats.defiSpringSupplyApr.value,
|
|
13056
|
+
assetInfo.stats.defiSpringSupplyApr.decimals
|
|
13057
|
+
).toFixed(6)
|
|
13058
|
+
) : 0,
|
|
12936
13059
|
netApy: 0
|
|
12937
13060
|
},
|
|
12938
|
-
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12939
|
-
|
|
13061
|
+
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
13062
|
+
Web3Number.fromWei(
|
|
13063
|
+
assetInfo.stats.currentUtilization.value,
|
|
13064
|
+
assetInfo.stats.currentUtilization.decimals
|
|
13065
|
+
).toFixed(6)
|
|
13066
|
+
),
|
|
13067
|
+
maxUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
13068
|
+
Web3Number.fromWei(
|
|
13069
|
+
assetInfo.config.maxUtilization.value,
|
|
13070
|
+
assetInfo.config.maxUtilization.decimals
|
|
13071
|
+
).toFixed(6)
|
|
13072
|
+
)
|
|
12940
13073
|
};
|
|
12941
13074
|
item.APY.netApy = item.APY.baseApy + item.APY.defiSpringApy;
|
|
12942
13075
|
return item;
|
|
@@ -12946,7 +13079,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12946
13079
|
* 1. Contract's allowed pools
|
|
12947
13080
|
* 2. Vesu positions API for current positions
|
|
12948
13081
|
* 3. Vesu pools API for APY and utilization data
|
|
12949
|
-
*
|
|
13082
|
+
*
|
|
12950
13083
|
* @returns {Promise<{
|
|
12951
13084
|
* data: Array<PoolInfoFull>,
|
|
12952
13085
|
* isErrorPositionsAPI: boolean
|
|
@@ -12963,15 +13096,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12963
13096
|
let isErrorPositionsAPI = false;
|
|
12964
13097
|
let vesuPositions = [];
|
|
12965
13098
|
try {
|
|
12966
|
-
const data2 = await getAPIUsingHeadlessBrowser(
|
|
13099
|
+
const data2 = await getAPIUsingHeadlessBrowser(
|
|
13100
|
+
`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`
|
|
13101
|
+
);
|
|
12967
13102
|
vesuPositions = data2.data;
|
|
12968
13103
|
} catch (e) {
|
|
12969
|
-
console.error(
|
|
13104
|
+
console.error(
|
|
13105
|
+
`${_VesuRebalance.name}: Error fetching positions for ${this.address.address}`,
|
|
13106
|
+
e
|
|
13107
|
+
);
|
|
12970
13108
|
isErrorPositionsAPI = true;
|
|
12971
13109
|
}
|
|
12972
13110
|
let { pools, isErrorPoolsAPI } = await this.getVesuPools();
|
|
12973
13111
|
const totalAssets = (await this.getTVL()).amount;
|
|
12974
|
-
const info = allowedPools.map(
|
|
13112
|
+
const info = allowedPools.map(
|
|
13113
|
+
(p) => this.getPoolInfo(
|
|
13114
|
+
p,
|
|
13115
|
+
pools,
|
|
13116
|
+
vesuPositions,
|
|
13117
|
+
totalAssets,
|
|
13118
|
+
isErrorPositionsAPI,
|
|
13119
|
+
isErrorPoolsAPI
|
|
13120
|
+
)
|
|
13121
|
+
);
|
|
12975
13122
|
const data = await Promise.all(info);
|
|
12976
13123
|
return {
|
|
12977
13124
|
data,
|
|
@@ -12984,18 +13131,25 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12984
13131
|
let isErrorPoolsAPI = false;
|
|
12985
13132
|
let pools = [];
|
|
12986
13133
|
try {
|
|
12987
|
-
const data = await getAPIUsingHeadlessBrowser(
|
|
13134
|
+
const data = await getAPIUsingHeadlessBrowser(
|
|
13135
|
+
"https://api.vesu.xyz/pools"
|
|
13136
|
+
);
|
|
12988
13137
|
pools = data.data;
|
|
12989
13138
|
for (const pool of vesu_pools_default.data) {
|
|
12990
13139
|
const found = pools.find((d) => d.id === pool.id);
|
|
12991
13140
|
if (!found) {
|
|
12992
13141
|
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
12993
|
-
logger.verbose(
|
|
13142
|
+
logger.verbose(
|
|
13143
|
+
`VesuRebalance: Pool ${pool.id} not found in Vesu API, using hardcoded data`
|
|
13144
|
+
);
|
|
12994
13145
|
throw new Error("pool not found [sanity check]");
|
|
12995
13146
|
}
|
|
12996
13147
|
}
|
|
12997
13148
|
} catch (e) {
|
|
12998
|
-
logger.error(
|
|
13149
|
+
logger.error(
|
|
13150
|
+
`${_VesuRebalance.name}: Error fetching pools for ${this.address.address}, retry ${retry}`,
|
|
13151
|
+
e
|
|
13152
|
+
);
|
|
12999
13153
|
isErrorPoolsAPI = true;
|
|
13000
13154
|
if (retry < 10) {
|
|
13001
13155
|
await new Promise((resolve) => setTimeout(resolve, 5e3 * (retry + 1)));
|
|
@@ -13033,8 +13187,8 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13033
13187
|
* 3. For each pool that needs more funds:
|
|
13034
13188
|
* - Takes funds from lowest APY pools that are over their target
|
|
13035
13189
|
* 4. Validates that total assets remain constant
|
|
13036
|
-
*
|
|
13037
|
-
* @returns {Promise<{
|
|
13190
|
+
*
|
|
13191
|
+
* @returns {Promise<{
|
|
13038
13192
|
* changes: Change[],
|
|
13039
13193
|
* finalPools: PoolInfoFull[],
|
|
13040
13194
|
* isAnyPoolOverMaxWeight: boolean
|
|
@@ -13050,27 +13204,38 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13050
13204
|
_pools = _pools2;
|
|
13051
13205
|
}
|
|
13052
13206
|
const feeDeductions = await this.getFee(_pools);
|
|
13053
|
-
logger.verbose(
|
|
13207
|
+
logger.verbose(
|
|
13208
|
+
`VesuRebalance: feeDeductions: ${JSON.stringify(feeDeductions)}`
|
|
13209
|
+
);
|
|
13054
13210
|
const pools = _pools.map((p) => {
|
|
13055
13211
|
const fee = feeDeductions.find((f) => p.v_token.eq(f.vToken))?.fee || Web3Number.fromWei("0", this.decimals());
|
|
13056
|
-
logger.verbose(
|
|
13212
|
+
logger.verbose(
|
|
13213
|
+
`FeeAdjustment: ${p.pool_id} => ${fee.toString()}, amt: ${p.amount.toString()}`
|
|
13214
|
+
);
|
|
13057
13215
|
return {
|
|
13058
13216
|
...p,
|
|
13059
13217
|
amount: p.amount.minus(fee)
|
|
13060
13218
|
};
|
|
13061
13219
|
});
|
|
13062
13220
|
let totalAssets = (await this.getTVL()).amount;
|
|
13063
|
-
if (totalAssets.eq(0))
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13221
|
+
if (totalAssets.eq(0))
|
|
13222
|
+
return {
|
|
13223
|
+
changes: [],
|
|
13224
|
+
finalPools: []
|
|
13225
|
+
};
|
|
13067
13226
|
feeDeductions.forEach((f) => {
|
|
13068
13227
|
totalAssets = totalAssets.minus(f.fee);
|
|
13069
13228
|
});
|
|
13070
|
-
const sumPools = pools.reduce(
|
|
13229
|
+
const sumPools = pools.reduce(
|
|
13230
|
+
(acc, curr) => acc.plus(curr.amount.toString()),
|
|
13231
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13232
|
+
);
|
|
13071
13233
|
logger.verbose(`Sum of pools: ${sumPools.toString()}`);
|
|
13072
13234
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13073
|
-
assert(
|
|
13235
|
+
assert(
|
|
13236
|
+
sumPools.lte(totalAssets.multipliedBy(1.00001).toString()),
|
|
13237
|
+
"Sum of pools.amount must be less than or equal to totalAssets"
|
|
13238
|
+
);
|
|
13074
13239
|
const sortedPools = [...pools].sort((a, b) => b.APY.netApy - a.APY.netApy);
|
|
13075
13240
|
const targetAmounts = {};
|
|
13076
13241
|
let remainingAssets = totalAssets;
|
|
@@ -13092,7 +13257,10 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13092
13257
|
assert(remainingAssets.lt(1e-5), "Remaining assets must be 0");
|
|
13093
13258
|
const changes = sortedPools.map((pool) => {
|
|
13094
13259
|
const target = targetAmounts[pool.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
13095
|
-
const change = Web3Number.fromWei(
|
|
13260
|
+
const change = Web3Number.fromWei(
|
|
13261
|
+
target.minus(pool.amount.toString()).toWei(),
|
|
13262
|
+
this.decimals()
|
|
13263
|
+
);
|
|
13096
13264
|
return {
|
|
13097
13265
|
pool_id: pool.pool_id,
|
|
13098
13266
|
changeAmt: change,
|
|
@@ -13101,14 +13269,21 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13101
13269
|
};
|
|
13102
13270
|
});
|
|
13103
13271
|
logger.verbose(`Changes: ${JSON.stringify(changes)}`);
|
|
13104
|
-
const sumChanges = changes.reduce(
|
|
13105
|
-
|
|
13272
|
+
const sumChanges = changes.reduce(
|
|
13273
|
+
(sum, c) => sum.plus(c.changeAmt.toString()),
|
|
13274
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13275
|
+
);
|
|
13276
|
+
const sumFinal = changes.reduce(
|
|
13277
|
+
(sum, c) => sum.plus(c.finalAmt.toString()),
|
|
13278
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13279
|
+
);
|
|
13106
13280
|
const hasChanges = changes.some((c) => !c.changeAmt.eq(0));
|
|
13107
13281
|
logger.verbose(`Sum of changes: ${sumChanges.toString()}`);
|
|
13108
13282
|
if (!sumChanges.eq(0)) throw new Error("Sum of changes must be zero");
|
|
13109
13283
|
logger.verbose(`Sum of final: ${sumFinal.toString()}`);
|
|
13110
13284
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13111
|
-
if (!sumFinal.eq(totalAssets.toString()))
|
|
13285
|
+
if (!sumFinal.eq(totalAssets.toString()))
|
|
13286
|
+
throw new Error("Sum of final amounts must equal total assets");
|
|
13112
13287
|
if (!hasChanges) throw new Error("No changes required");
|
|
13113
13288
|
const finalPools = pools.map((p) => {
|
|
13114
13289
|
const target = targetAmounts[p.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
@@ -13136,9 +13311,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13136
13311
|
if (p.changeAmt.eq(0)) return null;
|
|
13137
13312
|
actions.push({
|
|
13138
13313
|
pool_id: p.pool_id.address,
|
|
13139
|
-
feature: new import_starknet8.CairoCustomEnum(
|
|
13314
|
+
feature: new import_starknet8.CairoCustomEnum(
|
|
13315
|
+
p.isDeposit ? { DEPOSIT: {} } : { WITHDRAW: {} }
|
|
13316
|
+
),
|
|
13140
13317
|
token: this.asset().address.address,
|
|
13141
|
-
amount: import_starknet8.uint256.bnToUint256(
|
|
13318
|
+
amount: import_starknet8.uint256.bnToUint256(
|
|
13319
|
+
p.changeAmt.multipliedBy(p.isDeposit ? 1 : -1).toWei()
|
|
13320
|
+
)
|
|
13142
13321
|
});
|
|
13143
13322
|
});
|
|
13144
13323
|
if (actions.length === 0) return null;
|
|
@@ -13151,18 +13330,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13151
13330
|
const netYield = await this.netAPYGivenPools(pools);
|
|
13152
13331
|
const baseFlow = {
|
|
13153
13332
|
title: "Your Deposit",
|
|
13154
|
-
subItems: [
|
|
13333
|
+
subItems: [
|
|
13334
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
13335
|
+
{
|
|
13336
|
+
key: `Performance Fee`,
|
|
13337
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
13338
|
+
}
|
|
13339
|
+
],
|
|
13155
13340
|
linkedFlows: [],
|
|
13156
13341
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
13157
13342
|
};
|
|
13158
13343
|
let _pools = [...pools];
|
|
13159
|
-
_pools = _pools.sort(
|
|
13344
|
+
_pools = _pools.sort(
|
|
13345
|
+
(a, b) => Number(b.amount.toString()) - Number(a.amount.toString())
|
|
13346
|
+
);
|
|
13160
13347
|
_pools.forEach((p) => {
|
|
13161
13348
|
const flow = {
|
|
13162
13349
|
title: `Pool name: ${p.pool_name}`,
|
|
13163
13350
|
subItems: [
|
|
13164
13351
|
{ key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%` },
|
|
13165
|
-
{
|
|
13352
|
+
{
|
|
13353
|
+
key: "Weight",
|
|
13354
|
+
value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%`
|
|
13355
|
+
}
|
|
13166
13356
|
],
|
|
13167
13357
|
linkedFlows: [],
|
|
13168
13358
|
style: p.amount.greaterThan(0) ? { backgroundColor: "#35484f" /* Blue */.valueOf() } : { color: "gray" }
|
|
@@ -13194,7 +13384,12 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13194
13384
|
harvest.actualReward.toWei(),
|
|
13195
13385
|
this.address.address
|
|
13196
13386
|
);
|
|
13197
|
-
swapInfo = await avnu.getSwapInfo(
|
|
13387
|
+
swapInfo = await avnu.getSwapInfo(
|
|
13388
|
+
quote,
|
|
13389
|
+
this.address.address,
|
|
13390
|
+
0,
|
|
13391
|
+
this.address.address
|
|
13392
|
+
);
|
|
13198
13393
|
}
|
|
13199
13394
|
return [
|
|
13200
13395
|
this.contract.populate("harvest", [
|
|
@@ -13215,16 +13410,27 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13215
13410
|
* @returns {Promise<Array<{ vToken: ContractAddr, fee: Web3Number }>>} Array of fees deducted in different vTokens
|
|
13216
13411
|
*/
|
|
13217
13412
|
async getFee(allowedPools) {
|
|
13218
|
-
const assets = Web3Number.fromWei(
|
|
13219
|
-
|
|
13220
|
-
|
|
13413
|
+
const assets = Web3Number.fromWei(
|
|
13414
|
+
(await this.contract.total_assets()).toString(),
|
|
13415
|
+
this.asset().decimals
|
|
13416
|
+
);
|
|
13417
|
+
const totalSupply = Web3Number.fromWei(
|
|
13418
|
+
(await this.contract.total_supply()).toString(),
|
|
13419
|
+
this.asset().decimals
|
|
13420
|
+
);
|
|
13421
|
+
const prevIndex = Web3Number.fromWei(
|
|
13422
|
+
(await this.contract.get_previous_index()).toString(),
|
|
13423
|
+
18
|
|
13424
|
+
);
|
|
13221
13425
|
const currIndex = new Web3Number(1, 18).multipliedBy(assets).dividedBy(totalSupply);
|
|
13222
13426
|
logger.verbose(`Previous index: ${prevIndex.toString()}`);
|
|
13223
13427
|
logger.verbose(`Assets: ${assets.toString()}`);
|
|
13224
13428
|
logger.verbose(`Total supply: ${totalSupply.toString()}`);
|
|
13225
13429
|
logger.verbose(`Current index: ${currIndex.toNumber()}`);
|
|
13226
13430
|
if (currIndex.lt(prevIndex)) {
|
|
13227
|
-
logger.verbose(
|
|
13431
|
+
logger.verbose(
|
|
13432
|
+
`getFee::Current index is less than previous index, no fees to be deducted`
|
|
13433
|
+
);
|
|
13228
13434
|
return [];
|
|
13229
13435
|
}
|
|
13230
13436
|
const indexDiff = currIndex.minus(prevIndex);
|
|
@@ -13237,7 +13443,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13237
13443
|
return [];
|
|
13238
13444
|
}
|
|
13239
13445
|
const fees = [];
|
|
13240
|
-
let remainingFee = fee.plus(
|
|
13446
|
+
let remainingFee = fee.plus(
|
|
13447
|
+
Web3Number.fromWei("100", this.asset().decimals)
|
|
13448
|
+
);
|
|
13241
13449
|
for (const pool of allowedPools) {
|
|
13242
13450
|
const vToken = pool.v_token;
|
|
13243
13451
|
const balance = pool.amount;
|
|
@@ -13246,7 +13454,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13246
13454
|
break;
|
|
13247
13455
|
} else {
|
|
13248
13456
|
fees.push({ vToken, fee: Web3Number.fromWei(balance.toString(), 18) });
|
|
13249
|
-
remainingFee = remainingFee.minus(
|
|
13457
|
+
remainingFee = remainingFee.minus(
|
|
13458
|
+
Web3Number.fromWei(balance.toString(), 18)
|
|
13459
|
+
);
|
|
13250
13460
|
}
|
|
13251
13461
|
}
|
|
13252
13462
|
logger.verbose(`Fees: ${JSON.stringify(fees)}`);
|
|
@@ -13254,98 +13464,172 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13254
13464
|
}
|
|
13255
13465
|
};
|
|
13256
13466
|
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 = {
|
|
13467
|
+
var _protocol = {
|
|
13468
|
+
name: "Vesu",
|
|
13469
|
+
logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png"
|
|
13470
|
+
};
|
|
13258
13471
|
var _riskFactor = [
|
|
13259
13472
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
13260
13473
|
{ type: "Counterparty Risk" /* COUNTERPARTY_RISK */, value: 1, weight: 50 },
|
|
13261
13474
|
{ type: "Oracle Risk" /* ORACLE_RISK */, value: 0.5, weight: 25 }
|
|
13262
13475
|
];
|
|
13263
13476
|
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)
|
|
13477
|
+
var faqs = [
|
|
13478
|
+
{
|
|
13479
|
+
question: "What is the Vesu Rebalancing Strategy?",
|
|
13480
|
+
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
13481
|
},
|
|
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)
|
|
13482
|
+
{
|
|
13483
|
+
question: "Will I earn Vesu points?",
|
|
13484
|
+
answer: "Yes, of course! You will earn Vesu points for your deposits."
|
|
13294
13485
|
},
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
|
|
13298
|
-
},
|
|
13299
|
-
|
|
13300
|
-
|
|
13301
|
-
|
|
13302
|
-
|
|
13303
|
-
|
|
13304
|
-
|
|
13305
|
-
|
|
13306
|
-
|
|
13307
|
-
|
|
13308
|
-
|
|
13309
|
-
|
|
13310
|
-
|
|
13486
|
+
{
|
|
13487
|
+
question: "How does the strategy optimize yield?",
|
|
13488
|
+
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."
|
|
13489
|
+
},
|
|
13490
|
+
{
|
|
13491
|
+
question: "What are the risks associated with this strategy?",
|
|
13492
|
+
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."
|
|
13493
|
+
},
|
|
13494
|
+
{
|
|
13495
|
+
question: "How are fees calculated and deducted?",
|
|
13496
|
+
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."
|
|
13497
|
+
},
|
|
13498
|
+
{
|
|
13499
|
+
question: "What happens if a pool exceeds its maximum weight?",
|
|
13500
|
+
answer: "If a pool exceeds its maximum weight, the strategy rebalances by withdrawing excess funds and reallocating them to other pools with available capacity."
|
|
13501
|
+
},
|
|
13502
|
+
{
|
|
13503
|
+
question: "Can I withdraw my assets at any time?",
|
|
13504
|
+
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."
|
|
13505
|
+
},
|
|
13506
|
+
{
|
|
13507
|
+
question: "What happens to my Defi Spring STRK rewards?",
|
|
13508
|
+
answer: "STRK rewards are automatically harvested and reinvested into the strategy every week to maximize compounding returns."
|
|
13311
13509
|
},
|
|
13312
|
-
|
|
13313
|
-
|
|
13510
|
+
{
|
|
13511
|
+
question: "Is the strategy audited?",
|
|
13512
|
+
answer: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
13513
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
13514
|
+
/* @__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" }),
|
|
13515
|
+
"."
|
|
13516
|
+
] })
|
|
13314
13517
|
}
|
|
13315
|
-
|
|
13316
|
-
|
|
13317
|
-
|
|
13318
|
-
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
|
|
13325
|
-
|
|
13326
|
-
|
|
13327
|
-
|
|
13518
|
+
];
|
|
13519
|
+
var VesuRebalanceStrategies = [
|
|
13520
|
+
{
|
|
13521
|
+
name: "Vesu Fusion STRK",
|
|
13522
|
+
description: _description.replace("{{TOKEN}}", "STRK"),
|
|
13523
|
+
address: ContractAddr.from(
|
|
13524
|
+
"0x7fb5bcb8525954a60fde4e8fb8220477696ce7117ef264775a1770e23571929"
|
|
13525
|
+
),
|
|
13526
|
+
launchBlock: 0,
|
|
13527
|
+
type: "ERC4626",
|
|
13528
|
+
depositTokens: [
|
|
13529
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
13530
|
+
],
|
|
13531
|
+
protocols: [_protocol],
|
|
13532
|
+
auditUrl: AUDIT_URL,
|
|
13533
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13534
|
+
risk: {
|
|
13535
|
+
riskFactor: _riskFactor,
|
|
13536
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13537
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13538
|
+
},
|
|
13539
|
+
additionalInfo: {
|
|
13540
|
+
feeBps: 1e3
|
|
13541
|
+
},
|
|
13542
|
+
faqs
|
|
13543
|
+
},
|
|
13544
|
+
{
|
|
13545
|
+
name: "Vesu Fusion ETH",
|
|
13546
|
+
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13547
|
+
address: ContractAddr.from(
|
|
13548
|
+
"0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"
|
|
13549
|
+
),
|
|
13550
|
+
launchBlock: 0,
|
|
13551
|
+
type: "ERC4626",
|
|
13552
|
+
auditUrl: AUDIT_URL,
|
|
13553
|
+
depositTokens: [
|
|
13554
|
+
Global.getDefaultTokens().find((t) => t.symbol === "ETH")
|
|
13555
|
+
],
|
|
13556
|
+
protocols: [_protocol],
|
|
13557
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13558
|
+
risk: {
|
|
13559
|
+
riskFactor: _riskFactor,
|
|
13560
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13561
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13562
|
+
},
|
|
13563
|
+
additionalInfo: {
|
|
13564
|
+
feeBps: 1e3
|
|
13565
|
+
},
|
|
13566
|
+
faqs
|
|
13567
|
+
},
|
|
13568
|
+
{
|
|
13569
|
+
name: "Vesu Fusion USDC",
|
|
13570
|
+
description: _description.replace("{{TOKEN}}", "USDC"),
|
|
13571
|
+
address: ContractAddr.from(
|
|
13572
|
+
"0xa858c97e9454f407d1bd7c57472fc8d8d8449a777c822b41d18e387816f29c"
|
|
13573
|
+
),
|
|
13574
|
+
launchBlock: 0,
|
|
13575
|
+
type: "ERC4626",
|
|
13576
|
+
auditUrl: AUDIT_URL,
|
|
13577
|
+
depositTokens: [
|
|
13578
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC")
|
|
13579
|
+
],
|
|
13580
|
+
protocols: [_protocol],
|
|
13581
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13582
|
+
risk: {
|
|
13583
|
+
riskFactor: _riskFactor,
|
|
13584
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13585
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13586
|
+
},
|
|
13587
|
+
additionalInfo: {
|
|
13588
|
+
feeBps: 1e3
|
|
13589
|
+
},
|
|
13590
|
+
faqs
|
|
13328
13591
|
},
|
|
13329
|
-
|
|
13330
|
-
|
|
13592
|
+
{
|
|
13593
|
+
name: "Vesu Fusion USDT",
|
|
13594
|
+
description: _description.replace("{{TOKEN}}", "USDT"),
|
|
13595
|
+
address: ContractAddr.from(
|
|
13596
|
+
"0x115e94e722cfc4c77a2f15c4aefb0928c1c0029e5a57570df24c650cb7cec2c"
|
|
13597
|
+
),
|
|
13598
|
+
launchBlock: 0,
|
|
13599
|
+
type: "ERC4626",
|
|
13600
|
+
depositTokens: [
|
|
13601
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
13602
|
+
],
|
|
13603
|
+
auditUrl: AUDIT_URL,
|
|
13604
|
+
protocols: [_protocol],
|
|
13605
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13606
|
+
risk: {
|
|
13607
|
+
riskFactor: _riskFactor,
|
|
13608
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13609
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13610
|
+
},
|
|
13611
|
+
additionalInfo: {
|
|
13612
|
+
feeBps: 1e3
|
|
13613
|
+
},
|
|
13614
|
+
faqs
|
|
13615
|
+
// }, {
|
|
13616
|
+
// name: 'Vesu Fusion WBTC',
|
|
13617
|
+
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13618
|
+
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13619
|
+
// type: 'ERC4626',
|
|
13620
|
+
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13621
|
+
// auditUrl: AUDIT_URL,
|
|
13622
|
+
// protocols: [_protocol],
|
|
13623
|
+
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13624
|
+
// risk: {
|
|
13625
|
+
// riskFactor: _riskFactor,
|
|
13626
|
+
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13627
|
+
// },
|
|
13628
|
+
// additionalInfo: {
|
|
13629
|
+
// feeBps: 1000,
|
|
13630
|
+
// },
|
|
13331
13631
|
}
|
|
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
|
-
}];
|
|
13632
|
+
];
|
|
13349
13633
|
|
|
13350
13634
|
// src/strategies/ekubo-cl-vault.tsx
|
|
13351
13635
|
var import_starknet9 = require("starknet");
|
|
@@ -18250,7 +18534,7 @@ var erc4626_abi_default = [
|
|
|
18250
18534
|
];
|
|
18251
18535
|
|
|
18252
18536
|
// src/strategies/ekubo-cl-vault.tsx
|
|
18253
|
-
var
|
|
18537
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
18254
18538
|
var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
18255
18539
|
/**
|
|
18256
18540
|
* Creates a new VesuRebalance strategy instance.
|
|
@@ -18263,15 +18547,38 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18263
18547
|
super(config);
|
|
18264
18548
|
this.BASE_WEIGHT = 1e4;
|
|
18265
18549
|
this.pricer = pricer;
|
|
18266
|
-
assert(
|
|
18550
|
+
assert(
|
|
18551
|
+
metadata.depositTokens.length === 2,
|
|
18552
|
+
"EkuboCL only supports 2 deposit token"
|
|
18553
|
+
);
|
|
18267
18554
|
this.metadata = metadata;
|
|
18268
18555
|
this.address = metadata.address;
|
|
18269
|
-
this.contract = new import_starknet9.Contract(
|
|
18270
|
-
|
|
18556
|
+
this.contract = new import_starknet9.Contract(
|
|
18557
|
+
cl_vault_abi_default,
|
|
18558
|
+
this.address.address,
|
|
18559
|
+
this.config.provider
|
|
18560
|
+
);
|
|
18561
|
+
if (this.metadata.additionalInfo.lstContract) {
|
|
18562
|
+
this.lstContract = new import_starknet9.Contract(
|
|
18563
|
+
erc4626_abi_default,
|
|
18564
|
+
this.metadata.additionalInfo.lstContract.address,
|
|
18565
|
+
this.config.provider
|
|
18566
|
+
);
|
|
18567
|
+
} else {
|
|
18568
|
+
this.lstContract = null;
|
|
18569
|
+
}
|
|
18271
18570
|
const EKUBO_POSITION = "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
|
|
18272
|
-
this.ekuboPositionsContract = new import_starknet9.Contract(
|
|
18571
|
+
this.ekuboPositionsContract = new import_starknet9.Contract(
|
|
18572
|
+
ekubo_positions_abi_default,
|
|
18573
|
+
EKUBO_POSITION,
|
|
18574
|
+
this.config.provider
|
|
18575
|
+
);
|
|
18273
18576
|
const EKUBO_MATH = "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
|
|
18274
|
-
this.ekuboMathContract = new import_starknet9.Contract(
|
|
18577
|
+
this.ekuboMathContract = new import_starknet9.Contract(
|
|
18578
|
+
ekubo_math_abi_default,
|
|
18579
|
+
EKUBO_MATH,
|
|
18580
|
+
this.config.provider
|
|
18581
|
+
);
|
|
18275
18582
|
this.avnu = new AvnuWrapper();
|
|
18276
18583
|
}
|
|
18277
18584
|
async matchInputAmounts(amountInfo) {
|
|
@@ -18296,25 +18603,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18296
18603
|
/** Returns minimum amounts give given two amounts based on what can be added for liq */
|
|
18297
18604
|
async getMinDepositAmounts(amountInfo) {
|
|
18298
18605
|
const shares = await this.tokensToShares(amountInfo);
|
|
18299
|
-
const { amount0, amount1 } = await this.contract.call(
|
|
18606
|
+
const { amount0, amount1 } = await this.contract.call(
|
|
18607
|
+
"convert_to_assets",
|
|
18608
|
+
[import_starknet9.uint256.bnToUint256(shares.toWei())]
|
|
18609
|
+
);
|
|
18300
18610
|
return {
|
|
18301
18611
|
token0: {
|
|
18302
18612
|
tokenInfo: amountInfo.token0.tokenInfo,
|
|
18303
|
-
amount: Web3Number.fromWei(
|
|
18613
|
+
amount: Web3Number.fromWei(
|
|
18614
|
+
amount0.toString(),
|
|
18615
|
+
amountInfo.token0.tokenInfo.decimals
|
|
18616
|
+
)
|
|
18304
18617
|
},
|
|
18305
18618
|
token1: {
|
|
18306
18619
|
tokenInfo: amountInfo.token1.tokenInfo,
|
|
18307
|
-
amount: Web3Number.fromWei(
|
|
18620
|
+
amount: Web3Number.fromWei(
|
|
18621
|
+
amount1.toString(),
|
|
18622
|
+
amountInfo.token1.tokenInfo.decimals
|
|
18623
|
+
)
|
|
18308
18624
|
}
|
|
18309
18625
|
};
|
|
18310
18626
|
}
|
|
18311
18627
|
async depositCall(amountInfo, receiver) {
|
|
18312
18628
|
const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
|
|
18313
|
-
const token0Contract = new import_starknet9.Contract(
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
18317
|
-
|
|
18629
|
+
const token0Contract = new import_starknet9.Contract(
|
|
18630
|
+
erc4626_abi_default,
|
|
18631
|
+
amountInfo.token0.tokenInfo.address.address,
|
|
18632
|
+
this.config.provider
|
|
18633
|
+
);
|
|
18634
|
+
const token1Contract = new import_starknet9.Contract(
|
|
18635
|
+
erc4626_abi_default,
|
|
18636
|
+
amountInfo.token1.tokenInfo.address.address,
|
|
18637
|
+
this.config.provider
|
|
18638
|
+
);
|
|
18639
|
+
const call1 = token0Contract.populate("approve", [
|
|
18640
|
+
this.address.address,
|
|
18641
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token0.amount.toWei())
|
|
18642
|
+
]);
|
|
18643
|
+
const call2 = token1Contract.populate("approve", [
|
|
18644
|
+
this.address.address,
|
|
18645
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token1.amount.toWei())
|
|
18646
|
+
]);
|
|
18647
|
+
const call3 = this.contract.populate("deposit", [
|
|
18648
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token0.amount.toWei()),
|
|
18649
|
+
import_starknet9.uint256.bnToUint256(updateAmountInfo.token1.amount.toWei()),
|
|
18650
|
+
receiver.address
|
|
18651
|
+
]);
|
|
18318
18652
|
const calls = [];
|
|
18319
18653
|
if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
|
|
18320
18654
|
if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
|
|
@@ -18329,25 +18663,29 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18329
18663
|
}
|
|
18330
18664
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
18331
18665
|
const shares = await this.tokensToShares(amountInfo);
|
|
18332
|
-
logger.verbose(
|
|
18333
|
-
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18666
|
+
logger.verbose(
|
|
18667
|
+
`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`
|
|
18668
|
+
);
|
|
18669
|
+
return [
|
|
18670
|
+
this.contract.populate("withdraw", [
|
|
18671
|
+
import_starknet9.uint256.bnToUint256(shares.toWei()),
|
|
18672
|
+
receiver.address
|
|
18673
|
+
])
|
|
18674
|
+
];
|
|
18337
18675
|
}
|
|
18338
18676
|
rebalanceCall(newBounds, swapParams) {
|
|
18339
|
-
return [
|
|
18340
|
-
|
|
18341
|
-
|
|
18342
|
-
|
|
18343
|
-
|
|
18344
|
-
|
|
18345
|
-
|
|
18677
|
+
return [
|
|
18678
|
+
this.contract.populate("rebalance", [
|
|
18679
|
+
{
|
|
18680
|
+
lower: _EkuboCLVault.tickToi129(Number(newBounds.lowerTick)),
|
|
18681
|
+
upper: _EkuboCLVault.tickToi129(Number(newBounds.upperTick))
|
|
18682
|
+
},
|
|
18683
|
+
swapParams
|
|
18684
|
+
])
|
|
18685
|
+
];
|
|
18346
18686
|
}
|
|
18347
18687
|
handleUnusedCall(swapParams) {
|
|
18348
|
-
return [this.contract.populate("handle_unused", [
|
|
18349
|
-
swapParams
|
|
18350
|
-
])];
|
|
18688
|
+
return [this.contract.populate("handle_unused", [swapParams])];
|
|
18351
18689
|
}
|
|
18352
18690
|
handleFeesCall() {
|
|
18353
18691
|
return [this.contract.populate("handle_fees", [])];
|
|
@@ -18362,16 +18700,20 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18362
18700
|
const priceNow = await this.getCurrentPrice(blockIdentifier);
|
|
18363
18701
|
let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
18364
18702
|
const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
18365
|
-
const blockBefore = blockNow - sinceBlocks;
|
|
18366
|
-
const adjustedSupplyNow = supplyNow.minus(
|
|
18367
|
-
|
|
18703
|
+
const blockBefore = Math.max(blockNow - sinceBlocks, this.metadata.launchBlock);
|
|
18704
|
+
const adjustedSupplyNow = supplyNow.minus(
|
|
18705
|
+
await this.getHarvestRewardShares(blockBefore, blockNow)
|
|
18706
|
+
);
|
|
18707
|
+
let blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
18708
|
+
blockBefore
|
|
18709
|
+
);
|
|
18368
18710
|
const tvlBefore = await this._getTVL(blockBefore);
|
|
18369
18711
|
const supplyBefore = await this.totalSupply(blockBefore);
|
|
18370
18712
|
const priceBefore = await this.getCurrentPrice(blockBefore);
|
|
18371
18713
|
const tvlInToken0Now = tvlNow.amount0.multipliedBy(priceNow.price).plus(tvlNow.amount1);
|
|
18372
|
-
const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow);
|
|
18714
|
+
const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow.toString());
|
|
18373
18715
|
const tvlInToken0Bf = tvlBefore.amount0.multipliedBy(priceBefore.price).plus(tvlBefore.amount1);
|
|
18374
|
-
const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore);
|
|
18716
|
+
const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore.toString());
|
|
18375
18717
|
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
18376
18718
|
logger.verbose(`tvlInToken0Now: ${tvlInToken0Now.toString()}`);
|
|
18377
18719
|
logger.verbose(`tvlInToken0Bf: ${tvlInToken0Bf.toString()}`);
|
|
@@ -18382,7 +18724,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18382
18724
|
logger.verbose(`Supply before: ${supplyBefore.toString()}`);
|
|
18383
18725
|
logger.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
|
|
18384
18726
|
logger.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
|
|
18385
|
-
const apyForGivenBlocks = Number(
|
|
18727
|
+
const apyForGivenBlocks = Number(
|
|
18728
|
+
tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)
|
|
18729
|
+
) / 1e4;
|
|
18386
18730
|
return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
|
|
18387
18731
|
}
|
|
18388
18732
|
async getHarvestRewardShares(fromBlock, toBlock) {
|
|
@@ -18400,25 +18744,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18400
18744
|
} else {
|
|
18401
18745
|
shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
|
|
18402
18746
|
}
|
|
18403
|
-
logger.verbose(
|
|
18747
|
+
logger.verbose(
|
|
18748
|
+
`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`
|
|
18749
|
+
);
|
|
18404
18750
|
}
|
|
18405
18751
|
return shares;
|
|
18406
18752
|
}
|
|
18407
18753
|
async balanceOf(user, blockIdentifier = "pending") {
|
|
18408
|
-
let bal = await this.contract.call("balance_of", [user.address]
|
|
18754
|
+
let bal = await this.contract.call("balance_of", [user.address], {
|
|
18755
|
+
blockIdentifier
|
|
18756
|
+
});
|
|
18409
18757
|
return Web3Number.fromWei(bal.toString(), 18);
|
|
18410
18758
|
}
|
|
18411
18759
|
async getUserTVL(user, blockIdentifier = "pending") {
|
|
18412
18760
|
let bal = await this.balanceOf(user, blockIdentifier);
|
|
18413
|
-
const assets = await this.contract.call(
|
|
18414
|
-
|
|
18415
|
-
|
|
18761
|
+
const assets = await this.contract.call(
|
|
18762
|
+
"convert_to_assets",
|
|
18763
|
+
[import_starknet9.uint256.bnToUint256(bal.toWei())],
|
|
18764
|
+
{
|
|
18765
|
+
blockIdentifier
|
|
18766
|
+
}
|
|
18767
|
+
);
|
|
18416
18768
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18417
18769
|
this.assertValidDepositTokens(poolKey);
|
|
18418
18770
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18419
18771
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18420
|
-
const amount0 = Web3Number.fromWei(
|
|
18421
|
-
|
|
18772
|
+
const amount0 = Web3Number.fromWei(
|
|
18773
|
+
assets.amount0.toString(),
|
|
18774
|
+
token0Info.decimals
|
|
18775
|
+
);
|
|
18776
|
+
const amount1 = Web3Number.fromWei(
|
|
18777
|
+
assets.amount1.toString(),
|
|
18778
|
+
token1Info.decimals
|
|
18779
|
+
);
|
|
18422
18780
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18423
18781
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18424
18782
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
@@ -18442,7 +18800,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18442
18800
|
blockIdentifier
|
|
18443
18801
|
});
|
|
18444
18802
|
const bounds = await this.getCurrentBounds(blockIdentifier);
|
|
18445
|
-
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18803
|
+
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18804
|
+
Web3Number.fromWei(result.toString(), 18),
|
|
18805
|
+
bounds,
|
|
18806
|
+
blockIdentifier
|
|
18807
|
+
);
|
|
18446
18808
|
return { amount0, amount1 };
|
|
18447
18809
|
}
|
|
18448
18810
|
async totalSupply(blockIdentifier = "pending") {
|
|
@@ -18452,8 +18814,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18452
18814
|
return Web3Number.fromWei(res.toString(), 18);
|
|
18453
18815
|
}
|
|
18454
18816
|
assertValidDepositTokens(poolKey) {
|
|
18455
|
-
assert(
|
|
18456
|
-
|
|
18817
|
+
assert(
|
|
18818
|
+
poolKey.token0.eq(this.metadata.depositTokens[0].address),
|
|
18819
|
+
"Expected token0 in depositTokens[0]"
|
|
18820
|
+
);
|
|
18821
|
+
assert(
|
|
18822
|
+
poolKey.token1.eq(this.metadata.depositTokens[1].address),
|
|
18823
|
+
"Expected token1 in depositTokens[1]"
|
|
18824
|
+
);
|
|
18457
18825
|
}
|
|
18458
18826
|
async getTVL(blockIdentifier = "pending") {
|
|
18459
18827
|
const { amount0, amount1 } = await this._getTVL(blockIdentifier);
|
|
@@ -18483,26 +18851,35 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18483
18851
|
const nftID = await this.getCurrentNFTID();
|
|
18484
18852
|
const poolKey = await this.getPoolKey();
|
|
18485
18853
|
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
|
-
|
|
18854
|
+
const result = await this.ekuboPositionsContract.call(
|
|
18855
|
+
"get_token_info",
|
|
18856
|
+
[
|
|
18857
|
+
nftID,
|
|
18858
|
+
{
|
|
18859
|
+
token0: poolKey.token0.address,
|
|
18860
|
+
token1: poolKey.token1.address,
|
|
18861
|
+
fee: poolKey.fee,
|
|
18862
|
+
tick_spacing: poolKey.tick_spacing,
|
|
18863
|
+
extension: poolKey.extension
|
|
18864
|
+
},
|
|
18865
|
+
{
|
|
18866
|
+
lower: _EkuboCLVault.tickToi129(Number(currentBounds.lowerTick)),
|
|
18867
|
+
upper: _EkuboCLVault.tickToi129(Number(currentBounds.upperTick))
|
|
18868
|
+
}
|
|
18869
|
+
]
|
|
18870
|
+
);
|
|
18500
18871
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18501
18872
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18502
18873
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18503
18874
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18504
|
-
const token0Web3 = Web3Number.fromWei(
|
|
18505
|
-
|
|
18875
|
+
const token0Web3 = Web3Number.fromWei(
|
|
18876
|
+
result.fees0.toString(),
|
|
18877
|
+
token0Info.decimals
|
|
18878
|
+
);
|
|
18879
|
+
const token1Web3 = Web3Number.fromWei(
|
|
18880
|
+
result.fees1.toString(),
|
|
18881
|
+
token1Info.decimals
|
|
18882
|
+
);
|
|
18506
18883
|
const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
|
|
18507
18884
|
const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
|
|
18508
18885
|
return {
|
|
@@ -18524,31 +18901,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18524
18901
|
return Number(result.salt.toString());
|
|
18525
18902
|
}
|
|
18526
18903
|
async truePrice() {
|
|
18527
|
-
|
|
18528
|
-
|
|
18529
|
-
|
|
18904
|
+
if (this.metadata.additionalInfo.truePrice) {
|
|
18905
|
+
return this.metadata.additionalInfo.truePrice;
|
|
18906
|
+
} else if (this.lstContract) {
|
|
18907
|
+
const result = await this.lstContract.call("convert_to_assets", [
|
|
18908
|
+
import_starknet9.uint256.bnToUint256(BigInt(1e18).toString())
|
|
18909
|
+
]);
|
|
18910
|
+
const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
|
|
18911
|
+
return truePrice;
|
|
18912
|
+
}
|
|
18913
|
+
throw new Error("No true price available");
|
|
18530
18914
|
}
|
|
18531
18915
|
async getCurrentPrice(blockIdentifier = "pending") {
|
|
18532
18916
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18533
18917
|
return this._getCurrentPrice(poolKey, blockIdentifier);
|
|
18534
18918
|
}
|
|
18535
18919
|
async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
|
|
18536
|
-
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18920
|
+
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18921
|
+
"get_pool_price",
|
|
18922
|
+
[
|
|
18923
|
+
{
|
|
18924
|
+
token0: poolKey.token0.address,
|
|
18925
|
+
token1: poolKey.token1.address,
|
|
18926
|
+
fee: poolKey.fee,
|
|
18927
|
+
tick_spacing: poolKey.tick_spacing,
|
|
18928
|
+
extension: poolKey.extension
|
|
18929
|
+
}
|
|
18930
|
+
],
|
|
18537
18931
|
{
|
|
18538
|
-
|
|
18539
|
-
token1: poolKey.token1.address,
|
|
18540
|
-
fee: poolKey.fee,
|
|
18541
|
-
tick_spacing: poolKey.tick_spacing,
|
|
18542
|
-
extension: poolKey.extension
|
|
18932
|
+
blockIdentifier
|
|
18543
18933
|
}
|
|
18544
|
-
|
|
18545
|
-
|
|
18546
|
-
|
|
18547
|
-
|
|
18548
|
-
console.log(
|
|
18934
|
+
);
|
|
18935
|
+
const sqrtRatio = _EkuboCLVault.div2Power128(
|
|
18936
|
+
BigInt(priceInfo.sqrt_ratio.toString())
|
|
18937
|
+
);
|
|
18938
|
+
console.log(
|
|
18939
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`
|
|
18940
|
+
);
|
|
18549
18941
|
const price = sqrtRatio * sqrtRatio;
|
|
18550
|
-
const tick = _EkuboCLVault.priceToTick(
|
|
18551
|
-
|
|
18942
|
+
const tick = _EkuboCLVault.priceToTick(
|
|
18943
|
+
price,
|
|
18944
|
+
true,
|
|
18945
|
+
Number(poolKey.tick_spacing)
|
|
18946
|
+
);
|
|
18947
|
+
console.log(
|
|
18948
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`
|
|
18949
|
+
);
|
|
18552
18950
|
return {
|
|
18553
18951
|
price,
|
|
18554
18952
|
tick: tick.mag * (tick.sign == 0 ? 1 : -1),
|
|
@@ -18588,7 +18986,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18588
18986
|
};
|
|
18589
18987
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18590
18988
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18591
|
-
assert(
|
|
18989
|
+
assert(
|
|
18990
|
+
token0Info.decimals == token1Info.decimals,
|
|
18991
|
+
"Tested only for equal decimals"
|
|
18992
|
+
);
|
|
18592
18993
|
this.poolKey = poolKey;
|
|
18593
18994
|
return poolKey;
|
|
18594
18995
|
}
|
|
@@ -18612,11 +19013,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18612
19013
|
async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
|
|
18613
19014
|
assert(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
|
|
18614
19015
|
const sampleLiq = 1e20;
|
|
18615
|
-
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18616
|
-
|
|
19016
|
+
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
19017
|
+
Web3Number.fromWei(sampleLiq.toString(), 18),
|
|
19018
|
+
bounds
|
|
19019
|
+
);
|
|
19020
|
+
logger.verbose(
|
|
19021
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`
|
|
19022
|
+
);
|
|
18617
19023
|
assert(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
|
|
18618
19024
|
const price = await (await this.getCurrentPrice()).price;
|
|
18619
|
-
logger.verbose(
|
|
19025
|
+
logger.verbose(
|
|
19026
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`
|
|
19027
|
+
);
|
|
18620
19028
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18621
19029
|
if (sampleAmount1.eq(0)) {
|
|
18622
19030
|
return {
|
|
@@ -18646,12 +19054,22 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18646
19054
|
};
|
|
18647
19055
|
}
|
|
18648
19056
|
}
|
|
18649
|
-
assert(
|
|
19057
|
+
assert(
|
|
19058
|
+
sampleAmount0.decimals == sampleAmount1.decimals,
|
|
19059
|
+
"Sample amounts have different decimals"
|
|
19060
|
+
);
|
|
18650
19061
|
const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
|
|
18651
19062
|
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
18652
|
-
logger.verbose(
|
|
19063
|
+
logger.verbose(
|
|
19064
|
+
`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
|
|
19065
|
+
);
|
|
18653
19066
|
if (justUseInputAmount)
|
|
18654
|
-
return this._solveExpectedAmountsEq(
|
|
19067
|
+
return this._solveExpectedAmountsEq(
|
|
19068
|
+
amount0,
|
|
19069
|
+
amount1,
|
|
19070
|
+
ratioWeb3Number,
|
|
19071
|
+
price
|
|
19072
|
+
);
|
|
18655
19073
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18656
19074
|
const _amount1 = amount0.dividedBy(ratioWeb3Number);
|
|
18657
19075
|
return {
|
|
@@ -18667,7 +19085,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18667
19085
|
ratio
|
|
18668
19086
|
};
|
|
18669
19087
|
} else {
|
|
18670
|
-
throw new Error(
|
|
19088
|
+
throw new Error(
|
|
19089
|
+
"Both amounts are non-zero, cannot compute expected amounts"
|
|
19090
|
+
);
|
|
18671
19091
|
}
|
|
18672
19092
|
}
|
|
18673
19093
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
@@ -18684,34 +19104,65 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18684
19104
|
const erc20Mod = new ERC20(this.config);
|
|
18685
19105
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18686
19106
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18687
|
-
const token0Bal1 = await erc20Mod.balanceOf(
|
|
18688
|
-
|
|
18689
|
-
|
|
19107
|
+
const token0Bal1 = await erc20Mod.balanceOf(
|
|
19108
|
+
poolKey.token0,
|
|
19109
|
+
this.address.address,
|
|
19110
|
+
token0Info.decimals
|
|
19111
|
+
);
|
|
19112
|
+
const token1Bal1 = await erc20Mod.balanceOf(
|
|
19113
|
+
poolKey.token1,
|
|
19114
|
+
this.address.address,
|
|
19115
|
+
token1Info.decimals
|
|
19116
|
+
);
|
|
19117
|
+
logger.verbose(
|
|
19118
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`
|
|
19119
|
+
);
|
|
18690
19120
|
const token0Price = await this.pricer.getPrice(token0Info.symbol);
|
|
18691
19121
|
const token1Price = await this.pricer.getPrice(token1Info.symbol);
|
|
18692
19122
|
const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
|
|
18693
19123
|
const token1PriceUsd = token1Price.price * Number(token1Bal1.toFixed(13));
|
|
18694
19124
|
if (token0PriceUsd > 1 && token1PriceUsd > 1) {
|
|
18695
|
-
throw new Error(
|
|
19125
|
+
throw new Error(
|
|
19126
|
+
"Both tokens are non-zero and above $1, call handle_fees first"
|
|
19127
|
+
);
|
|
18696
19128
|
}
|
|
18697
19129
|
let token0Bal = token0Bal1;
|
|
18698
19130
|
let token1Bal = token1Bal1;
|
|
18699
19131
|
if (considerRebalance) {
|
|
18700
|
-
logger.verbose(
|
|
19132
|
+
logger.verbose(
|
|
19133
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`
|
|
19134
|
+
);
|
|
18701
19135
|
const tvl = await this.getTVL();
|
|
18702
19136
|
token0Bal = token0Bal.plus(tvl.token0.amount.toString());
|
|
18703
19137
|
token1Bal = token1Bal.plus(tvl.token1.amount.toString());
|
|
18704
19138
|
} else {
|
|
18705
|
-
logger.verbose(
|
|
19139
|
+
logger.verbose(
|
|
19140
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: false`
|
|
19141
|
+
);
|
|
18706
19142
|
}
|
|
18707
|
-
logger.verbose(
|
|
19143
|
+
logger.verbose(
|
|
19144
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
19145
|
+
);
|
|
18708
19146
|
const newBounds = await this.getNewBounds();
|
|
18709
|
-
logger.verbose(
|
|
18710
|
-
|
|
19147
|
+
logger.verbose(
|
|
19148
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`
|
|
19149
|
+
);
|
|
19150
|
+
return await this.getSwapInfoGivenAmounts(
|
|
19151
|
+
poolKey,
|
|
19152
|
+
token0Bal,
|
|
19153
|
+
token1Bal,
|
|
19154
|
+
newBounds
|
|
19155
|
+
);
|
|
18711
19156
|
}
|
|
18712
19157
|
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
|
|
18713
|
-
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
18714
|
-
|
|
19158
|
+
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
19159
|
+
token0Bal,
|
|
19160
|
+
token1Bal,
|
|
19161
|
+
bounds
|
|
19162
|
+
);
|
|
19163
|
+
logger.verbose(
|
|
19164
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19165
|
+
);
|
|
18715
19166
|
let retry = 0;
|
|
18716
19167
|
const maxRetry = 10;
|
|
18717
19168
|
while (retry < maxRetry) {
|
|
@@ -18728,9 +19179,15 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18728
19179
|
const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
|
|
18729
19180
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
18730
19181
|
const expectedRatio = expectedAmounts.ratio;
|
|
18731
|
-
logger.verbose(
|
|
18732
|
-
|
|
18733
|
-
|
|
19182
|
+
logger.verbose(
|
|
19183
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`
|
|
19184
|
+
);
|
|
19185
|
+
logger.verbose(
|
|
19186
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`
|
|
19187
|
+
);
|
|
19188
|
+
logger.verbose(
|
|
19189
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`
|
|
19190
|
+
);
|
|
18734
19191
|
if (amountToSell.eq(0)) {
|
|
18735
19192
|
return {
|
|
18736
19193
|
token_from_address: tokenToSell.address,
|
|
@@ -18744,23 +19201,62 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18744
19201
|
routes: []
|
|
18745
19202
|
};
|
|
18746
19203
|
}
|
|
18747
|
-
const quote = await this.avnu.getQuotes(
|
|
19204
|
+
const quote = await this.avnu.getQuotes(
|
|
19205
|
+
tokenToSell.address,
|
|
19206
|
+
tokenToBuy.address,
|
|
19207
|
+
amountToSell.toWei(),
|
|
19208
|
+
this.address.address
|
|
19209
|
+
);
|
|
18748
19210
|
if (remainingSellAmount.eq(0)) {
|
|
18749
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18750
|
-
|
|
19211
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19212
|
+
quote.buyAmount.toString(),
|
|
19213
|
+
tokenToBuyInfo.decimals
|
|
19214
|
+
).multipliedBy(0.9999);
|
|
19215
|
+
return await this.avnu.getSwapInfo(
|
|
19216
|
+
quote,
|
|
19217
|
+
this.address.address,
|
|
19218
|
+
0,
|
|
19219
|
+
this.address.address,
|
|
19220
|
+
minAmountOut.toWei()
|
|
19221
|
+
);
|
|
18751
19222
|
}
|
|
18752
|
-
const amountOut = Web3Number.fromWei(
|
|
19223
|
+
const amountOut = Web3Number.fromWei(
|
|
19224
|
+
quote.buyAmount.toString(),
|
|
19225
|
+
tokenToBuyInfo.decimals
|
|
19226
|
+
);
|
|
18753
19227
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
18754
19228
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
18755
|
-
logger.verbose(
|
|
18756
|
-
|
|
18757
|
-
|
|
19229
|
+
logger.verbose(
|
|
19230
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
19231
|
+
);
|
|
19232
|
+
logger.verbose(
|
|
19233
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swapPrice: ${swapPrice.toString()}`
|
|
19234
|
+
);
|
|
19235
|
+
logger.verbose(
|
|
19236
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newRatio: ${newRatio.toString()}`
|
|
19237
|
+
);
|
|
18758
19238
|
if (Number(newRatio.toString()) > expectedRatio * 1.0000001 || Number(newRatio.toString()) < expectedRatio * 0.9999999) {
|
|
18759
|
-
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
18760
|
-
|
|
19239
|
+
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
19240
|
+
token0Bal,
|
|
19241
|
+
token1Bal,
|
|
19242
|
+
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
19243
|
+
Number(swapPrice.toString())
|
|
19244
|
+
);
|
|
19245
|
+
logger.verbose(
|
|
19246
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19247
|
+
);
|
|
18761
19248
|
} else {
|
|
18762
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18763
|
-
|
|
19249
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19250
|
+
quote.buyAmount.toString(),
|
|
19251
|
+
tokenToBuyInfo.decimals
|
|
19252
|
+
).multipliedBy(0.9999);
|
|
19253
|
+
return await this.avnu.getSwapInfo(
|
|
19254
|
+
quote,
|
|
19255
|
+
this.address.address,
|
|
19256
|
+
0,
|
|
19257
|
+
this.address.address,
|
|
19258
|
+
minAmountOut.toWei()
|
|
19259
|
+
);
|
|
18764
19260
|
}
|
|
18765
19261
|
retry++;
|
|
18766
19262
|
}
|
|
@@ -18769,8 +19265,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18769
19265
|
/**
|
|
18770
19266
|
* Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
|
|
18771
19267
|
* Uses binary search approach to find optimal swap amount.
|
|
18772
|
-
*
|
|
18773
|
-
* @param newBounds - The new tick bounds to rebalance to
|
|
19268
|
+
*
|
|
19269
|
+
* @param newBounds - The new tick bounds to rebalance to
|
|
18774
19270
|
* @param swapInfo - Initial swap parameters for rebalancing
|
|
18775
19271
|
* @param acc - Account to estimate gas fees with
|
|
18776
19272
|
* @param retry - Current retry attempt number (default 0)
|
|
@@ -18797,7 +19293,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18797
19293
|
logger.error(`Rebalance failed after ${MAX_RETRIES} retries`);
|
|
18798
19294
|
throw err;
|
|
18799
19295
|
}
|
|
18800
|
-
logger.error(
|
|
19296
|
+
logger.error(
|
|
19297
|
+
`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`
|
|
19298
|
+
);
|
|
18801
19299
|
const newSwapInfo = { ...swapInfo };
|
|
18802
19300
|
const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
|
|
18803
19301
|
logger.verbose(`Current amount: ${currentAmount.toString()}`);
|
|
@@ -18889,23 +19387,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18889
19387
|
const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
|
|
18890
19388
|
const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
|
|
18891
19389
|
const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
|
|
18892
|
-
logger.verbose(
|
|
18893
|
-
|
|
18894
|
-
|
|
19390
|
+
logger.verbose(
|
|
19391
|
+
`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`
|
|
19392
|
+
);
|
|
19393
|
+
const result = await this.ekuboMathContract.call(
|
|
19394
|
+
"liquidity_delta_to_amount_delta",
|
|
19395
|
+
[
|
|
19396
|
+
import_starknet9.uint256.bnToUint256(currentPrice.sqrtRatio),
|
|
19397
|
+
{
|
|
19398
|
+
mag: liquidity.toWei(),
|
|
19399
|
+
sign: 0
|
|
19400
|
+
},
|
|
19401
|
+
import_starknet9.uint256.bnToUint256(
|
|
19402
|
+
_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()
|
|
19403
|
+
),
|
|
19404
|
+
import_starknet9.uint256.bnToUint256(
|
|
19405
|
+
_EkuboCLVault.priceToSqrtRatio(upperPrice).toString()
|
|
19406
|
+
)
|
|
19407
|
+
],
|
|
18895
19408
|
{
|
|
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
|
-
});
|
|
19409
|
+
blockIdentifier
|
|
19410
|
+
}
|
|
19411
|
+
);
|
|
18904
19412
|
const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
|
|
18905
19413
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18906
19414
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18907
|
-
const amount0 = Web3Number.fromWei(
|
|
18908
|
-
|
|
19415
|
+
const amount0 = Web3Number.fromWei(
|
|
19416
|
+
_EkuboCLVault.i129ToNumber(result.amount0).toString(),
|
|
19417
|
+
token0Info.decimals
|
|
19418
|
+
);
|
|
19419
|
+
const amount1 = Web3Number.fromWei(
|
|
19420
|
+
_EkuboCLVault.i129ToNumber(result.amount1).toString(),
|
|
19421
|
+
token1Info.decimals
|
|
19422
|
+
);
|
|
18909
19423
|
return {
|
|
18910
19424
|
amount0,
|
|
18911
19425
|
amount1
|
|
@@ -18913,7 +19427,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18913
19427
|
}
|
|
18914
19428
|
async harvest(acc) {
|
|
18915
19429
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
18916
|
-
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19430
|
+
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19431
|
+
this.address
|
|
19432
|
+
);
|
|
18917
19433
|
const poolKey = await this.getPoolKey();
|
|
18918
19434
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18919
19435
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -18923,18 +19439,38 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18923
19439
|
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
18924
19440
|
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
18925
19441
|
const isToken1 = claim.token.eq(poolKey.token1);
|
|
18926
|
-
logger.verbose(
|
|
19442
|
+
logger.verbose(
|
|
19443
|
+
`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
|
|
19444
|
+
);
|
|
18927
19445
|
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
18928
19446
|
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
18929
|
-
logger.verbose(
|
|
18930
|
-
|
|
19447
|
+
logger.verbose(
|
|
19448
|
+
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
|
|
19449
|
+
);
|
|
19450
|
+
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
19451
|
+
poolKey,
|
|
19452
|
+
token0Amt,
|
|
19453
|
+
token1Amt,
|
|
19454
|
+
bounds
|
|
19455
|
+
);
|
|
18931
19456
|
swapInfo.token_to_address = token0Info.address.address;
|
|
18932
|
-
logger.verbose(
|
|
18933
|
-
|
|
19457
|
+
logger.verbose(
|
|
19458
|
+
`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
19459
|
+
);
|
|
19460
|
+
logger.verbose(
|
|
19461
|
+
`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
19462
|
+
);
|
|
18934
19463
|
const harvestEstimateCall = async (swapInfo1) => {
|
|
18935
|
-
const swap1Amount = Web3Number.fromWei(
|
|
19464
|
+
const swap1Amount = Web3Number.fromWei(
|
|
19465
|
+
import_starknet9.uint256.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
19466
|
+
18
|
|
19467
|
+
// cause its always STRK?
|
|
19468
|
+
);
|
|
18936
19469
|
const remainingAmount = postFeeAmount.minus(swap1Amount);
|
|
18937
|
-
const swapInfo2 = {
|
|
19470
|
+
const swapInfo2 = {
|
|
19471
|
+
...swapInfo,
|
|
19472
|
+
token_from_amount: import_starknet9.uint256.bnToUint256(remainingAmount.toWei())
|
|
19473
|
+
};
|
|
18938
19474
|
swapInfo2.token_to_address = token1Info.address.address;
|
|
18939
19475
|
const calldata = [
|
|
18940
19476
|
claim.rewardsContract.address,
|
|
@@ -18947,11 +19483,23 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18947
19483
|
swapInfo,
|
|
18948
19484
|
swapInfo2
|
|
18949
19485
|
];
|
|
18950
|
-
logger.verbose(
|
|
19486
|
+
logger.verbose(
|
|
19487
|
+
`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
19488
|
+
calldata
|
|
19489
|
+
)}`
|
|
19490
|
+
);
|
|
18951
19491
|
return [this.contract.populate("harvest", calldata)];
|
|
18952
19492
|
};
|
|
18953
|
-
const _callsFinal = await this.rebalanceIter(
|
|
18954
|
-
|
|
19493
|
+
const _callsFinal = await this.rebalanceIter(
|
|
19494
|
+
swapInfo,
|
|
19495
|
+
acc,
|
|
19496
|
+
harvestEstimateCall
|
|
19497
|
+
);
|
|
19498
|
+
logger.verbose(
|
|
19499
|
+
`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|
|
19500
|
+
_callsFinal
|
|
19501
|
+
)}`
|
|
19502
|
+
);
|
|
18955
19503
|
calls.push(..._callsFinal);
|
|
18956
19504
|
}
|
|
18957
19505
|
return calls;
|
|
@@ -18961,24 +19509,37 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18961
19509
|
const poolKey = await this.getPoolKey();
|
|
18962
19510
|
const linkedFlow = {
|
|
18963
19511
|
title: this.metadata.name,
|
|
18964
|
-
subItems: [
|
|
19512
|
+
subItems: [
|
|
19513
|
+
{
|
|
19514
|
+
key: "Pool",
|
|
19515
|
+
value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing`
|
|
19516
|
+
}
|
|
19517
|
+
],
|
|
18965
19518
|
linkedFlows: [],
|
|
18966
19519
|
style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
|
|
18967
19520
|
};
|
|
18968
19521
|
const baseFlow = {
|
|
18969
19522
|
id: "base",
|
|
18970
19523
|
title: "Your Deposit",
|
|
18971
|
-
subItems: [
|
|
19524
|
+
subItems: [
|
|
19525
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
19526
|
+
{
|
|
19527
|
+
key: `Performance Fee`,
|
|
19528
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
19529
|
+
}
|
|
19530
|
+
],
|
|
18972
19531
|
linkedFlows: [linkedFlow],
|
|
18973
19532
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
18974
19533
|
};
|
|
18975
19534
|
const rebalanceFlow = {
|
|
18976
19535
|
id: "rebalance",
|
|
18977
19536
|
title: "Automated Rebalance",
|
|
18978
|
-
subItems: [
|
|
18979
|
-
|
|
18980
|
-
|
|
18981
|
-
|
|
19537
|
+
subItems: [
|
|
19538
|
+
{
|
|
19539
|
+
key: "Range selection",
|
|
19540
|
+
value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
|
|
19541
|
+
}
|
|
19542
|
+
],
|
|
18982
19543
|
linkedFlows: [linkedFlow],
|
|
18983
19544
|
style: { backgroundColor: "purple" /* Green */.valueOf() }
|
|
18984
19545
|
};
|
|
@@ -18986,43 +19547,145 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18986
19547
|
}
|
|
18987
19548
|
};
|
|
18988
19549
|
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 = {
|
|
19550
|
+
var _protocol2 = {
|
|
19551
|
+
name: "Ekubo",
|
|
19552
|
+
logo: "https://app.ekubo.org/favicon.ico"
|
|
19553
|
+
};
|
|
18990
19554
|
var _riskFactor2 = [
|
|
18991
19555
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
18992
19556
|
{ type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
|
|
18993
19557
|
];
|
|
19558
|
+
var _riskFactorStable = [
|
|
19559
|
+
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 }
|
|
19560
|
+
];
|
|
18994
19561
|
var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
18995
|
-
var
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
|
|
19000
|
-
|
|
19001
|
-
|
|
19562
|
+
var faqs2 = [
|
|
19563
|
+
{
|
|
19564
|
+
question: "What is the Ekubo CL Vault strategy?",
|
|
19565
|
+
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."
|
|
19566
|
+
},
|
|
19567
|
+
{
|
|
19568
|
+
question: "How are trading fees and rewards handled?",
|
|
19569
|
+
answer: "Trading fees and DeFi Spring rewards are automatically compounded back into the strategy, increasing your overall returns."
|
|
19570
|
+
},
|
|
19571
|
+
{
|
|
19572
|
+
question: "What happens during withdrawal?",
|
|
19573
|
+
answer: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices."
|
|
19574
|
+
},
|
|
19575
|
+
{
|
|
19576
|
+
question: "Is the strategy audited?",
|
|
19577
|
+
answer: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
19578
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
19579
|
+
/* @__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" }),
|
|
19580
|
+
"."
|
|
19002
19581
|
] })
|
|
19003
|
-
|
|
19004
|
-
|
|
19005
|
-
|
|
19006
|
-
|
|
19007
|
-
|
|
19008
|
-
|
|
19009
|
-
|
|
19010
|
-
|
|
19011
|
-
|
|
19012
|
-
|
|
19013
|
-
|
|
19014
|
-
|
|
19582
|
+
}
|
|
19583
|
+
];
|
|
19584
|
+
var EkuboCLVaultStrategies = [
|
|
19585
|
+
{
|
|
19586
|
+
name: "Ekubo xSTRK/STRK",
|
|
19587
|
+
description: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
19588
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK") }),
|
|
19589
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
19590
|
+
"ul",
|
|
19591
|
+
{
|
|
19592
|
+
style: {
|
|
19593
|
+
marginLeft: "20px",
|
|
19594
|
+
listStyle: "circle",
|
|
19595
|
+
fontSize: "12px"
|
|
19596
|
+
},
|
|
19597
|
+
children: [
|
|
19598
|
+
/* @__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." }),
|
|
19599
|
+
/* @__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." })
|
|
19600
|
+
]
|
|
19601
|
+
}
|
|
19602
|
+
)
|
|
19603
|
+
] }),
|
|
19604
|
+
address: ContractAddr.from(
|
|
19605
|
+
"0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"
|
|
19606
|
+
),
|
|
19607
|
+
launchBlock: 1209881,
|
|
19608
|
+
type: "Other",
|
|
19609
|
+
// must be same order as poolKey token0 and token1
|
|
19610
|
+
depositTokens: [
|
|
19611
|
+
Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"),
|
|
19612
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
19613
|
+
],
|
|
19614
|
+
protocols: [_protocol2],
|
|
19615
|
+
auditUrl: AUDIT_URL2,
|
|
19616
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
19617
|
+
risk: {
|
|
19618
|
+
riskFactor: _riskFactor2,
|
|
19619
|
+
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19620
|
+
notARisks: getNoRiskTags(_riskFactor2)
|
|
19621
|
+
},
|
|
19622
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19623
|
+
additionalInfo: {
|
|
19624
|
+
newBounds: {
|
|
19625
|
+
lower: -1,
|
|
19626
|
+
upper: 1
|
|
19627
|
+
},
|
|
19628
|
+
lstContract: ContractAddr.from(
|
|
19629
|
+
"0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
|
|
19630
|
+
),
|
|
19631
|
+
feeBps: 1e3
|
|
19632
|
+
},
|
|
19633
|
+
faqs: [
|
|
19634
|
+
...faqs2,
|
|
19635
|
+
{
|
|
19636
|
+
question: "Why might I see a negative APY?",
|
|
19637
|
+
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."
|
|
19638
|
+
}
|
|
19639
|
+
]
|
|
19015
19640
|
},
|
|
19016
|
-
|
|
19017
|
-
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
|
|
19641
|
+
{
|
|
19642
|
+
name: "Ekubo USDC/USDT",
|
|
19643
|
+
description: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
19644
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: _description2.replace("{{POOL_NAME}}", "USDC/USDT") }),
|
|
19645
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
19646
|
+
"ul",
|
|
19647
|
+
{
|
|
19648
|
+
style: {
|
|
19649
|
+
marginLeft: "20px",
|
|
19650
|
+
listStyle: "circle",
|
|
19651
|
+
fontSize: "12px"
|
|
19652
|
+
},
|
|
19653
|
+
children: /* @__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." })
|
|
19654
|
+
}
|
|
19655
|
+
)
|
|
19656
|
+
] }),
|
|
19657
|
+
address: ContractAddr.from(
|
|
19658
|
+
"0xd647ed735f0db52f2a5502b6e06ed21dc4284a43a36af4b60d3c80fbc56c91"
|
|
19659
|
+
),
|
|
19660
|
+
launchBlock: 1385576,
|
|
19661
|
+
type: "Other",
|
|
19662
|
+
// must be same order as poolKey token0 and token1
|
|
19663
|
+
depositTokens: [
|
|
19664
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC"),
|
|
19665
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
19666
|
+
],
|
|
19667
|
+
protocols: [_protocol2],
|
|
19668
|
+
auditUrl: AUDIT_URL2,
|
|
19669
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
19670
|
+
risk: {
|
|
19671
|
+
riskFactor: _riskFactorStable,
|
|
19672
|
+
netRisk: _riskFactorStable.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactorStable.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19673
|
+
notARisks: getNoRiskTags(_riskFactorStable)
|
|
19021
19674
|
},
|
|
19022
|
-
|
|
19023
|
-
|
|
19675
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19676
|
+
additionalInfo: {
|
|
19677
|
+
newBounds: {
|
|
19678
|
+
lower: -1,
|
|
19679
|
+
upper: 1
|
|
19680
|
+
},
|
|
19681
|
+
truePrice: 1,
|
|
19682
|
+
feeBps: 1e3
|
|
19683
|
+
},
|
|
19684
|
+
faqs: [
|
|
19685
|
+
...faqs2
|
|
19686
|
+
]
|
|
19024
19687
|
}
|
|
19025
|
-
|
|
19688
|
+
];
|
|
19026
19689
|
|
|
19027
19690
|
// src/notifs/telegram.ts
|
|
19028
19691
|
var import_node_telegram_bot_api = __toESM(require("node-telegram-bot-api"));
|