MultiSigWallet.submitTransaction is designed to queue a transfer for dual-owner approval before execution, with the expectation that approved transactions can always be executed.
There is no check that _value <= address(this).balance. Both owners can approve a transaction whose value exceeds the wallet balance; executeTransaction then reverts on the .call, but the transaction persists in a fully-approved-but-unexecutable state with no cancellation mechanism.
Likelihood:
A transaction is submitted for a value larger than the wallet balance — either by mistake, or deliberately by one owner to prevent the other from recovering funds.
The wallet balance decreases between submission and execution (e.g., a prior transaction drains funds).
Impact:
Owners waste gas approving transactions that can never execute.
The transactions array fills with permanently stuck entries since there is no cancelTransaction function.
The wallet can be rendered completely inoperable if the only submittable transactions are blocked.
This test shows the full lifecycle of a stuck transaction — submission, dual approval, failed execution, and the inability to cancel:
Setup — A MultiSigWallet is funded with exactly 1 ETH.
Oversize submission — owner1 calls submitTransaction(recipient, 10 ether). No revert occurs. The transaction is queued with value = 10 ETH despite the wallet holding only 1 ETH.
Both owners approve — approveTransaction(0) is called by each owner. No revert — neither approval knows about the balance. The transaction is now fully approved.
Execution fails — executeTransaction(0) is called. The .call{value: 10 ether} fails because the wallet balance is 1 ETH. require(success) reverts with "Transaction failed". txn.executed remains false.
Permanently stuck — The transaction cannot be re-executed (same failure), cannot be cancelled (no function exists), and occupies a permanent slot in the transactions array.
To run: forge test --match-test test_stuckApprovedTransaction -vvvv
Add a balance guard at submission time and a cancelTransaction function for recovery:
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.