@strkfarm/sdk 1.0.28 → 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/cli.js +48 -24
- package/dist/cli.mjs +46 -22
- package/dist/index.browser.global.js +496 -75
- package/dist/index.browser.mjs +504 -82
- package/dist/index.d.ts +56 -15
- package/dist/index.js +511 -88
- package/dist/index.mjs +506 -84
- package/package.json +1 -1
- package/src/dataTypes/_bignumber.ts +15 -7
- package/src/global.ts +13 -6
- package/src/interfaces/common.ts +3 -0
- package/src/modules/avnu.ts +74 -59
- package/src/modules/harvests.ts +74 -0
- package/src/modules/pricer-from-api.ts +9 -8
- package/src/modules/zkLend.ts +2 -1
- package/src/strategies/base-strategy.ts +3 -3
- package/src/strategies/ekubo-cl-vault.ts +477 -55
- package/src/strategies/index.ts +2 -1
- package/src/strategies/vesu-rebalance.ts +2 -2
|
@@ -6912,6 +6912,7 @@ var strkfarm_risk_engine = (() => {
|
|
|
6912
6912
|
__export(index_browser_exports, {
|
|
6913
6913
|
AutoCompounderSTRK: () => AutoCompounderSTRK,
|
|
6914
6914
|
AvnuWrapper: () => AvnuWrapper,
|
|
6915
|
+
BaseStrategy: () => BaseStrategy,
|
|
6915
6916
|
ContractAddr: () => ContractAddr,
|
|
6916
6917
|
ERC20: () => ERC20,
|
|
6917
6918
|
EkuboCLVault: () => EkuboCLVault,
|
|
@@ -9435,19 +9436,19 @@ var strkfarm_risk_engine = (() => {
|
|
|
9435
9436
|
return this.mul(10 ** this.decimals).toFixed(0);
|
|
9436
9437
|
}
|
|
9437
9438
|
multipliedBy(value) {
|
|
9438
|
-
|
|
9439
|
+
const _value = this.getStandardString(value);
|
|
9439
9440
|
return this.construct(this.mul(_value).toString(), this.decimals);
|
|
9440
9441
|
}
|
|
9441
9442
|
dividedBy(value) {
|
|
9442
|
-
|
|
9443
|
+
const _value = this.getStandardString(value);
|
|
9443
9444
|
return this.construct(this.div(_value).toString(), this.decimals);
|
|
9444
9445
|
}
|
|
9445
9446
|
plus(value) {
|
|
9446
|
-
const _value =
|
|
9447
|
+
const _value = this.getStandardString(value);
|
|
9447
9448
|
return this.construct(this.add(_value).toString(), this.decimals);
|
|
9448
9449
|
}
|
|
9449
9450
|
minus(n, base2) {
|
|
9450
|
-
const _value =
|
|
9451
|
+
const _value = this.getStandardString(n);
|
|
9451
9452
|
return this.construct(super.minus(_value, base2).toString(), this.decimals);
|
|
9452
9453
|
}
|
|
9453
9454
|
construct(value, decimals) {
|
|
@@ -9463,11 +9464,17 @@ var strkfarm_risk_engine = (() => {
|
|
|
9463
9464
|
return this.toString();
|
|
9464
9465
|
}
|
|
9465
9466
|
maxToFixedDecimals() {
|
|
9466
|
-
return Math.min(this.decimals,
|
|
9467
|
+
return Math.min(this.decimals, 18);
|
|
9468
|
+
}
|
|
9469
|
+
getStandardString(value) {
|
|
9470
|
+
if (typeof value == "string") {
|
|
9471
|
+
return value;
|
|
9472
|
+
}
|
|
9473
|
+
return value.toFixed(this.maxToFixedDecimals());
|
|
9467
9474
|
}
|
|
9468
9475
|
};
|
|
9469
|
-
import_bignumber.default.config({ DECIMAL_PLACES: 18 });
|
|
9470
|
-
_Web3Number.config({ DECIMAL_PLACES: 18 });
|
|
9476
|
+
import_bignumber.default.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: import_bignumber.default.ROUND_DOWN });
|
|
9477
|
+
_Web3Number.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: import_bignumber.default.ROUND_DOWN });
|
|
9471
9478
|
|
|
9472
9479
|
// src/dataTypes/bignumber.browser.ts
|
|
9473
9480
|
var Web3Number = class _Web3Number2 extends _Web3Number {
|
|
@@ -27218,42 +27225,48 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
27218
27225
|
logo: "https://assets.coingecko.com/coins/images/26433/small/starknet.png",
|
|
27219
27226
|
address: ContractAddr.from("0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"),
|
|
27220
27227
|
decimals: 18,
|
|
27221
|
-
coingeckId: "starknet"
|
|
27228
|
+
coingeckId: "starknet",
|
|
27229
|
+
displayDecimals: 2
|
|
27222
27230
|
}, {
|
|
27223
27231
|
name: "xSTRK",
|
|
27224
27232
|
symbol: "xSTRK",
|
|
27225
27233
|
logo: "https://dashboard.endur.fi/endur-fi.svg",
|
|
27226
27234
|
address: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"),
|
|
27227
27235
|
decimals: 18,
|
|
27228
|
-
coingeckId: void 0
|
|
27236
|
+
coingeckId: void 0,
|
|
27237
|
+
displayDecimals: 2
|
|
27229
27238
|
}, {
|
|
27230
27239
|
name: "ETH",
|
|
27231
27240
|
symbol: "ETH",
|
|
27232
27241
|
logo: "https://opbnb.bscscan.com/token/images/ether.svg",
|
|
27233
27242
|
address: ContractAddr.from("0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"),
|
|
27234
27243
|
decimals: 18,
|
|
27235
|
-
coingeckId: void 0
|
|
27244
|
+
coingeckId: void 0,
|
|
27245
|
+
displayDecimals: 4
|
|
27236
27246
|
}, {
|
|
27237
27247
|
name: "USDC",
|
|
27238
27248
|
symbol: "USDC",
|
|
27239
27249
|
logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png",
|
|
27240
27250
|
address: ContractAddr.from("0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8"),
|
|
27241
27251
|
decimals: 6,
|
|
27242
|
-
coingeckId: void 0
|
|
27252
|
+
coingeckId: void 0,
|
|
27253
|
+
displayDecimals: 2
|
|
27243
27254
|
}, {
|
|
27244
27255
|
name: "USDT",
|
|
27245
27256
|
symbol: "USDT",
|
|
27246
27257
|
logo: "https://assets.coingecko.com/coins/images/325/small/Tether.png",
|
|
27247
27258
|
address: ContractAddr.from("0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8"),
|
|
27248
27259
|
decimals: 6,
|
|
27249
|
-
coingeckId: void 0
|
|
27260
|
+
coingeckId: void 0,
|
|
27261
|
+
displayDecimals: 2
|
|
27250
27262
|
}, {
|
|
27251
27263
|
name: "WBTC",
|
|
27252
27264
|
symbol: "WBTC",
|
|
27253
27265
|
logo: "https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png",
|
|
27254
27266
|
address: ContractAddr.from("0x3fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac"),
|
|
27255
27267
|
decimals: 8,
|
|
27256
|
-
coingeckId: void 0
|
|
27268
|
+
coingeckId: void 0,
|
|
27269
|
+
displayDecimals: 6
|
|
27257
27270
|
}];
|
|
27258
27271
|
var tokens = defaultTokens;
|
|
27259
27272
|
var Global = class _Global {
|
|
@@ -27285,7 +27298,8 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
27285
27298
|
address: ContractAddr.from(token.address),
|
|
27286
27299
|
decimals: token.decimals,
|
|
27287
27300
|
logo: token.logoUri,
|
|
27288
|
-
coingeckId: token.extensions.coingeckoId
|
|
27301
|
+
coingeckId: token.extensions.coingeckoId,
|
|
27302
|
+
displayDecimals: 2
|
|
27289
27303
|
});
|
|
27290
27304
|
});
|
|
27291
27305
|
console.log(tokens);
|
|
@@ -27655,7 +27669,8 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
27655
27669
|
logo: "",
|
|
27656
27670
|
decimals: pool.token.decimals,
|
|
27657
27671
|
borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
|
|
27658
|
-
collareralFactor
|
|
27672
|
+
collareralFactor,
|
|
27673
|
+
displayDecimals: 2
|
|
27659
27674
|
};
|
|
27660
27675
|
this.tokens.push(token);
|
|
27661
27676
|
});
|
|
@@ -27771,7 +27786,7 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
27771
27786
|
try {
|
|
27772
27787
|
return await this.getPriceFromMyAPI(tokenSymbol);
|
|
27773
27788
|
} catch (e) {
|
|
27774
|
-
logger2.warn("getPriceFromMyAPI error", e);
|
|
27789
|
+
logger2.warn("getPriceFromMyAPI error", JSON.stringify(e.message || e));
|
|
27775
27790
|
}
|
|
27776
27791
|
logger2.log("getPrice coinbase", tokenSymbol);
|
|
27777
27792
|
let retry = 0;
|
|
@@ -27791,7 +27806,7 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
27791
27806
|
timestamp: /* @__PURE__ */ new Date()
|
|
27792
27807
|
};
|
|
27793
27808
|
} catch (e) {
|
|
27794
|
-
logger2.warn("getPrice coinbase error", e
|
|
27809
|
+
logger2.warn("getPrice coinbase error", JSON.stringify(e.message || e));
|
|
27795
27810
|
await new Promise((resolve) => setTimeout(resolve, retry * 1e3));
|
|
27796
27811
|
}
|
|
27797
27812
|
}
|
|
@@ -27805,9 +27820,6 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
27805
27820
|
const priceInfo = await priceInfoRes.json();
|
|
27806
27821
|
const now = /* @__PURE__ */ new Date();
|
|
27807
27822
|
const priceTime = new Date(priceInfo.timestamp);
|
|
27808
|
-
if (now.getTime() - priceTime.getTime() > 9e5) {
|
|
27809
|
-
throw new Error("Price is stale");
|
|
27810
|
-
}
|
|
27811
27823
|
const price = Number(priceInfo.price);
|
|
27812
27824
|
return {
|
|
27813
27825
|
price,
|
|
@@ -29280,17 +29292,28 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
29280
29292
|
}
|
|
29281
29293
|
|
|
29282
29294
|
// src/modules/avnu.ts
|
|
29283
|
-
var AvnuWrapper = class {
|
|
29284
|
-
async getQuotes(fromToken, toToken, amountWei, taker) {
|
|
29295
|
+
var AvnuWrapper = class _AvnuWrapper {
|
|
29296
|
+
async getQuotes(fromToken, toToken, amountWei, taker, retry = 0) {
|
|
29297
|
+
const MAX_RETRY = 5;
|
|
29298
|
+
logger2.verbose(`${_AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
|
|
29285
29299
|
const params = {
|
|
29286
29300
|
sellTokenAddress: fromToken,
|
|
29287
29301
|
buyTokenAddress: toToken,
|
|
29288
29302
|
sellAmount: amountWei,
|
|
29289
|
-
takerAddress: taker
|
|
29303
|
+
takerAddress: taker,
|
|
29304
|
+
// excludeSources: ['Nostra', 'Haiko(Solvers)']
|
|
29305
|
+
excludeSources: ["Haiko(Solvers)"]
|
|
29306
|
+
// to resolve InvalidOraclePrice error
|
|
29290
29307
|
};
|
|
29291
29308
|
assert3(fromToken != toToken, "From and to tokens are the same");
|
|
29292
29309
|
const quotes = await fetchQuotes(params);
|
|
29293
|
-
|
|
29310
|
+
if (quotes.length == 0) {
|
|
29311
|
+
if (retry < MAX_RETRY) {
|
|
29312
|
+
await new Promise((res) => setTimeout(res, 3e3));
|
|
29313
|
+
return await this.getQuotes(fromToken, toToken, amountWei, taker, retry + 1);
|
|
29314
|
+
}
|
|
29315
|
+
throw new Error("no quotes found");
|
|
29316
|
+
}
|
|
29294
29317
|
return quotes[0];
|
|
29295
29318
|
}
|
|
29296
29319
|
async getSwapInfo(quote, taker, integratorFeeBps, integratorFeeRecipient, minAmount) {
|
|
@@ -30958,10 +30981,10 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
30958
30981
|
async getTVL() {
|
|
30959
30982
|
throw new Error("Not implemented");
|
|
30960
30983
|
}
|
|
30961
|
-
depositCall(amountInfo, receiver) {
|
|
30984
|
+
async depositCall(amountInfo, receiver) {
|
|
30962
30985
|
throw new Error("Not implemented");
|
|
30963
30986
|
}
|
|
30964
|
-
withdrawCall(amountInfo, receiver, owner) {
|
|
30987
|
+
async withdrawCall(amountInfo, receiver, owner) {
|
|
30965
30988
|
throw new Error("Not implemented");
|
|
30966
30989
|
}
|
|
30967
30990
|
};
|
|
@@ -30997,7 +31020,7 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
30997
31020
|
* @param receiver - Address that will receive the strategy tokens
|
|
30998
31021
|
* @returns Populated contract call for deposit
|
|
30999
31022
|
*/
|
|
31000
|
-
depositCall(amountInfo, receiver) {
|
|
31023
|
+
async depositCall(amountInfo, receiver) {
|
|
31001
31024
|
assert3(amountInfo.tokenInfo.address.eq(this.asset().address), "Deposit token mismatch");
|
|
31002
31025
|
const assetContract = new Contract(vesu_rebalance_abi_default, this.asset().address.address, this.config.provider);
|
|
31003
31026
|
const call1 = assetContract.populate("approve", [this.address.address, uint256_exports.bnToUint256(amountInfo.amount.toWei())]);
|
|
@@ -31011,7 +31034,7 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
31011
31034
|
* @param owner - Address that owns the strategy tokens
|
|
31012
31035
|
* @returns Populated contract call for withdrawal
|
|
31013
31036
|
*/
|
|
31014
|
-
withdrawCall(amountInfo, receiver, owner) {
|
|
31037
|
+
async withdrawCall(amountInfo, receiver, owner) {
|
|
31015
31038
|
return [this.contract.populate("withdraw", [uint256_exports.bnToUint256(amountInfo.amount.toWei()), receiver.address, owner.address])];
|
|
31016
31039
|
}
|
|
31017
31040
|
/**
|
|
@@ -36312,6 +36335,57 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36312
36335
|
}
|
|
36313
36336
|
];
|
|
36314
36337
|
|
|
36338
|
+
// src/modules/harvests.ts
|
|
36339
|
+
var Harvests = class _Harvests {
|
|
36340
|
+
constructor(config3) {
|
|
36341
|
+
this.config = config3;
|
|
36342
|
+
}
|
|
36343
|
+
getHarvests(addr) {
|
|
36344
|
+
throw new Error("Not implemented");
|
|
36345
|
+
}
|
|
36346
|
+
async getUnHarvestedRewards(addr) {
|
|
36347
|
+
const rewards = await this.getHarvests(addr);
|
|
36348
|
+
if (rewards.length == 0) return [];
|
|
36349
|
+
const unClaimed = [];
|
|
36350
|
+
const cls = await this.config.provider.getClassAt(rewards[0].rewardsContract.address);
|
|
36351
|
+
for (let reward of rewards) {
|
|
36352
|
+
const contract = new Contract(cls.abi, reward.rewardsContract.address, this.config.provider);
|
|
36353
|
+
const isClaimed = await contract.call("is_claimed", [reward.claim.id]);
|
|
36354
|
+
logger2.verbose(`${_Harvests.name}: isClaimed: ${isClaimed}`);
|
|
36355
|
+
if (isClaimed)
|
|
36356
|
+
return unClaimed;
|
|
36357
|
+
unClaimed.unshift(reward);
|
|
36358
|
+
}
|
|
36359
|
+
return unClaimed;
|
|
36360
|
+
}
|
|
36361
|
+
};
|
|
36362
|
+
var EkuboHarvests = class extends Harvests {
|
|
36363
|
+
async getHarvests(addr) {
|
|
36364
|
+
const STRK = "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d";
|
|
36365
|
+
const EKUBO_API = `https://starknet-mainnet-api.ekubo.org/airdrops/${addr.address}?token=${STRK}`;
|
|
36366
|
+
const resultEkubo = await fetch(EKUBO_API);
|
|
36367
|
+
const items = await resultEkubo.json();
|
|
36368
|
+
const rewards = [];
|
|
36369
|
+
for (let i = 0; i < items.length; ++i) {
|
|
36370
|
+
const info = items[i];
|
|
36371
|
+
assert3(info.token == STRK, "expected strk token only");
|
|
36372
|
+
rewards.push({
|
|
36373
|
+
rewardsContract: ContractAddr.from(info.contract_address),
|
|
36374
|
+
token: ContractAddr.from(STRK),
|
|
36375
|
+
startDate: new Date(info.start_date),
|
|
36376
|
+
endDate: new Date(info.end_date),
|
|
36377
|
+
claim: {
|
|
36378
|
+
id: info.claim.id,
|
|
36379
|
+
amount: Web3Number.fromWei(info.claim.amount, 18),
|
|
36380
|
+
claimee: ContractAddr.from(info.claim.claimee)
|
|
36381
|
+
},
|
|
36382
|
+
proof: info.proof
|
|
36383
|
+
});
|
|
36384
|
+
}
|
|
36385
|
+
return rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
|
|
36386
|
+
}
|
|
36387
|
+
};
|
|
36388
|
+
|
|
36315
36389
|
// src/strategies/ekubo-cl-vault.ts
|
|
36316
36390
|
var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
36317
36391
|
/**
|
|
@@ -36336,11 +36410,66 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36336
36410
|
this.ekuboMathContract = new Contract(ekubo_math_abi_default, EKUBO_MATH, this.config.provider);
|
|
36337
36411
|
this.avnu = new AvnuWrapper();
|
|
36338
36412
|
}
|
|
36339
|
-
|
|
36340
|
-
|
|
36413
|
+
async matchInputAmounts(amountInfo) {
|
|
36414
|
+
const bounds = await this.getCurrentBounds();
|
|
36415
|
+
const res = await this._getExpectedAmountsForLiquidity(
|
|
36416
|
+
amountInfo.token0.amount,
|
|
36417
|
+
amountInfo.token1.amount,
|
|
36418
|
+
bounds,
|
|
36419
|
+
false
|
|
36420
|
+
);
|
|
36421
|
+
return {
|
|
36422
|
+
token0: {
|
|
36423
|
+
tokenInfo: amountInfo.token0.tokenInfo,
|
|
36424
|
+
amount: res.amount0
|
|
36425
|
+
},
|
|
36426
|
+
token1: {
|
|
36427
|
+
tokenInfo: amountInfo.token1.tokenInfo,
|
|
36428
|
+
amount: res.amount1
|
|
36429
|
+
}
|
|
36430
|
+
};
|
|
36341
36431
|
}
|
|
36342
|
-
|
|
36343
|
-
|
|
36432
|
+
/** Returns minimum amounts give given two amounts based on what can be added for liq */
|
|
36433
|
+
async getMinDepositAmounts(amountInfo) {
|
|
36434
|
+
const shares = await this.tokensToShares(amountInfo);
|
|
36435
|
+
const { amount0, amount1 } = await this.contract.call("convert_to_assets", [uint256_exports.bnToUint256(shares.toWei())]);
|
|
36436
|
+
return {
|
|
36437
|
+
token0: {
|
|
36438
|
+
tokenInfo: amountInfo.token0.tokenInfo,
|
|
36439
|
+
amount: Web3Number.fromWei(amount0.toString(), amountInfo.token0.tokenInfo.decimals)
|
|
36440
|
+
},
|
|
36441
|
+
token1: {
|
|
36442
|
+
tokenInfo: amountInfo.token1.tokenInfo,
|
|
36443
|
+
amount: Web3Number.fromWei(amount1.toString(), amountInfo.token1.tokenInfo.decimals)
|
|
36444
|
+
}
|
|
36445
|
+
};
|
|
36446
|
+
}
|
|
36447
|
+
async depositCall(amountInfo, receiver) {
|
|
36448
|
+
const updateAmountInfo = await this.getMinDepositAmounts(amountInfo);
|
|
36449
|
+
const token0Contract = new Contract(erc4626_abi_default, amountInfo.token0.tokenInfo.address.address, this.config.provider);
|
|
36450
|
+
const token1Contract = new Contract(erc4626_abi_default, amountInfo.token1.tokenInfo.address.address, this.config.provider);
|
|
36451
|
+
const call1 = token0Contract.populate("approve", [this.address.address, uint256_exports.bnToUint256(updateAmountInfo.token0.amount.toWei())]);
|
|
36452
|
+
const call2 = token1Contract.populate("approve", [this.address.address, uint256_exports.bnToUint256(updateAmountInfo.token1.amount.toWei())]);
|
|
36453
|
+
const call3 = this.contract.populate("deposit", [uint256_exports.bnToUint256(updateAmountInfo.token0.amount.toWei()), uint256_exports.bnToUint256(updateAmountInfo.token1.amount.toWei()), receiver.address]);
|
|
36454
|
+
const calls = [];
|
|
36455
|
+
if (updateAmountInfo.token0.amount.greaterThan(0)) calls.push(call1);
|
|
36456
|
+
if (updateAmountInfo.token1.amount.greaterThan(0)) calls.push(call2);
|
|
36457
|
+
return [...calls, call3];
|
|
36458
|
+
}
|
|
36459
|
+
async tokensToShares(amountInfo) {
|
|
36460
|
+
const shares = await this.contract.call("convert_to_shares", [
|
|
36461
|
+
uint256_exports.bnToUint256(amountInfo.token0.amount.toWei()),
|
|
36462
|
+
uint256_exports.bnToUint256(amountInfo.token1.amount.toWei())
|
|
36463
|
+
]);
|
|
36464
|
+
return Web3Number.fromWei(shares.toString(), 18);
|
|
36465
|
+
}
|
|
36466
|
+
async withdrawCall(amountInfo, receiver, owner) {
|
|
36467
|
+
const shares = await this.tokensToShares(amountInfo);
|
|
36468
|
+
logger2.verbose(`${_EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`);
|
|
36469
|
+
return [this.contract.populate("withdraw", [
|
|
36470
|
+
uint256_exports.bnToUint256(shares.toWei()),
|
|
36471
|
+
receiver.address
|
|
36472
|
+
])];
|
|
36344
36473
|
}
|
|
36345
36474
|
rebalanceCall(newBounds, swapParams) {
|
|
36346
36475
|
return [this.contract.populate("rebalance", [
|
|
@@ -36359,14 +36488,113 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36359
36488
|
handleFeesCall() {
|
|
36360
36489
|
return [this.contract.populate("handle_fees", [])];
|
|
36361
36490
|
}
|
|
36362
|
-
|
|
36363
|
-
|
|
36491
|
+
/**
|
|
36492
|
+
* Calculates assets before and now in a given token of TVL per share to observe growth
|
|
36493
|
+
* @returns {Promise<number>} The weighted average APY across all pools
|
|
36494
|
+
*/
|
|
36495
|
+
async netAPY(blockIdentifier = "pending", sinceBlocks = 2e4) {
|
|
36496
|
+
const tvlNow = await this._getTVL(blockIdentifier);
|
|
36497
|
+
const supplyNow = await this.totalSupply(blockIdentifier);
|
|
36498
|
+
const priceNow = await this.getCurrentPrice(blockIdentifier);
|
|
36499
|
+
let blockNow = typeof blockIdentifier == "number" ? blockIdentifier : (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
36500
|
+
const blockNowTime = typeof blockIdentifier == "number" ? (await this.config.provider.getBlockWithTxs(blockIdentifier)).timestamp : (/* @__PURE__ */ new Date()).getTime() / 1e3;
|
|
36501
|
+
const blockBefore = blockNow - sinceBlocks;
|
|
36502
|
+
const adjustedSupplyNow = supplyNow.minus(await this.getHarvestRewardShares(blockBefore, blockNow));
|
|
36503
|
+
let blockBeforeInfo = await this.config.provider.getBlockWithTxs(blockBefore);
|
|
36504
|
+
const tvlBefore = await this._getTVL(blockBefore);
|
|
36505
|
+
const supplyBefore = await this.totalSupply(blockBefore);
|
|
36506
|
+
const priceBefore = await this.getCurrentPrice(blockBefore);
|
|
36507
|
+
const tvlInToken0Now = tvlNow.amount0.multipliedBy(priceNow.price).plus(tvlNow.amount1);
|
|
36508
|
+
const tvlPerShareNow = tvlInToken0Now.multipliedBy(1e18).dividedBy(adjustedSupplyNow);
|
|
36509
|
+
const tvlInToken0Bf = tvlBefore.amount0.multipliedBy(priceBefore.price).plus(tvlBefore.amount1);
|
|
36510
|
+
const tvlPerShareBf = tvlInToken0Bf.multipliedBy(1e18).dividedBy(supplyBefore);
|
|
36511
|
+
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
36512
|
+
logger2.verbose(`tvlInToken0Now: ${tvlInToken0Now.toString()}`);
|
|
36513
|
+
logger2.verbose(`tvlInToken0Bf: ${tvlInToken0Bf.toString()}`);
|
|
36514
|
+
logger2.verbose(`tvlPerShareNow: ${tvlPerShareNow.toString()}`);
|
|
36515
|
+
logger2.verbose(`tvlPerShareBf: ${tvlPerShareBf.toString()}`);
|
|
36516
|
+
logger2.verbose(`Price before: ${priceBefore.price.toString()}`);
|
|
36517
|
+
logger2.verbose(`Price now: ${priceNow.price.toString()}`);
|
|
36518
|
+
logger2.verbose(`Supply before: ${supplyBefore.toString()}`);
|
|
36519
|
+
logger2.verbose(`Supply now: ${adjustedSupplyNow.toString()}`);
|
|
36520
|
+
logger2.verbose(`Time diff in seconds: ${timeDiffSeconds}`);
|
|
36521
|
+
const apyForGivenBlocks = Number(tvlPerShareNow.minus(tvlPerShareBf).multipliedBy(1e4).dividedBy(tvlPerShareBf)) / 1e4;
|
|
36522
|
+
return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
|
|
36523
|
+
}
|
|
36524
|
+
async getHarvestRewardShares(fromBlock, toBlock) {
|
|
36525
|
+
const len = Number(await this.contract.call("get_total_rewards"));
|
|
36526
|
+
let shares = Web3Number.fromWei(0, 18);
|
|
36527
|
+
for (let i = len - 1; i > 0; --i) {
|
|
36528
|
+
let record = await this.contract.call("get_rewards_info", [i]);
|
|
36529
|
+
logger2.verbose(`${_EkuboCLVault.name}: getHarvestRewardShares: ${i}`);
|
|
36530
|
+
console.log(record);
|
|
36531
|
+
const block = Number(record.block_number);
|
|
36532
|
+
if (block < fromBlock) {
|
|
36533
|
+
return shares;
|
|
36534
|
+
} else if (block > toBlock) {
|
|
36535
|
+
continue;
|
|
36536
|
+
} else {
|
|
36537
|
+
shares = shares.plus(Web3Number.fromWei(record.shares.toString(), 18));
|
|
36538
|
+
}
|
|
36539
|
+
logger2.verbose(`${_EkuboCLVault.name}: getHarvestRewardShares: ${i} => ${shares.toWei()}`);
|
|
36540
|
+
}
|
|
36541
|
+
return shares;
|
|
36364
36542
|
}
|
|
36365
|
-
async
|
|
36366
|
-
|
|
36367
|
-
|
|
36368
|
-
|
|
36369
|
-
|
|
36543
|
+
async balanceOf(user, blockIdentifier = "pending") {
|
|
36544
|
+
let bal = await this.contract.call("balance_of", [user.address]);
|
|
36545
|
+
return Web3Number.fromWei(bal.toString(), 18);
|
|
36546
|
+
}
|
|
36547
|
+
async getUserTVL(user, blockIdentifier = "pending") {
|
|
36548
|
+
let bal = await this.balanceOf(user, blockIdentifier);
|
|
36549
|
+
const assets = await this.contract.call("convert_to_assets", [uint256_exports.bnToUint256(bal.toWei())], {
|
|
36550
|
+
blockIdentifier
|
|
36551
|
+
});
|
|
36552
|
+
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
36553
|
+
this.assertValidDepositTokens(poolKey);
|
|
36554
|
+
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
36555
|
+
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
36556
|
+
const amount0 = Web3Number.fromWei(assets.amount0.toString(), token0Info.decimals);
|
|
36557
|
+
const amount1 = Web3Number.fromWei(assets.amount1.toString(), token1Info.decimals);
|
|
36558
|
+
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
36559
|
+
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
36560
|
+
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
36561
|
+
const token1Usd = Number(amount1.toFixed(13)) * P1.price;
|
|
36562
|
+
return {
|
|
36563
|
+
usdValue: token0Usd + token1Usd,
|
|
36564
|
+
token0: {
|
|
36565
|
+
tokenInfo: token0Info,
|
|
36566
|
+
amount: amount0,
|
|
36567
|
+
usdValue: token0Usd
|
|
36568
|
+
},
|
|
36569
|
+
token1: {
|
|
36570
|
+
tokenInfo: token1Info,
|
|
36571
|
+
amount: amount1,
|
|
36572
|
+
usdValue: token1Usd
|
|
36573
|
+
}
|
|
36574
|
+
};
|
|
36575
|
+
}
|
|
36576
|
+
async _getTVL(blockIdentifier = "pending") {
|
|
36577
|
+
const result = await this.contract.call("total_liquidity", [], {
|
|
36578
|
+
blockIdentifier
|
|
36579
|
+
});
|
|
36580
|
+
const bounds = await this.getCurrentBounds(blockIdentifier);
|
|
36581
|
+
const { amount0, amount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(result.toString(), 18), bounds, blockIdentifier);
|
|
36582
|
+
return { amount0, amount1 };
|
|
36583
|
+
}
|
|
36584
|
+
async totalSupply(blockIdentifier = "pending") {
|
|
36585
|
+
const res = await this.contract.call("total_supply", [], {
|
|
36586
|
+
blockIdentifier
|
|
36587
|
+
});
|
|
36588
|
+
return Web3Number.fromWei(res.toString(), 18);
|
|
36589
|
+
}
|
|
36590
|
+
assertValidDepositTokens(poolKey) {
|
|
36591
|
+
assert3(poolKey.token0.eq(this.metadata.depositTokens[0].address), "Expected token0 in depositTokens[0]");
|
|
36592
|
+
assert3(poolKey.token1.eq(this.metadata.depositTokens[1].address), "Expected token1 in depositTokens[1]");
|
|
36593
|
+
}
|
|
36594
|
+
async getTVL(blockIdentifier = "pending") {
|
|
36595
|
+
const { amount0, amount1 } = await this._getTVL(blockIdentifier);
|
|
36596
|
+
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
36597
|
+
this.assertValidDepositTokens(poolKey);
|
|
36370
36598
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
36371
36599
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
36372
36600
|
const P0 = await this.pricer.getPrice(token0Info.symbol);
|
|
@@ -36374,7 +36602,7 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36374
36602
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
36375
36603
|
const token1Usd = Number(amount1.toFixed(13)) * P1.price;
|
|
36376
36604
|
return {
|
|
36377
|
-
|
|
36605
|
+
usdValue: token0Usd + token1Usd,
|
|
36378
36606
|
token0: {
|
|
36379
36607
|
tokenInfo: token0Info,
|
|
36380
36608
|
amount: amount0,
|
|
@@ -36414,7 +36642,7 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36414
36642
|
const token0Usd = Number(token0Web3.toFixed(13)) * P0.price;
|
|
36415
36643
|
const token1Usd = Number(token1Web3.toFixed(13)) * P1.price;
|
|
36416
36644
|
return {
|
|
36417
|
-
|
|
36645
|
+
usdValue: token0Usd + token1Usd,
|
|
36418
36646
|
token0: {
|
|
36419
36647
|
tokenInfo: token0Info,
|
|
36420
36648
|
amount: token0Web3,
|
|
@@ -36436,11 +36664,11 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36436
36664
|
const truePrice = Number(BigInt(result.toString()) * BigInt(1e9) / BigInt(1e18)) / 1e9;
|
|
36437
36665
|
return truePrice;
|
|
36438
36666
|
}
|
|
36439
|
-
async getCurrentPrice() {
|
|
36440
|
-
const poolKey = await this.getPoolKey();
|
|
36441
|
-
return this._getCurrentPrice(poolKey);
|
|
36667
|
+
async getCurrentPrice(blockIdentifier = "pending") {
|
|
36668
|
+
const poolKey = await this.getPoolKey(blockIdentifier);
|
|
36669
|
+
return this._getCurrentPrice(poolKey, blockIdentifier);
|
|
36442
36670
|
}
|
|
36443
|
-
async _getCurrentPrice(poolKey) {
|
|
36671
|
+
async _getCurrentPrice(poolKey, blockIdentifier = "pending") {
|
|
36444
36672
|
const priceInfo = await this.ekuboPositionsContract.call("get_pool_price", [
|
|
36445
36673
|
{
|
|
36446
36674
|
token0: poolKey.token0.address,
|
|
@@ -36449,17 +36677,24 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36449
36677
|
tick_spacing: poolKey.tick_spacing,
|
|
36450
36678
|
extension: poolKey.extension
|
|
36451
36679
|
}
|
|
36452
|
-
]
|
|
36680
|
+
], {
|
|
36681
|
+
blockIdentifier
|
|
36682
|
+
});
|
|
36453
36683
|
const sqrtRatio = _EkuboCLVault.div2Power128(BigInt(priceInfo.sqrt_ratio.toString()));
|
|
36684
|
+
console.log(`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`);
|
|
36454
36685
|
const price = sqrtRatio * sqrtRatio;
|
|
36455
36686
|
const tick = _EkuboCLVault.priceToTick(price, true, Number(poolKey.tick_spacing));
|
|
36687
|
+
console.log(`EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`);
|
|
36456
36688
|
return {
|
|
36457
36689
|
price,
|
|
36458
|
-
tick: tick.mag * (tick.sign == 0 ? 1 : -1)
|
|
36690
|
+
tick: tick.mag * (tick.sign == 0 ? 1 : -1),
|
|
36691
|
+
sqrtRatio: priceInfo.sqrt_ratio.toString()
|
|
36459
36692
|
};
|
|
36460
36693
|
}
|
|
36461
|
-
async getCurrentBounds() {
|
|
36462
|
-
const result = await this.contract.call("get_position_key", []
|
|
36694
|
+
async getCurrentBounds(blockIdentifier = "pending") {
|
|
36695
|
+
const result = await this.contract.call("get_position_key", [], {
|
|
36696
|
+
blockIdentifier
|
|
36697
|
+
});
|
|
36463
36698
|
return {
|
|
36464
36699
|
lowerTick: _EkuboCLVault.i129ToNumber(result.bounds.lower),
|
|
36465
36700
|
upperTick: _EkuboCLVault.i129ToNumber(result.bounds.upper)
|
|
@@ -36473,11 +36708,13 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36473
36708
|
const tick = Math.floor(value / tickSpacing) * tickSpacing;
|
|
36474
36709
|
return this.tickToi129(tick);
|
|
36475
36710
|
}
|
|
36476
|
-
async getPoolKey() {
|
|
36711
|
+
async getPoolKey(blockIdentifier = "pending") {
|
|
36477
36712
|
if (this.poolKey) {
|
|
36478
36713
|
return this.poolKey;
|
|
36479
36714
|
}
|
|
36480
|
-
const result = await this.contract.call("get_settings", []
|
|
36715
|
+
const result = await this.contract.call("get_settings", [], {
|
|
36716
|
+
blockIdentifier
|
|
36717
|
+
});
|
|
36481
36718
|
const poolKey = {
|
|
36482
36719
|
token0: ContractAddr.from(result.pool_key.token0.toString()),
|
|
36483
36720
|
token1: ContractAddr.from(result.pool_key.token1.toString()),
|
|
@@ -36508,25 +36745,24 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36508
36745
|
* @param amount1: amount of token1
|
|
36509
36746
|
* @returns {amount0, amount1}
|
|
36510
36747
|
*/
|
|
36511
|
-
async _getExpectedAmountsForLiquidity(amount0, amount1, bounds) {
|
|
36748
|
+
async _getExpectedAmountsForLiquidity(amount0, amount1, bounds, justUseInputAmount = true) {
|
|
36512
36749
|
assert3(amount0.greaterThan(0) || amount1.greaterThan(0), "Amount is 0");
|
|
36513
|
-
const
|
|
36514
|
-
const sampleLiq = 1e18;
|
|
36750
|
+
const sampleLiq = 1e20;
|
|
36515
36751
|
const { amount0: sampleAmount0, amount1: sampleAmount1 } = await this.getLiquidityToAmounts(Web3Number.fromWei(sampleLiq.toString(), 18), bounds);
|
|
36516
36752
|
logger2.verbose(`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => sampleAmount0: ${sampleAmount0.toString()}, sampleAmount1: ${sampleAmount1.toString()}`);
|
|
36517
|
-
assert3(!sampleAmount0.eq(0)
|
|
36753
|
+
assert3(!sampleAmount0.eq(0) || !sampleAmount1.eq(0), "Sample amount is 0");
|
|
36518
36754
|
const price = await (await this.getCurrentPrice()).price;
|
|
36519
36755
|
logger2.verbose(`${_EkuboCLVault.name}: _getExpectedAmountsForLiquidity => price: ${price}`);
|
|
36520
36756
|
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
36521
36757
|
if (sampleAmount1.eq(0)) {
|
|
36522
36758
|
return {
|
|
36523
36759
|
amount0,
|
|
36524
|
-
amount1: Web3Number.fromWei("0",
|
|
36760
|
+
amount1: Web3Number.fromWei("0", amount1.decimals),
|
|
36525
36761
|
ratio: Infinity
|
|
36526
36762
|
};
|
|
36527
36763
|
} else if (sampleAmount0.eq(0)) {
|
|
36528
36764
|
return {
|
|
36529
|
-
amount0: Web3Number.fromWei("0",
|
|
36765
|
+
amount0: Web3Number.fromWei("0", amount0.decimals),
|
|
36530
36766
|
amount1: amount0.multipliedBy(price),
|
|
36531
36767
|
ratio: 0
|
|
36532
36768
|
};
|
|
@@ -36534,21 +36770,41 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36534
36770
|
} else if (amount0.eq(0) && amount1.greaterThan(0)) {
|
|
36535
36771
|
if (sampleAmount0.eq(0)) {
|
|
36536
36772
|
return {
|
|
36537
|
-
amount0: Web3Number.fromWei("0",
|
|
36773
|
+
amount0: Web3Number.fromWei("0", amount0.decimals),
|
|
36538
36774
|
amount1,
|
|
36539
36775
|
ratio: 0
|
|
36540
36776
|
};
|
|
36541
36777
|
} else if (sampleAmount1.eq(0)) {
|
|
36542
36778
|
return {
|
|
36543
36779
|
amount0: amount1.dividedBy(price),
|
|
36544
|
-
amount1: Web3Number.fromWei("0",
|
|
36780
|
+
amount1: Web3Number.fromWei("0", amount1.decimals),
|
|
36545
36781
|
ratio: Infinity
|
|
36546
36782
|
};
|
|
36547
36783
|
}
|
|
36548
36784
|
}
|
|
36549
|
-
|
|
36785
|
+
assert3(sampleAmount0.decimals == sampleAmount1.decimals, "Sample amounts have different decimals");
|
|
36786
|
+
const ratioWeb3Number = sampleAmount0.multipliedBy(1e18).dividedBy(sampleAmount1.toString()).dividedBy(1e18);
|
|
36787
|
+
const ratio = Number(ratioWeb3Number.toFixed(18));
|
|
36550
36788
|
logger2.verbose(`${_EkuboCLVault.name}: ${this.metadata.name} => ratio: ${ratio.toString()}`);
|
|
36551
|
-
|
|
36789
|
+
if (justUseInputAmount)
|
|
36790
|
+
return this._solveExpectedAmountsEq(amount0, amount1, ratioWeb3Number, price);
|
|
36791
|
+
if (amount1.eq(0) && amount0.greaterThan(0)) {
|
|
36792
|
+
const _amount1 = amount0.dividedBy(ratioWeb3Number);
|
|
36793
|
+
return {
|
|
36794
|
+
amount0,
|
|
36795
|
+
amount1: _amount1,
|
|
36796
|
+
ratio
|
|
36797
|
+
};
|
|
36798
|
+
} else if (amount0.eq(0) && amount1.greaterThan(0)) {
|
|
36799
|
+
const _amount0 = amount1.multipliedBy(ratio);
|
|
36800
|
+
return {
|
|
36801
|
+
amount0: _amount0,
|
|
36802
|
+
amount1,
|
|
36803
|
+
ratio
|
|
36804
|
+
};
|
|
36805
|
+
} else {
|
|
36806
|
+
throw new Error("Both amounts are non-zero, cannot compute expected amounts");
|
|
36807
|
+
}
|
|
36552
36808
|
}
|
|
36553
36809
|
_solveExpectedAmountsEq(availableAmount0, availableAmount1, ratio, price) {
|
|
36554
36810
|
const y = ratio.multipliedBy(availableAmount1).minus(availableAmount0).dividedBy(ratio.plus(1 / price));
|
|
@@ -36562,10 +36818,11 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36562
36818
|
async getSwapInfoToHandleUnused(considerRebalance = true) {
|
|
36563
36819
|
const poolKey = await this.getPoolKey();
|
|
36564
36820
|
const erc20Mod = new ERC20(this.config);
|
|
36565
|
-
const token0Bal1 = await erc20Mod.balanceOf(poolKey.token0, this.address.address, 18);
|
|
36566
|
-
const token1Bal1 = await erc20Mod.balanceOf(poolKey.token1, this.address.address, 18);
|
|
36567
36821
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
36568
36822
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
36823
|
+
const token0Bal1 = await erc20Mod.balanceOf(poolKey.token0, this.address.address, token0Info.decimals);
|
|
36824
|
+
const token1Bal1 = await erc20Mod.balanceOf(poolKey.token1, this.address.address, token1Info.decimals);
|
|
36825
|
+
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal1: ${token0Bal1.toString()}, token1Bal1: ${token1Bal1.toString()}`);
|
|
36569
36826
|
const token0Price = await this.pricer.getPrice(token0Info.symbol);
|
|
36570
36827
|
const token1Price = await this.pricer.getPrice(token1Info.symbol);
|
|
36571
36828
|
const token0PriceUsd = token0Price.price * Number(token0Bal1.toFixed(13));
|
|
@@ -36576,6 +36833,7 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36576
36833
|
let token0Bal = token0Bal1;
|
|
36577
36834
|
let token1Bal = token1Bal1;
|
|
36578
36835
|
if (considerRebalance) {
|
|
36836
|
+
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => considerRebalance: true`);
|
|
36579
36837
|
const tvl = await this.getTVL();
|
|
36580
36838
|
token0Bal = token0Bal.plus(tvl.token0.amount.toString());
|
|
36581
36839
|
token1Bal = token1Bal.plus(tvl.token1.amount.toString());
|
|
@@ -36585,7 +36843,10 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36585
36843
|
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => token0Bal: ${token0Bal.toString()}, token1Bal: ${token1Bal.toString()}`);
|
|
36586
36844
|
const newBounds = await this.getNewBounds();
|
|
36587
36845
|
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => newBounds: ${newBounds.lowerTick}, ${newBounds.upperTick}`);
|
|
36588
|
-
|
|
36846
|
+
return await this.getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, newBounds);
|
|
36847
|
+
}
|
|
36848
|
+
async getSwapInfoGivenAmounts(poolKey, token0Bal, token1Bal, bounds) {
|
|
36849
|
+
let expectedAmounts = await this._getExpectedAmountsForLiquidity(token0Bal, token1Bal, bounds);
|
|
36589
36850
|
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedAmounts: ${expectedAmounts.amount0.toString()}, ${expectedAmounts.amount1.toString()}`);
|
|
36590
36851
|
let retry = 0;
|
|
36591
36852
|
const maxRetry = 10;
|
|
@@ -36606,6 +36867,19 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36606
36867
|
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => tokenToSell: ${tokenToSell.address}, tokenToBuy: ${tokenToBuy.address}, amountToSell: ${amountToSell.toWei()}`);
|
|
36607
36868
|
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => remainingSellAmount: ${remainingSellAmount.toString()}`);
|
|
36608
36869
|
logger2.verbose(`${_EkuboCLVault.name}: getSwapInfoToHandleUnused => expectedRatio: ${expectedRatio}`);
|
|
36870
|
+
if (amountToSell.eq(0)) {
|
|
36871
|
+
return {
|
|
36872
|
+
token_from_address: tokenToSell.address,
|
|
36873
|
+
token_from_amount: uint256_exports.bnToUint256(0),
|
|
36874
|
+
token_to_address: tokenToSell.address,
|
|
36875
|
+
token_to_amount: uint256_exports.bnToUint256(0),
|
|
36876
|
+
token_to_min_amount: uint256_exports.bnToUint256(0),
|
|
36877
|
+
beneficiary: this.address.address,
|
|
36878
|
+
integrator_fee_amount_bps: 0,
|
|
36879
|
+
integrator_fee_recipient: this.address.address,
|
|
36880
|
+
routes: []
|
|
36881
|
+
};
|
|
36882
|
+
}
|
|
36609
36883
|
const quote = await this.avnu.getQuotes(tokenToSell.address, tokenToBuy.address, amountToSell.toWei(), this.address.address);
|
|
36610
36884
|
if (remainingSellAmount.eq(0)) {
|
|
36611
36885
|
const minAmountOut = Web3Number.fromWei(quote.buyAmount.toString(), tokenToBuyInfo.decimals).multipliedBy(0.9999);
|
|
@@ -36628,6 +36902,73 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36628
36902
|
}
|
|
36629
36903
|
throw new Error("Failed to get swap info");
|
|
36630
36904
|
}
|
|
36905
|
+
/**
|
|
36906
|
+
* Attempts to rebalance the vault by iteratively adjusting swap amounts if initial attempt fails.
|
|
36907
|
+
* Uses binary search approach to find optimal swap amount.
|
|
36908
|
+
*
|
|
36909
|
+
* @param newBounds - The new tick bounds to rebalance to
|
|
36910
|
+
* @param swapInfo - Initial swap parameters for rebalancing
|
|
36911
|
+
* @param acc - Account to estimate gas fees with
|
|
36912
|
+
* @param retry - Current retry attempt number (default 0)
|
|
36913
|
+
* @param adjustmentFactor - Percentage to adjust swap amount by (default 1)
|
|
36914
|
+
* @param isToken0Deficit - Whether token0 balance needs increasing (default true)
|
|
36915
|
+
* @returns Array of contract calls needed for rebalancing
|
|
36916
|
+
* @throws Error if max retries reached without successful rebalance
|
|
36917
|
+
*/
|
|
36918
|
+
async rebalanceIter(swapInfo, acc, estimateCall, retry = 0, adjustmentFactor = 1, isToken0Deficit = true) {
|
|
36919
|
+
const MAX_RETRIES = 20;
|
|
36920
|
+
const MIN_ADJUSTMENT = 1e-3;
|
|
36921
|
+
logger2.verbose(
|
|
36922
|
+
`Rebalancing ${this.metadata.name}: retry=${retry}, adjustment=${adjustmentFactor}%, token0Deficit=${isToken0Deficit}`
|
|
36923
|
+
);
|
|
36924
|
+
const fromAmount = uint256_exports.uint256ToBN(swapInfo.token_from_amount);
|
|
36925
|
+
logger2.verbose(
|
|
36926
|
+
`Selling ${fromAmount.toString()} of token ${swapInfo.token_from_address}`
|
|
36927
|
+
);
|
|
36928
|
+
try {
|
|
36929
|
+
const calls = await estimateCall(swapInfo);
|
|
36930
|
+
await acc.estimateInvokeFee(calls);
|
|
36931
|
+
return calls;
|
|
36932
|
+
} catch (err2) {
|
|
36933
|
+
if (retry >= MAX_RETRIES) {
|
|
36934
|
+
logger2.error(`Rebalance failed after ${MAX_RETRIES} retries`);
|
|
36935
|
+
throw err2;
|
|
36936
|
+
}
|
|
36937
|
+
if (adjustmentFactor < MIN_ADJUSTMENT) {
|
|
36938
|
+
logger2.error("Adjustment factor too small, likely oscillating");
|
|
36939
|
+
throw new Error("Failed to converge on valid swap amount");
|
|
36940
|
+
}
|
|
36941
|
+
logger2.error(`Rebalance attempt ${retry + 1} failed, adjusting swap amount...`);
|
|
36942
|
+
const newSwapInfo = { ...swapInfo };
|
|
36943
|
+
const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18);
|
|
36944
|
+
if (err2.message.includes("invalid token0 balance") || err2.message.includes("invalid token0 amount")) {
|
|
36945
|
+
logger2.verbose("Reducing swap amount - excess token0");
|
|
36946
|
+
newSwapInfo.token_from_amount = uint256_exports.bnToUint256(
|
|
36947
|
+
currentAmount.multipliedBy((100 - adjustmentFactor) / 100).toWei()
|
|
36948
|
+
);
|
|
36949
|
+
adjustmentFactor = isToken0Deficit ? adjustmentFactor * 2 : adjustmentFactor / 2;
|
|
36950
|
+
isToken0Deficit = true;
|
|
36951
|
+
} else if (err2.message.includes("invalid token1 balance") || err2.message.includes("invalid token1 amount")) {
|
|
36952
|
+
logger2.verbose("Increasing swap amount - excess token1");
|
|
36953
|
+
newSwapInfo.token_from_amount = uint256_exports.bnToUint256(
|
|
36954
|
+
currentAmount.multipliedBy((100 + adjustmentFactor) / 100).toWei()
|
|
36955
|
+
);
|
|
36956
|
+
adjustmentFactor = isToken0Deficit ? adjustmentFactor / 2 : adjustmentFactor * 2;
|
|
36957
|
+
isToken0Deficit = false;
|
|
36958
|
+
} else {
|
|
36959
|
+
logger2.error("Unexpected error:", err2);
|
|
36960
|
+
}
|
|
36961
|
+
newSwapInfo.token_to_min_amount = uint256_exports.bnToUint256("0");
|
|
36962
|
+
return this.rebalanceIter(
|
|
36963
|
+
newSwapInfo,
|
|
36964
|
+
acc,
|
|
36965
|
+
estimateCall,
|
|
36966
|
+
retry + 1,
|
|
36967
|
+
adjustmentFactor,
|
|
36968
|
+
isToken0Deficit
|
|
36969
|
+
);
|
|
36970
|
+
}
|
|
36971
|
+
}
|
|
36631
36972
|
static tickToi129(tick) {
|
|
36632
36973
|
if (tick < 0) {
|
|
36633
36974
|
return {
|
|
@@ -36650,21 +36991,23 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36650
36991
|
static tickToPrice(tick) {
|
|
36651
36992
|
return Math.pow(1.000001, Number(tick));
|
|
36652
36993
|
}
|
|
36653
|
-
async getLiquidityToAmounts(liquidity, bounds) {
|
|
36654
|
-
const currentPrice = await this.getCurrentPrice();
|
|
36655
|
-
const lowerPrice =
|
|
36656
|
-
const upperPrice =
|
|
36994
|
+
async getLiquidityToAmounts(liquidity, bounds, blockIdentifier = "pending", _poolKey = null, _currentPrice = null) {
|
|
36995
|
+
const currentPrice = _currentPrice || await this.getCurrentPrice(blockIdentifier);
|
|
36996
|
+
const lowerPrice = _EkuboCLVault.tickToPrice(bounds.lowerTick);
|
|
36997
|
+
const upperPrice = _EkuboCLVault.tickToPrice(bounds.upperTick);
|
|
36657
36998
|
logger2.verbose(`${_EkuboCLVault.name}: getLiquidityToAmounts => currentPrice: ${currentPrice.price}, lowerPrice: ${lowerPrice}, upperPrice: ${upperPrice}`);
|
|
36658
36999
|
const result = await this.ekuboMathContract.call("liquidity_delta_to_amount_delta", [
|
|
36659
|
-
uint256_exports.bnToUint256(
|
|
37000
|
+
uint256_exports.bnToUint256(currentPrice.sqrtRatio),
|
|
36660
37001
|
{
|
|
36661
37002
|
mag: liquidity.toWei(),
|
|
36662
37003
|
sign: 0
|
|
36663
37004
|
},
|
|
36664
37005
|
uint256_exports.bnToUint256(_EkuboCLVault.priceToSqrtRatio(lowerPrice).toString()),
|
|
36665
37006
|
uint256_exports.bnToUint256(_EkuboCLVault.priceToSqrtRatio(upperPrice).toString())
|
|
36666
|
-
]
|
|
36667
|
-
|
|
37007
|
+
], {
|
|
37008
|
+
blockIdentifier
|
|
37009
|
+
});
|
|
37010
|
+
const poolKey = _poolKey || await this.getPoolKey(blockIdentifier);
|
|
36668
37011
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
36669
37012
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
36670
37013
|
const amount0 = Web3Number.fromWei(_EkuboCLVault.i129ToNumber(result.amount0).toString(), token0Info.decimals);
|
|
@@ -36674,32 +37017,110 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
36674
37017
|
amount1
|
|
36675
37018
|
};
|
|
36676
37019
|
}
|
|
37020
|
+
async harvest(acc) {
|
|
37021
|
+
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
37022
|
+
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(this.address);
|
|
37023
|
+
const poolKey = await this.getPoolKey();
|
|
37024
|
+
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
37025
|
+
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
37026
|
+
const bounds = await this.getCurrentBounds();
|
|
37027
|
+
const calls = [];
|
|
37028
|
+
for (let claim of unClaimedRewards) {
|
|
37029
|
+
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
37030
|
+
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
37031
|
+
const isToken1 = claim.token.eq(poolKey.token1);
|
|
37032
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`);
|
|
37033
|
+
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
37034
|
+
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
37035
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`);
|
|
37036
|
+
const swapInfo = await this.getSwapInfoGivenAmounts(poolKey, token0Amt, token1Amt, bounds);
|
|
37037
|
+
swapInfo.token_to_address = token0Info.address.address;
|
|
37038
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`);
|
|
37039
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`);
|
|
37040
|
+
const harvestEstimateCall = async (swapInfo1) => {
|
|
37041
|
+
const swap1Amount = Web3Number.fromWei(uint256_exports.uint256ToBN(swapInfo1.token_from_amount).toString(), 18);
|
|
37042
|
+
const remainingAmount = postFeeAmount.minus(swap1Amount);
|
|
37043
|
+
const swapInfo2 = { ...swapInfo, token_from_amount: uint256_exports.bnToUint256(remainingAmount.toWei()) };
|
|
37044
|
+
swapInfo2.token_to_address = token1Info.address.address;
|
|
37045
|
+
const calldata = [
|
|
37046
|
+
claim.rewardsContract.address,
|
|
37047
|
+
{
|
|
37048
|
+
id: claim.claim.id,
|
|
37049
|
+
amount: claim.claim.amount.toWei(),
|
|
37050
|
+
claimee: claim.claim.claimee.address
|
|
37051
|
+
},
|
|
37052
|
+
claim.proof.map((p) => num_exports.getDecimalString(p)),
|
|
37053
|
+
swapInfo,
|
|
37054
|
+
swapInfo2
|
|
37055
|
+
];
|
|
37056
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(calldata)}`);
|
|
37057
|
+
return [this.contract.populate("harvest", calldata)];
|
|
37058
|
+
};
|
|
37059
|
+
const _callsFinal = await this.rebalanceIter(swapInfo, acc, harvestEstimateCall);
|
|
37060
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(_callsFinal)}`);
|
|
37061
|
+
calls.push(..._callsFinal);
|
|
37062
|
+
}
|
|
37063
|
+
return calls;
|
|
37064
|
+
}
|
|
37065
|
+
async getInvestmentFlows() {
|
|
37066
|
+
const netYield = await this.netAPY();
|
|
37067
|
+
const poolKey = await this.getPoolKey();
|
|
37068
|
+
const linkedFlow = {
|
|
37069
|
+
title: this.metadata.name,
|
|
37070
|
+
subItems: [{ key: "Pool", value: `${(_EkuboCLVault.div2Power128(BigInt(poolKey.fee)) * 100).toFixed(2)}%, ${poolKey.tick_spacing} tick spacing` }],
|
|
37071
|
+
linkedFlows: [],
|
|
37072
|
+
style: { backgroundColor: "#35484f" /* Blue */.valueOf() }
|
|
37073
|
+
};
|
|
37074
|
+
const baseFlow = {
|
|
37075
|
+
id: "base",
|
|
37076
|
+
title: "Your Deposit",
|
|
37077
|
+
subItems: [{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` }, { key: `Performance Fee`, value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%` }],
|
|
37078
|
+
linkedFlows: [linkedFlow],
|
|
37079
|
+
style: { backgroundColor: "#6e53dc" /* Purple */.valueOf() }
|
|
37080
|
+
};
|
|
37081
|
+
const rebalanceFlow = {
|
|
37082
|
+
id: "rebalance",
|
|
37083
|
+
title: "Automated Rebalance",
|
|
37084
|
+
subItems: [{
|
|
37085
|
+
key: "Range selection",
|
|
37086
|
+
value: `${this.metadata.additionalInfo.newBounds.lower * Number(poolKey.tick_spacing)} to ${this.metadata.additionalInfo.newBounds.upper * Number(poolKey.tick_spacing)} ticks`
|
|
37087
|
+
}],
|
|
37088
|
+
linkedFlows: [linkedFlow],
|
|
37089
|
+
style: { backgroundColor: "purple" /* Green */.valueOf() }
|
|
37090
|
+
};
|
|
37091
|
+
return [baseFlow, rebalanceFlow];
|
|
37092
|
+
}
|
|
36677
37093
|
};
|
|
36678
|
-
var _description2 = "
|
|
37094
|
+
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.";
|
|
36679
37095
|
var _protocol2 = { name: "Ekubo", logo: "https://app.ekubo.org/favicon.ico" };
|
|
36680
37096
|
var _riskFactor2 = [
|
|
36681
37097
|
{ type: "Smart Contract Risk" /* SMART_CONTRACT_RISK */, value: 0.5, weight: 25 },
|
|
36682
37098
|
{ type: "Impermanent Loss Risk" /* IMPERMANENT_LOSS */, value: 1, weight: 75 }
|
|
36683
37099
|
];
|
|
37100
|
+
var AUDIT_URL2 = "https://assets.strkfarm.com/strkfarm/audit_report_vesu_and_ekubo_strats.pdf";
|
|
36684
37101
|
var EkuboCLVaultStrategies = [{
|
|
36685
37102
|
name: "Ekubo xSTRK/STRK",
|
|
36686
|
-
description: _description2,
|
|
37103
|
+
description: _description2.replace("{{POOL_NAME}}", "xSTRK/STRK"),
|
|
36687
37104
|
address: ContractAddr.from("0x01f083b98674bc21effee29ef443a00c7b9a500fd92cf30341a3da12c73f2324"),
|
|
36688
37105
|
type: "Other",
|
|
36689
|
-
|
|
37106
|
+
// must be same order as poolKey token0 and token1
|
|
37107
|
+
depositTokens: [Global.getDefaultTokens().find((t) => t.symbol === "xSTRK"), Global.getDefaultTokens().find((t) => t.symbol === "STRK")],
|
|
36690
37108
|
protocols: [_protocol2],
|
|
37109
|
+
auditUrl: AUDIT_URL2,
|
|
36691
37110
|
maxTVL: Web3Number.fromWei("0", 18),
|
|
36692
37111
|
risk: {
|
|
36693
37112
|
riskFactor: _riskFactor2,
|
|
36694
37113
|
netRisk: _riskFactor2.reduce((acc, curr) => acc + curr.value * curr.weight, 0) / _riskFactor2.reduce((acc, curr) => acc + curr.weight, 0),
|
|
36695
37114
|
notARisks: getNoRiskTags(_riskFactor2)
|
|
36696
37115
|
},
|
|
37116
|
+
apyMethodology: "APY based on 7-day historical performance, including fees and rewards.",
|
|
36697
37117
|
additionalInfo: {
|
|
36698
37118
|
newBounds: {
|
|
36699
37119
|
lower: -1,
|
|
36700
37120
|
upper: 1
|
|
36701
37121
|
},
|
|
36702
|
-
lstContract: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a")
|
|
37122
|
+
lstContract: ContractAddr.from("0x028d709c875c0ceac3dce7065bec5328186dc89fe254527084d1689910954b0a"),
|
|
37123
|
+
feeBps: 1e3
|
|
36703
37124
|
}
|
|
36704
37125
|
}];
|
|
36705
37126
|
return __toCommonJS(index_browser_exports);
|