(Part 7/18 updated) Introduction to Web3.js 1.2 (Latest version) – Beginners Tutorial

When you develop an Ethereum Dapp, once you have the frontend and the smart contract, you need to connect them together. This is not easy. Fortunately, Web3 is here to save you! Wait… not so fast… Actually, it’s not that easy to learn how to use Web3:

  • Many Web3 tutorials use outdated syntax and code examples don’t work..
  • Or perhaps, you really didn’t understand this weird “abi” and “provider” thing..
  • Or perhaps that Web3 breaks EVERY time you update it..
  • And I am not even talking of connecting to wallets like Metamask

What if you could learn a simple and easy way to setup Web3 in your Dapp? What if you knew the exact steps to communicate with your smart contract? What if you could use a stable version of Web3 that work well and does not break between updates?

After you read this tutorial, you will learn all that 🙂 So stay focused.

By the way: I will publish this tutorial in several steps. Today is step 1. Each step will be published at 2 days interval.

  1. What is Web3.js?
  2. Setup project
  3. Install Web3
  4. Web3 versions
  5. Web3 providers
  6. Connect Web3 to Ethereum blockchain
  7. Connect Web3 to smart contract
  8. Reading smart contract data with Web3 – Todo
  9. Writing smart contract data with Web3 (transactions) – Todo
  10. Sending raw transactions with Web3 – Todo
  11. Listening to smart contract events with Web3 – Todo
  12. Listening to blockchain events with Web3 – Todo
  13. Running Web3 in backend vs frontend
  14. Connecting Web3 to Metamask wallet
  15. Dealing with numbers with Web3 – Todo
  16. Unit conversion with Web3 – Todo
  17. Other Web3 features – Todo
  18. Alternatives to Web3 – Todo

1. What is Web3.js?

Web3 is a Javascript Library to communicate with an Ethereum node. It offers an easy way to use the API of Ethereum. Ethereum has many APIs, but the most important ones for Dapp developers are eth_sendTransaction and eth_call. These 2 APIs allow you to interact with smart contracts. Web3.js is especially useful for these 2 APIs.

Web3.js

With these 2 APIs, you can specify:

  • which smart contract to call
  • which function to call
  • which arguments to provide to the functions
  • how many ether to send

In the REST API world, people would generally create a new endpoint for each function. But in the case of Ethereum, it’s impossible, because they are way too many smart contracts and new ones keep getting created. So we need a single endpoint to “multiplex” all the calls to many, many smart contracts and functions:

Web3.js and Ethereum

If you are interested in the technical details of the Ethereum API, it’s a JSON RPC interface, which means that it has a unique HTTP endpoint that receive a JSON payload. After this payload is parsed, Ethereum can know which API is targeted. Example:

// Request
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_call","params":[{...}],"id":1}'

// Result
{
  "id":1,
  "jsonrpc": "2.0",
  "result": "0x"
}

You can find all APIs here.

Using these APIs directly is a bit too low level and requires lot of work. Dapp Developers should use Web3 instead. That’s what we will do in this tutorial.

Oh and by the way, Web3 is an “Isomorphic” Javascript library (YES! I managed to use this word in an article. I feel so smart now!). In other words, it means that it can be used both on the frontend and backend (NodeJS).

In the next section, we are going to setup our project and start the coding!

2. Setup project

NOTE: it’s not strictly necessary to follow this part. You can skip it if you don’t want to code as you follow the tutorial. You can just look at the code examples and adapt them for your project.

In order to use Web3, we need a smart contract to play with, and a blockchain to run it.

For the smart contract, we will use the smart contract of the DAI stablecoin, an ERC20 token. And for the blockchain, we will use Ganache, a local Ethereum blockchain. And for deploying the smart contract to the blockchain, we will use Truffle, a popular framework for smart contract.

Just to be clear: This is NOT a tutorial about truffle, Ganache, ERC20 token or Solidity. You don’t need to understand any of these to follow the tutorial. But we do need to interact with a smart contract, so I prepared a project so that we have something to play with.

Before that, we need to install these tools. They and are all written in NodeJS, and can be installed with npm, the package manager of npm. Follow these steps:

  • Install NodeJS and npm (they are bundled together) by going to the website of NodeJS and using the LTS installer. You can check that you have at least Node v10 with node -v in your terminal
  • Install Truffle globally with npm install -g in your terminal. It will also install Ganache
  • Clone the project folder to your computer (screencast/17-intro-web3/start). To get access to the git repo, create a FREE account on EatTheBlocks pro.

If you are curious, the smart contract is the DAI ERC20 token contracts/Dai.sol.

Next, let’s install Web3, the missing link to the blockchain!

3. Install Web3

Now that we have our project setup, it’s time to add Web3.

Web3 is an open-source library, and its code is available on Github. You could just copy and paste the source from the Github repo of Web3, but that’s a bad practice.

Like for the other dependencies, we can install it with npm. Web3 is actually already installed inside the project we setup just before. So you don’t need to do anything. You can see it by checking the dependencies section of package.json.

But if you need to install Web3 yourself in another project you can do it with:

npm install web3

In the next section, we are going to talk about Web3 versions (Yes, that’s a BIG deal!).

4. Web3 versions

It used to be the BIGGEST frustration about Web3. It’s important to understand this.

