Project - Multisig Wallet

very nice and simple well done. id just recommend putting a requuire in thr approve functions to make sure a user cant approve twice. and also that a user can tapprove their woen transfer. in the transferToSomeone function you could include a require to prevent users from transfering funds to other wallet owners because it doesnt really make sense. its little things like that but other than this im really really impressed man very very goood attempt without looking at a solution first

you should also make a withdraw function to ket a user withdraw their funds if they wish.

1 Like

This is my answer

pragma solidity >0.8.0;

contract MultisigWallat {
    mapping(address => bool) owner;
    uint256 numberOfApprovel;

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

    uint256 transferRequestNum;
    Transfer[] transferRequests; 

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

    constructor(address[] memory _owners, uint256 _numberOfApprove) {
        for (uint i = 0; i < _owners.length; i++) {
            owner[_owners[i]] = true;
        } 
        numberOfApprovel = _numberOfApprove;
    }

    modifier onlyOwners(){
        require(owner[msg.sender], "Owner only.");
        _;
    }

    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 {
        transferRequestNum++;
        Transfer memory transfer = Transfer({
            amount: _amount,
            receiver: _receiver,
            approvals: 0,
            hasBeenSent: false,
            id: transferRequestNum
        });
        transferRequests.push(transfer);
    }
    
    //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], "this address has been approved!");
        for (uint i = 0; i < transferRequests.length; i++) {
            if (transferRequests[i].id == _id) {
                require(!transferRequests[i].hasBeenSent, "this request has been transferd!");
                transferRequests[i].approvals++;
                approvals[msg.sender][_id] = true;

                if (transferRequests[i].approvals >= numberOfApprovel) {
                    transferRequests[i].receiver.transfer(transferRequests[i].amount);
                    transferRequests[i].hasBeenSent = true;
                }
            }
        } 
    

    }

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

Hi everyone this is my code for the multisig Wallet… i would like to receive some feedbacks, thanks =D

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

contract MultiSigWallet{

address owner;
address approvalAddr1;
address approvalAddr2;
Tx[] listOfTx;
mapping(uint => address) public alreadyConfirmedBy; // used to avoid that an address approves 2 times the same tx

struct Tx {

    address payable to;
    address from;
    uint amount;
    uint id;
    uint numberOfApprovals;
    bool confirmed;
}

constructor(address _addr1, address _addr2) {

    owner = msg.sender;
    approvalAddr1 = _addr1;
    approvalAddr2 = _addr2;

}

modifier allowedAddresses{

    require((msg.sender == owner) || (msg.sender == approvalAddr1) || (msg.sender == approvalAddr2), "Address not allowed to send");
    _; // run the function
}

receive() external payable{}

function deposit() public payable{}

function send(address payable _to, uint _amount) public payable allowedAddresses{

    require( address(this).balance >= _amount );

    Tx memory newTx = Tx(_to, msg.sender, _amount, listOfTx.length, 0, false);

    listOfTx.push(newTx);

}

function approvePendingTx(uint _id) public payable allowedAddresses{

    if((listOfTx[_id].confirmed == false) && (msg.sender != listOfTx[_id].from) && (alreadyConfirmedBy[_id] != msg.sender)){

        listOfTx[_id].numberOfApprovals += 1;
        alreadyConfirmedBy[_id] = msg.sender;

        if(listOfTx[_id].numberOfApprovals == 3){

            listOfTx[_id].confirmed = true;

            require( address(this).balance >= listOfTx[_id].amount );

            listOfTx[_id].to.transfer(listOfTx[_id].amount);

        }

    }

}


function approveAllPendingTx() public payable allowedAddresses{

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

        if(alreadyConfirmedBy[i] != msg.sender){

            if(msg.sender != listOfTx[i].from){

                if(listOfTx[i].confirmed == false){

                    listOfTx[i].numberOfApprovals += 1;

                    alreadyConfirmedBy[i] = msg.sender;

                    if(listOfTx[i].numberOfApprovals == 3){

                        listOfTx[i].confirmed = true;

                        require( address(this).balance >= listOfTx[i].amount, "Insufficient funds");

                        listOfTx[i].to.transfer(listOfTx[i].amount);

                    }

                }

            }
        
        }
    
    }

}

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

function getTxInfo(uint _id) public view returns(Tx memory, uint){
    require(listOfTx.length > 0, "No Pending transactions");

    return (listOfTx[_id], listOfTx[_id].numberOfApprovals);
}

}

