Project - Multisig Wallet

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;
    }

    
}
1 Like

@thecil It was a typo and now im getting a ‘invalid input source specified’ Arrrrgghhh, this is so frustrating…Screenshot 2021-03-21 at 22.00.44

1 Like

Try to compile it with an specific compiler version, also if you click on the error message, does it take you some where in the code?

Carlos Z

This is the alpha version of my code haha. I did not check the project assistance video and start coding as a crazy man.
I know that the code is quite rudimentary but here it is.

pragma solidity 0.7.5;

//import "./OwnableMW.sol";

contract multisigWallet{
    
    //number of signatures needed
    uint signNeed;
    //counter for the votes
    uint counter = 0;
    //owners
    address internal owner1;
    address internal owner2;
    address internal owner3;
    
    modifier onlyOwners {
        
        if (msg.sender == owner1 || msg.sender == owner2 || msg.sender == owner3){
            _;
        }
    }
    
    constructor (uint _signNeed, address _owner2, address _owner3) {
        signNeed = _signNeed;
        owner1 = msg.sender;
        owner2 = _owner2;
        owner3 = _owner3;
    }
    
    event depositDone(address from, uint amount);
    
    mapping (address => uint) balanceOf;
    
    //anyone can deposit funds
    function depositFunds() public payable {
        balanceOf[msg.sender] += msg.value;
        emit depositDone(msg.sender, msg.value);
    }
    //see the balance of the sender
    function getBalance() public view returns (uint){
        
        return balanceOf[msg.sender];
    }
    //see the balance of the entire contract
    function getContractBalance() public view onlyOwners returns (uint){
        return address(this).balance;
    }
    //if a sender has funds can withdraw, no need to be owner
    function withdraw(uint _amount) public returns (uint){
        require(balanceOf[msg.sender] >= _amount);
        balanceOf[msg.sender] -= _amount;
        msg.sender.transfer(_amount);
        return balanceOf[msg.sender];
    }
    
    function signOwner1() public onlyOwners returns (bool){
        require(msg.sender == owner1, "This is for owner1"); 
        counter++;
        return true;
    }
    function signOwner2() public onlyOwners returns (bool){
        require(msg.sender == owner2, "This is for owner2");   
        counter++;
        return true;
    }
    function signOwner3() public onlyOwners returns (bool){
        require(msg.sender == owner3, "This is for owner3"); 
        counter++;
        return true;
    }
    
    function transferOwner(uint _amount) public onlyOwners {
        require(address(this).balance >= _amount);
        require(counter >= signNeed);
        balanceOf[msg.sender] -= _amount;
        msg.sender.transfer(_amount);
        counter = 0;
        
    }
    
}
1 Like

@thecil its takes me to the first line, Pragma Solidity. not sure which compiler version encompasses this type of smart contract syntax and functionality? or have i imported something incorrectly?

Im determined to solve this problem on my own without watching the additional help videos. I realize i may be bumping my head but so far I think im on the right track. Im trying to figure out how to use that modifier to point to the Bool in my Owners struct. So far I havent been able to find any information on using Booleans with modifiers. Is it Even possible? Everything works until i try to point to that Boolean in the Owner struct. Like i said, I want to figure this out on my own so if give advice dont just tell me the answer I would prefer you not respond if thats the case but thanks anyway. I explain my reasoning through ought the code, any help is appreciated.

pragma solidity 0.7.5;
pragma abicoder v2;

