Tutorial
Smart Contract

Smart Contracts

  1. Rename the file to MyContract.sol and save it in the same directory as the CrowdFunding.sol file.

Structure

  1. Copy the following code into the MyContract.sol file.
pragma solidity ^0.8.9;
 
contract CrowdFunding {
    struct Campaign {
        address owner;
        string title;
        string description;
        uint256 target;
        uint256 deadline;
        uint256 amountCollected;
        string image;
        address[] donators;
        uint256[] donations;
    }
 
    mapping(uint256 => Campaign) public campaigns;
 
    uint256 public numberOfCampaigns = 0;
 
    function createCampaign() {}
 
    function donateToCampaign() {}
 
    function getDonators() {}
 
    function getCampaigns() {}
}

What does this code do?

pragma solidity ^0.8.9;

This line specifies the version of Solidity that the contract is written in. In this case, it's version 0.8.9.

contract CrowdFunding {

This line starts the definition of a new contract called CrowdFunding.

struct Campaign {
    address owner;
    string title;
    string description;
    uint256 target;
    uint256 deadline;
    uint256 amountCollected;
    string image;
    address[] donators;
    uint256[] donations;
}

This line defines a new struct called Campaign which contains various properties such as owner, title, description, target, deadline, amountCollected, image, donators and donations.

Structs are similar to objects in JavaScript.

mapping(uint256 => Campaign) public campaigns;

This line creates a mapping called campaigns which maps a uint256 to a Campaign struct. The public keyword means that this mapping can be accessed from outside the contract.

uint256 public numberOfCampaigns = 0;

This line creates a public variable called numberOfCampaigns which is initialized to 0.

function createCampaign() {}

This line defines a function called createCampaign which doesn't take any arguments and doesn't return anything.

function donateToCampaign() {}

This line defines a function called donateToCampaign which doesn't take any arguments and doesn't return anything.

function getDonators() {}

This line defines a function called getDonators which doesn't take any arguments and doesn't return anything.

function getCampaigns() {}

This line defines a function called getCampaigns which doesn't take any arguments and doesn't return anything.

Create a Campaign

  1. Now write all the parameter for createCampaign function. The function should take the following parameters:
     function createCampaign(address _owner, string memory _title, string memory _description, uint256 _target, uint256 _deadline, string memory _image) public returns (uint256) {
        Campaign storage campaign = campaigns[numberOfCampaigns];
 
        require(campaign.deadline < block.timestamp, "The deadline should be a date in the future.");
 
        campaign.owner = _owner;
        campaign.title = _title;
        campaign.description = _description;
        campaign.target = _target;
        campaign.deadline = _deadline;
        campaign.amountCollected = 0;
        campaign.image = _image;
 
        numberOfCampaigns++;
 
        return numberOfCampaigns - 1;
    }

What does this code do?

function createCampaign(address _owner, string memory _title, string memory _description, uint256 _target, uint256 _deadline, string memory _image) public returns (uint256) {

This line defines a function called createCampaign() which takes six arguments: _owner, _title, _description, _target, _deadline, and _image. The function is public which means it can be called from outside the contract and it returns a uint256 value.

Campaign storage campaign = campaigns[numberOfCampaigns];

This line creates a new Campaign struct called campaign and assigns it to the mapping campaigns at the index of numberOfCampaigns.

require(campaign.deadline < block.timestamp, "The deadline should be a date in the future.");

This line checks if the deadline of the campaign is in the future. If it's not in the future, then an error message is returned.

campaign.owner = _owner;
campaign.title = _title;
campaign.description = _description;
campaign.target = _target;
campaign.deadline = _deadline;
campaign.amountCollected = 0;
campaign.image = _image;

These lines set the values of each property of the Campaign struct.

numberOfCampaigns++;
return numberOfCampaigns - 1;

These lines increment the value of numberOfCampaigns and return its value minus one.

Donate to a Campaign

  1. Now write all the parameter for donateToCampaign function. The function should take the following parameters:
    function donateToCampaign(uint256 _id) public payable {
        uint256 amount = msg.value;
 
        Campaign storage campaign = campaigns[_id];
 
        campaign.donators.push(msg.sender);
        campaign.donations.push(amount);
 
        (bool sent,) = payable(campaign.owner).call{value: amount}("");
 
        if(sent) {
            campaign.amountCollected = campaign.amountCollected + amount;
        }
    }

What does this code do?

function donateToCampaign(uint256 _id) public payable {

This line defines a function called donateToCampaign() which takes one argument: _id. The function is public which means it can be called from outside the contract and it is payable which means it can receive Ether.

uint256 amount = msg.value;

This line creates a new variable called amount and assigns it to the value of msg.value. msg.value is the amount of Ether that was sent with the transaction.

Campaign storage campaign = campaigns[_id];

This line creates a new Campaign struct called campaign and assigns it to the mapping campaigns at the index of _id.

campaign.donators.push(msg.sender);
campaign.donations.push(amount);

These lines add the address of the sender (msg.sender) to the donators array of the Campaign struct and add the amount of Ether sent (amount) to the donations array of the Campaign struct.

(bool sent,) = payable(campaign.owner).call{value: amount}("");

This line sends the amount of Ether (amount) to the owner of the Campaign (campaign.owner). The payable keyword allows this function to send Ether. The .call() function is used to send Ether and returns a tuple with two values. The first value is a boolean indicating whether or not the transaction was successful. The second value is unused in this case.

if(sent) {
  campaign.amountCollected = campaign.amountCollected + amount;
}

This line checks if the transaction was successful. If it was successful, then it adds the amount of Ether sent (amount) to the amountCollected property of the Campaign struct.

Get Donators

  1. Now write all the parameter for getDonators function. The function should take the following parameters:
 
    function getDonators(uint256 _id) view public returns (address[] memory, uint256[] memory) {
        return (campaigns[_id].donators, campaigns[_id].donations);
    }

What does this code do?

This is a function called getDonators() which takes one argument: _id. The function is public which means it can be called from outside the contract and it is view which means it doesn't modify the state of the contract. The function returns two arrays: donators and donations. The donators array contains the addresses of all the people who have donated to the Campaign with the given _id. The donations array contains the amount of Ether that each person has donated. Here's a point-by-point explanation of each line:

function getDonators(uint256 _id) view public returns (address[] memory, uint256[] memory) {

This line defines a function called getDonators() which takes one argument: _id. The function is public which means it can be called from outside the contract and it is view which means it doesn't modify the state of the contract. The function returns two arrays: donators and donations.

return (campaigns[_id].donators, campaigns[_id].donations);

This line returns a tuple containing two arrays: campaigns[_id].donators and campaigns[_id].donations. campaigns[_id] is used to access the Campaign struct with the given _id.

Get Campaigns

  1. Now write all the parameter for getCampaigns function. The function should take the following parameters:
         function getCampaigns() public view returns (Campaign[] memory) {
           Campaign[] memory allCampaigns = new Campaign[](numberOfCampaigns);
 
           for(uint i = 0; i < numberOfCampaigns; i++) {
               Campaign storage item = campaigns[i];
 
               allCampaigns[i] = item;
           }
 
           return allCampaigns;
       }

What does this code do?

This is a function called getCampaigns() which takes no arguments. The function is public which means it can be called from outside the contract and it is view which means it doesn't modify the state of the contract. The function returns an array of Campaign structs. Here's a point-by-point explanation of each line:

function getCampaigns() public view returns (Campaign[] memory) {

This line defines a function called getCampaigns() which takes no arguments. The function is public which means it can be called from outside the contract and it is view which means it doesn't modify the state of the contract. The function returns an array of Campaign structs.

Campaign[] memory allCampaigns = new Campaign[](numberOfCampaigns);

This line creates a new array called allCampaigns with a length of numberOfCampaigns. The new keyword is used to create a new dynamic array.

for(uint i = 0; i < numberOfCampaigns; i++) {
    Campaign storage item = campaigns[i];
 
    allCampaigns[i] = item;
}

These lines loop through each Campaign struct in the campaigns mapping and add it to the allCampaigns array. The storage keyword is used to indicate that we want to modify the original struct in storage rather than creating a copy in memory.

return allCampaigns;

This line returns the allCampaigns array.