@strkfarm/sdk 1.1.50 → 1.1.52
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.browser.global.js +101 -76
- package/dist/index.browser.mjs +99 -74
- package/dist/index.d.ts +5 -1
- package/dist/index.js +129 -75
- package/dist/index.mjs +129 -75
- package/package.json +1 -1
- package/src/modules/avnu.ts +4 -4
- package/src/modules/harvests.ts +22 -15
- package/src/node/deployer.ts +36 -1
- package/src/strategies/ekubo-cl-vault.tsx +97 -57
- package/src/strategies/universal-lst-muliplier-strategy.tsx +8 -4
|
@@ -53725,10 +53725,9 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
53725
53725
|
}
|
|
53726
53726
|
|
|
53727
53727
|
// src/modules/avnu.ts
|
|
53728
|
-
var AvnuWrapper = class
|
|
53728
|
+
var AvnuWrapper = class {
|
|
53729
53729
|
async getQuotes(fromToken, toToken, amountWei, taker, retry = 0, excludeSources = ["Haiko(Solvers)"]) {
|
|
53730
53730
|
const MAX_RETRY = 5;
|
|
53731
|
-
logger2.verbose(`${_AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
|
|
53732
53731
|
const params = {
|
|
53733
53732
|
sellTokenAddress: fromToken,
|
|
53734
53733
|
buyTokenAddress: toToken,
|
|
@@ -53771,9 +53770,6 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
53771
53770
|
startIndex += 5 + swap_params_len;
|
|
53772
53771
|
}
|
|
53773
53772
|
const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
|
|
53774
|
-
logger2.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
|
|
53775
|
-
logger2.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
|
|
53776
|
-
logger2.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
|
|
53777
53773
|
const swapInfo = {
|
|
53778
53774
|
token_from_address: quote.sellTokenAddress,
|
|
53779
53775
|
token_from_amount: uint256_exports.bnToUint256(quote.sellAmount),
|
|
@@ -55736,20 +55732,26 @@ ${JSON.stringify(data, null, 2)}`;
|
|
|
55736
55732
|
const rewards = await this.getHarvests(addr);
|
|
55737
55733
|
if (rewards.length == 0) return [];
|
|
55738
55734
|
const unClaimed = [];
|
|
55739
|
-
const
|
|
55740
|
-
|
|
55741
|
-
|
|
55742
|
-
|
|
55743
|
-
|
|
55744
|
-
|
|
55745
|
-
|
|
55746
|
-
|
|
55747
|
-
|
|
55748
|
-
|
|
55749
|
-
|
|
55750
|
-
|
|
55751
|
-
|
|
55752
|
-
|
|
55735
|
+
const sortedRewards = rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
|
|
55736
|
+
if (sortedRewards.length == 0) {
|
|
55737
|
+
logger2.verbose(`${_Harvests.name}: no rewards found`);
|
|
55738
|
+
return [];
|
|
55739
|
+
}
|
|
55740
|
+
const cls = await this.config.provider.getClassAt(sortedRewards[0].rewardsContract.address);
|
|
55741
|
+
for (const reward of sortedRewards) {
|
|
55742
|
+
const contract = new Contract({ abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider });
|
|
55743
|
+
const isClaimed = await contract.call("is_claimed", [reward.claim.id]);
|
|
55744
|
+
logger2.verbose(`${_Harvests.name}: isClaimed: ${isClaimed}, claim id: ${reward.claim.id}, address: ${reward.rewardsContract.address}`);
|
|
55745
|
+
if (isClaimed) {
|
|
55746
|
+
continue;
|
|
55747
|
+
}
|
|
55748
|
+
const bal = await new ERC20(this.config).balanceOf(reward.token, reward.rewardsContract.address, 18);
|
|
55749
|
+
if (bal.lessThan(reward.claim.amount)) {
|
|
55750
|
+
logger2.verbose(`${_Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
|
|
55751
|
+
continue;
|
|
55752
|
+
}
|
|
55753
|
+
unClaimed.push(reward);
|
|
55754
|
+
}
|
|
55753
55755
|
return unClaimed;
|
|
55754
55756
|
}
|
|
55755
55757
|
};
|
|
@@ -80563,11 +80565,15 @@ spurious results.`);
|
|
|
80563
80565
|
amount1
|
|
80564
80566
|
};
|
|
80565
80567
|
}
|
|
80566
|
-
async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
|
|
80568
|
+
async harvest(acc, maxIterations = 20, priceRatioPrecision = 4, minRewardAmount = new Web3Number(0, 18)) {
|
|
80567
80569
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
80568
|
-
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
80570
|
+
const unClaimedRewards = (await ekuboHarvests.getUnHarvestedRewards(
|
|
80569
80571
|
this.address
|
|
80570
|
-
);
|
|
80572
|
+
)).filter((claim) => claim.actualReward.greaterThanOrEqualTo(minRewardAmount));
|
|
80573
|
+
if (unClaimedRewards.length == 0) {
|
|
80574
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvest => no unclaimed rewards found`);
|
|
80575
|
+
return [];
|
|
80576
|
+
}
|
|
80571
80577
|
const poolKey = await this.getPoolKey();
|
|
80572
80578
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
80573
80579
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -80576,7 +80582,10 @@ spurious results.`);
|
|
|
80576
80582
|
`${_EkuboCLVault.name}: harvest => unClaimedRewards: ${unClaimedRewards.length}`
|
|
80577
80583
|
);
|
|
80578
80584
|
const calls = [];
|
|
80579
|
-
|
|
80585
|
+
const chosenClaim = unClaimedRewards[0];
|
|
80586
|
+
logger2.info(`${_EkuboCLVault.name}: harvest => doing one at a time`);
|
|
80587
|
+
logger2.info(`${_EkuboCLVault.name}: harvest => chosenClaim -> Claim ID: ${chosenClaim.claim.id}, Amount: ${chosenClaim.claim.amount.toString()}, actualAmount: ${chosenClaim.actualReward.toString()}, addr: ${chosenClaim.claim.claimee.toString()}`);
|
|
80588
|
+
for (let claim of [chosenClaim]) {
|
|
80580
80589
|
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
80581
80590
|
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
80582
80591
|
const isToken1 = claim.token.eq(poolKey.token1);
|
|
@@ -80625,57 +80634,41 @@ spurious results.`);
|
|
|
80625
80634
|
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
80626
80635
|
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
80627
80636
|
logger2.verbose(
|
|
80628
|
-
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.
|
|
80629
|
-
);
|
|
80630
|
-
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
80631
|
-
poolKey,
|
|
80632
|
-
token0Amt,
|
|
80633
|
-
token1Amt,
|
|
80634
|
-
bounds,
|
|
80635
|
-
maxIterations,
|
|
80636
|
-
priceRatioPrecision
|
|
80637
|
-
);
|
|
80638
|
-
swapInfo.token_to_address = token0Info.address.address;
|
|
80639
|
-
logger2.verbose(
|
|
80640
|
-
`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
80637
|
+
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toFixed(18)}, token1Amt: ${token1Amt.toFixed(18)}`
|
|
80641
80638
|
);
|
|
80642
80639
|
logger2.verbose(
|
|
80643
80640
|
`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
80644
80641
|
);
|
|
80645
|
-
const
|
|
80646
|
-
|
|
80647
|
-
|
|
80648
|
-
|
|
80649
|
-
|
|
80642
|
+
const claimTokenInfo = await Global.getTokenInfoFromAddr(claim.token);
|
|
80643
|
+
const harvestEstimateCall = async (baseSwapInfo2) => {
|
|
80644
|
+
let baseSwapAmount = Web3Number.fromWei(
|
|
80645
|
+
uint256_exports.uint256ToBN(baseSwapInfo2.token_from_amount).toString(),
|
|
80646
|
+
claimTokenInfo.decimals
|
|
80650
80647
|
).minimum(
|
|
80651
|
-
postFeeAmount.toFixed(
|
|
80652
|
-
// cause always strk
|
|
80653
|
-
);
|
|
80654
|
-
swapInfo.token_from_amount = uint256_exports.bnToUint256(swap1Amount.toWei());
|
|
80655
|
-
swapInfo.token_to_min_amount = uint256_exports.bnToUint256(
|
|
80656
|
-
swap1Amount.multipliedBy(0).toWei()
|
|
80657
|
-
// placeholder
|
|
80648
|
+
postFeeAmount.toFixed(claimTokenInfo.decimals)
|
|
80658
80649
|
);
|
|
80650
|
+
if (baseSwapAmount.lt(1e-4)) {
|
|
80651
|
+
baseSwapAmount = new Web3Number(0, claimTokenInfo.decimals);
|
|
80652
|
+
}
|
|
80653
|
+
baseSwapInfo2.token_from_amount = uint256_exports.bnToUint256(baseSwapAmount.toWei());
|
|
80654
|
+
const isToken0ClaimToken2 = claim.token.eq(poolKey.token0);
|
|
80659
80655
|
logger2.verbose(
|
|
80660
|
-
`${_EkuboCLVault.name}: harvest =>
|
|
80656
|
+
`${_EkuboCLVault.name}: harvest => isToken0ClaimToken: ${isToken0ClaimToken2}, baseSwapAmount: ${baseSwapAmount}`
|
|
80661
80657
|
);
|
|
80662
|
-
const remainingAmount = postFeeAmount.minus(
|
|
80658
|
+
const remainingAmount = postFeeAmount.minus(baseSwapAmount).maximum(0);
|
|
80663
80659
|
logger2.verbose(
|
|
80664
80660
|
`${_EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
|
|
80665
80661
|
);
|
|
80666
|
-
|
|
80667
|
-
|
|
80668
|
-
token_from_amount: uint256_exports.bnToUint256(remainingAmount.toWei())
|
|
80669
|
-
};
|
|
80670
|
-
swapInfo2.token_to_address = token1Info.address.address;
|
|
80662
|
+
let dummySwapInfo = AvnuWrapper.buildZeroSwap(claim.token, this.address.address, claim.token);
|
|
80663
|
+
dummySwapInfo.token_from_amount = uint256_exports.bnToUint256(remainingAmount.toWei());
|
|
80671
80664
|
logger2.verbose(
|
|
80672
|
-
`${_EkuboCLVault.name}: harvest =>
|
|
80673
|
-
|
|
80665
|
+
`${_EkuboCLVault.name}: harvest => dummySwapInfo: ${JSON.stringify(
|
|
80666
|
+
dummySwapInfo
|
|
80674
80667
|
)}`
|
|
80675
80668
|
);
|
|
80676
80669
|
logger2.verbose(
|
|
80677
|
-
`${_EkuboCLVault.name}: harvest =>
|
|
80678
|
-
|
|
80670
|
+
`${_EkuboCLVault.name}: harvest => baseSwapInfo: ${JSON.stringify(
|
|
80671
|
+
baseSwapInfo2
|
|
80679
80672
|
)}`
|
|
80680
80673
|
);
|
|
80681
80674
|
const calldata = [
|
|
@@ -80686,18 +80679,36 @@ spurious results.`);
|
|
|
80686
80679
|
claimee: claim.claim.claimee.address
|
|
80687
80680
|
},
|
|
80688
80681
|
claim.proof.map((p) => num_exports.getDecimalString(p)),
|
|
80689
|
-
|
|
80690
|
-
|
|
80682
|
+
isToken0ClaimToken2 ? dummySwapInfo : baseSwapInfo2,
|
|
80683
|
+
// is token0 claim token, its just dummy swap
|
|
80684
|
+
isToken0ClaimToken2 ? baseSwapInfo2 : dummySwapInfo
|
|
80691
80685
|
];
|
|
80692
|
-
logger2.verbose(
|
|
80693
|
-
`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
80694
|
-
calldata
|
|
80695
|
-
)}`
|
|
80696
|
-
);
|
|
80697
80686
|
return [this.contract.populate("harvest", calldata)];
|
|
80698
80687
|
};
|
|
80688
|
+
const isToken0ClaimToken = claim.token.eq(poolKey.token0);
|
|
80689
|
+
let baseSwapInfo = AvnuWrapper.buildZeroSwap(claim.token, this.address.address, isToken0ClaimToken ? token1Info.address : token0Info.address);
|
|
80690
|
+
baseSwapInfo.token_from_amount = uint256_exports.bnToUint256(postFeeAmount.toWei());
|
|
80691
|
+
if (postFeeAmount.greaterThan(0) && !isToken0ClaimToken) {
|
|
80692
|
+
const avnuWrapper = new AvnuWrapper();
|
|
80693
|
+
const quote = await avnuWrapper.getQuotes(
|
|
80694
|
+
claim.token.address,
|
|
80695
|
+
token0Info.address.address,
|
|
80696
|
+
postFeeAmount.toWei(),
|
|
80697
|
+
this.address.address
|
|
80698
|
+
);
|
|
80699
|
+
baseSwapInfo = await avnuWrapper.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
80700
|
+
} else if (postFeeAmount.greaterThan(0) && isToken0ClaimToken) {
|
|
80701
|
+
const avnuWrapper = new AvnuWrapper();
|
|
80702
|
+
const quote = await avnuWrapper.getQuotes(
|
|
80703
|
+
claim.token.address,
|
|
80704
|
+
token1Info.address.address,
|
|
80705
|
+
postFeeAmount.toWei(),
|
|
80706
|
+
this.address.address
|
|
80707
|
+
);
|
|
80708
|
+
baseSwapInfo = await avnuWrapper.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
80709
|
+
}
|
|
80699
80710
|
const _callsFinal = await this.rebalanceIter(
|
|
80700
|
-
|
|
80711
|
+
baseSwapInfo,
|
|
80701
80712
|
acc,
|
|
80702
80713
|
harvestEstimateCall,
|
|
80703
80714
|
claim.token.eq(poolKey.token0),
|
|
@@ -80741,32 +80752,40 @@ spurious results.`);
|
|
|
80741
80752
|
async harvestMismatchEstimateCallFn(params) {
|
|
80742
80753
|
const { postFeeAmount, claim, token0Info, token1Info, acc } = params;
|
|
80743
80754
|
let harvestCall = null;
|
|
80755
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => postFeeAmount: ${postFeeAmount.toString()}`);
|
|
80756
|
+
let attempt = 0;
|
|
80757
|
+
let MAX_ATTEMPTS = 50;
|
|
80744
80758
|
const binarySearchCallbackFn = async (mid) => {
|
|
80759
|
+
attempt++;
|
|
80760
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => mid: ${mid}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
80745
80761
|
const rewardPart2 = BigInt(postFeeAmount.toWei()) - mid;
|
|
80746
80762
|
const avnuWrapper = new AvnuWrapper();
|
|
80747
80763
|
const beneficiary = this.address.address;
|
|
80748
|
-
const
|
|
80764
|
+
const quote1Prom = avnuWrapper.getQuotes(
|
|
80749
80765
|
claim.token.address,
|
|
80750
80766
|
token0Info.address.address,
|
|
80751
80767
|
mid.toString(),
|
|
80752
80768
|
beneficiary
|
|
80753
80769
|
);
|
|
80770
|
+
const quote2Prom = avnuWrapper.getQuotes(
|
|
80771
|
+
claim.token.address,
|
|
80772
|
+
token1Info.address.address,
|
|
80773
|
+
rewardPart2.toString(),
|
|
80774
|
+
beneficiary
|
|
80775
|
+
);
|
|
80776
|
+
const [quote1, quote2] = await Promise.all([quote1Prom, quote2Prom]);
|
|
80754
80777
|
const swapInfo1 = await avnuWrapper.getSwapInfo(
|
|
80755
80778
|
quote1,
|
|
80756
80779
|
beneficiary,
|
|
80757
80780
|
0,
|
|
80758
|
-
|
|
80759
|
-
);
|
|
80760
|
-
const quote2 = await avnuWrapper.getQuotes(
|
|
80761
|
-
claim.token.address,
|
|
80762
|
-
token1Info.address.address,
|
|
80763
|
-
rewardPart2.toString(),
|
|
80781
|
+
// fee bps
|
|
80764
80782
|
beneficiary
|
|
80765
80783
|
);
|
|
80766
80784
|
const swapInfo2 = await avnuWrapper.getSwapInfo(
|
|
80767
80785
|
quote2,
|
|
80768
80786
|
beneficiary,
|
|
80769
80787
|
0,
|
|
80788
|
+
// fee bps
|
|
80770
80789
|
beneficiary
|
|
80771
80790
|
);
|
|
80772
80791
|
try {
|
|
@@ -80783,13 +80802,17 @@ spurious results.`);
|
|
|
80783
80802
|
];
|
|
80784
80803
|
harvestCall = this.contract.populate("harvest", calldata);
|
|
80785
80804
|
const gas = await acc.estimateInvokeFee(harvestCall);
|
|
80805
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => gas: ${gas.overall_fee.toString()}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
80786
80806
|
return "found";
|
|
80787
80807
|
} catch (err2) {
|
|
80788
80808
|
if (err2.message.includes("invalid token0 amount")) {
|
|
80809
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => invalid token0 amount, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
80789
80810
|
return "go_low";
|
|
80790
80811
|
} else if (err2.message.includes("invalid token1 amount")) {
|
|
80812
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => invalid token1 amount, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
80791
80813
|
return "go_high";
|
|
80792
80814
|
}
|
|
80815
|
+
logger2.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => error: ${err2.message}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
80793
80816
|
return "retry";
|
|
80794
80817
|
}
|
|
80795
80818
|
};
|
|
@@ -94932,6 +94955,7 @@ spurious results.`);
|
|
|
94932
94955
|
* @param params
|
|
94933
94956
|
*/
|
|
94934
94957
|
async getVesuMultiplyCall(params) {
|
|
94958
|
+
const maxEkuboPriceImpact = params.maxEkuboPriceImpact || 0.01;
|
|
94935
94959
|
const vesuAdapter1 = this.getVesuSameTokenAdapter();
|
|
94936
94960
|
const legLTV = await vesuAdapter1.getLTVConfig(this.config);
|
|
94937
94961
|
logger2.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
|
|
@@ -94967,7 +94991,8 @@ spurious results.`);
|
|
|
94967
94991
|
marginAmount,
|
|
94968
94992
|
debtAmount,
|
|
94969
94993
|
lstDexPriceInUnderlying: dexPrice,
|
|
94970
|
-
isIncrease: debtAmount.greaterThan(0)
|
|
94994
|
+
isIncrease: debtAmount.greaterThan(0),
|
|
94995
|
+
maxEkuboPriceImpact
|
|
94971
94996
|
});
|
|
94972
94997
|
}
|
|
94973
94998
|
getLSTUnderlyingTokenInfo() {
|
|
@@ -95144,7 +95169,7 @@ spurious results.`);
|
|
|
95144
95169
|
// negative for exact amount out
|
|
95145
95170
|
);
|
|
95146
95171
|
logger2.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
|
|
95147
|
-
assert3(leverSwapQuote.price_impact
|
|
95172
|
+
assert3(leverSwapQuote.price_impact <= params.maxEkuboPriceImpact, "getIncreaseLeverCall: Price impact is too high [Debt swap]");
|
|
95148
95173
|
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
|
|
95149
95174
|
logger2.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
|
|
95150
95175
|
let minLSTReceived = params.debtAmount.dividedBy(lstDexPriceInUnderlying).multipliedBy(1 - MAX_SLIPPAGE);
|
package/dist/index.browser.mjs
CHANGED
|
@@ -2116,10 +2116,9 @@ function getTrovesEndpoint() {
|
|
|
2116
2116
|
}
|
|
2117
2117
|
|
|
2118
2118
|
// src/modules/avnu.ts
|
|
2119
|
-
var AvnuWrapper = class
|
|
2119
|
+
var AvnuWrapper = class {
|
|
2120
2120
|
async getQuotes(fromToken, toToken, amountWei, taker, retry = 0, excludeSources = ["Haiko(Solvers)"]) {
|
|
2121
2121
|
const MAX_RETRY = 5;
|
|
2122
|
-
logger.verbose(`${_AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
|
|
2123
2122
|
const params = {
|
|
2124
2123
|
sellTokenAddress: fromToken,
|
|
2125
2124
|
buyTokenAddress: toToken,
|
|
@@ -2162,9 +2161,6 @@ var AvnuWrapper = class _AvnuWrapper {
|
|
|
2162
2161
|
startIndex += 5 + swap_params_len;
|
|
2163
2162
|
}
|
|
2164
2163
|
const _minAmount = minAmount || (quote.buyAmount * 95n / 100n).toString();
|
|
2165
|
-
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => sellToken: ${quote.sellTokenAddress}, sellAmount: ${quote.sellAmount}`);
|
|
2166
|
-
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyToken: ${quote.buyTokenAddress}`);
|
|
2167
|
-
logger.verbose(`${_AvnuWrapper.name}: getSwapInfo => buyAmount: ${quote.buyAmount}, minAmount: ${_minAmount}`);
|
|
2168
2164
|
const swapInfo = {
|
|
2169
2165
|
token_from_address: quote.sellTokenAddress,
|
|
2170
2166
|
token_from_amount: uint256.bnToUint256(quote.sellAmount),
|
|
@@ -4136,20 +4132,26 @@ var Harvests = class _Harvests {
|
|
|
4136
4132
|
const rewards = await this.getHarvests(addr);
|
|
4137
4133
|
if (rewards.length == 0) return [];
|
|
4138
4134
|
const unClaimed = [];
|
|
4139
|
-
const
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
logger.verbose(`${_Harvests.name}: isClaimed: ${isClaimed}`);
|
|
4144
|
-
if (isClaimed) {
|
|
4145
|
-
return unClaimed;
|
|
4135
|
+
const sortedRewards = rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime());
|
|
4136
|
+
if (sortedRewards.length == 0) {
|
|
4137
|
+
logger.verbose(`${_Harvests.name}: no rewards found`);
|
|
4138
|
+
return [];
|
|
4146
4139
|
}
|
|
4147
|
-
const
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4140
|
+
const cls = await this.config.provider.getClassAt(sortedRewards[0].rewardsContract.address);
|
|
4141
|
+
for (const reward of sortedRewards) {
|
|
4142
|
+
const contract = new Contract4({ abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider });
|
|
4143
|
+
const isClaimed = await contract.call("is_claimed", [reward.claim.id]);
|
|
4144
|
+
logger.verbose(`${_Harvests.name}: isClaimed: ${isClaimed}, claim id: ${reward.claim.id}, address: ${reward.rewardsContract.address}`);
|
|
4145
|
+
if (isClaimed) {
|
|
4146
|
+
continue;
|
|
4147
|
+
}
|
|
4148
|
+
const bal = await new ERC20(this.config).balanceOf(reward.token, reward.rewardsContract.address, 18);
|
|
4149
|
+
if (bal.lessThan(reward.claim.amount)) {
|
|
4150
|
+
logger.verbose(`${_Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
|
|
4151
|
+
continue;
|
|
4152
|
+
}
|
|
4153
|
+
unClaimed.push(reward);
|
|
4151
4154
|
}
|
|
4152
|
-
unClaimed.unshift(reward);
|
|
4153
4155
|
return unClaimed;
|
|
4154
4156
|
}
|
|
4155
4157
|
};
|
|
@@ -16640,11 +16642,15 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
16640
16642
|
amount1
|
|
16641
16643
|
};
|
|
16642
16644
|
}
|
|
16643
|
-
async harvest(acc, maxIterations = 20, priceRatioPrecision = 4) {
|
|
16645
|
+
async harvest(acc, maxIterations = 20, priceRatioPrecision = 4, minRewardAmount = new Web3Number(0, 18)) {
|
|
16644
16646
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
16645
|
-
const unClaimedRewards = await ekuboHarvests.getUnHarvestedRewards(
|
|
16647
|
+
const unClaimedRewards = (await ekuboHarvests.getUnHarvestedRewards(
|
|
16646
16648
|
this.address
|
|
16647
|
-
);
|
|
16649
|
+
)).filter((claim) => claim.actualReward.greaterThanOrEqualTo(minRewardAmount));
|
|
16650
|
+
if (unClaimedRewards.length == 0) {
|
|
16651
|
+
logger.verbose(`${_EkuboCLVault.name}: harvest => no unclaimed rewards found`);
|
|
16652
|
+
return [];
|
|
16653
|
+
}
|
|
16648
16654
|
const poolKey = await this.getPoolKey();
|
|
16649
16655
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
16650
16656
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
@@ -16653,7 +16659,10 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
16653
16659
|
`${_EkuboCLVault.name}: harvest => unClaimedRewards: ${unClaimedRewards.length}`
|
|
16654
16660
|
);
|
|
16655
16661
|
const calls = [];
|
|
16656
|
-
|
|
16662
|
+
const chosenClaim = unClaimedRewards[0];
|
|
16663
|
+
logger.info(`${_EkuboCLVault.name}: harvest => doing one at a time`);
|
|
16664
|
+
logger.info(`${_EkuboCLVault.name}: harvest => chosenClaim -> Claim ID: ${chosenClaim.claim.id}, Amount: ${chosenClaim.claim.amount.toString()}, actualAmount: ${chosenClaim.actualReward.toString()}, addr: ${chosenClaim.claim.claimee.toString()}`);
|
|
16665
|
+
for (let claim of [chosenClaim]) {
|
|
16657
16666
|
const fee = claim.claim.amount.multipliedBy(this.metadata.additionalInfo.feeBps).dividedBy(1e4);
|
|
16658
16667
|
const postFeeAmount = claim.claim.amount.minus(fee);
|
|
16659
16668
|
const isToken1 = claim.token.eq(poolKey.token1);
|
|
@@ -16702,57 +16711,41 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
16702
16711
|
const token0Amt = isToken1 ? new Web3Number(0, token0Info.decimals) : postFeeAmount;
|
|
16703
16712
|
const token1Amt = isToken1 ? postFeeAmount : new Web3Number(0, token0Info.decimals);
|
|
16704
16713
|
logger.verbose(
|
|
16705
|
-
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.
|
|
16706
|
-
);
|
|
16707
|
-
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
16708
|
-
poolKey,
|
|
16709
|
-
token0Amt,
|
|
16710
|
-
token1Amt,
|
|
16711
|
-
bounds,
|
|
16712
|
-
maxIterations,
|
|
16713
|
-
priceRatioPrecision
|
|
16714
|
-
);
|
|
16715
|
-
swapInfo.token_to_address = token0Info.address.address;
|
|
16716
|
-
logger.verbose(
|
|
16717
|
-
`${_EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
16714
|
+
`${_EkuboCLVault.name}: harvest => token0Amt: ${token0Amt.toFixed(18)}, token1Amt: ${token1Amt.toFixed(18)}`
|
|
16718
16715
|
);
|
|
16719
16716
|
logger.verbose(
|
|
16720
16717
|
`${_EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
16721
16718
|
);
|
|
16722
|
-
const
|
|
16723
|
-
|
|
16724
|
-
|
|
16725
|
-
|
|
16726
|
-
|
|
16719
|
+
const claimTokenInfo = await Global.getTokenInfoFromAddr(claim.token);
|
|
16720
|
+
const harvestEstimateCall = async (baseSwapInfo2) => {
|
|
16721
|
+
let baseSwapAmount = Web3Number.fromWei(
|
|
16722
|
+
uint2564.uint256ToBN(baseSwapInfo2.token_from_amount).toString(),
|
|
16723
|
+
claimTokenInfo.decimals
|
|
16727
16724
|
).minimum(
|
|
16728
|
-
postFeeAmount.toFixed(
|
|
16729
|
-
// cause always strk
|
|
16730
|
-
);
|
|
16731
|
-
swapInfo.token_from_amount = uint2564.bnToUint256(swap1Amount.toWei());
|
|
16732
|
-
swapInfo.token_to_min_amount = uint2564.bnToUint256(
|
|
16733
|
-
swap1Amount.multipliedBy(0).toWei()
|
|
16734
|
-
// placeholder
|
|
16725
|
+
postFeeAmount.toFixed(claimTokenInfo.decimals)
|
|
16735
16726
|
);
|
|
16727
|
+
if (baseSwapAmount.lt(1e-4)) {
|
|
16728
|
+
baseSwapAmount = new Web3Number(0, claimTokenInfo.decimals);
|
|
16729
|
+
}
|
|
16730
|
+
baseSwapInfo2.token_from_amount = uint2564.bnToUint256(baseSwapAmount.toWei());
|
|
16731
|
+
const isToken0ClaimToken2 = claim.token.eq(poolKey.token0);
|
|
16736
16732
|
logger.verbose(
|
|
16737
|
-
`${_EkuboCLVault.name}: harvest =>
|
|
16733
|
+
`${_EkuboCLVault.name}: harvest => isToken0ClaimToken: ${isToken0ClaimToken2}, baseSwapAmount: ${baseSwapAmount}`
|
|
16738
16734
|
);
|
|
16739
|
-
const remainingAmount = postFeeAmount.minus(
|
|
16735
|
+
const remainingAmount = postFeeAmount.minus(baseSwapAmount).maximum(0);
|
|
16740
16736
|
logger.verbose(
|
|
16741
16737
|
`${_EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
|
|
16742
16738
|
);
|
|
16743
|
-
|
|
16744
|
-
|
|
16745
|
-
token_from_amount: uint2564.bnToUint256(remainingAmount.toWei())
|
|
16746
|
-
};
|
|
16747
|
-
swapInfo2.token_to_address = token1Info.address.address;
|
|
16739
|
+
let dummySwapInfo = AvnuWrapper.buildZeroSwap(claim.token, this.address.address, claim.token);
|
|
16740
|
+
dummySwapInfo.token_from_amount = uint2564.bnToUint256(remainingAmount.toWei());
|
|
16748
16741
|
logger.verbose(
|
|
16749
|
-
`${_EkuboCLVault.name}: harvest =>
|
|
16750
|
-
|
|
16742
|
+
`${_EkuboCLVault.name}: harvest => dummySwapInfo: ${JSON.stringify(
|
|
16743
|
+
dummySwapInfo
|
|
16751
16744
|
)}`
|
|
16752
16745
|
);
|
|
16753
16746
|
logger.verbose(
|
|
16754
|
-
`${_EkuboCLVault.name}: harvest =>
|
|
16755
|
-
|
|
16747
|
+
`${_EkuboCLVault.name}: harvest => baseSwapInfo: ${JSON.stringify(
|
|
16748
|
+
baseSwapInfo2
|
|
16756
16749
|
)}`
|
|
16757
16750
|
);
|
|
16758
16751
|
const calldata = [
|
|
@@ -16763,18 +16756,36 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
16763
16756
|
claimee: claim.claim.claimee.address
|
|
16764
16757
|
},
|
|
16765
16758
|
claim.proof.map((p) => num5.getDecimalString(p)),
|
|
16766
|
-
|
|
16767
|
-
|
|
16759
|
+
isToken0ClaimToken2 ? dummySwapInfo : baseSwapInfo2,
|
|
16760
|
+
// is token0 claim token, its just dummy swap
|
|
16761
|
+
isToken0ClaimToken2 ? baseSwapInfo2 : dummySwapInfo
|
|
16768
16762
|
];
|
|
16769
|
-
logger.verbose(
|
|
16770
|
-
`${_EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
16771
|
-
calldata
|
|
16772
|
-
)}`
|
|
16773
|
-
);
|
|
16774
16763
|
return [this.contract.populate("harvest", calldata)];
|
|
16775
16764
|
};
|
|
16765
|
+
const isToken0ClaimToken = claim.token.eq(poolKey.token0);
|
|
16766
|
+
let baseSwapInfo = AvnuWrapper.buildZeroSwap(claim.token, this.address.address, isToken0ClaimToken ? token1Info.address : token0Info.address);
|
|
16767
|
+
baseSwapInfo.token_from_amount = uint2564.bnToUint256(postFeeAmount.toWei());
|
|
16768
|
+
if (postFeeAmount.greaterThan(0) && !isToken0ClaimToken) {
|
|
16769
|
+
const avnuWrapper = new AvnuWrapper();
|
|
16770
|
+
const quote = await avnuWrapper.getQuotes(
|
|
16771
|
+
claim.token.address,
|
|
16772
|
+
token0Info.address.address,
|
|
16773
|
+
postFeeAmount.toWei(),
|
|
16774
|
+
this.address.address
|
|
16775
|
+
);
|
|
16776
|
+
baseSwapInfo = await avnuWrapper.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
16777
|
+
} else if (postFeeAmount.greaterThan(0) && isToken0ClaimToken) {
|
|
16778
|
+
const avnuWrapper = new AvnuWrapper();
|
|
16779
|
+
const quote = await avnuWrapper.getQuotes(
|
|
16780
|
+
claim.token.address,
|
|
16781
|
+
token1Info.address.address,
|
|
16782
|
+
postFeeAmount.toWei(),
|
|
16783
|
+
this.address.address
|
|
16784
|
+
);
|
|
16785
|
+
baseSwapInfo = await avnuWrapper.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
16786
|
+
}
|
|
16776
16787
|
const _callsFinal = await this.rebalanceIter(
|
|
16777
|
-
|
|
16788
|
+
baseSwapInfo,
|
|
16778
16789
|
acc,
|
|
16779
16790
|
harvestEstimateCall,
|
|
16780
16791
|
claim.token.eq(poolKey.token0),
|
|
@@ -16818,32 +16829,40 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
16818
16829
|
async harvestMismatchEstimateCallFn(params) {
|
|
16819
16830
|
const { postFeeAmount, claim, token0Info, token1Info, acc } = params;
|
|
16820
16831
|
let harvestCall = null;
|
|
16832
|
+
logger.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => postFeeAmount: ${postFeeAmount.toString()}`);
|
|
16833
|
+
let attempt = 0;
|
|
16834
|
+
let MAX_ATTEMPTS = 50;
|
|
16821
16835
|
const binarySearchCallbackFn = async (mid) => {
|
|
16836
|
+
attempt++;
|
|
16837
|
+
logger.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => mid: ${mid}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
16822
16838
|
const rewardPart2 = BigInt(postFeeAmount.toWei()) - mid;
|
|
16823
16839
|
const avnuWrapper = new AvnuWrapper();
|
|
16824
16840
|
const beneficiary = this.address.address;
|
|
16825
|
-
const
|
|
16841
|
+
const quote1Prom = avnuWrapper.getQuotes(
|
|
16826
16842
|
claim.token.address,
|
|
16827
16843
|
token0Info.address.address,
|
|
16828
16844
|
mid.toString(),
|
|
16829
16845
|
beneficiary
|
|
16830
16846
|
);
|
|
16847
|
+
const quote2Prom = avnuWrapper.getQuotes(
|
|
16848
|
+
claim.token.address,
|
|
16849
|
+
token1Info.address.address,
|
|
16850
|
+
rewardPart2.toString(),
|
|
16851
|
+
beneficiary
|
|
16852
|
+
);
|
|
16853
|
+
const [quote1, quote2] = await Promise.all([quote1Prom, quote2Prom]);
|
|
16831
16854
|
const swapInfo1 = await avnuWrapper.getSwapInfo(
|
|
16832
16855
|
quote1,
|
|
16833
16856
|
beneficiary,
|
|
16834
16857
|
0,
|
|
16835
|
-
|
|
16836
|
-
);
|
|
16837
|
-
const quote2 = await avnuWrapper.getQuotes(
|
|
16838
|
-
claim.token.address,
|
|
16839
|
-
token1Info.address.address,
|
|
16840
|
-
rewardPart2.toString(),
|
|
16858
|
+
// fee bps
|
|
16841
16859
|
beneficiary
|
|
16842
16860
|
);
|
|
16843
16861
|
const swapInfo2 = await avnuWrapper.getSwapInfo(
|
|
16844
16862
|
quote2,
|
|
16845
16863
|
beneficiary,
|
|
16846
16864
|
0,
|
|
16865
|
+
// fee bps
|
|
16847
16866
|
beneficiary
|
|
16848
16867
|
);
|
|
16849
16868
|
try {
|
|
@@ -16860,13 +16879,17 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
|
|
|
16860
16879
|
];
|
|
16861
16880
|
harvestCall = this.contract.populate("harvest", calldata);
|
|
16862
16881
|
const gas = await acc.estimateInvokeFee(harvestCall);
|
|
16882
|
+
logger.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => gas: ${gas.overall_fee.toString()}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
16863
16883
|
return "found";
|
|
16864
16884
|
} catch (err) {
|
|
16865
16885
|
if (err.message.includes("invalid token0 amount")) {
|
|
16886
|
+
logger.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => invalid token0 amount, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
16866
16887
|
return "go_low";
|
|
16867
16888
|
} else if (err.message.includes("invalid token1 amount")) {
|
|
16889
|
+
logger.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => invalid token1 amount, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
16868
16890
|
return "go_high";
|
|
16869
16891
|
}
|
|
16892
|
+
logger.verbose(`${_EkuboCLVault.name}: harvestMismatchEstimateCallFn => error: ${err.message}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
16870
16893
|
return "retry";
|
|
16871
16894
|
}
|
|
16872
16895
|
};
|
|
@@ -31025,6 +31048,7 @@ var UniversalLstMultiplierStrategy = class _UniversalLstMultiplierStrategy exten
|
|
|
31025
31048
|
* @param params
|
|
31026
31049
|
*/
|
|
31027
31050
|
async getVesuMultiplyCall(params) {
|
|
31051
|
+
const maxEkuboPriceImpact = params.maxEkuboPriceImpact || 0.01;
|
|
31028
31052
|
const vesuAdapter1 = this.getVesuSameTokenAdapter();
|
|
31029
31053
|
const legLTV = await vesuAdapter1.getLTVConfig(this.config);
|
|
31030
31054
|
logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
|
|
@@ -31060,7 +31084,8 @@ var UniversalLstMultiplierStrategy = class _UniversalLstMultiplierStrategy exten
|
|
|
31060
31084
|
marginAmount,
|
|
31061
31085
|
debtAmount,
|
|
31062
31086
|
lstDexPriceInUnderlying: dexPrice,
|
|
31063
|
-
isIncrease: debtAmount.greaterThan(0)
|
|
31087
|
+
isIncrease: debtAmount.greaterThan(0),
|
|
31088
|
+
maxEkuboPriceImpact
|
|
31064
31089
|
});
|
|
31065
31090
|
}
|
|
31066
31091
|
getLSTUnderlyingTokenInfo() {
|
|
@@ -31237,7 +31262,7 @@ var UniversalLstMultiplierStrategy = class _UniversalLstMultiplierStrategy exten
|
|
|
31237
31262
|
// negative for exact amount out
|
|
31238
31263
|
);
|
|
31239
31264
|
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwapQuote: ${JSON.stringify(leverSwapQuote)}`);
|
|
31240
|
-
assert(leverSwapQuote.price_impact
|
|
31265
|
+
assert(leverSwapQuote.price_impact <= params.maxEkuboPriceImpact, "getIncreaseLeverCall: Price impact is too high [Debt swap]");
|
|
31241
31266
|
const leverSwap = ekuboQuoter.getVesuMultiplyQuote(leverSwapQuote, fromToken, toToken);
|
|
31242
31267
|
logger.verbose(`${this.getTag()}::getModifyLeverCall leverSwap: ${JSON.stringify(leverSwap)}`);
|
|
31243
31268
|
let minLSTReceived = params.debtAmount.dividedBy(lstDexPriceInUnderlying).multipliedBy(1 - MAX_SLIPPAGE);
|
package/dist/index.d.ts
CHANGED
|
@@ -833,7 +833,7 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
|
|
|
833
833
|
amount0: Web3Number;
|
|
834
834
|
amount1: Web3Number;
|
|
835
835
|
}>;
|
|
836
|
-
harvest(acc: Account, maxIterations?: number, priceRatioPrecision?: number): Promise<Call[]>;
|
|
836
|
+
harvest(acc: Account, maxIterations?: number, priceRatioPrecision?: number, minRewardAmount?: Web3Number): Promise<Call[]>;
|
|
837
837
|
/**
|
|
838
838
|
* @description This funciton requires atleast one of the pool tokens to be reward token
|
|
839
839
|
* i.e. STRK.
|
|
@@ -1459,6 +1459,7 @@ declare class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTS
|
|
|
1459
1459
|
getVesuMultiplyCall(params: {
|
|
1460
1460
|
isDeposit: boolean;
|
|
1461
1461
|
leg1DepositAmount: Web3Number;
|
|
1462
|
+
maxEkuboPriceImpact?: number;
|
|
1462
1463
|
}): Promise<Call[]>;
|
|
1463
1464
|
getLSTUnderlyingTokenInfo(): TokenInfo;
|
|
1464
1465
|
getMaxBorrowableAmount(params?: {
|
|
@@ -1508,6 +1509,7 @@ declare class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTS
|
|
|
1508
1509
|
debtAmount: Web3Number;
|
|
1509
1510
|
lstDexPriceInUnderlying: number;
|
|
1510
1511
|
isIncrease: boolean;
|
|
1512
|
+
maxEkuboPriceImpact: number;
|
|
1511
1513
|
}): Promise<Call[]>;
|
|
1512
1514
|
}
|
|
1513
1515
|
declare const HyperLSTStrategies: IStrategyMetadata<HyperLSTStrategySettings>[];
|
|
@@ -1696,6 +1698,7 @@ declare function executeDeployCalls(contractsInfo: DeployContractResult[], acc:
|
|
|
1696
1698
|
declare function executeTransactions(calls: Call[], acc: Account, provider: RpcProvider, remarks?: string): Promise<{
|
|
1697
1699
|
transaction_hash: string;
|
|
1698
1700
|
}>;
|
|
1701
|
+
declare function myWaitForTransaction(transaction_hash: string, provider: RpcProvider, retry?: number): Promise<boolean>;
|
|
1699
1702
|
declare const Deployer: {
|
|
1700
1703
|
getAccount: typeof getAccount;
|
|
1701
1704
|
myDeclare: typeof myDeclare;
|
|
@@ -1703,6 +1706,7 @@ declare const Deployer: {
|
|
|
1703
1706
|
prepareMultiDeployContracts: typeof prepareMultiDeployContracts;
|
|
1704
1707
|
executeDeployCalls: typeof executeDeployCalls;
|
|
1705
1708
|
executeTransactions: typeof executeTransactions;
|
|
1709
|
+
myWaitForTransaction: typeof myWaitForTransaction;
|
|
1706
1710
|
};
|
|
1707
1711
|
|
|
1708
1712
|
/**
|