Smart Contract for division
Tags: smartcontract, solidity, hardhat, blockchain
Published: 2022-11-01 03:02:11

## Story :sleeping:
In one of my freelancers jobs, I saw that the project was needing another smart contract for divide fees that other contract generates when clients used it (like 10% for dev, 10% for marketing..) and the client was doing it manually transfering to one wallet and then sharing it once a week.

## Use case :book:
So basically, I needed a smart contract to receive values in it's address(without calling an function, just 'receiving' transfers) and then allows specified wallets to withdraw it and costing the less fee as possible to the main function from client.

## Tools used :hammer_and_wrench:
Nowadays, for solidity i'm using 

- [Visual Studio Code](https://code.visualstudio.com/) as code editor
- [Github](https://github.com/) as Source Reposirory
- [Hardhat](https://hardhat.org/) as EVM development evironment
- [Chai](https://www.chaijs.com/ 'Chai') to help on tests


## Solution :thought_balloon:

1. Keep wallets addresses that needs to receive values in the contract and the total of active wallets
2. Store how much each wallet has totally withdraw; With that we can include it on the calculation of how much each wallet can withdraw
3. Block & unblock wallets storing it on total of wallets(when blocked, total of wallets to divide -1, when unblock, total of wallets to divide +1)
4. Store total of value that contract has
5. With the total that the contract has (calculate when receive value and when withdraw value), total of wallets to divide and how much each wallet has withdraw, we can calculate how much an specific wallet can withdraw

## Code + solution + comments :stethoscope:

###### I will break the smart contract code in small peaces and try to explain it 

```javascript
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

// Import this file to use console.log
import "hardhat/console.sol";

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

```
> One of the cool features that I love in hardhat is the possibility to use console.log inside smart contract, for this, we just need to import the "hardhat/console.sol"
Ownable.sol import help us to control who can access specifics functions.
SafeMath.sol import help us in math calculations

```javascript
contract Division is Ownable {

    struct participant {
        uint256 totalReceived;
        bool blocked;
    }

   mapping(address => participant) participants;

   uint256 totalInContract;

   uint256 public totalParticipants;

```
> On the declaration of the contract 'Division' we say that it 'is Ownable' and with that we are able to use functions from Ownable.sol
The struct 'participant' will allow us to know how much an address has received(totalReceived property) and if this address is blocked. How we will do that? In conjunction with the mapping.
Mapping is a list with an key and we used the address as it (1 address -> 1 struct)
totalInContract will let us know how much $ we have in contract to divide
totalParticipants will let us know how many addresses we should divide the $
```javascript
  constructor(address[] memory pDivisors) {
        for (uint256 i = 0; i < pDivisors.length; i++) {
            addParticipant(pDivisors[i]);
        }
    }
```
> In the constructor we expect an list of addresses that will be the owners of divisions

```javascript
    receive() external payable {
        if (msg.value <= 0) return;
        share(msg.value);
    }

    function share(uint256 valueToShare) private {
        totalInContract += valueToShare;
    }

```
> One of the magic tricks is this function 'receive()'. It allow us to do something when the contract receives an value. 
The share function is very simple, we just increment the totalInContract with the received value.(It don't loop all addresses in the mapping, if it did that, the gas cost will be expensiver)

```javascript
    function addParticipant(address participant_) public onlyOwner {
        participants[participant_] = participant(0, false);
        totalParticipants += 1;
    }

```
> This function allows to add an participant(to receive part of division).
The onlyOwner modifier garantees that just the Owner will be able to use this function

```javascript
    function getValueToShareForAddress(address participant_)
        private
        view
        returns (uint256)
    {
        if (participants[participant_].blocked) return 0;

        uint256 valueToShare = totalInContract / totalParticipants;
        return valueToShare - participants[participant_].totalReceived;
    }

```
> This function checks if the user is blocked, and if he's, no cash for him.
One good point is that we calculate the value shared for all, then we use the total received from the address to check how much it can receive
```javascript
    function withdraw() public payable {
        address msgSender = _msgSender();

        uint256 valueToShare = getValueToShareForAddress(msgSender);

        require(valueToShare > 0, 'you dont have funds for withdraw');

        participants[msgSender].totalReceived += valueToShare;

        payable(msgSender).transfer(valueToShare);
    }

```
> This function is for the address that wants to receive his dividens. It uses the function getValueToShareForAddress to get the value and if it's 0 it throws an error. If the user has something to withdraw, then we increment the totalReceived from this address then transfer it to him
```javascript
 function getBalance() external view returns (uint256) {
        return getValueToShareForAddress(_msgSender());
    }

  function getDivisorsQuantity() external view returns (uint256) {
        return totalParticipants;
    }

```
> This function 'getBalance' is where the user knows how much he is able to withdraw
The getDivisorsQuantity, will return how many participants enabled the contract has
```javascript
    function blockParticipant(address participant_) external onlyOwner {
        participants[participant_].blocked = true;
        totalParticipants -= 1;
    }

    function unblockParticipant(address participant_) external onlyOwner {
        participants[participant_].blocked = false;
        totalParticipants += 1;
    }

```
> As the functions name says, it blocks or unblock an address. 
A good point is that just the owner can do this
Another good point is that we always change the totalParticipants

## Testing :test_tube:
To test if everything is working as it should we need to do good tests before deploy our contract. Off course we could deploy to an  testnet and play there(I do it too before mainnet), but writing tests and run it with hardhat is so easy and fast that you gonna love it. 
To run the tests just type ```npx hardhat test``` 
Hardhat will look for .js files in /test folder and execute then


![tests runned 100%](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iqs31ncp2t5jxz1jxnye.png)
> I will make an post just talking about hardhat tests and how it's important

## Finally... Deploy ! :rocket:
Hardhat helps us in this mission too. First, you need to configure what blockchain and network you want to deploy. Check the hardhat.config.js for this.
```javascript 
require('@nomiclabs/hardhat-waffle')
require('dotenv').config()
require('@nomiclabs/hardhat-etherscan')

module.exports = {
    solidity: '0.8.14',
    networks: {
        bsc_testnet: {
            url: `${process.env.BSC_TESTNET_URL}`,
            accounts: [`${process.env.BSC_TESTNET_PRIVATE_KEY}`],
        },
    },
    etherscan: {
        apiKey: 'xxx',
    },
}
```
> Networks section is where you configure the blockchains and networks that you want to deploy. In this case, let's say that we want to deploy on Binance Smart Chain. 
For this I named this blockchain/network as bsc_testnet. 
We need to inform an Url of node to the desired BC/Network. I I usually use free nodes for deploy, you can get an free account on this providers([quicknode](https://www.quicknode.com/) [nownodes](https://nownodes.io/)).
We will need the Private Key from the deployer account, this account will be the owner of the Smart Contract and it will be the payer of the deploy fees.
The apiKey parameter we will talk on the end of deploy

One good practice is to store those sensitive values in a .env file, and for this the package ``` require('dotenv').config() ``` helps us, getting the file from root directory from project. I placed an sample.env that you can copy, rename to .env and adjust the variables.


![Where and how .env should be](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ud0o7qqm8g8mq5vgvim.png)

> For BSC Testnet, we can use an public node https://data-seed-prebsc-1-s1.binance.org:8545

The next step is configure the scripts/deploy.js file 
###### I will break the file in small peaces and try to explain it 

```javascript
const hre = require('hardhat')

async function main() {
    const [owner] = await hre.ethers.getSigners()

    const divisiondContractFactory = await hre.ethers.getContractFactory(
        'Division'
    )

```
> Here 'hre' is our magic guy coming from hardhat package and with him we get the owner address(the deployer)
The 'divisiondContractFactory' will get our contract by his name.
```javascript
let divisors = [
        '0x64eb4Abe091301Fb7403bb1b19159b80ea1Fac29',
        '0x3e10abf5d5F9D8396fc027DCbf00Fae1b9F9F160',
    ]

   const divisionContract = await divisiondContractFactory.deploy(divisors).deployed();    

    console.log('Le Division deployed to:', divisionContract.address)

    console.log('Le Division owner address:', owner.address)
```
> 'divisors' is an array of adresses that you want to be the initial winners from divisions(you can add more or block after deploy)
With the contract factory from our contract, we give to it the parameters then deploy the contract. In the end of all we get the contract and owner address.
To finally deploy the contract you just need to run 

```
npx hardhat run .\scripts\deploy.js --network bsc_testnet
```
> network name should be the same as we configured in the hardhat.config.js

![Contract deploy command after executed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4c3qdinibwvbstnuucss.png)
Voualá! Here is our contract address. You can check it at [here](https://testnet.bscscan.com/address/0x671D185d3cD266914A8a5C97e436500003D8f1B5)
One detail.. Remember about verify our contract? Check how it stays after deploy

![Contract bytecoded on scan](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z43gs1uxz7f9sdygdjbj.png)
Bytecoded! To solve this, we need to configure our API key on hardhat.config file. In most blockchains, you can get it by creating an account on the explorer and going into API-KEYs.

![Explorer page with api key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/18ymk600q38trbzhzls2.png)

After getting you apiKey and replacing it on hardhat.config, run the verify command: 
```
npx hardhat verify --network bsc_testnet  --constructor-args arguments.js 0x671D185d3cD266914A8a5C97e436500003D8f1B5
```
> again network name should be the same as we configured in the hardhat.config.js 
the next argument after network is about the parameters that we used in the deploy. As we used array we can pass it directaly on the command, for this we will use the [constructor-args](https://hardhat.org/hardhat-runner/plugins/nomiclabs-hardhat-etherscan#complex-arguments)

![Verify command success](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kby8kb124os3ldcx99fo.png)
Now if we check again the bsc scan.. 

![bsc scan ok!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtmib010ago55ewsmiw2.png)

Ow yes !!
Now we got 3 tabs on the contract, Code, Read and Write. 
You can use all the function of the contract directly on the explorer. \o

![yes!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/56ddyvc08i7ugmirsidi.gif)


Hope you liked it! 
[Here](https://github.com/marcoslobo/ledevision-smart-contract) is the repo in github used
Did it help you? Feel free to comment and share 	:upside_down_face: