Skip to content

owner => admin #30

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

Merged
merged 19 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 14 additions & 12 deletions src/PublicAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ contract PublicAllocator is IPublicAllocatorStaticTyping {
/* STORAGE */

/// @inheritdoc IPublicAllocatorBase
mapping(address => address) public owner;
mapping(address => address) public admin;
/// @inheritdoc IPublicAllocatorBase
mapping(address => uint256) public fee;
/// @inheritdoc IPublicAllocatorBase
Expand All @@ -47,9 +47,11 @@ contract PublicAllocator is IPublicAllocatorStaticTyping {

/* MODIFIER */

/// @dev Reverts if the caller is not the owner for this vault or the vault owner.
modifier onlyOwner(address vault) {
if (msg.sender != owner[vault] && msg.sender != IMetaMorpho(vault).owner()) revert ErrorsLib.NotOwner();
/// @dev Reverts if the caller is not the admin nor the owner of this vault.
modifier onlyAdminOrVaultOwner(address vault) {
if (msg.sender != admin[vault] && msg.sender != IMetaMorpho(vault).owner()) {
revert ErrorsLib.NotAdminNorVaultOwner();
}
_;
}

Expand All @@ -60,24 +62,24 @@ contract PublicAllocator is IPublicAllocatorStaticTyping {
MORPHO = IMorpho(morpho);
}

/* OWNER ONLY */
/* ADMIN OR VAULT OWNER ONLY */

/// @inheritdoc IPublicAllocatorBase
function setOwner(address vault, address newOwner) external onlyOwner(vault) {
if (owner[vault] == newOwner) revert ErrorsLib.AlreadySet();
owner[vault] = newOwner;
emit EventsLib.SetOwner(msg.sender, vault, newOwner);
function setAdmin(address vault, address newAdmin) external onlyAdminOrVaultOwner(vault) {
if (admin[vault] == newAdmin) revert ErrorsLib.AlreadySet();
admin[vault] = newAdmin;
emit EventsLib.SetAdmin(msg.sender, vault, newAdmin);
}

/// @inheritdoc IPublicAllocatorBase
function setFee(address vault, uint256 newFee) external onlyOwner(vault) {
function setFee(address vault, uint256 newFee) external onlyAdminOrVaultOwner(vault) {
if (fee[vault] == newFee) revert ErrorsLib.AlreadySet();
fee[vault] = newFee;
emit EventsLib.SetFee(msg.sender, vault, newFee);
}

/// @inheritdoc IPublicAllocatorBase
function setFlowCaps(address vault, FlowCapsConfig[] calldata config) external onlyOwner(vault) {
function setFlowCaps(address vault, FlowCapsConfig[] calldata config) external onlyAdminOrVaultOwner(vault) {
for (uint256 i = 0; i < config.length; i++) {
if (config[i].caps.maxIn > MAX_SETTABLE_FLOW_CAP || config[i].caps.maxOut > MAX_SETTABLE_FLOW_CAP) {
revert ErrorsLib.MaxSettableFlowCapExceeded();
Expand All @@ -89,7 +91,7 @@ contract PublicAllocator is IPublicAllocatorStaticTyping {
}

/// @inheritdoc IPublicAllocatorBase
function transferFee(address vault, address payable feeRecipient) external onlyOwner(vault) {
function transferFee(address vault, address payable feeRecipient) external onlyAdminOrVaultOwner(vault) {
uint256 claimed = accruedFee[vault];
accruedFee[vault] = 0;
feeRecipient.transfer(claimed);
Expand Down
11 changes: 5 additions & 6 deletions src/interfaces/IPublicAllocator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,11 @@ struct Withdrawal {
/// @dev This interface is used for factorizing IPublicAllocatorStaticTyping and IPublicAllocator.
/// @dev Consider using the IPublicAllocator interface instead of this one.
interface IPublicAllocatorBase {
/// @notice The address of the Morpho contract.
/// @notice The Morpho contract.
function MORPHO() external view returns (IMorpho);

/// @notice The address of the owner of the public allocator config for a given vault.
/// @dev The owner of the underlying vault always has the public allocator owner capabilities.
function owner(address vault) external view returns (address);
/// @notice The admin for a given vault.
function admin(address vault) external view returns (address);

/// @notice The current ETH fee for a given vault.
function fee(address vault) external view returns (uint256);
Expand All @@ -64,8 +63,8 @@ interface IPublicAllocatorBase {
external
payable;

/// @notice Sets the owner for a given vault.
function setOwner(address vault, address newOwner) external;
/// @notice Sets the admin for a given vault.
function setAdmin(address vault, address newAdmin) external;

/// @notice Sets the fee for a given vault.
function setFee(address vault, uint256 newFee) external;
Expand Down
4 changes: 2 additions & 2 deletions src/libraries/ErrorsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {Id} from "../../lib/metamorpho/src/interfaces/IMetaMorpho.sol";
/// @custom:contact security@morpho.org
/// @notice Library exposing error messages.
library ErrorsLib {
/// @notice Thrown when the `msg.sender` is not the `owner`.
error NotOwner();
/// @notice Thrown when the `msg.sender` is not the admin nor the owner of the vault.
error NotAdminNorVaultOwner();

/// @notice Thrown when the reallocation fee given is wrong.
error IncorrectFee();
Expand Down
10 changes: 5 additions & 5 deletions src/libraries/EventsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ library EventsLib {
address indexed sender, address indexed vault, Id indexed supplyMarketId, uint256 suppliedAssets
);

/// @notice Emitted when the owner is set for a vault.
event SetOwner(address indexed sender, address indexed vault, address indexed owner);
/// @notice Emitted when the admin is set for a vault.
event SetAdmin(address indexed sender, address indexed vault, address admin);

/// @notice Emitted when the owner changes the `fee` for a vault.
/// @notice Emitted when the fee is set for a vault.
event SetFee(address indexed sender, address indexed vault, uint256 fee);

/// @notice Emitted when the owner transfers the fee for a vault.
/// @notice Emitted when the fee is transfered for a vault.
event TransferFee(address indexed sender, address indexed vault, uint256 amount, address indexed feeRecipient);

/// @notice Emitted when the owner updates some flow caps for a vault.
/// @notice Emitted when the flow caps are set for a vault.
event SetFlowCaps(address indexed sender, address indexed vault, FlowCapsConfig[] config);
}
111 changes: 98 additions & 13 deletions test/PublicAllocatorTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,40 @@ contract PublicAllocatorTest is IntegrationTest {
_sortSupplyQueueIdleLast();
}

function testOwner() public {
assertEq(publicAllocator.owner(address(vault)), address(0));
function testAdmin() public {
assertEq(publicAllocator.admin(address(vault)), address(0));
}

function testSetOwner() public {
function testSetAdmin() public {
vm.prank(OWNER);
publicAllocator.setOwner(address(vault), address(1));
assertEq(publicAllocator.owner(address(vault)), address(1));
publicAllocator.setAdmin(address(vault), address(1));
assertEq(publicAllocator.admin(address(vault)), address(1));
}

function testSetOwnerFail() public {
function testSetAdminByAdmin(address sender, address newAdmin) public {
vm.assume(publicAllocator.admin(address(vault)) != sender);
vm.assume(sender != newAdmin);
vm.prank(OWNER);
publicAllocator.setAdmin(address(vault), sender);
vm.prank(sender);
publicAllocator.setAdmin(address(vault), newAdmin);
assertEq(publicAllocator.admin(address(vault)), newAdmin);
}

function testSetAdminAlreadySet() public {
vm.expectRevert(ErrorsLib.AlreadySet.selector);
vm.prank(OWNER);
publicAllocator.setOwner(address(vault), address(0));
publicAllocator.setAdmin(address(vault), address(0));
}

function testSetAdminAccessFail(address sender, address newAdmin) public {
vm.assume(sender != OWNER);
vm.assume(publicAllocator.admin(address(vault)) != sender);
vm.assume(publicAllocator.admin(address(vault)) != newAdmin);

vm.expectRevert(ErrorsLib.NotAdminNorVaultOwner.selector);
vm.prank(sender);
publicAllocator.setAdmin(address(vault), newAdmin);
}

function testReallocateCapZeroOutflowByDefault(uint128 flow) public {
Expand All @@ -112,28 +132,28 @@ contract PublicAllocatorTest is IntegrationTest {

function testConfigureFlowAccessFail(address sender) public {
vm.assume(sender != OWNER);
vm.assume(sender != address(0));
vm.assume(publicAllocator.admin(address(vault)) != sender);

flowCaps.push(FlowCapsConfig(idleParams.id(), FlowCaps(0, 0)));

vm.prank(sender);
vm.expectRevert(ErrorsLib.NotOwner.selector);
vm.expectRevert(ErrorsLib.NotAdminNorVaultOwner.selector);
publicAllocator.setFlowCaps(address(vault), flowCaps);
}

function testTransferFeeAccessFail(address sender, address payable recipient) public {
vm.assume(sender != OWNER);
vm.assume(sender != address(0));
vm.assume(publicAllocator.admin(address(vault)) != sender);
vm.prank(sender);
vm.expectRevert(ErrorsLib.NotOwner.selector);
vm.expectRevert(ErrorsLib.NotAdminNorVaultOwner.selector);
publicAllocator.transferFee(address(vault), recipient);
}

function testSetFeeAccessFail(address sender, uint256 fee) public {
vm.assume(sender != OWNER);
vm.assume(sender != address(0));
vm.assume(publicAllocator.admin(address(vault)) != sender);
vm.prank(sender);
vm.expectRevert(ErrorsLib.NotOwner.selector);
vm.expectRevert(ErrorsLib.NotAdminNorVaultOwner.selector);
publicAllocator.setFee(address(vault), fee);
}

Expand All @@ -146,6 +166,18 @@ contract PublicAllocatorTest is IntegrationTest {
assertEq(publicAllocator.fee(address(vault)), fee);
}

function testSetFeeByAdmin(uint256 fee, address sender) public {
vm.assume(publicAllocator.admin(address(vault)) != sender);
vm.assume(fee != publicAllocator.fee(address(vault)));
vm.prank(OWNER);
publicAllocator.setAdmin(address(vault), sender);
vm.prank(sender);
vm.expectEmit(address(publicAllocator));
emit EventsLib.SetFee(sender, address(vault), fee);
publicAllocator.setFee(address(vault), fee);
assertEq(publicAllocator.fee(address(vault)), fee);
}

function testSetFeeAlreadySet(uint256 fee) public {
vm.assume(fee != publicAllocator.fee(address(vault)));
vm.prank(OWNER);
Expand Down Expand Up @@ -180,6 +212,35 @@ contract PublicAllocatorTest is IntegrationTest {
assertEq(flowCap.maxOut, out1);
}

function testSetFlowCapsByAdmin(uint128 in0, uint128 out0, uint128 in1, uint128 out1, address sender) public {
vm.assume(publicAllocator.admin(address(vault)) != sender);
in0 = uint128(bound(in0, 0, MAX_SETTABLE_FLOW_CAP));
out0 = uint128(bound(out0, 0, MAX_SETTABLE_FLOW_CAP));
in1 = uint128(bound(in1, 0, MAX_SETTABLE_FLOW_CAP));
out1 = uint128(bound(out1, 0, MAX_SETTABLE_FLOW_CAP));

flowCaps.push(FlowCapsConfig(idleParams.id(), FlowCaps(in0, out0)));
flowCaps.push(FlowCapsConfig(allMarkets[0].id(), FlowCaps(in1, out1)));

vm.prank(OWNER);
publicAllocator.setAdmin(address(vault), sender);

vm.expectEmit(address(publicAllocator));
emit EventsLib.SetFlowCaps(sender, address(vault), flowCaps);

vm.prank(sender);
publicAllocator.setFlowCaps(address(vault), flowCaps);

FlowCaps memory flowCap;
flowCap = publicAllocator.flowCaps(address(vault), idleParams.id());
assertEq(flowCap.maxIn, in0);
assertEq(flowCap.maxOut, out0);

flowCap = publicAllocator.flowCaps(address(vault), allMarkets[0].id());
assertEq(flowCap.maxIn, in1);
assertEq(flowCap.maxOut, out1);
}

function testPublicReallocateEvent(uint128 flow, address sender) public {
flow = uint128(bound(flow, 1, CAP2 / 2));

Expand Down Expand Up @@ -298,6 +359,30 @@ contract PublicAllocatorTest is IntegrationTest {
assertEq(address(this).balance - before, 2 * 0.001 ether, "wrong fee transferred");
}

function testTransferFeeByAdminSuccess(address sender) public {
vm.assume(publicAllocator.admin(address(vault)) != sender);
vm.prank(OWNER);
publicAllocator.setAdmin(address(vault), sender);
vm.prank(sender);
publicAllocator.setFee(address(vault), 0.001 ether);

flowCaps.push(FlowCapsConfig(idleParams.id(), FlowCaps(0, 2 ether)));
flowCaps.push(FlowCapsConfig(allMarkets[0].id(), FlowCaps(2 ether, 0)));
vm.prank(OWNER);
publicAllocator.setFlowCaps(address(vault), flowCaps);
withdrawals.push(Withdrawal(idleParams, 1 ether));

publicAllocator.reallocateTo{value: 0.001 ether}(address(vault), withdrawals, allMarkets[0]);
publicAllocator.reallocateTo{value: 0.001 ether}(address(vault), withdrawals, allMarkets[0]);

uint256 before = address(this).balance;

vm.prank(sender);
publicAllocator.transferFee(address(vault), payable(address(this)));

assertEq(address(this).balance - before, 2 * 0.001 ether, "wrong fee transferred");
}

function testTransferFeeFail() public {
vm.prank(OWNER);
publicAllocator.setFee(address(vault), 0.001 ether);
Expand Down