Project - Multisig Wallet

Hey @Kalminkou, hope you are ok.

You dont have any function to deposit funds on the contract, so what should be transfer from your empty contract? :face_with_monocle:

You should create a function to deposit funds then try again with the contract.

Carlos Z

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

}

1 Like
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.7.5;

contract MultiSigWallet {
    
    event Deposit(address indexed sender, uint amount, uint balance);
    
    event SubmitTransaction(
        address indexed owner,
        uint indexed txID,
        address indexed to,
        uint value,
        bytes data
    );
    
    event ApproveTransaction(address indexed owner, uint indexed txID);
    
    event ExecuteTransaction(address indexed owner, uint indexed txID);
    
    address[] public owners;
    
    mapping(address => bool) public isOwner;
    
    uint public limitRequired;
    
    struct Transfer {
        address to;
        uint value;
        bytes data;
        bool executed;
        mapping(address => bool) isConfirmed;
        uint limit;
    }
    
    Transfer[] public transferRequest;
    
    constructor(address[] memory _owners, uint _limitRequired) public {
        require(_owners.length > 0, "owners required");
        require(_limitRequired > 0 && _limitRequired <= _owners.length);
        
        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);
        }
        
        limitRequired = _limitRequired;
    }
    
    function deposit() payable external {
        emit Deposit(msg.sender, msg.value, address(this).balance);
        
    }
    
    modifier onlyOwner() {
        require(isOwner[msg.sender], "now owner");
        _;
    }
    
    function submitTransaction(address _to, uint _value, bytes memory _data) public onlyOwner {
        uint txID = transferRequest.length;
        
        transferRequest.push(Transfer({
            to: _to,
            value: _value,
            data: _data,
            executed: false,
            limit: 0
        }));
        
        emit SubmitTransaction(msg.sender, txID, _to, _value, _data);
    }
    
    modifier txExists(_txID) {
        require(_txID < transferRequest.length, "tx does not exist");
        _;
    }
    
    modifier notExecuted(_txID) {
        require(!transferRequest[_txID].executed, "tx already executed");
    }
    
    modifier notConfirmed(_txID) {
        require(!transferRequest[_txID].isConfirmed[msg.sender], "tx already confirmed");
    }
    
    function approveTransaction(uint txID) public onlyOwner txExists(_txID) notExecuted(_txID) notConfirmed(_txID) {
        Transfer storage transfer = transferRequest[_txID];
        
        transfer.isConfirmed[msg.sender] = true;
        transfer.limit += 1;
        
        emit ApproveTransaction(msg.sender, _txID);
    }
    
    function executeTransaction(uint _txID) public onlyOwner txExists(_txID) notExecuted(_txID) {
        Transfer storage transfer = transferRequest[_txID];
        
        require(transfer.limit >= limitRequired, "can not execute transfer");
        
        transfer.executed = true;
        
        (bool success, ) = transfer.to.call.value(transfer.value)(transfer.data);
        require(success, "tx failed");
        
        emit ExecuteTransaction(msg.sender, _txID);
    }
    
}
1 Like

Hello.
I can now update the balance of the wallet after the transaction has been sent. See updated code below;

pragma solidity 0.7.5;
pragma abicoder v2;

