Ethereum DApp With Ethers.js and IPFS Using Angular, Angular Material and NgRx. Part I

This is the first part of our work-in-progress series demonstrating how to build an Escrow Smart Contract DApp with Angular and NgRx.

Alex Yevseyevich
Apr 21 · 8 min read

In the previous article, we began exploring the technique of building an Ethereum smart contract DApp using Angular NgRx.

In this part, we will dive into a more complicated and interesting case of a Solidity Escrow Smart Contract named FleaMarket. The source of the inspiration to build this DApp was taken from the blog published by Jackson Ng.

The entire project is built with the VS Code, and the complete source code is located at the GitHub repository.


Designing the FleaMarket Escrow Smart Contract

On every new product we’re putting up for sale, the FleaMarket escrow smart contract will spawn a new child product contract represented by the SafeRemotePurchase smart contract. We deal with the implementation of the Solidity CRUD pattern with the following requirements:

  • Each product has a unique key;
  • Ensure key uniqueness;
  • Insert a product with a key identifier;
  • Retrieve a product by its key identifier;
  • Remove a product by its key identifier;
  • Obtain a count of the products that exist;
  • Iterating over the products.

Fortunately, a general-purpose library HitchensUnorderedKeySet that tackles this task has been introduced by Rob Hitchens in his great post Solidity CRUD- Epilogue.

After applying this pattern to the FleaMarket smart contract, it will look like this:

A similar implementation of the Escrow Smart Contract has been previously discussed here.

A few important things to take away from this are:

  • It eliminates the need for face-to-face contact between buyer and seller.
  • It eliminates the need for a third-party escrow agent service.
  • No need to pay the listing fee.
  • It motivates both sellers and buyers to care for the transaction in a fair way from start to finish, by requiring both parties to deposit ETH worth 2x the value of the item for sale.

Build the Truffle Smart Contract Project

Create a new project directory FleaMarketDapp and open it in VS Code. Open a terminal window and initialize a new Truffle project:

truffle init

In the ./contracts folder, add the smart contract file FleaMarket.sol.

In the ./migrations folder, create a deployment script file 2_deploy_contracts.js with the following code:

var FleaMarket = artifacts.require("FleaMarket");
module.exports = function(deployer) {
deployer.deploy(FleaMarket);
}

In the Truffle project configuration file truffle-config.js, modify the solidity compile version to match the smart contract pragma directive:

Launch a new terminal window and run the npm init to create a default package.json file.


Configure Ropsten Network Provider

The next step is to edit the truffle-config.js file, to provide all the necessary configuration for deploying our smart contract to Ropsten.

  1. First, we need to authenticate our application with Infura. Login to Infura, and in the Infura Dashboard create a new project:

A new project will be set up with a Project ID, along with the link to the corresponding V3 API endpoint URL. This endpoint URL will be used to send Ethereum requests from our DApp to the Ropsten Ethereum blockchain.

2. Next, let’s install the HD Wallet Provider:

npm install --save truffle-hdwallet-provider

In truffle-config.js,add the Ropsten network definition:

Here, mnemonic is the 12-word secret key for our MetaMask digital wallet, and infuraProjectToken is the Project ID that has been granted by Infura above.

Notice that, if we skip the last argument in the HDWalletProvider constructor, by default, the account in charge of the smart contract deployment will be the first one generated by the mnemonic. If we pass in a specific index, it'll use that address instead (the index is zero-based).


Deploying and Validating the Smart Contract

For the purpose of this application, we would need to create three test accounts on the Ropsten Test Network.

The quick way to validate that we are using the right account on the blockchain is to open the Truffle console:

We can confirm that the account[0] is the same as the first account Master Deployer which we specified in MetaMask. To deploy our smart contract to the Ropsten network we run the following command:

truffle migrate --compile-all --reset --network ropsten

To verify that the smart contract has been deployed on the Ropsten blockchain, we can proceed to the Etherscan and examine our Ethereum Wallet account, which we specified in the HDWalletProvider constructor.

Now is the time to test our smart contract. Before we can run the test, we’ll also need to install two more dependencies from the Chai libraries:

