Project - Multisig Wallet

i put htat comment when i was posting my code here, just so that the person testing it knew that they had to put the contracts address, and not the accounts address, also i dont know how to start and finish a comment in the same line xD.

When you say that im using onlyOwner incorrectly, do you mean that the function is not doing what it is supposed to do? or that im defining it in a non traditional way?

and also could you help me, with my question i had in the first post? this one:

i was trying to make the currentProposa l function so that it didnt need input from the users, so that they didnt have to do the effort to know the index of the current proposal, but then i tried this :

function currentProposal () public view returns (//parameters){
return (proposedTx[proposedTx.length].amount, …//rest of the array)

This didnt work for me, i also tried other stuff like 0+proposedTx.length xD, but it didnt work, i knnow i should know the answer for this, but i dont D:

one last thing xD, my code looks like unfinished because it is xD, is just that i was eager to post the contract already, but yes my contract needs mroe work

Thats why i’m suggesting you to go into the 201 course, there are some lessons that I know they will help you understand some the issues you are facing with your external contracts (staking and withdraw dai for example). A cleaver guy like you will see what you are missing by just following the videos on the next course.

From my understanding, your enum variable is well defined, also you should have a error message on each of your require so when any revert errors is show up, it will help you understand where is that error being generated.

You have a great DeFi + multisig wallet contract, just need some few improvements and it will be amazing.

Here you can read a little bit more about the staking functions:

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

Carlos Z.

wow @Bdos87 Bdos is already doing DeFi stuff :o, i gotta get back to classes then *o *

im sorry sir for my agressive response of before D:, you got a lot of problem child in ths course xD

thank you man. I will read up on the links you sent and move onto 201 soon. Thanks again.

hey @thecil, Im getting a error when compiling stating: parser error: function, variable, struct, or modifier expected. enum TransType {trans_Eth, trans_Dai, stake_Dai, withdraw_Dai} . If you could just help me understand how to use the enum in the transaction struct and IF statements I would much appreciate that.

Barzo

1 Like

when you have a modifier like onlyOwner which does not require argument, you just need to use the name of the modifier, but if you modifier requires an argument, then you do something like onlyOwner(arguments).

Now for your currentProposal () function, you have 2 ways, or return the entire array or an index of it.

To return entire array:

contract Array
pragma solidity 0.7.5;
pragma abicoder v2;

contract Arrays{
    address owner;
    
    modifier onlyOwner (){
        require(msg.sender == owner, "You are not an owner sir");
        _;
    }
    
    constructor(){
        owner = msg.sender;
        propose(msg.sender, 100);
    }
    
    struct txn{
        address from; 
        address to;
        uint amount;
        uint index;
        bool tag;
        uint yes;
        uint no; 
    }
    
    txn [] proposedTx;
    
    function propose(address recipient, uint amount) public onlyOwner {
        //require(approvedTx.length + rejectedTx.length == proposedTx.length, "Current proposal needs to be approved first");
        //require(amount < balance[Wallet], "We do not have all that money");
        proposedTx.push( txn(msg.sender, recipient, amount, proposedTx.length, true, 0, 0) );
    }
    
    function currentProposal() public onlyOwner view returns (txn [] memory){
        return(proposedTx);  
        
    } 
}

Or just get the last element like if it was the last currentProposal:

contract Array
pragma solidity 0.7.5;
pragma abicoder v2;

contract Arrays{
    address owner;
    
    modifier onlyOwner (){
        require(msg.sender == owner, "You are not an owner sir");
        _;
    }
    
    constructor(){
        owner = msg.sender;
        propose(msg.sender, 100);
    }
    
    struct txn{
        address from; 
        address to;
        uint amount;
        uint index;
        bool tag;
        uint yes;
        uint no; 
    }
    
    txn [] proposedTx;
    
    function propose(address recipient, uint amount) public onlyOwner {
        //require(approvedTx.length + rejectedTx.length == proposedTx.length, "Current proposal needs to be approved first");
        //require(amount < balance[Wallet], "We do not have all that money");
        proposedTx.push( txn(msg.sender, recipient, amount, proposedTx.length, true, 0, 0) );
    }
    
    function currentProposal() public onlyOwner view returns (address, address, uint, uint, bool, uint, uint){
        uint _lastProposal = proposedTx.length;
        return(
            proposedTx[_lastProposal].from,
            proposedTx[_lastProposal].to,
            proposedTx[_lastProposal].amount,
            proposedTx[_lastProposal].index,
            proposedTx[_lastProposal].tag,
            proposedTx[_lastProposal].yes,
            proposedTx[_lastProposal].no
        );
        
    } 
}

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

Carlos Z.

I have written a quick contract just to show you the proper syntax for the enum and also you can check how the IF condition works perfectly, you almost have your contract work, also most of the issues you are facing can be solve in Ethereum Smart Contract Programming 201, how to use enums and external contracts interactions. :face_with_monocle:

Enum and IF contract
pragma solidity 0.7.5;
pragma abicoder v2;

contract Arrays{
    address owner;
    
    modifier onlyOwner (){
        require(msg.sender == owner, "You are not an owner sir");
        _;
    }
    
    constructor(){
        owner = msg.sender;
        submitTransaction(100, msg.sender, TransType.trans_Eth);
        submitTransaction(200, msg.sender, TransType.trans_Dai);
        submitTransaction(300, msg.sender, TransType.stake_Dai);
        submitTransaction(400, msg.sender, TransType.withdraw_Dai);
    }
    
    enum TransType {trans_Eth, trans_Dai, stake_Dai, withdraw_Dai}
    
    struct Transaction {
        TransType tType;
        address payable to;
        uint amount;
        uint index;
        bool hasBeenSent;
    }
    
    Transaction[] transactions;
    
    function submitTransaction (uint _amount, address payable _to, TransType _tType) public onlyOwner {
        if (_tType == TransType.trans_Eth) {
        //require(_amount <= totalEth);
        Transaction memory newTransaction = Transaction (TransType.trans_Eth, _to, _amount, transactions.length, false);
        transactions.push(newTransaction);
        }
        else if (_tType == TransType.trans_Dai) {
        //require(_amount <= totalDai);
        Transaction memory newTransaction = Transaction (TransType.trans_Dai, _to, _amount, transactions.length, false);
        transactions.push(newTransaction);
        }
        else if (_tType == TransType.stake_Dai) {
        //require(_amount <= totalDai);
        Transaction memory newTransaction = Transaction (TransType.stake_Dai, _to, _amount, transactions.length, false);
        transactions.push(newTransaction);
        }
        else if (_tType == TransType.withdraw_Dai) {
        //require(_amount <= stakedDai);
        Transaction memory newTransaction = Transaction (TransType.withdraw_Dai, _to, _amount, transactions.length, false);
        transactions.push(newTransaction);
        }
    }


    function getTx() public view returns(Transaction [] memory){
        return(transactions);
    }
    
}

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

Carlos Z.

@thecil Just give me the solution man hahaha, I promise i will go onto 201. so i need to declare a submit transaction in the constructor with assigned variables? youve kept the syntax the same, so its just a matter of declaring the submit transaction in the constructor?

thanks bro

1 Like

I’m just declaring the function in the constructor to validate that is working correctly with different parameters. The idea is just to show you the proper functionality and syntax over enums and conditionals.

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

Carlos Z.

@thecil so why am i getting the error when i compile the contract?. Brother please just tell me the issue, I appreciate the information but just a simple answer to what i should change to fix the issue would be a lot less time consuming. Ive done my best to use the course content to develop my understanding, but self learning and google can be very time consuming and frustrating, especially when the academies marketing pitch claims to have dedicated mentors/tutors to help us when we get stuck.

regards

Barzo

Bad Syntax, just compare the code i made for you with yours.

Carlos Z

@thecil ok i see, i removed the semi colon and that error went away but now im getting another error for other functions, parser error : expected ā€˜(’ but got identifier function …
I get this error for most of the internal and view functions, cant seem to find the answer on stack overflow. what does this mean ? is it a syntax error or have i made a fundamental solidity function mistake?. Thanks for your help

1 Like

Please provide an screenshot about the error you are facing :face_with_monocle:

Carlos Z

this is my solution for multisig wallet

pragma solidity 0.7.5;
pragma abicoder v2;
contract Multisig {
    
address private owner;  

    struct Wallet {
        uint balance;
        address[3] addresses;
    }
    
    Wallet[] private wallets;
    address person1 = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
    address person2 = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;
    address person3 = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db;
    address[3] addr=[person1, person2, person3]; // 
    mapping (address => bool) unlock2; // bool for authorized(sign)
    
    modifier walletOwners(){
        require (msg.sender == person1  || msg.sender == person2 || msg.sender == person3, "you are not authorize");
        _;
    }
    constructor(){
        owner = msg.sender; //owner created from constructor 
        Wallet memory wallet = Wallet(0, addr);
        wallets.push(wallet); // wallet created from constructor
    }
    
    
    function deposit() public payable {// any person can deposit 
        wallets[0].balance += msg.value;
        
    }
    
    function viewWallet() public view returns(Wallet memory) { // view wallet owner addresses and balance 
       return wallets[0];
    }
    
    function withdraw(uint amount) public walletOwners returns(uint){ // only wallet owners can withdraw
        require (wallets[0].balance > amount, "The wallet does not have sufficent funds");
        require (unlock2[person1] == true && unlock2[person2] == true  || unlock2[person1] == true &&  
        unlock2[person3] == true || unlock2[person3] && unlock2[person2] == true, 
        "This is not permited we need to get two of three permissions" );// we need at least two wallet owners authorization
        uint toTransfer = amount;
        wallets[0].balance -= amount;
        msg.sender.transfer(toTransfer);
        unlock2[person1] = false; unlock2[person2] = false; unlock2[person3] = false; // for every withdraw we need owners authorization
        assert(wallets[0].balance >= 0);
        return toTransfer;
    }
   
    function getUnlock() public walletOwners { //to sign  for only wallet owners
        require(unlock2[msg.sender] ==false, "Sorry you had already signed");
        unlock2[msg.sender] = true;
    }
    
    function viewUnlock() public view returns(bool, bool, bool ) { // view signs 
        return (unlock2[person1], unlock2[person2], unlock2[person3] );
    }
    
    
}
1 Like

Hello guys!
This project was an amazing one and I have learned a lot from just doing it and trying my best! :smiley: I was watching until the second video and after that I went for a full try on this and it actually went pretty well :raised_hands:

I have created one ā€œownable.solā€ and alse one ā€œMultisig_Project.solā€.

Ownable.sol:

pragma solidity 0.7.5;

contract Ownable {
    address internal owner;
    
    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }
    
    constructor(){
        owner = msg.sender;
    }
    
}

Multisig_Project.sol:

pragma solidity 0.7.5;
import "./ownable.sol";

contract MultisigWallet is Ownable {
    address[] public owners;
    
    uint requiredApprovals;
    
    mapping(address => uint) balance;
    mapping(address => mapping(uint => bool)) approvals;
    
    constructor(uint _requiredApprovals){
        requiredApprovals = _requiredApprovals;
    }
    
    struct Transfer {
    address payable to;
    uint amount;
    uint approveCount;
    bool isActive;
    }
    
    Transfer[] transferRequests;
    
    event depositDone (uint amount, address indexed receivedFrom);
    event transferDone (uint amount, address indexed transferredTo);
    event transferRequestCreated (uint amount, address indexed createdBy, address indexed sentTo);
    event transactionApprovalDone (address indexed approvedBy);
    
    function deposit() public payable returns (uint){
        balance[owner] += msg.value;
        emit depositDone (msg.value, msg.sender);
        return balance[owner];
    }
    
    function setOwner(address _ownerAddress) public onlyOwner {
        bool isAlreadyOwner;
        
        for(uint i=0;i<owners.length;i++) {
            if(_ownerAddress == owners[i]) {
                isAlreadyOwner = true;
                break;
            }
            else {
                isAlreadyOwner = false;
            }
        }
        
        require(isAlreadyOwner == false, "This address is already a owner.");
        
        owners.push(_ownerAddress);
    }
    
    function transferRequest(address payable _to, uint _amount) public returns (uint){
        bool isOwner;
        
        for(uint i=0;i<owners.length;i++) {
            if(msg.sender == owners[i]) {
                require(balance[owner] >= _amount, "Insufficient amount.");
                balance[owner] -= _amount;
                Transfer memory _transfer = Transfer(_to, _amount, 0, true);
                transferRequests.push(_transfer);
                emit transferRequestCreated (_amount, msg.sender, _to);
                isOwner = true;
                break;
            }
            else {
                isOwner = false;
            }
        }
        
        require(isOwner == true, "Only owners can make transfer requests.");
        
        return balance[owner];
    }
    
    function approveTransfer(uint _id) public returns (uint){
        require(transferRequests[_id].isActive != false, "This transfer has already been made.");
        require(approvals[msg.sender][_id] != true, "This approval has already been made.");
        
        bool isOwner;
        
        for(uint i=0;i<owners.length;i++) {
            if(msg.sender == owners[i]) {
                if(approvals[msg.sender][_id] != true) {
                    approvals[msg.sender][_id] = true;
                    transferRequests[_id].approveCount += 1;
                    emit transactionApprovalDone (msg.sender);
                    isOwner = true;
                    break;
                }
                else {
                    isOwner = true;
                    break;
                }
            }
            else {
                isOwner = false;
            }
        }
        
        require(isOwner == true, "Only owners can approve transfer requests.");
        
        if(transferRequests[_id].approveCount == requiredApprovals) {
            transfer(transferRequests[_id].to, _id);
            transferRequests[_id].isActive = false;
        }
        
        return transferRequests[_id].approveCount;
    }
    
    function transfer(address payable _to, uint _id) internal returns (uint){
        _to.transfer(transferRequests[_id].amount);
        emit transferDone (transferRequests[_id].amount, transferRequests[_id].to);
    }
    
}

thank you for your feedback :smiling_face_with_three_hearts:

2 Likes

MultiSigWallet.sol

pragma solidity 0.7.5;
pragma abicoder v2;
import "./SafeMath.sol";

contract MultiSigWallet {

    uint contractBalance;
    address[] public owners;
    uint numOfApprovalsRequired;

    struct TransferRequest {
	    uint index;
	    address payable receiver;
	    uint amount;
	    uint countApproved;
	    bool sent;
    }

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

    modifier onlyOwners(){
        
        uint i;
        bool isOwner = false;
        
        for (i=0; i < owners.length; i++){
            if(owners[i] == msg.sender) {
                isOwner = true;
                break;
            }
        }
        
        
        require(isOwner, "This function is restricted to Contract Owners");
        _; //Continue execution
    }

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

    TransferRequest [] public allTransferRequests;

    event transferFunds(uint, address);
    event depositedToContract(uint, address);

    //anyone can deposit ether to the contract
    function deposit() public payable {
	    
	    contractBalance = SafeMath.add(contractBalance,msg.value);
		emit depositedToContract(msg.value, msg.sender);
    }

    //anyone of the owners can initiate a transfer request - amount and to what address
    //transfer occurs when number of approvals is met

    function initiateTransfer(address payable _recipient, uint _amount) public payable onlyOwners{
	    
	    require(address(msg.sender).balance >= _amount, "Balance not sufficient");
	    require(msg.sender != _recipient, "Don't transfer money to yourself");

		//create new transaction and store in array
		TransferRequest memory newTransferRequest;
        
        newTransferRequest.index = allTransferRequests.length;
        newTransferRequest.receiver = _recipient;
        newTransferRequest.amount = _amount;
        newTransferRequest.countApproved = 0;
        newTransferRequest.sent = false;
        
        
        createNewTransferRequest(newTransferRequest);
    }

	function createNewTransferRequest(TransferRequest memory _newRequest) public {
	    allTransferRequests.push(_newRequest);
	}
	
    function getContractBalance() public view returns (uint) {
        return address(this).balance;
    }
    
    function getRequestDetails(uint index) public view returns (address recipient, uint amount, uint timesApproved, bool sent){
        return (allTransferRequests[index].receiver, allTransferRequests[index].amount, allTransferRequests[index].countApproved, allTransferRequests[index].sent);
    }
    
    function approveTransaction(uint index) public onlyOwners {
	    
	    
	    //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.
	    
	    if ((approvals[msg.sender][index] != true) && (allTransferRequests[index].sent == false)) {
	        
	        //Set your approval for one of the transfer requests.
	        allTransferRequests[index].countApproved++; //Need to update the Transfer object.
	    
	        approvals[msg.sender][index] = true;        //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.
	    
	    if ((allTransferRequests[index].countApproved >= numOfApprovalsRequired) && (allTransferRequests[index].sent == false)){
	        
	        uint previousContractBalance = address(this).balance;
		    contractBalance = SafeMath.sub(previousContractBalance, allTransferRequests[index].amount);
		    
		    allTransferRequests[index].receiver.transfer(allTransferRequests[index].amount);

		    assert(address(this).balance == contractBalance);
		    
		    emit transferFunds(allTransferRequests[index].amount, allTransferRequests[index].receiver);
		    allTransferRequests[index].sent = true;
	    }
	}
	
	function getTransferRequests() public view returns (TransferRequest[] memory){
	    return allTransferRequests;
    }
}
1 Like

@thecil thanks man. Screenshot 2021-03-21 at 16.12.07

as you can see the parser error pertains to the ā€˜getStakedDaiDetails’ view function. If i comment out this function i get the same error for the Internal functions (transferEth etc). Is it a syntax error? or have i made a fundamental solidity function mistake?. All the code for this contract is in the previous messages. Thanks for your help brother

1 Like

image

Try deleting the () for the onlyOwner modifier.

Carlos Z