Multi-signature Example

Intro

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract MultiSigWallet {
    address[] public owners;
    uint public numConfirmationsRequired;

    struct Transaction {
        address to;
        uint value;
        bytes data;
        bool executed;
        uint numConfirmations;
    }

    mapping(uint => mapping(address => bool)) public confirmations;
    mapping(uint => Transaction) public transactions;
    uint public transactionCount;

    constructor(address[] memory _owners, uint _numConfirmationsRequired) {
        require(_owners.length > 0, "Owners required");
        require(
            _numConfirmationsRequired > 0 && _numConfirmationsRequired <= _owners.length,
            "Invalid number of required confirmations"
        );
        for (uint i = 0; i < _owners.length; i++) {
            require(_owners[i] != address(0), "Invalid owner");
            for (uint j = i + 1; j < _owners.length; j++) {
                require(_owners[i] != _owners[j], "Duplicate owner");
            }
            owners.push(_owners[i]);
        }
        numConfirmationsRequired = _numConfirmationsRequired;
    }

    function addTransaction(address _to, uint _value, bytes memory _data)
        public
        returns (uint)
    {
        uint transactionId = transactionCount;
        transactions[transactionId] = Transaction({
            to: _to,
            value: _value,
            data: _data,
            executed: false,
            numConfirmations: 0
        });
        transactionCount++;
        return transactionId;
    }

    function confirmTransaction(uint _transactionId) public {
        require(confirmations[_transactionId][msg.sender] == false, "Transaction already confirmed");
        require(transactions[_transactionId].executed == false, "Transaction already executed");

        confirmations[_transactionId][msg.sender] = true;
        transactions[_transactionId].numConfirmations++;

        if (transactions[_transactionId].numConfirmations >= numConfirmationsRequired) {
            executeTransaction(_transactionId);
        }
    }

    function executeTransaction(uint _transactionId) public {
        require(transactions[_transactionId].executed == false, "Transaction already executed");

        if (isConfirmed(_transactionId)) {
            Transaction storage transaction = transactions[_transactionId];
            transaction.executed = true;
            (bool success, ) = transaction.to.call{value: transaction.value}(transaction.data);
            require(success, "Transaction failed");
        }
    }

    function isConfirmed(uint _transactionId) public view returns (bool) {
        uint count = 0;
        for (uint i = 0; i < owners.length; i++) {
            if (confirmations[_transactionId][owners[i]]) {
                count++;
            }
            if (count == numConfirmationsRequired) {
                return true;
            }
        }
        return false;
    }
}

In this contract, multiple owners are able to create and confirm transactions before they can be executed. The contract stores the list of owners, the number of required confirmations, and each transaction's data. It also keeps track of which transactions have been confirmed and executed. To create a transaction, an owner calls the addTransaction function, which creates a new transaction with the provided information. Owners can then confirm the transaction using the confirmTransaction function. Once the required number of confirmations has been reached, any owner can execute the transaction by calling the executeTransaction function. The isConfirmed function is used to check whether a transaction has been confirmed by the required number of owners.

Last updated