Skip to content

Commit 06fec21

Browse files
committed
add ERC4626Fees
1 parent 0f753aa commit 06fec21

File tree

1 file changed

+103
-0
lines changed

1 file changed

+103
-0
lines changed

contracts/token/ERC4626Fees.sol

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.20;
4+
5+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6+
import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
7+
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
8+
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
9+
10+
/// @dev ERC-4626 vault with entry/exit fees expressed in https://en.wikipedia.org/wiki/Basis_point[basis point (bp)].
11+
abstract contract ERC4626Fees is ERC4626 {
12+
using Math for uint256;
13+
14+
uint256 private constant _BASIS_POINT_SCALE = 1e4;
15+
16+
// === Overrides ===
17+
18+
/// @dev Preview taking an entry fee on deposit. See {IERC4626-previewDeposit}.
19+
function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
20+
uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints());
21+
return super.previewDeposit(assets - fee);
22+
}
23+
24+
/// @dev Preview adding an entry fee on mint. See {IERC4626-previewMint}.
25+
function previewMint(uint256 shares) public view virtual override returns (uint256) {
26+
uint256 assets = super.previewMint(shares);
27+
return assets + _feeOnRaw(assets, _entryFeeBasisPoints());
28+
}
29+
30+
/// @dev Preview adding an exit fee on withdraw. See {IERC4626-previewWithdraw}.
31+
function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
32+
uint256 fee = _feeOnRaw(assets, _exitFeeBasisPoints());
33+
return super.previewWithdraw(assets + fee);
34+
}
35+
36+
/// @dev Preview taking an exit fee on redeem. See {IERC4626-previewRedeem}.
37+
function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
38+
uint256 assets = super.previewRedeem(shares);
39+
return assets - _feeOnTotal(assets, _exitFeeBasisPoints());
40+
}
41+
42+
/// @dev Send entry fee to {_entryFeeRecipient}. See {IERC4626-_deposit}.
43+
function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual override {
44+
uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints());
45+
address recipient = _entryFeeRecipient();
46+
47+
super._deposit(caller, receiver, assets, shares);
48+
49+
if (fee > 0 && recipient != address(this)) {
50+
SafeERC20.safeTransfer(IERC20(asset()), recipient, fee);
51+
}
52+
}
53+
54+
/// @dev Send exit fee to {_exitFeeRecipient}. See {IERC4626-_deposit}.
55+
function _withdraw(
56+
address caller,
57+
address receiver,
58+
address owner,
59+
uint256 assets,
60+
uint256 shares
61+
) internal virtual override {
62+
uint256 fee = _feeOnRaw(assets, _exitFeeBasisPoints());
63+
address recipient = _exitFeeRecipient();
64+
65+
super._withdraw(caller, receiver, owner, assets, shares);
66+
67+
if (fee > 0 && recipient != address(this)) {
68+
SafeERC20.safeTransfer(IERC20(asset()), recipient, fee);
69+
}
70+
}
71+
72+
// === Fee configuration ===
73+
74+
function _entryFeeBasisPoints() internal view virtual returns (uint256) {
75+
return 0; // replace with e.g. 100 for 1%
76+
}
77+
78+
function _exitFeeBasisPoints() internal view virtual returns (uint256) {
79+
return 0; // replace with e.g. 100 for 1%
80+
}
81+
82+
function _entryFeeRecipient() internal view virtual returns (address) {
83+
return address(0); // replace with e.g. a treasury address
84+
}
85+
86+
function _exitFeeRecipient() internal view virtual returns (address) {
87+
return address(0); // replace with e.g. a treasury address
88+
}
89+
90+
// === Fee operations ===
91+
92+
/// @dev Calculates the fees that should be added to an amount `assets` that does not already include fees.
93+
/// Used in {IERC4626-mint} and {IERC4626-withdraw} operations.
94+
function _feeOnRaw(uint256 assets, uint256 feeBasisPoints) private pure returns (uint256) {
95+
return assets.mulDiv(feeBasisPoints, _BASIS_POINT_SCALE, Math.Rounding.Ceil);
96+
}
97+
98+
/// @dev Calculates the fee part of an amount `assets` that already includes fees.
99+
/// Used in {IERC4626-deposit} and {IERC4626-redeem} operations.
100+
function _feeOnTotal(uint256 assets, uint256 feeBasisPoints) private pure returns (uint256) {
101+
return assets.mulDiv(feeBasisPoints, feeBasisPoints + _BASIS_POINT_SCALE, Math.Rounding.Ceil);
102+
}
103+
}

0 commit comments

Comments
 (0)