Thanks a lot for the feedback!

I have a question, instead of do a loop to verify if the address is an owner or not, we could not create directly a mapping inside the constructor??

1 Like

Hi everyone, I have a quick question: does anyone know if there is a way of firing a transaction automatically once a specific condition is met?
For example, if you had a state variable set to 0, would it be possible to fire a function when the state variable becomes 3?

1 Like

I wanted to try myself without any help from videos - so maybe missed some things…
Split in 3 files owners.sol transactions.sol and multisig.sol (deployable)
Would appreciate comment…

// owners.sol
contract Owners {

    address[3] internal owners;
    uint8 internal approvalsNeeded;

    modifier onlyOwners {
        require(owners[0] == msg.sender || owners[1] == msg.sender || owners[2] == msg.sender, "Only owners can run this!");
        _;
    }

    constructor(address[3] memory _owners, uint8 _approvalsNeeded) {
        for(uint256 i; i<_owners.length; i++) {
            owners[i] = _owners[i];
        }
        approvalsNeeded = _approvalsNeeded;
    }
}
// transactions.sol
import "./owners.sol";
abstract contract Transactions is Owners {
    uint256 balance = 0;
    struct Transaction {
       address to;
       uint256 amount;
       bool processed;
    }
    mapping(uint256=>Transaction) transactions;
    mapping(uint256=>mapping(address=>bool)) pendingTransactions;
    uint256 txid;

    event WidthdrawalEventStarted(uint256 indexed txid, address indexed to, uint256 amount);
    event WidthdrawalEventFinished(uint256 indexed txid, address indexed to, uint256 amount);

    modifier onlyUnprocessedTx(uint256 _txid) {
        require(transactions[_txid].processed==false, "Transaction already processed");
        _;
    }

    function checkOwnerConfirmations(uint256 _txid) private returns (bool){
        uint8 approvalNo = 0;
        for (uint8 i=0; i<owners.length; i++) {
            if(pendingTransactions[_txid][owners[i]] == true) {
                approvalNo += 1;
            }
        }
        if(approvalNo >= approvalsNeeded) {
            return true;
        }
        return false;
    }

    function widthdraw(address _to, uint256 _amount) public onlyOwners {
        txid += 1;
        transactions[txid] = Transaction(_to, _amount, false);
        pendingTransactions[txid][msg.sender] = true;
        emit WidthdrawalEventStarted(txid, _to, _amount);
    }

    function signTransaction(uint256 _txid) public onlyOwners onlyUnprocessedTx(_txid){
        if(pendingTransactions[_txid][msg.sender]==true){
                revert("You're already confirmed");
        }
        pendingTransactions[_txid][msg.sender]=true;
        if(checkOwnerConfirmations(_txid)) {
            confirmTransaction(_txid);
        }
    }

    function confirmTransaction(uint256 _txid) private onlyOwners onlyUnprocessedTx(_txid){
        payable(transactions[_txid].to).transfer(transactions[_txid].amount);
        balance -= transactions[_txid].amount;
        transactions[_txid].processed = true;
        emit WidthdrawalEventFinished(_txid, transactions[_txid].to, transactions[_txid].amount);
    }

    function checkBalance() public view returns (uint256){
        return balance;
    }
}
// multisig.sol
import "./transactions.sol";
contract MultiSigWallet is Transactions {

    constructor(address[3] memory _owners, uint8 _approvalsNeeded) Owners(_owners,_approvalsNeeded){}

    receive() external payable {
            balance += msg.value;
    }
}
1 Like

Hi everyone, these are my contracts. I haven’t looked at the final code video yet. I’m sure there are things that need to be modified. I would really appreciate some feedback. Thanks in advance.

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.5;

