Creating a Crowdfunding Campaign with BuildBear

In this article, we will be deploy our own crowdfunding campaign contract where users can create campaigns and / or donate to other campaigns.

Before diving into the fun part, make sure you follow BuildBear on Twitter and LinkedIn and do not forget to join the Telegram group so you don’t miss any updates.

1. Create a project and install dependencies

1.1: Use the following commands on your CLI to initialize your project.

mkdir crowdfund-campaign-with-buildbear && cd crowdfund-campaign-with-buildbear

1.2: Run the command npx hardhat on your CLI and choose Create a Javascript Object; choose yes for all the options.

Once you have successfully executed the above and if you were to open this repo in VS Code, you should have a directory structure, similar to the following:

post image

2. Coding the Crowdfund Campaign Smart Contract

2.1: Create a file named Campaign.sol in the contracts folder.

2.2: We will start with defining the Solidity Version (this will determine the Solidity Compiler, which we will also use in our hardhat.confing.js file).

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;contract Campaign {

2.3 Inside our Campaign contract, we need to have some attributes that we are going to use in the future. So let us define them and understand each of them one by one

mapping (address => uint256) public contributers;
    string public title;
    uint256 public requiredAmount;
    string public story;
    address payable public owner;
    uint256 public receivedAmount;
    bool public acceptingDonation = true;
    uint256 public deadline;

2.3.1 Here,

  • contributers is a mapped variable where the amount of contribution done by a user will be stored by its address. This way we can keep a track of all the contributors who have contributed to the campaign.

  • title will store the title of the campaign that will be created

  • the amount required by the campaign will be stored by requiredAmount

  • any additional data can be written inside story

  • owner will store the address of the deployer or we can say the owner of the campaign. We need to store the owner to make sure that only the owner will be able to withdraw the funds.

  • receivedAmount will be used to keep a track of funds received and to make sure once the campaign amount is achieved, no one could donate to the campaign.

  • acceptingDonation will store a boolean value which will be true by default and once the donation amount is received, its value will become false and then no additional donation will be done.

  • lastly, we have a uint variable for the deadline so that after a deadline time is achieved, no more donations can take place

2.4 Now, we will define the constructor for our contract. Constructor will store all the parameters that will be required to initialise the campaign.

Thus, to start a campaign, we will require a campaign title, the amount that has to be raised, additional data about the campaign, owner of the campaign and the deadline of the contract.

Your constructor code should look similar to this

constructor (
        string memory campaignTitle,
        uint256 requiredCampaignAmount,
        string memory storyURI,
        address campaignOwner,
        uint256 campaignDeadline
    ) {
        title = campaignTitle;
        requiredAmount = requiredCampaignAmount;
        story = storyURI;
        owner = payable(campaignOwner);
        deadline = campaignDeadline;
    }

Then lastly, we are going to write 3 functions for our contract:

  • donate(): Donate function will be used to perform the donation for the campaign. Anyone can contribute to the campaign if the deadline time is not achieved and the donation amount is not yet achieved.

function donate() public payable {
        require(acceptingDonation);
        require(block.timestamp < deadline, "Deadline has Ended!");
        require(msg.value > 0, "Msg.value = 0");
        if (msg.value > requiredAmount - receivedAmount)
            revert TooHighDonation({
                message: "Donation room left",
                donationRemaining: requiredAmount - receivedAmount
            });
        receivedAmount += msg.value;
        contributers[msg.sender] += msg.value;
        if (receivedAmount >= requiredAmount) {
            acceptingDonation = false;
        }
        emit donated(msg.sender, msg.value, block.timestamp);
    }

Make sure that your function is payable.

  • withdraw(): Withdraw function will be used once the campaign amount is achieved, then only the owner can withdraw the fund by calling this function.

function withdraw() public {
        require(msg.sender == owner, "Only the owner can withdraw");
        require(!acceptingDonation, "Campaign still not over");
        owner.transfer(receivedAmount);
        emit withdrawn(receivedAmount);
    }
  • refund(): Refund function, as the same suggests, will return back all the contributions back to the contributors if the requested amount is not achieved by the campaign.It will automatically transfer the contribution back to the contributor.

function refund() public {
        require(block.timestamp > deadline && receivedAmount < requiredAmount);
        require(contributers[msg.sender] > 0, "Not eligible for refund");
        address payable user = payable(msg.sender);
        contributers[msg.sender] = 0;
        user.transfer(contributers[msg.sender]);
    }

2.5 Lastly, we will add the fallback function.

A fallback function is an unnamed external function without any input or output parameters. The EVM executes the fallback function on a contract if none of the other functions matches the intended function calls.

fallback() external payable {
        donate();
}    receive() external payable {
        donate();
}

2.6 We will also add the events for:

  • donation that will be triggered once the donate function is used. It will require the address of the donor, the amount of donation to be done, and the timestamp at which the donation is done

  • withdrawn will only require the amount of donation done by the contributor

  • And an error message to be triggered if the donation amount is more than the required amount

event donated (
        address indexed donar,
        uint256 indexed amount,
        uint256 indexed timestamp
    );event withdrawn(uint256 amount);error TooHighDonation(string message, uint256 donationRemaining);

And that’s all, our campaign smart contract is ready!

Your final contract should look similar to this

post image

3. Deployment to a blockchain, calling smart contract functions and inviting your friends / audience to participate with you in your testing

3.1: Creating a Private Testnet on BuildBear 🐻‍❄️ *(why BuildBear, you ask? Have a look over here: **Where Localhost Fails and *Win Web3 Hackathons, using BuildBear Testnet’s analytics)

3.1.1: Visit the BuildBear App. Once you login with your Github account, you will see a page similar to the image added below

post image

Here we have to create a simple node for our staking application so we will click on the create an endpoint button and then we will be redirected to the node configuration page, where we have the forking options, hardhat options and EVM options.

But for this article, we will be using the default configuration for our smart contract.

So we will simply click on the submit button as shown below.

post image

Congratulations! You have created your first node!

Your node may look similar to this

post image

BuildBear provides you with a custom RPC URL, Explorer, Faucet and other additional features.

In order to perform transactions, we need fund from faucet.

Don’t worry! We don’t have to find random faucets to get test ether.

Click on the Open Faucet option and add your choose your account.

post image

After that, click on the Add to Metamask option in the right corner of the faucet page.

Your metamask would be provided with 1000 BB ETH immediately.

post image

Your hardhat.config.js file should look similar to this

post image

In the networks, we have created a network called buildbear and added our RPC URL there and then added the private key of the metamask account.

If you have any doubt regarding any of the steps provided above, please DM us on Twitter or Linkedin.

Now our config is ready! Let’s write our smart contract.

4. Deploy the campaign contract on private testnet

4.1: Use the following script to deploy your staking contract:

const hre = require('hardhat');async function main() {    const Campaign = await hre.ethers.getContractFactory("Campaign")
    const campaignContract = await Campaign.deploy("Campaign for Dogs", 10000000000000, "This campaign will help all the stray dogs by providing them food and shelter", "0xD5142501AE71b570024938370ef197623AF055A1", 1663755818);    await campaignContract.deployed();    console.log("Factory deployed to:", campaignContract.address);
}   main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.log(error);
        process.exit(1);
    });

