DatingDapp

First Flight #33
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: low
Invalid

MultiSig: Lack of Arbitrary Call Data in Multisig Transactions

Summary

The multisig contract does not allow for the inclusion of arbitrary data when creating a transaction. Consequently, only simple Ether transfers are supported, without the ability to interact with other smart contracts through function calls, if the matched users need to interact with a smart contract in their date they won't be able to do so. Modern multisigs typically include a bytes data field in transactions, allowing the owners to perform complex operations and interact with DeFi protocols or token contracts.

Vulnerability Details

  1. No data Field in Transactions

    • The Transaction struct only has to, value, and flags for approvedByOwner1, approvedByOwner2, and executed.

    • Unlike the reference implementation, there is no data field that can store arbitrary function calls or parameters, limiting the wallet’s capabilities to simple ETH transfers.

  2. Restricted Functionality

    • Without a data payload, the multisig cannot call external contracts with method signatures or parameters, preventing most on-chain interactions (e.g., swapping tokens, interacting with protocols, or calling custom functions in other contracts).

Impact

  • Limited Use Cases: The current implementation can only send ETH and cannot perform DeFi operations, upgrade proxies, or transfer ERC20 tokens directly (unless done manually by an external call with a separate contract).

  • User Inconvenience: Owners are forced to rely on external solutions or deploy additional wrappers if they want to interact with other contracts.

  • Not Future-Proof: As the contract ecosystem evolves, not being able to pass arbitrary data significantly reduces the multisig’s usefulness.

Tools Used

  • Manual Code Inspection: Observed that the Transaction struct lacks a bytes data parameter.

  • High-Level Contract Comparison: Compared the simple multisig’s structure with a typical production multisig that supports arbitrary calls.

Recommendations

  • Add a bytes data Field

    • Include a data parameter in the submitTransaction function and the Transaction struct:

      struct Transaction {
      address to;
      uint256 value;
      bytes data;
      bool approvedByOwner1;
      bool approvedByOwner2;
      bool executed;
      }
    • Update executeTransaction to call:

      (bool success, ) = txn.to.call{ value: txn.value }(txn.data);
    • Update the submitTransaction to:

      /// @notice Submit a transaction with arbitrary data
      function submitTransaction(
      address _to,
      uint256 _value,
      bytes calldata _data
      )
      external
      onlyOwners
      {
      if (_to == address(0)) revert InvalidRecipient();
      if (_value == 0) revert InvalidAmount();
      // Push a new Transaction with the `data` field
      transactions.push(Transaction({
      to: _to,
      value: _value,
      data: _data,
      approvedByOwner1: false,
      approvedByOwner2: false,
      executed: false
      }));
      uint256 txId = transactions.length - 1;
      emit TransactionCreated(txId, _to, _value);
      }
  • Use a Reference Implementation

Updates

Appeal created

n0kto Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

Informational or Gas

Please read the CodeHawks documentation to know which submissions are valid. If you disagree, provide a coded PoC and explain the real likelyhood and the detailed impact on the mainnet without any supposition (if, it could, etc) to prove your point.

Support

FAQs

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