@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.js CHANGED
@@ -32,6 +32,7 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AutoCompounderSTRK: () => AutoCompounderSTRK,
34
34
  AvnuWrapper: () => AvnuWrapper,
35
+ BaseStrategy: () => BaseStrategy,
35
36
  ContractAddr: () => ContractAddr,
36
37
  ERC20: () => ERC20,
37
38
  EkuboCLVault: () => EkuboCLVault,
@@ -86,19 +87,19 @@ var _Web3Number = class extends import_bignumber.default {
86
87
  return this.mul(10 ** this.decimals).toFixed(0);
87
88
  }
88
89
  multipliedBy(value) {
89
- let _value = Number(value).toFixed(this.maxToFixedDecimals());
90
+ const _value = this.getStandardString(value);
90
91
  return this.construct(this.mul(_value).toString(), this.decimals);
91
92
  }
92
93
  dividedBy(value) {
93
- let _value = Number(value).toFixed(this.maxToFixedDecimals());
94
+ const _value = this.getStandardString(value);
94
95
  return this.construct(this.div(_value).toString(), this.decimals);
95
96
  }
96
97
  plus(value) {
97
- const _value = Number(value).toFixed(this.maxToFixedDecimals());
98
+ const _value = this.getStandardString(value);
98
99
  return this.construct(this.add(_value).toString(), this.decimals);
99
100
  }
100
101
  minus(n, base) {
101
- const _value = Number(n).toFixed(this.maxToFixedDecimals());
102
+ const _value = this.getStandardString(n);
102
103
  return this.construct(super.minus(_value, base).toString(), this.decimals);
103
104
  }
104
105
  construct(value, decimals) {
@@ -114,11 +115,17 @@ var _Web3Number = class extends import_bignumber.default {
114
115
  return this.toString();
115
116
  }
116
117
  maxToFixedDecimals() {
117
- return Math.min(this.decimals, 13);
118
+ return Math.min(this.decimals, 18);
119
+ }
120
+ getStandardString(value) {
121
+ if (typeof value == "string") {
122
+ return value;
123
+ }
124
+ return value.toFixed(this.maxToFixedDecimals());
118
125
  }
119
126
  };
120
- import_bignumber.default.config({ DECIMAL_PLACES: 18 });
121
- _Web3Number.config({ DECIMAL_PLACES: 18 });
127
+ import_bignumber.default.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: import_bignumber.default.ROUND_DOWN });
128
+ _Web3Number.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: import_bignumber.default.ROUND_DOWN });
122
129
 
123
130
  // src/dataTypes/bignumber.node.ts
124
131
  var Web3Number = class _Web3Number2 extends _Web3Number {
@@ -190,42 +197,48 @@ var defaultTokens = [{
190
197
  logo: "https://assets.coingecko.com/coins/images/26433/small/starknet.png",
191
198
  address: ContractAddr.from("0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"),
192
199
  decimals: 18,
193
- coingeckId: "starknet"
200
+ coingeckId: "starknet",
201
+ displayDecimals: 2
194
202
  }, {
195
203
  name: "xSTRK",
196
204
  symbol: "xSTRK",
197
205
  logo: "https://dashboard.endur.fi/endur-fi.svg",
198
206
  address: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"),
199
207
  decimals: 18,
200
- coingeckId: void 0
208
+ coingeckId: void 0,
209
+ displayDecimals: 2
201
210
  }, {
202
211
  name: "ETH",
203
212
  symbol: "ETH",
204
213
  logo: "https://opbnb.bscscan.com/token/images/ether.svg",
205
214
  address: ContractAddr.from("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
206
215
  decimals: 18,
207
- coingeckId: void 0
216
+ coingeckId: void 0,
217
+ displayDecimals: 4
208
218
  }, {
209
219
  name: "USDC",
210
220
  symbol: "USDC",
211
221
  logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
212
222
  address: ContractAddr.from("0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8"),
213
223
  decimals: 6,
214
- coingeckId: void 0
224
+ coingeckId: void 0,
225
+ displayDecimals: 2
215
226
  }, {
216
227
  name: "USDT",
217
228
  symbol: "USDT",
218
229
  logo: "https://assets.coingecko.com/coins/images/325/small/Tether.png",
219
230
  address: ContractAddr.from("0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8"),
220
231
  decimals: 6,
221
- coingeckId: void 0
232
+ coingeckId: void 0,
233
+ displayDecimals: 2
222
234
  }, {
223
235
  name: "WBTC",
224
236
  symbol: "WBTC",
225
237
  logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png",
226
238
  address: ContractAddr.from("0x3fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac"),
227
239
  decimals: 8,
228
- coingeckId: void 0
240
+ coingeckId: void 0,
241
+ displayDecimals: 6
229
242
  }];
230
243
  var tokens = defaultTokens;
231
244
  var Global = class _Global {
@@ -257,7 +270,8 @@ var Global = class _Global {
257
270
  address: ContractAddr.from(token.address),
258
271
  decimals: token.decimals,
259
272
  logo: token.logoUri,
260
- coingeckId: token.extensions.coingeckoId
273
+ coingeckId: token.extensions.coingeckoId,
274
+ displayDecimals: 2
261
275
  });
262
276
  });
263
277
  console.log(tokens);
@@ -641,7 +655,8 @@ var _ZkLend = class _ZkLend extends ILending {
641
655
  logo: "",
642
656
  decimals: pool.token.decimals,
643
657
  borrowFactor: Web3Number2.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
644
- collareralFactor
658
+ collareralFactor,
659
+ displayDecimals: 2
645
660
  };
646
661
  this.tokens.push(token);
647
662
  });
@@ -758,7 +773,7 @@ var PricerFromApi = class extends PricerBase {
758
773
  try {
759
774
  return await this.getPriceFromMyAPI(tokenSymbol);
760
775
  } catch (e) {
761
- logger.warn("getPriceFromMyAPI error", e);
776
+ logger.warn("getPriceFromMyAPI error", JSON.stringify(e.message || e));
762
777
  }
763
778
  logger.log("getPrice coinbase", tokenSymbol);
764
779
  let retry = 0;
@@ -778,7 +793,7 @@ var PricerFromApi = class extends PricerBase {
778
793
  timestamp: /* @__PURE__ */ new Date()
779
794
  };
780
795
  } catch (e) {
781
- logger.warn("getPrice coinbase error", e, retry);
796
+ logger.warn("getPrice coinbase error", JSON.stringify(e.message || e));
782
797
  await new Promise((resolve) => setTimeout(resolve, retry * 1e3));
783
798
  }