contract MultisigaWalleto{

address payable contractCreator         //payable function that holds the contract 
                                           funds and can add owners
address owner;                                     
int ownerCount=0;            
                
mapping (address=>uint) balance;                  //standard balance mapping
mapping (address=>bool) status;                    //mapping i intend to point to status                                                       
                                                    bool in Owner struct

constructor (){
    contractCreator = msg.sender;
}

modifier onlyCreator{
    require (msg.sender == contractCreator,"This Boss sh** HOMMIE, above Yo payGrade");
_;
}

modifier onlyOwner{
    require (owners[msg.sender].ownerstatus == true,"You aint running nothin in the managerial Dept over here");
    _;
}

struct Owners{
    string ownerName;                       //I was wondering if i should use an 
                                              int here insted of name 
    address ownerAddress;
    bool ownerstatus;
}

Owners [] owners;

function addOwner(string memory _ownerName, address _ownerAddress ) public payable onlyCreator{
    Owners memory newOwner = Owners(_ownerName, _ownerAddress, true);
    owners.push (newOwner);
    ownerCount++;

}

function addMoney(uint amount) public payable {
    require (balance[msg.sender] >= amount,"Cant send what Uaint got");
    balance [msg.sender] -=amount;
    balance [contractCreator]+=amount;
}

function addBalance(uint _amount) public {
    balance [msg.sender]+=_amount;
}
   
function getBalance() public view returns(uint){
    return balance [msg.sender];
}

function ownersCount() public view onlyCreator returns (int){
    return ownerCount;
}

function getOwners(uint _index) public view onlyCreator returns(string memory,address){
       Owners memory returnthem= owners [_index];
       return (returnthem.ownerName, returnthem.ownerAddress);
   
   }  
   function getContractBalance() public view onlyOwner returns (uint){     //works for onlyCreator but I havent figured out the owner modifier
       return balance [contractCreator];
   }

}

1 Like

Hi,
This is my basic MultiSigWallet code which is based on internet code research and final adaption for the project requirement. I have tested it using my limited experience in Remix but still may have some issues?

I do have a question, how are the Approvers notified that a spend request is pending? does the" emit" operation notify all who have the contract address in there wallets?

Thanks for any advice you can provide.

pragma solidity 0.7.5;
pragma abicoder v2;

contract MultiSigWallet {

address private _owner; // contract creator 
mapping(address => uint) private _owners; // all approveal owners
uint private requiredApprovals; // minimum number of approval owners max 3
uint private _transactionIdx;
uint private totalBalance;

struct Transaction {
    address from;
    address payable to;
    address firstSigner;
    uint amount;
    uint8 signatureCount;
    bool complete;
    
}

mapping (uint => Transaction) private _transactions;
uint[] private _pendingTransactions;

event TransactionRequest(address from, address to, uint amount, uint transactionId);
event TransactionCompleted(address from, address to, uint amount, uint transactionId);
event TransactionSigned(address by, uint transactionId);
event DepositDone(address from, uint amount);


modifier isOwner() {
    require(msg.sender == _owner, " Invalid not contract creator ") ;
    _;
}

modifier validOwner() {      // valid owners who can request and approve transactions
    require(_owners[msg.sender]==1, " Invalid not a registered Approval owner");
    _;
}

constructor(address owner1, address owner2,address owner3, uint _requiredApprovals) {
    
    _owner = msg.sender;

    requiredApprovals= _requiredApprovals;
    require(_requiredApprovals <= 3," Invalid to many Approvers");
    
    // check for duplicate owners
    require(owner1 != owner2, "Invalid Duplicate owner address");
    require(owner2 != owner3, "Invalid Duplicate owner address");
    require(owner1 != owner3, "Invalid Duplicate owner address");

    //register addresses of authorised approval owners
    _owners[owner1] = 1; // true
    _owners[owner2] = 1;
    _owners[owner3] = 1;
}


function deposit() public payable {
    require(msg.value >0);
    totalBalance += msg.value;
    emit DepositDone(msg.sender, msg.value);
}


// only valid owners can request a transfer.
function transferRequest(address payable _to, uint _amount) validOwner public returns (uint) {
    
    require(totalBalance >= _amount, "Not enough funds to transfer");
    uint transactionId = _transactionIdx++;

    // initialise new Transaction
    Transaction memory transaction;
    transaction.from = msg.sender;
    transaction.to = _to;
    transaction.amount = _amount; 
    transaction.signatureCount = 0;
    transaction.firstSigner= address(0);
    transaction.complete=false;

    _transactions[transactionId] = transaction;
   // _pendingTransactions.push(transactionId); // not implemented

    emit TransactionRequest(msg.sender, _to, _amount, transactionId);
    return (transactionId);

}


function approveTransaction(uint transactionId) validOwner payable public  {


     Transaction storage transaction = _transactions[transactionId];
     
    require(transaction.complete !=true," transaction already completed");
    // Transaction must exist
    require(address(0x0) != transaction.from, " Invalid signing address");
    // Creator cannot sign the transaction
    require(msg.sender != transaction.from, " Invalid signer was transaction requester");
       
     // Cannot sign same transaction twicw   
    require ( msg.sender!= transaction.firstSigner, " Duplicate signer detected");
    transaction.firstSigner = msg.sender;
    transaction.signatureCount++;
    emit TransactionSigned(msg.sender, transactionId);

    if (transaction.signatureCount >= requiredApprovals) { // transaction approved by 2 

    require(totalBalance >= transaction.amount, "Not enough funds to transfer");

    transaction.to.transfer(transaction.amount);
    totalBalance -=transaction.amount;
    transaction.complete=true;
    emit TransactionCompleted(transaction.from, transaction.to, transaction.amount, transactionId);
  }
}



// balance on this contact
function walletBalance() public view returns (uint) {
return totalBalance;
    
    
}

}

