Today I will guide you through the basic aspects of Solidity – programming language defined for writing smart contracts. Even though this article is “for beginners” I assume you have some experience with object-oriented programming  (getters&setters, error handling and inheritance aren’t mysterious concepts to you) and you want to expand your skills into this new and fascinating field of programmable agreements. Starting with just a basic layout we will, with every iteration showing you a new aspect of Solidity, gradually morph our code into a fully functional smart contract. But before we jump straight into the action – have you ever taken part in so-called “Ethereum giveaways”?  Well, I hope your answer was a big NO because all of them are Twitter scams. Scammers, pretending to be a major figure in a crypto world, promise you to send back ten ethers for every ether you send them first.  They are so common that Vitalik – founder of Ethereum changed his name to Vitalik “Not giving away ETH” Buterin. A snapshot of that scam looks like that:

Scammer pretending to be Charlie Lee (founder of litecoin) giving away ethers. Don’t be gullible. Always check what is after @ on Twitter. And remember – no one will give you something for free!

 

Wait a second! Are you going to teach me how to pull off a scam? On the contrary – I will show you how to stop them. Let me explain why…

There are two types of accounts on the Ethereum network: external and internal. For simplicity sake, we can say that external accounts are for user wallets and the internal accounts are for smart contracts. A wallet account is controlled only by the user. If you send him some ethers you need to trust him that he will do what he’d promised.  See the main problem here? Smart contracts, on the other hand, are not controlled by the user but by the code executed by the internal software called Ethereum Virtual Machine (EVM). If a smart contract is programmed to send you 10 ethers for every ether you send first you don’t need to trust it. You can be certain that it will do it (obviously, such a smart contract will never be launched on a mainnet, cos, unfortunately – spoiler alert, Santa Claus doesn’t exist).  I hope now you can see why smart contracts are so powerful. They can completely eradicate scams. No “ETH giveaway” will ever be constructed as a smart contract.  Two sum up in two points why it is crucial for you to learn at least basics of smart contracts:

  1. They are the future. Making agreements trustless solves problems in so many areas such as e-commerce, loans, paying dividends and many more.
  2. Obviously, like with any other contract, before agreeing to it you should read it! You can check out the code of any smart contract deployed on the Ethereum blockchain. By doing so you will gain certainty what it will do.

Okey, are you excited? We are about to create a smart contract which will finally silence all the scammers! IDE we are going to use for our smart contracting in Solidity is browser-based Remix. Remix allows you an easy compilation and debugging of Solidity. Through Web3.js 1 we can deploy our smart contract on the Ethereum blockchains (either mainnet , testnets or even our private one) or simply test it in a local, sandbox virtual machine executing JavaScript.  I advise you to use JavaScript VM  as your first run environment (in a Run tab we select JavaScript VM as environment). Before we start coding one last pointer about Solidity architecture:

Solidity is a contract-oriented language compiled to a byte-code which is then executed on the Ethereum Virtual Machine. If it sounds like Java to you, you are quite spot on. There is a difference between JVM and EVM though. When JVM is a local machine executing local code EVM is like a public super machine exacuting public code (stored on a Ethereum blockchain). You can think of EVM like of thousands sub-computers executing the same code and together creating a one powerful computer. It is not powerful in terms of computing power or speed but powerful in terms of distributed governance. If thousands of sub-computers are outputting the same result no single entity can change how it works. No one can also turn off EVM cos it is not located in one physical location (every Ethereum full node has its own EVM running independently).

Smart contract layout

Disclaimer: I will keep the code as simple as possible which usually means not safe-enough for the real-life purposes – especially when there is real money involved.

Okey, first lets create the layout of our smart contract:

pragma solidity ^0.4.24;

contract ETHGiveAway {

    constructor() {
         //This is the contract constructor
    }

    function (){
        // a fallback function
    }

}

The first line of our contract should always be a pragma version, which tells the compiler the version of Solidity we used. Since Solidity is a new, constantly evolving language, major updates aren’t backwards-compatible. Smart contract written in Solidity v. 0.4.x not be complied with a compiler v. 0.3.x nor with v. 0.5.x. After the pragma version we declared a constructor and a  function without a name – so called fallback function.

The constructor is a special function that is only used once – during contract’s deployment. It initializes the state of our contract. Constructor declaration is optional. Side note: in the previous version of Solidity construct declaration was different – in our example it would be function ETHGiveAway(). But since v. 0.4.22 recommended semantics is to use constructor  keyword. It helps with potential bugs when contract name is changed without updating the constructor.

