Skip to main content

With the release of Moonbase Alpha v2, which was just announced by PureStake, we are adding some new and exciting features that help Moonbeam move closer to its primary goal of providing a seamless experience for projects from Ethereum on the Polkadot ecosystem. One of the main features being added is the ability to subscribe to Ethereum smart contracts events, and other blockchain information.

Contract events are a super important part of dApps in Ethereum, as they facilitate communication between smart contracts and their user interfaces. Events can be considered asynchronous triggers with data. When a contract emits an event, this can subsequently result in an action on the front-end side.

Use Cases for Events

A simple example of an event you could track is a transfer. Let’s say a transfer is initiated by a user using the front-end of a dApp, where a transaction hash is obtained once this is submitted. But to assure the user that the payment was sent, the dApp can listen for an event (emitted by the contract) when the transaction is committed to the blockchain. This can consequently trigger a display message to the user notifying them that their action was successful.

Another powerful use case of events is cheaper storage. On average, logs cost 8 gas per byte, whereas contract storage costs 20,000 gas per 32 bytes. Therefore, events can serve as a tool to save and retrieve necessary information such as transfer logs as well. However, they can’t be used as storage for all use cases, because they can’t be accessed by other smart contracts, for example.

The Significance of Pub/Sub

Given all this context, now we are ready to talk about pub/sub.

Publish-subscribe, or pub/sub for short, is an asynchronous messaging service that acts as a middleware between the publishers of messages, and people that subscribe to them. In general terms, publishers categorize these messages into classes and publish them without really knowing who is subscribed to them. Similarly, subscribers enroll in the classes that are of interest, receiving only messages associated with that class, without knowing who their publisher is.

With the release of Moonbase Alpha v2, a pub/sub service compatible with Ethereum-style events is now available.

Tutorial: How to Use Pub/Sub on Moonbeam

Since a picture is worth a thousand words, let’s jump into some examples to showcase how pub/sub works on Moonbeam.

To follow this demo, you will need the following:

    • Have MetaMask installed and connected to Moonbase
    • Have an account with funds, which you can get from Mission Control
    • Deploy your own ERC20 token on Moonbase, which you can do following our Remix tutorial but first pointing MetaMask to Moonbase
    • Install NodeJS and the Web3 JS library. For systems on Ubuntu 18.04 or similar, you can follow the first part of this tutorial

Subscribing to Event Logs in Moonbase Alpha v2

Any contract that follows the ERC-20 token standard emits an event related to a transfer of tokens, that is, event Transfer(address indexed from, address indexed to, uint256 value). For this example, we will subscribe to the logs of such events. Using the Web3 JS library, we need the following piece of code:

const Web3 = require('web3');
const web3 = new Web3('wss://wss.testnet.moonbeam.network');

web3.eth.subscribe('logs', {
    address: 'ContractAddress',
    topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}, (error, result) => {
    if (error)
        console.error(error);
})
    .on("connected", function (subscriptionId) {
        console.log(subscriptionId);
    })
    .on("data", function (log) {
        console.log(log);
    });

Note that we are connecting to the WebSocket endpoint of Moonbase Alpha. We use the web3.eth.subscribe(‘logs’, options [, callback]) method to subscribe to the logs, filtered by the given options. In our case, the options are the contract’s address where the events are emitted from, and the topics used to describe the event. More information about topics can be found in this Medium post. If no topics are included, you subscribe to all events emitted by the contract. But in order to filter only the Transfer event, we need to include the signature of the event, calculated as:

EventSignature = keccak256(Transfer(address,address,uint256))

The result of the previous calculation is what’s shown in the code snippet from before. We’ll go back to filtering by topics later on. The rest of the code handles the callback function. Once we execute this code, we’ll get a subscription ID, and the terminal will wait for any event through that subscription:

