Solidity Smart Contract Anatomy

Jeroen Ouwehand

Today I will explain you the anatomy of a sample Solidity smart contract. This article will be a brief explaining so you can get an idea what is used inside a Solidity contract, no in-depth stuff. My code is not perfect! So if you find something or have questions, please reach out to me.

What is Solidity:

“Solidity is a contract-oriented, high-level language for implementing smart contracts. It was influenced by C++, Python and JavaScript and is designed to target the Ethereum Virtual Machine (EVM).”

The example contract is called “Freelancer.sol”, the idea is that a freelancer can set:

  • Own data
  • Own assets

A freelancer can get:

  • A look at his own account data.

A freelancer has:

  • A first name
  • A last name
  • An amount of ‘coins’
  • An amount of ‘cash’
  • And offers a ‘service’


A solidity contract always start with pragma solidity [version];

The latest version at this moment is 0.4.24 which we are using in this contract. Never set a caret (^) before the version number, because this automatically will update your contract to the latest version which can break the contract. In this example, we are using two experimental versions. Which are optional. By setting pragma experimental “v0.5.0”; we are using already a yet to be released version so we can use already the newest features. The last line pragma experimental ABIEncoderV2; is needed because we will return a struct in a function (will be shown in the function section).


After setting your version you come to the next part, which is make a contract with the name Freelancer and adding global variables. The contract can be called with the name that is given. A variable is consists out of a [type] [function_call] [variable_name].

Inside the contact we are declaring four variables:

  • address public owner;
  • uint256 private coins;
  • uint256 private cash;
  • bytes32 private service;

As you can see we use public and private, besides these two you can also use internal or external.

  • external — can’ t be accessed internally, only externally (saves around 50% gas compared to public).
  • public — everyone can access.
  • internal — only this contract and contracts deriving from it can access.
  • private — can be accessed only from this contract.

I’ve used the bytes32 type instead of the string type to save some more gas.


To get some additional information at each transaction we can log extra data with an event. In this case, we have two events called: logFreelancerChangedand logAssetsChanged. An event can have three indexed items. With indexed, you can search for these events using the indexed parameters as filters. To use an event you can do this by emit [event_name(parameters)];.


With structs you can define a new type, each freelancer will have his own FreelancerData. In this example we want to have some data of a freelancer consisting out of:

  • bytes32 firstname;
  • bytes32 lastname;
  • uint256 coins;
  • uint256 cash;
  • bytes32 service;


A mapping consists out mapping([key_type] => [value_type]) [mapping_name];

“Mappings can be seen as hash tables which are virtually initialized such that every possible key exists and is mapped to a value whose byte-representation is all zeros: a type’s default value. The similarity ends here, though: The key data is not actually stored in a mapping, only its keccak256hash used to look up the value.”

We made a mapping which is called FreelancersData (note the plural). We can use the address to get the FreelancerData of the freelancer we want to use.


A modifier is most of the time a code which can be used multiple time, this can be added to a function. We made a modifier called onlyFreelancer(). This gives only the owner of the data access to his/her own data. If someone else is trying to get access to the data, they will get a message “Sender not authorized”.


The constructor executes by creating the contract, it always needs to be set to public. We set msg.sender as the owner, give freelancer zero coins and cash and give an empty service role.


Functions are executed when being called. In our contract we declared three functions: setFreelancer(), setAssets() and myAccount(). In the code you can see the comments which give a description of what every function does, contains and returns.

A function looks like this:

  • function
  • [function_name([parameters])] —name of the function
  • [external/public/internal/private] — give the amount of access.
  • [modifier: optional] — give a modifier you created earlier
  • [view/pure/payable: optional] — make it more strict or give a fallback.
  • returns ([type] [name: optional]) — what to return inside the function.
  • { [function_code] } . — your code which going to be executed upon calling.

Inside the myAccount() function we also made use of memory:

“memory, of which a contract obtains a freshly cleared instance for each message call. Memory is linear and can be addressed at byte level, but reads are limited to a width of 256 bits, while writes can be either 8 bits or 256 bits wide. It is expanded by a word (256-bit), when accessing (either reading or writing) a previously untouched memory word (ie. any offset within a word). At the time of expansion, the cost in gas must be paid. And it is more costly the larger it grows (it scales quadratically).”


You can easily test out this smart contract online with Remix. The complete code of Freelancer.sol:

Thank you for reading! Check my Github


you might also like