@harvest-finance/harvest-strategy-arbitrum 0.0.1-security → 1.0.1

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.

Potentially problematic release.


This version of @harvest-finance/harvest-strategy-arbitrum might be problematic. Click here for more details.

Files changed (199) hide show
  1. package/README.md +127 -5
  2. package/contracts/base/Controller.sol +358 -0
  3. package/contracts/base/Drip.sol +86 -0
  4. package/contracts/base/PotPool.sol +367 -0
  5. package/contracts/base/ProfitSharingReceiver.sol +38 -0
  6. package/contracts/base/Reader.sol +54 -0
  7. package/contracts/base/RewardForwarder.sol +109 -0
  8. package/contracts/base/VaultProxy.sol +34 -0
  9. package/contracts/base/VaultStorage.sol +205 -0
  10. package/contracts/base/VaultV1.sol +371 -0
  11. package/contracts/base/VaultV1GMX.sol +465 -0
  12. package/contracts/base/VaultV2.sol +111 -0
  13. package/contracts/base/VaultV2GMX.sol +111 -0
  14. package/contracts/base/factory/MegaFactory.sol +120 -0
  15. package/contracts/base/factory/interface/IPoolFactory.sol +6 -0
  16. package/contracts/base/factory/interface/IStrategyFactory.sol +6 -0
  17. package/contracts/base/factory/interface/IVaultFactory.sol +7 -0
  18. package/contracts/base/factory/pool/PotPoolFactory.sol +41 -0
  19. package/contracts/base/factory/strategy/UpgradableStrategyFactory.sol +19 -0
  20. package/contracts/base/factory/vault/RegularVaultFactory.sol +34 -0
  21. package/contracts/base/incentives/GlobalIncentivesExecutor.sol +85 -0
  22. package/contracts/base/incentives/GlobalIncentivesHelper.sol +174 -0
  23. package/contracts/base/incentives/NotifyHelperGeneric.sol +61 -0
  24. package/contracts/base/incentives/NotifyHelperStateful.sol +290 -0
  25. package/contracts/base/incentives/ViewerNotifyHelperStateful.sol +25 -0
  26. package/contracts/base/inheritance/Controllable.sol +25 -0
  27. package/contracts/base/inheritance/ControllableInit.sol +30 -0
  28. package/contracts/base/inheritance/Governable.sol +28 -0
  29. package/contracts/base/inheritance/GovernableInit.sol +50 -0
  30. package/contracts/base/inheritance/IUpgradeSource.sol +7 -0
  31. package/contracts/base/inheritance/OwnableWhitelist.sol +17 -0
  32. package/contracts/base/inheritance/Storage.sol +35 -0
  33. package/contracts/base/interface/IBalDex.sol +7 -0
  34. package/contracts/base/interface/IController.sol +132 -0
  35. package/contracts/base/interface/IDex.sol +9 -0
  36. package/contracts/base/interface/IERC4626.sol +261 -0
  37. package/contracts/base/interface/IGMXStrategy.sol +37 -0
  38. package/contracts/base/interface/IGlobalIncentivesHelper.sol +6 -0
  39. package/contracts/base/interface/IPotPool.sol +70 -0
  40. package/contracts/base/interface/IProfitSharingReceiver.sol +9 -0
  41. package/contracts/base/interface/IRewardForwarder.sol +57 -0
  42. package/contracts/base/interface/IStrategy.sol +37 -0
  43. package/contracts/base/interface/IUniversalLiquidator.sol +21 -0
  44. package/contracts/base/interface/IUniversalLiquidatorRegistry.sol +20 -0
  45. package/contracts/base/interface/IUpgradeSource.sol +9 -0
  46. package/contracts/base/interface/IVault.sol +58 -0
  47. package/contracts/base/interface/IVaultGMX.sol +71 -0
  48. package/contracts/base/interface/aura/IAuraBaseRewardPool.sol +25 -0
  49. package/contracts/base/interface/aura/IAuraBooster.sol +17 -0
  50. package/contracts/base/interface/aura/IAuraDepositor.sol +7 -0
  51. package/contracts/base/interface/balancer/Gauge.sol +22 -0
  52. package/contracts/base/interface/balancer/IBVault.sol +580 -0
  53. package/contracts/base/interface/balancer/IBalancerMinter.sol +114 -0
  54. package/contracts/base/interface/balancer/IGyroPool.sol +7 -0
  55. package/contracts/base/interface/balancer/linearPool/ILinearPool.sol +184 -0
  56. package/contracts/base/interface/balancer/linearPool/ILinearPoolFactory.sol +16 -0
  57. package/contracts/base/interface/balancer/linearPool/ILinearPoolRebalancer.sol +8 -0
  58. package/contracts/base/interface/balancer/linearPool/IPoolSwapStructs.sol +56 -0
  59. package/contracts/base/interface/compound/CTokenInterface.sol +29 -0
  60. package/contracts/base/interface/compound/IComptroller.sol +9 -0
  61. package/contracts/base/interface/dolomite/IDepositWithdraw.sol +13 -0
  62. package/contracts/base/interface/dolomite/IDolomiteMargin.sol +15 -0
  63. package/contracts/base/interface/dolomite/IRewardsDistributor.sol +11 -0
  64. package/contracts/base/interface/gamma/IClearing.sol +7 -0
  65. package/contracts/base/interface/gamma/IHypervisor.sol +9 -0
  66. package/contracts/base/interface/gamma/IUniProxy.sol +14 -0
  67. package/contracts/base/interface/gmx/EventUtils.sol +253 -0
  68. package/contracts/base/interface/gmx/ICallbackReceiver.sol +119 -0
  69. package/contracts/base/interface/gmx/IDataStore.sol +7 -0
  70. package/contracts/base/interface/gmx/IExchangeRouter.sol +38 -0
  71. package/contracts/base/interface/gmx/IGMXViewer.sol +7 -0
  72. package/contracts/base/interface/gmx/IHandler.sol +12 -0
  73. package/contracts/base/interface/gmx/IMarket.sol +7 -0
  74. package/contracts/base/interface/gmx/IOracle.sol +6 -0
  75. package/contracts/base/interface/gmx/IPriceFeed.sol +12 -0
  76. package/contracts/base/interface/gmx/IReader.sol +49 -0
  77. package/contracts/base/interface/gmx/IRoleStore.sol +6 -0
  78. package/contracts/base/interface/ipor/Errors.sol +20 -0
  79. package/contracts/base/interface/ipor/FuseStorageLib.sol +71 -0
  80. package/contracts/base/interface/ipor/FusesLib.sol +149 -0
  81. package/contracts/base/interface/ipor/IFuseCommon.sol +9 -0
  82. package/contracts/base/interface/ipor/IFuseInstantWithdraw.sol +14 -0
  83. package/contracts/base/interface/ipor/IMarketBalanceFuse.sol +10 -0
  84. package/contracts/base/interface/ipor/IPriceOracleMiddleware.sol +42 -0
  85. package/contracts/base/interface/ipor/IporMath.sol +110 -0
  86. package/contracts/base/interface/ipor/PlasmaVaultConfigLib.sol +106 -0
  87. package/contracts/base/interface/ipor/PlasmaVaultLib.sol +293 -0
  88. package/contracts/base/interface/ipor/PlasmaVaultStorageLib.sol +352 -0
  89. package/contracts/base/interface/merkl/IDistributor.sol +6 -0
  90. package/contracts/base/interface/notional/INProxy.sol +44 -0
  91. package/contracts/base/interface/notional/IPrimeToken.sol +6 -0
  92. package/contracts/base/interface/venus/IRewardsDistributor.sol +6 -0
  93. package/contracts/base/interface/weth/IWETH.sol +39 -0
  94. package/contracts/base/ipor/Erc4626BalanceFuse.sol +54 -0
  95. package/contracts/base/ipor/Erc4626SupplyFuse.sol +134 -0
  96. package/contracts/base/noop/NoopStrategyUpgradeable.sol +90 -0
  97. package/contracts/base/upgradability/BaseUpgradeabilityProxy.sol +60 -0
  98. package/contracts/base/upgradability/BaseUpgradeableStrategy.sol +144 -0
  99. package/contracts/base/upgradability/BaseUpgradeableStrategyStorage.sol +284 -0
  100. package/contracts/base/upgradability/IUpgradable.sol +7 -0
  101. package/contracts/base/upgradability/ReentrancyGuardUpgradeable.sol +51 -0
  102. package/contracts/base/upgradability/StrategyProxy.sol +34 -0
  103. package/contracts/strategies/aura/AuraStrategy.sol +403 -0
  104. package/contracts/strategies/aura/AuraStrategyMainnet_MORE_GYD.sol +32 -0
  105. package/contracts/strategies/aura/AuraStrategyMainnet_sUSDe_GYD.sol +31 -0
  106. package/contracts/strategies/aura/AuraStrategyMainnet_waFRAX_sFRAX.sol +31 -0
  107. package/contracts/strategies/aura/AuraStrategyMainnet_waGHO_GYD.sol +31 -0
  108. package/contracts/strategies/aura/AuraStrategyMainnet_waUSDC_GHO.sol +32 -0
  109. package/contracts/strategies/aura/AuraStrategyMainnet_waUSDC_GYD.sol +31 -0
  110. package/contracts/strategies/aura/AuraStrategyMainnet_waUSDT_GYD.sol +31 -0
  111. package/contracts/strategies/aura/AuraStrategyMainnet_wstETH_GYD.sol +31 -0
  112. package/contracts/strategies/camelot/CamelotV3Strategy.sol +304 -0
  113. package/contracts/strategies/camelot/CamelotV3StrategyMainnet_ARB_USDC.sol +28 -0
  114. package/contracts/strategies/camelot/CamelotV3StrategyMainnet_ETH_USDC.sol +28 -0
  115. package/contracts/strategies/camelot/CamelotV3StrategyMainnet_ETH_USDT.sol +28 -0
  116. package/contracts/strategies/camelot/CamelotV3StrategyMainnet_GMX_ETH.sol +28 -0
  117. package/contracts/strategies/camelot/CamelotV3StrategyMainnet_GRAIL_ETH.sol +28 -0
  118. package/contracts/strategies/camelot/CamelotV3StrategyMainnet_USDC_USDT.sol +28 -0
  119. package/contracts/strategies/camelot/CamelotV3StrategyMainnet_WBTC_ETH.sol +28 -0
  120. package/contracts/strategies/dolomite/DolomiteLendStrategy.sol +273 -0
  121. package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_DAI.sol +26 -0
  122. package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_GMX.sol +26 -0
  123. package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_USDC.sol +26 -0
  124. package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_USDCe.sol +26 -0
  125. package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_USDT.sol +26 -0
  126. package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_WBTC.sol +26 -0
  127. package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_WETH.sol +26 -0
  128. package/contracts/strategies/fluid/FluidLendStrategy.sol +241 -0
  129. package/contracts/strategies/fluid/FluidLendStrategyMainnet_ETH.sol +25 -0
  130. package/contracts/strategies/fluid/FluidLendStrategyMainnet_USDC.sol +25 -0
  131. package/contracts/strategies/fluid/FluidLendStrategyMainnet_USDT.sol +25 -0
  132. package/contracts/strategies/gmx/GMXStrategy.sol +472 -0
  133. package/contracts/strategies/gmx/GMXStrategyMainnet_WBTC.sol +25 -0
  134. package/contracts/strategies/gmx/GMXViewer.sol +110 -0
  135. package/contracts/strategies/notional/NotionalStrategy.sol +223 -0
  136. package/contracts/strategies/notional/NotionalStrategyMainnet_nETH.sol +27 -0
  137. package/contracts/strategies/notional/NotionalStrategyMainnet_nUSDC.sol +27 -0
  138. package/contracts/strategies/notional/NotionalStrategyMainnet_nUSDT.sol +27 -0
  139. package/contracts/strategies/notional/NotionalStrategyMainnet_nwstETH.sol +27 -0
  140. package/contracts/strategies/venus/VenusFoldStrategy.sol +591 -0
  141. package/contracts/strategies/venus/VenusFoldStrategyMainnet_ARB.sol +32 -0
  142. package/contracts/strategies/venus/VenusFoldStrategyMainnet_ETH_core.sol +32 -0
  143. package/contracts/strategies/venus/VenusFoldStrategyMainnet_ETH_lsd.sol +32 -0
  144. package/contracts/strategies/venus/VenusFoldStrategyMainnet_USDC.sol +32 -0
  145. package/contracts/strategies/venus/VenusFoldStrategyMainnet_USDT.sol +32 -0
  146. package/contracts/strategies/venus/VenusFoldStrategyMainnet_WBTC.sol +32 -0
  147. package/hardhat.config.js +60 -0
  148. package/index.js +42 -0
  149. package/package.json +39 -6
  150. package/scripts/01-deploy-vault-regular-with-upgradable-strategy.js +41 -0
  151. package/scripts/02-deploy-vault-regular.js +35 -0
  152. package/scripts/03-deploy-upgradable-strategy.js +40 -0
  153. package/scripts/04-deploy-new-implementation.js +24 -0
  154. package/scripts/05-deploy-GMXViewer.js +17 -0
  155. package/scripts/06-deploy-GMXVault.js +49 -0
  156. package/scripts/07-deploy-ipor-fuses.js +29 -0
  157. package/scripts/08-deploy-drip.js +20 -0
  158. package/scripts/README.md +55 -0
  159. package/scripts/utils.js +22 -0
  160. package/test/aura/more-gyd.js +140 -0
  161. package/test/aura/susde-gyd.js +140 -0
  162. package/test/aura/wafrax-sfrax.js +140 -0
  163. package/test/aura/wagho-gyd.js +140 -0
  164. package/test/aura/wausdc-gho.js +141 -0
  165. package/test/aura/wausdc-gyd.js +140 -0
  166. package/test/aura/wausdt-gyd.js +140 -0
  167. package/test/aura/wsteth-gyd.js +138 -0
  168. package/test/camelot/arb-usdc.js +125 -0
  169. package/test/camelot/eth-usdc.js +125 -0
  170. package/test/camelot/eth-usdt.js +125 -0
  171. package/test/camelot/gmx-eth.js +125 -0
  172. package/test/camelot/grail-eth.js +125 -0
  173. package/test/camelot/usdc-usdt.js +125 -0
  174. package/test/camelot/wbtc-eth.js +125 -0
  175. package/test/dolomite/dai.js +127 -0
  176. package/test/dolomite/gmx.js +134 -0
  177. package/test/dolomite/usdc.js +127 -0
  178. package/test/dolomite/usdce.js +127 -0
  179. package/test/dolomite/usdt.js +127 -0
  180. package/test/dolomite/wbtc.js +127 -0
  181. package/test/dolomite/weth.js +127 -0
  182. package/test/fluid/eth.js +127 -0
  183. package/test/fluid/usdc.js +134 -0
  184. package/test/fluid/usdt.js +134 -0
  185. package/test/gmx/wbtc.js +184 -0
  186. package/test/notional/neth.js +133 -0
  187. package/test/notional/nusdc.js +133 -0
  188. package/test/notional/nusdt.js +133 -0
  189. package/test/notional/nwsteth.js +133 -0
  190. package/test/test-config.js +28 -0
  191. package/test/utilities/Utils.js +96 -0
  192. package/test/utilities/hh-utils.js +248 -0
  193. package/test/utilities/make-vault.js +16 -0
  194. package/test/venus/arb.js +135 -0
  195. package/test/venus/eth-core.js +133 -0
  196. package/test/venus/eth-lsd.js +133 -0
  197. package/test/venus/usdc.js +133 -0
  198. package/test/venus/usdt.js +133 -0
  199. package/test/venus/wbtc.js +133 -0
