Lesson 11

The Web3.js library

2

In this chapter, we're going to deploy a first version of our contracts to the test network so that we can interact with them from our web app using the Web3 library.

Running migrations

In your Truffle project, run the following command:

This will compile and upload the contracts to your local Ethereum network.

After the migration, you'll notice that there's a new build folder in your Truffle project. In it, there's a contracts folder with your contract artifacts.

These files serve as a bridge between the web app and the deployed contract that they each represent, as we'll see soon.

Calling the contract

In order to use the artifacts in our app, we need to import them somehow. Thankfully, Next allows us to import JSON files without the need for any special conversion. The only caveat in our case it that the artifact files are currently outside the Next project folder (since build/contracts is outside of client). Therefore, we need to copy them into the web project!

To automate this process, we will add a new script called artifacts in our package.json file, whose task is to copy the entire build/contracts folder into a new one called client/web3/artifacts.

We'll also alter our dev command so that it always runs npm run artifacts before starting the server, just in case we forget to run it ourself.

package.json

Now we can import one of our artifact files, say UserStorage.json, into our index page and log its content!

client/pages/index.js

While running the app with npm run dev, you should see the following in your browser's JavaScript console:

Now, in order to actually call one of the functions in the contract, we'll need to convert this JSON data into some kind of contract object, just like we did in the earlier chapter "Running locally". For this, theres a great little library called truffle-contract.

In our provider file, we create a new function called getInstance. It takes the artifact's JSON data as a parameter, converts it into a contract object using truffle-contract, and returns the last deployed instance.

client/web3/provider.js

And we're done! This function can now be used anywhere in our web app to get an interactive version of any of our contracts. Let's try it out!

Going back to the index page file, we can now use getInstance on our JSON and then call the contract's profiles function to fetch the user with ID $1$.

client/pages/index.js

Refresh your browser, and you should see the following:

The reason we get an empty hex (0x000...) for the username is obviously because there's no user with ID 1 on our network yet. But at least it seems to work!

Abstracting the user functions

Now that we have some proof that we can interact with our contracts from the web, let's abstract some of this functionality away by creating functions for the most common operations. This will help us keep the app clean and simple.

First of all, we can create a special function for what we did above – getting a user's info!

We create a new file (client/web3/users.js) that contains a getUserInfo function:

client/web3/users.js

Then we'll rewrite the code in our index page so that we instead need to click a button in order to fetch and log the user info. You can remove all the code that's in the componentDidMount hook, as well as the imports at the top of the file.

client/pages/index.js

There we go! Easy peasy!

Next, we want another function called createUser which will call the UserController's function with the same name.

Remember that createUser is a writable function. This means that it costs gas to execute it, and we can't just use .call as we did with profiles.call(). Instead, we have to pass a second parameter specifying which address we want to pay from.

In this case, we know we can get the user's MetaMask addresses using eth.getAccounts(), so let's just select the first address that exists in that list.

client/web3/users.js

Then we add a button to our index page...

client/pages/index.js

After that, try clicking the button and you'll get to see the cool process of confirming a contract transaction through MetaMask!

Now that our first user is created on the network, you should be able to click the "Get user" button again and get your username in hex value!

To make it readable, we'll do just like we've done in our tests files – use the toAscii function that's built into Web3.

client/web3/users.js

Abstracting the tweet functions

While we're on a roll, let's also make some abstractions for the functions in TweetStorage and TweetController. That way, we can pretty much do anything from our simple button interface.

First of all, create a tweets.js file, right next to the users.js file in your web3 folder. This time, we'll create the getTweetInfo and createTweet functions in one go, since we know how this stuff works already.

client/web3/tweets.js

And, as usual, we import these functions in the page index and create some buttons for them to make them easy to test in the browser:

client/pages/index.js

When it's done, click the "Create tweet" button, confirm the transaction, then click on the "Get tweet" button...

Well done, we're one step closer to having a real DApp!

In the next chapter, we will go beyond having simple buttons and look closer at how to build the web app's interface using React.

Thank you Tristan and all Ludo's folks for this interisting project. I have been learning a lot with it. But in this lesson I have break in. I believe that the code was outdated. This error appears in my browser's console related about Webpack and Eth.utils.

I really apreciate a help, if somebody have a updated solution. Thank you.

Issue#:

Err: TypeError: _provider_js__WEBPACK_IMPORTED_MODULE_2__.eth.utils is undefined

It is indeed a bit outdated now :) I suggest you use the ethers.js library for this. Then you can do ethers.utils.parseBytes32String(YOUR-BYTES32-STR)

Thank you for this detailed Simple project. One Suggession for this, change is required in this Tutorial, truffle-contract is deprecated and need to update with @truffle/contract.

I'm getting errors in my eth.utils calls, I've had to change them to eth.extend.utils to get them to work. Why is that, is it a version difference?