Nice alpha version :nerd_face:

few suggestions :face_with_monocle:

    function signOwner1() public onlyOwners returns (bool){
        require(msg.sender == owner1, "This is for owner1"); 
        counter++;
        return true;
    }
    function signOwner2() public onlyOwners returns (bool){
        require(msg.sender == owner2, "This is for owner2");   
        counter++;
        return true;
    }
    function signOwner3() public onlyOwners returns (bool){
        require(msg.sender == owner3, "This is for owner3"); 
        counter++;
        return true;
    }

this 3 can be turned into 1, something like:

    function signOwner() public onlyOwners returns (bool){
        require(msg.sender == owner3 || msg.sender == owner2 || msg.sender == owner1, "This is for owner"); 
        counter++;
        return true;
    }

Or just adjust your modifier like this:

    modifier onlyOwners {
        require(msg.sender == owner3 || msg.sender == owner2 || msg.sender == owner1, "This is for owner"); 
    _;
    }

Then will not need to ask in the function for that require statement.

Carlos Z

You have to debug your code in order to know what is happening, the only thing we know right now is that the error is caused from a bad solidity version, which one? idk, so lets try it with which ever works, 0.7.0, 0.7.5, 0.8.0…just try one and see how behaves, probably one is going to request to change the syntax, you are very close to make it work, so just try to have a prototype (what ever version and code syntax, the goal is to make it work) and then you improve from it. :nerd_face:

Carlos Z

I guess you mean for something like this, you can take it has an example for booleans in modifiers :face_with_monocle:

    modifier onlyOwners(){
      bool isOwner = false;
      for(uint _index = 0; _index < owners.length; _index++){
        if(owners[_index] == msg.sender){
          isOwner = true;
        }
      }
      require(isOwner == true, "Only multisig owners allow");
      _;
    }

Carlos Z

Hi, i hope my solution is correct! for me it works just fine.
Open for any kind of feedback. I did not look to the next “hint” video’s or watched this forum to get this working. so maybe i’m totaly wrong in the way of coding this.

pragma solidity 0.8.2;
pragma abicoder v2;
// SPDX-License-Identifier: MIT


import "./ownable.sol";
import "./destroyable.sol";


interface GovermentInterface{
    
    function addTransaction(address from, address to, uint amount)payable external;
}