Our next function without a name is called a fallback function which is executed if the transaction upon which contract is called doesn’t specify any function or if the specified function doesn’t match any of the function identifiers of our smart contract. Simply speaking if the transaction wants to call a function which doesn’t exist or doesn’t want to call any function our fallback function will be called. In practice, a lot of transactions only send ethers to our smart contract without calling any specific methods. They will be dealt inside our fallback function. Smart contract can have only one fallback function.

Payable modifier

Having this new knowledge is about time to compile our smart contract,  send 1 ether to it and see what happens. If you are not familiar with Remix the following gif might help you greatly:

How to compile our smart contract and send a transaction with 1 ether to it which causes an error 🙁

So we are getting an error:
transact to ETHGiveAway.(fallback) errored:
VM error: revert. revert The transaction has been reverted to the initial state.
Note: The constructor should be payable if you send value. Debug the transaction to get more information.

Looks like we need to add a payable modifier somewhere. In Solidity we use modifiers to change function behavior. We can use built-n modifiers like payable or create our own (which we will later in this article!). From Solidity v0.4 if we need to send ethers to our smart contract we need to have at least one payable method.

pragma solidity ^0.4.24;

contract ETHGiveAway {

    constructor() {
         //This is the contract constructor
    }

    function () payable{
        // a fallback function
    }

}

As you can see we added the payable modifier to our fallback function which will be invoked by any transaction sending ethers and not specifying any other method. Just give it a go and try to deploy our new code and send again 1 ehter to it. Now it works!

Now what we wanna do is to add functionality to our fallback function. Our smart contract has to receive ethers, read how many of them were sent and send 10 times that amount back to the sender. Really, really generous indeed!

function () payable {

        msg.sender.transfer(...);

}

Msg object represents the transaction that triggered the execution of our smart contract. Sender attribute returns the address of the original sender (it can be either a human wallet address or an address of another smart contract). Transfer is a built-in function that transfers a specific number of ethers to the address it is called from. So what this line does is it sends ethers to the address which invoked this smart contract (sent the transaction to it). Know the last thing we need to know is how much ethers we want to send back as our giveaway. We promised to send back 10 times the original amount. So first we need to retrieve how much ethers was sent to our smart contract, multiply it by 10, and put is a parameter of transfer function. All that can be achieved by this code:


function () payable {

        msg.sender.transfer(msg.value*10);

} 

Msg.value is a number of wei sent to the smart contract. Wei is the smallest denomination of ether – 1 ether is equal to 10^17 wei (if you want to learn more go to my article What Is Ethereum Gas And Why We Need It? ). After we got the sent value we multiply it by 10 and send it back to the original sender.  Now let’s try to deploy it and then send 1 ether to it.  Did it work? Seriously? I really want you to stop reading now and try it out because it is not working again! It again returns the same error message as prior to setting our fallback function as payable. What the heck? As you can see a VM machine returns a revert error which reverts the transaction. All the ethers which were sent to this contract are reverted back to the original sender. Can you think about the reason why?? Well, this smart contract cannot send  10 times the original ethers back cos in its balance it has none (to be more precise – before the revert error – it had only 1 ether we send to it with our transaction).  Our smart contract cannot print ethers out of thin air (it’s not a government or a bank).  How we can send an initial amount of ethers to our smart contract? During deployment. Deployment of a smart contract is actually just a function which can also send some ethers to it. What happens to that initial deposit can be declared in the constructor which also needs to have a payable modifier. Our working code now should look like:

pragma solidity ^0.4.24;

contract ETHGiveAway {

    constructor() payable {
         //This is the contract constructor
    }

    function () payable{
        msg.sender.transfer(msg.value*10);
    }

}

Let’s try it now:

How to set an initial balance during contract’s deployment so we can send some ehters back. Notice that we’ve added payable modifier to our fallback function.

If there is an insufficient balance for the transfer method an implicit error is thrown. However, it might be better to explicitly check whether our smart contract has enough balance to complete the transfer method.  By doing so we can attach a message to our error which will help us understand what went wrong if our transaction would be reverted. There are a couple of built-in functions in Solidity handling errors: assert, require, rever and (with current version obsolete) throw. In our example when we want to check an input (msg.value) we should use require:

