Solidity: Multisig Demo using our Bank smart contract

thumbnail

Hello friends, In this post, we will use the multisig (multisignature) concept to approve the Fixed Deposit claims. So far we are using the contract owner to approve the fixed deposit withdrawal claim request. So we will add some employees to our Bank and give them the responsibility to approve the claims. So as it is a continuation of our previous built contracts you can follow our previous post.

Understand Re-entrancy attack by building a Bank Smart Contract in Solidity

Solidity: Fixed Deposit feature in our Bank Smart Contract

MultiSig helps us in adding an extra layer of security which helps in providing confirmations before making any transaction. Here we will add the Multisig concept to our fixed deposit claim approval. You can also try to implement the same in other transactions as well.

Let's start by adding employees to our bank

In our bank contract, we will add a new mapping for employee address to Employee struct which will contain employee status and type, as of now we have l type and 2 status. For function, we will take the employee's address and type it as an argument and the conditions are only the owner can add the employee and the employee should not exist. Also, we have a getting of getting employee details.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "hardhat/console.sol";

contract Bank {
    /**
     * Variable Declaration
     */
    ...

    enum EmployeeStatus {
        NOT_ACTIVE,
        ACTIVE
    }
    enum EmployeeType {
        MANAGER
    }
    struct Employee {
        EmployeeStatus employeeStatus;
        EmployeeType employeeType;
    }
    mapping(address => Employee) employees;
    
    ...

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

    modifier notEmployee(address employeeAddress) {
        require(
            employees[employeeAddress].employeeStatus ==
                EmployeeStatus.NOT_ACTIVE,
            "Employee already exists"
        );
        _;
    }
    modifier employeeExists(address employeeAddress) {
        require(
            employees[employeeAddress].employeeStatus == EmployeeStatus.ACTIVE,
            "Employee does not exists"
        );
        _;
    }


    /**
     * Adding employee
     */

    function addEmployee(
        address employeeAddress,
        EmployeeType empType
    ) external onlyOwner notEmployee(employeeAddress) {
        employees[employeeAddress] = Employee(EmployeeStatus.ACTIVE, empType);
    }

    function employeeDetails(
        address employeeAddress
    ) public view returns (Employee memory) {
        return employees[employeeAddress];
    }
    function removeEmployee(
        address employeeAddress
    ) external onlyOwner employeeExists(employeeAddress) {
        delete employees[employeeAddress];
    }
}

In our FixedDeposit contract, we need to add a new mapping to store the claim confirmations and update our FD struct to store the number of confirmations got. Also, need a variable and getter-setter to store the confirmations needed. To approve the claim we have conditions such as the employee cannot approve already approved claims and claim confirmations should be less than claimConfirmationsNeeded. And inside approve a claim if claimConfirmations is equal to claimConfirmationsNeeded then we will update the status to CLAIM_APPROVED and assign the claim amount.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "./Bank.sol";

contract FixedDeposit {
    enum FDStatus {
        NOT_REQUESTED,
        STARTED,
        CLAIM_REQUESTED,
        CLAIM_APPROVED,
        CLOSED
    }
    struct FD {
        uint256 depositedAmount;
        uint256 noOfYears;
        uint256 lockTime;
        uint256 claimAmount;
        uint256 claimConfirmations;
        FDStatus status;
    }

    mapping(address => mapping(address => bool)) claimConfirmations;

    uint256 private claimConfirmationsNeeded = 2;

    Bank bank;
    
    ...

    modifier canApproveClaim(address account) {
        FD memory fd = fds[account];
        require(
            bank.employeeDetails(msg.sender).employeeStatus ==
                Bank.EmployeeStatus.ACTIVE,
            "You are not employee"
        );
        require(
            fd.status == FDStatus.CLAIM_REQUESTED,
            "canApproveClaim: Claim request does not exists"
        );
        require(
            claimConfirmations[account][msg.sender] == false,
            "canApproveClaim: You have already approved"
        );
        require(
            fd.claimConfirmations < 2,
            "canApproveClaim: Already approved to withdraw"
        );
        _;
    }

    modifier isEmployee() {
        require(
            bank.employeeDetails(msg.sender).employeeStatus ==
                Bank.EmployeeStatus.ACTIVE,
            "Only Employee can approve"
        );
        _;
    }


    function openFD(
        uint256 amount,
        uint256 noOfYears
    ) public payable isEligibleForFD(amount, noOfYears) {
        fds[msg.sender] = FD(
            amount,
            noOfYears,
            block.timestamp + (noOfYears * 31556926),
            0,
            0,
            FDStatus.STARTED
        );

    }


    function approveClaim(address account) public canApproveClaim(account) {
        fds[account].claimConfirmations += 1;
        claimConfirmations[account][msg.sender] = true;
        FD memory fd = fds[account];
        if (fd.claimConfirmations == 2) {
            fds[account].status = FDStatus.CLAIM_APPROVED;
            fds[account].claimAmount = checkReturns(account);
        }
    }
    ...
}

The next part is testing we can use Remix IDE to run our code and check it’s working. We can also write unit testing for the contract and check whether it is working as expected. First, let’s deploy as done in our previous post

npx hardhat deploy — tags bank,fd

I leave the testing part to you as a challenge, if you get stuck please refer to the GitHub code.

Now we have built a contract we can develop a frontend for interacting with the contract for this you can follow the previous post where we built a frontend for ERC20 TOKEN and faucets Dapp.

How to build ERC 20 Token and Faucets Dapp

If you want a separate post for developing a front end, let me know in the comments. We will be back again with the new feature to our Bank smart contract. If you have any suggestions please let me know in the comments.

Thanks for reading this post, if you found this post helpful please share maximum. Will be back with amazing content on Web3. Stay tuned.

Find the code on GitHub

GitHub - CodeWithMarish/cwm-bank-dapp

Also please don’t forget to subscribe to our youtube channel codewithmarish for all web development-related challenges.

Code With Marish | Youtube