// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; /** * @title MultiSigWallet * @notice Multi-signature wallet for admin functions * @dev Requires multiple signatures for critical operations */ contract MultiSigWallet { address[] public owners; uint256 public required; mapping(bytes32 => bool) public executed; event Deposit(address indexed sender, uint256 amount); event SubmitTransaction(uint256 indexed txIndex, address indexed owner, address indexed to, uint256 value, bytes data); event ConfirmTransaction(uint256 indexed txIndex, address indexed owner); event RevokeConfirmation(uint256 indexed txIndex, address indexed owner); event ExecuteTransaction(uint256 indexed txIndex, address indexed owner); modifier onlyOwner() { require(isOwner(msg.sender), "Not owner"); _; } modifier txExists(uint256 _txIndex) { require(_txIndex < transactions.length, "Transaction does not exist"); _; } modifier notExecuted(uint256 _txIndex) { require(!transactions[_txIndex].executed, "Transaction already executed"); _; } modifier notConfirmed(uint256 _txIndex) { require(!confirmations[_txIndex][msg.sender], "Transaction already confirmed"); _; } struct Transaction { address to; uint256 value; bytes data; bool executed; } Transaction[] public transactions; mapping(uint256 => mapping(address => bool)) public confirmations; constructor(address[] memory _owners, uint256 _required) { require(_owners.length > 0, "Owners required"); require(_required > 0 && _required <= _owners.length, "Invalid required"); owners = _owners; required = _required; } receive() external payable { emit Deposit(msg.sender, msg.value); } function isOwner(address addr) public view returns (bool) { for (uint256 i = 0; i < owners.length; i++) { if (owners[i] == addr) return true; } return false; } function submitTransaction(address _to, uint256 _value, bytes memory _data) public onlyOwner returns (uint256) { uint256 txIndex = transactions.length; transactions.push(Transaction({ to: _to, value: _value, data: _data, executed: false })); emit SubmitTransaction(txIndex, msg.sender, _to, _value, _data); confirmTransaction(txIndex); return txIndex; } function confirmTransaction(uint256 _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) notConfirmed(_txIndex) { confirmations[_txIndex][msg.sender] = true; emit ConfirmTransaction(_txIndex, msg.sender); if (isConfirmed(_txIndex)) { executeTransaction(_txIndex); } } function revokeConfirmation(uint256 _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) { require(confirmations[_txIndex][msg.sender], "Transaction not confirmed"); confirmations[_txIndex][msg.sender] = false; emit RevokeConfirmation(_txIndex, msg.sender); } function executeTransaction(uint256 _txIndex) public txExists(_txIndex) notExecuted(_txIndex) { require(isConfirmed(_txIndex), "Transaction not confirmed"); Transaction storage transaction = transactions[_txIndex]; transaction.executed = true; (bool success, ) = transaction.to.call{value: transaction.value}(transaction.data); require(success, "Transaction execution failed"); emit ExecuteTransaction(_txIndex, msg.sender); } function isConfirmed(uint256 _txIndex) public view returns (bool) { uint256 count = 0; for (uint256 i = 0; i < owners.length; i++) { if (confirmations[_txIndex][owners[i]]) count++; if (count == required) return true; } return false; } function getTransactionCount() public view returns (uint256) { return transactions.length; } function getOwners() public view returns (address[] memory) { return owners; } }