Using useDapp with Moonriver

Jacob E. Dawson
TrueFi Engineering

--

In this tutorial we’re going to learn how to set-up Metamask to interact with the Moonriver network, and then build a dapp with useDapp to interact with accounts on the network. Before we get into the tutorial, let’s do a quick overview of the Polkadot ecosystem to ensure we understand the technology we’re dealing with.

What is Polkadot (DOT)?

Polkadot is a “next-generation” blockchain that combines multiple specialized blockchains into a scalable network. It’s multi-chain at its core, designed to allow private, permissioned, custom blockchains to interact with public chains via secure cross-chain communication. For example: a university could run its own private chain that sends degree-verification to a public chain while keeping the private data hidden.

Unlike Ethereum, which is built to be a monolithic blockchain, Polkadot is predicated on a system of users creating their own blockchains on top of it. The foundation layer is called the “Relay Chain”, and the L1 networks that sit above it are know as “parachains”. The Relay Chain is where transactions are settled with permanence, while the parachains operate at faster speeds, and depend on the relay chain for security & finality.

What is a Relay Chain?

In the Polkadot system, the Relay Chain is the “core chain” of the system, which provides security for the parachains that sit above it. In addition to being a settlement layer, the Relay Chain distributes messages between parachains using the XCMP (cross-chain message passing) protocol.

What are Parachains?

Parachain stands for “parallelizable chain”, which is essentially an independent L1 chain that attaches to the base Relay Chain, and relies upon it for security (meaning that each parachain doesn’t need to bootstrap a set of validators). Each parachain can have its own token, governance, and functionality. The parachain architecture allows the separation of public & private chains, while maintaining interoperability between parachains, and also provides the ability to scale computations. The idea is that each parachain will optimize for a particular set of features, for example, file-storage, or identity management.

What is Kusama (KSM)?

Alright, so we have the Polkadot ecosystem, and a Relay Chain <> parachain architecture. Since Polkadot is still in development, the publicly accessible pre-production environment is called “Kusama”, which is also the name of the Relay Chain. Kusama allows developers to experiment and test new parachains & applications in preparation for Polkadot to be launched into production. We can think of Kusama as the sandbox for Polkadot (it is referred to as the “canary network”), except the native tokens are real and tradeable on the open market (unlike, for example, Ropsten ETH). Upgrades to Polkadot are tested on Kusama before they are released.

What is Moonbeam?

Moonbeam is an EVM-compatible smart-contract parachain on the Polkadot network that aims to provide a seamless experience for Ethereum developers looking to build on Polkadot. It includes bridges that connect it to existing Ethereum networks, and includes a Web3-compatible API, so we can use a standard Ethereum development toolkit, including useDapp, to create dapps & deploy them with minimal changes.

At the moment Moonbeam is still in development, and is currently at an Alpha Testnet stage. The development of Moonbeam is tested and initially deployed on Moonriver, the last thing we need to know about before we get started!

What is Moonriver (MOVR)?

Moonriver is a parachain that sits on the Kusama network, acting as the “sister” of Moonbeam, and allowing developers to test and experiment with Moonbeam code in a “canary” environment under real economic conditions. Even though Moonriver is a sandbox parachain, MOVR tokens are real — they are publicly tradeable on open markets for real money. Today we’re going to connect MetaMask to Moonriver, and then build a simple dapp that will allow us to interact with a Moonriver test network, display a user’s account balance, and send MOVR to another account.

Alright — now we’ve got a good grasp of the ecosystem we’re dealing with, let’s start building!

Step 1.) Setting up Metamask to Work with Moonriver

When setting up a new network on MetaMask, we have to add several pieces of information. Open up MetaMask, select the Network dropdown at the top, then select “Custom RPC”. For Moonriver, we’ll use the following config:

Network Name: Moonriver

New RPC URL: https://rpc.moonriver.moonbeam.network

Chain ID: 1285

Currency Symbol (optional): MOVR

Once you’ve saved that configuration you’ll be able to select the Moonriver network, which is compatible with your Ethereum account:

Great, so we’ll be able to sign transactions and interact with the Moonriver network via MetaMask, let’s start building the dapp.

Step 2.) Creating the App Folder & Adding Dependencies

For this tutorial we’ll use a classic stack of React (via Create React App), Chakra UI (for components & style), and of course, useDapp to provide authentication and interaction with the Moonriver blockchain.

We’ll start by creating a React app with a Chakra UI template:

npx create-react-app moonriver-usedapp — template @chakra-ui

Then let’s move into our newly created project:

cd moonriver-usedapp

Add useDapp:

yarn add @usedapp/core

Then let’s start up the dapp:

yarn start

If everything’s gone well we should see the following screen at localhost:3000

Step 3.) Create the Page Layout

We’re going to keep this to a simple single page app, with a small input form and some text to display balances. First, rename App.js to App.jsx so that we get autocomplete if you’re using VSCode. We’ll remove some of the boilerplate code and add a button that we’ll use for connecting to the network via useDapp:

