We will implement a contract utilizing the knowledge accumulated up to Day 16. As an example, we will create a contract with basic crowdfunding functionality. First, let’s understand the development process for developing a contract and then introduce the actual code.
Development Process
The content explained so far has been primarily about syntax, which is just a part of the coding (programming) process in the overall development workflow. Before even starting to code, it’s essential to decide what to create and how to realize it. In general system development, not limited to web3 development, the following steps are taken.
Define the requirements of the contract you are trying to create. Consider who will use this contract, how it will be used, and determine the features needed to realize these requirements.
For a crowdfunding example, the following functionalities are necessary:
- People who want to contribute can send money to this contract, and their addresses and the amount are recorded.
- The owner of the contract (the person receiving the contributions) can withdraw the total contributed amount.
- Anyone can view information about the owner, contributors, and the amount contributed.
Design a contract that has the features defined in the requirement definition. Define necessary data structures, functions, events, modifiers, etc.
For the crowdfunding example, features 1 to 3 above would be defined as functions, and the data used in these functions (contributor address and amount information, owner information, etc.) would need to be defined as variables in the contract.
Implement the contract based on the designed content (coding, programming).
In the case of Remix, it is possible to test by executing the deployed contract from the Remix UI. When using frameworks like Hardhat, it’s possible to conduct testing by creating test codes (coding tests allows for automation, significantly easing the testing process, especially effective when repeatedly modifying the same program).
Code verified in a local environment is deployed on the testnet for further verification. If there are no issues on the testnet, the next step is deploying on the mainnet, leading to the official launch of the contract.
Coding
Following the process described above, here is an example of the actual implemented code.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
error Funding__NotOwner();
error Funding__NotEnoughETH();
/**
* @title A contract for crowdfunding
* @author simochan
* @notice This contract is to demo a sample funding contract
*/
contract Funding {
// State variables
address[] private s_funders;
mapping(address => uint256) private s_addressToAmountFunded;
address private immutable i_owner;
// Event
event Funded(address indexed funder, uint256 indexed fundedAmount);
// Modifiers
modifier onlyOwner() {
if (msg.sender != i_owner) {
revert Funding__NotOwner();
}
_;
}
constructor() {
i_owner = msg.sender;
}
function fund() public payable {
if(msg.value <= 100000000000000000) {
revert Funding__NotEnoughETH();
}
s_funders.push(msg.sender);
s_addressToAmountFunded[msg.sender] += msg.value;
emit Funded(msg.sender, msg.value);
}
function withdraw() public onlyOwner {
for (uint256 i = 0; i < s_funders.length; i++) {
address funder = s_funders[i];
s_addressToAmountFunded[funder] = 0;
}
s_funders = new address[](0);
(bool success, ) = i_owner.call{value: address(this).balance}("");
require(success);
}
function getOwner() public view returns (address) {
return i_owner;
}
function getFunder(uint256 index) public view returns (address) {
return s_funders[index];
}
function getAddressToAmountFunded(address funder) public view returns(uint256) {
return s_addressToAmountFunded[funder];
}
}
Testing
Creating test codes using Hardhat and conducting tests with them will be detailed in a later chapter. Here, let’s try manual testing with a contract deployed on Remix’s local network. Specifically, we will consider several cases and check if they work correctly.
- After deployment, confirm whether the owner information obtained through getOwner matches the address that deployed the contract.
- Execute funding (contributions) from multiple addresses other than the owner with amounts exceeding 0.1 ETH, and verify that the contract’s ETH balance has increased (assume funding of 1.0 ETH each from Address 1 and Address 2). Also, confirm that the defined event Funded (funding event) is recorded on the console.
- Execute getFunder with arguments 0 and 1 to retrieve the addresses of the funders and confirm that the addresses used in step 2 are returned.
- Use the addresses returned in step 3 as arguments to execute getAddressToAmountFunded (retrieve funded amount) and confirm that the funding amount is returned.
- Execute funding with an amount less than 0.1 ETH, confirm that it is reverted, and an error is displayed on the console.
- Have the owner execute withdraw and confirm that the contract’s ETH balance has become zero.
Comments