contract MultiSigWallet {
 
    address[] public owners;
    uint limit;
    
    constructor (address[] memory _owners, uint _limit) {
        owners = _owners;
        limit = _limit;
    }
    
    modifier onlyOwners {
        bool owner = false; 
        for(uint i = 0; i < owners.length; i++){
            if(owners[i] == msg.sender){
                owner = true;
            }
        }
        require(owner == true);
        _;
    }
    
    struct Transfer {
        uint amount;
        address payable to;
        uint approval;
        bool sent;
        uint id;
    }
    
    Transfer[] transferList;
    
    mapping(address => mapping(uint => bool)) approval;
    mapping(address => uint) balance;
    
    event deposited(address from, uint amount);
    event transferRequestCreated(uint _id, uint amount, address receiver, address requisitor);
    event approved(uint id, uint approval, address approver);
    event transferred(uint id);
    
    function deposit() public payable {
        balance[address(this)] += msg.value;
        emit deposited(msg.sender, msg.value);
    }
    
    function transferRequest(uint _amount, address payable _to) public onlyOwners {
        require(_amount != 0, "You cannot send nothing");
        require(_amount <= balance[address(this)],"Balance Insufficient");
        emit transferRequestCreated(transferList.length,_amount, _to, msg.sender);
        Transfer memory createTransfer = Transfer(_amount, _to, 0, false, transferList.length);
        transferList.push(createTransfer);   
    }
    
    
    function approve(uint _id) public onlyOwners {
        require(approval[msg.sender][_id] == false);
        require(transferList[_id].sent == false);
        
        approval[msg.sender][_id] = true;
        transferList[_id].approval++;
        emit approved(_id, transferList[_id].approval, msg.sender);
        
        if(transferList[_id].approval >= limit){
            transferList[_id].to.transfer(transferList[_id].amount);
            transferList[_id].sent == true;
            balance[address(this)] -= transferList[_id].amount;
            //balance[transferList[_id].to] += transferList[_id].amount;
            emit transferred(_id);
        }
        
        
    }
    
    function getTransfersList() public view returns (Transfer[] memory){
        return transferList;
    }
    
    function getBalance() public view returns (uint) {
        return balance[address(this)];
    }
}
1 Like

Here is my solution attempt, written without watching any of the project assistance videos. Later I will watch those and see if I can improve my code based on that.

//IvanOnTech multisig wallet assignment solution V1.0 by Florian P
//This solution was written without watching any of the extra information videos

pragma solidity 0.7.5; 
pragma abicoder v2; 

import "./Ownable.sol";


contract MultisigWallet is Ownable {
    
    address[] public Signees; //The list of addresses eligible for signing a proposed transaction
    uint public approvalsNeeded; //The amount of approvals needed to complete a proposed transaction
    event balanceAdded(uint, address); //Balance added to the smart contract
    
    
    modifier onlySignees { //Check if msg.sender is a signee (one of the addressses chosen by the contract owner eligible for signing the proposed transaction)
        bool senderIsSignee = false;
        for(uint i = 0; i < Signees.length; i++){
            if(Signees[i] == msg.sender) senderIsSignee = true;
        }
        require(senderIsSignee == true, "You're not eligible for approving this transaction!");
        _;
    }
    
    modifier notYetSigned { //Check that msg.sender hasn't already signed the proposed transaction (to avoid double counting)
        bool notSigned = true;
        for(uint i = 0; i < proposedTransaction.hasSigned.length; i++){
            if(proposedTransaction.hasSigned[i] == msg.sender) notSigned = false;
        }
        require(notSigned == true, "You've already signed this transaction!");
        _;
    }
    
    struct proposedTransactionStruct { //A proposed transaction is an instance of this struct
        uint amount;
        address payable recipient;
        uint approvals; //The number of approvals from signees so far for this proposed transaction
        address[] hasSigned; //The list of signees who has signed the transaction so far
    }
    
    proposedTransactionStruct private proposedTransaction;
    
    function getContractBalance() public view returns(uint){
        return address(this).balance;
    }
    
    function setSignees(address[] memory _Signees, uint _approvalsNeeded) onlyOwner public { 
        //The contract owner can set which addresses are signees and how many signatures are required
        
        require(_Signees.length > 0);
        require(_approvalsNeeded > 0 && _approvalsNeeded <= _Signees.length);
        
        Signees = _Signees; 
        approvalsNeeded = _approvalsNeeded;
        
    }
    
    
    function depositEther() public payable { 
        emit balanceAdded(msg.value, msg.sender); 
    }
    

    function proposeTransaction(uint _amount, address payable _recipient) public onlyOwner {
        //The owner proposes a transaction by creating an instance of the proposedTransactionStruct
        require(_amount <= getContractBalance());
        address[] memory hasSigned; // An empty list of who signed the proposed transaction so far
        proposedTransaction = proposedTransactionStruct(_amount, _recipient, 0, hasSigned);
    }
    
    function getProposedTransaction() public view returns(proposedTransactionStruct memory){
        return proposedTransaction;
    }
    
    function signTransaction() public onlySignees notYetSigned returns(uint){
        //Signees who have not yet signed the current proposed transaction can sign it. Their address gets added to the
        //list of signors inside the proposedTransactionStruct instance and the amount of approvals in incremented.
        
        proposedTransaction.hasSigned.push(msg.sender);
        proposedTransaction.approvals += 1;
        
        if(proposedTransaction.approvals == approvalsNeeded){ //If their are sufficient approvals then the executeTransaction() function is called
            executeTransaction();
        }
        
        return proposedTransaction.approvals;
        
    }
    
    function executeTransaction() private { //Executes the transaction
        assert(proposedTransaction.amount <= getContractBalance());
        proposedTransaction.recipient.transfer(proposedTransaction.amount);
        reset();

        
    }
    
    function reset() private {
        //reset the values in proposedTransaction and then the struct instance waits to be replaced by a new instance upon proposition of a new transaction
        address[] memory _hasSigned;
        proposedTransaction.amount = 0;
        proposedTransaction.recipient = payable(0x0);
        proposedTransaction.hasSigned = _hasSigned;
        proposedTransaction.approvals = 0;
    }
    
    
}

