Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

MultiSignerERC7913Upgradeable

Inherits: Initializable, AbstractSigner

*Implementation of {AbstractSigner} using multiple ERC-7913 signers with a threshold-based signature verification system. This contract allows managing a set of authorized signers and requires a minimum number of signatures (threshold) to approve operations. It uses ERC-7913 formatted signers, which makes it natively compatible with ECDSA and ERC-1271 signers. Example of usage:

contract MyMultiSignerAccount is Account, MultiSignerERC7913, Initializable {
function initialize(bytes[] memory signers, uint64 threshold) public initializer {
_addSigners(signers);
_setThreshold(threshold);
}
function addSigners(bytes[] memory signers) public onlyEntryPointOrSelf {
_addSigners(signers);
}
function removeSigners(bytes[] memory signers) public onlyEntryPointOrSelf {
_removeSigners(signers);
}
function setThreshold(uint64 threshold) public onlyEntryPointOrSelf {
_setThreshold(threshold);
}
}

IMPORTANT: Failing to properly initialize the signers and threshold either during construction (if used standalone) or during initialization (if used as a clone) may leave the contract either front-runnable or unusable.*

State Variables

MultiSignerERC7913StorageLocation

bytes32 private constant MultiSignerERC7913StorageLocation =
    0xdfe169305a533e288e0557da8d289146d3cc75fa6e04c87599b048a60a4e5600;

Functions

_getMultiSignerERC7913Storage

function _getMultiSignerERC7913Storage() private pure returns (MultiSignerERC7913Storage storage $);

__MultiSignerERC7913_init

function __MultiSignerERC7913_init(bytes[] memory signers_, uint64 threshold_) internal onlyInitializing;

__MultiSignerERC7913_init_unchained

function __MultiSignerERC7913_init_unchained(bytes[] memory signers_, uint64 threshold_) internal onlyInitializing;

getSigners

Returns a slice of the set of authorized signers. Using start = 0 and end = type(uint64).max will return the entire set of signers. WARNING: Depending on the start and end, this operation can copy a large amount of data to memory, which can be expensive. This is designed for view accessors queried without gas fees. Using it in state-changing functions may become uncallable if the slice grows too large.

function getSigners(uint64 start, uint64 end) public view virtual returns (bytes[] memory);

getSignerCount

Returns the number of authorized signers

function getSignerCount() public view virtual returns (uint256);

isSigner

Returns whether the signer is an authorized signer.

function isSigner(bytes memory signer) public view virtual returns (bool);

threshold

Returns the minimum number of signers required to approve a multisignature operation.

function threshold() public view virtual returns (uint64);

_addSigners

Adds the newSigners to those allowed to sign on behalf of this contract. Internal version without access control. Requirements: Each of newSigners must be at least 20 bytes long. Reverts with MultiSignerERC7913InvalidSigner if not. Each of newSigners must not be authorized. See {isSigner}. Reverts with {MultiSignerERC7913AlreadyExists} if so.

function _addSigners(bytes[] memory newSigners) internal virtual;

_removeSigners

Removes the oldSigners from the authorized signers. Internal version without access control. Requirements: Each of oldSigners must be authorized. See isSigner. Otherwise {MultiSignerERC7913NonexistentSigner} is thrown. See {_validateReachableThreshold} for the threshold validation.

function _removeSigners(bytes[] memory oldSigners) internal virtual;

_setThreshold

Sets the signatures threshold required to approve a multisignature operation. Internal version without access control. Requirements: See _validateReachableThreshold for the threshold validation.

function _setThreshold(uint64 newThreshold) internal virtual;

_validateReachableThreshold

Validates the current threshold is reachable. Requirements: The getSignerCount must be greater or equal than to the {threshold}. Throws {MultiSignerERC7913UnreachableThreshold} if not.

function _validateReachableThreshold() internal view virtual;

_rawSignatureValidation

*Decodes, validates the signature and checks the signers are authorized. See _validateSignatures and {_validateThreshold} for more details. Example of signature encoding:

// Encode signers (verifier || key)
bytes memory signer1 = abi.encodePacked(verifier1, key1);
bytes memory signer2 = abi.encodePacked(verifier2, key2);
// Order signers by their id
if (keccak256(signer1) > keccak256(signer2)) {
(signer1, signer2) = (signer2, signer1);
(signature1, signature2) = (signature2, signature1);
}
// Assign ordered signers and signatures
bytes[] memory signers = new bytes[](2);
bytes[] memory signatures = new bytes[](2);
signers[0] = signer1;
signatures[0] = signature1;
signers[1] = signer2;
signatures[1] = signature2;
// Encode the multi signature
bytes memory signature = abi.encode(signers, signatures);

Requirements: The signature must be encoded as abi.encode(signers, signatures).*

function _rawSignatureValidation(bytes32 hash, bytes calldata signature)
    internal
    view
    virtual
    override
    returns (bool);

_validateSignatures

Validates the signatures using the signers and their corresponding signatures. Returns whether the signers are authorized and the signatures are valid for the given hash. IMPORTANT: Sorting the signers by their keccak256 hash will improve the gas efficiency of this function. See {SignatureChecker-areValidSignaturesNow-bytes32-bytes[]-bytes[]} for more details. Requirements: The signatures and signers arrays must be equal in length. Returns false otherwise.

function _validateSignatures(bytes32 hash, bytes[] memory signers, bytes[] memory signatures)
    internal
    view
    virtual
    returns (bool valid);

_validateThreshold

Validates that the number of signers meets the threshold requirement. Assumes the signers were already validated. See {_validateSignatures} for more details.

function _validateThreshold(bytes[] memory validatingSigners) internal view virtual returns (bool);

Events

ERC7913SignerAdded

Emitted when a signer is added.

event ERC7913SignerAdded(bytes indexed signers);

ERC7913SignerRemoved

Emitted when a signers is removed.

event ERC7913SignerRemoved(bytes indexed signers);

ERC7913ThresholdSet

Emitted when the threshold is updated.

event ERC7913ThresholdSet(uint64 threshold);

Errors

MultiSignerERC7913AlreadyExists

The signer already exists.

error MultiSignerERC7913AlreadyExists(bytes signer);

MultiSignerERC7913NonexistentSigner

The signer does not exist.

error MultiSignerERC7913NonexistentSigner(bytes signer);

MultiSignerERC7913InvalidSigner

The signer is less than 20 bytes long.

error MultiSignerERC7913InvalidSigner(bytes signer);

MultiSignerERC7913ZeroThreshold

The threshold is zero.

error MultiSignerERC7913ZeroThreshold();

MultiSignerERC7913UnreachableThreshold

The threshold is unreachable given the number of signers.

error MultiSignerERC7913UnreachableThreshold(uint64 signers, uint64 threshold);

Structs

MultiSignerERC7913Storage

Note: storage-location: erc7201:openzeppelin.storage.MultiSignerERC7913

struct MultiSignerERC7913Storage {
    EnumerableSet.BytesSet _signers;
    uint64 _threshold;
}