Layout of a Solidity Source File

In this article, I am going to explain the fundamentals of a Solidity source file structure. In a way, a rookie (like me and you) can understand the basics of Ethereum programming.

First things first – the Solidity file that stores your code will have a .sol extension.

Take action.

  1. In your favorite browser go to https://remix.ethereum.org/ 
  2. Using Create New File create a new finxter.sol file

Well done. Your file – empty so far – is ready for more actions!

In this article, we’ll add (i) license identifier, (ii) pragma, (iii) another file via import, and (iv) add some comments.

The actual smart contract code is out of scope here but check out other Finxter tutorials – there is plenty of that.

License Identifier

I know you are eager to get to the meat, but before you jump there, bear with me.  The so-called SPDX license identifier is the first element you need to jot down.

What the heck is that? SPDX, or the Software Package Data Exchange, is an international, open standard for communicating software information including licenses or copyrights. 

Being a standard means that many companies and organizations have agreed to do some things in a certain way. And Solidity has also adopted that standard.

Why bother, you ask?

Well, your code will be transparent in a blockchain and that transparency triggers copyright issues. The SPDX identifier hence allows you to specify what you allow others to do with your code. And vice versa, you learn what you can do with other people’s code too.

An example comment line with an identifier would be:

// SPDX-License-Identifier: MIT

What this means is:

πŸ‘©β€βš–οΈ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, [etc etc…]

SPDX license list has over 450 various license identifiers!

But don’t worry too much now, we are here to learn Solidity, not the legal twists. So for now, take my word and use MIT as your default one. Or if you do not want to specify a license or if the source code is not open-source, please use the special value UNLICENSED.

πŸ›‘ Caution here folks – UNLICENSE (without ‘d’ at the end) is a completely different license! Open door type of one. It offers free and unencumbered software released into the public domain.

By the way, a good rule of thumb is to use the OSI-approved ones when browsing https://spdx.org/licenses/. β€œOnly” about 120+ options for consideration.

Does that identifier do anything technically to your code? No, it won’t break how it works. After all, it is a comment.

But from Solidity >=0.6.8 (so 0.6.8 and any higher), that comment must be part of your code. Otherwise expect a Warning in your compiler.

The compiler checks the existence but does not know if your identifier is the right one (if it exists in the SPDX list). Starting in Solidity 0.8.8 it checks for multiple SPDX license identifiers next to each other and validates them. It still allows you to play with it though πŸ™‚

In 0.8.7 you could easily get away with some crazy identifier.

// SPDX-License-Identifier: cheating_on_you

pragma solidity 0.8.7;

From 0.8.8 onwards it actually starts to pay attention and throws errors. Note the red error icon on the left in line 1.

Same happens when you add multiple licenses inappropriately. 

This one below goes unnoticed though – compiler says OK.

// SPDX-License-Identifier: cheatingonyou

pragma solidity 0.8.15;

Since SPDX info is a comment, it is recognized by the compiler anywhere in the file at the file level, but for clarity put it at the very top of the file.

Lastly, the identifier will become part of your metadata once it’s compiled. And that is machine-readable so others will find it easy to query.

Take action.

1. Add the license-related comment to your code (finxter.sol)

// SPDX-License-Identifier: MIT

Pragmas

The second important keyword is pragma. It comes in several shapes and forms – as a version pragma, ABI Coder pragma or experimental pragma.

⭐ Note: A pragma directive is always local to a source file. So you must add it to all your files if you want to enable it in your whole project.

Remember also that if you import another file, the pragma from that file does not automatically apply to the importing file.

1. Version pragma defines for which versions of Solidity the code is written.

In the example:

pragma solidity >= 0.8.7;

we can expect that no compiler on version 0.8.7 or higher will throw any pragma errors.

Other examples – for illustration and education – how to define pragma versions:

pragma solidity 0.6.8;         //single instance
pragma solidity >= 0.6.8;         //0.6.8 and any above
pragma solidity ^0.6.8;         //0.6.8 and any above but less than 0.7.0
pragma solidity 0.6.8 ^0.7.5;     // single instance AND any above 0.7.5 but less than 0.8 -> this AND condition cannot be met here
pragma solidity 0.8.1 || ^0.8.10;     // one instance OR any from 0.8.10 but less than 0.9.0

For practical reasons, the version pragma is the only type you should really care about when you start your Solidity journey.

2. ABI Coder pragma

As per Solidity documentation, you have two options to choose from:

pragma abicoder v1;
pragma abicoder v2;