// App.jsx
import React from 'react';
import {
ChakraProvider,
Spacer,
Flex,
Button,
Box,
Text,
VStack,
Grid,
theme,
} from '@chakra-ui/react';
import { useEthers } from '@usedapp/core';
function App() {
return (
<ChakraProvider theme={theme}>
<Box textAlign="center" fontSize="xl">
<Flex justifyItems="flex-end" p={2}>
<Spacer />
<Button>Login</Button>
</Flex>
<Grid minH="100vh" p={3}>
<VStack spacing={8}>
<Text>Connecting to Moonriver with useDapp</Text>
</VStack>
</Grid>
</Box>
</ChakraProvider>
);
}
export default App;

That’s all we need to get started, the page should now look like this:

Step 4.) Adding useDapp & Setting Up the Connect Button

In order to connect to the Moonriver network, we’ll need to trigger MetaMask to ask for permission to access the user’s account. We’ll design a Connect button to open MetaMask if we’re not logged in, and show our account address if we are logged in.

To get started, first we’ll import the DAppProvider & ChainId from useDapp, so that we can wrap our application in the provider. Let's remove the unnecessary code from index.js and configure our provider:

// index.js
import React, { StrictMode } from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ChainId, DAppProvider } from '@usedapp/core';
const config = {
readOnlyChainId: ChainId.Moonriver,
};
ReactDOM.render(
<StrictMode>
<DAppProvider config={config}>
<App />
</DAppProvider>
</StrictMode>,
document.getElementById('root')
);

Now that our useDapp provider is set up, we’re able to access our account state globally and interact with our Web3Provider. Let’s move over to App.jsx and import what we need from @usedapp/core in order to trigger authorize via MetaMask and access our account:

// App.jsx
// ...other code
// import what we need from useDapp
import { useEtherBalance, useEthers } from '@usedapp/core';

Now that our useDapp provider is set up, we’re able to access our account state globally and interact with our Web3Provider. Let’s move over to App.jsx and import what we need from @usedapp/core in order to trigger authorize via MetaMask and access our account:

// App.tsx
function App() {
const { activateBrowserWallet, account } = useEthers();
const etherBalance = useEtherBalance(account); // pass in the user's account
//.. other code
}

So now we have our methods & data set up, the last step to get our Connect button working is to hook up an onClick event handler:

// App.tsx
// ...other code
{/* configure our button */}
<Button onClick={() => activateBrowserWallet()}>
{account ? account : `Connect`}
</Button>

Here’s what the code in App.jsx should look like now:

// App.jsx
import React from 'react';
import {
ChakraProvider,
Spacer,
Flex,
Button,
Box,
Text,
VStack,
Grid,
theme,
} from '@chakra-ui/react';
import { useEtherBalance, useEthers } from '@usedapp/core';
function App() {
const { activateBrowserWallet, account } = useEthers();
const etherBalance = useEtherBalance(account);
return (
<ChakraProvider theme={theme}>
<Box textAlign="center" fontSize="xl">
<Flex justifyItems="flex-end" p={2}>
<Spacer />
{/* configure our button */}
<Button onClick={() => activateBrowserWallet()}>
{account ? account : `Connect`}
</Button>
</Flex>
<Grid minH="100vh" p={3}>
<VStack spacing={8}>
<Text>Connecting to Moonriver with useDapp</Text>
</VStack>
</Grid>
</Box>
</ChakraProvider>
);
}
export default App;

Now it’s time to test out our app! Make sure your MetaMask is connected to the Moonriver network. If you click the Connect button, your MetaMask extension should pop-up and show the “Connect With MetaMask” screen. Click “Next”, double check the permissions, and then click “Connect” to allow the dapp to view your account:

Now the Connect button will show your full address while you’re logged in. That’s actually a bit long to comfortably display, and the standard practice in web3 dapps is to shorten the user’s account, so we’ll quickly add a function to trim the account:

// App.jsx
// ...other code
function trimAccount(accountString) {
return `${accountString.slice(0, 6)}...${accountString.slice(
accountString.length - 4,
accountString.length
)}`;
// ...other code
{/* configure our button */}
<Button onClick={() => activateBrowserWallet()}>
{account ? trimAccount(account) : `Connect`}
</Button>

There we go! When you’re logged in you should now see a nice short address in the Connect button:

Great — our Connect button is all set up, and we’re reading the user’s account. Our next steps will be to display the user balance, and then send some MOVR to another account…

Step 5.) Display the User’s MOVR Balance

NOTE: Moonriver is a live sister network to Moonbeam, and uses real tokens. The MOVR tokens that we’re using in this section have real economic value. To purchase MOVR tokens you can use well-known centralized exchanges such as Kraken.

In this section we’re going to display the user’s MOVR balance on the Moonriver parachain, and then we’ll create a simple interface to send tokens from one account to another. To start off, let’s display the user’s balance.

First, install @ethersproject/units:

yarn add @ethersproject/units

Then, inside App.jsx, we’ll import formatEther from ethers:

// App.jsx
// ..other imports
import { formatEther } from "@ethersproject/units";

We’re already pulling in the user’s account balance via the useDapp useEtherBalance hook, now all we have to do is format the user’s MOVR balance using the formatEther method and display the output in a Text component:

// App.jsx
// ...other code
<Text>
{/* we use a short-circuit && to make sure etherBalance is defined */}
MOVR balance: {etherBalance && formatEther(etherBalance)}
</Text>

Now when we’re logged in, we will see a MOVR balance displayed on the screen:

Excellent! Let’s go one step further and send MOVR from one account to another via the Moonriver network..

Step 6.) Sending MOVR to another Moonriver address

In this section we’re going to make a real transaction, and send MOVR to another account address. To follow along you’ll need some MOVR in your account, and you should control the other account that you’ll be sending MOVR to.

We’re going to need a few elements here — An input in order to accept an amount to send, an input in order to select an address, and a button in order to trigger the transaction via MetaMask. Let’s start by adding our components:

// App.jsx
// ...other imports
import { Input } from "@chakra-ui/react";
// ...other code
<Grid minH="100vh" p={3}>
<VStack spacing={8}>
<Text>Connecting to Moonriver with useDapp</Text>
<Text>
MOVR balance: {etherBalance && formatEther(etherBalance)}
</Text>
<Input placeholder="Receiver address" />
<Input placeholder="Amount to send" />
<Button>Send</Button>
</VStack>
</Grid>

You should now see the following layout:

We’ll need some state to handle the inputs, so let’s add useState to manage it:

// App.jsx
import { useState } from "react";
// ..other code
const [receiverAddress, setReceiverAddress] = useState("");
const [amountToSend, setAmountToSend] = useState(0);

And now we can hook up event handlers to our inputs in order to define our receiver and amount values:

// App.jsx
// ...other code
<Input
placeholder="Receiver address"
value={receiverAddress}
onChange={e => setReceiverAddress(e.target.value)}
/>
<Input
placeholder="Amount to send"
type="number"
value={amountToSend}
onChange={e => setAmountToSend(e.target.value)}
/>

Excellent! If you have React Dev Tools installed (and you should), we can see that our state is being registered and stored within our App component:

The last things we’ll need to do are to setup a click handler for our Send button, which will pass our transaction data to useDapp and then trigger MetaMask to let us approve the transaction. For this step, we’ll be using the useSendTransaction hook in our component, so let's import that, and destructure the sendTransaction and state values:

// App.jsx
// ...other imports
import { useSendTransaction } from "@usedapp/core";
// ...other code
function App() {
const { sendTransaction, state } = useSendTransaction();

Since we’re going to be parsing Ether in order to send valid values, we’ll import utils from ethers so we can use the parseEther method:

// App.jsx
import { utils } from "ethers";

Now we can write our event handler, which we’ll trigger from our Send button. At the same time, let’s add a disabled state to prevent users triggering the Send button while the transaction is processing:

// App.jsx
// ...other code
const [disabled, setDisabled] = useState(false);
function handleSendTransaction() {
if (receiverAddress && amountToSend) {
setDisabled(true);
sendTransaction({
to: receiverAddress,
value: utils.parseEther(amountToSend)
})
}
}

We’re almost there! We have a couple more things to add and we’re ready to send our MOVR tokens across the Moonriver network with the help of useDapp! We’re going to add a useEffect hook that will listen to the state variable in order to handle the disabled state of our Send button and also to reset the inputs once our transaction is complete, and we’ll hook up the handleSendTransaction function to our Send button:

// App.jsx
// ...other imports
import { useState } from "react";
// ...other code
useEffect(() => {
if (state.status !== "Mining") {
setDisabled(false);
setAmount(0);
setReceiverAddress("");
}
}, [state]); // add state as our dependency
// ...other code
<Button onClick={handleSendTransaction} disabled={disabled}>Send</Button>

Now we’re ready to test it out. Input a Moonriver address that you control into the receiver input, and an amount of MOVR into the amount input. Remember that if you put too high a value into amount input MetaMask will return an error, so for this example I’m going to send 0.01 MOVR. Click “Send” and MetaMask should now prompt you to confirm the transaction:

Click confirm and we’ll see that the Send button is disabled, and MetaMask is showing a pending transaction. After a few seconds we’ll see a confirmation message, and we’ll see that our MOVR balance has decreased!

Let’s quickly check that our MOVR arrived at the other account:

Perfect, the 0.01 MOVR has arrived.

And that’s it — we’ve done everything we set out to do. Let’s quickly summarize:

  • Polkadot is a “blockchain of blockchains”, with Relay Chains and parachains
  • Moonbeam is a new parachain that will soon launch on Polkadot
  • Kusama is Polkadot’s “sandbox” or “canary network”, for testing under real economic conditions
  • Moonriver is Moonbeam’s “sister” parachain, running on top of the Kusama Relay Chain
  • We added Moonriver to our MetaMask extension
  • We create a simple React dapp with useDapp
  • We connected our Moonriver account to our dapp
  • We displayed our MOVR balance
  • We sent real MOVR tokens from one Moonriver account to another

Well done! Hopefully this tutorial gives you some insight into the Polkadot ecosystem and opens your eyes to the possibilities of creating dapps quickly and efficiently with useDapp.

The full source code for this dapp can be found at: https://github.com/jacobedawson/moonriver-usedapp

--

--