This post is implementing a multisig wallet in solidity,where there must be a number of people to authorize transfer of funds from the wallet.
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract MultisigWallet {
event Deposit (address indexed sender,uint amount,uint balance);
event SubmitingTransaction(address indexed owner, uint indexed txIndex, address indexed to, uint value, bytes data);
event ConfirmingTransaction( address indexed owner, uint indexed txIndex);
event RevokingConfirmation(address indexed owner, uint indexed txIndex);
event ExecutingTransaction(address indexed owner, uint indexed txIndex);
//State Variables.
address[] public owners;
mapping(address => bool) public isOwner;
uint public numConfirmationsRequired;
struct Transaction {
address to;
uint value;
bytes data;
bool executed;
uint numConfirmations;
}
Transaction [] public transactions;
// mapping from tx index => owner => bool
mapping(uint => mapping(address => bool)) public isConfirmed;
constructor(address [] memory _owners, uint _numConfirmationsRequired) {
require(_owners.length > 0,"Owners Required");
require(_numConfirmationsRequired > 0 && _numConfirmationsRequired <= _owners.length, "Invalid number of required Confirmations");
for (uint i = 0; i < _owners.length; i++){
address owner = _owners[i];
require(owner != address(0), "invalid Owner");
require(!isOwner[owner], "Owner is not unique");
isOwner[owner] = true;
owners.push(owner);
}
numConfirmationsRequired = _numConfirmationsRequired;
}
modifier onlyOwner () {
require(isOwner[msg.sender], "Not Owner");
_;
}
modifier txExists(uint _txIndex) {
require(_txIndex < transactions.length, "Transaction does not exist");
_;
}
modifier notExecuted(uint _txIndex) {
require(!transactions[_txIndex].executed, "Transactions already executed");
_;
}
modifier notconfirmed(uint _txIndex){
require(!isConfirmed[_txIndex][msg.sender], "tx already confirmed");
_;
}
receive() external payable {
emit Deposit(msg.sender, msg.value, address(this).balance);
}
function deposit() external payable {
emit Deposit (msg.sender,msg.value,address(this).balance);
}
function submitTransaction(address _to, uint _value, bytes memory _data) public onlyOwner {
uint txIndex = transactions.length;
transactions.push(Transaction({
to: _to,
value: _value,
data: _data,
executed: false,
numConfirmations: 0
}));
emit SubmitingTransaction(msg.sender, txIndex, _to, _value, _data);
}
function confirmTransaction(uint _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) notconfirmed(_txIndex){
Transaction storage transaction = transactions[_txIndex];
isConfirmed[_txIndex][msg.sender] = true;
transaction.numConfirmations += 1;
emit ConfirmingTransaction( msg.sender, _txIndex);
}
function executeTransaction(uint _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) {
Transaction storage transaction = transactions[_txIndex];
require(transaction.numConfirmations >= numConfirmationsRequired,"Cannot Execute Transaction");
transaction.executed = true;
(bool success,) = transaction.to.call{value: transaction.value}(transaction.data);
require(success, "tx failed");
emit ExecutingTransaction(msg.sender, _txIndex);
}
function revokeConfirmation(uint _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex){
Transaction storage transaction = transactions [_txIndex];
require(isConfirmed[_txIndex][msg.sender], "Transaction not confirmed");
transaction.numConfirmations -= 1;
isConfirmed[_txIndex][msg.sender] = false;
emit RevokingConfirmation(msg.sender, _txIndex);
}
function getOwners() public view returns (address[] memory) {
return owners;
}
function getTransactionCount() public view returns (uint) {
return transactions.length;
}
}