4.1.1Here in the deploy script, we have provided:

  • the title name for the campaign

  • the amount to be raised, in wei

  • information about the campaign

  • the owner of the campaign and

  • lastly, we have added a deadline in UNIX timestamp

4.2 To deploy this contract on buildbear, use the following command in your terminal

npx hardhat run scripts/deploy.js --network buildbear

5. Executing transactions on our contract

5.1 To see your contract deployed, open BuildBear and click on the “view on explorer” option.

And paste the contract address provided in the terminal in the BuildBear explorer.

post image

To interact with our contract, let us first submit the contract ABI to the node container.

Open BuildBear and click on the advanced button and drop your contracts folder there.

Once you’ll submit the ABI, click on the contract option adjacent to transactions

You will be automatically redirected to the read contract page.

You will be able to see all our functions listed there.

post image

5.2 In order to donate to this campaign, click on the write contract option

post image

Connect to the web3 account and then simply add the amount to be contributed in ethers.

post image

Here is an example to test the donate function. We have simply provided the amount of contribution and then approved the transaction with the help of Metamask wallet.

If you’ll check the trace of the transaction hash, you will be able to see that donation of amount 1000000000000 has been done

post image

And the receivedAmount will show you the contribution done by the contributor.

post image

You can even ask your friends to contribute to your campaign.

5.3 Now once the required amount is achieved and the contributors will not be able to contribute to the campaign. Let us try that out.

post image

Now again we tried to donate to the campaign, but this time we got a failed transaction since the required amount was already achieved.

If you will click on the transaction hash, you will see the information of the failed transaction.

post image

Now the owner will be able to withdraw the amount and can receive the campaign fund.

post image
post image

This way you can ask your friends to contribute to your campaign 😃

*You give you a better hands-on experience with the smart contract, I have created my private testnet and the campaign contract is available over *here.

*You can get the Faucet for the testnet over *here.

Get the above Github code from here.

This is how we can write a crowdfunding smart contract and can play around with it with the help of BuildBear.

To learn more about BuildBear, read this docs

Make sure you follow us on Twitter and Join the Telegram group if you haven’t done yet.

Author:

Pari Tomar (Twitter || LinkedIn), always open to feedback and learning.

BTW, if you would know anyone who would be keen to working with BuildBear. Please have a look over here!!!