1 Like

Hi Carlos and Daniele,

When i go through the video for the solution, there is a part I do not understand about dot notation:

transferRequests[_id].approvals++;

I understand transferRequests[_id] is an array with _id,
and i understand approvals is a mapping which address points to uint=>bool
the mapping does not have any struct to it, can we just combine these two ?

Can you advise me more about dot notation too ?

Does the reverse work as well ?
approvals.transferRequests9_id)++ ?

I am just trying to understand one code at a time, and then i will try to connect the codes when i can.

Cheers,

Hello. I tried my hand at this but must be doing something wrong. Specifically line 45 I have not been able to get past.

Are you not able to arrays in structs?
I am doing something wrong with how I manipulate this array… help :-).

Here is the code:

pragma solidity 0.7.5;

contract MultiSig {
    
    event ApprovalRequired(uint indexed txID);
    event ApprovalGranted(uint indexed txID, address approver);
    
    uint private approvalLimit = 0;
    address[] signatoriesMasterList;
    
    struct PendingTransaction {
        address from;
        address to;
        uint ammount;
        address[] signatories;
    }
    
    mapping (uint => PendingTransaction) internal pendingTransactions;
    
    constructor(uint _approvalLimit, address _signatory_0, address _signatory_1, address _signatory_2) {
        approvalLimit = _approvalLimit;
        signatoriesMasterList.push(_signatory_0);
        signatoriesMasterList.push(_signatory_1);
        signatoriesMasterList.push(_signatory_2);
    }
    
    function postTransaction(address to_, uint amount_) public payable returns(uint) {
        address[] sigs storage = new address[](2);
        uint txID = uint(keccak256(abi.encodePacked(msg.sender,to_,amount_)));
        pendingTransactions[txID] = PendingTransaction(msg.sender, to_, amount_,sigs);
        if(isSignatory(msg.sender)) { pendingTransactions[txID].signatories.push(msg.sender); }
        emit ApprovalRequired(txID);
        return txID;
    }
    
    function isSignatory(address candidate) private returns(bool) {
        uint count = 0;
        for (int i = 0; i < signatoriesMasterList.length; i++) {
            if(signatoriesMasterList == candidate) { count++; break;}
        }
        return (count > 0);
    }
    
    function approveTransaction(uint txID) public {
        require(isSignatory(msg.sender));
        bool alreadyApproved = false;
        
        if(pendingTransactions[txID].signatories.length == approvalLimit - 1) {
            //send the transaction off
            pendingTransactions[txID].to.transfer(pendingTransactions[txID].amount);
            emit ApprovalGranted(txID, msg.sender);
            //clean up behind ourselves and remove transaction entry
            delete pendingTransactions[txID];
        }else{
            //since we have not reached the approval limit we add ourselves to the pending transaction, only if we are not already there.
            for(int i = 0; i <= pendingTransactions[txID].signatories.length - 1; i++) {
                if(msg.sender == pendingTransactions[txID].signatories[i]) { alreadyApproved = true; }
            }   
            if(!alreadyApproved) { pendingTransactions[txID].signatories.push(msg.sender); }
            
            //send an event that indicates that we need another signatory
            emit ApprovalRequired(txID);
        }
    }
}

Thanks.

Is there no Set data structure in Solidity?
I had to code in Set semantics into the array of “signatories”.

