The most popular library for calling into Ethereum smart contracts from the Javascript world is Web3.js (repo). In fact, Truffle (the popular Ethereum dapp framework) injects this within their own test environment.

Unfortunately, the framework itself (and its official examples) use the 0.20.x version of Web3.js. And while v1.0 is still technically in beta, more and more people are using it because it provides an API that is more explicit and is easier to work with (like PromiEvents, I’ll talk about this below).

In this article, I’ll show you how to convert your Truffle tests to use the new Web3.js v1.0 API rather than the older 0.20.x versions. In the process, I will introduce some of the major changes that the v1.0 API brings.

Our smart contract

For our example, let’s use a simple increment/decrement counter with the following smart contract:

pragma solidity ^0.4.24; contract Counter {

int count = 0; function increment() public {

count++;

} function decrement() public {

count--;

} function get() public view returns (int) {

return count;

}

}

Pretty basic, nothing too special about it. Now let’s get into our Truffle tests.

Our tests

This is what our test would look like if we followed the official Truffle documentation:

This should also be pretty straight forward. We simply check that the initial value is 0, and that it can increment and decrement.

Now let’s see what it would look like with v1.0.

It’s largely the same, but there are two important differences:

We use helpers from helpers.js (a file that we will create soon) so that we get access to the new Web3.js and its contract instance API. There is a new API for calling the smart contract methods.

The helpers

First, make sure you have a package.json file in your project. If not, simply run:

npm init -y

And then install the v1.0 version of web3:

npm install web3

Once we have that, create helpers.js in the same folder, then put the following into the file:

For getWeb3 , Since Truffle automagically injects the v0.20.x web3 into the global scope, we wrap it with our own v1.0 web3 and return the new one.

For getContractInstance , because we want to make it a reusable function, we first take in the new web3, and then return a function that can create contract instances simply by passing in the contract name. So you can use it like this:

// v1.0

const web3 = getWeb3()

const getInstance = getContractInstance(web3) const myContract = getInstance("MyContractName") myContract.methods.myMethodName().call()

Note that calling getInstance in the above example basically replaces the old way of getting a contract instance:

// v0.20.x

const MyContract = artifacts.require("MyContractName")

const myContract = await MyContract.deployed() myContract.myMethodName.call()

The new smart contract method API

Calling smart contract methods are now namespaced under methods of the contract instance object. The new API looks something like this, depending on whether you are calling a constant function or a mutating one (i.e. read vs write):

instance.methods.myMethod().call()

instance.methods.myMethod().send({ from: "0x..." })

Notice that the arguments for your smart contract method is now separated from the transaction-specific options (e.g. from and gas options). This allows you to build up your method call before actually sending it.

Previously, the final function call would contain all the arguments of the methods, with the final argument at the end being your transaction options.

// Old

instance.myMethod(123, true, { from: "0x..." }) // New

instance.methods.myMethod(123, true).send({ from: "0x..." }) // Also new

const doTheThing = instance.methods.myMethod(123, true)

doTheThing.send({ from: "0x..." }) // execute it later

PromiEvents

One of the most convenient things with the new API is that contract methods now return a PromiEvent, which is a hybrid of a conventional Promise and an event emitter.

That means you can attach numerous event handlers for the lifecycle of the transaction. But since it’s also just a regular Promise, you can use await or .then() to do some work after the transaction has been mined. This provides a lot more flexibility for a better user experience.

The following example is from the docs:

web3.eth.sendTransaction({from: '0x123...', data: '0x432...'})

.once('transactionHash', function(hash){ ... })

.once('receipt', function(receipt){ ... })

.on('confirmation', function(confNumber, receipt){ ... })

.on('error', function(error){ ... })

.then(function(receipt){

// will be fired once the receipt its mined

});

This would mean we can do something like this:

const doSomething = instance.methods.myMethod(123, true) await doSomething.send({ from: "0x..." })

.once('transactionHash', (hash) => {

// notify user of the tx hash with an etherscan link

})

.on('confirmation', (confNumber) => {

// update UI to show the number of confirmations for this tx

}) // the transaction has been mined, execute other code here!

First, we created our method call and assigned it to doSomething . And then when we wanted to execute the transaction, we called .send() on it and passed in our transaction options.

From there, we immediately let the user know the transaction hash and give them a link to the relevant Etherscan page. Once the transaction has been mined, the code will resume execution below.

As an added bonus, we are also able to show the number of confirmations onto the UI if we wanted to.

Try it out

It’s my hope that the Truffle team can incorporate some of these ideas soon. It would be great to see more support for Web3.js v1.0. There’s a severe lack of up-to-date and well-maintained Javascript libraries for working with Ethereum and I think this is the best way forward.

In the mean time, I hope this article has helped you to better understand how to work with the new version of Web3.js.

If you felt that this was helpful, please leave me a few claps and share!