-
Notifications
You must be signed in to change notification settings - Fork 43
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev/macro-audit-12' of https://github.com/PeggyJV/cella…
…r-contracts into feat/s-frax-support
- Loading branch information
Showing
13 changed files
with
1,191 additions
and
154 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.21; | ||
|
||
interface IBaseRewardPool { | ||
// Aura Pool Example: https://etherscan.deth.net/address/0x032B676d5D55e8ECbAe88ebEE0AA10fB5f72F6CB | ||
|
||
function getReward(address _account, bool _claimExtras) external returns (bool); | ||
} |
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,78 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity 0.8.21; | ||
|
||
import { BaseAdaptor, ERC20, SafeTransferLib, Cellar, PriceRouter, Math } from "src/modules/adaptors/BaseAdaptor.sol"; | ||
import { IBaseRewardPool } from "src/interfaces/external/Aura/IBaseRewardPool.sol"; | ||
import { ERC4626 } from "@solmate/mixins/ERC4626.sol"; | ||
import { ERC4626Adaptor } from "src/modules/adaptors/ERC4626Adaptor.sol"; | ||
|
||
/** | ||
* @title Aura ERC4626 Adaptor | ||
* @dev This adaptor is specifically for Aura contracts. | ||
* @notice Carries out typical ERC4626Adaptor functionality and allows Cellars to claim rewards from AURA pools | ||
* @author crispymangoes, 0xEinCodes | ||
* NOTE: Transferrance of aura-wrapped BPT is not alowed as per their contracts: ref - https://etherscan.io/address/0xdd1fe5ad401d4777ce89959b7fa587e569bf125d#code#F1#L254 | ||
*/ | ||
contract AuraERC4626Adaptor is ERC4626Adaptor { | ||
using SafeTransferLib for ERC20; | ||
using Math for uint256; | ||
|
||
//==================== Adaptor Data Specification ==================== | ||
// adaptorData = abi.encode(address auraPool) | ||
// Where: | ||
// `auraPool` is the AURA pool address position this adaptor is working with. | ||
//================= Configuration Data Specification ================= | ||
// NA | ||
//==================================================================== | ||
|
||
/** | ||
* @notice Attempted to interact with an auraPool the Cellar is not using. | ||
*/ | ||
error AuraExtrasAdaptor__AuraPoolPositionsMustBeTracked(address auraPool); | ||
|
||
//============================================ Global Functions =========================================== | ||
/** | ||
* @dev Identifier unique to this adaptor for a shared registry. | ||
* Normally the identifier would just be the address of this contract, but this | ||
* Identifier is needed during Cellar Delegate Call Operations, so getting the address | ||
* of the adaptor is more difficult. | ||
*/ | ||
function identifier() public pure virtual override returns (bytes32) { | ||
return keccak256(abi.encode("Aura ERC4626 Adaptor V 0.1")); | ||
} | ||
|
||
//============================================ Strategist Functions =========================================== | ||
|
||
/** | ||
* @notice Allows strategists to get rewards for an AuraPool. | ||
* @param _auraPool the specified AuraPool | ||
* @param _claimExtras Whether or not to claim extra rewards associated to the AuraPool (outside of rewardToken for AuraPool) | ||
*/ | ||
function getRewards(IBaseRewardPool _auraPool, bool _claimExtras) public { | ||
_validateAuraPool(address(_auraPool)); | ||
_getRewards(_auraPool, _claimExtras); | ||
} | ||
|
||
/** | ||
* @notice Validates that a given auraPool is set up as a position in the calling Cellar. | ||
* @dev This function uses `address(this)` as the address of the calling Cellar. | ||
*/ | ||
function _validateAuraPool(address _auraPool) internal view { | ||
bytes32 positionHash = keccak256(abi.encode(identifier(), false, abi.encode(_auraPool))); | ||
uint32 positionId = Cellar(address(this)).registry().getPositionHashToPositionId(positionHash); | ||
if (!Cellar(address(this)).isPositionUsed(positionId)) | ||
revert AuraExtrasAdaptor__AuraPoolPositionsMustBeTracked(_auraPool); | ||
} | ||
|
||
//============================================ Interface Helper Functions =========================================== | ||
|
||
//============================== Interface Details ============================== | ||
// It is unlikely, but AURA pool interfaces can change between versions. | ||
// To account for this, internal functions will be used in case it is needed to | ||
// implement new functionality. | ||
//=============================================================================== | ||
|
||
function _getRewards(IBaseRewardPool _auraPool, bool _claimExtras) internal virtual { | ||
_auraPool.getReward(address(this), _claimExtras); | ||
} | ||
} |
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,174 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
pragma solidity 0.8.21; | ||
|
||
import { BaseAdaptor, ERC20, SafeTransferLib, Cellar } from "src/modules/adaptors/BaseAdaptor.sol"; | ||
import { ERC4626 } from "@solmate/mixins/ERC4626.sol"; | ||
|
||
/** | ||
* @title Generic ERC4626 Vault Adaptor (basically a copy of Cellar Adaptor w/ virtual function sigs to enable inheritance and overriding). | ||
* @notice Allows Cellars to interact with other ERC4626 contracts. | ||
* @author crispymangoes, 0xEinCodes | ||
* NOTE: `CellarAdaptor.sol` was used, and is still used for nested Cellar positions for Sommelier Cellars (ERC4626 Vaults). Integrations into Aura, among other ERC4626 contracts outside of Sommelier have led to using a separate ERC4626Vault.sol file that allows more flexibility via inheritance. | ||
* NOTE: `Cellar` is still used throughout this contract at times because it is a contract made for the Sommelier protocol (has extra pricing and accounting mechanics, etc.) | ||
*/ | ||
contract ERC4626Adaptor is BaseAdaptor { | ||
using SafeTransferLib for ERC20; | ||
|
||
//==================== Adaptor Data Specification ==================== | ||
// adaptorData = abi.encode(ERC4626 erc4626Vault) | ||
// Where: | ||
// `erc4626Vault` is the underling ERC4626 this adaptor is working with | ||
//================= Configuration Data Specification ================= | ||
// configurationData = abi.encode(bool isLiquid) | ||
// Where: | ||
// `isLiquid` dictates whether the position is liquid or not | ||
// If true: | ||
// position can support use withdraws | ||
// else: | ||
// position can not support user withdraws | ||
// | ||
//==================================================================== | ||
|
||
/** | ||
* @notice Strategist attempted to interact with a erc4626Vault with no position setup for it. | ||
*/ | ||
error ERC4626Adaptor__ERC4626PositionNotUsed(address erc4626Vault); | ||
|
||
//============================================ Global Functions =========================================== | ||
/** | ||
* @dev Identifier unique to this adaptor for a shared registry. | ||
* Normally the identifier would just be the address of this contract, but this | ||
* Identifier is needed during Cellar Delegate Call Operations, so getting the address | ||
* of the adaptor is more difficult. | ||
*/ | ||
function identifier() public pure virtual override returns (bytes32) { | ||
return keccak256(abi.encode("Sommelier General ERC4626 Adaptor V 0.0")); | ||
} | ||
|
||
//============================================ Implement Base Functions =========================================== | ||
/** | ||
* @notice Cellar must approve ERC4626 position to spend its assets, then deposit into the ERC4626 position. | ||
* @param assets the amount of assets to deposit into the ERC4626 position | ||
* @param adaptorData adaptor data containining the abi encoded ERC4626 | ||
* @dev configurationData is NOT used | ||
*/ | ||
function deposit(uint256 assets, bytes memory adaptorData, bytes memory) public virtual override { | ||
// Deposit assets to `vault`. | ||
ERC4626 erc4626Vault = abi.decode(adaptorData, (ERC4626)); | ||
_verifyERC4626PositionIsUsed(address(erc4626Vault)); | ||
ERC20 asset = erc4626Vault.asset(); | ||
asset.safeApprove(address(erc4626Vault), assets); | ||
erc4626Vault.deposit(assets, address(this)); | ||
|
||
// Zero out approvals if necessary. | ||
_revokeExternalApproval(asset, address(erc4626Vault)); | ||
} | ||
|
||
/** | ||
* @notice Cellar needs to call withdraw on ERC4626 position. | ||
* @dev Important to verify that external receivers are allowed if receiver is not Cellar address. | ||
* @param assets the amount of assets to withdraw from the ERC4626 position | ||
* @param receiver address to send assets to' | ||
* @param adaptorData data needed to withdraw from the ERC4626 position | ||
* @param configurationData abi encoded bool indicating whether the position is liquid or not | ||
*/ | ||
function withdraw( | ||
uint256 assets, | ||
address receiver, | ||
bytes memory adaptorData, | ||
bytes memory configurationData | ||
) public virtual override { | ||
// Check that position is setup to be liquid. | ||
bool isLiquid = abi.decode(configurationData, (bool)); | ||
if (!isLiquid) revert BaseAdaptor__UserWithdrawsNotAllowed(); | ||
|
||
// Run external receiver check. | ||
_externalReceiverCheck(receiver); | ||
|
||
// Withdraw assets from `Vault`. | ||
ERC4626 erc4626Vault = abi.decode(adaptorData, (ERC4626)); | ||
_verifyERC4626PositionIsUsed(address(erc4626Vault)); | ||
erc4626Vault.withdraw(assets, receiver, address(this)); | ||
} | ||
|
||
/** | ||
* @notice Cellar needs to call `maxWithdraw` to see if its assets are locked. | ||
*/ | ||
function withdrawableFrom( | ||
bytes memory adaptorData, | ||
bytes memory configurationData | ||
) public view virtual override returns (uint256) { | ||
bool isLiquid = abi.decode(configurationData, (bool)); | ||
if (isLiquid) { | ||
ERC4626 erc4626Vault = abi.decode(adaptorData, (ERC4626)); | ||
return erc4626Vault.maxWithdraw(msg.sender); | ||
} else return 0; | ||
} | ||
|
||
/** | ||
* @notice Uses ERC4626 `previewRedeem` to determine Cellars balance in ERC4626 position. | ||
*/ | ||
function balanceOf(bytes memory adaptorData) public view virtual override returns (uint256) { | ||
ERC4626 erc4626Vault = abi.decode(adaptorData, (ERC4626)); | ||
return erc4626Vault.previewRedeem(erc4626Vault.balanceOf(msg.sender)); | ||
} | ||
|
||
/** | ||
* @notice Returns the asset the ERC4626 position uses. | ||
*/ | ||
function assetOf(bytes memory adaptorData) public view virtual override returns (ERC20) { | ||
ERC4626 erc4626Vault = abi.decode(adaptorData, (ERC4626)); | ||
return ERC20(erc4626Vault.asset()); | ||
} | ||
|
||
/** | ||
* @notice This adaptor returns collateral, and not debt. | ||
*/ | ||
function isDebt() public pure virtual override returns (bool) { | ||
return false; | ||
} | ||
|
||
//============================================ Strategist Functions =========================================== | ||
/** | ||
* @notice Allows strategists to deposit into ERC4626 positions. | ||
* @dev Uses `_maxAvailable` helper function, see BaseAdaptor.sol | ||
* @param erc4626Vault the ERC4626 to deposit `assets` into | ||
* @param assets the amount of assets to deposit into `Vault` | ||
*/ | ||
function depositToVault(ERC4626 erc4626Vault, uint256 assets) public { | ||
_verifyERC4626PositionIsUsed(address(erc4626Vault)); | ||
ERC20 asset = erc4626Vault.asset(); | ||
assets = _maxAvailable(asset, assets); | ||
asset.safeApprove(address(erc4626Vault), assets); | ||
erc4626Vault.deposit(assets, address(this)); | ||
|
||
// Zero out approvals if necessary. | ||
_revokeExternalApproval(asset, address(erc4626Vault)); | ||
} | ||
|
||
/** | ||
* @notice Allows strategists to withdraw from ERC4626 positions. | ||
* @param erc4626Vault the ERC4626 to withdraw `assets` from | ||
* @param assets the amount of assets to withdraw from `Vault` | ||
*/ | ||
function withdrawFromVault(ERC4626 erc4626Vault, uint256 assets) public { | ||
_verifyERC4626PositionIsUsed(address(erc4626Vault)); | ||
if (assets == type(uint256).max) assets = erc4626Vault.maxWithdraw(address(this)); | ||
erc4626Vault.withdraw(assets, address(this), address(this)); | ||
} | ||
|
||
//============================================ Helper Functions =========================================== | ||
|
||
/** | ||
* @notice Reverts if a given `erc4626Vault` is not set up as a position in the calling Cellar. | ||
* @dev This function is only used in a delegate call context, hence why address(this) is used | ||
* to get the calling Cellar. | ||
*/ | ||
function _verifyERC4626PositionIsUsed(address erc4626Vault) internal view { | ||
// Check that erc4626Vault position is setup to be used in the calling cellar. | ||
bytes32 positionHash = keccak256(abi.encode(identifier(), false, abi.encode(erc4626Vault))); | ||
uint32 positionId = Cellar(address(this)).registry().getPositionHashToPositionId(positionHash); | ||
if (!Cellar(address(this)).isPositionUsed(positionId)) | ||
revert ERC4626Adaptor__ERC4626PositionNotUsed(erc4626Vault); | ||
} | ||
} |
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
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
Oops, something went wrong.