React App Setup

This tutorial shows how you can build a basic React app with Create React App that uses the squid-js JavaScript package to publish a data set, get a data set, and more.

Git repository and CodeSandbox

All code snippets in this tutorial are sourced from the oceanprotocol/react-tutorial GitHub repository:

react-tutorial

🦄🦑 React + web3.js + squid.js interacting in the most minimal way with Ocean Protocol

The final source of this tutorial is also available as a CodeSandbox:

Edit react-tutorial

Requirements

  • Node.js >= 10 is installed. You can check using node -v
  • npm >= 5.2 is installed. You can check using npm -v
  • Docker & Docker Compose
  • A Web3 capable browser, like Firefox/Chrome with MetaMask installed, connected to Nile network
  • Some Nile ETH from the Nile Faucet. You can either go to commons.nile.dev-ocean.com/faucet, or execute this command replacing <YOUR ADDRESS> with your MetaMask account address:

    curl --data '{"address": "<YOUR ADDRESS>", "agent": "curl"}' -H "Content-Type: application/json" -X POST https://faucet.nile.dev-ocean.com/faucet

New Create React App

We are going to use Create React App to bootstrap our React app. You could use npx create-react-app marketplace but it creates more files than needed for the scope of this tutorial.

So let’s go minimal and build up our app from scratch with this structure:

marketplace/
├── package.json
├── public/
├──── index.html
├── src/
├──── index.js

First, create a new project folder for your new app, e.g. marketplace. Within that, add a new file package.json with the following content:

/**
 * https://github.com/oceanprotocol/react-tutorial/blob/2765a7e6ae9a948d311d3949636cf832d2664900/package.json
 */
{
  "name": "react-tutorial",
  "version": "1.0.0",
  "description": "React + squid.js interacting in the most minimal way with Ocean Protocol.",
  "dependencies": {
    "@oceanprotocol/keeper-contracts": "0.10.3",
    "@oceanprotocol/squid": "0.6.6",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "^3.0.1",
    "web3": "^1.2.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "browserslist": [
    "defaults"
  ]
}

Notice the @oceanprotocol/squid dependency, which is the Ocean Protocol JavaScript library. Save that file, and in your terminal install the dependencies we have just defined in package.json:

npm install

Then create the HTML file used to render the React app into. For that, create a folder public/ and in it a file index.html with the following content:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <title>My Little Ocean</title>
  </head>

  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
  </body>
</html>

Add Basic Markup

Create a new folder src/ and within that a index.js file with the following content as our base, where we already import squid-js and web3.js:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Ocean } from '@oceanprotocol/squid'
import Web3 from 'web3'

