From 4b9a83165dc821f95d672e49021181fae6779d55 Mon Sep 17 00:00:00 2001 From: George Carder Date: Thu, 21 Jul 2022 12:00:41 -0700 Subject: [PATCH 01/17] modern timelock and threshold --- contracts/Strategy.sol | 24 +- contracts/StrategyController.sol | 75 ++-- contracts/StrategyControllerStorage.sol | 1 + contracts/StrategyTokenStorage.sol | 2 +- contracts/helpers/Timelocks.sol | 28 +- contracts/libraries/ControllerLibrary.sol | 16 +- .../oracles/estimators/EmergencyEstimator.sol | 21 +- errors/errors.json | 329 +++++++++--------- 8 files changed, 264 insertions(+), 232 deletions(-) diff --git a/contracts/Strategy.sol b/contracts/Strategy.sol index 30d41321..10b28db3 100644 --- a/contracts/Strategy.sol +++ b/contracts/Strategy.sol @@ -168,20 +168,21 @@ contract Strategy is IStrategy, IStrategyManagement, StrategyTokenFees, Initiali _tempRouter = router; } - function updateTimelock(bytes4 functionSelector, uint256 delay) external override { + function updateTimelock(bytes32 identifier, uint256 delay) external override { _onlyManager(); - _startTimelock(this.updateTimelock.selector, abi.encode(functionSelector, delay)); + _startTimelock(this.updateTimelock.selector, abi.encode(identifier, delay)); emit UpdateTimelock(delay, false); } function finalizeTimelock() external override { - if (!_timelockIsReady(this.updateTimelock.selector)) { - TimelockData memory td = _timelockData(this.updateTimelock.selector); + bytes32 key = keccak256(abi.encode(this.updateTimelock.selector)); + if (!_timelockIsReady(key)) { + TimelockData memory td = _timelockData(key); _require(td.delay == 0, uint256(0xb3e5dea2190e00) /* error_macro_for("finalizeTimelock: timelock is not ready.") */); } - (bytes4 selector, uint256 delay) = abi.decode(_getTimelockValue(this.updateTimelock.selector), (bytes4, uint256)); + (bytes4 selector, uint256 delay) = abi.decode(_getTimelockValue(key), (bytes4, uint256)); _setTimelock(selector, delay); - _resetTimelock(this.updateTimelock.selector); + _resetTimelock(key); emit UpdateTimelock(delay, true); } @@ -361,10 +362,11 @@ contract Strategy is IStrategy, IStrategyManagement, StrategyTokenFees, Initiali } function finalizeUpdateTradeData() external { - _require(_timelockIsReady(this.updateTradeData.selector), uint256(0xb3e5dea2190e06) /* error_macro_for("finalizeUpdateTradeData: timelock not ready.") */); - (address item, TradeData memory data) = abi.decode(_getTimelockValue(this.updateTradeData.selector), (address, TradeData)); + bytes32 key = keccak256(abi.encode(this.updateTradeData.selector)); + _require(_timelockIsReady(key), uint256(0xb3e5dea2190e06) /* error_macro_for("finalizeUpdateTradeData: timelock not ready.") */); + (address item, TradeData memory data) = abi.decode(_getTimelockValue(key), (address, TradeData)); _tradeData[item] = data; - _resetTimelock(this.updateTradeData.selector); + _resetTimelock(key); emit UpdateTradeData(item, true); } @@ -562,7 +564,7 @@ contract Strategy is IStrategy, IStrategyManagement, StrategyTokenFees, Initiali (ok, ) = exists.getValue(bytes32(uint256(token))); } - function _timelockData(bytes4 functionSelector) internal override returns(TimelockData storage) { - return __timelockData[functionSelector]; + function _timelockData(bytes32 identifier) internal override returns(TimelockData storage) { + return __timelockData[identifier]; } } diff --git a/contracts/StrategyController.sol b/contracts/StrategyController.sol index 3541021c..e2b2a63d 100644 --- a/contracts/StrategyController.sol +++ b/contracts/StrategyController.sol @@ -10,6 +10,7 @@ import "./libraries/SafeERC20.sol"; import "./libraries/ControllerLibrary.sol"; import "./interfaces/IStrategyController.sol"; import "./interfaces/IStrategyProxyFactory.sol"; +import "./helpers/Timelocks.sol"; import "./helpers/Require.sol"; import "./StrategyControllerStorage.sol"; @@ -18,7 +19,7 @@ import "./StrategyControllerStorage.sol"; * @dev Whitelisted routers are able to execute different swapping strategies as long as total strategy value doesn't drop below the defined slippage amount * @dev To avoid someone from repeatedly skimming off this slippage value, rebalance threshold should be set sufficiently high */ -contract StrategyController is IStrategyController, StrategyControllerStorage, Initializable, Require { +contract StrategyController is IStrategyController, StrategyControllerStorage, Initializable, Timelocks, Require { using SafeMath for uint256; using SignedSafeMath for int256; using SafeERC20 for IERC20; @@ -30,6 +31,8 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I uint256 private constant FEE_BOUND = 200; // Max fee of 20% int256 private constant PERCENTAGE_BOUND = 10000; // Max 10x leverage + uint256 public constant REBALANCE_TIMELOCK_PERIOD = 5 minutes; // FIXME should this be variable? a different value? + address public immutable factory; event NewStructure(address indexed strategy, StrategyItem[] items, bool indexed finalized); @@ -174,6 +177,11 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _isInitialized(address(strategy)); _setStrategyLock(strategy); _onlyManager(strategy); + + bytes32 key = keccak256(abi.encode(this.rebalance.selector, strategy)); + _require(_timelockIsReady(key), uint256(0x1bb63a90056c04) /* error_macro_for("rebalance timelock not ready.") */); + _resetTimelock(key); + ControllerLibrary.rebalance(strategy, router, _oracle, _weth, _strategyStates[address(strategy)].rebalanceSlippage, data); _removeStrategyLock(strategy); } @@ -200,7 +208,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _onlyManager(strategy); ITokenRegistry.ItemDetails memory itemDetails = oracle().tokenRegistry().itemDetails(address(-1)); address adapter = itemDetails.tradeData.adapters[0]; - _require(adapter != address(0), uint256(0x1bb63a90056c04) /* error_macro_for("Invalid adapter") */); + _require(adapter != address(0), uint256(0x1bb63a90056c05) /* error_macro_for("Invalid adapter") */); ControllerLibrary.repositionSynths(strategy, adapter, token, _susd); _removeStrategyLock(strategy); } @@ -223,9 +231,9 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I lock.timestamp == 0 || block.timestamp > lock.timestamp.add(uint256(_strategyStates[address(strategy)].timelock)), - uint256(0x1bb63a90056c05) /* error_macro_for("Timelock active") */ + uint256(0x1bb63a90056c06) /* error_macro_for("Timelock active") */ ); - _require(ControllerLibrary.verifyStructure(address(strategy), strategyItems), uint256(0x1bb63a90056c06) /* error_macro_for("Invalid structure") */); + _require(ControllerLibrary.verifyStructure(address(strategy), strategyItems), uint256(0x1bb63a90056c07) /* error_macro_for("Invalid structure") */); lock.category = TimelockCategory.RESTRUCTURE; lock.timestamp = block.timestamp; lock.data = abi.encode(strategyItems); @@ -256,12 +264,12 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _require( !strategyState.social || block.timestamp >= lock.timestamp.add(uint256(strategyState.timelock)), - uint256(0x1bb63a90056c07) /* error_macro_for("Timelock active") */ + uint256(0x1bb63a90056c08) /* error_macro_for("Timelock active") */ ); - _require(lock.category == TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c08) /* error_macro_for("Wrong category") */); + _require(lock.category == TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c09) /* error_macro_for("Wrong category") */); (StrategyItem[] memory strategyItems) = abi.decode(lock.data, (StrategyItem[])); - _require(ControllerLibrary.verifyStructure(address(strategy), strategyItems), uint256(0x1bb63a90056c09) /* error_macro_for("Invalid structure") */); + _require(ControllerLibrary.verifyStructure(address(strategy), strategyItems), uint256(0x1bb63a90056c0a) /* error_macro_for("Invalid structure") */); _finalizeStructure(strategy, router, strategyItems, data); delete lock.category; delete lock.timestamp; @@ -288,9 +296,9 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I lock.timestamp == 0 || block.timestamp > lock.timestamp.add(uint256(_strategyStates[address(strategy)].timelock)), - uint256(0x1bb63a90056c0a) /* error_macro_for("Timelock active") */ + uint256(0x1bb63a90056c0b) /* error_macro_for("Timelock active") */ ); - _require(category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0b) /* error_macro_for("updateValue: category is RESTRUCTURE.") */); + _require(category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0c) /* error_macro_for("updateValue: category is RESTRUCTURE.") */); _checkAndEmit(address(strategy), category, newValue, false); lock.category = category; lock.timestamp = block.timestamp; @@ -307,11 +315,11 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _setStrategyLock(strategy); StrategyState storage strategyState = _strategyStates[address(strategy)]; Timelock storage lock = _timelocks[address(strategy)]; - _require(lock.category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0c) /* error_macro_for("Wrong category") */); + _require(lock.category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0d) /* error_macro_for("Wrong category") */); _require( !strategyState.social || block.timestamp >= lock.timestamp.add(uint256(strategyState.timelock)), - uint256(0x1bb63a90056c0d) /* error_macro_for("Timelock active") */ + uint256(0x1bb63a90056c0e) /* error_macro_for("Timelock active") */ ); uint256 newValue = abi.decode(lock.data, (uint256)); if (lock.category == TimelockCategory.TIMELOCK) { @@ -343,7 +351,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _setStrategyLock(strategy); _onlyManager(strategy); StrategyState storage strategyState = _strategyStates[address(strategy)]; - _require(!strategyState.social, uint256(0x1bb63a90056c0e) /* error_macro_for("Strategy already open") */); + _require(!strategyState.social, uint256(0x1bb63a90056c0f) /* error_macro_for("Strategy already open") */); strategyState.social = true; emit StrategyOpen(address(strategy)); _removeStrategyLock(strategy); @@ -358,12 +366,20 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _setStrategyLock(strategy); _onlyManager(strategy); StrategyState storage strategyState = _strategyStates[address(strategy)]; - _require(!strategyState.set, uint256(0x1bb63a90056c0f) /* error_macro_for("Strategy already set") */); + _require(!strategyState.set, uint256(0x1bb63a90056c10) /* error_macro_for("Strategy already set") */); strategyState.set = true; emit StrategySet(address(strategy)); _removeStrategyLock(strategy); } + function updateTimelock(bytes32 identifier, uint256 delay) external override { + revert("timelock update not supported."); + } + + function finalizeTimelock() external override { + revert("timelock update not supported"); + } + function verifyStructure(address strategy, StrategyItem[] memory newItems) public view @@ -447,6 +463,9 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I IStrategy(strategy).updateRebalanceThreshold(state.rebalanceThreshold); if (state.social) emit StrategyOpen(strategy); if (state.set) emit StrategySet(strategy); + bytes32 key = keccak256(abi.encode(this.rebalance.selector, strategy)); + _setTimelock(key, REBALANCE_TIMELOCK_PERIOD); + _startTimelock(key, new bytes(0)); } function _deposit( @@ -461,7 +480,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I ) private { address weth; if (msg.value > 0) { - _require(amount == 0, uint256(0x1bb63a90056c10) /* error_macro_for("Ambiguous amount") */); + _require(amount == 0, uint256(0x1bb63a90056c11) /* error_macro_for("Ambiguous amount") */); amount = msg.value; weth = _weth; IWETH(weth).deposit{value: amount}(); @@ -497,8 +516,8 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I // Liquidate unused tokens ControllerLibrary.useRouter(strategy, router, router.restructure, _weth, currentItems, strategy.debt(), data); // Check balance - (bool balancedAfter, uint256 totalAfter, ) = ControllerLibrary.verifyBalance(address(strategy), _oracle); - _require(balancedAfter, uint256(0x1bb63a90056c11) /* error_macro_for("Not balanced") */); + (bool balancedAfter, uint256 totalAfter, ) = ControllerLibrary.verifyBalance(address(strategy), _oracle, false); // outer=false + _require(balancedAfter, uint256(0x1bb63a90056c12) /* error_macro_for("Not balanced") */); _checkSlippage(totalAfter, totalBefore, _strategyStates[address(strategy)].restructureSlippage); strategy.updateTokenValue(totalAfter, strategy.totalSupply()); } @@ -506,20 +525,20 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I function _checkSlippage(uint256 slippedValue, uint256 referenceValue, uint256 slippage) private pure { _require( slippedValue >= referenceValue.mul(slippage).div(DIVISOR), - uint256(0x1bb63a90056c12) /* error_macro_for("Too much slippage") */ + uint256(0x1bb63a90056c13) /* error_macro_for("Too much slippage") */ ); } function _checkDivisor(uint256 value) private pure { - _require(value <= DIVISOR, uint256(0x1bb63a90056c13) /* error_macro_for("Out of bounds") */); + _require(value <= DIVISOR, uint256(0x1bb63a90056c14) /* error_macro_for("Out of bounds") */); } function _checkFee(uint256 value) private pure { - _require(value <= FEE_BOUND, uint256(0x1bb63a90056c14) /* error_macro_for("Fee too high") */); + _require(value <= FEE_BOUND, uint256(0x1bb63a90056c15) /* error_macro_for("Fee too high") */); } function _checkTimelock(uint256 value) private { - _require(value <= 30 days, uint256(0x1bb63a90056c15) /* error_macro_for("Timelock is too long") */); + _require(value <= 30 days, uint256(0x1bb63a90056c16) /* error_macro_for("Timelock is too long") */); } function _checkAndEmit(address strategy, TimelockCategory category, uint256 value, bool finalized) private { @@ -552,21 +571,21 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I * @notice Checks that strategy is initialized */ function _isInitialized(address strategy) private view { - _require(initialized(strategy), uint256(0x1bb63a90056c16) /* error_macro_for("Not initialized") */); + _require(initialized(strategy), uint256(0x1bb63a90056c17) /* error_macro_for("Not initialized") */); } /** * @notice Checks that router is whitelisted */ function _onlyApproved(address account) private view { - _require(whitelist().approved(account), uint256(0x1bb63a90056c17) /* error_macro_for("Not approved") */); + _require(whitelist().approved(account), uint256(0x1bb63a90056c18) /* error_macro_for("Not approved") */); } /** * @notice Checks if msg.sender is manager */ function _onlyManager(IStrategy strategy) private view { - _require(msg.sender == strategy.manager(), uint256(0x1bb63a90056c18) /* error_macro_for("Not manager") */); + _require(msg.sender == strategy.manager(), uint256(0x1bb63a90056c19) /* error_macro_for("Not manager") */); } /** @@ -575,15 +594,19 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I function _socialOrManager(IStrategy strategy) private view { _require( msg.sender == strategy.manager() || _strategyStates[address(strategy)].social, - uint256(0x1bb63a90056c19) /* error_macro_for("Not manager") */ + uint256(0x1bb63a90056c1a) /* error_macro_for("Not manager") */ ); } function _notSet(address strategy) private view { - _require(!_strategyStates[strategy].set, uint256(0x1bb63a90056c1a) /* error_macro_for("Strategy cannot change") */); + _require(!_strategyStates[strategy].set, uint256(0x1bb63a90056c1b) /* error_macro_for("Strategy cannot change") */); + } + + function _timelockData(bytes32 identifier) internal override returns(TimelockData storage) { + return __timelockData[identifier]; } receive() external payable { - _require(msg.sender == _weth, uint256(0x1bb63a90056c1b) /* error_macro_for("Not WETH") */); + _require(msg.sender == _weth, uint256(0x1bb63a90056c1c) /* error_macro_for("Not WETH") */); } } diff --git a/contracts/StrategyControllerStorage.sol b/contracts/StrategyControllerStorage.sol index 6f63e2a9..1dc1f1fc 100644 --- a/contracts/StrategyControllerStorage.sol +++ b/contracts/StrategyControllerStorage.sol @@ -13,6 +13,7 @@ contract StrategyControllerStorage is StrategyTypes { mapping(address => StrategyState) internal _strategyStates; mapping(address => Timelock) internal _timelocks; address internal _pool; + mapping(bytes32 => TimelockData) internal __timelockData; // Gap for future storage changes uint256[49] private __gap; diff --git a/contracts/StrategyTokenStorage.sol b/contracts/StrategyTokenStorage.sol index 5cf12bf0..5734f0eb 100644 --- a/contracts/StrategyTokenStorage.sol +++ b/contracts/StrategyTokenStorage.sol @@ -34,7 +34,7 @@ contract StrategyTokenStorage is StrategyTypes { address[] internal _debt; mapping(address => int256) internal _percentage; mapping(address => TradeData) internal _tradeData; - mapping(bytes4 => TimelockData) internal __timelockData; + mapping(bytes32 => TimelockData) internal __timelockData; uint256 internal _managementFee; uint256 internal _managementFeeRate; diff --git a/contracts/helpers/Timelocks.sol b/contracts/helpers/Timelocks.sol index 5f6dc290..71058bb5 100644 --- a/contracts/helpers/Timelocks.sol +++ b/contracts/helpers/Timelocks.sol @@ -5,48 +5,48 @@ import "./StrategyTypes.sol"; abstract contract Timelocks is StrategyTypes { - event TimelockSet(bytes4 selector, uint256 value); + event TimelockSet(bytes32 identifier, uint256 value); event UpdateTimelock(uint256 delay, bool finalized); bytes constant public UNSET_VALUE = abi.encode(keccak256("Timelocks: unset value.")); // updgradable implementations would benefit from the ability to set new timelocks. - function updateTimelock(bytes4 selector, uint256 delay) external virtual; + function updateTimelock(bytes32 identifier, uint256 delay) external virtual; function finalizeTimelock() external virtual; // delay value is not validated but is assumed to be sensible // since this function is internal, this way `_timelockIsReady` will not overflow - function _setTimelock(bytes4 selector, uint256 delay) internal { - TimelockData storage td = _timelockData(selector); + function _setTimelock(bytes32 identifier, uint256 delay) internal { + TimelockData storage td = _timelockData(identifier); require(delay <= uint128(-1), "_setTimelock: delay out of range."); td.delay = uint128(delay); td.value = UNSET_VALUE; - emit TimelockSet(selector, delay); + emit TimelockSet(identifier, delay); } - function _timelockData(bytes4 functionSelector) internal virtual returns(TimelockData storage); + function _timelockData(bytes32 identifier) internal virtual returns(TimelockData storage); - function _startTimelock(bytes4 selector, bytes memory value) internal { - TimelockData storage td = _timelockData(selector); + function _startTimelock(bytes32 identifier, bytes memory value) internal { + TimelockData storage td = _timelockData(identifier); td.timestamp = uint128(block.timestamp); td.value = value; } - function _timelockIsReady(bytes4 selector) internal returns(bool) { - TimelockData memory td = _timelockData(selector); + function _timelockIsReady(bytes32 identifier) internal returns(bool) { + TimelockData memory td = _timelockData(identifier); if (td.timestamp == 0) return false; if (uint128(block.timestamp) >= td.timestamp + td.delay) return true; } // unchecked, assumes caller has checked `isReady` - function _getTimelockValue(bytes4 selector) internal returns(bytes memory) { - return _timelockData(selector).value; + function _getTimelockValue(bytes32 identifier) internal returns(bytes memory) { + return _timelockData(identifier).value; } - function _resetTimelock(bytes4 selector) internal { - TimelockData storage td = _timelockData(selector); + function _resetTimelock(bytes32 identifier) internal { + TimelockData storage td = _timelockData(identifier); td.timestamp = 0; td.value = UNSET_VALUE; } diff --git a/contracts/libraries/ControllerLibrary.sol b/contracts/libraries/ControllerLibrary.sol index 2d7dd2b8..e3707924 100644 --- a/contracts/libraries/ControllerLibrary.sol +++ b/contracts/libraries/ControllerLibrary.sol @@ -17,6 +17,7 @@ library ControllerLibrary { using SafeERC20 for IERC20; int256 private constant DIVISOR = 1000; + uint256 private constant REBALANCE_THRESHOLD_SCALAR = 10**2; // FIXME tune uint256 private constant PRECISION = 10**18; uint256 private constant WITHDRAW_UPPER_BOUND = 10**17; // Upper condition for including pool's tokens as part of burn during withdraw @@ -155,14 +156,14 @@ library ControllerLibrary { ) public { _onlyApproved(address(router)); strategy.settleSynths(); - (bool balancedBefore, uint256 totalBefore, int256[] memory estimates) = verifyBalance(address(strategy), oracle); + (bool balancedBefore, uint256 totalBefore, int256[] memory estimates) = verifyBalance(address(strategy), oracle, true); // outer=true require(!balancedBefore, "Balanced"); if (router.category() != IStrategyRouter.RouterCategory.GENERIC) data = abi.encode(totalBefore, estimates); // Rebalance _useRouter(strategy, router, router.rebalance, weth, data); // Recheck total - (bool balancedAfter, uint256 totalAfter, ) = verifyBalance(address(strategy), oracle); + (bool balancedAfter, uint256 totalAfter, ) = verifyBalance(address(strategy), oracle, false); // outer=false require(balancedAfter, "Not balanced"); _checkSlippage(totalAfter, totalBefore, rebalanceSlippage); strategy.updateTokenValue(totalAfter, strategy.totalSupply()); @@ -352,10 +353,17 @@ library ControllerLibrary { * whether the strategy is balanced. Necessary to confirm the balance * before and after a rebalance to ensure nothing fishy happened */ - function verifyBalance(address strategy, address oracle) public view returns (bool, uint256, int256[] memory) { + function verifyBalance(address strategy, address oracle, bool outer) public view returns (bool, uint256, int256[] memory) { + uint256 threshold = IStrategy(strategy).rebalanceThreshold(); + if (outer) { // wider threshold + threshold = threshold.mul(REBALANCE_THRESHOLD_SCALAR); + } + return _verifyBalance(strategy, oracle, threshold); + } + + function _verifyBalance(address strategy, address oracle, uint256 threshold) private view returns (bool, uint256, int256[] memory) { (uint256 total, int256[] memory estimates) = IOracle(oracle).estimateStrategy(IStrategy(strategy)); - uint256 threshold = IStrategy(strategy).rebalanceThreshold(); bool balanced = true; address[] memory strategyItems = IStrategy(strategy).items(); diff --git a/contracts/oracles/estimators/EmergencyEstimator.sol b/contracts/oracles/estimators/EmergencyEstimator.sol index 17df3739..f4750174 100644 --- a/contracts/oracles/estimators/EmergencyEstimator.sol +++ b/contracts/oracles/estimators/EmergencyEstimator.sol @@ -12,7 +12,7 @@ contract EmergencyEstimator is IEstimator, Ownable, Timelocks { using SignedSafeMath for int256; mapping(address => int256) public estimates; - mapping(bytes4 => TimelockData) private __timelockData; + mapping(bytes32 => TimelockData) private __timelockData; event EstimateSet(address token, int256 amount, bool finalized); @@ -20,19 +20,20 @@ contract EmergencyEstimator is IEstimator, Ownable, Timelocks { _setTimelock(this.updateEstimate.selector, 5 minutes); } - function updateTimelock(bytes4 functionSelector, uint256 delay) external override onlyOwner { - _startTimelock(this.updateTimelock.selector, abi.encode(functionSelector, delay)); + function updateTimelock(bytes32 identifier, uint256 delay) external override onlyOwner { + _startTimelock(this.updateTimelock.selector, abi.encode(identifier, delay)); emit UpdateTimelock(delay, false); } function finalizeTimelock() external override { - if (!_timelockIsReady(this.updateTimelock.selector)) { - TimelockData memory td = _timelockData(this.updateTimelock.selector); + bytes32 key = keccak256(abi.encode(this.updateTimelock.selector)); + if (!_timelockIsReady(key)) { + TimelockData memory td = _timelockData(key); require(td.delay == 0, "finalizeTimelock: timelock is not ready."); } - (bytes4 selector, uint256 delay) = abi.decode(_getTimelockValue(this.updateTimelock.selector), (bytes4, uint256)); - _setTimelock(selector, delay); - _resetTimelock(this.updateTimelock.selector); + (bytes32 identifier, uint256 delay) = abi.decode(_getTimelockValue(key), (bytes4, uint256)); + _setTimelock(identifier, delay); + _resetTimelock(key); emit UpdateTimelock(delay, true); } @@ -62,7 +63,7 @@ contract EmergencyEstimator is IEstimator, Ownable, Timelocks { return int256(balance).mul(estimates[token]).div(int256(10**uint256(IERC20NonStandard(token).decimals()))); } - function _timelockData(bytes4 functionSelector) internal override returns(TimelockData storage) { - return __timelockData[functionSelector]; + function _timelockData(bytes32 identifier) internal override returns(TimelockData storage) { + return __timelockData[identifier]; } } diff --git a/errors/errors.json b/errors/errors.json index 3370b030..e101abdc 100644 --- a/errors/errors.json +++ b/errors/errors.json @@ -1,8 +1,101 @@ [ + { + "contractId": "c992c0df3436", + "contractName": "PlatformProxyAdmin.sol" + }, + { + "contractId": "b3e5dea2190e", + "contractName": "Strategy.sol", + "errorcodes": { + "b3e5dea2190e00": "finalizeTimelock: timelock is not ready.", + "b3e5dea2190e01": "Router only", + "b3e5dea2190e02": "Cannot withdraw debt", + "b3e5dea2190e03": "0 amount", + "b3e5dea2190e04": "claimAll: caller must be controller or manager.", + "b3e5dea2190e05": "Manager already set", + "b3e5dea2190e06": "finalizeUpdateTradeData: timelock not ready.", + "b3e5dea2190e07": "Only StrategyProxyFactory" + } + }, + { + "contractId": "ee78cef3375d", + "contractName": "StrategyCommon.sol" + }, + { + "contractId": "1bb63a90056c", + "contractName": "StrategyController.sol", + "errorcodes": { + "1bb63a90056c00": "Sanity check that Library shares context.", + "1bb63a90056c01": "Not factory", + "1bb63a90056c02": "Strategy restructuring", + "1bb63a90056c03": "withdrawETH: call failed.", + "1bb63a90056c04": "rebalance timelock not ready.", + "1bb63a90056c05": "Invalid adapter", + "1bb63a90056c06": "Timelock active", + "1bb63a90056c07": "Invalid structure", + "1bb63a90056c08": "Timelock active", + "1bb63a90056c09": "Wrong category", + "1bb63a90056c0a": "Invalid structure", + "1bb63a90056c0b": "Timelock active", + "1bb63a90056c0c": "updateValue: category is RESTRUCTURE.", + "1bb63a90056c0d": "Wrong category", + "1bb63a90056c0e": "Timelock active", + "1bb63a90056c0f": "Strategy already open", + "1bb63a90056c10": "Strategy already set", + "1bb63a90056c11": "Ambiguous amount", + "1bb63a90056c12": "Not balanced", + "1bb63a90056c13": "Too much slippage", + "1bb63a90056c14": "Out of bounds", + "1bb63a90056c15": "Fee too high", + "1bb63a90056c16": "Timelock is too long", + "1bb63a90056c17": "Not initialized", + "1bb63a90056c18": "Not approved", + "1bb63a90056c19": "Not manager", + "1bb63a90056c1a": "Not manager", + "1bb63a90056c1b": "Strategy cannot change", + "1bb63a90056c1c": "Not WETH" + } + }, + { + "contractId": "dbc2b7686bf5", + "contractName": "StrategyControllerStorage.sol" + }, + { + "contractId": "f19f85f5ead4", + "contractName": "StrategyProxyAdmin.sol" + }, + { + "contractId": "676a717422f5", + "contractName": "StrategyProxyFactory.sol" + }, + { + "contractId": "89b89b5bfe68", + "contractName": "StrategyProxyFactoryStorage.sol" + }, + { + "contractId": "90dae108da2b", + "contractName": "StrategyToken.sol" + }, + { + "contractId": "ea334a07e51b", + "contractName": "StrategyTokenFees.sol" + }, + { + "contractId": "3d55eff30f48", + "contractName": "StrategyTokenStorage.sol" + }, + { + "contractId": "3218a8876422", + "contractName": "Whitelist.sol" + }, { "contractId": "dbdaf7ee2982", "contractName": "BaseAdapter.sol" }, + { + "contractId": "3b17cfcc5c47", + "contractName": "ProtocolAdapter.sol" + }, { "contractId": "9d5d2741081b", "contractName": "AaveV2DebtAdapter.sol" @@ -47,10 +140,6 @@ "contractId": "0da34511d7a3", "contractName": "CurveLPAdapter.sol" }, - { - "contractId": "3b17cfcc5c47", - "contractName": "ProtocolAdapter.sol" - }, { "contractId": "e61173344bc0", "contractName": "MetaStrategyAdapter.sol" @@ -104,112 +193,112 @@ "contractName": "StrategyControllerPaused.sol" }, { - "contractId": "50f8a65c2df3", - "contractName": "IAToken.sol" + "contractId": "91e8076401bf", + "contractName": "IBaseAdapter.sol" }, { - "contractId": "c326af50ec41", - "contractName": "IDebtToken.sol" + "contractId": "2ebf3ce0e034", + "contractName": "IERC20NonStandard.sol" }, { - "contractId": "3d647fefe612", - "contractName": "ILendingPoolAddressesProvider.sol" + "contractId": "906224e4c805", + "contractName": "IEstimator.sol" }, { - "contractId": "273a4e18db0f", - "contractName": "ILendingPool.sol" + "contractId": "79db5b508d34", + "contractName": "IOracle.sol" }, { - "contractId": "a0dcdefa851d", - "contractName": "IPriceOracleGetter.sol" + "contractId": "db3dccf77bac", + "contractName": "IProtocolOracle.sol" }, { - "contractId": "da10e236a9f4", - "contractName": "IComptroller.sol" + "contractId": "e72bd811e08a", + "contractName": "IRewardsAdapter.sol" }, { - "contractId": "3ee5f64bb23f", - "contractName": "ICToken.sol" + "contractId": "e385ec642d7c", + "contractName": "IStrategy.sol" }, { - "contractId": "e27e5151c74b", - "contractName": "ICurveAddressProvider.sol" + "contractId": "4d75d9fd2891", + "contractName": "IStrategyController.sol" }, { - "contractId": "fdfe34fee4a5", - "contractName": "ICurveCrypto.sol" + "contractId": "f4d7cc0cbd2a", + "contractName": "IStrategyFees.sol" }, { - "contractId": "e85cc71ba224", - "contractName": "ICurveDeposit.sol" + "contractId": "6096fda362bb", + "contractName": "IStrategyManagement.sol" }, { - "contractId": "e9dcc83b52d8", - "contractName": "ICurveGauge.sol" + "contractId": "30801c49821f", + "contractName": "IStrategyProxyFactory.sol" }, { - "contractId": "780f570fb009", - "contractName": "ICurveRegistry.sol" + "contractId": "69f724b1c69c", + "contractName": "IStrategyRouter.sol" }, { - "contractId": "166191cfc32f", - "contractName": "ICurveStableSwap.sol" + "contractId": "763d337fbb62", + "contractName": "IStrategyToken.sol" }, { - "contractId": "91e8076401bf", - "contractName": "IBaseAdapter.sol" + "contractId": "0902058bce5a", + "contractName": "IWhitelist.sol" }, { - "contractId": "2ebf3ce0e034", - "contractName": "IERC20NonStandard.sol" + "contractId": "50f8a65c2df3", + "contractName": "IAToken.sol" }, { - "contractId": "906224e4c805", - "contractName": "IEstimator.sol" + "contractId": "c326af50ec41", + "contractName": "IDebtToken.sol" }, { - "contractId": "79db5b508d34", - "contractName": "IOracle.sol" + "contractId": "273a4e18db0f", + "contractName": "ILendingPool.sol" }, { - "contractId": "db3dccf77bac", - "contractName": "IProtocolOracle.sol" + "contractId": "3d647fefe612", + "contractName": "ILendingPoolAddressesProvider.sol" }, { - "contractId": "e72bd811e08a", - "contractName": "IRewardsAdapter.sol" + "contractId": "a0dcdefa851d", + "contractName": "IPriceOracleGetter.sol" }, { - "contractId": "4d75d9fd2891", - "contractName": "IStrategyController.sol" + "contractId": "3ee5f64bb23f", + "contractName": "ICToken.sol" }, { - "contractId": "f4d7cc0cbd2a", - "contractName": "IStrategyFees.sol" + "contractId": "da10e236a9f4", + "contractName": "IComptroller.sol" }, { - "contractId": "6096fda362bb", - "contractName": "IStrategyManagement.sol" + "contractId": "e27e5151c74b", + "contractName": "ICurveAddressProvider.sol" }, { - "contractId": "30801c49821f", - "contractName": "IStrategyProxyFactory.sol" + "contractId": "fdfe34fee4a5", + "contractName": "ICurveCrypto.sol" }, { - "contractId": "69f724b1c69c", - "contractName": "IStrategyRouter.sol" + "contractId": "e85cc71ba224", + "contractName": "ICurveDeposit.sol" }, { - "contractId": "e385ec642d7c", - "contractName": "IStrategy.sol" + "contractId": "e9dcc83b52d8", + "contractName": "ICurveGauge.sol" }, { - "contractId": "763d337fbb62", - "contractName": "IStrategyToken.sol" + "contractId": "780f570fb009", + "contractName": "ICurveRegistry.sol" }, { - "contractId": "0902058bce5a", - "contractName": "IWhitelist.sol" + "contractId": "166191cfc32f", + "contractName": "ICurveStableSwap.sol" }, { "contractId": "e06c798d2f53", @@ -251,14 +340,14 @@ "contractId": "d5648740c575", "contractName": "IIssuer.sol" }, - { - "contractId": "8f1163de284d", - "contractName": "ISynthetix.sol" - }, { "contractId": "96c19af48e5b", "contractName": "ISynth.sol" }, + { + "contractId": "8f1163de284d", + "contractName": "ISynthetix.sol" + }, { "contractId": "5308f6e26a33", "contractName": "IVirtualSynth.sol" @@ -271,10 +360,6 @@ "contractId": "2e04868f1ba0", "contractName": "IYEarnV2Vault.sol" }, - { - "contractId": "49569a8c2c39", - "contractName": "AddressArrays.sol" - }, { "contractId": "a0976939a344", "contractName": "BinaryTreeWithPayload.sol" @@ -311,6 +396,14 @@ "contractId": "3775cdcb37a0", "contractName": "ERC20Mock.sol" }, + { + "contractId": "b95559c962c2", + "contractName": "UniswapMock.sol" + }, + { + "contractId": "d42b479fa07f", + "contractName": "WethMock.sol" + }, { "contractId": "44602be1de22", "contractName": "IStakingRewards.sol" @@ -327,14 +420,6 @@ "contractId": "f81fb48f908c", "contractName": "StakingRewards.sol" }, - { - "contractId": "b95559c962c2", - "contractName": "UniswapMock.sol" - }, - { - "contractId": "d42b479fa07f", - "contractName": "WethMock.sol" - }, { "contractId": "1c40961e41f2", "contractName": "EnsoOracle.sol" @@ -403,10 +488,6 @@ "contractId": "6b1672987604", "contractName": "UniswapV3Registry.sol" }, - { - "contractId": "c992c0df3436", - "contractName": "PlatformProxyAdmin.sol" - }, { "contractId": "ce61f7e73fa0", "contractName": "BatchDepositRouter.sol" @@ -427,98 +508,18 @@ "contractId": "35530d527674", "contractName": "StrategyRouter.sol" }, - { - "contractId": "ee78cef3375d", - "contractName": "StrategyCommon.sol" - }, - { - "contractId": "1bb63a90056c", - "contractName": "StrategyController.sol", - "errorcodes": { - "1bb63a90056c00": "Sanity check that Library shares context.", - "1bb63a90056c01": "Not factory", - "1bb63a90056c02": "Strategy restructuring", - "1bb63a90056c03": "withdrawETH: call failed.", - "1bb63a90056c04": "Invalid adapter", - "1bb63a90056c05": "Timelock active", - "1bb63a90056c06": "Invalid structure", - "1bb63a90056c07": "Timelock active", - "1bb63a90056c08": "Wrong category", - "1bb63a90056c09": "Invalid structure", - "1bb63a90056c0a": "Timelock active", - "1bb63a90056c0b": "updateValue: category is RESTRUCTURE.", - "1bb63a90056c0c": "Wrong category", - "1bb63a90056c0d": "Timelock active", - "1bb63a90056c0e": "Strategy already open", - "1bb63a90056c0f": "Strategy already set", - "1bb63a90056c10": "Ambiguous amount", - "1bb63a90056c11": "Not balanced", - "1bb63a90056c12": "Too much slippage", - "1bb63a90056c13": "Out of bounds", - "1bb63a90056c14": "Fee too high", - "1bb63a90056c15": "Timelock is too long", - "1bb63a90056c16": "Not initialized", - "1bb63a90056c17": "Not approved", - "1bb63a90056c18": "Not manager", - "1bb63a90056c19": "Not manager", - "1bb63a90056c1a": "Strategy cannot change", - "1bb63a90056c1b": "Not WETH" - } - }, - { - "contractId": "dbc2b7686bf5", - "contractName": "StrategyControllerStorage.sol" - }, - { - "contractId": "f19f85f5ead4", - "contractName": "StrategyProxyAdmin.sol" - }, - { - "contractId": "676a717422f5", - "contractName": "StrategyProxyFactory.sol" - }, - { - "contractId": "89b89b5bfe68", - "contractName": "StrategyProxyFactoryStorage.sol" - }, - { - "contractId": "b3e5dea2190e", - "contractName": "Strategy.sol", - "errorcodes": { - "b3e5dea2190e00": "finalizeTimelock: timelock is not ready.", - "b3e5dea2190e01": "Router only", - "b3e5dea2190e02": "Cannot withdraw debt", - "b3e5dea2190e03": "0 amount", - "b3e5dea2190e04": "claimAll: caller must be controller or manager.", - "b3e5dea2190e05": "Manager already set", - "b3e5dea2190e06": "finalizeUpdateTradeData: timelock not ready.", - "b3e5dea2190e07": "Only StrategyProxyFactory" - } - }, - { - "contractId": "ea334a07e51b", - "contractName": "StrategyTokenFees.sol" - }, - { - "contractId": "90dae108da2b", - "contractName": "StrategyToken.sol" - }, - { - "contractId": "3d55eff30f48", - "contractName": "StrategyTokenStorage.sol" - }, { "contractId": "4ed253196e4a", "contractName": "Arbitrager.sol" }, - { - "contractId": "62e52f24d678", - "contractName": "BalancerRegistry.sol" - }, { "contractId": "07a8c36aa215", "contractName": "Balancer.sol" }, + { + "contractId": "62e52f24d678", + "contractName": "BalancerRegistry.sol" + }, { "contractId": "cddb89594569", "contractName": "FailAdapter.sol" @@ -618,9 +619,5 @@ { "contractId": "98f9f325de21", "contractName": "Upgradable.sol" - }, - { - "contractId": "3218a8876422", - "contractName": "Whitelist.sol" } ] From 9c5d47f2d27faf375a1c66a97dbfda6c978faae3 Mon Sep 17 00:00:00 2001 From: George Carder Date: Thu, 21 Jul 2022 14:09:27 -0700 Subject: [PATCH 02/17] del duplicity in LibraryWrapper --- contracts/libraries/ControllerLibrary.sol | 14 ++++- contracts/test/LibraryWrapper.sol | 71 ++++------------------- lib/deploy.ts | 44 ++++++++------ 3 files changed, 49 insertions(+), 80 deletions(-) diff --git a/contracts/libraries/ControllerLibrary.sol b/contracts/libraries/ControllerLibrary.sol index e3707924..a5e888bb 100644 --- a/contracts/libraries/ControllerLibrary.sol +++ b/contracts/libraries/ControllerLibrary.sol @@ -17,7 +17,7 @@ library ControllerLibrary { using SafeERC20 for IERC20; int256 private constant DIVISOR = 1000; - uint256 private constant REBALANCE_THRESHOLD_SCALAR = 10**2; // FIXME tune + uint256 private constant REBALANCE_THRESHOLD_SCALAR = 2**2; // FIXME tune uint256 private constant PRECISION = 10**18; uint256 private constant WITHDRAW_UPPER_BOUND = 10**17; // Upper condition for including pool's tokens as part of burn during withdraw @@ -348,6 +348,18 @@ library ControllerLibrary { return address(this); } + function getExpectedTokenValue( + uint256 total, + address strategy, + address token + ) public view returns (int256) { + return StrategyLibrary.getExpectedTokenValue(total, strategy, token); + } + + function getRange(int256 expectedValue, uint256 threshold) public pure returns (int256) { + return StrategyLibrary.getRange(expectedValue, threshold); + } + /** * @notice This function gets the strategy value from the oracle and checks * whether the strategy is balanced. Necessary to confirm the balance diff --git a/contracts/test/LibraryWrapper.sol b/contracts/test/LibraryWrapper.sol index 3a0fee0a..af0d3600 100644 --- a/contracts/test/LibraryWrapper.sol +++ b/contracts/test/LibraryWrapper.sol @@ -8,7 +8,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IOracle.sol"; import "../interfaces/IStrategy.sol"; import "../interfaces/IStrategyController.sol"; -import "../libraries/StrategyLibrary.sol"; +import "../libraries/ControllerLibrary.sol"; import "../helpers/StrategyTypes.sol"; contract LibraryWrapper is StrategyTypes{ @@ -23,25 +23,23 @@ contract LibraryWrapper is StrategyTypes{ strategy = IStrategy(strategy_); } - function isBalanced() external view returns (bool) { - return - _checkBalance( - strategy.rebalanceThreshold() - ); + function isBalanced() external view returns (bool balanced) { + (balanced,,) = ControllerLibrary.verifyBalance(address(strategy), address(oracle), true); // outer=true + return balanced; } - function isRebalanceNeeded(uint256 alertThreshold) external view returns (bool) { - bool balanced = _checkBalance(alertThreshold); - return !balanced; + function isBalancedInner() external view returns (bool balanced) { + (balanced,,) = ControllerLibrary.verifyBalance(address(strategy), address(oracle), false); // outer=false + return balanced; } function getRange(int256 expectedValue, uint256 range) external pure returns (int256) { - return StrategyLibrary.getRange(expectedValue, range); + return ControllerLibrary.getRange(expectedValue, range); } function getRebalanceRange(int256 expectedValue) external view returns (int256) { uint256 range = strategy.rebalanceThreshold(); - return StrategyLibrary.getRange(expectedValue, range); + return ControllerLibrary.getRange(expectedValue, range); } function getStrategyValue() external view returns (uint256) { @@ -54,7 +52,7 @@ contract LibraryWrapper is StrategyTypes{ } function getExpectedTokenValue(uint256 total, address token) external view returns (int256) { - return StrategyLibrary.getExpectedTokenValue(total, address(strategy), token); + return ControllerLibrary.getExpectedTokenValue(total, address(strategy), token); } function _getTokenValue(IStrategy s, address token) internal view returns (int256) { @@ -83,53 +81,4 @@ contract LibraryWrapper is StrategyTypes{ ); } } - - function _checkBalance( - uint256 threshold - ) internal view returns (bool) { - (uint256 total, int256[] memory estimates) = - oracle.estimateStrategy(strategy); - bool balanced = true; - address[] memory strategyItems = strategy.items(); - for (uint256 i = 0; i < strategyItems.length; i++) { - int256 expectedValue = StrategyLibrary.getExpectedTokenValue(total, address(strategy), strategyItems[i]); - if (expectedValue > 0) { - int256 rebalanceRange = StrategyLibrary.getRange(expectedValue, threshold); - if (estimates[i] > expectedValue.add(rebalanceRange)) { - balanced = false; - break; - } - if (estimates[i] < expectedValue.sub(rebalanceRange)) { - balanced = false; - break; - } - } else { - // Token has an expected value of 0, so any value can cause the contract - // to be 'unbalanced' so we need an alternative way to determine balance. - // Min percent = 0.1%. If token value is above, consider it unbalanced - if (estimates[i] > StrategyLibrary.getRange(int256(total), 1)) { - balanced = false; - break; - } - } - } - if (balanced) { - address[] memory strategyDebt = strategy.debt(); - for (uint256 i = 0; i < strategyDebt.length; i++) { - int256 expectedValue = StrategyLibrary.getExpectedTokenValue(total, address(strategy), strategyDebt[i]); - int256 rebalanceRange = StrategyLibrary.getRange(expectedValue, threshold); - uint256 index = strategyItems.length + i; - // Debt - if (estimates[index] < expectedValue.add(rebalanceRange)) { - balanced = false; - break; - } - if (estimates[index] > expectedValue.sub(rebalanceRange)) { - balanced = false; - break; - } - } - } - return balanced; - } } diff --git a/lib/deploy.ts b/lib/deploy.ts index 62b41cad..fe1490ed 100644 --- a/lib/deploy.ts +++ b/lib/deploy.ts @@ -88,23 +88,26 @@ export class Platform { controller: Contract oracles: Oracles administration: Administration - library: Contract - strategyLibraries: any // + strategyLibrary: Contract + controllerLibrary: Contract + strategyLibraries: any // public constructor( strategyFactory: Contract, controller: Contract, oracles: Oracles, administration: Administration, - library: Contract, - strategyLibraries: any + strategyLibrary: Contract, + controllerLibrary: Contract, + strategyLibraries: any ) { this.strategyFactory = strategyFactory this.controller = controller this.oracles = oracles this.administration = administration - this.library = library - this.strategyLibraries = strategyLibraries + this.strategyLibrary = strategyLibrary + this.controllerLibrary = controllerLibrary + this.strategyLibraries = strategyLibraries } print() { @@ -116,9 +119,9 @@ export class Platform { console.log(' TokenRegistry: ', this.oracles.registries.tokenRegistry.address) } - public getStrategyContractFactory() : any { - return getContractFactory('Strategy', this.strategyLibraries) - } + public getStrategyContractFactory(): any { + return getContractFactory('Strategy', this.strategyLibraries) + } } export async function deployTokens(owner: SignerWithAddress, numTokens: number, value: BigNumber): Promise { @@ -386,21 +389,18 @@ export async function deployPlatform( // Strategy Implementation const strategyClaim = await waffle.deployContract(owner, StrategyClaim, []) - const strategyLibraries = { libraries: { StrategyClaim: strategyClaim.address }} + const strategyLibraries = { libraries: { StrategyClaim: strategyClaim.address } } await strategyClaim.deployed() const strategyClaimLink = createLink(StrategyClaim, strategyClaim.address) - const strategyLinked = linkBytecode(Strategy, [strategyClaimLink]) + const strategyLinked = linkBytecode(Strategy, [strategyClaimLink]) - const strategyImplementation = await waffle.deployContract( - owner, - strategyLinked, - [ + const strategyImplementation = await waffle.deployContract(owner, strategyLinked, [ factoryAddress, controllerAddress, MAINNET_ADDRESSES.SYNTHETIX_ADDRESS_PROVIDER, - MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER,] - ) + MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, + ]) await strategyImplementation.deployed() await platformProxyAdmin @@ -442,7 +442,15 @@ export async function deployPlatform( platformProxyAdmin, } - return new Platform(factory, controller, oracles, administration, strategyLibrary, strategyLibraries) + return new Platform( + factory, + controller, + oracles, + administration, + strategyLibrary, + controllerLibrary, + strategyLibraries + ) } export async function deployUniswapV2Adapter( From 346232784df471536c695b8f0a0998b27efd9ada Mon Sep 17 00:00:00 2001 From: George Carder Date: Thu, 21 Jul 2022 14:09:51 -0700 Subject: [PATCH 03/17] update timelock properly --- contracts/StrategyController.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/StrategyController.sol b/contracts/StrategyController.sol index e2b2a63d..e617674b 100644 --- a/contracts/StrategyController.sol +++ b/contracts/StrategyController.sol @@ -180,7 +180,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I bytes32 key = keccak256(abi.encode(this.rebalance.selector, strategy)); _require(_timelockIsReady(key), uint256(0x1bb63a90056c04) /* error_macro_for("rebalance timelock not ready.") */); - _resetTimelock(key); + _startTimelock(key, new bytes(0)); ControllerLibrary.rebalance(strategy, router, _oracle, _weth, _strategyStates[address(strategy)].rebalanceSlippage, data); _removeStrategyLock(strategy); From 40e59f3b96125715cfa530b452eac03ee6ca9e14 Mon Sep 17 00:00:00 2001 From: George Carder Date: Thu, 21 Jul 2022 14:10:12 -0700 Subject: [PATCH 04/17] update strategy-controller test, passes --- test/strategy-controller.ts | 381 +++++++++++++++++++++--------------- 1 file changed, 219 insertions(+), 162 deletions(-) diff --git a/test/strategy-controller.ts b/test/strategy-controller.ts index 580154dd..82ce2981 100644 --- a/test/strategy-controller.ts +++ b/test/strategy-controller.ts @@ -1,3 +1,4 @@ +const runAll = true import chai from 'chai' const { expect } = chai import hre from 'hardhat' @@ -42,7 +43,7 @@ describe('StrategyController', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, failAdapter: Contract, strategy: Contract, @@ -61,11 +62,11 @@ describe('StrategyController', function () { controller = platform.controller oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(owner, uniswapFactory, weth) await whitelist.connect(owner).approve(adapter.address) - router = await deployLoopRouter(owner, controller, library) + router = await deployLoopRouter(owner, controller, controllerLibrary) await whitelist.connect(owner).approve(router.address) }) @@ -269,7 +270,7 @@ describe('StrategyController', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -545,7 +546,18 @@ describe('StrategyController', function () { expect(BigNumber.from((await controller.strategyState(strategy.address)).timelock).eq(timelock)).to.equal(true) }) + it('Should fail to rebalance, rebalance timelock not ready.', async function () { + expect( + await isRevertedWith( + controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), + 'rebalance timelock not ready.', + 'StrategyController.sol' + ) + ).to.be.true + }) + it('Should fail to rebalance, already balanced', async function () { + await increaseTime(5 * 60 + 1) expect( await isRevertedWith( controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), @@ -555,9 +567,9 @@ describe('StrategyController', function () { ).to.be.true }) - it('Should purchase a token, requiring a rebalance', async function () { + it('Should purchase a token, not quite enough for a rebalance', async function () { // Approve the user to use the adapter - const value = WeiPerEther.mul(50) + const value = WeiPerEther.mul(1) await weth.connect(accounts[2]).deposit({ value: value.mul(2) }) await weth.connect(accounts[2]).approve(adapter.address, value.mul(2)) await adapter @@ -568,7 +580,10 @@ describe('StrategyController', function () { .connect(accounts[2]) .swap(value.div(4), 0, weth.address, tokens[3].address, accounts[2].address, accounts[2].address) //await displayBalances(wrapper, strategyItems, weth) - expect(await wrapper.isBalanced()).to.equal(false) + + // note the differences in inner and outer rebalance thresholds + expect(await wrapper.isBalanced()).to.equal(true) + expect(await wrapper.isBalancedInner()).to.equal(false) // inner and outer wrt rebalance threshold }) it('Should fail to rebalance, router not approved', async function () { @@ -581,7 +596,35 @@ describe('StrategyController', function () { ).to.be.true }) + it('Should fail to rebalance, balanced', async function () { + expect( + await isRevertedWith( + controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), + 'Balanced', + 'StrategyController.sol' + ) + ).to.be.true + }) + + it('Should purchase a token, requiring a rebalance', async function () { + // Approve the user to use the adapter + const value = WeiPerEther.mul(10) + await weth.connect(accounts[2]).deposit({ value: value.mul(2) }) + await weth.connect(accounts[2]).approve(adapter.address, value.mul(2)) + await adapter + .connect(accounts[2]) + .swap(value, 0, weth.address, tokens[1].address, accounts[2].address, accounts[2].address) + //The following trade should increase the value of the token such that it doesn't need to be rebalanced + await adapter + .connect(accounts[2]) + .swap(value.div(4), 0, weth.address, tokens[3].address, accounts[2].address, accounts[2].address) + //await displayBalances(wrapper, strategyItems, weth) + expect(await wrapper.isBalanced()).to.equal(false) + expect(await wrapper.isBalancedInner()).to.equal(false) // inner and outer wrt rebalance threshold + }) + it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -628,7 +671,7 @@ describe('StrategyController', function () { await isRevertedWith( controller .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, 1000, '0x', { value: BigNumber.from('1000') }), + .deposit(strategy.address, router.address, 0, 1000, '0x', { value: BigNumber.from('10000') }), 'Too much slippage', 'StrategyController.sol' ) @@ -777,7 +820,7 @@ describe('StrategyController', function () { .connect(accounts[2]) .swap(value, 0, tokens[1].address, weth.address, accounts[2].address, accounts[2].address) } else { - const value = WeiPerEther.mul(100) + const value = WeiPerEther.mul(1000) await weth.connect(accounts[2]).deposit({ value: value }) await weth.connect(accounts[2]).approve(adapter.address, value) await adapter @@ -789,177 +832,191 @@ describe('StrategyController', function () { expect(await wrapper.isBalanced()).to.equal(false) }) - it('Should fail to rebalance: not controller', async function () { - await expect(router.rebalance(strategy.address, '0x')).to.be.revertedWith('Only controller') - }) - - it('Should rebalance strategy', async function () { - const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems, weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) - - it('Should fail to open strategy: not manager', async function () { - expect( - await isRevertedWith( - controller.connect(owner).openStrategy(strategy.address), - 'Not manager', - 'StrategyController.sol' - ) - ).to.be.true - }) - - it('Should open strategy', async function () { - await controller.connect(accounts[1]).openStrategy(strategy.address) - expect((await controller.strategyState(strategy.address)).social).to.equal(true) - }) - - it('Should fail to open strategy: already open', async function () { - expect( - await isRevertedWith( - controller.connect(accounts[1]).openStrategy(strategy.address), - 'Strategy already open', - 'StrategyController.sol' - ) - ).to.be.true - }) + if (runAll) { + it('Should fail to rebalance: not controller', async function () { + await expect(router.rebalance(strategy.address, '0x')).to.be.revertedWith('Only controller') + }) - it('Should deploy fail adapter + setup strategy to need rebalance', async function () { - const FailAdapter = await getContractFactory('FailAdapter') - failAdapter = await FailAdapter.deploy(weth.address) - await failAdapter.deployed() + it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) + const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems, weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - const value = WeiPerEther.mul(100) - await weth.connect(accounts[2]).deposit({ value: value }) - await weth.connect(accounts[2]).approve(adapter.address, value) - await adapter - .connect(accounts[2]) - .swap(value, 0, weth.address, tokens[1].address, accounts[2].address, accounts[2].address) - }) + it('Should fail to open strategy: not manager', async function () { + expect( + await isRevertedWith( + controller.connect(owner).openStrategy(strategy.address), + 'Not manager', + 'StrategyController.sol' + ) + ).to.be.true + }) - it('Should restructure', async function () { - const positions = [ - { token: tokens[1].address, percentage: BigNumber.from(500) }, - { token: tokens[2].address, percentage: BigNumber.from(0) }, - { token: tokens[3].address, percentage: BigNumber.from(500) }, - ] as Position[] + it('Should open strategy', async function () { + await controller.connect(accounts[1]).openStrategy(strategy.address) + expect((await controller.strategyState(strategy.address)).social).to.equal(true) + }) - strategyItems = prepareStrategy(positions, adapter.address) - await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) - }) + it('Should fail to open strategy: already open', async function () { + expect( + await isRevertedWith( + controller.connect(accounts[1]).openStrategy(strategy.address), + 'Strategy already open', + 'StrategyController.sol' + ) + ).to.be.true + }) - it('Should finalize structure', async function () { - await controller.connect(accounts[1]).finalizeStructure(strategy.address, router.address, '0x') - //await displayBalances(wrapper, strategyItems, weth) - }) + it('Should deploy fail adapter + setup strategy to need rebalance', async function () { + const FailAdapter = await getContractFactory('FailAdapter') + failAdapter = await FailAdapter.deploy(weth.address) + await failAdapter.deployed() - it('Transfer reserve token to require rebalance', async function () { - expect( - await isRevertedWith( - controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), - 'Balanced', - 'StrategyController.sol' - ) - ).to.be.true + const value = WeiPerEther.mul(100) + await weth.connect(accounts[2]).deposit({ value: value }) + await weth.connect(accounts[2]).approve(adapter.address, value) + await adapter + .connect(accounts[2]) + .swap(value, 0, weth.address, tokens[1].address, accounts[2].address, accounts[2].address) + }) - const value = WeiPerEther.div(1000) - await tokens[2].connect(accounts[0]).transfer(strategy.address, value) - expect(await wrapper.isBalanced()).to.equal(false) - await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + it('Should restructure', async function () { + const positions = [ + { token: tokens[1].address, percentage: BigNumber.from(500) }, + { token: tokens[2].address, percentage: BigNumber.from(0) }, + { token: tokens[3].address, percentage: BigNumber.from(500) }, + ] as Position[] - it('Transfer reserve weth to require rebalance', async function () { - expect( - await isRevertedWith( - controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), - 'Balanced', - 'StrategyController.sol' - ) - ).to.be.true + strategyItems = prepareStrategy(positions, adapter.address) + await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) + }) - const value = WeiPerEther.div(100) - await weth.connect(accounts[4]).deposit({ value: value }) - await weth.connect(accounts[4]).transfer(strategy.address, value) - expect(await wrapper.isBalanced()).to.equal(false) - await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + it('Should finalize structure', async function () { + await controller.connect(accounts[1]).finalizeStructure(strategy.address, router.address, '0x') + //await displayBalances(wrapper, strategyItems, weth) + }) - it('Should fail to estimate and require emergency estimator', async function () { - const originalEstimate = await oracle['estimateItem(uint256,address)'](WeiPerEther, tokens[1].address) - const emergencyEstimatorAddress = await platform.oracles.registries.tokenRegistry.estimators( - ESTIMATOR_CATEGORY.BLOCKED - ) + it('Transfer reserve token to require rebalance', async function () { + await increaseTime(5 * 60 + 1) + expect( + await isRevertedWith( + controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), + 'Balanced', + 'StrategyController.sol' + ) + ).to.be.true + + const value = WeiPerEther.div(1000) + await tokens[2].connect(accounts[0]).transfer(strategy.address, value) + expect(await wrapper.isBalanced()).to.equal(false) + await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - const GasBurnerEstimator = await getContractFactory('GasBurnerEstimator') - const gasBurnerEstimator = await GasBurnerEstimator.deploy() - await gasBurnerEstimator.connect(owner).deployed() - let tx = await strategyFactory - .connect(owner) - .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, gasBurnerEstimator.address) - await tx.wait() - tx = await strategyFactory - .connect(owner) - .addItemToRegistry(ITEM_CATEGORY.BASIC, ESTIMATOR_CATEGORY.BLOCKED, tokens[1].address) - await tx.wait() + it('Transfer reserve weth to require rebalance', async function () { + await increaseTime(5 * 60 + 1) + expect( + await isRevertedWith( + controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), + 'Balanced', + 'StrategyController.sol' + ) + ).to.be.true + + const value = WeiPerEther.div(100) + await weth.connect(accounts[4]).deposit({ value: value }) + await weth.connect(accounts[4]).transfer(strategy.address, value) + expect(await wrapper.isBalanced()).to.equal(false) + await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - await expect( - controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { value: WeiPerEther }) - ).to.be.revertedWith('') + it('Should fail to estimate and require emergency estimator', async function () { + const originalEstimate = await oracle['estimateItem(uint256,address)'](WeiPerEther, tokens[1].address) + const emergencyEstimatorAddress = await platform.oracles.registries.tokenRegistry.estimators( + ESTIMATOR_CATEGORY.BLOCKED + ) + + const GasBurnerEstimator = await getContractFactory('GasBurnerEstimator') + const gasBurnerEstimator = await GasBurnerEstimator.deploy() + await gasBurnerEstimator.connect(owner).deployed() + let tx = await strategyFactory + .connect(owner) + .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, gasBurnerEstimator.address) + await tx.wait() + tx = await strategyFactory + .connect(owner) + .addItemToRegistry(ITEM_CATEGORY.BASIC, ESTIMATOR_CATEGORY.BLOCKED, tokens[1].address) + await tx.wait() + + await expect( + controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { + value: WeiPerEther, + }) + ).to.be.revertedWith('') - tx = await strategyFactory - .connect(owner) - .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, emergencyEstimatorAddress) - await tx.wait() + tx = await strategyFactory + .connect(owner) + .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, emergencyEstimatorAddress) + await tx.wait() - await expect( - controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { value: WeiPerEther }) - ).to.be.revertedWith('') + await expect( + controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { + value: WeiPerEther, + }) + ).to.be.revertedWith('') - const EmergencyEstimator = await getContractFactory('EmergencyEstimator') - const emergencyEstimator = await EmergencyEstimator.attach(emergencyEstimatorAddress) - await emergencyEstimator.updateEstimate(tokens[1].address, originalEstimate) + const EmergencyEstimator = await getContractFactory('EmergencyEstimator') + const emergencyEstimator = await EmergencyEstimator.attach(emergencyEstimatorAddress) + await emergencyEstimator.updateEstimate(tokens[1].address, originalEstimate) - await increaseTime(10 * 60) + await increaseTime(10 * 60) - await emergencyEstimator.finalizeSetEstimate() + await emergencyEstimator.finalizeSetEstimate() - await expect( - controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { value: WeiPerEther }) - ).to.emit(controller, 'Deposit') - }) + await expect( + controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { + value: WeiPerEther, + }) + ).to.emit(controller, 'Deposit') + }) - it('Should set strategy', async function () { - await expect(controller.connect(accounts[1]).setStrategy(strategy.address)).to.emit(controller, 'StrategySet') - const positions = [{ token: weth.address, percentage: BigNumber.from(1000) }] as Position[] - strategyItems = prepareStrategy(positions, adapter.address) - expect( - await isRevertedWith( - controller.connect(accounts[1]).restructure(strategy.address, strategyItems), - 'Strategy cannot change', - 'StrategyController.sol' - ) - ).to.be.true - }) + it('Should set strategy', async function () { + await expect(controller.connect(accounts[1]).setStrategy(strategy.address)).to.emit( + controller, + 'StrategySet' + ) + const positions = [{ token: weth.address, percentage: BigNumber.from(1000) }] as Position[] + strategyItems = prepareStrategy(positions, adapter.address) + expect( + await isRevertedWith( + controller.connect(accounts[1]).restructure(strategy.address, strategyItems), + 'Strategy cannot change', + 'StrategyController.sol' + ) + ).to.be.true + }) - it('Should fail to set strategy: already set', async function () { - expect( - await isRevertedWith( - controller.connect(accounts[1]).setStrategy(strategy.address), - 'Strategy already set', - 'StrategyController.sol' - ) - ).to.be.true - }) + it('Should fail to set strategy: already set', async function () { + expect( + await isRevertedWith( + controller.connect(accounts[1]).setStrategy(strategy.address), + 'Strategy already set', + 'StrategyController.sol' + ) + ).to.be.true + }) + } }) From 1aa16602d0945f51823bf48aeab540dae4b88cc0 Mon Sep 17 00:00:00 2001 From: George Carder Date: Thu, 21 Jul 2022 14:10:34 -0700 Subject: [PATCH 05/17] update library in other tests, not sure if passing --- test/aave-adapter.ts | 16 ++++++++-------- test/balancer-adapter.ts | 8 ++++---- test/batch-router.ts | 8 ++++---- test/compound-adapter.ts | 8 ++++---- test/curve-adapter.ts | 14 +++++++------- test/experimental-strategy.ts | 8 ++++---- test/flash-loan.ts | 6 +++--- test/kyber-adapter.ts | 8 ++++---- test/leverage-adapter.ts | 8 ++++---- test/library.ts | 10 +++++----- test/live-estimates.ts | 2 +- test/meta-strategy-adapter.ts | 12 ++++++------ test/multicall-router.ts | 6 +++--- test/reentrancy.ts | 6 +++--- test/social-strategy.ts | 8 ++++---- test/strategy-admin.ts | 4 ++-- test/strategy-factory.ts | 8 ++++---- test/strategy-token.ts | 6 +++--- test/synthetix-adapter.ts | 13 +++++++++---- test/token-registry.ts | 6 +++--- test/uniswap-v3-adapter.ts | 18 +++++++++--------- test/weird-erc20s.ts | 20 ++++++++++---------- test/x-fees.ts | 8 ++++---- test/yearn-adapter.ts | 8 ++++---- 24 files changed, 112 insertions(+), 107 deletions(-) diff --git a/test/aave-adapter.ts b/test/aave-adapter.ts index 12f91dc3..f4ca7d67 100644 --- a/test/aave-adapter.ts +++ b/test/aave-adapter.ts @@ -49,7 +49,7 @@ describe('AaveAdapter', function () { strategyFactory: Contract, controller: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, whitelist: Contract, uniswapAdapter: Contract, aaveV2Adapter: Contract, @@ -81,7 +81,7 @@ describe('AaveAdapter', function () { controller = platform.controller strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle - library = platform.library + controllerLibrary = platform.controllerLibrary whitelist = platform.administration.whitelist const aaveAddressProvider = new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], accounts[0]) const synthetixAddressProvider = new Contract(MAINNET_ADDRESSES.SYNTHETIX_ADDRESS_PROVIDER, [], accounts[0]) @@ -92,7 +92,7 @@ describe('AaveAdapter', function () { collateralToken = tokens.aWETH collateralToken2 = tokens.aCRV - router = await deployFullRouter(accounts[0], aaveAddressProvider, controller, library) + router = await deployFullRouter(accounts[0], aaveAddressProvider, controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapAdapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) @@ -180,7 +180,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -457,7 +457,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -538,7 +538,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -626,7 +626,7 @@ describe('AaveAdapter', function () { strategy = await Strategy.attach(strategyAddress) const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -717,7 +717,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) let metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) diff --git a/test/balancer-adapter.ts b/test/balancer-adapter.ts index 8a1b44cf..eae0c1cb 100644 --- a/test/balancer-adapter.ts +++ b/test/balancer-adapter.ts @@ -21,7 +21,7 @@ describe('BalancerAdapter', function () { strategyFactory: Contract, controller: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, router: Contract, balancerAdapter: Contract, uniswapAdapter: Contract, @@ -44,13 +44,13 @@ describe('BalancerAdapter', function () { controller = platform.controller strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle - library = platform.library + controllerLibrary = platform.controllerLibrary const whitelist = platform.administration.whitelist uniswapAdapter = await deployer.deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) balancerAdapter = await deployer.deployBalancerAdapter(accounts[0], balancerRegistry, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) await whitelist.connect(accounts[0]).approve(balancerAdapter.address) - router = await deployer.deployLoopRouter(accounts[0], controller, library) + router = await deployer.deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) }) @@ -85,7 +85,7 @@ describe('BalancerAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/batch-router.ts b/test/batch-router.ts index 9b34c988..79228666 100644 --- a/test/batch-router.ts +++ b/test/batch-router.ts @@ -34,7 +34,7 @@ describe('BatchDepositRouter', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, strategy: Contract, strategyItems: StrategyItem[], @@ -50,10 +50,10 @@ describe('BatchDepositRouter', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) - router = await deployBatchDepositRouter(accounts[0], controller, library) + router = await deployBatchDepositRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) }) @@ -119,7 +119,7 @@ describe('BatchDepositRouter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/compound-adapter.ts b/test/compound-adapter.ts index 42682c60..74dfd733 100644 --- a/test/compound-adapter.ts +++ b/test/compound-adapter.ts @@ -34,7 +34,7 @@ describe('CompoundAdapter', function () { strategyFactory: Contract, controller: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, uniswapAdapter: Contract, compoundAdapter: Contract, strategy: Contract, @@ -54,13 +54,13 @@ describe('CompoundAdapter', function () { controller = platform.controller strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle - library = platform.library + controllerLibrary = platform.controllerLibrary const whitelist = platform.administration.whitelist const { tokenRegistry } = platform.oracles.registries await tokens.registerTokens(accounts[0], strategyFactory) - router = await deployLoopRouter(accounts[0], controller, library) + router = await deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapAdapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) @@ -145,7 +145,7 @@ describe('CompoundAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/curve-adapter.ts b/test/curve-adapter.ts index 3b1f0fbb..1ff29465 100644 --- a/test/curve-adapter.ts +++ b/test/curve-adapter.ts @@ -48,7 +48,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, uniswapV2Adapter: Contract, uniswapV2Factory: Contract, uniswapV3Adapter: Contract, @@ -81,7 +81,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { controller = platform.controller oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary const { tokenRegistry, curveDepositZapRegistry, chainlinkRegistry, uniswapV3Registry } = platform.oracles.registries @@ -95,7 +95,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { ) const addressProvider = new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], accounts[0]) - router = await deployLoopRouter(accounts[0], controller, library) + router = await deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapV2Adapter = await deployUniswapV2Adapter(accounts[0], uniswapV2Factory, weth) await whitelist.connect(accounts[0]).approve(uniswapV2Adapter.address) @@ -362,7 +362,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -593,7 +593,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -723,7 +723,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -812,7 +812,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/experimental-strategy.ts b/test/experimental-strategy.ts index 382913b4..bf36adab 100644 --- a/test/experimental-strategy.ts +++ b/test/experimental-strategy.ts @@ -36,7 +36,7 @@ describe('Experimental Strategy', function () { strategyFactory: Contract, controller: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, uniswapV2Adapter: Contract, curveLPAdapter: Contract, yearnAdapter: Contract, @@ -63,7 +63,7 @@ describe('Experimental Strategy', function () { controller = platform.controller strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle - library = platform.library + controllerLibrary = platform.controllerLibrary const whitelist = platform.administration.whitelist const curveAddressProvider = new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], accounts[0]) const aaveAddressProvider = new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], accounts[0]) @@ -71,7 +71,7 @@ describe('Experimental Strategy', function () { const { tokenRegistry, curveDepositZapRegistry, chainlinkRegistry } = platform.oracles.registries await tokens.registerTokens(accounts[0], strategyFactory, undefined, chainlinkRegistry, curveDepositZapRegistry) - router = await deployFullRouter(accounts[0], aaveAddressProvider, controller, library) + router = await deployFullRouter(accounts[0], aaveAddressProvider, controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapV2Adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapV2Adapter.address) @@ -135,7 +135,7 @@ describe('Experimental Strategy', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) diff --git a/test/flash-loan.ts b/test/flash-loan.ts index 5bf0219b..314e5795 100644 --- a/test/flash-loan.ts +++ b/test/flash-loan.ts @@ -38,7 +38,7 @@ describe('Flash Loan', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, sushiAdapter: Contract, sushiFactory: Contract, uniswapAdapter: Contract, @@ -56,7 +56,7 @@ describe('Flash Loan', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary uniswapAdapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) sushiAdapter = await deployUniswapV2Adapter(accounts[0], sushiFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) @@ -107,7 +107,7 @@ describe('Flash Loan', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) diff --git a/test/kyber-adapter.ts b/test/kyber-adapter.ts index 5bcecf92..5a2d8cc5 100644 --- a/test/kyber-adapter.ts +++ b/test/kyber-adapter.ts @@ -37,7 +37,7 @@ describe('KyberSwapAdapter', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, kyberAdapter: Contract, uniswapV2Adapter: Contract, strategy: Contract, @@ -63,7 +63,7 @@ describe('KyberSwapAdapter', function () { controller = platform.controller oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary const { curveDepositZapRegistry, chainlinkRegistry, uniswapV3Registry } = platform.oracles.registries await tokens.registerTokens( @@ -74,7 +74,7 @@ describe('KyberSwapAdapter', function () { curveDepositZapRegistry ) - router = await deployLoopRouter(owner, controller, library) + router = await deployLoopRouter(owner, controller, controllerLibrary) await whitelist.connect(owner).approve(router.address) kyberAdapter = await deployKyberSwapAdapter(owner, kyberFactory, kyberRouter, weth) await whitelist.connect(owner).approve(kyberAdapter.address) @@ -115,7 +115,7 @@ describe('KyberSwapAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/leverage-adapter.ts b/test/leverage-adapter.ts index 2b385bd6..7cbc4ae4 100644 --- a/test/leverage-adapter.ts +++ b/test/leverage-adapter.ts @@ -43,7 +43,7 @@ describe('Leverage2XAdapter', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, uniswapAdapter: Contract, aaveV2Adapter: Contract, aaveV2DebtAdapter: Contract, @@ -63,7 +63,7 @@ describe('Leverage2XAdapter', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary const { tokenRegistry } = platform.oracles.registries await tokens.registerTokens(accounts[0], strategyFactory) @@ -156,7 +156,7 @@ describe('Leverage2XAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) @@ -242,7 +242,7 @@ describe('Leverage2XAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) diff --git a/test/library.ts b/test/library.ts index 3d387711..13e2a002 100644 --- a/test/library.ts +++ b/test/library.ts @@ -9,7 +9,7 @@ const { AddressZero, WeiPerEther } = constants const NUM_TOKENS = 15 -describe('StrategyLibrary', function () { +describe('ControllerLibrary', function () { let tokens: Contract[], weth: Contract, accounts: SignerWithAddress[], @@ -19,7 +19,7 @@ describe('StrategyLibrary', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, strategyItems: StrategyItem[], wrapper: Contract @@ -39,10 +39,10 @@ describe('StrategyLibrary', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) - router = await deployLoopRouter(accounts[0], controller, library) + router = await deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) const positions = [ @@ -84,7 +84,7 @@ describe('StrategyLibrary', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/live-estimates.ts b/test/live-estimates.ts index 7016c560..feabae67 100644 --- a/test/live-estimates.ts +++ b/test/live-estimates.ts @@ -100,7 +100,7 @@ describe('Live Estimates', function () { accounts[0], new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], accounts[0]), controller, - enso.platform.library + enso.platform.controllerLibrary ) // Whitelist await enso.platform.administration.whitelist.connect(owner).approve(router.address) diff --git a/test/meta-strategy-adapter.ts b/test/meta-strategy-adapter.ts index 70da4cbb..7e22cc9e 100644 --- a/test/meta-strategy-adapter.ts +++ b/test/meta-strategy-adapter.ts @@ -57,7 +57,7 @@ describe('MetaStrategyAdapter', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, uniswapAdapter: Contract, metaStrategyAdapter: Contract, basicStrategy: Contract, @@ -80,8 +80,8 @@ describe('MetaStrategyAdapter', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library - loopRouter = await deployLoopRouter(accounts[0], controller, library) + controllerLibrary = platform.controllerLibrary + loopRouter = await deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(loopRouter.address) multicallRouter = await deployMulticallRouter(accounts[0], controller) await whitelist.connect(accounts[0]).approve(multicallRouter.address) @@ -116,7 +116,7 @@ describe('MetaStrategyAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) basicWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) @@ -156,7 +156,7 @@ describe('MetaStrategyAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) @@ -218,7 +218,7 @@ describe('MetaStrategyAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) metaMetaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) diff --git a/test/multicall-router.ts b/test/multicall-router.ts index 7c2ec0aa..d4a9c359 100644 --- a/test/multicall-router.ts +++ b/test/multicall-router.ts @@ -52,7 +52,7 @@ describe('MulticallRouter', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, strategy: Contract, strategyItems: StrategyItem[], @@ -68,7 +68,7 @@ describe('MulticallRouter', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) multicallRouter = await deployMulticallRouter(accounts[0], controller) @@ -129,7 +129,7 @@ describe('MulticallRouter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) diff --git a/test/reentrancy.ts b/test/reentrancy.ts index 6657e889..d9073c5c 100644 --- a/test/reentrancy.ts +++ b/test/reentrancy.ts @@ -35,7 +35,7 @@ describe('Reentrancy ', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, strategy: Contract, strategyItems: StrategyItem[], @@ -50,7 +50,7 @@ describe('Reentrancy ', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) multicallRouter = await deployMulticallRouter(accounts[0], controller) @@ -98,7 +98,7 @@ describe('Reentrancy ', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) diff --git a/test/social-strategy.ts b/test/social-strategy.ts index 07855889..44bee980 100644 --- a/test/social-strategy.ts +++ b/test/social-strategy.ts @@ -43,7 +43,7 @@ describe('StrategyController - Social', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, strategy: Contract, strategyItems: StrategyItem[], @@ -59,10 +59,10 @@ describe('StrategyController - Social', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) - router = await deployLoopRouter(accounts[0], controller, library) + router = await deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) }) @@ -89,7 +89,7 @@ describe('StrategyController - Social', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/strategy-admin.ts b/test/strategy-admin.ts index 28f7880b..cafe5955 100644 --- a/test/strategy-admin.ts +++ b/test/strategy-admin.ts @@ -47,13 +47,13 @@ describe('StrategyProxyAdmin', function () { controller = platform.controller strategyFactory = platform.strategyFactory whitelist = platform.administration.whitelist - const library = platform.library + const controllerLibrary = platform.controllerLibrary const strategyAdminAddress = await strategyFactory.admin() const StrategyAdmin = await getContractFactory('StrategyProxyAdmin') strategyAdmin = await StrategyAdmin.attach(strategyAdminAddress) adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, library) + router = await deployLoopRouter(accounts[10], controller, controllerLibrary) await whitelist.connect(accounts[10]).approve(router.address) }) diff --git a/test/strategy-factory.ts b/test/strategy-factory.ts index 6e4864ac..4e0f0754 100644 --- a/test/strategy-factory.ts +++ b/test/strategy-factory.ts @@ -36,7 +36,7 @@ describe('StrategyProxyFactory', function () { newOracle: Contract, newWhitelist: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, newRouter: Contract, strategy: Contract, @@ -53,10 +53,10 @@ describe('StrategyProxyFactory', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, library) + router = await deployLoopRouter(accounts[10], controller, controllerLibrary) await whitelist.connect(accounts[10]).approve(router.address) }) @@ -70,7 +70,7 @@ describe('StrategyProxyFactory', function () { newFactory = platform.strategyFactory newOracle = platform.oracles.ensoOracle newWhitelist = platform.administration.whitelist - newRouter = await deployLoopRouter(accounts[10], controller, library) + newRouter = await deployLoopRouter(accounts[10], controller, controllerLibrary) await newWhitelist.connect(accounts[10]).approve(adapter.address) await newWhitelist.connect(accounts[10]).approve(newRouter.address) newImplementationAddress = await newFactory.implementation() diff --git a/test/strategy-token.ts b/test/strategy-token.ts index 87095d81..ab397317 100644 --- a/test/strategy-token.ts +++ b/test/strategy-token.ts @@ -33,7 +33,7 @@ describe('StrategyToken', function () { whitelist: Contract, router: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, strategy: Contract, strategyItems: StrategyItem[], @@ -50,10 +50,10 @@ describe('StrategyToken', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, library) + router = await deployLoopRouter(accounts[10], controller, controllerLibrary) await whitelist.connect(accounts[10]).approve(router.address) const Strategy = await platform.getStrategyContractFactory() const strategyImplementation = await Strategy.connect(accounts[10]).deploy( diff --git a/test/synthetix-adapter.ts b/test/synthetix-adapter.ts index 30b5861a..d89ee538 100644 --- a/test/synthetix-adapter.ts +++ b/test/synthetix-adapter.ts @@ -41,7 +41,7 @@ describe('SynthetixAdapter', function () { strategyFactory: Contract, controller: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, uniswapAdapter: Contract, compoundAdapter: Contract, curveAdapter: Contract, @@ -80,7 +80,7 @@ describe('SynthetixAdapter', function () { strategyFactory = platform.strategyFactory controller = platform.controller oracle = platform.oracles.ensoOracle - library = platform.library + controllerLibrary = platform.controllerLibrary const synthetixResolver = new Contract( '0x823bE81bbF96BEc0e25CA13170F5AaCb5B79ba83', @@ -90,7 +90,12 @@ describe('SynthetixAdapter', function () { const curveAddressProvider = new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], accounts[0]) const whitelist = platform.administration.whitelist - router = await deployFullRouter(accounts[10], new Contract(AddressZero, [], accounts[0]), controller, library) + router = await deployFullRouter( + accounts[10], + new Contract(AddressZero, [], accounts[0]), + controller, + controllerLibrary + ) await whitelist.connect(accounts[10]).approve(router.address) uniswapAdapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(uniswapAdapter.address) @@ -210,7 +215,7 @@ describe('SynthetixAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/token-registry.ts b/test/token-registry.ts index e0ac2c05..1612d7db 100644 --- a/test/token-registry.ts +++ b/test/token-registry.ts @@ -36,7 +36,7 @@ describe('TokenRegistry', function () { this.controller = platform.controller this.oracle = platform.oracles.ensoOracle this.whitelist = platform.administration.whitelist - this.library = platform.library + this.controllerLibrary = platform.controllerLibrary this.tokenRegistry = platform.oracles.registries.tokenRegistry const { curveDepositZapRegistry, chainlinkRegistry } = platform.oracles.registries @@ -48,7 +48,7 @@ describe('TokenRegistry', function () { curveDepositZapRegistry ) - this.router = await deployLoopRouter(this.accounts[0], this.controller, this.library) + this.router = await deployLoopRouter(this.accounts[0], this.controller, this.controllerLibrary) await this.whitelist.connect(this.accounts[0]).approve(this.router.address) this.uniswapAdapter = await deployUniswapV2Adapter(this.accounts[0], this.uniswapFactory, this.weth) await this.whitelist.connect(this.accounts[0]).approve(this.uniswapAdapter.address) @@ -93,7 +93,7 @@ describe('TokenRegistry', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: this.library.address, + ControllerLibrary: this.controllerLibrary.address, }, }) this.wrapper = await LibraryWrapper.deploy(this.oracle.address, strategyAddress) diff --git a/test/uniswap-v3-adapter.ts b/test/uniswap-v3-adapter.ts index f6c9ad23..4d3bd652 100644 --- a/test/uniswap-v3-adapter.ts +++ b/test/uniswap-v3-adapter.ts @@ -14,7 +14,7 @@ import { createLink, linkBytecode } from '../lib/link' import StrategyController from '../artifacts/contracts/StrategyController.sol/StrategyController.json' import ControllerLibrary from '../artifacts/contracts/libraries/ControllerLibrary.sol/ControllerLibrary.json' -import StrategyLibrary from '../artifacts/contracts/libraries/StrategyLibrary.sol/StrategyLibrary.json' +import ControllerLibrary from '../artifacts/contracts/libraries/ControllerLibrary.sol/ControllerLibrary.json' import SwapRouter from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json' import Quoter from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json' @@ -28,7 +28,7 @@ let tokens: Contract[], strategyFactory: Contract, controller: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, uniswapOracle: Contract, adapter: Contract, router: Contract, @@ -118,9 +118,9 @@ describe('UniswapV3Adapter', function () { const whitelist = await Whitelist.connect(owner).deploy() await whitelist.deployed() - const StrategyLibraryFactory = await getContractFactory('StrategyLibrary') - library = await StrategyLibraryFactory.connect(owner).deploy() - await library.deployed() + const ControllerLibraryFactory = await getContractFactory('ControllerLibrary') + controllerLibrary = await ControllerLibraryFactory.connect(owner).deploy() + await controllerLibrary.deployed() const PlatformProxyAdmin = await getContractFactory('PlatformProxyAdmin') const platformProxyAdmin = await PlatformProxyAdmin.connect(owner).deploy() @@ -128,9 +128,9 @@ describe('UniswapV3Adapter', function () { const controllerAddress = await platformProxyAdmin.controller() const factoryAddress = await platformProxyAdmin.factory() - const strategyLibrary = await waffle.deployContract(accounts[0], StrategyLibrary, []) + const strategyLibrary = await waffle.deployContract(accounts[0], ControllerLibrary, []) await strategyLibrary.deployed() - const strategyLibraryLink = createLink(StrategyLibrary, strategyLibrary.address) + const strategyLibraryLink = createLink(ControllerLibrary, strategyLibrary.address) const controllerLibrary = await waffle.deployContract( accounts[0], @@ -183,7 +183,7 @@ describe('UniswapV3Adapter', function () { adapter = await deployUniswapV3Adapter(owner, uniswapRegistry, uniswapRouter, weth) await whitelist.connect(owner).approve(adapter.address) - router = await deployLoopRouter(accounts[0], controller, library) + router = await deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(owner).approve(router.address) uniswapQuoter = await deployContract(trader, Quoter, [uniswapV3Factory.address, weth.address]) @@ -231,7 +231,7 @@ describe('UniswapV3Adapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/weird-erc20s.ts b/test/weird-erc20s.ts index 6ed93cda..20795c69 100644 --- a/test/weird-erc20s.ts +++ b/test/weird-erc20s.ts @@ -66,7 +66,7 @@ describe('Weird ERC20s', function () { controller: Contract, oracle: Contract, whitelist: Contract, - library: Contract, + controllerLibrary: Contract, router: Contract, adapter: Contract, strategy: Contract, @@ -124,10 +124,10 @@ describe('Weird ERC20s', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, library) + router = await deployLoopRouter(accounts[10], controller, controllerLibrary) await whitelist.connect(accounts[10]).approve(router.address) // remove weth from weird token list @@ -157,7 +157,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -202,7 +202,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -246,7 +246,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -290,7 +290,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -334,7 +334,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -378,7 +378,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) @@ -422,7 +422,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/x-fees.ts b/test/x-fees.ts index 0320f7b2..409adbe4 100644 --- a/test/x-fees.ts +++ b/test/x-fees.ts @@ -38,7 +38,7 @@ describe('StrategyToken Fees', function () { whitelist: Contract, router: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, adapter: Contract, strategy: Contract, wrapper: Contract, @@ -66,10 +66,10 @@ describe('StrategyToken Fees', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - library = platform.library + controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(owner, uniswapFactory, weth) await whitelist.connect(owner).approve(adapter.address) - router = await deployLoopRouter(owner, controller, library) + router = await deployLoopRouter(owner, controller, controllerLibrary) await whitelist.connect(owner).approve(router.address) }) @@ -114,7 +114,7 @@ describe('StrategyToken Fees', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) diff --git a/test/yearn-adapter.ts b/test/yearn-adapter.ts index 116db042..d035914a 100644 --- a/test/yearn-adapter.ts +++ b/test/yearn-adapter.ts @@ -37,7 +37,7 @@ describe('YEarnV2Adapter', function () { strategyFactory: Contract, controller: Contract, oracle: Contract, - library: Contract, + controllerLibrary: Contract, uniswapV2Adapter: Contract, uniswapV3Adapter: Contract, curveAdapter: Contract, @@ -60,7 +60,7 @@ describe('YEarnV2Adapter', function () { controller = platform.controller strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle - library = platform.library + controllerLibrary = platform.controllerLibrary const { tokenRegistry, curveDepositZapRegistry, chainlinkRegistry, uniswapV3Registry } = platform.oracles.registries @@ -74,7 +74,7 @@ describe('YEarnV2Adapter', function () { const addressProvider = new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], accounts[0]) const whitelist = platform.administration.whitelist - router = await deployLoopRouter(accounts[0], controller, library) + router = await deployLoopRouter(accounts[0], controller, controllerLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapV2Adapter = await deployUniswapV2Adapter(accounts[0], uniswapV2Factory, weth) await whitelist.connect(accounts[0]).approve(uniswapV2Adapter.address) @@ -140,7 +140,7 @@ describe('YEarnV2Adapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { - StrategyLibrary: library.address, + ControllerLibrary: controllerLibrary.address, }, }) wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) From e303b04a0e61915d808fbd754a90f2359984e734 Mon Sep 17 00:00:00 2001 From: George Carder Date: Thu, 21 Jul 2022 16:24:01 -0700 Subject: [PATCH 06/17] update live-upgrades test, passes --- contracts/Strategy.sol | 18 +- contracts/StrategyController.sol | 8 - contracts/helpers/Timelocks.sol | 6 +- .../oracles/estimators/EmergencyEstimator.sol | 22 ++- lib/enso.ts | 165 ++++++++++++------ lib/mainnet.ts | 106 +++++------ test/live-upgrades.ts | 3 +- 7 files changed, 199 insertions(+), 129 deletions(-) diff --git a/contracts/Strategy.sol b/contracts/Strategy.sol index 10b28db3..fcc9decf 100644 --- a/contracts/Strategy.sol +++ b/contracts/Strategy.sol @@ -168,20 +168,24 @@ contract Strategy is IStrategy, IStrategyManagement, StrategyTokenFees, Initiali _tempRouter = router; } - function updateTimelock(bytes32 identifier, uint256 delay) external override { + function updateTimelock(bytes4 selector, uint256 delay) external { _onlyManager(); - _startTimelock(this.updateTimelock.selector, abi.encode(identifier, delay)); + _startTimelock( + keccak256(abi.encode(this.updateTimelock.selector)), // identifier + abi.encode(keccak256(abi.encode(selector)), delay)); // payload emit UpdateTimelock(delay, false); } - function finalizeTimelock() external override { + function finalizeTimelock() external { bytes32 key = keccak256(abi.encode(this.updateTimelock.selector)); if (!_timelockIsReady(key)) { TimelockData memory td = _timelockData(key); _require(td.delay == 0, uint256(0xb3e5dea2190e00) /* error_macro_for("finalizeTimelock: timelock is not ready.") */); } - (bytes4 selector, uint256 delay) = abi.decode(_getTimelockValue(key), (bytes4, uint256)); - _setTimelock(selector, delay); + bytes memory value = _getTimelockValue(key); + require(value.length != 0, "timelock never started."); + (bytes32 identifier, uint256 delay) = abi.decode(value, (bytes32, uint256)); + _setTimelock(identifier, delay); _resetTimelock(key); emit UpdateTimelock(delay, true); } @@ -357,7 +361,9 @@ contract Strategy is IStrategy, IStrategyManagement, StrategyTokenFees, Initiali */ function updateTradeData(address item, TradeData memory data) external override { _onlyManager(); - _startTimelock(this.updateTradeData.selector, abi.encode(item, data)); + _startTimelock( + keccak256(abi.encode(this.updateTradeData.selector)), // identifier + abi.encode(item, data)); // payload emit UpdateTradeData(item, false); } diff --git a/contracts/StrategyController.sol b/contracts/StrategyController.sol index e617674b..71acb298 100644 --- a/contracts/StrategyController.sol +++ b/contracts/StrategyController.sol @@ -372,14 +372,6 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _removeStrategyLock(strategy); } - function updateTimelock(bytes32 identifier, uint256 delay) external override { - revert("timelock update not supported."); - } - - function finalizeTimelock() external override { - revert("timelock update not supported"); - } - function verifyStructure(address strategy, StrategyItem[] memory newItems) public view diff --git a/contracts/helpers/Timelocks.sol b/contracts/helpers/Timelocks.sol index 71058bb5..70be4d8e 100644 --- a/contracts/helpers/Timelocks.sol +++ b/contracts/helpers/Timelocks.sol @@ -12,8 +12,10 @@ abstract contract Timelocks is StrategyTypes { bytes constant public UNSET_VALUE = abi.encode(keccak256("Timelocks: unset value.")); // updgradable implementations would benefit from the ability to set new timelocks. - function updateTimelock(bytes32 identifier, uint256 delay) external virtual; - function finalizeTimelock() external virtual; + // not mandatory so suppressing "virtual". See EmergencyEstimator and StrategyController + // for an example and non-example + //function updateTimelock(bytes32 identifier, uint256 delay) external virtual; + //function finalizeTimelock() external virtual; // delay value is not validated but is assumed to be sensible diff --git a/contracts/oracles/estimators/EmergencyEstimator.sol b/contracts/oracles/estimators/EmergencyEstimator.sol index f4750174..87bcde5b 100644 --- a/contracts/oracles/estimators/EmergencyEstimator.sol +++ b/contracts/oracles/estimators/EmergencyEstimator.sol @@ -17,15 +17,19 @@ contract EmergencyEstimator is IEstimator, Ownable, Timelocks { event EstimateSet(address token, int256 amount, bool finalized); constructor() public { - _setTimelock(this.updateEstimate.selector, 5 minutes); + _setTimelock( + keccak256(abi.encode(this.updateEstimate.selector)), // identifier + 5 minutes); } - function updateTimelock(bytes32 identifier, uint256 delay) external override onlyOwner { - _startTimelock(this.updateTimelock.selector, abi.encode(identifier, delay)); + function updateTimelock(bytes32 identifier, uint256 delay) external onlyOwner { + _startTimelock( + keccak256(abi.encode(this.updateTimelock.selector)), // identifier + abi.encode(identifier, delay)); // payload emit UpdateTimelock(delay, false); } - function finalizeTimelock() external override { + function finalizeTimelock() external { bytes32 key = keccak256(abi.encode(this.updateTimelock.selector)); if (!_timelockIsReady(key)) { TimelockData memory td = _timelockData(key); @@ -42,14 +46,16 @@ contract EmergencyEstimator is IEstimator, Ownable, Timelocks { } function updateEstimate(address token, int256 amount) external onlyOwner { - _startTimelock(this.updateEstimate.selector, abi.encode(token, amount)); + _startTimelock( + keccak256(abi.encode(this.updateEstimate.selector)), // identifier + abi.encode(token, amount)); // payload emit EstimateSet(token, amount, false); } function finalizeSetEstimate() external { - require(_timelockIsReady(this.updateEstimate.selector), "finalizeSetEstimate: timelock not ready."); - (address token, int256 amount) = abi.decode(_getTimelockValue(this.updateEstimate.selector), (address, int256)); - _resetTimelock(this.updateEstimate.selector); + require(_timelockIsReady(keccak256(abi.encode(this.updateEstimate.selector))), "finalizeSetEstimate: timelock not ready."); + (address token, int256 amount) = abi.decode(_getTimelockValue(keccak256(abi.encode(this.updateEstimate.selector))), (address, int256)); + _resetTimelock(keccak256(abi.encode(this.updateEstimate.selector))); estimates[token] = amount; emit EstimateSet(token, amount, true); } diff --git a/lib/enso.ts b/lib/enso.ts index 05612b21..16dc6609 100644 --- a/lib/enso.ts +++ b/lib/enso.ts @@ -1,4 +1,4 @@ -import { ethers, waffle } from "hardhat"; +import { ethers, waffle } from 'hardhat' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { BigNumber, Contract } from 'ethers' import BalancerFactory from '../artifacts/contracts/test/Balancer.sol/Balancer.json' @@ -31,13 +31,13 @@ import { deployLoopRouter, deployMulticallRouter, deployBatchDepositRouter, - Platform + Platform, } from './deploy' import { MAINNET_ADDRESSES, ESTIMATOR_CATEGORY } from './constants' const { AddressZero, WeiPerEther } = ethers.constants -const NULL_CONTRACT = new Contract(AddressZero, [], ethers.provider) +const NULL_CONTRACT = new Contract(AddressZero, [], ethers.provider) export const wethPerToken = (numTokens: number) => BigNumber.from(WeiPerEther).mul(100 * (numTokens - 1)) @@ -149,35 +149,19 @@ export class EnsoBuilder { let registry = {} as Contract switch (this.network) { case Networks.LocalTestnet: - [factory, registry] = await deployBalancer(this.signer, this.tokens) + ;[factory, registry] = await deployBalancer(this.signer, this.tokens) balancer = new Balancer(factory, registry) break case Networks.Mainnet: - factory = new Contract( - MAINNET_ADDRESSES.BALANCER_FACTORY, - BalancerFactory.abi, - this.signer - ) - registry = new Contract( - MAINNET_ADDRESSES.BALANCER_REGISTRY, - BalancerRegistry.abi, - this.signer - ) + factory = new Contract(MAINNET_ADDRESSES.BALANCER_FACTORY, BalancerFactory.abi, this.signer) + registry = new Contract(MAINNET_ADDRESSES.BALANCER_REGISTRY, BalancerRegistry.abi, this.signer) balancer = new Balancer(factory, registry) break case Networks.ExternalTestnet: throw Error('External testnet not implemented yet') default: - factory = new Contract( - MAINNET_ADDRESSES.BALANCER_FACTORY, - BalancerFactory.abi, - this.signer - ) - registry = new Contract( - MAINNET_ADDRESSES.BALANCER_REGISTRY, - BalancerRegistry.abi, - this.signer - ) + factory = new Contract(MAINNET_ADDRESSES.BALANCER_FACTORY, BalancerFactory.abi, this.signer) + registry = new Contract(MAINNET_ADDRESSES.BALANCER_REGISTRY, BalancerRegistry.abi, this.signer) balancer = new Balancer(factory, registry) break } @@ -204,9 +188,12 @@ export class EnsoBuilder { case Networks.LocalTestnet: this.tokens = await deployTokens(this.signer, this.defaults.numTokens, this.defaults.wethSupply) if (this.tokens === undefined) throw Error('Failed to deploy erc20 tokens') - uniswapV2Factory = await deployUniswapV2(this.signer, this.tokens); - [uniswapV3Factory, ] = await deployUniswapV3(this.signer, this.tokens) - uniswapV3Router = await waffle.deployContract(this.signer, UniswapV3Router, [uniswapV3Factory.address, this.tokens[0].address]) + uniswapV2Factory = await deployUniswapV2(this.signer, this.tokens) + ;[uniswapV3Factory] = await deployUniswapV3(this.signer, this.tokens) + uniswapV3Router = await waffle.deployContract(this.signer, UniswapV3Router, [ + uniswapV3Factory.address, + this.tokens[0].address, + ]) break case Networks.Mainnet: this.tokens[0] = new Contract(MAINNET_ADDRESSES.WETH, WETH9.abi, this.signer) @@ -248,8 +235,8 @@ export class EnsoBuilder { this.addRouter('batch') } this.routers = await Promise.all( - this.routers.map(async r => { - await r.deploy(this.signer, ensoPlatform.controller, ensoPlatform.library) + this.routers.map(async (r) => { + await r.deploy(this.signer, ensoPlatform.controller, ensoPlatform.strategyLibrary) await ensoPlatform.administration.whitelist.connect(this.signer).approve(r.contract?.address) return r }) @@ -269,45 +256,86 @@ export class EnsoBuilder { } // Deploy adapters if (this.adapters?.aaveV2 !== undefined) { - await this.adapters.aaveV2.deploy(this.signer, ensoPlatform.administration.whitelist, [new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], this.signer), ensoPlatform.controller, weth, ensoPlatform.oracles.registries.tokenRegistry, ESTIMATOR_CATEGORY.AAVE_V2]) + await this.adapters.aaveV2.deploy(this.signer, ensoPlatform.administration.whitelist, [ + new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], this.signer), + ensoPlatform.controller, + weth, + ensoPlatform.oracles.registries.tokenRegistry, + ESTIMATOR_CATEGORY.AAVE_V2, + ]) } if (this.adapters?.aaveV2Debt !== undefined) { - await this.adapters.aaveV2Debt.deploy(this.signer, ensoPlatform.administration.whitelist, [new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], this.signer), weth]) + await this.adapters.aaveV2Debt.deploy(this.signer, ensoPlatform.administration.whitelist, [ + new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], this.signer), + weth, + ]) } if (this.adapters?.balancer !== undefined) { balancer = await this.deployBalancer() - await this.adapters.balancer.deploy(this.signer, ensoPlatform.administration.whitelist, [balancer.registry, weth]) + await this.adapters.balancer.deploy(this.signer, ensoPlatform.administration.whitelist, [ + balancer.registry, + weth, + ]) } if (this.adapters?.compound !== undefined) { - await this.adapters.compound.deploy(this.signer, ensoPlatform.administration.whitelist, [new Contract(MAINNET_ADDRESSES.COMPOUND_COMPTROLLER, [], this.signer), weth, ensoPlatform.oracles.registries.tokenRegistry, ESTIMATOR_CATEGORY.COMPOUND]) + await this.adapters.compound.deploy(this.signer, ensoPlatform.administration.whitelist, [ + new Contract(MAINNET_ADDRESSES.COMPOUND_COMPTROLLER, [], this.signer), + weth, + ensoPlatform.oracles.registries.tokenRegistry, + ESTIMATOR_CATEGORY.COMPOUND, + ]) } if (this.adapters?.curve !== undefined) { - await this.adapters.curve.deploy(this.signer, ensoPlatform.administration.whitelist, [new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], this.signer), weth]) + await this.adapters.curve.deploy(this.signer, ensoPlatform.administration.whitelist, [ + new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], this.signer), + weth, + ]) } if (this.adapters?.curveLP !== undefined) { await this.adapters.curveLP.deploy(this.signer, ensoPlatform.administration.whitelist, [ new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], this.signer), ensoPlatform.oracles.registries.curveDepositZapRegistry, - weth + weth, ]) } if (this.adapters?.curveGauge !== undefined) { - await this.adapters.curveGauge.deploy(this.signer, ensoPlatform.administration.whitelist, [weth, ensoPlatform.oracles.registries.tokenRegistry, ESTIMATOR_CATEGORY.CURVE_GAUGE]) + await this.adapters.curveGauge.deploy(this.signer, ensoPlatform.administration.whitelist, [ + weth, + ensoPlatform.oracles.registries.tokenRegistry, + ESTIMATOR_CATEGORY.CURVE_GAUGE, + ]) } if (this.adapters?.synthetix !== undefined) { - await this.adapters.synthetix.deploy(this.signer, ensoPlatform.administration.whitelist, [new Contract(MAINNET_ADDRESSES.SYNTHETIX_ADDRESS_PROVIDER, [], this.signer), weth]) + await this.adapters.synthetix.deploy(this.signer, ensoPlatform.administration.whitelist, [ + new Contract(MAINNET_ADDRESSES.SYNTHETIX_ADDRESS_PROVIDER, [], this.signer), + weth, + ]) } if (this.adapters?.uniswap !== undefined) { - await this.adapters.uniswap.deploy(this.signer, ensoPlatform.administration.whitelist, [uniswapV2Factory, weth]) + await this.adapters.uniswap.deploy(this.signer, ensoPlatform.administration.whitelist, [ + uniswapV2Factory, + weth, + ]) } if (this.adapters?.uniswapV2 !== undefined) { - await this.adapters.uniswapV2.deploy(this.signer, ensoPlatform.administration.whitelist, [uniswapV2Factory, weth]) + await this.adapters.uniswapV2.deploy(this.signer, ensoPlatform.administration.whitelist, [ + uniswapV2Factory, + weth, + ]) } if (this.adapters?.uniswapV3 !== undefined) { - await this.adapters.uniswapV3.deploy(this.signer, ensoPlatform.administration.whitelist, [ensoPlatform.oracles.registries.uniswapV3Registry, uniswapV3Router, weth]) + await this.adapters.uniswapV3.deploy(this.signer, ensoPlatform.administration.whitelist, [ + ensoPlatform.oracles.registries.uniswapV3Registry, + uniswapV3Router, + weth, + ]) } if (this.adapters?.yearnV2 !== undefined) { - await this.adapters.yearnV2.deploy(this.signer, ensoPlatform.administration.whitelist, [weth, ensoPlatform.oracles.registries.tokenRegistry, ESTIMATOR_CATEGORY.YEARN_V2]) + await this.adapters.yearnV2.deploy(this.signer, ensoPlatform.administration.whitelist, [ + weth, + ensoPlatform.oracles.registries.tokenRegistry, + ESTIMATOR_CATEGORY.YEARN_V2, + ]) } // Goes last since it depends on other adapters if (this.adapters?.leverage !== undefined) { @@ -317,12 +345,16 @@ export class EnsoBuilder { this.adapters?.aaveV2Debt.contract || NULL_CONTRACT, new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], this.signer), usdc, - weth + weth, ]) } - const fullRouterIndex = this.routers.findIndex(router => router.type == Routers.Full) + const fullRouterIndex = this.routers.findIndex((router) => router.type == Routers.Full) if (this.adapters?.metastrategy !== undefined && fullRouterIndex > -1) { - await this.adapters.metastrategy.deploy(this.signer, ensoPlatform.administration.whitelist, [ensoPlatform.controller, this.routers[fullRouterIndex].contract || NULL_CONTRACT, weth]) + await this.adapters.metastrategy.deploy(this.signer, ensoPlatform.administration.whitelist, [ + ensoPlatform.controller, + this.routers[fullRouterIndex].contract || NULL_CONTRACT, + weth, + ]) } // Safety check @@ -409,7 +441,7 @@ export enum Adapters { Uniswap = 'uniswap', UniswapV2 = 'uniswapv2', UniswapV3 = 'uniswapv3', - YEarnV2 = 'yearnv2' + YEarnV2 = 'yearnv2', } export class Adapter { @@ -418,7 +450,7 @@ export class Adapter { constructor(adapterType: string) { const isAdapter = Object.values(Adapters).findIndex((v: string) => v === adapterType.toLowerCase()) !== -1 if (isAdapter) { - this.type = adapterType.toLowerCase() + this.type = adapterType.toLowerCase() } else { throw Error('Invalid adapter selected!') } @@ -430,16 +462,28 @@ export class Adapter { this.contract = await deployAaveV2DebtAdapter(signer, parameters[0], parameters[1]) } else if (this.type === Adapters.AaveV2) { if (parameters.length == 5) - this.contract = await deployAaveV2Adapter(signer, parameters[0], parameters[1], parameters[2], parameters[3], parameters[4]) + this.contract = await deployAaveV2Adapter( + signer, + parameters[0], + parameters[1], + parameters[2], + parameters[3], + parameters[4] + ) } else if (this.type === Adapters.Balancer) { if (parameters.length == 2) this.contract = await deployBalancerAdapter(signer, parameters[0], parameters[1]) } else if (this.type === Adapters.Compound) { if (parameters.length == 4) - this.contract = await deployCompoundAdapter(signer, parameters[0], parameters[1], parameters[2], parameters[3]) + this.contract = await deployCompoundAdapter( + signer, + parameters[0], + parameters[1], + parameters[2], + parameters[3] + ) } else if (this.type === Adapters.Curve) { - if (parameters.length == 2) - this.contract = await deployCurveAdapter(signer, parameters[0], parameters[1]) + if (parameters.length == 2) this.contract = await deployCurveAdapter(signer, parameters[0], parameters[1]) } else if (this.type === Adapters.CurveLP) { if (parameters.length == 3) this.contract = await deployCurveLPAdapter(signer, parameters[0], parameters[1], parameters[2]) @@ -448,11 +492,19 @@ export class Adapter { this.contract = await deployCurveGaugeAdapter(signer, parameters[0], parameters[1], parameters[2]) } else if (this.type === Adapters.Leverage) { if (parameters.length == 6) - this.contract = await deployLeverage2XAdapter(signer, parameters[0], parameters[1], parameters[2], parameters[3], parameters[4], parameters[5]) + this.contract = await deployLeverage2XAdapter( + signer, + parameters[0], + parameters[1], + parameters[2], + parameters[3], + parameters[4], + parameters[5] + ) } else if (this.type === Adapters.Synthetix) { if (parameters.length == 2) this.contract = await deploySynthetixAdapter(signer, parameters[0], parameters[1]) - } else if (this.type === Adapters.MetaStrategy){ + } else if (this.type === Adapters.MetaStrategy) { if (parameters.length == 3) this.contract = await deployMetaStrategyAdapter(signer, parameters[0], parameters[1], parameters[2]) } else if (this.type === Adapters.Uniswap) { @@ -476,7 +528,7 @@ export enum Routers { Multicall, Loop, Full, - Batch + Batch, } // TODO: implement encoding for each Router (chain calldata for each type of router MulticallRouter is IRouter, LoopRouter is IRouter etc..) @@ -508,7 +560,12 @@ export class Router { if (this.type == Routers.Multicall) { this.contract = await deployMulticallRouter(signer, controller) } else if (this.type == Routers.Full) { - this.contract = await deployFullRouter(signer, new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], ethers.provider), controller, library) + this.contract = await deployFullRouter( + signer, + new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], ethers.provider), + controller, + library + ) } else if (this.type == Routers.Batch) { this.contract = await deployBatchDepositRouter(signer, controller, library) } else { diff --git a/lib/mainnet.ts b/lib/mainnet.ts index 1f4c1d62..02a9d0ea 100644 --- a/lib/mainnet.ts +++ b/lib/mainnet.ts @@ -2,11 +2,16 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { Contract } from 'ethers' import { Platform, Administration, Oracles } from './deploy' import deployments from '../deployments.json' +import hre from 'hardhat' +const { ethers } = hre +const { constants } = ethers +const { AddressZero } = constants import PlatformProxyAdmin from '../artifacts/contracts/PlatformProxyAdmin.sol/PlatformProxyAdmin.json' import StrategyController from '../artifacts/contracts/StrategyController.sol/StrategyController.json' import StrategyProxyFactory from '../artifacts/contracts/StrategyProxyFactory.sol/StrategyProxyFactory.json' import StrategyLibrary from '../artifacts/contracts/libraries/StrategyLibrary.sol/StrategyLibrary.json' +import ControllerLibrary from '../artifacts/contracts/libraries/ControllerLibrary.sol/ControllerLibrary.json' import EnsoOracle from '../artifacts/contracts/oracles/EnsoOracle.sol/EnsoOracle.json' import UniswapV3Oracle from '../artifacts/contracts/oracles/protocols/UniswapV3Oracle.sol/UniswapV3Oracle.json' import ChainlinkOracle from '../artifacts/contracts/oracles/protocols/ChainlinkOracle.sol/ChainlinkOracle.json' @@ -55,7 +60,7 @@ export class LiveEnvironment { platform: Platform, adapters: LiveAdapters, routers: LiveRouters, - estimators: Estimators, + estimators: Estimators ) { this.signer = signer this.platform = platform @@ -65,10 +70,9 @@ export class LiveEnvironment { } } - export type LiveAdapters = { aaveV2: Contract - aaveV2Debt: Contract + aaveV2Debt: Contract balancer: Contract compound: Contract curve: Contract @@ -97,14 +101,14 @@ export enum AdapterTypes { Synthetix = 'synthetix', UniswapV2 = 'uniswapv2', UniswapV3 = 'uniswapv3', - YEarnV2 = 'yearnv2' + YEarnV2 = 'yearnv2', } export enum RouterTypes { Multicall = 'multicall', Loop = 'loop', Full = 'full', - Batch = 'batch' + Batch = 'batch', } export type LiveRouters = { @@ -128,39 +132,41 @@ export type Estimators = { } export function liveEstimators(signer: SignerWithAddress) { - if (!deployments.mainnet) throw Error("Deployment addresses not found") - const addrs = deployments.mainnet; - const defaultEstimator = new Contract(addrs.DefaultEstimator, BasicEstimator.abi, signer) - const chainlink = new Contract(addrs.ChainlinkEstimator, BasicEstimator.abi, signer) - const strategy = new Contract(addrs.StrategyEstimator, StrategyEstimator.abi, signer) - const emergency = new Contract(addrs.EmergencyEstimator, EmergencyEstimator.abi, signer) - const aaveV2 = new Contract(addrs.AaveV2Estimator, AaveV2Estimator.abi, signer) - const aaveV2Debt = new Contract(addrs.AaveV2DebtEstimator, AaveV2DebtEstimator.abi, signer) - const compound = new Contract(addrs.CompoundEstimator, CompoundEstimator.abi, signer) - const curveLP = new Contract(addrs.CurveLPEstimator, CurveLPEstimator.abi, signer) - const curveGauge = new Contract(addrs.CurveGaugeEstimator, CurveGaugeEstimator.abi, signer) - const yearnV2 = new Contract(addrs.YEarnV2Estimator, YEarnV2Estimator.abi, signer) - const estimators: Estimators = { - defaultEstimator, - chainlink, - strategy, - emergency, - aaveV2, - aaveV2Debt, - compound, - curveLP, - curveGauge, - yearnV2 - } - return estimators + if (!deployments.mainnet) throw Error('Deployment addresses not found') + const addrs = deployments.mainnet + const defaultEstimator = new Contract(addrs.DefaultEstimator, BasicEstimator.abi, signer) + const chainlink = new Contract(addrs.ChainlinkEstimator, BasicEstimator.abi, signer) + const strategy = new Contract(addrs.StrategyEstimator, StrategyEstimator.abi, signer) + const emergency = new Contract(addrs.EmergencyEstimator, EmergencyEstimator.abi, signer) + const aaveV2 = new Contract(addrs.AaveV2Estimator, AaveV2Estimator.abi, signer) + const aaveV2Debt = new Contract(addrs.AaveV2DebtEstimator, AaveV2DebtEstimator.abi, signer) + const compound = new Contract(addrs.CompoundEstimator, CompoundEstimator.abi, signer) + const curveLP = new Contract(addrs.CurveLPEstimator, CurveLPEstimator.abi, signer) + const curveGauge = new Contract(addrs.CurveGaugeEstimator, CurveGaugeEstimator.abi, signer) + const yearnV2 = new Contract(addrs.YEarnV2Estimator, YEarnV2Estimator.abi, signer) + const estimators: Estimators = { + defaultEstimator, + chainlink, + strategy, + emergency, + aaveV2, + aaveV2Debt, + compound, + curveLP, + curveGauge, + yearnV2, + } + return estimators } - - export function livePlatform(signer: SignerWithAddress): Platform { - if (!deployments.mainnet) throw Error("Deployment addresses not found") - const addrs = deployments.mainnet; - const strategyLibrary = new Contract(addrs.StrategyLibrary, StrategyLibrary.abi, signer) + if (!deployments.mainnet) throw Error('Deployment addresses not found') + const addrs = deployments.mainnet + const strategyLibrary = new Contract(addrs.StrategyLibrary, StrategyLibrary.abi, signer) + console.log('debug before') + const controllerLibrary = new Contract(AddressZero, ControllerLibrary.abi, signer) // FIXME ControllerLibrary address when deployed + console.log('debug after') + const tokenRegistry = new Contract(addrs.TokenRegistry, TokenRegistry.abi, signer) const curveDepositZapRegistry = new Contract(addrs.CurveDepositZapRegistry, CurveDepositZapRegistry.abi, signer) const uniswapV3Registry = new Contract(addrs.UniswapV3Registry, UniswapV3Registry.abi, signer) @@ -179,27 +185,27 @@ export function livePlatform(signer: SignerWithAddress): Platform { ensoOracle, protocols: { uniswapOracle, - chainlinkOracle + chainlinkOracle, }, registries: { tokenRegistry, curveDepositZapRegistry, uniswapV3Registry, - chainlinkRegistry - } + chainlinkRegistry, + }, } // Admin const administration: Administration = { whitelist, - platformProxyAdmin + platformProxyAdmin, } - return new Platform(factory, controller, oracles, administration, strategyLibrary, {}) // last param, strategyLibraries, currently not deployed on mainnet + return new Platform(factory, controller, oracles, administration, strategyLibrary, controllerLibrary, {}) // last params, strategyLibraries, controllerLibrary, currently not deployed on mainnet } export function liveAdapters(signer: SignerWithAddress): LiveAdapters { - const addrs = deployments.mainnet; + const addrs = deployments.mainnet const aaveV2 = new Contract(addrs.AaveV2Adapter, AaveV2Adapter.abi, signer) const aaveV2Debt = new Contract(addrs.AaveV2DebtAdapter, AaveV2DebtAdapter.abi, signer) const balancer = new Contract(addrs.BalancerAdapter, BalancerAdapter.abi, signer) @@ -230,14 +236,14 @@ export function liveAdapters(signer: SignerWithAddress): LiveAdapters { synthetix, uniswapV2, uniswapV3, - yearnV2 + yearnV2, } return liveAdapters } export function liveRouters(signer: SignerWithAddress): LiveRouters { - if (!deployments.mainnet) throw Error("Deployment addresses not found") - const addrs = deployments.mainnet; + if (!deployments.mainnet) throw Error('Deployment addresses not found') + const addrs = deployments.mainnet const multicall = new Contract(addrs.MulticallRouter, MulticallRouter.abi, signer) const loop = new Contract(addrs.LoopRouter, LoopRouter.abi, signer) const full = new Contract(addrs.FullRouter, FullRouter.abi, signer) @@ -246,22 +252,22 @@ export function liveRouters(signer: SignerWithAddress): LiveRouters { multicall, loop, full, - batch + batch, } return routers } export function getLiveContracts(signer: SignerWithAddress): LiveEnvironment { - const platform = livePlatform(signer); - const adapters = liveAdapters(signer); - const routers = liveRouters(signer); - const estimators = liveEstimators(signer); + const platform = livePlatform(signer) + const adapters = liveAdapters(signer) + const routers = liveRouters(signer) + const estimators = liveEstimators(signer) const liveContracts: LiveEnvironment = { signer, platform, adapters, routers, - estimators + estimators, } return liveContracts } diff --git a/test/live-upgrades.ts b/test/live-upgrades.ts index a2bcce69..00cef838 100644 --- a/test/live-upgrades.ts +++ b/test/live-upgrades.ts @@ -64,8 +64,9 @@ describe('Live Upgrades', function () { it('Should updateTradeData respecting timelock', async function () { // first manager must setup the timelock - let updateTradeDataSelector = eDPI.interface.getSighash('updateTradeData') + const updateTradeDataSelector = eDPI.interface.getSighash('updateTradeData') await eDPI.connect(manager).updateTimelock(updateTradeDataSelector, 5 * 60) + await eDPI.connect(accounts[1]).finalizeTimelock() // now updateTradeData respecting the timelock From 504f96d02ae5977e539150584d79448dd5afeaf9 Mon Sep 17 00:00:00 2001 From: George Carder Date: Fri, 22 Jul 2022 12:44:01 -0700 Subject: [PATCH 07/17] some tests updated, not all pass --- contracts/libraries/ControllerLibrary.sol | 2 +- test/aave-adapter.ts | 1079 +++++++++++---------- test/balancer-adapter.ts | 2 + test/batch-router.ts | 2 + test/compound-adapter.ts | 3 + test/curve-adapter.ts | 15 +- test/experimental-strategy.ts | 2 + test/flash-loan.ts | 2 + test/kyber-adapter.ts | 3 + test/meta-strategy-adapter.ts | 5 + test/multicall-router.ts | 6 + test/social-strategy.ts | 2 + test/strategy-controller.ts | 330 ++++--- test/uniswap-v3-adapter.ts | 14 +- test/weird-erc20s.ts | 8 + test/yearn-adapter.ts | 3 + 16 files changed, 766 insertions(+), 712 deletions(-) diff --git a/contracts/libraries/ControllerLibrary.sol b/contracts/libraries/ControllerLibrary.sol index a5e888bb..5e850b48 100644 --- a/contracts/libraries/ControllerLibrary.sol +++ b/contracts/libraries/ControllerLibrary.sol @@ -17,7 +17,7 @@ library ControllerLibrary { using SafeERC20 for IERC20; int256 private constant DIVISOR = 1000; - uint256 private constant REBALANCE_THRESHOLD_SCALAR = 2**2; // FIXME tune + uint256 private constant REBALANCE_THRESHOLD_SCALAR = 2; // FIXME tune uint256 private constant PRECISION = 10**18; uint256 private constant WITHDRAW_UPPER_BOUND = 10**17; // Upper condition for including pool's tokens as part of burn during withdraw diff --git a/test/aave-adapter.ts b/test/aave-adapter.ts index f4ca7d67..9946a82f 100644 --- a/test/aave-adapter.ts +++ b/test/aave-adapter.ts @@ -1,3 +1,4 @@ +const runAll = true import chai from 'chai' const { expect } = chai import { ethers } from 'hardhat' @@ -25,6 +26,7 @@ import { MAINNET_ADDRESSES, ESTIMATOR_CATEGORY } from '../lib/constants' import ERC20 from '@uniswap/v2-periphery/build/ERC20.json' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import UniswapV2Factory from '@uniswap/v2-core/build/UniswapV2Factory.json' +import { increaseTime } from '../lib/utils' chai.use(solidity) @@ -159,7 +161,7 @@ describe('AaveAdapter', function () { const strategyState: InitialState = { timelock: BigNumber.from(60), rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), + rebalanceSlippage: BigNumber.from(990), restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance managementFee: BigNumber.from(0), social: false, @@ -200,8 +202,9 @@ describe('AaveAdapter', function () { }) it('Should purchase a token, requiring a rebalance of strategy', async function () { + // simulates a large sway in the market over some period // Approve the user to use the adapter - const value = WeiPerEther.mul(1000) + const value = WeiPerEther.mul(2000) await weth.connect(accounts[19]).deposit({ value: value }) await weth.connect(accounts[19]).approve(uniswapAdapter.address, value) await uniswapAdapter @@ -213,6 +216,7 @@ describe('AaveAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller .connect(accounts[1]) .rebalance(strategy.address, router.address, '0x', { gasLimit: '5000000' }) @@ -222,564 +226,577 @@ describe('AaveAdapter', function () { expect(await wrapper.isBalanced()).to.equal(true) }) - it('Should purchase a token, requiring a rebalance of strategy', async function () { - // Approve the user to use the adapter - const value = await usdc.balanceOf(accounts[19].address) - await usdc.connect(accounts[19]).approve(uniswapAdapter.address, value) - await uniswapAdapter - .connect(accounts[19]) - .swap(value, 0, usdc.address, weth.address, accounts[19].address, accounts[19].address) - - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(false) - }) - - it('Should rebalance strategy', async function () { - const tx = await controller - .connect(accounts[1]) - .rebalance(strategy.address, router.address, '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) - - it('Should fail to withdrawAll: cannot withdraw debt', async function () { - const amount = BigNumber.from('10000000000000000') - expect( - await isRevertedWith( - strategy.connect(accounts[1]).withdrawAll(amount), - 'Cannot withdraw debt', - 'Strategy.sol' - ) - ).to.be.true - }) - - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) - - it('Should withdraw ETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const ethBalanceBefore = await accounts[1].getBalance() - const tx = await controller.connect(accounts[1]).withdrawETH( - strategy.address, - router.address, - amount, - '979', // note the high slippage! - '0x', - { gasLimit: '5000000' } - ) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const ethBalanceAfter = await accounts[1].getBalance() - expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) - }) - - it('Should withdraw WETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const wethBalanceBefore = await weth.balanceOf(accounts[1].address) - const tx = await controller.connect(accounts[1]).withdrawWETH( - strategy.address, - router.address, - amount, - '963', // note the high slippage! - '0x', - { gasLimit: '5000000' } - ) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const wethBalanceAfter = await weth.balanceOf(accounts[1].address) - expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) - }) - - it('Should restructure - basic', async function () { - const positions = [{ token: tokens.usdt, percentage: BigNumber.from(1000), adapters: [uniswapAdapter.address] }] - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) - }) - - it('Should finalize structure - basic', async function () { - const tx = await controller - .connect(accounts[1]) - .finalizeStructure(strategy.address, router.address, '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) - - it('Should restructure - debt positions', async function () { - const positions = [ - { - token: collateralToken, - percentage: BigNumber.from(1000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: collateralToken2, - percentage: BigNumber.from(1000), - adapters: [uniswapAdapter.address, aaveV2Adapter.address], - path: [tokens.crv], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-1000), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], //ending in weth allows for a leverage feedback loop - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [ - [ - { token: collateralToken, percentage: 500 }, - { token: collateralToken2, percentage: 500 }, - ], - ] //define what tokens you want to loop back on here - ), - }, - ] - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) - }) - - it('Should finalize structure - debt positions', async function () { - const tx = await controller - .connect(accounts[1]) - .finalizeStructure(strategy.address, router.address, '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) - - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) - - it('Should withdraw ETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const ethBalanceBefore = await accounts[1].getBalance() - const tx = await controller - .connect(accounts[1]) - .withdrawETH(strategy.address, router.address, amount, '970', '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const ethBalanceAfter = await accounts[1].getBalance() - expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) - }) - - it('Should withdraw WETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const wethBalanceBefore = await weth.balanceOf(accounts[1].address) - const tx = await controller - .connect(accounts[1]) - .withdrawWETH(strategy.address, router.address, amount, '970', '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const wethBalanceAfter = await weth.balanceOf(accounts[1].address) - expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) - }) - - it('Should deploy new strategy', async function () { - const name = 'New Strategy' - const symbol = 'NEW' - - const positions = [ - { - token: tokens.aWETH, - percentage: BigNumber.from(2000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-1000), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 500 }]] //define what tokens you want to loop back on here - ), - }, - ] - - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - const strategyState: InitialState = { - timelock: BigNumber.from(60), - rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), - restructureSlippage: BigNumber.from(995), // Restucturing from this strategy requires higher slippage tolerance - managementFee: BigNumber.from(0), - social: false, - set: false, - } - - const tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - - const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() - strategy = await Strategy.attach(strategyAddress) - - expect(await controller.initialized(strategyAddress)).to.equal(true) - - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + if (runAll) { + it('Should purchase a token, requiring a rebalance of strategy', async function () { + // Approve the user to use the adapter + const value = await usdc.balanceOf(accounts[19].address) + await usdc.connect(accounts[19]).approve(uniswapAdapter.address, value) + await uniswapAdapter + .connect(accounts[19]) + .swap(value, 0, usdc.address, weth.address, accounts[19].address, accounts[19].address) + + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(false) }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) - await wrapper.deployed() - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) - - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) - - it('Should deploy another strategy', async function () { - const name = 'Another Strategy' - const symbol = 'ANOTHER' - - const positions = [ - { - token: tokens.aWETH, - percentage: BigNumber.from(2000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-500), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), - ), - }, - { - token: tokens.debtWBTC, - percentage: BigNumber.from(-500), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.wbtc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), - ), - }, - ] - - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - const strategyState: InitialState = { - timelock: BigNumber.from(60), - rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), - restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance - managementFee: BigNumber.from(0), - social: false, - set: false, - } - - const tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) + const tx = await controller + .connect(accounts[1]) + .rebalance(strategy.address, router.address, '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() - strategy = await Strategy.attach(strategyAddress) + it('Should fail to withdrawAll: cannot withdraw debt', async function () { + const amount = BigNumber.from('10000000000000000') + expect( + await isRevertedWith( + strategy.connect(accounts[1]).withdrawAll(amount), + 'Cannot withdraw debt', + 'Strategy.sol' + ) + ).to.be.true + }) - expect(await controller.initialized(strategyAddress)).to.equal(true) + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + it('Should withdraw ETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const ethBalanceBefore = await accounts[1].getBalance() + const tx = await controller.connect(accounts[1]).withdrawETH( + strategy.address, + router.address, + amount, + '977', // note the high slippage! + '0x', + { gasLimit: '5000000' } + ) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const ethBalanceAfter = await accounts[1].getBalance() + expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) - await wrapper.deployed() - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + it('Should withdraw WETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const wethBalanceBefore = await weth.balanceOf(accounts[1].address) + const tx = await controller.connect(accounts[1]).withdrawWETH( + strategy.address, + router.address, + amount, + '960', // note the high slippage! + '0x', + { gasLimit: '5000000' } + ) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const wethBalanceAfter = await weth.balanceOf(accounts[1].address) + expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) + }) - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) + it('Should restructure - basic', async function () { + const positions = [ + { token: tokens.usdt, percentage: BigNumber.from(1000), adapters: [uniswapAdapter.address] }, + ] + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) + }) - it('Should deploy ambiguous strategy', async function () { - const name = 'Ambiguous Strategy' - const symbol = 'AMBI' + it('Should finalize structure - basic', async function () { + const tx = await controller + .connect(accounts[1]) + .finalizeStructure(strategy.address, router.address, '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - const positions = [ - { - token: collateralToken, - percentage: BigNumber.from(500), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), - }, - { - token: collateralToken2, - percentage: BigNumber.from(1500), - adapters: [uniswapAdapter.address, aaveV2Adapter.address], - path: [tokens.crv], - cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-875), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [ + it('Should restructure - debt positions', async function () { + const positions = [ + { + token: collateralToken, + percentage: BigNumber.from(1000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: collateralToken2, + percentage: BigNumber.from(1000), + adapters: [uniswapAdapter.address, aaveV2Adapter.address], + path: [tokens.crv], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-1000), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], //ending in weth allows for a leverage feedback loop + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], [ - { token: collateralToken, percentage: 250 }, - { token: collateralToken2, percentage: 500 }, - ], - ] //define what tokens you want to loop back on here - ), - }, - { - token: tokens.debtWBTC, - percentage: BigNumber.from(-125), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.wbtc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here - ), - }, - ] - - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - const strategyState: InitialState = { - timelock: BigNumber.from(60), - rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), - restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance - managementFee: BigNumber.from(0), - social: false, - set: false, - } + [ + { token: collateralToken, percentage: 500 }, + { token: collateralToken2, percentage: 500 }, + ], + ] //define what tokens you want to loop back on here + ), + }, + ] + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) + }) - const tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + it('Should finalize structure - debt positions', async function () { + const tx = await controller + .connect(accounts[1]) + .finalizeStructure(strategy.address, router.address, '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - strategy = await Strategy.attach(strategyAddress) - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + it('Should withdraw ETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const ethBalanceBefore = await accounts[1].getBalance() + // note the high slippage! + const tx = await controller + .connect(accounts[1]) + .withdrawETH(strategy.address, router.address, amount, '970', '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const ethBalanceAfter = await accounts[1].getBalance() + expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) - await wrapper.deployed() - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + it('Should withdraw WETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const wethBalanceBefore = await weth.balanceOf(accounts[1].address) + // note the high slippage! + const tx = await controller + .connect(accounts[1]) + .withdrawWETH(strategy.address, router.address, amount, '960', '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const wethBalanceAfter = await weth.balanceOf(accounts[1].address) + expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) + }) - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) + it('Should deploy new strategy', async function () { + const name = 'New Strategy' + const symbol = 'NEW' + + const positions = [ + { + token: tokens.aWETH, + percentage: BigNumber.from(2000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-1000), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 500 }]] //define what tokens you want to loop back on here + ), + }, + ] + + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + const strategyState: InitialState = { + timelock: BigNumber.from(60), + rebalanceThreshold: BigNumber.from(50), + rebalanceSlippage: BigNumber.from(997), + restructureSlippage: BigNumber.from(995), // Restucturing from this strategy requires higher slippage tolerance + managementFee: BigNumber.from(0), + social: false, + set: false, + } + + const tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { + value: WeiPerEther, + }) + const receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + + const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() + strategy = await Strategy.attach(strategyAddress) + + expect(await controller.initialized(strategyAddress)).to.equal(true) + + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, + }) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + await wrapper.deployed() - it('Should deploy debt meta strategy', async function () { - //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - let name = 'Basic Strategy' - let symbol = 'BASIC' - let positions = [ - { token: weth.address, percentage: BigNumber.from(500) }, - { token: usdc.address, percentage: BigNumber.from(500) }, - ] - const basicStrategyItems = prepareStrategy(positions, uniswapAdapter.address) + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - let tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, basicStrategyItems, STRATEGY_STATE, router.address, '0x', { - value: ethers.BigNumber.from('10000000000000000'), + it('Should deploy another strategy', async function () { + const name = 'Another Strategy' + const symbol = 'ANOTHER' + + const positions = [ + { + token: tokens.aWETH, + percentage: BigNumber.from(2000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-500), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), + ), + }, + { + token: tokens.debtWBTC, + percentage: BigNumber.from(-500), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.wbtc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), + ), + }, + ] + + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + const strategyState: InitialState = { + timelock: BigNumber.from(60), + rebalanceThreshold: BigNumber.from(50), + rebalanceSlippage: BigNumber.from(997), + restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance + managementFee: BigNumber.from(0), + social: false, + set: false, + } + + const tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { + value: WeiPerEther, + }) + const receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + + const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() + strategy = await Strategy.attach(strategyAddress) + + expect(await controller.initialized(strategyAddress)).to.equal(true) + + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, }) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + await wrapper.deployed() - let receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - - let strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() - const basicStrategy = await Strategy.attach(strategyAddress) - - let metaStrategyAdapter = await deployMetaStrategyAdapter(accounts[0], controller, router, weth) - await whitelist.connect(accounts[0]).approve(metaStrategyAdapter.address) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - name = 'Debt Meta Strategy' - symbol = 'DMETA' - let positions2 = [ - { - token: basicStrategy.address, - percentage: BigNumber.from(400), - adapters: [metaStrategyAdapter.address], - path: [], - }, - { - token: tokens.aWETH, - percentage: BigNumber.from(1200), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-600), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, weth.address], //ending in weth allows for a leverage feedback loop - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: tokens.aWETH, percentage: 500 }]] - ), - }, - ] + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - let metaStrategyItems = prepareStrategy(positions2, uniswapAdapter.address) - tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, metaStrategyItems, STRATEGY_STATE, router.address, '0x', { - value: ethers.BigNumber.from('10000000000000000'), + it('Should deploy ambiguous strategy', async function () { + const name = 'Ambiguous Strategy' + const symbol = 'AMBI' + + const positions = [ + { + token: collateralToken, + percentage: BigNumber.from(500), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), + }, + { + token: collateralToken2, + percentage: BigNumber.from(1500), + adapters: [uniswapAdapter.address, aaveV2Adapter.address], + path: [tokens.crv], + cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-875), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [ + [ + { token: collateralToken, percentage: 250 }, + { token: collateralToken2, percentage: 500 }, + ], + ] //define what tokens you want to loop back on here + ), + }, + { + token: tokens.debtWBTC, + percentage: BigNumber.from(-125), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.wbtc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here + ), + }, + ] + + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + const strategyState: InitialState = { + timelock: BigNumber.from(60), + rebalanceThreshold: BigNumber.from(50), + rebalanceSlippage: BigNumber.from(997), + restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance + managementFee: BigNumber.from(0), + social: false, + set: false, + } + + const tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { + value: WeiPerEther, + }) + const receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + + const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() + + strategy = await Strategy.attach(strategyAddress) + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, }) - receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + await wrapper.deployed() - strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - expect(await controller.initialized(strategyAddress)).to.equal(true) - - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) }) - let metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) - await metaWrapper.deployed() - //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) - expect(await metaWrapper.isBalanced()).to.equal(true) - }) + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - it('Should fail to deploy synth + debt strategy', async function () { - const name = 'SynthDebt Strategy' - const symbol = 'SD' + it('Should deploy debt meta strategy', async function () { + //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) + + let name = 'Basic Strategy' + let symbol = 'BASIC' + let positions = [ + { token: weth.address, percentage: BigNumber.from(500) }, + { token: usdc.address, percentage: BigNumber.from(500) }, + ] + const basicStrategyItems = prepareStrategy(positions, uniswapAdapter.address) + + let tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, basicStrategyItems, STRATEGY_STATE, router.address, '0x', { + value: ethers.BigNumber.from('10000000000000000'), + }) + + let receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + + let strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() + const basicStrategy = await Strategy.attach(strategyAddress) + + let metaStrategyAdapter = await deployMetaStrategyAdapter(accounts[0], controller, router, weth) + await whitelist.connect(accounts[0]).approve(metaStrategyAdapter.address) + + name = 'Debt Meta Strategy' + symbol = 'DMETA' + let positions2 = [ + { + token: basicStrategy.address, + percentage: BigNumber.from(400), + adapters: [metaStrategyAdapter.address], + path: [], + }, + { + token: tokens.aWETH, + percentage: BigNumber.from(1200), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-600), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, weth.address], //ending in weth allows for a leverage feedback loop + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: tokens.aWETH, percentage: 500 }]] + ), + }, + ] + + let metaStrategyItems = prepareStrategy(positions2, uniswapAdapter.address) + tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, metaStrategyItems, STRATEGY_STATE, router.address, '0x', { + value: ethers.BigNumber.from('10000000000000000'), + }) + receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + + strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + expect(await controller.initialized(strategyAddress)).to.equal(true) + + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, + }) + let metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) + await metaWrapper.deployed() - const positions = [ - { - token: tokens.sUSD, - percentage: BigNumber.from(0), - adapters: [uniswapAdapter.address, curveAdapter.address], - path: [tokens.usdc], - cache: '0x', - }, - { - token: tokens.sAAVE, - percentage: BigNumber.from(500), - adapters: [synthetixAdapter.address], - path: [], - cache: '0x', - }, - { - token: tokens.aWETH, - percentage: BigNumber.from(1000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-500), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: tokens.aWETH, percentage: 500 }]] //define what tokens you want to loop back on here), - ), - }, - ] + //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) + expect(await metaWrapper.isBalanced()).to.equal(true) + }) - const failItems = prepareStrategy(positions, uniswapAdapter.address) - - expect( - await isRevertedWith( - strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, failItems, STRATEGY_STATE, router.address, '0x', { - value: WeiPerEther, - }), - 'No synths and debt', - 'StrategyController.sol' - ) - ).to.be.true - }) + it('Should fail to deploy synth + debt strategy', async function () { + const name = 'SynthDebt Strategy' + const symbol = 'SD' + + const positions = [ + { + token: tokens.sUSD, + percentage: BigNumber.from(0), + adapters: [uniswapAdapter.address, curveAdapter.address], + path: [tokens.usdc], + cache: '0x', + }, + { + token: tokens.sAAVE, + percentage: BigNumber.from(500), + adapters: [synthetixAdapter.address], + path: [], + cache: '0x', + }, + { + token: tokens.aWETH, + percentage: BigNumber.from(1000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-500), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: tokens.aWETH, percentage: 500 }]] //define what tokens you want to loop back on here), + ), + }, + ] + + const failItems = prepareStrategy(positions, uniswapAdapter.address) + + expect( + await isRevertedWith( + strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, failItems, STRATEGY_STATE, router.address, '0x', { + value: WeiPerEther, + }), + 'No synths and debt', + 'StrategyController.sol' + ) + ).to.be.true + }) + } }) diff --git a/test/balancer-adapter.ts b/test/balancer-adapter.ts index eae0c1cb..f1c03c51 100644 --- a/test/balancer-adapter.ts +++ b/test/balancer-adapter.ts @@ -6,6 +6,7 @@ import * as deployer from '../lib/deploy' import { prepareStrategy, Position, StrategyItem, InitialState } from '../lib/encode' import { BigNumber, Contract, Event } from 'ethers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { increaseTime } from '../lib/utils' const { AddressZero, WeiPerEther, MaxUint256 } = constants const NUM_TOKENS = 3 @@ -155,6 +156,7 @@ describe('BalancerAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/batch-router.ts b/test/batch-router.ts index 79228666..8d0be769 100644 --- a/test/batch-router.ts +++ b/test/batch-router.ts @@ -18,6 +18,7 @@ import { import { displayBalances } from '../lib/logging' import { DEFAULT_DEPOSIT_SLIPPAGE } from '../lib/constants' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { increaseTime } from '../lib/utils' const NUM_TOKENS = 15 @@ -150,6 +151,7 @@ describe('BatchDepositRouter', function () { }) it('Should fail to rebalance: router revert', async function () { + await increaseTime(5 * 60 + 1) await expect( controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') ).to.be.revertedWith('Rebalance not supported') diff --git a/test/compound-adapter.ts b/test/compound-adapter.ts index 74dfd733..b50ad520 100644 --- a/test/compound-adapter.ts +++ b/test/compound-adapter.ts @@ -20,6 +20,7 @@ import { DEFAULT_DEPOSIT_SLIPPAGE, MAINNET_ADDRESSES, ESTIMATOR_CATEGORY, ITEM_C import ERC20 from '@uniswap/v2-periphery/build/ERC20.json' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import UniswapV2Factory from '@uniswap/v2-core/build/UniswapV2Factory.json' +import { increaseTime } from '../lib/utils' chai.use(solidity) @@ -169,6 +170,7 @@ describe('CompoundAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -189,6 +191,7 @@ describe('CompoundAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/curve-adapter.ts b/test/curve-adapter.ts index 1ff29465..77b28d29 100644 --- a/test/curve-adapter.ts +++ b/test/curve-adapter.ts @@ -26,6 +26,7 @@ import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import UniswapV2Factory from '@uniswap/v2-core/build/UniswapV2Factory.json' import UniswapV2Pair from '@uniswap/v2-core/build/UniswapV2Pair.json' import UniswapV3Factory from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json' +import { increaseTime } from '../lib/utils' chai.use(solidity) @@ -571,7 +572,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const strategyState: InitialState = { timelock: BigNumber.from(60), rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), + rebalanceSlippage: BigNumber.from(990), restructureSlippage: BigNumber.from(980), //Slippage is set low because of low-liquidity in EURS' UniV2 pool managementFee: BigNumber.from(0), social: false, @@ -605,7 +606,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { it('Should purchase a token, requiring a rebalance of strategy', async function () { // Approve the user to use the adapter - const value = WeiPerEther.mul(500) + const value = WeiPerEther.mul(800) await weth.connect(accounts[19]).deposit({ value: value }) await weth.connect(accounts[19]).approve(uniswapV2Adapter.address, value) await uniswapV2Adapter @@ -617,6 +618,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -672,6 +674,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -735,7 +738,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { it('Should purchase a token, requiring a rebalance of strategy', async function () { // Approve the user to use the adapter - const value = WeiPerEther.mul(500) + const value = WeiPerEther.mul(800) await weth.connect(accounts[19]).deposit({ value: value }) await weth.connect(accounts[19]).approve(uniswapV2Adapter.address, value) await uniswapV2Adapter @@ -747,6 +750,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -767,6 +771,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -824,7 +829,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { it('Should purchase a token, requiring a rebalance of strategy', async function () { // Approve the user to use the adapter - const value = WeiPerEther.mul(500) + const value = WeiPerEther.mul(1000) await weth.connect(accounts[19]).deposit({ value: value }) await weth.connect(accounts[19]).approve(uniswapV2Adapter.address, value) await uniswapV2Adapter @@ -836,6 +841,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -856,6 +862,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/experimental-strategy.ts b/test/experimental-strategy.ts index bf36adab..71be130a 100644 --- a/test/experimental-strategy.ts +++ b/test/experimental-strategy.ts @@ -23,6 +23,7 @@ import { MAINNET_ADDRESSES, ESTIMATOR_CATEGORY } from '../lib/constants' import ERC20 from '@uniswap/v2-periphery/build/ERC20.json' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import UniswapV2Factory from '@uniswap/v2-core/build/UniswapV2Factory.json' +import { increaseTime } from '../lib/utils' chai.use(solidity) @@ -168,6 +169,7 @@ describe('Experimental Strategy', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/flash-loan.ts b/test/flash-loan.ts index 314e5795..fd29ceca 100644 --- a/test/flash-loan.ts +++ b/test/flash-loan.ts @@ -23,6 +23,7 @@ import { Contract, BigNumber } from 'ethers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' const { constants, getContractFactory, getSigners } = ethers const { AddressZero, WeiPerEther } = constants +import { increaseTime } from '../lib/utils' const NUM_TOKENS = 4 @@ -154,6 +155,7 @@ describe('Flash Loan', function () { ) const calls = [...rebalanceCalls, ...flashLoanCalls] const data = await multicallRouter.encodeCalls(calls) + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, multicallRouter.address, data) const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/kyber-adapter.ts b/test/kyber-adapter.ts index 5a2d8cc5..3c5cfcee 100644 --- a/test/kyber-adapter.ts +++ b/test/kyber-adapter.ts @@ -22,6 +22,7 @@ import UniswapV2Factory from '@uniswap/v2-core/build/UniswapV2Factory.json' import UniswapV3Factory from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json' import IDMMFactory from '../artifacts/contracts/interfaces/kyber/IDMMFactory.sol/IDMMFactory.json' import IDMMRouter02 from '../artifacts/contracts/interfaces/kyber/IDMMRouter02.sol/IDMMRouter02.json' +import { increaseTime } from '../lib/utils' chai.use(solidity) @@ -148,6 +149,7 @@ describe('KyberSwapAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -168,6 +170,7 @@ describe('KyberSwapAdapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/meta-strategy-adapter.ts b/test/meta-strategy-adapter.ts index 7e22cc9e..d11f641f 100644 --- a/test/meta-strategy-adapter.ts +++ b/test/meta-strategy-adapter.ts @@ -31,6 +31,7 @@ import { } from '../lib/deploy' import { DEFAULT_DEPOSIT_SLIPPAGE } from '../lib/constants' //import { displayBalances } from '../lib/logging' +import { increaseTime } from '../lib/utils' const NUM_TOKENS = 15 const STRATEGY_STATE: InitialState = { @@ -241,6 +242,7 @@ describe('MetaStrategyAdapter', function () { it('Should rebalance strategy: selling basic strategy tokens', async function () { //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) const balanceBefore = await basicStrategy.balanceOf(metaStrategy.address) + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(metaStrategy.address, loopRouter.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -254,6 +256,7 @@ describe('MetaStrategyAdapter', function () { //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) //await displayBalances(metaWrapper, metaStrategyItems.map((item) => item.item), weth) const balanceBefore = await metaStrategy.balanceOf(metaMetaStrategy.address) + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(metaMetaStrategy.address, loopRouter.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -278,6 +281,7 @@ describe('MetaStrategyAdapter', function () { it('Should rebalance strategy: buying basic strategy tokens', async function () { const balanceBefore = await basicStrategy.balanceOf(metaStrategy.address) + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(metaStrategy.address, loopRouter.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -423,6 +427,7 @@ describe('MetaStrategyAdapter', function () { ) //Encode multicalls and rebalance const rebalanceData = await multicallRouter.encodeCalls([...maliciousCalls, ...rebalanceCalls]) + await increaseTime(5 * 60 + 1) await expect( controller.connect(accounts[1]).rebalance(basicStrategy.address, multicallRouter.address, rebalanceData) ).to.be.revertedWith('') diff --git a/test/multicall-router.ts b/test/multicall-router.ts index d4a9c359..065ac34d 100644 --- a/test/multicall-router.ts +++ b/test/multicall-router.ts @@ -30,6 +30,7 @@ import { } from '../lib/encode' import { isRevertedWith } from '../lib/errors' import { DEFAULT_DEPOSIT_SLIPPAGE } from '../lib/constants' +import { increaseTime } from '../lib/utils' const { constants, getContractFactory, getSigners } = ethers const { AddressZero, WeiPerEther } = constants @@ -192,6 +193,7 @@ describe('MulticallRouter', function () { // it should fail before that check is made const calls = [transferCall, reentrancyCall] const data = await multicallRouter.encodeCalls(calls) + await increaseTime(5 * 60 + 1) await expect( controller.connect(accounts[1]).rebalance(strategy.address, multicallRouter.address, data) ).to.be.revertedWith('') @@ -210,6 +212,7 @@ describe('MulticallRouter', function () { strategy.address ) const data = await multicallRouter.encodeCalls([call]) + await increaseTime(5 * 60 + 1) expect( await isRevertedWith( controller.connect(accounts[1]).rebalance(strategy.address, multicallRouter.address, data), @@ -232,6 +235,7 @@ describe('MulticallRouter', function () { strategy.address ) const data = await multicallRouter.encodeCalls([call]) + await increaseTime(5 * 60 + 1) await expect( controller.connect(accounts[1]).rebalance(strategy.address, multicallRouter.address, data) ).to.be.revertedWith('') //Revert in calldata @@ -326,6 +330,7 @@ describe('MulticallRouter', function () { } const data = await multicallRouter.encodeCalls(calls) + await increaseTime(5 * 60 + 1) expect( await isRevertedWith( controller.connect(accounts[1]).rebalance(strategy.address, multicallRouter.address, data), @@ -339,6 +344,7 @@ describe('MulticallRouter', function () { // Multicall gets initial tokens from uniswap const calls = await prepareRebalanceMulticall(strategy, multicallRouter, adapter, oracle, weth) const data = await multicallRouter.encodeCalls(calls) + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, multicallRouter.address, data) const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/social-strategy.ts b/test/social-strategy.ts index 44bee980..dae2f21e 100644 --- a/test/social-strategy.ts +++ b/test/social-strategy.ts @@ -129,6 +129,7 @@ describe('StrategyController - Social', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -206,6 +207,7 @@ describe('StrategyController - Social', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/strategy-controller.ts b/test/strategy-controller.ts index 82ce2981..f414de47 100644 --- a/test/strategy-controller.ts +++ b/test/strategy-controller.ts @@ -1,4 +1,3 @@ -const runAll = true import chai from 'chai' const { expect } = chai import hre from 'hardhat' @@ -832,191 +831,186 @@ describe('StrategyController', function () { expect(await wrapper.isBalanced()).to.equal(false) }) - if (runAll) { - it('Should fail to rebalance: not controller', async function () { - await expect(router.rebalance(strategy.address, '0x')).to.be.revertedWith('Only controller') - }) + it('Should fail to rebalance: not controller', async function () { + await expect(router.rebalance(strategy.address, '0x')).to.be.revertedWith('Only controller') + }) - it('Should rebalance strategy', async function () { - await increaseTime(5 * 60 + 1) - const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems, weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) + const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems, weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - it('Should fail to open strategy: not manager', async function () { - expect( - await isRevertedWith( - controller.connect(owner).openStrategy(strategy.address), - 'Not manager', - 'StrategyController.sol' - ) - ).to.be.true - }) + it('Should fail to open strategy: not manager', async function () { + expect( + await isRevertedWith( + controller.connect(owner).openStrategy(strategy.address), + 'Not manager', + 'StrategyController.sol' + ) + ).to.be.true + }) - it('Should open strategy', async function () { - await controller.connect(accounts[1]).openStrategy(strategy.address) - expect((await controller.strategyState(strategy.address)).social).to.equal(true) - }) + it('Should open strategy', async function () { + await controller.connect(accounts[1]).openStrategy(strategy.address) + expect((await controller.strategyState(strategy.address)).social).to.equal(true) + }) - it('Should fail to open strategy: already open', async function () { - expect( - await isRevertedWith( - controller.connect(accounts[1]).openStrategy(strategy.address), - 'Strategy already open', - 'StrategyController.sol' - ) - ).to.be.true - }) + it('Should fail to open strategy: already open', async function () { + expect( + await isRevertedWith( + controller.connect(accounts[1]).openStrategy(strategy.address), + 'Strategy already open', + 'StrategyController.sol' + ) + ).to.be.true + }) - it('Should deploy fail adapter + setup strategy to need rebalance', async function () { - const FailAdapter = await getContractFactory('FailAdapter') - failAdapter = await FailAdapter.deploy(weth.address) - await failAdapter.deployed() + it('Should deploy fail adapter + setup strategy to need rebalance', async function () { + const FailAdapter = await getContractFactory('FailAdapter') + failAdapter = await FailAdapter.deploy(weth.address) + await failAdapter.deployed() - const value = WeiPerEther.mul(100) - await weth.connect(accounts[2]).deposit({ value: value }) - await weth.connect(accounts[2]).approve(adapter.address, value) - await adapter - .connect(accounts[2]) - .swap(value, 0, weth.address, tokens[1].address, accounts[2].address, accounts[2].address) - }) + const value = WeiPerEther.mul(100) + await weth.connect(accounts[2]).deposit({ value: value }) + await weth.connect(accounts[2]).approve(adapter.address, value) + await adapter + .connect(accounts[2]) + .swap(value, 0, weth.address, tokens[1].address, accounts[2].address, accounts[2].address) + }) - it('Should restructure', async function () { - const positions = [ - { token: tokens[1].address, percentage: BigNumber.from(500) }, - { token: tokens[2].address, percentage: BigNumber.from(0) }, - { token: tokens[3].address, percentage: BigNumber.from(500) }, - ] as Position[] + it('Should restructure', async function () { + const positions = [ + { token: tokens[1].address, percentage: BigNumber.from(500) }, + { token: tokens[2].address, percentage: BigNumber.from(0) }, + { token: tokens[3].address, percentage: BigNumber.from(500) }, + ] as Position[] - strategyItems = prepareStrategy(positions, adapter.address) - await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) - }) + strategyItems = prepareStrategy(positions, adapter.address) + await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) + }) - it('Should finalize structure', async function () { - await controller.connect(accounts[1]).finalizeStructure(strategy.address, router.address, '0x') - //await displayBalances(wrapper, strategyItems, weth) - }) + it('Should finalize structure', async function () { + await controller.connect(accounts[1]).finalizeStructure(strategy.address, router.address, '0x') + //await displayBalances(wrapper, strategyItems, weth) + }) - it('Transfer reserve token to require rebalance', async function () { - await increaseTime(5 * 60 + 1) - expect( - await isRevertedWith( - controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), - 'Balanced', - 'StrategyController.sol' - ) - ).to.be.true - - const value = WeiPerEther.div(1000) - await tokens[2].connect(accounts[0]).transfer(strategy.address, value) - expect(await wrapper.isBalanced()).to.equal(false) - await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + it('Transfer reserve token to require rebalance', async function () { + await increaseTime(5 * 60 + 1) + expect( + await isRevertedWith( + controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), + 'Balanced', + 'StrategyController.sol' + ) + ).to.be.true - it('Transfer reserve weth to require rebalance', async function () { - await increaseTime(5 * 60 + 1) - expect( - await isRevertedWith( - controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), - 'Balanced', - 'StrategyController.sol' - ) - ).to.be.true - - const value = WeiPerEther.div(100) - await weth.connect(accounts[4]).deposit({ value: value }) - await weth.connect(accounts[4]).transfer(strategy.address, value) - expect(await wrapper.isBalanced()).to.equal(false) - await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + const value = WeiPerEther.div(1000) + await tokens[2].connect(accounts[0]).transfer(strategy.address, value) + expect(await wrapper.isBalanced()).to.equal(false) + await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - it('Should fail to estimate and require emergency estimator', async function () { - const originalEstimate = await oracle['estimateItem(uint256,address)'](WeiPerEther, tokens[1].address) - const emergencyEstimatorAddress = await platform.oracles.registries.tokenRegistry.estimators( - ESTIMATOR_CATEGORY.BLOCKED - ) - - const GasBurnerEstimator = await getContractFactory('GasBurnerEstimator') - const gasBurnerEstimator = await GasBurnerEstimator.deploy() - await gasBurnerEstimator.connect(owner).deployed() - let tx = await strategyFactory - .connect(owner) - .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, gasBurnerEstimator.address) - await tx.wait() - tx = await strategyFactory - .connect(owner) - .addItemToRegistry(ITEM_CATEGORY.BASIC, ESTIMATOR_CATEGORY.BLOCKED, tokens[1].address) - await tx.wait() - - await expect( - controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { - value: WeiPerEther, - }) - ).to.be.revertedWith('') + it('Transfer reserve weth to require rebalance', async function () { + await increaseTime(5 * 60 + 1) + expect( + await isRevertedWith( + controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x'), + 'Balanced', + 'StrategyController.sol' + ) + ).to.be.true - tx = await strategyFactory - .connect(owner) - .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, emergencyEstimatorAddress) - await tx.wait() + const value = WeiPerEther.div(100) + await weth.connect(accounts[4]).deposit({ value: value }) + await weth.connect(accounts[4]).transfer(strategy.address, value) + expect(await wrapper.isBalanced()).to.equal(false) + await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - await expect( - controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { - value: WeiPerEther, - }) - ).to.be.revertedWith('') + it('Should fail to estimate and require emergency estimator', async function () { + const originalEstimate = await oracle['estimateItem(uint256,address)'](WeiPerEther, tokens[1].address) + const emergencyEstimatorAddress = await platform.oracles.registries.tokenRegistry.estimators( + ESTIMATOR_CATEGORY.BLOCKED + ) - const EmergencyEstimator = await getContractFactory('EmergencyEstimator') - const emergencyEstimator = await EmergencyEstimator.attach(emergencyEstimatorAddress) - await emergencyEstimator.updateEstimate(tokens[1].address, originalEstimate) + const GasBurnerEstimator = await getContractFactory('GasBurnerEstimator') + const gasBurnerEstimator = await GasBurnerEstimator.deploy() + await gasBurnerEstimator.connect(owner).deployed() + let tx = await strategyFactory + .connect(owner) + .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, gasBurnerEstimator.address) + await tx.wait() + tx = await strategyFactory + .connect(owner) + .addItemToRegistry(ITEM_CATEGORY.BASIC, ESTIMATOR_CATEGORY.BLOCKED, tokens[1].address) + await tx.wait() - await increaseTime(10 * 60) + await expect( + controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { + value: WeiPerEther, + }) + ).to.be.revertedWith('') - await emergencyEstimator.finalizeSetEstimate() + tx = await strategyFactory + .connect(owner) + .addEstimatorToRegistry(ESTIMATOR_CATEGORY.BLOCKED, emergencyEstimatorAddress) + await tx.wait() - await expect( - controller - .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { - value: WeiPerEther, - }) - ).to.emit(controller, 'Deposit') - }) + await expect( + controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { + value: WeiPerEther, + }) + ).to.be.revertedWith('') - it('Should set strategy', async function () { - await expect(controller.connect(accounts[1]).setStrategy(strategy.address)).to.emit( - controller, - 'StrategySet' - ) - const positions = [{ token: weth.address, percentage: BigNumber.from(1000) }] as Position[] - strategyItems = prepareStrategy(positions, adapter.address) - expect( - await isRevertedWith( - controller.connect(accounts[1]).restructure(strategy.address, strategyItems), - 'Strategy cannot change', - 'StrategyController.sol' - ) - ).to.be.true - }) + const EmergencyEstimator = await getContractFactory('EmergencyEstimator') + const emergencyEstimator = await EmergencyEstimator.attach(emergencyEstimatorAddress) + await emergencyEstimator.updateEstimate(tokens[1].address, originalEstimate) - it('Should fail to set strategy: already set', async function () { - expect( - await isRevertedWith( - controller.connect(accounts[1]).setStrategy(strategy.address), - 'Strategy already set', - 'StrategyController.sol' - ) - ).to.be.true - }) - } + await increaseTime(10 * 60) + + await emergencyEstimator.finalizeSetEstimate() + + await expect( + controller + .connect(accounts[1]) + .deposit(strategy.address, router.address, 0, DEFAULT_DEPOSIT_SLIPPAGE, '0x', { + value: WeiPerEther, + }) + ).to.emit(controller, 'Deposit') + }) + + it('Should set strategy', async function () { + await expect(controller.connect(accounts[1]).setStrategy(strategy.address)).to.emit(controller, 'StrategySet') + const positions = [{ token: weth.address, percentage: BigNumber.from(1000) }] as Position[] + strategyItems = prepareStrategy(positions, adapter.address) + expect( + await isRevertedWith( + controller.connect(accounts[1]).restructure(strategy.address, strategyItems), + 'Strategy cannot change', + 'StrategyController.sol' + ) + ).to.be.true + }) + + it('Should fail to set strategy: already set', async function () { + expect( + await isRevertedWith( + controller.connect(accounts[1]).setStrategy(strategy.address), + 'Strategy already set', + 'StrategyController.sol' + ) + ).to.be.true + }) }) diff --git a/test/uniswap-v3-adapter.ts b/test/uniswap-v3-adapter.ts index 4d3bd652..de8c2ef4 100644 --- a/test/uniswap-v3-adapter.ts +++ b/test/uniswap-v3-adapter.ts @@ -14,7 +14,7 @@ import { createLink, linkBytecode } from '../lib/link' import StrategyController from '../artifacts/contracts/StrategyController.sol/StrategyController.json' import ControllerLibrary from '../artifacts/contracts/libraries/ControllerLibrary.sol/ControllerLibrary.json' -import ControllerLibrary from '../artifacts/contracts/libraries/ControllerLibrary.sol/ControllerLibrary.json' +import StrategyLibrary from '../artifacts/contracts/libraries/StrategyLibrary.sol/StrategyLibrary.json' import SwapRouter from '@uniswap/v3-periphery/artifacts/contracts/SwapRouter.sol/SwapRouter.json' import Quoter from '@uniswap/v3-periphery/artifacts/contracts/lens/Quoter.sol/Quoter.json' @@ -118,21 +118,17 @@ describe('UniswapV3Adapter', function () { const whitelist = await Whitelist.connect(owner).deploy() await whitelist.deployed() - const ControllerLibraryFactory = await getContractFactory('ControllerLibrary') - controllerLibrary = await ControllerLibraryFactory.connect(owner).deploy() - await controllerLibrary.deployed() - const PlatformProxyAdmin = await getContractFactory('PlatformProxyAdmin') const platformProxyAdmin = await PlatformProxyAdmin.connect(owner).deploy() await platformProxyAdmin.deployed() const controllerAddress = await platformProxyAdmin.controller() const factoryAddress = await platformProxyAdmin.factory() - const strategyLibrary = await waffle.deployContract(accounts[0], ControllerLibrary, []) + const strategyLibrary = await waffle.deployContract(accounts[0], StrategyLibrary, []) await strategyLibrary.deployed() - const strategyLibraryLink = createLink(ControllerLibrary, strategyLibrary.address) + const strategyLibraryLink = createLink(StrategyLibrary, strategyLibrary.address) - const controllerLibrary = await waffle.deployContract( + controllerLibrary = await waffle.deployContract( accounts[0], linkBytecode(ControllerLibrary, [strategyLibraryLink]), [] @@ -257,6 +253,7 @@ describe('UniswapV3Adapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -271,6 +268,7 @@ describe('UniswapV3Adapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) diff --git a/test/weird-erc20s.ts b/test/weird-erc20s.ts index 20795c69..fad5c83a 100644 --- a/test/weird-erc20s.ts +++ b/test/weird-erc20s.ts @@ -17,6 +17,7 @@ import { } from '../lib/deploy' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { BigNumber, Event, Contract } from 'ethers' +import { increaseTime } from '../lib/utils' const NUM_TOKENS = 10 const STRATEGY_STATE: InitialState = { @@ -177,6 +178,7 @@ describe('Weird ERC20s', function () { expect(await wrapper.isBalanced()).to.equal(false) // Rebalance + await increaseTime(5 * 60 + 1) await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') }) @@ -221,6 +223,7 @@ describe('Weird ERC20s', function () { expect(await wrapper.isBalanced()).to.equal(false) // Rebalance + await increaseTime(5 * 60 + 1) await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') }) @@ -265,6 +268,7 @@ describe('Weird ERC20s', function () { expect(await wrapper.isBalanced()).to.equal(false) // Rebalance + await increaseTime(5 * 60 + 1) await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') }) @@ -309,6 +313,7 @@ describe('Weird ERC20s', function () { expect(await wrapper.isBalanced()).to.equal(false) // Rebalance + await increaseTime(5 * 60 + 1) await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') }) @@ -353,6 +358,7 @@ describe('Weird ERC20s', function () { expect(await wrapper.isBalanced()).to.equal(false) // Rebalance + await increaseTime(5 * 60 + 1) await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') }) @@ -397,6 +403,7 @@ describe('Weird ERC20s', function () { expect(await wrapper.isBalanced()).to.equal(false) // Rebalance + await increaseTime(5 * 60 + 1) await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') }) @@ -441,6 +448,7 @@ describe('Weird ERC20s', function () { expect(await wrapper.isBalanced()).to.equal(false) // Rebalance + await increaseTime(5 * 60 + 1) await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') }) diff --git a/test/yearn-adapter.ts b/test/yearn-adapter.ts index d035914a..4b39a58f 100644 --- a/test/yearn-adapter.ts +++ b/test/yearn-adapter.ts @@ -24,6 +24,7 @@ import ERC20 from '@uniswap/v2-periphery/build/ERC20.json' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import UniswapV2Factory from '@uniswap/v2-core/build/UniswapV2Factory.json' import UniswapV3Factory from '@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json' +import { increaseTime } from '../lib/utils' chai.use(solidity) @@ -164,6 +165,7 @@ describe('YEarnV2Adapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) @@ -181,6 +183,7 @@ describe('YEarnV2Adapter', function () { }) it('Should rebalance strategy', async function () { + await increaseTime(5 * 60 + 1) const tx = await controller.connect(accounts[1]).rebalance(strategy.address, router.address, '0x') const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) From 0bf180cd9788d4132e3956176aca0668d5048ed0 Mon Sep 17 00:00:00 2001 From: George Carder Date: Fri, 22 Jul 2022 13:48:16 -0700 Subject: [PATCH 08/17] update live-estimates test, passes --- lib/mainnet.ts | 2 -- test/live-estimates.ts | 13 ++++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/mainnet.ts b/lib/mainnet.ts index 02a9d0ea..04c6a5b7 100644 --- a/lib/mainnet.ts +++ b/lib/mainnet.ts @@ -163,9 +163,7 @@ export function livePlatform(signer: SignerWithAddress): Platform { if (!deployments.mainnet) throw Error('Deployment addresses not found') const addrs = deployments.mainnet const strategyLibrary = new Contract(addrs.StrategyLibrary, StrategyLibrary.abi, signer) - console.log('debug before') const controllerLibrary = new Contract(AddressZero, ControllerLibrary.abi, signer) // FIXME ControllerLibrary address when deployed - console.log('debug after') const tokenRegistry = new Contract(addrs.TokenRegistry, TokenRegistry.abi, signer) const curveDepositZapRegistry = new Contract(addrs.CurveDepositZapRegistry, CurveDepositZapRegistry.abi, signer) diff --git a/test/live-estimates.ts b/test/live-estimates.ts index feabae67..5a7953f3 100644 --- a/test/live-estimates.ts +++ b/test/live-estimates.ts @@ -7,9 +7,12 @@ import { getLiveContracts } from '../lib/mainnet' import { increaseTime } from '../lib/utils' import { deployFullRouter } from '../lib/deploy' import { DIVISOR, MAINNET_ADDRESSES } from '../lib/constants' +import { createLink, linkBytecode } from '../lib/link' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import StrategyClaim from '../artifacts/contracts/libraries/StrategyClaim.sol/StrategyClaim.json' +import ControllerLibrary from '../artifacts/contracts/libraries/ControllerLibrary.sol/ControllerLibrary.json' +import StrategyLibrary from '../artifacts/contracts/libraries/StrategyLibrary.sol/StrategyLibrary.json' const { constants, getSigners, getContractFactory } = ethers const { WeiPerEther } = constants @@ -89,6 +92,14 @@ describe('Live Estimates', function () { eNFTP = await Strategy.attach('16f7a9c3449f9c67e8c7e8f30ae1ee5d7b8ed10d') eETH2X = await Strategy.attach('0x81cddbf4a9d21cf52ef49bda5e5d5c4ae2e40b3e') + const strategyLibraryLink = createLink(StrategyLibrary, enso.platform.strategyLibrary.address) + const controllerLibrary = await waffle.deployContract( + accounts[0], + linkBytecode(ControllerLibrary, [strategyLibraryLink]), + [] + ) + await controllerLibrary.deployed() + // Impersonate owner await network.provider.request({ method: 'hardhat_impersonateAccount', @@ -100,7 +111,7 @@ describe('Live Estimates', function () { accounts[0], new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], accounts[0]), controller, - enso.platform.controllerLibrary + controllerLibrary ) // Whitelist await enso.platform.administration.whitelist.connect(owner).approve(router.address) From 037cbc7d0adef2dfac4bda0f6260542f3e8e87c1 Mon Sep 17 00:00:00 2001 From: George Carder Date: Fri, 22 Jul 2022 13:52:16 -0700 Subject: [PATCH 09/17] update storage gap --- contracts/StrategyControllerStorage.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/StrategyControllerStorage.sol b/contracts/StrategyControllerStorage.sol index 1dc1f1fc..7b2dc715 100644 --- a/contracts/StrategyControllerStorage.sol +++ b/contracts/StrategyControllerStorage.sol @@ -16,5 +16,5 @@ contract StrategyControllerStorage is StrategyTypes { mapping(bytes32 => TimelockData) internal __timelockData; // Gap for future storage changes - uint256[49] private __gap; + uint256[48] private __gap; } From 2d5505e2838a6f3e14c915f9297522ae56a73019 Mon Sep 17 00:00:00 2001 From: George Carder Date: Fri, 22 Jul 2022 14:50:41 -0700 Subject: [PATCH 10/17] smooth out aave-adapter test for rebase --- test/aave-adapter.ts | 139 +++++++++++++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 31 deletions(-) diff --git a/test/aave-adapter.ts b/test/aave-adapter.ts index 9946a82f..6c2fd1db 100644 --- a/test/aave-adapter.ts +++ b/test/aave-adapter.ts @@ -1,4 +1,3 @@ -const runAll = true import chai from 'chai' const { expect } = chai import { ethers } from 'hardhat' @@ -7,7 +6,7 @@ const { AddressZero, WeiPerEther } = constants import { solidity } from 'ethereum-waffle' import { BigNumber, Contract, Event } from 'ethers' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { prepareStrategy, StrategyItem, InitialState } from '../lib/encode' +import { prepareStrategy, encodeTransferFrom, StrategyItem, InitialState, /*TradeData*/} from '../lib/encode' import { Tokens } from '../lib/tokens' import { isRevertedWith } from '../lib/errors' import { @@ -20,8 +19,9 @@ import { deployCurveAdapter, deployPlatform, deployFullRouter, + deployMulticallRouter, } from '../lib/deploy' -import { MAINNET_ADDRESSES, ESTIMATOR_CATEGORY } from '../lib/constants' +import { MAINNET_ADDRESSES, ESTIMATOR_CATEGORY/*, ITEM_CATEGORY*/ } from '../lib/constants' //import { displayBalances } from '../lib/logging' import ERC20 from '@uniswap/v2-periphery/build/ERC20.json' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' @@ -47,7 +47,8 @@ describe('AaveAdapter', function () { usdc: Contract, accounts: SignerWithAddress[], uniswapFactory: Contract, - router: Contract, + fullRouter: Contract, + multicallRouter: Contract, strategyFactory: Contract, controller: Contract, oracle: Contract, @@ -63,7 +64,8 @@ describe('AaveAdapter', function () { wrapper: Contract, tokens: Tokens, collateralToken: string, - collateralToken2: string + collateralToken2: string, + stkAAVE: Contract before('Setup Uniswap + Factory', async function () { accounts = await getSigners() @@ -71,6 +73,7 @@ describe('AaveAdapter', function () { weth = new Contract(tokens.weth, WETH9.abi, accounts[0]) susd = new Contract(tokens.sUSD, ERC20.abi, accounts[0]) usdc = new Contract(tokens.usdc, ERC20.abi, accounts[0]) + stkAAVE = new Contract('0x4da27a545c0c5B758a6BA100e3a049001de870f5', ERC20.abi, accounts[0]) uniswapFactory = new Contract(MAINNET_ADDRESSES.UNISWAP_V2_FACTORY, UniswapV2Factory.abi, accounts[0]) platform = await deployPlatform( @@ -94,10 +97,14 @@ describe('AaveAdapter', function () { collateralToken = tokens.aWETH collateralToken2 = tokens.aCRV - router = await deployFullRouter(accounts[0], aaveAddressProvider, controller, controllerLibrary) - await whitelist.connect(accounts[0]).approve(router.address) uniswapAdapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) + + fullRouter = await deployFullRouter(accounts[0], aaveAddressProvider, controller, controllerLibrary) + await whitelist.connect(accounts[0]).approve(fullRouter.address) + multicallRouter = await deployMulticallRouter(accounts[0], controller) + await whitelist.connect(accounts[0]).approve(multicallRouter.address) + aaveV2Adapter = await deployAaveV2Adapter( accounts[0], aaveAddressProvider, @@ -113,6 +120,42 @@ describe('AaveAdapter', function () { await whitelist.connect(accounts[0]).approve(synthetixAdapter.address) curveAdapter = await deployCurveAdapter(accounts[0], curveAddressProvider, weth) await whitelist.connect(accounts[0]).approve(curveAdapter.address) + + /* + let tradeData: TradeData = { + adapters: [], + path: [], + cache: '0x', + } + await strategyFactory + .connect(accounts[0]) + .addItemDetailedToRegistry( + ITEM_CATEGORY.BASIC, + ESTIMATOR_CATEGORY.AAVE_V2, + collateralToken, + tradeData, + true + ) + await strategyFactory + .connect(accounts[0]) + .addItemDetailedToRegistry( + ITEM_CATEGORY.BASIC, + ESTIMATOR_CATEGORY.AAVE_V2, + collateralToken2, + tradeData, + true + ) + tradeData.adapters.push(uniswapAdapter.address) + await strategyFactory + .connect(accounts[0]) + .addItemDetailedToRegistry( + ITEM_CATEGORY.BASIC, + ESTIMATOR_CATEGORY.DEFAULT_ORACLE, + stkAAVE.address, + tradeData, + false + ) + */ }) it('Should deploy strategy', async function () { @@ -170,7 +213,7 @@ describe('AaveAdapter', function () { const tx = await strategyFactory .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { value: WeiPerEther }) + .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { value: WeiPerEther }) const receipt = await tx.wait() console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) @@ -192,10 +235,15 @@ describe('AaveAdapter', function () { expect(await wrapper.isBalanced()).to.equal(true) }) + it('Should getAllRewardTokens', async function () { + const rewardTokens = await strategy.connect(accounts[1]).callStatic.getAllRewardTokens() + expect(rewardTokens[0]).to.be.equal(stkAAVE.address) + }) + it('Should deposit', async function () { const tx = await controller .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) const receipt = await tx.wait() console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -219,14 +267,13 @@ describe('AaveAdapter', function () { await increaseTime(5 * 60 + 1) const tx = await controller .connect(accounts[1]) - .rebalance(strategy.address, router.address, '0x', { gasLimit: '5000000' }) + .rebalance(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) expect(await wrapper.isBalanced()).to.equal(true) }) - if (runAll) { it('Should purchase a token, requiring a rebalance of strategy', async function () { // Approve the user to use the adapter const value = await usdc.balanceOf(accounts[19].address) @@ -239,11 +286,25 @@ describe('AaveAdapter', function () { expect(await wrapper.isBalanced()).to.equal(false) }) + it('Should claim stkAAVE', async function () { + const balanceBefore = await stkAAVE.balanceOf(strategy.address) + const tx = await strategy.connect(accounts[1]).claimAll() + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + const balanceAfter = await stkAAVE.balanceOf(strategy.address) + expect(balanceAfter).to.be.gt(balanceBefore) + }) + it('Should rebalance strategy', async function () { + // the strategy has a balance of stkAAVE within its "claimables" + // meaning that the percentage of which should be 0% in the strategy + // so during this rebalance, this stkAAVE is just sold on uniswap for WETH. + // If later, we register stkAAVE itself as a "claimable", the strategy + // could maintain a balance in stkAAVE from which it would claim rewards in AAVE await increaseTime(5 * 60 + 1) const tx = await controller .connect(accounts[1]) - .rebalance(strategy.address, router.address, '0x', { gasLimit: '5000000' }) + .rebalance(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -264,7 +325,7 @@ describe('AaveAdapter', function () { it('Should deposit', async function () { const tx = await controller .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) const receipt = await tx.wait() console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -276,7 +337,7 @@ describe('AaveAdapter', function () { const ethBalanceBefore = await accounts[1].getBalance() const tx = await controller.connect(accounts[1]).withdrawETH( strategy.address, - router.address, + fullRouter.address, amount, '977', // note the high slippage! '0x', @@ -295,7 +356,7 @@ describe('AaveAdapter', function () { const wethBalanceBefore = await weth.balanceOf(accounts[1].address) const tx = await controller.connect(accounts[1]).withdrawWETH( strategy.address, - router.address, + fullRouter.address, amount, '960', // note the high slippage! '0x', @@ -316,10 +377,27 @@ describe('AaveAdapter', function () { await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) }) + it('Should fail to finalize structure - debt oracle exploit', async function () { + const collateral = new Contract(collateralToken, ERC20.abi, accounts[0]) + const collateral2 = new Contract(collateralToken2, ERC20.abi, accounts[0]) + const collateralBalance = await collateral.balanceOf(strategy.address) + const collateral2Balance = await collateral2.balanceOf(strategy.address) + const calls = [ + encodeTransferFrom(collateral, strategy.address, accounts[1].address, collateralBalance.div(6)), + encodeTransferFrom(collateral2, strategy.address, accounts[1].address, collateral2Balance.div(6)), + ] + const data = await multicallRouter.encodeCalls(calls) + await expect( + controller + .connect(accounts[1]) + .finalizeStructure(strategy.address, multicallRouter.address, data, { gasLimit: '5000000' }) + ).to.be.revertedWith('Former debt remaining') + }) + it('Should finalize structure - basic', async function () { const tx = await controller .connect(accounts[1]) - .finalizeStructure(strategy.address, router.address, '0x', { gasLimit: '5000000' }) + .finalizeStructure(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) const receipt = await tx.wait() console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -370,7 +448,7 @@ describe('AaveAdapter', function () { it('Should finalize structure - debt positions', async function () { const tx = await controller .connect(accounts[1]) - .finalizeStructure(strategy.address, router.address, '0x', { gasLimit: '5000000' }) + .finalizeStructure(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) const receipt = await tx.wait() console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -379,7 +457,7 @@ describe('AaveAdapter', function () { it('Should deposit', async function () { const tx = await controller .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) const receipt = await tx.wait() console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -392,7 +470,7 @@ describe('AaveAdapter', function () { // note the high slippage! const tx = await controller .connect(accounts[1]) - .withdrawETH(strategy.address, router.address, amount, '970', '0x', { gasLimit: '5000000' }) + .withdrawETH(strategy.address, fullRouter.address, amount, '970', '0x', { gasLimit: '5000000' }) const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -407,7 +485,7 @@ describe('AaveAdapter', function () { // note the high slippage! const tx = await controller .connect(accounts[1]) - .withdrawWETH(strategy.address, router.address, amount, '960', '0x', { gasLimit: '5000000' }) + .withdrawWETH(strategy.address, fullRouter.address, amount, '960', '0x', { gasLimit: '5000000' }) const receipt = await tx.wait() console.log('Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -455,7 +533,7 @@ describe('AaveAdapter', function () { const tx = await strategyFactory .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { + .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { value: WeiPerEther, }) const receipt = await tx.wait() @@ -482,7 +560,7 @@ describe('AaveAdapter', function () { it('Should deposit', async function () { const tx = await controller .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) const receipt = await tx.wait() console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -538,7 +616,7 @@ describe('AaveAdapter', function () { const tx = await strategyFactory .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { + .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { value: WeiPerEther, }) const receipt = await tx.wait() @@ -565,7 +643,7 @@ describe('AaveAdapter', function () { it('Should deposit', async function () { const tx = await controller .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) const receipt = await tx.wait() console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -630,7 +708,7 @@ describe('AaveAdapter', function () { const tx = await strategyFactory .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, router.address, '0x', { + .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { value: WeiPerEther, }) const receipt = await tx.wait() @@ -655,7 +733,7 @@ describe('AaveAdapter', function () { it('Should deposit', async function () { const tx = await controller .connect(accounts[1]) - .deposit(strategy.address, router.address, 0, '990', '0x', { value: WeiPerEther }) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) const receipt = await tx.wait() console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -674,7 +752,7 @@ describe('AaveAdapter', function () { let tx = await strategyFactory .connect(accounts[1]) - .createStrategy(name, symbol, basicStrategyItems, STRATEGY_STATE, router.address, '0x', { + .createStrategy(name, symbol, basicStrategyItems, STRATEGY_STATE, fullRouter.address, '0x', { value: ethers.BigNumber.from('10000000000000000'), }) @@ -685,7 +763,7 @@ describe('AaveAdapter', function () { const Strategy = await platform.getStrategyContractFactory() const basicStrategy = await Strategy.attach(strategyAddress) - let metaStrategyAdapter = await deployMetaStrategyAdapter(accounts[0], controller, router, weth) + let metaStrategyAdapter = await deployMetaStrategyAdapter(accounts[0], controller, fullRouter, weth) await whitelist.connect(accounts[0]).approve(metaStrategyAdapter.address) name = 'Debt Meta Strategy' @@ -722,7 +800,7 @@ describe('AaveAdapter', function () { let metaStrategyItems = prepareStrategy(positions2, uniswapAdapter.address) tx = await strategyFactory .connect(accounts[1]) - .createStrategy(name, symbol, metaStrategyItems, STRATEGY_STATE, router.address, '0x', { + .createStrategy(name, symbol, metaStrategyItems, STRATEGY_STATE, fullRouter.address, '0x', { value: ethers.BigNumber.from('10000000000000000'), }) receipt = await tx.wait() @@ -790,7 +868,7 @@ describe('AaveAdapter', function () { await isRevertedWith( strategyFactory .connect(accounts[1]) - .createStrategy(name, symbol, failItems, STRATEGY_STATE, router.address, '0x', { + .createStrategy(name, symbol, failItems, STRATEGY_STATE, fullRouter.address, '0x', { value: WeiPerEther, }), 'No synths and debt', @@ -798,5 +876,4 @@ describe('AaveAdapter', function () { ) ).to.be.true }) - } }) From 2cf1929cadfdaf1b406548bbbed24142c14b060d Mon Sep 17 00:00:00 2001 From: George Carder Date: Fri, 22 Jul 2022 15:12:56 -0700 Subject: [PATCH 11/17] fix rebase, compiles --- contracts/StrategyController.sol | 4 ++-- contracts/test/LibraryWrapper.sol | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/StrategyController.sol b/contracts/StrategyController.sol index ec175ed1..8d6d96e9 100644 --- a/contracts/StrategyController.sol +++ b/contracts/StrategyController.sol @@ -182,7 +182,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _require(_timelockIsReady(key), uint256(0x1bb63a90056c04) /* error_macro_for("rebalance timelock not ready.") */); _startTimelock(key, new bytes(0)); - ControllerLibrary.rebalance(strategy, router, _oracle, _weth, _strategyStates[address(strategy)].rebalanceSlippage, data); + ControllerLibrary.rebalance(strategy, router, oracle(), _weth, _strategyStates[address(strategy)].rebalanceSlippage, data); _removeStrategyLock(strategy); } @@ -515,7 +515,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I ControllerLibrary.verifyFormerDebt(address(strategy), newDebt, currentDebt); } // Check balance - (bool balancedAfter, uint256 totalAfter, ) = ControllerLibrary.verifyBalance(strategy, _oracle, false); // outer=false + (bool balancedAfter, uint256 totalAfter, ) = ControllerLibrary.verifyBalance(strategy, oracle(), false); // outer=false _require(balancedAfter, uint256(0x1bb63a90056c12) /* error_macro_for("Not balanced") */); _checkSlippage(totalAfter, totalBefore, _strategyStates[address(strategy)].restructureSlippage); strategy.updateTokenValue(totalAfter, strategy.totalSupply()); diff --git a/contracts/test/LibraryWrapper.sol b/contracts/test/LibraryWrapper.sol index af0d3600..53559cf8 100644 --- a/contracts/test/LibraryWrapper.sol +++ b/contracts/test/LibraryWrapper.sol @@ -24,12 +24,12 @@ contract LibraryWrapper is StrategyTypes{ } function isBalanced() external view returns (bool balanced) { - (balanced,,) = ControllerLibrary.verifyBalance(address(strategy), address(oracle), true); // outer=true + (balanced,,) = ControllerLibrary.verifyBalance(strategy, oracle, true); // outer=true return balanced; } function isBalancedInner() external view returns (bool balanced) { - (balanced,,) = ControllerLibrary.verifyBalance(address(strategy), address(oracle), false); // outer=false + (balanced,,) = ControllerLibrary.verifyBalance(strategy, oracle, false); // outer=false return balanced; } From f2f25e871216cf027c0caaf918bdd15fba9545d0 Mon Sep 17 00:00:00 2001 From: George Carder Date: Fri, 22 Jul 2022 15:15:30 -0700 Subject: [PATCH 12/17] prettier test --- test/aave-adapter.ts | 1068 +++++++++++++++++++++--------------------- 1 file changed, 533 insertions(+), 535 deletions(-) diff --git a/test/aave-adapter.ts b/test/aave-adapter.ts index 13ceeb82..fadfb5ce 100644 --- a/test/aave-adapter.ts +++ b/test/aave-adapter.ts @@ -274,17 +274,17 @@ describe('AaveAdapter', function () { expect(await wrapper.isBalanced()).to.equal(true) }) - it('Should purchase a token, requiring a rebalance of strategy', async function () { - // Approve the user to use the adapter - const value = await usdc.balanceOf(accounts[19].address) - await usdc.connect(accounts[19]).approve(uniswapAdapter.address, value) - await uniswapAdapter - .connect(accounts[19]) - .swap(value, 0, usdc.address, weth.address, accounts[19].address, accounts[19].address) - - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(false) - }) + it('Should purchase a token, requiring a rebalance of strategy', async function () { + // Approve the user to use the adapter + const value = await usdc.balanceOf(accounts[19].address) + await usdc.connect(accounts[19]).approve(uniswapAdapter.address, value) + await uniswapAdapter + .connect(accounts[19]) + .swap(value, 0, usdc.address, weth.address, accounts[19].address, accounts[19].address) + + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(false) + }) it('Should claim stkAAVE', async function () { const balanceBefore = await stkAAVE.balanceOf(strategy.address) @@ -295,87 +295,85 @@ describe('AaveAdapter', function () { expect(balanceAfter).to.be.gt(balanceBefore) }) - it('Should rebalance strategy', async function () { + it('Should rebalance strategy', async function () { // the strategy has a balance of stkAAVE within its "claimables" // meaning that the percentage of which should be 0% in the strategy // so during this rebalance, this stkAAVE is just sold on uniswap for WETH. // If later, we register stkAAVE itself as a "claimable", the strategy // could maintain a balance in stkAAVE from which it would claim rewards in AAVE - await increaseTime(5 * 60 + 1) - const tx = await controller - .connect(accounts[1]) - .rebalance(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + await increaseTime(5 * 60 + 1) + const tx = await controller + .connect(accounts[1]) + .rebalance(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - it('Should fail to withdrawAll: cannot withdraw debt', async function () { - const amount = BigNumber.from('10000000000000000') - expect( - await isRevertedWith( - strategy.connect(accounts[1]).withdrawAll(amount), - 'Cannot withdraw debt', - 'Strategy.sol' - ) - ).to.be.true - }) + it('Should fail to withdrawAll: cannot withdraw debt', async function () { + const amount = BigNumber.from('10000000000000000') + expect( + await isRevertedWith( + strategy.connect(accounts[1]).withdrawAll(amount), + 'Cannot withdraw debt', + 'Strategy.sol' + ) + ).to.be.true + }) - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - it('Should withdraw ETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const ethBalanceBefore = await accounts[1].getBalance() - const tx = await controller.connect(accounts[1]).withdrawETH( - strategy.address, - fullRouter.address, - amount, - '977', // note the high slippage! - '0x', - { gasLimit: '5000000' } - ) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const ethBalanceAfter = await accounts[1].getBalance() - expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) - }) + it('Should withdraw ETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const ethBalanceBefore = await accounts[1].getBalance() + const tx = await controller.connect(accounts[1]).withdrawETH( + strategy.address, + fullRouter.address, + amount, + '977', // note the high slippage! + '0x', + { gasLimit: '5000000' } + ) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const ethBalanceAfter = await accounts[1].getBalance() + expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) + }) - it('Should withdraw WETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const wethBalanceBefore = await weth.balanceOf(accounts[1].address) - const tx = await controller.connect(accounts[1]).withdrawWETH( - strategy.address, - fullRouter.address, - amount, - '960', // note the high slippage! - '0x', - { gasLimit: '5000000' } - ) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const wethBalanceAfter = await weth.balanceOf(accounts[1].address) - expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) - }) + it('Should withdraw WETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const wethBalanceBefore = await weth.balanceOf(accounts[1].address) + const tx = await controller.connect(accounts[1]).withdrawWETH( + strategy.address, + fullRouter.address, + amount, + '960', // note the high slippage! + '0x', + { gasLimit: '5000000' } + ) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const wethBalanceAfter = await weth.balanceOf(accounts[1].address) + expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) + }) - it('Should restructure - basic', async function () { - const positions = [ - { token: tokens.usdt, percentage: BigNumber.from(1000), adapters: [uniswapAdapter.address] }, - ] - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) - }) + it('Should restructure - basic', async function () { + const positions = [{ token: tokens.usdt, percentage: BigNumber.from(1000), adapters: [uniswapAdapter.address] }] + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) + }) it('Should fail to finalize structure - debt oracle exploit', async function () { const collateral = new Contract(collateralToken, ERC20.abi, accounts[0]) @@ -394,486 +392,486 @@ describe('AaveAdapter', function () { ).to.be.revertedWith('Former debt remaining') }) - it('Should finalize structure - basic', async function () { - const tx = await controller - .connect(accounts[1]) - .finalizeStructure(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) + it('Should finalize structure - basic', async function () { + const tx = await controller + .connect(accounts[1]) + .finalizeStructure(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - it('Should restructure - debt positions', async function () { - const positions = [ - { - token: collateralToken, - percentage: BigNumber.from(1000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: collateralToken2, - percentage: BigNumber.from(1000), - adapters: [uniswapAdapter.address, aaveV2Adapter.address], - path: [tokens.crv], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-1000), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], //ending in weth allows for a leverage feedback loop - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], + it('Should restructure - debt positions', async function () { + const positions = [ + { + token: collateralToken, + percentage: BigNumber.from(1000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: collateralToken2, + percentage: BigNumber.from(1000), + adapters: [uniswapAdapter.address, aaveV2Adapter.address], + path: [tokens.crv], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-1000), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], //ending in weth allows for a leverage feedback loop + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [ [ - [ - { token: collateralToken, percentage: 500 }, - { token: collateralToken2, percentage: 500 }, - ], - ] //define what tokens you want to loop back on here - ), - }, - ] - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) - }) + { token: collateralToken, percentage: 500 }, + { token: collateralToken2, percentage: 500 }, + ], + ] //define what tokens you want to loop back on here + ), + }, + ] + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + await controller.connect(accounts[1]).restructure(strategy.address, strategyItems) + }) - it('Should finalize structure - debt positions', async function () { - const tx = await controller - .connect(accounts[1]) - .finalizeStructure(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) + it('Should finalize structure - debt positions', async function () { + const tx = await controller + .connect(accounts[1]) + .finalizeStructure(strategy.address, fullRouter.address, '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Finalize Structure Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - }) + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - it('Should withdraw ETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const ethBalanceBefore = await accounts[1].getBalance() - // note the high slippage! - const tx = await controller - .connect(accounts[1]) - .withdrawETH(strategy.address, fullRouter.address, amount, '970', '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const ethBalanceAfter = await accounts[1].getBalance() - expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) - }) + it('Should withdraw ETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const ethBalanceBefore = await accounts[1].getBalance() + // note the high slippage! + const tx = await controller + .connect(accounts[1]) + .withdrawETH(strategy.address, fullRouter.address, amount, '970', '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const ethBalanceAfter = await accounts[1].getBalance() + expect(ethBalanceAfter.gt(ethBalanceBefore)).to.equal(true) + }) - it('Should withdraw WETH', async function () { - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const amount = BigNumber.from('5000000000000000') - const wethBalanceBefore = await weth.balanceOf(accounts[1].address) - // note the high slippage! - const tx = await controller - .connect(accounts[1]) - .withdrawWETH(strategy.address, fullRouter.address, amount, '960', '0x', { gasLimit: '5000000' }) - const receipt = await tx.wait() - console.log('Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - const wethBalanceAfter = await weth.balanceOf(accounts[1].address) - expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) - }) + it('Should withdraw WETH', async function () { + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const amount = BigNumber.from('5000000000000000') + const wethBalanceBefore = await weth.balanceOf(accounts[1].address) + // note the high slippage! + const tx = await controller + .connect(accounts[1]) + .withdrawWETH(strategy.address, fullRouter.address, amount, '960', '0x', { gasLimit: '5000000' }) + const receipt = await tx.wait() + console.log('Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + const wethBalanceAfter = await weth.balanceOf(accounts[1].address) + expect(wethBalanceAfter.gt(wethBalanceBefore)).to.equal(true) + }) - it('Should deploy new strategy', async function () { - const name = 'New Strategy' - const symbol = 'NEW' - - const positions = [ - { - token: tokens.aWETH, - percentage: BigNumber.from(2000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-1000), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 500 }]] //define what tokens you want to loop back on here - ), - }, - ] - - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - const strategyState: InitialState = { - timelock: BigNumber.from(60), - rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), - restructureSlippage: BigNumber.from(995), // Restucturing from this strategy requires higher slippage tolerance - managementFee: BigNumber.from(0), - social: false, - set: false, - } - - const tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { - value: WeiPerEther, - }) - const receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - - const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() - strategy = await Strategy.attach(strategyAddress) - - expect(await controller.initialized(strategyAddress)).to.equal(true) - - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + it('Should deploy new strategy', async function () { + const name = 'New Strategy' + const symbol = 'NEW' + + const positions = [ + { + token: tokens.aWETH, + percentage: BigNumber.from(2000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-1000), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 500 }]] //define what tokens you want to loop back on here + ), + }, + ] + + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + const strategyState: InitialState = { + timelock: BigNumber.from(60), + rebalanceThreshold: BigNumber.from(50), + rebalanceSlippage: BigNumber.from(997), + restructureSlippage: BigNumber.from(995), // Restucturing from this strategy requires higher slippage tolerance + managementFee: BigNumber.from(0), + social: false, + set: false, + } + + const tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { + value: WeiPerEther, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) - await wrapper.deployed() + const receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() + strategy = await Strategy.attach(strategyAddress) - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await controller.initialized(strategyAddress)).to.equal(true) + + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, }) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + await wrapper.deployed() - it('Should deploy another strategy', async function () { - const name = 'Another Strategy' - const symbol = 'ANOTHER' - - const positions = [ - { - token: tokens.aWETH, - percentage: BigNumber.from(2000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-500), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), - ), - }, - { - token: tokens.debtWBTC, - percentage: BigNumber.from(-500), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.wbtc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), - ), - }, - ] - - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - const strategyState: InitialState = { - timelock: BigNumber.from(60), - rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), - restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance - managementFee: BigNumber.from(0), - social: false, - set: false, - } - - const tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { - value: WeiPerEther, - }) - const receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - - const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() - strategy = await Strategy.attach(strategyAddress) - - expect(await controller.initialized(strategyAddress)).to.equal(true) - - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) + + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) + + it('Should deploy another strategy', async function () { + const name = 'Another Strategy' + const symbol = 'ANOTHER' + + const positions = [ + { + token: tokens.aWETH, + percentage: BigNumber.from(2000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-500), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), + ), + }, + { + token: tokens.debtWBTC, + percentage: BigNumber.from(-500), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.wbtc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here), + ), + }, + ] + + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + const strategyState: InitialState = { + timelock: BigNumber.from(60), + rebalanceThreshold: BigNumber.from(50), + rebalanceSlippage: BigNumber.from(997), + restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance + managementFee: BigNumber.from(0), + social: false, + set: false, + } + + const tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { + value: WeiPerEther, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) - await wrapper.deployed() + const receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() + strategy = await Strategy.attach(strategyAddress) - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await controller.initialized(strategyAddress)).to.equal(true) + + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, }) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + await wrapper.deployed() + + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - it('Should deploy ambiguous strategy', async function () { - const name = 'Ambiguous Strategy' - const symbol = 'AMBI' - - const positions = [ - { - token: collateralToken, - percentage: BigNumber.from(500), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), - }, - { - token: collateralToken2, - percentage: BigNumber.from(1500), - adapters: [uniswapAdapter.address, aaveV2Adapter.address], - path: [tokens.crv], - cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-875), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) + + it('Should deploy ambiguous strategy', async function () { + const name = 'Ambiguous Strategy' + const symbol = 'AMBI' + + const positions = [ + { + token: collateralToken, + percentage: BigNumber.from(500), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), + }, + { + token: collateralToken2, + percentage: BigNumber.from(1500), + adapters: [uniswapAdapter.address, aaveV2Adapter.address], + path: [tokens.crv], + cache: ethers.utils.defaultAbiCoder.encode(['uint16'], [500]), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-875), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [ [ - [ - { token: collateralToken, percentage: 250 }, - { token: collateralToken2, percentage: 500 }, - ], - ] //define what tokens you want to loop back on here - ), - }, - { - token: tokens.debtWBTC, - percentage: BigNumber.from(-125), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.wbtc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here - ), - }, - ] - - strategyItems = prepareStrategy(positions, uniswapAdapter.address) - const strategyState: InitialState = { - timelock: BigNumber.from(60), - rebalanceThreshold: BigNumber.from(50), - rebalanceSlippage: BigNumber.from(997), - restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance - managementFee: BigNumber.from(0), - social: false, - set: false, - } - - const tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { - value: WeiPerEther, - }) - const receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - - const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() - - strategy = await Strategy.attach(strategyAddress) - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + { token: collateralToken, percentage: 250 }, + { token: collateralToken2, percentage: 500 }, + ], + ] //define what tokens you want to loop back on here + ), + }, + { + token: tokens.debtWBTC, + percentage: BigNumber.from(-125), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.wbtc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: collateralToken, percentage: 250 }]] //define what tokens you want to loop back on here + ), + }, + ] + + strategyItems = prepareStrategy(positions, uniswapAdapter.address) + const strategyState: InitialState = { + timelock: BigNumber.from(60), + rebalanceThreshold: BigNumber.from(50), + rebalanceSlippage: BigNumber.from(997), + restructureSlippage: BigNumber.from(980), // Restucturing from this strategy requires higher slippage tolerance + managementFee: BigNumber.from(0), + social: false, + set: false, + } + + const tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, strategyItems, strategyState, fullRouter.address, '0x', { + value: WeiPerEther, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) - await wrapper.deployed() + const receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) - expect(await wrapper.isBalanced()).to.equal(true) - }) + const strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() - it('Should deposit', async function () { - const tx = await controller - .connect(accounts[1]) - .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) - const receipt = await tx.wait() - console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) - //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + strategy = await Strategy.attach(strategyAddress) + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, }) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + await wrapper.deployed() - it('Should deploy debt meta strategy', async function () { - //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + expect(await wrapper.isBalanced()).to.equal(true) + }) - let name = 'Basic Strategy' - let symbol = 'BASIC' - let positions = [ - { token: weth.address, percentage: BigNumber.from(500) }, - { token: usdc.address, percentage: BigNumber.from(500) }, - ] - const basicStrategyItems = prepareStrategy(positions, uniswapAdapter.address) + it('Should deposit', async function () { + const tx = await controller + .connect(accounts[1]) + .deposit(strategy.address, fullRouter.address, 0, '990', '0x', { value: WeiPerEther }) + const receipt = await tx.wait() + console.log('Deposit Gas Used: ', receipt.gasUsed.toString()) + //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) + }) - let tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, basicStrategyItems, STRATEGY_STATE, fullRouter.address, '0x', { - value: ethers.BigNumber.from('10000000000000000'), - }) - - let receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - - let strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - const Strategy = await platform.getStrategyContractFactory() - const basicStrategy = await Strategy.attach(strategyAddress) - - let metaStrategyAdapter = await deployMetaStrategyAdapter(accounts[0], controller, fullRouter, weth) - await whitelist.connect(accounts[0]).approve(metaStrategyAdapter.address) - - name = 'Debt Meta Strategy' - symbol = 'DMETA' - let positions2 = [ - { - token: basicStrategy.address, - percentage: BigNumber.from(400), - adapters: [metaStrategyAdapter.address], - path: [], - }, - { - token: tokens.aWETH, - percentage: BigNumber.from(1200), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-600), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, weth.address], //ending in weth allows for a leverage feedback loop - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: tokens.aWETH, percentage: 500 }]] - ), - }, - ] - - let metaStrategyItems = prepareStrategy(positions2, uniswapAdapter.address) - tx = await strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, metaStrategyItems, STRATEGY_STATE, fullRouter.address, '0x', { - value: ethers.BigNumber.from('10000000000000000'), - }) - receipt = await tx.wait() - console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - - strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy - expect(await controller.initialized(strategyAddress)).to.equal(true) - - const LibraryWrapper = await getContractFactory('LibraryWrapper', { - libraries: { - ControllerLibrary: controllerLibrary.address, - }, + it('Should deploy debt meta strategy', async function () { + //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) + + let name = 'Basic Strategy' + let symbol = 'BASIC' + let positions = [ + { token: weth.address, percentage: BigNumber.from(500) }, + { token: usdc.address, percentage: BigNumber.from(500) }, + ] + const basicStrategyItems = prepareStrategy(positions, uniswapAdapter.address) + + let tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, basicStrategyItems, STRATEGY_STATE, fullRouter.address, '0x', { + value: ethers.BigNumber.from('10000000000000000'), }) - let metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) - await metaWrapper.deployed() - //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) - expect(await metaWrapper.isBalanced()).to.equal(true) - }) + let receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) - it('Should fail to deploy synth + debt strategy', async function () { - const name = 'SynthDebt Strategy' - const symbol = 'SD' - - const positions = [ - { - token: tokens.sUSD, - percentage: BigNumber.from(0), - adapters: [uniswapAdapter.address, curveAdapter.address], - path: [tokens.usdc], - cache: '0x', - }, - { - token: tokens.sAAVE, - percentage: BigNumber.from(500), - adapters: [synthetixAdapter.address], - path: [], - cache: '0x', - }, - { - token: tokens.aWETH, - percentage: BigNumber.from(1000), - adapters: [aaveV2Adapter.address], - path: [], - cache: ethers.utils.defaultAbiCoder.encode( - ['uint16'], - [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage - ), - }, - { - token: tokens.debtUSDC, - percentage: BigNumber.from(-500), - adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], - path: [tokens.usdc, tokens.weth], - cache: ethers.utils.defaultAbiCoder.encode( - ['tuple(address token, uint16 percentage)[]'], - [[{ token: tokens.aWETH, percentage: 500 }]] //define what tokens you want to loop back on here), - ), - }, - ] - - const failItems = prepareStrategy(positions, uniswapAdapter.address) - - expect( - await isRevertedWith( - strategyFactory - .connect(accounts[1]) - .createStrategy(name, symbol, failItems, STRATEGY_STATE, fullRouter.address, '0x', { - value: WeiPerEther, - }), - 'No synths and debt', - 'StrategyController.sol' - ) - ).to.be.true + let strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + const Strategy = await platform.getStrategyContractFactory() + const basicStrategy = await Strategy.attach(strategyAddress) + + let metaStrategyAdapter = await deployMetaStrategyAdapter(accounts[0], controller, fullRouter, weth) + await whitelist.connect(accounts[0]).approve(metaStrategyAdapter.address) + + name = 'Debt Meta Strategy' + symbol = 'DMETA' + let positions2 = [ + { + token: basicStrategy.address, + percentage: BigNumber.from(400), + adapters: [metaStrategyAdapter.address], + path: [], + }, + { + token: tokens.aWETH, + percentage: BigNumber.from(1200), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-600), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, weth.address], //ending in weth allows for a leverage feedback loop + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: tokens.aWETH, percentage: 500 }]] + ), + }, + ] + + let metaStrategyItems = prepareStrategy(positions2, uniswapAdapter.address) + tx = await strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, metaStrategyItems, STRATEGY_STATE, fullRouter.address, '0x', { + value: ethers.BigNumber.from('10000000000000000'), + }) + receipt = await tx.wait() + console.log('Deployment Gas Used: ', receipt.gasUsed.toString()) + + strategyAddress = receipt.events.find((ev: Event) => ev.event === 'NewStrategy').args.strategy + expect(await controller.initialized(strategyAddress)).to.equal(true) + + const LibraryWrapper = await getContractFactory('LibraryWrapper', { + libraries: { + ControllerLibrary: controllerLibrary.address, + }, }) + let metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) + await metaWrapper.deployed() + + //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) + expect(await metaWrapper.isBalanced()).to.equal(true) + }) + + it('Should fail to deploy synth + debt strategy', async function () { + const name = 'SynthDebt Strategy' + const symbol = 'SD' + + const positions = [ + { + token: tokens.sUSD, + percentage: BigNumber.from(0), + adapters: [uniswapAdapter.address, curveAdapter.address], + path: [tokens.usdc], + cache: '0x', + }, + { + token: tokens.sAAVE, + percentage: BigNumber.from(500), + adapters: [synthetixAdapter.address], + path: [], + cache: '0x', + }, + { + token: tokens.aWETH, + percentage: BigNumber.from(1000), + adapters: [aaveV2Adapter.address], + path: [], + cache: ethers.utils.defaultAbiCoder.encode( + ['uint16'], + [500] // Multiplier 50% (divisor = 1000). For calculating the amount to purchase based off of the percentage + ), + }, + { + token: tokens.debtUSDC, + percentage: BigNumber.from(-500), + adapters: [aaveV2DebtAdapter.address, uniswapAdapter.address], + path: [tokens.usdc, tokens.weth], + cache: ethers.utils.defaultAbiCoder.encode( + ['tuple(address token, uint16 percentage)[]'], + [[{ token: tokens.aWETH, percentage: 500 }]] //define what tokens you want to loop back on here), + ), + }, + ] + + const failItems = prepareStrategy(positions, uniswapAdapter.address) + + expect( + await isRevertedWith( + strategyFactory + .connect(accounts[1]) + .createStrategy(name, symbol, failItems, STRATEGY_STATE, fullRouter.address, '0x', { + value: WeiPerEther, + }), + 'No synths and debt', + 'StrategyController.sol' + ) + ).to.be.true + }) }) From deb4329d6cc2622154219609d5a09072bc261f70 Mon Sep 17 00:00:00 2001 From: George Carder Date: Fri, 22 Jul 2022 15:20:49 -0700 Subject: [PATCH 13/17] fix errors.json after rebase --- contracts/StrategyController.sol | 46 +++++----- errors/errors.json | 146 ++++++++----------------------- 2 files changed, 58 insertions(+), 134 deletions(-) diff --git a/contracts/StrategyController.sol b/contracts/StrategyController.sol index 8d6d96e9..3c6436c2 100644 --- a/contracts/StrategyController.sol +++ b/contracts/StrategyController.sol @@ -261,16 +261,16 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I strategy.settleSynths(); StrategyState memory strategyState = _strategyStates[address(strategy)]; Timelock storage lock = _timelocks[address(strategy)]; - _require(lock.timestamp > 0, uint256(0x1bb63a90056c07) /* error_macro_for("No changes queued") */); + _require(lock.timestamp > 0, uint256(0x1bb63a90056c08) /* error_macro_for("No changes queued") */); _require( !strategyState.social || block.timestamp >= lock.timestamp.add(uint256(strategyState.timelock)), - uint256(0x1bb63a90056c08) /* error_macro_for("Timelock active") */ + uint256(0x1bb63a90056c09) /* error_macro_for("Timelock active") */ ); - _require(lock.category == TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c09) /* error_macro_for("Wrong category") */); + _require(lock.category == TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0a) /* error_macro_for("Wrong category") */); (StrategyItem[] memory strategyItems) = abi.decode(lock.data, (StrategyItem[])); - _require(ControllerLibrary.verifyStructure(address(strategy), strategyItems), uint256(0x1bb63a90056c0a) /* error_macro_for("Invalid structure") */); + _require(ControllerLibrary.verifyStructure(address(strategy), strategyItems), uint256(0x1bb63a90056c0b) /* error_macro_for("Invalid structure") */); _finalizeStructure(strategy, router, strategyItems, data); delete lock.category; delete lock.timestamp; @@ -297,9 +297,9 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I lock.timestamp == 0 || block.timestamp > lock.timestamp.add(uint256(_strategyStates[address(strategy)].timelock)), - uint256(0x1bb63a90056c0b) /* error_macro_for("Timelock active") */ + uint256(0x1bb63a90056c0c) /* error_macro_for("Timelock active") */ ); - _require(category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0c) /* error_macro_for("updateValue: category is RESTRUCTURE.") */); + _require(category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0d) /* error_macro_for("updateValue: category is RESTRUCTURE.") */); _checkAndEmit(address(strategy), category, newValue, false); lock.category = category; lock.timestamp = block.timestamp; @@ -316,12 +316,12 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _setStrategyLock(strategy); StrategyState storage strategyState = _strategyStates[address(strategy)]; Timelock storage lock = _timelocks[address(strategy)]; - _require(lock.timestamp > 0, uint256(0x1bb63a90056c0d) /* error_macro_for("No changes queued") */); - _require(lock.category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0e) /* error_macro_for("Wrong category") */); + _require(lock.timestamp > 0, uint256(0x1bb63a90056c0e) /* error_macro_for("No changes queued") */); + _require(lock.category != TimelockCategory.RESTRUCTURE, uint256(0x1bb63a90056c0f) /* error_macro_for("Wrong category") */); _require( !strategyState.social || block.timestamp >= lock.timestamp.add(uint256(strategyState.timelock)), - uint256(0x1bb63a90056c0f) /* error_macro_for("Timelock active") */ + uint256(0x1bb63a90056c10) /* error_macro_for("Timelock active") */ ); uint256 newValue = abi.decode(lock.data, (uint256)); if (lock.category == TimelockCategory.TIMELOCK) { @@ -353,7 +353,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _setStrategyLock(strategy); _onlyManager(strategy); StrategyState storage strategyState = _strategyStates[address(strategy)]; - _require(!strategyState.social, uint256(0x1bb63a90056c10) /* error_macro_for("Strategy already open") */); + _require(!strategyState.social, uint256(0x1bb63a90056c11) /* error_macro_for("Strategy already open") */); strategyState.social = true; emit StrategyOpen(address(strategy)); _removeStrategyLock(strategy); @@ -368,7 +368,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I _setStrategyLock(strategy); _onlyManager(strategy); StrategyState storage strategyState = _strategyStates[address(strategy)]; - _require(!strategyState.set, uint256(0x1bb63a90056c11) /* error_macro_for("Strategy already set") */); + _require(!strategyState.set, uint256(0x1bb63a90056c12) /* error_macro_for("Strategy already set") */); strategyState.set = true; emit StrategySet(address(strategy)); _removeStrategyLock(strategy); @@ -474,7 +474,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I ) private { address weth; if (msg.value > 0) { - _require(amount == 0, uint256(0x1bb63a90056c12) /* error_macro_for("Ambiguous amount") */); + _require(amount == 0, uint256(0x1bb63a90056c13) /* error_macro_for("Ambiguous amount") */); amount = msg.value; weth = _weth; IWETH(weth).deposit{value: amount}(); @@ -516,7 +516,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I } // Check balance (bool balancedAfter, uint256 totalAfter, ) = ControllerLibrary.verifyBalance(strategy, oracle(), false); // outer=false - _require(balancedAfter, uint256(0x1bb63a90056c12) /* error_macro_for("Not balanced") */); + _require(balancedAfter, uint256(0x1bb63a90056c14) /* error_macro_for("Not balanced") */); _checkSlippage(totalAfter, totalBefore, _strategyStates[address(strategy)].restructureSlippage); strategy.updateTokenValue(totalAfter, strategy.totalSupply()); } @@ -524,20 +524,20 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I function _checkSlippage(uint256 slippedValue, uint256 referenceValue, uint256 slippage) private pure { _require( slippedValue >= referenceValue.mul(slippage).div(DIVISOR), - uint256(0x1bb63a90056c13) /* error_macro_for("Too much slippage") */ + uint256(0x1bb63a90056c15) /* error_macro_for("Too much slippage") */ ); } function _checkDivisor(uint256 value) private pure { - _require(value <= DIVISOR, uint256(0x1bb63a90056c14) /* error_macro_for("Out of bounds") */); + _require(value <= DIVISOR, uint256(0x1bb63a90056c16) /* error_macro_for("Out of bounds") */); } function _checkFee(uint256 value) private pure { - _require(value <= FEE_BOUND, uint256(0x1bb63a90056c15) /* error_macro_for("Fee too high") */); + _require(value <= FEE_BOUND, uint256(0x1bb63a90056c17) /* error_macro_for("Fee too high") */); } function _checkTimelock(uint256 value) private { - _require(value <= 30 days, uint256(0x1bb63a90056c16) /* error_macro_for("Timelock is too long") */); + _require(value <= 30 days, uint256(0x1bb63a90056c18) /* error_macro_for("Timelock is too long") */); } function _checkAndEmit(address strategy, TimelockCategory category, uint256 value, bool finalized) private { @@ -570,21 +570,21 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I * @notice Checks that strategy is initialized */ function _isInitialized(address strategy) private view { - _require(initialized(strategy), uint256(0x1bb63a90056c17) /* error_macro_for("Not initialized") */); + _require(initialized(strategy), uint256(0x1bb63a90056c19) /* error_macro_for("Not initialized") */); } /** * @notice Checks that router is whitelisted */ function _onlyApproved(address account) private view { - _require(whitelist().approved(account), uint256(0x1bb63a90056c18) /* error_macro_for("Not approved") */); + _require(whitelist().approved(account), uint256(0x1bb63a90056c1a) /* error_macro_for("Not approved") */); } /** * @notice Checks if msg.sender is manager */ function _onlyManager(IStrategy strategy) private view { - _require(msg.sender == strategy.manager(), uint256(0x1bb63a90056c19) /* error_macro_for("Not manager") */); + _require(msg.sender == strategy.manager(), uint256(0x1bb63a90056c1b) /* error_macro_for("Not manager") */); } /** @@ -593,12 +593,12 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I function _socialOrManager(IStrategy strategy) private view { _require( msg.sender == strategy.manager() || _strategyStates[address(strategy)].social, - uint256(0x1bb63a90056c1a) /* error_macro_for("Not manager") */ + uint256(0x1bb63a90056c1c) /* error_macro_for("Not manager") */ ); } function _notSet(address strategy) private view { - _require(!_strategyStates[strategy].set, uint256(0x1bb63a90056c1b) /* error_macro_for("Strategy cannot change") */); + _require(!_strategyStates[strategy].set, uint256(0x1bb63a90056c1d) /* error_macro_for("Strategy cannot change") */); } function _timelockData(bytes32 identifier) internal override returns(TimelockData storage) { @@ -606,6 +606,6 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I } receive() external payable { - _require(msg.sender == _weth, uint256(0x1bb63a90056c1c) /* error_macro_for("Not WETH") */); + _require(msg.sender == _weth, uint256(0x1bb63a90056c1e) /* error_macro_for("Not WETH") */); } } diff --git a/errors/errors.json b/errors/errors.json index 9f078ca3..1d3e2da9 100644 --- a/errors/errors.json +++ b/errors/errors.json @@ -33,27 +33,29 @@ "1bb63a90056c05": "Invalid adapter", "1bb63a90056c06": "Timelock active", "1bb63a90056c07": "Invalid structure", - "1bb63a90056c08": "Timelock active", - "1bb63a90056c09": "Wrong category", - "1bb63a90056c0a": "Invalid structure", - "1bb63a90056c0b": "Timelock active", - "1bb63a90056c0c": "updateValue: category is RESTRUCTURE.", - "1bb63a90056c0d": "Wrong category", - "1bb63a90056c0e": "Timelock active", - "1bb63a90056c0f": "Strategy already open", - "1bb63a90056c10": "Strategy already set", - "1bb63a90056c11": "Ambiguous amount", - "1bb63a90056c12": "Not balanced", - "1bb63a90056c13": "Too much slippage", - "1bb63a90056c14": "Out of bounds", - "1bb63a90056c15": "Fee too high", - "1bb63a90056c16": "Timelock is too long", - "1bb63a90056c17": "Not initialized", - "1bb63a90056c18": "Not approved", - "1bb63a90056c19": "Not manager", - "1bb63a90056c1a": "Not manager", - "1bb63a90056c1b": "Strategy cannot change", - "1bb63a90056c1c": "Not WETH" + "1bb63a90056c08": "No changes queued", + "1bb63a90056c09": "Timelock active", + "1bb63a90056c0a": "Wrong category", + "1bb63a90056c0b": "Invalid structure", + "1bb63a90056c0c": "Timelock active", + "1bb63a90056c0d": "updateValue: category is RESTRUCTURE.", + "1bb63a90056c0e": "No changes queued", + "1bb63a90056c0f": "Wrong category", + "1bb63a90056c10": "Timelock active", + "1bb63a90056c11": "Strategy already open", + "1bb63a90056c12": "Strategy already set", + "1bb63a90056c13": "Ambiguous amount", + "1bb63a90056c14": "Not balanced", + "1bb63a90056c15": "Too much slippage", + "1bb63a90056c16": "Out of bounds", + "1bb63a90056c17": "Fee too high", + "1bb63a90056c18": "Timelock is too long", + "1bb63a90056c19": "Not initialized", + "1bb63a90056c1a": "Not approved", + "1bb63a90056c1b": "Not manager", + "1bb63a90056c1c": "Not manager", + "1bb63a90056c1d": "Strategy cannot change", + "1bb63a90056c1e": "Not WETH" } }, { @@ -196,14 +198,6 @@ "contractId": "91e8076401bf", "contractName": "IBaseAdapter.sol" }, - { - "contractId": "dcc862dda0e3", - "contractName": "IAaveIncentivesController.sol" - }, - { - "contractId": "50f8a65c2df3", - "contractName": "IAToken.sol" - }, { "contractId": "2ebf3ce0e034", "contractName": "IERC20NonStandard.sol" @@ -260,6 +254,10 @@ "contractId": "50f8a65c2df3", "contractName": "IAToken.sol" }, + { + "contractId": "dcc862dda0e3", + "contractName": "IAaveIncentivesController.sol" + }, { "contractId": "c326af50ec41", "contractName": "IDebtToken.sol" @@ -368,6 +366,10 @@ "contractId": "2e04868f1ba0", "contractName": "IYEarnV2Vault.sol" }, + { + "contractId": "49569a8c2c39", + "contractName": "AddressArrays.sol" + }, { "contractId": "a0976939a344", "contractName": "BinaryTreeWithPayload.sol" @@ -516,88 +518,6 @@ "contractId": "35530d527674", "contractName": "StrategyRouter.sol" }, - { - "contractId": "ee78cef3375d", - "contractName": "StrategyCommon.sol" - }, - { - "contractId": "1bb63a90056c", - "contractName": "StrategyController.sol", - "errorcodes": { - "1bb63a90056c00": "Sanity check that Library shares context.", - "1bb63a90056c01": "Not factory", - "1bb63a90056c02": "Strategy restructuring", - "1bb63a90056c03": "withdrawETH: call failed.", - "1bb63a90056c04": "Invalid adapter", - "1bb63a90056c05": "Timelock active", - "1bb63a90056c06": "Invalid structure", - "1bb63a90056c07": "No changes queued", - "1bb63a90056c08": "Timelock active", - "1bb63a90056c09": "Wrong category", - "1bb63a90056c0a": "Invalid structure", - "1bb63a90056c0b": "Timelock active", - "1bb63a90056c0c": "updateValue: category is RESTRUCTURE.", - "1bb63a90056c0d": "No changes queued", - "1bb63a90056c0e": "Wrong category", - "1bb63a90056c0f": "Timelock active", - "1bb63a90056c10": "Strategy already open", - "1bb63a90056c11": "Strategy already set", - "1bb63a90056c12": "Ambiguous amount", - "1bb63a90056c13": "Not balanced", - "1bb63a90056c14": "Too much slippage", - "1bb63a90056c15": "Out of bounds", - "1bb63a90056c16": "Fee too high", - "1bb63a90056c17": "Timelock is too long", - "1bb63a90056c18": "Not initialized", - "1bb63a90056c19": "Not approved", - "1bb63a90056c1a": "Not manager", - "1bb63a90056c1b": "Not manager", - "1bb63a90056c1c": "Strategy cannot change", - "1bb63a90056c1d": "Not WETH" - } - }, - { - "contractId": "dbc2b7686bf5", - "contractName": "StrategyControllerStorage.sol" - }, - { - "contractId": "f19f85f5ead4", - "contractName": "StrategyProxyAdmin.sol" - }, - { - "contractId": "676a717422f5", - "contractName": "StrategyProxyFactory.sol" - }, - { - "contractId": "89b89b5bfe68", - "contractName": "StrategyProxyFactoryStorage.sol" - }, - { - "contractId": "b3e5dea2190e", - "contractName": "Strategy.sol", - "errorcodes": { - "b3e5dea2190e00": "finalizeTimelock: timelock is not ready.", - "b3e5dea2190e01": "Router only", - "b3e5dea2190e02": "Cannot withdraw debt", - "b3e5dea2190e03": "0 amount", - "b3e5dea2190e04": "claimAll: caller must be controller or manager.", - "b3e5dea2190e05": "Manager already set", - "b3e5dea2190e06": "finalizeUpdateTradeData: timelock not ready.", - "b3e5dea2190e07": "Only StrategyProxyFactory" - } - }, - { - "contractId": "ea334a07e51b", - "contractName": "StrategyTokenFees.sol" - }, - { - "contractId": "90dae108da2b", - "contractName": "StrategyToken.sol" - }, - { - "contractId": "3d55eff30f48", - "contractName": "StrategyTokenStorage.sol" - }, { "contractId": "4ed253196e4a", "contractName": "Arbitrager.sol" @@ -646,6 +566,10 @@ "contractId": "c728f8ccc626", "contractName": "SushiswapFactory.sol" }, + { + "contractId": "9effd4c5cb9b", + "contractName": "TestBinaryTree.sol" + }, { "contractId": "48f342a7366c", "contractName": "UniswapNaiveOracle.sol" From 433f7ad023c89748982123cca1dc1b91b62c99cf Mon Sep 17 00:00:00 2001 From: George Carder Date: Mon, 25 Jul 2022 08:58:44 -0700 Subject: [PATCH 14/17] update strategy-controller test, passes --- test/strategy-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/strategy-controller.ts b/test/strategy-controller.ts index f414de47..4e606e13 100644 --- a/test/strategy-controller.ts +++ b/test/strategy-controller.ts @@ -926,7 +926,7 @@ describe('StrategyController', function () { ) ).to.be.true - const value = WeiPerEther.div(100) + const value = WeiPerEther.div(10) await weth.connect(accounts[4]).deposit({ value: value }) await weth.connect(accounts[4]).transfer(strategy.address, value) expect(await wrapper.isBalanced()).to.equal(false) From f0e96e1b185fb7f8c9b6634ab468eb49f799899b Mon Sep 17 00:00:00 2001 From: George Carder Date: Mon, 25 Jul 2022 13:02:48 -0700 Subject: [PATCH 15/17] timelocked updateRebalanceParameters --- contracts/StrategyController.sol | 61 +++++++++++++------ contracts/StrategyControllerStorage.sol | 5 +- contracts/StrategyProxyFactory.sol | 4 ++ .../recovery/StrategyControllerPaused.sol | 8 +++ contracts/interfaces/IStrategyController.sol | 4 ++ contracts/libraries/ControllerLibrary.sol | 12 ++-- contracts/test/LibraryWrapper.sol | 8 ++- errors/errors.json | 26 ++++---- test/aave-adapter.ts | 14 +++-- test/balancer-adapter.ts | 2 +- test/batch-router.ts | 2 +- test/compound-adapter.ts | 2 +- test/curve-adapter.ts | 8 +-- test/experimental-strategy.ts | 2 +- test/flash-loan.ts | 2 +- test/kyber-adapter.ts | 2 +- test/leverage-adapter.ts | 4 +- test/library.ts | 2 +- test/meta-strategy-adapter.ts | 18 +++++- test/multicall-router.ts | 2 +- test/reentrancy.ts | 2 +- test/social-strategy.ts | 2 +- test/strategy-controller.ts | 2 +- test/strategy-factory.ts | 29 +++++++++ test/synthetix-adapter.ts | 2 +- test/token-registry.ts | 2 +- test/uniswap-v3-adapter.ts | 2 +- test/weird-erc20s.ts | 14 ++--- test/x-fees.ts | 2 +- test/yearn-adapter.ts | 2 +- 30 files changed, 171 insertions(+), 76 deletions(-) diff --git a/contracts/StrategyController.sol b/contracts/StrategyController.sol index 3c6436c2..0da2f36c 100644 --- a/contracts/StrategyController.sol +++ b/contracts/StrategyController.sol @@ -31,14 +31,13 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I uint256 private constant FEE_BOUND = 200; // Max fee of 20% int256 private constant PERCENTAGE_BOUND = 10000; // Max 10x leverage - uint256 public constant REBALANCE_TIMELOCK_PERIOD = 5 minutes; // FIXME should this be variable? a different value? - address public immutable factory; event NewStructure(address indexed strategy, StrategyItem[] items, bool indexed finalized); event NewValue(address indexed strategy, TimelockCategory category, uint256 newValue, bool indexed finalized); event StrategyOpen(address indexed strategy); event StrategySet(address indexed strategy); + event RebalanceParametersUpdated(uint256 indexed rebalanceTimelockPeriod, uint256 indexed rebalanceThreshold, bool indexed finalized); // hack! these events are called in the `ControllerLibrary` // but cannot be tracked unless they are defined here! @@ -55,6 +54,11 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I * @dev Called to initialize proxy */ function initialize() external initializer { + _rebalanceTimelockPeriod = 5 minutes; + _rebalanceThresholdScalar = 2000; + bytes32 key = keccak256(abi.encode(this.updateRebalanceParameters.selector)); + _setTimelock(key, _rebalanceTimelockPeriod); + updateAddresses(); _require(address(this)== ControllerLibrary.self(), uint256(0x1bb63a90056c00) /* error_macro_for("Sanity check that Library shares context.") */); } @@ -180,9 +184,10 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I bytes32 key = keccak256(abi.encode(this.rebalance.selector, strategy)); _require(_timelockIsReady(key), uint256(0x1bb63a90056c04) /* error_macro_for("rebalance timelock not ready.") */); + _setTimelock(key, _rebalanceTimelockPeriod); // in case factory updateRebalanceParameters _startTimelock(key, new bytes(0)); - ControllerLibrary.rebalance(strategy, router, oracle(), _weth, _strategyStates[address(strategy)].rebalanceSlippage, data); + ControllerLibrary.rebalance(strategy, router, oracle(), _weth, _strategyStates[address(strategy)].rebalanceSlippage, _rebalanceThresholdScalar, data); _removeStrategyLock(strategy); } @@ -409,6 +414,24 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I } } + function updateRebalanceParameters(uint256 rebalanceTimelockPeriod, uint256 rebalanceThresholdScalar) external override { + _require(msg.sender == factory, uint256(0x1bb63a90056c13) /* error_macro_for("Not factory") */); + _startTimelock( + keccak256(abi.encode(this.updateRebalanceParameters.selector)), // identifier + abi.encode(rebalanceTimelockPeriod, rebalanceThresholdScalar)); // payload + emit RebalanceParametersUpdated(rebalanceTimelockPeriod, rebalanceThresholdScalar, false); + } + + function finalizeRebalanceParameters() public { + bytes32 key = keccak256(abi.encode(this.updateRebalanceParameters.selector)); + _require(_timelockIsReady(key), uint256(0x1bb63a90056c14) /* error_macro_for("updateRebalanceParameters timelock not ready.") */); + (uint256 rebalanceTimelockPeriod, uint256 rebalanceThresholdScalar) = abi.decode(_getTimelockValue(key), (uint256, uint256)); + _resetTimelock(key); + _rebalanceTimelockPeriod = rebalanceTimelockPeriod; + _rebalanceThresholdScalar = rebalanceThresholdScalar; + emit RebalanceParametersUpdated(rebalanceTimelockPeriod, rebalanceThresholdScalar, true); + } + function oracle() public view override returns (IOracle) { return IOracle(_oracle); } @@ -425,6 +448,10 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I return _pool; } + function rebalanceThresholdScalar() external view override returns(uint256) { + return _rebalanceThresholdScalar; + } + function _withdrawWETH( IStrategy strategy, IStrategyRouter router, @@ -458,7 +485,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I if (state.social) emit StrategyOpen(strategy); if (state.set) emit StrategySet(strategy); bytes32 key = keccak256(abi.encode(this.rebalance.selector, strategy)); - _setTimelock(key, REBALANCE_TIMELOCK_PERIOD); + _setTimelock(key, _rebalanceTimelockPeriod); _startTimelock(key, new bytes(0)); } @@ -474,7 +501,7 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I ) private { address weth; if (msg.value > 0) { - _require(amount == 0, uint256(0x1bb63a90056c13) /* error_macro_for("Ambiguous amount") */); + _require(amount == 0, uint256(0x1bb63a90056c15) /* error_macro_for("Ambiguous amount") */); amount = msg.value; weth = _weth; IWETH(weth).deposit{value: amount}(); @@ -515,8 +542,8 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I ControllerLibrary.verifyFormerDebt(address(strategy), newDebt, currentDebt); } // Check balance - (bool balancedAfter, uint256 totalAfter, ) = ControllerLibrary.verifyBalance(strategy, oracle(), false); // outer=false - _require(balancedAfter, uint256(0x1bb63a90056c14) /* error_macro_for("Not balanced") */); + (bool balancedAfter, uint256 totalAfter, ) = ControllerLibrary.verifyBalance(strategy, oracle(), 0); + _require(balancedAfter, uint256(0x1bb63a90056c16) /* error_macro_for("Not balanced") */); _checkSlippage(totalAfter, totalBefore, _strategyStates[address(strategy)].restructureSlippage); strategy.updateTokenValue(totalAfter, strategy.totalSupply()); } @@ -524,20 +551,20 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I function _checkSlippage(uint256 slippedValue, uint256 referenceValue, uint256 slippage) private pure { _require( slippedValue >= referenceValue.mul(slippage).div(DIVISOR), - uint256(0x1bb63a90056c15) /* error_macro_for("Too much slippage") */ + uint256(0x1bb63a90056c17) /* error_macro_for("Too much slippage") */ ); } function _checkDivisor(uint256 value) private pure { - _require(value <= DIVISOR, uint256(0x1bb63a90056c16) /* error_macro_for("Out of bounds") */); + _require(value <= DIVISOR, uint256(0x1bb63a90056c18) /* error_macro_for("Out of bounds") */); } function _checkFee(uint256 value) private pure { - _require(value <= FEE_BOUND, uint256(0x1bb63a90056c17) /* error_macro_for("Fee too high") */); + _require(value <= FEE_BOUND, uint256(0x1bb63a90056c19) /* error_macro_for("Fee too high") */); } function _checkTimelock(uint256 value) private { - _require(value <= 30 days, uint256(0x1bb63a90056c18) /* error_macro_for("Timelock is too long") */); + _require(value <= 30 days, uint256(0x1bb63a90056c1a) /* error_macro_for("Timelock is too long") */); } function _checkAndEmit(address strategy, TimelockCategory category, uint256 value, bool finalized) private { @@ -570,21 +597,21 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I * @notice Checks that strategy is initialized */ function _isInitialized(address strategy) private view { - _require(initialized(strategy), uint256(0x1bb63a90056c19) /* error_macro_for("Not initialized") */); + _require(initialized(strategy), uint256(0x1bb63a90056c1b) /* error_macro_for("Not initialized") */); } /** * @notice Checks that router is whitelisted */ function _onlyApproved(address account) private view { - _require(whitelist().approved(account), uint256(0x1bb63a90056c1a) /* error_macro_for("Not approved") */); + _require(whitelist().approved(account), uint256(0x1bb63a90056c1c) /* error_macro_for("Not approved") */); } /** * @notice Checks if msg.sender is manager */ function _onlyManager(IStrategy strategy) private view { - _require(msg.sender == strategy.manager(), uint256(0x1bb63a90056c1b) /* error_macro_for("Not manager") */); + _require(msg.sender == strategy.manager(), uint256(0x1bb63a90056c1d) /* error_macro_for("Not manager") */); } /** @@ -593,12 +620,12 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I function _socialOrManager(IStrategy strategy) private view { _require( msg.sender == strategy.manager() || _strategyStates[address(strategy)].social, - uint256(0x1bb63a90056c1c) /* error_macro_for("Not manager") */ + uint256(0x1bb63a90056c1e) /* error_macro_for("Not manager") */ ); } function _notSet(address strategy) private view { - _require(!_strategyStates[strategy].set, uint256(0x1bb63a90056c1d) /* error_macro_for("Strategy cannot change") */); + _require(!_strategyStates[strategy].set, uint256(0x1bb63a90056c1f) /* error_macro_for("Strategy cannot change") */); } function _timelockData(bytes32 identifier) internal override returns(TimelockData storage) { @@ -606,6 +633,6 @@ contract StrategyController is IStrategyController, StrategyControllerStorage, I } receive() external payable { - _require(msg.sender == _weth, uint256(0x1bb63a90056c1e) /* error_macro_for("Not WETH") */); + _require(msg.sender == _weth, uint256(0x1bb63a90056c20) /* error_macro_for("Not WETH") */); } } diff --git a/contracts/StrategyControllerStorage.sol b/contracts/StrategyControllerStorage.sol index 7b2dc715..4035be8b 100644 --- a/contracts/StrategyControllerStorage.sol +++ b/contracts/StrategyControllerStorage.sol @@ -15,6 +15,9 @@ contract StrategyControllerStorage is StrategyTypes { address internal _pool; mapping(bytes32 => TimelockData) internal __timelockData; + uint256 internal _rebalanceTimelockPeriod; + uint256 internal _rebalanceThresholdScalar; + // Gap for future storage changes - uint256[48] private __gap; + uint256[46] private __gap; } diff --git a/contracts/StrategyProxyFactory.sol b/contracts/StrategyProxyFactory.sol index 846968d1..e98aaea9 100644 --- a/contracts/StrategyProxyFactory.sol +++ b/contracts/StrategyProxyFactory.sol @@ -197,6 +197,10 @@ contract StrategyProxyFactory is IStrategyProxyFactory, StrategyProxyFactoryStor emit NewStreamingFee(uint256(fee)); } + function updateRebalanceParameters(uint256 rebalanceTimelockPeriod, uint256 rebalanceThresholdScalar) external onlyOwner { + IStrategyController(controller).updateRebalanceParameters(rebalanceTimelockPeriod, rebalanceThresholdScalar); + } + /* * @dev This function is called by StrategyProxyAdmin */ diff --git a/contracts/implementations/recovery/StrategyControllerPaused.sol b/contracts/implementations/recovery/StrategyControllerPaused.sol index fa83243d..7d26a59d 100644 --- a/contracts/implementations/recovery/StrategyControllerPaused.sol +++ b/contracts/implementations/recovery/StrategyControllerPaused.sol @@ -272,6 +272,10 @@ contract StrategyControllerPaused is IStrategyController, StrategyControllerStor } } + function updateRebalanceParameters(uint256 rebalanceTimelockPeriod, uint256 rebalanceThresholdScalar) external override { + revert("StrategyControllerPaused."); + } + function oracle() public view override returns (IOracle) { return IOracle(_oracle); } @@ -288,6 +292,10 @@ contract StrategyControllerPaused is IStrategyController, StrategyControllerStor return _pool; } + function rebalanceThresholdScalar() external view override returns(uint256) { + return _rebalanceThresholdScalar; + } + // Internal Strategy Functions /** * @notice Deposit eth or weth into strategy diff --git a/contracts/interfaces/IStrategyController.sol b/contracts/interfaces/IStrategyController.sol index 995d24bb..59d335cf 100644 --- a/contracts/interfaces/IStrategyController.sol +++ b/contracts/interfaces/IStrategyController.sol @@ -68,6 +68,8 @@ interface IStrategyController is StrategyTypes { uint256 newValue ) external; + function updateRebalanceParameters(uint256 rebalanceTimelockPeriod, uint256 rebalanceThresholdScalar) external; + function finalizeValue(IStrategy strategy) external; function openStrategy(IStrategy strategy) external; @@ -90,4 +92,6 @@ interface IStrategyController is StrategyTypes { function weth() external view returns (address); function pool() external view returns (address); + + function rebalanceThresholdScalar() external view returns(uint256); } diff --git a/contracts/libraries/ControllerLibrary.sol b/contracts/libraries/ControllerLibrary.sol index a59bddbd..27d2afee 100644 --- a/contracts/libraries/ControllerLibrary.sol +++ b/contracts/libraries/ControllerLibrary.sol @@ -19,7 +19,6 @@ library ControllerLibrary { using AddressArrays for address[]; int256 private constant DIVISOR = 1000; - uint256 private constant REBALANCE_THRESHOLD_SCALAR = 2; // FIXME tune uint256 private constant PRECISION = 10**18; uint256 private constant WITHDRAW_UPPER_BOUND = 10**17; // Upper condition for including pool's tokens as part of burn during withdraw @@ -154,18 +153,19 @@ library ControllerLibrary { IOracle oracle, address weth, uint256 rebalanceSlippage, + uint256 rebalanceThresholdScalar, bytes memory data ) public { _onlyApproved(address(router)); strategy.settleSynths(); - (bool balancedBefore, uint256 totalBefore, int256[] memory estimates) = verifyBalance(strategy, oracle, true); // outer=true + (bool balancedBefore, uint256 totalBefore, int256[] memory estimates) = verifyBalance(strategy, oracle, rebalanceThresholdScalar); require(!balancedBefore, "Balanced"); if (router.category() != IStrategyRouter.RouterCategory.GENERIC) data = abi.encode(totalBefore, estimates); // Rebalance _useRouter(strategy, router, router.rebalance, weth, data); // Recheck total - (bool balancedAfter, uint256 totalAfter, ) = verifyBalance(strategy, oracle, false); // outer=false + (bool balancedAfter, uint256 totalAfter, ) = verifyBalance(strategy, oracle, 0); require(balancedAfter, "Not balanced"); _checkSlippage(totalAfter, totalBefore, rebalanceSlippage); strategy.updateTokenValue(totalAfter, strategy.totalSupply()); @@ -377,10 +377,10 @@ library ControllerLibrary { * whether the strategy is balanced. Necessary to confirm the balance * before and after a rebalance to ensure nothing fishy happened */ - function verifyBalance(IStrategy strategy, IOracle oracle, bool outer) public view returns (bool, uint256, int256[] memory) { + function verifyBalance(IStrategy strategy, IOracle oracle, uint256 rebalanceThresholdScalar) public view returns (bool, uint256, int256[] memory) { uint256 threshold = strategy.rebalanceThreshold(); - if (outer) { // wider threshold - threshold = threshold.mul(REBALANCE_THRESHOLD_SCALAR); + if (rebalanceThresholdScalar > 0) { // wider threshold + threshold = threshold.mul(rebalanceThresholdScalar) / uint256(DIVISOR); } return _verifyBalance(strategy, oracle, threshold); } diff --git a/contracts/test/LibraryWrapper.sol b/contracts/test/LibraryWrapper.sol index 53559cf8..73904304 100644 --- a/contracts/test/LibraryWrapper.sol +++ b/contracts/test/LibraryWrapper.sol @@ -17,19 +17,21 @@ contract LibraryWrapper is StrategyTypes{ IOracle public oracle; IStrategy public strategy; + IStrategyController public controller; - constructor(address oracle_, address strategy_) public { + constructor(address oracle_, address strategy_, address controller_) public { oracle = IOracle(oracle_); strategy = IStrategy(strategy_); + controller = IStrategyController(controller_); } function isBalanced() external view returns (bool balanced) { - (balanced,,) = ControllerLibrary.verifyBalance(strategy, oracle, true); // outer=true + (balanced,,) = ControllerLibrary.verifyBalance(strategy, oracle, controller.rebalanceThresholdScalar()); return balanced; } function isBalancedInner() external view returns (bool balanced) { - (balanced,,) = ControllerLibrary.verifyBalance(strategy, oracle, false); // outer=false + (balanced,,) = ControllerLibrary.verifyBalance(strategy, oracle, 0); return balanced; } diff --git a/errors/errors.json b/errors/errors.json index 1d3e2da9..a653d541 100644 --- a/errors/errors.json +++ b/errors/errors.json @@ -44,18 +44,20 @@ "1bb63a90056c10": "Timelock active", "1bb63a90056c11": "Strategy already open", "1bb63a90056c12": "Strategy already set", - "1bb63a90056c13": "Ambiguous amount", - "1bb63a90056c14": "Not balanced", - "1bb63a90056c15": "Too much slippage", - "1bb63a90056c16": "Out of bounds", - "1bb63a90056c17": "Fee too high", - "1bb63a90056c18": "Timelock is too long", - "1bb63a90056c19": "Not initialized", - "1bb63a90056c1a": "Not approved", - "1bb63a90056c1b": "Not manager", - "1bb63a90056c1c": "Not manager", - "1bb63a90056c1d": "Strategy cannot change", - "1bb63a90056c1e": "Not WETH" + "1bb63a90056c13": "Not factory", + "1bb63a90056c14": "updateRebalanceParameters timelock not ready.", + "1bb63a90056c15": "Ambiguous amount", + "1bb63a90056c16": "Not balanced", + "1bb63a90056c17": "Too much slippage", + "1bb63a90056c18": "Out of bounds", + "1bb63a90056c19": "Fee too high", + "1bb63a90056c1a": "Timelock is too long", + "1bb63a90056c1b": "Not initialized", + "1bb63a90056c1c": "Not approved", + "1bb63a90056c1d": "Not manager", + "1bb63a90056c1e": "Not manager", + "1bb63a90056c1f": "Strategy cannot change", + "1bb63a90056c20": "Not WETH" } }, { diff --git a/test/aave-adapter.ts b/test/aave-adapter.ts index fadfb5ce..37578206 100644 --- a/test/aave-adapter.ts +++ b/test/aave-adapter.ts @@ -228,7 +228,7 @@ describe('AaveAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -548,7 +548,7 @@ describe('AaveAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -631,7 +631,7 @@ describe('AaveAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -721,7 +721,7 @@ describe('AaveAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -812,7 +812,11 @@ describe('AaveAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - let metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) + let metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy( + oracle.address, + strategyAddress, + controller.address + ) await metaWrapper.deployed() //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) diff --git a/test/balancer-adapter.ts b/test/balancer-adapter.ts index f1c03c51..760cb354 100644 --- a/test/balancer-adapter.ts +++ b/test/balancer-adapter.ts @@ -89,7 +89,7 @@ describe('BalancerAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems, weth) diff --git a/test/batch-router.ts b/test/batch-router.ts index 8d0be769..b654e353 100644 --- a/test/batch-router.ts +++ b/test/batch-router.ts @@ -123,7 +123,7 @@ describe('BatchDepositRouter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() await displayBalances( diff --git a/test/compound-adapter.ts b/test/compound-adapter.ts index b50ad520..ec3202b5 100644 --- a/test/compound-adapter.ts +++ b/test/compound-adapter.ts @@ -149,7 +149,7 @@ describe('CompoundAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() // await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) diff --git a/test/curve-adapter.ts b/test/curve-adapter.ts index 77b28d29..ecc00abb 100644 --- a/test/curve-adapter.ts +++ b/test/curve-adapter.ts @@ -366,7 +366,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -597,7 +597,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -729,7 +729,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) @@ -820,7 +820,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) diff --git a/test/experimental-strategy.ts b/test/experimental-strategy.ts index 71be130a..440539c5 100644 --- a/test/experimental-strategy.ts +++ b/test/experimental-strategy.ts @@ -139,7 +139,7 @@ describe('Experimental Strategy', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) + wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) diff --git a/test/flash-loan.ts b/test/flash-loan.ts index fd29ceca..3cbb4811 100644 --- a/test/flash-loan.ts +++ b/test/flash-loan.ts @@ -111,7 +111,7 @@ describe('Flash Loan', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) + wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyConfig.strategyItems, weth) diff --git a/test/kyber-adapter.ts b/test/kyber-adapter.ts index 3c5cfcee..302a0343 100644 --- a/test/kyber-adapter.ts +++ b/test/kyber-adapter.ts @@ -119,7 +119,7 @@ describe('KyberSwapAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) diff --git a/test/leverage-adapter.ts b/test/leverage-adapter.ts index 7cbc4ae4..a5ec1ea4 100644 --- a/test/leverage-adapter.ts +++ b/test/leverage-adapter.ts @@ -159,7 +159,7 @@ describe('Leverage2XAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) + wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address, controller.address) await wrapper.deployed() await displayBalances( @@ -245,7 +245,7 @@ describe('Leverage2XAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) + wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address, controller.address) await wrapper.deployed() await displayBalances( diff --git a/test/library.ts b/test/library.ts index 13e2a002..7fa703ca 100644 --- a/test/library.ts +++ b/test/library.ts @@ -87,7 +87,7 @@ describe('ControllerLibrary', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) diff --git a/test/meta-strategy-adapter.ts b/test/meta-strategy-adapter.ts index d11f641f..e088b2fa 100644 --- a/test/meta-strategy-adapter.ts +++ b/test/meta-strategy-adapter.ts @@ -120,7 +120,11 @@ describe('MetaStrategyAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - basicWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) + basicWrapper = await LibraryWrapper.connect(accounts[0]).deploy( + oracle.address, + strategyAddress, + controller.address + ) await basicWrapper.deployed() expect(await basicWrapper.isBalanced()).to.equal(true) @@ -160,7 +164,11 @@ describe('MetaStrategyAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) + metaWrapper = await LibraryWrapper.connect(accounts[0]).deploy( + oracle.address, + strategyAddress, + controller.address + ) await metaWrapper.deployed() //await displayBalances(basicWrapper, basicStrategyItems.map((item) => item.item), weth) @@ -222,7 +230,11 @@ describe('MetaStrategyAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - metaMetaWrapper = await LibraryWrapper.connect(accounts[0]).deploy(oracle.address, strategyAddress) + metaMetaWrapper = await LibraryWrapper.connect(accounts[0]).deploy( + oracle.address, + strategyAddress, + controller.address + ) await metaMetaWrapper.deployed() }) diff --git a/test/multicall-router.ts b/test/multicall-router.ts index 065ac34d..2baaad3e 100644 --- a/test/multicall-router.ts +++ b/test/multicall-router.ts @@ -133,7 +133,7 @@ describe('MulticallRouter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) + wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems, weth) diff --git a/test/reentrancy.ts b/test/reentrancy.ts index d9073c5c..b0658b5c 100644 --- a/test/reentrancy.ts +++ b/test/reentrancy.ts @@ -101,7 +101,7 @@ describe('Reentrancy ', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address) + wrapper = await LibraryWrapper.deploy(oracle.address, strategy.address, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems, weth) diff --git a/test/social-strategy.ts b/test/social-strategy.ts index dae2f21e..9508b9e2 100644 --- a/test/social-strategy.ts +++ b/test/social-strategy.ts @@ -92,7 +92,7 @@ describe('StrategyController - Social', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) diff --git a/test/strategy-controller.ts b/test/strategy-controller.ts index 4e606e13..1354ced6 100644 --- a/test/strategy-controller.ts +++ b/test/strategy-controller.ts @@ -272,7 +272,7 @@ describe('StrategyController', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) diff --git a/test/strategy-factory.ts b/test/strategy-factory.ts index 1f4d40a0..6be47b97 100644 --- a/test/strategy-factory.ts +++ b/test/strategy-factory.ts @@ -15,6 +15,7 @@ import { isRevertedWith } from '../lib/errors' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' const { constants, getSigners } = ethers const { AddressZero, MaxUint256, WeiPerEther } = constants +import { increaseTime } from '../lib/utils' const chai = require('chai') import { solidity } from 'ethereum-waffle' @@ -124,6 +125,34 @@ describe('StrategyProxyFactory', function () { expect(await controller.oracle()).to.equal(newOracle.address) }) + it('Should update rebalanceParameters', async function () { + expect( + await isRevertedWith( + // sanity check + controller.connect(accounts[5]).finalizeRebalanceParameters(), + 'updateRebalanceParameters timelock not ready.', + 'StrategyController.sol' + ) + ).to.be.true + let rebalanceTimelockPeriod = 5 * 60 + let rebalanceThresholdScalar = 1000 + await strategyFactory + .connect(accounts[10]) + .updateRebalanceParameters(rebalanceTimelockPeriod, rebalanceThresholdScalar) + await increaseTime(5 * 60 + 1) + await controller.connect(accounts[5]).finalizeRebalanceParameters() + expect(await controller.callStatic.rebalanceThresholdScalar()).to.eq(rebalanceThresholdScalar) + + // settle on this value + rebalanceThresholdScalar = 2000 + await strategyFactory + .connect(accounts[10]) + .updateRebalanceParameters(rebalanceTimelockPeriod, rebalanceThresholdScalar) + await increaseTime(5 * 60 + 1) + await controller.connect(accounts[5]).finalizeRebalanceParameters() + expect(await controller.callStatic.rebalanceThresholdScalar()).to.eq(rebalanceThresholdScalar) + }) + it('Should fail to update whitelist: not owner', async function () { await expect(strategyFactory.connect(accounts[1]).updateWhitelist(newWhitelist.address)).to.be.revertedWith( 'Not owner' diff --git a/test/synthetix-adapter.ts b/test/synthetix-adapter.ts index d89ee538..3bdb8fb2 100644 --- a/test/synthetix-adapter.ts +++ b/test/synthetix-adapter.ts @@ -218,7 +218,7 @@ describe('SynthetixAdapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) diff --git a/test/token-registry.ts b/test/token-registry.ts index 1612d7db..ab65592c 100644 --- a/test/token-registry.ts +++ b/test/token-registry.ts @@ -96,7 +96,7 @@ describe('TokenRegistry', function () { ControllerLibrary: this.controllerLibrary.address, }, }) - this.wrapper = await LibraryWrapper.deploy(this.oracle.address, strategyAddress) + this.wrapper = await LibraryWrapper.deploy(this.oracle.address, strategyAddress, this.controller.address) await this.wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) diff --git a/test/uniswap-v3-adapter.ts b/test/uniswap-v3-adapter.ts index de8c2ef4..7e38a06b 100644 --- a/test/uniswap-v3-adapter.ts +++ b/test/uniswap-v3-adapter.ts @@ -230,7 +230,7 @@ describe('UniswapV3Adapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) diff --git a/test/weird-erc20s.ts b/test/weird-erc20s.ts index fad5c83a..944ddb24 100644 --- a/test/weird-erc20s.ts +++ b/test/weird-erc20s.ts @@ -161,7 +161,7 @@ describe('Weird ERC20s', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) @@ -207,7 +207,7 @@ describe('Weird ERC20s', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) @@ -252,7 +252,7 @@ describe('Weird ERC20s', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) @@ -297,7 +297,7 @@ describe('Weird ERC20s', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) @@ -342,7 +342,7 @@ describe('Weird ERC20s', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) @@ -387,7 +387,7 @@ describe('Weird ERC20s', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) @@ -432,7 +432,7 @@ describe('Weird ERC20s', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() expect(await wrapper.isBalanced()).to.equal(true) diff --git a/test/x-fees.ts b/test/x-fees.ts index 18453781..03849a31 100644 --- a/test/x-fees.ts +++ b/test/x-fees.ts @@ -117,7 +117,7 @@ describe('StrategyToken Fees', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() }) diff --git a/test/yearn-adapter.ts b/test/yearn-adapter.ts index 4b39a58f..0a5887c6 100644 --- a/test/yearn-adapter.ts +++ b/test/yearn-adapter.ts @@ -144,7 +144,7 @@ describe('YEarnV2Adapter', function () { ControllerLibrary: controllerLibrary.address, }, }) - wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress) + wrapper = await LibraryWrapper.deploy(oracle.address, strategyAddress, controller.address) await wrapper.deployed() //await displayBalances(wrapper, strategyItems.map((item) => item.item), weth) From 817baf3113403f374e734df90e973a41a5489fc0 Mon Sep 17 00:00:00 2001 From: George Carder Date: Tue, 26 Jul 2022 10:02:01 -0700 Subject: [PATCH 16/17] keep library functions disjoint --- contracts/libraries/ControllerLibrary.sol | 12 ------------ contracts/test/LibraryWrapper.sol | 7 ++++--- test/aave-adapter.ts | 7 ++++++- test/balancer-adapter.ts | 3 ++- test/batch-router.ts | 3 ++- test/compound-adapter.ts | 3 ++- test/curve-adapter.ts | 6 +++++- test/experimental-strategy.ts | 3 ++- test/flash-loan.ts | 1 + test/kyber-adapter.ts | 3 ++- test/leverage-adapter.ts | 2 ++ test/library.ts | 3 ++- test/live-estimates.ts | 11 +---------- test/meta-strategy-adapter.ts | 5 ++++- test/multicall-router.ts | 1 + test/reentrancy.ts | 1 + test/social-strategy.ts | 3 ++- test/strategy-admin.ts | 3 +-- test/strategy-controller.ts | 3 ++- test/strategy-factory.ts | 6 ++---- test/strategy-token.ts | 4 +--- test/synthetix-adapter.ts | 3 ++- test/token-registry.ts | 3 ++- test/uniswap-v3-adapter.ts | 6 ++++-- test/weird-erc20s.ts | 9 ++++++++- test/x-fees.ts | 3 ++- test/yearn-adapter.ts | 3 ++- 27 files changed, 65 insertions(+), 52 deletions(-) diff --git a/contracts/libraries/ControllerLibrary.sol b/contracts/libraries/ControllerLibrary.sol index 27d2afee..88fa2ab9 100644 --- a/contracts/libraries/ControllerLibrary.sol +++ b/contracts/libraries/ControllerLibrary.sol @@ -350,18 +350,6 @@ library ControllerLibrary { return address(this); } - function getExpectedTokenValue( - uint256 total, - address strategy, - address token - ) public view returns (int256) { - return StrategyLibrary.getExpectedTokenValue(total, strategy, token); - } - - function getRange(int256 expectedValue, uint256 threshold) public pure returns (int256) { - return StrategyLibrary.getRange(expectedValue, threshold); - } - // @notice Checks that there is no debt remaining for tokens that are no longer part of the strategy function verifyFormerDebt(address strategy, address[] memory newDebt, address[] memory formerDebt) public view { formerDebt = formerDebt.without(newDebt); diff --git a/contracts/test/LibraryWrapper.sol b/contracts/test/LibraryWrapper.sol index 73904304..6966ba18 100644 --- a/contracts/test/LibraryWrapper.sol +++ b/contracts/test/LibraryWrapper.sol @@ -9,6 +9,7 @@ import "../interfaces/IOracle.sol"; import "../interfaces/IStrategy.sol"; import "../interfaces/IStrategyController.sol"; import "../libraries/ControllerLibrary.sol"; +import "../libraries/StrategyLibrary.sol"; import "../helpers/StrategyTypes.sol"; contract LibraryWrapper is StrategyTypes{ @@ -36,12 +37,12 @@ contract LibraryWrapper is StrategyTypes{ } function getRange(int256 expectedValue, uint256 range) external pure returns (int256) { - return ControllerLibrary.getRange(expectedValue, range); + return StrategyLibrary.getRange(expectedValue, range); } function getRebalanceRange(int256 expectedValue) external view returns (int256) { uint256 range = strategy.rebalanceThreshold(); - return ControllerLibrary.getRange(expectedValue, range); + return StrategyLibrary.getRange(expectedValue, range); } function getStrategyValue() external view returns (uint256) { @@ -54,7 +55,7 @@ contract LibraryWrapper is StrategyTypes{ } function getExpectedTokenValue(uint256 total, address token) external view returns (int256) { - return ControllerLibrary.getExpectedTokenValue(total, address(strategy), token); + return StrategyLibrary.getExpectedTokenValue(total, address(strategy), token); } function _getTokenValue(IStrategy s, address token) internal view returns (int256) { diff --git a/test/aave-adapter.ts b/test/aave-adapter.ts index 37578206..70b80f18 100644 --- a/test/aave-adapter.ts +++ b/test/aave-adapter.ts @@ -100,7 +100,7 @@ describe('AaveAdapter', function () { uniswapAdapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) - fullRouter = await deployFullRouter(accounts[0], aaveAddressProvider, controller, controllerLibrary) + fullRouter = await deployFullRouter(accounts[0], aaveAddressProvider, controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(fullRouter.address) multicallRouter = await deployMulticallRouter(accounts[0], controller) await whitelist.connect(accounts[0]).approve(multicallRouter.address) @@ -225,6 +225,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -545,6 +546,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -628,6 +630,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -718,6 +721,7 @@ describe('AaveAdapter', function () { strategy = await Strategy.attach(strategyAddress) const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -809,6 +813,7 @@ describe('AaveAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/balancer-adapter.ts b/test/balancer-adapter.ts index 760cb354..0400ebc4 100644 --- a/test/balancer-adapter.ts +++ b/test/balancer-adapter.ts @@ -51,7 +51,7 @@ describe('BalancerAdapter', function () { balancerAdapter = await deployer.deployBalancerAdapter(accounts[0], balancerRegistry, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) await whitelist.connect(accounts[0]).approve(balancerAdapter.address) - router = await deployer.deployLoopRouter(accounts[0], controller, controllerLibrary) + router = await deployer.deployLoopRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) }) @@ -86,6 +86,7 @@ describe('BalancerAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/batch-router.ts b/test/batch-router.ts index b654e353..237ca4f3 100644 --- a/test/batch-router.ts +++ b/test/batch-router.ts @@ -54,7 +54,7 @@ describe('BatchDepositRouter', function () { controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) - router = await deployBatchDepositRouter(accounts[0], controller, controllerLibrary) + router = await deployBatchDepositRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) }) @@ -120,6 +120,7 @@ describe('BatchDepositRouter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/compound-adapter.ts b/test/compound-adapter.ts index ec3202b5..7b489e63 100644 --- a/test/compound-adapter.ts +++ b/test/compound-adapter.ts @@ -61,7 +61,7 @@ describe('CompoundAdapter', function () { await tokens.registerTokens(accounts[0], strategyFactory) - router = await deployLoopRouter(accounts[0], controller, controllerLibrary) + router = await deployLoopRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapAdapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapAdapter.address) @@ -146,6 +146,7 @@ describe('CompoundAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/curve-adapter.ts b/test/curve-adapter.ts index ecc00abb..99450e97 100644 --- a/test/curve-adapter.ts +++ b/test/curve-adapter.ts @@ -96,7 +96,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { ) const addressProvider = new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], accounts[0]) - router = await deployLoopRouter(accounts[0], controller, controllerLibrary) + router = await deployLoopRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapV2Adapter = await deployUniswapV2Adapter(accounts[0], uniswapV2Factory, weth) await whitelist.connect(accounts[0]).approve(uniswapV2Adapter.address) @@ -363,6 +363,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -594,6 +595,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -726,6 +728,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -817,6 +820,7 @@ describe('CurveLPAdapter + CurveGaugeAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/experimental-strategy.ts b/test/experimental-strategy.ts index 440539c5..174e1905 100644 --- a/test/experimental-strategy.ts +++ b/test/experimental-strategy.ts @@ -72,7 +72,7 @@ describe('Experimental Strategy', function () { const { tokenRegistry, curveDepositZapRegistry, chainlinkRegistry } = platform.oracles.registries await tokens.registerTokens(accounts[0], strategyFactory, undefined, chainlinkRegistry, curveDepositZapRegistry) - router = await deployFullRouter(accounts[0], aaveAddressProvider, controller, controllerLibrary) + router = await deployFullRouter(accounts[0], aaveAddressProvider, controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapV2Adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(uniswapV2Adapter.address) @@ -136,6 +136,7 @@ describe('Experimental Strategy', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/flash-loan.ts b/test/flash-loan.ts index 3cbb4811..6b46cc88 100644 --- a/test/flash-loan.ts +++ b/test/flash-loan.ts @@ -108,6 +108,7 @@ describe('Flash Loan', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/kyber-adapter.ts b/test/kyber-adapter.ts index 302a0343..f2b10e32 100644 --- a/test/kyber-adapter.ts +++ b/test/kyber-adapter.ts @@ -75,7 +75,7 @@ describe('KyberSwapAdapter', function () { curveDepositZapRegistry ) - router = await deployLoopRouter(owner, controller, controllerLibrary) + router = await deployLoopRouter(owner, controller, platform.strategyLibrary) await whitelist.connect(owner).approve(router.address) kyberAdapter = await deployKyberSwapAdapter(owner, kyberFactory, kyberRouter, weth) await whitelist.connect(owner).approve(kyberAdapter.address) @@ -116,6 +116,7 @@ describe('KyberSwapAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/leverage-adapter.ts b/test/leverage-adapter.ts index a5ec1ea4..cbb972b6 100644 --- a/test/leverage-adapter.ts +++ b/test/leverage-adapter.ts @@ -156,6 +156,7 @@ describe('Leverage2XAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -242,6 +243,7 @@ describe('Leverage2XAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/library.ts b/test/library.ts index 7fa703ca..9b583455 100644 --- a/test/library.ts +++ b/test/library.ts @@ -42,7 +42,7 @@ describe('ControllerLibrary', function () { controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) - router = await deployLoopRouter(accounts[0], controller, controllerLibrary) + router = await deployLoopRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) const positions = [ @@ -84,6 +84,7 @@ describe('ControllerLibrary', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/live-estimates.ts b/test/live-estimates.ts index 5a7953f3..02d866ed 100644 --- a/test/live-estimates.ts +++ b/test/live-estimates.ts @@ -7,7 +7,6 @@ import { getLiveContracts } from '../lib/mainnet' import { increaseTime } from '../lib/utils' import { deployFullRouter } from '../lib/deploy' import { DIVISOR, MAINNET_ADDRESSES } from '../lib/constants' -import { createLink, linkBytecode } from '../lib/link' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import StrategyClaim from '../artifacts/contracts/libraries/StrategyClaim.sol/StrategyClaim.json' @@ -92,14 +91,6 @@ describe('Live Estimates', function () { eNFTP = await Strategy.attach('16f7a9c3449f9c67e8c7e8f30ae1ee5d7b8ed10d') eETH2X = await Strategy.attach('0x81cddbf4a9d21cf52ef49bda5e5d5c4ae2e40b3e') - const strategyLibraryLink = createLink(StrategyLibrary, enso.platform.strategyLibrary.address) - const controllerLibrary = await waffle.deployContract( - accounts[0], - linkBytecode(ControllerLibrary, [strategyLibraryLink]), - [] - ) - await controllerLibrary.deployed() - // Impersonate owner await network.provider.request({ method: 'hardhat_impersonateAccount', @@ -111,7 +102,7 @@ describe('Live Estimates', function () { accounts[0], new Contract(MAINNET_ADDRESSES.AAVE_ADDRESS_PROVIDER, [], accounts[0]), controller, - controllerLibrary + enso.platform.strategyLibrary ) // Whitelist await enso.platform.administration.whitelist.connect(owner).approve(router.address) diff --git a/test/meta-strategy-adapter.ts b/test/meta-strategy-adapter.ts index e088b2fa..a1704940 100644 --- a/test/meta-strategy-adapter.ts +++ b/test/meta-strategy-adapter.ts @@ -82,7 +82,7 @@ describe('MetaStrategyAdapter', function () { oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist controllerLibrary = platform.controllerLibrary - loopRouter = await deployLoopRouter(accounts[0], controller, controllerLibrary) + loopRouter = await deployLoopRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(loopRouter.address) multicallRouter = await deployMulticallRouter(accounts[0], controller) await whitelist.connect(accounts[0]).approve(multicallRouter.address) @@ -117,6 +117,7 @@ describe('MetaStrategyAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -161,6 +162,7 @@ describe('MetaStrategyAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -227,6 +229,7 @@ describe('MetaStrategyAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/multicall-router.ts b/test/multicall-router.ts index 2baaad3e..29f4bfac 100644 --- a/test/multicall-router.ts +++ b/test/multicall-router.ts @@ -130,6 +130,7 @@ describe('MulticallRouter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/reentrancy.ts b/test/reentrancy.ts index b0658b5c..769021fe 100644 --- a/test/reentrancy.ts +++ b/test/reentrancy.ts @@ -98,6 +98,7 @@ describe('Reentrancy ', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/social-strategy.ts b/test/social-strategy.ts index 9508b9e2..1717082e 100644 --- a/test/social-strategy.ts +++ b/test/social-strategy.ts @@ -62,7 +62,7 @@ describe('StrategyController - Social', function () { controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[0], uniswapFactory, weth) await whitelist.connect(accounts[0]).approve(adapter.address) - router = await deployLoopRouter(accounts[0], controller, controllerLibrary) + router = await deployLoopRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) }) @@ -89,6 +89,7 @@ describe('StrategyController - Social', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/strategy-admin.ts b/test/strategy-admin.ts index cafe5955..ba3511f6 100644 --- a/test/strategy-admin.ts +++ b/test/strategy-admin.ts @@ -47,13 +47,12 @@ describe('StrategyProxyAdmin', function () { controller = platform.controller strategyFactory = platform.strategyFactory whitelist = platform.administration.whitelist - const controllerLibrary = platform.controllerLibrary const strategyAdminAddress = await strategyFactory.admin() const StrategyAdmin = await getContractFactory('StrategyProxyAdmin') strategyAdmin = await StrategyAdmin.attach(strategyAdminAddress) adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, controllerLibrary) + router = await deployLoopRouter(accounts[10], controller, platform.strategyLibrary) await whitelist.connect(accounts[10]).approve(router.address) }) diff --git a/test/strategy-controller.ts b/test/strategy-controller.ts index 1354ced6..6da93aa5 100644 --- a/test/strategy-controller.ts +++ b/test/strategy-controller.ts @@ -65,7 +65,7 @@ describe('StrategyController', function () { adapter = await deployUniswapV2Adapter(owner, uniswapFactory, weth) await whitelist.connect(owner).approve(adapter.address) - router = await deployLoopRouter(owner, controller, controllerLibrary) + router = await deployLoopRouter(owner, controller, platform.strategyLibrary) await whitelist.connect(owner).approve(router.address) }) @@ -269,6 +269,7 @@ describe('StrategyController', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/strategy-factory.ts b/test/strategy-factory.ts index 6be47b97..e3866a89 100644 --- a/test/strategy-factory.ts +++ b/test/strategy-factory.ts @@ -37,7 +37,6 @@ describe('StrategyProxyFactory', function () { newOracle: Contract, newWhitelist: Contract, whitelist: Contract, - controllerLibrary: Contract, adapter: Contract, newRouter: Contract, strategy: Contract, @@ -54,10 +53,9 @@ describe('StrategyProxyFactory', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, controllerLibrary) + router = await deployLoopRouter(accounts[10], controller, platform.strategyLibrary) await whitelist.connect(accounts[10]).approve(router.address) }) @@ -71,7 +69,7 @@ describe('StrategyProxyFactory', function () { newFactory = platform.strategyFactory newOracle = platform.oracles.ensoOracle newWhitelist = platform.administration.whitelist - newRouter = await deployLoopRouter(accounts[10], controller, controllerLibrary) + newRouter = await deployLoopRouter(accounts[10], controller, platform.strategyLibrary) await newWhitelist.connect(accounts[10]).approve(adapter.address) await newWhitelist.connect(accounts[10]).approve(newRouter.address) newImplementationAddress = await newFactory.implementation() diff --git a/test/strategy-token.ts b/test/strategy-token.ts index ab397317..d5c3b3f9 100644 --- a/test/strategy-token.ts +++ b/test/strategy-token.ts @@ -33,7 +33,6 @@ describe('StrategyToken', function () { whitelist: Contract, router: Contract, oracle: Contract, - controllerLibrary: Contract, adapter: Contract, strategy: Contract, strategyItems: StrategyItem[], @@ -50,10 +49,9 @@ describe('StrategyToken', function () { strategyFactory = platform.strategyFactory oracle = platform.oracles.ensoOracle whitelist = platform.administration.whitelist - controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, controllerLibrary) + router = await deployLoopRouter(accounts[10], controller, platform.strategyLibrary) await whitelist.connect(accounts[10]).approve(router.address) const Strategy = await platform.getStrategyContractFactory() const strategyImplementation = await Strategy.connect(accounts[10]).deploy( diff --git a/test/synthetix-adapter.ts b/test/synthetix-adapter.ts index 3bdb8fb2..114fc0eb 100644 --- a/test/synthetix-adapter.ts +++ b/test/synthetix-adapter.ts @@ -94,7 +94,7 @@ describe('SynthetixAdapter', function () { accounts[10], new Contract(AddressZero, [], accounts[0]), controller, - controllerLibrary + platform.strategyLibrary ) await whitelist.connect(accounts[10]).approve(router.address) uniswapAdapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) @@ -215,6 +215,7 @@ describe('SynthetixAdapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/token-registry.ts b/test/token-registry.ts index ab65592c..eb6497bc 100644 --- a/test/token-registry.ts +++ b/test/token-registry.ts @@ -48,7 +48,7 @@ describe('TokenRegistry', function () { curveDepositZapRegistry ) - this.router = await deployLoopRouter(this.accounts[0], this.controller, this.controllerLibrary) + this.router = await deployLoopRouter(this.accounts[0], this.controller, platform.strategyLibrary) await this.whitelist.connect(this.accounts[0]).approve(this.router.address) this.uniswapAdapter = await deployUniswapV2Adapter(this.accounts[0], this.uniswapFactory, this.weth) await this.whitelist.connect(this.accounts[0]).approve(this.uniswapAdapter.address) @@ -93,6 +93,7 @@ describe('TokenRegistry', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: this.controllerLibrary.address, }, }) diff --git a/test/uniswap-v3-adapter.ts b/test/uniswap-v3-adapter.ts index 7e38a06b..ce1d7d19 100644 --- a/test/uniswap-v3-adapter.ts +++ b/test/uniswap-v3-adapter.ts @@ -33,6 +33,7 @@ let tokens: Contract[], adapter: Contract, router: Contract, strategy: Contract, + strategyLibrary: Contract, strategyClaim: Contract, wrapper: Contract, uniswapRegistry: Contract, @@ -124,7 +125,7 @@ describe('UniswapV3Adapter', function () { const controllerAddress = await platformProxyAdmin.controller() const factoryAddress = await platformProxyAdmin.factory() - const strategyLibrary = await waffle.deployContract(accounts[0], StrategyLibrary, []) + strategyLibrary = await waffle.deployContract(accounts[0], StrategyLibrary, []) await strategyLibrary.deployed() const strategyLibraryLink = createLink(StrategyLibrary, strategyLibrary.address) @@ -179,7 +180,7 @@ describe('UniswapV3Adapter', function () { adapter = await deployUniswapV3Adapter(owner, uniswapRegistry, uniswapRouter, weth) await whitelist.connect(owner).approve(adapter.address) - router = await deployLoopRouter(accounts[0], controller, controllerLibrary) + router = await deployLoopRouter(accounts[0], controller, strategyLibrary) await whitelist.connect(owner).approve(router.address) uniswapQuoter = await deployContract(trader, Quoter, [uniswapV3Factory.address, weth.address]) @@ -227,6 +228,7 @@ describe('UniswapV3Adapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/weird-erc20s.ts b/test/weird-erc20s.ts index 944ddb24..a1282db6 100644 --- a/test/weird-erc20s.ts +++ b/test/weird-erc20s.ts @@ -128,7 +128,7 @@ describe('Weird ERC20s', function () { controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(accounts[10], uniswapFactory, weth) await whitelist.connect(accounts[10]).approve(adapter.address) - router = await deployLoopRouter(accounts[10], controller, controllerLibrary) + router = await deployLoopRouter(accounts[10], controller, platform.strategyLibrary) await whitelist.connect(accounts[10]).approve(router.address) // remove weth from weird token list @@ -158,6 +158,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -204,6 +205,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -249,6 +251,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -294,6 +297,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -339,6 +343,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -384,6 +389,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) @@ -429,6 +435,7 @@ describe('Weird ERC20s', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/x-fees.ts b/test/x-fees.ts index 03849a31..de72be5d 100644 --- a/test/x-fees.ts +++ b/test/x-fees.ts @@ -69,7 +69,7 @@ describe('StrategyToken Fees', function () { controllerLibrary = platform.controllerLibrary adapter = await deployUniswapV2Adapter(owner, uniswapFactory, weth) await whitelist.connect(owner).approve(adapter.address) - router = await deployLoopRouter(owner, controller, controllerLibrary) + router = await deployLoopRouter(owner, controller, platform.strategyLibrary) await whitelist.connect(owner).approve(router.address) }) @@ -114,6 +114,7 @@ describe('StrategyToken Fees', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) diff --git a/test/yearn-adapter.ts b/test/yearn-adapter.ts index 0a5887c6..6512bead 100644 --- a/test/yearn-adapter.ts +++ b/test/yearn-adapter.ts @@ -75,7 +75,7 @@ describe('YEarnV2Adapter', function () { const addressProvider = new Contract(MAINNET_ADDRESSES.CURVE_ADDRESS_PROVIDER, [], accounts[0]) const whitelist = platform.administration.whitelist - router = await deployLoopRouter(accounts[0], controller, controllerLibrary) + router = await deployLoopRouter(accounts[0], controller, platform.strategyLibrary) await whitelist.connect(accounts[0]).approve(router.address) uniswapV2Adapter = await deployUniswapV2Adapter(accounts[0], uniswapV2Factory, weth) await whitelist.connect(accounts[0]).approve(uniswapV2Adapter.address) @@ -141,6 +141,7 @@ describe('YEarnV2Adapter', function () { const LibraryWrapper = await getContractFactory('LibraryWrapper', { libraries: { + StrategyLibrary: platform.strategyLibrary.address, ControllerLibrary: controllerLibrary.address, }, }) From 75a81356675c7ea2054312c3f6496ddec2b61419 Mon Sep 17 00:00:00 2001 From: George Carder Date: Tue, 26 Jul 2022 10:21:03 -0700 Subject: [PATCH 17/17] update live-estimates test, passing --- test/live-estimates.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/live-estimates.ts b/test/live-estimates.ts index 02d866ed..82d040ec 100644 --- a/test/live-estimates.ts +++ b/test/live-estimates.ts @@ -10,8 +10,6 @@ import { DIVISOR, MAINNET_ADDRESSES } from '../lib/constants' import WETH9 from '@uniswap/v2-periphery/build/WETH9.json' import StrategyClaim from '../artifacts/contracts/libraries/StrategyClaim.sol/StrategyClaim.json' -import ControllerLibrary from '../artifacts/contracts/libraries/ControllerLibrary.sol/ControllerLibrary.json' -import StrategyLibrary from '../artifacts/contracts/libraries/StrategyLibrary.sol/StrategyLibrary.json' const { constants, getSigners, getContractFactory } = ethers const { WeiPerEther } = constants