@@ -0,0 +1,25 @@
1
+ //SPDX-License-Identifier: Unlicense
2
+ pragma solidity 0.8.26;
3
+
4
+ import "./FluidLendStrategy.sol";
5
+
6
+ contract FluidLendStrategyMainnet_ETH is FluidLendStrategy {
7
+
8
+ constructor() {}
9
+
10
+ function initializeStrategy(
11
+ address _storage,
12
+ address _vault
13
+ ) public initializer {
14
+ address underlying = address(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);
15
+ address fToken = address(0x45Df0656F8aDf017590009d2f1898eeca4F0a205);
16
+ address weth = address(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);
17
+ FluidLendStrategy.initializeBaseStrategy(
18
+ _storage,
19
+ underlying,
20
+ _vault,
21
+ fToken,
22
+ weth
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ //SPDX-License-Identifier: Unlicense
2
+ pragma solidity 0.8.26;
3
+
4
+ import "./FluidLendStrategy.sol";
5
+
6
+ contract FluidLendStrategyMainnet_USDC is FluidLendStrategy {
7
+
8
+ constructor() {}
9
+
10
+ function initializeStrategy(
11
+ address _storage,
12
+ address _vault
13
+ ) public initializer {
14
+ address underlying = address(0xaf88d065e77c8cC2239327C5EDb3A432268e5831);
15
+ address fToken = address(0x1A996cb54bb95462040408C06122D45D6Cdb6096);
16
+ address weth = address(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);
17
+ FluidLendStrategy.initializeBaseStrategy(
18
+ _storage,
19
+ underlying,
20
+ _vault,
21
+ fToken,
22
+ weth
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ //SPDX-License-Identifier: Unlicense
2
+ pragma solidity 0.8.26;
3
+
4
+ import "./FluidLendStrategy.sol";
5
+
6
+ contract FluidLendStrategyMainnet_USDT is FluidLendStrategy {
7
+
8
+ constructor() {}
9
+
10
+ function initializeStrategy(
11
+ address _storage,
12
+ address _vault
13
+ ) public initializer {
14
+ address underlying = address(0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9);
15
+ address fToken = address(0x4A03F37e7d3fC243e3f99341d36f4b829BEe5E03);
16
+ address weth = address(0x82aF49447D8a07e3bd95BD0d56f35241523fBab1);
17
+ FluidLendStrategy.initializeBaseStrategy(
18
+ _storage,
19
+ underlying,
20
+ _vault,
21
+ fToken,
22
+ weth
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,472 @@
1
+ // SPDX-License-Identifier: Unlicense
2
+ pragma solidity 0.8.26;
3
+
4
+ import "@openzeppelin/contracts/utils/math/Math.sol";
5
+ import "@openzeppelin/contracts/utils/math/SafeMath.sol";
6
+ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
+ import "../../base/interface/IVaultGMX.sol";
8
+ import "../../base/upgradability/BaseUpgradeableStrategy.sol";
9
+ import "../../base/interface/gmx/IMarket.sol";
10
+ import "../../base/interface/gmx/IExchangeRouter.sol";
11
+ import "../../base/interface/gmx/ICallbackReceiver.sol";
12
+ import "../../base/interface/gmx/IRoleStore.sol";
13
+ import "../../base/interface/gmx/IGMXViewer.sol";
14
+
15
+ // import "hardhat/console.sol";
16
+
17
+ contract GMXStrategy is BaseUpgradeableStrategy, ICallbackReceiver {
18
+
19
+ using SafeMath for uint256;
20
+ using SafeERC20 for IERC20;
21
+
22
+ address public constant harvestMSIG = address(0xf3D1A027E858976634F81B7c41B09A05A46EdA21);
23
+
24
+ // additional storage slots (on top of BaseUpgradeableStrategy ones) are defined here
25
+ bytes32 internal constant _EXCHANGE_ROUTER_SLOT = 0x37102ee245290fca7b7083aa93785ed04d2277eb4da3e0f305fc09592036c401;
26
+ bytes32 internal constant _MARKET_SLOT = 0x7e894854bb2aa938fcac0eb9954ddb51bd061fc228fb4e5b8e859d96c06bfaa0;
27
+ bytes32 internal constant _DEPOSIT_VAULT_SLOT = 0xb0598bc38333ab6eeb7272aded2335ca73c3fd494cec5ce1f0849cce000c3925;
28
+ bytes32 internal constant _WITHDRAW_VAULT_SLOT = 0x65c967117bb0f9ef871538fd2bba12ca5f9d9ffa82f6b0f94c7f86b0c1575d56;
29
+ bytes32 internal constant _VIEWER_SLOT = 0xe73eae2b084bf3db1612fa5a6e359b575d495e1d242881a0b5eb2c190b98da89;
30
+ bytes32 internal constant _STORED_SUPPLIED_SLOT = 0x280539da846b4989609abdccfea039bd1453e4f710c670b29b9eeaca0730c1a2;
31
+ bytes32 internal constant _PENDING_FEE_SLOT = 0x0af7af9f5ccfa82c3497f40c7c382677637aee27293a6243a22216b51481bd97;
32
+
33
+ struct PendingDeposit {
34
+ uint256 amountIn;
35
+ uint256 expectedOut;
36
+ uint256 underlyingBalance;
37
+ uint256 marketBalance;
38
+ }
39
+
40
+ struct PendingWithdrawal {
41
+ uint256 amountIn;
42
+ uint256 expectedOut;
43
+ uint256 underlyingBalance;
44
+ uint256 marketBalance;
45
+ bool isFee;
46
+ bool withdrawAll;
47
+ }
48
+
49
+ uint256 internal underlyingInPending;
50
+ uint256 internal marketInPending;
51
+ uint256 internal txGasValue = 2e16;
52
+
53
+ mapping (bytes32 => PendingDeposit) public pendingDeposits;
54
+ mapping (bytes32 => PendingWithdrawal) public pendingWithdrawals;
55
+
56
+ modifier onlyGmxKeeperOrGovernance() {
57
+ address roleStore = IMarket(market()).roleStore();
58
+ require(IRoleStore(roleStore).hasRole(msg.sender, keccak256(abi.encode("CONTROLLER"))) || msg.sender == governance());
59
+ _;
60
+ }
61
+
62
+ constructor() BaseUpgradeableStrategy() {
63
+ assert(_EXCHANGE_ROUTER_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.exchangeRouter")) - 1));
64
+ assert(_MARKET_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.market")) - 1));
65
+ assert(_DEPOSIT_VAULT_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.depositVault")) - 1));
66
+ assert(_WITHDRAW_VAULT_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.withdrawVault")) - 1));
67
+ assert(_VIEWER_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.viewer")) - 1));
68
+ assert(_STORED_SUPPLIED_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.storedSupplied")) - 1));
69
+ assert(_PENDING_FEE_SLOT == bytes32(uint256(keccak256("eip1967.strategyStorage.pendingFee")) - 1));
70
+ }
71
+
72
+ function initializeBaseStrategy(
73
+ address _storage,
74
+ address _underlying,
75
+ address _vault,
76
+ address _exchangeRouter,
77
+ address _market,
78
+ address _depositVault,
79
+ address _withdrawVault,
80
+ address _viewer
81
+ )
82
+ public initializer {
83
+ BaseUpgradeableStrategy.initialize(
84
+ _storage,
85
+ _underlying,
86
+ _vault,
87
+ _market,
88
+ _underlying,
89
+ harvestMSIG
90
+ );
91
+
92
+ _setMarket(_market);
93
+ setAddress(_EXCHANGE_ROUTER_SLOT, _exchangeRouter);
94
+ _setDepositVault(_depositVault);
95
+ _setWithdrawVault(_withdrawVault);
96
+ _setViewer(_viewer);
97
+ }
98
+
99
+ function investedUnderlyingBalance() public view returns (uint256) {
100
+ uint256 underlyingBalance = IERC20(underlying()).balanceOf(address(this)).add(underlyingInPending);
101
+ return underlyingBalance.add(storedBalance()).sub(pendingFee());
102
+ }
103
+
104
+ function currentBalance() public view returns (uint256) {
105
+ address _market = market();
106
+ uint256 balanceInMarket = IGMXViewer(viewer()).getWithdrawalAmountOut(
107
+ _market,
108
+ IERC20(_market).balanceOf(address(this)).add(marketInPending),
109
+ false
110
+ );
111
+ return balanceInMarket;
112
+ }
113
+
114
+ function storedBalance() public view returns (uint256) {
115
+ return getUint256(_STORED_SUPPLIED_SLOT);
116
+ }
117
+
118
+ function _updateStoredBalance() internal {
119
+ uint256 balance = currentBalance();
120
+ setUint256(_STORED_SUPPLIED_SLOT, balance);
121
+ }
122
+
123
+ function totalFeeNumerator() public view returns (uint256) {
124
+ return strategistFeeNumerator().add(platformFeeNumerator()).add(profitSharingNumerator());
125
+ }
126
+
127
+ function pendingFee() public view returns (uint256) {
128
+ return getUint256(_PENDING_FEE_SLOT);
129
+ }
130
+
131
+ function _accrueFee() internal {
132
+ uint256 fee;
133
+ if (currentBalance() > storedBalance()) {
134
+ fee = currentBalance().sub(storedBalance()).mul(totalFeeNumerator()).div(feeDenominator());
135
+ }
136
+ setUint256(_PENDING_FEE_SLOT, pendingFee().add(fee));
137
+ }
138
+
139
+ function _handleFee() internal {
140
+ _accrueFee();
141
+ uint256 fee = pendingFee();
142
+ if (fee > 10) {
143
+ if (fee > IERC20(underlying()).balanceOf(address(this))) {
144
+ uint256 toWithdraw = IERC20(market()).balanceOf(address(this))
145
+ .mul(fee)
146
+ .div(investedUnderlyingBalance());
147
+ _withdraw(toWithdraw, true, false);
148
+ } else {
149
+ _notifyProfitInRewardToken(underlying(), fee.mul(feeDenominator()).div(totalFeeNumerator()));
150
+ setUint256(_PENDING_FEE_SLOT, 0);
151
+ }
152
+ }
153
+ _updateStoredBalance();
154
+ }
155
+
156
+ function depositArbCheck() public pure returns (bool) {
157
+ // there's no arb here.
158
+ return true;
159
+ }
160
+
161
+ /**
162
+ * The strategy invests by supplying the underlying as a collateral.
163
+ */
164
+ function _investAllUnderlying() internal onlyNotPausedInvesting returns(bytes32) {
165
+ uint256 underlyingBalance = IERC20(underlying()).balanceOf(address(this));
166
+ bytes32 depositHash;
167
+ if (underlyingBalance > 0) {
168
+ depositHash = _deposit(underlyingBalance);
169
+ }
170
+ return depositHash;
171
+ }
172
+
173
+ function withdrawAllToVault() public restricted returns (bytes32) {
174
+ _handleFee();
175
+ address _market = market();
176
+ bytes32 withdrawal;
177
+ if (IERC20(_market).balanceOf(address(this)) > 0) {
178
+ withdrawal = _withdraw(IERC20(_market).balanceOf(address(this)), false, true);
179
+ }
180
+ _updateStoredBalance();
181
+ return withdrawal;
182
+ }
183
+
184
+ function withdrawToVault(uint256 amountUnderlying) public restricted returns(bytes32) {
185
+ _accrueFee();
186
+ address _underlying = underlying();
187
+ uint256 balance = IERC20(_underlying).balanceOf(address(this));
188
+ if (amountUnderlying <= balance) {
189
+ IERC20(_underlying).safeTransfer(vault(), amountUnderlying);
190
+ return bytes32(0);
191
+ }
192
+ uint256 toWithdraw = IERC20(market()).balanceOf(address(this))
193
+ .mul(amountUnderlying)
194
+ .div(investedUnderlyingBalance().sub(balance));
195
+ // get some of the underlying
196
+ bytes32 withdrawal = _withdraw(toWithdraw, false, false);
197
+ _updateStoredBalance();
198
+ return withdrawal;
199
+ }
200
+
201
+ function doHardWork() public restricted returns(bytes32) {
202
+ _handleFee();
203
+ bytes32 depositHash = _investAllUnderlying();
204
+ _updateStoredBalance();
205
+ return depositHash;
206
+ }
207
+
208
+ /**
209
+ * Salvages a token.
210
+ */
211
+ function salvage(address payable recipient, address token, uint256 amount) public onlyGovernance {
212
+ if (token == address(0)) {
213
+ recipient.transfer(amount);
214
+ } else {
215
+ IERC20(token).safeTransfer(recipient, amount);
216
+ }
217
+ }
218
+
219
+ function _deposit(uint256 amount) internal returns(bytes32) {
220
+ // console.log("Depositing", amount);
221
+ address _market = market();
222
+ address _underlying = underlying();
223
+ address _depositVault = depositVault();
224
+ address _exchangeRouter = exchangeRouter();
225
+ uint256 expectedOut = IGMXViewer(viewer()).getDepositAmountOut(_market, amount, true);
226
+ // console.log("Expected", expectedOut);
227
+ bytes[] memory data = new bytes[](3);
228
+ data[0] = abi.encodeWithSelector(
229
+ IExchangeRouter.sendWnt.selector,
230
+ _depositVault,
231
+ txGasValue
232
+ );
233
+ data[1] = abi.encodeWithSelector(
234
+ IExchangeRouter.sendTokens.selector,
235
+ _underlying,
236
+ _depositVault,
237
+ amount
238
+ );
239
+ data[2] = abi.encodeWithSelector(
240
+ IExchangeRouter.createDeposit.selector,
241
+ IExchangeRouter.CreateDepositParams({
242
+ receiver: address(this), //receiver
243
+ callbackContract: address(this), //callbackContract
244
+ uiFeeReceiver: address(0), //uiFeeReceiver
245
+ market: _market, //market
246
+ initialLongToken: _underlying, //initialLongToken
247
+ initialShortToken: _underlying, //initialShortToken
248
+ longTokenSwapPath: new address[](0), //longTokenSwapPath
249
+ shortTokenSwapPath: new address[](0), //shortTokenSwapPath
250
+ minMarketTokens: expectedOut.mul(999).div(1000), //minMarketTokens
251
+ shouldUnwrapNativeToken: false, //shouldUnwrapNativeToken
252
+ executionFee: txGasValue, //executionFee
253
+ callbackGasLimit: 1e6 //callbackGasLimit
254
+ })
255
+ );
256
+ IERC20(_underlying).safeApprove(address(0x7452c558d45f8afC8c83dAe62C3f8A5BE19c71f6), 0);
257
+ IERC20(_underlying).safeApprove(address(0x7452c558d45f8afC8c83dAe62C3f8A5BE19c71f6), amount);
258
+ bytes[] memory results = IExchangeRouter(_exchangeRouter).multicall{value: txGasValue}(data);
259
+ // console.logBytes(results[2]);
260
+
261
+ underlyingInPending += amount;
262
+ pendingDeposits[bytes32(results[2])] = PendingDeposit(
263
+ amount,
264
+ expectedOut,
265
+ IERC20(_underlying).balanceOf(address(this)),
266
+ IERC20(_market).balanceOf(address(this))
267
+ );
268
+ return bytes32(results[2]);
269
+ }
270
+
271
+ function _withdraw(uint256 amount, bool isFee, bool withdrawAll) internal returns(bytes32) {
272
+ // console.log("Withdrawing", amount);
273
+ address _market = market();
274
+ address _withdrawVault = withdrawVault();
275
+ address _exchangeRouter = exchangeRouter();
276
+ uint256 expectedOut = IGMXViewer(viewer()).getWithdrawalAmountOut(_market, amount, true);
277
+ // console.log("Expected", expectedOut);
278
+ bytes[] memory data = new bytes[](3);
279
+ data[0] = abi.encodeWithSelector(
280
+ IExchangeRouter.sendWnt.selector,
281
+ _withdrawVault,
282
+ txGasValue
283
+ );
284
+ data[1] = abi.encodeWithSelector(
285
+ IExchangeRouter.sendTokens.selector,
286
+ _market,
287
+ _withdrawVault,
288
+ amount
289
+ );
290
+ data[2] = abi.encodeWithSelector(
291
+ IExchangeRouter.createWithdrawal.selector,
292
+ IExchangeRouter.CreateWithdrawalParams({
293
+ receiver: address(this), //receiver
294
+ callbackContract: address(this), //callbackContract
295
+ uiFeeReceiver: address(0), //uiFeeReceiver
296
+ market: _market, //market
297
+ longTokenSwapPath: new address[](0), //longTokenSwapPath
298
+ shortTokenSwapPath: new address[](0), //shortTokenSwapPath
299
+ minLongTokenAmount: expectedOut.mul(4995).div(10000), //minLongTokenAmount
300
+ minShortTokenAmount: expectedOut.mul(4995).div(10000), //minShortTokenAmount
301
+ shouldUnwrapNativeToken: false, //shouldUnwrapNativeToken
302
+ executionFee: txGasValue, //executionFee
303
+ callbackGasLimit: 1e6 //callbackGasLimit
304
+ })
305
+ );
306
+ IERC20(_market).safeApprove(address(0x7452c558d45f8afC8c83dAe62C3f8A5BE19c71f6), 0);
307
+ IERC20(_market).safeApprove(address(0x7452c558d45f8afC8c83dAe62C3f8A5BE19c71f6), amount);
308
+ bytes[] memory results = IExchangeRouter(_exchangeRouter).multicall{value: txGasValue}(data);
309
+ // console.logBytes(results[2]);
310
+
311
+ marketInPending += amount;
312
+ pendingWithdrawals[bytes32(results[2])] = PendingWithdrawal(
313
+ amount,
314
+ expectedOut,
315
+ IERC20(underlying()).balanceOf(address(this)),
316
+ IERC20(_market).balanceOf(address(this)),
317
+ isFee,
318
+ withdrawAll
319
+ );
320
+ return bytes32(results[2]);
321
+ }
322
+
323
+ function afterDepositExecution(
324
+ bytes32 key, DepositProps memory deposit, EventUtils.EventLogData memory
325
+ ) override external onlyGmxKeeperOrGovernance nonReentrant {
326
+ // console.log("Deposit made:");
327
+ // console.logBytes32(key);
328
+ // console.log("MarketBalance: ", IERC20(market()).balanceOf(address(this)));
329
+ // console.log("UnderlyingBalance:", IERC20(underlying()).balanceOf(address(this)));
330
+
331
+ uint256 depAmt = deposit.numbers.initialLongTokenAmount;
332
+ uint256 received = IERC20(market()).balanceOf(address(this)).sub(pendingDeposits[key].marketBalance);
333
+ // uint256 ratio = depAmt.mul(1e24).div(received);
334
+ // console.log("Ratio: ", ratio);
335
+ // console.log("Expected Ratio:", depAmt.mul(1e24).div(pendingDeposits[key].expectedOut));
336
+ // console.log("Diff: ", ratio.mul(1e6).div(depAmt.mul(1e24).div(pendingDeposits[key].expectedOut)));
337
+
338
+ uint256 correctedAmount = depAmt.mul(received).div(pendingDeposits[key].expectedOut);
339
+ // console.log(depAmt, correctedAmount);
340
+
341
+ IVaultGMX.PendingAction memory pending = IVaultGMX(vault()).pendingDeposits(key);
342
+ if (pending.pending) {
343
+ IVaultGMX(vault()).finalizeDeposit(true, key, correctedAmount);
344
+ }
345
+ underlyingInPending -= depAmt;
346
+ pendingDeposits[key] = PendingDeposit(0, 0, 0, 0);
347
+ _updateStoredBalance();
348
+ }
349
+
350
+ function afterDepositCancellation(
351
+ bytes32 key, DepositProps memory deposit, EventUtils.EventLogData memory
352
+ ) override external onlyGmxKeeperOrGovernance nonReentrant {
353
+ IVaultGMX.PendingAction memory pending = IVaultGMX(vault()).pendingDeposits(key);
354
+ if (pending.pending) {
355
+ IERC20(underlying()).safeTransfer(vault(), deposit.numbers.initialLongTokenAmount);
356
+ IVaultGMX(vault()).finalizeDeposit(false, key, 0);
357
+ }
358
+ underlyingInPending -= deposit.numbers.initialLongTokenAmount;
359
+ pendingDeposits[key] = PendingDeposit(0, 0, 0, 0);
360
+ _updateStoredBalance();
361
+ }
362
+
363
+ function afterWithdrawalExecution(
364
+ bytes32 key, WithdrawalProps memory withdrawal, EventUtils.EventLogData memory
365
+ ) override external onlyGmxKeeperOrGovernance nonReentrant {
366
+ // console.log("Withdrawal made:");
367
+ // console.logBytes32(key);
368
+ // console.log("MarketBalance: ", IERC20(market()).balanceOf(address(this)));
369
+ // console.log("UnderlyingBalance:", IERC20(underlying()).balanceOf(address(this)));
370
+
371
+ uint256 withAmt = withdrawal.numbers.marketTokenAmount;
372
+ uint256 received = pendingWithdrawals[key].withdrawAll ?
373
+ IERC20(underlying()).balanceOf(address(this)) :
374
+ IERC20(underlying()).balanceOf(address(this)).sub(pendingWithdrawals[key].underlyingBalance);
375
+ // uint256 ratio = received.mul(1e24).div(withAmt);
376
+ // console.log("Ratio: ", ratio);
377
+ // console.log("Expected Ratio:", pendingWithdrawals[key].expectedOut.mul(1e24).div(withAmt));
378
+ // console.log("Diff: ", ratio.mul(1e6).div(pendingWithdrawals[key].expectedOut.mul(1e24).div(withAmt)));
379
+
380
+ if (pendingWithdrawals[key].isFee) {
381
+ uint256 balanceIncrease;
382
+ uint256 _pendingFee = pendingFee();
383
+ uint256 remainingFee;
384
+ if (received < _pendingFee) {
385
+ balanceIncrease = received.mul(feeDenominator()).div(totalFeeNumerator());
386
+ remainingFee = _pendingFee.sub(received);
387
+ } else {
388
+ balanceIncrease = _pendingFee.mul(feeDenominator()).div(totalFeeNumerator());
389
+ remainingFee = 0;
390
+ }
391
+ _notifyProfitInRewardToken(underlying(), balanceIncrease);
392
+ setUint256(_PENDING_FEE_SLOT, remainingFee);
393
+
394
+ marketInPending -= withAmt;
395
+ pendingWithdrawals[key] = PendingWithdrawal(0, 0, 0, 0, false, false);
396
+ _updateStoredBalance();
397
+ } else {
398
+ IERC20(underlying()).safeTransfer(vault(), received);
399
+
400
+ IVaultGMX.PendingAction memory pending = IVaultGMX(vault()).pendingWithdrawals(key);
401
+ if (pending.pending) {
402
+ IVaultGMX(vault()).finalizeWithdrawal(true, key, received);
403
+ }
404
+ marketInPending -= withAmt;
405
+ pendingWithdrawals[key] = PendingWithdrawal(0, 0, 0, 0, false, false);
406
+ _updateStoredBalance();
407
+ }
408
+ }
409
+
410
+ function afterWithdrawalCancellation(
411
+ bytes32 key, WithdrawalProps memory withdrawal, EventUtils.EventLogData memory
412
+ ) override external onlyGmxKeeperOrGovernance nonReentrant {
413
+ IVaultGMX.PendingAction memory pending = IVaultGMX(vault()).pendingWithdrawals(key);
414
+ if (pending.pending) {
415
+ IVaultGMX(vault()).finalizeWithdrawal(false, key, 0);
416
+ }
417
+ marketInPending -= withdrawal.numbers.marketTokenAmount;
418
+ pendingWithdrawals[key] = PendingWithdrawal(0, 0, 0, 0, false, false);
419
+ _updateStoredBalance();
420
+ }
421
+
422
+
423
+ function _setMarket (address _target) internal {
424
+ setAddress(_MARKET_SLOT, _target);
425
+ }
426
+
427
+ function market() public view returns (address) {
428
+ return getAddress(_MARKET_SLOT);
429
+ }
430
+
431
+ function setExchangeRouter(address _target) external onlyGovernance {
432
+ setAddress(_EXCHANGE_ROUTER_SLOT, _target);
433
+ }
434
+
435
+ function exchangeRouter() public view returns (address) {
436
+ return getAddress(_EXCHANGE_ROUTER_SLOT);
437
+ }
438
+
439
+ function _setDepositVault(address _target) internal {
440
+ setAddress(_DEPOSIT_VAULT_SLOT, _target);
441
+ }
442
+
443
+ function depositVault() internal view returns (address) {
444
+ return getAddress(_DEPOSIT_VAULT_SLOT);
445
+ }
446
+
447
+ function _setWithdrawVault(address _target) internal {
448
+ setAddress(_WITHDRAW_VAULT_SLOT, _target);
449
+ }
450
+
451
+ function withdrawVault() internal view returns (address) {
452
+ return getAddress(_WITHDRAW_VAULT_SLOT);
453
+ }
454
+
455
+ function _setViewer(address _target) internal {
456
+ setAddress(_VIEWER_SLOT, _target);
457
+ }
458
+
459
+ function viewer() internal view returns (address) {
460
+ return getAddress(_VIEWER_SLOT);
461
+ }
462
+
463
+ function setGasValue(uint256 value) external onlyGovernance {
464
+ txGasValue = value;
465
+ }
466
+
467
+ function finalizeUpgrade() external onlyGovernance {
468
+ _finalizeUpgrade();
469
+ }
470
+
471
+ receive() external payable {}
472
+ }
@@ -0,0 +1,25 @@
1
+ //SPDX-License-Identifier: Unlicense
2
+ pragma solidity 0.8.26;
3
+
4
+ import "./GMXStrategy.sol";
5
+
6
+ contract GMXStrategyMainnet_WBTC is GMXStrategy {
7
+
8
+ constructor() {}
9
+
10
+ function initializeStrategy(
11
+ address _storage,
12
+ address _vault
13
+ ) public initializer {
14
+ GMXStrategy.initializeBaseStrategy(
15
+ _storage,
16
+ address(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f), //underlying
17
+ _vault,
18
+ address(0x900173A66dbD345006C51fA35fA3aB760FcD843b), //exchangeRouter
19
+ address(0x7C11F78Ce78768518D743E81Fdfa2F860C6b9A77), //market
20
+ address(0xF89e77e8Dc11691C9e8757e84aaFbCD8A67d7A55), //depositVault
21
+ address(0x0628D46b5D145f183AdB6Ef1f2c97eD1C4701C55), //withdrawVault
22
+ address(0xC5d323907696C513842a1ce4B48125cD8918f0b5) //viewer
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,110 @@
1
+ // SPDX-License-Identifier: Unlicense
2
+ pragma solidity 0.8.26;
3
+
4
+ import "@openzeppelin/contracts/utils/math/SafeMath.sol";
5
+ import "@openzeppelin/contracts/access/Ownable.sol";
6
+ import "../../base/interface/gmx/IReader.sol";
7
+ import "../../base/interface/gmx/IDataStore.sol";
8
+ import "../../base/interface/gmx/IOracle.sol";
9
+ import "../../base/interface/gmx/IPriceFeed.sol";
10
+
11
+ contract GMXViewer is Ownable {
12
+
13
+ using SafeMath for uint256;
14
+
15
+ uint256 public constant FLOAT_PRECISION = 10 ** 30;
16
+ bytes32 public constant PRICE_FEED = keccak256(abi.encode("PRICE_FEED"));
17
+ bytes32 public constant PRICE_FEED_HEARTBEAT_DURATION = keccak256(abi.encode("PRICE_FEED_HEARTBEAT_DURATION"));
18
+
19
+ address public dataStore;
20
+ address public oracle;
21
+ address public reader;
22
+
23
+ constructor (
24
+ address _dataStore,
25
+ address _oracle,
26
+ address _reader
27
+ ) {
28
+ dataStore = _dataStore;
29
+ oracle = _oracle;
30
+ reader = _reader;
31
+ }
32
+
33
+ function setDataStore(address value) external onlyOwner {
34
+ dataStore = value;
35
+ }
36
+
37
+ function setOracle(address value) external onlyOwner {
38
+ oracle = value;
39
+ }
40
+
41
+ function setReader(address value) external onlyOwner {
42
+ reader = value;
43
+ }
44
+
45
+ function _priceFeedKey(address token) internal pure returns (bytes32) {
46
+ return keccak256(abi.encode(PRICE_FEED, token));
47
+ }
48
+
49
+ function _priceFeedHeartbeatDurationKey(address token) internal pure returns (bytes32) {
50
+ return keccak256(abi.encode(PRICE_FEED_HEARTBEAT_DURATION, token));
51
+ }
52
+
53
+ function _getMarketPrices(IReader.MarketProps memory marketProps, bool stalenessCheck)
54
+ internal view returns (IReader.MarketPrices memory marketPrices) {
55
+ marketPrices.indexTokenPrice = _getPriceFeedPrice(marketProps.indexToken, stalenessCheck);
56
+ marketPrices.longTokenPrice = _getPriceFeedPrice(marketProps.longToken, stalenessCheck);
57
+ marketPrices.shortTokenPrice = _getPriceFeedPrice(marketProps.shortToken, stalenessCheck);
58
+ }
59
+
60
+ function _getPriceFeedPrice(address token, bool stalenessCheck)
61
+ internal view returns (IReader.PriceProps memory price) {
62
+ (, int256 latestAnswer, , uint256 updatedAt, ) = IPriceFeed(
63
+ IDataStore(dataStore).getAddress(_priceFeedKey(token))
64
+ ).latestRoundData();
65
+
66
+ if (stalenessCheck) {
67
+ uint256 heartbeatDuration = IDataStore(dataStore).getUint(
68
+ _priceFeedHeartbeatDurationKey(token)
69
+ );
70
+ if (block.timestamp > updatedAt && block.timestamp - updatedAt > heartbeatDuration) {
71
+ revert("Stale PriceFeed");
72
+ }
73
+ }
74
+
75
+ uint256 multipler = IOracle(oracle).getPriceFeedMultiplier(dataStore, token);
76
+ uint256 adjustedPrice = uint256(latestAnswer).mul(multipler).div(FLOAT_PRECISION);
77
+ return IReader.PriceProps({min: adjustedPrice, max: adjustedPrice});
78
+ }
79
+
80
+ function getWithdrawalAmountOut(address market, uint256 amount, bool stalenessCheck) external view returns (uint256) {
81
+ IReader.MarketProps memory marketData = IReader(reader).getMarket(dataStore, market);
82
+ IReader.MarketPrices memory marketPrices = _getMarketPrices(marketData, stalenessCheck);
83
+
84
+ (uint256 longAmount, uint256 shortAmount) = IReader(reader).getWithdrawalAmountOut(
85
+ dataStore,
86
+ marketData,
87
+ marketPrices,
88
+ amount,
89
+ address(0),
90
+ IReader.SwapPricingType.TwoStep
91
+ );
92
+ return longAmount + shortAmount;
93
+ }
94
+
95
+ function getDepositAmountOut(address market, uint256 amount, bool stalenessCheck) external view returns (uint256) {
96
+ IReader.MarketProps memory marketData = IReader(reader).getMarket(dataStore, market);
97
+ IReader.MarketPrices memory marketPrices = _getMarketPrices(marketData, stalenessCheck);
98
+
99
+ return IReader(reader).getDepositAmountOut(
100
+ dataStore,
101
+ marketData,
102
+ marketPrices,
103
+ amount,
104
+ 0,
105
+ address(0),
106
+ IReader.SwapPricingType.TwoStep,
107
+ false
108
+ );
109
+ }
110
+ }