contract solich is Ownable, Destroyable{
    

    
struct Transaction{
    address from;
    address to;
    uint amount;
    uint timesApproved;
    address[] signatures;
    bool approved;
    
}

Transaction[] Transactions;
uint approvalLimit = 2;
GovermentInterface GovermentInstance = GovermentInterface(0x9da9df2Fe440fA9E05B620a05990d7c644aCBBB8);

mapping(address => uint)public balance;   

event depositDone(uint amount, address indexed depositedTo);
event widthrawDone(uint amout, address indexed widthrawTo);
event transferApproved(uint amout, address indexed transferedTo);

function deposit() public payable returns (uint){
    balance[msg.sender] += msg.value;
    emit depositDone(msg.value, msg.sender);
    return balance[msg.sender];
}



function getBalance() public view returns (uint){
    return balance[msg.sender];
}

function transfer(uint _amount, address _recipient)public onlyOwner{
    require(_amount>0);
    require(balance[msg.sender] >= _amount, "you don't have enough tokens");
    require(msg.sender != _recipient, "don't send tokens to yourself");
    
    address[] memory emptySignature = new address[](1);
    emptySignature[0] = msg.sender;
   
    Transaction memory tempTransaction = Transaction(msg.sender,_recipient, _amount, 1, emptySignature, false);
    tempTransaction.signatures[0] = msg.sender;
    Transactions.push(Transaction(msg.sender, _recipient, _amount, 1, emptySignature, false));
    
}


    
function _transfer(uint _index) private{
    require(Transactions[_index].timesApproved == approvalLimit);
    //uint previousBalance = balance[Transactions[_index].from]; Commented to not have a "not used" warning.
    
    balance[Transactions[_index].from] -= Transactions[_index].amount;
    balance[Transactions[_index].to] += Transactions[_index].amount;
    Transactions[_index].approved = true;
    assert(Transactions[_index].approved=true);
    //assert(previousBalance == balance[Transactions[_index].from] - Transactions[_index].amount); Find out why i get an Error on this one..
    emit transferApproved(Transactions[_index].amount, Transactions[_index].to);
}

function approveTransfer(uint _index)public onlyOwner {
    //add check if not dubble sign
    //if sign limit reached -> transfer
    //add signature owner
    bool dubbleSign;
    uint totalSignatures = Transactions[_index].signatures.length;
    uint i;
    // check dubble signs;
    while(i<totalSignatures){
        if(Transactions[_index].signatures[i] == msg.sender){
            dubbleSign=true;
        }else{
            dubbleSign=false;
        }
        i++;
    }
    require(dubbleSign==false, "You already signed!");
    address[1] memory tempSignature;
    tempSignature[0] = msg.sender;
    Transactions[_index].signatures.push(tempSignature[0]);
    Transactions[_index].timesApproved +=1;
    _transfer(_index);
    
}

function getNotApprovedTransactions(uint _index)public view returns(address, address, uint, uint, address[] memory, bool){
    
    return (Transactions[_index].from, Transactions[_index].to, Transactions[_index].amount, Transactions[_index].timesApproved, Transactions[_index].signatures, Transactions[_index].approved);
    
}


function widthraw(uint _amount) public returns (uint){
    //requires always comes first!
    require(_amount>0);
    require(balance[msg.sender] >=_amount, "you don't have that much ETH");
    uint targetBalance;
    targetBalance = balance[msg.sender] - _amount;
    payable(msg.sender).transfer(_amount);
    balance[msg.sender] -= _amount;
    assert(targetBalance == balance[msg.sender]);
    emit widthrawDone(_amount, msg.sender);
    return balance[msg.sender];
}



function setApprovalLimit(uint _level) public onlyOwner(){
        approvalLimit = _level;
}
}

//Ownable.sol

pragma solidity 0.8.2;
pragma abicoder v2;

contract Ownable{
    mapping(address => uint) public userLevel; //0 normalUser; 1 = Owner;
  
    struct strctOwners{
        address id;
    }
    
    strctOwners[] Owners;
    address internal owner;
    
    constructor(){
        owner = msg.sender;
        userLevel[msg.sender] = 1;
        addOwner(msg.sender);
        
    }
    
    modifier onlyOwner {
        require(userLevel[msg.sender]==1);
        _;   //run the function
    }


    function addOwner(address _user)public onlyOwner{
        userLevel[_user] = 1;
        Owners.push(strctOwners(_user));
    }
    
    function getOwner(uint _index)public view returns(address){
    return Owners[_index].id;
}

    
}
1 Like

my getNotApprovedTransactions needs to return only the transactions that has less then “approvalLimit”

Here is my code for the wallet.

