forked from OpenZeppelin/openzeppelin-contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathERC7540Fees.sol
More file actions
89 lines (71 loc) · 3.36 KB
/
ERC7540Fees.sol
File metadata and controls
89 lines (71 loc) · 3.36 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {IERC4626} from "../../interfaces/IERC4626.sol";
import {IERC20} from "../../token/ERC20/IERC20.sol";
import {ERC7540} from "../../token/ERC20/extensions/ERC7540.sol";
import {ERC4626} from "../../token/ERC20/extensions/ERC4626.sol";
import {SafeERC20} from "../../token/ERC20/utils/SafeERC20.sol";
import {Math} from "../../utils/math/Math.sol";
/**
* @dev ERC-7540 vault with entry/exit fees expressed in basis points.
*
* NOTE: This contract charges fees in terms of assets, not shares. The fees are calculated
* based on the amount of assets being deposited or withdrawn, not the shares being minted or redeemed.
* This is an opinionated design decision that should be taken into account when integrating.
*
* WARNING: This contract is for demonstration purposes and has not been audited. Use it with caution.
*/
abstract contract ERC7540Fees is ERC7540 {
using SafeERC20 for IERC20;
using Math for uint256;
uint256 private constant _BASIS_POINT_SCALE = 1e4;
function previewDeposit(uint256 assets) public view virtual override(ERC4626, IERC4626) returns (uint256) {
uint256 fee = _feeOnTotal(assets, _entryFeeBasisPoints());
return super.previewDeposit(assets - fee);
}
function previewRedeem(uint256 shares) public view virtual override(ERC4626, IERC4626) returns (uint256) {
uint256 assets = super.previewRedeem(shares);
uint256 fee = _feeOnTotal(assets, _exitFeeBasisPoints());
return assets - fee;
}
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 - fee, shares);
if (fee > 0 && recipient != address(this)) {
IERC20(asset()).safeTransfer(recipient, fee);
}
}
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 - fee, shares);
if (fee > 0 && recipient != address(this)) {
IERC20(asset()).safeTransfer(recipient, fee);
}
}
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
}
function _feeOnRaw(uint256 assets, uint256 feeBasisPoints) private pure returns (uint256) {
return assets.mulDiv(feeBasisPoints, _BASIS_POINT_SCALE, Math.Rounding.Ceil);
}
function _feeOnTotal(uint256 assets, uint256 feeBasisPoints) private pure returns (uint256) {
return assets.mulDiv(feeBasisPoints, feeBasisPoints + _BASIS_POINT_SCALE, Math.Rounding.Ceil);
}
}