n*rd*nc*d

Let's write our first Solidity contract

Why should you learn Solidity?

Solidity is is an object-oriented programming language for implementing smart contracts on various blockchain platforms, most notably, Ethereum. Programs in Solidity run on Ethereum Virtual Machine.
Wikipedia

Mastering Solidity is a must for anyone who wants to be a Blockchain developer. Many prevalent cryptocurrencies and blockchain platforms such as Avalanche, Binance smart chain, Tron and Polkadot are compatible with Ethereum Virtual Machine (EVM). Thus Solidity works out of the box.

Even if you want to build a dApp on other blockchain platforms that don't use Solidity, your knowledge can be easily transferred to other smart contract languages.

breathe...

Let's use Remix

Remix IDE allows developing, deploying and administering smart contracts for Ethereum like blockchains. It can also be used as a learning platform.
— Remix project

Go to Remix IDE and clean up all the folders. Make sure to have a folder called `contracts`. Inside this folder, create a new file called UserStorage.sol.

At the top of your Solidity code, you must add the version of Solidity you are using. Solidity is constantly changing and updating the language. Therefore you need to state which version of Solidity you are using for this specific contract.

pragma solidity ^0.8.8;

The caret symbol `^` is to tell solidity that you want to use version 0.8.8 and above.

pragma solidity 0.8.8;

This means you want to use a specific version.

pragma solidity >=0.8.8 <=0.9.0;

This means that any compiler between version 0.8.7 and 0.9.0 would work, but not 0.9.1.

Optionally you could also add `SPDX license identifier` to make licensing and sharing code easier. Some compiler might nag-warning you if you don't add this line of code. To learn more about software licenses, you can read here.

For this blog, I'd like to use Solidity ^0.8.8 and choose `MIT` to grant the software end user rights such as copying, modifying, merging, distributing, etc.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;

Great, let's write our first contract!

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {}

When you hit Cmd + S (Ctrl + S) on Remix, you will see that the Solidity tab compiler will show a green checkmark. That means you compile your first contract successfully!

green compiler remix ethereum
Remix: Green checkmark means sucessfully compiled.

You could deploy this contract, and it would be a valid contract. It would be a contract that doesn't really do anything, but technically speaking, you could.

breathe...

Solidity types

Solidity has multiple different types. Boolean, uint, int, address, and bytes are the most basic types that are often used. To learn more, you can check out solidity's documentation about types.

Boolean

Returns `true` or `false`. You can use various operators to compare.

String

A string is actually a byte object but only for text.

Uint

It's an unsigned integer, meaning it's a whole number that is not positive or negative. Just positive. Type `uint` is unique because you can specify how many bits we want to allocate for the value we keep in the variable from `uint8` up to `uint256`. If you don't specify the bits, it automatically defaults to 256.

Int

It's a signed integer, meaning it's a wholly positive or negative number. Same as `uint`, you can allocate the number of bits from 8 to 256.

Address

It's the address to which people can transfer and send values. An example is your Metamask address.

Bytes

A byte is a unit of memory data equal to eight bits. A bit is the smallest form of data, and it can either be `0` or `1`. In Solidity, bytes are treated like an array, and you can decode the bytes in the front-end. This is good because less data is stored on the blockchain.

The reason why we have these types are to define different types of variables. Variables are holders for different values.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    bool isHuman = true;
    string callMe = "Ivana";
    uint256 luckyNumber = 9;
    int256 favNumber = -4;
    address isAddress = 0xa3E2836600fD72a5D20e9EB63F61838C610dF8D5;
    bytes32 spritAnimal = "fox";
}
breathe...

Let's create a simple smart contract!

But first, let's talk about function. Function (or method) is a block of reusable code that can take some input as arguments and returns an output.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
contract MyContract {
    uint256 luckyNumber;

    function show(uint256 _luckyNumber) public {
        luckyNumber = _luckyNumber;
    }
}

Compile and deploy this contract on Remix:

Remix ethereum deploying a contract
Remix: Deploy your contract, click the fourth icon from top .

When you change a different value on the `show` function, you can see something happening to the contract, but you cannot see the new value yet. So how can you see that the transaction is executed successfully?

The reason is that the visibility of our variable `luckyNumber` is set to private. To make our variable `luckyNumber` visible, we must set it to `public`.

...
contract MyContract {
    uint256 public luckyNumber;
    ...
}

When we set the variable to `public`, you will see an extra button where you can see the value of `luckyNumber`.

breathe...

Visibility specifier

There are four types of visibility:

function functionName() <visibility specifier> returns (bool) {
    ...
}
  • Public: visible externally and internally. This creates a getter function for storage/state variables.
  • Private: visible for the current contract and the only.
  • External: visible for external functions only. Only developers outside this `MyContract` can call the function set to `external`.
  • Internal: visible internally. Meaning only `MyContract` and its children can access it. When we don't specify a visibility specifier to a function or variable, they are automatically deployed as internal.
breathe...

Transaction

Remember that every time we change the state of the blockchain, we do a transaction. You can see all the details when you expand the transaction on the logging area of remix.

Expand the transaction on the terminal
Remix: Expand the transaction to see the breakdown of gas costs.

The more complex the function, the more expensive the transaction will cost. You can easily prove this by adding more code to our function `show` and then compiling and redeploying the contract.

// Transaction cost is 20402 gas
function show(uint256 _luckyNumber) public {
    luckyNumber = _luckyNumber;
}

// Transaction cost is 44126 gas
function showAndMultiply(uint256 _luckyNumber) public {
    luckyNumber = _luckyNumber;
    luckyNumber = luckyNumber * 9;
}

No gas: view and pure functions

In Solidity, there are two functions that can be called without spending gas on it. Those are view and pure.

View and pure functions disallow modification of state on the blockchain. Pure function disallows reading from the blockchain state.

For our case, we can use a `view` function to read the state of our `luckyNumber` for free.

function show(uint256 _luckyNumber) public {
    luckyNumber = _luckyNumber;
}

// `view` => Reading the value but not making any changes to the blockchain.
function retrieve() public view returns(uint256) {
    return luckyNumber;
}
Remix: Blue button indicates no gas transaction cost.
Remix: Blue button indicates no gas transaction cost.

You might ask, why would anyone use a pure function if you cannot modify or read from the blockchain state?

// Example of pure function
// calculate the cost based on price and amount
function getCost(uint256 _price, uint256 _amount) public pure returns(uint256 cost) {
    cost = _price * _amount;
}

Generally speaking, pure functions are guaranteed to not have side effects. It is easier to read and maintain. You will see the benefits down the road of your programming journey, and we can go into detail in a different post about the whys. As for now, I just need you to be aware that pure function exists in Solidity.

However...

We are aware now that both view and pure function are gas free. However, if we have a gas cost function that calls view or pure function, this will cost us a gas fee.

// Example of gas cost function that calls a pure function
function show(uint256 _luckyNumber) public returns(uint256)  {
    luckyNumber = getCost(4, _luckyNumber);
    return luckyNumber;
}

// Pure function
function getCost(uint256 _price, uint256 _amount) public pure returns(uint256 cost) {
    cost = _price * _amount;
}
breathe...

Next...

Following the steps above, you have officially written and deployed your first Solidity smart contract 👏👏👏

Read next? "Solidity Structs and Arrays".

✨ I will write more about arrays, structs, bytes, the front-end part of Web 3.0 and eventually a "real world" smart contract project (could be NFT or DAO.. we'll figure it out later together) 🌈

Any tips, feedback, or recommendations? Don't hesitate to reach me. I love learning new things 🤓

breathe...

Helpers