Tutorial: Real-time Frontend Data with Embark’s Subspace and Infura

https://blog.infura.io/frontend-dapp-development-2/

In the previous post on frontend development, we ended with a website that allowed a user to connect via MetaMask and it displayed their account balance of Ether. Yet many Ethereum use-cases such as DeFi involve sending and receiving ERC20 tokens, which are tracked differently than native Ether. In this guide, we’ll show how to track a deployed contract’s transactions, and display and update those numbers in the frontend as they can potentially update in each new confirmed block. For this example, let’s track the Dai<>Eth trades on Uniswap.

Uniswap

To do this, we’ll use a library called Subspace from the Embark team at Status. Our preferred way to track real-time data is using React Hooks, so we’ll walk through setting up a frontend with Ethereum data streaming by working through Embark’s example code found here.  So in total this frontend uses Infura, React (with helper libraries), and Subspace.

  • This tutorial is a separate website from the prior frontend tutorial.  We’ll go through the important pieces of code but not all of it, so it’s recommended to clone the example repository and then run npm install and npm start in the root directory (or alternatively use yarn) which will get your website up on localhost:3000 . From there, we’ll see how adding more data tracking is easy with Hooks and useEffect().  

There’s only 3 important files to go over. To start, look in the short file of src/index.js, in it we have <SubspaceProvider> wrapping the entire App, which gives every component access to the web3 object, which is our connection to Ethereum through Infura as the web3 provider. We’ll set that up later in App.js to use Infura for the web3 connection.

const rootElement = document.getElementById('root')
ReactDOM.render(
  <SubspaceProvider web3={web3}>
    <App />
  </SubspaceProvider>,
  rootElement
);

