/
ERC4626.sol
173 lines (133 loc) · 5.99 KB
/
ERC4626.sol
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../ERC20.sol";
import "../utils/SafeERC20.sol";
import "../../../interfaces/IERC4626.sol";
abstract contract ERC4626 is ERC20, IERC4626 {
IERC20Metadata private immutable _asset;
constructor(IERC20Metadata __asset) {
_asset = __asset;
}
/** @dev See {IERC4262-asset} */
function asset() public view virtual override returns (address) {
return address(_asset);
}
/** @dev See {IERC4262-totalAssets} */
function totalAssets() public view virtual override returns (uint256) {
return _asset.balanceOf(address(this));
}
/**
* @dev See {IERC4262-convertToShares}
*
* Will revert if asserts > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset
* would represent an infinite amout of shares.
*/
function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {
uint256 supply = totalSupply();
return
(assets == 0 || supply == 0)
? (assets * 10**decimals()) / 10**_asset.decimals()
: (assets * supply) / totalAssets();
}
/** @dev See {IERC4262-convertToAssets} */
function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {
uint256 supply = totalSupply();
return (supply == 0) ? (shares * 10**_asset.decimals()) / 10**decimals() : (shares * totalAssets()) / supply;
}
/** @dev See {IERC4262-maxDeposit} */
function maxDeposit(address) public view virtual override returns (uint256) {
return type(uint256).max;
}
/** @dev See {IERC4262-maxMint} */
function maxMint(address) public view virtual override returns (uint256) {
return type(uint256).max;
}
/** @dev See {IERC4262-maxWithdraw} */
function maxWithdraw(address owner) public view virtual override returns (uint256) {
return convertToAssets(balanceOf(owner));
}
/** @dev See {IERC4262-maxRedeem} */
function maxRedeem(address owner) public view virtual override returns (uint256) {
return balanceOf(owner);
}
/** @dev See {IERC4262-previewDeposit} */
function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
return convertToShares(assets);
}
/** @dev See {IERC4262-previewMint} */
function previewMint(uint256 shares) public view virtual override returns (uint256) {
uint256 assets = convertToAssets(shares);
return assets + (convertToShares(assets) < shares ? 1 : 0);
}
/** @dev See {IERC4262-previewWithdraw} */
function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
uint256 shares = convertToShares(assets);
return shares + (convertToAssets(shares) < assets ? 1 : 0);
}
/** @dev See {IERC4262-previewRedeem} */
function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
return convertToAssets(shares);
}
/** @dev See {IERC4262-deposit} */
function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
require(assets <= maxDeposit(receiver), "ERC4626: deposit more then max");
address caller = _msgSender();
uint256 shares = previewDeposit(assets);
// if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through
// the tokensToSend hook, so we need to transfer before we mint to keep the invariants.
SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
_mint(receiver, shares);
emit Deposit(caller, receiver, assets, shares);
return shares;
}
/** @dev See {IERC4262-mint} */
function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
require(shares <= maxMint(receiver), "ERC4626: mint more then max");
address caller = _msgSender();
uint256 assets = previewMint(shares);
// if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through
// the tokensToSend hook, so we need to transfer before we mint to keep the invariants.
SafeERC20.safeTransferFrom(_asset, caller, address(this), assets);
_mint(receiver, shares);
emit Deposit(caller, receiver, assets, shares);
return assets;
}
/** @dev See {IERC4262-withdraw} */
function withdraw(
uint256 assets,
address receiver,
address owner
) public virtual override returns (uint256) {
require(assets <= maxWithdraw(owner), "ERC4626: withdraw more then max");
address caller = _msgSender();
uint256 shares = previewWithdraw(assets);
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
// if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through
// the tokensReceived hook, so we need to transfer after we burn to keep the invariants.
_burn(owner, shares);
SafeERC20.safeTransfer(_asset, receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
return shares;
}
/** @dev See {IERC4262-redeem} */
function redeem(
uint256 shares,
address receiver,
address owner
) public virtual override returns (uint256) {
require(shares <= maxRedeem(owner), "ERC4626: redeem more then max");
address caller = _msgSender();
uint256 assets = previewRedeem(shares);
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
// if _asset is ERC777, transferFrom can call reenter BEFORE the transfer happens through
// the tokensReceived hook, so we need to transfer after we burn to keep the invariants.
_burn(owner, shares);
SafeERC20.safeTransfer(_asset, receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
return assets;
}
}