Skip to content
5 changes: 5 additions & 0 deletions .changeset/shiny-dolphins-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'openzeppelin-solidity': minor
---

`ERC4626`: compute `maxWithdraw` using `maxRedeem` and `previewRedeem` so that changes to the preview functions affect the max functions.
34 changes: 17 additions & 17 deletions contracts/token/ERC20/extensions/ERC4626.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,67 +107,67 @@ abstract contract ERC4626 is ERC20, IERC4626 {
return _underlyingDecimals + _decimalsOffset();
}

/** @dev See {IERC4626-asset}. */
/// @inheritdoc IERC4626
function asset() public view virtual returns (address) {
return address(_asset);
}

/** @dev See {IERC4626-totalAssets}. */
/// @inheritdoc IERC4626
function totalAssets() public view virtual returns (uint256) {
return _asset.balanceOf(address(this));
}

/** @dev See {IERC4626-convertToShares}. */
/// @inheritdoc IERC4626
function convertToShares(uint256 assets) public view virtual returns (uint256) {
return _convertToShares(assets, Math.Rounding.Floor);
}

/** @dev See {IERC4626-convertToAssets}. */
/// @inheritdoc IERC4626
function convertToAssets(uint256 shares) public view virtual returns (uint256) {
return _convertToAssets(shares, Math.Rounding.Floor);
}

/** @dev See {IERC4626-maxDeposit}. */
/// @inheritdoc IERC4626
function maxDeposit(address) public view virtual returns (uint256) {
return type(uint256).max;
}

/** @dev See {IERC4626-maxMint}. */
/// @inheritdoc IERC4626
function maxMint(address) public view virtual returns (uint256) {
return type(uint256).max;
}

/** @dev See {IERC4626-maxWithdraw}. */
/// @inheritdoc IERC4626
function maxWithdraw(address owner) public view virtual returns (uint256) {
return _convertToAssets(balanceOf(owner), Math.Rounding.Floor);
return previewRedeem(maxRedeem(owner));
}

/** @dev See {IERC4626-maxRedeem}. */
/// @inheritdoc IERC4626
function maxRedeem(address owner) public view virtual returns (uint256) {
return balanceOf(owner);
}

/** @dev See {IERC4626-previewDeposit}. */
/// @inheritdoc IERC4626
function previewDeposit(uint256 assets) public view virtual returns (uint256) {
return _convertToShares(assets, Math.Rounding.Floor);
}

/** @dev See {IERC4626-previewMint}. */
/// @inheritdoc IERC4626
function previewMint(uint256 shares) public view virtual returns (uint256) {
return _convertToAssets(shares, Math.Rounding.Ceil);
}

/** @dev See {IERC4626-previewWithdraw}. */
/// @inheritdoc IERC4626
function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
return _convertToShares(assets, Math.Rounding.Ceil);
}

/** @dev See {IERC4626-previewRedeem}. */
/// @inheritdoc IERC4626
function previewRedeem(uint256 shares) public view virtual returns (uint256) {
return _convertToAssets(shares, Math.Rounding.Floor);
}

/** @dev See {IERC4626-deposit}. */
/// @inheritdoc IERC4626
function deposit(uint256 assets, address receiver) public virtual returns (uint256) {
uint256 maxAssets = maxDeposit(receiver);
if (assets > maxAssets) {
Expand All @@ -180,7 +180,7 @@ abstract contract ERC4626 is ERC20, IERC4626 {
return shares;
}

/** @dev See {IERC4626-mint}. */
/// @inheritdoc IERC4626
function mint(uint256 shares, address receiver) public virtual returns (uint256) {
uint256 maxShares = maxMint(receiver);
if (shares > maxShares) {
Expand All @@ -193,7 +193,7 @@ abstract contract ERC4626 is ERC20, IERC4626 {
return assets;
}

/** @dev See {IERC4626-withdraw}. */
/// @inheritdoc IERC4626
function withdraw(uint256 assets, address receiver, address owner) public virtual returns (uint256) {
uint256 maxAssets = maxWithdraw(owner);
if (assets > maxAssets) {
Expand All @@ -206,7 +206,7 @@ abstract contract ERC4626 is ERC20, IERC4626 {
return shares;
}

/** @dev See {IERC4626-redeem}. */
/// @inheritdoc IERC4626
function redeem(uint256 shares, address receiver, address owner) public virtual returns (uint256) {
uint256 maxShares = maxRedeem(owner);
if (shares > maxShares) {
Expand Down