function () payable{
        
        uint valueToTransferBack = msg.value * 10;
        require(this.balance >= valueToTransferBack, "Insufficient balance");
        
        msg.sender.transfer(valueToTransferBack);
}

Require as a first argument takes a boolean value and as a second (optional) error message. If the condition is false then it throws an error with the provided message. Although explicitly checking required condition makes your code easier to understand and by throwing a custom error message easier to debug, you have to keep in mind that every instruction in Solidity costs gas. Every smart contract programmer needs to decide for herself what is the right balance between gas consumption and error checking.

Additional payable and a getter function

Our smart contract, even though it works, is only a rough, extremely simplified version. Now let’s try to improve it. We probably may want to have the ability to deposit more funds for our giveaway (now we can only deposit once – during contract deployment).  In order to do that we need to add new payable function deposit. We may probably want to have a method which will tell us what the current balance is. We will do all that in one go:

pragma solidity ^0.4.24;

contract ETHGiveAway {
    
    constructor() payable {
         //This is the contract constructor
    }

    function () payable{
        uint valueToTransferBack = msg.value * 10;
        require(this.balance >= valueToTransferBack, "Insufficient balance");
        
        msg.sender.transfer(valueToTransferBack);
    }
    
    function deposit () payable {
        //every ether sent to this method are 
        // automatically added to our contract balance 
    }
    
    function getBalance() returns(uint){
        return this.balance;
    }

}

We added an empty payable function deposit.  The value of a transaction invoking this method will be automatically added to our contract balance. In order to get the balance we added also a getter method – take a good look at its structure. In Solidity we can define whether our int should be signed (int having also negative values) on unsigned (uint – only natural numbers).

Custom modifier

Now, how about adding more functionality to our deposit method? Let’s say we want only the creator of this contract to have the permission to add more ethers into the contract’s balance. We can do that an easier way using only built-in require function. Require checks whether the set requirements are met. It takes a boolean argument and if it’s true does nothing but if it’s false it throws an error and reverts the whole transaction. Here how it could be done:

pragma solidity ^0.4.24;

contract ETHGiveAway {
    
    address owner;
    
    constructor() payable {
         owner = msg.sender;
    }

    function () payable{
        uint valueToTransferBack = msg.value * 10;
        require(this.balance >= valueToTransferBack, "Insufficient balance");
        
        msg.sender.transfer(valueToTransferBack);
    }
    
    function deposit () payable {
        require(owner == msg.sender);
        //only ether send by the owner are accepted
    }
    
    function getBalance() returns(uint){
        return this.balance;
    }

}

 

Address data type stores the Ethereum address. In the constructor we get the owner’s address from the msg object (remember, creating a smart contract is done by sending a transaction). Now if our deposit method is invoked from a different address than the owners it throws the error stopping the execution of our smart contract and reverts the transaction. There is also a more generic way of achieving that through Solidity modifiers. We already mentioned them when we first introduced payable modifier. Now we are going to write our own ownerFunc modifier which will set a condition on any function modified by it, requiring to be invoked only by the smart contract’s owner:


modifier ownerFunc {
        require(owner == msg.sender);
        _;
    }

One element probably caught your attention – this peculiar underscore followed by the semicolon  ‘_;’. It is a placeholder for the modified function. Any function body modified by our ownerFunc modifier will be placed instead _;.  Now we only have to add it to our deposit method:


function deposit () payable ownerFunc{
        //only ether send by the owner are accepted
    }

As you can see the modifier encapsulate the function modification really neatly and we can apply it to any function we want, for example we may want to create a method deleting the smart contract and sending any remaining balance to the specific address. It can be achieved by the special EVM opcode called SELFDESTRUCT, which in Solidity is invoked by the built-in function of the same name taking only one argument – address on which any remaining balance will be sent. On top of that, we want only owner to have the permission to call this function.


function destroy(address _addr) ownerFunc {
    selfdestruct(_addr);
}

Our destroy method has only one argument – the address on which remaining balance will be sent. Also, by applying ownerFunc modifier we are certain that only the owner can invoke this method. All our conditions are met then! The code of our giveaway smart contract looks like that now:

pragma solidity ^0.4.24;

