Making Web3.js work asynchronously with JavaScript Promises and await

One of the things I learned when writing my “Hello World” tutorial for Ethereum and Web3.js was the importance of having your functions which call the blockchain run asynchronously. Without this, we would be unable to support users who use MetaMask as their Ethereum provider, and probably even more important, we may bring bad user experiences by locking up the browser during long HTTP requests. From the MetaMask developer FAQ:

Using synchronous calls is both a technical limitation and a user experience issue. They block the user’s interface. So using them is a bad practice, anyway. Think of this API restriction as a gift to your users.

Setting up a Web3 function to work asynchronously was pretty easy to figure out for a single call; but what about making multiple calls through Web3, that all need to be asynchronous, but also have dependencies on one another?

An example would be calculating the ERC-20 token balance of an Ethereum address. To do this, you need to know both the balance of tokens at the address, but also the decimals value for that token to convert to the right units. JavaScript Promises are the natural solution here. They allow you to track the status of an asynchronous function, and perform actions after your multiple dependencies all resolve.

Turning Web3.js functions into JavaScript Promises

In my “Hello World” tutorial, I show you can make an asynchronous requests by adding an error first callback to the Web3.js functions:

web3.eth.getBalance(address, function (error, result) {
    if (!error) {
        console.log(result);
    } else {
        console.error(error);
    }
});

As I mentioned, if we depend on multiple calls from the Ethereum blockchain to create a result, using JavaScript Promises is a good solution. They allow you to react to a success or a failure from an asynchronous function. Creating a promise from the error first callback function is pretty straightforward:

function getBalance (address) {
  return new Promise (function (resolve, reject) {
    web3.eth.getBalance(address, function (error, result) {
      if (error) {
        reject(error);
      } else {
        resolve(result);
    }
  })
}

But we can actually make this process even simpler for multiple Web3 functions by creating a wrapper which both makes the function asynchronous, and turn it into a promise; basically automating what we would repeat above for each different Web3 function we call.

Here is the wrapper from 0xcaff posted in StackExchange:

const promisify = (inner) =>
    new Promise((resolve, reject) =>
        inner((err, res) => {
            if (err) {
                reject(err);
            } else {
                resolve(res);
            }
        })
    );

Now that we have a Promise, we can take advantage of the async/await pattern which simplifies not only the look, but also the behavior of Promises.

Putting this all together, let’s show how simple this makes getting the token balance for an ETH account. We convert our original “Hello World” getBalance into an asyncronous function, like so:

async function getBalance() {
    var address, wei, balance
    address = document.getElementById("address").value;
    wei = promisify(cb => web3.eth.getBalance(address, cb))
    try {
        balance = web3.fromWei(await wei, 'ether')
        document.getElementById("output").innerHTML = balance + " ETH";
    } catch (error) {
        document.getElementById("output").innerHTML = error;
    }
}

Not much shorter for a single function, but will certainly make things better the more separate functions we call. My next post will show the results of these smaller educational posts, and how we can put it together to create the project I have been hinting above: Getting the ERC-20 balance of an Ethereum Address.

I hope this teaches you something! Again, this may be trivial to many, but was not so straightforward when I first started to tackling these problems. If it did help you, feel free to support me with a small donation here:

0xD62835Fe2B40C8411A10E7980a290270e6A23cDA

Ethereum Token Contract ABI in Web3.js for ERC-20 and Human Standard Tokens

This post will introduce you to Token Contract ABIs in Ethereum, and show you how you can use a the Human Standard Token ABI to access the token balances of ERC-20 compatible tokens.

 

Let me start by saying that this post may be trivial to some, but was very confusing to me, a brand new user to the Ethereum and Web3.js development world. After writing my “Hello World” project which gets the ETH balance for an Ethereum Address in Web3, I began to think about the next small step I can take and teach to others. Naturally, I thought it would make sense to do a similar project, but instead, get the Token Balance for ERC-20 tokens at an Ethereum Address.

With Web3.js, you can easily find the template for this functionality:

var tokenContract = eth.contract(tokenABI).at(tokenAddress);
var tokenBalance = tokenContract.balanceOf(ethereumAddress);

Seems easy enough to get the Token Address, but what is this tokenABI value that we need to use?

A simplified explanation of a token contract ABI

A little bit of searching will give you documents that teach you about the Application Binary Interface (ABI) like this, but no real layman’s terms explanation. Here is my attempt at one:

In Ethereum, the Application Binary Interface (ABI) is a predefined template of the functions a contract exposes.

You know when you import a new library into an IDE, you automatically get all that nice autocomplete and Intellisense? You type the library name, add “.” and a list of functions appears in front of you:

Imagine if instead, you needed to know ahead of time the functions the library exposes, and then define them for the IDE so that the autocomplete would work… that is pretty much what is happening here.

An Ethereum Contract ABI will define the different functions that a contract exposes, and each function definition will contain things like the function type, name, inputs, outputs, and more. It even contains information like whether the contract accepts payments in ether or not. Here is the JSON ABI of the balanceOf() function we ultimately want to use:

{
  "constant": true,
  "inputs": [
    {
      "name": "_owner",
      "type": "address"
    }
  ],
  "name": "balanceOf",
  "outputs": [
    {
      "name": "balance",
      "type": "uint256"
    }
  ],
  "payable": false,
  "type": "function"
}

I think this is easy enough to read: It is a function which accepts an address as an input, and outputs a balance as an unsigned int. But is this enough to start talking to an Ethereum contract? What about all the other functions that contract might have? How will I find the full contract ABI for each contract I want to talk to?

Here are the things I learned when trying to answer these questions:

  • You do NOT need the full ABI to interact with a Token Contract. You only need to define the functions which you want to use.
  • You cannot programmatically generate the ABI for a given contract using data from the Ethereum blockchain. In order to generate the full contract ABI from scratch, you will need the full contract source code, before it is compiled. Note that only the compiled code exists at a contract address.
  • Some contracts have functions which are intentionally ‘hidden’ from the public, and they do not intend the public to use.

Therefore, there really is no way to dynamically call any contract address. If only there were some standard set of functions shared across all contracts…

The ERC-20 and Human Standard Token

From the specification:

Simple Summary

A standard interface for tokens.

The value of ERC-20 tokens is that they all have a standard set of functions which allow you to interact with each of them in the exact same way. This is why there is so much hype around new Ethereum application which use ERC-20 tokens: there is nearly zero effort to add these tokens to existing platforms like Cryptocurrency Exchanges which means smoother adoption, easier to sell/track, and of course more hype ($$$).

What does it take to be ERC-20 compliant?

Not much really. You just need to expose the non-optional methods and events described here. Beyond the core ERC-20 standard, there are also standard optional parameters which are intended for humans. See here:

In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans.
Imagine coins, currencies, shares, voting weight, etc.
Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners.

1) Initial Finite Supply (upon creation one specifies how much is minted).
2) In the absence of a token registry: Optional Decimal, Symbol & Name.
3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred.

