Project - Multisig Wallet

pragma solidity 0.7.5;
pragma abicoder v2;

contract Wallet {

    address payable owner1;
    address payable owner2;
    address payable owner3;
    uint256 approvalLimit;

    constructor() {
        owner1 = msg.sender;
        owner2 = msg.sender;
        owner3 = msg.sender;
        approvalLimit = 2;
    }

    modifier onlyDeveloper {
        require(msg.sender == owner1);
        _;
    }

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

    struct transfer {
        address sender;
        address recipient;
        uint256 amount;
        bool completed;
        uint256 approvals;
    }

    transfer[] transfers;

    uint256 id = 0;

    mapping(address => uint256) balance;

    mapping(address => mapping(uint256 => bool)) approvalStatus;

    event transferCompleted(address sender, address recipient, uint256 amount, uint256 approvals, bool owner1approval, bool owner2approval, bool owner3approval);

    function setOwners(address payable _owner2Address, address payable _owner3Address) public payable onlyDeveloper returns (address, address) {
        owner2 = _owner2Address;
        owner3 = _owner3Address;
        return (owner2, owner3);
    }

    function getOwners() public view returns (address, address, address) {
        return (owner1, owner2, owner3);
    }

    function setApprovalLimit(uint256 _approvalLimit) public onlyDeveloper returns (uint256) {
        approvalLimit = _approvalLimit;
        require(approvalLimit > 1 && approvalLimit <= 3, "Approval limit must be either two or three.");
        return (approvalLimit);
    }

    function getApprovalLimit() public view returns (uint256) {
        return (approvalLimit);
    }

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

    function withdraw(uint256 _amount) public returns (uint) {
        require(_amount <= balance[msg.sender], "You cannot withdraw more than your balance.");
        msg.sender.transfer(_amount);
        balance[msg.sender] -= _amount;
        return balance[msg.sender];
    }

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

    function transferRequest(address _sender, address _recipient, uint256 _amount) public onlyOwner returns (string memory) {
        require(balance[_sender] >= _amount, "The senders balance is not sufficient.");
        require(_sender != _recipient, "Sender and recipient cannot be the same.");
        transfers.push(transfer(_sender, _recipient, _amount, false, 0));
        uint256 index = transfers.length;
        approvalStatus[owner1][index] == false;
        approvalStatus[owner2][index] == false;
        approvalStatus[owner3][index] == false;
        return "You have successfully submitted your transfer request! Now, please wait for it to be approved by sufficient signatories.";
    }

    function getAllTransfers() public view onlyOwner returns (transfer[] memory) {
        transfer[] memory transfersLogOutput = new transfer[](transfers.length);
        for (uint256 i = 0; i < transfers.length; i++) {
        transfersLogOutput[i] = transfers[i];
        }
        return transfersLogOutput;
    }

    function getSingleTransfer(uint256 _index) public view onlyOwner returns (address sender, address recipient, uint256 amount, bool completed, uint256 approvals, bool status) {
        return (transfers[_index].sender, transfers[_index].recipient, transfers[_index].amount, transfers[_index].completed, transfers[_index].approvals, approvalStatus[msg.sender][_index]);
    }

    function approveTransfer(uint256 _index) public onlyOwner returns (string memory) {
        if(approvalStatus[msg.sender][_index] == true){
            return "You have already approved this transfer.";
        }
        else if(transfers[_index].completed == true){
            return "This transfer has already been completed.";
        }
        else if(transfers[_index].approvals < (approvalLimit - 1)){
            approvalStatus[msg.sender][_index] = true;
            transfers[_index].approvals++;
            return "Thank you for your approval! We are now waiting on further signatories for the transfer to be released.";
        }
        else{
            approvalStatus[msg.sender][_index] = true;
            transfers[_index].approvals++;
            id = _index;
            _transfer();
            return "Thank you for your approval! As sufficient signatories have now approved, the transfer has been released.";
        }
    }

    function disapproveTransfer(uint256 _index) public onlyOwner returns (string memory) {
        if(transfers[_index].completed == true){
            return "This transfer has already been completed, so it can no longer be disapproved.";
        }
        else{
            approvalStatus[msg.sender][_index] = false;
            transfers[_index].approvals--;
            return "You have successfully disapproved of this transfer.";
        }
    }

    function _transfer() private {
        balance[transfers[id].sender] -= transfers[id].amount;
        balance[transfers[id].recipient] += transfers[id].amount;
        transfers[id].completed = true;
        emit transferCompleted(transfers[id].sender, transfers[id].recipient, transfers[id].amount, transfers[id].approvals, approvalStatus[owner1][id], approvalStatus[owner2][id], approvalStatus[owner3][id]);
    }

}
1 Like