@strkfarm/sdk 1.1.38 → 1.1.40
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 +726 -185
- package/dist/index.browser.mjs +725 -182
- package/dist/index.d.ts +126 -13
- package/dist/index.js +726 -182
- package/dist/index.mjs +725 -182
- package/package.json +1 -1
- package/src/global.ts +18 -0
- package/src/modules/avnu.ts +5 -4
- package/src/modules/harvests.ts +16 -15
- package/src/strategies/ekubo-cl-vault.tsx +255 -79
- package/src/strategies/universal-adapters/baseAdapter.ts +184 -2
- package/src/strategies/universal-adapters/vesu-adapter.ts +34 -17
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +322 -0
- package/src/strategies/universal-lst-muliplier-strategy.tsx +231 -72
- package/src/strategies/universal-strategy.tsx +5 -5
- package/src/utils/health-factor-math.ts +83 -0
- package/src/utils/math-utils.ts +150 -0
package/package.json
CHANGED
package/src/global.ts
CHANGED
|
@@ -136,6 +136,24 @@ const defaultTokens: TokenInfo[] = [{
|
|
|
136
136
|
displayDecimals: 6,
|
|
137
137
|
priceProxySymbol: 'WBTC',
|
|
138
138
|
priceCheckAmount: 0.0001, // 112000 * 0.0001 = $11.2
|
|
139
|
+
}, {
|
|
140
|
+
name: 'mRe7BTC',
|
|
141
|
+
symbol: 'mRe7BTC',
|
|
142
|
+
logo: 'https://imagedelivery.net/0xPAQaDtnQhBs8IzYRIlNg/3a62ecee-1e58-45d3-9862-3ce90dff1900/logo',
|
|
143
|
+
address: ContractAddr.from('0x4e4fb1a9ca7e84bae609b9dc0078ad7719e49187ae7e425bb47d131710eddac'),
|
|
144
|
+
decimals: 18,
|
|
145
|
+
coingeckId: undefined,
|
|
146
|
+
displayDecimals: 6,
|
|
147
|
+
priceCheckAmount: 0.0001, // 112000 * 0.0001 = $11.2
|
|
148
|
+
}, {
|
|
149
|
+
name: 'mRe7YIELD',
|
|
150
|
+
symbol: 'mRe7YIELD',
|
|
151
|
+
logo: 'https://midas.app/assets/mre7-BcOOHm7i.svg',
|
|
152
|
+
address: ContractAddr.from('0x4be8945e61dc3e19ebadd1579a6bd53b262f51ba89e6f8b0c4bc9a7e3c633fc'),
|
|
153
|
+
decimals: 18,
|
|
154
|
+
coingeckId: undefined,
|
|
155
|
+
displayDecimals: 2,
|
|
156
|
+
priceCheckAmount: 100,
|
|
139
157
|
}]
|
|
140
158
|
const tokens: TokenInfo[] = defaultTokens;
|
|
141
159
|
|
package/src/modules/avnu.ts
CHANGED
|
@@ -120,17 +120,18 @@ export class AvnuWrapper {
|
|
|
120
120
|
|
|
121
121
|
static buildZeroSwap(
|
|
122
122
|
tokenToSell: ContractAddr,
|
|
123
|
-
|
|
123
|
+
beneficiary: string,
|
|
124
|
+
tokenToBuy: ContractAddr = tokenToSell
|
|
124
125
|
): SwapInfo {
|
|
125
126
|
return {
|
|
126
127
|
token_from_address: tokenToSell.address,
|
|
127
128
|
token_from_amount: uint256.bnToUint256(0),
|
|
128
|
-
token_to_address:
|
|
129
|
+
token_to_address: tokenToBuy.address,
|
|
129
130
|
token_to_amount: uint256.bnToUint256(0),
|
|
130
131
|
token_to_min_amount: uint256.bnToUint256(0),
|
|
131
|
-
beneficiary:
|
|
132
|
+
beneficiary: beneficiary,
|
|
132
133
|
integrator_fee_amount_bps: 0,
|
|
133
|
-
integrator_fee_recipient:
|
|
134
|
+
integrator_fee_recipient: beneficiary,
|
|
134
135
|
routes: [],
|
|
135
136
|
};
|
|
136
137
|
}
|
package/src/modules/harvests.ts
CHANGED
|
@@ -33,23 +33,24 @@ export class Harvests {
|
|
|
33
33
|
|
|
34
34
|
const unClaimed: HarvestInfo[] = [];
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const contract = new Contract({abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider});
|
|
39
|
-
const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
|
|
40
|
-
logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}`);
|
|
41
|
-
if (isClaimed) {
|
|
42
|
-
return unClaimed;
|
|
43
|
-
}
|
|
44
|
-
// rewards contract must have enough balance to claim
|
|
45
|
-
const bal = await (new ERC20(this.config)).balanceOf(reward.token, reward.rewardsContract.address, 18);
|
|
46
|
-
if (bal.lessThan(reward.claim.amount)) {
|
|
47
|
-
logger.verbose(`${Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
36
|
+
// use the latest one
|
|
37
|
+
const reward = rewards.sort((a, b) => b.endDate.getTime() - a.endDate.getTime())[0];
|
|
50
38
|
|
|
51
|
-
|
|
39
|
+
const cls = await this.config.provider.getClassAt(reward.rewardsContract.address);
|
|
40
|
+
const contract = new Contract({abi: cls.abi, address: reward.rewardsContract.address, providerOrAccount: this.config.provider});
|
|
41
|
+
const isClaimed = await contract.call('is_claimed', [reward.claim.id]);
|
|
42
|
+
logger.verbose(`${Harvests.name}: isClaimed: ${isClaimed}`);
|
|
43
|
+
if (isClaimed) {
|
|
44
|
+
return unClaimed;
|
|
52
45
|
}
|
|
46
|
+
// rewards contract must have enough balance to claim
|
|
47
|
+
const bal = await (new ERC20(this.config)).balanceOf(reward.token, reward.rewardsContract.address, 18);
|
|
48
|
+
if (bal.lessThan(reward.claim.amount)) {
|
|
49
|
+
logger.verbose(`${Harvests.name}: balance: ${bal.toString()}, amount: ${reward.claim.amount.toString()}`);
|
|
50
|
+
return unClaimed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
unClaimed.unshift(reward); // to ensure older harvest is first
|
|
53
54
|
return unClaimed;
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -33,12 +33,13 @@ import { BaseStrategy } from "./base-strategy";
|
|
|
33
33
|
import { DualActionAmount } from "./base-strategy";
|
|
34
34
|
import { DualTokenInfo } from "./base-strategy";
|
|
35
35
|
import { log } from "winston";
|
|
36
|
-
import { EkuboHarvests } from "@/modules/harvests";
|
|
36
|
+
import { EkuboHarvests, HarvestInfo } from "@/modules/harvests";
|
|
37
37
|
import { logger } from "@/utils/logger";
|
|
38
38
|
import { COMMON_CONTRACTS } from "./constants";
|
|
39
39
|
import { DepegRiskLevel, ImpermanentLossLevel, MarketRiskLevel, SmartContractRiskLevel } from "@/interfaces/risks";
|
|
40
40
|
import { gql } from "@apollo/client";
|
|
41
41
|
import apolloClient from "@/modules/apollo-client";
|
|
42
|
+
import { binarySearch } from "@/utils/math-utils";
|
|
42
43
|
|
|
43
44
|
export interface EkuboPoolKey {
|
|
44
45
|
token0: ContractAddr;
|
|
@@ -1592,70 +1593,249 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1592
1593
|
}: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
|
|
1593
1594
|
);
|
|
1594
1595
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1596
|
+
const isRewardTokenMatch = claim.token.eq(poolKey.token0) || claim.token.eq(poolKey.token1);
|
|
1597
|
+
if (isRewardTokenMatch) {
|
|
1598
|
+
const _callsFinal = await this._handleRewardAndVaultTokenMatchHarvest({
|
|
1599
|
+
acc,
|
|
1600
|
+
claim,
|
|
1601
|
+
isToken1,
|
|
1602
|
+
token0Info,
|
|
1603
|
+
token1Info,
|
|
1604
|
+
postFeeAmount,
|
|
1605
|
+
poolKey,
|
|
1606
|
+
bounds,
|
|
1607
|
+
maxIterations,
|
|
1608
|
+
priceRatioPrecision,
|
|
1609
|
+
});
|
|
1610
|
+
calls.push(..._callsFinal);
|
|
1611
|
+
} else {
|
|
1612
|
+
const _callsFinal = await this._handleRewardAndVaultTokenMismatchHarvest({
|
|
1613
|
+
claim,
|
|
1614
|
+
token0Info,
|
|
1615
|
+
token1Info,
|
|
1616
|
+
postFeeAmount,
|
|
1617
|
+
poolKey,
|
|
1618
|
+
bounds,
|
|
1619
|
+
maxIterations,
|
|
1620
|
+
priceRatioPrecision,
|
|
1621
|
+
acc,
|
|
1622
|
+
});
|
|
1623
|
+
calls.push(..._callsFinal);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
return calls;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
/**
|
|
1630
|
+
* @description This funciton requires atleast one of the pool tokens to be reward token
|
|
1631
|
+
* i.e. STRK.
|
|
1632
|
+
* @param params
|
|
1633
|
+
*/
|
|
1634
|
+
async _handleRewardAndVaultTokenMatchHarvest(params: {
|
|
1635
|
+
claim: HarvestInfo;
|
|
1636
|
+
isToken1: boolean;
|
|
1637
|
+
token0Info: TokenInfo;
|
|
1638
|
+
token1Info: TokenInfo;
|
|
1639
|
+
postFeeAmount: Web3Number;
|
|
1640
|
+
poolKey: EkuboPoolKey;
|
|
1641
|
+
bounds: EkuboBounds;
|
|
1642
|
+
maxIterations: number;
|
|
1643
|
+
priceRatioPrecision: number;
|
|
1644
|
+
acc: Account;
|
|
1645
|
+
}) {
|
|
1646
|
+
const { acc, claim, isToken1, token0Info, token1Info, postFeeAmount, poolKey, bounds, maxIterations, priceRatioPrecision } = params;
|
|
1647
|
+
const token0Amt = isToken1
|
|
1648
|
+
? new Web3Number(0, token0Info.decimals)
|
|
1649
|
+
: postFeeAmount;
|
|
1650
|
+
const token1Amt = isToken1
|
|
1651
|
+
? postFeeAmount
|
|
1652
|
+
: new Web3Number(0, token0Info.decimals);
|
|
1653
|
+
logger.verbose(
|
|
1654
|
+
`${
|
|
1655
|
+
EkuboCLVault.name
|
|
1656
|
+
}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
|
|
1657
|
+
);
|
|
1658
|
+
|
|
1659
|
+
// THis function cannot handle swapping of non-STRK pool,
|
|
1660
|
+
// bcz atleast one of token0Amt or token1Amt are in STRK terms.
|
|
1661
|
+
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
1662
|
+
poolKey,
|
|
1663
|
+
token0Amt,
|
|
1664
|
+
token1Amt,
|
|
1665
|
+
bounds,
|
|
1666
|
+
maxIterations,
|
|
1667
|
+
priceRatioPrecision
|
|
1668
|
+
);
|
|
1669
|
+
swapInfo.token_to_address = token0Info.address.address;
|
|
1670
|
+
logger.verbose(
|
|
1671
|
+
`${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
1672
|
+
);
|
|
1673
|
+
|
|
1674
|
+
logger.verbose(
|
|
1675
|
+
`${EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
1676
|
+
);
|
|
1677
|
+
const harvestEstimateCall = async (swapInfo1: SwapInfo) => {
|
|
1678
|
+
const swap1Amount = Web3Number.fromWei(
|
|
1679
|
+
uint256.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
1680
|
+
18 // cause its always STRK?
|
|
1681
|
+
).minimum(
|
|
1682
|
+
postFeeAmount.toFixed(18) // cause always strk
|
|
1683
|
+
); // ensure we don't swap more than we have
|
|
1684
|
+
swapInfo.token_from_amount = uint256.bnToUint256(swap1Amount.toWei());
|
|
1685
|
+
swapInfo.token_to_min_amount = uint256.bnToUint256(
|
|
1686
|
+
swap1Amount.multipliedBy(0).toWei() // placeholder
|
|
1687
|
+
); // 0.01% slippage
|
|
1688
|
+
|
|
1602
1689
|
logger.verbose(
|
|
1603
|
-
`${
|
|
1604
|
-
EkuboCLVault.name
|
|
1605
|
-
}: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
|
|
1690
|
+
`${EkuboCLVault.name}: harvest => swap1Amount: ${swap1Amount}`
|
|
1606
1691
|
);
|
|
1607
1692
|
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
token1Amt,
|
|
1612
|
-
bounds,
|
|
1613
|
-
maxIterations,
|
|
1614
|
-
priceRatioPrecision
|
|
1693
|
+
const remainingAmount = postFeeAmount.minus(swap1Amount).maximum(0);
|
|
1694
|
+
logger.verbose(
|
|
1695
|
+
`${EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
|
|
1615
1696
|
);
|
|
1616
|
-
|
|
1697
|
+
const swapInfo2 = {
|
|
1698
|
+
...swapInfo,
|
|
1699
|
+
token_from_amount: uint256.bnToUint256(remainingAmount.toWei()),
|
|
1700
|
+
};
|
|
1701
|
+
swapInfo2.token_to_address = token1Info.address.address;
|
|
1617
1702
|
logger.verbose(
|
|
1618
|
-
`${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(
|
|
1703
|
+
`${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(
|
|
1704
|
+
swapInfo
|
|
1705
|
+
)}`
|
|
1619
1706
|
);
|
|
1620
|
-
|
|
1621
1707
|
logger.verbose(
|
|
1622
|
-
`${EkuboCLVault.name}: harvest =>
|
|
1708
|
+
`${EkuboCLVault.name}: harvest => swapInfo2: ${JSON.stringify(
|
|
1709
|
+
swapInfo2
|
|
1710
|
+
)}`
|
|
1711
|
+
);
|
|
1712
|
+
const calldata = [
|
|
1713
|
+
claim.rewardsContract.address,
|
|
1714
|
+
{
|
|
1715
|
+
id: claim.claim.id,
|
|
1716
|
+
amount: claim.claim.amount.toWei(),
|
|
1717
|
+
claimee: claim.claim.claimee.address,
|
|
1718
|
+
},
|
|
1719
|
+
claim.proof.map((p) => num.getDecimalString(p)),
|
|
1720
|
+
swapInfo,
|
|
1721
|
+
swapInfo2,
|
|
1722
|
+
];
|
|
1723
|
+
logger.verbose(
|
|
1724
|
+
`${EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
1725
|
+
calldata
|
|
1726
|
+
)}`
|
|
1727
|
+
);
|
|
1728
|
+
return [this.contract.populate("harvest", calldata)];
|
|
1729
|
+
};
|
|
1730
|
+
const _callsFinal = await this.rebalanceIter(
|
|
1731
|
+
swapInfo,
|
|
1732
|
+
acc,
|
|
1733
|
+
harvestEstimateCall,
|
|
1734
|
+
claim.token.eq(poolKey.token0),
|
|
1735
|
+
0,
|
|
1736
|
+
0n,
|
|
1737
|
+
BigInt(postFeeAmount.toWei()), // upper limit is the post fee amount
|
|
1738
|
+
);
|
|
1739
|
+
logger.verbose(
|
|
1740
|
+
`${EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|
|
1741
|
+
_callsFinal
|
|
1742
|
+
)}`
|
|
1743
|
+
);
|
|
1744
|
+
|
|
1745
|
+
return _callsFinal;
|
|
1746
|
+
}
|
|
1747
|
+
|
|
1748
|
+
/**
|
|
1749
|
+
* @description This function handles harvesting of reward token that is not the same as any of the vault token
|
|
1750
|
+
* i.e. STRK is not part of vault tokens like BTC/ETH
|
|
1751
|
+
* @param params
|
|
1752
|
+
* @returns
|
|
1753
|
+
*/
|
|
1754
|
+
async _handleRewardAndVaultTokenMismatchHarvest(params: {
|
|
1755
|
+
claim: HarvestInfo;
|
|
1756
|
+
token0Info: TokenInfo;
|
|
1757
|
+
token1Info: TokenInfo;
|
|
1758
|
+
postFeeAmount: Web3Number;
|
|
1759
|
+
poolKey: EkuboPoolKey;
|
|
1760
|
+
bounds: EkuboBounds;
|
|
1761
|
+
maxIterations: number;
|
|
1762
|
+
priceRatioPrecision: number;
|
|
1763
|
+
acc: Account;
|
|
1764
|
+
}) {
|
|
1765
|
+
const { acc, claim, token0Info, token1Info, postFeeAmount, poolKey, bounds, maxIterations, priceRatioPrecision } = params;
|
|
1766
|
+
let token0Amt = postFeeAmount;
|
|
1767
|
+
|
|
1768
|
+
// receiver of output swap tokens (vault itself)
|
|
1769
|
+
const beneficiary = this.address.address;
|
|
1770
|
+
let harvestCall: Call | null = null;
|
|
1771
|
+
|
|
1772
|
+
/**
|
|
1773
|
+
* Approach: Use binary search to decide the optimal split of reward
|
|
1774
|
+
* such that the output tokens can be used for exact liquidity addition
|
|
1775
|
+
*/
|
|
1776
|
+
harvestCall = await this.harvestMismatchEstimateCallFn({
|
|
1777
|
+
postFeeAmount,
|
|
1778
|
+
claim,
|
|
1779
|
+
token0Info,
|
|
1780
|
+
token1Info,
|
|
1781
|
+
acc,
|
|
1782
|
+
});
|
|
1783
|
+
if (!harvestCall) {
|
|
1784
|
+
throw new Error("Harvest call not found");
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
return [harvestCall];
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
// given an amount (i.e. portion of reward to use to swap to token0), returns info on increasing or decreasing
|
|
1791
|
+
// amount for binary search
|
|
1792
|
+
async harvestMismatchEstimateCallFn(params: {
|
|
1793
|
+
postFeeAmount: Web3Number;
|
|
1794
|
+
claim: HarvestInfo;
|
|
1795
|
+
token0Info: TokenInfo;
|
|
1796
|
+
token1Info: TokenInfo;
|
|
1797
|
+
acc: Account;
|
|
1798
|
+
}) {
|
|
1799
|
+
const { postFeeAmount, claim, token0Info, token1Info, acc } = params;
|
|
1800
|
+
let harvestCall: Call | null = null;
|
|
1801
|
+
|
|
1802
|
+
const binarySearchCallbackFn = async (mid: bigint) => {
|
|
1803
|
+
const rewardPart2 = BigInt(postFeeAmount.toWei()) - mid;
|
|
1804
|
+
const avnuWrapper = new AvnuWrapper();
|
|
1805
|
+
const beneficiary = this.address.address;
|
|
1806
|
+
|
|
1807
|
+
// get quote for 1st part
|
|
1808
|
+
const quote1 = await avnuWrapper.getQuotes(
|
|
1809
|
+
claim.token.address,
|
|
1810
|
+
token0Info.address.address,
|
|
1811
|
+
mid.toString(),
|
|
1812
|
+
beneficiary
|
|
1813
|
+
);
|
|
1814
|
+
// default min amount is ok
|
|
1815
|
+
const swapInfo1 = await avnuWrapper.getSwapInfo(
|
|
1816
|
+
quote1,
|
|
1817
|
+
beneficiary,
|
|
1818
|
+
0,
|
|
1819
|
+
beneficiary
|
|
1623
1820
|
);
|
|
1624
|
-
const harvestEstimateCall = async (swapInfo1: SwapInfo) => {
|
|
1625
|
-
const swap1Amount = Web3Number.fromWei(
|
|
1626
|
-
uint256.uint256ToBN(swapInfo1.token_from_amount).toString(),
|
|
1627
|
-
18 // cause its always STRK?
|
|
1628
|
-
).minimum(
|
|
1629
|
-
postFeeAmount.toFixed(18) // cause always strk
|
|
1630
|
-
); // ensure we don't swap more than we have
|
|
1631
|
-
swapInfo.token_from_amount = uint256.bnToUint256(swap1Amount.toWei());
|
|
1632
|
-
swapInfo.token_to_min_amount = uint256.bnToUint256(
|
|
1633
|
-
swap1Amount.multipliedBy(0).toWei() // placeholder
|
|
1634
|
-
); // 0.01% slippage
|
|
1635
1821
|
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1822
|
+
// get quote for 2nd part
|
|
1823
|
+
const quote2 = await avnuWrapper.getQuotes(
|
|
1824
|
+
claim.token.address,
|
|
1825
|
+
token1Info.address.address,
|
|
1826
|
+
rewardPart2.toString(),
|
|
1827
|
+
beneficiary
|
|
1828
|
+
);
|
|
1829
|
+
// default min amount is ok
|
|
1830
|
+
const swapInfo2 = await avnuWrapper.getSwapInfo(
|
|
1831
|
+
quote2,
|
|
1832
|
+
beneficiary,
|
|
1833
|
+
0,
|
|
1834
|
+
beneficiary
|
|
1835
|
+
);
|
|
1639
1836
|
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
`${EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
|
|
1643
|
-
);
|
|
1644
|
-
const swapInfo2 = {
|
|
1645
|
-
...swapInfo,
|
|
1646
|
-
token_from_amount: uint256.bnToUint256(remainingAmount.toWei()),
|
|
1647
|
-
};
|
|
1648
|
-
swapInfo2.token_to_address = token1Info.address.address;
|
|
1649
|
-
logger.verbose(
|
|
1650
|
-
`${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(
|
|
1651
|
-
swapInfo
|
|
1652
|
-
)}`
|
|
1653
|
-
);
|
|
1654
|
-
logger.verbose(
|
|
1655
|
-
`${EkuboCLVault.name}: harvest => swapInfo2: ${JSON.stringify(
|
|
1656
|
-
swapInfo2
|
|
1657
|
-
)}`
|
|
1658
|
-
);
|
|
1837
|
+
// estimate the harvest using this swap info
|
|
1838
|
+
try {
|
|
1659
1839
|
const calldata = [
|
|
1660
1840
|
claim.rewardsContract.address,
|
|
1661
1841
|
{
|
|
@@ -1664,33 +1844,29 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1664
1844
|
claimee: claim.claim.claimee.address,
|
|
1665
1845
|
},
|
|
1666
1846
|
claim.proof.map((p) => num.getDecimalString(p)),
|
|
1667
|
-
|
|
1847
|
+
swapInfo1,
|
|
1668
1848
|
swapInfo2,
|
|
1669
1849
|
];
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
);
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
BigInt(postFeeAmount.toWei()), // upper limit is the post fee amount
|
|
1685
|
-
);
|
|
1686
|
-
logger.verbose(
|
|
1687
|
-
`${EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
|
|
1688
|
-
_callsFinal
|
|
1689
|
-
)}`
|
|
1690
|
-
);
|
|
1691
|
-
calls.push(..._callsFinal);
|
|
1850
|
+
harvestCall = this.contract.populate("harvest", calldata)
|
|
1851
|
+
const gas = await acc.estimateInvokeFee(harvestCall);
|
|
1852
|
+
return 'found';
|
|
1853
|
+
} catch (err: any) {
|
|
1854
|
+
console.error(err);
|
|
1855
|
+
if (err.message.includes('invalid token0 amount')) {
|
|
1856
|
+
// too much token0 amount left, may be swap less to token0
|
|
1857
|
+
return 'go_low';
|
|
1858
|
+
} else if (err.message.includes('invalid token1 amount')) {
|
|
1859
|
+
// too much token1 balance left, may be swap more to token0
|
|
1860
|
+
return 'go_high';
|
|
1861
|
+
}
|
|
1862
|
+
return 'retry';
|
|
1863
|
+
}
|
|
1692
1864
|
}
|
|
1693
|
-
|
|
1865
|
+
|
|
1866
|
+
// run the binary search
|
|
1867
|
+
await binarySearch(0n, BigInt(postFeeAmount.toWei()), binarySearchCallbackFn);
|
|
1868
|
+
|
|
1869
|
+
return harvestCall;
|
|
1694
1870
|
}
|
|
1695
1871
|
|
|
1696
1872
|
async getInvestmentFlows() {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Call, hash, num, shortString } from "starknet";
|
|
2
2
|
import { SIMPLE_SANITIZER, toBigInt } from "./adapter-utils";
|
|
3
|
-
import { ContractAddr } from "@/dataTypes";
|
|
3
|
+
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
4
|
+
import { IConfig, TokenInfo } from "@/interfaces";
|
|
5
|
+
import { PricerBase } from "@/modules/pricerBase";
|
|
4
6
|
import { LeafData } from "@/utils";
|
|
5
7
|
import { CacheClass } from "@/utils/cacheClass";
|
|
6
8
|
|
|
@@ -15,10 +17,96 @@ export interface ManageCall {
|
|
|
15
17
|
|
|
16
18
|
export type GenerateCallFn<T> = (params: T) => ManageCall;
|
|
17
19
|
export type AdapterLeafType<T> = {leaf: LeafData, callConstructor: GenerateCallFn<T>}
|
|
20
|
+
// export type GenerateCallFn<T> = (params: T) => ManageCall[];
|
|
21
|
+
// export type AdapterLeafType<T> = {leaves: LeafData[], callConstructor: GenerateCallFn<T>}
|
|
18
22
|
export type LeafAdapterFn<T> = () => AdapterLeafType<T>;
|
|
19
23
|
|
|
20
|
-
export
|
|
24
|
+
export enum APYType {
|
|
25
|
+
BASE = "base",
|
|
26
|
+
REWARD = "reward",
|
|
27
|
+
LST = "lst"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SupportedPosition {
|
|
31
|
+
asset: TokenInfo,
|
|
32
|
+
isDebt: boolean
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface BaseAdapterConfig {
|
|
36
|
+
baseToken: TokenInfo,
|
|
37
|
+
supportedPositions: SupportedPosition[],
|
|
38
|
+
networkConfig: IConfig,
|
|
39
|
+
pricer: PricerBase,
|
|
40
|
+
vaultAllocator: ContractAddr
|
|
41
|
+
vaultAddress: ContractAddr
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type PositionAPY = { apy: number, type: APYType };
|
|
45
|
+
export type PositionInfo = {
|
|
46
|
+
amount: Web3Number,
|
|
47
|
+
usdValue: number,
|
|
48
|
+
remarks?: string,
|
|
49
|
+
apy: PositionAPY
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// export abstract class BaseAdapter<T1, T2> extends CacheClass {
|
|
53
|
+
export abstract class BaseAdapter extends CacheClass {
|
|
54
|
+
|
|
55
|
+
// readonly config: BaseAdapterConfig;
|
|
56
|
+
|
|
57
|
+
// constructor(config: BaseAdapterConfig) {
|
|
58
|
+
// super();
|
|
59
|
+
// this.config = config;
|
|
60
|
+
// }
|
|
61
|
+
|
|
62
|
+
constructor() {
|
|
63
|
+
super();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// /**
|
|
67
|
+
// * Loop through all supported positions and return amount, usd value, remarks and apy for each
|
|
68
|
+
// */
|
|
69
|
+
// async getPositions(): Promise<PositionInfo[]> {
|
|
70
|
+
// const results: PositionInfo[] = [];
|
|
71
|
+
// for (const supported of this.config.supportedPositions) {
|
|
72
|
+
// const amount = await this.getPosition(supported);
|
|
73
|
+
// const usdValue = await this.getUSDValue(supported.asset, amount);
|
|
74
|
+
// const apy = await this.getAPY(supported);
|
|
75
|
+
// results.push({ amount, usdValue, apy });
|
|
76
|
+
// }
|
|
77
|
+
// return results;
|
|
78
|
+
// }
|
|
21
79
|
|
|
80
|
+
// /**
|
|
81
|
+
// * Implemented by child adapters to compute APY for a given supported position
|
|
82
|
+
// */
|
|
83
|
+
// protected abstract getAPY(supportedPosition: SupportedPosition): Promise<PositionAPY>;
|
|
84
|
+
|
|
85
|
+
// /**
|
|
86
|
+
// * Implemented by child adapters to fetch amount for a given supported position
|
|
87
|
+
// */
|
|
88
|
+
// protected abstract getPosition(supportedPosition: SupportedPosition): Promise<Web3Number>;
|
|
89
|
+
|
|
90
|
+
// /**
|
|
91
|
+
// * Implemented by child adapters to calculate maximum deposit positions
|
|
92
|
+
// * @param amount Optional amount in baseToken to deposit
|
|
93
|
+
// */
|
|
94
|
+
// protected abstract maxDeposit(amount?: Web3Number): Promise<PositionInfo[]>;
|
|
95
|
+
|
|
96
|
+
// /**
|
|
97
|
+
// * Implemented by child adapters to calculate maximum withdraw positions
|
|
98
|
+
// */
|
|
99
|
+
// protected abstract maxWithdraw(): Promise<PositionInfo[]>;
|
|
100
|
+
|
|
101
|
+
// /**
|
|
102
|
+
// * Uses pricer to convert an amount of an asset to USD value
|
|
103
|
+
// */
|
|
104
|
+
// protected async getUSDValue(asset: TokenInfo, amount: Web3Number): Promise<number> {
|
|
105
|
+
// const priceInfo = await this.config.pricer.getPrice(asset.symbol);
|
|
106
|
+
// return amount.toNumber() * priceInfo.price;
|
|
107
|
+
// }
|
|
108
|
+
|
|
109
|
+
|
|
22
110
|
protected constructSimpleLeafData(params: {
|
|
23
111
|
id: string,
|
|
24
112
|
target: ContractAddr,
|
|
@@ -38,4 +126,98 @@ export class BaseAdapter extends CacheClass {
|
|
|
38
126
|
]
|
|
39
127
|
};
|
|
40
128
|
}
|
|
129
|
+
|
|
130
|
+
// /**
|
|
131
|
+
// * Implementor must provide target/method/packedArguments/sanitizer for deposit leaf construction
|
|
132
|
+
// */
|
|
133
|
+
// protected abstract _getDepositLeaf(): {
|
|
134
|
+
// target: ContractAddr,
|
|
135
|
+
// method: string,
|
|
136
|
+
// packedArguments: bigint[],
|
|
137
|
+
// sanitizer: ContractAddr,
|
|
138
|
+
// id: string
|
|
139
|
+
// }[];
|
|
140
|
+
|
|
141
|
+
// /**
|
|
142
|
+
// * Implementor must provide target/method/packedArguments/sanitizer for withdraw leaf construction
|
|
143
|
+
// */
|
|
144
|
+
// protected abstract _getWithdrawLeaf(): {
|
|
145
|
+
// target: ContractAddr,
|
|
146
|
+
// method: string,
|
|
147
|
+
// packedArguments: bigint[],
|
|
148
|
+
// sanitizer: ContractAddr,
|
|
149
|
+
// id: string
|
|
150
|
+
// }[];
|
|
151
|
+
|
|
152
|
+
// /**
|
|
153
|
+
// * Returns deposit leaf adapter using configured proof id
|
|
154
|
+
// */
|
|
155
|
+
// getDepositLeaf(): AdapterLeafType<T1> {
|
|
156
|
+
// const leafConfigs = this._getDepositLeaf();
|
|
157
|
+
// const leaves = leafConfigs.map(config => {
|
|
158
|
+
// const { target, method, packedArguments, sanitizer, id } = config;
|
|
159
|
+
// const leaf = this.constructSimpleLeafData({
|
|
160
|
+
// id: id,
|
|
161
|
+
// target,
|
|
162
|
+
// method,
|
|
163
|
+
// packedArguments
|
|
164
|
+
// }, sanitizer);
|
|
165
|
+
// return leaf;
|
|
166
|
+
// });
|
|
167
|
+
// return { leaves, callConstructor: this.getDepositCall.bind(this) as unknown as GenerateCallFn<T1> };
|
|
168
|
+
// }
|
|
169
|
+
|
|
170
|
+
// /**
|
|
171
|
+
// * Returns withdraw leaf adapter using configured proof id
|
|
172
|
+
// */
|
|
173
|
+
// getWithdrawLeaf(): AdapterLeafType<T2> {
|
|
174
|
+
// const leafConfigs = this._getWithdrawLeaf();
|
|
175
|
+
// const leaves = leafConfigs.map(config => {
|
|
176
|
+
// const { target, method, packedArguments, sanitizer, id } = config;
|
|
177
|
+
// const leaf = this.constructSimpleLeafData({
|
|
178
|
+
// id: id,
|
|
179
|
+
// target,
|
|
180
|
+
// method,
|
|
181
|
+
// packedArguments
|
|
182
|
+
// }, sanitizer ?? SIMPLE_SANITIZER);
|
|
183
|
+
// return leaf;
|
|
184
|
+
// });
|
|
185
|
+
// return { leaves, callConstructor: this.getWithdrawCall.bind(this) as unknown as GenerateCallFn<T2> };
|
|
186
|
+
// }
|
|
187
|
+
|
|
188
|
+
// /**
|
|
189
|
+
// * Default deposit callConstructor: expects params as calldata (bigint[])
|
|
190
|
+
// */
|
|
191
|
+
// protected getDepositCall<T1 = bigint[]>(params: T1): ManageCall[] {
|
|
192
|
+
// const leafConfigs = this._getDepositLeaf();
|
|
193
|
+
// return leafConfigs.map(config => {
|
|
194
|
+
// const { target, method, sanitizer } = config;
|
|
195
|
+
// return {
|
|
196
|
+
// sanitizer: sanitizer ?? SIMPLE_SANITIZER,
|
|
197
|
+
// call: {
|
|
198
|
+
// contractAddress: target,
|
|
199
|
+
// selector: hash.getSelectorFromName(method),
|
|
200
|
+
// calldata: params as unknown as bigint[]
|
|
201
|
+
// }
|
|
202
|
+
// };
|
|
203
|
+
// });
|
|
204
|
+
// }
|
|
205
|
+
|
|
206
|
+
// /**
|
|
207
|
+
// * Default withdraw callConstructor: expects params as calldata (bigint[])
|
|
208
|
+
// */
|
|
209
|
+
// protected getWithdrawCall<T2 = bigint[]>(params: T2): ManageCall[] {
|
|
210
|
+
// const leafConfigs = this._getWithdrawLeaf();
|
|
211
|
+
// return leafConfigs.map(config => {
|
|
212
|
+
// const { target, method, sanitizer } = config;
|
|
213
|
+
// return {
|
|
214
|
+
// sanitizer: sanitizer ?? SIMPLE_SANITIZER,
|
|
215
|
+
// call: {
|
|
216
|
+
// contractAddress: target,
|
|
217
|
+
// selector: hash.getSelectorFromName(method),
|
|
218
|
+
// calldata: params as unknown as bigint[]
|
|
219
|
+
// }
|
|
220
|
+
// };
|
|
221
|
+
// });
|
|
222
|
+
// }
|
|
41
223
|
}
|