Old stable versions of Web3 were on the 0.2.x branch. Then, when work started on the 1.x branch, the first versions were labeled “beta” versions. However, time went by, and… no stable 1.x version ever came out… The “beta” version numbers kept increasing for more than a year, going all the way up to beta-55!

From a version to the next one, you were lucky if your Dapp didn’t break…

Some projects, like Drizzle, stopped at Web3 1.0-beta35, the latest “stable” beta version, and it was ok-ish, but they didn’t get the latest features of Web3.

Fortunately, Web3 FINALLY released a stable version in mid-2019: Web3 1.2.0. ALLELUYA BROTHERS! (and sisters!). That’s what you should be using. And if you like to live dangerously and want the bleeding edge of the development, you can use Web3-beta55 and above. Not recommended unless you develop on Web3 itself.

Up next, we will finally start the coding and learn how to setup Web3!

5. Web3 providers

Before you can use Web3, you need to connect it to an Ethereum node. And for this connection, you need a Web3 provider.

Without a provider, Web3 is like an empty shell that has no way of communicating with the blockchain:

Web3.js no provider

Give it a provider, and tada! The link with the blockchain can be established!

Web3 provider

Providers can use different transports, like HTTP or Websocket. In general, providers use HTTP transports, but for some Ethereum APIs like events, Websocket transport is better.

You won’t have to create your own providers from scratch. You will use providers that either:

  • were injected in your frontend (more on that in the next section)
  • or you will create providers using some helper functions, provided by Web3 itself.

Next we will create a provider and connect it to Web3.

6. Connect Web3 to Ethereum blockchain

It’s time to put our knowledge to good use and start to setup Web3! Before we can interact with a smart contract though, we first need to connect to the Ethereum blockchain. Let’s do this now.

In your project folder, create a script file:

script.js

Inside, we are going to instantiate a Web3 instance. Add this code:

//Import Web3. Notice the uppercase W. Important!
const Web3 = require('web3'); 

//Instantiate web3 here, and give it a provider
const web3 = new Web3(Web3.HttpProvider('http://localhost:9545'));
//Can be abbreviated const web3 = new Web3('http://localhost:9545');

After that, we can communicate with the blockchain with the web3 variable

A few remarks:

  • Web3 and web3 are 2 different things. The first one is the library, and the second is a web3 instance, i.e an object ready to communicate with the blockchain
  • There are 2 ways to specify an http provider. Most of the time, you will use the shortcut, just specifying the url
  • The url of the provider will change depending on how you run your Ethereum Node. The one specified here is compatible with Ganache running with the truffle develop command.

Next, let’s do something interesting with web3!

...
async function run() {
  const id = await web3.eth.getId();
  //Get network id
  console.log(id);
  const accounts = await web3.eth.getAccounts();
  //Get list of addresses generated by Ganache (local development blockchain)
  console.log(accounts);
  const balance = await web.eth.getBalance(accounts[0]);
  //Print Ether balance in wei of first address of `accounts` array
  console.log(balance);
}
run();

A few remarks:

  • All the interactions with the blockchain return a Promise. We can either wait for them with then() or with the await keyword. await is more elegant, but requires to be used inside an async function
  • web3.eth.getAccounts() can also return the list of address of a wallet, if you use a wallet provider (will see this later). The list of addresses of Ganache is a special case that you will not have in production.
  • Web3 returns balance in Wei, not in Ether

This is great to communicate with the blockchain, but the only reason why we do this is because ultimately we want to communicate with our smart contract. That’s what we will do in the next section!

7. Connect Web3 to smart contract

With our web3 variable, we can communicate with the Ethereum blockchain, but not with any smart contract.

To communicate with smart contracts, we need to create a Web3 contract instances. A contract instance is:

  • a Javascript representation of an existing smart contract
  • specific to a smart contract.
  • cannot be created if the smart contract is not already deployed

To create a contract instance, we need:

  • the contract address
  • the ABI of the contract

ABI (Application Binary Interface) is a scary word, but it’s actually pretty simple. This is just a JSON document that represents the interface of the smart contract. More specifically, the interface is the set of Solidity functions that can be called from outside the smart contract, by Web3 for example. (only Solidity functions marked as external or public are callable from outside the smart contract).

This is an extract of the ABI of the DAI smart contract we will use for our example:

[
    {
      "constant": true,
      "inputs": [],
      "name": "name",
      "outputs": [
        {
          "name": "",
          "type": "string"
        }
      ],
      "payable": false,
      "stateMutability": "view",
      "type": "function"
    },
    {
      "constant": false,
      "inputs": [
        {
          "name": "spender",
          "type": "address"
        },
        {
          "name": "value",
          "type": "uint256"
        }
      ],
    ...
]

The big question is: How the hell are we going to get this ABI?

Every time you compile a smart contract, you will get this ABI. With Truffle (the smart contract framework we will use), this happens every time you explicitly run a compilation with truffle compile or you deploy the smart contracts with truffle migrate.

Truffle will put the ABI in a json file located in the build folder. But for our project we customized this location (because of Webpack… this is a long story let’s ignore this!). In our case the json file with the ABI is inside client/src/contracts/Dai.json.

//the json file produced by truffle is called an artifact
const artifact = require('client/src/contracts/Dai.json');

//dai is the contract instance.
const dai = new web3.eth.Contract( //With an UPPERCASE C
  artifact.abi,
  artifact.networks[id] //We use the network `id` that we got from the previous section
);

Leave a Reply

Your email address will not be published. Required fields are marked *