contract ETHGiveAway {
    
    address owner;
    
    modifier ownerFunc {
        require(owner == msg.sender);
        _;
    }
    
    constructor() payable {
         owner = msg.sender;
    }

    function () payable{
        uint valueToTransferBack = msg.value * 10;
        require(this.balance >= valueToTransferBack, "Insufficient balance");
        
        msg.sender.transfer(valueToTransferBack);
    }
    
    function deposit () payable ownerFunc{
        //only ether send by the owner are accepted
    }
    
    function getBalance() returns(uint){
        return this.balance;
    }
    
    function destroy(address _addr) ownerFunc {
	    selfdestruct(_addr);
    }

}

Events and logging

We are almost there! The last thing we want to do is to log any deposits. Logging in Solidity is achieved through events.

Events in Solidity can inform JavaScript callback functions  in the user interface that something occured in our contract – in our following example, the owner deposited more ethers. When they are triggered their arguments are stored in the transaction’s log making them also a logging mechanism.

We declare them with the keyword event and a set of arguments. Our even will look like that:

 event DepositLogger(uint amount);

After the declaration of our new event we need to trigger it when the owner makes a deposit. Our final code looks like:

pragma solidity ^0.4.24;

contract ETHGiveAway {
    
    address owner;
    event DepositLogger(uint amount);
    
    modifier ownerFunc {
        require(owner == msg.sender);
        _;
    }
    
    constructor() payable {
         owner = msg.sender;
    }

    function () payable{
        uint valueToTransferBack = msg.value * 10;
        require(this.balance >= valueToTransferBack, "Insufficient balance");
        
        msg.sender.transfer(valueToTransferBack);
    }
    
    function deposit () payable ownerFunc{
       DepositLogger(msg.value);
    }
    
    function getBalance() returns(uint){
        return this.balance;
    }
    
    function destroy(address _addr) ownerFunc {
	    selfdestruct(_addr);
    }

}

Inheritance

As our last improvement to our smart contract we will implement inheritance which will increase extensibility and modularity. Untangling more general parts of your code and making them available for a potential reuse is always a really good programming practice.  In our code we can see that modifier ownerFunc has more general functionality and it could also be applied to other contracts. Let’s untangle it then from our ETHGiveAway  and create a new contract for it named owned.

contract owned {
    
    address owner;
    
    modifier ownerFunc {
        require(owner == msg.sender);
        _;
    }
    
}

Owned contract encapsulates only the functionality of restricted access by ownerFunc modifier. Now, in any of our future smart contracts we will want some methods to be accessible by the owner we can simply extend it functionality with inheritance from contract owned. In order to do that we have to use keyword is.


contract OurNewContract is owned { 
}

Now, you probably have a good assumption how our updated code will look like. We move all the logic of restricting access modifier to the owned contract which will be the parent of our ETHGiveAway contract:

pragma solidity ^0.4.24;

contract owned {
    
    address owner;
    
    modifier ownerFunc {
        require(owner == msg.sender);
        _;
    }
    
}

contract ETHGiveAway is owned {
    
    event DepositLogger(uint amount);

    constructor() payable {
         owner = msg.sender;
    }

    function () payable{
        uint valueToTransferBack = msg.value * 10;
        require(this.balance >= valueToTransferBack, "Insufficient balance");
        
        msg.sender.transfer(valueToTransferBack);
    }
    
    function deposit () payable ownerFunc{
       DepositLogger(msg.value);
    }
    
    function getBalance() returns(uint){
        return this.balance;
    }
    
    function destroy(address _addr) ownerFunc {
	    selfdestruct(_addr);
    }

}

And this is it! We finally have our fully-functional giveaway contract. The last thing is to test whether all our modules work properly:

We deploy our smart contract with the initial balance of 8 ethers. Then we try to send 1 ether to it which returns an error “insufficient funds” – our smart contract has to send 10 ethers back but its balance at the time of execution equals to only 9 ethers. We want to correct that mistake by depositing one more ether and trying again. Now it goes without an error and correctly sets our contract balance to 0.

 

Now if you ever see an “ETH giveaway scam” again just simply inform the scammer that you are more than happy to send your ethers but only if his giveaway will have a form of the smart contract we wrote today. I can guarantee that none of them will agree to that. That shows the strength of Ethereum smart contracts – you don’t need to trust anyone or anything but the code.

  1. Official Ethereum javascript API
Pawel Bakiewicz

Pawel Bakiewicz

Software developer, blockchain advocate and proponent of cryptocurrencies as our financial future. If you have any queries please contact me at admin@blockchained.blog.