Creating A Crowd Sourcing Solidity Contract
Tags: solidity, smartcontract, crowdsourcing, contractfactory
Published: 2022-02-06 11:28:54


## Description
The code for this post is available [here](https://github.com/jamiebones/DAPP_crowdsourcing)

This blog post will walk you through how to create a crowd sourcing smart contract. The implementation consist of two smart contracts which are the main smart contract implementation called **CrowdSourcing.sol** and the factory used to create other proxies from the main contract called **CrowdSourcingFactory.sol**. There is also an interface called **ICrowdSourcing.sol** that has some methods signature.

## The project
The project was set up using hardhat and selecting the advanced project setup with Typescript. 

## Code
EIP-1167 minimal proxies of Ethereum proposed how to save gas when deploying the same contract on the blockchain. The contract crowdSourcing is the main contract which is deployed first and its deployed address is attached to the factory contract (crowdSourcingFactory) to use to create proxy contracts off the main contract. The code makes use of Openzepplein Clones library to create clones of the deployed crowd sourcing contract.

A schematic representation:

![Diagram showing proxy contract](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dm95wfolxatyxn8t81z2.png)

A user who wants to create a new crowd sourcing contract calls the contract factory which creates new proxy contracts with their own state. This is done by delegateCall. The crowd sourcing master contract is firstly deployed and its address is passed on to the factory contract that will create clone of the master contract.

## crowdSourcing.sol
```solidity
  //SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";

contract CrowdSourcing is Ownable, Initializable {
    string public purpose;
    uint256 public targetAmount;
    uint256 public amountDonated;
    mapping(address => uint256) public donors;
    address[] public donorsAddress;
    address public deployer;
    bool public campaignRunning;
    
    //Events
    event DonationMade(address indexed donorAddress, uint256 amount, string indexed crowdsource );
    event DonationWithdrawn(address indexed receipient, uint256 amount, string indexed crowdsource);

    struct DonorsAmount {
        address donorsAddress;
        uint256 amount;
    }

    function initialize(string memory _purpose, uint256 _targetamount, address _deployer) public initializer {
        purpose = _purpose;
        targetAmount = _targetamount;
        campaignRunning = true;
        deployer = _deployer;
        
        //Ownable.initialize(msg.sender);
    }

    function donateToCause() public payable isCampaignOn   {
      require(msg.value > 0, "donation of 0 ether made");
      require(msg.sender != address(this), "contract cannot make donation");
      //add the amount to the amountDonated
      amountDonated += (msg.value);
      //check if we have a donation made before
      bool exists = donors[msg.sender] != 0;
       //include addres and amount in the mapping donors
      donors[msg.sender] += msg.value;
      if ( !exists ){
          //push address to the donors addresss
          donorsAddress.push(msg.sender);
      }

      //emit a Donation Made event
      emit DonationMade(msg.sender, msg.value, purpose);
    }

    function withdrwaDonation() public payable isCampaignOn isDeployer {
        require(amountDonated > 0, "Nothing to withdraw");
        campaignRunning = false;
        payable(address(msg.sender)).transfer(amountDonated);
        emit DonationWithdrawn(msg.sender, amountDonated, purpose);
    }

    function getDonorsList() public view returns (DonorsAmount[] memory){
        uint256 totalDonors = donorsAddress.length;
        DonorsAmount[] memory donorsArray = new DonorsAmount[](totalDonors);
        for (uint256 i=0; i < totalDonors; i++){
            address currentAddress = donorsAddress[i];
            uint256 amount = donors[currentAddress];
            donorsArray[i] = DonorsAmount(
                currentAddress,
                amount
            );
        }
        return donorsArray;
    }


    modifier isCampaignOn {
        require(campaignRunning == true, "Campaign is not running");
        _;
    }

    modifier isDeployer{
        require(deployer == msg.sender, "caller not deployer");
        _;
    }
}
```
The contract declares some state variable for storing data that is peculiar to each created proxy contract. 

### variables in contract
`purpose` : to store the purpose for the crowd funding
`targetAmount` : to store the amount hoped to raised
`amountDonated` : to store how much has being donated
`donors` : a mapping used to store the address of donors and amount donated
`donorsAddress` : to store the address of each donor. This is useful because we want the ability to be able to pull out all the donors and their donated amount. [See Iterable map pattern](https://dev.to/jamiescript/design-patterns-in-solidity-1i28#iterable)
`deployer` : to store the person that deployed the contract
`campaignRuning` : a boolean to check if the campaign is running
`DonationMade` : event that is fired when a donation is made
`DonationWithdrawn` : event that is emitted when the donation is withdrawn

## Contract function

### initialize

The contract has an `initialize` function that is called when the contract is created by the factory. The initialize function acts like a constructor as it is called once. OpenZeppelin `Initializable` contract which is inherited ensures that the `initialize` function is only called once. The `deployer` variable is used to store the address of the person that deploys the contract.

### donateToCause

This function is called when someone makes a donation to your cause. It stores the address of the donor and the amount in the `donors` variable. A `DonationMade` event is emitted on a successful donation.

### withdrwaDonation
This function can only be called by the owner of the crowd sourcing contract to withdraw the donation made. The `DonationWithdrawn` event is emitted on a successful withdrawal.

### getDonorsList
This function is used to get the list of donors.

## crowdSourcingFactory.sol

```solidity
  // SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./ICrowdSourcing.sol";


contract CrowdSourcingFactory is Ownable {
    address public implementation;
    address[] public allCrowdSource;
    mapping(bytes32 => address ) private idToAddress;
    

    constructor(address _implementation) {
        implementation = _implementation;
    }

    function createCrowdSourceContract(string memory _purpose, uint256 _targetamount, address _deployer) 
    external payable returns (address crowdContract){
        bytes32 id = _getOptionId(_purpose, _targetamount);
        require(idToAddress[id] == address(0), "Crowd sourcing type exist");
        bytes32 salt = keccak256(abi.encodePacked(_purpose, _targetamount));
        crowdContract = Clones.cloneDeterministic(implementation, salt);
        ICrowdSourcing(crowdContract).initialize(_purpose, _targetamount, _deployer);
        allCrowdSource.push(crowdContract);
        idToAddress[id] = crowdContract;
    }

    function getCrowdSource(string memory _purpose, uint256 _amount ) public view returns (address){
        bytes32 id = _getOptionId(_purpose, _amount);
        return idToAddress[id];
    }

    function _getOptionId(string memory _purpose, uint256 amount) internal pure returns (bytes32){
        return keccak256(
            abi.encodePacked(_purpose, amount)
        );
    }

    function getNumberofCloneMade() public view returns (uint256) {
        return allCrowdSource.length;
    }
}

```

### variables in contract factory
`implementation` : this stores the address of the master contract (CrowdSourcing)
`allCrowdSource` : this stores the address of all the created proxy contract
`idToAddress` : this stores a mapping in bytes of the contract purpose and the targeted amount.

## Contract functions

### constructor
This sets the address of the master contract in the factory contract. The factory creates proxies from the master.

### createCrowdSourceContract
This function accepts three arguments which are the purpose of the crowd sourcing, the targeted amount and the address of the deployer. This function is used to create a proxy contract. The function firstly checked if an exact contract with the same name and target sum already exist. It does this by an internal function called `_getOptionId` If a contract with such parameters does not exists, it encodes the purpose and target sum of the contract into `bytes`. The factory uses the `Clones` contract imported from Openzeppelin to create clones by passing address of the already deployed master contract and the encoded bytes to the `Clones.cloneDeterministic` function which returns the address of the proxy contract.

`crowdContract = Clones.cloneDeterministic(implementation, salt)`;
`crowdContract` is the address of the created proxy. 
 `ICrowdSourcing(crowdContract).initialize(_purpose, _targetamount, _deployer)`;
We imported the interface `ICrowdSourcing` and used it with the created proxy address by calling the function `initialize` with the three parameters which are : `purpose`, `targetAmount` and `deployer`.

> I noticed that calling `msg.sender` in the `initialize` function equated to the zero address. That's why I explicitly passed the address of the deployer.

### getCrowdsource

This is used to get the address of the deployed proxy contract by passing in the purpose of the contract and the target amount hoping to raise.

### getNumberofCloneMade
This function returns the number of clones made. It reads and returns the length of the `allCrowdSource` array.

## Summary
The EIP-1167 minimal proxy is the cheapest way to clone contract and this post has talked about how that can be done. Thanks for reading.