Gm gm! 👋🏼
Before we begin, please make sure bookmark/save the following link as all developer content will be posted to Mantle Network’s official website — link. In this tutorial, we will be building a crowdfunding dApp on Mantle Network using Ankr and ..
- Smart Contract Language: Solidity
- Smart Contract Deploy and Verify Scripts: Javascript
- Smart Contract Development Environment: Hardhat(opens in a new tab)↗
- Frontend Language: React — TypeScript
- Wallet Connect: RainbowKit
- Interacting with Contract through Frontend: wagmi(opens in a new tab)↗
- User Interface: TailwindCSS(opens in a new tab)↗
- RPC Provider: Ankr(opens in a new tab)↗
A quick shoutout to Ankr’s DevRel team for their articles in the docs, which made it really easy to reference existing examples and build this on short-notice!
By the end of this tutorial, you’ll be able to:
- deploy crowdfunding smart contract on Mantle Network
- and create a full-fledged frontend for your dApp
The Functionalities
- Start New Campaign — users will be able to start a new crowdfunding project by inputting some details about the campaign like title, story and goal amount to be raised.
- View Projects — users can see all the existing projects and campaign details on the homepage
- Make Donation — anyone can fund to the project they want to support in $MNT tokens
Step 1: Create a Hardhat project
We’re going to set up our project using Hardhat, the industry-standard development environment for Ethereum smart contracts. Additionally, we’ll also install OpenZeppelin contracts.
To set up Hardhat, run the following commands in your terminal:
mkdir mantle_crowdfunding-dapp && cd mantle_crowdfunding-dapp
npm init -y
npm install --save-dev hardhat
npx hardhat
Note: Choose the Create a Javascript project option from the menu and accept all defaults. To verify everything is installed properly, run this command in your terminal:
npx hardhat test
To install OpenZeppelin, run the following commands in your terminal:
npm install @openzeppelin/contracts
Step 2: Set Up Hardhat Configs
Navigate back to the project and find the hardhat.config.js
file in root directory and the following code:
File: hardhat.config.js
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require("dotenv").config();
require("@nomiclabs/hardhat-ethers");
require("@nomiclabs/hardhat-etherscan");
module.exports = {
solidity: "0.8.15",
networks: {
"mantle-testnet": {
url: "https://rpc.ankr.com/mantle_testnet",
accounts: [process.env.PRIVATE_KEY], // Uses the private key from the .env file
}
}
};
In this file, we have configured the solidity version, and network details and plugged Ankr’s free public RPC ↗ for Mantle Network Testnet.
Step 3: Connect Mantle Network to Your Project
Having created a MetaMask wallet and a smart contract, we can now interlink them. To accomplish this, we’ll employ the dotenv package.
- Install the
dotenv
pack in your project directory by running the following command from your terminal:
npm install dotenv --save
- Create aÂ
.env
file in the root directory of our project. Note: YourÂ.env
file must only be namedÂ.env
. Do not change the name to any formxx.env
. - Add your MetaMask private keyÂ
PRIVATE_KEY = 0x___your__private___key
Step 4: Write Crowdfunding Smart Contract
Now that we have everything set up, we are all ready to write the smart contract for the donation-based crowdfunding platform. For that, navigate to contracts
directory and you'll create a new file named crowdfunding.sol
file. There's two main things to consider while making a crowdfunding contract -
- CrowdfundingProject — deals with a single project or campaign being created. It includes the
makeDonation()
function which calculates the amount raised and checks if thegoalAmount
is achieved or not while recording the wallet address of the donor. - CrowdFactory — records for all the crowdfunding projects being created and launched.
Complete crowdfunding.sol
file would look like:
// SPDX-License-Identifier: Unlicensed
pragma solidity ^0.8.15;
//contract to record all crowdfunding projects
contract CrowdFactory {
address[] public publishedProjs;
event Projectcreated(
string projTitle,
uint256 goalAmount,
address indexed ownerWallet,
address projAddress,
uint256 indexed timestamp
);
function totalPublishedProjs() public view returns (uint256) {
return publishedProjs.length;
}
function createProject(
string memory projectTitle,
uint256 projgoalAmount,
string memory projDescript,
address ownerWallet
) public {
//initializing CrowdfundingProject contract
CrowdfundingProject newproj = new CrowdfundingProject(
//passing arguments from constructor function
projectTitle,
projgoalAmount,
projDescript,
ownerWallet
);
//pushing project address
publishedProjs.push(address(newproj));
//calling Projectcreated (event above)
emit Projectcreated(
projectTitle,
projgoalAmount,
msg.sender,
address(newproj),
block.timestamp
);
}
}
contract CrowdfundingProject {
//defining state variables
string public projTitle;
string public projDescription;
uint256 public goalAmount;
uint256 public raisedAmount;
address ownerWallet; //address where amount to be transfered
event Funded(
address indexed donar,
uint256 indexed amount,
uint256 indexed timestamp
);
constructor(
string memory projectTitle,
uint256 projgoalAmount,
string memory projDescript,
address ownerWallet_
) {
//mapping values
projTitle = projectTitle;
goalAmount = projgoalAmount;
projDescription = projDescript;
ownerWallet = ownerWallet_;
}
//donation function
function makeDonation() public payable {
//if goal amount is achieved, close the proj
require(goalAmount > raisedAmount, "GOAL ACHIEVED");
//record walletaddress of donor
(bool success, ) = payable(ownerWallet).call{value: msg.value}("");
require(success, "VALUE NOT TRANSFERRED");
//calculate total amount raised
raisedAmount += msg.value;
emit Funded(msg.sender, msg.value, block.timestamp);
}
}
Now, let’s compile hardhat to see if everything’s good to go!
yarn hardhat compile
Output would look something like this below 🦄
Step 5: Write the Deploy Script(s)
Now that we’ve got our contract set up, let’s create the deployment scripts. There will be two scripts, one for deploying the CrowdFactory contract and second for a dummy campaign creation with some dummy project info.
- For the first script, navigate to the
scripts
folder and add the following code indeploy.js
file
const { ethers } = require("hardhat");
async function main() {
// Grab the contract factory
const CrowdFactory = await ethers.getContractFactory("CrowdFactory");
// Start deployment, returning a promise that resolves to a contract object
const crowd = await CrowdFactory.deploy(); // Instance of the contract
await crowd.deployed();
console.log("Contract deployed to address:", crowd.address);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
- Save this file and run the following command to deploy the CrowdFactory contract
npx hardhat run scripts/deploy.js --network mantle-testnet
Running this command will prompt you with the address of the deployed contract.
Note: Save/copy your contract address as you will need it in your next deployment script.
Before we go on deploying another contract, let’s save this contract address in a file named constants.tx under src directory.
File: src/constants.tx
export const FACTORY_CONTRACT_ADDRESS = "paste your contract which was deployed above";
//just for testing purpose
export const DEBUG = false;
Now let’s deploy another contract with a sudo campaign details for us to get started. For this, we are going to create createCampaigns.js file under scripts
directory. Navigate to that file and save the following code.
⚠️ Edits to make:
- Line 6, replace your contract address with the address mentioned already in the script (check line 6)
- Line 13, replace my wallet address with yours.
scripts/createCampaigns.js
const { ethers } = require("hardhat");
async function main() {
const contract = await ethers.getContractAt("CrowdFactory",
//add the contract address that you just deployed in the last step
"0x997ab899CC4b4a21EE3407b93b3EA2C4e32c4e21") //line 6
await contract.createProject(
"first title",
ethers.utils.parseUnits("0.1", 18),
"description",
//insert your wallet's public key
"0x0e7771d884588C77e0c65d062b30a6b6850b8337") //line 13
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Once done, run the following command to deploy this script as well:
yarn hardhat run scripts/createCampaigns.js --network mantle-testnet
Step 6: Get Contract ABI for Verified Codes
In this step, we are going to do two things:
- we will verify our smart contracts on Mantle Explorer ↗
- get contract ABIs for verified contract source codes
Verifying Smart Contracts
- Go to
verify-factory.js
file under scripts folder and add the following verify script:
Note: In this file you need to edit one thing. The contractAddress you see on line number 5 needs to be replaced by your contract’s address.
const { run } = require("hardhat");
async function main() {
//add the contract address that you deployed in the prev steps
const contractAddress = "0x997ab899CC4b4a21EE3407b93b3EA2C4e32c4e21"; //line 5
try {
await run("verify:verify", {
address: contractAddress,
constructorArguments: [],
contract: "contracts/crowdfunding.sol:CrowdFactory",
});
} catch (error) {
if (error.message.toLowerCase().includes("already verified")) {
console.log("Already verified!");
} else {
console.log(error);
}
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
- Now in the terminal, run this command to verify the CrowdFactory smart contract on:
yarn hardhat run scripts/verify-factory.js --network mantle-testnet
Output:
Note: You might wonder how the above step worked when the Mantle Explorer isn’t built on EtherScan. Adding Mantle Network’s configuration to the hardhat.config.js
file to enable contract verification on a custom network. This worked because Blockscout's contract verification interface is the same as Etherscan!
We could have also used Sourcify to verify our contracts — https://docs.mantle.xyz/network/for-devs/tutorials/verifying-smart-contracts. Next, head over to the Mantle Explorer link you get as the output after running the command and it will land you to the verified contract’s page. Click on Code and you’ll find the Contract ABI section.
- Copy the ABI and paste it into the
crowdfactory.json
file undersrc/abis
directory - Go back to the same Mantle Explorer link, click on the Read Contract button, and query “0” from the publishedProjs.
Copy and save the address it outputs, we will need it in line 5 of the next file.
Now, we are going to follow the similar steps to first verify our dummy crowdfunding project and get the contract’s ABI.
- Navigate to
verify-crowdfundingproject.js
file under the scripts folder and insert the following code in the file:
Replace contractAddress in the file with the one we saved above querying the 0th publishedProj and also insert your wallet address on line 9
const { run, ethers } = require("hardhat");
async function main() {
//replace contractAddress with the one we saved above querying the 0th publishedProj
const contractAddress = "0x4a086a54e4d6bb8becb5752f46d290341f0e9af9"; //line5
const args = [
"first title", ethers.utils.parseUnits("0.1", 18), "description",
//Insert you wallet's public address here
"0x0e7771d884588C77e0c65d062b30a6b6850b8337", //line 9
];
try {
await run("verify:verify", {
address: contractAddress,
constructorArguments: args,
contract: "contracts/crowdfunding.sol:CrowdfundingProject",
});
} catch (error) {
if (error.message.toLowerCase().includes("already verified")) {
console.log("Already verified!");
} else {
console.log(error);
}
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
- Once you save the above file, run this command to verify it on Mantle Explorer:
yarn hardhat run scripts/verify-crowdfundingproject.js --network mantle-testnet
Output:
- Head over to the Mantle Explorer link you get as the output after running the command and it will land you on the verified contract’s page
- Click on Code and you’ll find the Contract ABI section. find Contract ABI section and copy-paste ABI in the
crowdfundingproject.json
file undersrc/abis
directory
Once this is done, run the following command in the terminal:
yarn typechain
TypeChain is a typescript binding and code generator used to create smart contracts that feature static typing and IDE support.
Congrats if you’ve followed along till now! In the next part, we will be creating components and building the UI.