Skip to content
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

feat: timelock and threshold #22

Merged
merged 4 commits into from
Oct 4, 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
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