Build a Web3 Microfinance support group system (SACCO) using Solidity / Smart contract

Photo by Tezos on Unsplash

Build a Web3 Microfinance support group system (SACCO) using Solidity / Smart contract

Smart Contracts, what are they?

Before we build anything we need to first understand what Smart Contracts are, and what they do. Smart Contracts are simply programs of code that are stored on the block chain which run whenever a certain set of conditions are met. They are typically immutable and once deployed to the block chain, they are unchangeable.

How do Smart Contracts work?

Take it as just a set of coded computer instructions on the block chain which will run when a set of certain conditions are made. These conditions are meant to trigger the execution of the smart contract. If you ask what kind of instructions can these be?

Well, to get a more clear explanation, take an example of a smart contract that distributes money between two parties when a certain condition is made. Maybe Person A has a house they are selling and when person B raises the exact money required for the house, the smart Contract is triggered to transfer the money between the two parties.

Now if you can see the whole picture, there are lots of benefits from such a system from the usual banking system we all know. Usually, the house transaction would not be that instant and fast. You would have to first go through lots legal paperwork, lots of middlemen, lawyers etc. With a smart contract, you'd complete all that instantly, party to party without banks and lawyers.

So, that's a brief explanation of the simple theory of Smart Contracts. Fortunately, we are going to create one today.

If you're as excited as I am, then lets get into it ;)

What we are going to build

Well I come from Africa/Uganda and when you stay around for some good time, you'll probably here the term "SACCO". Basically SACCOs are user-owned financial institutions that offer both savings and credit services to their members. Members of these financial institutions can be both net savers and net borrowers. These are sometimes setup by the bank and most of them are self managed by people locally in their neighborhood.

If want to know more about how SACCOs work, you can read through below

Building the Smart Contract

We are going to use Solidity to build the Smart Contract and to keep things very simple, we are going to use Remix which is just an embedded IDE in chrome. Just to make it very easy for everyone to follow up because there is not software installation required.

Open this link in your browser here

Folder structure

On the left side of your browser tab, you'll see a folder structure that contains contracts, tests, scripts and README.txt Our main focus is going to be in the contracts folder.

Create a file called SACCO.sol under the contracts folder. This is where we are going to code our smart contract

Well start by declaring a solidity version system and SPDX license code

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

Next, well declare some variables that we'll later use in the smart contract.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract MySacco {
    address public admin; // admin address
    uint256 public count; // member count
    uint256 public accountBalance = 0; // sacco balance amount
    uint256 private joinFee = 15; // sacco join fee amount
    uint256 private maxMembers = 10; // maximum number of members on the sacco
}

Lets look into these variables:

  • admin, which will be the main minter of the smart contract. This will be set when the smart contract is deployed to the block chain. The owner of the address that will deploy the smart contract will be the admin
  • count: which will be the number of members in the SACCO
  • accountBalance: The amount of funds currently stored on the Smart Contract
  • joinFee: The amount of funds any user interested in joining the SACCO will be required to pay. Notice that we have set it in >private. That's because we don't want this variable to be accessible outside the smart contract
  • maxMember: The maximum number of poeple required on the smart contract

In order to update the admin address, we define a constructor function. This function will execute ONCE when the smart contract is deployed to the block chain, with every single line of code defined inside it.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract MySacco {
    address public admin; // admin address
    uint256 public count; // member count
    uint256 public accountBalance = 0; // sacco balance amount
    uint256 private joinFee = 15; // sacco join fee amount
    uint256 private maxMembers = 10; // maximum number of members on the sacco

    constructor() {
        admin = msg.sender;
    }
}

We use the msg.sender to set the admin address to the smart contract. This contains the address info of the minter

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract MySacco {
    address public admin; // admin address
    uint256 public count; // member count
    uint256 public accountBalance = 0; // sacco balance amount
    uint256 public joinFee = 15; // sacco join fee amount
    uint256 private maxMembers = 10; // maximum number of members on the sacco

    constructor() {
        admin = msg.sender;
    }

    // modifiers
    modifier isAdmin() {
        require(msg.sender != admin, "You are not permitted to update this fee");
        _;
    }

    function setJoinFee(uint256 _fee) public isAdmin {
        joinFee = _fee;
    }

}

