Project - Multisig Wallet

Hi @Khudeja

This is where we need to track the smart contract events. Every time an important change occurs in the contract we can emit an event to keep track of updates. And events can be listened to in real-time.

So these can be used to show notifications to your frontend app user in real time.

I hope that answers your question.

thank you! makes sense

1 Like

Was a bit challenging to start. Had a lot of the functionalities but just had to get the syntax right for accessing some on the data, which is why mine is now similar to Filips. The videos were a great help and really made it all come together in an interesting use-case.

After finishing the assignment I have refreshed some of the videos which helped!

pragma solidity 0.8.17;
pragma abicoder v2;


contract Multisig{

    address[] public owners;
    uint limit;

    struct Transfer{
        uint amount;
        address payable receiver;
        uint approvals;
        bool hasBeenSent;
        uint id;
    }


    event TransferRequestCreated(uint _id, uint _amount, address _initiator, address _receiver);
    event ApprovalReceived(uint _id, uint _approvals, address _approver);
    event TransferApproved (uint _id);
    
    //Array to keep track of transfer requests
    Transfer[] transferRequests;

    //Double mapping indicating which address approved which transactions.
    mapping(address => mapping(uint => bool)) approvals;

    modifier onlyOwners(){
        bool owner = false;
        for(uint i=0; i<owners.length;i++){
            if(owners[i] == msg.sender){
                owner = true;
            }
        } 
        require(owner == true);
        _;
    }


    constructor(address[] memory _owners, uint _limit){
        owners = _owners;
        limit = _limit;
    }

    
    function deposit() public payable returns (uint){
    }

    
    //Function to make a transfer request
    //Should maybe append to tranfer array (default false)
    function transferRequest(uint _amount, address payable _receiver) public onlyOwners{
        emit TransferRequestCreated(transferRequests.length, _amount, msg.sender, _receiver);
        transferRequests.push(
            Transfer(_amount, _receiver, 0, false, transferRequests.length)
        );
    }
    


    //Function to approve transfers. Setting respective address and txID to true when approved.
    function approve(uint txID) public onlyOwners{
        require(approvals[msg.sender][txID] == false);
        require(transferRequests[txID].hasBeenSent == false);
        

        approvals[msg.sender][txID] = true;
        transferRequests[txID].approvals++;

        emit ApprovalReceived(txID, transferRequests[txID].approvals, msg.sender);

        if(transferRequests[txID].approvals >= limit){
            transferRequests[txID].hasBeenSent = true;
            transferRequests[txID].receiver.transfer(transferRequests[txID].amount);
            emit TransferApproved(txID);
        }
    }

    function getTransferRequests() public view returns (Transfer[] memory){
            return transferRequests;
    }

}
1 Like

I first tried without watching the Project Assistance to check the limitation of my ā€œlogicā€.
The goal was to work on the contract’s balance itself and not the individual balances.
However, what should we consider in terms of individual balances? Split the amount between the owners?

/// @dev Coded before watching the video Project Assistance
//pragma solidity 0.7.5; //version of Solidity in Moralis at the time of writing
pragma solidity 0.8.5;

contract multisig {

    //state variables and adresses needed in the constructor
    uint public required; //number of signatures required to execute a transaction
    //owner of the contract, and addresses that will be used in the multisig
    address public owner; 
    address address0;
    address address1;
    address address2;
    
    //constructor function executed upon contract creation
    constructor(){ 
        owner = msg.sender; //set the owner of the contract to the address that deployed it
        required = 2; //set the number of signatures required to execute a transaction to 2
        address0 = owner; //set the first address to be used in the multisig
        address1 = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; //set the second address to be used in the multisig
        address2 = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; //set the third address to be used in the multisig
    }

    //structure for transactions
    struct Transactions { 
        address payable destination; //address of the destination
        uint value; //value of the transaction
        uint approvalCount; //number of approvals
        bool approval0; //approval from the first address
        bool approval1; //approval from the second address
        bool approval2; //approval from the third address
        bool executed; //whether the transaction has been executed
    }

    mapping(uint => Transactions) public transactions; //mapping of transaction ids to transactions
    mapping(address => uint) balance; //mapping of addresses to balances

    //event to be emitted when a deposit is made
    event depositDone(uint  amount, address indexed depositedTo); 

    //function to return the balance of owners in the contract
    function getBalanceOwners() public view returns (uint) { 
        return balance[msg.sender];
   }

   //function to get balance of contract
    function getBalanceContract() public view returns (uint) {
         return address(this).balance;
    }

    //function to deposit ether into the contract
    function deposit() public payable returns (uint) { 
        balance[msg.sender] += msg.value; //add the amount of ether sent to the sender's balance in the contract
        emit depositDone(msg.value, msg.sender); //emit the depositDone event
        return balance[msg.sender]; //return the sender's balance in the contract
    }

    function setTransaction(uint _idTrans, address payable recipient, uint amount) public payable {

        //require that the sender is one of the addresses in the multisig
        require(msg.sender == address0 || msg.sender == address1 || msg.sender == address2, "You are not authorized to execute this transaction");
        //require that the recipient is not one of the addresses in the multisig
        require(recipient != address0 && recipient != address1 && recipient != address2, "You are not authorized to execute this transaction");
        //require that the amount is not 0
        require(amount != 0, "You cannot send 0 ether");
        //require that the recipient is not the contract itself
        require(recipient != address(this), "You cannot send ether to the contract");  
    
        transactions[_idTrans].value = amount;
        transactions[_idTrans].destination = recipient;

        //if the sender is the first address in the multisig
        if(msg.sender == address0) { 
            transactions[_idTrans].approval0 = true; //set the sender's approval to true
            transactions[_idTrans].approvalCount++; //increment the approval count
        }
        //if the sender is the second address in the multisig
        else if(msg.sender == address1) { 
            transactions[_idTrans].approval1 = true;
            transactions[_idTrans].approvalCount++;
        }
        //if the sender is the third address in the multisig
        else if(msg.sender == address2) {
            transactions[_idTrans].approval2 = true;
            transactions[_idTrans].approvalCount++;
        }
    }

    //function to return the details of a transaction
    function getTransaction(uint _idTrans) public view returns (address, uint, uint, bool, bool, bool, bool) {
        return (transactions[_idTrans].destination, transactions[_idTrans].value, transactions[_idTrans].approvalCount, transactions[_idTrans].approval0, transactions[_idTrans].approval1, transactions[_idTrans].approval2,transactions[_idTrans].executed);
    }

    //function to approve a transaction
    function approveTransaction(uint _idTrans) public returns (uint) {
        if(msg.sender == address0) {
            //require that first sender has not already approved the transaction
            require(transactions[_idTrans].approval0 == false, "You have already approved this transaction");
            transactions[_idTrans].approval0 = true; //set the sender's approval to true
            transactions[_idTrans].approvalCount++; //increment the approval count
        }
        //require that second sender has not already approved the transaction
        else if(msg.sender == address1) {
            require(transactions[_idTrans].approval1 == false, "You have already approved this transaction");
            transactions[_idTrans].approval1 = true;
            transactions[_idTrans].approvalCount++;
        }
        //require that third sender has not already approved the transaction
        else if(msg.sender == address2) {
            require(transactions[_idTrans].approval2 == false, "You have already approved this transaction");
            transactions[_idTrans].approval2 = true;
            transactions[_idTrans].approvalCount++;
        }
        return transactions[_idTrans].approvalCount; //return the approval count
    }

    //private function to transfer funds from the contract to another address
    function _transfer(address payable to, uint amount) private { 
        to.transfer(amount); //transfer the amount from the contract to the recipient

        //what should we consider in terms of individual balances? Split the amount between the owners?
    }

    //function to execute a transaction
    function executeTransaction(uint _idTrans) public {
        //require that the transaction has not already been executed
        require(transactions[_idTrans].executed == false, "Transaction already executed");
        //require that the transaction has enough approvals
        require(transactions[_idTrans].approvalCount >= required, "Not enough approvals");
        //require that the sender is one of the addresses in the multisig
        require(msg.sender == address0 || msg.sender == address1 || msg.sender == address2, "You are not authorized to execute this transaction");
         
        //store the contract's balance before the transaction
        uint previousContractBalance = address(this).balance; 
 
        //execute the transaction  
        _transfer(transactions[_idTrans].destination, transactions[_idTrans].value);  
        transactions[_idTrans].executed = true; 
 
        //assert that the contract's balance is equal to the previous balance minus the amount sent
        assert(address(this).balance == previousContractBalance - transactions[_idTrans].value); 
    }
}

