Web3.js

Interacting With Smart Contracts Using Web3.js (Part I)

VTECH
0xCODE
Published in
7 min readJun 8, 2021

--

The Web3.JS framework allows developers to interact with Ethereum smart contracts using Javascript Node.js. The framework consists of modules with specific functions:

  • web3-eth is for the blockchain and smart contracts.
  • web3-shh is for the whisper protocol, to communicate p2p and broadcast.
  • web3-bzz is for the swarm protocol, a decentralized file storage.
  • web3-utils contains useful helper functions for DApp (Decentralized Application) developers.

This provides a standard way for developers to use API calls and integrating functions with a Node.js frontend application. This allows users to access to blockchain features like sending transactions, checking account balances and signing messages.

Getting Started

The following are the requirements to begin developing applications that can interact with smart contracts.

  • Node.js — This must be installed for the specific operating system in use. It is highly recommended to use the LTS version (Linux, macOS and Windows versions are available).
  • Web3.js — Install Web3.js using either npm or yarn from your terminal console project folder.
  • Connection to an Ethereum node — This requires either a local node (e.g. Ganache) or connecting to live node on the network (e.g. Infura). For those who want to test on a network, I recommend using an Infura gateway.

The Web3 Object

The web3 object is the main class in the framework. To call any function, the web3 object must be used.

web3.<name of function or call>

A new instance of the object must first be instantiated. From the Node prompt on a terminal console:

> const Web3 = require('web3')

Now that we have instantiated an object, we must create a provider that creates an instance of a Web3 object that connects to the Ethereum network. There are several types of networks.

(Take note that it is a capital ‘W’ for Web3 provider)

  • Ethereum Mainnet or main network (the live blockchain used for ETH)
  • Test Networks (e.g. Kovan, Rinkeby, Ropsten, Goerli)
  • Web3 Providers (e.g. Using port 7545 on local host or remote server)
  • Injected Web3 (e.g. Infura Gateway)

For this example, an Infura endpoint will be used. This is a url that is provided by Infura when you create a project. This must be created first before the url is issued.

Note: The url provided is just an example and should not be used in an actual production or test environment.

> const web3 = new Web3('https://ropsten.infura.io/v3/76162cfy074u522fa49be7ca11cc9a33')

When you call the Web3 object, it will return the object’s functionalities. Here is a snippet of the returned results (check screenshot):

> Web3

If you scroll down further, you will see the following:

This shows the contents of the library’s modules. There are functions and supported units (e.g. wei) used in the Ethereum blockchain. This is what developers must follow in order to interact with the blockchain. You cannot send transactions unless these conventions are followed properly, so these are like the rules regarding transactions for smart contracts. Developers can reference these when writing statements.

Working With Ethereum Accounts

One of the basic uses for Web3.js is to work with accounts. Let’s say we want to create a new account on the Ethereum blockchain. We can do that by issuing a command and the returned result will be a fully functional account that a user can use, with a public address and private key.

Note: This account is just used as an example. Do not use for production or testing purposes.

> web3.eth.accounts.create();
{
address: "0xb8CE9ab6943e1eCED004cDe8e3bBed6568c2Fv01",
privateKey: "0x348ce564d427a3311b6546bbcff9390d69395b06ed6c486954e971d860fe8709",
signTransaction: function(tx){...},
sign: function(data){...},
encrypt: function(password){...}
}

You create an account with an Ethereum address of:

0xb8CE9ab6943e1eCED004cDe8e3bBed6568c2Fv01

This account can be used on the network you specified as your provider. It cannot be used on another network, other than that it was created on.

The account also comes with a private key. This is very important because the private key is what grants user authorization to the account.

0x348ce564d427a3311b6546bbcff9390d69395b06ed6c486954e971d860fe8709

If it is lost, any balances on the account can no longer be recovered unless there is a backup of the private key. Anyone who steals the private key can also use it to access the account. Keep the private key safe and never share or reveal it to others.

Likewise, you can derive the address from the private key using the privateKeyToAccount() function.

> web3.eth.accounts.privateKeyToAccount('0x348ce564d427a3311b6546bbcff9390d69395b06ed6c486954e971d860fe8709');
{
address: '0xb8CE9ab6943e1eCED004cDe8e3bBed6568c2Fv01',
privateKey: '0x348ce564d427a3311b6546bbcff9390d69395b06ed6c486954e971d860fe8709',
signTransaction: function(tx){...},
sign: function(data){...},
encrypt: function(password){...}
}

Other functions available for developers include encrypting and signing messages using an Ethereum account.

Querying The Blockchain

