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

Refactor interfaces #240

Merged
merged 7 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 14 additions & 18 deletions src/MetaMorpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ pragma solidity 0.8.21;

import {IMorphoMarketParams} from "./interfaces/IMorphoMarketParams.sol";
import {
IMetaMorpho, MarketConfig, PendingUint192, PendingAddress, MarketAllocation
MarketConfig,
PendingUint192,
PendingAddress,
MarketAllocation,
IMetaMorphoStaticTyping
} from "./interfaces/IMetaMorpho.sol";
import {Id, MarketParams, Market, IMorpho} from "@morpho-blue/interfaces/IMorpho.sol";

Expand All @@ -28,7 +32,7 @@ import {IERC20, IERC4626, ERC20, ERC4626, Math, SafeERC20} from "@openzeppelin/t
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice ERC4626 compliant vault allowing users to deposit assets to Morpho.
contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorpho {
contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorphoStaticTyping {
using Math for uint256;
using UtilsLib for uint256;
using SafeCast for uint256;
Expand Down Expand Up @@ -452,44 +456,40 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph
/* ERC4626 (PUBLIC) */

/// @inheritdoc IERC20Metadata
function decimals() public view override(IERC20Metadata, ERC20, ERC4626) returns (uint8) {
function decimals() public view override(ERC20, ERC4626) returns (uint8) {
return ERC4626.decimals();
}

/// @inheritdoc IERC4626
function maxWithdraw(address owner) public view override(IERC4626, ERC4626) returns (uint256 assets) {
function maxWithdraw(address owner) public view override returns (uint256 assets) {
(assets,,) = _maxWithdraw(owner);
}

/// @inheritdoc IERC4626
function maxRedeem(address owner) public view override(IERC4626, ERC4626) returns (uint256) {
function maxRedeem(address owner) public view override returns (uint256) {
(uint256 assets, uint256 newTotalSupply, uint256 newTotalAssets) = _maxWithdraw(owner);

return _convertToSharesWithFeeAccrued(assets, newTotalSupply, newTotalAssets, Math.Rounding.Floor);
}

/// @inheritdoc IERC4626
function deposit(uint256 assets, address receiver) public override(IERC4626, ERC4626) returns (uint256 shares) {
function deposit(uint256 assets, address receiver) public override returns (uint256 shares) {
uint256 newTotalAssets = _accrueFee();

shares = _convertToSharesWithFeeAccrued(assets, totalSupply(), newTotalAssets, Math.Rounding.Floor);
_deposit(_msgSender(), receiver, assets, shares);
}

/// @inheritdoc IERC4626
function mint(uint256 shares, address receiver) public override(IERC4626, ERC4626) returns (uint256 assets) {
function mint(uint256 shares, address receiver) public override returns (uint256 assets) {
uint256 newTotalAssets = _accrueFee();

assets = _convertToAssetsWithFeeAccrued(shares, totalSupply(), newTotalAssets, Math.Rounding.Ceil);
_deposit(_msgSender(), receiver, assets, shares);
}

/// @inheritdoc IERC4626
function withdraw(uint256 assets, address receiver, address owner)
public
override(IERC4626, ERC4626)
returns (uint256 shares)
{
function withdraw(uint256 assets, address receiver, address owner) public override returns (uint256 shares) {
uint256 newTotalAssets = _accrueFee();

// Do not call expensive `maxWithdraw` and optimistically withdraw assets.
Expand All @@ -499,11 +499,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph
}

/// @inheritdoc IERC4626
function redeem(uint256 shares, address receiver, address owner)
public
override(IERC4626, ERC4626)
returns (uint256 assets)
{
function redeem(uint256 shares, address receiver, address owner) public override returns (uint256 assets) {
uint256 newTotalAssets = _accrueFee();

// Do not call expensive `maxRedeem` and optimistically redeem shares.
Expand All @@ -513,7 +509,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph
}

/// @inheritdoc IERC4626
function totalAssets() public view override(IERC4626, ERC4626) returns (uint256 assets) {
function totalAssets() public view override returns (uint256 assets) {
for (uint256 i; i < withdrawQueue.length; ++i) {
assets += _supplyBalance(_marketParams(withdrawQueue[i]));
}
Expand Down
41 changes: 32 additions & 9 deletions src/interfaces/IMetaMorpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity >=0.5.0;

import {IMorpho, Id, MarketParams} from "@morpho-blue/interfaces/IMorpho.sol";
import {IERC4626} from "@openzeppelin/interfaces/IERC4626.sol";
import {IERC20Permit} from "@openzeppelin/token/ERC20/extensions/IERC20Permit.sol";

struct MarketConfig {
/// @notice The maximum amount of assets that can be allocated to the market.
Expand Down Expand Up @@ -35,7 +36,21 @@ struct MarketAllocation {
uint256 shares;
}

interface IMetaMorpho is IERC4626 {
interface IMulticall {
function multicall(bytes[] calldata) external returns (bytes[] memory);
}

interface IOwnable {
QGarchery marked this conversation as resolved.
Show resolved Hide resolved
function owner() external returns (address);
function transferOwnership(address) external;
function renounceOwnership() external;
function acceptOwnership() external;
function pendingOwner() external view returns (address);
}

/// @dev This interface is used for factorizing IMetaMorphoStaticTyping and IMetaMorpho.
/// @dev Consider using the IMetaMorpho interface instead of this one.
interface IMetaMorphoBase {
function MORPHO() external view returns (IMorpho);

function curator() external view returns (address);
Expand All @@ -50,29 +65,24 @@ interface IMetaMorpho is IERC4626 {
function supplyQueueSize() external view returns (uint256);
function withdrawQueue(uint256) external view returns (Id);
function withdrawQueueSize() external view returns (uint256);
function config(Id) external view returns (uint192 cap, uint64 withdrawRank);

function idle() external view returns (uint256);
function lastTotalAssets() external view returns (uint256);

function submitTimelock(uint256 newTimelock) external;
function acceptTimelock() external;
function revokeTimelock() external;
function pendingTimelock() external view returns (uint192 value, uint64 submittedAt);

function submitCap(MarketParams memory marketParams, uint256 marketCap) external;
function acceptCap(Id id) external;
function revokeCap(Id id) external;
function pendingCap(Id) external view returns (uint192 value, uint64 submittedAt);

function submitFee(uint256 newFee) external;
function acceptFee() external;
function pendingFee() external view returns (uint192 value, uint64 submittedAt);

function submitGuardian(address newGuardian) external;
function acceptGuardian() external;
function revokeGuardian() external;
function pendingGuardian() external view returns (address guardian, uint96 submittedAt);

function transferRewards(address) external;

Expand All @@ -86,8 +96,21 @@ interface IMetaMorpho is IERC4626 {
function reallocate(MarketAllocation[] calldata withdrawn, MarketAllocation[] calldata supplied) external;
}

interface IPending {
function pendingTimelock() external view returns (PendingUint192 memory);
function pendingCap(Id) external view returns (PendingUint192 memory);
/// @dev This interface is inherited by MetaMorpho so that signatures are checked by the compiler.
QGarchery marked this conversation as resolved.
Show resolved Hide resolved
/// @dev Consider using the IMetaMorpho interface instead of this one.
interface IMetaMorphoStaticTyping is IMetaMorphoBase {
function config(Id) external view returns (uint192 cap, uint64 withdrawRank);
function pendingGuardian() external view returns (address guardian, uint96 submittedAt);
function pendingCap(Id) external view returns (uint192 value, uint64 submittedAt);
function pendingTimelock() external view returns (uint192 value, uint64 submittedAt);
function pendingFee() external view returns (uint192 value, uint64 submittedAt);
}

/// @dev Use this interface for MetaMorpho vaults to have access to all the functions with the appropriate signatures.
interface IMetaMorpho is IMetaMorphoBase, IERC4626, IERC20Permit, IOwnable, IMulticall {
function config(Id) external view returns (MarketConfig memory);
function pendingGuardian() external view returns (PendingAddress memory);
function pendingCap(Id) external view returns (PendingUint192 memory);
function pendingTimelock() external view returns (PendingUint192 memory);
function pendingFee() external view returns (PendingUint192 memory);
}
30 changes: 15 additions & 15 deletions test/forge/GuardianTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ contract GuardianTest is IntegrationTest {
vm.warp(block.timestamp + elapsed);

vm.expectEmit();
emit EventsLib.RevokeTimelock(GUARDIAN, IPending(address(vault)).pendingTimelock());
emit EventsLib.RevokeTimelock(GUARDIAN, vault.pendingTimelock());
vm.prank(GUARDIAN);
vault.revokeTimelock();

uint256 newTimelock = vault.timelock();
(uint256 pendingTimelock, uint64 submittedAt) = vault.pendingTimelock();
PendingUint192 memory pendingTimelock = vault.pendingTimelock();

assertEq(newTimelock, TIMELOCK, "newTimelock");
assertEq(pendingTimelock, 0, "pendingTimelock");
assertEq(submittedAt, 0, "submittedAt");
assertEq(pendingTimelock.value, 0, "pendingTimelock.value");
assertEq(pendingTimelock.submittedAt, 0, "pendingTimelock.submittedAt");
}

function testRevokeCapIncreased(uint256 seed, uint256 cap, uint256 elapsed) public {
Expand All @@ -63,17 +63,17 @@ contract GuardianTest is IntegrationTest {
Id id = marketParams.id();

vm.expectEmit();
emit EventsLib.RevokeCap(GUARDIAN, id, IPending(address(vault)).pendingCap(id));
emit EventsLib.RevokeCap(GUARDIAN, id, vault.pendingCap(id));
vm.prank(GUARDIAN);
vault.revokeCap(id);

(uint192 newCap, uint64 withdrawRank) = vault.config(id);
(uint256 pendingCap, uint64 submittedAt) = vault.pendingCap(id);
MarketConfig memory marketConfig = vault.config(id);
PendingUint192 memory pendingCap = vault.pendingCap(id);

assertEq(newCap, 0, "newCap");
assertEq(withdrawRank, 0, "withdrawRank");
assertEq(pendingCap, 0, "pendingCap");
assertEq(submittedAt, 0, "submittedAt");
assertEq(marketConfig.cap, 0, "marketConfig.cap");
assertEq(marketConfig.withdrawRank, 0, "marketConfig.withdrawRank");
assertEq(pendingCap.value, 0, "pendingCap.value");
assertEq(pendingCap.submittedAt, 0, "pendingCap.submittedAt");
}

function testRevokeGuardian(uint256 elapsed) public {
Expand All @@ -87,15 +87,15 @@ contract GuardianTest is IntegrationTest {
vm.warp(block.timestamp + elapsed);

vm.expectEmit();
emit EventsLib.RevokeGuardian(GUARDIAN, IPending(address(vault)).pendingGuardian());
emit EventsLib.RevokeGuardian(GUARDIAN, vault.pendingGuardian());
vm.prank(GUARDIAN);
vault.revokeGuardian();

address newGuardian = vault.guardian();
(address pendingGuardian, uint96 submittedAt) = vault.pendingGuardian();
PendingAddress memory pendingGuardian = vault.pendingGuardian();

assertEq(newGuardian, GUARDIAN, "newGuardian");
assertEq(pendingGuardian, address(0), "pendingGuardian");
assertEq(submittedAt, 0, "submittedAt");
assertEq(pendingGuardian.value, address(0), "pendingGuardian.value");
assertEq(pendingGuardian.submittedAt, 0, "pendingGuardian.submittedAt");
}
}
Loading
Loading