Skip to content

Commit

Permalink
feat: timelock and threshold (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmanuelJet authored Oct 4, 2024
1 parent ec43128 commit 98bd7f6
Show file tree
Hide file tree
Showing 32 changed files with 1,651 additions and 566 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,20 @@ An open-source, enterprise-grade Multi-Signature Vault smart contract developed

## Features

- Multi-signature functionality with flexible threshold settings.
- Separate timelocks for transactions and owner overrides.
- Multi-Signature Vault functionality to manage **ETH** and **ERC20** tokens.
- Separate timelocks for transactions and owner overrides with delays.
- Role-based access control (Owner, Executor, Signers).
- Secure self-destruct mechanism with safety checks.
- Flexible and administrative threshold settings.

## Use Cases

The MultiSigEnterpriseVault is ideal for:

- **Enterprise Teams**: Organizations that require multiple approvals before executing high-value transactions, ensuring decentralized control.
- **DAOs (Decentralised Autonomous Organizations)**: Enables decentralized decision-making with threshold-based approvals for critical actions.
- **Family or Joint Accounts**: Multiple signatories can manage shared assets securely with transaction approval mechanisms.
- **Fund Custodians**: Securely manage pooled funds with transaction timelock to prevent unilateral decisions.

## Getting Started

Expand Down
6 changes: 4 additions & 2 deletions script/MultiSigEnterpriseVault.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ contract MultiSigEnterpriseVaultScript is Script {
vm.startBroadcast();

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

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

console.log('MultiSigVault Contract Address:', vaultAddress);
Expand Down
75 changes: 59 additions & 16 deletions src/MultiSigEnterpriseVault.sol
Original file line number Diff line number Diff line change
@@ -1,42 +1,85 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.27;

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

/**
* @title MultiSig Enterprise Vault Contract
* @author Emmanuel Joseph (JET)
* @notice This contract manages a Multi-Signature Vault system, providing role-based access control with customizable
* signatory threshold and timelocks for actions. The vault supports both ETH and ERC20 tokens for transaction execution.
*/
contract MultiSigEnterpriseVault is MultiSigTransaction, IMultiSigEnterpriseVault {
contract MultiSigEnterpriseVault is MultiSigTransaction {
/**
* @dev Initializes the MultiSigEnterpriseVault with the owner, initial signatory threshold, and owner override limit.
* @dev Initializes the MultiSigEnterpriseVault with the owner, initial signatory threshold, initial timelock, and owner override timelock.
* @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.
* @param initialMultiSigTimelock The initial timelock for signatory approval.
* @param initialOwnerOverrideTimelock The initial timelock for owner override.
*/
constructor(
address owner,
uint256 initialThreshold,
uint256 initialOwnerOverrideLimit
) MultiSigTransaction(owner, initialThreshold, initialOwnerOverrideLimit) {
AddressUtils.requireValidUserAddress(owner);
uint256 initialMultiSigTimelock,
uint256 initialOwnerOverrideTimelock
) MultiSigTransaction(owner, initialThreshold, initialMultiSigTimelock, initialOwnerOverrideTimelock) {}

/**
* @dev Modifier to ensure an owner can perform an action without requiring signatory approval.
* Reverts with `SignersApprovalRequired` if the total number of signers is equal to or greater than the signatory threshold.
*/
modifier withoutSignersApproval() {
if (_totalValidSigners() >= signatoryThreshold) {
revert SignersApprovalRequired();
}
_;
}

/**
* @notice Owner updates the signatory threshold for the vault.
* @notice 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.
* @dev
* - Requires the total valid signers to be less than the signatory threshold.
* - Emits a `ThresholdUpdated` event upon successful execution.
* - Only callable by the owner.
*/
function ownerUpdateSignatoryThreshold(
function updateSignatoryThreshold(
uint256 newThreshold
) public onlyOwner {
if (newThreshold > signatoryThreshold && totalSigners() >= signatoryThreshold) {
revert SignersApprovalRequired();
}
) public onlyOwner withoutSignersApproval {
_updateSignatoryThreshold(newThreshold);
}

signatoryThreshold = newThreshold;
emit ThresholdUpdated(newThreshold);
/**
* @notice Adds a new signer to the vault.
* @param newSigner The address of the new signer.
* @dev
* - Requires the new signer to be a valid address.
* - Requires the total valid signers to be less than the signatory threshold.
* - Emits a `SignerAdded` event upon successful execution.
* - Only callable by the owner.
*/
function addSigner(
address newSigner
) public onlyOwner withoutSignersApproval {
AddressUtils.requireValidUserAddress(newSigner);
if (isSigner(newSigner)) revert SignerAlreadyExists(newSigner);
_addSigner(newSigner);
}

/**
* @notice Removes a signer from the vault.
* @param signer The address of the signer to be removed.
* @dev
* - Requires the signer to be a valid address.
* - Requires the total valid signers to be less than the signatory threshold.
* - Emits a `SignerRemoved` event upon successful execution.
* - Only callable by the owner.
*/
function removeSigner(
address signer
) public onlyOwner withoutSignersApproval {
if (!isSigner(signer)) revert SignerDoesNotExist(signer);
_removeSigner(signer);
}
}
Loading

0 comments on commit 98bd7f6

Please sign in to comment.