A Gentle Introduction to Ethereum Programming, Part 3

Photo by Vladimir Kudinov on Unsplash

Thank you for your interest in this post! We’re undergoing a rebranding process, so please excuse us if some names are out of date. Also have in mind that this post might not reference the latest version of our products. For up-to-date guides, please check our documentation site.

This is the third part of our Ethereum introduction guide. If you haven’t read parts 1 and 2, I highly recommend them to better understand this post.

Enjoy, and please do not hesitate to reach out with questions, corrections or feedback.

Index

1. Taking the first steps

2. Interacting with a contract

3. Frameworks & Tools in the real world

3.1. Deploying with Truffle

3.2. Testing smart contracts

3.3. OpenZeppelin

4. A real DApp, a token marketplace — coming soon

3. Frameworks & Tools in the real world

As you may have noticed, most of the work that we’ve been doing was pretty manual. Although this is a young industry, there are some tools that will make development easier. Let’s see some of them.

3.1. Deploying with Truffle

Until now, the only way we used to interact with our contracts was to deploy them manually through a Node console into a testrpc node and then load them using Web3. Now, let me introduce Truffle to you. It is an Ethereum development framework that will help us debugging, deploying, and testing smart contracts, among other things.

The first thing we’re going to do is to deploy a contract using Truffle. Let’s create a new directory for this exercise and run the following commands to install Truffle and initialize our project:

$ mkdir truffle-experiment
$ cd truffle-experiment/
$ npm install truffle@4.0.4
$ npx truffle init

You will see some folders and files were created. Our directory should look like:

truffle-experiment/
├── contracts/
│   └── Migrations.sol
├── migrations/
│   └── 1_initial_migration.js
├── test/
├── truffle.js
└── truffle-config.js

The contracts folder is where the smart contracts should be. The migrations folder will host javascript files that will help us deploying our contracts to the network. You may also have seen a Migrations contract in the first folder, this is where the history of our migrations is going to be stored on-chain. The test folder is initially empty and is intended to keep our test files. Finally, you will see a truffle.js and a truffle-config.js files. We will skip them by now, but you can read more in their documentation.

Now, let’s leave that boring stuff behind and focus on the interesting parts. To see an example of how we can deploy a contract using Truffle, we can use the same token contract example from the previous post of this guide. Please grab that code and paste it into a MyToken.sol file inside the contracts folder. Then, create a new migration file called 2_deploy_my_token.js file and copy the following lines into it:

const MyToken = artifacts.require('./MyToken.sol')
module.exports = function(deployer) {
  deployer.deploy(MyToken)
}

As you can see, that migration will just deploy our token to the network. This time, we won’t need a testrpc node running since Truffle already comes with a simulation node for development and testing purposes. We just need to open a development console running npx truffle develop and run the migrations using truffle migrate inside it. Then, you should see an output like this:

truffle(develop)> truffle migrate
Using network ‘develop’.
Running migration: 1_initial_migration.js
Deploying Migrations…
… 0xf5776c9f32a9b5b7600d88a6a24b0ef433f559c31aaeb5eaf6e2fc5e2f7fa669
Migrations: 0x8cdaf0cd259887258bc13a92c0a6da92698644c0
Saving successful migration to network…
… 0xd7bc86d31bee32fa3988f1c1eabce403a1b5d570340a3a9cdba53a472ee8c956
Saving artifacts…
Running migration: 2_deploy_my_token.js
Deploying MyToken…
… 0xc74019c2fe3b3ef1d4e2033c2e4b9fa13611f3150f8b6b37334a8e29e24b056c

MyToken: 0x345ca3e014aaf5dca488057592ee47305d9b3e10

Saving successful migration to network…
… 0xf36163615f41ef7ed8f4a8f192149a0bf633fe1a2398ce001bf44c43dc7bdda0
Saving artifacts…

We’ll just care about the line MyToken: 0x345ca3e0...305d9b3e10, which tells us the address of our deployed token contract. By default, Truffle initializes the simulation node with 10 addresses with fake ETH as we saw when using testrpc, and we can access this array via web3.eth.accounts. Moreover, it deploys these contracts using the first address of this list (the one with index 0), which means that it will be the owner of MyToken.

Given that Web3 is available inside the Truffle console, you can just run the following commands to check the owner’s balance:

truffle(develop)> owner = web3.eth.accounts[0]
truffle(develop)> instance = MyToken.at('[DEPLOYED_ADDRESS]')
truffle(develop)> instance.balanceOf(owner)

Note: Please remember to replace [DEPLOYED_ADDRESS] by the address of the deployed contract given by Truffle, in this case: 0x345ca3e0...305d9b3e10.

We can also send some tokens to another address and then check the updated balances:

// send tokens
amount = 10
recipient = web3.eth.accounts[1]
txHash = instance.sendTokens(recipient, amount, { from: owner })
// check balances
instance.balanceOf(owner)
instance.balanceOf(recipient)

We’ve seen the recipient account now has 10 tokens! We can search the transaction information with the following line of code:

web3.eth.getTransaction(txHash)

3.2. Testing smart contracts

The next more interesting and useful thing about Truffle is that we can test our contracts. This framework lets you write tests in two different ways: Javascript and Solidity. In this post, we will just learn some basics about JS tests, which is the most used option.

Truffle uses Mocha under the hood as the testing framework and Chai to perform assertions. It doesn’t matter if you are not familiar with these libraries, both are really straightforward and implement a similar syntax to other testing frameworks. You can also read the official Mocha documentation if you want.

