Making a User Interface for DApps in Redwood
Published: 2021-11-15 22:10:35

---
title: Making a User Interface for DApps in Redwood
published: true
description: There are a lot of cool uses for blockchain. So let's take a look at what tracking videos might look like.
tags: 
cover_image: https://cdn.sanity.io/images/5ad74sb4/production/ff0ee6fbd5cc5d44eea84b000793a2c21728b7ab-1000x420.png?w=2000&h=2000&fit=max
canonical_url: https://mediajams.dev/post/Making-a-User-Interface-for-DApps
---

As much of a buzz term as blockchain has become, it's still a great technology. To take some of the magic out of it, blockchain is essentially a distributed database. A blockchain is made of blocks that contain data and those blocks are chained together across multiple machines, which make up the distributed network.

In this article, we'll build a distributed app in the Redwood framework that handles video data. We'll make a smart contract to handle our blockchain interactions. Then we'll set up the Redwood distributed app that will work with the blockchain.

## Background on DApps

A DApp is very similar to a regular app. The only difference is that the back-end runs on a decentralized network instead of a central server that might host APIs or other services. It uses the [Ethereum](https://ethereum.org/en/what-is-ethereum/) blockchain to store data and it has smart contracts to handle the business logic.

By using smart contracts, DApps don't have an owner. A smart contract is like a set of rules that every part of the network has to follow. Once the smart contract has been deployed to Ethereum, you can't change it. That means no companies or individuals can change the instructions for that set of rules in any way.

### Ethereum

For a little context, Ethereum is a decentralized, open-source blockchain that has smart contract functionality. This is used as the network to host smart contracts. Anybody can join an Ethereum network and they will automatically follow the rules in the smart contract.

## Setting up a Redwood app

Now that we have a little background on what we'll be making, let's start by making the Redwood app. To do this, open a terminal and run:

```bash
yarn create redwood-app user-dapp
```

This will generate new directories and files for the project that covers everything from the front-end to the back-end, including a database. Even when you're working with a DApp, there might be reasons you want to track data in a more common way, like easier data processing.

With the app created, you should see two main directories: `api` and `web`. The `api` folder holds all of the logic for the GraphQL back-end and the code to manage the database transactions. The `web` folder, which is where we'll be focused, holds all of the front-end logic and user interface.

## Some Installations

You'll need to [download Ganache](https://www.trufflesuite.com/ganache) to set up a local Ethereum network that you can develop on. Once you have it installed, you can use the "Quickstart" option.

You'll also need to install Truffle with the following command in the `web` directory.

```bash
yarn add truffle
```

This is how we'll deploy the smart contract to the local Ethereum network.

There are a few dependencies that need to be installed too. In the `web` directory run:

```bash
yarn add truffle truffle-contract web3
```

This gives us everything we need for the smart contract.

## Initializing the Truffle project

Now run the following in the `web` directory:

```bash
yarn truffle init
```

This will create an initial migration and the smart contract for it in the `web` directory. Now we'll jump straight into some code since we have everything set up.

## Writing the smart contract

Inside the `web > contracts` directory, add a new file called `VideoList.sol`. This is where we'll write some [Solidity](https://docs.soliditylang.org/en/v0.8.7/) code to define the rules for this contract. It looks very similar to JavaScript, but it's not the same language. Copy and paste the following code into this new file.

```sol
pragma solidity ^0.5.0;

contract VideoList {
  uint public videoCount = 0;

  struct Video {
    uint id;
    string url;
  }

  mapping(uint => Video) public videos;

  constructor() public {
    createVideo("https://res.cloudinary.com/milecia/video/upload/c_pad,h_360,w_480,q_70,du_10/elephant_herd.mp4");
  }

  function createVideo(string memory _content) public {
    videoCount ++;
    videos[videoCount] = Video(videoCount, _content);
  }
}
```

Let's walk through what happened here. We start by defining the version of Solidity we want to work with. Then we create the definition for the contract. In this example, the contract is called `VideoList`.

In the contract, we have a publically exposed `videoCount` variable that holds how many videos have been added to the blockchain.

Next, there's a `struct` that defines a `Video` record. It has an integer `id` value and a string `url` value. Then we map all of the videos into a public `videos` array.

Using the `constructor`, we add a default video to the blockchain when the smart contract is deployed. Lastly, we have a function that will let us add new videos to the blockchain.

This is the whole smart contract! All that's left to do is create a migration for it and deploy it to the local blockchain.

### Migrations and deploys

Before we actually do the migration and deploy, there's a little config file we need to add. In the `web` directory, add a new file called `truffle-config.js`. Inside the file, paste the following code.

```javascript
module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*" // so it can match any network id
    }
  },
  solc: {
    optimizer: {
      enabled: true,
      runs: 200
    }
  }
}
```

This is how we connect to the local Ethereum blockchain that's running through Ganache. These are the default values in Ganache and you can see them directly in the app.

![Ganache configs](https://res.cloudinary.com/jesse-thisdot/image/upload/v1629510091/e-603fc55d218a650069f5228b/brte8mdkajhzmtcgvzkq.png)

Then we'll need to add a new migration file to the `migrations` folder. Create a new migration file called `2_deploy_contracts.js`. We put the number at the beginning of the migration file so the network knows which order they should be run in.

Open this file and add the following code.

```javascript
const VideoList = artifacts.require("./VideoList.sol");

module.exports = function(deployer) {
  deployer.deploy(VideoList);
};
```

This is how we deploy the `VideoList` smart contract we created.

With this in place, we can run the migration for the smart contract. In the `web` directory, run:

```bash
truffle migrate
```

You should see that the migration has run successfully. Now let's take a look at the smart contract in the Truffle console. We'll need the address of the smart contract for a later part of this article, so let's grab it while we're here.

In the `web` directory, run:

```bash
truffle console
```

This should bring you to an instance of the smart contract we deployed with the migration. Let's run so code in the console to get an instance of the contract with a function.

```bash
videoList = await VideoList.deployed()
```

We have to interact with the blockchain in an async manner, that's why we're using the `await` keyword in the Truffle console. Now let's get the address for this contract because we'll need it in the DApp.

```bash
videoList.address
```

This should return a string similiar to this:

`0x82293fe0BE6cCbA6Eb9bd6d5824fC6ACeB6d3957`

Since we have the smart contract built and deployed, we can turn our attention to the DApp which uses the data from the blockchain.

## Working on the front-end

We'll take advantage of a Redwood command to get started on the front-end. We need a page that lets users interact with the DApp. Leave the `web` directory and go to the project root in the terminal and run the following command.

```bash
yarn rw g page dapp /
```

This will generate the page component, a Storybook component for the page, and a test for the page. It'll also add the route for this new page to the `Routes.tsx` file. Since we added the `/` in the generate command above, it makes this new page the root page for the app.

Back in the `web` directory, take a look inside `src > pages > DappPage`. This has all the files we talked about. Our focus will be on `DappPage.tsx`. Open this file and delete everything out of it.

### Connecting to the Ethereum blockchain with Web3.js

The first thing we need to do is make a new file called `config.tsx` in the `web > src` directory. This is where we'll add some config values that let the front-end connect to the blockchain network we have running with Ganache.

Remember that address we got for our smart contract earlier? It's ok if you don't. All you have to do is open a terminal in the `web` directory and run the following commands to get that address again.

```bash
truffle console
videoList = await VideoList.deployed()
videoList.address
```

In the `config.tsx` file, add the following line with your own smart contract address.

```javascript
export const VIDEO_LIST_ADDRESS = '0x82293fe0BE6cCbA6Eb9bd6d5824fC6ACeB6d3957'
```

Next, you'll have to get an array of values for the ABI of the smart contract. This describes the smart contract behavior and functionality to the front-end. You'll find this in `web > build > contracts > VideoList.json`. In the json file, you'll see a key-value pair named `"abi"`. Copy that whole array and add the following code to `config.tsx` like this.

```javascript
export const VIDEO_LIST_ABI: any = [
  {
    "constant": true,
    "inputs": [],
    "name": "videoCount",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function",
    "signature": "0xc61b5f4c"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "",
        "type": "uint256"
      }
    ],
    "name": "videos",
    "outputs": [
      {
        "name": "id",
        "type": "uint256"
      },
      {
        "name": "url",
        "type": "string"
      }
    ],
    "payable": false,
    "stateMutability": "view",
    "type": "function",
    "signature": "0xe6821bf5"
  },
  {
    "inputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "constructor",
    "signature": "constructor"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "_content",
        "type": "string"
      }
    ],
    "name": "createVideo",
    "outputs": [],
    "payable": false,
    "stateMutability": "nonpayable",
    "type": "function",
    "signature": "0x8e878969"
  }
]
```

With this config file in place, let's import a few things in `DappPage.tsx`.

```javascript
import { useState, useEffect } from 'react'
import Web3 from 'web3'
import { VIDEO_LIST_ABI, VIDEO_LIST_ADDRESS } from '../../config'
```

Now let's build the `DappPage` component. We'll start with just the outline of the component and a few states.

```javascript
export const DappPage = () => {
  const [account, setAccount] = useState<string>('')
  const [videoList, setVideoList] = useState<any>()
  const [videos, setVideos] = useState([])
}
```

Next, we'll call the function that will connect to the Ethereum network. We're using the `useEffect` hook here because we only want this to happen when the page initially loads. So right below the states we defined, add this code.

```javascript
useEffect(() => {
  loadData()
}, [])
```

Let's jump into writing the `loadData` method. This is where we'll actually connect to the network and read data from the blockchain.

```javascript
const loadData = async () => {
  const web3 = new Web3('http://localhost:7545')

  const accounts = await web3.eth?.getAccounts()

  setAccount(accounts[0])

  const videoList = new web3.eth.Contract(VIDEO_LIST_ABI, VIDEO_LIST_ADDRESS)
  setVideoList(videoList)

  const videoCount = await videoList.methods.videoCount().call()

  for (var i = 1; i <= videoCount; i++) {
    const video = await videoList.methods.videos(i).call()
    setVideos([...videos, video])
  }
}
```

This is an async function because we want to wait for the blockchain to handle the request. We make a new instance of `Web3` that connects to the local Ethereum blockchain we have running. Then we get the accounts associated with this blockchain and grab the first one. We'll need this a little later.

Next, we pass in the config values we wrote earlier to interact with the smart contract. We're getting the `videoList` contract and fetching the `videoCount` to see how many videos are available.

Then we loop through all of the videos in the blockchain and save them to the app's state.

Now we can return a view to the page that displays the smart contract id and the videos we've fetched from the blockchain. Right below the `loadData` method, add the following return statement.

```javascript
return (
  <div>
    Dapp account id: {account}
    <ul id="videoList">
      {videos.map((video, key) => {
        return (
          <div key={key}>
            <label>
              <video src={video.url}></video>
            </label>
          </div>
        )
      })}
    </ul>
  </div>
)
```

If you go to the root of the project in a terminal and run `yarn rw dev`, you should see something similar to this in the browser.

![Dapp page](https://res.cloudinary.com/jesse-thisdot/image/upload/v1629510132/e-603fc55d218a650069f5228b/p3ofurjfeeixmqensrcz.png)

You've officially connected an app to an Ethereum blockchain using a smart contract you wrote! All that's left is adding a way for users to add new videos to the blockchain.

## Adding the videos to the blockchain

For the last bit of functionality, we'll use the Cloudinary upload widget to add videos directly to the blockchain.

We need to add the `react-cloudinary-upload-widget` package in a terminal in the `web` directory.

```bash
yarn add react-cloudinary-upload-widget
```

Then we'll add another import line to `DappPage.tsx` below the others.

```javascript
import { WidgetLoader, Widget } from 'react-cloudinary-upload-widget'
```

Now we can write the function to add a video to the blockchain. After the `loadData` function, add this code.

```javascript
const createVideo = (content) => {
  videoList.methods.createVideo(content).send({ from: account, gas: 4712388 })
    .once('receipt', (receipt) => {
      console.log(receipt)
    })
}
```

This is how we use the `createVideo` method we defined in the smart contract. One thing to note is that we need to send a certain amount of gas with this request. The amount of gas you have is tied to the account you're connected with. Each transaction in a smart contract has a gas price, but since we didn't change any default values `4712388` is what the contract expects as a gas payment.

Now we'll define the callback that will give us a response from Cloudinary when the video has successfully uploaded. Right below the `createVideo` function we just wrote, add this.

```javascript
const successCallBack = (results) => {
  const videoInfo = results.info
  const url = videoInfo.url

  createVideo(url)
}
```

This will be called in the `Widget` when we get a success response from Cloudinary. It'll take the URL from Cloudinary and add it to the blockchain. That means it's finally time to add that widget to the return statement. Below the line that displays the account id, add the following code.

```javascript
<WidgetLoader />
<Widget
  sources={['local', 'camera']}
  cloudName={'test_name'}
  uploadPreset={'fwe9ewffw'}
  buttonText={'Open'}
  style={{
    color: 'white',
    border: 'none',
    width: '120px',
    backgroundColor: 'green',
    borderRadius: '4px',
    height: '25px',
  }}
  folder={'test0'}
  onSuccess={successCallBack}
/>
```

This is how we connect to Cloudinary and get URLs for videos we upload. You'll need to log in to your Cloudinary account and get the `cloudName` and `uploadPreset` name from the dashboard. Now you should see a new "Open" button in the browser.

![video upload button](https://res.cloudinary.com/jesse-thisdot/image/upload/v1629510164/e-603fc55d218a650069f5228b/he28v1wwslpygya3uytu.png)

If you click that button, you'll see the upload widget.

![upload widget](https://res.cloudinary.com/jesse-thisdot/image/upload/v1629510177/e-603fc55d218a650069f5228b/fvjvv1yc6ayxgvxkghxc.png)

Go ahead and upload a new video and then reload the page. You should see your new video similar to this.

![second video](https://res.cloudinary.com/jesse-thisdot/image/upload/v1629510196/e-603fc55d218a650069f5228b/y7zpztupp57f2jy3s2yk.png)

You have a fully functioning DApp now! Feel free to add more functionality to the smart contract!

## Finished code

You can check out some of the code in [this Code Sandbox](https://codesandbox.io/s/quizzical-frog-cw0sh) or clone the project from the `user-dapp` folder of [this repo](https://github.com/flippedcoder/blog-examples/tree/main/user-dapp).

## Conclusion

DApps are a cool way to interact with blockchains. Some good next steps would be learning more about Solidity and smart contracts or just making more DApps with other frameworks!