After we deposited into the 2 owners addresses, how do iwrite the function to check the balance of their contract? Please advise

pragma solidity 0.7.5;
contract Wallet{

uint amount;
mapping (address => mapping( uint=>bool) ) approvals;
function deposit ()public payable {}

function checkBalance (uint index) public view returns (uint)
return approvals [index] [msg.value] ;// /??
// i dont know how to code this
}

I connected a request to a struct myself, but struggled with the concept of approvals - how to write as well as count them. I knew it had to do with nested mappings, but couldn’t quite put my finger on it. So I had watched the Project Assistance video, which showed me the way.

pragma solidity 0.7.5;
pragma abicoder v2;

contract MultiSigWallet {
    
    address owner1;
    address owner2;
    address owner3;
    uint approvalsNecessary;
    
    mapping(address => mapping(uint => bool)) approval;

    modifier onlyOwners {
        require(msg.sender == owner1 || msg.sender == owner2 || msg.sender == owner3, "Not an owner");
        _;
    }
    
    struct Request {
        address payable recipient;
        uint amount;
        uint idNumber;
        uint numberOfApprovals; 
        string state;
    }
    Request[] requests;
    Request[] pendingRequests;
    Request[] sentRequests;
    
    event depositDone(address depositor, uint amount);
    event requestMade(address recipient, uint amount, uint idNumber);
    event approvalMade(address approver, uint idNumber);
    event transactionSuccessful(address recipient, uint amount);
    
    //An alternative way to set the owners
    /*constructor(address address1, address address2, address address3) {
        owner1 = address1;
        owner2 = address2;
        owner3 = address3;
    }*/
 
    constructor() {
        owner1 = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148;
        owner2 = 0x583031D1113aD414F02576BD6afaBfb302140225;
        owner3 = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB;
        
        approvalsNecessary = 2;
    }
    
    function deposit() payable public {
        emit depositDone(msg.sender, msg.value);
    }
   
    function makeRequest(address payable recipient, uint amount) public onlyOwners {
        require(address(this).balance >= amount,"Insufficient funds to make request");
        Request memory requestToBeMade = Request(recipient, amount, requests.length, 0, "Pending");
        emit requestMade(recipient, amount, requests.length);
        requests.push(requestToBeMade);
    }
    
    function approve(uint idNumber) public onlyOwners {
        require(approval[msg.sender][idNumber] == false, "Already approved");
        
        approval[msg.sender][idNumber] = true;
        emit approvalMade(msg.sender, idNumber);
        
        requests[idNumber].numberOfApprovals += 1;
        
        if(requests[idNumber].numberOfApprovals == approvalsNecessary) {
            require(address(this).balance >= requests[idNumber].amount,"Insufficient funds to send transfer");
            requests[idNumber].recipient.transfer(requests[idNumber].amount);
            emit transactionSuccessful(requests[idNumber].recipient, requests[idNumber].amount);
            requests[idNumber].state = "Sent";
            sentRequests.push(requests[idNumber]);
        }
    }
    
    //The following functions are extra. They don't actually do anything except give information
    
    function getAllRequests() public view returns(Request[] memory) {
        return requests;
    }
    
    function getNumberOfRequests() public view returns(uint) {
        return requests.length;
    }

    function getRequestById(uint idNumber) public view returns(Request memory) {
        return requests[idNumber];
    }
    
    function getPendingRequests() public returns(Request[] memory) {
        reset();
        for(uint counter = 0; counter < requests.length; counter++) {
           if(requests[counter].numberOfApprovals < 2) {
               pendingRequests.push(requests[counter]);
           }
        } 
        return pendingRequests;
    }
    
    function reset() private {
        delete pendingRequests;
    }
    
    function getSentRequests() public view returns(Request[] memory) {
        return sentRequests;
    }

    function getContractBalance() public view returns(uint) {
        return address(this).balance;
    }
    
    function doWeLoveTheIvanOnTechAcademy() public pure returns(bool) {
        return true;
    }
}
1 Like

This checks the balance of the contract as a whole.

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

address(this) refers to the address of the contract and .balance checks the balance.

Hope this helps :slight_smile:
Em

3 Likes

Hi @M.K

You can populate a struct by calling its mapping and use the dot notation to indicate the property of the struct (like objects in JS).

