diff --git a/contracts/mocks/docs/ERC4626FeesUpgradeable.sol b/contracts/mocks/docs/ERC4626FeesUpgradeable.sol index a30aa1b96..70fa9cc4e 100644 --- a/contracts/mocks/docs/ERC4626FeesUpgradeable.sol +++ b/contracts/mocks/docs/ERC4626FeesUpgradeable.sol @@ -5,41 +5,46 @@ pragma solidity ^0.8.0; import "../../token/ERC20/extensions/ERC4626Upgradeable.sol"; import "../../proxy/utils/Initializable.sol"; +/// @dev ERC4626 vault with entry/exit fees expressed in https://en.wikipedia.org/wiki/Basis_point[basis point (bp)]. abstract contract ERC4626FeesUpgradeable is Initializable, ERC4626Upgradeable { using MathUpgradeable for uint256; + uint256 private constant _BASIS_POINT_SCALE = 1e4; + function __ERC4626Fees_init() internal onlyInitializing { } function __ERC4626Fees_init_unchained() internal onlyInitializing { } - /** @dev See {IERC4626-previewDeposit}. */ + // === 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, _entryFeeBasePoint()); + uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints()); return super.previewDeposit(assets - fee); } - /** @dev See {IERC4626-previewMint}. */ + /// @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, _entryFeeBasePoint()); + return assets + _feeOnRaw(assets, _entryFeeBasisPoints()); } - /** @dev See {IERC4626-previewWithdraw}. */ + /// @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, _exitFeeBasePoint()); + uint256 fee = _feeOnRaw(assets, _exitFeeBasisPoints()); return super.previewWithdraw(assets + fee); } - /** @dev See {IERC4626-previewRedeem}. */ + /// @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, _exitFeeBasePoint()); + return assets - _feeOnTotal(assets, _exitFeeBasisPoints()); } - /** @dev See {IERC4626-_deposit}. */ + /// @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, _entryFeeBasePoint()); + uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints()); address recipient = _entryFeeRecipient(); super._deposit(caller, receiver, assets, shares); @@ -49,7 +54,7 @@ abstract contract ERC4626FeesUpgradeable is Initializable, ERC4626Upgradeable { } } - /** @dev See {IERC4626-_deposit}. */ + /// @dev Send exit fee to {_exitFeeRecipient}. See {IERC4626-_deposit}. function _withdraw( address caller, address receiver, @@ -57,7 +62,7 @@ abstract contract ERC4626FeesUpgradeable is Initializable, ERC4626Upgradeable { uint256 assets, uint256 shares ) internal virtual override { - uint256 fee = _feeOnRaw(assets, _exitFeeBasePoint()); + uint256 fee = _feeOnRaw(assets, _exitFeeBasisPoints()); address recipient = _exitFeeRecipient(); super._withdraw(caller, receiver, owner, assets, shares); @@ -67,28 +72,36 @@ abstract contract ERC4626FeesUpgradeable is Initializable, ERC4626Upgradeable { } } - function _entryFeeBasePoint() internal view virtual returns (uint256) { - return 0; + // === Fee configuration === + + function _entryFeeBasisPoints() internal view virtual returns (uint256) { + return 0; // replace with e.g. 100 for 1% } - function _entryFeeRecipient() internal view virtual returns (address) { - return address(0); + function _exitFeeBasisPoints() internal view virtual returns (uint256) { + return 0; // replace with e.g. 100 for 1% } - function _exitFeeBasePoint() internal view virtual returns (uint256) { - return 0; + 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); + return address(0); // replace with e.g. a treasury address } - function _feeOnRaw(uint256 assets, uint256 feeBasePoint) private pure returns (uint256) { - return assets.mulDiv(feeBasePoint, 1e5, MathUpgradeable.Rounding.Up); + // === 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, MathUpgradeable.Rounding.Up); } - function _feeOnTotal(uint256 assets, uint256 feeBasePoint) private pure returns (uint256) { - return assets.mulDiv(feeBasePoint, feeBasePoint + 1e5, MathUpgradeable.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, MathUpgradeable.Rounding.Up); } /** diff --git a/contracts/mocks/token/ERC4646FeesMockUpgradeable.sol b/contracts/mocks/token/ERC4646FeesMockUpgradeable.sol index 721a3ca4e..e43173aa2 100644 --- a/contracts/mocks/token/ERC4646FeesMockUpgradeable.sol +++ b/contracts/mocks/token/ERC4646FeesMockUpgradeable.sol @@ -6,42 +6,42 @@ import "../docs/ERC4626FeesUpgradeable.sol"; import "../../proxy/utils/Initializable.sol"; abstract contract ERC4626FeesMockUpgradeable is Initializable, ERC4626FeesUpgradeable { - uint256 private _entryFeeBasePointValue; + uint256 private _entryFeeBasisPointValue; address private _entryFeeRecipientValue; - uint256 private _exitFeeBasePointValue; + uint256 private _exitFeeBasisPointValue; address private _exitFeeRecipientValue; function __ERC4626FeesMock_init( - uint256 entryFeeBasePoint, + uint256 entryFeeBasisPoints, address entryFeeRecipient, - uint256 exitFeeBasePoint, + uint256 exitFeeBasisPoints, address exitFeeRecipient ) internal onlyInitializing { - __ERC4626FeesMock_init_unchained(entryFeeBasePoint, entryFeeRecipient, exitFeeBasePoint, exitFeeRecipient); + __ERC4626FeesMock_init_unchained(entryFeeBasisPoints, entryFeeRecipient, exitFeeBasisPoints, exitFeeRecipient); } function __ERC4626FeesMock_init_unchained( - uint256 entryFeeBasePoint, + uint256 entryFeeBasisPoints, address entryFeeRecipient, - uint256 exitFeeBasePoint, + uint256 exitFeeBasisPoints, address exitFeeRecipient ) internal onlyInitializing { - _entryFeeBasePointValue = entryFeeBasePoint; + _entryFeeBasisPointValue = entryFeeBasisPoints; _entryFeeRecipientValue = entryFeeRecipient; - _exitFeeBasePointValue = exitFeeBasePoint; + _exitFeeBasisPointValue = exitFeeBasisPoints; _exitFeeRecipientValue = exitFeeRecipient; } - function _entryFeeBasePoint() internal view virtual override returns (uint256) { - return _entryFeeBasePointValue; + function _entryFeeBasisPoints() internal view virtual override returns (uint256) { + return _entryFeeBasisPointValue; } function _entryFeeRecipient() internal view virtual override returns (address) { return _entryFeeRecipientValue; } - function _exitFeeBasePoint() internal view virtual override returns (uint256) { - return _exitFeeBasePointValue; + function _exitFeeBasisPoints() internal view virtual override returns (uint256) { + return _exitFeeBasisPointValue; } function _exitFeeRecipient() internal view virtual override returns (address) { diff --git a/test/token/ERC20/extensions/ERC4626.test.js b/test/token/ERC20/extensions/ERC4626.test.js index 55b3e5d20..e6999d3c2 100644 --- a/test/token/ERC20/extensions/ERC4626.test.js +++ b/test/token/ERC20/extensions/ERC4626.test.js @@ -689,9 +689,9 @@ contract('ERC4626', function (accounts) { } describe('ERC4626Fees', function () { - const feeBasePoint = web3.utils.toBN(5e3); + const feeBasisPoints = web3.utils.toBN(5e3); const amountWithoutFees = web3.utils.toBN(10000); - const fees = amountWithoutFees.mul(feeBasePoint).divn(1e5); + const fees = amountWithoutFees.mul(feeBasisPoints).divn(1e4); const amountWithFees = amountWithoutFees.add(fees); describe('input fees', function () { @@ -701,7 +701,7 @@ contract('ERC4626', function (accounts) { name + ' Vault', symbol + 'V', this.token.address, - feeBasePoint, + feeBasisPoints, other, 0, constants.ZERO_ADDRESS,