This is the “Human Standard Token”, which of course is a super-set of the ERC-20 standard. Additionally, many tokens have a version() function which is also available in the ABI provided below.

So let’s get it working!

Now that you have sufficient background to understand what is going on, lets actually go and make some calls to ERC-20 token contracts.

To start, you can find the Human Standard Token ABI here on GitHub. The JS file simply puts the ABI JSON object into a varaible called human_standard_token_abi which allows you to really easily use it in a project.

The most basic project that can take advantage of these things would look something like this:

<html>
<head>
  <meta charset="UTF-8">
  <script type="text/javascript" src="./web3.min.js"></script>
  <script type="text/javascript" src="./human_standard_token_abi.js"></script>
  <script>
    var web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/<APIKEY>"));

    address = "0x0e2e75240c69495d2b9e768b548db381de2142b9" //From Etherscan
    contractAddress = "0xd26114cd6EE289AccF82350c8d8487fedB8A0C07" //OMG
    contractABI = human_standard_token_abi

    tokenContract = web3.eth.contract(contractABI).at(contractAddress)

    console.log(tokenContract.balanceOf(address).toNumber())
  </script>
</head>
<body>
  <p>Check the console (F12)</p>
</body>
</html>

And the output:

What if…

Let’s try a few fringe scenarios with this contract ABI. We will be testing this against OmiseGo.

  • What if we call a contract with a function we defined, but does not exist on the contract?

OMG does not have a version() function, but we define it by default in our human_standard_token_abi. So let’s call it:

console.log(tokenContract.version())

“Uncaught Error: new BigNumber() not a base 16 number”

  • What if we call a contract with a function that exists, but we did not define in the ABI?

OMG has the function mintingFinished() which is not part of the Human Standard Token ABI. If we call it, we get the following error:

“Uncaught TypeError: tokenContract.mintingFinished is not a function”

 

What I hope you learned:

  • The requirements to call an ERC-20 compliant token contract using Web3.js
  • What a Token Contract ABI is on the Ethereum Blockchain
  • Why the ERC-20 standard is pretty great for develeopers
  • How to easily add the human_standard_token_abi to your JavaScript web application

Keep an eye out for my next post, which will detail how to set up Web3.js for JavaScript promises, so we can execute multiple Web3 functions asynchronously. Then we will take both of these pieces, and make a simple web app that can fetch the ERC-20 token balance at an Ethereum address.

If you liked this post, and want to support me, feel free to send donations here:

0xD62835Fe2B40C8411A10E7980a290270e6A23cDA

Correcting the Ethereum and Web3.js “Hello World”

Just 2 days ago I blogged about a quick project which I considered a “Hello World” application for Ethereum and Web3.js. However, I quickly learned that even in my short 31 lines of code, I made numerous mistakes which do not follow the best practices for developing Web3.js applications.

The main part of the sample was the Web3.js stuff, which could be broken into two logical sections:

  1. Establishing a Web3 Provider
  2. Getting the ETH balance of an Ethereum Address

Both of these sections had mistakes in my original code, and this post will show you how to fix them! I will be updating the main blog post to include these changes as well, but I wanted to document the subtleties of the changes, and what I have learned since then. BTW, all of these mistakes could be avoided if you read the MetaMask developer documentation.

Ethereum Browser Environment Check

In my original sample, I simply depend on the Web3 HTTP Provider to access the Ethereum network. However, using MetaMask or the Mist Browser, users will already have direct access to the Ethereum network through those providers, and do not need to use the HTTP Provider. As said in the Web3 JavaScript app API Documentation:

…you need to create a web3 instance, setting a provider. To make sure you don’t overwrite the already set provider when in mist, check first if the web3 is available…

To fix this, we mostly follow the code sample provided by MetaMask:

window.addEventListener('load', function () {
    if (typeof web3 !== 'undefined') {
        console.log('Web3 Detected! ' + web3.currentProvider.constructor.name)
        window.web3 = new Web3(web3.currentProvider);
    } else {
        console.log('No Web3 Detected... using HTTP Provider')
        window.web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/noapikey"));
    }
})

As they mention on the MetaMask developer documentation:

Note that the environmental web3 check is wrapped in a window.addEventListener('load', ...) handler. This approach avoids race conditions with web3 injection timing.

With our new code, as soon as the page loads, we detect if the browser being used already has a Web3 provider set up, and if it does we use it! Otherwise, we will use the HTTP Provider from Infura.io. For most users, I would assume they do not have MetaMask, and thus this change is not very important; but it is certainly best practice, and I am happy to oblige.

Chrome with MetaMask:

Firefox without Web3 Provider:

Asynchronous calls to the Ethereum network

If you have been following along word for word, you might have copied the changes mentioned above, loaded it in your MetaMask enabled browser (from your web server), and tried to get your ETH balance… Here is what you will see:

If we continue to read the MetaMask developer documentation, we would see the following:

The user does not have the full blockchain on their machine, so data lookups can be a little slow. For this reason, we are unable to support most synchronous methods.

This means we need to turn our call to get the ETH balance, which is currently a synchronous HTTP request, into an asynchronous request. We can do this by adding an error first callback as the last parameter of the function:

function getBalance() {
    var address, wei, balance
    address = document.getElementById("address").value
    try {
        web3.eth.getBalance(address, function (error, wei) {
            if (!error) {
                var balance = web3.fromWei(wei, 'ether');
                document.getElementById("output").innerHTML = balance + " ETH";
            }
        });
    } catch (err) {
        document.getElementById("output").innerHTML = err;
    }
}

If we try to run this code now with MetaMask as our provider, everything works again!

The first, but certainly not last mistake…

Phew! We fixed our Hello World application! Take a look at the overall changes on GitHub. I think this goes to show how difficult it can be to learn things on your own, and some of the best practices that can be overlooked so easily. I hope that I am able to go through these issues so that you don’t have to. If you find any other issues with this or future samples I create, please let me know!

Special shout out to Reddit user JonnyLatte for telling me the errors in my ways, and getting me to read more of the documentation around Web3!

