The Right Way To Develop Your Ethereum Dapp is Here: DRIZZLE!

Written by Niharika3297 | Published 2018/12/08
Tech Story Tags: react | blockchain | bitcoin | technology | ethereum

TLDRvia the TL;DR App

Before starting: Please don’t ask me why I put up those heavenly, mouth-watering desserts. I couldn’t help it. Truffle guys chose very yummy names. I am sure these photos will make dev part more bewitching! 😉

This article is inspired by Adrian Li’s contribution to Truffle framework. Adrian’s work can be found here. It is highly recommended that you do give it a read!

So let’s get started..

Source: https://giphy.com/gifs/chris-pratt-jimmy-kimmel-rVbAzUUSUC6dO

What is Drizzle?

Drizzle was added to Truffle Suite in February 2018. It is Truffle’s first front-end development tool. Of course, it is here to make developers life a lot easier. It makes use of React under the hood.

What does Drizzle do?

  • Synchronises your smart contract data with front-end seamlessly
  • Moving transaction data from blockchain to Redux store is easy.

In this tutorial, true power of Drizzle will be demonstrated from scratch. It is probably the best way to gauge potential of a framework with gaining knowledge about how the entire mechanism functions under the hood.

Before Starting

  • Basic knowledge about truffle is required. (Not Drizzle!)
  • Knowledge of JavaScript, ReactJS

Setting up dev environment

  • Node.js v8+ LTS and npm. Npm version 5.2+.
  • Truffle: npm install truffle -g. To check if it is installed properly, type truffle version on your terminal.
  • Ganache-cli: npm install ganache-cli -g. Ganache will be used to spin up a personal, local Ethereum blockchain with dummy accounts for dev purpose.
  • create-react-app: npm install create-react-app -g. Set up a modern React web app with a single command. Thanks to Facebook.

Step 1: Create Truffle Project

Step 1.1: Create your dev folder and move into it.

$ mkdir drizzle-tutorial$ cd drizzle-tutorial

Step 1.2 : Initialise truffle

$ truffle init

Output will look like this:

Downloading...

Unpacking...

Setting up...

Unbox successful. Sweet!

Commands:

Compile: truffle compile

Migrate: truffle migrate

Test contracts: truffle test