Next, an ERC20 token transfer will be sent with the following parameters:

  • From address: 0x6Be02d1d3665660d22FF9624b7BE0551ee1Ac91b
  • To address: 0xfcB0B397BB28046C01be6A3d66c7Eda99Fb0f344
  • Value (tokens): 10000000000000000000 – that is 10 with 18 zeros

Once we send the transaction, the log of the event emitted by the transaction will appear in the terminal:

A lot of information is provided in the logs, but you might ask yourself: where is the information in the emitted event? And the answer is: in the logs!

Our target event sends two pieces of indexed information, the “from” and “to” addresses (in that order), which are treated like topics. The other piece of data shared by our event is the number of tokens, that is not indexed. Therefore, there is a total of three topics (the maximum is four), which correspond to the opcode LOG3:

Consequently, you can see that the “from” and “to” addresses are contained inside the topics returned by the logs. Ethereum addresses are 40 hex characters long (1 hex character is 4 bits, hence 160 bits or H160 format). Thus, the extra 24 zeros are needed to fill the gap to H256, which are 64 hex characters long.

What about the number of tokens? Unindexed data is returned in the “data” field of the logs, but this is encoded in bytes32/hex. To decode it we can use for example, this online tool, and verify that the “data” is in fact 10 (plus 18 zeros).

If the event returns multiple unindexed values, these will be appended one after the other in the same order the event emits them. Therefore, each value is then obtained by deconstructing data into separate 32 bytes (or 64 hex character long) pieces.

This example showed how we could subscribe only to event logs of a specific contract. But the Web3 JS library provides other subscription types that we’ll go over in the following sections.

Subscribe to Incoming Pending Transactions

In order to subscribe to pending transactions, we can use the web3.eth.subscribe(‘pendingTransactions’, [, callback]) method, implementing the same callback function to check for the response. This is much simpler than our previous example, and it returns the transaction hash of the pending transactions.

We can verify that this transaction hash is the same as that shown in MetaMask (or Remix).

Subscribe to Incoming Block Headers

Another type available under the Web3 JS library is to subscribe to new block headers. To do so, we use the web3.eth.subscribe('newBlockHeaders' [, callback]) method, implementing the same callback function to check for the response. This subscription provides incoming block headers and can be used to track changes in the blockchain.

Note that only one block header is shown in the image. These messages are displayed for every block produced, so they can fill up the terminal quite quickly.

Check If the Node is Synchronized With the Network

With pub/sub it is also possible to check whether a particular node, which you are subscribed to, is currently synchronized with the network. For that, we can leverage the web3.eth.subscribe(‘syncing' [, callback]) method, implementing the same callback function to check for the response. This subscription will return an object when the node is synced with the network.

Current Limitations

The pub/sub implementation in Frontier is still in active development. This first version allows dApp developers (or users in general) to subscribe to specific event types, but there are still some limitations. From the previous examples, you might have noticed that some of the fields are not showing proper information, and that is because certain properties are yet to be supported by Frontier.

Another limitation is related to the logs of the event. On Ethereum, you can use wildcards and pass in multiple input addresses, for example, to filter specific logs. Let’s say we would like to subscribe to all events of a contract that have two specific addresses in the “topic_1” field (remember that topic_0 is reserved to the event signature). Then we could pass in the following topic as input:

topics: [null, [address1, address2]]

Here, by using the wildcard null in place for the event signature, we are listening to all events emitted by the contract that we subscribed to. But with this configuration, we can also use a second input field, that is topic_1, to define a filter by address as mentioned before.

The current Frontier implementation does not support these features. As an alternative, you can create multiple subscriptions for all the events of the contract and the different addresses, but this increases the number of operations to be carried out. However, this is expected to be supported in future versions of the Moonbase TestNet.

Contact Us

If you have any feedback regarding the Moonbase Alpha v2, pub/sub, or any other Moonbeam related topic, feel free to reach out through our official development Discord server.

Alberto Viera, PaperMoon

Author Alberto Viera, PaperMoon

Developer Relations Manager at PureStake and Moonbeam

More posts by Alberto Viera, PaperMoon