As always, if you found this content helpful, feel free to show some appreciation at this address: 0xD62835Fe2B40C8411A10E7980a290270e6A23cDA

Ethereum and Web3.js “Hello World”: Get the ETH Balance of an Ethereum Address

Using just 41 lines of HTML + JS, we create a Web3.JS application which can get the ETH Balance of an Ethereum Address [Final Result] [GitHub]

 

For me, the hardest part of learning new technical skills is overcoming the hurdle of simply getting started. The Ethereum development space is booming, and the ability to make relatively simple web applications that interact with the Ethereum blockchain is at a premium. Today, development on World Wide Web requires you to compete with a huge number of fully developed, feature rich applications, where it is very unlikely that you are actually contributing value. However, the same is absolutely not true for Ethereum and blockchain as a whole. There are so many utilities and tools that can bring value to this ecosystem, all with relatively low feature requirements.

So let’s overcome the first barrier by building a “Hello World” application.

From my perspective, the perfect project for something like this would be a bare-bones single-page application which fetches the ETH balance of an Ethereum address. This is about as simple as it gets to allow a user to interact with the blockchain, and thanks to Web3.js, it is also really simple to implement!

Prerequisites

To gain access to the Ethereum network, you will need to gain access to a Web3 Provider. As I will talk about more below, this comes natively with certain Ethereum focused browsers, but for the average user you will need to provide them with their own gateway to the blockchain. Basically, you need someone to provide your app the data that is actually on the blockchain, and Web3.js has the ability to interact directly with an HTTP Provider to bring you this data with minimal effort.

I used Infura.io as my Ethereum provider (for no other reason than they showed up first when searching), and after spending less than a minute registering with them for free, I was given my unique address to their Main Ethereum Network where my API Key was appended at the end.

Save this URL, as you will be using it very shortly.

https://mainnet.infura.io/<APIKEY>

The only other thing you need to get started is your own copy of Web3.js which can be found on GitHub. Just download and unpack the ZIP file.

Create a new folder where you want your project to live, and create an index.html file. Then, from the Web3.js download, copy web3.min.js to that folder.

ethbalance/     (folder)
├── index.html
└── web3.min.js

Finally, make sure you initialize your index.html skeleton:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
</head>

<body>
</body>
</html>

Let’s get started!

To make this as simple as possible, we are going to create a single HTML file which will contain all the code necessary to complete this project. It will be broken into 2 parts:

  1. HTML to render a bare-bones web page
  2. JavaScript to initialize a Web3 object, and interact with our HTTP Provider

HTML Body

Our HTML body needs a text field for the user to input an Ethereum address, a button to trigger the JavaScript, and an output area to display the result. I am going to assume that if you are reading this post, you have enough familiarity with HTML that I can just breeze over this, and give you the code:

<body>
    <h1>ETH Balance Fetcher</h1>
    <p>Enter your Ethereum Address:</p>
    <input type="text" size="50" id="address" />
    <button type="button" onClick="getBalance();">Get Balance</button>
    <br />
    <br />
    <div id="output"></div>
</body>

The only thing of note here is that when you click the button we created, it triggers a JavaScript function getBalance(), and that is what we are going to write next!

HTML Head: JavaScript and Web3

Now it is time to prepare the JavaScript required to make this all work. We are going to need to get the Ethereum address inputted by the user, initiate our connection to the Ethereum Provider, and then query the blockchain for the ETH balance at that address. Oh, of course we will also send back the result and update the HTML with the value. Here is our HTML head template:

<head>
    <!-- Check if Web3 already defined -->
    <!-- If not, connect to HTTP Provider -->
    <!-- getBalance() function -->
</head>

First we will load and set up our Web3 provider. If you are using an Ethereum compatible browser like Brave, Chrome + MetaMask, or Mist, you will already have your own Web3 provider established natively. You can access that connection with this:

<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="./web3.min.js"></script>
    <script type="text/javascript">
        window.addEventListener('load', function () {
            if (typeof web3 !== 'undefined') {
                console.log('Web3 Detected! ' + web3.currentProvider.constructor.name)
                window.web3 = new Web3(web3.currentProvider);
            }
        })

    <!-- If not, connect to HTTP Provider -->
    <!-- getBalance() function -->
    </script>
</head>

 

More likely, the user does not have one of these browsers, so we need to establish our own connection to the Ethereum network. We can do this with the URL that you saved earlier from Infura.io, and establishing an HTTP Provider:

<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="./web3.min.js"></script>
    <script type="text/javascript">
        window.addEventListener('load', function () {
            if (typeof web3 !== 'undefined') {
                console.log('Web3 Detected! ' + web3.currentProvider.constructor.name)
                window.web3 = new Web3(web3.currentProvider);
            } else {
                console.log('No Web3 Detected... using HTTP Provider')
                window.web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/<APIKEY>"));
            }
        })

    <!-- getBalance() function -->
    </script>
</head>

At this point, we can do just about anything that Web3.js offers, but for our purposes we only need to query the blockchain for the address, and return the ETH balance. So let’s set up our getBalance() function:

<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="./web3.min.js"></script>
    <script type="text/javascript">
        window.addEventListener('load', function () {
            if (typeof web3 !== 'undefined') {
                console.log('Web3 Detected! ' + web3.currentProvider.constructor.name)
                window.web3 = new Web3(web3.currentProvider);
            } else {
                console.log('No Web3 Detected... using HTTP Provider')
                window.web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/<APIKEY>"));
            }
        })
        function getBalance() {
            var address, wei, balance
            address = document.getElementById("address").value
            try {
                web3.eth.getBalance(address, function (error, wei) {
                    if (!error) {
                        var balance = web3.fromWei(wei, 'ether');
                        document.getElementById("output").innerHTML = balance + " ETH";
                    }
                });
            } catch (err) {
                document.getElementById("output").innerHTML = err;
            }
        }
    </script>
</head>

Walking through the function:

First we store the value of the text field from our HTML page into the address variable. Then, we will try to use the web3 object we intialized earlier to call the function web3.eth.getBalance() which accepts an Ethereum Address as an input. Note that we need to make this call asynchronously as the user does not have the full blockchain loaded on their machine, so some calls may run slow. Rather than lock the user’s interface, we let the the call happen in the background, and when it is complete, we trigger an update to the page. This is required to support MetaMask, but benefits all Web3 applications. If you want to learn more about how to make these requests asynchronous, take a look at the “Using callbacks” section in the Web3 documentation.

Once the asynchronous request is complete, we will get back a Wei balance as a result. But we want the Ether value, so we do one last step to convert the value: web3.fromWei(wei, 'ether'). If all of this is successful, we update the output div with our result, otherwise if it fails at any point we catch the error, and output that message instead.

Here is the final index.html file which you should be able to use as soon as you paste in your <APIKEY> from Infura.io. You can also download this project directly from my GitHub.

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="./web3.min.js"></script>
    <script type="text/javascript">
        window.addEventListener('load', function () {
            if (typeof web3 !== 'undefined') {
                console.log('Web3 Detected! ' + web3.currentProvider.constructor.name)
                window.web3 = new Web3(web3.currentProvider);
            } else {
                console.log('No Web3 Detected... using HTTP Provider')
                window.web3 = new Web3(new Web3.providers.HttpProvider("https://mainnet.infura.io/<APIKEY>"));
            }
        })
        function getBalance() {
            var address, wei, balance
            address = document.getElementById("address").value
            try {
                web3.eth.getBalance(address, function (error, wei) {
                    if (!error) {
                        var balance = web3.fromWei(wei, 'ether');
                        document.getElementById("output").innerHTML = balance + " ETH";
                    }
                });
            } catch (err) {
                document.getElementById("output").innerHTML = err;
            }
        }
    </script>
</head>
<body>
    <h1>ETH Balance Fetcher</h1>
    <p>Enter your Ethereum Address:</p>
    <input type="text" size="50" id="address" />
    <button type="button" onClick="getBalance();">Get Balance</button>
    <br />
    <br />
    <div id="output"></div>
</body>
</html>

You can try this out right now using the version I hosted here: http://shawntabrizi.com/ethbalance/

One thing you should note is that this is a client-side JavaScript application. There are a million reasons why making client-side apps is so much easier for development, but one big downside is that your API key will be exposed to anyone who simply inspects the HTML. Please do not abuse my API key, and please do not ship a production application using a method like this unless you are prepared to get your API key terminated.

Note that I have made updates to this blog post, correcting some issues regarding best practices with Web3. You can learn more about the errors I made along the way here.

I hope this helps you get started with developing for Ethereum using Web3.js! If you liked this content, and want to support me, feel free to send donations to this address: 0xD62835Fe2B40C8411A10E7980a290270e6A23cDA