contract multiOwners {

    mapping(address => bool) Owners;

    function setOwner(address _owner) internal {
        Owners[_owner] = true;
    }

    modifier onlyOwners{
        require(Owners[msg.sender] == true, "address must be an owner");
        _;
    }
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.7.5;
pragma abicoder v2;

import "./multiOwners.sol";

contract multiSig is multiOwners  {

    mapping(address => uint) balance;

    mapping(address => mapping(uint => bool)) approval;
    
    uint approvalsNeeded;

    struct Transfer {
        uint _id;
        uint _approvalsCount;
        address _from;
        address payable _to;
        uint _amount;
    }

    Transfer[] transferRequests;

    constructor (address owner1, address owner2, uint _approvals) {
        setOwner(msg.sender);
        setOwner(owner1);
        setOwner(owner2);
        approvalsNeeded = _approvals;
    }
    
    function addBalance() public payable returns (uint) {
        balance[msg.sender] += msg.value;
        return balance[msg.sender];
    }
   function transferRequest(address payable _to, uint _amount) public onlyOwners {
        require(balance[msg.sender] >= _amount, "you don't have enough balance");
        
        if (transferRequests.length == 0) {
            transferRequests.push(Transfer(0, 1, msg.sender, _to, _amount));
        } else {
            uint _lastId = transferRequests[transferRequests.length -1]._id;
            uint _newId = _lastId += 1;
            transferRequests.push(Transfer(_newId, 1, msg.sender, _to, _amount));
        }
    }

    function _findTransfer (uint _transferId) internal view onlyOwners returns (Transfer memory) {
        require(transferRequests.length > 0, "There are no transfer requests yet, start a transfer request first");
        for (uint i = 0; i < transferRequests.length; i++) {
            if (transferRequests[i]._id == _transferId) {
                return (transferRequests[i]);
            } else {
                revert();
            }
        }
    }

    function initiateTransfer(uint _transferId) public onlyOwners {
        Transfer memory transferFound = _findTransfer(_transferId);
        require(transferFound._approvalsCount >= approvalsNeeded, "you don't have enough approvals");
        transferFound._to.transfer(transferFound._amount);
        balance[msg.sender] -= transferFound._amount;
    }

    function approveTransfer(uint _transferId) public onlyOwners {
        require(approval[msg.sender][_transferId] = false, "you already approved this tranfer");
        Transfer memory transferFound = _findTransfer(_transferId);
        approval[msg.sender][_transferId] = true;
        transferFound._approvalsCount += 1;
    }

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

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

}
1 Like

Here is my version of the wallet.

pragma solidity 0.8.12;

contract Wallet {    

    // state variables
    uint public approvalsNeeded;
    address[] public owners;

    struct RequestData {
        address recipient;
        uint amount;
        uint approvals;
    }
    
    RequestData[] transferRequests;
    mapping(uint => address[]) approvers;    
    
    //events
    event depositDone(uint amount, address depositedFrom);
    event transferRequestCreated(uint requestId);
    event fundsTransferred(address recipient, uint amount);

    //loop the array of owners and return true if _address is one of them
    function isOwner(address _address) public view returns (bool) {
        for (uint i = 0; i < owners.length; i++) {
            if (owners[i] == _address) { return true; }   
        }
        return false;
    }

    modifier onlyOwner {
        require(isOwner(msg.sender), "You are not the owner");
        _;
    }

    //the main code
    constructor(address[] memory _owners , uint _approvalsNeeded) {
        owners = _owners;
        approvalsNeeded = _approvalsNeeded;
    }    

    function deposit() public payable {
        emit depositDone(msg.value, msg.sender);
    }

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

    function getApprovers(uint _id) public view returns(address[] memory) {
        return (approvers[_id]);
    }

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

    function createTransferRequest(address _recipient, uint _amount) public onlyOwner {        
        transferRequests.push(RequestData(_recipient, _amount, 1));
        uint id = transferRequests.length - 1;
        approvers[id].push(msg.sender);
        emit transferRequestCreated(id);
    }

    function approve(uint _id) public onlyOwner {
        require(transferRequests[_id].approvals < approvalsNeeded); // funds already transferred

        bool notApprovedBySender = true;
        for (uint i = 0; i < approvers[_id].length; i++) {            
            if (approvers[_id][i] == msg.sender) {
              notApprovedBySender = false;
              break;  
            }   
        }

        if (notApprovedBySender) {
            approvers[_id].push(msg.sender);
            transferRequests[_id].approvals++;
        }

        if (transferRequests[_id].approvals == approvalsNeeded) {
            _transfer(transferRequests[_id].recipient, transferRequests[_id].amount);
        }
    }

    function _transfer(address _recipient, uint _amount) private {
        payable(_recipient).transfer(_amount);
        emit fundsTransferred(_recipient, _amount);
    }
}

Here my solution, i used much loops and that is more gas :sob: i need to practice more, i add a scram function too, and i see something interesting, when you use the scram function but you deposit again, you lost your funds and can’t be recovered :anguished:

pragma solidity ^0.8.0;

contract MultiSign {

    // Global variables
    address owner;
    uint public balance;

    // Structs
    struct User {
        string name;
        address addr;
        bool exist;
    }

    struct Request {
        address to;
        uint approves;
        address[] addr_decisions;
        uint amount;
        bool exist;
    }

    // Arrays
    User[3] public users;
    Request[] public requests;

    // Constructor
    constructor(string memory _nameSuperOwner){
        owner = msg.sender;
        users[0] = User({ name : _nameSuperOwner, addr : msg.sender, exist : true});
    }

    // Modifiers
    modifier onlySuperOwner {
        require(msg.sender == owner,'You are not the superOwner');
        _;
    }

    modifier onlyOwners {
        bool _owner = false;
        for(uint i=0; i < users.length; i++){
            if(users[i].addr == msg.sender) _owner = true;
        }
        require(_owner,'You are not owner');
        _;
    }

    modifier isNotApproved(uint _idTransaction){
        require(requests[_idTransaction].exist,'Request not exist');
        bool _isApproved = false;
        for(uint i=0; i < requests[_idTransaction].addr_decisions.length; i++){
            if(requests[_idTransaction].addr_decisions[i] == msg.sender) _isApproved = true;
        }
        require(!_isApproved, 'This request already approved for this address');
        _;
    }

    modifier requestAproved(uint _idTransaction) {
        require(requests[_idTransaction].approves >= 2,'This transaction need more approves');
        _;
    }

    // Functions
    function addNewOwner(uint _idOwner, string memory _name, address _addr) onlySuperOwner public {
        require(!users[_idOwner].exist,'This id owner already exist');
        require(users.length <= 3,'You cant add more Owners');
        bool existAddr = false;
        for(uint i = 0; i < users.length; i++){
            if(users[i].addr == _addr) { existAddr = true; break; }
        }
        require(!existAddr,'This address already exist');
        users[_idOwner] = User({ name : _name, addr : _addr, exist : true });
    }

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

    function approve(uint _idRequest) onlyOwners isNotApproved(_idRequest) public {
        requests[_idRequest].approves += 1;
        requests[_idRequest].addr_decisions.push(msg.sender);
    }

    function addRequestTransfer(address _to, uint _amount) onlyOwners public {        
        address[] memory temp_array = new address[](1);
        temp_array[0] = msg.sender;
        requests.push(Request({
            to : _to,
            approves : 1,
            addr_decisions : temp_array,
            amount : _amount,
            exist : true
        }));
    }

    function transfer(uint _idTransaction) public onlyOwners requestAproved(_idTransaction) {
        address _to = requests[_idTransaction].to;
        uint _amount = requests[_idTransaction].amount;
        require(_amount <= balance,'Dont have suficent balance');
        payable(address(_to)).transfer(_amount);
        balance = balance - _amount;
        delete requests[_idTransaction];
    }

    function scram() public payable onlySuperOwner {
        selfdestruct(payable(address(owner)));
    }

}
1 Like

hey @ismael_zamora. I would be so harsh on yourslef. This is a great solution for someone just starting off. If your concious about loops you can solve these with mappings. So in your modifierisNotApproved() you dont actually need this. you should only ever use a modifier whenever you have the same bit of logic repeated twice in multiople functions. the purpose of the modifier is then to reduce this code so that you only have to write it once and then just place the modiifer on the function you want to enforce the code on.

For your function sNotApproved() you dont need to loop. as long as you know the transaction id or the index of the transacton in your requests array, to check is that owner is approved you can just directly index the array and check the approvals property of that item. So instead of having an array of addresses that is filled with owners who have approved a transaction i would make a global mapping called approvals which is dependant on the txId something like

mapping(address => mapping(uint =>bool)) approved

then every time someone approves a transaction you you set the mapping to true like follows

approved[msg.sender][tx_Id] = true

then whenever someone tries to approve the tx you make a requirement that the approved mapping for that owner for that particular tx id is false

require(approved[msg.sender][tx_Id] == false, "already approved this tx")

the same idea could be applied to your onlyOwners modifier. you could make a mapping called isOwner and set any owner to true. so in the addOwner function something like

isOwner[msg.sender] = true

and also in the same function require that the person being added isnt already and owner

require(isOwner[msg.sender] == false, "cannot have duplicate owners")

Now from my experience having the for loop to catch duplucate owners is actually less expensive for gas in the case where you only have one or two owners. but if your wallet could have like 10 plus owners then it becomes more efficent because the worst case scenario is that the you would have to loop over the entire array just to find out that the owner you adding is unique.

Great work tho i love your solution

1 Like

hey @Giupi. nice solution. I have a few modications you could make. the first one is for your _findTransfer() function. You dont need to loop through the transfers array and return the index that matches the id you pass in. instead there is a much simpler way. since your using abiencoder v2, then alls you need to do is return the transfers array with the index of the transfer you want to return

function _findTransfer (uint _transferId) internal view onlyOwners returns (Transfer memory) {
       return TransferRequests[_id]
    }

also great that your trying to optimise by using different function visibilities such as internal. In fact if this was me i would put all of the functions as internal. Because the only people that will be using this wallet are in fact the wallet owners so there is a high gaurantee that noone will be inetracting with this contract externally. but its up to you.

Also for your initiate transfer function i see your passing in an txId. You would be better off making a global txId var initiall set to 0, that gets incremented after a transfer is initiated. You should event look into openzeppelins counter.sol smart contract which is specifically designed for this purpose.

Other than this well done great solution

@pegg great solution as of now it look spretty good to me

1 Like

Thanks - let’s now see this DAPP programming 201 now :slight_smile:

1 Like

see you there. you will super enjoy it

1 Like

hello ! i really appreciated your comments :slight_smile: :heart:

i have some question right know :

Āæ what happens if in one case i wanna get all the approves of one account ?
Āæ can i do that with a mapping ?

i am thinking converting the mapping to array and after that returns in a function, but i don’t know if that is possible, Āæ and if is possible , is a good way to store for example 2k into a mapping, for example the transactions requests ?
and Āæ is possible to return 2k of transactions in a public view function ? i thinking the public view has limits for returned values

Now from my experience having the for loop to catch duplicate owners is actually less expensive for gas in the case where you only have one or two owners. but if your wallet could have like 10 plus owners then it becomes more efficient because the worst case scenario is that the you would have to loop over the entire array just to find out that the owner you adding is unique.

i thinking the same, one thing is 3 iterations in a for loop , and another is 2k iterations because thats gonna be more expensive, if the info that i wanna store is a lot may be the best way is use a mapping

know i am thinking what happens if i have 10k of transactions, for example in real cases that can be possible, Āæ how exactly i can return 10k of transactions ?, the only way that i thinking right know for more speed response is storing the transactions in a db like mongodb after the functions is called, for example :

function transfer => emit transfer => websocket capture the transfer in the backend => store in mongodb => returns all the transfers for the final user

i dont know if this is the best idea but i really dont know if it possible to return 10k of data in a public view function and what speed is gonna be

with the method that i thinking ( storing in db ) when the final user wants to see the transfers has more speed of response, obviously is not a decentralized method to get him but is more fast if the case is 10k of transactions

if you have some ideas about this please let me know, i really appreciated your comments and sorry if you cant understand my english, i am still practice :slight_smile: :heart:

1 Like

thanks, @mcgrane5 I really appreciate your feedback, I’ll go ahead and make some improvements on the code. :grinning:

1 Like

Ownedby Contract

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

contract OwnedBy{

    address owner1 = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
    address owner2 = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;
    address owner3 = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db;

    modifier ContractOwners{
        require(msg.sender == owner1 || msg.sender == owner2 || msg.sender == owner3);
        _;
    }


}

Multisig Contract

// SPDX-License-Identifier: MIT
pragma solidity 0.7.5;
import "./OwnedBy.sol";

contract MultisigWallet is OwnedBy{

    mapping (address => mapping(uint => bool)) ApprovalOf;
    mapping (address => mapping(address =>mapping(address =>uint))) BalanceOf;
    mapping (address => uint)balanceOf;

    address _Owner1;
    address _Owner2;
    address _Owner3;

    uint Limit;

   
    constructor(address _owner1, address  _owner2 , address  _owner3, uint _Limit) ContractOwners{
        require( _owner2 != _owner3 || _owner1 != _owner2 || _owner1 != _owner3);
        require( _Limit == 2 || _Limit == 3);
        

        _Owner1 = _owner1;
        _Owner2 = _owner2;
        _Owner3 = _owner3;

        Limit = _Limit;

    }

    struct Transfer{
        uint TransferID;
        address Sender;
        address Recipient;
        uint Amount;
    
    }
  
    Transfer[] TransferRequests;
   
    function Deposit() public payable {

        BalanceOf[owner1][owner2][owner3] += msg.value;

    }

    function Transact(uint _id, address _Recipient,uint _amount) public ContractOwners {
        require(_Recipient != owner1);
        require(_Recipient != owner2);
        require(_Recipient != owner3);

        if(Limit == 2){
            require(ApprovalOf[_Owner1][_id] == true && ApprovalOf[_Owner2][_id] == true  ||
             ApprovalOf[_Owner1][_id] == true && ApprovalOf[_Owner3][_id] == true || 
             ApprovalOf[_Owner2][_id] == true && ApprovalOf[_Owner3][_id] == true);
        }
        else if( Limit == 3){
            require( ApprovalOf[_Owner1][_id] == true && ApprovalOf[_Owner2][_id] == true && ApprovalOf[_Owner3][_id] == true);
        }

        _Transfer(_id, _Recipient, _amount);
    
    }

    function Approval1(uint id, bool _Approval) public ContractOwners{
        require(msg.sender == _Owner1);

        ApprovalOf[_Owner1][id] = _Approval;
        
    }

    function Approval2(uint id, bool _Approval) public ContractOwners{
        require(msg.sender == _Owner2);

        ApprovalOf[_Owner2][id] = _Approval;
     
    }

    function Approval3(uint id, bool _Approval) public ContractOwners{
        require(msg.sender == _Owner3);

        ApprovalOf[_Owner3][id] = _Approval;
     
    }

    function _Transfer(uint _id, address _Recipient, uint _amount) private{
        require( BalanceOf[owner1][owner2][owner3] >= _amount, "Balance not enough");

        BalanceOf[owner1][owner2][owner3] -= _amount;
        balanceOf[_Recipient] += _amount;

         msg.sender.transfer(_amount);

        Transfer memory newTransfer = Transfer(_id, msg.sender, _Recipient , _amount);
        TransferRequests.push(newTransfer);

    }

    function GetTransferDetails(uint index) public view returns(uint, address, address, uint){
        Transfer memory TransferToReturn = TransferRequests[index];
        return (TransferToReturn.TransferID, TransferToReturn.Sender, TransferToReturn.Recipient, TransferToReturn.Amount);
    }

    function GetBalance() public view returns(uint){

        return BalanceOf[owner1][owner2][owner3];

    }

    function GetReceiverBalance() public view returns(uint){

        return balanceOf[msg.sender];

    }
    

}

  

Any feedback would be appreciated :grinning:

1 Like

will get back to you soon

1 Like

I did it my way but works perfectly.

pragma solidity ^0.8.0;
contract MultiSigWallet{
    event deposited(address who, uint256 amount, uint256 walletBalance);
    event TxApproved(uint256 indexed txId, string name, uint256 amount, address recipient ,uint256 approvals);
    mapping(address => bool) isOwner;
    uint256 requiredApprovals;
    uint txCount=0;
    constructor(address owner2, address owner3){
        
        isOwner[msg.sender]=true;
        isOwner[owner2]=true;
        isOwner[owner3]=true;

        requiredApprovals=2;

    }

    struct transaction{
        uint256 txId;
        string  name;
        address payable recipient;
        uint256 amount;
        address creator;
        uint256 approvals;
    }
    transaction [] unapprovedTransactions;
    modifier onlyOwner{
        require(isOwner[msg.sender]==true);
        _;
    }
    function getWalletBalance() public onlyOwner view returns(uint256){
        return address(this).balance;
    }

    function isitOwner(address ad) public view returns(bool){
        return isOwner[ad];
    }
    function deposit() public payable {
        emit deposited(msg.sender, msg.value , address(this).balance);

    }

    function newTransaction(string memory _name,address payable _recipient, uint256 _amount) public onlyOwner{
        unapprovedTransactions.push(transaction(txCount, _name, _recipient, _amount,msg.sender,1));
        txCount++;
    }
    function getUnnaprovedtx() public onlyOwner view returns( transaction[] memory){
        return unapprovedTransactions;
    }

    function approveTx(uint256 txId) public onlyOwner{
        require(msg.sender!=unapprovedTransactions[txId].creator);
        unapprovedTransactions[txId].approvals ++;
        _transact(txId);


    }
    function _transact(uint256 txId) private{
        require(unapprovedTransactions[txId].approvals>=requiredApprovals);
        unapprovedTransactions[txId].recipient.transfer( unapprovedTransactions[txId].amount);
        delete unapprovedTransactions[txId];

    
     }
}
1 Like