@strkfarm/sdk 1.1.39 → 1.1.41

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strkfarm/sdk",
3
- "version": "1.1.39",
3
+ "version": "1.1.41",
4
4
  "description": "STRKFarm TS SDK (Meant for our internal use, but feel free to use it)",
5
5
  "typings": "dist/index.d.ts",
6
6
  "types": "dist/index.d.ts",
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
 
@@ -120,17 +120,18 @@ export class AvnuWrapper {
120
120
 
121
121
  static buildZeroSwap(
122
122
  tokenToSell: ContractAddr,
123
- address: string
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: tokenToSell.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: address,
132
+ beneficiary: beneficiary,
132
133
  integrator_fee_amount_bps: 0,
133
- integrator_fee_recipient: address,
134
+ integrator_fee_recipient: beneficiary,
134
135
  routes: [],
135
136
  };
136
137
  }
@@ -33,23 +33,24 @@ export class Harvests {
33
33
 
34
34
  const unClaimed: HarvestInfo[] = [];
35
35
 
36
- const cls = await this.config.provider.getClassAt(rewards[0].rewardsContract.address);
37
- for (let reward of rewards) {
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
- unClaimed.unshift(reward); // to ensure older harvest is first
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;
@@ -481,7 +482,6 @@ export class EkuboCLVault extends BaseStrategy<
481
482
  for (let i = len - 1; i >= 0; --i) {
482
483
  let record: any = await this.contract.call("get_rewards_info", [i]);
483
484
  logger.verbose(`${EkuboCLVault.name}: getHarvestRewardShares: ${i}`);
484
- console.log(record);
485
485
  const block = Number(record.block_number);
486
486
  if (block < fromBlock) {
487
487
  return shares;
@@ -713,16 +713,10 @@ export class EkuboCLVault extends BaseStrategy<
713
713
  const sqrtRatio = EkuboCLVault.div2Power128(
714
714
  BigInt(priceInfo.sqrt_ratio.toString())
715
715
  );
716
- console.log(
717
- `EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, sqrtRatio: ${sqrtRatio}, ${priceInfo.sqrt_ratio.toString()}`
718
- );
719
716
  const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
720
717
  const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
721
718
  const price = sqrtRatio * sqrtRatio * (10 ** token0Info.decimals) / ( 10 ** token1Info.decimals);
722
719
  const tick = priceInfo.tick;
723
- console.log(
724
- `EkuboCLVault: getCurrentPrice: blockIdentifier: ${blockIdentifier}, price: ${price}, tick: ${tick.mag}, ${tick.sign}`
725
- );
726
720
  return {
727
721
  price,
728
722
  tick: Number(tick.mag) * (tick.sign ? -1 : 1),
@@ -1592,70 +1586,249 @@ export class EkuboCLVault extends BaseStrategy<
1592
1586
  }: harvest => Processing claim, isToken1: ${isToken1} amount: ${postFeeAmount.toWei()}`
1593
1587
  );
1594
1588
 
1595
- // todo what if the claim token is neither token0 or token1
1596
- const token0Amt = isToken1
1597
- ? new Web3Number(0, token0Info.decimals)
1598
- : postFeeAmount;
1599
- const token1Amt = isToken1
1600
- ? postFeeAmount
1601
- : new Web3Number(0, token0Info.decimals);
1589
+ const isRewardTokenMatch = claim.token.eq(poolKey.token0) || claim.token.eq(poolKey.token1);
1590
+ if (isRewardTokenMatch) {
1591
+ const _callsFinal = await this._handleRewardAndVaultTokenMatchHarvest({
1592
+ acc,
1593
+ claim,
1594
+ isToken1,
1595
+ token0Info,
1596
+ token1Info,
1597
+ postFeeAmount,
1598
+ poolKey,
1599
+ bounds,
1600
+ maxIterations,
1601
+ priceRatioPrecision,
1602
+ });
1603
+ calls.push(..._callsFinal);
1604
+ } else {
1605
+ const _callsFinal = await this._handleRewardAndVaultTokenMismatchHarvest({
1606
+ claim,
1607
+ token0Info,
1608
+ token1Info,
1609
+ postFeeAmount,
1610
+ poolKey,
1611
+ bounds,
1612
+ maxIterations,
1613
+ priceRatioPrecision,
1614
+ acc,
1615
+ });
1616
+ calls.push(..._callsFinal);
1617
+ }
1618
+ }
1619
+ return calls;
1620
+ }
1621
+
1622
+ /**
1623
+ * @description This funciton requires atleast one of the pool tokens to be reward token
1624
+ * i.e. STRK.
1625
+ * @param params
1626
+ */
1627
+ async _handleRewardAndVaultTokenMatchHarvest(params: {
1628
+ claim: HarvestInfo;
1629
+ isToken1: boolean;
1630
+ token0Info: TokenInfo;
1631
+ token1Info: TokenInfo;
1632
+ postFeeAmount: Web3Number;
1633
+ poolKey: EkuboPoolKey;
1634
+ bounds: EkuboBounds;
1635
+ maxIterations: number;
1636
+ priceRatioPrecision: number;
1637
+ acc: Account;
1638
+ }) {
1639
+ const { acc, claim, isToken1, token0Info, token1Info, postFeeAmount, poolKey, bounds, maxIterations, priceRatioPrecision } = params;
1640
+ const token0Amt = isToken1
1641
+ ? new Web3Number(0, token0Info.decimals)
1642
+ : postFeeAmount;
1643
+ const token1Amt = isToken1
1644
+ ? postFeeAmount
1645
+ : new Web3Number(0, token0Info.decimals);
1646
+ logger.verbose(
1647
+ `${
1648
+ EkuboCLVault.name
1649
+ }: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
1650
+ );
1651
+
1652
+ // THis function cannot handle swapping of non-STRK pool,
1653
+ // bcz atleast one of token0Amt or token1Amt are in STRK terms.
1654
+ const swapInfo = await this.getSwapInfoGivenAmounts(
1655
+ poolKey,
1656
+ token0Amt,
1657
+ token1Amt,
1658
+ bounds,
1659
+ maxIterations,
1660
+ priceRatioPrecision
1661
+ );
1662
+ swapInfo.token_to_address = token0Info.address.address;
1663
+ logger.verbose(
1664
+ `${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
1665
+ );
1666
+
1667
+ logger.verbose(
1668
+ `${EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
1669
+ );
1670
+ const harvestEstimateCall = async (swapInfo1: SwapInfo) => {
1671
+ const swap1Amount = Web3Number.fromWei(
1672
+ uint256.uint256ToBN(swapInfo1.token_from_amount).toString(),
1673
+ 18 // cause its always STRK?
1674
+ ).minimum(
1675
+ postFeeAmount.toFixed(18) // cause always strk
1676
+ ); // ensure we don't swap more than we have
1677
+ swapInfo.token_from_amount = uint256.bnToUint256(swap1Amount.toWei());
1678
+ swapInfo.token_to_min_amount = uint256.bnToUint256(
1679
+ swap1Amount.multipliedBy(0).toWei() // placeholder
1680
+ ); // 0.01% slippage
1681
+
1602
1682
  logger.verbose(
1603
- `${
1604
- EkuboCLVault.name
1605
- }: harvest => token0Amt: ${token0Amt.toString()}, token1Amt: ${token1Amt.toString()}`
1683
+ `${EkuboCLVault.name}: harvest => swap1Amount: ${swap1Amount}`
1606
1684
  );
1607
1685
 
1608
- const swapInfo = await this.getSwapInfoGivenAmounts(
1609
- poolKey,
1610
- token0Amt,
1611
- token1Amt,
1612
- bounds,
1613
- maxIterations,
1614
- priceRatioPrecision
1686
+ const remainingAmount = postFeeAmount.minus(swap1Amount).maximum(0);
1687
+ logger.verbose(
1688
+ `${EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
1689
+ );
1690
+ const swapInfo2 = {
1691
+ ...swapInfo,
1692
+ token_from_amount: uint256.bnToUint256(remainingAmount.toWei()),
1693
+ };
1694
+ swapInfo2.token_to_address = token1Info.address.address;
1695
+ logger.verbose(
1696
+ `${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(
1697
+ swapInfo
1698
+ )}`
1615
1699
  );
1616
- swapInfo.token_to_address = token0Info.address.address;
1617
1700
  logger.verbose(
1618
- `${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
1701
+ `${EkuboCLVault.name}: harvest => swapInfo2: ${JSON.stringify(
1702
+ swapInfo2
1703
+ )}`
1619
1704
  );
1620
-
1705
+ const calldata = [
1706
+ claim.rewardsContract.address,
1707
+ {
1708
+ id: claim.claim.id,
1709
+ amount: claim.claim.amount.toWei(),
1710
+ claimee: claim.claim.claimee.address,
1711
+ },
1712
+ claim.proof.map((p) => num.getDecimalString(p)),
1713
+ swapInfo,
1714
+ swapInfo2,
1715
+ ];
1621
1716
  logger.verbose(
1622
- `${EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
1717
+ `${EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
1718
+ calldata
1719
+ )}`
1720
+ );
1721
+ return [this.contract.populate("harvest", calldata)];
1722
+ };
1723
+ const _callsFinal = await this.rebalanceIter(
1724
+ swapInfo,
1725
+ acc,
1726
+ harvestEstimateCall,
1727
+ claim.token.eq(poolKey.token0),
1728
+ 0,
1729
+ 0n,
1730
+ BigInt(postFeeAmount.toWei()), // upper limit is the post fee amount
1731
+ );
1732
+ logger.verbose(
1733
+ `${EkuboCLVault.name}: harvest => _callsFinal: ${JSON.stringify(
1734
+ _callsFinal
1735
+ )}`
1736
+ );
1737
+
1738
+ return _callsFinal;
1739
+ }
1740
+
1741
+ /**
1742
+ * @description This function handles harvesting of reward token that is not the same as any of the vault token
1743
+ * i.e. STRK is not part of vault tokens like BTC/ETH
1744
+ * @param params
1745
+ * @returns
1746
+ */
1747
+ async _handleRewardAndVaultTokenMismatchHarvest(params: {
1748
+ claim: HarvestInfo;
1749
+ token0Info: TokenInfo;
1750
+ token1Info: TokenInfo;
1751
+ postFeeAmount: Web3Number;
1752
+ poolKey: EkuboPoolKey;
1753
+ bounds: EkuboBounds;
1754
+ maxIterations: number;
1755
+ priceRatioPrecision: number;
1756
+ acc: Account;
1757
+ }) {
1758
+ const { acc, claim, token0Info, token1Info, postFeeAmount, poolKey, bounds, maxIterations, priceRatioPrecision } = params;
1759
+ let token0Amt = postFeeAmount;
1760
+
1761
+ // receiver of output swap tokens (vault itself)
1762
+ const beneficiary = this.address.address;
1763
+ let harvestCall: Call | null = null;
1764
+
1765
+ /**
1766
+ * Approach: Use binary search to decide the optimal split of reward
1767
+ * such that the output tokens can be used for exact liquidity addition
1768
+ */
1769
+ harvestCall = await this.harvestMismatchEstimateCallFn({
1770
+ postFeeAmount,
1771
+ claim,
1772
+ token0Info,
1773
+ token1Info,
1774
+ acc,
1775
+ });
1776
+ if (!harvestCall) {
1777
+ throw new Error("Harvest call not found");
1778
+ }
1779
+
1780
+ return [harvestCall];
1781
+ }
1782
+
1783
+ // given an amount (i.e. portion of reward to use to swap to token0), returns info on increasing or decreasing
1784
+ // amount for binary search
1785
+ async harvestMismatchEstimateCallFn(params: {
1786
+ postFeeAmount: Web3Number;
1787
+ claim: HarvestInfo;
1788
+ token0Info: TokenInfo;
1789
+ token1Info: TokenInfo;
1790
+ acc: Account;
1791
+ }) {
1792
+ const { postFeeAmount, claim, token0Info, token1Info, acc } = params;
1793
+ let harvestCall: Call | null = null;
1794
+
1795
+ const binarySearchCallbackFn = async (mid: bigint) => {
1796
+ const rewardPart2 = BigInt(postFeeAmount.toWei()) - mid;
1797
+ const avnuWrapper = new AvnuWrapper();
1798
+ const beneficiary = this.address.address;
1799
+
1800
+ // get quote for 1st part
1801
+ const quote1 = await avnuWrapper.getQuotes(
1802
+ claim.token.address,
1803
+ token0Info.address.address,
1804
+ mid.toString(),
1805
+ beneficiary
1806
+ );
1807
+ // default min amount is ok
1808
+ const swapInfo1 = await avnuWrapper.getSwapInfo(
1809
+ quote1,
1810
+ beneficiary,
1811
+ 0,
1812
+ beneficiary
1623
1813
  );
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
1814
 
1636
- logger.verbose(
1637
- `${EkuboCLVault.name}: harvest => swap1Amount: ${swap1Amount}`
1638
- );
1815
+ // get quote for 2nd part
1816
+ const quote2 = await avnuWrapper.getQuotes(
1817
+ claim.token.address,
1818
+ token1Info.address.address,
1819
+ rewardPart2.toString(),
1820
+ beneficiary
1821
+ );
1822
+ // default min amount is ok
1823
+ const swapInfo2 = await avnuWrapper.getSwapInfo(
1824
+ quote2,
1825
+ beneficiary,
1826
+ 0,
1827
+ beneficiary
1828
+ );
1639
1829
 
1640
- const remainingAmount = postFeeAmount.minus(swap1Amount).maximum(0);
1641
- logger.verbose(
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
- );
1830
+ // estimate the harvest using this swap info
1831
+ try {
1659
1832
  const calldata = [
1660
1833
  claim.rewardsContract.address,
1661
1834
  {
@@ -1664,33 +1837,28 @@ export class EkuboCLVault extends BaseStrategy<
1664
1837
  claimee: claim.claim.claimee.address,
1665
1838
  },
1666
1839
  claim.proof.map((p) => num.getDecimalString(p)),
1667
- swapInfo,
1840
+ swapInfo1,
1668
1841
  swapInfo2,
1669
1842
  ];
1670
- logger.verbose(
1671
- `${EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
1672
- calldata
1673
- )}`
1674
- );
1675
- return [this.contract.populate("harvest", calldata)];
1676
- };
1677
- const _callsFinal = await this.rebalanceIter(
1678
- swapInfo,
1679
- acc,
1680
- harvestEstimateCall,
1681
- claim.token.eq(poolKey.token0),
1682
- 0,
1683
- 0n,
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);
1843
+ harvestCall = this.contract.populate("harvest", calldata)
1844
+ const gas = await acc.estimateInvokeFee(harvestCall);
1845
+ return 'found';
1846
+ } catch (err: any) {
1847
+ if (err.message.includes('invalid token0 amount')) {
1848
+ // too much token0 amount left, may be swap less to token0
1849
+ return 'go_low';
1850
+ } else if (err.message.includes('invalid token1 amount')) {
1851
+ // too much token1 balance left, may be swap more to token0
1852
+ return 'go_high';
1853
+ }
1854
+ return 'retry';
1855
+ }
1692
1856
  }
1693
- return calls;
1857
+
1858
+ // run the binary search
1859
+ await binarySearch(0n, BigInt(postFeeAmount.toWei()), binarySearchCallbackFn);
1860
+
1861
+ return harvestCall;
1694
1862
  }
1695
1863
 
1696
1864
  async getInvestmentFlows() {