Hi @fsicard

Can you elaborate on what you are trying to do?

It seems you already have a mapping to store the balances of the address, which can be used to get the balance of an address.

Thanks @JohnVersus.
Actually I think my question seems to be more related to coherence of the contract more than the coding itself.

At the moment I am using balance[msg.sender] += msg.value; in function deposit() to deposit individual amounts into the smart contract. These amounts initially add up in the contract’s balance, ie address(this).balance is equal to the sum of all the deposits (the sum of all the balance[msg.sender])

However, I decided to make the transactions from the contract’s balance itself via transactions[_idTrans].approvalCount in function approveTransaction. By doing this, the individual balance[msg.sender] are not updated (even if the whole balance of the contract is).

I was thinking it should be more coherent to also update the individual balances accordingly, like dividing the whole amount transfered across the individual balances. This could be done by dividing equally the amount between the owners (or other alternatives). However, doing so I would end up doing additional math and this might not be efficient in term of ā€œgas/costā€.

Hope it makes sense. Best

2 Likes

pragma solidity 0.7.5;

pragma abicoder v2;

contract Wallet {

address[] public owners;

uint limit;



struct Transfer{

    uint amount;

    address payable receiver;

    uint approvals;

    bool hasBeenSent;

    uint id;

}



event TransferRequestCreated(uint _id, uint _amount, address _initiator, address _receiver);

event ApprovalReceived(uint _id, uint _approvals, address _approver);

event TransferApproved(uint _id);

Transfer[] transferRequests;



mapping(address => mapping(uint => bool)) approvals;



//Should only allow people in the owners list to continue the execution.

modifier onlyOwners(){

    bool owner = false;

    for(uint i=0; i<owners.length;i++){

        if(owners[i] == msg.sender){

            owner = true;

        }

    }

    require(owner == true);

    _;

}

//Should initialize the owners list and the limit

constructor(address[] memory _owners, uint _limit) {

    owners = _owners;

    limit = _limit;

}



//Empty function

function deposit() public payable {}



//Create an instance of the Transfer struct and add it to the transferRequests array

function createTransfer(uint _amount, address payable _receiver) public onlyOwners {

    emit TransferRequestCreated(transferRequests.length, _amount, msg.sender, _receiver);

    transferRequests.push(

        Transfer(_amount, _receiver, 0, false, transferRequests.length)

    );

   

}



//Set your approval for one of the transfer requests.

//Need to update the Transfer object.

//Need to update the mapping to record the approval for the msg.sender.

//When the amount of approvals for a transfer has reached the limit, this function should send the transfer to the recipient.

//An owner should not be able to vote twice.

//An owner should not be able to vote on a tranfer request that has already been sent.

function approve(uint _id) public onlyOwners {

    require(approvals[msg.sender][_id] == false);

    require(transferRequests[_id].hasBeenSent == false);

   

    approvals[msg.sender][_id] = true;

    transferRequests[_id].approvals++;

   

    emit ApprovalReceived(_id, transferRequests[_id].approvals, msg.sender);

   

    if(transferRequests[_id].approvals >= limit){

        transferRequests[_id].hasBeenSent = true;

        transferRequests[_id].receiver.transfer(transferRequests[_id].amount);

        emit TransferApproved(_id);

    }

}



//Should return all transfer requests

function getTransferRequests() public view returns (Transfer[] memory){

    return transferRequests;

}

}

