Skip to content

Commit

Permalink
feat: owner role (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmanuelJet authored Sep 26, 2024
1 parent b43b7da commit 2512775
Show file tree
Hide file tree
Showing 29 changed files with 1,253 additions and 58 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ If you have any questions or need further assistance, feel free to reach out in
Describe the purpose of the PR so that if you looked at it in 6 months, it would be clear from the overview why this was created. E.g.:
```md
This PR entails the implementation of #2. It introduces the owner role functionality and the following actions were implemented:
This PR entails the implementation of emmanuelJet/MultiSigEnterpriseVault#2. It introduces the owner role functionality and the following actions were implemented:
- **[feat]** `OwnerContract` with owner functionalities
- **[perf]** Optimized `foundry.toml` file
Expand Down
6 changes: 6 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ out = "out"
libs = ["lib"]
solc = "0.8.27"

[fmt]
tab_width = 2
quote_style = "single"
multiline_func_header = "params_first"
single_line_statement_blocks = "preserve"

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
19 changes: 0 additions & 19 deletions script/Counter.s.sol

This file was deleted.

27 changes: 27 additions & 0 deletions script/MultiSigEnterpriseVault.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.27;

import {Script, console} from 'forge-std/Script.sol';
import {MultiSigEnterpriseVault} from '../src/MultiSigEnterpriseVault.sol';

contract MultiSigEnterpriseVaultScript is Script {
MultiSigEnterpriseVault internal vault;
address internal vaultAddress;

function setUp() public {}

function run() public {
vm.startBroadcast();

uint256 initialThreshold = 3;
uint256 initialOwnerOverrideLimit = 3 days;
address vaultOwner = makeAddr('vaultOwner');

vault = new MultiSigEnterpriseVault(vaultOwner, initialThreshold, initialOwnerOverrideLimit);
vaultAddress = address(vault);

console.log('MultiSigVault Contract Address:', vaultAddress);

vm.stopBroadcast();
}
}
14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

47 changes: 47 additions & 0 deletions src/MultiSigEnterpriseVault.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.27;

import {User} from './components/User.sol';
import {AddressUtils} from './libraries/AddressUtils.sol';
import {IMultiSigEnterpriseVault} from './interfaces/IMultiSigEnterpriseVault.sol';

/**
* @title MultiSig Enterprise Vault Contract
* @author Emmanuel Joseph (JET)
*/
contract MultiSigEnterpriseVault is User, IMultiSigEnterpriseVault {
/// @notice The current threshold required for signatory approval.
uint256 public signatoryThreshold;

/**
* @dev Initializes the MultiSigEnterpriseVault with the owner, initial signatory threshold, and owner override limit.
* @param owner The address of the contract owner.
* @param initialThreshold The initial threshold for signatory approval.
* @param initialOwnerOverrideLimit The initial timelock limit for owner override.
*/
constructor(
address owner,
uint256 initialThreshold,
uint256 initialOwnerOverrideLimit
) User(owner, initialOwnerOverrideLimit) {
if (AddressUtils.isValidUserAddress(owner)) {
signatoryThreshold = initialThreshold;
}
}

/**
* @notice Owner updates the signatory threshold for the vault.
* @param newThreshold The new threshold value for signatory approval.
* @dev Only callable by the owner of the contract.
*/
function ownerUpdateSignatoryThreshold(
uint256 newThreshold
) public onlyOwner {
if (totalSigners() >= signatoryThreshold) {
revert SignersApprovalRequired();
}

signatoryThreshold = newThreshold;
emit ThresholdUpdated(newThreshold);
}
}
183 changes: 183 additions & 0 deletions src/components/User.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.27;

import '../libraries/Counters.sol';
import {IUser} from '../interfaces/IUser.sol';
import {OwnerRole} from './roles/OwnerRole.sol';
import {SignerRole} from './roles/SignerRole.sol';
import {RoleType} from '../utilities/VaultEnums.sol';
import {ExecutorRole} from './roles/ExecutorRole.sol';
import {UserProfile} from '../utilities/VaultStructs.sol';
import {AddressUtils} from '../libraries/AddressUtils.sol';

/**
* @title User Contract
* @author Emmanuel Joseph (JET)
* @dev Manages user profiles and integrates with the Owner role for user administration within the MultiSigVault system.
*/
abstract contract User is OwnerRole, ExecutorRole, SignerRole, IUser {
using Counters for Counters.Counter;
using AddressUtils for address;

/// @notice Using Counter library for total users
Counters.Counter private _userCount;

/// @notice Mapping to store UserProfile by their address
mapping(address => UserProfile) private _users;

/**
* @dev Constructor to initialize the User and Owner role contracts.
* @param ownerAddress The address of the owner to be assigned the OWNER_ROLE.
* @param initialOwnerOverrideLimit The initial timelock limit for owner override.
*/
constructor(
address ownerAddress,
uint256 initialOwnerOverrideLimit
) OwnerRole(ownerAddress, initialOwnerOverrideLimit) {
_addUser(ownerAddress, RoleType.OWNER);
}

/**
* @dev Modifier to restrict access to functions to only vault users.
* Reverts with `AccessControlUnauthorizedSigner` if the caller is not the signer.
*/
modifier onlyUser() {
if (!_isUser(_msgSender())) {
revert InvalidUserProfile(_msgSender());
}
_;
}

/**
* @notice Returns the total number of users.
* @return uint256 The total number of users.
* @dev Only callable by the owner.
*/
function totalUsers() public view onlyOwner returns (uint256) {
return _userCount.current();
}

/**
* @notice Returns the user profile object of a given address.
*
* @param user The address of the user whose profile is requested.
* @return UserProfile The user profile object associated with the provided address.
* @dev Requirements:
* - Limited to the owner account.
* - `user` cannot be the zero address.
*/
function getUserProfile(
address user
) public view onlyOwner returns (UserProfile memory) {
if (!_isUser(user)) {
revert InvalidUserProfile(user);
}
return _users[user];
}

/**
* @notice Adds a new executor.
* @param newExecutor The address of the new executor.
* @dev Only callable by the owner.
*/
function addExecutor(
address newExecutor
) public onlyOwner {
_addExecutor(newExecutor);
_addUser(newExecutor, RoleType.EXECUTOR);
}

/**
* @notice Updates the executor by replacing the old executor with a new one.
* @param newExecutor The address of the new executor.
* @dev Only callable by the owner.
*/
function updateExecutor(
address newExecutor
) public onlyOwner {
address oldExecutor = executor();
if (oldExecutor.isValidUserAddress() && newExecutor.isValidUserAddress()) {
_updateExecutor(newExecutor);
_removeUser(oldExecutor);
_addUser(newExecutor, RoleType.EXECUTOR);
}
}

/**
* @notice Removes the current executor.
* @dev Only callable by the owner.
*
* NOTE: Removing executor will leave the contract without an executor,
* thereby disabling any functionality that is only available to the executor.
*/
function removeExecutor() public onlyOwner {
address oldExecutor = executor();
if (oldExecutor.isValidUserAddress()) {
_removeExecutor();
_removeUser(oldExecutor);
}
}

/**
* @notice Adds a new signer user.
* @param newSigner The address of the new signer.
* @dev Only callable by the owner.
*/
function addSigner(
address newSigner
) public onlyOwner {
_addSigner(newSigner);
_addUser(newSigner, RoleType.SIGNER);
}

/**
* @notice Removes an existing signer and deletes the user's profile.
* @param signer The address of the signer to be removed.
* @dev Only callable by the owner.
*/
function removeSigner(
address signer
) public onlyOwner {
if (signer.isValidUserAddress()) {
_removeSigner(signer);
_removeUser(signer);
}
}

/**
* @notice Checks if an address is a user.
* @param user The address to check.
* @return status True if the address is a user, otherwise false.
*/
function _isUser(
address user
) internal view returns (bool status) {
status = _users[user].user != address(0);
}

/**
* @dev Adds a user profile and assigns a role.
*
* @param user The address of the user.
* @param role The role type assigned to the user.
* @dev This is a private function to store user details.
*/
function _addUser(address user, RoleType role) private {
_users[user] = UserProfile(user, role, block.timestamp);
_userCount.increment();
}

/**
* @notice Removes a user's profile.
* @param user The address of the user to be removed.
*/
function _removeUser(
address user
) private {
UserProfile storage profile = _users[user];
if (profile.user.isValidUserAddress()) {
delete _users[user];
_userCount.decrement();
}
}
}
Loading

0 comments on commit 2512775

Please sign in to comment.