Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ERC4626 #3171

Merged
merged 39 commits into from Jun 2, 2022
Merged

ERC4626 #3171

Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4f1e397
IERC4626
Amxx Feb 7, 2022
7bf3143
intitial ERC4626 implementation
Amxx Feb 7, 2022
a0363e5
fix convertion in case of decimals missmatch
Amxx Feb 7, 2022
8cbf623
fix bug
Amxx Feb 18, 2022
d84975b
Merge branch 'master' into feature/ERC4626
Amxx Feb 21, 2022
2f33819
ERC4626 testing
Amxx Feb 21, 2022
51ccbf8
update ERC4626 interface to match the current state of the ERC
Amxx Feb 21, 2022
b1c5367
fix lint
Amxx Feb 21, 2022
e730222
slither
Amxx Feb 21, 2022
7fc3633
more ERC4626 testing
Amxx Feb 21, 2022
b9709dc
more testing
Amxx Mar 10, 2022
fac4303
fix and document reentrancy risk
Amxx Mar 10, 2022
3931537
use Math.mulDiv
Amxx May 5, 2022
370de87
safe muldiv with rouding direction
Amxx May 5, 2022
a9affd7
Debug + simplify muldiv
Amxx May 5, 2022
3520d7f
minor improvement
Amxx May 5, 2022
f73f183
cleanup testing
Amxx May 5, 2022
3d1899f
fix lint
Amxx May 5, 2022
222bb7f
Merge branch 'master' into feature/ERC4626
Amxx May 6, 2022
2fda29a
add changelog entry
Amxx May 6, 2022
33d2481
cleanup
Amxx May 8, 2022
f16dc72
Merge branch 'master' into feature/ERC4626
Amxx May 8, 2022
d27b743
Apply suggestions from code review
Amxx May 12, 2022
c41c94b
fix muldiv tests
Amxx May 12, 2022
5673259
Merge branch 'master' into feature/ERC4626
Amxx May 16, 2022
5a86254
slither false positive silence + documentation
Amxx May 19, 2022
baa4d01
_deposit and _withdraw private functions
Amxx May 20, 2022
b8cba33
private functions renaming
Amxx May 20, 2022
32a2b9b
lint
Amxx May 20, 2022
a5d5fb4
documentation
Amxx May 23, 2022
fe5cfe4
rename ERC4626 to ERC20TokenizedVault
Amxx May 23, 2022
aa9b2f8
gas optimisation in muldiv
Amxx May 30, 2022
477f675
lint
Amxx May 30, 2022
82c7386
Apply suggestions from code review
Amxx May 31, 2022
e8947db
address issues from the PR
Amxx May 31, 2022
62f1a63
try to improve the symetry for broken vaults (>0 shares & 0 assets)
Amxx May 31, 2022
bdd9c0d
Revert systematic _sanity check & improve maxDeposit
Amxx Jun 1, 2022
9a6ab42
fix lint
Amxx Jun 1, 2022
cab514a
lint
frangio Jun 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 33 additions & 0 deletions contracts/interfaces/draft-IERC4626.sol
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";

interface IERC4626 is IERC20, IERC20Metadata {

event Deposit(address indexed sender, address indexed receiver, uint256 assets, uint256 shares);
event Withdraw(address indexed sender, address indexed receiver, uint256 assets, uint256 shares);

function asset() external view returns (address assetTokenAddress);
function totalAssets() external view returns (uint256 totalManagedAssets);
function assetsPerShare() external view returns (uint256 assetsPerUnitShare);
function assetsOf(address depositor) external view returns (uint256 assets);

function maxDeposit(address caller) external view returns (uint256 maxAssets);
function previewDeposit(uint256 assets) external view returns (uint256 shares);
function deposit(uint256 assets, address receiver) external returns (uint256 shares);

function maxMint(address caller) external view returns (uint256 maxShares);
function previewMint(uint256 shares) external view returns (uint256 assets);
function mint(uint256 shares, address receiver) external returns (uint256 assets);

function maxWithdraw(address caller) external view returns (uint256 maxAssets);
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

function maxRedeem(address caller) external view returns (uint256 maxShares);
function previewRedeem(uint256 shares) external view returns (uint256 assets);
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
127 changes: 127 additions & 0 deletions contracts/token/ERC20/extensions/draft-ERC4646.sol
@@ -0,0 +1,127 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "../ERC20.sol";
import "../utils/SafeERC20.sol";
import "../../../interfaces/draft-IERC4626.sol";

abstract contract ERC4262 is ERC20, IERC4626 {
IERC20Metadata private immutable _asset;

constructor (IERC20Metadata __asset) {
_asset = __asset;
}

function asset() public view virtual override returns (address) {
return address(_asset);
}

function totalAssets() public view virtual override returns (uint256) {
return _asset.balanceOf(address(this));
}

function assetsPerShare() public view virtual override returns (uint256) {
return _sharesToAssets(10 ** decimals());
}

function assetsOf(address depositor) public view virtual override returns (uint256) {
return totalAssets() * balanceOf(depositor) / totalSupply();
}

function maxDeposit(address /*caller*/) public view virtual override returns (uint256) {
return type(uint256).max;
}

function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
return _assetsToShares(assets);
}

function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
uint256 shares = previewDeposit(assets);
SafeERC20.safeTransferFrom(_asset, _msgSender(), address(this), assets);
_mint(receiver, shares);
return shares;
}

function maxMint(address /*caller*/) public view virtual override returns (uint256) {
return type(uint256).max;
}

function previewMint(uint256 shares) public view virtual override returns (uint256) {
return _sharesToAssets(shares);
}

function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
uint256 assets = _sharesToAssets(shares);
SafeERC20.safeTransferFrom(_asset, _msgSender(), address(this), assets);
_mint(receiver, shares);
return assets;
}

function maxWithdraw(address caller) public view virtual override returns (uint256) {
return assetsOf(caller);
}

function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
return _assetsToShares(assets);
}

function withdraw(uint256 assets, address receiver, address owner) public virtual override returns (uint256) {
address sender = _msgSender();
uint256 shares = previewWithdraw(assets);

if (sender != owner) {
uint256 currentAllowance = allowance(owner, sender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= shares, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(owner, sender, currentAllowance - shares);
}
}
}

_burn(owner, shares);
SafeERC20.safeTransfer(_asset, receiver, assets);
return shares;
}

function maxRedeem(address caller) public view virtual override returns (uint256) {
return balanceOf(caller);
}

function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
return _sharesToAssets(shares);
}

function redeem(uint256 shares, address receiver, address owner) public virtual override returns (uint256) {
address sender = _msgSender();
uint256 assets = previewRedeem(shares);

if (sender != owner) {
uint256 currentAllowance = allowance(owner, sender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= shares, "ERC20: transfer amount exceeds allowance");
unchecked {
_approve(owner, sender, currentAllowance - shares);
Amxx marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

_burn(owner, shares);
SafeERC20.safeTransfer(_asset, receiver, assets);
return assets;
}

function _sharesToAssets(uint256 shares) internal view virtual returns (uint256) {
return totalAssets() == 0 && totalSupply() == 0
? shares * (10 ** _asset.decimals()) / (10 ** decimals())
: shares * totalAssets() / totalSupply();
}

function _assetsToShares(uint256 assets) internal view virtual returns (uint256) {
return totalAssets() == 0 && totalSupply() == 0
? assets * (10 ** decimals()) / (10 ** _asset.decimals())
: assets * totalSupply() / totalAssets();
Amxx marked this conversation as resolved.
Show resolved Hide resolved
}
}