Hi Guys,

I’m just wondering about the ((owners)) option that appears when I deeply the contract.

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

contract Wallet{

 address [] public  owners;
    uint limit;

 struct Transfer{
    uint amount;
    address payable receiver;
    uint approvals;
    bool hasBeenSent;
    uint id;
 }

 event TransferRequestCreated(uint _id, uint _amount, address _intiator, address _receiver);
 event ApprovalReceived (uint _id, uint _approvals, address approver);
 event TransferApproved(uint _id);



 Transfer [] transferRequests;

 mapping (address => mapping(uint =>bool)) approvals;
 mapping (address=>uint) balance;

 modifier onlyOwner{
  bool owner = false;
  for(uint i = 0; i<    owners.length; i++){
      if (owners[i] == msg.sender){
          owner= true;
      }
  }
  require(owner == true);
  _;
 }

 constructor (address [] memory _owners, uint _limit) {
        owners = _owners;
        limit = _limit;
 }

 function deposit() public payable {}
 

 function creatTransfer(uint _amount, address payable _receiver) public onlyOwner{
    require(balance[msg.sender]>= _amount); 
    transferRequests.push( Transfer (_amount, _receiver, 0, false, transferRequests.length));
    emit TransferRequestCreated(transferRequests.length, _amount, msg.sender, _receiver);
 }


 function approve (uint _id) public onlyOwner{
     require (approvals[msg.sender][_id] == false);
     require(transferRequests[_id].hasBeenSent == false);
     approvals[msg.sender][_id] = true;
     transferRequests[_id].approvals++;

     emit ApprovalReceived (_id, transferRequests[_id].approvals, msg.sender);

     if(transferRequests[_id].approvals >= limit){
        transferRequests[_id].hasBeenSent= true;
        transferRequests[_id].receiver.transfer(transferRequests[_id].amount);
        emit TransferApproved(_id);
     }
 }
 function getTransferRequests() public view returns (Transfer[] memory){
       return transferRequests;
 }   
 
 function getBalance() public  view returns (uint){
     return balance[msg.sender];
 }
 function getContractBalance () public view returns (uint){
     return address (this).balance;
 }

}


It gives me this error
call to Wallet.owners errored: Error encoding arguments: Error: invalid BigNumber string (argument=ā€œvalueā€, value="", code=INVALID_ARGUMENT, version=bignumber/5.7.0)

Hi @Ahmad_Abd_Allah

That looks like an error from javascript. When you are passing the address add it like a string like this
ā€œ0x5B38Da6a701c568545dCfcB03FcB875f56beddC4ā€ or else js will consider it as a big number.

If this is not the case please share how you are passing the address and limit values for deploying the contract.

1 Like

3 addresses inside [] are separated by a comma, between " ".

All functions are working correctly, except the owners function.

Hmm :thinking: I tried deploying your same code with similar parameters. It deployed without any error.

1 Like

Hi @JohnVersus

My issue is with this function
image

It gives me this error in the console.

call to Wallet.owners errored: Error encoding arguments: Error: invalid BigNumber string (argument=ā€œvalueā€, value="", code=INVALID_ARGUMENT, version=bignumber/5.7.0)

@Ahmad_Abd_Allah Did you pass any param value to the owners function?

For example to get the first address of owners array you need to pass the param value as 0

1 Like

@JohnVersus Ohhh, I got it now, I totally mixed up with the whole array.
Thanks John

1 Like

Here is my contract. Probably could have made it in a way that is bit more organized or used less lines of code, but it achieves all of the functionality it needs to have, so I’m happy with it.

Please let me know any suggestions, questions or anything I need to fix. Going to go watch Filip’s solution now.
:slight_smile:

pragma solidity 0.7.5;
pragma experimental ABIEncoderV2; 

import "./Ownable.sol";

