1pragma solidity ^0.8.0;
2
3contract MultisigWallet {
4struct Transaction {
5address destination;
6uint256 value;
7bytes data;
8bool executed;
9uint256 numConfirmations;
10mapping(address => bool) confirmations;
11}
12
13address[] public owners;
14uint256 public requiredConfirmations;
15mapping(uint256 => Transaction) public transactions;
16uint256 public transactionCount;
17
18event Deposit(address indexed sender, uint256 value);
19event Submission(uint256 indexed transactionId);
20event Confirmation(address indexed sender, uint256 indexed transactionId);
21event Revocation(address indexed sender, uint256 indexed transactionId);
22event Execution(uint256 indexed transactionId);
23
24modifier onlyOwner() {
25require(isOwner(msg.sender), "Not an owner");
26_;
27}
28
29modifier transactionExists(uint256 _transactionId) {
30require(_transactionId < transactionCount, "Transaction does not exist");
31_;
32}
33
34modifier notConfirmed(uint256 _transactionId, address _owner) {
35require(!transactions[_transactionId].confirmations[_owner], "Transaction already confirmed by this owner");
36_;
37}
38
39modifier notExecuted(uint256 _transactionId) {
40require(!transactions[_transactionId].executed, "Transaction already executed");
41_;
42}
43
44constructor(address[] memory _owners, uint256 _requiredConfirmations) {
45require(_owners.length > 0, "Owners required");
46require(_requiredConfirmations > 0 && _requiredConfirmations <= _owners.length, "Invalid number of required confirmations");
47
48for (uint i = 0; i < _owners.length; i++) {
49require(_owners[i] != address(0), "Owner cannot be the zero address");
50
51for (uint j = 0; j < i; j++) {
52require(_owners[i] != _owners[j], "Duplicate owner");
53}
54owners.push(_owners[i]);
55}
56requiredConfirmations = _requiredConfirmations;
57}
58
59function submitTransaction(address _destination, uint256 _value, bytes memory _data) public onlyOwner {
60uint256 transactionId = transactionCount;
61transactions[transactionId] = Transaction({
62destination: _destination,
63value: _value,
64data: _data,
65executed: false,
66numConfirmations: 0
67});
68transactionCount++;
69emit Submission(transactionId);
70}
71
72function confirmTransaction(uint256 _transactionId) public onlyOwner transactionExists(_transactionId) notConfirmed(_transactionId, msg.sender) notExecuted(_transactionId) {
73Transaction storage transaction = transactions[_transactionId];
74transaction.confirmations[msg.sender] = true;
75transaction.numConfirmations++;
76emit Confirmation(msg.sender, _transactionId);
77}
78
79function revokeConfirmation(uint256 _transactionId) public onlyOwner transactionExists(_transactionId) notExecuted(_transactionId) {
80require(transactions[_transactionId].confirmations[msg.sender], "Transaction not confirmed by this owner");
81Transaction storage transaction = transactions[_transactionId];
82transaction.confirmations[msg.sender] = false;
83transaction.numConfirmations--;
84emit Revocation(msg.sender, _transactionId);
85}
86
87function executeTransaction(uint256 _transactionId) public onlyOwner transactionExists(_transactionId) notExecuted(_transactionId) {
88Transaction storage transaction = transactions[_transactionId];
89require(transaction.numConfirmations >= requiredConfirmations, "Not enough confirmations");
90
91transaction.executed = true;
92(bool success, ) = transaction.destination.call{value: transaction.value}(transaction.data);
93require(success, "Transaction failed");
94
95emit Execution(_transactionId);
96}
97
98function isOwner(address _address) internal view returns (bool) {
99for (uint i = 0; i < owners.length; i++) {
100if (owners[i] == _address) {
101return true;
102}
103}
104return false;
105}
106
107
108receive() external payable {
109emit Deposit(msg.sender, msg.value);
110}
111}