Skip to content

Commit

Permalink
Merge pull request #335 from morpho-org/feat/natspecs-interfaces
Browse files Browse the repository at this point in the history
refactor(ifc): move natspecs to interfaces
  • Loading branch information
Rubilmax authored Dec 13, 2023
2 parents bc6c789 + 60ae4fa commit 0b63c34
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 78 deletions.
105 changes: 38 additions & 67 deletions src/MetaMorpho.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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();

Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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);
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();

Expand All @@ -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);
Expand All @@ -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();
Expand All @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();

Expand All @@ -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();

Expand All @@ -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();

Expand All @@ -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;

Expand All @@ -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();

Expand Down
12 changes: 3 additions & 9 deletions src/MetaMorphoFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 0b63c34

Please sign in to comment.