@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.mjs
CHANGED
|
@@ -7,6 +7,43 @@ import axios from "axios";
|
|
|
7
7
|
// src/dataTypes/bignumber.node.ts
|
|
8
8
|
import util from "util";
|
|
9
9
|
|
|
10
|
+
// src/utils/logger.node.ts
|
|
11
|
+
import winston, { format } from "winston";
|
|
12
|
+
var colors = {
|
|
13
|
+
error: "red",
|
|
14
|
+
warn: "yellow",
|
|
15
|
+
info: "blue",
|
|
16
|
+
verbose: "white",
|
|
17
|
+
debug: "white"
|
|
18
|
+
};
|
|
19
|
+
winston.addColors(colors);
|
|
20
|
+
var logger = winston.createLogger({
|
|
21
|
+
level: "verbose",
|
|
22
|
+
// Set the minimum logging level
|
|
23
|
+
format: format.combine(
|
|
24
|
+
format.colorize({ all: true }),
|
|
25
|
+
// Apply custom colors
|
|
26
|
+
format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
|
|
27
|
+
// Add timestamp to log messages
|
|
28
|
+
format.printf(({ timestamp, level, message, ...meta }) => {
|
|
29
|
+
let msg = `${timestamp} ${level}: ${message}`;
|
|
30
|
+
if (meta && meta[Symbol.for("splat")]) {
|
|
31
|
+
for (const arg of meta[Symbol.for("splat")]) {
|
|
32
|
+
if (arg instanceof Error) {
|
|
33
|
+
msg += `
|
|
34
|
+
${arg.stack}`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return msg;
|
|
39
|
+
})
|
|
40
|
+
),
|
|
41
|
+
transports: [
|
|
42
|
+
new winston.transports.Console()
|
|
43
|
+
// Output logs to the console
|
|
44
|
+
]
|
|
45
|
+
});
|
|
46
|
+
|
|
10
47
|
// src/dataTypes/_bignumber.ts
|
|
11
48
|
import BigNumber from "bignumber.js";
|
|
12
49
|
var _Web3Number = class extends BigNumber {
|
|
@@ -23,6 +60,7 @@ var _Web3Number = class extends BigNumber {
|
|
|
23
60
|
}
|
|
24
61
|
dividedBy(value) {
|
|
25
62
|
const _value = this.getStandardString(value);
|
|
63
|
+
console.log("dividedBy", _value);
|
|
26
64
|
return this.construct(this.div(_value).toString(), this.decimals);
|
|
27
65
|
}
|
|
28
66
|
plus(value) {
|
|
@@ -36,13 +74,15 @@ var _Web3Number = class extends BigNumber {
|
|
|
36
74
|
construct(value, decimals) {
|
|
37
75
|
return new this.constructor(value, decimals);
|
|
38
76
|
}
|
|
39
|
-
toString(
|
|
40
|
-
return super.
|
|
77
|
+
toString() {
|
|
78
|
+
return super.toString();
|
|
41
79
|
}
|
|
42
80
|
toJSON() {
|
|
81
|
+
logger.verbose(`converting to json with decimals`);
|
|
43
82
|
return this.toString();
|
|
44
83
|
}
|
|
45
84
|
valueOf() {
|
|
85
|
+
logger.verbose(`converting to valueOf with decimals`);
|
|
46
86
|
return this.toString();
|
|
47
87
|
}
|
|
48
88
|
maxToFixedDecimals() {
|
|
@@ -107,12 +147,6 @@ var ContractAddr = class _ContractAddr {
|
|
|
107
147
|
};
|
|
108
148
|
|
|
109
149
|
// src/global.ts
|
|
110
|
-
var logger = {
|
|
111
|
-
...console,
|
|
112
|
-
verbose(message) {
|
|
113
|
-
console.log(`[VERBOSE] ${message}`);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
150
|
var FatalError = class extends Error {
|
|
117
151
|
constructor(message, err) {
|
|
118
152
|
super(message);
|
|
@@ -706,7 +740,7 @@ var PricerFromApi = class extends PricerBase {
|
|
|
706
740
|
} catch (e) {
|
|
707
741
|
logger.warn("getPriceFromMyAPI error", JSON.stringify(e.message || e));
|
|
708
742
|
}
|
|
709
|
-
logger.
|
|
743
|
+
logger.info("getPrice coinbase", tokenSymbol);
|
|
710
744
|
let retry = 0;
|
|
711
745
|
const MAX_RETRIES = 5;
|
|
712
746
|
for (retry = 1; retry < MAX_RETRIES + 1; retry++) {
|
|
@@ -1887,6 +1921,11 @@ var ERC20 = class {
|
|
|
1887
1921
|
const balance = await contract.call("balanceOf", [address.toString()]);
|
|
1888
1922
|
return Web3Number.fromWei(balance.toString(), tokenDecimals);
|
|
1889
1923
|
}
|
|
1924
|
+
async allowance(token, owner, spender, tokenDecimals) {
|
|
1925
|
+
const contract = this.contract(token);
|
|
1926
|
+
const allowance = await contract.call("allowance", [owner.toString(), spender.toString()]);
|
|
1927
|
+
return Web3Number.fromWei(allowance.toString(), tokenDecimals);
|
|
1928
|
+
}
|
|
1890
1929
|
};
|
|
1891
1930
|
|
|
1892
1931
|
// src/modules/avnu.ts
|
|
@@ -2026,7 +2065,9 @@ var getRiskColor = (risk) => {
|
|
|
2026
2065
|
};
|
|
2027
2066
|
var getNoRiskTags = (risks) => {
|
|
2028
2067
|
const noRisks1 = risks.filter((risk) => risk.value === 0).map((risk) => risk.type);
|
|
2029
|
-
const noRisks2 = Object.values(RiskType).filter(
|
|
2068
|
+
const noRisks2 = Object.values(RiskType).filter(
|
|
2069
|
+
(risk) => !risks.map((risk2) => risk2.type).includes(risk)
|
|
2070
|
+
);
|
|
2030
2071
|
const mergedUnique = [.../* @__PURE__ */ new Set([...noRisks1, ...noRisks2])];
|
|
2031
2072
|
return mergedUnique.map((risk) => `No ${risk}`);
|
|
2032
2073
|
};
|
|
@@ -12718,6 +12759,7 @@ var vesu_pools_default = {
|
|
|
12718
12759
|
};
|
|
12719
12760
|
|
|
12720
12761
|
// src/strategies/vesu-rebalance.tsx
|
|
12762
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12721
12763
|
var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
12722
12764
|
// 10000 bps = 100%
|
|
12723
12765
|
/**
|
|
@@ -12731,10 +12773,17 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12731
12773
|
super(config);
|
|
12732
12774
|
this.BASE_WEIGHT = 1e4;
|
|
12733
12775
|
this.pricer = pricer;
|
|
12734
|
-
assert(
|
|
12776
|
+
assert(
|
|
12777
|
+
metadata.depositTokens.length === 1,
|
|
12778
|
+
"VesuRebalance only supports 1 deposit token"
|
|
12779
|
+
);
|
|
12735
12780
|
this.metadata = metadata;
|
|
12736
12781
|
this.address = metadata.address;
|
|
12737
|
-
this.contract = new Contract5(
|
|
12782
|
+
this.contract = new Contract5(
|
|
12783
|
+
vesu_rebalance_abi_default,
|
|
12784
|
+
this.address.address,
|
|
12785
|
+
this.config.provider
|
|
12786
|
+
);
|
|
12738
12787
|
}
|
|
12739
12788
|
/**
|
|
12740
12789
|
* Creates a deposit call to the strategy contract.
|
|
@@ -12743,10 +12792,23 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12743
12792
|
* @returns Populated contract call for deposit
|
|
12744
12793
|
*/
|
|
12745
12794
|
async depositCall(amountInfo, receiver) {
|
|
12746
|
-
assert(
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12795
|
+
assert(
|
|
12796
|
+
amountInfo.tokenInfo.address.eq(this.asset().address),
|
|
12797
|
+
"Deposit token mismatch"
|
|
12798
|
+
);
|
|
12799
|
+
const assetContract = new Contract5(
|
|
12800
|
+
vesu_rebalance_abi_default,
|
|
12801
|
+
this.asset().address.address,
|
|
12802
|
+
this.config.provider
|
|
12803
|
+
);
|
|
12804
|
+
const call1 = assetContract.populate("approve", [
|
|
12805
|
+
this.address.address,
|
|
12806
|
+
uint2563.bnToUint256(amountInfo.amount.toWei())
|
|
12807
|
+
]);
|
|
12808
|
+
const call2 = this.contract.populate("deposit", [
|
|
12809
|
+
uint2563.bnToUint256(amountInfo.amount.toWei()),
|
|
12810
|
+
receiver.address
|
|
12811
|
+
]);
|
|
12750
12812
|
return [call1, call2];
|
|
12751
12813
|
}
|
|
12752
12814
|
/**
|
|
@@ -12757,7 +12819,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12757
12819
|
* @returns Populated contract call for withdrawal
|
|
12758
12820
|
*/
|
|
12759
12821
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
12760
|
-
return [
|
|
12822
|
+
return [
|
|
12823
|
+
this.contract.populate("withdraw", [
|
|
12824
|
+
uint2563.bnToUint256(amountInfo.amount.toWei()),
|
|
12825
|
+
receiver.address,
|
|
12826
|
+
owner.address
|
|
12827
|
+
])
|
|
12828
|
+
];
|
|
12761
12829
|
}
|
|
12762
12830
|
/**
|
|
12763
12831
|
* Returns the underlying asset token of the strategy.
|
|
@@ -12780,9 +12848,16 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12780
12848
|
*/
|
|
12781
12849
|
async getUserTVL(user) {
|
|
12782
12850
|
const shares = await this.contract.balanceOf(user.address);
|
|
12783
|
-
const assets = await this.contract.convert_to_assets(
|
|
12784
|
-
|
|
12785
|
-
|
|
12851
|
+
const assets = await this.contract.convert_to_assets(
|
|
12852
|
+
uint2563.bnToUint256(shares)
|
|
12853
|
+
);
|
|
12854
|
+
const amount = Web3Number.fromWei(
|
|
12855
|
+
assets.toString(),
|
|
12856
|
+
this.metadata.depositTokens[0].decimals
|
|
12857
|
+
);
|
|
12858
|
+
let price = await this.pricer.getPrice(
|
|
12859
|
+
this.metadata.depositTokens[0].symbol
|
|
12860
|
+
);
|
|
12786
12861
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12787
12862
|
return {
|
|
12788
12863
|
tokenInfo: this.asset(),
|
|
@@ -12796,8 +12871,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12796
12871
|
*/
|
|
12797
12872
|
async getTVL() {
|
|
12798
12873
|
const assets = await this.contract.total_assets();
|
|
12799
|
-
const amount = Web3Number.fromWei(
|
|
12800
|
-
|
|
12874
|
+
const amount = Web3Number.fromWei(
|
|
12875
|
+
assets.toString(),
|
|
12876
|
+
this.metadata.depositTokens[0].decimals
|
|
12877
|
+
);
|
|
12878
|
+
let price = await this.pricer.getPrice(
|
|
12879
|
+
this.metadata.depositTokens[0].symbol
|
|
12880
|
+
);
|
|
12801
12881
|
const usdValue = Number(amount.toFixed(6)) * price.price;
|
|
12802
12882
|
return {
|
|
12803
12883
|
tokenInfo: this.asset(),
|
|
@@ -12823,51 +12903,104 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12823
12903
|
return pools;
|
|
12824
12904
|
}
|
|
12825
12905
|
async getPoolInfo(p, pools, vesuPositions, totalAssets, isErrorPositionsAPI, isErrorPoolsAPI) {
|
|
12826
|
-
const vesuPosition = vesuPositions.find(
|
|
12906
|
+
const vesuPosition = vesuPositions.find(
|
|
12907
|
+
(d) => d.pool.id.toString() === num3.getDecimalString(p.pool_id.address.toString())
|
|
12908
|
+
);
|
|
12827
12909
|
const _pool = pools.find((d) => {
|
|
12828
|
-
logger.verbose(
|
|
12910
|
+
logger.verbose(
|
|
12911
|
+
`pool check: ${d.id == num3.getDecimalString(p.pool_id.address.toString())}, id: ${d.id}, pool_id: ${num3.getDecimalString(
|
|
12912
|
+
p.pool_id.address.toString()
|
|
12913
|
+
)}`
|
|
12914
|
+
);
|
|
12829
12915
|
return d.id == num3.getDecimalString(p.pool_id.address.toString());
|
|
12830
12916
|
});
|
|
12831
12917
|
logger.verbose(`pool: ${JSON.stringify(_pool)}`);
|
|
12832
12918
|
logger.verbose(typeof _pool);
|
|
12833
12919
|
logger.verbose(`name: ${_pool?.name}`);
|
|
12834
12920
|
const name = _pool?.name;
|
|
12835
|
-
logger.verbose(
|
|
12836
|
-
|
|
12921
|
+
logger.verbose(
|
|
12922
|
+
`name2: ${name}, ${!name ? true : false}, ${name?.length}, ${typeof name}`
|
|
12923
|
+
);
|
|
12924
|
+
const assetInfo = _pool?.assets.find(
|
|
12925
|
+
(d) => this.asset().address.eqString(d.address)
|
|
12926
|
+
);
|
|
12837
12927
|
if (!name) {
|
|
12838
12928
|
logger.verbose(`Pool not found`);
|
|
12839
12929
|
throw new Error(`Pool name ${p.pool_id.address.toString()} not found`);
|
|
12840
12930
|
}
|
|
12841
12931
|
if (!assetInfo) {
|
|
12842
|
-
throw new Error(
|
|
12932
|
+
throw new Error(
|
|
12933
|
+
`Asset ${this.asset().address.toString()} not found in pool ${p.pool_id.address.toString()}`
|
|
12934
|
+
);
|
|
12843
12935
|
}
|
|
12844
|
-
let vTokenContract = new Contract5(
|
|
12936
|
+
let vTokenContract = new Contract5(
|
|
12937
|
+
vesu_rebalance_abi_default,
|
|
12938
|
+
p.v_token.address,
|
|
12939
|
+
this.config.provider
|
|
12940
|
+
);
|
|
12845
12941
|
const bal = await vTokenContract.balanceOf(this.address.address);
|
|
12846
|
-
const assets = await vTokenContract.convert_to_assets(
|
|
12942
|
+
const assets = await vTokenContract.convert_to_assets(
|
|
12943
|
+
uint2563.bnToUint256(bal.toString())
|
|
12944
|
+
);
|
|
12847
12945
|
logger.verbose(`Collateral: ${JSON.stringify(vesuPosition?.collateral)}`);
|
|
12848
12946
|
logger.verbose(`supplyApy: ${JSON.stringify(assetInfo?.stats.supplyApy)}`);
|
|
12849
|
-
logger.verbose(
|
|
12850
|
-
|
|
12851
|
-
|
|
12947
|
+
logger.verbose(
|
|
12948
|
+
`defiSpringSupplyApr: ${JSON.stringify(
|
|
12949
|
+
assetInfo?.stats.defiSpringSupplyApr
|
|
12950
|
+
)}`
|
|
12951
|
+
);
|
|
12952
|
+
logger.verbose(
|
|
12953
|
+
`currentUtilization: ${JSON.stringify(
|
|
12954
|
+
assetInfo?.stats.currentUtilization
|
|
12955
|
+
)}`
|
|
12956
|
+
);
|
|
12957
|
+
logger.verbose(
|
|
12958
|
+
`maxUtilization: ${JSON.stringify(assetInfo?.config.maxUtilization)}`
|
|
12959
|
+
);
|
|
12852
12960
|
const item = {
|
|
12853
12961
|
pool_id: p.pool_id,
|
|
12854
12962
|
pool_name: _pool?.name,
|
|
12855
12963
|
max_weight: p.max_weight,
|
|
12856
|
-
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12964
|
+
current_weight: isErrorPositionsAPI || !vesuPosition ? 0 : Number(
|
|
12965
|
+
Web3Number.fromWei(vesuPosition.collateral.value, this.decimals()).dividedBy(totalAssets.toString()).toFixed(6)
|
|
12966
|
+
),
|
|
12857
12967
|
v_token: p.v_token,
|
|
12858
12968
|
amount: Web3Number.fromWei(assets.toString(), this.decimals()),
|
|
12859
|
-
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
12969
|
+
usdValue: isErrorPositionsAPI || !vesuPosition ? Web3Number.fromWei("0", this.decimals()) : Web3Number.fromWei(
|
|
12970
|
+
vesuPosition.collateral.usdPrice.value,
|
|
12971
|
+
vesuPosition.collateral.usdPrice.decimals
|
|
12972
|
+
),
|
|
12860
12973
|
APY: isErrorPoolsAPI || !assetInfo ? {
|
|
12861
12974
|
baseApy: 0,
|
|
12862
12975
|
defiSpringApy: 0,
|
|
12863
12976
|
netApy: 0
|
|
12864
12977
|
} : {
|
|
12865
|
-
baseApy: Number(
|
|
12866
|
-
|
|
12978
|
+
baseApy: Number(
|
|
12979
|
+
Web3Number.fromWei(
|
|
12980
|
+
assetInfo.stats.supplyApy.value,
|
|
12981
|
+
assetInfo.stats.supplyApy.decimals
|
|
12982
|
+
).toFixed(6)
|
|
12983
|
+
),
|
|
12984
|
+
defiSpringApy: assetInfo.stats.defiSpringSupplyApr ? Number(
|
|
12985
|
+
Web3Number.fromWei(
|
|
12986
|
+
assetInfo.stats.defiSpringSupplyApr.value,
|
|
12987
|
+
assetInfo.stats.defiSpringSupplyApr.decimals
|
|
12988
|
+
).toFixed(6)
|
|
12989
|
+
) : 0,
|
|
12867
12990
|
netApy: 0
|
|
12868
12991
|
},
|
|
12869
|
-
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12870
|
-
|
|
12992
|
+
currentUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12993
|
+
Web3Number.fromWei(
|
|
12994
|
+
assetInfo.stats.currentUtilization.value,
|
|
12995
|
+
assetInfo.stats.currentUtilization.decimals
|
|
12996
|
+
).toFixed(6)
|
|
12997
|
+
),
|
|
12998
|
+
maxUtilization: isErrorPoolsAPI || !assetInfo ? 0 : Number(
|
|
12999
|
+
Web3Number.fromWei(
|
|
13000
|
+
assetInfo.config.maxUtilization.value,
|
|
13001
|
+
assetInfo.config.maxUtilization.decimals
|
|
13002
|
+
).toFixed(6)
|
|
13003
|
+
)
|
|
12871
13004
|
};
|
|
12872
13005
|
item.APY.netApy = item.APY.baseApy + item.APY.defiSpringApy;
|
|
12873
13006
|
return item;
|
|
@@ -12877,7 +13010,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12877
13010
|
* 1. Contract's allowed pools
|
|
12878
13011
|
* 2. Vesu positions API for current positions
|
|
12879
13012
|
* 3. Vesu pools API for APY and utilization data
|
|
12880
|
-
*
|
|
13013
|
+
*
|
|
12881
13014
|
* @returns {Promise<{
|
|
12882
13015
|
* data: Array<PoolInfoFull>,
|
|
12883
13016
|
* isErrorPositionsAPI: boolean
|
|
@@ -12894,15 +13027,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12894
13027
|
let isErrorPositionsAPI = false;
|
|
12895
13028
|
let vesuPositions = [];
|
|
12896
13029
|
try {
|
|
12897
|
-
const data2 = await getAPIUsingHeadlessBrowser(
|
|
13030
|
+
const data2 = await getAPIUsingHeadlessBrowser(
|
|
13031
|
+
`https://api.vesu.xyz/positions?walletAddress=${this.address.address}`
|
|
13032
|
+
);
|
|
12898
13033
|
vesuPositions = data2.data;
|
|
12899
13034
|
} catch (e) {
|
|
12900
|
-
console.error(
|
|
13035
|
+
console.error(
|
|
13036
|
+
`${_VesuRebalance.name}: Error fetching positions for ${this.address.address}`,
|
|
13037
|
+
e
|
|
13038
|
+
);
|
|
12901
13039
|
isErrorPositionsAPI = true;
|
|
12902
13040
|
}
|
|
12903
13041
|
let { pools, isErrorPoolsAPI } = await this.getVesuPools();
|
|
12904
13042
|
const totalAssets = (await this.getTVL()).amount;
|
|
12905
|
-
const info = allowedPools.map(
|
|
13043
|
+
const info = allowedPools.map(
|
|
13044
|
+
(p) => this.getPoolInfo(
|
|
13045
|
+
p,
|
|
13046
|
+
pools,
|
|
13047
|
+
vesuPositions,
|
|
13048
|
+
totalAssets,
|
|
13049
|
+
isErrorPositionsAPI,
|
|
13050
|
+
isErrorPoolsAPI
|
|
13051
|
+
)
|
|
13052
|
+
);
|
|
12906
13053
|
const data = await Promise.all(info);
|
|
12907
13054
|
return {
|
|
12908
13055
|
data,
|
|
@@ -12915,18 +13062,25 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12915
13062
|
let isErrorPoolsAPI = false;
|
|
12916
13063
|
let pools = [];
|
|
12917
13064
|
try {
|
|
12918
|
-
const data = await getAPIUsingHeadlessBrowser(
|
|
13065
|
+
const data = await getAPIUsingHeadlessBrowser(
|
|
13066
|
+
"https://api.vesu.xyz/pools"
|
|
13067
|
+
);
|
|
12919
13068
|
pools = data.data;
|
|
12920
13069
|
for (const pool of vesu_pools_default.data) {
|
|
12921
13070
|
const found = pools.find((d) => d.id === pool.id);
|
|
12922
13071
|
if (!found) {
|
|
12923
13072
|
logger.verbose(`VesuRebalance: pools: ${JSON.stringify(pools)}`);
|
|
12924
|
-
logger.verbose(
|
|
13073
|
+
logger.verbose(
|
|
13074
|
+
`VesuRebalance: Pool ${pool.id} not found in Vesu API, using hardcoded data`
|
|
13075
|
+
);
|
|
12925
13076
|
throw new Error("pool not found [sanity check]");
|
|
12926
13077
|
}
|
|
12927
13078
|
}
|
|
12928
13079
|
} catch (e) {
|
|
12929
|
-
logger.error(
|
|
13080
|
+
logger.error(
|
|
13081
|
+
`${_VesuRebalance.name}: Error fetching pools for ${this.address.address}, retry ${retry}`,
|
|
13082
|
+
e
|
|
13083
|
+
);
|
|
12930
13084
|
isErrorPoolsAPI = true;
|
|
12931
13085
|
if (retry < 10) {
|
|
12932
13086
|
await new Promise((resolve) => setTimeout(resolve, 5e3 * (retry + 1)));
|
|
@@ -12964,8 +13118,8 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12964
13118
|
* 3. For each pool that needs more funds:
|
|
12965
13119
|
* - Takes funds from lowest APY pools that are over their target
|
|
12966
13120
|
* 4. Validates that total assets remain constant
|
|
12967
|
-
*
|
|
12968
|
-
* @returns {Promise<{
|
|
13121
|
+
*
|
|
13122
|
+
* @returns {Promise<{
|
|
12969
13123
|
* changes: Change[],
|
|
12970
13124
|
* finalPools: PoolInfoFull[],
|
|
12971
13125
|
* isAnyPoolOverMaxWeight: boolean
|
|
@@ -12981,27 +13135,38 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
12981
13135
|
_pools = _pools2;
|
|
12982
13136
|
}
|
|
12983
13137
|
const feeDeductions = await this.getFee(_pools);
|
|
12984
|
-
logger.verbose(
|
|
13138
|
+
logger.verbose(
|
|
13139
|
+
`VesuRebalance: feeDeductions: ${JSON.stringify(feeDeductions)}`
|
|
13140
|
+
);
|
|
12985
13141
|
const pools = _pools.map((p) => {
|
|
12986
13142
|
const fee = feeDeductions.find((f) => p.v_token.eq(f.vToken))?.fee || Web3Number.fromWei("0", this.decimals());
|
|
12987
|
-
logger.verbose(
|
|
13143
|
+
logger.verbose(
|
|
13144
|
+
`FeeAdjustment: ${p.pool_id} => ${fee.toString()}, amt: ${p.amount.toString()}`
|
|
13145
|
+
);
|
|
12988
13146
|
return {
|
|
12989
13147
|
...p,
|
|
12990
13148
|
amount: p.amount.minus(fee)
|
|
12991
13149
|
};
|
|
12992
13150
|
});
|
|
12993
13151
|
let totalAssets = (await this.getTVL()).amount;
|
|
12994
|
-
if (totalAssets.eq(0))
|
|
12995
|
-
|
|
12996
|
-
|
|
12997
|
-
|
|
13152
|
+
if (totalAssets.eq(0))
|
|
13153
|
+
return {
|
|
13154
|
+
changes: [],
|
|
13155
|
+
finalPools: []
|
|
13156
|
+
};
|
|
12998
13157
|
feeDeductions.forEach((f) => {
|
|
12999
13158
|
totalAssets = totalAssets.minus(f.fee);
|
|
13000
13159
|
});
|
|
13001
|
-
const sumPools = pools.reduce(
|
|
13160
|
+
const sumPools = pools.reduce(
|
|
13161
|
+
(acc, curr) => acc.plus(curr.amount.toString()),
|
|
13162
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13163
|
+
);
|
|
13002
13164
|
logger.verbose(`Sum of pools: ${sumPools.toString()}`);
|
|
13003
13165
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13004
|
-
assert(
|
|
13166
|
+
assert(
|
|
13167
|
+
sumPools.lte(totalAssets.multipliedBy(1.00001).toString()),
|
|
13168
|
+
"Sum of pools.amount must be less than or equal to totalAssets"
|
|
13169
|
+
);
|
|
13005
13170
|
const sortedPools = [...pools].sort((a, b) => b.APY.netApy - a.APY.netApy);
|
|
13006
13171
|
const targetAmounts = {};
|
|
13007
13172
|
let remainingAssets = totalAssets;
|
|
@@ -13023,7 +13188,10 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13023
13188
|
assert(remainingAssets.lt(1e-5), "Remaining assets must be 0");
|
|
13024
13189
|
const changes = sortedPools.map((pool) => {
|
|
13025
13190
|
const target = targetAmounts[pool.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
13026
|
-
const change = Web3Number.fromWei(
|
|
13191
|
+
const change = Web3Number.fromWei(
|
|
13192
|
+
target.minus(pool.amount.toString()).toWei(),
|
|
13193
|
+
this.decimals()
|
|
13194
|
+
);
|
|
13027
13195
|
return {
|
|
13028
13196
|
pool_id: pool.pool_id,
|
|
13029
13197
|
changeAmt: change,
|
|
@@ -13032,14 +13200,21 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13032
13200
|
};
|
|
13033
13201
|
});
|
|
13034
13202
|
logger.verbose(`Changes: ${JSON.stringify(changes)}`);
|
|
13035
|
-
const sumChanges = changes.reduce(
|
|
13036
|
-
|
|
13203
|
+
const sumChanges = changes.reduce(
|
|
13204
|
+
(sum, c) => sum.plus(c.changeAmt.toString()),
|
|
13205
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13206
|
+
);
|
|
13207
|
+
const sumFinal = changes.reduce(
|
|
13208
|
+
(sum, c) => sum.plus(c.finalAmt.toString()),
|
|
13209
|
+
Web3Number.fromWei("0", this.decimals())
|
|
13210
|
+
);
|
|
13037
13211
|
const hasChanges = changes.some((c) => !c.changeAmt.eq(0));
|
|
13038
13212
|
logger.verbose(`Sum of changes: ${sumChanges.toString()}`);
|
|
13039
13213
|
if (!sumChanges.eq(0)) throw new Error("Sum of changes must be zero");
|
|
13040
13214
|
logger.verbose(`Sum of final: ${sumFinal.toString()}`);
|
|
13041
13215
|
logger.verbose(`Total assets: ${totalAssets.toString()}`);
|
|
13042
|
-
if (!sumFinal.eq(totalAssets.toString()))
|
|
13216
|
+
if (!sumFinal.eq(totalAssets.toString()))
|
|
13217
|
+
throw new Error("Sum of final amounts must equal total assets");
|
|
13043
13218
|
if (!hasChanges) throw new Error("No changes required");
|
|
13044
13219
|
const finalPools = pools.map((p) => {
|
|
13045
13220
|
const target = targetAmounts[p.pool_id.address.toString()] || Web3Number.fromWei("0", this.decimals());
|
|
@@ -13067,9 +13242,13 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13067
13242
|
if (p.changeAmt.eq(0)) return null;
|
|
13068
13243
|
actions.push({
|
|
13069
13244
|
pool_id: p.pool_id.address,
|
|
13070
|
-
feature: new CairoCustomEnum(
|
|
13245
|
+
feature: new CairoCustomEnum(
|
|
13246
|
+
p.isDeposit ? { DEPOSIT: {} } : { WITHDRAW: {} }
|
|
13247
|
+
),
|
|
13071
13248
|
token: this.asset().address.address,
|
|
13072
|
-
amount: uint2563.bnToUint256(
|
|
13249
|
+
amount: uint2563.bnToUint256(
|
|
13250
|
+
p.changeAmt.multipliedBy(p.isDeposit ? 1 : -1).toWei()
|
|
13251
|
+
)
|
|
13073
13252
|
});
|
|
13074
13253
|
});
|
|
13075
13254
|
if (actions.length === 0) return null;
|
|
@@ -13082,18 +13261,29 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13082
13261
|
const netYield = await this.netAPYGivenPools(pools);
|
|
13083
13262
|
const baseFlow = {
|
|
13084
13263
|
title: "Your Deposit",
|
|
13085
|
-
subItems: [
|
|
13264
|
+
subItems: [
|
|
13265
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
13266
|
+
{
|
|
13267
|
+
key: `Performance Fee`,
|
|
13268
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
13269
|
+
}
|
|
13270
|
+
],
|
|
13086
13271
|
linkedFlows: [],
|
|
13087
13272
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
13088
13273
|
};
|
|
13089
13274
|
let _pools = [...pools];
|
|
13090
|
-
_pools = _pools.sort(
|
|
13275
|
+
_pools = _pools.sort(
|
|
13276
|
+
(a, b) => Number(b.amount.toString()) - Number(a.amount.toString())
|
|
13277
|
+
);
|
|
13091
13278
|
_pools.forEach((p) => {
|
|
13092
13279
|
const flow = {
|
|
13093
13280
|
title: `Pool name: ${p.pool_name}`,
|
|
13094
13281
|
subItems: [
|
|
13095
13282
|
{ key: `APY`, value: `${(p.APY.netApy * 100).toFixed(2)}%` },
|
|
13096
|
-
{
|
|
13283
|
+
{
|
|
13284
|
+
key: "Weight",
|
|
13285
|
+
value: `${(p.current_weight * 100).toFixed(2)} / ${(p.max_weight * 100).toFixed(2)}%`
|
|
13286
|
+
}
|
|
13097
13287
|
],
|
|
13098
13288
|
linkedFlows: [],
|
|
13099
13289
|
style: p.amount.greaterThan(0) ? { backgroundColor: "#35484f" /* Blue */.valueOf() } : { color: "gray" }
|
|
@@ -13125,7 +13315,12 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13125
13315
|
harvest.actualReward.toWei(),
|
|
13126
13316
|
this.address.address
|
|
13127
13317
|
);
|
|
13128
|
-
swapInfo = await avnu.getSwapInfo(
|
|
13318
|
+
swapInfo = await avnu.getSwapInfo(
|
|
13319
|
+
quote,
|
|
13320
|
+
this.address.address,
|
|
13321
|
+
0,
|
|
13322
|
+
this.address.address
|
|
13323
|
+
);
|
|
13129
13324
|
}
|
|
13130
13325
|
return [
|
|
13131
13326
|
this.contract.populate("harvest", [
|
|
@@ -13146,16 +13341,27 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13146
13341
|
* @returns {Promise<Array<{ vToken: ContractAddr, fee: Web3Number }>>} Array of fees deducted in different vTokens
|
|
13147
13342
|
*/
|
|
13148
13343
|
async getFee(allowedPools) {
|
|
13149
|
-
const assets = Web3Number.fromWei(
|
|
13150
|
-
|
|
13151
|
-
|
|
13344
|
+
const assets = Web3Number.fromWei(
|
|
13345
|
+
(await this.contract.total_assets()).toString(),
|
|
13346
|
+
this.asset().decimals
|
|
13347
|
+
);
|
|
13348
|
+
const totalSupply = Web3Number.fromWei(
|
|
13349
|
+
(await this.contract.total_supply()).toString(),
|
|
13350
|
+
this.asset().decimals
|
|
13351
|
+
);
|
|
13352
|
+
const prevIndex = Web3Number.fromWei(
|
|
13353
|
+
(await this.contract.get_previous_index()).toString(),
|
|
13354
|
+
18
|
|
13355
|
+
);
|
|
13152
13356
|
const currIndex = new Web3Number(1, 18).multipliedBy(assets).dividedBy(totalSupply);
|
|
13153
13357
|
logger.verbose(`Previous index: ${prevIndex.toString()}`);
|
|
13154
13358
|
logger.verbose(`Assets: ${assets.toString()}`);
|
|
13155
13359
|
logger.verbose(`Total supply: ${totalSupply.toString()}`);
|
|
13156
13360
|
logger.verbose(`Current index: ${currIndex.toNumber()}`);
|
|
13157
13361
|
if (currIndex.lt(prevIndex)) {
|
|
13158
|
-
logger.verbose(
|
|
13362
|
+
logger.verbose(
|
|
13363
|
+
`getFee::Current index is less than previous index, no fees to be deducted`
|
|
13364
|
+
);
|
|
13159
13365
|
return [];
|
|
13160
13366
|
}
|
|
13161
13367
|
const indexDiff = currIndex.minus(prevIndex);
|
|
@@ -13168,7 +13374,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13168
13374
|
return [];
|
|
13169
13375
|
}
|
|
13170
13376
|
const fees = [];
|
|
13171
|
-
let remainingFee = fee.plus(
|
|
13377
|
+
let remainingFee = fee.plus(
|
|
13378
|
+
Web3Number.fromWei("100", this.asset().decimals)
|
|
13379
|
+
);
|
|
13172
13380
|
for (const pool of allowedPools) {
|
|
13173
13381
|
const vToken = pool.v_token;
|
|
13174
13382
|
const balance = pool.amount;
|
|
@@ -13177,7 +13385,9 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13177
13385
|
break;
|
|
13178
13386
|
} else {
|
|
13179
13387
|
fees.push({ vToken, fee: Web3Number.fromWei(balance.toString(), 18) });
|
|
13180
|
-
remainingFee = remainingFee.minus(
|
|
13388
|
+
remainingFee = remainingFee.minus(
|
|
13389
|
+
Web3Number.fromWei(balance.toString(), 18)
|
|
13390
|
+
);
|
|
13181
13391
|
}
|
|
13182
13392
|
}
|
|
13183
13393
|
logger.verbose(`Fees: ${JSON.stringify(fees)}`);
|
|
@@ -13185,101 +13395,179 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
|
|
|
13185
13395
|
}
|
|
13186
13396
|
};
|
|
13187
13397
|
var _description = "Automatically diversify {{TOKEN}} holdings into different Vesu pools while reducing risk and maximizing yield. Defi spring STRK Rewards are auto-compounded as well.";
|
|
13188
|
-
var _protocol = {
|
|
13398
|
+
var _protocol = {
|
|
13399
|
+
name: "Vesu",
|
|
13400
|
+
logo: "https://static-assets-8zct.onrender.com/integrations/vesu/logo.png"
|
|
13401
|
+
};
|
|
13189
13402
|
var _riskFactor = [
|
|
13190
13403
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
13191
13404
|
{ type: "Counterparty Risk" /* COUNTERPARTY_RISK */, value: 1, weight: 50 },
|
|
13192
13405
|
{ type: "Oracle Risk" /* ORACLE_RISK */, value: 0.5, weight: 25 }
|
|
13193
13406
|
];
|
|
13194
13407
|
var AUDIT_URL = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
13195
|
-
var
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13199
|
-
type: "ERC4626",
|
|
13200
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
13201
|
-
protocols: [_protocol],
|
|
13202
|
-
auditUrl: AUDIT_URL,
|
|
13203
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13204
|
-
risk: {
|
|
13205
|
-
riskFactor: _riskFactor,
|
|
13206
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13207
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13408
|
+
var faqs = [
|
|
13409
|
+
{
|
|
13410
|
+
question: "What is the Vesu Rebalancing Strategy?",
|
|
13411
|
+
answer: "The Vesu Rebalancing Strategy is an automated investment strategy that diversifies your holdings across multiple Vesu pools. It optimizes yield by rebalancing assets based on pool performance while adhering to risk constraints."
|
|
13208
13412
|
},
|
|
13209
|
-
|
|
13210
|
-
|
|
13211
|
-
|
|
13212
|
-
}, {
|
|
13213
|
-
name: "Vesu Fusion ETH",
|
|
13214
|
-
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13215
|
-
address: ContractAddr.from("0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"),
|
|
13216
|
-
type: "ERC4626",
|
|
13217
|
-
auditUrl: AUDIT_URL,
|
|
13218
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "ETH")],
|
|
13219
|
-
protocols: [_protocol],
|
|
13220
|
-
maxTVL: Web3Number.fromWei("0", 18),
|
|
13221
|
-
risk: {
|
|
13222
|
-
riskFactor: _riskFactor,
|
|
13223
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13224
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13413
|
+
{
|
|
13414
|
+
question: "Will I earn Vesu points?",
|
|
13415
|
+
answer: "Yes, of course! You will earn Vesu points for your deposits."
|
|
13225
13416
|
},
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13229
|
-
},
|
|
13230
|
-
|
|
13231
|
-
|
|
13232
|
-
|
|
13233
|
-
type: "ERC4626",
|
|
13234
|
-
auditUrl: AUDIT_URL,
|
|
13235
|
-
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "USDC")],
|
|
13236
|
-
protocols: [_protocol],
|
|
13237
|
-
maxTVL: Web3Number.fromWei("0", 6),
|
|
13238
|
-
risk: {
|
|
13239
|
-
riskFactor: _riskFactor,
|
|
13240
|
-
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13241
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
13417
|
+
{
|
|
13418
|
+
question: "How does the strategy optimize yield?",
|
|
13419
|
+
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."
|
|
13420
|
+
},
|
|
13421
|
+
{
|
|
13422
|
+
question: "What are the risks associated with this strategy?",
|
|
13423
|
+
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."
|
|
13242
13424
|
},
|
|
13243
|
-
|
|
13244
|
-
|
|
13425
|
+
{
|
|
13426
|
+
question: "How are fees calculated and deducted?",
|
|
13427
|
+
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."
|
|
13428
|
+
},
|
|
13429
|
+
{
|
|
13430
|
+
question: "What happens if a pool exceeds its maximum weight?",
|
|
13431
|
+
answer: "If a pool exceeds its maximum weight, the strategy rebalances by withdrawing excess funds and reallocating them to other pools with available capacity."
|
|
13432
|
+
},
|
|
13433
|
+
{
|
|
13434
|
+
question: "Can I withdraw my assets at any time?",
|
|
13435
|
+
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."
|
|
13436
|
+
},
|
|
13437
|
+
{
|
|
13438
|
+
question: "What happens to my Defi Spring STRK rewards?",
|
|
13439
|
+
answer: "STRK rewards are automatically harvested and reinvested into the strategy every week to maximize compounding returns."
|
|
13440
|
+
},
|
|
13441
|
+
{
|
|
13442
|
+
question: "Is the strategy audited?",
|
|
13443
|
+
answer: /* @__PURE__ */ jsxs("div", { children: [
|
|
13444
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
13445
|
+
/* @__PURE__ */ jsx("a", { href: "https://docs.strkfarm.com/p/strategies/vesu-fusion-rebalancing-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
13446
|
+
"."
|
|
13447
|
+
] })
|
|
13245
13448
|
}
|
|
13246
|
-
|
|
13247
|
-
|
|
13248
|
-
|
|
13249
|
-
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
|
|
13256
|
-
|
|
13257
|
-
|
|
13258
|
-
|
|
13449
|
+
];
|
|
13450
|
+
var VesuRebalanceStrategies = [
|
|
13451
|
+
{
|
|
13452
|
+
name: "Vesu Fusion STRK",
|
|
13453
|
+
description: _description.replace("{{TOKEN}}", "STRK"),
|
|
13454
|
+
address: ContractAddr.from(
|
|
13455
|
+
"0x7fb5bcb8525954a60fde4e8fb8220477696ce7117ef264775a1770e23571929"
|
|
13456
|
+
),
|
|
13457
|
+
launchBlock: 0,
|
|
13458
|
+
type: "ERC4626",
|
|
13459
|
+
depositTokens: [
|
|
13460
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
13461
|
+
],
|
|
13462
|
+
protocols: [_protocol],
|
|
13463
|
+
auditUrl: AUDIT_URL,
|
|
13464
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13465
|
+
risk: {
|
|
13466
|
+
riskFactor: _riskFactor,
|
|
13467
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13468
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13469
|
+
},
|
|
13470
|
+
additionalInfo: {
|
|
13471
|
+
feeBps: 1e3
|
|
13472
|
+
},
|
|
13473
|
+
faqs
|
|
13474
|
+
},
|
|
13475
|
+
{
|
|
13476
|
+
name: "Vesu Fusion ETH",
|
|
13477
|
+
description: _description.replace("{{TOKEN}}", "ETH"),
|
|
13478
|
+
address: ContractAddr.from(
|
|
13479
|
+
"0x5eaf5ee75231cecf79921ff8ded4b5ffe96be718bcb3daf206690ad1a9ad0ca"
|
|
13480
|
+
),
|
|
13481
|
+
launchBlock: 0,
|
|
13482
|
+
type: "ERC4626",
|
|
13483
|
+
auditUrl: AUDIT_URL,
|
|
13484
|
+
depositTokens: [
|
|
13485
|
+
Global.getDefaultTokens().find((t) => t.symbol === "ETH")
|
|
13486
|
+
],
|
|
13487
|
+
protocols: [_protocol],
|
|
13488
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
13489
|
+
risk: {
|
|
13490
|
+
riskFactor: _riskFactor,
|
|
13491
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13492
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13493
|
+
},
|
|
13494
|
+
additionalInfo: {
|
|
13495
|
+
feeBps: 1e3
|
|
13496
|
+
},
|
|
13497
|
+
faqs
|
|
13259
13498
|
},
|
|
13260
|
-
|
|
13261
|
-
|
|
13499
|
+
{
|
|
13500
|
+
name: "Vesu Fusion USDC",
|
|
13501
|
+
description: _description.replace("{{TOKEN}}", "USDC"),
|
|
13502
|
+
address: ContractAddr.from(
|
|
13503
|
+
"0xa858c97e9454f407d1bd7c57472fc8d8d8449a777c822b41d18e387816f29c"
|
|
13504
|
+
),
|
|
13505
|
+
launchBlock: 0,
|
|
13506
|
+
type: "ERC4626",
|
|
13507
|
+
auditUrl: AUDIT_URL,
|
|
13508
|
+
depositTokens: [
|
|
13509
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC")
|
|
13510
|
+
],
|
|
13511
|
+
protocols: [_protocol],
|
|
13512
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13513
|
+
risk: {
|
|
13514
|
+
riskFactor: _riskFactor,
|
|
13515
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13516
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13517
|
+
},
|
|
13518
|
+
additionalInfo: {
|
|
13519
|
+
feeBps: 1e3
|
|
13520
|
+
},
|
|
13521
|
+
faqs
|
|
13522
|
+
},
|
|
13523
|
+
{
|
|
13524
|
+
name: "Vesu Fusion USDT",
|
|
13525
|
+
description: _description.replace("{{TOKEN}}", "USDT"),
|
|
13526
|
+
address: ContractAddr.from(
|
|
13527
|
+
"0x115e94e722cfc4c77a2f15c4aefb0928c1c0029e5a57570df24c650cb7cec2c"
|
|
13528
|
+
),
|
|
13529
|
+
launchBlock: 0,
|
|
13530
|
+
type: "ERC4626",
|
|
13531
|
+
depositTokens: [
|
|
13532
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
13533
|
+
],
|
|
13534
|
+
auditUrl: AUDIT_URL,
|
|
13535
|
+
protocols: [_protocol],
|
|
13536
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
13537
|
+
risk: {
|
|
13538
|
+
riskFactor: _riskFactor,
|
|
13539
|
+
netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13540
|
+
notARisks: getNoRiskTags(_riskFactor)
|
|
13541
|
+
},
|
|
13542
|
+
additionalInfo: {
|
|
13543
|
+
feeBps: 1e3
|
|
13544
|
+
},
|
|
13545
|
+
faqs
|
|
13546
|
+
// }, {
|
|
13547
|
+
// name: 'Vesu Fusion WBTC',
|
|
13548
|
+
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13549
|
+
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13550
|
+
// type: 'ERC4626',
|
|
13551
|
+
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13552
|
+
// auditUrl: AUDIT_URL,
|
|
13553
|
+
// protocols: [_protocol],
|
|
13554
|
+
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13555
|
+
// risk: {
|
|
13556
|
+
// riskFactor: _riskFactor,
|
|
13557
|
+
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13558
|
+
// },
|
|
13559
|
+
// additionalInfo: {
|
|
13560
|
+
// feeBps: 1000,
|
|
13561
|
+
// },
|
|
13262
13562
|
}
|
|
13263
|
-
|
|
13264
|
-
// name: 'Vesu Fusion WBTC',
|
|
13265
|
-
// description: _description.replace('{{TOKEN}}', 'WBTC'),
|
|
13266
|
-
// address: ContractAddr.from('0x778007f8136a5b827325d21613803e796bda4d676fbe1e34aeab0b2a2ec027f'),
|
|
13267
|
-
// type: 'ERC4626',
|
|
13268
|
-
// depositTokens: [Global.getDefaultTokens().find(t => t.symbol === 'WBTC')!],
|
|
13269
|
-
// auditUrl: AUDIT_URL,
|
|
13270
|
-
// protocols: [_protocol],
|
|
13271
|
-
// maxTVL: Web3Number.fromWei('0', 8),
|
|
13272
|
-
// risk: {
|
|
13273
|
-
// riskFactor: _riskFactor,
|
|
13274
|
-
// netRisk: _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
13275
|
-
// },
|
|
13276
|
-
// additionalInfo: {
|
|
13277
|
-
// feeBps: 1000,
|
|
13278
|
-
// },
|
|
13279
|
-
}];
|
|
13563
|
+
];
|
|
13280
13564
|
|
|
13281
13565
|
// src/strategies/ekubo-cl-vault.tsx
|
|
13282
|
-
import {
|
|
13566
|
+
import {
|
|
13567
|
+
Contract as Contract6,
|
|
13568
|
+
num as num4,
|
|
13569
|
+
uint256 as uint2564
|
|
13570
|
+
} from "starknet";
|
|
13283
13571
|
|
|
13284
13572
|
// src/data/cl-vault.abi.json
|
|
13285
13573
|
var cl_vault_abi_default = [
|
|
@@ -18181,7 +18469,7 @@ var erc4626_abi_default = [
|
|
|
18181
18469
|
];
|
|
18182
18470
|
|
|
18183
18471
|
// src/strategies/ekubo-cl-vault.tsx
|
|
18184
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18472
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
18185
18473
|
var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
18186
18474
|
/**
|
|
18187
18475
|
* Creates a new VesuRebalance strategy instance.
|
|
@@ -18194,15 +18482,38 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18194
18482
|
super(config);
|
|
18195
18483
|
this.BASE_WEIGHT = 1e4;
|
|
18196
18484
|
this.pricer = pricer;
|
|
18197
|
-
assert(
|
|
18485
|
+
assert(
|
|
18486
|
+
metadata.depositTokens.length === 2,
|
|
18487
|
+
"EkuboCL only supports 2 deposit token"
|
|
18488
|
+
);
|
|
18198
18489
|
this.metadata = metadata;
|
|
18199
18490
|
this.address = metadata.address;
|
|
18200
|
-
this.contract = new Contract6(
|
|
18201
|
-
|
|
18491
|
+
this.contract = new Contract6(
|
|
18492
|
+
cl_vault_abi_default,
|
|
18493
|
+
this.address.address,
|
|
18494
|
+
this.config.provider
|
|
18495
|
+
);
|
|
18496
|
+
if (this.metadata.additionalInfo.lstContract) {
|
|
18497
|
+
this.lstContract = new Contract6(
|
|
18498
|
+
erc4626_abi_default,
|
|
18499
|
+
this.metadata.additionalInfo.lstContract.address,
|
|
18500
|
+
this.config.provider
|
|
18501
|
+
);
|
|
18502
|
+
} else {
|
|
18503
|
+
this.lstContract = null;
|
|
18504
|
+
}
|
|
18202
18505
|
const EKUBO_POSITION = "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
|
|
18203
|
-
this.ekuboPositionsContract = new Contract6(
|
|
18506
|
+
this.ekuboPositionsContract = new Contract6(
|
|
18507
|
+
ekubo_positions_abi_default,
|
|
18508
|
+
EKUBO_POSITION,
|
|
18509
|
+
this.config.provider
|
|
18510
|
+
);
|
|
18204
18511
|
const EKUBO_MATH = "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
|
|
18205
|
-
this.ekuboMathContract = new Contract6(
|
|
18512
|
+
this.ekuboMathContract = new Contract6(
|
|
18513
|
+
ekubo_math_abi_default,
|
|
18514
|
+
EKUBO_MATH,
|
|
18515
|
+
this.config.provider
|
|
18516
|
+
);
|
|
18206
18517
|
this.avnu = new AvnuWrapper();
|
|
18207
18518
|
}
|
|
18208
18519
|
async matchInputAmounts(amountInfo) {
|
|
@@ -18227,25 +18538,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18227
18538
|
/** Returns minimum amounts give given two amounts based on what can be added for liq */
|
|
18228
18539
|
async getMinDepositAmounts(amountInfo) {
|
|
18229
18540
|
const shares = await this.tokensToShares(amountInfo);
|
|
18230
|
-
const { amount0, amount1 } = await this.contract.call(
|
|
18541
|
+
const { amount0, amount1 } = await this.contract.call(
|
|
18542
|
+
"convert_to_assets",
|
|
18543
|
+
[uint2564.bnToUint256(shares.toWei())]
|
|
18544
|
+
);
|
|
18231
18545
|
return {
|
|
18232
18546
|
token0: {
|
|
18233
18547
|
tokenInfo: amountInfo.token0.tokenInfo,
|
|
18234
|
-
amount: Web3Number.fromWei(
|
|
18548
|
+
amount: Web3Number.fromWei(
|
|
18549
|
+
amount0.toString(),
|
|
18550
|
+
amountInfo.token0.tokenInfo.decimals
|
|
18551
|
+
)
|
|
18235
18552
|
},
|
|
18236
18553
|
token1: {
|
|
18237
18554
|
tokenInfo: amountInfo.token1.tokenInfo,
|
|
18238
|
-
amount: Web3Number.fromWei(
|
|
18555
|
+
amount: Web3Number.fromWei(
|
|
18556
|
+
amount1.toString(),
|
|
18557
|
+
amountInfo.token1.tokenInfo.decimals
|
|
18558
|
+
)
|
|
18239
18559
|
}
|
|
18240
18560
|
};
|
|
18241
18561
|
}
|
|
18242
18562
|
async depositCall(amountInfo, receiver) {
|
|
18243
18563
|
const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
|
|
18244
|
-
const token0Contract = new Contract6(
|
|
18245
|
-
|
|
18246
|
-
|
|
18247
|
-
|
|
18248
|
-
|
|
18564
|
+
const token0Contract = new Contract6(
|
|
18565
|
+
erc4626_abi_default,
|
|
18566
|
+
amountInfo.token0.tokenInfo.address.address,
|
|
18567
|
+
this.config.provider
|
|
18568
|
+
);
|
|
18569
|
+
const token1Contract = new Contract6(
|
|
18570
|
+
erc4626_abi_default,
|
|
18571
|
+
amountInfo.token1.tokenInfo.address.address,
|
|
18572
|
+
this.config.provider
|
|
18573
|
+
);
|
|
18574
|
+
const call1 = token0Contract.populate("approve", [
|
|
18575
|
+
this.address.address,
|
|
18576
|
+
uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei())
|
|
18577
|
+
]);
|
|
18578
|
+
const call2 = token1Contract.populate("approve", [
|
|
18579
|
+
this.address.address,
|
|
18580
|
+
uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei())
|
|
18581
|
+
]);
|
|
18582
|
+
const call3 = this.contract.populate("deposit", [
|
|
18583
|
+
uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei()),
|
|
18584
|
+
uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei()),
|
|
18585
|
+
receiver.address
|
|
18586
|
+
]);
|
|
18249
18587
|
const calls = [];
|
|
18250
18588
|
if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
|
|
18251
18589
|
if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
|
|
@@ -18260,25 +18598,29 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18260
18598
|
}
|
|
18261
18599
|
async withdrawCall(amountInfo, receiver, owner) {
|
|
18262
18600
|
const shares = await this.tokensToShares(amountInfo);
|
|
18263
|
-
logger.verbose(
|
|
18264
|
-
|
|
18265
|
-
|
|
18266
|
-
|
|
18267
|
-
|
|
18601
|
+
logger.verbose(
|
|
18602
|
+
`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`
|
|
18603
|
+
);
|
|
18604
|
+
return [
|
|
18605
|
+
this.contract.populate("withdraw", [
|
|
18606
|
+
uint2564.bnToUint256(shares.toWei()),
|
|
18607
|
+
receiver.address
|
|
18608
|
+
])
|
|
18609
|
+
];
|
|
18268
18610
|
}
|
|
18269
18611
|
rebalanceCall(newBounds, swapParams) {
|
|
18270
|
-
return [
|
|
18271
|
-
|
|
18272
|
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18612
|
+
return [
|
|
18613
|
+
this.contract.populate("rebalance", [
|
|
18614
|
+
{
|
|
18615
|
+
lower: _EkuboCLVault.tickToi129(Number(newBounds.lowerTick)),
|
|
18616
|
+
upper: _EkuboCLVault.tickToi129(Number(newBounds.upperTick))
|
|
18617
|
+
},
|
|
18618
|
+
swapParams
|
|
18619
|
+
])
|
|
18620
|
+
];
|
|
18277
18621
|
}
|
|
18278
18622
|
handleUnusedCall(swapParams) {
|
|
18279
|
-
return [this.contract.populate("handle_unused", [
|
|
18280
|
-
swapParams
|
|
18281
|
-
])];
|
|
18623
|
+
return [this.contract.populate("handle_unused", [swapParams])];
|
|
18282
18624
|
}
|
|
18283
18625
|
handleFeesCall() {
|
|
18284
18626
|
return [this.contract.populate("handle_fees", [])];
|
|
@@ -18293,16 +18635,20 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18293
18635
|
const priceNow = await this.getCurrentPrice(blockIdentifier);
|
|
18294
18636
|
let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
18295
18637
|
const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
18296
|
-
const blockBefore = blockNow - sinceBlocks;
|
|
18297
|
-
const adjustedSupplyNow = supplyNow.minus(
|
|
18298
|
-
|
|
18638
|
+
const blockBefore = Math.max(blockNow - sinceBlocks, this.metadata.launchBlock);
|
|
18639
|
+
const adjustedSupplyNow = supplyNow.minus(
|
|
18640
|
+
await this.getHarvestRewardShares(blockBefore, blockNow)
|
|
18641
|
+
);
|
|
18642
|
+
let blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
18643
|
+
blockBefore
|
|
18644
|
+
);
|
|
18299
18645
|
const tvlBefore = await this._getTVL(blockBefore);
|
|
18300
18646
|
const supplyBefore = await this.totalSupply(blockBefore);
|
|
18301
18647
|
const priceBefore = await this.getCurrentPrice(blockBefore);
|
|
18302
18648
|
const tvlInToken0Now = tvlNow.amount0.multipliedBy(priceNow.price).plus(tvlNow.amount1);
|
|
18303
|
-
const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow);
|
|
18649
|
+
const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow.toString());
|
|
18304
18650
|
const tvlInToken0Bf = tvlBefore.amount0.multipliedBy(priceBefore.price).plus(tvlBefore.amount1);
|
|
18305
|
-
const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore);
|
|
18651
|
+
const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore.toString());
|
|
18306
18652
|
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
18307
18653
|
logger.verbose(`tvlInToken0Now: ${tvlInToken0Now.toString()}`);
|
|
18308
18654
|
logger.verbose(`tvlInToken0Bf: ${tvlInToken0Bf.toString()}`);
|
|
@@ -18313,7 +18659,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18313
18659
|
logger.verbose(`Supply before: ${supplyBefore.toString()}`);
|
|
18314
18660
|
logger.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
|
|
18315
18661
|
logger.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
|
|
18316
|
-
const apyForGivenBlocks = Number(
|
|
18662
|
+
const apyForGivenBlocks = Number(
|
|
18663
|
+
tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)
|
|
18664
|
+
) / 1e4;
|
|
18317
18665
|
return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
|
|
18318
18666
|
}
|
|
18319
18667
|
async getHarvestRewardShares(fromBlock, toBlock) {
|
|
@@ -18331,25 +18679,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18331
18679
|
} else {
|
|
18332
18680
|
shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
|
|
18333
18681
|
}
|
|
18334
|
-
logger.verbose(
|
|
18682
|
+
logger.verbose(
|
|
18683
|
+
`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`
|
|
18684
|
+
);
|
|
18335
18685
|
}
|
|
18336
18686
|
return shares;
|
|
18337
18687
|
}
|
|
18338
18688
|
async balanceOf(user, blockIdentifier = "pending") {
|
|
18339
|
-
let bal = await this.contract.call("balance_of", [user.address]
|
|
18689
|
+
let bal = await this.contract.call("balance_of", [user.address], {
|
|
18690
|
+
blockIdentifier
|
|
18691
|
+
});
|
|
18340
18692
|
return Web3Number.fromWei(bal.toString(), 18);
|
|
18341
18693
|
}
|
|
18342
18694
|
async getUserTVL(user, blockIdentifier = "pending") {
|
|
18343
18695
|
let bal = await this.balanceOf(user, blockIdentifier);
|
|
18344
|
-
const assets = await this.contract.call(
|
|
18345
|
-
|
|
18346
|
-
|
|
18696
|
+
const assets = await this.contract.call(
|
|
18697
|
+
"convert_to_assets",
|
|
18698
|
+
[uint2564.bnToUint256(bal.toWei())],
|
|
18699
|
+
{
|
|
18700
|
+
blockIdentifier
|
|
18701
|
+
}
|
|
18702
|
+
);
|
|
18347
18703
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18348
18704
|
this.assertValidDepositTokens(poolKey);
|
|
18349
18705
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18350
18706
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18351
|
-
const amount0 = Web3Number.fromWei(
|
|
18352
|
-
|
|
18707
|
+
const amount0 = Web3Number.fromWei(
|
|
18708
|
+
assets.amount0.toString(),
|
|
18709
|
+
token0Info.decimals
|
|
18710
|
+
);
|
|
18711
|
+
const amount1 = Web3Number.fromWei(
|
|
18712
|
+
assets.amount1.toString(),
|
|
18713
|
+
token1Info.decimals
|
|
18714
|
+
);
|
|
18353
18715
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18354
18716
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18355
18717
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
@@ -18373,7 +18735,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18373
18735
|
blockIdentifier
|
|
18374
18736
|
});
|
|
18375
18737
|
const bounds = await this.getCurrentBounds(blockIdentifier);
|
|
18376
|
-
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18738
|
+
const { amount0, amount1 } = await this.getLiquidityToAmounts(
|
|
18739
|
+
Web3Number.fromWei(result.toString(), 18),
|
|
18740
|
+
bounds,
|
|
18741
|
+
blockIdentifier
|
|
18742
|
+
);
|
|
18377
18743
|
return { amount0, amount1 };
|
|
18378
18744
|
}
|
|
18379
18745
|
async totalSupply(blockIdentifier = "pending") {
|
|
@@ -18383,8 +18749,14 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18383
18749
|
return Web3Number.fromWei(res.toString(), 18);
|
|
18384
18750
|
}
|
|
18385
18751
|
assertValidDepositTokens(poolKey) {
|
|
18386
|
-
assert(
|
|
18387
|
-
|
|
18752
|
+
assert(
|
|
18753
|
+
poolKey.token0.eq(this.metadata.depositTokens[0].address),
|
|
18754
|
+
"Expected token0 in depositTokens[0]"
|
|
18755
|
+
);
|
|
18756
|
+
assert(
|
|
18757
|
+
poolKey.token1.eq(this.metadata.depositTokens[1].address),
|
|
18758
|
+
"Expected token1 in depositTokens[1]"
|
|
18759
|
+
);
|
|
18388
18760
|
}
|
|
18389
18761
|
async getTVL(blockIdentifier = "pending") {
|
|
18390
18762
|
const { amount0, amount1 } = await this._getTVL(blockIdentifier);
|
|
@@ -18414,26 +18786,35 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18414
18786
|
const nftID = await this.getCurrentNFTID();
|
|
18415
18787
|
const poolKey = await this.getPoolKey();
|
|
18416
18788
|
const currentBounds = await this.getCurrentBounds();
|
|
18417
|
-
const result = await this.ekuboPositionsContract.call(
|
|
18418
|
-
|
|
18419
|
-
|
|
18420
|
-
|
|
18421
|
-
|
|
18422
|
-
|
|
18423
|
-
|
|
18424
|
-
|
|
18425
|
-
|
|
18426
|
-
|
|
18427
|
-
|
|
18428
|
-
|
|
18429
|
-
|
|
18430
|
-
|
|
18789
|
+
const result = await this.ekuboPositionsContract.call(
|
|
18790
|
+
"get_token_info",
|
|
18791
|
+
[
|
|
18792
|
+
nftID,
|
|
18793
|
+
{
|
|
18794
|
+
token0: poolKey.token0.address,
|
|
18795
|
+
token1: poolKey.token1.address,
|
|
18796
|
+
fee: poolKey.fee,
|
|
18797
|
+
tick_spacing: poolKey.tick_spacing,
|
|
18798
|
+
extension: poolKey.extension
|
|
18799
|
+
},
|
|
18800
|
+
{
|
|
18801
|
+
lower: _EkuboCLVault.tickToi129(Number(currentBounds.lowerTick)),
|
|
18802
|
+
upper: _EkuboCLVault.tickToi129(Number(currentBounds.upperTick))
|
|
18803
|
+
}
|
|
18804
|
+
]
|
|
18805
|
+
);
|
|
18431
18806
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18432
18807
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18433
18808
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
18434
18809
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
18435
|
-
const token0Web3 = Web3Number.fromWei(
|
|
18436
|
-
|
|
18810
|
+
const token0Web3 = Web3Number.fromWei(
|
|
18811
|
+
result.fees0.toString(),
|
|
18812
|
+
token0Info.decimals
|
|
18813
|
+
);
|
|
18814
|
+
const token1Web3 = Web3Number.fromWei(
|
|
18815
|
+
result.fees1.toString(),
|
|
18816
|
+
token1Info.decimals
|
|
18817
|
+
);
|
|
18437
18818
|
const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
|
|
18438
18819
|
const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
|
|
18439
18820
|
return {
|
|
@@ -18455,31 +18836,52 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18455
18836
|
return Number(result.salt.toString());
|
|
18456
18837
|
}
|
|
18457
18838
|
async truePrice() {
|
|
18458
|
-
|
|
18459
|
-
|
|
18460
|
-
|
|
18839
|
+
if (this.metadata.additionalInfo.truePrice) {
|
|
18840
|
+
return this.metadata.additionalInfo.truePrice;
|
|
18841
|
+
} else if (this.lstContract) {
|
|
18842
|
+
const result = await this.lstContract.call("convert_to_assets", [
|
|
18843
|
+
uint2564.bnToUint256(BigInt(1e18).toString())
|
|
18844
|
+
]);
|
|
18845
|
+
const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
|
|
18846
|
+
return truePrice;
|
|
18847
|
+
}
|
|
18848
|
+
throw new Error("No true price available");
|
|
18461
18849
|
}
|
|
18462
18850
|
async getCurrentPrice(blockIdentifier = "pending") {
|
|
18463
18851
|
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
18464
18852
|
return this._getCurrentPrice(poolKey, blockIdentifier);
|
|
18465
18853
|
}
|
|
18466
18854
|
async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
|
|
18467
|
-
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18855
|
+
const priceInfo = await this.ekuboPositionsContract.call(
|
|
18856
|
+
"get_pool_price",
|
|
18857
|
+
[
|
|
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
|
+
],
|
|
18468
18866
|
{
|
|
18469
|
-
|
|
18470
|
-
token1: poolKey.token1.address,
|
|
18471
|
-
fee: poolKey.fee,
|
|
18472
|
-
tick_spacing: poolKey.tick_spacing,
|
|
18473
|
-
extension: poolKey.extension
|
|
18867
|
+
blockIdentifier
|
|
18474
18868
|
}
|
|
18475
|
-
|
|
18476
|
-
|
|
18477
|
-
|
|
18478
|
-
|
|
18479
|
-
console.log(
|
|
18869
|
+
);
|
|
18870
|
+
const sqrtRatio = _EkuboCLVault.div2Power128(
|
|
18871
|
+
BigInt(priceInfo.sqrt_ratio.toString())
|
|
18872
|
+
);
|
|
18873
|
+
console.log(
|
|
18874
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`
|
|
18875
|
+
);
|
|
18480
18876
|
const price = sqrtRatio * sqrtRatio;
|
|
18481
|
-
const tick = _EkuboCLVault.priceToTick(
|
|
18482
|
-
|
|
18877
|
+
const tick = _EkuboCLVault.priceToTick(
|
|
18878
|
+
price,
|
|
18879
|
+
true,
|
|
18880
|
+
Number(poolKey.tick_spacing)
|
|
18881
|
+
);
|
|
18882
|
+
console.log(
|
|
18883
|
+
`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`
|
|
18884
|
+
);
|
|
18483
18885
|
return {
|
|
18484
18886
|
price,
|
|
18485
18887
|
tick: tick.mag * (tick.sign == 0 ? 1 : -1),
|
|
@@ -18519,7 +18921,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18519
18921
|
};
|
|
18520
18922
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18521
18923
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18522
|
-
assert(
|
|
18924
|
+
assert(
|
|
18925
|
+
token0Info.decimals == token1Info.decimals,
|
|
18926
|
+
"Tested only for equal decimals"
|
|
18927
|
+
);
|
|
18523
18928
|
this.poolKey = poolKey;
|
|
18524
18929
|
return poolKey;
|
|
18525
18930
|
}
|
|
@@ -18543,11 +18948,18 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18543
18948
|
async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
|
|
18544
18949
|
assert(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
|
|
18545
18950
|
const sampleLiq = 1e20;
|
|
18546
|
-
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18547
|
-
|
|
18951
|
+
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(
|
|
18952
|
+
Web3Number.fromWei(sampleLiq.toString(), 18),
|
|
18953
|
+
bounds
|
|
18954
|
+
);
|
|
18955
|
+
logger.verbose(
|
|
18956
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`
|
|
18957
|
+
);
|
|
18548
18958
|
assert(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
|
|
18549
18959
|
const price = await (await this.getCurrentPrice()).price;
|
|
18550
|
-
logger.verbose(
|
|
18960
|
+
logger.verbose(
|
|
18961
|
+
`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`
|
|
18962
|
+
);
|
|
18551
18963
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18552
18964
|
if (sampleAmount1.eq(0)) {
|
|
18553
18965
|
return {
|
|
@@ -18577,12 +18989,22 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18577
18989
|
};
|
|
18578
18990
|
}
|
|
18579
18991
|
}
|
|
18580
|
-
assert(
|
|
18992
|
+
assert(
|
|
18993
|
+
sampleAmount0.decimals == sampleAmount1.decimals,
|
|
18994
|
+
"Sample amounts have different decimals"
|
|
18995
|
+
);
|
|
18581
18996
|
const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
|
|
18582
18997
|
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
18583
|
-
logger.verbose(
|
|
18998
|
+
logger.verbose(
|
|
18999
|
+
`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`
|
|
19000
|
+
);
|
|
18584
19001
|
if (justUseInputAmount)
|
|
18585
|
-
return this._solveExpectedAmountsEq(
|
|
19002
|
+
return this._solveExpectedAmountsEq(
|
|
19003
|
+
amount0,
|
|
19004
|
+
amount1,
|
|
19005
|
+
ratioWeb3Number,
|
|
19006
|
+
price
|
|
19007
|
+
);
|
|
18586
19008
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
18587
19009
|
const _amount1 = amount0.dividedBy(ratioWeb3Number);
|
|
18588
19010
|
return {
|
|
@@ -18598,7 +19020,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18598
19020
|
ratio
|
|
18599
19021
|
};
|
|
18600
19022
|
} else {
|
|
18601
|
-
throw new Error(
|
|
19023
|
+
throw new Error(
|
|
19024
|
+
"Both amounts are non-zero, cannot compute expected amounts"
|
|
19025
|
+
);
|
|
18602
19026
|
}
|
|
18603
19027
|
}
|
|
18604
19028
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
@@ -18615,34 +19039,65 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18615
19039
|
const erc20Mod = new ERC20(this.config);
|
|
18616
19040
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18617
19041
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18618
|
-
const token0Bal1 = await erc20Mod.balanceOf(
|
|
18619
|
-
|
|
18620
|
-
|
|
19042
|
+
const token0Bal1 = await erc20Mod.balanceOf(
|
|
19043
|
+
poolKey.token0,
|
|
19044
|
+
this.address.address,
|
|
19045
|
+
token0Info.decimals
|
|
19046
|
+
);
|
|
19047
|
+
const token1Bal1 = await erc20Mod.balanceOf(
|
|
19048
|
+
poolKey.token1,
|
|
19049
|
+
this.address.address,
|
|
19050
|
+
token1Info.decimals
|
|
19051
|
+
);
|
|
19052
|
+
logger.verbose(
|
|
19053
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`
|
|
19054
|
+
);
|
|
18621
19055
|
const token0Price = await this.pricer.getPrice(token0Info.symbol);
|
|
18622
19056
|
const token1Price = await this.pricer.getPrice(token1Info.symbol);
|
|
18623
19057
|
const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
|
|
18624
19058
|
const token1PriceUsd = token1Price.price * Number(token1Bal1.toFixed(13));
|
|
18625
19059
|
if (token0PriceUsd > 1 && token1PriceUsd > 1) {
|
|
18626
|
-
throw new Error(
|
|
19060
|
+
throw new Error(
|
|
19061
|
+
"Both tokens are non-zero and above $1, call handle_fees first"
|
|
19062
|
+
);
|
|
18627
19063
|
}
|
|
18628
19064
|
let token0Bal = token0Bal1;
|
|
18629
19065
|
let token1Bal = token1Bal1;
|
|
18630
19066
|
if (considerRebalance) {
|
|
18631
|
-
logger.verbose(
|
|
19067
|
+
logger.verbose(
|
|
19068
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`
|
|
19069
|
+
);
|
|
18632
19070
|
const tvl = await this.getTVL();
|
|
18633
19071
|
token0Bal = token0Bal.plus(tvl.token0.amount.toString());
|
|
18634
19072
|
token1Bal = token1Bal.plus(tvl.token1.amount.toString());
|
|
18635
19073
|
} else {
|
|
18636
|
-
logger.verbose(
|
|
19074
|
+
logger.verbose(
|
|
19075
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: false`
|
|
19076
|
+
);
|
|
18637
19077
|
}
|
|
18638
|
-
logger.verbose(
|
|
19078
|
+
logger.verbose(
|
|
19079
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`
|
|
19080
|
+
);
|
|
18639
19081
|
const newBounds = await this.getNewBounds();
|
|
18640
|
-
logger.verbose(
|
|
18641
|
-
|
|
19082
|
+
logger.verbose(
|
|
19083
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`
|
|
19084
|
+
);
|
|
19085
|
+
return await this.getSwapInfoGivenAmounts(
|
|
19086
|
+
poolKey,
|
|
19087
|
+
token0Bal,
|
|
19088
|
+
token1Bal,
|
|
19089
|
+
newBounds
|
|
19090
|
+
);
|
|
18642
19091
|
}
|
|
18643
19092
|
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
|
|
18644
|
-
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
18645
|
-
|
|
19093
|
+
let expectedAmounts = await this._getExpectedAmountsForLiquidity(
|
|
19094
|
+
token0Bal,
|
|
19095
|
+
token1Bal,
|
|
19096
|
+
bounds
|
|
19097
|
+
);
|
|
19098
|
+
logger.verbose(
|
|
19099
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19100
|
+
);
|
|
18646
19101
|
let retry = 0;
|
|
18647
19102
|
const maxRetry = 10;
|
|
18648
19103
|
while (retry < maxRetry) {
|
|
@@ -18659,9 +19114,15 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18659
19114
|
const remainingSellAmount = tokenToSell == poolKey.token0 ? expectedAmounts.amount0 : expectedAmounts.amount1;
|
|
18660
19115
|
const tokenToBuyInfo = await Global.getTokenInfoFromAddr(tokenToBuy);
|
|
18661
19116
|
const expectedRatio = expectedAmounts.ratio;
|
|
18662
|
-
logger.verbose(
|
|
18663
|
-
|
|
18664
|
-
|
|
19117
|
+
logger.verbose(
|
|
19118
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`
|
|
19119
|
+
);
|
|
19120
|
+
logger.verbose(
|
|
19121
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`
|
|
19122
|
+
);
|
|
19123
|
+
logger.verbose(
|
|
19124
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`
|
|
19125
|
+
);
|
|
18665
19126
|
if (amountToSell.eq(0)) {
|
|
18666
19127
|
return {
|
|
18667
19128
|
token_from_address: tokenToSell.address,
|
|
@@ -18675,23 +19136,62 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18675
19136
|
routes: []
|
|
18676
19137
|
};
|
|
18677
19138
|
}
|
|
18678
|
-
const quote = await this.avnu.getQuotes(
|
|
19139
|
+
const quote = await this.avnu.getQuotes(
|
|
19140
|
+
tokenToSell.address,
|
|
19141
|
+
tokenToBuy.address,
|
|
19142
|
+
amountToSell.toWei(),
|
|
19143
|
+
this.address.address
|
|
19144
|
+
);
|
|
18679
19145
|
if (remainingSellAmount.eq(0)) {
|
|
18680
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18681
|
-
|
|
19146
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19147
|
+
quote.buyAmount.toString(),
|
|
19148
|
+
tokenToBuyInfo.decimals
|
|
19149
|
+
).multipliedBy(0.9999);
|
|
19150
|
+
return await this.avnu.getSwapInfo(
|
|
19151
|
+
quote,
|
|
19152
|
+
this.address.address,
|
|
19153
|
+
0,
|
|
19154
|
+
this.address.address,
|
|
19155
|
+
minAmountOut.toWei()
|
|
19156
|
+
);
|
|
18682
19157
|
}
|
|
18683
|
-
const amountOut = Web3Number.fromWei(
|
|
19158
|
+
const amountOut = Web3Number.fromWei(
|
|
19159
|
+
quote.buyAmount.toString(),
|
|
19160
|
+
tokenToBuyInfo.decimals
|
|
19161
|
+
);
|
|
18684
19162
|
const swapPrice = tokenToSell == poolKey.token0 ? amountOut.dividedBy(amountToSell) : amountToSell.dividedBy(amountOut);
|
|
18685
19163
|
const newRatio = tokenToSell == poolKey.token0 ? remainingSellAmount.dividedBy(token1Bal.plus(amountOut)) : token0Bal.plus(amountOut).dividedBy(remainingSellAmount);
|
|
18686
|
-
logger.verbose(
|
|
18687
|
-
|
|
18688
|
-
|
|
19164
|
+
logger.verbose(
|
|
19165
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => amountOut: ${amountOut.toString()}`
|
|
19166
|
+
);
|
|
19167
|
+
logger.verbose(
|
|
19168
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => swapPrice: ${swapPrice.toString()}`
|
|
19169
|
+
);
|
|
19170
|
+
logger.verbose(
|
|
19171
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newRatio: ${newRatio.toString()}`
|
|
19172
|
+
);
|
|
18689
19173
|
if (Number(newRatio.toString()) > expectedRatio * 1.0000001 || Number(newRatio.toString()) < expectedRatio * 0.9999999) {
|
|
18690
|
-
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
18691
|
-
|
|
19174
|
+
expectedAmounts = await this._solveExpectedAmountsEq(
|
|
19175
|
+
token0Bal,
|
|
19176
|
+
token1Bal,
|
|
19177
|
+
new Web3Number(Number(expectedRatio).toFixed(13), 18),
|
|
19178
|
+
Number(swapPrice.toString())
|
|
19179
|
+
);
|
|
19180
|
+
logger.verbose(
|
|
19181
|
+
`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`
|
|
19182
|
+
);
|
|
18692
19183
|
} else {
|
|
18693
|
-
const minAmountOut = Web3Number.fromWei(
|
|
18694
|
-
|
|
19184
|
+
const minAmountOut = Web3Number.fromWei(
|
|
19185
|
+
quote.buyAmount.toString(),
|
|
19186
|
+
tokenToBuyInfo.decimals
|
|
19187
|
+
).multipliedBy(0.9999);
|
|
19188
|
+
return await this.avnu.getSwapInfo(
|
|
19189
|
+
quote,
|
|
19190
|
+
this.address.address,
|
|
19191
|
+
0,
|
|
19192
|
+
this.address.address,
|
|
19193
|
+
minAmountOut.toWei()
|
|
19194
|
+
);
|
|
18695
19195
|
}
|
|
18696
19196
|
retry++;
|
|
18697
19197
|
}
|
|
@@ -18700,8 +19200,8 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18700
19200
|
/**
|
|
18701
19201
|
* Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
|
|
18702
19202
|
* Uses binary search approach to find optimal swap amount.
|
|
18703
|
-
*
|
|
18704
|
-
* @param newBounds - The new tick bounds to rebalance to
|
|
19203
|
+
*
|
|
19204
|
+
* @param newBounds - The new tick bounds to rebalance to
|
|
18705
19205
|
* @param swapInfo - Initial swap parameters for rebalancing
|
|
18706
19206
|
* @param acc - Account to estimate gas fees with
|
|
18707
19207
|
* @param retry - Current retry attempt number (default 0)
|
|
@@ -18728,7 +19228,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18728
19228
|
logger.error(`Rebalance failed after ${MAX_RETRIES} retries`);
|
|
18729
19229
|
throw err;
|
|
18730
19230
|
}
|
|
18731
|
-
logger.error(
|
|
19231
|
+
logger.error(
|
|
19232
|
+
`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`
|
|
19233
|
+
);
|
|
18732
19234
|
const newSwapInfo = { ...swapInfo };
|
|
18733
19235
|
const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
|
|
18734
19236
|
logger.verbose(`Current amount: ${currentAmount.toString()}`);
|
|
@@ -18820,23 +19322,39 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18820
19322
|
const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
|
|
18821
19323
|
const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
|
|
18822
19324
|
const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
|
|
18823
|
-
logger.verbose(
|
|
18824
|
-
|
|
18825
|
-
|
|
19325
|
+
logger.verbose(
|
|
19326
|
+
`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`
|
|
19327
|
+
);
|
|
19328
|
+
const result = await this.ekuboMathContract.call(
|
|
19329
|
+
"liquidity_delta_to_amount_delta",
|
|
19330
|
+
[
|
|
19331
|
+
uint2564.bnToUint256(currentPrice.sqrtRatio),
|
|
19332
|
+
{
|
|
19333
|
+
mag: liquidity.toWei(),
|
|
19334
|
+
sign: 0
|
|
19335
|
+
},
|
|
19336
|
+
uint2564.bnToUint256(
|
|
19337
|
+
_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()
|
|
19338
|
+
),
|
|
19339
|
+
uint2564.bnToUint256(
|
|
19340
|
+
_EkuboCLVault.priceToSqrtRatio(upperPrice).toString()
|
|
19341
|
+
)
|
|
19342
|
+
],
|
|
18826
19343
|
{
|
|
18827
|
-
|
|
18828
|
-
|
|
18829
|
-
|
|
18830
|
-
uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
|
|
18831
|
-
uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
|
|
18832
|
-
], {
|
|
18833
|
-
blockIdentifier
|
|
18834
|
-
});
|
|
19344
|
+
blockIdentifier
|
|
19345
|
+
}
|
|
19346
|
+
);
|
|
18835
19347
|
const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
|
|
18836
19348
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18837
19349
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
18838
|
-
const amount0 = Web3Number.fromWei(
|
|
18839
|
-
|
|
19350
|
+
const amount0 = Web3Number.fromWei(
|
|
19351
|
+
_EkuboCLVault.i129ToNumber(result.amount0).toString(),
|
|
19352
|
+
token0Info.decimals
|
|
19353
|
+
);
|
|
19354
|
+
const amount1 = Web3Number.fromWei(
|
|
19355
|
+
_EkuboCLVault.i129ToNumber(result.amount1).toString(),
|
|
19356
|
+
token1Info.decimals
|
|
19357
|
+
);
|
|
18840
19358
|
return {
|
|
18841
19359
|
amount0,
|
|
18842
19360
|
amount1
|
|
@@ -18844,7 +19362,9 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18844
19362
|
}
|
|
18845
19363
|
async harvest(acc) {
|
|
18846
19364
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
18847
|
-
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19365
|
+
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
19366
|
+
this.address
|
|
19367
|
+
);
|
|
18848
19368
|
const poolKey = await this.getPoolKey();
|
|
18849
19369
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
18850
19370
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -18854,18 +19374,38 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18854
19374
|
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
18855
19375
|
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
18856
19376
|
const isToken1 = claim.token.eq(poolKey.token1);
|
|
18857
|
-
logger.verbose(
|
|
19377
|
+
logger.verbose(
|
|
19378
|
+
`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
|
|
19379
|
+
);
|
|
18858
19380
|
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
18859
19381
|
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
18860
|
-
logger.verbose(
|
|
18861
|
-
|
|
19382
|
+
logger.verbose(
|
|
19383
|
+
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
|
|
19384
|
+
);
|
|
19385
|
+
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
19386
|
+
poolKey,
|
|
19387
|
+
token0Amt,
|
|
19388
|
+
token1Amt,
|
|
19389
|
+
bounds
|
|
19390
|
+
);
|
|
18862
19391
|
swapInfo.token_to_address = token0Info.address.address;
|
|
18863
|
-
logger.verbose(
|
|
18864
|
-
|
|
19392
|
+
logger.verbose(
|
|
19393
|
+
`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
19394
|
+
);
|
|
19395
|
+
logger.verbose(
|
|
19396
|
+
`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
19397
|
+
);
|
|
18865
19398
|
const harvestEstimateCall = async (swapInfo1) => {
|
|
18866
|
-
const swap1Amount = Web3Number.fromWei(
|
|
19399
|
+
const swap1Amount = Web3Number.fromWei(
|
|
19400
|
+
uint2564.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
19401
|
+
18
|
|
19402
|
+
// cause its always STRK?
|
|
19403
|
+
);
|
|
18867
19404
|
const remainingAmount = postFeeAmount.minus(swap1Amount);
|
|
18868
|
-
const swapInfo2 = {
|
|
19405
|
+
const swapInfo2 = {
|
|
19406
|
+
...swapInfo,
|
|
19407
|
+
token_from_amount: uint2564.bnToUint256(remainingAmount.toWei())
|
|
19408
|
+
};
|
|
18869
19409
|
swapInfo2.token_to_address = token1Info.address.address;
|
|
18870
19410
|
const calldata = [
|
|
18871
19411
|
claim.rewardsContract.address,
|
|
@@ -18878,11 +19418,23 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18878
19418
|
swapInfo,
|
|
18879
19419
|
swapInfo2
|
|
18880
19420
|
];
|
|
18881
|
-
logger.verbose(
|
|
19421
|
+
logger.verbose(
|
|
19422
|
+
`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
19423
|
+
calldata
|
|
19424
|
+
)}`
|
|
19425
|
+
);
|
|
18882
19426
|
return [this.contract.populate("harvest", calldata)];
|
|
18883
19427
|
};
|
|
18884
|
-
const _callsFinal = await this.rebalanceIter(
|
|
18885
|
-
|
|
19428
|
+
const _callsFinal = await this.rebalanceIter(
|
|
19429
|
+
swapInfo,
|
|
19430
|
+
acc,
|
|
19431
|
+
harvestEstimateCall
|
|
19432
|
+
);
|
|
19433
|
+
logger.verbose(
|
|
19434
|
+
`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|
|
19435
|
+
_callsFinal
|
|
19436
|
+
)}`
|
|
19437
|
+
);
|
|
18886
19438
|
calls.push(..._callsFinal);
|
|
18887
19439
|
}
|
|
18888
19440
|
return calls;
|
|
@@ -18892,24 +19444,37 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18892
19444
|
const poolKey = await this.getPoolKey();
|
|
18893
19445
|
const linkedFlow = {
|
|
18894
19446
|
title: this.metadata.name,
|
|
18895
|
-
subItems: [
|
|
19447
|
+
subItems: [
|
|
19448
|
+
{
|
|
19449
|
+
key: "Pool",
|
|
19450
|
+
value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing`
|
|
19451
|
+
}
|
|
19452
|
+
],
|
|
18896
19453
|
linkedFlows: [],
|
|
18897
19454
|
style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
|
|
18898
19455
|
};
|
|
18899
19456
|
const baseFlow = {
|
|
18900
19457
|
id: "base",
|
|
18901
19458
|
title: "Your Deposit",
|
|
18902
|
-
subItems: [
|
|
19459
|
+
subItems: [
|
|
19460
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
19461
|
+
{
|
|
19462
|
+
key: `Performance Fee`,
|
|
19463
|
+
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`
|
|
19464
|
+
}
|
|
19465
|
+
],
|
|
18903
19466
|
linkedFlows: [linkedFlow],
|
|
18904
19467
|
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
18905
19468
|
};
|
|
18906
19469
|
const rebalanceFlow = {
|
|
18907
19470
|
id: "rebalance",
|
|
18908
19471
|
title: "Automated Rebalance",
|
|
18909
|
-
subItems: [
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
19472
|
+
subItems: [
|
|
19473
|
+
{
|
|
19474
|
+
key: "Range selection",
|
|
19475
|
+
value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
|
|
19476
|
+
}
|
|
19477
|
+
],
|
|
18913
19478
|
linkedFlows: [linkedFlow],
|
|
18914
19479
|
style: { backgroundColor: "purple" /* Green */.valueOf() }
|
|
18915
19480
|
};
|
|
@@ -18917,43 +19482,145 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
18917
19482
|
}
|
|
18918
19483
|
};
|
|
18919
19484
|
var _description2 = "Deploys your {{POOL_NAME}} into an Ekubo liquidity pool, automatically rebalancing positions around the current price to optimize yield and reduce the need for manual adjustments. Trading fees and DeFi Spring rewards are automatically compounded back into the strategy. In return, you receive an ERC-20 token representing your share of the strategy. The APY is calculated based on 7-day historical performance.";
|
|
18920
|
-
var _protocol2 = {
|
|
19485
|
+
var _protocol2 = {
|
|
19486
|
+
name: "Ekubo",
|
|
19487
|
+
logo: "https://app.ekubo.org/favicon.ico"
|
|
19488
|
+
};
|
|
18921
19489
|
var _riskFactor2 = [
|
|
18922
19490
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
18923
19491
|
{ type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
|
|
18924
19492
|
];
|
|
19493
|
+
var _riskFactorStable = [
|
|
19494
|
+
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 }
|
|
19495
|
+
];
|
|
18925
19496
|
var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
18926
|
-
var
|
|
18927
|
-
|
|
18928
|
-
|
|
18929
|
-
|
|
18930
|
-
|
|
18931
|
-
|
|
18932
|
-
|
|
19497
|
+
var faqs2 = [
|
|
19498
|
+
{
|
|
19499
|
+
question: "What is the Ekubo CL Vault strategy?",
|
|
19500
|
+
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."
|
|
19501
|
+
},
|
|
19502
|
+
{
|
|
19503
|
+
question: "How are trading fees and rewards handled?",
|
|
19504
|
+
answer: "Trading fees and DeFi Spring rewards are automatically compounded back into the strategy, increasing your overall returns."
|
|
19505
|
+
},
|
|
19506
|
+
{
|
|
19507
|
+
question: "What happens during withdrawal?",
|
|
19508
|
+
answer: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices."
|
|
19509
|
+
},
|
|
19510
|
+
{
|
|
19511
|
+
question: "Is the strategy audited?",
|
|
19512
|
+
answer: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19513
|
+
"Yes, the strategy has been audited. You can review the audit report in our docs ",
|
|
19514
|
+
/* @__PURE__ */ jsx2("a", { href: "https://docs.strkfarm.com/p/ekubo-cl-vaults#technical-details", style: { textDecoration: "underline", marginLeft: "5px" }, children: "Here" }),
|
|
19515
|
+
"."
|
|
18933
19516
|
] })
|
|
18934
|
-
|
|
18935
|
-
|
|
18936
|
-
|
|
18937
|
-
|
|
18938
|
-
|
|
18939
|
-
|
|
18940
|
-
|
|
18941
|
-
|
|
18942
|
-
|
|
18943
|
-
|
|
18944
|
-
|
|
18945
|
-
|
|
19517
|
+
}
|
|
19518
|
+
];
|
|
19519
|
+
var EkuboCLVaultStrategies = [
|
|
19520
|
+
{
|
|
19521
|
+
name: "Ekubo xSTRK/STRK",
|
|
19522
|
+
description: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19523
|
+
/* @__PURE__ */ jsx2("p", { children: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK") }),
|
|
19524
|
+
/* @__PURE__ */ jsxs2(
|
|
19525
|
+
"ul",
|
|
19526
|
+
{
|
|
19527
|
+
style: {
|
|
19528
|
+
marginLeft: "20px",
|
|
19529
|
+
listStyle: "circle",
|
|
19530
|
+
fontSize: "12px"
|
|
19531
|
+
},
|
|
19532
|
+
children: [
|
|
19533
|
+
/* @__PURE__ */ jsx2("li", { style: { marginTop: "10px" }, children: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices." }),
|
|
19534
|
+
/* @__PURE__ */ jsx2("li", { style: { marginTop: "10px" }, children: "Sometimes you might see a negative APY \u2014 this is usually not a big deal. It happens when xSTRK's price drops on DEXes, but things typically bounce back within a few days or a week." })
|
|
19535
|
+
]
|
|
19536
|
+
}
|
|
19537
|
+
)
|
|
19538
|
+
] }),
|
|
19539
|
+
address: ContractAddr.from(
|
|
19540
|
+
"0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"
|
|
19541
|
+
),
|
|
19542
|
+
launchBlock: 1209881,
|
|
19543
|
+
type: "Other",
|
|
19544
|
+
// must be same order as poolKey token0 and token1
|
|
19545
|
+
depositTokens: [
|
|
19546
|
+
Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"),
|
|
19547
|
+
Global.getDefaultTokens().find((t) => t.symbol === "STRK")
|
|
19548
|
+
],
|
|
19549
|
+
protocols: [_protocol2],
|
|
19550
|
+
auditUrl: AUDIT_URL2,
|
|
19551
|
+
maxTVL: Web3Number.fromWei("0", 18),
|
|
19552
|
+
risk: {
|
|
19553
|
+
riskFactor: _riskFactor2,
|
|
19554
|
+
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19555
|
+
notARisks: getNoRiskTags(_riskFactor2)
|
|
19556
|
+
},
|
|
19557
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19558
|
+
additionalInfo: {
|
|
19559
|
+
newBounds: {
|
|
19560
|
+
lower: -1,
|
|
19561
|
+
upper: 1
|
|
19562
|
+
},
|
|
19563
|
+
lstContract: ContractAddr.from(
|
|
19564
|
+
"0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"
|
|
19565
|
+
),
|
|
19566
|
+
feeBps: 1e3
|
|
19567
|
+
},
|
|
19568
|
+
faqs: [
|
|
19569
|
+
...faqs2,
|
|
19570
|
+
{
|
|
19571
|
+
question: "Why might I see a negative APY?",
|
|
19572
|
+
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."
|
|
19573
|
+
}
|
|
19574
|
+
]
|
|
18946
19575
|
},
|
|
18947
|
-
|
|
18948
|
-
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
|
|
19576
|
+
{
|
|
19577
|
+
name: "Ekubo USDC/USDT",
|
|
19578
|
+
description: /* @__PURE__ */ jsxs2("div", { children: [
|
|
19579
|
+
/* @__PURE__ */ jsx2("p", { children: _description2.replace("{{POOL_NAME}}", "USDC/USDT") }),
|
|
19580
|
+
/* @__PURE__ */ jsx2(
|
|
19581
|
+
"ul",
|
|
19582
|
+
{
|
|
19583
|
+
style: {
|
|
19584
|
+
marginLeft: "20px",
|
|
19585
|
+
listStyle: "circle",
|
|
19586
|
+
fontSize: "12px"
|
|
19587
|
+
},
|
|
19588
|
+
children: /* @__PURE__ */ jsx2("li", { style: { marginTop: "10px" }, children: "During withdrawal, you may receive either or both tokens depending on market conditions and prevailing prices." })
|
|
19589
|
+
}
|
|
19590
|
+
)
|
|
19591
|
+
] }),
|
|
19592
|
+
address: ContractAddr.from(
|
|
19593
|
+
"0xd647ed735f0db52f2a5502b6e06ed21dc4284a43a36af4b60d3c80fbc56c91"
|
|
19594
|
+
),
|
|
19595
|
+
launchBlock: 1385576,
|
|
19596
|
+
type: "Other",
|
|
19597
|
+
// must be same order as poolKey token0 and token1
|
|
19598
|
+
depositTokens: [
|
|
19599
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDC"),
|
|
19600
|
+
Global.getDefaultTokens().find((t) => t.symbol === "USDT")
|
|
19601
|
+
],
|
|
19602
|
+
protocols: [_protocol2],
|
|
19603
|
+
auditUrl: AUDIT_URL2,
|
|
19604
|
+
maxTVL: Web3Number.fromWei("0", 6),
|
|
19605
|
+
risk: {
|
|
19606
|
+
riskFactor: _riskFactorStable,
|
|
19607
|
+
netRisk: _riskFactorStable.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactorStable.reduce((acc, curr) => acc + curr.weight, 0),
|
|
19608
|
+
notARisks: getNoRiskTags(_riskFactorStable)
|
|
18952
19609
|
},
|
|
18953
|
-
|
|
18954
|
-
|
|
19610
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
19611
|
+
additionalInfo: {
|
|
19612
|
+
newBounds: {
|
|
19613
|
+
lower: -1,
|
|
19614
|
+
upper: 1
|
|
19615
|
+
},
|
|
19616
|
+
truePrice: 1,
|
|
19617
|
+
feeBps: 1e3
|
|
19618
|
+
},
|
|
19619
|
+
faqs: [
|
|
19620
|
+
...faqs2
|
|
19621
|
+
]
|
|
18955
19622
|
}
|
|
18956
|
-
|
|
19623
|
+
];
|
|
18957
19624
|
|
|
18958
19625
|
// src/notifs/telegram.ts
|
|
18959
19626
|
import TelegramBot from "node-telegram-bot-api";
|