This operation creates a basic project structure. Open drizzle-tutorial folder in any text editor.

  • **contracts/**: Directory for Solidity smart contracts
  • **migrations/**: Directory for deployment files
  • **test/**: Directory for test files for testing your application and contracts
  • **truffle.js**: Truffle configuration file (will be deprecated soon)
  • **truffle-config.js**: Truffle configuration file

Step 2: Launch personal blockchain using Ganache

Open the terminal and run following command:

ganache-cli -b 2

-b stands for block time which is 2 seconds. In other words, this personal blockchain mines a new block every 2 seconds. This creates a fake time lag between when you request to commit a transaction and when it is actually committed. By default, Ganache does it instantly.

This process runs on http://127.0.0.1:8545 by default. You can open up this RPC on metamask as well.

Step 3: Writing a minimal smart contract and configuring the contract

Step 3.1: Create a new file named **Addition.sol** in the **contracts/** directory.

pragma solidity ^0.4.24;

contract Addition {int public sum = 0;

function add (int x, int y) public {sum = x + y;}}

Step 3.2: To tell truffle where our personal blockchain (Ganache) is running, we have to do the following

In truffle.js (if you’re on windows truffle-config.js) do the following changes.

module.exports = {networks: {development: {host: "localhost",port: 8545,network_id: "*" // Match any network id}}};

Step 4: Compiling and migrating smart contract

Step 4.1: Compile smart contract

Fire up your terminal and run the command:

truffle compile

Following is the output of the command:

Compiling ./contracts/Addition.sol...

Compiling ./contracts/Migrations.sol...

Writing artifacts to ./build/contracts

Step 4.2: Migrate (deploy) them to our personal blockchain

Create a new file named **2_deploy_contracts.js** in the **migrations/**directory.

const Addition = artifacts.require("Addition");

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

In the terminal, run following command: truffle migrate. Make sure ganache is running. Also, make sure Solidity compiler used is up to date.

Following is the output of the above command:

Running migration: 1_initial_migration.js

Deploying Migrations...

... 0x8c5942b90b785a51ccfe55dcbbc62e0f5469484b72f0ef5ee5fe21e8cef91d3c

Migrations: 0x980b663f1cedfad76349e0c4da1e0c7405b1fd23

Saving successful migration to network...

... 0xe3b1ad2a400d341d894170b8930471ee6d7fcb50314993df3418b4b3a517ba03

Saving artifacts...

Running migration: 2_deploy_contracts.js

Deploying Addition...

... 0x1629b636f1457d91350d5e069ca6a9e88fde72e3438ad9f6abb83c5dfa4675af

Addition: 0x558c220aac56c27a21215bd77551a51e0070d1b6

Saving successful migration to network...

... 0xb3530be9102f4b2d914fd07dcdd51d10d7cbc496aab28b278ec35e4e4b476af3

Saving artifacts...

Step 5: Testing our contract

To validate if everything is working all right, let us Mocha test our contract.

Create a new file named **Addition.js** in the **test/** directory.

const Addition = artifacts.require("./Addition.sol");

contract("Addition", accounts => {it("should display 5", async () => {const addition = await Addition.deployed();await addition.add(3, 2, { from: accounts[0] });const storedSum= await addition.sum.call();

assert.equal(storedSum, 5);});});

Run the test using the command: truffle test. Following is the output:

Awesome! Now we know that the contract actually works.

Step 6: Creating ReactJS project

To bootstrap a minimalistic react project, run the following command: npx create-react-app client. This may take a couple of minutes.

This command will create client.

Output will look like this. (This is not complete output because its hella long).

Creating a new React app in /Users/niharikasingh/Desktop/playground/drizzle-tutorial/client.

Installing packages. This might take a couple of minutes.

Installing react, react-dom, and react-scripts...

yarn add v1.10.0

info No lockfile found.

[1/4] 🔍 Resolving packages...

warning react-scripts > eslint > file-entry-cache > flat-cache > circular-json@0.3.3: CircularJSON is in maintenance only, flatted is its successor.

warning react-scripts > jest > jest-cli > prompts > kleur@2.0.2: Please upgrade to kleur@3 or migrate to 'ansi-colors' if you prefer the old syntax. Visit https://github.com/lukeed/kleur/releases/tag/v3.0.0\\ for migration path(s).

[2/4] 🚚 Fetching packages...

[3/4] 🔗 Linking dependencies...

[4/4] 📃 Building fresh packages...

success Saved lockfile.

success Saved 828 new dependencies.

Now let’s cd client. We have to link our smart contracts with this React project. We can copy the /build/contracts folder in client folder directly or use the following command:

cd srcln -s ../../build/contracts contracts

Step 7: Install Drizzle (YAY!)

Before running the installation command, make sure you’re in the client directory of your project. To do that, run cd .. in the terminal.

To install Drizzle, run the following command which took me about 3 minutes to be precise:

npm install drizzle --save

AND NO OTHER DEPENDENCY! How cool is that? No need to install Web3.js or anything else. Drizzle contains everything we need to work with our smart contracts.

Run the React front-end on port 3000. To do this, run npm start. And point your browser to http://localhost:3000. The following page should come up.

Step 8: Instantiate Drizzle Store

We have to make some changes in **client/src/index.js** first.

import React from "react";import ReactDOM from "react-dom";import "./index.css";import App from "./App";

// import drizzle functions and contract artifactimport { Drizzle, generateStore } from "drizzle";import Addition from "./contracts/Addition.json";

// let drizzle know what contracts we wantconst options = { contracts: [Addition] };

// setup the drizzle store and drizzleconst drizzleStore = generateStore(options);const drizzle = new Drizzle(options, drizzleStore);

// pass in the drizzle instanceReactDOM.render(<App drizzle={drizzle} />, document.getElementById("root"));

Note that the **drizzle** instance is passed into the **App** component as props.

Step 9: Reshaping client/src/App.js

Step 9.1: Adding state variables

We will do is to add the following line inside our App component:

state = { loading: true, drizzleState: null };

We are going to be using two state variables here:

  1. **loading** — Indicates if Drizzle has finished initializing and the app is ready. The initialization process includes instantiating **web3** and our smart contracts, fetching any available Ethereum accounts and listening (or, in cases where subscriptions are not supported: polling) for new blocks.
  2. **drizzleState** — This is where we will store the state of the Drizzle store in our top-level component. If we can keep this state variable up-to-date, then we can simply use simple **props** and **state** to work with Drizzle (i.e. you don't have to use any Redux or advanced React patterns).

Step 9.2: Writing some initialisation logic

componentDidMount() {  const { drizzle } = this.props;  // subscribe to changes in the store  this.unsubscribe = drizzle.store.subscribe(() => {    // every time the store updates, grab the state from drizzle    const drizzleState = drizzle.store.getState();    // check to see if it's ready, if so, update local component state    if (drizzleState.drizzleStatus.initialized) {      this.setState({ loading: false, drizzleState });    }  });}

Step 9.3: Unsubscribing from the store

compomentWillUnmount() {  this.unsubscribe();}

This will safely unsubscribe when the App component un-mounts so we can prevent any memory leaks.

Step 9.4: Modify render() method

render() {  if (this.state.loading) return "Loading Drizzle...";  return <div className="App">Drizzle is ready</div>;}

The final file should look like this:

import React, { Component } from 'react';import logo from './logo.svg';import './App.css';

class App extends Component {state = { loading: true, drizzleState: null };

componentDidMount() {const { drizzle } = this.props;

// subscribe to changes in the storethis.unsubscribe = drizzle.store.subscribe(() => {

// every time the store updates, grab the state from drizzleconst drizzleState = drizzle.store.getState();

// check to see if it's ready, if so, update local component stateif (drizzleState.drizzleStatus.initialized) {this.setState({ loading: false, drizzleState });}});}

compomentWillUnmount() {this.unsubscribe();}

render() {if (this.state.loading) return "Loading Drizzle...";return <div className="App">Drizzle is ready</div>;}}

export default App;

After this, localhost:3000 should look like this:

Step 10: Reading from smart contract using Drizzle

Create a new file at **client/src/ReadSum.js** and paste in the following:

import React from "react";

class ReadSum extends React.Component {componentDidMount() {const { drizzle, drizzleState } = this.props;console.log(drizzle);console.log(drizzleState);}

render() {return <div>ReadSum Component</div>;}}

export default ReadSum;

And then inside **App.js**, import the new component with this statement:

import ReadSum from "./ReadSum";

Change App.js render() method to the following:

render() {  if (this.state.loading) return "Loading Drizzle...";  return (    <div className="App">      <ReadSum        drizzle={this.props.drizzle}        drizzleState={this.state.drizzleState}      />    </div>  );}

Looks like this in action:

Now, let’s actually read the value from our smart contract.

import React from "react";

class ReadSum extends React.Component {state = { dataKey: null };

componentDidMount() {const { drizzle } = this.props;const contract = drizzle.contracts.Addition;

// let drizzle know we want to watch 'sum'var dataKey = contract.methods["sum"].cacheCall();

// save the `dataKey` to local component state for later referencethis.setState({ dataKey });}

render() {// get the contract state from drizzleStateconst { Addition } = this.props.drizzleState.contracts;

// using the saved `dataKey`, get the variable we're interested inconst sum = Addition.sum[this.state.dataKey];

// if it exists, then we display its valuereturn <p>Sum: {sum && sum.value}</p>;}}

export default ReadSum;

Step 11: Writing on the blockchain using Drizzle

If we wish to add two new numbers and store the sum on the blockchain, do the following:

First, let’s create a new file **client/src/SetSum.js** and paste in the following:

import React from "react";

class SetSum extends React.Component {state = { stackId: null};

handleKeyDown1 = e => {// if the enter key is pressed, set the value with the stringif (e.keyCode === 13) {this.setValue(e.target.value1);}};handleKeyDown2 = f => {// if the enter key is pressed, set the value with the stringif (f.keyCode === 13) {this.setValue(f.target.value2);}};

setValue = (value1, value2) => {const { drizzle, drizzleState } = this.props;const contract = drizzle.contracts.Addition;

// let drizzle know we want to call the `add` method with `value1 and value2`const stackId = contract.methods["add"].cacheSend(this.textInput1.value, this.textInput2.value, {from: drizzleState.accounts[0]});

// save the `stackId` for later referencethis.setState({ stackId });};

getTxStatus = () => {// get the transaction states from the drizzle stateconst { transactions, transactionStack } = this.props.drizzleState;

// get the transaction hash using our saved `stackId`const txHash = transactionStack[this.state.stackId];

// if transaction hash does not exist, don't display anythingif (!txHash) return null;

// otherwise, return the transaction statusreturn `Transaction status: ${transactions[txHash].status}`;};

render() {return (<div><input type="number" ref={(input1) => this.textInput1 = input1} onKeyDown={this.handleKeyDown1} /><input type="number" ref={(input2) => this.textInput2 = input2} onKeyDown={this.handleKeyDown2} /><div>{this.getTxStatus()}</div></div>);}}

export default SetSum;

Import and include it inside **App.js**

import SetSum from "./SetSum";

And change the render() method of App.js to:

render() {    if (this.state.loading) return "Loading Drizzle...";    return (      <div className="App">        <ReadSum          drizzle={this.props.drizzle}          drizzleState={this.state.drizzleState}        />        <SetSum          drizzle={this.props.drizzle}          drizzleState={this.state.drizzleState}        />      </div>    );  }

This should work!

Transaction status can be ‘pending’ or ‘successful’.

And this was the end to Drizzle 101.

Source: https://gfycat.com/giantindeliblebinturong


Published by HackerNoon on 2018/12/08