From 42f0bc24feedb2eeac955d57ed45d943f1c7f75b Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Mon, 27 Nov 2023 12:29:50 +0100 Subject: [PATCH 1/4] refactor(interface): add metamorpho factory interface --- src/MetaMorpho.sol | 3 +-- src/MetaMorphoFactory.sol | 10 +++++++--- src/interfaces/IMetaMorphoFactory.sol | 23 +++++++++++++++++++++++ src/interfaces/IMorphoMarketParams.sol | 8 -------- test/forge/MetaMorphoFactoryTest.sol | 2 +- 5 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 src/interfaces/IMetaMorphoFactory.sol delete mode 100644 src/interfaces/IMorphoMarketParams.sol diff --git a/src/MetaMorpho.sol b/src/MetaMorpho.sol index aec32622..47320c4c 100644 --- a/src/MetaMorpho.sol +++ b/src/MetaMorpho.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.21; -import {IMorphoMarketParams} from "./interfaces/IMorphoMarketParams.sol"; import { MarketConfig, PendingUint192, @@ -721,7 +720,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /// @dev Returns the market params of the market defined by `id`. function _marketParams(Id id) internal view returns (MarketParams memory) { - return IMorphoMarketParams(address(MORPHO)).idToMarketParams(id); + return MORPHO.idToMarketParams(id); } /// @dev Accrues interest on Morpho Blue and returns the vault's assets & corresponding shares supplied on the diff --git a/src/MetaMorphoFactory.sol b/src/MetaMorphoFactory.sol index 6d40ef50..c419f9ea 100644 --- a/src/MetaMorphoFactory.sol +++ b/src/MetaMorphoFactory.sol @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.21; +import {IMetaMorpho} from "./interfaces/IMetaMorpho.sol"; +import {IMetaMorphoFactory} from "./interfaces/IMetaMorphoFactory.sol"; + import {EventsLib} from "./libraries/EventsLib.sol"; import {ErrorsLib} from "./libraries/ErrorsLib.sol"; @@ -10,7 +13,7 @@ import {MetaMorpho} from "./MetaMorpho.sol"; /// @author Morpho Labs /// @custom:contact security@morpho.org /// @notice This contract allows to create MetaMorpho vaults, and to index them easily. -contract MetaMorphoFactory { +contract MetaMorphoFactory is IMetaMorphoFactory { /* IMMUTABLES */ /// @notice The address of the Morpho contract. @@ -47,8 +50,9 @@ contract MetaMorphoFactory { string memory name, string memory symbol, bytes32 salt - ) external returns (MetaMorpho metaMorpho) { - metaMorpho = new MetaMorpho{salt: salt}(initialOwner, MORPHO, initialTimelock, asset, name, symbol); + ) external returns (IMetaMorpho metaMorpho) { + metaMorpho = + IMetaMorpho(address(new MetaMorpho{salt: salt}(initialOwner, MORPHO, initialTimelock, asset, name, symbol))); isMetaMorpho[address(metaMorpho)] = true; diff --git a/src/interfaces/IMetaMorphoFactory.sol b/src/interfaces/IMetaMorphoFactory.sol new file mode 100644 index 00000000..d9f42dcf --- /dev/null +++ b/src/interfaces/IMetaMorphoFactory.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +import {IMetaMorpho} from "./IMetaMorpho.sol"; + +/// @title IMetaMorphoFactory +/// @author Morpho Labs +/// @custom:contact security@morpho.org +/// @notice Interface of MetaMorpho's factory. +interface IMetaMorphoFactory { + function MORPHO() external returns (address); + + function isMetaMorpho(address target) external returns (bool); + + function createMetaMorpho( + address initialOwner, + uint256 initialTimelock, + address asset, + string memory name, + string memory symbol, + bytes32 salt + ) external returns (IMetaMorpho metaMorpho); +} diff --git a/src/interfaces/IMorphoMarketParams.sol b/src/interfaces/IMorphoMarketParams.sol deleted file mode 100644 index 9cac62cc..00000000 --- a/src/interfaces/IMorphoMarketParams.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity >=0.5.0; - -import {MarketParams, Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; - -interface IMorphoMarketParams { - function idToMarketParams(Id id) external view returns (MarketParams memory marketParams); -} diff --git a/test/forge/MetaMorphoFactoryTest.sol b/test/forge/MetaMorphoFactoryTest.sol index 072e0e82..1a6fe692 100644 --- a/test/forge/MetaMorphoFactoryTest.sol +++ b/test/forge/MetaMorphoFactoryTest.sol @@ -40,7 +40,7 @@ contract MetaMorphoFactoryTest is IntegrationTest { expectedAddress, address(this), initialOwner, initialTimelock, address(loanToken), name, symbol, salt ); - MetaMorpho metaMorpho = + IMetaMorpho metaMorpho = factory.createMetaMorpho(initialOwner, initialTimelock, address(loanToken), name, symbol, salt); assertEq(expectedAddress, address(metaMorpho), "computeCreate2Address"); From f9b0911bd15f5512bc1b61e90ad9a052796d4047 Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Mon, 27 Nov 2023 17:37:30 +0100 Subject: [PATCH 2/4] fix(ifc): make view functions view --- src/interfaces/IMetaMorphoFactory.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/IMetaMorphoFactory.sol b/src/interfaces/IMetaMorphoFactory.sol index d9f42dcf..a100d1fa 100644 --- a/src/interfaces/IMetaMorphoFactory.sol +++ b/src/interfaces/IMetaMorphoFactory.sol @@ -8,9 +8,9 @@ import {IMetaMorpho} from "./IMetaMorpho.sol"; /// @custom:contact security@morpho.org /// @notice Interface of MetaMorpho's factory. interface IMetaMorphoFactory { - function MORPHO() external returns (address); + function MORPHO() external view returns (address); - function isMetaMorpho(address target) external returns (bool); + function isMetaMorpho(address target) external view returns (bool); function createMetaMorpho( address initialOwner, From 1ed02bc7b27750f807c5d24572b4557972d17be8 Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Tue, 28 Nov 2023 15:30:36 +0100 Subject: [PATCH 3/4] refactor(ifc): move natspecs to interfaces --- src/MetaMorpho.sol | 105 ++++++++++---------------- src/MetaMorphoFactory.sol | 12 +-- src/interfaces/IMetaMorpho.sol | 99 ++++++++++++++++++++++++ src/interfaces/IMetaMorphoFactory.sol | 9 +++ 4 files changed, 149 insertions(+), 76 deletions(-) diff --git a/src/MetaMorpho.sol b/src/MetaMorpho.sol index 47320c4c..ff5d6b47 100644 --- a/src/MetaMorpho.sol +++ b/src/MetaMorpho.sol @@ -6,6 +6,7 @@ import { PendingUint192, PendingAddress, MarketAllocation, + IMetaMorphoBase, IMetaMorphoStaticTyping } from "./interfaces/IMetaMorpho.sol"; import {Id, MarketParams, Market, IMorpho} from "../lib/morpho-blue/src/interfaces/IMorpho.sol"; @@ -54,55 +55,51 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /* IMMUTABLES */ - /// @notice The address of the Morpho contract. + /// @inheritdoc IMetaMorphoBase IMorpho public immutable MORPHO; /* STORAGE */ - /// @notice The address of the curator. + /// @inheritdoc IMetaMorphoBase address public curator; - /// @notice Stores whether an address is an allocator or not. + /// @inheritdoc IMetaMorphoBase mapping(address => bool) public isAllocator; - /// @notice The current guardian. Can be set even without the timelock set. + /// @inheritdoc IMetaMorphoBase address public guardian; - /// @notice Stores the current configuration of each market. + /// @inheritdoc IMetaMorphoStaticTyping mapping(Id => MarketConfig) public config; - /// @notice The current timelock. + /// @inheritdoc IMetaMorphoBase uint256 public timelock; - /// @notice The pending guardian. + /// @inheritdoc IMetaMorphoStaticTyping PendingAddress public pendingGuardian; - /// @notice Stores the pending cap for each market. + /// @inheritdoc IMetaMorphoStaticTyping mapping(Id => PendingUint192) public pendingCap; - /// @notice The pending timelock. + /// @inheritdoc IMetaMorphoStaticTyping PendingUint192 public pendingTimelock; - /// @notice The current fee. + /// @inheritdoc IMetaMorphoBase uint96 public fee; - /// @notice The fee recipient. + /// @inheritdoc IMetaMorphoBase address public feeRecipient; - /// @notice The skim recipient. + /// @inheritdoc IMetaMorphoBase address public skimRecipient; - /// @dev Stores the order of markets on which liquidity is supplied upon deposit. - /// @dev Can contain any market. A market is skipped as soon as its supply cap is reached. + /// @inheritdoc IMetaMorphoBase Id[] public supplyQueue; - /// @dev Stores the order of markets from which liquidity is withdrawn upon withdrawal. - /// @dev Always contain all non-zero cap markets as well as all markets on which the vault supplies liquidity, - /// without duplicate. + /// @inheritdoc IMetaMorphoBase Id[] public withdrawQueue; - /// @notice Stores the total assets managed by this vault when the fee was last accrued. - /// @dev May be a little off `totalAssets()` after each interaction, due to some roundings. + /// @inheritdoc IMetaMorphoBase uint256 public lastTotalAssets; /* CONSTRUCTOR */ @@ -181,7 +178,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /* ONLY OWNER FUNCTIONS */ - /// @notice Sets `curator` to `newCurator`. + /// @inheritdoc IMetaMorphoBase function setCurator(address newCurator) external onlyOwner { if (newCurator == curator) revert ErrorsLib.AlreadySet(); @@ -190,7 +187,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.SetCurator(newCurator); } - /// @notice Sets `newAllocator` as an allocator or not (`newIsAllocator`). + /// @inheritdoc IMetaMorphoBase function setIsAllocator(address newAllocator, bool newIsAllocator) external onlyOwner { if (isAllocator[newAllocator] == newIsAllocator) revert ErrorsLib.AlreadySet(); @@ -199,7 +196,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.SetIsAllocator(newAllocator, newIsAllocator); } - /// @notice Sets `skimRecipient` to `newSkimRecipient`. + /// @inheritdoc IMetaMorphoBase function setSkimRecipient(address newSkimRecipient) external onlyOwner { if (newSkimRecipient == skimRecipient) revert ErrorsLib.AlreadySet(); @@ -208,9 +205,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.SetSkimRecipient(newSkimRecipient); } - /// @notice Submits a `newTimelock`. - /// @dev In case the new timelock is higher than the current one, the timelock is set immediately. - /// @dev Warning: Submitting a timelock will overwrite the current pending timelock. + /// @inheritdoc IMetaMorphoBase function submitTimelock(uint256 newTimelock) external onlyOwner { if (newTimelock == timelock) revert ErrorsLib.AlreadySet(); _checkTimelockBounds(newTimelock); @@ -228,7 +223,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph } } - /// @notice Sets the `fee` to `newFee`. + /// @inheritdoc IMetaMorphoBase function setFee(uint256 newFee) external onlyOwner { if (newFee == fee) revert ErrorsLib.AlreadySet(); if (newFee > ConstantsLib.MAX_FEE) revert ErrorsLib.MaxFeeExceeded(); @@ -243,7 +238,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.SetFee(_msgSender(), fee); } - /// @notice Sets `feeRecipient` to `newFeeRecipient`. + /// @inheritdoc IMetaMorphoBase function setFeeRecipient(address newFeeRecipient) external onlyOwner { if (newFeeRecipient == feeRecipient) revert ErrorsLib.AlreadySet(); if (newFeeRecipient == address(0) && fee != 0) revert ErrorsLib.ZeroFeeRecipient(); @@ -256,11 +251,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.SetFeeRecipient(newFeeRecipient); } - /// @notice Submits a `newGuardian`. - /// @notice Warning: a malicious guardian could disrupt the vault's operation, and would have the power to revoke - /// any pending guardian. - /// @dev In case there is no guardian, the gardian is set immediately. - /// @dev Warning: Submitting a gardian will overwrite the current pending gardian. + /// @inheritdoc IMetaMorphoBase function submitGuardian(address newGuardian) external onlyOwner { if (newGuardian == guardian) revert ErrorsLib.AlreadySet(); @@ -279,9 +270,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /* ONLY CURATOR FUNCTIONS */ - /// @notice Submits a `newSupplyCap` for the market defined by `marketParams`. - /// @dev In case the new cap is lower than the current one, the cap is set immediately. - /// @dev Warning: Submitting a cap will overwrite the current pending cap. + /// @inheritdoc IMetaMorphoBase function submitCap(MarketParams memory marketParams, uint256 newSupplyCap) external onlyCuratorRole { Id id = marketParams.id(); if (marketParams.loanToken != asset()) revert ErrorsLib.InconsistentAsset(id); @@ -302,8 +291,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph } } - /// @notice Submits a forced market removal from the vault, eventually losing all funds supplied to the market. - /// @dev Warning: Submitting a forced removal will overwrite the timestamp at which the market will be removable. + /// @inheritdoc IMetaMorphoBase function submitMarketRemoval(Id id) external onlyCuratorRole { if (config[id].removableAt != 0) revert ErrorsLib.AlreadySet(); if (!config[id].enabled) revert ErrorsLib.MarketNotEnabled(); @@ -318,9 +306,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /* ONLY ALLOCATOR FUNCTIONS */ - /// @notice Sets `supplyQueue` to `newSupplyQueue`. - /// @param newSupplyQueue is an array of enabled markets, and can contain duplicate markets, but it would only - /// increase the cost of depositing to the vault. + /// @inheritdoc IMetaMorphoBase function setSupplyQueue(Id[] calldata newSupplyQueue) external onlyAllocatorRole { uint256 length = newSupplyQueue.length; @@ -335,13 +321,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.SetSupplyQueue(_msgSender(), newSupplyQueue); } - /// @notice Sets the withdraw queue as a permutation of the previous one, although markets with both zero cap and - /// zero vault's supply can be removed from the permutation. - /// @notice This is the only entry point to disable a market. - /// @notice Removing a market requires the vault to have 0 supply on it; but anyone can supply on behalf of the - /// vault so the call to `sortWithdrawQueue` can be griefed by a frontrun. To circumvent this, the allocator can - /// simply bundle a reallocation that withdraws max from this market with a call to `sortWithdrawQueue`. - /// @param indexes The indexes of each market in the previous withdraw queue, in the new withdraw queue's order. + /// @inheritdoc IMetaMorphoBase function updateWithdrawQueue(uint256[] calldata indexes) external onlyAllocatorRole { uint256 newLength = indexes.length; uint256 currLength = withdrawQueue.length; @@ -383,16 +363,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.SetWithdrawQueue(_msgSender(), newWithdrawQueue); } - /// @notice Reallocates the vault's liquidity so as to reach a given allocation of assets on each given market. - /// @notice The allocator can withdraw from any market, even if it's not in the withdraw queue, as long as the loan - /// token of the market is the same as the vault's asset. - /// @dev The behavior of the reallocation can be altered by state changes, including: - /// - Deposits on the vault that supplies to markets that are expected to be supplied to during reallocation. - /// - Withdrawals from the vault that withdraws from markets that are expected to be withdrawn from during - /// reallocation. - /// - Donations to the vault on markets that are expected to be supplied to during reallocation. - /// - Withdrawals from markets that are expected to be withdrawn from during reallocation. - /// @dev Any additional liquidity withdrawn during reallocation will be kept idle. + /// @inheritdoc IMetaMorphoBase function reallocate(MarketAllocation[] calldata allocations) external onlyAllocatorRole { uint256 totalSupplied; uint256 totalWithdrawn; @@ -446,7 +417,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /* REVOKE FUNCTIONS */ - /// @notice Revokes the pending timelock. + /// @inheritdoc IMetaMorphoBase function revokePendingTimelock() external onlyGuardianRole { if (pendingTimelock.validAt == 0) revert ErrorsLib.NoPendingValue(); @@ -455,7 +426,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.RevokePendingTimelock(_msgSender()); } - /// @notice Revokes the pending guardian. + /// @inheritdoc IMetaMorphoBase function revokePendingGuardian() external onlyGuardianRole { if (pendingGuardian.validAt == 0) revert ErrorsLib.NoPendingValue(); @@ -464,7 +435,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.RevokePendingGuardian(_msgSender()); } - /// @notice Revokes the pending cap of the market defined by `id`. + /// @inheritdoc IMetaMorphoBase function revokePendingCap(Id id) external onlyCuratorOrGuardianRole { if (pendingCap[id].validAt == 0) revert ErrorsLib.NoPendingValue(); @@ -473,7 +444,7 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph emit EventsLib.RevokePendingCap(_msgSender(), id); } - /// @notice Revokes the pending removal of the market defined by `id`. + /// @inheritdoc IMetaMorphoBase function revokePendingMarketRemoval(Id id) external onlyCuratorOrGuardianRole { delete config[id].removableAt; @@ -482,33 +453,33 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /* EXTERNAL */ - /// @notice Returns the length of the supply queue. + /// @inheritdoc IMetaMorphoBase function supplyQueueLength() external view returns (uint256) { return supplyQueue.length; } - /// @notice Returns the length of the withdraw queue. + /// @inheritdoc IMetaMorphoBase function withdrawQueueLength() external view returns (uint256) { return withdrawQueue.length; } - /// @notice Accepts the pending timelock. + /// @inheritdoc IMetaMorphoBase function acceptTimelock() external afterTimelock(pendingTimelock.validAt) { _setTimelock(pendingTimelock.value); } - /// @notice Accepts the pending guardian. + /// @inheritdoc IMetaMorphoBase function acceptGuardian() external afterTimelock(pendingGuardian.validAt) { _setGuardian(pendingGuardian.value); } - /// @notice Accepts the pending cap of the market defined by `id`. + /// @inheritdoc IMetaMorphoBase function acceptCap(Id id) external afterTimelock(pendingCap[id].validAt) { // Safe "unchecked" cast because pendingCap <= type(uint184).max. _setCap(id, uint184(pendingCap[id].value)); } - /// @notice Skims the vault `token` balance to `skimRecipient`. + /// @inheritdoc IMetaMorphoBase function skim(address token) external { if (skimRecipient == address(0)) revert ErrorsLib.ZeroAddress(); diff --git a/src/MetaMorphoFactory.sol b/src/MetaMorphoFactory.sol index c419f9ea..209432ce 100644 --- a/src/MetaMorphoFactory.sol +++ b/src/MetaMorphoFactory.sol @@ -16,12 +16,12 @@ import {MetaMorpho} from "./MetaMorpho.sol"; contract MetaMorphoFactory is IMetaMorphoFactory { /* IMMUTABLES */ - /// @notice The address of the Morpho contract. + /// @inheritdoc IMetaMorphoFactory address public immutable MORPHO; /* STORAGE */ - /// @notice Whether a MetaMorpho vault was created with the factory. + /// @inheritdoc IMetaMorphoFactory mapping(address => bool) public isMetaMorpho; /* CONSTRUCTOR */ @@ -36,13 +36,7 @@ contract MetaMorphoFactory is IMetaMorphoFactory { /* EXTERNAL */ - /// @notice Creates a new MetaMorpho vault. - /// @param initialOwner The owner of the vault. - /// @param initialTimelock The initial timelock of the vault. - /// @param asset The address of the underlying asset. - /// @param name The name of the vault. - /// @param symbol The symbol of the vault. - /// @param salt The salt to use for the MetaMorpho vault's CREATE2 address. + /// @inheritdoc IMetaMorphoFactory function createMetaMorpho( address initialOwner, uint256 initialTimelock, diff --git a/src/interfaces/IMetaMorpho.sol b/src/interfaces/IMetaMorpho.sol index f92527ac..e6e47a73 100644 --- a/src/interfaces/IMetaMorpho.sol +++ b/src/interfaces/IMetaMorpho.sol @@ -30,57 +30,149 @@ interface IOwnable { /// @dev This interface is used for factorizing IMetaMorphoStaticTyping and IMetaMorpho. /// @dev Consider using the IMetaMorpho interface instead of this one. interface IMetaMorphoBase { + /// @notice The address of the Morpho contract. function MORPHO() external view returns (IMorpho); + /// @notice The address of the curator. function curator() external view returns (address); + + /// @notice Stores whether an address is an allocator or not. function isAllocator(address target) external view returns (bool); + + /// @notice The current guardian. Can be set even without the timelock set. function guardian() external view returns (address); + /// @notice The current fee. function fee() external view returns (uint96); + + /// @notice The fee recipient. function feeRecipient() external view returns (address); + + /// @notice The skim recipient. function skimRecipient() external view returns (address); + + /// @notice The current timelock. function timelock() external view returns (uint256); + + /// @dev Stores the order of markets on which liquidity is supplied upon deposit. + /// @dev Can contain any market. A market is skipped as soon as its supply cap is reached. function supplyQueue(uint256) external view returns (Id); + + /// @notice Returns the length of the supply queue. function supplyQueueLength() external view returns (uint256); + + /// @dev Stores the order of markets from which liquidity is withdrawn upon withdrawal. + /// @dev Always contain all non-zero cap markets as well as all markets on which the vault supplies liquidity, + /// without duplicate. function withdrawQueue(uint256) external view returns (Id); + + /// @notice Returns the length of the withdraw queue. function withdrawQueueLength() external view returns (uint256); + /// @notice Stores the total assets managed by this vault when the fee was last accrued. + /// @dev May be a little off `totalAssets()` after each interaction, due to some roundings. function lastTotalAssets() external view returns (uint256); + /// @notice Submits a `newTimelock`. + /// @dev In case the new timelock is higher than the current one, the timelock is set immediately. + /// @dev Warning: Submitting a timelock will overwrite the current pending timelock. function submitTimelock(uint256 newTimelock) external; + + /// @notice Accepts the pending timelock. function acceptTimelock() external; + + /// @notice Revokes the pending timelock. function revokePendingTimelock() external; + /// @notice Submits a `newSupplyCap` for the market defined by `marketParams`. + /// @dev In case the new cap is lower than the current one, the cap is set immediately. + /// @dev Warning: Submitting a cap will overwrite the current pending cap. function submitCap(MarketParams memory marketParams, uint256 supplyCap) external; + + /// @notice Accepts the pending cap of the market defined by `id`. function acceptCap(Id id) external; + + /// @notice Revokes the pending cap of the market defined by `id`. function revokePendingCap(Id id) external; + /// @notice Submits a forced market removal from the vault, eventually losing all funds supplied to the market. + /// @dev Warning: Submitting a forced removal will overwrite the timestamp at which the market will be removable. function submitMarketRemoval(Id id) external; + + /// @notice Revokes the pending removal of the market defined by `id`. function revokePendingMarketRemoval(Id id) external; + /// @notice Submits a `newGuardian`. + /// @notice Warning: a malicious guardian could disrupt the vault's operation, and would have the power to revoke + /// any pending guardian. + /// @dev In case there is no guardian, the gardian is set immediately. + /// @dev Warning: Submitting a gardian will overwrite the current pending gardian. function submitGuardian(address newGuardian) external; + + /// @notice Accepts the pending guardian. function acceptGuardian() external; + + /// @notice Revokes the pending guardian. function revokePendingGuardian() external; + /// @notice Skims the vault `token` balance to `skimRecipient`. function skim(address) external; + /// @notice Sets `newAllocator` as an allocator or not (`newIsAllocator`). function setIsAllocator(address newAllocator, bool newIsAllocator) external; + + /// @notice Sets `curator` to `newCurator`. function setCurator(address newCurator) external; + + /// @notice Sets the `fee` to `newFee`. function setFee(uint256 newFee) external; + + /// @notice Sets `feeRecipient` to `newFeeRecipient`. function setFeeRecipient(address newFeeRecipient) external; + + /// @notice Sets `skimRecipient` to `newSkimRecipient`. function setSkimRecipient(address) external; + /// @notice Sets `supplyQueue` to `newSupplyQueue`. + /// @param newSupplyQueue is an array of enabled markets, and can contain duplicate markets, but it would only + /// increase the cost of depositing to the vault. function setSupplyQueue(Id[] calldata newSupplyQueue) external; + + /// @notice Sets the withdraw queue as a permutation of the previous one, although markets with both zero cap and + /// zero vault's supply can be removed from the permutation. + /// @notice This is the only entry point to disable a market. + /// @notice Removing a market requires the vault to have 0 supply on it; but anyone can supply on behalf of the + /// vault so the call to `sortWithdrawQueue` can be griefed by a frontrun. To circumvent this, the allocator can + /// simply bundle a reallocation that withdraws max from this market with a call to `sortWithdrawQueue`. + /// @param indexes The indexes of each market in the previous withdraw queue, in the new withdraw queue's order. function updateWithdrawQueue(uint256[] calldata indexes) external; + + /// @notice Reallocates the vault's liquidity so as to reach a given allocation of assets on each given market. + /// @notice The allocator can withdraw from any market, even if it's not in the withdraw queue, as long as the loan + /// token of the market is the same as the vault's asset. + /// @dev The behavior of the reallocation can be altered by state changes, including: + /// - Deposits on the vault that supplies to markets that are expected to be supplied to during reallocation. + /// - Withdrawals from the vault that withdraws from markets that are expected to be withdrawn from during + /// reallocation. + /// - Donations to the vault on markets that are expected to be supplied to during reallocation. + /// - Withdrawals from markets that are expected to be withdrawn from during reallocation. + /// @dev Any additional liquidity withdrawn during reallocation will be kept idle. function reallocate(MarketAllocation[] calldata allocations) external; } /// @dev This interface is inherited by MetaMorpho so that function signatures are checked by the compiler. /// @dev Consider using the IMetaMorpho interface instead of this one. interface IMetaMorphoStaticTyping is IMetaMorphoBase { + /// @notice Stores the current configuration of each market. function config(Id) external view returns (uint184 cap, bool enabled, uint64 removableAt); + + /// @notice The pending guardian. function pendingGuardian() external view returns (address guardian, uint64 validAt); + + /// @notice Stores the pending cap for each market. function pendingCap(Id) external view returns (uint192 value, uint64 validAt); + + /// @notice The pending timelock. function pendingTimelock() external view returns (uint192 value, uint64 validAt); } @@ -89,8 +181,15 @@ interface IMetaMorphoStaticTyping is IMetaMorphoBase { /// @custom:contact security@morpho.org /// @dev Use this interface for MetaMorpho to have access to all the functions with the appropriate function signatures. interface IMetaMorpho is IMetaMorphoBase, IERC4626, IERC20Permit, IOwnable, IMulticall { + /// @notice Stores the current configuration of each market. function config(Id) external view returns (MarketConfig memory); + + /// @notice The pending guardian. function pendingGuardian() external view returns (PendingAddress memory); + + /// @notice Stores the pending cap for each market. function pendingCap(Id) external view returns (PendingUint192 memory); + + /// @notice The pending timelock. function pendingTimelock() external view returns (PendingUint192 memory); } diff --git a/src/interfaces/IMetaMorphoFactory.sol b/src/interfaces/IMetaMorphoFactory.sol index a100d1fa..1fc10bb8 100644 --- a/src/interfaces/IMetaMorphoFactory.sol +++ b/src/interfaces/IMetaMorphoFactory.sol @@ -8,10 +8,19 @@ import {IMetaMorpho} from "./IMetaMorpho.sol"; /// @custom:contact security@morpho.org /// @notice Interface of MetaMorpho's factory. interface IMetaMorphoFactory { + /// @notice The address of the Morpho contract. function MORPHO() external view returns (address); + /// @notice Whether a MetaMorpho vault was created with the factory. function isMetaMorpho(address target) external view returns (bool); + /// @notice Creates a new MetaMorpho vault. + /// @param initialOwner The owner of the vault. + /// @param initialTimelock The initial timelock of the vault. + /// @param asset The address of the underlying asset. + /// @param name The name of the vault. + /// @param symbol The symbol of the vault. + /// @param salt The salt to use for the MetaMorpho vault's CREATE2 address. function createMetaMorpho( address initialOwner, uint256 initialTimelock, From 60ae4fa745e857133567620fcb80ac390203f962 Mon Sep 17 00:00:00 2001 From: Rubilmax Date: Wed, 29 Nov 2023 09:45:31 +0100 Subject: [PATCH 4/4] fix(ifc): fix natspecs --- src/interfaces/IMetaMorpho.sol | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/interfaces/IMetaMorpho.sol b/src/interfaces/IMetaMorpho.sol index e6e47a73..1c7f9ca6 100644 --- a/src/interfaces/IMetaMorpho.sol +++ b/src/interfaces/IMetaMorpho.sol @@ -87,7 +87,7 @@ interface IMetaMorphoBase { /// @notice Submits a `newSupplyCap` for the market defined by `marketParams`. /// @dev In case the new cap is lower than the current one, the cap is set immediately. /// @dev Warning: Submitting a cap will overwrite the current pending cap. - function submitCap(MarketParams memory marketParams, uint256 supplyCap) external; + function submitCap(MarketParams memory marketParams, uint256 newSupplyCap) external; /// @notice Accepts the pending cap of the market defined by `id`. function acceptCap(Id id) external; @@ -95,7 +95,7 @@ interface IMetaMorphoBase { /// @notice Revokes the pending cap of the market defined by `id`. function revokePendingCap(Id id) external; - /// @notice Submits a forced market removal from the vault, eventually losing all funds supplied to the market. + /// @notice Submits a forced market removal from the vault, potentially losing all funds supplied to the market. /// @dev Warning: Submitting a forced removal will overwrite the timestamp at which the market will be removable. function submitMarketRemoval(Id id) external; @@ -131,7 +131,7 @@ interface IMetaMorphoBase { function setFeeRecipient(address newFeeRecipient) external; /// @notice Sets `skimRecipient` to `newSkimRecipient`. - function setSkimRecipient(address) external; + function setSkimRecipient(address newSkimRecipient) external; /// @notice Sets `supplyQueue` to `newSupplyQueue`. /// @param newSupplyQueue is an array of enabled markets, and can contain duplicate markets, but it would only @@ -142,8 +142,8 @@ interface IMetaMorphoBase { /// zero vault's supply can be removed from the permutation. /// @notice This is the only entry point to disable a market. /// @notice Removing a market requires the vault to have 0 supply on it; but anyone can supply on behalf of the - /// vault so the call to `sortWithdrawQueue` can be griefed by a frontrun. To circumvent this, the allocator can - /// simply bundle a reallocation that withdraws max from this market with a call to `sortWithdrawQueue`. + /// vault so the call to `updateWithdrawQueue` can be griefed by a frontrun. To circumvent this, the allocator can + /// simply bundle a reallocation that withdraws max from this market with a call to `updateWithdrawQueue`. /// @param indexes The indexes of each market in the previous withdraw queue, in the new withdraw queue's order. function updateWithdrawQueue(uint256[] calldata indexes) external; @@ -156,23 +156,22 @@ interface IMetaMorphoBase { /// reallocation. /// - Donations to the vault on markets that are expected to be supplied to during reallocation. /// - Withdrawals from markets that are expected to be withdrawn from during reallocation. - /// @dev Any additional liquidity withdrawn during reallocation will be kept idle. function reallocate(MarketAllocation[] calldata allocations) external; } /// @dev This interface is inherited by MetaMorpho so that function signatures are checked by the compiler. /// @dev Consider using the IMetaMorpho interface instead of this one. interface IMetaMorphoStaticTyping is IMetaMorphoBase { - /// @notice Stores the current configuration of each market. + /// @notice Returns the current configuration of each market. function config(Id) external view returns (uint184 cap, bool enabled, uint64 removableAt); - /// @notice The pending guardian. + /// @notice Returns the pending guardian. function pendingGuardian() external view returns (address guardian, uint64 validAt); - /// @notice Stores the pending cap for each market. + /// @notice Returns the pending cap for each market. function pendingCap(Id) external view returns (uint192 value, uint64 validAt); - /// @notice The pending timelock. + /// @notice Returns the pending timelock. function pendingTimelock() external view returns (uint192 value, uint64 validAt); } @@ -181,15 +180,15 @@ interface IMetaMorphoStaticTyping is IMetaMorphoBase { /// @custom:contact security@morpho.org /// @dev Use this interface for MetaMorpho to have access to all the functions with the appropriate function signatures. interface IMetaMorpho is IMetaMorphoBase, IERC4626, IERC20Permit, IOwnable, IMulticall { - /// @notice Stores the current configuration of each market. + /// @notice Returns the current configuration of each market. function config(Id) external view returns (MarketConfig memory); - /// @notice The pending guardian. + /// @notice Returns the pending guardian. function pendingGuardian() external view returns (PendingAddress memory); - /// @notice Stores the pending cap for each market. + /// @notice Returns the pending cap for each market. function pendingCap(Id) external view returns (PendingUint192 memory); - /// @notice The pending timelock. + /// @notice Returns the pending timelock. function pendingTimelock() external view returns (PendingUint192 memory); }