This repository contains a Solidity smart contract implementing a K-of-N multisignature wallet for EVM-compatible blockchains. This contract manages its own authorization logic based on multiple signatures verified on-chain.
It allows a predefined group of n authorized signers to collectively approve actions by reaching a threshold of k signatures. The contract emphasizes security and decentralization by having no single owner and requiring the multisignature mechanism even for managing the wallet itself.
This contract meets the following requirements:
- K-of-N Signing: Manages a set of
nauthorized signers (signers) and requires a minimum ofksignatures (threshold) to approve actions. Bothkandnare configurable. - Arbitrary Execution: The core
executefunction allows signers to approve calls to any target contract (_target) with any specified Ether value (_value) and arbitrary calldata (_data). This enables the multisig to interact with any other smart contract or perform any on-chain action. - Permissionless Execution Trigger: Anyone can submit a transaction proposal to the
executefunction. Authorization depends solely on providing a sufficient number of valid signatures from the authorized signer set, not on themsg.senderof the transaction. - On-Chain Signature Verification: The contract verifies signatures internally. It does not rely on the blockchain's native transaction signing mechanism or built-in multisig account features for authorization.
- ECDSA Signatures: Uses the standard Elliptic Curve Digital Signature Algorithm (ECDSA) common to Ethereum and EVM chains. Signatures (r, s, v components) are passed directly as parameters to the
executefunction. - EIP-712 Compliance: Implements EIP-712 for typed structured data hashing. This provides clearer signing messages for users (when using compatible wallets/signers) and robust protection against replay attacks across different contracts and chains via domain separation.
- Self-Governance: The contract governs itself. Changes to the signer set (via
updateSigners) or the signature threshold (viaupdateThreshold) must be executed through the standardexecutefunction, requiring k-of-n approval from the current set of signers. There is no special owner or admin role.
- Replay Protection: Crucially handled by the
nonceincremented on each execution and inclusion in the EIP-712 signed hash. The EIP-712 domain separator further prevents cross-chain or cross-contract replays. - Signature Verification: Relies on OpenZeppelin's battle-tested
ECDSA.recoverimplementation. Adds new validations on top of the precompiledecrecover. - Access Control: Internal visibility of governance functions prevents direct external calls. All state changes require passing the
_verifySignaturescheck withinexecute. - External Call Safety: Uses low-level
call.executefollows Check-Effect-Interaction pattern. Failures in external calls are logged via event but do not revert the multisig state change (nonce increment).
- Deployment:
- Successful deployment with valid parameters (k > 0, k <= n).
- Revert on invalid threshold (k=0, k > n).
- Revert on empty initial signer set.
- Revert on zero address in initial signer set.
- Revert on duplicate signers in initial set.
- Correct initial state (threshold, nonce, signers mapping/array).
executeFunction (Success Cases):- Execution with exactly
kvalid unique signatures. - Execution with more than
kvalid unique signatures. - Execution involving Ether transfer (
_value > 0). - Execution with complex
_datatargeting another contract. - Execution with empty
_datatargeting an EOA or contractreceive().
- Execution with exactly
executeFunction (Failure Cases):- Revert if
< ksignatures provided (initial check). - Revert if duplicated valid signatures.
- Revert on invalid signature format (e.g., incorrect length).
- Revert on invalid signature content (e.g., signed wrong hash/nonce).
- Revert if signature is from a non-authorized address.
- Revert on replay attack attempt (reusing signatures with an incremented nonce).
- Failure of external call (check for
ExecutionFailureevent, check nonce is incremented).
- Revert if
updateThreshold(viaexecute):- Successful threshold update.
- Revert on attempt to set threshold to 0.
- Revert on attempt to set threshold > current signer count.
- Correct event emission (
ThresholdUpdated,ExecutionSuccess).
updateSigners(viaexecute):- Successful signer set replacement.
- Revert on attempt to set empty signer array.
- Revert on attempt to set zero address as a signer.
- Revert on attempt to set duplicate signers in the new set.
- Revert if the update makes the current threshold impossible (threshold > new signer count).
- Correct state updates (
isSignermap,signersarray,signerCount). - Correct event emission (
SignersUpdated,ExecutionSuccess).
- Nonce: Ensure nonce increments exactly once per successful
executecall (whether internal or external call succeeds/fails) and does not increment on revert. - Ether Handling: Test direct Ether transfer to the contract via
receive().
- OpenZeppelin Contracts: Uses
ECDSA.solfor signature recovery andEIP712.solfor EIP-712 hashing support.