Project - Multisig Wallet

I tired without watching the next videos, got a parsing error for an import directive that I could not figure out so not sure if it was going to work.

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

contract MultisigWallet {
   event Deposit(address indexed sender, uint amount, uint balance);
   event SubmitTransaction(
       address indexed owner,
       uint indexed txIndex,
       address indexed txIndex,
       address indexed to,
       uint value,
       bytes data);
       
    event ConfirmTransaction(address indexed owner, uint indexed txIndex); 
    event RevokeConfirmation(address indexed owner, uint indexed txIndex);  
    event ExecuteTransaction(address indexed owner, uint indexed txIndex);  
    
    // store owners in array of addresses
    address[] public owners;
    
    // check for no duplicate owners 
    mapping(address => bool) public isOwner;
    
    // store # of confirmations required to execute a tx
    uint public numConfirmationRequired;
    
    // When a tx is proposed by calling SubmitTransaction >> create struct
    struct Transaction {
        address to;
        uint value;
        bytes data;
        bool executed;
        uint numConfirmations;
    }
    
        // mapping from tx index => owner => bool
    mapping(uint => mapping(address => bool)) public isConfirmed;

    
    // store struct inside array of transactions
    Transaction[] public transactions;
    
     modifier onlyOwner() {
        require(isOwner[msg.sender], "not owner");
        _; // if caller is owner execute the rest of the function
    }
    
     modifier txExists(uint _txIndex) {
        require(_txIndex < transactions.length, "tx does not exist");
       _;
   }
   
     modifier notExecuted(uint _txIndex) {
        require(!transactions[_txIndex].executed, "tx already executed");
       _;
   }
   
     modifier notConfirmed(uint _txIndex) {
        require(!transactions[_txIndex].isConfirmed[msg.sender], "tx already confirmed");
   }
   
    
    
    constructor(address[] memory _owners, uint _numConfirmationsRequired) public {
        require(_owners.length > 0, "owners required"); // require array of owners !empty
        require( _numConfirmationsRequired > 0 && _numConfirmationsRequired <= _owners.length, "invalid number of required confirmations");
        
        // copy owners from input into state variables
        for (uint i = 0; i < _owners.length; i++)
            address owner = _owners[i];
        
            require(owner != address(0), "invalid owner");
            require(!isOwner[owner], "owner not unique");
        
            isOwner[owner] = true;
            owners.push(owner);
       }   
        numConfirmationsRequired  _numConfirmationsRequired;
    }
      
   // define fallback function declared as payable so we can send Ether to contract 
    // when called emit deposit event  
    function deposit () payable external {
        emit Deposit(msg.sender, msg.value, address(this).balance);
    }
      
        // owner submits tx that must be approved by other owners
   function submitTransaction(address _to, uint _value, bytes memory _data) public onlyOwner {
       uint txIndex = transactions.length; // id for tx created

// initialize a tx struct and append to array of transactions
       transactions.push(Transaction({
           to: _to,
           value: _value,
           data: _data,
           executed: false,
           numConfirmations: 0
       }));
       
       emit SumbitTransaction(msg.sender, txIndex, _to, _value, _data);
   }
      
         
   // allow owners to approve transaction
   function confirmTransaction(uint _txIndex) public onlyOwner
   txExists(_txIndex) 
   notExecuted(_txIndex)
   notConfirmed(_txIndex){
       
       // update tx struct 
       Transaction storage transaction = transactions[_txIndex];
       
       // msg.sender has approved tx
       transaction.numConfirmations += 1;
       isConfirmed[_txIndex][msg.sender] = true;
       
       emit ConfirmTransaction(msg.sender, _txIndex);
   }
      
       // if enough owners approve, execute transaction
   function executeTransaction(uint _txIndex) public onlyOwner
   txExists(_txIndex)
   notExecuted(_txIndex) {
       
       Transaction storage transaction = transactions[_txIndex];
       
       // require min number of owners to confirm tx
       require(transaction.numConfirmations >= numConfirmationsRequired, "cannot execute tx");
       
       // if enough confirmations >> true
       transaction.executed = true;
       
       // execute tx using call method and check call was successful 
       (bool success, ) = transaction.to.call.value(transaction.value)(transaction.data);
       require(success, "tx failed");
       
       // emit execute tx with owner that called this function and the tx that was executed
       emit ExecuteTransaction(msg.sender, _txIndex);
   }
      
    function revokeConfirmation(uint _txIndex)
        public
        onlyOwner
        txExists(_txIndex)
        notExecuted(_txIndex)
    {
        Transaction storage transaction = transactions[_txIndex];

        require(isConfirmed[_txIndex][msg.sender], "tx not confirmed");

        transaction.numConfirmations -= 1;
        isConfirmed[_txIndex][msg.sender] = false;

        emit RevokeConfirmation(msg.sender, _txIndex);
    }
    
     function getOwners() public view returns (address[] memory) {
        return owners;
    }

    function getTransactionCount() public view returns (uint) {
        return transactions.length;
    }

    function getTransaction(uint _txIndex)
        public
        view
        returns (address to, uint value, bytes memory data, bool executed, uint numConfirmations)
    {
        Transaction storage transaction = transactions[_txIndex];

        return (
            transaction.to,
            transaction.value,
            transaction.data,
            transaction.executed,
            transaction.numConfirmations
        );
    }
}