784
799
  }
@@ -792,9 +807,6 @@ var PricerFromApi = class extends PricerBase {
792
807
  const priceInfo = await priceInfoRes.json();
793
808
  const now = /* @__PURE__ */ new Date();
794
809
  const priceTime = new Date(priceInfo.timestamp);
795
- if (now.getTime() - priceTime.getTime() > 9e5) {
796
- throw new Error("Price is stale");
797
- }
798
810
  const price = Number(priceInfo.price);
799
811
  return {
800
812
  price,
@@ -1958,17 +1970,28 @@ function assert(condition, message) {
1958
1970
  }
1959
1971
 
1960
1972
  // src/modules/avnu.ts
1961
- var AvnuWrapper = class {
1962
- async getQuotes(fromToken, toToken, amountWei, taker) {
1973
+ var AvnuWrapper = class _AvnuWrapper {
1974
+ async getQuotes(fromToken, toToken, amountWei, taker, retry = 0) {
1975
+ const MAX_RETRY = 5;
1976
+ logger.verbose(`${_AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
1963
1977
  const params = {
1964
1978
  sellTokenAddress: fromToken,
1965
1979
  buyTokenAddress: toToken,
1966
1980
  sellAmount: amountWei,
1967
- takerAddress: taker
1981
+ takerAddress: taker,
1982
+ // excludeSources: ['Nostra', 'Haiko(Solvers)']
1983
+ excludeSources: ["Haiko(Solvers)"]
1984
+ // to resolve InvalidOraclePrice error
1968
1985
  };
1969
1986
  assert(fromToken != toToken, "From and to tokens are the same");
1970
1987
  const quotes = await (0, import_avnu_sdk.fetchQuotes)(params);
1971
- assert(quotes.length > 0, "No quotes found");
1988
+ if (quotes.length == 0) {
1989
+ if (retry < MAX_RETRY) {
1990
+ await new Promise((res) => setTimeout(res, 3e3));
1991
+ return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
1992
+ }
1993
+ throw new Error("no quotes found");
1994
+ }
1972
1995
  return quotes[0];
1973
1996
  }
1974
1997
  async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
@@ -3641,10 +3664,10 @@ var BaseStrategy = class {
3641
3664
  async getTVL() {
3642
3665
  throw new Error("Not implemented");
3643
3666
  }
3644
- depositCall(amountInfo, receiver) {
3667
+ async depositCall(amountInfo, receiver) {
3645
3668
  throw new Error("Not implemented");
3646
3669
  }
3647
- withdrawCall(amountInfo, receiver, owner) {
3670
+ async withdrawCall(amountInfo, receiver, owner) {
3648
3671
  throw new Error("Not implemented");
3649
3672
  }
3650
3673
  };
@@ -3681,7 +3704,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
3681
3704
  * @param receiver - Address that will receive the strategy tokens
3682
3705
  * @returns Populated contract call for deposit
3683
3706
  */
3684
- depositCall(amountInfo, receiver) {
3707
+ async depositCall(amountInfo, receiver) {
3685
3708
  assert(amountInfo.tokenInfo.address.eq(this.asset().address), "Deposit token mismatch");
3686
3709
  const assetContract = new import_starknet7.Contract(vesu_rebalance_abi_default, this.asset().address.address, this.config.provider);
3687
3710
  const call1 = assetContract.populate("approve", [this.address.address, import_starknet7.uint256.bnToUint256(amountInfo.amount.toWei())]);
@@ -3695,7 +3718,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
3695
3718
  * @param owner - Address that owns the strategy tokens
3696
3719
  * @returns Populated contract call for withdrawal
3697
3720
  */
3698
- withdrawCall(amountInfo, receiver, owner) {
3721
+ async withdrawCall(amountInfo, receiver, owner) {
3699
3722
  return [this.contract.populate("withdraw", [import_starknet7.uint256.bnToUint256(amountInfo.amount.toWei()), receiver.address, owner.address])];
3700
3723
  }
3701
3724
  /**
@@ -3982,7 +4005,7 @@ var VesuRebalance = class _VesuRebalance extends BaseStrategy {
3982
4005
  const netYield = await this.netAPYGivenPools(pools);
3983
4006
  const baseFlow = {
3984
4007
  title: "Your Deposit",
3985
- subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }],
4008
+ subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }, { key: `Performance Fee`, value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%` }],
3986
4009
  linkedFlows: [],
3987
4010
  style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
3988
4011
  };
@@ -4098,7 +4121,7 @@ var VesuRebalanceStrategies = [{
4098
4121
  }];
4099
4122
 
4100
4123
  // src/strategies/ekubo-cl-vault.ts
4101
- var import_starknet8 = require("starknet");
4124
+ var import_starknet9 = require("starknet");
4102
4125
 
4103
4126
  // src/data/cl-vault.abi.json
4104
4127
  var cl_vault_abi_default = [
@@ -8999,6 +9022,58 @@ var erc4626_abi_default = [
8999
9022
  }
9000
9023
  ];
9001
9024
 
9025
+ // src/modules/harvests.ts
9026
+ var import_starknet8 = require("starknet");
9027
+ var Harvests = class _Harvests {
9028
+ constructor(config) {
9029
+ this.config = config;
9030
+ }
9031
+ getHarvests(addr) {
9032
+ throw new Error("Not implemented");
9033
+ }
9034
+ async getUnHarvestedRewards(addr) {
9035
+ const rewards = await this.getHarvests(addr);
9036
+ if (rewards.length == 0) return [];
9037
+ const unClaimed = [];
9038
+ const cls = await this.config.provider.getClassAt(rewards[0].rewardsContract.address);
9039
+ for (let reward of rewards) {
9040
+ const contract = new import_starknet8.Contract(cls.abi, reward.rewardsContract.address, this.config.provider);
9041
+ const isClaimed = await contract.call("is_claimed", [reward.claim.id]);
9042
+ logger.verbose(`${_Harvests.name}: isClaimed: ${isClaimed}`);
9043
+ if (isClaimed)
9044
+ return unClaimed;
9045
+ unClaimed.unshift(reward);
9046
+ }
9047
+ return unClaimed;
9048
+ }
9049
+ };
9050
+ var EkuboHarvests = class extends Harvests {
9051
+ async getHarvests(addr) {
9052
+ const STRK = "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
9053
+ const EKUBO_API = `https://starknet-mainnet-api.ekubo.org/airdrops/${addr.address}?token=${STRK}`;
9054
+ const resultEkubo = await fetch(EKUBO_API);
9055
+ const items = await resultEkubo.json();
9056
+ const rewards = [];
9057
+ for (let i = 0; i < items.length; ++i) {
9058
+ const info = items[i];
9059
+ assert(info.token == STRK, "expected strk token only");
9060
+ rewards.push({
9061
+ rewardsContract: ContractAddr.from(info.contract_address),
9062
+ token: ContractAddr.from(STRK),
9063
+ startDate: new Date(info.start_date),
9064
+ endDate: new Date(info.end_date),
9065
+ claim: {
9066
+ id: info.claim.id,
9067
+ amount: Web3Number.fromWei(info.claim.amount, 18),
9068
+ claimee: ContractAddr.from(info.claim.claimee)
9069
+ },
9070
+ proof: info.proof
9071
+ });
9072
+ }
9073
+ return rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
9074
+ }
9075
+ };
9076
+
9002
9077
  // src/strategies/ekubo-cl-vault.ts
9003
9078
  var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9004
9079
  /**
@@ -9015,19 +9090,74 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9015
9090
  assert(metadata.depositTokens.length === 2, "EkuboCL only supports 2 deposit token");
9016
9091
  this.metadata = metadata;
9017
9092
  this.address = metadata.address;
9018
- this.contract = new import_starknet8.Contract(cl_vault_abi_default, this.address.address, this.config.provider);
9019
- this.lstContract = new import_starknet8.Contract(erc4626_abi_default, this.metadata.additionalInfo.lstContract.address, this.config.provider);
9093
+ this.contract = new import_starknet9.Contract(cl_vault_abi_default, this.address.address, this.config.provider);
9094
+ this.lstContract = new import_starknet9.Contract(erc4626_abi_default, this.metadata.additionalInfo.lstContract.address, this.config.provider);
9020
9095
  const EKUBO_POSITION = "0x02e0af29598b407c8716b17f6d2795eca1b471413fa03fb145a5e33722184067";
9021
- this.ekuboPositionsContract = new import_starknet8.Contract(ekubo_positions_abi_default, EKUBO_POSITION, this.config.provider);
9096
+ this.ekuboPositionsContract = new import_starknet9.Contract(ekubo_positions_abi_default, EKUBO_POSITION, this.config.provider);
9022
9097
  const EKUBO_MATH = "0x04a72e9e166f6c0e9d800af4dc40f6b6fb4404b735d3f528d9250808b2481995";
9023
- this.ekuboMathContract = new import_starknet8.Contract(ekubo_math_abi_default, EKUBO_MATH, this.config.provider);
9098
+ this.ekuboMathContract = new import_starknet9.Contract(ekubo_math_abi_default, EKUBO_MATH, this.config.provider);
9024
9099
  this.avnu = new AvnuWrapper();
9025
9100
  }
9026
- depositCall(amountInfo, receiver) {
9027
- return [];
9101
+ async matchInputAmounts(amountInfo) {
9102
+ const bounds = await this.getCurrentBounds();
9103
+ const res = await this._getExpectedAmountsForLiquidity(
9104
+ amountInfo.token0.amount,
9105
+ amountInfo.token1.amount,
9106
+ bounds,
9107
+ false
9108
+ );
9109
+ return {
9110
+ token0: {
9111
+ tokenInfo: amountInfo.token0.tokenInfo,
9112
+ amount: res.amount0
9113
+ },
9114
+ token1: {
9115
+ tokenInfo: amountInfo.token1.tokenInfo,
9116
+ amount: res.amount1
9117
+ }
9118
+ };
9028
9119
  }
9029
- withdrawCall(amountInfo, receiver, owner) {
9030
- return [];
9120
+ /** Returns minimum amounts give given two amounts based on what can be added for liq */
9121
+ async getMinDepositAmounts(amountInfo) {
9122
+ const shares = await this.tokensToShares(amountInfo);
9123
+ const { amount0, amount1 } = await this.contract.call("convert_to_assets", [import_starknet9.uint256.bnToUint256(shares.toWei())]);
9124
+ return {
9125
+ token0: {
9126
+ tokenInfo: amountInfo.token0.tokenInfo,
9127
+ amount: Web3Number.fromWei(amount0.toString(), amountInfo.token0.tokenInfo.decimals)
9128
+ },
9129
+ token1: {
9130
+ tokenInfo: amountInfo.token1.tokenInfo,
9131
+ amount: Web3Number.fromWei(amount1.toString(), amountInfo.token1.tokenInfo.decimals)
9132
+ }
9133
+ };
9134
+ }
9135
+ async depositCall(amountInfo, receiver) {
9136
+ const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
9137
+ const token0Contract = new import_starknet9.Contract(erc4626_abi_default, amountInfo.token0.tokenInfo.address.address, this.config.provider);
9138
+ const token1Contract = new import_starknet9.Contract(erc4626_abi_default, amountInfo.token1.tokenInfo.address.address, this.config.provider);
9139
+ const call1 = token0Contract.populate("approve", [this.address.address, import_starknet9.uint256.bnToUint256(updateAmountInfo.token0.amount.toWei())]);
9140
+ const call2 = token1Contract.populate("approve", [this.address.address, import_starknet9.uint256.bnToUint256(updateAmountInfo.token1.amount.toWei())]);
9141
+ const call3 = this.contract.populate("deposit", [import_starknet9.uint256.bnToUint256(updateAmountInfo.token0.amount.toWei()), import_starknet9.uint256.bnToUint256(updateAmountInfo.token1.amount.toWei()), receiver.address]);
9142
+ const calls = [];
9143
+ if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
9144
+ if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
9145
+ return [...calls, call3];
9146
+ }
9147
+ async tokensToShares(amountInfo) {
9148
+ const shares = await this.contract.call("convert_to_shares", [
9149
+ import_starknet9.uint256.bnToUint256(amountInfo.token0.amount.toWei()),
9150
+ import_starknet9.uint256.bnToUint256(amountInfo.token1.amount.toWei())
9151
+ ]);
9152
+ return Web3Number.fromWei(shares.toString(), 18);
9153
+ }
9154
+ async withdrawCall(amountInfo, receiver, owner) {
9155
+ const shares = await this.tokensToShares(amountInfo);
9156
+ logger.verbose(`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`);
9157
+ return [this.contract.populate("withdraw", [
9158
+ import_starknet9.uint256.bnToUint256(shares.toWei()),
9159
+ receiver.address
9160
+ ])];
9031
9161
  }
9032
9162
  rebalanceCall(newBounds, swapParams) {
9033
9163
  return [this.contract.populate("rebalance", [
@@ -9046,14 +9176,113 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9046
9176
  handleFeesCall() {
9047
9177
  return [this.contract.populate("handle_fees", [])];
9048
9178
  }
9049
- async getUserTVL(user) {
9050
- throw new Error("Not implemented");
9179
+ /**
9180
+ * Calculates assets before and now in a given token of TVL per share to observe growth
9181
+ * @returns {Promise<number>} The weighted average APY across all pools
9182
+ */
9183
+ async netAPY(blockIdentifier = "pending", sinceBlocks = 2e4) {
9184
+ const tvlNow = await this._getTVL(blockIdentifier);
9185
+ const supplyNow = await this.totalSupply(blockIdentifier);
9186
+ const priceNow = await this.getCurrentPrice(blockIdentifier);
9187
+ let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
9188
+ const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
9189
+ const blockBefore = blockNow - sinceBlocks;
9190
+ const adjustedSupplyNow = supplyNow.minus(await this.getHarvestRewardShares(blockBefore, blockNow));
9191
+ let blockBeforeInfo = await this.config.provider.getBlockWithTxs(blockBefore);
9192
+ const tvlBefore = await this._getTVL(blockBefore);
9193
+ const supplyBefore = await this.totalSupply(blockBefore);
9194
+ const priceBefore = await this.getCurrentPrice(blockBefore);
9195
+ const tvlInToken0Now = tvlNow.amount0.multipliedBy(priceNow.price).plus(tvlNow.amount1);
9196
+ const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow);
9197
+ const tvlInToken0Bf = tvlBefore.amount0.multipliedBy(priceBefore.price).plus(tvlBefore.amount1);
9198
+ const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore);
9199
+ const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
9200
+ logger.verbose(`tvlInToken0Now: ${tvlInToken0Now.toString()}`);
9201
+ logger.verbose(`tvlInToken0Bf: ${tvlInToken0Bf.toString()}`);
9202
+ logger.verbose(`tvlPerShareNow: ${tvlPerShareNow.toString()}`);
9203
+ logger.verbose(`tvlPerShareBf: ${tvlPerShareBf.toString()}`);
9204
+ logger.verbose(`Price before: ${priceBefore.price.toString()}`);
9205
+ logger.verbose(`Price now: ${priceNow.price.toString()}`);
9206
+ logger.verbose(`Supply before: ${supplyBefore.toString()}`);
9207
+ logger.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
9208
+ logger.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
9209
+ const apyForGivenBlocks = Number(tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)) / 1e4;
9210
+ return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
9211
+ }
9212
+ async getHarvestRewardShares(fromBlock, toBlock) {
9213
+ const len = Number(await this.contract.call("get_total_rewards"));
9214
+ let shares = Web3Number.fromWei(0, 18);
9215
+ for (let i = len - 1; i > 0; --i) {
9216
+ let record = await this.contract.call("get_rewards_info", [i]);
9217
+ logger.verbose(`${_EkuboCLVault.name}: getHarvestRewardShares: ${i}`);
9218
+ console.log(record);
9219
+ const block = Number(record.block_number);
9220
+ if (block < fromBlock) {
9221
+ return shares;
9222
+ } else if (block > toBlock) {
9223
+ continue;
9224
+ } else {
9225
+ shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
9226
+ }
9227
+ logger.verbose(`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`);
9228
+ }
9229
+ return shares;
9051
9230
  }
9052
- async getTVL() {
9053
- const result = await this.contract.call("total_liquidity", []);
9054
- const bounds = await this.getCurrentBounds();
9055
- const { amount0, amount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(result.toString(), 18), bounds);
9056
- const poolKey = await this.getPoolKey();
9231
+ async balanceOf(user, blockIdentifier = "pending") {
9232
+ let bal = await this.contract.call("balance_of", [user.address]);
9233
+ return Web3Number.fromWei(bal.toString(), 18);
9234
+ }
9235
+ async getUserTVL(user, blockIdentifier = "pending") {
9236
+ let bal = await this.balanceOf(user, blockIdentifier);
9237
+ const assets = await this.contract.call("convert_to_assets", [import_starknet9.uint256.bnToUint256(bal.toWei())], {
9238
+ blockIdentifier
9239
+ });
9240
+ const poolKey = await this.getPoolKey(blockIdentifier);
9241
+ this.assertValidDepositTokens(poolKey);
9242
+ const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9243
+ const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9244
+ const amount0 = Web3Number.fromWei(assets.amount0.toString(), token0Info.decimals);
9245
+ const amount1 = Web3Number.fromWei(assets.amount1.toString(), token1Info.decimals);
9246
+ const P0 = await this.pricer.getPrice(token0Info.symbol);
9247
+ const P1 = await this.pricer.getPrice(token1Info.symbol);
9248
+ const token0Usd = Number(amount0.toFixed(13)) * P0.price;
9249
+ const token1Usd = Number(amount1.toFixed(13)) * P1.price;
9250
+ return {
9251
+ usdValue: token0Usd + token1Usd,
9252
+ token0: {
9253
+ tokenInfo: token0Info,
9254
+ amount: amount0,
9255
+ usdValue: token0Usd
9256
+ },
9257
+ token1: {
9258
+ tokenInfo: token1Info,
9259
+ amount: amount1,
9260
+ usdValue: token1Usd
9261
+ }
9262
+ };
9263
+ }
9264
+ async _getTVL(blockIdentifier = "pending") {
9265
+ const result = await this.contract.call("total_liquidity", [], {
9266
+ blockIdentifier
9267
+ });
9268
+ const bounds = await this.getCurrentBounds(blockIdentifier);
9269
+ const { amount0, amount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(result.toString(), 18), bounds, blockIdentifier);
9270
+ return { amount0, amount1 };
9271
+ }
9272
+ async totalSupply(blockIdentifier = "pending") {
9273
+ const res = await this.contract.call("total_supply", [], {
9274
+ blockIdentifier
9275
+ });
9276
+ return Web3Number.fromWei(res.toString(), 18);
9277
+ }
9278
+ assertValidDepositTokens(poolKey) {
9279
+ assert(poolKey.token0.eq(this.metadata.depositTokens[0].address), "Expected token0 in depositTokens[0]");
9280
+ assert(poolKey.token1.eq(this.metadata.depositTokens[1].address), "Expected token1 in depositTokens[1]");
9281
+ }
9282
+ async getTVL(blockIdentifier = "pending") {
9283
+ const { amount0, amount1 } = await this._getTVL(blockIdentifier);
9284
+ const poolKey = await this.getPoolKey(blockIdentifier);
9285
+ this.assertValidDepositTokens(poolKey);
9057
9286
  const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9058
9287
  const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9059
9288
  const P0 = await this.pricer.getPrice(token0Info.symbol);
@@ -9061,7 +9290,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9061
9290
  const token0Usd = Number(amount0.toFixed(13)) * P0.price;
9062
9291
  const token1Usd = Number(amount1.toFixed(13)) * P1.price;
9063
9292
  return {
9064
- netUsdValue: token0Usd + token1Usd,
9293
+ usdValue: token0Usd + token1Usd,
9065
9294
  token0: {
9066
9295
  tokenInfo: token0Info,
9067
9296
  amount: amount0,
@@ -9101,7 +9330,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9101
9330
  const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
9102
9331
  const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
9103
9332
  return {
9104
- netUsdValue: token0Usd + token1Usd,
9333
+ usdValue: token0Usd + token1Usd,
9105
9334
  token0: {
9106
9335
  tokenInfo: token0Info,
9107
9336
  amount: token0Web3,
@@ -9119,15 +9348,15 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9119
9348
  return Number(result.salt.toString());
9120
9349
  }
9121
9350
  async truePrice() {
9122
- const result = await this.lstContract.call("convert_to_assets", [import_starknet8.uint256.bnToUint256(BigInt(1e18).toString())]);
9351
+ const result = await this.lstContract.call("convert_to_assets", [import_starknet9.uint256.bnToUint256(BigInt(1e18).toString())]);
9123
9352
  const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
9124
9353
  return truePrice;
9125
9354
  }
9126
- async getCurrentPrice() {
9127
- const poolKey = await this.getPoolKey();
9128
- return this._getCurrentPrice(poolKey);
9355
+ async getCurrentPrice(blockIdentifier = "pending") {
9356
+ const poolKey = await this.getPoolKey(blockIdentifier);
9357
+ return this._getCurrentPrice(poolKey, blockIdentifier);
9129
9358
  }
9130
- async _getCurrentPrice(poolKey) {
9359
+ async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
9131
9360
  const priceInfo = await this.ekuboPositionsContract.call("get_pool_price", [
9132
9361
  {
9133
9362
  token0: poolKey.token0.address,
@@ -9136,35 +9365,44 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9136
9365
  tick_spacing: poolKey.tick_spacing,
9137
9366
  extension: poolKey.extension
9138
9367
  }
9139
- ]);
9368
+ ], {
9369
+ blockIdentifier
9370
+ });
9140
9371
  const sqrtRatio = _EkuboCLVault.div2Power128(BigInt(priceInfo.sqrt_ratio.toString()));
9372
+ console.log(`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`);
9141
9373
  const price = sqrtRatio * sqrtRatio;
9142
9374
  const tick = _EkuboCLVault.priceToTick(price, true, Number(poolKey.tick_spacing));
9375
+ console.log(`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`);
9143
9376
  return {
9144
9377
  price,
9145
- tick: tick.mag * (tick.sign == 0 ? 1 : -1)
9378
+ tick: tick.mag * (tick.sign == 0 ? 1 : -1),
9379
+ sqrtRatio: priceInfo.sqrt_ratio.toString()
9146
9380
  };
9147
9381
  }
9148
- async getCurrentBounds() {
9149
- const result = await this.contract.call("get_position_key", []);
9382
+ async getCurrentBounds(blockIdentifier = "pending") {
9383
+ const result = await this.contract.call("get_position_key", [], {
9384
+ blockIdentifier
9385
+ });
9150
9386
  return {
9151
9387
  lowerTick: _EkuboCLVault.i129ToNumber(result.bounds.lower),
9152
9388
  upperTick: _EkuboCLVault.i129ToNumber(result.bounds.upper)
9153
9389
  };
9154
9390
  }
9155
- static div2Power128(num3) {
9156
- return Number(BigInt(num3.toString()) * 1000000n / BigInt(2 ** 128)) / 1e6;
9391
+ static div2Power128(num4) {
9392
+ return Number(BigInt(num4.toString()) * 1000000n / BigInt(2 ** 128)) / 1e6;
9157
9393
  }
9158
9394
  static priceToTick(price, isRoundDown, tickSpacing) {
9159
9395
  const value = isRoundDown ? Math.floor(Math.log(price) / Math.log(1.000001)) : Math.ceil(Math.log(price) / Math.log(1.000001));
9160
9396
  const tick = Math.floor(value / tickSpacing) * tickSpacing;
9161
9397
  return this.tickToi129(tick);
9162
9398
  }
9163
- async getPoolKey() {
9399
+ async getPoolKey(blockIdentifier = "pending") {
9164
9400
  if (this.poolKey) {
9165
9401
  return this.poolKey;
9166
9402
  }
9167
- const result = await this.contract.call("get_settings", []);
9403
+ const result = await this.contract.call("get_settings", [], {
9404
+ blockIdentifier
9405
+ });
9168
9406
  const poolKey = {
9169
9407
  token0: ContractAddr.from(result.pool_key.token0.toString()),
9170
9408
  token1: ContractAddr.from(result.pool_key.token1.toString()),
@@ -9195,25 +9433,24 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9195
9433
  * @param amount1: amount of token1
9196
9434
  * @returns {amount0, amount1}
9197
9435
  */
9198
- async _getExpectedAmountsForLiquidity(amount0, amount1, bounds) {
9436
+ async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
9199
9437
  assert(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
9200
- const poolKey = await this.getPoolKey();
9201
- const sampleLiq = 1e18;
9438
+ const sampleLiq = 1e20;
9202
9439
  const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(sampleLiq.toString(), 18), bounds);
9203
9440
  logger.verbose(`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`);
9204
- assert(!sampleAmount0.eq(0) && !sampleAmount1.eq(0), "Sample amount is 0");
9441
+ assert(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
9205
9442
  const price = await (await this.getCurrentPrice()).price;
9206
9443
  logger.verbose(`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`);
9207
9444
  if (amount1.eq(0) && amount0.greaterThan(0)) {
9208
9445
  if (sampleAmount1.eq(0)) {
9209
9446
  return {
9210
9447
  amount0,
9211
- amount1: Web3Number.fromWei("0", 18),
9448
+ amount1: Web3Number.fromWei("0", amount1.decimals),
9212
9449
  ratio: Infinity
9213
9450
  };
9214
9451
  } else if (sampleAmount0.eq(0)) {
9215
9452
  return {
9216
- amount0: Web3Number.fromWei("0", 18),
9453
+ amount0: Web3Number.fromWei("0", amount0.decimals),
9217
9454
  amount1: amount0.multipliedBy(price),
9218
9455
  ratio: 0
9219
9456
  };
@@ -9221,21 +9458,41 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9221
9458
  } else if (amount0.eq(0) && amount1.greaterThan(0)) {
9222
9459
  if (sampleAmount0.eq(0)) {
9223
9460
  return {
9224
- amount0: Web3Number.fromWei("0", 18),
9461
+ amount0: Web3Number.fromWei("0", amount0.decimals),
9225
9462
  amount1,
9226
9463
  ratio: 0
9227
9464
  };
9228
9465
  } else if (sampleAmount1.eq(0)) {
9229
9466
  return {
9230
9467
  amount0: amount1.dividedBy(price),
9231
- amount1: Web3Number.fromWei("0", 18),
9468
+ amount1: Web3Number.fromWei("0", amount1.decimals),
9232
9469
  ratio: Infinity
9233
9470
  };
9234
9471
  }
9235
9472
  }
9236
- const ratio = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
9473
+ assert(sampleAmount0.decimals == sampleAmount1.decimals, "Sample amounts have different decimals");
9474
+ const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
9475
+ const ratio = Number(ratioWeb3Number.toFixed(18));
9237
9476
  logger.verbose(`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`);
9238
- return this._solveExpectedAmountsEq(amount0, amount1, ratio, price);
9477
+ if (justUseInputAmount)
9478
+ return this._solveExpectedAmountsEq(amount0, amount1, ratioWeb3Number, price);
9479
+ if (amount1.eq(0) && amount0.greaterThan(0)) {
9480
+ const _amount1 = amount0.dividedBy(ratioWeb3Number);
9481
+ return {
9482
+ amount0,
9483
+ amount1: _amount1,
9484
+ ratio
9485
+ };
9486
+ } else if (amount0.eq(0) && amount1.greaterThan(0)) {
9487
+ const _amount0 = amount1.multipliedBy(ratio);
9488
+ return {
9489
+ amount0: _amount0,
9490
+ amount1,
9491
+ ratio
9492
+ };
9493
+ } else {
9494
+ throw new Error("Both amounts are non-zero, cannot compute expected amounts");
9495
+ }
9239
9496
  }
9240
9497
  _solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
9241
9498
  const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
@@ -9249,10 +9506,11 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9249
9506
  async getSwapInfoToHandleUnused(considerRebalance = true) {
9250
9507
  const poolKey = await this.getPoolKey();
9251
9508
  const erc20Mod = new ERC20(this.config);
9252
- const token0Bal1 = await erc20Mod.balanceOf(poolKey.token0, this.address.address, 18);
9253
- const token1Bal1 = await erc20Mod.balanceOf(poolKey.token1, this.address.address, 18);
9254
9509
  const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9255
9510
  const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9511
+ const token0Bal1 = await erc20Mod.balanceOf(poolKey.token0, this.address.address, token0Info.decimals);
9512
+ const token1Bal1 = await erc20Mod.balanceOf(poolKey.token1, this.address.address, token1Info.decimals);
9513
+ logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`);
9256
9514
  const token0Price = await this.pricer.getPrice(token0Info.symbol);
9257
9515
  const token1Price = await this.pricer.getPrice(token1Info.symbol);
9258
9516
  const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
@@ -9263,6 +9521,7 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9263
9521
  let token0Bal = token0Bal1;
9264
9522
  let token1Bal = token1Bal1;
9265
9523
  if (considerRebalance) {
9524
+ logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`);
9266
9525
  const tvl = await this.getTVL();
9267
9526
  token0Bal = token0Bal.plus(tvl.token0.amount.toString());
9268
9527
  token1Bal = token1Bal.plus(tvl.token1.amount.toString());
@@ -9272,7 +9531,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9272
9531
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`);
9273
9532
  const newBounds = await this.getNewBounds();
9274
9533
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`);
9275
- let expectedAmounts = await this._getExpectedAmountsForLiquidity(token0Bal, token1Bal, newBounds);
9534
+ return await this.getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, newBounds);
9535
+ }
9536
+ async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
9537
+ let expectedAmounts = await this._getExpectedAmountsForLiquidity(token0Bal, token1Bal, bounds);
9276
9538
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`);
9277
9539
  let retry = 0;
9278
9540
  const maxRetry = 10;
@@ -9293,6 +9555,19 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9293
9555
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`);
9294
9556
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`);
9295
9557
  logger.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`);
9558
+ if (amountToSell.eq(0)) {
9559
+ return {
9560
+ token_from_address: tokenToSell.address,
9561
+ token_from_amount: import_starknet9.uint256.bnToUint256(0),
9562
+ token_to_address: tokenToSell.address,
9563
+ token_to_amount: import_starknet9.uint256.bnToUint256(0),
9564
+ token_to_min_amount: import_starknet9.uint256.bnToUint256(0),
9565
+ beneficiary: this.address.address,
9566
+ integrator_fee_amount_bps: 0,
9567
+ integrator_fee_recipient: this.address.address,
9568
+ routes: []
9569
+ };
9570
+ }
9296
9571
  const quote = await this.avnu.getQuotes(tokenToSell.address, tokenToBuy.address, amountToSell.toWei(), this.address.address);
9297
9572
  if (remainingSellAmount.eq(0)) {
9298
9573
  const minAmountOut = Web3Number.fromWei(quote.buyAmount.toString(), tokenToBuyInfo.decimals).multipliedBy(0.9999);
@@ -9315,6 +9590,73 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9315
9590
  }
9316
9591
  throw new Error("Failed to get swap info");
9317
9592
  }
9593
+ /**
9594
+ * Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
9595
+ * Uses binary search approach to find optimal swap amount.
9596
+ *
9597
+ * @param newBounds - The new tick bounds to rebalance to
9598
+ * @param swapInfo - Initial swap parameters for rebalancing
9599
+ * @param acc - Account to estimate gas fees with
9600
+ * @param retry - Current retry attempt number (default 0)
9601
+ * @param adjustmentFactor - Percentage to adjust swap amount by (default 1)
9602
+ * @param isToken0Deficit - Whether token0 balance needs increasing (default true)
9603
+ * @returns Array of contract calls needed for rebalancing
9604
+ * @throws Error if max retries reached without successful rebalance
9605
+ */
9606
+ async rebalanceIter(swapInfo, acc, estimateCall, retry = 0, adjustmentFactor = 1, isToken0Deficit = true) {
9607
+ const MAX_RETRIES = 20;
9608
+ const MIN_ADJUSTMENT = 1e-3;
9609
+ logger.verbose(
9610
+ `Rebalancing ${this.metadata.name}: retry=${retry}, adjustment=${adjustmentFactor}%, token0Deficit=${isToken0Deficit}`
9611
+ );
9612
+ const fromAmount = import_starknet9.uint256.uint256ToBN(swapInfo.token_from_amount);
9613
+ logger.verbose(
9614
+ `Selling ${fromAmount.toString()} of token ${swapInfo.token_from_address}`
9615
+ );
9616
+ try {
9617
+ const calls = await estimateCall(swapInfo);
9618
+ await acc.estimateInvokeFee(calls);
9619
+ return calls;
9620
+ } catch (err) {
9621
+ if (retry >= MAX_RETRIES) {
9622
+ logger.error(`Rebalance failed after ${MAX_RETRIES} retries`);
9623
+ throw err;
9624
+ }
9625
+ if (adjustmentFactor < MIN_ADJUSTMENT) {
9626
+ logger.error("Adjustment factor too small, likely oscillating");
9627
+ throw new Error("Failed to converge on valid swap amount");
9628
+ }
9629
+ logger.error(`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`);
9630
+ const newSwapInfo = { ...swapInfo };
9631
+ const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
9632
+ if (err.message.includes("invalid token0 balance") || err.message.includes("invalid token0 amount")) {
9633
+ logger.verbose("Reducing swap amount - excess token0");
9634
+ newSwapInfo.token_from_amount = import_starknet9.uint256.bnToUint256(
9635
+ currentAmount.multipliedBy((100 - adjustmentFactor) / 100).toWei()
9636
+ );
9637
+ adjustmentFactor = isToken0Deficit ? adjustmentFactor * 2 : adjustmentFactor / 2;
9638
+ isToken0Deficit = true;
9639
+ } else if (err.message.includes("invalid token1 balance") || err.message.includes("invalid token1 amount")) {
9640
+ logger.verbose("Increasing swap amount - excess token1");
9641
+ newSwapInfo.token_from_amount = import_starknet9.uint256.bnToUint256(
9642
+ currentAmount.multipliedBy((100 + adjustmentFactor) / 100).toWei()
9643
+ );
9644
+ adjustmentFactor = isToken0Deficit ? adjustmentFactor / 2 : adjustmentFactor * 2;
9645
+ isToken0Deficit = false;
9646
+ } else {
9647
+ logger.error("Unexpected error:", err);
9648
+ }
9649
+ newSwapInfo.token_to_min_amount = import_starknet9.uint256.bnToUint256("0");
9650
+ return this.rebalanceIter(
9651
+ newSwapInfo,
9652
+ acc,
9653
+ estimateCall,
9654
+ retry + 1,
9655
+ adjustmentFactor,
9656
+ isToken0Deficit
9657
+ );
9658
+ }
9659
+ }
9318
9660
  static tickToi129(tick) {
9319
9661
  if (tick < 0) {
9320
9662
  return {
@@ -9337,21 +9679,23 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9337
9679
  static tickToPrice(tick) {
9338
9680
  return Math.pow(1.000001, Number(tick));
9339
9681
  }
9340
- async getLiquidityToAmounts(liquidity, bounds) {
9341
- const currentPrice = await this.getCurrentPrice();
9342
- const lowerPrice = await _EkuboCLVault.tickToPrice(bounds.lowerTick);
9343
- const upperPrice = await _EkuboCLVault.tickToPrice(bounds.upperTick);
9682
+ async getLiquidityToAmounts(liquidity, bounds, blockIdentifier = "pending", _poolKey = null, _currentPrice = null) {
9683
+ const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
9684
+ const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
9685
+ const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
9344
9686
  logger.verbose(`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`);
9345
9687
  const result = await this.ekuboMathContract.call("liquidity_delta_to_amount_delta", [
9346
- import_starknet8.uint256.bnToUint256(_EkuboCLVault.priceToSqrtRatio(currentPrice.price).toString()),
9688
+ import_starknet9.uint256.bnToUint256(currentPrice.sqrtRatio),
9347
9689
  {
9348
9690
  mag: liquidity.toWei(),
9349
9691
  sign: 0
9350
9692
  },
9351
- import_starknet8.uint256.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
9352
- import_starknet8.uint256.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
9353
- ]);
9354
- const poolKey = await this.getPoolKey();
9693
+ import_starknet9.uint256.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
9694
+ import_starknet9.uint256.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
9695
+ ], {
9696
+ blockIdentifier
9697
+ });
9698
+ const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
9355
9699
  const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9356
9700
  const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9357
9701
  const amount0 = Web3Number.fromWei(_EkuboCLVault.i129ToNumber(result.amount0).toString(), token0Info.decimals);
@@ -9361,32 +9705,110 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
9361
9705
  amount1
9362
9706
  };
9363
9707
  }
9708
+ async harvest(acc) {
9709
+ const ekuboHarvests = new EkuboHarvests(this.config);
9710
+ const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(this.address);
9711
+ const poolKey = await this.getPoolKey();
9712
+ const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
9713
+ const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
9714
+ const bounds = await this.getCurrentBounds();
9715
+ const calls = [];
9716
+ for (let claim of unClaimedRewards) {
9717
+ const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
9718
+ const postFeeAmount = claim.claim.amount.minus(fee);
9719
+ const isToken1 = claim.token.eq(poolKey.token1);
9720
+ logger.verbose(`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`);
9721
+ const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
9722
+ const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
9723
+ logger.verbose(`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`);
9724
+ const swapInfo = await this.getSwapInfoGivenAmounts(poolKey, token0Amt, token1Amt, bounds);
9725
+ swapInfo.token_to_address = token0Info.address.address;
9726
+ logger.verbose(`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`);
9727
+ logger.verbose(`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`);
9728
+ const harvestEstimateCall = async (swapInfo1) => {
9729
+ const swap1Amount = Web3Number.fromWei(import_starknet9.uint256.uint256ToBN(swapInfo1.token_from_amount).toString(), 18);
9730
+ const remainingAmount = postFeeAmount.minus(swap1Amount);
9731
+ const swapInfo2 = { ...swapInfo, token_from_amount: import_starknet9.uint256.bnToUint256(remainingAmount.toWei()) };
9732
+ swapInfo2.token_to_address = token1Info.address.address;
9733
+ const calldata = [
9734
+ claim.rewardsContract.address,
9735
+ {
9736
+ id: claim.claim.id,
9737
+ amount: claim.claim.amount.toWei(),
9738
+ claimee: claim.claim.claimee.address
9739
+ },
9740
+ claim.proof.map((p) => import_starknet9.num.getDecimalString(p)),
9741
+ swapInfo,
9742
+ swapInfo2
9743
+ ];
9744
+ logger.verbose(`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(calldata)}`);
9745
+ return [this.contract.populate("harvest", calldata)];
9746
+ };
9747
+ const _callsFinal = await this.rebalanceIter(swapInfo, acc, harvestEstimateCall);
9748
+ logger.verbose(`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(_callsFinal)}`);
9749
+ calls.push(..._callsFinal);
9750
+ }
9751
+ return calls;
9752
+ }
9753
+ async getInvestmentFlows() {
9754
+ const netYield = await this.netAPY();
9755
+ const poolKey = await this.getPoolKey();
9756
+ const linkedFlow = {
9757
+ title: this.metadata.name,
9758
+ subItems: [{ key: "Pool", value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing` }],
9759
+ linkedFlows: [],
9760
+ style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
9761
+ };
9762
+ const baseFlow = {
9763
+ id: "base",
9764
+ title: "Your Deposit",
9765
+ subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }, { key: `Performance Fee`, value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%` }],
9766
+ linkedFlows: [linkedFlow],
9767
+ style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
9768
+ };
9769
+ const rebalanceFlow = {
9770
+ id: "rebalance",
9771
+ title: "Automated Rebalance",
9772
+ subItems: [{
9773
+ key: "Range selection",
9774
+ value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
9775
+ }],
9776
+ linkedFlows: [linkedFlow],
9777
+ style: { backgroundColor: "purple" /* Green */.valueOf() }
9778
+ };
9779
+ return [baseFlow, rebalanceFlow];
9780
+ }
9364
9781
  };
9365
- 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.";
9782
+ 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.";
9366
9783
  var _protocol2 = { name: "Ekubo", logo: "https://app.ekubo.org/favicon.ico" };
9367
9784
  var _riskFactor2 = [
9368
9785
  { type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
9369
9786
  { type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
9370
9787
  ];
9788
+ var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
9371
9789
  var EkuboCLVaultStrategies = [{
9372
9790
  name: "Ekubo xSTRK/STRK",
9373
- description: _description2,
9791
+ description: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK"),
9374
9792
  address: ContractAddr.from("0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"),
9375
9793
  type: "Other",
9376
- depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "STRK"), Global.getDefaultTokens().find((t) => t.symbol === "xSTRK")],
9794
+ // must be same order as poolKey token0 and token1
9795
+ depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"), Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
9377
9796
  protocols: [_protocol2],
9797
+ auditUrl: AUDIT_URL2,
9378
9798
  maxTVL: Web3Number.fromWei("0", 18),
9379
9799
  risk: {
9380
9800
  riskFactor: _riskFactor2,
9381
9801
  netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
9382
9802
  notARisks: getNoRiskTags(_riskFactor2)
9383
9803
  },
9804
+ apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
9384
9805
  additionalInfo: {
9385
9806
  newBounds: {
9386
9807
  lower: -1,
9387
9808
  upper: 1
9388
9809
  },
9389
- lstContract: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a")
9810
+ lstContract: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"),
9811
+ feeBps: 1e3
9390
9812
  }
9391
9813
  }];
9392
9814
 
@@ -9496,7 +9918,7 @@ var PricerRedis = class extends Pricer {
9496
9918
 
9497
9919
  // src/utils/store.ts
9498
9920
  var import_fs = __toESM(require("fs"));
9499
- var import_starknet9 = require("starknet");
9921
+ var import_starknet10 = require("starknet");
9500
9922
  var crypto2 = __toESM(require("crypto"));
9501
9923
 
9502
9924
  // src/utils/encrypt.ts
@@ -9581,7 +10003,7 @@ var Store = class _Store {
9581
10003
  logger.warn(`This not stored anywhere, please you backup this password for future use`);
9582
10004
  logger.warn(`\u26A0\uFE0F=========================================\u26A0\uFE0F`);
9583
10005
  }
9584
- getAccount(accountKey, txVersion = import_starknet9.constants.TRANSACTION_VERSION.V2) {
10006
+ getAccount(accountKey, txVersion = import_starknet10.constants.TRANSACTION_VERSION.V2) {
9585
10007
  const accounts = this.loadAccounts();
9586
10008
  logger.verbose(`nAccounts loaded for network: ${Object.keys(accounts).length}`);
9587
10009
  const data = accounts[accountKey];
@@ -9590,7 +10012,7 @@ var Store = class _Store {
9590
10012
  }
9591
10013
  logger.verbose(`Account loaded: ${accountKey} from network: ${this.config.network}`);
9592
10014
  logger.verbose(`Address: ${data.address}`);
9593
- const acc = new import_starknet9.Account(this.config.provider, data.address, data.pk, void 0, txVersion);
10015
+ const acc = new import_starknet10.Account(this.config.provider, data.address, data.pk, void 0, txVersion);
9594
10016
  return acc;
9595
10017
  }
9596
10018
  addAccount(accountKey, address, pk) {
@@ -9655,6 +10077,7 @@ var Store = class _Store {
9655
10077
  0 && (module.exports = {
9656
10078
  AutoCompounderSTRK,
9657
10079
  AvnuWrapper,
10080
+ BaseStrategy,
9658
10081
  ContractAddr,
9659
10082
  ERC20,
9660
10083
  EkuboCLVault,