I am curious about the implementation of the ERC4626 standard. As always, the best option is to look up your standards and recommendations.
I have tried to implement a contract with the ERC4626Fees example you share in your documentation:
But when I try to compile it I get this error:
TypeError: Member "Up" not found or not visible after argument-dependent lookup in type(enum Math.Rounding).
Contract:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
/// @dev ERC4626 vault with entry/exit fees expressed in https://en.wikipedia.org/wiki/Basis_point[basis point (bp)].
abstract contract ERC4626Fees is ERC4626 {
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 {_entryFeeRecipient}. See {IERC4626-_deposit}.
function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual override {
uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints());
address recipient = _entryFeeRecipient();
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 = _exitFeeRecipient();
super._withdraw(caller, receiver, owner, assets, shares);
if (fee > 0 && recipient != address(this)) {
SafeERC20.safeTransfer(IERC20(asset()), recipient, fee);
}
}
// === Fee configuration ===
function _entryFeeBasisPoints() internal view virtual returns (uint256) {
return 0; // replace with e.g. 100 for 1%
}
function _exitFeeBasisPoints() internal view virtual returns (uint256) {
return 0; // replace with e.g. 100 for 1%
}
function _entryFeeRecipient() internal view virtual returns (address) {
return address(0); // replace with e.g. a treasury address
}
function _exitFeeRecipient() internal view virtual returns (address) {
return address(0); // replace with e.g. a treasury 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.Up);
}
/// @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.Up);
}
}
contract Staking is ERC4626Fees {
constructor(IERC20 asset_) ERC4626(asset_) ERC20("Staking token", "STAKE") {
}
}
Error in function:
function _feeOnRaw(uint256 assets, uint256 feeBasisPoints) private pure returns (uint256) {
return assets.mulDiv(feeBasisPoints, _BASIS_POINT_SCALE, Math.Rounding.Up);
}