MultiSig Timelock

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

Mismatch Between Documented Signer Privileges and Enforced Owner-Only Proposals

Author Revealed upon completion

Description

The proposeTransaction() function is restricted by the onlyOwner modifier, allowing only the contract owner to create transaction proposals. This behavior contradicts the documented design, which states that any address holding the SIGNING_ROLE should be able to propose transactions. As a result, signers are limited to confirming and executing transactions but cannot independently initiate proposals.

This creates a mismatch between the contract’s documented governance model and its actual implementation.


Impact

  • centralization risk, the owner becomes a single point of control for all transaction initiation.

  • Prevents signers from proposing urgent or corrective transactions if the owner is unavailable, compromised, or unresponsive.

  • Breaks assumptions made by users, auditors, and integrators who rely on the documentation.

  • Reduces fault tolerance and decentralization guarantees expected from a multisignature wallet.


Affected Area

permalink: https://github.com/CodeHawks-Contests/2025-12-multisig-timelock/blob/3c88fea850b25724b71778bdc7bfe96c3bd97b63/src/MultiSigTimelock.sol#L249

Proof of Concept

function testSignerCannotProposeTransaction() public {
multiSigTimelock.grantSigningRole(SIGNER_TWO); // make SIGNER_TWO as signer
vm.deal(SIGNER_TWO, OWNER_BALANCE_ONE);
//signer trying to propose transaction
vm.prank(SIGNER_TWO);
vm.expectRevert();
multiSigTimelock.proposeTransaction(
SPENDER_ONE,
OWNER_BALANCE_ONE,
hex""
);
}

output:

Ran 1 test for test/unit/MultiSigTimelockTest.t.sol:MultiSigTimeLockTest
[PASS] testSignerCannotProposeTransaction() (gas: 102525)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.18ms (185.30µs CPU time)
Ran 1 test suite in 19.89ms (4.18ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

replace the onlyOwner modifier with role-based access control

onlyRole(SIGNING_ROLE)

This allows any authorized signer to propose transactions while preserving multisignature confirmation and timelock security.

Support

FAQs

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

Give us feedback!