Believe it or not, Web3 and distributed applications are here to stay! These technologies are contributing to creating a more decentralised Internet where the data is not owned by single institutions and applications are not necessarily running on servers managed by big companies. We are talking about a utopic future where data and applications are not attached to any particular place, they just will exist in the networks.
Until that utopic point, traditional cloud applications are going to coexist with distributed applications and in some use cases, they will need to interact between them. Cloud applications could need to read transactions around a specific address, check that a smart contract fires a specific event or simply send a transaction or data to a specific address, among other interactions.
We are going to cover the necessary pieces to achieve that a nodeJS application will be able to interact with the Ethereum network with some simple examples.
The Intermediary
The first actor in this integration between the traditional nodeJS application and the Ethereum network is the mechanism that will allow us to listen to the blockchain and send data to the blockchain.
To have this communication mechanism we have two options, create an Ethereum node or use a third-party provider. The main difference between the two options is confidence.
If we manage our node, we know that information is not managed by a third party and we can trust the data that we are reading. On the contrary, the drawback of managing your node is that normally it is more complex than using a third party to connect to the blockchain network.
To mount an Ethereum node, the recommended option is to use Geth. Geth is one of the three original implementations of the Ethereum protocol implemented in Go and it is one of the most used implementations to create an Ethereum node. You need a machine with A LOT space in your hard drive, and download, configure and launch Geth.
Another alternative to mounting your node is to use a cloud provider such as AWS or Azure. In cloud providers, you can mount Geth on a normal cloud instance or use specific services such as Amazon Managed Blockchain or the Ethereum node from Azure Marketplace.
If you don’t want to deal with the configuration of an Ethereum node you can use a third-party provider that can provide you access to the network through them. Some of the most well-known providers are Infura, Alchemy and QuickNode. In our example, we are going to use Infura.
Finally, not covered here, another option to interact with blockchain could be to use the Etherscan API to read information about accounts, transactions, blocks, etc… using a REST API.
The Javascript interface
From the previous step, using a self-owned node or a node provided by a third party, we are going to obtain an URL to a JSON gRPC API.
To interact with the Ethereum node we are going to use a JSON gRPC API but fortunately, we won’t need to interact directly with it. Several libraries work as a wrapper around the node API making our life easier. Web3js and Ethers are two of the most commonly used. In our examples, we are going to use Web3js.
At this point, we have all the pieces in place to start to connect nodeJS with Ethereum and start to receive events and monitoring addresses. The following diagram summarises what we have said.
Reading information
Once we have everything ready in our NodeJS application, we can start to receive information from the Ethereum network. Web3js provides us with some convenient methods to receive information depending on what we want to monitor. If we want to monitor what’s going on around a smart contract we are going to use the subscribe
method. This method allows us to receive every event emitted by a smart contract.
An important consideration here is that with emitted events we are talking about Solidity logging events. If your contract doesn’t emit any event you won’t see any data using the subscribe method. Here is an extract of code using the subscribe method:
const options = {address: '0xcontract_address'};
var subscription = web3.eth.subscribe('logs', options, function(error, result){
if (!error) console.log('got result');
else console.log(error);
}).on("data", function(log){
const decodedLogs = abiDecoder.decodeLogs([log]);
console.log(decodedLogs)
}).on("changed", function(log){
console.log('changed');
});
In this fragment of code, we are simply subscribing our application to Ethereum logs of a specific contract address provided in the options
object. Additionally, we are decoding the event using the library abiDecoder. To use this library, we are providing in the configuration the Contract.json file including the ABI (Application Binary Interface) of the contract.
Using Web3js we can subscribe our application to specific events using the configuration of the topic. In this case, we are subscribed to all the events emitted by the contract. Here is an example of an event received from our test contract after emitting a transfer:
Inside the event array we get more information about the emitted event:
Get more information from the network
At this point, we can monitor a smart contract but we can try to go further and try to monitor what’s going on around a specific address. Thanks to Web3js we can also subscribe to every block added to the blockchain and we can explore the content of that block.
let blockSubscription = web3.eth.subscribe('newBlockHeaders')
blockSubscription.subscribe((error, result) => {
if (error) {
console.log("Error subscribing to event", error)
process.exit()
}
}).on('data', async blockHeader => {
if (!blockHeader || !blockHeader.number)
return
const block = await web3.eth.getBlock(blockHeader.number)
console.log(block)
})
In this case, we are just subscribing our application to the newBlockHeader
and printing the result:
In the block header information, we have a lot of information about the latest mined block but there is a very important section for us, the transactions
.
That array includes all the hashes of the transactions included in that block and again thanks to Web3js we have a method to obtain the information of a specific transaction. As you can see, if we want to monitor just a subset of addresses now is where the intensive processing stuff starts XD.
const block = await web3.eth.getBlock(blockHeader.number)
block.transactions.foreach( transactionHash => {
transaction = await web3.eth.getTransaction(transactionHash)
console.log(transaction)
})
As a result of this we can get all the information related to the transaction:
We can get the from
and to
addresses, any additional information included inside the transaction in the input
field or the amount of the transaction in wei inside the value
field.
DISCLAIMER: As you might be guessing, we are performing a lot of requests against the network, for each received block, we are requesting the block information and then we are asking for the information about all transactions included in that block. Because of this, we could reach the limit of usage of a third party node such as Infura. For this specific use case, it could be recommended to have a self-managed node.
For more information about configurations and to check how it works, all the code shown can be found in this repository. The repository includes a simple smart contract developed with Hardhat and all the necessary code to monitor the contract and some Ethereum addresses using NodeJS.
Cover Photo by Shubham Dhage on Unsplash