contract Wallet {
    address owner;
    address[] public spenders;
    uint appovalsRequired;
    
    struct Transfer {
        uint amount;
        address payable receiver;
        uint approvals;
        bool sent;
        uint txid;
    }
    
    Transfer[] transferRequests;
    
    mapping(address => mapping(uint => bool)) approvals;
    
    modifier onlyOwner() {
        require(owner == msg.sender);
        _;
    }
    
    modifier onlySpenders() {
        bool spender = false;
        for(uint i = 0; i < spenders.length; i++){
            if (spenders[i] == msg.sender){
                spender = true;
            }
        }
        require(spender == true);
        _;
    }
    
    constructor(uint _approvalsRequired){
        owner = msg.sender;
        appovalsRequired = _approvalsRequired;
    }
    
    function addSpender(address _spender) public onlyOwner{
        spenders.push(_spender);
    }
    
    function setApprovalsRequired(uint _approvals) public onlyOwner{
        appovalsRequired = _approvals;
    }
    
    function deposit() public payable {}
    
    function createTransferRequest(uint _amount, address payable _receiver) public onlySpenders {
        transferRequests.push(Transfer(_amount, _receiver, 0, false, transferRequests.length));
    }
    
    function getTransferRequests() public view onlySpenders returns(Transfer[] memory){
        return transferRequests;
    }
    
    function approveTransfer(uint _txid) public onlySpenders {
        require(transferRequests[_txid].sent == false);
        require(approvals[msg.sender][_txid] == false);
         
        transferRequests[_txid].approvals++;
        approvals[msg.sender][_txid] = true;
        
        if (transferRequests[_txid].approvals >= appovalsRequired) {
            transferRequests[_txid].sent = true;
            transferRequests[_txid].receiver.transfer(transferRequests[_txid].amount);
        }
    }
    
    function getWalletBalance() public view returns(uint){
        return address(this).balance;
    }
}
2 Likes

Hey @solich, hope you are well.

I have tested your contracts and its works well.

Steps I made:

  • deploy the contract.
  • addOwner once, so I have 2 owners (contract owner, new owner)
  • setApprovalLimit() of 2.
  • deposit() 3 ethers from 1st owner.
  • getBalance() and balance shows the correct amount of 3 ethers (in wei)
  • transfer() 2,5 ether from 1st owner to non-owner address.
  • getNotApprovedTransactions() returns the values of the pending transfer (index 0).
  • approveTransfer() with 1st owner (revert: already signed!) and 2nd owner.
  • widthraw() with non-owner address, it works!
  • Check Balance on non-owner address and it got the ethers properly.

Overall you did a very great job, nice solution! :partying_face:

If you have any more questions, please let us know so we can help you! :slight_smile:

Carlos Z.

1 Like

Thank you Carlos!
Just wondering what im doing wrong with the assert in _transfer that i commented.

1 Like

This is my attempt, I struggled with the approval() function so found some help online. Keen to get some feedback.

contract Wallet {

    mapping(address => uint)balance;
    
    address[] owners;
    uint approvalLimit;
    
    constructor(address[] memory _owners, uint approval_Limit ){
        owners = _owners;
        approvalLimit = approval_Limit;
    }
    
    modifier onlyOwners {
        bool owner = false;
        for(uint i = 0; i < owners.length; i++){
            if(owners[i] == msg.sender){
                owner = true;
            }
        }
        require(owner == true);
        _;
    }
    
    //Transaction info 
    struct Transaction {
        address spender;
        address reciever;
        uint amount;
        uint numApprovals;
        uint id;
    }
    
    Transaction [] transaction; 
    
    event Approval_Count(uint _id, uint _numApprovals, address _approver );
    event SubmittedTransfers(uint _id, uint _amount, address _spender, address _reciever);
    
    
    function deposit() public payable returns (uint){
        
        balance[address(this)] += msg.value;
        return balance[address(this)];
    }
    
    function getBalance() public view returns(uint){
        return balance[address(this)];
    }
    
    
    function submitTransaction(address payable _reciever, uint _amount) public onlyOwners {
        emit SubmittedTransfers(transaction.length, _amount, msg.sender, _reciever);
        
        transaction.push(Transaction(msg.sender, _reciever, _amount, 1, transaction.length));

    }
    
    function getTransaction(uint _index) public view returns(address spender, address reciever, uint amount, uint numApprovals, uint id){
        return (transaction[_index].spender, transaction[_index].reciever, transaction[_index].amount, transaction[_index].numApprovals, transaction[_index].id);
    }
    
    function approval(uint _id) public onlyOwners{
        transaction[_id].numApprovals++;
        
        emit Approval_Count(_id, transaction[_id].numApprovals, msg.sender);
        
        if(transaction[_id].numApprovals >= approvalLimit){
            balance[transaction[_id].spender] += transaction[_id].amount;
            balance[address(this)] -= transaction[_id].amount;
            payable(transaction[_id].reciever).transfer(transaction[_id].amount);
            
        }
        
    }

}