contract Helloworld {
    
    struct TestStruct {
        uint age;
    }
        
    mapping (address => TestStruct) public tests;
    
    function set (uint _age) public {
        tests[msg.sender].age = _age;    
    } 
}

The function set is using the mapping [msg.sender] to point the struct related to that msg.sender, then populates the property age with _age.

Does the reverse work as well ?
approvals.transferRequests9_id)++ ?

Programming languages are, indeed, languages.
Like spoken languages they have specific rules to follow, it is not possibile to reverse/ modify those rules.

Cheers,
Dani

2 Likes

Hi @gmb

Screenshot 2021-04-20 at 14.59.01

That type of array declaration has to use memory instead of storage (documentation)

Cheers,
Dani

Hi Daniele,

for this modifier

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

When we write owners.length, do we mean the number of owners addresses in the contract? eg 4 owners addresses

Or does it mean the id of the address of the owners array?
eg, the second owner address out of the 4 owners addresses

Cheers,

Hi @M.K

.length is a property of arrays (and in some languages like js it is also a property of strings).
.length returns the array length:

["a","b","c"]

length = 3

HI Guys,

I deployed the Wallet contract after i entered three addresses and limit.
I was also able to deposit the amount into the three owners addresses, but i realised that whenever i deposit into the second address , there will be 1 or 2 ether deducted from the deposit, Is this supposed to happen ?

And next when i tried to input (value, address )into createTransaction function , i was shown an error message
< transact to Wallet.createTransaction errored: VM error: invalid opcode. invalid opcode The execution might have thrown. Debug the transaction to get more information.>

I followed the code as closely as possible and after i looked though the correct code and compare with my code , i cant see anything that is different. My createTransaction function is as below

function createTransaction ( uint _amount, address payable _receiver)public onlyOwners{
  
emit transferRequestCreated( transferRequests.length, _amount, msg.sender, _receiver); 

transferRequests.push (
    Transfer (_amount, _receiver, 0, false, transferRequests.length )
    );
            
}

Any advice please ?
Cheers,

HI Daniele,

i do not really understand what is property of array .
Does you mean id of the array ? like array[0]. or array[1] ?

but .length actually also returns the array length , like your example

[“a”,“b”,“c”]
length =3

so for owners.length, is it

  1. number of owners in the array ?
  2. the id/ index of the array
  3. or both depending on how we want to use it ?

Sorry i am still unclear with this

Hello everyone. So i made a quick attaempt at the wallet. I was struggling for about an hour on my own i think i just misunderstood what i actually had to do so i did have to watch Fillips video going over structure just to give me a boost to get started. So this is my version of the program and attempt at the solution

I added the following features

  • Addingr function to add users to the wallet

  • Get users function to return the users

  • Delete users function to delete users (only current users can do this)

  • general simple functions like get balanceAccount get contract balance, user, etc blah blah

  • Lastly I wanted to implement the transfer function so that we can send funds to different accounts in the SC aswell as withthdraw funds. These are not implemented aswell and have any edge cases i need to handle

  • One thing i want to implement is like a pending balance mapping in tandom with the normal balance mapping perhaps whereby you can edit this in such a way that lets users only create transactions as by the amount of funds in their account because my current code allows as many transactions to be created. or perhaps a better way to do it would to not let the transaction get approved if funds not sufficent.

As for the approval limit i cheaped out and just set it so that its always 1 less than the current length of the user array. I originally wanted to have it set so that its always like 60% of the length of the user array and then take abs(limit), but solidity has no such abs function i believe. So this will suffice for now.

My code is attached below its not the best but im sure many can improve upon it because there are so many edge cases that compromise the codes security but its just my first attempt and i havent really gotten deep into dealing with them yet which i will work on it for the next day or two handling as many edge cases as i can before starting the 201 course. Its also all in one file which makes it leess readable.

pragma solidity 0.7.5;
pragma abicoder v2;


//TO DO -- make withdrawal struct

contract Wallet {

address[] owners;
uint public limit;

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


Transfer[] transferRequests;

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


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

event TransferRequestCreated(uint _id, uint _amount, address _initiator, address _receiver);
event ApprovalReceived(uint _id, uint _approvals, address _approver);
event TransferApproved(uint _id);

function addUsers(address _owners) public
{
    for (uint user = 0; user < owners.length; user++)
    {
        require(owners[user] != _owners, "Already registered");
    }
    owners.push(_owners);
    
    //from the current array calculate the value of minimum consensus
    limit = owners.length - 1;
}

function removeUser(address _user) public
{
    uint user_index;
    for(uint user = 0; user < owners.length; user++)
    {
        if (owners[user] == _user)
        {   
            user_index = user;
            require(owners[user] == _user);
        }
    }
    
    owners[user_index] = owners[owners.length - 1];
    owners.pop();
    //owners;
}


function getUsers() public view returns(address[] memory)
{
    return owners;
}

function getApprovalLimit() public view returns (uint)
{
    return (limit);
}


//Empty function
function deposit() public payable onlyOwners
{
    require(msg.value >= 0);
    require(owners.length > 1, "need to have more than one signer");

    balance[msg.sender] += msg.value;
}


//next we want to make a get balance function
function getAccountBalance() public view returns(uint)
{
    return balance[msg.sender];
}


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


//next we want to make q function to return the address of the wallet owner
function getOwner() public view returns(address)
{
    return msg.sender;
}


//Create an instance of the Transfer struct and add it to the transferRequests array
function createTransfer(uint _amount, address payable _receiver) public onlyOwners {
    require(owners.length > 1, "need to have more than one signer");
    //require(msg.sender != _receiver);
    for (uint i = 0; i < owners.length; i++)
    {
        require(owners[i] != _receiver);
    //   if  (owners[i] == _receiver)
    //   {
    //       revert();
    //   }
    }
    emit TransferRequestCreated(transferRequests.length, _amount, msg.sender, _receiver);
    transferRequests.push(
        Transfer(_amount, _receiver, 0, false, transferRequests.length)
    );
    
}



function approve(uint _id) public onlyOwners {
    require(owners.length > 1, "need to have more than one signer");
    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);

    
}


 //now we need to create a function to actually transfer the funds after the
//transfer has been recieved
function TransferFunds(uint _id) public returns(uint)
{
    require(owners.length > 1, "need to have more than one signer");
    require(transferRequests[_id].approvals >= limit);
    
    if(transferRequests[_id].approvals >= limit)
    {
        transferRequests[_id].hasBeenSent = true;
        balance[msg.sender] -= transferRequests[_id].amount;
        balance[transferRequests[_id].receiver] += transferRequests[_id].amount;
        
    }
    return balance[msg.sender];
}

//after transfer is called our balance i < transaction amount thus we cannot withfraw
//update amount after transfer function.
function withdraw(uint _id, uint _amount) public onlyOwners returns (uint)
{
    //take in amount and update struct 
    require(owners.length > 1, "need to have more than one signer");
    require(transferRequests[_id].approvals >= limit);
    
    if(transferRequests[_id].approvals >= limit)
    {
        transferRequests[_id].hasBeenSent = true;
        transferRequests[_id].receiver.transfer(_amount);
        emit TransferApproved(_id);
    }
    
    return balance[msg.sender];
    
}


function getApprovalState(uint _id) public view returns(uint)
{
    return transferRequests[_id].approvals;
}


//Should return all transfer requests
function getTransferRequests() public view returns (Transfer[] memory){
   
    return transferRequests;
}

}

2 Likes

@M.K the owners.length is simply just the length of the array but it can be used in clever ways. So in your above post the length of the transfer array is used in a clever way to get the ID of the transaction. So when there is no transfer requests the array is empty and thus its length is 0. so when the transaction_ID of the first transfer is specified to be = transferRequest.length then its just taking the length of the array which is currently 0 as its empty and assigning it to the id of the first transaction. Likewise if we now make a new transaction the array size is now 1 and thus the second transfer request will have an id of 1. and so on

2 Likes

HI Guys,

I do not understand one part . Can you explain why we use i<owners.length in the for loop ?

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

Say there are 3 owners addresses, so by putting owners.length, we are saying we want the i to add 3 times before it stops. RIght ?

I really dont understand the use of for loop. Can we just use the below ?
modifier onlyOwners(){
bool owner = false
uint i;
if(owners[i] == msg.sender){
owner = true;
}
}
require(owner == true);
_;
}

Any pointers would be appreciated.
Cheers

1 Like