MultiSig Timelock

First Flight #55
Beginner FriendlyWallet
100 EXP
Submission Details
Impact: medium
Likelihood: medium

Proposing Transactions with Fewer Than 3 Signers Leads to Pinned (Stuck) Transactions That Cannot Be Executed

Author Revealed upon completion

The contract allows the owner to propose transactions regardless of the current signer count. However, execution requires exactly 3 confirmations (REQUIRED_CONFIRMATIONS = 3). If the signer count drops below 3 (e.g., via revokeSigningRole, possible down to 1), proposed transactions can accumulate but never reach the required confirmations, effectively "pinning" them in a pending state. This could lock funds if the contract holds ETH, as no withdrawals can occur until more signers are added.

Description: The contract uses a fixed confirmation threshold of 3 (REQUIRED_CONFIRMATIONS) for executing transactions, but does not prevent proposals when the current number of signers (s_signerCount) is below this threshold. Proposals can be made via proposeTransaction (restricted to the owner), incrementing s_transactionCount and storing the transaction details. Signers can attempt to confirm via confirmTransaction, but if fewer than 3 signers exist, the confirmation count can never reach 3.

In _executeTransaction, it checks if (txn.confirmations = REQUIRED_CONFIRMATIONS)

  • revokeSigningRole (allows reduction to 1 or 2 signers)

  • _executeTransaction (enforces fixed 3 confirmations)

Impact:

  • Funds in the contract become inaccessible if signers drop below 3 after proposals.

  • Wasted gas on futile confirmations/revocations for pinned transactions.

  • Reduced usability in a multisig setup, as it doesn't fail-fast on proposals that are doomed to fail.

  • Potential for denial-of-service if many invalid proposals are made, bloating storage (though limited by owner-only proposals).

Proof of Concept (Conceptual):

  1. Deploy contract (1 signer: owner).

  2. Grant 2 more signers (now 3).

  3. Propose a transaction (ID 0).

  4. Revoke 2 signers (now 1 signer).

  5. Attempt to confirm/execute ID 0: Fails due to insufficient confirmations. Transaction is stuck.

Recommendation:

  • Add a check in proposeTransaction or _proposeTransaction: if (s_signerCount < REQUIRED_CONFIRMATIONS) revert MultiSigTimelock__InsufficientSignersForProposal();.

  • In revokeSigningRole, add: if (s_signerCount - 1 < REQUIRED_CONFIRMATIONS && _hasPendingTransactions()) revert ...; (implement _hasPendingTransactions to scan for unexecuted txns).

  • Alternatively, make REQUIRED_CONFIRMATIONS dynamic: e.g., uint256 required = min(REQUIRED_CONFIRMATIONS, s_signerCount); in an internal getter, ensuring it's at least 1. This maintains flexibility but requires careful testing.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!