Light.js: How to Build Your Dapp on a Light Client

This article introduces a new JavaScript library, light.js, which provides a set of high-level tools to build light-client-efficient Dapps. It is primarily aimed for Dapp developers looking for an alternative to web3.js.

Why build on top of a light client?

Running a full Ethereum node is laborious: it’s unthinkable today to have end-users run full nodes on consumer laptops. As a result, Dapp developers rely on external services for their backend infrastructure. These services run at scale, which in some degree is a step towards centralization.

Light clients solve this problem. A light client is a special kind of Ethereum node that is, as suggested by its name, light. Concretely, this means:

  • low on resources usage: CPU, memory, storage, I/O operations…
  • embeddable: in a desktop application, on mobile, within a web app.
  • but still remains trustless.

If a Dapp is paired with its own light client to query state and make transactions, it does not need to rely on centralized infrastructures anymore. At Parity Technologies, we wish to embed light clients everywhere: in desktop and mobile apps, browser extensions, web apps… We believe light clients will play a key role in Ethereum mass adoption.

Meet light.js

We have built over fifteen Dapps for our now-defunct Parity UI. When building these Dapps, we came across development patterns that were popping up again and again. For example, if we want to constantly show the latest balance of an ERC20 token for a particular address, we can do one of the following:

  • poll the chain every 5 seconds
  • do some smart-polling, e.g. make a new call only once the previous call has finished
  • listen to incoming blocks via pub/sub, and only make a call on new block
  • listen to state changes via pub/sub (Parity only, see the documentation)

These patterns appear often in the Dapps ecosystem, if you are a Dapp developer, chances are you are using one of them.

However, these are empirical patterns: they are well-known amongst Dapp developers, but are not always enforced.

The goal of light.js is simple:

  • pick the best pattern that works with light clients: listen to headers via pub/sub and make API calls on new header, while making sure that the amount of network calls is not excessive.
  • put it into a high-level library, so that Dapp developers use a simple API instead of following a supposedly well-known pattern.

Concretely, it means:

// Before, with web3.js

const contract = web3.eth.Contract(ABI, '0x00..ff');

async function updateBalance() {
const balance = await contract.methods.balanceOf('0xabc').call();
console.log(balance);
}

// Dapp developer: "This should be light-client-friendly..."
web3.eth.subscribe('newBlockHeaders').on('data', updateBalance);

---

// Now, with light.js

import { makeContract } from '@parity/light.js';
// Dapp developer: "I'm sure this is light-client-friendly!"
makeContract(ABI, '0x00..ff')
.balanceOf$('0xabc')
.subscribe(console.log); // Logs the result when balance changes

High-level reactive programming

light.js is meant to be high-level: the Dapp developer calls a function and the library chooses the best pattern, handles caching, and does all the dirty work behind the scenes.

light.js only exposes 10 high-level functions, amongst them:

  • query the balance: balanceOf$('0x123');
  • interact with contracts: makeContract(abi, contractAddress);
  • post a transaction: post$(myTx);
  • manage accounts: account$();
  • etc. (you can find the full list in the documentation)

We only expose ten functions, but they should cater to the needs of most Dapps today. For reference, we built a fully-featured Ethereum & ERC20 wallet on top of light.js, called Parity Fether. Based on feedback, we might of course add new functions—feel free to create an issue if you need one.

You might have noticed the $ sign behind the names of the functions. This is a JavaScript convention to represent Observables. Observables are a JavaScript structure that basically represents an ongoing asynchronous stream of data, ordered in time. Some examples that can be expressed in Observables:

  • clicks on a button
  • tweets on your Twitter feed
  • new blocks on the Ethereum blockchain

Data streams are an intuitive, high-level way to express changes happening on the Ethereum blockchain. The Observables in light.js fire when something changes, and the Dapp reacts to those changes. Moreover, Dapp developers can leverage the huge ecosystem around Observables to build complex Dapps with the basic ones provided by light.js.

To learn more about reactive programming, head towards our documentation.

Start hacking with light.js

The best way to start hacking with light.js is to:

An important thing to note is that while light.js is optimized for light clients, it will of course work with full nodes, local or remote. In particular, it works out-of-the-box if you have MetaMask installed:

import light, { balanceOf$ } from '@parity/light.js';

// Wait for web3.currentProvider to be injected
window.addEventListener('load', () => {
// Tell light.js to use MetaMask provider
light.setProvider(window.web3.currentProvider);

balanceOf$('0x123').subscribe(console.log); // Logs the balance
});

Even if you can’t embed a light-client in your app today for whatever reason, light.js works well with remote full nodes, so we do recommend light.js for any kind of Dapp. The day you decide to finally switch to a light client, you won’t have to change a single line of code.

If you are writing your Dapp with React, we also provide light.js-react, a tiny library to integrate light.js easily with React apps:

import * as React from 'react';
import { balanceOf$ } from '@parity/light.js';
import light from '@parity/light.js-react';

@light({
myBalance: () => balanceOf$('0x123')
})
class MyComponent extends React.Component {
render() {
return (
<div>
The balance of "0x123" is {this.props.myBalance.toFormat(2)}.
</div>
);
}
}

To see an example of how light.js is used in a real-world app, see how we built Parity Fether on top of it.

Next steps

light.js is a small step in the direction of our effort to promote the use of decentralised light clients. We would love to hear feedback from developers, so if you have questions or ideas about this library, make sure to post an issue on the Github repo: https://github.com/paritytech/js-libs/tree/master/packages/light.js.