1 Like
pragma solidity 0.7.5;
pragma abicoder v2;

contract multisig_wallet{
    uint voteSize;
    address owner1;
    address owner2;
    address owner3;
    
    struct transferRequest{
        uint amount;
        address payable adr;
        uint confirmed;
        uint unconfirmed;
    }
    
    transferRequest txRequest;
    
    bool  activetx;
    
    mapping (address=>bool) responseList;
    
    constructor(address _owner1,address _owner2,address _owner3, uint _votesize){
        owner1 =  _owner1;
        owner2 =  _owner2;
        owner3 =  _owner3;
        
        voteSize = _votesize;
        
    }
    
    modifier onlyOwners(){
        require((msg.sender == owner1) || (msg.sender == owner2) || (msg.sender == owner3),"Not admin");
        _;
    }
    
    function deposit() payable public returns(string memory){
        require(msg.value>0,"Have to send value");
        return "Deposit complete";
    }
    
    function showBalance() public view returns(uint){
        return address(this).balance;
    }
    
    function MakeTransferRequest(uint _amount, address payable _adr) public onlyOwners{
        require(address(this).balance >= _amount,"Not enough ether");
        require(activetx == false,"Transaction request already active");
        transferRequest memory tx; //new request
        tx.amount = _amount;
        tx.adr = _adr;
        tx.confirmed = 0;
        txRequest = tx;
        responseList[owner1] = false; //reset responses
        responseList[owner2] = false;
        responseList[owner3] = false;
        activetx = true; //set an active request flag 
    }
    
    function view_request() public view onlyOwners returns(transferRequest memory){ //function to view the requested transaction
        require(activetx == true,"No active transaction request");
        return txRequest;
    }
    
    function respond(string memory _response) public onlyOwners{
        require(activetx,"No active transaction request");
        require(responseList[msg.sender] == false,"You already responded");
        
        if ((keccak256(bytes(_response)) == keccak256(bytes("approve")))){ //enter specifically "approve" for confirmation of transaction
            responseList[msg.sender] = true;
            txRequest.confirmed += 1;
            if (txRequest.confirmed >= voteSize){ //check the response state
                confirmtx();
                reset();
            }

        }
        else if ((keccak256(bytes(_response)) == keccak256(bytes("decline")))){ //enter specifically "decline" to decline transaction
            responseList[msg.sender] = true;
            txRequest.unconfirmed += 1;
            if (txRequest.unconfirmed >= voteSize){ //check the response state
                reset();
            }
        }
    }
    
    function confirmtx() private onlyOwners returns(string memory){ //function to send the ether when the transaction is complete
       txRequest.adr.transfer(txRequest.amount);
       
       return "Transaction complete";
    }
    
    function reset() private onlyOwners { //reset function of transaction request
        txRequest.unconfirmed = 0;
        txRequest.confirmed = 0;
        activetx = false;
        transferRequest memory tx;
    }
}
1 Like

Hey @ol_frank, hope you are great.

I have test your contract and it works good. You approve function was able to transfer the funds to the 3rd owner after I approved it twice with the 1st owner (also the same who submit the transaction).
image

what is exactly the issue for your approval() function?

Carlos Z

1 Like

Hey @thecil , I’m well thank you.

Good, so i need to require that the person who submitted the transaction cannot approve it multiple times? I’ll get on it now.

Thanks for your help

1 Like

what is exactly the issue with it? :face_with_monocle:

Carlos Z