-
Notifications
You must be signed in to change notification settings - Fork 386
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce PoolRecoveryEnabler (#2013)
* Add PoolRecoveryEnabler * Add tests * Apply suggestions from code review Co-authored-by: EndymionJkb <[email protected]> * lint * Rename contract and function * Add docs * Apply suggestions from code review Co-authored-by: EndymionJkb <[email protected]> * Apply suggestions from code review Co-authored-by: Tom French <[email protected]> Co-authored-by: Jeffrey Bennett <[email protected]> Co-authored-by: Tom French <[email protected]>
- Loading branch information
1 parent
751f00b
commit d701551
Showing
6 changed files
with
404 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
pragma solidity ^0.7.0; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@balancer-labs/v2-interfaces/contracts/pool-utils/IBasePoolFactory.sol"; | ||
import "@balancer-labs/v2-interfaces/contracts/pool-utils/IRateProviderPool.sol"; | ||
import "@balancer-labs/v2-interfaces/contracts/pool-utils/IRecoveryMode.sol"; | ||
|
||
import "@balancer-labs/v2-solidity-utils/contracts/helpers/SingletonAuthentication.sol"; | ||
import "@balancer-labs/v2-solidity-utils/contracts/openzeppelin/EnumerableSet.sol"; | ||
|
||
/** | ||
* @dev This contract allows anyone to check a given Pool's rate providers and put the Pool into recovery mode | ||
* if any are reverting on `getRate`. This allows LPs to exit promptly, and also helps off-chain mechanisms | ||
* identify failed pools and prevent further traffic from being routed to them (since in this state swap operations | ||
* would fail). | ||
*/ | ||
contract PoolRecoveryHelper is SingletonAuthentication { | ||
using EnumerableSet for EnumerableSet.AddressSet; | ||
|
||
EnumerableSet.AddressSet private _factories; | ||
|
||
constructor(IVault vault, address[] memory initialFactories) SingletonAuthentication(vault) { | ||
for (uint256 i = 0; i < initialFactories.length; ++i) { | ||
require(_factories.add(initialFactories[i]), "Duplicate initial factory"); | ||
} | ||
} | ||
|
||
/** | ||
* @notice Adds a Pool Factory to the helper. Only Pools created from factories added via this function can be | ||
* passed to `enableRecoveryMode()`. | ||
*/ | ||
function addPoolFactory(address factory) external authenticate { | ||
require(_factories.add(factory), "Duplicate factory"); | ||
} | ||
|
||
/** | ||
* @notice Removes a Pool Factory from the helper. | ||
*/ | ||
function removePoolFactory(address factory) external authenticate { | ||
require(_factories.remove(factory), "Non-existent factory"); | ||
} | ||
|
||
/** | ||
* @notice Returns the total number of Pool Factories. | ||
*/ | ||
function getFactoryCount() external view returns (uint256) { | ||
return _factories.length(); | ||
} | ||
|
||
/** | ||
* @notice Returns the address of a Pool Factory at an index between 0 and the return value of `getFactoryCount()`. | ||
*/ | ||
function getFactoryAtIndex(uint256 index) external view returns (IBasePoolFactory) { | ||
return IBasePoolFactory(_factories.at(index)); | ||
} | ||
|
||
/** | ||
* @notice Returns true if the Pool has been created from a known factory. | ||
*/ | ||
function isPoolFromKnownFactory(address pool) public view returns (bool) { | ||
uint256 totalFactories = _factories.length(); | ||
for (uint256 i = 0; i < totalFactories; ++i) { | ||
IBasePoolFactory factory = IBasePoolFactory(_factories.unchecked_at(i)); | ||
|
||
if (factory.isPoolFromFactory(pool)) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
/** | ||
* @notice Enables Recovery Mode in a Pool, provided some of its rate providers are failing (i.e. `getRate()` | ||
* reverts). | ||
* | ||
* Pools that are in Recovery Mode can be exited by LPs via the special Recovery Mode Exit, which avoids any complex | ||
* computations and does not call into any external contracts, which makes it a very dependable way to retrieve the | ||
* underlying tokens. | ||
* | ||
* However, while Recovery Mode is enabled the Pool pays no protocol fees. Additionally, any protocol fees | ||
* accrued before enabling Recovery Mode will be forfeited. | ||
* | ||
* The Pool must have been created via a known Pool Factory contract. | ||
*/ | ||
function enableRecoveryMode(address pool) external { | ||
// We require that the Pools come from known factories as a sanity check since this function is permissionless. | ||
// This ensures we're actually calling legitimate Pools, and that they support both the IRateProviderPool and | ||
// IRecoveryMode interfaces. | ||
require(isPoolFromKnownFactory(pool), "Pool is not from known factory"); | ||
|
||
// The Pool will be placed in recovery mode if any of its rate providers reverts. | ||
IRateProvider[] memory rateProviders = IRateProviderPool(pool).getRateProviders(); | ||
for (uint256 i = 0; i < rateProviders.length; ++i) { | ||
if (rateProviders[i] != IRateProvider(0)) { | ||
try rateProviders[i].getRate() { | ||
// On success, we simply keep processing rate providers | ||
continue; | ||
} catch { | ||
IRecoveryMode(pool).enableRecoveryMode(); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
// If no rate providers revert, we then revert to both signal that calling this function performs no state | ||
// changes, and to help prevent these accidental wasteful calls. | ||
revert("Pool's rate providers do not revert"); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
pkg/standalone-utils/contracts/test/MockRecoveryRateProviderPool.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
pragma solidity ^0.7.0; | ||
|
||
import "@balancer-labs/v2-interfaces/contracts/pool-utils/IRateProviderPool.sol"; | ||
import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; | ||
import "@balancer-labs/v2-pool-utils/contracts/BasePoolAuthorization.sol"; | ||
import "@balancer-labs/v2-pool-utils/contracts/RecoveryMode.sol"; | ||
|
||
contract MockRecoveryRateProviderPool is IRateProviderPool, BasePoolAuthorization, RecoveryMode { | ||
IVault private immutable _vault; | ||
bool private _recoveryMode; | ||
|
||
IRateProvider[] private _rateProviders; | ||
|
||
constructor(IVault vault, IRateProvider[] memory rateProviders) | ||
Authentication(bytes32(uint256(address(this)))) | ||
BasePoolAuthorization(_DELEGATE_OWNER) | ||
{ | ||
_vault = vault; | ||
_rateProviders = rateProviders; | ||
} | ||
|
||
// IRateProviderPool | ||
|
||
function getRateProviders() external view override returns (IRateProvider[] memory) { | ||
return _rateProviders; | ||
} | ||
|
||
// BasePoolAuthorization | ||
|
||
function _getAuthorizer() internal view override returns (IAuthorizer) { | ||
return _vault.getAuthorizer(); | ||
} | ||
|
||
// Recovery Mode | ||
|
||
function inRecoveryMode() public view override returns (bool) { | ||
return _recoveryMode; | ||
} | ||
|
||
function _setRecoveryMode(bool enabled) internal override { | ||
_recoveryMode = enabled; | ||
} | ||
|
||
function _doRecoveryModeExit( | ||
uint256[] memory, | ||
uint256, | ||
bytes memory | ||
) internal override returns (uint256, uint256[] memory) {} | ||
} |
32 changes: 32 additions & 0 deletions
32
pkg/standalone-utils/contracts/test/MockRecoveryRateProviderPoolFactory.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
pragma solidity ^0.7.0; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "@balancer-labs/v2-interfaces/contracts/vault/IVault.sol"; | ||
import "@balancer-labs/v2-pool-utils/contracts/factories/BasePoolFactory.sol"; | ||
import "./MockRecoveryRateProviderPool.sol"; | ||
|
||
contract MockRecoveryRateProviderPoolFactory is BasePoolFactory { | ||
constructor(IVault _vault, IProtocolFeePercentagesProvider protocolFeeProvider) | ||
BasePoolFactory(_vault, protocolFeeProvider, type(MockRecoveryRateProviderPool).creationCode) | ||
{ | ||
// solhint-disable-previous-line no-empty-blocks | ||
} | ||
|
||
function create(IRateProvider[] memory rateProviders) external returns (address) { | ||
return _create(abi.encode(getVault(), rateProviders)); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
pkg/standalone-utils/contracts/test/MockRevertingRateProvider.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
// This program is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// This program is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
pragma solidity ^0.7.0; | ||
|
||
import "@balancer-labs/v2-interfaces/contracts/pool-utils/IRateProvider.sol"; | ||
import "@balancer-labs/v2-solidity-utils/contracts/math/FixedPoint.sol"; | ||
|
||
contract MockRevertingRateProvider is IRateProvider { | ||
uint256 private _rate; | ||
|
||
bool private _revertOnGetRate; | ||
|
||
constructor() { | ||
_rate = FixedPoint.ONE; | ||
_revertOnGetRate = false; | ||
} | ||
|
||
function getRate() external view override returns (uint256) { | ||
if (_revertOnGetRate) { | ||
revert("getRate revert"); | ||
} | ||
|
||
return _rate; | ||
} | ||
|
||
function setRevertOnGetRate(bool revertOnGetRate) external { | ||
_revertOnGetRate = revertOnGetRate; | ||
} | ||
} |
Oops, something went wrong.