An introduction to Ethereum smart contract development

  • Tutorial

Recently, an unprecedented hype has risen around blockchain, cryptocurrencies, smart contracts and related technologies. One gets the feeling that even the most lazy and passive laymen trumpet this from every swamp. A huge number of startups and companies with a history and experience in the field of IT, services, retail and the real sector of the economy are thinking about introducing blockchain into their activities, and more than 100,000 companies have already done this .

The Initial Coin Offering mechanism not only contributed to a new wave of interest in crowdfunding, increasing the volume of investments attracted with its help, but also spurred interest in cryptocurrencies and blockchain technology. Today, more and more often come across new vacancies in this area, as well as requests for help in conducting ICOs in general and the development of smart contracts in particular.

Unfortunately, today there is a strong lack of specialists in this field, although in reality the process of developing smart contracts is much simpler than it might seem at first glance. The problem of a shortage of specialists stems directly from the fact that blockchain technology reached the mass user not so long ago - in the form of cryptocurrency Bitcoin, and smart contracts came to the blockchain much later. So personally, I was not very surprised by the fact that there are still very few materials on the development of smart contracts.

On the other hand, being the technical director of a company in which we are building a blockchain-based digital jurisdiction and simplifying the process of creating smart contracts for ordinary users who do not have programming skills, I feel the need for technical specialists with an understanding of the technology, and even better - experience working with her.

In this article I want to show that the threshold for entering the development of smart contracts is actually quite low, and try to attract new developers into the sphere. Of course, not everything in life is as simple as in training materials, and there are a lot of subtleties in developing smart contracts. Nevertheless, this is true for any other technology, including JavaScript that is familiar to many, and with this, as a rule, practical experience helps us to cope, which cannot be gained without starting to program, design, experiment, test and do everything that developers usually engaged.

In this lesson, we will install all the necessary tools and frameworks to start development, write our first ERC20 token in Solidity, compile it, publish it on the local blockchain, write a couple of tests and run them. We will use the framework truffle , which will help us with migration management, compilation, dependency management, and testing. In addition, we will use the OpenZeppelin framework , which contains a set of contracts and libraries written in the Solidity language and which have already proved their usefulness and safety over time.

To get started, let's install framework truffle. To do this, simply go to the framework documentation and find the installation instructions for your operating system there. After installation, create a folder for your project. In my case, this folder will be called “jcr-token.” After that, open the created project folder in the terminal and initialize the project using the command truffle init.

Now open the project in your favorite text editor (I use Atom). Let's look at the structure of the project. Initially, you can see three folders in the root directory:

  • contracts , designed to store the source code of our smart contracts;
  • migrations , containing migration files that are used to publish contracts to the blockchain;
  • test containing tests of our contracts. It is important to note that tests can be written both in Solidity and in JavaScript. If something seems incomprehensible to you now, don’t worry, then we will talk about each part in more detail and learn how to create contracts, migrations and tests.


As you may have noticed, truffle has already created a couple of simple smart contracts for us. We use them to test our installation.

In order to interact with contracts and create tests for them, we must first publish them on the blockchain. In our case, I suggest using a light ethereum client called testrpc for these purposes. It simply emulates the normal ethereum network client behavior, but works much faster and consumes fewer resources, which makes it a good development tool. After installation, you can start your own development blockchain simply by typing a command in the terminal testrpc.

After a successful launch, you will see the host and port of your blockchain. Open the `truffle.js` file in the root directory of the project. This file contains the configuration of your project. Make sure that the host and port in the configuration match the host and port of the running testrpc.

Now let's try compiling smart contracts by typing truffle compile. This step will generate files in the build directory. If you run compile for the first time, the directory will be created automatically. Build artifacts will be located in this folder. We will talk about them a little later.


To test the contracts, type truffle test. If you are familiar with unit tests in JavaScript or any other language, then you already know how to test smart contracts. Please pay special attention to the tests, because the price of an error in the blockchain is very high! You don’t even need to pre-compile the contracts before running the tests, as the `test` command does this automatically for you.


