@strkfarm/sdk 1.0.27 → 1.0.29

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/index.mjs CHANGED
@@ -18,19 +18,19 @@ var _Web3Number = class extends BigNumber {
18
18
  return this.mul(10 ** this.decimals).toFixed(0);
19
19
  }
20
20
  multipliedBy(value) {
21
- let _value = Number(value).toFixed(this.maxToFixedDecimals());
21
+ const _value = this.getStandardString(value);
22
22
  return this.construct(this.mul(_value).toString(), this.decimals);
23
23
  }
24
24
  dividedBy(value) {
25
- let _value = Number(value).toFixed(this.maxToFixedDecimals());
25
+ const _value = this.getStandardString(value);
26
26
  return this.construct(this.div(_value).toString(), this.decimals);
27
27
  }
28
28
  plus(value) {
29
- const _value = Number(value).toFixed(this.maxToFixedDecimals());
29
+ const _value = this.getStandardString(value);
30
30
  return this.construct(this.add(_value).toString(), this.decimals);
31
31
  }
32
32
  minus(n, base) {
33
- const _value = Number(n).toFixed(this.maxToFixedDecimals());
33
+ const _value = this.getStandardString(n);
34
34
  return this.construct(super.minus(_value, base).toString(), this.decimals);
35
35
  }
36
36
  construct(value, decimals) {
@@ -46,11 +46,17 @@ var _Web3Number = class extends BigNumber {
46
46
  return this.toString();
47
47
  }
48
48
  maxToFixedDecimals() {
49
- return Math.min(this.decimals, 13);
49
+ return Math.min(this.decimals, 18);
50
+ }
51
+ getStandardString(value) {
52
+ if (typeof value == "string") {
53
+ return value;
54
+ }
55
+ return value.toFixed(this.maxToFixedDecimals());
50
56
  }
51
57
  };
52
- BigNumber.config({ DECIMAL_PLACES: 18 });
53
- _Web3Number.config({ DECIMAL_PLACES: 18 });
58
+ BigNumber.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN });
59
+ _Web3Number.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN });
54
60
 
55
61
  // src/dataTypes/bignumber.node.ts
56
62
  var Web3Number = class _Web3Number2 extends _Web3Number {
@@ -122,42 +128,48 @@ var defaultTokens = [{
122
128
  logo: "https://assets.coingecko.com/coins/images/26433/small/starknet.png",
123
129
  address: ContractAddr.from("0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"),
124
130
  decimals: 18,
125
- coingeckId: "starknet"
131
+ coingeckId: "starknet",
132
+ displayDecimals: 2
126
133
  }, {
127
134
  name: "xSTRK",
128
135
  symbol: "xSTRK",
129
136
  logo: "https://dashboard.endur.fi/endur-fi.svg",
130
137
  address: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"),
131
138
  decimals: 18,
132
- coingeckId: void 0
139
+ coingeckId: void 0,
140
+ displayDecimals: 2
133
141
  }, {
134
142
  name: "ETH",
135
143
  symbol: "ETH",
136
144
  logo: "https://opbnb.bscscan.com/token/images/ether.svg",
137
145
  address: ContractAddr.from("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
138
146
  decimals: 18,
139
- coingeckId: void 0
147
+ coingeckId: void 0,
148
+ displayDecimals: 4
140
149
  }, {
141
150
  name: "USDC",
142
151
  symbol: "USDC",
143
152
  logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
144
153
  address: ContractAddr.from("0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8"),
145
154
  decimals: 6,
146
- coingeckId: void 0
155
+ coingeckId: void 0,
156
+ displayDecimals: 2
147
157
  }, {
148
158
  name: "USDT",
149
159
  symbol: "USDT",
150
160
  logo: "https://assets.coingecko.com/coins/images/325/small/Tether.png",
151
161
  address: ContractAddr.from("0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8"),
152
162
  decimals: 6,
153
- coingeckId: void 0
163
+ coingeckId: void 0,
164
+ displayDecimals: 2
154
165
  }, {
155
166
  name: "WBTC",
156
167
  symbol: "WBTC",
157
168
  logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png",
158
169
  address: ContractAddr.from("0x3fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac"),
159
170
  decimals: 8,
160
- coingeckId: void 0
171
+ coingeckId: void 0,
172
+ displayDecimals: 6
161
173
  }];
162
174
  var tokens = defaultTokens;
163
175
  var Global = class _Global {
@@ -189,7 +201,8 @@ var Global = class _Global {
189
201
  address: ContractAddr.from(token.address),
190
202
  decimals: token.decimals,
191
203
  logo: token.logoUri,
192
- coingeckId: token.extensions.coingeckoId
204
+ coingeckId: token.extensions.coingeckoId,
205
+ displayDecimals: 2
193
206
  });
194
207
  });
195
208
  console.log(tokens);
@@ -573,7 +586,8 @@ var _ZkLend = class _ZkLend extends ILending {
573
586
  logo: "",
574
587
  decimals: pool.token.decimals,
575
588
  borrowFactor: Web3Number2.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
576
- collareralFactor
589
+ collareralFactor,
590
+ displayDecimals: 2
577
591
  };
578
592
  this.tokens.push(token);
579
593
  });
@@ -690,7 +704,7 @@ var PricerFromApi = class extends PricerBase {
690
704
  try {
691
705
  return await this.getPriceFromMyAPI(tokenSymbol);
692
706
  } catch (e) {
693
- logger.warn("getPriceFromMyAPI error", e);
707
+ logger.warn("getPriceFromMyAPI error", JSON.stringify(e.message || e));
694
708
  }
695
709
  logger.log("getPrice coinbase", tokenSymbol);
696
710
  let retry = 0;
@@ -710,7 +724,7 @@ var PricerFromApi = class extends PricerBase {
710
724
  timestamp: /* @__PURE__ */ new Date()
711
725
  };
712
726
  } catch (e) {
713
- logger.warn("getPrice coinbase error", e, retry);
727
+ logger.warn("getPrice coinbase error", JSON.stringify(e.message || e));
714
728
  await new Promise((resolve) => setTimeout(resolve, retry * 1e3));
715
729
  }