Developers can use the web3-eth package to query information from the blockchain. This will not require any cost using gas, because these calls do not alter the state. They are just reading data from and not writing data to the blockchain.

To get the balance from an account, use the getBalance() function with the address as the parameter.

> web3.eth.getBalance('0x6e35A20a740bC7288bdec5d4E138a253D4A72660') .then(console.log)Promise { <pending> }> 5984521207000000000

The account’s balance is returned in wei, which is the lowest denomination of ether. There is another way to get the balance in ETH or ether, but this requires the use of the web.utils function call. The following is an example:

> web3.eth.getBalance('0x6e35A20a740bC7288bdec5d4E138a253D4A72660', (call, wei) => { balance = web3.utils.fromWei(wei, 'ether')})Promise { <pending> }> balance'5.984521207'

As you can see, it requires using a statement with a function call that throws the result of getBalance to a variable balance with the conversion from wei to ether. The result is 5.98 which is denominated in ether.

Running A Transaction

This is a deeper dive into Web3.js, this time using transactions. This will require the cost of gas since a transaction makes changes to the state. This alters data on the blockchain, which requires consensus through mining before the transaction can be committed to a block.

The functions used will come from the EthereumJS library. Make sure that ethereumjs-tx is installed first before proceeding.

First create a file called app.js (any name is fine with a js extension). This will serve as a frontend application that will talk to the Ethereum blockchain using the functions from the libraries we installed.

Here is the following code for app.js:

Note: This is only an example. Private keys are not revealed for demonstration purposes.

const Tx = require('ethereumjs-tx').Transactionconst Web3 = require('web3')
const web3 = new Web3('<endpoint>')
const account1 = '<address 1>'
const account2 = '<address 2>'
const privateKey1 = Buffer.from('<private key 1>', 'hex')
const privateKey2 = Buffer.from('<private key 2>', 'hex')
web3.eth.getTransactionCount(account1, (err, txCount) => {// Build a transactionconst txObject = { nonce: web3.utils.toHex(txCount),
to: account2,
value: web3.utils.toHex(web3.utils.toWei('1', 'ether')),
gasLimit: web3.utils.toHex(21000),
gasPrice: web3.utils.toHex(web3.utils.toWei('10', 'gwei'))
}// Sign the transactionconst tx = new Tx(txObject, { chain: 'ropsten' })
tx.sign(privateKey1)
const serializedTransaction = tx.serialize()
const raw = '0x' + serializedTransaction.toString('hex')
// Broadcast the transactionweb3.eth.sendSignedTransaction(raw, (err, txHash) => {
console.log('txHash: ', txHash)
console.log(err)
})})

What the application will do is transfer an amount of 1 ether (1 ETH) from user account1 to account2. The transaction takes into consideration gas costs at 10 gwei and a limit of 21000 wei. The nonce indicates the transaction count value. These values must be converted to hexadecimal, which is why we use the web3.utils.toHex() function.

There are actually 3 parts to creating a transaction.

First, we build the transaction using the following data structure:

     nonce: web3.utils.toHex(txCount),
to: account2,
value: web3.utils.toHex(web3.utils.toWei('1', 'ether')),
gasLimit: web3.utils.toHex(21000),
gasPrice: web3.utils.toHex(web3.utils.toWei('10', 'gwei'))

That is our transaction object that will be sent to the Ethereum blockchain.

The transaction must then be signed by the user sending it, account1.

tx.sign(privateKey1)

The tx.sign() function is passed the private key value of the user. This authorizes the transaction since it signed.

Finally, the transaction is broadcast to the network. It will be picked up by the miners and packaged into a block where it must be mined and validated.

A console log was added at the end so we can see the returned result, which is important (will be explained later).

To send our transaction, type the following at the terminal prompt (not inside the Node environment, but from the folder).

$ node app.js
txHash: 0x92c0c7d98b0a13f157ed6e4d6aaf28c4525c55c354a3792e097b91c2fafc8e80

The result is important here because it will show whether or not the transaction was processed successfully. If you get the txHash value, then it is a success. Otherwise there are errors in the code that need to be fixed.

To verify the transaction, open a blockchain explorer for the provider network you used (e.g. Ethereum). Look up at the transaction hash value and it will return the details of the transaction.

Synopsis

Web3.js provides a way for application developers to build DApps and interfaces. This is a way to bring frontend functionality on the web that can interact with smart contracts and the Ethereum blockchain.

To Be Continued … (See Part II)

--

--

VTECH
0xCODE

Blockchain, AI, DevOps, Cybersecurity, Software Development, Engineering, Photography, Technology