From b5134b6d18492658f28ab3b7ab82b1022b4002dd Mon Sep 17 00:00:00 2001 From: MathisGD Date: Fri, 9 Aug 2024 20:13:16 +0200 Subject: [PATCH 1/2] feat: mutable name and symbol --- src/MetaMorpho.sol | 27 +++++++++++++++++++ src/interfaces/IMetaMorpho.sol | 6 +++++ src/libraries/ConstantsLib.sol | 6 +++++ src/libraries/ErrorsLib.sol | 3 +++ test/forge/DeploymentTest.sol | 48 ++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/src/MetaMorpho.sol b/src/MetaMorpho.sol index 96e2c7b8..002101ba 100644 --- a/src/MetaMorpho.sol +++ b/src/MetaMorpho.sol @@ -35,6 +35,7 @@ import { Math, SafeERC20 } from "../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol"; +import {console} from "forge-std/console.sol"; /// @title MetaMorpho /// @author Morpho Labs @@ -187,6 +188,32 @@ contract MetaMorpho is ERC4626, ERC20Permit, Ownable2Step, Multicall, IMetaMorph /* ONLY OWNER FUNCTIONS */ + /// @inheritdoc IMetaMorphoBase + function setName(string calldata newName) external onlyOwner { + uint256 length = bytes(newName).length; + if (length > 31) revert ErrorsLib.StringTooLong(); + + uint256 slot = ConstantsLib.NAME_SLOT; + bytes32 value = bytes32(bytes(newName)); + + assembly { + sstore(slot, or(value, mul(length, 2))) + } + } + + /// @inheritdoc IMetaMorphoBase + function setSymbol(string calldata newSymbol) external onlyOwner { + uint256 length = bytes(newSymbol).length; + if (length > 31) revert ErrorsLib.StringTooLong(); + + uint256 slot = ConstantsLib.SYMBOL_SLOT; + bytes32 value = bytes32(bytes(newSymbol)); + + assembly { + sstore(slot, or(value, mul(length, 2))) + } + } + /// @inheritdoc IMetaMorphoBase function setCurator(address newCurator) external onlyOwner { if (newCurator == curator) revert ErrorsLib.AlreadySet(); diff --git a/src/interfaces/IMetaMorpho.sol b/src/interfaces/IMetaMorpho.sol index 0baaaafa..f3db7487 100644 --- a/src/interfaces/IMetaMorpho.sol +++ b/src/interfaces/IMetaMorpho.sol @@ -118,6 +118,12 @@ interface IMetaMorphoBase { /// @dev Does not revert if there is no pending market removal. function revokePendingMarketRemoval(Id id) external; + /// @notice Changes the name of the vault. + function setName(string memory newName) external; + + /// @notice Changes the symbol of the vault. + function setSymbol(string memory newSymbol) 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. diff --git a/src/libraries/ConstantsLib.sol b/src/libraries/ConstantsLib.sol index 8d0d1639..d7fb4546 100644 --- a/src/libraries/ConstantsLib.sol +++ b/src/libraries/ConstantsLib.sol @@ -17,4 +17,10 @@ library ConstantsLib { /// @dev The maximum fee the vault can have (50%). uint256 internal constant MAX_FEE = 0.5e18; + + /// @dev Storage slot of the name. + uint256 internal constant NAME_SLOT = 3; + + /// @dev Storage slot of the symbol. + uint256 internal constant SYMBOL_SLOT = 4; } diff --git a/src/libraries/ErrorsLib.sol b/src/libraries/ErrorsLib.sol index e64fa9b6..5b2560c7 100644 --- a/src/libraries/ErrorsLib.sol +++ b/src/libraries/ErrorsLib.sol @@ -95,4 +95,7 @@ library ErrorsLib { /// @notice Thrown when all caps have been reached. error AllCapsReached(); + + /// @notice Thrown when trying to set a string that is too long. + error StringTooLong(); } diff --git a/test/forge/DeploymentTest.sol b/test/forge/DeploymentTest.sol index dd8d935b..6c3944b9 100644 --- a/test/forge/DeploymentTest.sol +++ b/test/forge/DeploymentTest.sol @@ -4,6 +4,54 @@ pragma solidity ^0.8.0; import "./helpers/IntegrationTest.sol"; contract DeploymentTest is IntegrationTest { + function testSetName(string memory name) public { + vm.assume(bytes(name).length <= 31); + + vm.prank(OWNER); + vault.setName(name); + + assertEq(vault.name(), name); + } + + function testSetNameNotOwner(string memory name) public { + vm.assume(bytes(name).length <= 31); + + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, address(this))); + vault.setName(name); + } + + function testSetNameTooLong(string memory name) public { + vm.assume(bytes(name).length > 31); + + vm.expectRevert(ErrorsLib.StringTooLong.selector); + vm.prank(OWNER); + vault.setName(name); + } + + function testSetSymbolNotOwner(string memory name) public { + vm.assume(bytes(name).length <= 31); + + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, address(this))); + vault.setName(name); + } + + function testSetSymbolTooLong(string memory symbol) public { + vm.assume(bytes(symbol).length > 31); + + vm.expectRevert(ErrorsLib.StringTooLong.selector); + vm.prank(OWNER); + vault.setName(symbol); + } + + function testSetSymbol(string memory symbol) public { + vm.assume(bytes(symbol).length <= 31); + + vm.prank(OWNER); + vault.setSymbol(symbol); + + assertEq(vault.symbol(), symbol); + } + function testDeployMetaMorphoAddresssZero() public { vm.expectRevert(ErrorsLib.ZeroAddress.selector); createMetaMorpho(OWNER, address(0), ConstantsLib.MIN_TIMELOCK, address(loanToken), "MetaMorpho Vault", "MMV"); From da5f5d944d4137ce3c46c7cab4310c8128b89719 Mon Sep 17 00:00:00 2001 From: MathisGD Date: Sat, 10 Aug 2024 12:28:46 +0200 Subject: [PATCH 2/2] chore: remove console import --- src/MetaMorpho.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/MetaMorpho.sol b/src/MetaMorpho.sol index 002101ba..ddcf5ea4 100644 --- a/src/MetaMorpho.sol +++ b/src/MetaMorpho.sol @@ -35,7 +35,6 @@ import { Math, SafeERC20 } from "../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC4626.sol"; -import {console} from "forge-std/console.sol"; /// @title MetaMorpho /// @author Morpho Labs