After setting the minter / admin address, we want to give them the ability to set the join fee of the contract, so we create the setJoinFee function that will accept a new fee that the admin may want to update. But first, we have to provide restricted permission to whom is able to set this fee. We have to verify that the person attempting to update the fee is the admin and this is done through the isAdmin modifier function.

    mapping(address => Member) public member;
    Member[] public allMembers;

    struct Member {
        address memberAddress;
        string name;
        int age;
        uint256 balance;
    }

That being done, we want to get the basic structure of what the member information looks like. And this is defined in the struct. In this, we have the member's address, name, age and account balance information. The struct has properties similar to a dictionary datatype in python. In order to change or query any info in the struct, we use a mapping which will accept the address in the Member.

    modifier validateAmount(){
        require(msg.value != joinFee);
        _;
    }

       function join(
            address _memberAddress, 
            string memory _name, 
            int _age
       ) public payable validateAmount() {
            count += 1;
            updateBalance(msg.value);
        return members.push(Member(_memberAddress, _name, _age, 0));
    }

    function updateBalance(uint256 _balance) private {
        accountBalance += _balance;
    }

In order for the SACCO to get new members, we need to declare a join function that will allow new members pay to join the SACCO. Obviously, we have to validate the amount of ether they are paying is equal to the required joinFee of the smart contract. And this is done through the validateAmount modifier. On successfull payment, we mint the paid fee to the account balance using the updateBalance function. Remember, we have to keep track of the member count and we have to validate it whenever a new member joins. Hence the count += 1;.

Here is our code so far:

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract MySacco {
    address public admin; // admin address
    uint256 public count; // member count
    uint256 public accountBalance = 0; // sacco balance amount
    uint256 public joinFee = 15; // sacco join fee amount
    uint256 private maxMembers = 10; // maximum number of members on the sacco

    mapping(address => Member) public member;
    Member[] public allMembers;

    struct Member {
        address memberAddress;
        string name;
        int age;
        uint256 balance;
    }

    constructor() {
        admin = msg.sender;
    }

    // modifiers
    modifier isAdmin() {
        require(msg.sender != admin, "You are not permitted to update this fee");
        _;
    }

    modifier validateAmount(){
        require(msg.value != joinFee);
        _;
    }

    function setJoinFee(uint256 _fee) public isAdmin {
        joinFee = _fee;
    }

    function join(
        address _memberAddress, 
        string memory _name, 
        int _age
    ) public payable validateAmount() {
        count += 1;
        updateBalance(msg.value);
        Member memory newMember = Member(count, _memberAddress, _name, _age, 0, 
    PaymentStatus.Future);
        members[_memberAddress] = newMember;
        allMembers.push(newMember);
    }

    function updateBalance(uint256 _balance) private {
        accountBalance += _balance;
    }
}

We need to keep track of which member is going to be paid, who previously received and who is not. To best structure that information, we'll use the enum data type and we'll call it PaymentStatus. We'll add PaymentStatus field to the Member struct. Also in order to keep track of the number of members in the SACCO, we've given each member an identification number in the struct memberNumber

    enum PaymentStatus { Previous, Next, Future  }

        struct Member {
        uint256 memberNumber;
        address memberAddress;
        string name;
        int age;
        uint256 balance;
        PaymentStatus paymentStatus;
    }

Now we update the join to have an initial default value of Future paid status to whoever initially joins the SACCO.


    function join(
        address _memberAddress, 
        string memory _name, 
        int _age
    ) public payable validateAmount() {
        count += 1;
        updateBalance(msg.value);
        Member memory newMember = Member(count, _memberAddress, _name, _age, 0, 
   PaymentStatus.Future);
        members[_memberAddress] = newMember;
        allMembers.push(newMember);
    }

So far we've covered the join functtionality of the members to SACCO, admin can set and change the join fee, validation and more...

In the next tutorial, we are going to implement the payout functionality of our Smart Contract. Please watch out for it..

Thanks.