716
730
  }
@@ -724,9 +738,6 @@ var PricerFromApi = class extends PricerBase {
724
738
  const priceInfo = await priceInfoRes.json();
725
739
  const now = /* @__PURE__ */ new Date();
726
740
  const priceTime = new Date(priceInfo.timestamp);
727
- if (now.getTime() - priceTime.getTime() > 9e5) {
728
- throw new Error("Price is stale");
729
- }
730
741
  const price = Number(priceInfo.price);
731
742
  return {
732
743
  price,
@@ -1890,17 +1901,28 @@ function assert(condition, message) {
1890
1901
  }
1891
1902
 
1892
1903
  // src/modules/avnu.ts
1893
- var AvnuWrapper = class {
1894
- async getQuotes(fromToken, toToken, amountWei, taker) {
1904
+ var AvnuWrapper = class _AvnuWrapper {
1905
+ async getQuotes(fromToken, toToken, amountWei, taker, retry = 0) {
1906
+ const MAX_RETRY = 5;
1907
+ logger.verbose(`${_AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
1895
1908
  const params = {
1896
1909
  sellTokenAddress: fromToken,
1897
1910
  buyTokenAddress: toToken,
1898
1911
  sellAmount: amountWei,
1899
- takerAddress: taker
1912
+ takerAddress: taker,
1913
+ // excludeSources: ['Nostra', 'Haiko(Solvers)']
1914
+ excludeSources: ["Haiko(Solvers)"]
1915
+ // to resolve InvalidOraclePrice error
1900
1916
  };
1901
1917
  assert(fromToken != toToken, "From and to tokens are the same");
1902
1918
  const quotes = await fetchQuotes(params);
1903
- assert(quotes.length > 0, "No quotes found");
1919
+ if (quotes.length == 0) {
1920
+ if (retry < MAX_RETRY) {
1921
+ await new Promise((res) => setTimeout(res, 3e3));
1922
+ return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
1923
+ }
1924
+ throw new Error("no quotes found");
1925
+ }
1904
1926
  return quotes[0];
1905
1927
  }
1906
1928
  async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
@@ -3573,10 +3595,10 @@ var BaseStrategy = class {
3573
3595
  async getTVL() {
3574
3596
  throw new Error("Not implemented");
3575
3597
  }
3576
- depositCall(amountInfo, receiver) {
3598
+ async depositCall(amountInfo, receiver) {
3577
3599
  throw new Error("Not implemented");
3578
3600
  }
3579
- withdrawCall(amountInfo, receiver, owner) {
3601
+ async withdrawCall(amountInfo, receiver, owner) {
3580
3602
  throw new Error("Not implemented");
3581
3603
  }
3582
3604
  };
@@ -3613,7 +3635,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
3613
3635
  * @param receiver - Address that will receive the strategy tokens
3614
3636
  * @returns Populated contract call for deposit
3615
3637
  */
3616
- depositCall(amountInfo, receiver) {
3638
+ async depositCall(amountInfo, receiver) {
3617
3639
  assert(amountInfo.tokenInfo.address.eq(this.asset().address), "Deposit token mismatch");
3618
3640
  const assetContract = new Contract4(vesu_rebalance_abi_default, this.asset().address.address, this.config.provider);
3619
3641
  const call1 = assetContract.populate("approve", [this.address.address, uint2563.bnToUint256(amountInfo.amount.toWei())]);
@@ -3627,7 +3649,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
3627
3649
  * @param owner - Address that owns the strategy tokens
3628
3650
  * @returns Populated contract call for withdrawal
3629
3651
  */
3630
- withdrawCall(amountInfo, receiver, owner) {
3652
+ async withdrawCall(amountInfo, receiver, owner) {
3631
3653
  return [this.contract.populate("withdraw", [uint2563.bnToUint256(amountInfo.amount.toWei()), receiver.address, owner.address])];
3632
3654
  }
3633
3655
  /**
@@ -3914,7 +3936,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
3914
3936
  const netYield = await this.netAPYGivenPools(pools);
3915
3937
  const baseFlow = {
3916
3938
  title: "Your Deposit",
3917
- subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }],
3939
+ subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }, { key: `Performance Fee`, value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%` }],
3918
3940
  linkedFlows: [],
3919
3941
  style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
3920
3942
  };
@@ -4030,7 +4052,7 @@ var VesuRebalanceStrategies = [{
4030
4052
  }];
4031
4053
 
4032
4054
  // src/strategies/ekubo-cl-vault.ts
4033
- import { Contract as Contract5, uint256 as uint2564 } from "starknet";
4055
+ import { Contract as Contract6, num as num3, uint256 as uint2564 } from "starknet";
4034
4056
 
4035
4057
  // src/data/cl-vault.abi.json
4036
4058
  var cl_vault_abi_default = [
@@ -8931,6 +8953,58 @@ var erc4626_abi_default = [
8931
8953
  }
8932
8954
  ];
8933
8955
 
8956
+ // src/modules/harvests.ts
8957
+ import { Contract as Contract5 } from "starknet";
8958
+ var Harvests = class _Harvests {
8959
+ constructor(config) {
8960
+ this.config = config;
8961
+ }
8962
+ getHarvests(addr) {
8963
+ throw new Error("Not implemented");
8964
+ }
8965
+ async getUnHarvestedRewards(addr) {
8966
+ const rewards = await this.getHarvests(addr);
8967
+ if (rewards.length == 0) return [];
8968
+ const unClaimed = [];
8969
+ const cls = await this.config.provider.getClassAt(rewards[0].rewardsContract.address);
8970
+ for (let reward of rewards) {
8971
+ const contract = new Contract5(cls.abi, reward.rewardsContract.address, this.config.provider);
8972
+ const isClaimed = await contract.call("is_claimed", [reward.claim.id]);
8973
+ logger.verbose(`${_Harvests.name}: isClaimed: ${isClaimed}`);
8974
+ if (isClaimed)
8975
+ return unClaimed;
8976
+ unClaimed.unshift(reward);
8977
+ }
8978
+ return unClaimed;
8979
+ }
8980
+ };
8981
+ var EkuboHarvests = class extends Harvests {
8982
+ async getHarvests(addr) {
8983
+ const STRK = "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
8984
+ const EKUBO_API = `https://starknet-mainnet-api.ekubo.org/airdrops/${addr.address}?token=${STRK}`;
8985
+ const resultEkubo = await fetch(EKUBO_API);
8986
+ const items = await resultEkubo.json();
8987
+ const rewards = [];
8988
+ for (let i = 0; i < items.length; ++i) {
8989
+ const info = items[i];
8990
+ assert(info.token == STRK, "expected strk token only");
8991
+ rewards.push({
8992
+ rewardsContract: ContractAddr.from(info.contract_address),
8993
+ token: ContractAddr.from(STRK),
8994
+ startDate: new Date(info.start_date),
8995
+ endDate: new Date(info.end_date),
8996
+ claim: {
8997
+ id: info.claim.id,
8998
+ amount: Web3Number.fromWei(info.claim.amount, 18),
8999
+ claimee: ContractAddr.from(info.claim.claimee)
9000
+ },
9001
+ proof: info.proof
9002
+ });
9003
+ }
9004
+ return rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
9005
+ }
9006
+ };
9007
+
8934
9008
  // src/strategies/ekubo-cl-vault.ts
8935
9009
  var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
8936
9010
  /**
@@ -8947,19 +9021,74 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
8947
9021
  assert(metadata.depositTokens.length === 2, "EkuboCL only supports 2 deposit token");
8948
9022
  this.metadata = metadata;
8949
9023
  this.address = metadata.address;
8950
- this.contract = new Contract5(cl_vault_abi_default, this.address.address, this.config.provider);
8951
- this.lstContract = new Contract5(erc4626_abi_default, this.metadata.additionalInfo.lstContract.address, this.config.provider);
9024
+ this.contract = new Contract6(cl_vault_abi_default, this.address.address, this.config.provider);
9025
+ this.lstContract = new Contract6(erc4626_abi_default, this.metadata.additionalInfo.lstContract.address, this.config.provider);
8952
9026
  const EKUBO_POSITION = "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
8953
- this.ekuboPositionsContract = new Contract5(ekubo_positions_abi_default, EKUBO_POSITION, this.config.provider);
9027
+ this.ekuboPositionsContract = new Contract6(ekubo_positions_abi_default, EKUBO_POSITION, this.config.provider);
8954
9028
  const EKUBO_MATH = "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
8955
- this.ekuboMathContract = new Contract5(ekubo_math_abi_default, EKUBO_MATH, this.config.provider);
9029
+ this.ekuboMathContract = new Contract6(ekubo_math_abi_default, EKUBO_MATH, this.config.provider);
8956
9030
  this.avnu = new AvnuWrapper();
8957
9031
  }
8958
- depositCall(amountInfo, receiver) {
8959
- return [];
9032
+ async matchInputAmounts(amountInfo) {
9033
+ const bounds = await this.getCurrentBounds();
9034
+ const res = await this._getExpectedAmountsForLiquidity(
9035
+ amountInfo.token0.amount,
9036
+ amountInfo.token1.amount,
9037
+ bounds,
9038
+ false
9039
+ );
9040
+ return {
9041
+ token0: {
9042
+ tokenInfo: amountInfo.token0.tokenInfo,
9043
+ amount: res.amount0
9044
+ },
9045
+ token1: {
9046
+ tokenInfo: amountInfo.token1.tokenInfo,
9047
+ amount: res.amount1
9048
+ }
9049
+ };
8960
9050
  }
8961
- withdrawCall(amountInfo, receiver, owner) {
8962
- return [];
9051
+ /** Returns minimum amounts give given two amounts based on what can be added for liq */
9052
+ async getMinDepositAmounts(amountInfo) {
9053
+ const shares = await this.tokensToShares(amountInfo);
9054
+ const { amount0, amount1 } = await this.contract.call("convert_to_assets", [uint2564.bnToUint256(shares.toWei())]);
9055
+ return {
9056
+ token0: {
9057
+ tokenInfo: amountInfo.token0.tokenInfo,
9058
+ amount: Web3Number.fromWei(amount0.toString(), amountInfo.token0.tokenInfo.decimals)
9059
+ },
9060
+ token1: {
9061
+ tokenInfo: amountInfo.token1.tokenInfo,
9062
+ amount: Web3Number.fromWei(amount1.toString(), amountInfo.token1.tokenInfo.decimals)
9063
+ }
9064
+ };
9065
+ }
9066
+ async depositCall(amountInfo, receiver) {
9067
+ const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
9068
+ const token0Contract = new Contract6(erc4626_abi_default, amountInfo.token0.tokenInfo.address.address, this.config.provider);
9069
+ const token1Contract = new Contract6(erc4626_abi_default, amountInfo.token1.tokenInfo.address.address, this.config.provider);
9070
+ const call1 = token0Contract.populate("approve", [this.address.address, uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei())]);
9071
+ const call2 = token1Contract.populate("approve", [this.address.address, uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei())]);
9072
+ const call3 = this.contract.populate("deposit", [uint2564.bnToUint256(updateAmountInfo.token0.amount.toWei()), uint2564.bnToUint256(updateAmountInfo.token1.amount.toWei()), receiver.address]);
9073
+ const calls = [];
9074
+ if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
9075
+ if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
9076
+ return [...calls, call3];
9077
+ }
9078
+ async tokensToShares(amountInfo) {
9079
+ const shares = await this.contract.call("convert_to_shares", [
9080
+ uint2564.bnToUint256(amountInfo.token0.amount.toWei()),
9081
+ uint2564.bnToUint256(amountInfo.token1.amount.toWei())
9082
+ ]);
9083
+ return Web3Number.fromWei(shares.toString(), 18);
9084
+ }
9085
+ async withdrawCall(amountInfo, receiver, owner) {
9086
+ const shares = await this.tokensToShares(amountInfo);
9087
+ logger.verbose(`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`);
9088
+ return [this.contract.populate("withdraw", [
9089
+ uint2564.bnToUint256(shares.toWei()),
9090
+ receiver.address
9091
+ ])];
8963
9092
  }
8964
9093
  rebalanceCall(newBounds, swapParams) {
8965
9094
  return [this.contract.populate("rebalance", [
@@ -8978,14 +9107,113 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
8978
9107
  handleFeesCall() {
8979
9108
  return [this.contract.populate("handle_fees", [])];
8980
9109
  }
8981
- async getUserTVL(user) {
8982
- throw new Error("Not implemented");
9110
+ /**
9111
+ * Calculates assets before and now in a given token of TVL per share to observe growth
9112
+ * @returns {Promise<number>} The weighted average APY across all pools
9113
+ */
9114
+ async netAPY(blockIdentifier = "pending", sinceBlocks = 2e4) {
9115
+ const tvlNow = await this._getTVL(blockIdentifier);
9116
+ const supplyNow = await this.totalSupply(blockIdentifier);
9117
+ const priceNow = await this.getCurrentPrice(blockIdentifier);
9118
+ let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
9119
+ const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
9120
+ const blockBefore = blockNow - sinceBlocks;
9121
+ const adjustedSupplyNow = supplyNow.minus(await this.getHarvestRewardShares(blockBefore, blockNow));
9122
+ let blockBeforeInfo = await this.config.provider.getBlockWithTxs(blockBefore);
9123
+ const tvlBefore = await this._getTVL(blockBefore);
9124
+ const supplyBefore = await this.totalSupply(blockBefore);
9125
+ const priceBefore = await this.getCurrentPrice(blockBefore);
9126
+ const tvlInToken0Now = tvlNow.amount0.multipliedBy(priceNow.price).plus(tvlNow.amount1);
9127
+ const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow);
9128
+ const tvlInToken0Bf = tvlBefore.amount0.multipliedBy(priceBefore.price).plus(tvlBefore.amount1);
9129
+ const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore);
9130
+ const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
9131
+ logger.verbose(`tvlInToken0Now: ${tvlInToken0Now.toString()}`);
9132
+ logger.verbose(`tvlInToken0Bf: ${tvlInToken0Bf.toString()}`);
9133
+ logger.verbose(`tvlPerShareNow: ${tvlPerShareNow.toString()}`);
9134
+ logger.verbose(`tvlPerShareBf: ${tvlPerShareBf.toString()}`);
9135
+ logger.verbose(`Price before: ${priceBefore.price.toString()}`);
9136
+ logger.verbose(`Price now: ${priceNow.price.toString()}`);
9137
+ logger.verbose(`Supply before: ${supplyBefore.toString()}`);
9138
+ logger.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
9139
+ logger.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
9140
+ const apyForGivenBlocks = Number(tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)) / 1e4;
9141
+ return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
9142
+ }
9143
+ async getHarvestRewardShares(fromBlock, toBlock) {
9144
+ const len = Number(await this.contract.call("get_total_rewards"));
9145
+ let shares = Web3Number.fromWei(0, 18);
9146
+ for (let i = len - 1; i > 0; --i) {
9147
+ let record = await this.contract.call("get_rewards_info", [i]);
9148
+ logger.verbose(`${_EkuboCLVault.name}: getHarvestRewardShares: ${i}`);
9149
+ console.log(record);
9150
+ const block = Number(record.block_number);
9151
+ if (block < fromBlock) {
9152
+ return shares;
9153
+ } else if (block > toBlock) {
9154
+ continue;
9155
+ } else {
9156
+ shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
9157
+ }
9158
+ logger.verbose(`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`);
9159
+ }
9160
+ return shares;
8983
9161
  }
8984
- async getTVL() {
8985
- const result = await this.contract.call("total_liquidity", []);
8986
- const bounds = await this.getCurrentBounds();
8987
- const { amount0, amount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(result.toString(), 18), bounds);
8988
- const poolKey = await this.getPoolKey();
9162
+ async balanceOf(user, blockIdentifier = "pending") {
9163
+ let bal = await this.contract.call("balance_of", [user.address]);
9164
+ return Web3Number.fromWei(bal.toString(), 18);
9165
+ }
9166
+ async getUserTVL(user, blockIdentifier = "pending") {
9167
+ let bal = await this.balanceOf(user, blockIdentifier);
9168
+ const assets = await this.contract.call("convert_to_assets", [uint2564.bnToUint256(bal.toWei())], {
9169
+ blockIdentifier
9170
+ });
9171
+ const poolKey = await this.getPoolKey(blockIdentifier);
9172
+ this.assertValidDepositTokens(poolKey);
9173
+ const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9174
+ const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9175
+ const amount0 = Web3Number.fromWei(assets.amount0.toString(), token0Info.decimals);
9176
+ const amount1 = Web3Number.fromWei(assets.amount1.toString(), token1Info.decimals);
9177
+ const P0 = await this.pricer.getPrice(token0Info.symbol);
9178
+ const P1 = await this.pricer.getPrice(token1Info.symbol);
9179
+ const token0Usd = Number(amount0.toFixed(13)) * P0.price;
9180
+ const token1Usd = Number(amount1.toFixed(13)) * P1.price;
9181
+ return {
9182
+ usdValue: token0Usd + token1Usd,
9183
+ token0: {
9184
+ tokenInfo: token0Info,
9185
+ amount: amount0,
9186
+ usdValue: token0Usd
9187
+ },
9188
+ token1: {
9189
+ tokenInfo: token1Info,
9190
+ amount: amount1,
9191
+ usdValue: token1Usd
9192
+ }
9193
+ };
9194
+ }
9195
+ async _getTVL(blockIdentifier = "pending") {
9196
+ const result = await this.contract.call("total_liquidity", [], {
9197
+ blockIdentifier
9198
+ });
9199
+ const bounds = await this.getCurrentBounds(blockIdentifier);
9200
+ const { amount0, amount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(result.toString(), 18), bounds, blockIdentifier);
9201
+ return { amount0, amount1 };
9202
+ }
9203
+ async totalSupply(blockIdentifier = "pending") {
9204
+ const res = await this.contract.call("total_supply", [], {
9205
+ blockIdentifier
9206
+ });
9207
+ return Web3Number.fromWei(res.toString(), 18);
9208
+ }
9209
+ assertValidDepositTokens(poolKey) {
9210
+ assert(poolKey.token0.eq(this.metadata.depositTokens[0].address), "Expected token0 in depositTokens[0]");
9211
+ assert(poolKey.token1.eq(this.metadata.depositTokens[1].address), "Expected token1 in depositTokens[1]");
9212
+ }
9213
+ async getTVL(blockIdentifier = "pending") {
9214
+ const { amount0, amount1 } = await this._getTVL(blockIdentifier);
9215
+ const poolKey = await this.getPoolKey(blockIdentifier);
9216
+ this.assertValidDepositTokens(poolKey);
8989
9217
  const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
8990
9218
  const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
8991
9219
  const P0 = await this.pricer.getPrice(token0Info.symbol);
@@ -8993,7 +9221,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
8993
9221
  const token0Usd = Number(amount0.toFixed(13)) * P0.price;
8994
9222
  const token1Usd = Number(amount1.toFixed(13)) * P1.price;
8995
9223
  return {
8996
- netUsdValue: token0Usd + token1Usd,
9224
+ usdValue: token0Usd + token1Usd,
8997
9225
  token0: {
8998
9226
  tokenInfo: token0Info,
8999
9227
  amount: amount0,
@@ -9033,7 +9261,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9033
9261
  const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
9034
9262
  const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
9035
9263
  return {
9036
- netUsdValue: token0Usd + token1Usd,
9264
+ usdValue: token0Usd + token1Usd,
9037
9265
  token0: {
9038
9266
  tokenInfo: token0Info,
9039
9267
  amount: token0Web3,
@@ -9055,11 +9283,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9055
9283
  const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
9056
9284
  return truePrice;
9057
9285
  }
9058
- async getCurrentPrice() {
9059
- const poolKey = await this.getPoolKey();
9060
- return this._getCurrentPrice(poolKey);
9286
+ async getCurrentPrice(blockIdentifier = "pending") {
9287
+ const poolKey = await this.getPoolKey(blockIdentifier);
9288
+ return this._getCurrentPrice(poolKey, blockIdentifier);
9061
9289
  }
9062
- async _getCurrentPrice(poolKey) {
9290
+ async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
9063
9291
  const priceInfo = await this.ekuboPositionsContract.call("get_pool_price", [
9064
9292
  {
9065
9293
  token0: poolKey.token0.address,
@@ -9068,35 +9296,44 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9068
9296
  tick_spacing: poolKey.tick_spacing,
9069
9297
  extension: poolKey.extension
9070
9298
  }
9071
- ]);
9299
+ ], {
9300
+ blockIdentifier
9301
+ });
9072
9302
  const sqrtRatio = _EkuboCLVault.div2Power128(BigInt(priceInfo.sqrt_ratio.toString()));
9303
+ console.log(`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`);
9073
9304
  const price = sqrtRatio * sqrtRatio;
9074
9305
  const tick = _EkuboCLVault.priceToTick(price, true, Number(poolKey.tick_spacing));
9306
+ console.log(`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`);
9075
9307
  return {
9076
9308
  price,
9077
- tick: tick.mag * (tick.sign == 0 ? 1 : -1)
9309
+ tick: tick.mag * (tick.sign == 0 ? 1 : -1),
9310
+ sqrtRatio: priceInfo.sqrt_ratio.toString()
9078
9311
  };
9079
9312
  }
9080
- async getCurrentBounds() {
9081
- const result = await this.contract.call("get_position_key", []);
9313
+ async getCurrentBounds(blockIdentifier = "pending") {
9314
+ const result = await this.contract.call("get_position_key", [], {
9315
+ blockIdentifier
9316
+ });
9082
9317
  return {
9083
9318
  lowerTick: _EkuboCLVault.i129ToNumber(result.bounds.lower),
9084
9319
  upperTick: _EkuboCLVault.i129ToNumber(result.bounds.upper)
9085
9320
  };
9086
9321
  }
9087
- static div2Power128(num3) {
9088
- return Number(BigInt(num3.toString()) * 1000000n / BigInt(2 ** 128)) / 1e6;
9322
+ static div2Power128(num4) {
9323
+ return Number(BigInt(num4.toString()) * 1000000n / BigInt(2 ** 128)) / 1e6;
9089
9324
  }
9090
9325
  static priceToTick(price, isRoundDown, tickSpacing) {
9091
9326
  const value = isRoundDown ? Math.floor(Math.log(price) / Math.log(1.000001)) : Math.ceil(Math.log(price) / Math.log(1.000001));
9092
9327
  const tick = Math.floor(value / tickSpacing) * tickSpacing;
9093
9328
  return this.tickToi129(tick);
9094
9329
  }
9095
- async getPoolKey() {
9330
+ async getPoolKey(blockIdentifier = "pending") {
9096
9331
  if (this.poolKey) {
9097
9332
  return this.poolKey;
9098
9333
  }
9099
- const result = await this.contract.call("get_settings", []);
9334
+ const result = await this.contract.call("get_settings", [], {
9335
+ blockIdentifier
9336
+ });
9100
9337
  const poolKey = {
9101
9338
  token0: ContractAddr.from(result.pool_key.token0.toString()),
9102
9339
  token1: ContractAddr.from(result.pool_key.token1.toString()),
@@ -9127,25 +9364,24 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9127
9364
  * @param amount1: amount of token1
9128
9365
  * @returns {amount0, amount1}
9129
9366
  */
9130
- async _getExpectedAmountsForLiquidity(amount0, amount1, bounds) {
9367
+ async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
9131
9368
  assert(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
9132
- const poolKey = await this.getPoolKey();
9133
- const sampleLiq = 1e18;
9369
+ const sampleLiq = 1e20;
9134
9370
  const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(sampleLiq.toString(), 18), bounds);
9135
9371
  logger.verbose(`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`);
9136
- assert(!sampleAmount0.eq(0) && !sampleAmount1.eq(0), "Sample amount is 0");
9372
+ assert(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
9137
9373
  const price = await (await this.getCurrentPrice()).price;
9138
9374
  logger.verbose(`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`);
9139
9375
  if (amount1.eq(0) && amount0.greaterThan(0)) {
9140
9376
  if (sampleAmount1.eq(0)) {
9141
9377
  return {
9142
9378
  amount0,
9143
- amount1: Web3Number.fromWei("0", 18),
9379
+ amount1: Web3Number.fromWei("0", amount1.decimals),
9144
9380
  ratio: Infinity
9145
9381
  };
9146
9382
  } else if (sampleAmount0.eq(0)) {
9147
9383
  return {
9148
- amount0: Web3Number.fromWei("0", 18),
9384
+ amount0: Web3Number.fromWei("0", amount0.decimals),
9149
9385
  amount1: amount0.multipliedBy(price),
9150
9386
  ratio: 0
9151
9387
  };
@@ -9153,21 +9389,41 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9153
9389
  } else if (amount0.eq(0) && amount1.greaterThan(0)) {
9154
9390
  if (sampleAmount0.eq(0)) {
9155
9391
  return {
9156
- amount0: Web3Number.fromWei("0", 18),
9392
+ amount0: Web3Number.fromWei("0", amount0.decimals),
9157
9393
  amount1,
9158
9394
  ratio: 0
9159
9395
  };
9160
9396
  } else if (sampleAmount1.eq(0)) {
9161
9397
  return {
9162
9398
  amount0: amount1.dividedBy(price),
9163
- amount1: Web3Number.fromWei("0", 18),
9399
+ amount1: Web3Number.fromWei("0", amount1.decimals),
9164
9400
  ratio: Infinity
9165
9401
  };
9166
9402
  }
9167
9403
  }
9168
- const ratio = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
9404
+ assert(sampleAmount0.decimals == sampleAmount1.decimals, "Sample amounts have different decimals");
9405
+ const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
9406
+ const ratio = Number(ratioWeb3Number.toFixed(18));
9169
9407
  logger.verbose(`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`);
9170
- return this._solveExpectedAmountsEq(amount0, amount1, ratio, price);
9408
+ if (justUseInputAmount)
9409
+ return this._solveExpectedAmountsEq(amount0, amount1, ratioWeb3Number, price);
9410
+ if (amount1.eq(0) && amount0.greaterThan(0)) {
9411
+ const _amount1 = amount0.dividedBy(ratioWeb3Number);
9412
+ return {
9413
+ amount0,
9414
+ amount1: _amount1,
9415
+ ratio
9416
+ };
9417
+ } else if (amount0.eq(0) && amount1.greaterThan(0)) {
9418
+ const _amount0 = amount1.multipliedBy(ratio);
9419
+ return {
9420
+ amount0: _amount0,
9421
+ amount1,
9422
+ ratio
9423
+ };
9424
+ } else {
9425
+ throw new Error("Both amounts are non-zero, cannot compute expected amounts");
9426
+ }
9171
9427
  }
9172
9428
  _solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
9173
9429
  const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
@@ -9181,10 +9437,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9181
9437
  async getSwapInfoToHandleUnused(considerRebalance = true) {
9182
9438
  const poolKey = await this.getPoolKey();
9183
9439
  const erc20Mod = new ERC20(this.config);
9184
- const token0Bal1 = await erc20Mod.balanceOf(poolKey.token0, this.address.address, 18);
9185
- const token1Bal1 = await erc20Mod.balanceOf(poolKey.token1, this.address.address, 18);
9186
9440
  const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9187
9441
  const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9442
+ const token0Bal1 = await erc20Mod.balanceOf(poolKey.token0, this.address.address, token0Info.decimals);
9443
+ const token1Bal1 = await erc20Mod.balanceOf(poolKey.token1, this.address.address, token1Info.decimals);
9444
+ logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`);
9188
9445
  const token0Price = await this.pricer.getPrice(token0Info.symbol);
9189
9446
  const token1Price = await this.pricer.getPrice(token1Info.symbol);
9190
9447
  const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
@@ -9195,6 +9452,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9195
9452
  let token0Bal = token0Bal1;
9196
9453
  let token1Bal = token1Bal1;
9197
9454
  if (considerRebalance) {
9455
+ logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`);
9198
9456
  const tvl = await this.getTVL();
9199
9457
  token0Bal = token0Bal.plus(tvl.token0.amount.toString());
9200
9458
  token1Bal = token1Bal.plus(tvl.token1.amount.toString());
@@ -9204,7 +9462,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9204
9462
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`);
9205
9463
  const newBounds = await this.getNewBounds();
9206
9464
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`);
9207
- let expectedAmounts = await this._getExpectedAmountsForLiquidity(token0Bal, token1Bal, newBounds);
9465
+ return await this.getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, newBounds);
9466
+ }
9467
+ async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
9468
+ let expectedAmounts = await this._getExpectedAmountsForLiquidity(token0Bal, token1Bal, bounds);
9208
9469
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`);
9209
9470
  let retry = 0;
9210
9471
  const maxRetry = 10;
@@ -9225,6 +9486,19 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9225
9486
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`);
9226
9487
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`);
9227
9488
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`);
9489
+ if (amountToSell.eq(0)) {
9490
+ return {
9491
+ token_from_address: tokenToSell.address,
9492
+ token_from_amount: uint2564.bnToUint256(0),
9493
+ token_to_address: tokenToSell.address,
9494
+ token_to_amount: uint2564.bnToUint256(0),
9495
+ token_to_min_amount: uint2564.bnToUint256(0),
9496
+ beneficiary: this.address.address,
9497
+ integrator_fee_amount_bps: 0,
9498
+ integrator_fee_recipient: this.address.address,
9499
+ routes: []
9500
+ };
9501
+ }
9228
9502
  const quote = await this.avnu.getQuotes(tokenToSell.address, tokenToBuy.address, amountToSell.toWei(), this.address.address);
9229
9503
  if (remainingSellAmount.eq(0)) {
9230
9504
  const minAmountOut = Web3Number.fromWei(quote.buyAmount.toString(), tokenToBuyInfo.decimals).multipliedBy(0.9999);
@@ -9247,6 +9521,73 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9247
9521
  }
9248
9522
  throw new Error("Failed to get swap info");
9249
9523
  }
9524
+ /**
9525
+ * Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
9526
+ * Uses binary search approach to find optimal swap amount.
9527
+ *
9528
+ * @param newBounds - The new tick bounds to rebalance to
9529
+ * @param swapInfo - Initial swap parameters for rebalancing
9530
+ * @param acc - Account to estimate gas fees with
9531
+ * @param retry - Current retry attempt number (default 0)
9532
+ * @param adjustmentFactor - Percentage to adjust swap amount by (default 1)
9533
+ * @param isToken0Deficit - Whether token0 balance needs increasing (default true)
9534
+ * @returns Array of contract calls needed for rebalancing
9535
+ * @throws Error if max retries reached without successful rebalance
9536
+ */
9537
+ async rebalanceIter(swapInfo, acc, estimateCall, retry = 0, adjustmentFactor = 1, isToken0Deficit = true) {
9538
+ const MAX_RETRIES = 20;
9539
+ const MIN_ADJUSTMENT = 1e-3;
9540
+ logger.verbose(
9541
+ `Rebalancing ${this.metadata.name}: retry=${retry}, adjustment=${adjustmentFactor}%, token0Deficit=${isToken0Deficit}`
9542
+ );
9543
+ const fromAmount = uint2564.uint256ToBN(swapInfo.token_from_amount);
9544
+ logger.verbose(
9545
+ `Selling ${fromAmount.toString()} of token ${swapInfo.token_from_address}`
9546
+ );
9547
+ try {
9548
+ const calls = await estimateCall(swapInfo);
9549
+ await acc.estimateInvokeFee(calls);
9550
+ return calls;
9551
+ } catch (err) {
9552
+ if (retry >= MAX_RETRIES) {
9553
+ logger.error(`Rebalance failed after ${MAX_RETRIES} retries`);
9554
+ throw err;
9555
+ }
9556
+ if (adjustmentFactor < MIN_ADJUSTMENT) {
9557
+ logger.error("Adjustment factor too small, likely oscillating");
9558
+ throw new Error("Failed to converge on valid swap amount");
9559
+ }
9560
+ logger.error(`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`);
9561
+ const newSwapInfo = { ...swapInfo };
9562
+ const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
9563
+ if (err.message.includes("invalid token0 balance") || err.message.includes("invalid token0 amount")) {
9564
+ logger.verbose("Reducing swap amount - excess token0");
9565
+ newSwapInfo.token_from_amount = uint2564.bnToUint256(
9566
+ currentAmount.multipliedBy((100 - adjustmentFactor) / 100).toWei()
9567
+ );
9568
+ adjustmentFactor = isToken0Deficit ? adjustmentFactor * 2 : adjustmentFactor / 2;
9569
+ isToken0Deficit = true;
9570
+ } else if (err.message.includes("invalid token1 balance") || err.message.includes("invalid token1 amount")) {
9571
+ logger.verbose("Increasing swap amount - excess token1");
9572
+ newSwapInfo.token_from_amount = uint2564.bnToUint256(
9573
+ currentAmount.multipliedBy((100 + adjustmentFactor) / 100).toWei()
9574
+ );
9575
+ adjustmentFactor = isToken0Deficit ? adjustmentFactor / 2 : adjustmentFactor * 2;
9576
+ isToken0Deficit = false;
9577
+ } else {
9578
+ logger.error("Unexpected error:", err);
9579
+ }
9580
+ newSwapInfo.token_to_min_amount = uint2564.bnToUint256("0");
9581
+ return this.rebalanceIter(
9582
+ newSwapInfo,
9583
+ acc,
9584
+ estimateCall,
9585
+ retry + 1,
9586
+ adjustmentFactor,
9587
+ isToken0Deficit
9588
+ );
9589
+ }
9590
+ }
9250
9591
  static tickToi129(tick) {
9251
9592
  if (tick < 0) {
9252
9593
  return {
@@ -9269,21 +9610,23 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9269
9610
  static tickToPrice(tick) {
9270
9611
  return Math.pow(1.000001, Number(tick));
9271
9612
  }
9272
- async getLiquidityToAmounts(liquidity, bounds) {
9273
- const currentPrice = await this.getCurrentPrice();
9274
- const lowerPrice = await _EkuboCLVault.tickToPrice(bounds.lowerTick);
9275
- const upperPrice = await _EkuboCLVault.tickToPrice(bounds.upperTick);
9613
+ async getLiquidityToAmounts(liquidity, bounds, blockIdentifier = "pending", _poolKey = null, _currentPrice = null) {
9614
+ const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
9615
+ const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
9616
+ const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
9276
9617
  logger.verbose(`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`);
9277
9618
  const result = await this.ekuboMathContract.call("liquidity_delta_to_amount_delta", [
9278
- uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(currentPrice.price).toString()),
9619
+ uint2564.bnToUint256(currentPrice.sqrtRatio),
9279
9620
  {
9280
9621
  mag: liquidity.toWei(),
9281
9622
  sign: 0
9282
9623
  },
9283
9624
  uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
9284
9625
  uint2564.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
9285
- ]);
9286
- const poolKey = await this.getPoolKey();
9626
+ ], {
9627
+ blockIdentifier
9628
+ });
9629
+ const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
9287
9630
  const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9288
9631
  const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9289
9632
  const amount0 = Web3Number.fromWei(_EkuboCLVault.i129ToNumber(result.amount0).toString(), token0Info.decimals);
@@ -9293,32 +9636,110 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9293
9636
  amount1
9294
9637
  };
9295
9638
  }
9639
+ async harvest(acc) {
9640
+ const ekuboHarvests = new EkuboHarvests(this.config);
9641
+ const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(this.address);
9642
+ const poolKey = await this.getPoolKey();
9643
+ const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9644
+ const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9645
+ const bounds = await this.getCurrentBounds();
9646
+ const calls = [];
9647
+ for (let claim of unClaimedRewards) {
9648
+ const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
9649
+ const postFeeAmount = claim.claim.amount.minus(fee);
9650
+ const isToken1 = claim.token.eq(poolKey.token1);
9651
+ logger.verbose(`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`);
9652
+ const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
9653
+ const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
9654
+ logger.verbose(`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`);
9655
+ const swapInfo = await this.getSwapInfoGivenAmounts(poolKey, token0Amt, token1Amt, bounds);
9656
+ swapInfo.token_to_address = token0Info.address.address;
9657
+ logger.verbose(`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`);
9658
+ logger.verbose(`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`);
9659
+ const harvestEstimateCall = async (swapInfo1) => {
9660
+ const swap1Amount = Web3Number.fromWei(uint2564.uint256ToBN(swapInfo1.token_from_amount).toString(), 18);
9661
+ const remainingAmount = postFeeAmount.minus(swap1Amount);
9662
+ const swapInfo2 = { ...swapInfo, token_from_amount: uint2564.bnToUint256(remainingAmount.toWei()) };
9663
+ swapInfo2.token_to_address = token1Info.address.address;
9664
+ const calldata = [
9665
+ claim.rewardsContract.address,
9666
+ {
9667
+ id: claim.claim.id,
9668
+ amount: claim.claim.amount.toWei(),
9669
+ claimee: claim.claim.claimee.address
9670
+ },
9671
+ claim.proof.map((p) => num3.getDecimalString(p)),
9672
+ swapInfo,
9673
+ swapInfo2
9674
+ ];
9675
+ logger.verbose(`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(calldata)}`);
9676
+ return [this.contract.populate("harvest", calldata)];
9677
+ };
9678
+ const _callsFinal = await this.rebalanceIter(swapInfo, acc, harvestEstimateCall);
9679
+ logger.verbose(`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(_callsFinal)}`);
9680
+ calls.push(..._callsFinal);
9681
+ }
9682
+ return calls;
9683
+ }
9684
+ async getInvestmentFlows() {
9685
+ const netYield = await this.netAPY();
9686
+ const poolKey = await this.getPoolKey();
9687
+ const linkedFlow = {
9688
+ title: this.metadata.name,
9689
+ subItems: [{ key: "Pool", value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing` }],
9690
+ linkedFlows: [],
9691
+ style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
9692
+ };
9693
+ const baseFlow = {
9694
+ id: "base",
9695
+ title: "Your Deposit",
9696
+ subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }, { key: `Performance Fee`, value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%` }],
9697
+ linkedFlows: [linkedFlow],
9698
+ style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
9699
+ };
9700
+ const rebalanceFlow = {
9701
+ id: "rebalance",
9702
+ title: "Automated Rebalance",
9703
+ subItems: [{
9704
+ key: "Range selection",
9705
+ value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
9706
+ }],
9707
+ linkedFlows: [linkedFlow],
9708
+ style: { backgroundColor: "purple" /* Green */.valueOf() }
9709
+ };
9710
+ return [baseFlow, rebalanceFlow];
9711
+ }
9296
9712
  };
9297
- var _description2 = "Automatically rebalances liquidity near current price to maximize yield while reducing the necessity to manually rebalance positions frequently. Fees earn and Defi spring rewards are automatically re-invested.";
9713
+ 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.";
9298
9714
  var _protocol2 = { name: "Ekubo", logo: "https://app.ekubo.org/favicon.ico" };
9299
9715
  var _riskFactor2 = [
9300
9716
  { type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
9301
9717
  { type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
9302
9718
  ];
9719
+ var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
9303
9720
  var EkuboCLVaultStrategies = [{
9304
9721
  name: "Ekubo xSTRK/STRK",
9305
- description: _description2,
9722
+ description: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK"),
9306
9723
  address: ContractAddr.from("0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"),
9307
9724
  type: "Other",
9308
- depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK"), Global.getDefaultTokens().find((t) => t.symbol === "xSTRK")],
9725
+ // must be same order as poolKey token0 and token1
9726
+ depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"), Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
9309
9727
  protocols: [_protocol2],
9728
+ auditUrl: AUDIT_URL2,
9310
9729
  maxTVL: Web3Number.fromWei("0", 18),
9311
9730
  risk: {
9312
9731
  riskFactor: _riskFactor2,
9313
9732
  netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
9314
9733
  notARisks: getNoRiskTags(_riskFactor2)
9315
9734
  },
9735
+ apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
9316
9736
  additionalInfo: {
9317
9737
  newBounds: {
9318
9738
  lower: -1,
9319
9739
  upper: 1
9320
9740
  },
9321
- lstContract: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a")
9741
+ lstContract: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"),
9742
+ feeBps: 1e3
9322
9743
  }
9323
9744
  }];
9324
9745
 
@@ -9428,7 +9849,7 @@ var PricerRedis = class extends Pricer {
9428
9849
 
9429
9850
  // src/utils/store.ts
9430
9851
  import fs, { readFileSync, writeFileSync } from "fs";
9431
- import { Account, constants } from "starknet";
9852
+ import { Account as Account2, constants } from "starknet";
9432
9853
  import * as crypto2 from "crypto";
9433
9854
 
9434
9855
  // src/utils/encrypt.ts
@@ -9522,7 +9943,7 @@ var Store = class _Store {
9522
9943
  }
9523
9944
  logger.verbose(`Account loaded: ${accountKey} from network: ${this.config.network}`);
9524
9945
  logger.verbose(`Address: ${data.address}`);
9525
- const acc = new Account(this.config.provider, data.address, data.pk, void 0, txVersion);
9946
+ const acc = new Account2(this.config.provider, data.address, data.pk, void 0, txVersion);
9526
9947
  return acc;
9527
9948
  }
9528
9949
  addAccount(accountKey, address, pk) {
@@ -9586,6 +10007,7 @@ var Store = class _Store {
9586
10007
  export {
9587
10008
  AutoCompounderSTRK,
9588
10009
  AvnuWrapper,
10010
+ BaseStrategy,
9589
10011
  ContractAddr,
9590
10012
  ERC20,
9591
10013
  EkuboCLVault,