-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of github.com:thesis/acre into error-after-deposi…
…t-action
- Loading branch information
Showing
37 changed files
with
1,788 additions
and
4,562 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
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,104 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity ^0.8.21; | ||
|
||
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; | ||
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; | ||
|
||
import {ZeroAddress} from "./utils/Errors.sol"; | ||
|
||
/// @title PausableOwnable | ||
/// @notice This abstract contract extracts a common part of the emergency stop | ||
/// mechanism. The emergency stop mechanism can be triggered by an | ||
/// authorized account. Only owner of the contract can update pause | ||
/// admin address. | ||
abstract contract PausableOwnable is | ||
PausableUpgradeable, | ||
Ownable2StepUpgradeable | ||
{ | ||
/// @notice An authorized account that can trigger emergency stop mechanism. | ||
address public pauseAdmin; | ||
|
||
// Reserved storage space that allows adding more variables without affecting | ||
// the storage layout of the child contracts. The convention from OpenZeppelin | ||
// suggests the storage space should add up to 50 slots. If more variables are | ||
// added in the upcoming versions one need to reduce the array size accordingly. | ||
// See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps | ||
// slither-disable-next-line unused-state | ||
uint256[49] private __gap; | ||
|
||
/// @notice Emitted when a pause admin address is updated. | ||
/// @param newAccount New pause admin address. | ||
/// @param oldAccount Old pause admin address. | ||
event PauseAdminUpdated(address newAccount, address oldAccount); | ||
|
||
/// @notice Reverts when an unauthorized account triggers the emergency stop | ||
/// mechanism. | ||
error PausableUnauthorizedAccount(address account); | ||
|
||
/// @notice Reverts if called by any account other than the pause admin | ||
/// or the contract owner. | ||
modifier onlyPauseAdminOrOwner() { | ||
address msgSender = _msgSender(); | ||
|
||
if (pauseAdmin != msgSender && owner() != msgSender) { | ||
revert PausableUnauthorizedAccount(msgSender); | ||
} | ||
_; | ||
} | ||
|
||
/// @notice Initializes the contract. MUST BE CALLED from the child | ||
/// contract initializer. | ||
/// @param initialOwner Initial owner of the contract. | ||
/// @param initialPauseAdmin Initial emergency stop account that can trigger | ||
/// the emergency stop mechanism. | ||
// solhint-disable-next-line func-name-mixedcase | ||
function __PausableOwnable_init( | ||
address initialOwner, | ||
address initialPauseAdmin | ||
) internal onlyInitializing { | ||
__Pausable_init(); | ||
__Ownable2Step_init(); | ||
__Ownable_init(initialOwner); | ||
__PausableOwnable_init_unchained(initialPauseAdmin); | ||
} | ||
|
||
// solhint-disable-next-line func-name-mixedcase | ||
function __PausableOwnable_init_unchained( | ||
address initialPauseAdmin | ||
) internal onlyInitializing { | ||
pauseAdmin = initialPauseAdmin; | ||
} | ||
|
||
/// @notice Enables an emergency stop mechanism. | ||
/// @dev Requirements: | ||
/// - The caller must be an authorized account to trigger pause. | ||
/// - The contract must not be already paused. | ||
// solhint-disable-next-line ordering | ||
function pause() external onlyPauseAdminOrOwner { | ||
_pause(); | ||
} | ||
|
||
/// @notice Turns off the emergency stop mechanism. | ||
/// @dev Requirements: | ||
/// - The caller must be an authorized account to trigger unpause. | ||
/// - The contract must be paused. | ||
function unpause() external onlyPauseAdminOrOwner { | ||
_unpause(); | ||
} | ||
|
||
/// @notice Updates an authorized account that can trigger emergency stop | ||
/// mechanism. | ||
/// @dev Throws if called by any account other than the owner. | ||
/// @param newPauseAdmin New account that can trigger emergency | ||
/// stop mechanism. | ||
function updatePauseAdmin(address newPauseAdmin) external onlyOwner { | ||
// TODO: Introduce a parameters update process. | ||
if (newPauseAdmin == address(0)) { | ||
revert ZeroAddress(); | ||
} | ||
|
||
emit PauseAdminUpdated(newPauseAdmin, pauseAdmin); | ||
|
||
pauseAdmin = newPauseAdmin; | ||
} | ||
} |
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,128 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
// Inspired by https://docs.openzeppelin.com/contracts/5.x/erc4626#fees | ||
|
||
pragma solidity ^0.8.21; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import {ERC4626Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; | ||
|
||
/// @dev ERC4626 vault with entry/exit fees expressed in https://en.wikipedia.org/wiki/Basis_point[basis point (bp)]. | ||
abstract contract ERC4626Fees is ERC4626Upgradeable { | ||
using Math for uint256; | ||
|
||
uint256 private constant _BASIS_POINT_SCALE = 1e4; | ||
|
||
// === Overrides === | ||
|
||
/// @dev Preview taking an entry fee on deposit. See {IERC4626-previewDeposit}. | ||
function previewDeposit( | ||
uint256 assets | ||
) public view virtual override returns (uint256) { | ||
uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints()); | ||
return super.previewDeposit(assets - fee); | ||
} | ||
|
||
/// @dev Preview adding an entry fee on mint. See {IERC4626-previewMint}. | ||
function previewMint( | ||
uint256 shares | ||
) public view virtual override returns (uint256) { | ||
uint256 assets = super.previewMint(shares); | ||
return assets + _feeOnRaw(assets, _entryFeeBasisPoints()); | ||
} | ||
|
||
/// @dev Preview adding an exit fee on withdraw. See {IERC4626-previewWithdraw}. | ||
function previewWithdraw( | ||
uint256 assets | ||
) public view virtual override returns (uint256) { | ||
uint256 fee = _feeOnRaw(assets, _exitFeeBasisPoints()); | ||
return super.previewWithdraw(assets + fee); | ||
} | ||
|
||
/// @dev Preview taking an exit fee on redeem. See {IERC4626-previewRedeem}. | ||
function previewRedeem( | ||
uint256 shares | ||
) public view virtual override returns (uint256) { | ||
uint256 assets = super.previewRedeem(shares); | ||
return assets - _feeOnTotal(assets, _exitFeeBasisPoints()); | ||
} | ||
|
||
/// @dev Send entry fee to {_feeRecipient}. See {IERC4626-_deposit}. | ||
function _deposit( | ||
address caller, | ||
address receiver, | ||
uint256 assets, | ||
uint256 shares | ||
) internal virtual override { | ||
uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints()); | ||
address recipient = _feeRecipient(); | ||
|
||
super._deposit(caller, receiver, assets, shares); | ||
|
||
if (fee > 0 && recipient != address(this)) { | ||
SafeERC20.safeTransfer(IERC20(asset()), recipient, fee); | ||
} | ||
} | ||
|
||
/// @dev Send exit fee to {_exitFeeRecipient}. See {IERC4626-_deposit}. | ||
function _withdraw( | ||
address caller, | ||
address receiver, | ||
address owner, | ||
uint256 assets, | ||
uint256 shares | ||
) internal virtual override { | ||
uint256 fee = _feeOnRaw(assets, _exitFeeBasisPoints()); | ||
address recipient = _feeRecipient(); | ||
|
||
super._withdraw(caller, receiver, owner, assets, shares); | ||
|
||
if (fee > 0 && recipient != address(this)) { | ||
SafeERC20.safeTransfer(IERC20(asset()), recipient, fee); | ||
} | ||
} | ||
|
||
// === Fee configuration === | ||
|
||
// slither-disable-next-line dead-code | ||
function _entryFeeBasisPoints() internal view virtual returns (uint256); | ||
|
||
// slither-disable-next-line dead-code | ||
function _exitFeeBasisPoints() internal view virtual returns (uint256); | ||
|
||
// slither-disable-next-line dead-code | ||
function _feeRecipient() internal view virtual returns (address); | ||
|
||
// === Fee operations === | ||
|
||
/// @dev Calculates the fees that should be added to an amount `assets` | ||
/// that does not already include fees. | ||
/// Used in {IERC4626-mint} and {IERC4626-withdraw} operations. | ||
function _feeOnRaw( | ||
uint256 assets, | ||
uint256 feeBasisPoints | ||
) private pure returns (uint256) { | ||
return | ||
assets.mulDiv( | ||
feeBasisPoints, | ||
_BASIS_POINT_SCALE, | ||
Math.Rounding.Ceil | ||
); | ||
} | ||
|
||
/// @dev Calculates the fee part of an amount `assets` that already includes fees. | ||
/// Used in {IERC4626-deposit} and {IERC4626-redeem} operations. | ||
function _feeOnTotal( | ||
uint256 assets, | ||
uint256 feeBasisPoints | ||
) private pure returns (uint256) { | ||
return | ||
assets.mulDiv( | ||
feeBasisPoints, | ||
feeBasisPoints + _BASIS_POINT_SCALE, | ||
Math.Rounding.Ceil | ||
); | ||
} | ||
} |
Oops, something went wrong.