However as of Solidity 0.8.0 the ABIEncoder is activated by default so for a rookie like you and me, there’s nothing to worry about anymore.

With version 0.8.0+ you can already enjoy the benefits of working more effectively with arrays and structs. These are just some of data types but explaining this goes way beyond this tutorial.

And no need to call this pragma additionally, as you had to do in the past, e.g.:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.4.16;
pragma experimental ABIEncoderV2;

3. Experimental pragma

Getting here is a risky business so better do not try it yourself at home πŸ™‚

Solidity might be offering features that are – as labeled – experimental.

So if you have some technical appetite and skills and want to play, showcase to your potential clients or whatever the purpose, go ahead. But if again, you are still early in the game, just park for now. 

Take Action.

1. Add a version pragma directive to your code

pragma solidity ^0.8.15;

Importing other Source Files

You can import files in Solidity. That sounds obvious but let’s say this upfront.

Importing other files is important since you can break down your code into multiple files, which makes it more modular, easier to manage and control, and – best of all – re-usable.

The simplest way to import is using this line of code:

import "filename";

In our quick Remix exercise, imagine we have another file called β€œhelloWorld.sol” located in the same directory. In order to import it to our finxter.sol file, one would use:

import "./helloWorld.sol";

Note: Pythonic  import "helloWorld.sol" would not work here.

For education purposes and in very simple implementations, this is the shortest way to import. Its disadvantage is that it pollutes the namespace by importing all global symbols from the imported file into the current global system.

That approach carries also a risk of importing symbols that are imported into the file we are importing. That file can contain symbols imported for yet another file and so on. Such subsequent importing may lead to confusion about where the symbols come from and where actually they are defined.

Solidity recommends using a variant, which may look more complex at first. But it only adds one new global symbol to the namespace, here symbolName, whose members are symbols from the imported file.

Makes sense?

import * as symbolName from "filename";

The best approach however would be to import relevant symbols explicitly.

So for instance, if the imported file β€œhelloWorld.sol” would have a contract named β€œsayHello”, one could use only that. Rule of thumb here: import only the things you will use. 

import {something} from "filename";

Take action:

1. Add a new file named β€œhelloWorld.sol” that contains this code

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract sayHello {
    // empty contract
}

2. In the β€œfinxter.sol” file, add import 

import {sayHello} from "./helloWorld.sol";

Comments

Commenting the code is possible in the two following ways:

1. Regular comments

1.1 Single-line comment, e.g.

// this is a single-line regular comment

1.2 Multi-line comment, e.g.

/*
This
comment
spans
many
lines
*/

2. NatSpec comments

NatSpec stands for Ethereum Natural Language Specification. It is a special form of comments to provide rich documentation for functions, return variables, and more. 

The recommendation is that Solidity contracts are fully annotated using NatSpec for all public interfaces (everything in the ABI).

Use NatSpecs comments directly above function declarations or statements.e.g.

2.1 Single-line

/// single-line NatSpec comment

2.2 Multi-line

/**
multi-line
NatSpec
comment
*/

CODE example

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;

/// @author The Solidity Team
/// @title A simple storage example
contract SimpleStorage {
   uint storedData;

   /// Store `x`.
   /// @param x the new value to store
   /// @dev stores the number in the state variable `storedData`
   function set(uint x) public {
       storedData = x;
   }

   /** Return the stored value.
       @dev retrieves the value of the state variable `storedData`
       @return the stored value
   */
   function get() public view returns (uint) {
       return storedData;
   }
}

Take action:

1. Add a regular multi-line comment to your finxter.sol file

/*
This
tutorial
comes
from
finxter.com
*/

Reference: This article is based on some contents from the documentation. Check out this awesome resource too!


Learn Solidity Course

Solidity is the programming language of the future.

It gives you the rare and sought-after superpower to program against the “Internet Computer”, i.e., against decentralized Blockchains such as Ethereum, Binance Smart Chain, Ethereum Classic, Tron, and Avalanche – to mention just a few Blockchain infrastructures that support Solidity.

In particular, Solidity allows you to create smart contracts, i.e., pieces of code that automatically execute on specific conditions in a completely decentralized environment. For example, smart contracts empower you to create your own decentralized autonomous organizations (DAOs) that run on Blockchains without being subject to centralized control.

NFTs, DeFi, DAOs, and Blockchain-based games are all based on smart contracts.

This course is a simple, low-friction introduction to creating your first smart contract using the Remix IDE on the Ethereum testnet – without fluff, significant upfront costs to purchase ETH, or unnecessary complexity.