Solidity: Contract Layout and Style Guide

Good code is a clean code.

·

6 min read

Solidity: Contract Layout and Style Guide

Hi friends, welcome!

In this first post, I’ll want to share some layouts for contracts and common designs that I learn in the Solidity documentation. It's nice developers adopt when writing their smart contracts.

And why should I care?

First, you know most contracts receive money. I hear from Alex Van de Sande when I was in the EthereumRio Bootcamp that every new design for Solidity contracts was because someone loses a lot of money stuck inside the contract.

This problem involves more writing the code than the style guide, but writing neatly makes it easier to find things you don't want to happen.

Remember, smart contracts are an empty paper, you need to tell him every little thing you want him to do and what not to do. (Don't give free money to bots!)

Second, it’s nice to write clean code, which anyone can take, read, and understand. Hey, we are in an open-source world! It’s not too hard to keep clarity and make it easy for new people to come.

Lastly, according to solidity official docs, this style guide is not about “the right” or “the best” way to write smart contracts, is all about consistency.

Look at this note from Solidity Docs:

"Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important.
But most importantly: know when to be inconsistent – sometimes the style guide just doesn’t apply. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don’t hesitate to ask!"

Very inspiring huh? So let’s start.

Basic Structure of a Contract:

Every contract will have these 3 things:

  • SPDX – license: Every source file should start with a comment indicating its license. You can l find more information about licenses here.
  • pragma version: Enable certain compiler features or checks, it just instructs the compiler to check whether its version matches the one required by the pragma. Here you specify the compiler version to be used for the current Solidity file. If it does not match, the compiler issues an error.
  • Contract: Contain all the data in state variables and functions that can modify these variables. Basically the heart and brain of a smart contract.

first.png

Now you know the basic struct, but contracts can have too: import, interfaces, and libraries. We go more deeply in other posts about it.

Inside Contracts

Each contract can contain declarations of State Variables, Functions, Function Modifiers, Events, Errors, Struct Types, and Enum Types. Furthermore, contracts can inherit from other contracts. Lots of things huh?

It’s nice to organize a contract in this order:

  1. State Variables
  2. Events
  3. Function Modifiers
  4. Struct, Arrays, or Enums
  5. Constructor
  6. Fallback function
  7. External visible functions
  8. Public visible functions
  9. Internal visible functions
  10. Private visible functions

This is an example:

example.png

This is a consistent structure of a basic contract! In the code comments, I also put the name styles to avoid confusion. But now, let’s put some code inside the functions and see some more styles:

finalexample.png

Let's comment each line.

But for now, without many details about every little type, there will be other more detailed posts.

Here we declare, inside the contract, a few state variables of different types. You can see an address, boolean, string and uint256.
I initialized the value of price with 0.01 ether, and the BLOG_NAME with Ian's road to Web3.

1 - state variables.png

In this line, we declare an event called newMember and when it is called, store their arguments sender and nickname on the blockchain.

2 - event.png

The modifier function onlyOwner requires the EOA (Externally Owned Account) who wants to use (msg.sender) have an address equivalent to the owner we have created. If the requirements don’t be attended to, then the function reverts the transaction and throws a message “Only owner”. And if attend, the underscore tells the contract to execute the rest of the code.

3 - modifier.png

Inside the struct we create a group of state variables, a name, wallet, and balance. Right below we create an array of this struct, so we can save which User.

4 - struct array.png

A mapping type uses the syntax key => value. Inside the isMember, we will pass an address and the mapping will return us if is a member or not.

5 - mapping.png

Constructor function is executed only one time when the contract is created, and can’t be called afterward. Inside the constructor, we passed a value for the state variable owner, the EOA who create the contract (msg.sender) will be de owner, so he will have the permission to call functions that have the modifier function onlyOwner.

6 - constructor.png

The receive function is the way you can send ether to this contract without sending any data or via the payable function.

7 - receive.png

Now the functions :D

The first external function called setMember receives a parameter _name of type string. The memory word specifies the location of the data. This is also payable, so you can send ether to the contract via this function.
The first line requires a condition of the value sent, by calling this function, to be more or equal to the variable price (0.01 ether). If low, reverts the transaction and throws "insufficient funds", and if attends the minimum value then execute the rest of the function.

8 -  first function external.png

The next line, adds the msg.sender to the isMember mapping. Now, the contract knowsmsg.sender is a member. Next, the new member is added to the users array with the information from the User struct.
Finally, is emitted to the blockchain the address of the new member and the name he chose.


The second external function stopMessages can only be called by the conditions specified by the modifier function onlyOwner. Inside we call another function pause(), below we will see what this function does, but you can imagine by the name hehe.

9 - function modifier.png

Did you note how the style of this function was written? We put the public and returns keywords right below the function. This is the style indicated for long-named functions by the solidity documentation.

The sendMessage() is a public function, any EOA or contract can access this function. It receives a parameter _message and returns a string. The first line requires that the variable paused be false to execute the next lines, otherwise it reverts. In the next line we put the value of _message on the message, and the next, call addMessage() that will see below. And finally returns a message with the new value.

function sendMessage.png

The addMessage() is an internal function, and can only be called inside this contract or inside contracts that inherit this contract. When it's called, increments the totalMessages variable. (that's why we used this function on the third line of sendMessage function).

11 - addMessage.png

Finally, the private function pause(), it's can only be called inside this contract. When the owner calls the stopMessage() function, pause() is called, and change the value of the pause to true. This will stop the execution of sendMessage() function because the condition will not be satisfied.

12 - pause.png

Did you notice any vulnerability or inconsistency within the contract? No?

Maybe you'll look again and find it because I purposely left some loopholes that can disrupt its functioning, you can comment on them below (:

So we're done here!

Today we learned a little more about structuring a smart contract and its writing styles for the different types of variables and keeping it well organized so that it is easy for everyone to understand. If you liked it and found it useful, share it with your solidity beginner dev group!

See you in the next post!

Did you find this article valuable?

Support Ian Flexa by becoming a sponsor. Any amount is appreciated!