And finally, in order to start migrations that publish our contracts on the testrpc blockchain, type truffle migrate.


Soon after, you will see the output of the command in the terminal and the output of the event in the testrpc client. Congratulations! We just published our first smart blockchain network emulator contract.


To create more advanced contracts, we will use the OpenZeppelin framework, which includes several very useful libraries, interfaces, and ready-made contracts. It is worth noting that the Solidity language supports inheritance and we can easily expand the functionality of existing contracts. This opens up great opportunities for thinking through your architecture.

You can install OpenZeppelin just like any other JavaScript library using your favorite dependency manager, for example, npm or yarn. You can find more information on the OpenZeppelin project page on github, or their website.

So we installed everything necessary to start developing our own smart contracts. And it wasn’t so difficult, right?

Now let's move on to the text editor and see the project files. You may notice that truffle already created some basic contracts during initialization. Let's delete all these files, with the exception of the migration files. We will remove the extra contract, library, tests, and also remove some code from the migrations.

Create a new solidity file with the code for our custom smart contract. I will make a token called JCR for conducting the Jincor ICO, which means I will name the file JCR.sol.

Solidity is a fairly young and dynamic programming language. This imposes certain inconveniences, one of which is frequent changes, some of which can break backward compatibility, which, in turn, can cause problems with already published contracts. In order to prevent this, we need to specify the version of the Solidity language compiler. Personally, I will use `^ 0.4.11`.

Note that here `^` means that we are also happy with the newer minor versions and bug fixes, rather than the strictly specified version 0.4.11. You can define much more flexible versioning patterns just like you do in npm.

As I said, we are going to use some contracts from the OpenZeppelin library in order not to reinvent the wheel, but simply to use working, safe practices containing the best practices of the community and regularly undergoing security audits.

To import a contract, you need to use the keyword `import` and then in quotation marks indicate the path to the imported file, as, for example, I did this:` import “zeppelin-solidity / contracts / token / MintableToken.sol"; `.

I highly recommend that you read the OpenZeppelin documentation from cover to cover and look at the implementation of their smart contracts in order to have a clear idea of ​​exactly how they work. In addition, during the study, you will see examples of code design and can find inspiration for writing new contracts. You can find the source in the node_modules directory.

Let's look at the Mintable token that I am going to use today. Now we will not dive into the details very deeply, because you can familiarize yourself with the source codes and documentation without my help. We will just discuss the most important.

We see that "Mintable token is Ownable, Standard token." The “is” keyword means roughly the same as “extends” in Java or PHP. The Mintable token contract adds 2 events, one public property, 2 modifiers and 2 functions. Together, this makes up the functionality for issuing tokens. Standard Token adds the functionality of transferring tokens on behalf of another user with a previously obtained permission. Basic token is just an implementation of the ERC20Basic interface that defines token transfer and balance checking. The Ownable contract adds the onlyOwner modifier, which is usually used to restrict third-party function calls.

Now back to the sources of our contract and modify them a little. After we have imported all the necessary contracts, we call our own contract JCR. Please give the contract files and names the same names.

In the contract, we will define the publicly available name, symbol and number of decimal places (equal to 18, as in Ethereum).

contract JCR is MintableToken {
  string public name = "Jincor Token";
  string public symbol = "JCR";
  uint public decimals = 18;
}

Then we write the constructor code. A constructor is simply a function that is called exactly like a contract, and is called when the contract object is instantiated, or, in other words, when a new instance is created. This is a pretty good place for initialization code. Personally, I want to make a contract that can issue the specified number of tokens and transfer all tokens to the balance of the contract creator. To do this, I simply add the amount argument to the constructor, designate the creator of the contract as its owner, and issue the specified number of tokens to the owner’s wallet.


function JCR(uint256 _amount) {
    owner = msg.sender;
    mint(owner, _amount);
  }

The resulting JCR.sol file (also available on github)

