@harvest-finance/harvest-strategy-arbitrum 0.0.1-security → 1.0.0
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.
- package/README.md +127 -5
- package/contracts/base/Controller.sol +358 -0
- package/contracts/base/Drip.sol +86 -0
- package/contracts/base/PotPool.sol +367 -0
- package/contracts/base/ProfitSharingReceiver.sol +38 -0
- package/contracts/base/Reader.sol +54 -0
- package/contracts/base/RewardForwarder.sol +109 -0
- package/contracts/base/VaultProxy.sol +34 -0
- package/contracts/base/VaultStorage.sol +205 -0
- package/contracts/base/VaultV1.sol +371 -0
- package/contracts/base/VaultV1GMX.sol +465 -0
- package/contracts/base/VaultV2.sol +111 -0
- package/contracts/base/VaultV2GMX.sol +111 -0
- package/contracts/base/factory/MegaFactory.sol +120 -0
- package/contracts/base/factory/interface/IPoolFactory.sol +6 -0
- package/contracts/base/factory/interface/IStrategyFactory.sol +6 -0
- package/contracts/base/factory/interface/IVaultFactory.sol +7 -0
- package/contracts/base/factory/pool/PotPoolFactory.sol +41 -0
- package/contracts/base/factory/strategy/UpgradableStrategyFactory.sol +19 -0
- package/contracts/base/factory/vault/RegularVaultFactory.sol +34 -0
- package/contracts/base/incentives/GlobalIncentivesExecutor.sol +85 -0
- package/contracts/base/incentives/GlobalIncentivesHelper.sol +174 -0
- package/contracts/base/incentives/NotifyHelperGeneric.sol +61 -0
- package/contracts/base/incentives/NotifyHelperStateful.sol +290 -0
- package/contracts/base/incentives/ViewerNotifyHelperStateful.sol +25 -0
- package/contracts/base/inheritance/Controllable.sol +25 -0
- package/contracts/base/inheritance/ControllableInit.sol +30 -0
- package/contracts/base/inheritance/Governable.sol +28 -0
- package/contracts/base/inheritance/GovernableInit.sol +50 -0
- package/contracts/base/inheritance/IUpgradeSource.sol +7 -0
- package/contracts/base/inheritance/OwnableWhitelist.sol +17 -0
- package/contracts/base/inheritance/Storage.sol +35 -0
- package/contracts/base/interface/IBalDex.sol +7 -0
- package/contracts/base/interface/IController.sol +132 -0
- package/contracts/base/interface/IDex.sol +9 -0
- package/contracts/base/interface/IERC4626.sol +261 -0
- package/contracts/base/interface/IGMXStrategy.sol +37 -0
- package/contracts/base/interface/IGlobalIncentivesHelper.sol +6 -0
- package/contracts/base/interface/IPotPool.sol +70 -0
- package/contracts/base/interface/IProfitSharingReceiver.sol +9 -0
- package/contracts/base/interface/IRewardForwarder.sol +57 -0
- package/contracts/base/interface/IStrategy.sol +37 -0
- package/contracts/base/interface/IUniversalLiquidator.sol +21 -0
- package/contracts/base/interface/IUniversalLiquidatorRegistry.sol +20 -0
- package/contracts/base/interface/IUpgradeSource.sol +9 -0
- package/contracts/base/interface/IVault.sol +58 -0
- package/contracts/base/interface/IVaultGMX.sol +71 -0
- package/contracts/base/interface/aura/IAuraBaseRewardPool.sol +25 -0
- package/contracts/base/interface/aura/IAuraBooster.sol +17 -0
- package/contracts/base/interface/aura/IAuraDepositor.sol +7 -0
- package/contracts/base/interface/balancer/Gauge.sol +22 -0
- package/contracts/base/interface/balancer/IBVault.sol +580 -0
- package/contracts/base/interface/balancer/IBalancerMinter.sol +114 -0
- package/contracts/base/interface/balancer/IGyroPool.sol +7 -0
- package/contracts/base/interface/balancer/linearPool/ILinearPool.sol +184 -0
- package/contracts/base/interface/balancer/linearPool/ILinearPoolFactory.sol +16 -0
- package/contracts/base/interface/balancer/linearPool/ILinearPoolRebalancer.sol +8 -0
- package/contracts/base/interface/balancer/linearPool/IPoolSwapStructs.sol +56 -0
- package/contracts/base/interface/compound/CTokenInterface.sol +29 -0
- package/contracts/base/interface/compound/IComptroller.sol +9 -0
- package/contracts/base/interface/dolomite/IDepositWithdraw.sol +13 -0
- package/contracts/base/interface/dolomite/IDolomiteMargin.sol +15 -0
- package/contracts/base/interface/dolomite/IRewardsDistributor.sol +11 -0
- package/contracts/base/interface/gamma/IClearing.sol +7 -0
- package/contracts/base/interface/gamma/IHypervisor.sol +9 -0
- package/contracts/base/interface/gamma/IUniProxy.sol +14 -0
- package/contracts/base/interface/gmx/EventUtils.sol +253 -0
- package/contracts/base/interface/gmx/ICallbackReceiver.sol +119 -0
- package/contracts/base/interface/gmx/IDataStore.sol +7 -0
- package/contracts/base/interface/gmx/IExchangeRouter.sol +38 -0
- package/contracts/base/interface/gmx/IGMXViewer.sol +7 -0
- package/contracts/base/interface/gmx/IHandler.sol +12 -0
- package/contracts/base/interface/gmx/IMarket.sol +7 -0
- package/contracts/base/interface/gmx/IOracle.sol +6 -0
- package/contracts/base/interface/gmx/IPriceFeed.sol +12 -0
- package/contracts/base/interface/gmx/IReader.sol +49 -0
- package/contracts/base/interface/gmx/IRoleStore.sol +6 -0
- package/contracts/base/interface/ipor/Errors.sol +20 -0
- package/contracts/base/interface/ipor/FuseStorageLib.sol +71 -0
- package/contracts/base/interface/ipor/FusesLib.sol +149 -0
- package/contracts/base/interface/ipor/IFuseCommon.sol +9 -0
- package/contracts/base/interface/ipor/IFuseInstantWithdraw.sol +14 -0
- package/contracts/base/interface/ipor/IMarketBalanceFuse.sol +10 -0
- package/contracts/base/interface/ipor/IPriceOracleMiddleware.sol +42 -0
- package/contracts/base/interface/ipor/IporMath.sol +110 -0
- package/contracts/base/interface/ipor/PlasmaVaultConfigLib.sol +106 -0
- package/contracts/base/interface/ipor/PlasmaVaultLib.sol +293 -0
- package/contracts/base/interface/ipor/PlasmaVaultStorageLib.sol +352 -0
- package/contracts/base/interface/merkl/IDistributor.sol +6 -0
- package/contracts/base/interface/notional/INProxy.sol +44 -0
- package/contracts/base/interface/notional/IPrimeToken.sol +6 -0
- package/contracts/base/interface/venus/IRewardsDistributor.sol +6 -0
- package/contracts/base/interface/weth/IWETH.sol +39 -0
- package/contracts/base/ipor/Erc4626BalanceFuse.sol +54 -0
- package/contracts/base/ipor/Erc4626SupplyFuse.sol +134 -0
- package/contracts/base/noop/NoopStrategyUpgradeable.sol +90 -0
- package/contracts/base/upgradability/BaseUpgradeabilityProxy.sol +60 -0
- package/contracts/base/upgradability/BaseUpgradeableStrategy.sol +144 -0
- package/contracts/base/upgradability/BaseUpgradeableStrategyStorage.sol +284 -0
- package/contracts/base/upgradability/IUpgradable.sol +7 -0
- package/contracts/base/upgradability/ReentrancyGuardUpgradeable.sol +51 -0
- package/contracts/base/upgradability/StrategyProxy.sol +34 -0
- package/contracts/strategies/aura/AuraStrategy.sol +403 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_MORE_GYD.sol +32 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_sUSDe_GYD.sol +31 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_waFRAX_sFRAX.sol +31 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_waGHO_GYD.sol +31 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_waUSDC_GHO.sol +32 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_waUSDC_GYD.sol +31 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_waUSDT_GYD.sol +31 -0
- package/contracts/strategies/aura/AuraStrategyMainnet_wstETH_GYD.sol +31 -0
- package/contracts/strategies/camelot/CamelotV3Strategy.sol +304 -0
- package/contracts/strategies/camelot/CamelotV3StrategyMainnet_ARB_USDC.sol +28 -0
- package/contracts/strategies/camelot/CamelotV3StrategyMainnet_ETH_USDC.sol +28 -0
- package/contracts/strategies/camelot/CamelotV3StrategyMainnet_ETH_USDT.sol +28 -0
- package/contracts/strategies/camelot/CamelotV3StrategyMainnet_GMX_ETH.sol +28 -0
- package/contracts/strategies/camelot/CamelotV3StrategyMainnet_GRAIL_ETH.sol +28 -0
- package/contracts/strategies/camelot/CamelotV3StrategyMainnet_USDC_USDT.sol +28 -0
- package/contracts/strategies/camelot/CamelotV3StrategyMainnet_WBTC_ETH.sol +28 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategy.sol +273 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_DAI.sol +26 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_GMX.sol +26 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_USDC.sol +26 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_USDCe.sol +26 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_USDT.sol +26 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_WBTC.sol +26 -0
- package/contracts/strategies/dolomite/DolomiteLendStrategyMainnet_WETH.sol +26 -0
- package/contracts/strategies/fluid/FluidLendStrategy.sol +241 -0
- package/contracts/strategies/fluid/FluidLendStrategyMainnet_ETH.sol +25 -0
- package/contracts/strategies/fluid/FluidLendStrategyMainnet_USDC.sol +25 -0
- package/contracts/strategies/fluid/FluidLendStrategyMainnet_USDT.sol +25 -0
- package/contracts/strategies/gmx/GMXStrategy.sol +472 -0
- package/contracts/strategies/gmx/GMXStrategyMainnet_WBTC.sol +25 -0
- package/contracts/strategies/gmx/GMXViewer.sol +110 -0
- package/contracts/strategies/notional/NotionalStrategy.sol +223 -0
- package/contracts/strategies/notional/NotionalStrategyMainnet_nETH.sol +27 -0
- package/contracts/strategies/notional/NotionalStrategyMainnet_nUSDC.sol +27 -0
- package/contracts/strategies/notional/NotionalStrategyMainnet_nUSDT.sol +27 -0
- package/contracts/strategies/notional/NotionalStrategyMainnet_nwstETH.sol +27 -0
- package/contracts/strategies/venus/VenusFoldStrategy.sol +591 -0
- package/contracts/strategies/venus/VenusFoldStrategyMainnet_ARB.sol +32 -0
- package/contracts/strategies/venus/VenusFoldStrategyMainnet_ETH_core.sol +32 -0
- package/contracts/strategies/venus/VenusFoldStrategyMainnet_ETH_lsd.sol +32 -0
- package/contracts/strategies/venus/VenusFoldStrategyMainnet_USDC.sol +32 -0
- package/contracts/strategies/venus/VenusFoldStrategyMainnet_USDT.sol +32 -0
- package/contracts/strategies/venus/VenusFoldStrategyMainnet_WBTC.sol +32 -0
- package/hardhat.config.js +60 -0
- package/index.js +42 -0
- package/package.json +38 -6
- package/scripts/01-deploy-vault-regular-with-upgradable-strategy.js +41 -0
- package/scripts/02-deploy-vault-regular.js +35 -0
- package/scripts/03-deploy-upgradable-strategy.js +40 -0
- package/scripts/04-deploy-new-implementation.js +24 -0
- package/scripts/05-deploy-GMXViewer.js +17 -0
- package/scripts/06-deploy-GMXVault.js +49 -0
- package/scripts/07-deploy-ipor-fuses.js +29 -0
- package/scripts/08-deploy-drip.js +20 -0
- package/scripts/README.md +55 -0
- package/scripts/utils.js +22 -0
- package/test/aura/more-gyd.js +140 -0
- package/test/aura/susde-gyd.js +140 -0
- package/test/aura/wafrax-sfrax.js +140 -0
- package/test/aura/wagho-gyd.js +140 -0
- package/test/aura/wausdc-gho.js +141 -0
- package/test/aura/wausdc-gyd.js +140 -0
- package/test/aura/wausdt-gyd.js +140 -0
- package/test/aura/wsteth-gyd.js +138 -0
- package/test/camelot/arb-usdc.js +125 -0
- package/test/camelot/eth-usdc.js +125 -0
- package/test/camelot/eth-usdt.js +125 -0
- package/test/camelot/gmx-eth.js +125 -0
- package/test/camelot/grail-eth.js +125 -0
- package/test/camelot/usdc-usdt.js +125 -0
- package/test/camelot/wbtc-eth.js +125 -0
- package/test/dolomite/dai.js +127 -0
- package/test/dolomite/gmx.js +134 -0
- package/test/dolomite/usdc.js +127 -0
- package/test/dolomite/usdce.js +127 -0
- package/test/dolomite/usdt.js +127 -0
- package/test/dolomite/wbtc.js +127 -0
- package/test/dolomite/weth.js +127 -0
- package/test/fluid/eth.js +127 -0
- package/test/fluid/usdc.js +134 -0
- package/test/fluid/usdt.js +134 -0
- package/test/gmx/wbtc.js +184 -0
- package/test/notional/neth.js +133 -0
- package/test/notional/nusdc.js +133 -0
- package/test/notional/nusdt.js +133 -0
- package/test/notional/nwsteth.js +133 -0
- package/test/test-config.js +28 -0
- package/test/utilities/Utils.js +96 -0
- package/test/utilities/hh-utils.js +248 -0
- package/test/utilities/make-vault.js +16 -0
- package/test/venus/arb.js +135 -0
- package/test/venus/eth-core.js +133 -0
- package/test/venus/eth-lsd.js +133 -0
- package/test/venus/usdc.js +133 -0
- package/test/venus/usdt.js +133 -0
- package/test/venus/wbtc.js +133 -0
package/README.md
CHANGED
@@ -1,5 +1,127 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
This
|
4
|
-
|
5
|
-
|
1
|
+
# Arbitrum Chain: Harvest Strategy Development
|
2
|
+
|
3
|
+
This [Hardhat](https://hardhat.org/) environment is configured to use Mainnet fork by default and provides templates and utilities for strategy development and testing.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
1. Run `npm install` to install all the dependencies.
|
8
|
+
2. Sign up on [Alchemy](https://dashboard.alchemyapi.io/signup/). We recommend using Alchemy over Infura to allow for a reproducible
|
9
|
+
Mainnet fork testing environment as well as efficiency due to caching.
|
10
|
+
3. Create a file `dev-keys.json`:
|
11
|
+
```
|
12
|
+
{
|
13
|
+
"alchemyKey": "<your-alchemy-key>"
|
14
|
+
}
|
15
|
+
```
|
16
|
+
|
17
|
+
## Run
|
18
|
+
|
19
|
+
All tests are located under the `test` folder.
|
20
|
+
|
21
|
+
1. Run `npx hardhat test [test file location]`: `npx hardhat test ./test/balancer/wsteth-usdc.js` (if for some reason the NodeJS heap runs out of memory, make sure to explicitly increase its size via `export NODE_OPTIONS=--max_old_space_size=4096`). This will produce the following output:
|
22
|
+
```
|
23
|
+
Arbitrum Mainnet Balancer wstETH-USDC
|
24
|
+
Impersonating...
|
25
|
+
0x6a74649aCFD7822ae8Fb78463a9f2192752E5Aa2
|
26
|
+
0x0345Bc8EDdbba03E11e366cFb4E2b232b8f1b739
|
27
|
+
Fetching Underlying at: 0x178E029173417b1F9C8bC16DCeC6f697bC323746
|
28
|
+
New Vault Deployed: 0xa4B5Ad0ca0E05ea73E59046CBCb6b3641318c9d3
|
29
|
+
Strategy Deployed: 0xf47FCd7914b24676C78222Af1431f4826B368F30
|
30
|
+
Happy path
|
31
|
+
loop 0
|
32
|
+
old shareprice: 1000000000000000000
|
33
|
+
new shareprice: 1000000000000000000
|
34
|
+
growth: 1
|
35
|
+
instant APR: 0 %
|
36
|
+
instant APY: 0 %
|
37
|
+
loop 1
|
38
|
+
old shareprice: 1000000000000000000
|
39
|
+
new shareprice: 1000068403677420556
|
40
|
+
growth: 1.0000684036774206
|
41
|
+
instant APR: 26.04926042303911 %
|
42
|
+
instant APY: 29.74485660521895 %
|
43
|
+
loop 2
|
44
|
+
old shareprice: 1000068403677420556
|
45
|
+
new shareprice: 1000131319321304444
|
46
|
+
growth: 1.0000629113405168
|
47
|
+
instant APR: 23.95768699115301 %
|
48
|
+
instant APY: 27.061149684423057 %
|
49
|
+
loop 3
|
50
|
+
old shareprice: 1000131319321304444
|
51
|
+
new shareprice: 1000193897701257046
|
52
|
+
growth: 1.0000625701632813
|
53
|
+
instant APR: 23.827761013559037 %
|
54
|
+
instant APY: 26.896279294823277 %
|
55
|
+
loop 4
|
56
|
+
old shareprice: 1000193897701257046
|
57
|
+
new shareprice: 1000256321542735812
|
58
|
+
growth: 1.000062411739986
|
59
|
+
instant APR: 23.767430782293474 %
|
60
|
+
instant APY: 26.819795416465155 %
|
61
|
+
loop 5
|
62
|
+
old shareprice: 1000256321542735812
|
63
|
+
new shareprice: 1000318634555811359
|
64
|
+
growth: 1.0000622970450008
|
65
|
+
instant APR: 23.723753020397634 %
|
66
|
+
instant APY: 26.76445146036619 %
|
67
|
+
loop 6
|
68
|
+
old shareprice: 1000318634555811359
|
69
|
+
new shareprice: 1000380840931449184
|
70
|
+
growth: 1.0000621865608508
|
71
|
+
instant APR: 23.681678814655715 %
|
72
|
+
instant APY: 26.711162141078937 %
|
73
|
+
loop 7
|
74
|
+
old shareprice: 1000380840931449184
|
75
|
+
new shareprice: 1000442949039498353
|
76
|
+
growth: 1.0000620844637442
|
77
|
+
instant APR: 23.642798534864234 %
|
78
|
+
instant APY: 26.661937967659497 %
|
79
|
+
loop 8
|
80
|
+
old shareprice: 1000442949039498353
|
81
|
+
new shareprice: 1000479005035224559
|
82
|
+
growth: 1.0000360400318287
|
83
|
+
instant APR: 13.724644787553073 %
|
84
|
+
instant APY: 14.708122498585574 %
|
85
|
+
loop 9
|
86
|
+
old shareprice: 1000479005035224559
|
87
|
+
new shareprice: 1000479005035224559
|
88
|
+
growth: 1
|
89
|
+
instant APR: 0 %
|
90
|
+
instant APY: 0 %
|
91
|
+
earned!
|
92
|
+
APR: 18.24131008308575 %
|
93
|
+
APY: 20.005517445690145 %
|
94
|
+
✔ Farmer should earn money (32947ms)
|
95
|
+
|
96
|
+
|
97
|
+
1 passing (37s)
|
98
|
+
```
|
99
|
+
|
100
|
+
## Develop
|
101
|
+
|
102
|
+
Under `contracts/strategies`, there are plenty of examples to choose from in the repository already, therefore, creating a strategy is no longer a complicated task. Copy-pasting existing strategies with minor modifications is acceptable.
|
103
|
+
|
104
|
+
Under `contracts/base`, there are existing base interfaces and contracts that can speed up development.
|
105
|
+
|
106
|
+
## Contribute
|
107
|
+
|
108
|
+
When ready, open a pull request with the following information:
|
109
|
+
1. Instructions on how to run the test and at which block number
|
110
|
+
2. A **mainnet fork test output** (like the one above in the README) clearly showing the increases of share price
|
111
|
+
3. Info about the protocol, including:
|
112
|
+
- Live farm page(s)
|
113
|
+
- GitHub link(s)
|
114
|
+
- Etherscan link(s)
|
115
|
+
- Start/end dates for rewards
|
116
|
+
- Any limitations (e.g., maximum pool size)
|
117
|
+
- Current pool sizes used for liquidation (to make sure they are not too shallow)
|
118
|
+
|
119
|
+
The first few items can be omitted for well-known protocols (such as `curve.fi`).
|
120
|
+
|
121
|
+
5. A description of **potential value** for Harvest: why should your strategy be live? High APYs, decent pool sizes, longevity of rewards, well-secured protocols, high-potential collaborations, etc.
|
122
|
+
|
123
|
+
A more extensive checklist for assessing protocols and farming opportunities can be found [here](https://www.notion.so/harvestfinance/Farm-ops-check-list-7cd2e0d9da364252ac465cb8a176f0e0)
|
124
|
+
|
125
|
+
## Deployment
|
126
|
+
|
127
|
+
If your pull request is merged and given a green light for deployment, the Harvest team will take care of on-chain deployment.
|
@@ -0,0 +1,358 @@
|
|
1
|
+
// SPDX-License-Identifier: Unlicense
|
2
|
+
pragma solidity 0.8.26;
|
3
|
+
|
4
|
+
import "@openzeppelin/contracts/utils/Address.sol";
|
5
|
+
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
6
|
+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
7
|
+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
8
|
+
|
9
|
+
import "./inheritance/Governable.sol";
|
10
|
+
|
11
|
+
import "./interface/IController.sol";
|
12
|
+
import "./interface/IStrategy.sol";
|
13
|
+
import "./interface/IVault.sol";
|
14
|
+
|
15
|
+
import "./RewardForwarder.sol";
|
16
|
+
|
17
|
+
|
18
|
+
contract Controller is Governable {
|
19
|
+
using SafeERC20 for IERC20;
|
20
|
+
using Address for address;
|
21
|
+
using SafeMath for uint256;
|
22
|
+
|
23
|
+
// ========================= Fields =========================
|
24
|
+
|
25
|
+
// external parties
|
26
|
+
address public targetToken;
|
27
|
+
address public protocolFeeReceiver;
|
28
|
+
address public profitSharingReceiver;
|
29
|
+
address public rewardForwarder;
|
30
|
+
address public universalLiquidator;
|
31
|
+
address public dolomiteYieldFarmingRouter;
|
32
|
+
|
33
|
+
uint256 public nextImplementationDelay;
|
34
|
+
|
35
|
+
/// 15% of fees captured go to iFARM stakers
|
36
|
+
uint256 public profitSharingNumerator = 700;
|
37
|
+
uint256 public nextProfitSharingNumerator = 0;
|
38
|
+
uint256 public nextProfitSharingNumeratorTimestamp = 0;
|
39
|
+
|
40
|
+
/// 5% of fees captured go to strategists
|
41
|
+
uint256 public strategistFeeNumerator = 0;
|
42
|
+
uint256 public nextStrategistFeeNumerator = 0;
|
43
|
+
uint256 public nextStrategistFeeNumeratorTimestamp = 0;
|
44
|
+
|
45
|
+
/// 5% of fees captured go to the devs of the platform
|
46
|
+
uint256 public platformFeeNumerator = 300;
|
47
|
+
uint256 public nextPlatformFeeNumerator = 0;
|
48
|
+
uint256 public nextPlatformFeeNumeratorTimestamp = 0;
|
49
|
+
|
50
|
+
/// used for queuing a new delay
|
51
|
+
uint256 public tempNextImplementationDelay = 0;
|
52
|
+
uint256 public tempNextImplementationDelayTimestamp = 0;
|
53
|
+
|
54
|
+
uint256 public constant MAX_TOTAL_FEE = 3000;
|
55
|
+
uint256 public constant FEE_DENOMINATOR = 10000;
|
56
|
+
|
57
|
+
/// @notice This mapping allows certain contracts to stake on a user's behalf
|
58
|
+
mapping (address => bool) public addressWhitelist;
|
59
|
+
mapping (bytes32 => bool) public codeWhitelist;
|
60
|
+
|
61
|
+
// All eligible hardWorkers that we have
|
62
|
+
mapping (address => bool) public hardWorkers;
|
63
|
+
|
64
|
+
// ========================= Events =========================
|
65
|
+
|
66
|
+
event QueueProfitSharingChange(uint profitSharingNumerator, uint validAtTimestamp);
|
67
|
+
event ConfirmProfitSharingChange(uint profitSharingNumerator);
|
68
|
+
|
69
|
+
event QueueStrategistFeeChange(uint strategistFeeNumerator, uint validAtTimestamp);
|
70
|
+
event ConfirmStrategistFeeChange(uint strategistFeeNumerator);
|
71
|
+
|
72
|
+
event QueuePlatformFeeChange(uint platformFeeNumerator, uint validAtTimestamp);
|
73
|
+
event ConfirmPlatformFeeChange(uint platformFeeNumerator);
|
74
|
+
|
75
|
+
event QueueNextImplementationDelay(uint implementationDelay, uint validAtTimestamp);
|
76
|
+
event ConfirmNextImplementationDelay(uint implementationDelay);
|
77
|
+
|
78
|
+
event AddedAddressToWhitelist(address indexed _address);
|
79
|
+
event RemovedAddressFromWhitelist(address indexed _address);
|
80
|
+
|
81
|
+
event AddedCodeToWhitelist(address indexed _address);
|
82
|
+
event RemovedCodeFromWhitelist(address indexed _address);
|
83
|
+
|
84
|
+
event SharePriceChangeLog(
|
85
|
+
address indexed vault,
|
86
|
+
address indexed strategy,
|
87
|
+
uint256 oldSharePrice,
|
88
|
+
uint256 newSharePrice,
|
89
|
+
uint256 timestamp
|
90
|
+
);
|
91
|
+
|
92
|
+
// ========================= Modifiers =========================
|
93
|
+
|
94
|
+
modifier onlyHardWorkerOrGovernance() {
|
95
|
+
require(hardWorkers[msg.sender] || (msg.sender == governance()),
|
96
|
+
"only hard worker can call this");
|
97
|
+
_;
|
98
|
+
}
|
99
|
+
|
100
|
+
constructor(
|
101
|
+
address _storage,
|
102
|
+
address _targetToken,
|
103
|
+
address _protocolFeeReceiver,
|
104
|
+
address _profitSharingReceiver,
|
105
|
+
address _rewardForwarder,
|
106
|
+
address _universalLiquidator,
|
107
|
+
uint _nextImplementationDelay
|
108
|
+
)
|
109
|
+
Governable(_storage)
|
110
|
+
{
|
111
|
+
require(_targetToken != address(0), "_targetToken should not be empty");
|
112
|
+
require(_protocolFeeReceiver != address(0), "_protocolFeeReceiver should not be empty");
|
113
|
+
require(_profitSharingReceiver != address(0), "_profitSharingReceiver should not be empty");
|
114
|
+
require(_rewardForwarder != address(0), "_rewardForwarder should not be empty");
|
115
|
+
require(_nextImplementationDelay > 0, "_nextImplementationDelay should be gt 0");
|
116
|
+
|
117
|
+
targetToken = _targetToken;
|
118
|
+
protocolFeeReceiver = _protocolFeeReceiver;
|
119
|
+
profitSharingReceiver = _profitSharingReceiver;
|
120
|
+
rewardForwarder = _rewardForwarder;
|
121
|
+
universalLiquidator = _universalLiquidator;
|
122
|
+
nextImplementationDelay = _nextImplementationDelay;
|
123
|
+
}
|
124
|
+
|
125
|
+
// [Grey list]
|
126
|
+
// An EOA can safely interact with the system no matter what.
|
127
|
+
// If you're using Metamask, you're using an EOA.
|
128
|
+
// Only smart contracts may be affected by this grey list.
|
129
|
+
//
|
130
|
+
// This contract will not be able to ban any EOA from the system
|
131
|
+
// even if an EOA is being added to the greyList, he/she will still be able
|
132
|
+
// to interact with the whole system as if nothing happened.
|
133
|
+
// Only smart contracts will be affected by being added to the greyList.
|
134
|
+
function greyList(address _addr) public view returns (bool) {
|
135
|
+
return !addressWhitelist[_addr] && !codeWhitelist[getContractHash(_addr)];
|
136
|
+
}
|
137
|
+
|
138
|
+
// Only smart contracts will be affected by the whitelist.
|
139
|
+
function addToWhitelist(address _target) public onlyGovernance {
|
140
|
+
addressWhitelist[_target] = true;
|
141
|
+
emit AddedAddressToWhitelist(_target);
|
142
|
+
}
|
143
|
+
|
144
|
+
function addMultipleToWhitelist(address[] memory _targets) public onlyGovernance {
|
145
|
+
for (uint256 i = 0; i < _targets.length; i++) {
|
146
|
+
addressWhitelist[_targets[i]] = true;
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
function removeFromWhitelist(address _target) public onlyGovernance {
|
151
|
+
addressWhitelist[_target] = false;
|
152
|
+
emit RemovedAddressFromWhitelist(_target);
|
153
|
+
}
|
154
|
+
|
155
|
+
function removeMultipleFromWhitelist(address[] memory _targets) public onlyGovernance {
|
156
|
+
for (uint256 i = 0; i < _targets.length; i++) {
|
157
|
+
addressWhitelist[_targets[i]] = false;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
function getContractHash(address a) public view returns (bytes32 hash) {
|
162
|
+
assembly {
|
163
|
+
hash := extcodehash(a)
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
function addCodeToWhitelist(address _target) public onlyGovernance {
|
168
|
+
codeWhitelist[getContractHash(_target)] = true;
|
169
|
+
emit AddedCodeToWhitelist(_target);
|
170
|
+
}
|
171
|
+
|
172
|
+
function removeCodeFromWhitelist(address _target) public onlyGovernance {
|
173
|
+
codeWhitelist[getContractHash(_target)] = false;
|
174
|
+
emit RemovedCodeFromWhitelist(_target);
|
175
|
+
}
|
176
|
+
|
177
|
+
function setRewardForwarder(address _rewardForwarder) public onlyGovernance {
|
178
|
+
require(_rewardForwarder != address(0), "new reward forwarder should not be empty");
|
179
|
+
rewardForwarder = _rewardForwarder;
|
180
|
+
}
|
181
|
+
|
182
|
+
function setTargetToken(address _targetToken) public onlyGovernance {
|
183
|
+
require(_targetToken != address(0), "new target token should not be empty");
|
184
|
+
targetToken = _targetToken;
|
185
|
+
}
|
186
|
+
|
187
|
+
function setProfitSharingReceiver(address _profitSharingReceiver) public onlyGovernance {
|
188
|
+
require(_profitSharingReceiver != address(0), "new profit sharing receiver should not be empty");
|
189
|
+
profitSharingReceiver = _profitSharingReceiver;
|
190
|
+
}
|
191
|
+
|
192
|
+
function setProtocolFeeReceiver(address _protocolFeeReceiver) public onlyGovernance {
|
193
|
+
require(_protocolFeeReceiver != address(0), "new protocol fee receiver should not be empty");
|
194
|
+
protocolFeeReceiver = _protocolFeeReceiver;
|
195
|
+
}
|
196
|
+
|
197
|
+
function setUniversalLiquidator(address _universalLiquidator) public onlyGovernance {
|
198
|
+
require(_universalLiquidator != address(0), "new universal liquidator should not be empty");
|
199
|
+
universalLiquidator = _universalLiquidator;
|
200
|
+
}
|
201
|
+
|
202
|
+
function setDolomiteYieldFarmingRouter(address _dolomiteYieldFarmingRouter) public onlyGovernance {
|
203
|
+
require(_dolomiteYieldFarmingRouter != address(0), "new reward forwarder should not be empty");
|
204
|
+
dolomiteYieldFarmingRouter = _dolomiteYieldFarmingRouter;
|
205
|
+
}
|
206
|
+
|
207
|
+
function getPricePerFullShare(address _vault) public view returns (uint256) {
|
208
|
+
return IVault(_vault).getPricePerFullShare();
|
209
|
+
}
|
210
|
+
|
211
|
+
function doHardWork(address _vault) external onlyHardWorkerOrGovernance {
|
212
|
+
uint256 oldSharePrice = IVault(_vault).getPricePerFullShare();
|
213
|
+
IVault(_vault).doHardWork();
|
214
|
+
emit SharePriceChangeLog(
|
215
|
+
_vault,
|
216
|
+
IVault(_vault).strategy(),
|
217
|
+
oldSharePrice,
|
218
|
+
IVault(_vault).getPricePerFullShare(),
|
219
|
+
block.timestamp
|
220
|
+
);
|
221
|
+
}
|
222
|
+
|
223
|
+
function addHardWorker(address _worker) public onlyGovernance {
|
224
|
+
require(_worker != address(0), "_worker must be defined");
|
225
|
+
hardWorkers[_worker] = true;
|
226
|
+
}
|
227
|
+
|
228
|
+
function removeHardWorker(address _worker) public onlyGovernance {
|
229
|
+
require(_worker != address(0), "_worker must be defined");
|
230
|
+
hardWorkers[_worker] = false;
|
231
|
+
}
|
232
|
+
|
233
|
+
// transfers token in the controller contract to the governance
|
234
|
+
function salvage(address _token, uint256 _amount) external onlyGovernance {
|
235
|
+
IERC20(_token).safeTransfer(governance(), _amount);
|
236
|
+
}
|
237
|
+
|
238
|
+
function salvageStrategy(address _strategy, address _token, uint256 _amount) external onlyGovernance {
|
239
|
+
// the strategy is responsible for maintaining the list of
|
240
|
+
// salvageable tokens, to make sure that governance cannot come
|
241
|
+
// in and take away the coins
|
242
|
+
IStrategy(_strategy).salvageToken(governance(), _token, _amount);
|
243
|
+
}
|
244
|
+
|
245
|
+
function feeDenominator() public pure returns (uint) {
|
246
|
+
// keep the interface for this function as a `view` for now, in case it changes in the future
|
247
|
+
return FEE_DENOMINATOR;
|
248
|
+
}
|
249
|
+
|
250
|
+
function setProfitSharingNumerator(uint _profitSharingNumerator) public onlyGovernance {
|
251
|
+
require(
|
252
|
+
_profitSharingNumerator + strategistFeeNumerator + platformFeeNumerator <= MAX_TOTAL_FEE,
|
253
|
+
"total fee too high"
|
254
|
+
);
|
255
|
+
|
256
|
+
nextProfitSharingNumerator = _profitSharingNumerator;
|
257
|
+
nextProfitSharingNumeratorTimestamp = block.timestamp + nextImplementationDelay;
|
258
|
+
emit QueueProfitSharingChange(nextProfitSharingNumerator, nextProfitSharingNumeratorTimestamp);
|
259
|
+
}
|
260
|
+
|
261
|
+
function confirmSetProfitSharingNumerator() public onlyGovernance {
|
262
|
+
require(
|
263
|
+
nextProfitSharingNumerator != 0
|
264
|
+
&& nextProfitSharingNumeratorTimestamp != 0
|
265
|
+
&& block.timestamp >= nextProfitSharingNumeratorTimestamp,
|
266
|
+
"invalid timestamp or no new profit sharing numerator confirmed"
|
267
|
+
);
|
268
|
+
require(
|
269
|
+
nextProfitSharingNumerator + strategistFeeNumerator + platformFeeNumerator <= MAX_TOTAL_FEE,
|
270
|
+
"total fee too high"
|
271
|
+
);
|
272
|
+
|
273
|
+
profitSharingNumerator = nextProfitSharingNumerator;
|
274
|
+
nextProfitSharingNumerator = 0;
|
275
|
+
nextProfitSharingNumeratorTimestamp = 0;
|
276
|
+
emit ConfirmProfitSharingChange(profitSharingNumerator);
|
277
|
+
}
|
278
|
+
|
279
|
+
function setStrategistFeeNumerator(uint _strategistFeeNumerator) public onlyGovernance {
|
280
|
+
require(
|
281
|
+
_strategistFeeNumerator + platformFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
|
282
|
+
"total fee too high"
|
283
|
+
);
|
284
|
+
|
285
|
+
nextStrategistFeeNumerator = _strategistFeeNumerator;
|
286
|
+
nextStrategistFeeNumeratorTimestamp = block.timestamp + nextImplementationDelay;
|
287
|
+
emit QueueStrategistFeeChange(nextStrategistFeeNumerator, nextStrategistFeeNumeratorTimestamp);
|
288
|
+
}
|
289
|
+
|
290
|
+
function confirmSetStrategistFeeNumerator() public onlyGovernance {
|
291
|
+
require(
|
292
|
+
nextStrategistFeeNumerator != 0
|
293
|
+
&& nextStrategistFeeNumeratorTimestamp != 0
|
294
|
+
&& block.timestamp >= nextStrategistFeeNumeratorTimestamp,
|
295
|
+
"invalid timestamp or no new strategist fee numerator confirmed"
|
296
|
+
);
|
297
|
+
require(
|
298
|
+
nextStrategistFeeNumerator + platformFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
|
299
|
+
"total fee too high"
|
300
|
+
);
|
301
|
+
|
302
|
+
strategistFeeNumerator = nextStrategistFeeNumerator;
|
303
|
+
nextStrategistFeeNumerator = 0;
|
304
|
+
nextStrategistFeeNumeratorTimestamp = 0;
|
305
|
+
emit ConfirmStrategistFeeChange(strategistFeeNumerator);
|
306
|
+
}
|
307
|
+
|
308
|
+
function setPlatformFeeNumerator(uint _platformFeeNumerator) public onlyGovernance {
|
309
|
+
require(
|
310
|
+
_platformFeeNumerator + strategistFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
|
311
|
+
"total fee too high"
|
312
|
+
);
|
313
|
+
|
314
|
+
nextPlatformFeeNumerator = _platformFeeNumerator;
|
315
|
+
nextPlatformFeeNumeratorTimestamp = block.timestamp + nextImplementationDelay;
|
316
|
+
emit QueuePlatformFeeChange(nextPlatformFeeNumerator, nextPlatformFeeNumeratorTimestamp);
|
317
|
+
}
|
318
|
+
|
319
|
+
function confirmSetPlatformFeeNumerator() public onlyGovernance {
|
320
|
+
require(
|
321
|
+
nextPlatformFeeNumerator != 0
|
322
|
+
&& nextPlatformFeeNumeratorTimestamp != 0
|
323
|
+
&& block.timestamp >= nextPlatformFeeNumeratorTimestamp,
|
324
|
+
"invalid timestamp or no new platform fee numerator confirmed"
|
325
|
+
);
|
326
|
+
require(
|
327
|
+
nextPlatformFeeNumerator + strategistFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
|
328
|
+
"total fee too high"
|
329
|
+
);
|
330
|
+
|
331
|
+
platformFeeNumerator = nextPlatformFeeNumerator;
|
332
|
+
nextPlatformFeeNumerator = 0;
|
333
|
+
nextPlatformFeeNumeratorTimestamp = 0;
|
334
|
+
emit ConfirmPlatformFeeChange(platformFeeNumerator);
|
335
|
+
}
|
336
|
+
|
337
|
+
function setNextImplementationDelay(uint256 _nextImplementationDelay) public onlyGovernance {
|
338
|
+
require(
|
339
|
+
_nextImplementationDelay > 0,
|
340
|
+
"invalid _nextImplementationDelay"
|
341
|
+
);
|
342
|
+
|
343
|
+
tempNextImplementationDelay = _nextImplementationDelay;
|
344
|
+
tempNextImplementationDelayTimestamp = block.timestamp + nextImplementationDelay;
|
345
|
+
emit QueueNextImplementationDelay(tempNextImplementationDelay, tempNextImplementationDelayTimestamp);
|
346
|
+
}
|
347
|
+
|
348
|
+
function confirmNextImplementationDelay() public onlyGovernance {
|
349
|
+
require(
|
350
|
+
tempNextImplementationDelayTimestamp != 0 && block.timestamp >= tempNextImplementationDelayTimestamp,
|
351
|
+
"invalid timestamp or no new implementation delay confirmed"
|
352
|
+
);
|
353
|
+
nextImplementationDelay = tempNextImplementationDelay;
|
354
|
+
tempNextImplementationDelay = 0;
|
355
|
+
tempNextImplementationDelayTimestamp = 0;
|
356
|
+
emit ConfirmNextImplementationDelay(nextImplementationDelay);
|
357
|
+
}
|
358
|
+
}
|
@@ -0,0 +1,86 @@
|
|
1
|
+
// SPDX-License-Identifier: Unlicense
|
2
|
+
pragma solidity 0.8.26;
|
3
|
+
|
4
|
+
import "@openzeppelin/contracts/utils/Address.sol";
|
5
|
+
import "@openzeppelin/contracts/utils/math/Math.sol";
|
6
|
+
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
|
7
|
+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
8
|
+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
9
|
+
|
10
|
+
import "./inheritance/Controllable.sol";
|
11
|
+
import "./interface/IERC4626.sol";
|
12
|
+
|
13
|
+
contract Drip is Controllable {
|
14
|
+
using SafeERC20 for IERC20;
|
15
|
+
using Address for address;
|
16
|
+
using SafeMath for uint256;
|
17
|
+
|
18
|
+
event DripAdded(DripMode mode, address vault, uint256 perSecond);
|
19
|
+
event DripRemoved(DripMode mode, address vault, uint256 perSecond);
|
20
|
+
event Dripped(address vault, uint256 amount);
|
21
|
+
|
22
|
+
enum DripMode { TokenAmount, FixedRate}
|
23
|
+
|
24
|
+
struct DripInfo {
|
25
|
+
DripMode mode;
|
26
|
+
address vault;
|
27
|
+
uint256 perSecond;
|
28
|
+
uint256 lastDripTime;
|
29
|
+
}
|
30
|
+
|
31
|
+
DripInfo[] public drips;
|
32
|
+
|
33
|
+
constructor(address _storage) Controllable(_storage) {}
|
34
|
+
|
35
|
+
function addDrip(DripMode _mode, address _vault, uint256 _perSecond) public onlyGovernance {
|
36
|
+
drips.push(DripInfo({
|
37
|
+
mode: _mode,
|
38
|
+
vault: _vault,
|
39
|
+
perSecond: _perSecond,
|
40
|
+
lastDripTime: block.timestamp
|
41
|
+
}));
|
42
|
+
emit DripAdded(_mode, _vault, _perSecond);
|
43
|
+
}
|
44
|
+
|
45
|
+
function removeDrip(uint256 _dripIndex) public onlyGovernance {
|
46
|
+
require(_dripIndex < drips.length, "Invalid index");
|
47
|
+
emit DripRemoved(drips[_dripIndex].mode, drips[_dripIndex].vault, drips[_dripIndex].perSecond);
|
48
|
+
drips[_dripIndex] = drips[drips.length - 1];
|
49
|
+
drips.pop();
|
50
|
+
}
|
51
|
+
|
52
|
+
function drip(uint256 _dripIndex) public {
|
53
|
+
require(_dripIndex < drips.length, "Invalid index");
|
54
|
+
DripInfo storage dripInfo = drips[_dripIndex];
|
55
|
+
uint256 timePassed = block.timestamp.sub(dripInfo.lastDripTime);
|
56
|
+
if (timePassed > 0) {
|
57
|
+
address token = IERC4626(dripInfo.vault).asset();
|
58
|
+
uint256 amount;
|
59
|
+
if (dripInfo.mode == DripMode.TokenAmount) {
|
60
|
+
amount = dripInfo.perSecond.mul(timePassed);
|
61
|
+
} else if (dripInfo.mode == DripMode.FixedRate) {
|
62
|
+
uint256 totalAssets = IERC4626(dripInfo.vault).totalAssets();
|
63
|
+
uint256 rate = dripInfo.perSecond.mul(timePassed);
|
64
|
+
amount = totalAssets.mul(rate).div(1e18);
|
65
|
+
} else {
|
66
|
+
revert("Invalid drip mode");
|
67
|
+
}
|
68
|
+
uint256 balance = IERC20(token).balanceOf(address(this));
|
69
|
+
if (Math.min(amount, balance) > 0) {
|
70
|
+
dripInfo.lastDripTime = block.timestamp;
|
71
|
+
IERC20(token).safeTransfer(dripInfo.vault, Math.min(amount, balance));
|
72
|
+
emit Dripped(dripInfo.vault, Math.min(amount, balance));
|
73
|
+
}
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
function dripAll() public {
|
78
|
+
for (uint256 i = 0; i < drips.length; i++) {
|
79
|
+
drip(i);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
function salvage(address _token, uint256 _amount) public onlyGovernance {
|
84
|
+
IERC20(_token).safeTransfer(governance(), _amount);
|
85
|
+
}
|
86
|
+
}
|