That said, let’s start with our first test case. We’ll need to create a MyToken.js file inside the test folder. Once you have done that, please paste the next chunk of code into it:

const MyToken = artifacts.require('MyToken')
contract('MyToken', accounts => {
  it('has a total supply and a creator', async function () {
    const owner = accounts[0]
    const myToken = await MyToken.new({ from: owner })
    const creator = await myToken.creator()
    const totalSupply = await myToken.totalSupply()
    assert(creator === owner)
    assert(totalSupply.eq(10000))
  })
})

To run Truffle tests you just need to use the command npx truffle test. Again, there is no need to have running a rpc test node in background since Truffle will do that for you.

As you may have noticed, this is the second time we use artifacts.require() in our code. The first time was to write the MyToken migration, and by now you might be wondering what it means. Artifacts are the result of compiling every contract separately. These will be placed in the build/contracts/ directory relative to your project root. Through artifacts.require() is how we tell Truffle which contract we’d like to interact with. Just provide the name of a contract and get an abstraction to use it. You can read more about Truffle artifacts here.

The only important thing left is the contract() function, which is really similar to Mocha’s describe() function. This is how Truffle guarantees a clean-room environment, it will redeploy your contracts to your Ethereum client and provide a list of the available accounts in it every time it gets called. We do not recommend using deployed instances of contracts for tests, though. It’s better to have each test manage their own instances.

Now that we know some basics about testing with Truffle, let’s add another interesting scenario. We will test token transfers between accounts:

it('allows token transfers', async function () {
  const owner = accounts[0]
  const recipient = accounts[1]
  const myToken = await MyToken.new({ from: owner })
  await myToken.sendTokens(recipient, 10, { from: owner })
  const ownerBalance = await myToken.balanceOf(owner)
  assert(ownerBalance.eq(9990))
  const recipientBalance = await myToken.balanceOf(recipient)
  assert(recipientBalance.eq(10))
})

Finally, it would be good to add some other edge cases, but I will leave that to you. You can also see how I finished this mini DApp using Truffle with the rest of the test cases here. You’ll see that I just tackled the same features as we did for the app in the previous post. The only thing that changed is that we’re using Truffle to launch a testing node, deploy our contract and add some tests to make sure our contract works the way we expected.

3.3. OpenZeppelin

If you got here I’m almost sure you’ve heard about OpenZeppelin. If you haven’t, you just need to know that it is the most used framework that helps you build smart contracts. It is an open-source framework that provides reusable smart contracts to build distributed applications, protocols and organizations, reducing the risk of vulnerabilities by using standard, tested and community-reviewed code.

Given the huge amount of token contracts, the Ethereum community created a token standard called ERC20 two years ago. The idea was to allow DApps and wallets to handle tokens across multiple interfaces and DApps in a common way.

That said, it is understandable that some of the most used OpenZeppelin contracts are ERC20 implementations. And this is what we are going to do with our MyToken contract as a first step: make it ERC20 compliant. Let’s install the OpenZeppelin framework first, to do so we will need to run:

$ npm install zeppelin-solidity

Now, take a look at the new implementation we built using some of OpenZeppelin’s contracts:

import 'zeppelin-solidity/contracts/token/BasicToken.sol';
import 'zeppelin-solidity/contracts/ownership/Ownable.sol';
contract MyToken is BasicToken, Ownable {
  uint256 public constant INITIAL_SUPPLY = 10000;
  function MyToken() {
    totalSupply = INITIAL_SUPPLY;
    balances[msg.sender] = INITIAL_SUPPLY;
  }
}

As you can see we have removed a lot of core functionality. Well, we haven’t removed it, we are just delegating that behavior to the OpenZeppelin contracts. This is really useful since we’re reusing secured and audited code, meaning that we have reduced the attack surface of our contracts.

Moreover, you may have noticed that we’re extending our token contract from two OpenZeppelin contracts: Ownable and BasicToken. Yes, Solidity supports multiple inheritance, and it is really important for you to know that order matters. Unfortunately this is out of the scope of this post, but you can learn more about it here.

As we said, we are extending MyToken from Ownable. Let’s take a look at this contract:

Zeppelin Blog
OpenZeppelin Ownable contract

Ownable provides three main functionalities:

  • It holds a special address we’ll call its “owner”,
  • It allows us to transfer the ownership of a contract, and
  • It provides a useful onlyOwner modifier that will guarantee that a function can only be called by the owner.

Pretty useful, isn’t it? On the other hand, we are extending the BasicToken contract too. Let’s see how that one works:

Zeppelin Blog
OpenZeppelin Ownable contract

I’m sure you are more familiar with this code. It is basically what we were doing inside MyToken contract. There are some minor differences because we were not following the ERC20 standard in the original version. What we called sendTokens here it is just transfer, which implements pretty much the same behaviour besides triggering the Transfer event.

Another important thing is the using SafeMath for uint256 line. SafeMath is a library proposed by OpenZeppelin to do mathematical operations with safety checks. This is another of the most used contracts since it guarantees that math operations don’t overflow.

OpenZeppelin is a whole world in itself, please take the time to analyze and learn it deeply. You can start by reading and paying careful attention to the security details of the audited and reviewed codebase.

We have learned two amazing tools of the Ethereum world that will definitely make your development easier. Truffle will help you testing and deploying, and OpenZeppelin will help you writing secure smart contracts through an audited codebase.

Thank you for reading this post, please remember that any question, feedback or suggestions are welcome! If you liked it, stay tuned for the fourth part of this guide, focused on building a real DApp, a token marketplace!