1 Like

I got an error on that assert, i will find out. Going to debug it

Exactly, a require statement can verify if a owner had signed or not a transaction, but for that, you will also need a variable that tracks who have sign what.

I could suggest you to add the double mapping, which will be the next lessons, there you will learn how to code exactly what i mention you :nerd_face:

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

Carlos Z.

1 Like

Hey @Ciantera_R, hope you are great.

Your contracts looks very good, but you have some few syntax issues (minimal human errors when typing to many lines of code).

First error is just a missing of a bracket on the for loop {. (just have to add the bracket at the start of the loop)
image

Then this error appears, which shows that you have 2 variables with the same name.
Also here, you should only use the keyword indexed for address variables, not in uint.
image

Here you miss to add the _; statement which is necessary on the modifiers to continue with the function execution if the modifier conditions are met.
image

Another mistype error here, numConfirmationsRequired but you declare the variable has: (code line 26)
You are also missing the keyword = to assign the value of _numConfirmationsRequired to the numConfirmationRequired variable

    // store # of confirmations required to execute a tx
    uint public numConfirmationRequired;

image

Here is another mistype, you mispell the word submit
image

Finally here you are mispell the same variable, an extra S.
image

Another error in the syntax to invoke the double mapping, i have comment the proper way to call the value of the double mapping:
image

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

Carlos Z.

1 Like

Here’s my second try. This seems to be working. There’s some helper- and debug functions at the end.

pragma solidity 0.7.5;
pragma abicoder v2;

contract MSWallet {
    
    address[] private owners;
    uint private approvalLimit;
    
    struct TransferRequest {
        address payable recipient;
        uint amount;
        uint approveCount;
    }
    
    TransferRequest[] transferRequests;
    
    constructor(address[] memory _owners, uint _approvalLimit) {
        owners = _owners;
        approvalLimit = _approvalLimit;
    }
    
    function deposit() public payable {
        //This actually works. Since function is payable, msg.value will be added to address(this).balance.
    }
    
    function t_transfer(address payable _address, uint _amount) public {
        require(address(this).balance >= _amount);
        require(msg.sender != _address);
        _address.transfer(_amount);
    }
    
    function createTransferRequest(address _owner, address payable _recipient, uint _amount) public {
        require(checkOwner(_owner));
        TransferRequest memory newTransferRequest = TransferRequest(_recipient, _amount, 0);
        transferRequests.push(newTransferRequest);
    }
    
    function approveRequest(uint _index, address _owner) public {
        require(transferRequests.length != 0);
        require(checkOwner(_owner));
        transferRequests[_index].approveCount += 1;
        if (transferRequests[_index].approveCount == approvalLimit) {
            t_transfer(transferRequests[_index].recipient, transferRequests[_index].amount);
        }
    }
    
    function checkOwner(address _address) private view returns(bool _bool) {
        for (uint i=0; i<owners.length; i++) {
            if (owners[i] == _address) {
                return true;
            } else {
                return false;
            }
        }
    }
    
    //Getter just to check balance of contract.
    function getBalance() public view returns(uint) {
        return address(this).balance;
    }
    
    //Getter to check transfer requests.
    function getTransfersRequest() public view returns(TransferRequest[] memory) {
        return transferRequests;
    }
}

@thecil lHi Carlos, thank you so much for your detailed explanations of the bugs in my code! I was able to fix and update everything except for the double mapping. I don’t see the comment you wanted to include for the correct way to call the value of the double mapping, but I would love to see it! This project drove me to madness but I would love continue this version if I can figure that out. Thanks again, most appreciated!

1 Like

I have mention this to an student before, about the double mappings, maybe it could help you.

If you still are confuse it with double mappings let me know to explain it, is quite simple to be honest.

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

Carlos Z.

1 Like

Hello, here is my modest code :

pragma solidity 0.7.5;

contract MultisigWallet {
    
    address public owner;
    address[] private keyholders;
    
    constructor(){
        owner = msg.sender;
        keyholders = [0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2, 
        0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db, 
        0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB];
        
    }
    
    modifier onlyOwner{
        require(msg.sender == owner);
        _;
    }
    
    modifier onlyKeyholder{
        require(msg.sender == keyholders[0] || msg.sender == keyholders[1] || msg.sender == keyholders[2]);
        _;
    }
    
    mapping(address => bool) approvals;
    
    struct Vault {
        uint quantity;
        bool transferRequested;
        address payable transferTo;
        uint amountToTransfer;
    }
    
    Vault private ourVault;

    function deposit() public payable {
        ourVault.quantity += msg.value;
    }
    
    function executeTransfer() private {
        ourVault.transferTo.transfer(ourVault.amountToTransfer);
        ourVault.quantity -= ourVault.amountToTransfer;
    }
    
    function requestTransfer(address payable _recipient, uint _amount) public onlyKeyholder {
        approvals[msg.sender] = true;
        ourVault.transferTo = _recipient;
        ourVault.amountToTransfer = _amount;
        ourVault.transferRequested = true;
    }
    
    function approveTransfer() public onlyKeyholder returns(bool){
        require(ourVault.quantity >= ourVault.amountToTransfer);
        approvals[msg.sender] = true;
        if (approvals[keyholders[0]] == true && approvals[keyholders[1]] == true && approvals[keyholders[2]] == true) {
            executeTransfer();
            ourVault.transferTo = msg.sender;
            ourVault.amountToTransfer = 0;
            ourVault.transferRequested = false;
            approvals[keyholders[0]] = false;
            approvals[keyholders[1]] = false;
            approvals[keyholders[2]] = false;
            return true;
        }
        else{
            return false;
        }
    }
    
    function getVaultBalance() public view returns(uint){
        return ourVault.quantity;
    }
    
    function getTransferRequest() public view returns(address, uint){
        return (ourVault.transferTo, ourVault.amountToTransfer);
    }
    
    function getApprovals() public view returns(bool, bool, bool){
        return (approvals[keyholders[0]], approvals[keyholders[1]], approvals[keyholders[2]]);
    }
}
2 Likes
pragma solidity 0.7.5;
pragma abicoder v2;

//my test accounts - ["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2","0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db"]'
contract MultiSigWallet {
    address[] public memberAccts;
    uint approvalLimit;

    struct TransferRequest {
        address payable fundsDestination;
        uint transferAmount;
        uint approvals;
        uint transferId;
    }
    
    mapping(address => mapping(uint => bool)) transferApproval;
    mapping(address => uint) balance;

    event depositDone(uint _amount, address indexed _depositedTo);
    event tranferRequestCreated(uint _transferId, uint _amount, address indexed _depositTo);
    event transferRequestCompleted(uint _transferId, uint _amount, address indexed _depositedTo);
    event approvalReceived(uint _id,uint _approvals, address _approver);
    
    modifier isMember() {
        bool member = false;
        for(uint i=0; i<memberAccts.length;i++){
            if(memberAccts[i]==msg.sender){
                member=true;
            }
        }
        require(member==true);
        _; //run function
    }

    TransferRequest[] transfers;
    
    constructor(address[] memory _memberAccts, uint _approvalLimit){
        approvalLimit=_approvalLimit;
        memberAccts=_memberAccts;
    }
    
    function deposit() public payable isMember returns (uint) {
        //walletBalance+= msg.value;
        emit depositDone(msg.value, msg.sender);
        return address(this).balance;
    }
    
    function createTransferRequest(address payable _destination,uint _amount) public isMember{
        uint transferId = transfers.length;
        transfers.push(TransferRequest(_destination, _amount, 0, transferId));
        emit tranferRequestCreated(transferId, _amount, _destination);
    }
    
    function approveTransfer(uint _transferRequestId, bool _approve) public isMember{
        require(transfers[_transferRequestId].approvals<approvalLimit,"This transaction has already been approved");
        require(transferApproval[msg.sender][_transferRequestId]== false,"You have already submitted your vote");
        if(_approve){
            transfers[_transferRequestId].approvals ++;
            emit approvalReceived(_transferRequestId,transfers[_transferRequestId].approvals,msg.sender);
            if(transfers[_transferRequestId].approvals==approvalLimit){
                uint previousSenderBalance = address(this).balance;
                transfers[_transferRequestId].fundsDestination.transfer(transfers[_transferRequestId].transferAmount);
                assert(address(this).balance == previousSenderBalance - transfers[_transferRequestId].transferAmount);
                emit transferRequestCompleted(_transferRequestId, transfers[_transferRequestId].transferAmount, transfers[_transferRequestId].fundsDestination);
                
            }
        }
        transferApproval[msg.sender][_transferRequestId]=true;
    }
    function getWalletBalance() public view returns(uint){
        //was using a variable called walletBalance, but found address(this).balance and thought it was much cooler
        return address(this).balance;
    }
    
    function getTransferRequests() public view returns (TransferRequest[] memory){
        return transfers;
    }
    
    
}
1 Like

How do I initialize the constructor of the contract when I deploy it. I keep entering the address to address[] owner but it keeps rejecting it. What am I supposed to enter to deploy it?

image

image

1 Like

hey so what I did here was, I copied the first line of each function from the final copy (not including events… etc) and I filled it in with what I had remembered or what made sense, I cannot figure out why some of my lines are wrong, can you maybe check it out?

pragma solidity 0.7.5;
pragma abicoder v2;

contract Wallet {
    
    uint limit;
   
    address[] public owners;
    
    struct transaction{
        address payable receiver;
        uint amount;
        uint approvals;
        bool hasbeensent;
        uint id;
    }
    
      

    transaction[] transferRequests;
    
    mapping(address => mapping(uint => bool)) approvals;
    
    //Should only allow people in the owners list to continue the execution. 
    (this line is wrong)   modifier onlyOwners () {
        require( msg.sender == owner);
        for(uint i=0; i<owners.length; i++ ){
            if(owners[i] == msg.sender){
                owner = true;
            }
        }
        
        
    }
       event tranferapporved( uint _id, address approver, uint amountofapprovals);
       event transferrequest(uint _id, address tranferto, uint amount, uint maker);
       event transfersent(uint _id);
        
        
    //Should initialize the owners list and the limit 
    constructor(address[] memory _owners, uint _limit){
        limit = _limit;
        owners = _owners;
        
    }
    
    
    //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{
  (this line as well)      emit transferrequest(transferRequests.length, _amount, msg.sender, _receiver);
        transferrequest.push(transaction( _receiver, _amount, approvals, false, transferRequests.length));
    }

Oh. My God. I did it. It took about 8 hours & 4 americanos over the course of three days, but I have some functional code!

I’m proud to say I created the following without looking at any of the follow-up videos to the assignment, but I’m sure I will learn plenty from what others have done & the suggestions Filip makes.

I started with a parent contract called MultiOwnable.sol that help the onlyOwners modifier, but I was having a helluva time trying to pass arguments into the parent constructor. (Turns out I was struggling with a browser bug & I just had to refresh Remix—but I learned that after writing all of MultiOwnable back into the main file, so f*ck it.)

My code:

pragma solidity ^0.8.3;
pragma abicoder v2;

/* args for Deployment
["0x5B38Da6a701c568545dCfcB03FcB875f56beddC4","0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2","0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db"],"2"
 */

contract multiSigWallet {
        
    address payable[] Owners;
    uint approvalRequirement;
    
    TransferRequest[] activeRequests;
    uint totalTransfersProposed;
    uint balance;
    
    mapping(address => bool) private isOwner;
    
    event depositTx(uint balanceAdded, uint currentBalance);
    event transferTxEvent(uint amountSent, uint currentBalance);
        
    modifier onlyOwners {
        require(_checkOwner(payable(msg.sender)));
        _;
    }
    
    struct TransferRequest {
        uint transferId;
        address requestCreator;
        uint amount;
        address payable destination;
        uint approvalCount;
        uint approvalReq;
    }
    
    constructor(address payable[] memory _owners, uint _approvalsRequired) {
        require(_approvalsRequired <= _owners.length, "ERROR: Required number of Approvals must be <= Owners");
        approvalRequirement = _approvalsRequired;
        Owners = _owners;
        totalTransfersProposed = 0;
        balance = 0;
    }
    
    // I would make this onlyOwner but req's say "anyone can deposit to the contract"
    function desposit() public payable returns(uint) {
        balance += msg.value;
        emit depositTx(msg.value, balance);
        return(balance);
    }
    
    function createTransferRequest(uint _amount, address payable _destination, bool _selfApproval) public onlyOwners {
        
        require(balance >= _amount, "ERROR: Insufficient funds");
        
        // *** NOTE: *** The number of approvals required before _transferTx() fires is currently _hard coded_ to 2
        
        TransferRequest memory _tReq = TransferRequest(totalTransfersProposed, msg.sender, _amount, _destination, 0, approvalRequirement);
        if (_selfApproval) {
            _tReq.approvalCount ++;
        }
        activeRequests.push(_tReq);
        totalTransfersProposed ++;
    }
    
    // User must input transferId & correct proposed amount to Approve.
    // This function will call _transferTx() upon approval requirements met
    function approveTransferRequest(uint _transferId, uint _amount) public onlyOwners {
        
        require(_amount == activeRequests[_transferId].amount, "ERROR: Incorrect Approval, ensure proper TransferID & Amount");
        
        activeRequests[_transferId].approvalCount ++;
        if (activeRequests[_transferId].approvalCount >= activeRequests[_transferId].approvalReq) {
            _transferTx(activeRequests[_transferId]);
        }
    }
    
    function _transferTx(TransferRequest memory _transferRequest) private {
        
        // execute transfer
        balance -= _transferRequest.amount;
        _transferRequest.destination.transfer(_transferRequest.amount);
        emit transferTxEvent(_transferRequest.amount, balance);
    }
    
    function getBalance() public view returns(uint) {
        return(balance);
    }
    
    function _checkOwner(address payable _auth) private view returns(bool) {
        bool x = false;
        for(uint i=0; i<Owners.length; i++) {
            if (_auth == Owners[i]) {
                x = true;
            }
        }
        return(x);
    }
    
}

Amazing course, amazing final project. I look forward to diving back in & refining this code, but for now I’m going to celebrate a bit! Cheers y’all, and best of luck.

1 Like

you should input an array data, something like

["address1", "address2", "address3"] 

whenever the addresses you should change them for real addresses.

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

Carlos Z.

1 Like

Ok so i finnaly got the code to work and I figured out that I was trying to use the .transfer function with individual addresses instead of the wallet account. Im having trouble with the payable addresses but for the most part I can honestly say I have gotten a pretty good foundation to build on and Im looking forward to 201

pragma solidity 0.7.5;
pragma abicoder v2;
contract MultisigaWalleto {
    
    event pendingTransfers (address _recipient, uint id);
    event CompletTransfers (uint id);
    
mapping (address=>uint) balance;
mapping (address=> mapping (uint=>bool)) status;
uint approvalLimit = 2;
address  payable contractCreator;
address [] ownersArray;
constructor (){
     contractCreator=msg.sender;
}

    modifier onlyCreator{
        require( contractCreator==msg.sender,"U aint the creator");
        _;
    }
    modifier onlyOwner{
        bool owner = false;
    for(uint i=0; i < ownersArray.length; i++){
        if (ownersArray[i]== msg.sender){
        owner = true;
    }
    }
    require (owner==true,"U aint a Owner");
    _;
}
       struct Transferstruct{
        uint TransactionId;
        bool hasBeenSent;
        address payable recipient;
        uint amount;
        uint numberofApprovals;
    }  
    
    Transferstruct [] Transfers;
    
    function AddTransaction(address payable _recipient, uint _amount ) public onlyOwner{
        require (balance[msg.sender]>=_amount,"Cant send what U aint Got!");
        Transferstruct memory newOwner= Transferstruct(Transfers.length, false, _recipient, _amount, 0);
        Transfers.push (newOwner);
        emit pendingTransfers(_recipient, Transfers.length);
    }
    
    function addOwners(address payable owner2Add) public onlyCreator {  //Make sure to add contractCreator as an owner as well
    ownersArray.push (owner2Add);
    }

    function add2Account(uint amount) public  {
    balance[msg.sender] += amount;
    }
    
    function deposit() public payable{}
    
    function getBalance() public view returns (uint){
        return balance[msg.sender];
    }
    
    function Approve (uint id) public onlyOwner{
        require(status[msg.sender][id]==false,"You already aproved this transaction");
        require(Transfers[id].hasBeenSent== false,"this Transaction is already complete");
        status[msg.sender][id]=true;
        Transfers[id].numberofApprovals++;
        if (Transfers[id].numberofApprovals>=2){
           payable (Transfers [id].recipient).transfer (Transfers[id].amount);
            emit CompletTransfers(id);
        }
    }
    function getOwners() public view onlyCreator returns (address [] memory){
        return ownersArray;
    }
    function getTransfers() public view onlyOwner returns (Transferstruct [] memory ){
        return Transfers;
    }
}
1 Like

I’m getting an error on this modifier not exactly sure why. Would appreciate help.


` modifier Makerseye(){
        bool Makerman = false;
        for(uint fish=0; fish<LosDuenos.length;fish++){
            for(LosDuenos[fish] == msg.sender){
                Makerman = true;
            }
        }
        require Makerman == true;
        _;
    }
    ``
1 Like

Hey @Moneymonty, hope you are ok.

I’m testing your contract, it compiles properly, but you have some few errors on the function logic that you could improve better.

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

You define add2Account to add funds to an account balance, but that function is not payable, so it can receive true funds (msg.value), i could just type whatever number i like and it will be take it has balance. You can just that codeline to the deposit function, it will end in something like this

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

Now for the .transfer function you just have common syntax for it, there is no need to specify payable. So it looks like this:

Transfers[id].recipient.transfer(Transfers[id].amount);

By just doing that, i was able to transfer properly to an external account after approving the tx with the 2 owners, the bug i have to made is on your add2Accounts function, because i deposited the funds with deposit, then i just add the same amount on the add2Accounts (or i could add what ever amount i like).

You almost have it, overall you have made a very nice job! :muscle:

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

Carlos Z.

1 Like

your require should have its proper brackets ().

It should look like:

require(Makerman == true, "FAIL MESSAGE");

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

Carlos Z.

2 Likes

Hey thanks alot for the detailed explanation, it makes perfect sense now that i think about it. The function im depositing to needs to be payable in order to deal with real $, its so obvious now i cant beleive i didnt see it. Thanks alot i feel like im starting to get a holistic view of how smart contracts work. They really arent that complicated when you put it all together with external functionality and inheritance. Looking forward to learning more in 201

1 Like

ill be honest this was just too much for me.
i understood every video but not ready yet to create a contract like this

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

Transfer[] transferRequests;

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

//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 {
 transferRequests.push(
 Transfer( _amount, _receiver, 0 , false , transferRequests.length)
 );
}

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 ++;
    
    if(transferRequests[_id].approvals >= limit) {
        transferRequests[_id].hasBeenSent = true ;
        transferRequests[_id].receiver.transfer(transferRequests[_id].amount);
        
    }
}

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

}

2 Likes

Hi @Bogdan_Manzar

  • Modifiers always end with _; that is missing in yours.
    There are other errors in your modifiers that you will see once fixed the one reported above.
modifier onlyOwners () {
   require( msg.sender == owner);
   for(uint i=0; i<owners.length; i++ ){
       if(owners[i] == msg.sender){
           owner = true;
       }
   }
}
  • The order of the emitter parameters is different compared to the order in which they are stated, therefore you get an error. Check them out.
 event transferrequest(uint _id, address tranferto, uint amount, uint maker);
 emit transferrequest(transferRequests.length, _amount, msg.sender, _receiver);

Regards,
Dani