DatingDapp

AI First Flight #6
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: low
Likelihood: low
Invalid

`MultiSigWallet::executeTransaction` does not check contract balance before executing, leading to wasted gas and poor UX

MultiSigWallet::executeTransaction does not check contract balance before executing, leading to wasted gas and poor UX

Description

The MultiSigWallet::executeTransaction function does not verify that the contract has sufficient balance before attempting to execute a transaction. While the transaction will ultimately revert if the balance is insufficient, users will have already spent gas on submitting and approving a transaction that can never succeed. Additionally, the generic "Transaction failed" error message does not indicate why the execution failed.

function executeTransaction(uint256 _txId) external onlyOwners {
require(_txId < transactions.length, "Invalid transaction ID");
Transaction storage txn = transactions[_txId];
require(!txn.executed, "Transaction already executed");
require(txn.approvedByOwner1 && txn.approvedByOwner2, "Not enough approvals");
txn.executed = true;
@> // No check for sufficient contract balance
(bool success,) = payable(txn.to).call{value: txn.value}("");
require(success, "Transaction failed");
emit TransactionExecuted(_txId, txn.to, txn.value);
}

Risk

Likelihood:

  • Users can submit and approve transactions for amounts greater than the contract balance

  • This occurs when matched users try to withdraw more than their pooled rewards

Impact:

  • Gas wasted on approvals for transactions that can never execute

  • Poor user experience with unclear error message when execution fails

  • No way to check upfront if a transaction is executable

Proof of Concept

  1. Two users match and receive a multisig with 1.8 ETH (2 ETH minus 10% fee)

  2. Owner1 submits a transaction to withdraw 2 ETH (more than available)

  3. Owner2 approves the transaction (gas wasted)

  4. Owner1 tries to execute - transaction reverts with generic "Transaction failed"

Add the following test:

function testExecuteTransactionWithInsufficientBalance() public {
address owner1 = makeAddr("owner1");
address owner2 = makeAddr("owner2");
// Deploy multisig with owner1 and owner2
MultiSigWallet multiSig = new MultiSigWallet(owner1, owner2);
// Fund with only 1 ETH
vm.deal(address(multiSig), 1 ether);
// Submit transaction for 2 ETH (more than balance)
vm.prank(owner1);
multiSig.submitTransaction(owner1, 2 ether);
// Both owners approve
vm.prank(owner1);
multiSig.approveTransaction(0);
vm.prank(owner2);
multiSig.approveTransaction(0);
// Execution fails with generic error
vm.prank(owner1);
vm.expectRevert("Transaction failed");
multiSig.executeTransaction(0);
}

Recommended Mitigation

Add a balance check before executing the transaction:

function executeTransaction(uint256 _txId) external onlyOwners {
require(_txId < transactions.length, "Invalid transaction ID");
Transaction storage txn = transactions[_txId];
require(!txn.executed, "Transaction already executed");
require(txn.approvedByOwner1 && txn.approvedByOwner2, "Not enough approvals");
+ require(address(this).balance >= txn.value, "Insufficient contract balance");
txn.executed = true;
(bool success,) = payable(txn.to).call{value: txn.value}("");
require(success, "Transaction failed");
emit TransactionExecuted(_txId, txn.to, txn.value);
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 20 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!