class App extends Component {
  render() {
    return (
      <div
        style={{ fontFamily: '"Fira Code", monospace', textAlign: 'center' }}
      >
        <h1>
          <span role="img" aria-label="squid">
            🦑
          </span>
          <br /> My Little Ocean
        </h1>

      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

At this point you can start up the app and see the result in your browser:

npm start

Go to localhost:3000 to inspect your newly created app:

Initial React App
Initial React App

Setup Web3

We already are importing web3.js but we still need to enable account access for the browsers supporting it, and make sure nothing breaks in browsers which are not Web3-capable.

To do that we add a simple check at top of src/index.js and then enable account access with:

let web3

if (window.web3) {
  web3 = new Web3(window.web3.currentProvider)
  window.ethereum.enable()
}

And let’s also output some warning for non-Web3 browsers within our render() function:

        {!web3 && <p>No Web3 Browser!</p>}

This should give you the following markup:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Ocean } from '@oceanprotocol/squid'
import Web3 from 'web3'

let web3

if (window.web3) {
  web3 = new Web3(window.web3.currentProvider)
  window.ethereum.enable()
}

class App extends Component {
  render() {
    return (
      <div
        style={{ fontFamily: '"Fira Code", monospace', textAlign: 'center' }}
      >
        <h1>
          <span role="img" aria-label="squid">
            🦑
          </span>
          <br /> My Little Ocean
        </h1>

        {!web3 && <p>No Web3 Browser!</p>}
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

After those steps go to your browser. You should see MetaMask asking you to allow access to your account:

MetaMask confirmation
MetaMask confirmation

Note: If you see an error like inpage.js:1 MetaMask - RPC Error: Internal JSON-RPC error. in your browser console, don’t worry about it. It’s a MetaMask thing and won’t affect functionality.

Create Ocean Instance

Now that we are successfully connected with Web3, we can set up our Ocean instance.

At the beginning of your component, create a new Ocean instance with all required endpoint configurations within the componentDidMount lifecycle method. All Ocean Protocol operations can be executed from this Ocean instance.

  state = {
    ocean: undefined,
  }

  async componentDidMount() {
    const ocean = await new Ocean.getInstance({
      web3Provider: web3,
      nodeUri: 'https://nile.dev-ocean.com',
      aquariusUri: 'https://aquarius.marketplace.dev-ocean.com',
      brizoUri: 'https://brizo.marketplace.dev-ocean.com',
      brizoAddress: '0x4aaab179035dc57b35e2ce066919048686f82972',
      secretStoreUri: 'https://secret-store.nile.dev-ocean.com',
      verbose: true
    })
    this.setState({ ocean })
    console.log('Finished loading contracts.')
  }

This will initiate a connection to all Ocean components in Nile, load the contracts, and finally store the Ocean object in the local component state for reuse.

We also set the verbose option of squid-js so we better see what’s going on.

Final Result

That’s it, if you have no errors in your console.log then you have successfully initialized an Ocean instance in your brand new React app and you are ready for the next part of this tutorial.

Initial React App with Ocean initiated
Initial React App with Ocean initiated

Here is the full source of src/index.js that you should have if you followed this tutorial:

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { Ocean } from '@oceanprotocol/squid'
import Web3 from 'web3'

let web3

if (window.web3) {
  web3 = new Web3(window.web3.currentProvider)
  window.ethereum.enable()
}

class App extends Component {
  state = {
    ocean: undefined,
  }

  async componentDidMount() {
    const ocean = await new Ocean.getInstance({
      web3Provider: web3,
      nodeUri: 'https://nile.dev-ocean.com',
      aquariusUri: 'https://aquarius.marketplace.dev-ocean.com',
      brizoUri: 'https://brizo.marketplace.dev-ocean.com',
      brizoAddress: '0x4aaab179035dc57b35e2ce066919048686f82972',
      secretStoreUri: 'https://secret-store.nile.dev-ocean.com',
      verbose: true
    })
    this.setState({ ocean })
    console.log('Finished loading contracts.')
  }

  render() {
    return (
      <div
        style={{ fontFamily: '"Fira Code", monospace', textAlign: 'center' }}
      >
        <h1>
          <span role="img" aria-label="squid">
            🦑
          </span>
          <br /> My Little Ocean
        </h1>

        {!web3 && <p>No Web3 Browser!</p>}
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

Move on to Publish a Data Set.

Bonus: Connect against local Spree network

Spree, a local Ocean test network, can be used instead of remotely connecting to Nile. For this you first have to get up the Spree network by using oceanprotocol/barge.

Run Spree with Barge

Clone the barge repository and use its startup script:

git clone https://github.com/oceanprotocol/barge.git
cd barge/

./start_ocean.sh --no-commons

Note that compiling and deploying the contracts in your local Docker network takes some time so it can take a few minutes until the network is ready to be interacted with. That usually is the case once keeper-contracts_1 container doesn’t show any messages anymore.

Copy Contract Artifacts

At the end of the contract compiling and deploying you need to copy the resulting Spree contract artifacts from the Docker container to your local @oceanprotocol/keeper-contracts dependency folder. The keeper-contracts Docker container will output all artifacts in a hidden folder in your home folder so you can copy from there:

cp ~/.ocean/keeper-contracts/artifacts/* ./node_modules/@oceanprotocol/keeper-contracts/artifacts/

Get Spree Ether

You will also need some Spree Ether in your MetaMask account. You can execute this, replacing <YOUR ADDRESS> with your MetaMask account address:

curl --data '{"address": "<YOUR ADDRESS>", "agent": "curl"}' -H "Content-Type: application/json" -X POST http://localhost:3001/faucet

Adjust App Config

Finally, move back to your marketplace React app and modify the Ocean instance config in src/index.js to use the Spree endpoints:

const ocean = await new Ocean.getInstance({
  web3Provider: web3,
  nodeUri: 'http://localhost:8545',
  aquariusUri: 'http://aquarius:5000',
  brizoUri: 'http://localhost:8030',
  brizoAddress: '0x00bd138abd70e2f00903268f3db08f2d25677c9e',
  secretStoreUri: 'http://localhost:12001',
  verbose: true
})

If you are on macOS, you need to additionally tweak your /etc/hosts file so Brizo can connect to Aquarius within Docker. This is only required on macOS and is a known limitation of Docker for Mac:

sudo vi /etc/hosts

# add this line, and save
127.0.0.1    aquarius

And then use aquariusUri: 'http://aquarius:5000' in your Ocean instance config.

Then start up the app as usual:

npm start

Move on to Publish a Data Set.