contract multiSigWallet is Ownable {
    
    address[] public owners;
    uint public signersRequired;
    uint numOfTransfers = 0;

    struct Transfer {
        address approver; 
        uint id;
        address payable recipient;
        uint amountToTransfer;
        uint approvalsReceived; 
        bool approved; 
    }

    Transfer[] public transferRequests; 
    mapping(address => mapping(uint => bool)) public approvals; 

    function addOwners(address owner2, address owner3, uint minimumSignersRequired) public onlyOwner returns(address[] memory)  {     
        owners.push(owner);
        owners.push(owner2);
        owners.push(owner3);
        require(minimumSignersRequired > 0 && minimumSignersRequired < 4, "Minimum 1 signer required, can't have more signers than owners");
        require(owner2 != owner3, "You cannot put the same address as two different owners");
        require(owner != owner2, "You cannot put the same address as two different owners");
        require(owner != owner3, "You cannot put the same address as two different owners"); 
        signersRequired = minimumSignersRequired;
        return owners;        
    }

    function deposit() public payable returns (uint) {  
        return address(this).balance;
    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
        
    function createTransferRequest(address payable recipient, uint amount) public returns(string memory){
                
        require(address(this).balance >= amount, "Balance not sufficient");
        require(recipient != address(this), "You shouldn't transfer money from the multisig wallet to the multisig wallet");
        
        bool isOwner = false;
        for (uint i = 0; i < owners.length; i++) {
            if (owners[i] == msg.sender) {
            isOwner = true;
            break;
            }
        }
    
        require(isOwner, "Only owners can perfrom transfers");

        uint previousSenderBalance = address(this).balance;

        _logTransferRequest(msg.sender, recipient, amount); //call the internal/private function where the computation takes place.
        return "Transfer request successfully created! Please get other owners to approve the transfer and it will be executed.";         
    } 

    function _logTransferRequest(address from, address payable to, uint amount) private {  
        transferRequests.push(Transfer(from, numOfTransfers, to, amount, 1, false));
        approvals[from][numOfTransfers] = true;
        numOfTransfers++;
    }

    function approveTransfers(uint id) public payable returns (string memory){

        require(id < transferRequests.length, "There is no transfer with that ID number");

        require(transferRequests[id].approved == false, "This transfer has already been approved and executed."); 

        require(transferRequests[id].approver != msg.sender, "You created and approved this transfer, other owners need to approve it.");
        
        require(transferRequests[id].amountToTransfer < address(this).balance, "Available funds not sufficient to make this transfer.");

        require(approvals[msg.sender][id] == false, "You have already approved this transfer, the other owners need to approve it for it to be executed");

        bool isOwner = false;
        for (uint i = 0; i < owners.length; i++) {
            if (owners[i] == msg.sender) {
            isOwner = true;
            break;
            }
        }
        require(isOwner == true, "Only the owners of the wallet can approve transfers");

        transferRequests[id].approvalsReceived++;
        approvals[msg.sender][id] = true;

        if(transferRequests[id].approvalsReceived == signersRequired) {
            
            transferRequests[id].approved = true;
            
            uint previousSenderBalance = address(this).balance;

            transferRequests[id].recipient.transfer(transferRequests[id].amountToTransfer);
            assert(address(this).balance == previousSenderBalance - transferRequests[id].amountToTransfer);

            return "You have approved the transfer. The transfer has recieved enough approvals and has been executed";
        }

        return "You have approved the transfer, if the transfer has not executed it needs to be approved by other owners."; 
    }

}
pragma solidity 0.7.5;

contract multisigWallet {

    struct TransferRequest {
        bool executed;
        uint amount,
        address payable recipient;
        address[] approvals;
    }

    mapping(address => string) owners;
    uint sigsRequired;
    uint balance;
    TransferRequest[] transferRequests;

    modifier isCreator {
        require(owners[msg.sender] == "creator");
        _;
    }

    modifier isOwner {
        require(owners[msg.sender] == "creator" || owners[msg.sender] == "owner");
        _;
    }

    constructor (address[] _owners, uint _sigsRequired) {
        owners[msg.sender] = "creator";
        _totalOwners = 1;
        for (uint i = 0; i < _owners.length; i++) {
            if (owners[_owners[i]] != "creator" && owners[_owners[i]] != "owner") {
                owners[_owners[i]] = "owner";
                _totalOwners += 1;
            }
        }
        require(_sigsRequired > 1, "Must require more than one signature");
        require(_sigsRequired <= _totalOwners, "Too many signatures required");
        sigsRequired = _sigsRequired;
    }

    function deposit() public payable returns (uint) {
        balance += msg.value;
        return balance;
    }

    function createTransferRequest(uint _amount, address payable _recipient) public isOwner returns(uint) {
        _id = transferRequests.length;
        require(_amount <= balance, "Not enough money for transfer");
        TransferRequest _transferRequest;
        _transferRequest.executed = false;
        _transferRequest.amount = _amount;
        _transferRequest.recipient = _recipient;
        _transferRequest.approvals = [msg.sender];
        transferRequests.push(_transferRequest);
        return _id;
    }

    function approveTransferRequest(uint _id) public isOwner returns(memory string) {
        require(_id < transferRequests.length, "Transfer request doesn't exist");
        require(transferRequests[_id].executed == false, "Transfer already executed");
        bool _notYetApproved = true;
        for (uint i = 0; i < transferRequests[_id].approvals.length; i++) {
            if (transferRequests[_id].approvals[i] == msg.sender) {_notYetApproved = false; }
        }
        if (_notYetApproved) {
            transferRequests[_id].approvals.push(msg.sender);
            if (transferRequests[_id].approvals.length >= sigsRequired) {
                transferRequests[_id].executed = true;
                if (transferRequests[_id].amount <= balance) {
                    transferRequests[_id].recipient.transfer(transferRequests[_id].amount);
                    return("transfer completed");
                } else { return ("required signatures reached but not enough money to execute"); }
        } else { return ("more signatures required"); }
    }
}

I solved the task without using a template in a little bit different way:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;

contract MultisigWallet {

    // array of addresses of the owners
    address[] owners;

    // minimal signatures
    uint mininimum;

    // requests for approval
    struct Transfer {
        address payable sender;
        address payable recipient;
        uint amount;
        bool tnxSent;
        address[] ownersAproved;
    }

    // array of transaction needed for aproval check
    Transfer[] transfersScope;

    //Should only allow people in the owners list to continue the execution.
    modifier onlyOwners(){
        require(exists(msg.sender));
        _;
    }

    // checking if sender is one of the owners
    function exists(address oneOfowners) private view returns (bool) {
        for (uint i = 0; i < owners.length; i++) {
            if (owners[i] == oneOfowners) {
                return true;
            }
        }
        return false;
    }

    // constructor should initialize the owners list and the limit 
    constructor(address[] memory _owners, uint _minimum){
        owners = _owners;
        mininimum = _minimum;
    }


    // events
    event balanceAdded(uint amount, address indexed deliveredTo);
    event TransferRequestCreated(uint _txid, uint _amount, address _initiator, address _receiver);
    event transferedAmounts(uint amount, address indexed sentFrom, address deliveredTo);



    // anyone should be able to deposit ether into the contract
    function deposit() public payable returns (uint) {
        emit balanceAdded(msg.value, msg.sender);
        return address(this).balance;
    }

    // creating a pending transaction with false sent status and 0 aprovals having
    function createTnx(address recipient, uint amount) public onlyOwners returns(Transfer[] memory) {
        address[] memory emptyArray;
        transfersScope.push( Transfer(payable(msg.sender), payable(recipient), amount, false, emptyArray ) );
        emit TransferRequestCreated( (transfersScope.length - 1), amount, msg.sender, recipient);
        return transfersScope;
    }

    // geting transaction from the scope of pending
    function getTrnx(uint _index) public view onlyOwners returns(address, uint, bool, uint) {
        return (transfersScope[_index].recipient, transfersScope[_index].amount, transfersScope[_index].tnxSent, transfersScope[_index].ownersAproved.length );
    }

    // aproving or not transactions from the scrope of pending
    function approve(uint _index) public onlyOwners {
        
        if( !transfersScope[_index].tnxSent ) {

            if ( transfersScope[_index].ownersAproved.length == 0 ) {

                aprovedSuccess(_index);

            } else {

                // checking if current sender already make a approval for this transaction
                for (uint i = 0; i < transfersScope[_index].ownersAproved.length; i++) {
                    
                    if (transfersScope[_index].ownersAproved[i] == msg.sender) {
                        //return error "You've already approved this transaction";
                        break;
                    } else {
                        aprovedSuccess(_index);
                    }
                }

            }
            
        }

    }

    function aprovedSuccess(uint _index) private onlyOwners {
        transfersScope[_index].ownersAproved.push(msg.sender);
 
        // checking how many aproval now trnx has
        if( transfersScope[_index].ownersAproved.length >= mininimum ) {
            if ( transfer(transfersScope[_index].recipient, transfersScope[_index].amount) ) {
                transfersScope[_index].tnxSent = true;
            }
        }
    }

    // transfer anywhere with 2/3 approval
    function transfer(address recipient, uint amount) private onlyOwners returns(bool){

        // checking for conditions
        require(msg.sender != recipient, "You can't send money to yourself.");

        uint previousSenderBalance = address(this).balance;
        payable (recipient).transfer(amount);

        emit transferedAmounts(amount, msg.sender, recipient);

        if ( (address(this).balance == previousSenderBalance - amount) ) {
            return true;
        } else {
            return false;
        }

    }

    function getBalance() public view returns (uint) {
        return address(this).balance;
    }

}

One thing I didn’t know how to show error with ā€œbreakā€.

I don’t think that’s possible. break statement is only used to stop the running loop.

You could use require to throw an error instead of using an if condition.

1 Like

Ah yes, that has more sense. Thanks

1 Like

Here is my attempt on this project without viewing the video of the solution. It appears to work.

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

contract multiSigWallet {

    address[] public owners;
    address owner1;
    address owner2;
    address owner3;
    uint requiredApprovals;

    mapping(address => uint) balance;

    constructor(address _owner1, address _owner2, address _owner3, uint _approvals) {
        owner1 = _owner1;
        owners.push(_owner1);
        owner2 = _owner2;
        owners.push(_owner2);
        owner3 = _owner3;
        owners.push(_owner3);
    
        requiredApprovals = _approvals;
    }

    modifier onlyOwners{
        require(msg.sender == owner1 || msg.sender == owner2 || msg.sender == owner3, "Only an owner of the contract can execute this function!");
        _;
    }

    struct Transfer {
        address sender;
        address recipient;
        uint amount;
        uint transactionID;
        uint numApprovals;   
    }

    Transfer[] transferRequests;

    mapping(address => mapping(uint => bool)) approvals;

    function deposit() public payable {
        balance[msg.sender] += msg.value;
    }

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

    function transferRequest(address _from, address _to, uint _amount) public onlyOwners {
        transferRequests.push(Transfer(_from, _to, _amount, transferRequests.length, 1));
        approvals[msg.sender][transferRequests.length-1] = true;
        transfer(_from, _to, _amount);
    }

    function viewPendingTransfers(uint _transactionID) public view returns(bool) {
        return approvals[msg.sender][_transactionID];
    }

    function numTransfers() public view returns (uint) { // so owners can see how many transactions to help them check the pending tranactions
        return transferRequests.length -1;
    }

    function approveTransfer(uint _transactionID) public onlyOwners {
        require(transferRequests[_transactionID].numApprovals == 1);
        require(approvals[msg.sender][_transactionID] == false);
        approvals[msg.sender][_transactionID] = true;
        transferRequests[_transactionID].numApprovals++;
        if((approvals[msg.sender][_transactionID] == true) && (transferRequests[_transactionID].numApprovals == requiredApprovals)) {
            transfer;
        }
    }

    function transfer(address _from, address _to, uint _amount) internal {
        balance[_from] -= _amount;
        balance[_to] += _amount;
    }

}
1 Like