npm install --save-dev chai
npm install --save-dev chai-as-promised

The first library is for using the Assertion with Chai. And the second one is for testing JavaScript promises.

We will split the unit-tests into two test-cases. In the first test-case, we would like to confirm that the FleaMarket smart contract has been deployed successfully and has a valid address and the name.

In the second test-case, we include the methods to validate the different aspects of the smart contract functionality.


Setting Up the Angular Project

Let’s get started by installing the latest major versions of the Angular CLI.

npm install -g @angular/cli

To create a new Angular project, open a new terminal in the root folder of your Truffle project and run the following CLI command:

ng new ClientApp --skip-install=true --minimal=true --style=css --routing=true --skipGit=true

Next, we install the Angular Material, by running the following schematic:

ng add @angular/material

Let’s add a navigation component to the application by running the navigation schematic.

ng generate @angular/material:nav nav

We can also add a dashboard component using the material schematic:

ng generate @angular/material:dashboard dashboard

Next, we install the Flex-layout module, which is a superior layout engine to assist with the CSS flex-box features:

npm install @angular/flex-layout --save

The next thing is to wire up the @NgRx state management library, which lays in the core of our application design.

npm install @ngrx/store, @ngrx/effects, @ngrx/router-store, @ngrx/entity, @ngrx/store-devtools, ngrx-store-freeze --save

We’re using a new NgRx version 8, which comes with the support of many cool additions and futures such as createAction, createReducer and createEffect functions. The benefits of using them have been illustrated very well by Alex Okrushko in his recent article.

Next but not least, to interact with Ethereum blockchain we’re going to use the Ethers.js library developed by RicMoo (Richard Moore).

npm install ethers --save


The Root State and the Guard

The application state in NgRx is a single immutable data tree structure. The state tree will start from the root state once the AppModule is loaded.

The root state is shared globally among all components. It will then continue to grow in size as more lazily-loaded Angular modules are loaded into the application. We define our root state interface as follows:

It is composed of the router state, the spinner state, the error state, and the web3Provider state.

  • The spinner state is responsible for toggling the display of a loading indicator, defined in the loader component. We manage the spinner state by dispatching the following actions to the store:
  • The error state is listening for the action:

and it is handled by the side effect:

On the side note, there was some very interesting discussion recently about how to properly manage loading or error state in the NgRx store. Regarding this topic please check this and this.

  • The web3Provider root state is where the fun begins. It has the following properties,

and is responsible for monitoring the gateway between our DApp and the Ethereum blockchain.

The approach we use closely resembles the idea discussed in the blog written by GrandSchtroumpf. To be able to communicate with the Ethereum network using MetaMask, we need to set up the corresponding ethers.js web3 provider.

First, we define the tree-shakable InjectionToken MetamaskWeb3Provider, and instruct it to inject the Ethereum web3 provider injected by the MetaMask windows.ethereum.

Then, we extend the ethers.js Web3Provider provider and inject the MetamaskWeb3Provider token into its constructor. To get access to the user account on the blockchain, MetaMask requires that we call theenable() method on its native web3 provider. We manage it in the metaMaskEnable$ effect by listening for the '[Web3/Provider] Init]action.

Here is what’s happening behind the scenes. We introduced the EthInitGuard, which we built to control the route resolution process.

The guard is observing the metamaskEnable property of the state. If this value is false, the guard will dispatch the '[Web3/Provider] Init]action, to broadcast the request to connect to the Ethereum blockchain.

The biggest benefit of this approach is that we are now in control of the MetaMask’s popup window for user-approved account access.

Once we compile and run the application, it will load our root state and result in the following home page:

If we try to navigate to any other route guarded by the EthInitGuard, it will trigger an approval popup from MetaMask, asking permission to access the user account.

Thank you! Please stay tuned for other articles in this blog series.


Better Programming

Advice for programmers.

Thanks to Mike Gibbons

Alex Yevseyevich

Written by

Enjoy studying modern web and blockchain smart contract technologies. The current focus of interest: Angular, NgRx, Ethereum DApps

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade