My first smart contract… Very interesting…
pragma solidity 0.7.5;
contract MultiSigWallet {
// state variables
// create a address array to store the owner addresses
address[] public owners;
uint public numConfirmationsRequired;
uint public depositBalance;
mapping(address => bool) public isOwner;
// create the transaction struct
struct Transaction {
address payable to;
uint amount;
bool executed;
uint numConfirmations;
uint txId;
}
// ["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4", "0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2", "0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db"], 2
Transaction[] public transactions;
event SubmitTransaction(uint _txIndex, address _from, address _to, uint _amount);
event ConfirmTransaction(address indexed owner, uint txIndex);
event RevokeConfirmation(address indexed owner, uint txIndex);
event ExecuteTransaction(address indexed owner, address indexed to, uint txIndex);
event Deposit(address indexed owner, uint amount);
// double mapping where the address can have multiple txId that returns a true or false
// mapping[msg.sender][5] = true;
mapping(address => mapping(uint => bool)) public isConfirmed;
modifier onlyOwner() {
bool owner = false;
// loop through all owner addresses in the array
for (uint i; i < owners.length; i++){
if (owners[i] == msg.sender){
owner = true;
}
}
// test if the owner is true
require(owner == true, "only a wallet owner can make the transaction!!");
_;
}
modifier txExists(uint _txIndex) {
require(_txIndex < transactions.length, "tx does not exist");
_;
}
modifier notExecuted(uint _txIndex) {
require(!transactions[_txIndex].executed, "tx already executed");
_;
}
modifier txConfirmed (uint _txIndex) {
require(isConfirmed[msg.sender][_txIndex] != true, "you can only confirm once");
_;
}
// initializes the contract with the owners and numConfirmationsRequired
constructor (address[] memory _owners, uint _numConfirmationsRequired){
owners = _owners;
numConfirmationsRequired = _numConfirmationsRequired;
}
function deposit() public payable returns(uint) {
depositBalance += msg.value;
emit Deposit(msg.sender, msg.value);
return depositBalance;
}
function submitTransaction(address payable _to, uint _amount) public onlyOwner{
uint txIndex = transactions.length;
transactions.push(Transaction(_to, _amount, false, 0, txIndex));
emit SubmitTransaction(txIndex, msg.sender, _to, _amount);
}
function confirmTransaction(uint _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex) txConfirmed(_txIndex) {
Transaction storage transaction = transactions[_txIndex];
transaction.numConfirmations += 1;
isConfirmed[msg.sender][_txIndex] = true;
emit ConfirmTransaction(msg.sender, _txIndex);
}
function executeTransaction(uint _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex){
Transaction storage transaction = transactions[_txIndex];
require(transaction.numConfirmations >= numConfirmationsRequired, "Transaction cannot be executed!");
require(transaction.amount <= depositBalance, "Not enough crypto in your Balance, deposit some extra funds");
transaction.to.transfer(transaction.amount);
transaction.executed = true;
depositBalance -= transaction.amount;
emit ExecuteTransaction(msg.sender, transaction.to, _txIndex);
}
function revokeConfirmation(uint _txIndex) public onlyOwner txExists(_txIndex) notExecuted(_txIndex){
Transaction storage transaction = transactions[_txIndex];
transaction.numConfirmations -= 1;
isConfirmed[msg.sender][_txIndex] = false;
emit RevokeConfirmation(msg.sender, _txIndex);
}
function getBalance() external view returns (uint) {
return msg.sender.balance;
}
function getDepositBalance() external view returns (uint) {
return depositBalance;
}
}