Solidity Basics. Artemis Week-3
After covering a lot of theoretical (but yet important) ground in the previous 2 weeks, we finally got into solidity. This week has been packed with basic concepts and plenty of exercises to get familiar with the language. We went through the different object types, functions, events, modifiers, inheritance, etc.
There are plenty of excellent online resources which are publically available. On top of that, they are in all sorts of formats, so you can check both written and video formats.
Because of that, instead of explaining all these basic concepts, I will try to explain a couple of interesting things worth sharing (i.e. security and gas optimization tips), and also go through my thought process when attempting to solve some exercises.
Tips of the week
Sponsored by Gonçalo.
send vs transfer vs call
Despite the 3 methods can be used to transfer ether, they have key distinctions that are worth knowing. send()
and transfer()
used to be recommended, because they would limit gas expenditure to 2300 gwei. With this amount of available gas, people were able to do minor fallback operations like emitting events or even updating storage, but it wasn't enough to reenter a contract. Nevertheless, this was never a good method to avoid reentrancy attacks, as gas costs can vary with protocol updates (i.e. the Istanbul fork increased the gas cost for the SLOD
opcode).
Because of that, it is recommended to use the low-level function call()
, which defaults to forward all the available gas. Then, you can use a Reentrancy Guard or the checks-effects-interactions pattern to protect your functions.
Note: For a more detailed explanation, read this article.
indexed event parameters vs non-indexed (regular)
Events with indexed parameters are easier to retrieve because they are specially indexed in the world state (in the logsBloom). Because of that, they are slightly more expensive than events with only regular parameters.
This explanation can be confirmed with this simple Foundry example:
Note: Logs are composed of Topics (up to 4) and Data. One topic is reserved for the event signature, so you can have a maximum of 3 indexed parameters.
In the Ethereum Yellow Paper, it can be asserted that each topic costs 375gwei, and each byte in data costs 8gwei * numberBytes.
Task. Creating a basic Crowdfunding Platform
Implement the backbone for a simple Crowdfunding platform with the following requirements:
Anyone can create a campaign to acquire funding for their project.
Each campaign must, at least, have:
- A name.
- An owner.
- A campaign type (start-up or charity).
- A funding goal.
- A time limit to reach it, which can't be higher than 60 days.
All of the above properties must be defined at campaign creation, and can't be updated.
Until the time limit of a given campaign is reached, anyone can fund it by sending Ether. After the time limit is reached, it can't receive any more funds.
The owner of a campaign can only withdraw the raised Eth after the time limit is reached.
After the owner withdraws the funds, campaigns should be marked either as Fully-Funded (if raised >= funding goal) or Partially-Funded (if raised < funding goal).
An owner must be able to cancel a campaign. If this happens that campaign can't receive any more Ether.
Implement all the relevant events.
Solution Design
When having an idea, the first thing you need to figure out is the architectural design that your solution should have. In this case, the most reasonable thing to do, in my eyes, was to have an independent contract for each campaign, so owners could directly interact with it and have full ownership. Because of that, I decided to create a factory contract CampaignFactory and a campaign contract called Campaign.
Because of the explained architecture, I started by creating the Campaign contract, making sure that it was compliant with all the given requirements.
Note: you can find the full script in this GitHub gist.
First of all, we need to define the variables and any enum or structs that we may need.
Once we have set up all the variables, we can create the contract constructor. In this case, the constructor will use a require()
to ensure that the deadline is less than 60 days.
Since in solidity timestamps are expressed in unix (a second-based system that starts on January 1st, 1970), the input variable _deadline
used in the constructor will have a relative scope (see that the require clause states _deadline <= 3600*24*60
). Nevertheless, the global variable deadline
will have an absolute reference, so that we can compare it with any given timestamp.
After coding the contract constructed, I then created the relevant events and some modifiers that will help with the required validations that we will have to implement in the campaign functions.
Finally, we need to implement the contract functions. In this case, we only need 4 functions: fallback(), receive(), withdrawFunds(), cancelCampaign()
.
For both fallback()
and receive()
to be able to receive ether from other contracts and EOAs, we need to make them payable
. On top of that, we will also add the liveCampaign
modifier to ensure that no more donations are received after a campaign is closed.
Another topic worth mentioning is how to deal with withdrawals. As explained before in the tips of the week section, the low-level function call()
is used.
With this, the campaign contract is finished. Now, we only need to create the crowdfunding factory contract.
In this case, the factory contract will only have a single function that will deploy a new instance of the campaign contract whenever it is called. On top of that, we will also add a couple of mappings to better deal with the deployed campaigns. These mappings will be tracking the deployed campaign contracts by owner (using an array), and the amount of deployed campaigns by each of them (to navigate the array).
Recommended Resources
If you want to get started with Solidity from the ground up, you should definitely check this youtube playlist by @SmartContractProgrammer.
If you prefer written content, you will also find plenty of resources on this webpage.