In contracts/exchange_abi.json we have the ABI of Uniswap, which is a specification for the deployed Uniswap contract of every function that we have Subspace track. The ABI is specified in JSON, and we’ll use it for web3’s Contract object to interact with Uniswap throughout the dapp. Every deployed contract on Ethereum has an ABI, so you could add any existing contract to this frontend and track it’s transactions by knowing it’s ABI and the deployed contract’s address.


    {
        "name": "TokenPurchase",
        "inputs": [
            {
                "type": "address",
                "name": "buyer",
                "indexed": true
            },
            {
                "type": "uint256",
                "name": "eth_sold",
                "indexed": true
            },
  ... and a lot more

In App.js, we start by initializing the web3 object with our Infura connection. If you don’t have an API key, you can get one for free by signing up at infura.io/register . Next, create a Contract object by combining the ABI and contract address of that ABI. This address is Uniswap’s contract that holds the Dai in its liquidity pool.

  • Note, you can now go back to index.js to add this same Infura URL as the Web3 provider there.
const web3 = new Web3("wss://mainnet.infura.io/ws/v3/806ce35b64344f04a9a7e47379d9ca41");
const dai = new web3.eth.Contract(exchangeABI, '0x2a1530C4C41db0B0b2bB646CB5Eb1A67b7158667');

In this next part, we set up the React state variables that Subspace will use. The subspace object is created from useSubspace(), and we pass in the Contract object we just created. Then a couple definitions for helping work with values in wei coming from the transactions.

function App(props) {

    const subspace = useSubspace();
    const daiContract = subspace.contract(dai);

    const [txnObserver, setObservable] = useState();
    const [last5Observable, setlast5Observable] = useState();
    const [latestBlock, setBlock] = useState();
    const [last5, setLast5] = useState([]);

    //Trade details object for calculating exchange rate
    function TradeDetails(tokensSold, ethBought) {
        this.tokensSold = web3.utils.fromWei(tokensSold);
        this.ethBought = web3.utils.fromWei(ethBought);
        this.exchangeRate = this.tokensSold / this.ethBought;
    }

These next 3 code blocks are the Hooks that are the magic of the dapp and give us the real-time streaming we want. We’ll set it up so that we look at the last 50 mined blocks, and show the 5 most recent Eth->Dai trades that happen in those blocks, which will continuously update as more blocks are mined and trades happen.  

useEffect(() => {
        web3.eth.getBlockNumber().then((block) => setBlock(block));
        if (typeof(latestBlock) != "number") 
            return;

        const EthPurchased$ = daiContract.events.EthPurchase.track({
            fromBlock: latestBlock - 50
        });
        const last5$ = EthPurchased$.pipe($latest(5));
        setObservable(EthPurchased$);
        setlast5Observable(last5$)
    },[latestBlock])

The above useEffect() sets up the Hook that gets the last 5 EthPurchase events from the latest 50 Ethereum blocks. Importantly, the setObservable(EthPurchased$) is every trade event we are tracking, and we restrict it to five events shown in the frontend by using the pipe operator (imported from RxJS) and creating an Observable of those five.

Next, we have another  useEffect() Hook that subscribes to all transactions fitting the requirements we defined above for EthPurchase, and just puts them in console.log

   useEffect(() => {
        if ((txnObserver === undefined) || (typeof latestBlock != "number")) {
            return;
        }
        txnObserver.subscribe((trade) => {
            console.log(trade);
        });
    
        return () => { txnObserver.unsubscribe(); }
    }, [txnObserver, latestBlock]);

From the last5Observable created in our first Hook, we get the transaction details of those here:

  useEffect(() => {
        if (last5Observable === undefined) {
            return;
        }
        last5Observable.subscribe((fiveTrades) => {
            const prices = fiveTrades.map(trade => {
                const txnDetails = new TradeDetails(trade.tokens_sold, trade.eth_bought);
                return {'block': trade.blockNumber, 'rate': txnDetails.exchangeRate}
            });
            setLast5(prices);
        });
    
        return () => { last5Observable.unsubscribe(); }
    }, [last5Observable]);

Lastly, we have some React UI code and than you can see the frontend!  Here’s a gif showing it. This was recently started, and there have been 3 trades so far.  In the developer console, we can see the additional transaction details of each of those trades. As the trades we are watching stream in, the oldest will fall off the stack.

We hope this was a useful explanation of the Subspace library, which really makes frontend development for dapps simple and easy, especially when it uses Infura for web3 data! So clone this boilerplate repo here, use Subspace, and if you have any questions on this please discuss it with us here For more tutorials, visit the Tutorials section in our Community.

Building Dapp Frontends with React & Network.js

https://blog.infura.io/dapp-frontend-network/

Building Dapp Frontends with React & Network.js

Building a dapp frontend, using React & Network.js, that can connect and request data from the Ethereum mainnet using Infura & Metamask

Developing a service or business on Ethereum can be difficult, due to the intricacies of working with a blockchain and the novel UX / UI issues to solve. This series of guides aims to serve as a quickstart to Ethereum dapp development, with a focus on front-end solutions. Thankfully, you don’t need to reinvent the chain. There are many pre-existing tools and services that can be leveraged to get a dapp running quickly, including:

  • Infura as a JSON-RPC connection to the mainnet or testnets;
  • MetaMask to serve as the user’s wallet and web3 provider;
  • OpenZeppelin for contracts and Network JS, a javascript library for web3 (i.e. blockchain development).

Using these products together has the benefit of replacing database setup and user credential management, resulting in a quick-to-start blockchain infrastructure. In this tutorial, we’ll create a React dapp using Network JS that can interact with MetaMask, which uses Infura as its connection to the Ethereum mainnet.

Environment Setup

Before we start you should have Node.js with npm installed. Let’s begin by creating a new project…

mkdir web3-infura && cd web3-infura

npm init -y

…and installing Network JS by OpenZeppelin to get library methods for working with web3:

npm install @openzeppelin/network

Now we’re ready to create a Dapp

Using a common React boilerplate called create-react-app we create the initial version of our dapp. This is easy to setup with npx (npm’s package runner). One line and we’ll have the scaffolding files for our dapp:

npx create-react-app client

In order to use Infura inside your dapp, you’ll need to head to infura.io/register to set up an account and create a Project.

In the client/src/App.js file, we find the react project and look for the placeholder code written in App.js. We replace the placeholder code with the following:

import React from 'react';
import './App.css';
import { useWeb3 } from '@openzeppelin/network/react';
const infuraProjectId = '<YOUR_INFURA_PROJECT_ID';
function App() {
const web3Context = useWeb3(`wss://mainnet.infura.io/ws/v3/${infuraProjectId}`);
const { networkId, networkName, providerName } = web3Context;
return (
<div className="App">
	<div>
	<h1>Infura/MetaMask/OpenZeppelin Dapp</h1>
		<div>
    	Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'}
		</div>
		<div>
		Provider: {providerName}
		</div>
	</div>
</div>
);
}
export default App;

Our dapp will now reveal which Ethereum network (mainnet or a testnet) it is currently connected to, and the web3 provider it’s using.

Building Dapp Frontends with React & Network.js

Let’s test this out by saving and starting the browser by running npm start from within the /client directory. Test it out by replacing the mainnet in the Infura websocket URL to rinkeby. If MetaMask is already installed, disable the extension here as you will see it takes precedence.

How this works is that in the above code we import useWeb3 from the React implementation of Network JS (@openzeppelin/network/react) in order to get a web3Context. This is a Javascript hook that will attempt to retrieve an injected web3 provider which, by default, is MetaMask. If no provider is injected, it will use the Infura URL set as web3Context.  The term injected means code or data that comes from a user’s browser and is available for the website to use.

Please note there are a variety of Ethereum web3 libraries available in many languages, and while the implementation specifics differ, they all implement creating a web3 connection through a ‘provider’ or ‘signer’ so this step is important to understand.

The useWeb3 hook attempts to obtain an injected web3 provider first, before falling back to a network connection. Alternatively use useWeb3Injected for an injected web3 provider or useWeb3Network for a network provider such as Infura or a private node.

Add a React Component

Our next goal is to move the display of the current Ethereum network to a component and see how components are re-rendered when changes are made, such as the network.

To do this, the first step is to select a web3 provider to be injected, and install it. We’re going to use MetaMask as our web3 provider in this tutorial, so head to metamask.io to install it.

In the code below, the Web3Data component expects a web3Context. The networkId, networkName and providerName are obtained from the web3Context and displayed in the component.

We then create a components directory in the client/src directory, and create a Web3Data.js file with the code below:

import React from 'react';
export default function Web3Data(props) {
const { web3Context } = props;
const { networkId, networkName, providerName } = web3Context;
return (
<div>
	<h3> {props.title} </h3>
	<div>
    Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'
	</div>
	<div>
	Provider: {providerName}
	</div>
</div>
);
}

Now back inside App.js, we’ll add the Web3Data component to provide a web3Context. In the client/src/App.js file, add this import for the Web3Data component:

import Web3Data from './components/Web3Data.js';

Then replace the entire contents of the App function in App.js with the following code:

const web3Context = useWeb3(`wss://mainnet.infura.io/ws/v3/${infuraProjectId}`);
return (
<div className="App">
	<div>
	<h1>Infura React Dapp with Components!</h1>
	<Web3Data title="Web3 Data" web3Context={web3Context} />
	</div>
</div>
);

Let’s run npm start from within the client directory to start the dapp again to see how React components are re-rendered by Network JS when changes are made to accounts, connection type or network. You can test this out by changing networks in MetaMask.

Building Dapp Frontends with React & Network.js

Requesting Access to Account Address

Requesting a user’s account address and displaying it is one of the most common patterns among dapps. However, injected web3 providers don’t give the dapp access to a user’s address until that user has given permission. This bit of friction has been intentionally designed to protect user privacy and prevent bad actors from using an account address to track the user around the internet. You can learn more about this communication protocol here.

In our Web3Data.js file, we replace the contents of the component with the following code:

import React, { useCallback } from 'react';
export default function Web3Data(props) {
const { web3Context } = props;
const { networkId, networkName, accounts, providerName } = web3Context;
const requestAuth = async web3Context => {
try {
	await web3Context.requestAuth();
	} catch (e) {
	console.error(e);
	}
};
    
const requestAccess = useCallback(() => requestAuth(web3Context), []);

return (
<div>
<h3> {props.title} </h3>
	<div>
    Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'}
	</div>
	<div>
	Your address: {accounts && accounts.length ? accounts[0] : 'Unknown'}
	</div>
	<div>
	Provider: {providerName}
	</div>

	{accounts && accounts.length ? (
	<div>
	Accounts & Signing Status: Access Granted
	</div>
	) : !!networkId && providerName !== 'infura' ? (
	<div>
	<button onClick={requestAccess}>Request Access</button>
	</div>
	) : (
	<div></div>
	)}
</div>
);
}

Here’s what the above code is doing:

  • Accounts are pulled from the web3Context and, if available, the user’s address is displayed in the dapp;
  • If the accounts we have requested are not available, the dapp will produce a button that enables the user to give the dapp access to their address;
  • When the user presses that button, the requestAuth function in web3Context is called and the injected web3 provider can display a dialog to the user to request access. Using React functionality, useCallback is used to set up the callback for account request access.

Now, we start the dapp again by running npm start from within the client directory.

In the browser, we press ‘Request Access’ to request access to the user’s address, and then accept the request in the Metamask popup (or extension if no popup). The user’s address will then be displayed. To restart the process, logging out of Metamask will make the user request access again.

Building Dapp Frontends with React & Network.js

Account Balance

Finally, our third goal is to add to the React component to display the account balance.

In the code below, we get the lib object, which contains the blockchain data we want, from the web3Context, which is an initialized instance of web3.js (a library that Network.js relies upon).

We use lib for the method getBalance to request the balance of an account, which returns the balance as in value in units of wei. It’s easier to work with in units of ether, so we convert it with the method fromWei.

Since we want to track the state of the account balance, we use a React-specific function called useState. We also use useEffect to watch for if the account address or networkId has changed, and update accordingly.

To get our dapp to display the account balance, we replace the contents of the component in our Web3Data.js file with the following code:

import React, { useState, useEffect, useCallback } from 'react';
export default function Web3Data(props) {
const { web3Context } = props;
const { networkId, networkName, accounts, providerName, lib } = web3Context;
const [balance, setBalance] = useState(0);
const getBalance = useCallback(async () => {
	let balance = accounts && accounts.length > 0 ? lib.utils.fromWei(await lib.eth.getBalance(accounts[0]), 'ether') : 'Unknown';
	setBalance(balance);
}, [accounts, lib.eth, lib.utils]);
    
useEffect(() => {
getBalance();
}, [accounts, getBalance, networkId]);

const requestAuth = async web3Context => {
	try {
		await web3Context.requestAuth();
	} catch (e) {
		console.error(e);
	}
};
const requestAccess = useCallback((web3Context) => requestAuth(web3Context), []);
return (
<div>
<h3> {props.title} </h3>
	<div>
    Network: {networkId ? `${networkId} – ${networkName}` : 'No connection'}
    </div>
	<div>
    Your address: {accounts && accounts.length ? accounts[0] : 'Unknown'}
    </div>
	<div>
    Your ETH balance: {balance}
    </div>
	<div>
    Provider: {providerName}
    </div>
	{accounts && accounts.length ? (
	<div>
    Accounts & Signing Status: Access Granted
    </div>
	) : !!networkId && providerName !== 'infura' ? (
	<div>
	<button onClick={requestAccess}>Request Access</button>
	</div>
	) : (
	<div></div>
	)}
</div>
);
}

We start the dapp again by running npm start from within the client directory and ta-da! The dapp should now display the account balance.

Building Dapp Frontends with React & Network.js

What you have now built is a dapp frontend (using Network.js) that can connect to the Ethereum mainnet and request data from it (using Infura). Users “login” (using Metamask), and the dapp displays the information the user cares about and reacts to state changes.  

Keep Learning

Our next tutorial will show you how to display the ERC20 balance of a user and let them transfer it. Further web3 development guides will include topics such as building with Ethers.js, using OpenZeppelin’s Starter Kits, and more complex integrations such as setting up Eth Log filters with Truffle’s Drizzle framework to react in real time to blockchain data changes on the frontend.


Never miss a tutorial — subscribe to our newsletter to stay up to date. Ready to test out Infura? Sign up to start building for free!