pragma solidity ^0.4.11;
import "zeppelin-solidity/contracts/token/MintableToken.sol";
contract JCR is MintableToken {
  string public name = "Jincor Token";
  string public symbol = "JCR";
  uint public decimals = 18;
  function JCR(uint256 _amount) {
    owner = msg.sender;
    mint(owner, _amount);
  }
}

Now let's try to compile what we got. You can do this by typing `truffle compile`. Let's look at the build folder and see what we got on the output. The first thing that catches your eye is that we received artifacts of all the used contracts from ERC20, to Mintable and JCR. Artifacts are saved in JSON files. These JSON files contain the name of the contract, Application Binary Interface (abi), binary code that will later be launched on the Ethereum Virtual Machine and some additional information.



We can use artifacts to publish our smart contracts on the blockchain. However, it’s better from the very beginning to accustom yourself to the good and immediately automate this routine process by adding migration. Remember that we still have to pass the number of issued tokens to the constructor? Migrations are a good place for such manipulations. Let's open the 2_deploy_contracts.js file and add some code. I am going to issue 1.4 million tokens.

We can publish the contracts in the migration script using the deployer.deploy method, to which we will pass the build artifact as the first argument, followed by the arguments that will be passed to the contract constructor in the same order. In our case, we have only one argument - 1,400,000. Truffle will take care of the rest. To make the deployment process easier and more enjoyable, you can unlock your account (allow you to perform actions on its behalf). To do this, when starting testrpc, add the argument `-u 0`


var JCR = artifacts.require("./JCR.sol");
module.exports = function(deployer) {
  const tokenAmount = 1400000;
  deployer.deploy(JCR, tokenAmount);
};

Launch our new migration and see if it works. Excellent! Now we need to make sure that our token works as we expect from it. To do this, we will write a couple of tests in JavaScript. Type `truffle create test` and the name of the contract under test to generate a JavaScript file with the test. I note that tests can also be written in Solidity, but we will talk about this later.

I hope that you already have some experience writing tests in other programming languages, such as PHP. If you have already encountered the Mocha and Chai frameworks, then everything will seem familiar to you, since truffle uses them. More information can be found in the official documentation.

Finally, let's try to make sure that the owner received the correct number of tokens when creating the contract and the token transfer function behaves as expected.

var JCR = artifacts.require("./JCR.sol");
contract('JCR', function(accounts) {
  it("Create 1 400 000 tokens at the owner account", function(done) {
    JCR.deployed().then(function(instance) {
      return instance.balanceOf.call(accounts[0]);
    }).then(function(balance) {
      assert.equal(web3.toWei(balance.valueOf(), 'ether'), web3.toWei(1400000, 'ether'), "1400000 wasn't in the first account");
    });
    done();
  });
  it('Should transfer tokens correctly', function(done){
    var token;
    var amount = 10;
    var account_one = accounts[0];
    var account_two = accounts[1];
    var acc_one_before;
    var acc_one_after;
    var acc_two_before;
    var acc_two_after;
    JCR.deployed().then(function(instance){
      token = instance;
      return token.balanceOf.call(account_one);
    }).then(function(balanceOne) {
      acc_one_before = balanceOne.toNumber();
      return token.balanceOf.call(account_two);
    }).then(function(balanceTwo) {
      acc_two_before = balanceTwo.toNumber();
      return token.transfer(account_two, amount, {from: account_one});
    }).then(function() {
      return token.balanceOf.call(account_one);
    }).then(function(balanceOne){
      acc_one_after = balanceOne.toNumber();
      return token.balanceOf.call(account_two);
    }).then(function(balanceTwo){
        acc_two_after = balanceTwo.toNumber();
        assert.equal(acc_one_after, acc_one_before - amount, "Token transfer works wrong!");
        assert.equal(acc_two_after, acc_two_before + amount, "Token transfer works wrong!");
    });
    done();
  });
});

That's all for today. In the next lesson, we will try to test our contract in a more real environment, and also write an ICO contract for the sale of our token.

By the way! A video version of this article is available on youtube in 2 parts:

1. Preparation for development
2. We write a token contract

The code is available on github

Also popular now: