
Supercharging Our Ethereum DApp with Ethers.js and Angular NgRx v8
This is Part V in our work-in-progress project demonstrating how to build an Escrow smart contract DApp with Angular and NgRx
One in the single and the first degree
One above one for eternity
No unit or triad, source or cause for the one, super one, this is the one above those
— “One Above One” by Vitalic
What We’re Going to Explore
In the previous article, we discussed how to create a new instance of the purchase contract. In this post, we will walk through the code that lets us implement the following functionalities of our FleaMarket Escrow smart contract:
- Display the list of all purchase contract widgets
- Implement the search for contract
- Load the purchase contract
- Abort contract by the seller
- Remove contract by the seller
- Confirm purchase by the buyer
- Confirm delivery by the buyer
The source of the inspiration to build this DApp was taken from this article. The full code for this blog is located in my GitHub repository:
1. Load Purchase Widget Collection
The feature-level Routes
array file for the lazy-loaded module P2pBazaarModule
has the following context:
It introduces the route-guardProductsLoadedGuard
that controls the navigation to the top-level route /products.
The guard will wait for the collection of the purchase contract widgets to load from the blockchain by observing the loaded
property of the entity state:
If this value is false, the guard will dispatch the loadProducts()
action to the store effect loadProducts$
:
The effect then handles the call to the service method getPurchaseContractList()
to fetch those pieces of data:
We call the smart contract getContractCount()
function to retrieve the total amount of the purchase contract instances. Then, we use the combination of the mergeMap
and forkJoin
operators to execute multiple requests to the smart contract to retrieve the purchase widget collection.
Once the method getPurchaseContractList()
gets resolved with the list of thePurchaseWidgetModel
entities, we pass it into the payload of the loadProductsSuccess()
action and dispatch it back to the entity store.
The entity state reducer then calls the entity adapter method addAll()
to replace the entity state with a new collection. The reducer will also change the state property loaded
to true and permit access to the /products
route.
Since the /products
route becomes active, it will also activate the route component ViewProductCollection
. Here we implement the search by the contract key functionality and hook up the getAllProducts
selector value into the products$
observable:
As a result, the component template should render the collection of our purchase widgets as follows:

Each item in the widget collection includes the key and the address of the corresponding purchase contract. When a new instance of the purchase contract is created, the new purchase widget model is added to the entity store. This will trigger the getAllProducts
selector to emit a new value and cause the content of the list to get updated.
2. View Purchase Contract Details
When users click an item in the widget list, the app will navigate to the ViewPurchaseContractComponent
and load the selected purchase contract’s details from the blockchain:

Let’s take a look at the code that drives that piece of logic. In ViewPurchaseContractComponent
, we declare two public observable fields:

We use the selectedPurchaseContract$
selector to pull out the purchase contract properties from the Ethereum blockchain and the image$
selector to load the corresponding product image from the IPFS blockchain.
We set selectedPurchaseContract$
in the ngOnInit()
life-cycle hook method as follows:
It is hooked-up into the feature selectorgetSelectedProduct
:

Notice that the getSelectedProduct
is a combined selector that returns a specific member from the product widgets collection based on the product key which we specify in the route parameter. We then dispatch the widget contract address with the loadPurchaseContract
action to the side effect:
In the body of the effect, we use the switchMap
operator to switch to a new inner observable of type PurchaseContractModel
, which is emitted from the service method loadPurchaseContract
. This method loads the purchase contract from the blockchain as follows:
We use the ethers.js
library to retrieve the smart contract observable properties and apply the zip
operator to combine them into a new observable objectPurchaseContractModel
.
We then dispatch the loadPurchaseContractSuccess
action, providing the PurchaseContractModel
object as the payload to update the feature state property selectedPurchaseContrac
.
Finally, we use the switchMap
again to observe the selected PurchaseContractModel
object using the select()
method on the store, providing the getSelectedPurchaseContract
selector function.
Let’s quickly review the code for another component’s observableimage$
that binds the purchase contract image to the template.
Notice that the image isn’t stored in the smart contract. We only store the corresponding IPFS hash value. The hash can be used to retrieve the image context from a different location on a decentralized file system called IPFS.
The image$
selector property is defined in the in ngOnInit()
as follows:
We set it by observing the selected PurchaseContractModel
object using the select()
method and providing the getSelectedPurchaseContract
selector function. Then, the IPFS hash gets extracted and dispatched with download_image
action to the downloadImage$
effect like this:
We use the switchMap()
operator to extract the IPFS hash value and then call thegetFile
method in IpfsDaemonService
to retrieve the image Blob object from the IPFS node.
We are then dispatching the download_image_success
action to the store, providing the image Blob object as the payload.
It will cause the store reducer to update the feature state and trigger the selector()
function to emit a new value. Finally, we use the template reference variable #ipfsImage
to anchor the Blob image to its src
property:
this.imageRef.nativeElement.src = this.windowRef.URL.createObjectURL(this.image);
3. Abort Contract
In our DApp, we allow the seller to cancel a purchase contract. This is only allowed if the contract is in the state Created.
If the seller decides to abort the contract, the amount of ETH staked by the seller will be refunded back to him. Let’s take a look at what happened when the seller clicked on the flash-on icon.
We have created an effect that listens for theabortSelectedPurchaseContract
action.
Here, we use an appropriate method from the PurchaseContractService
to perform the asynchronous call on the smart contract using ethers.js
.
Upon successful execution of the transaction on the blockchain, the effect then broadcasts the abortSelectedPurchaseContractSuccess
action. This action is picked up by another non-dispatching effect:
The sole purpose of this side effect is to force our application to reload the selected purchase contract from the blockchain and update the template. We do it by refreshing the route state by quickly changing the route parameter and then reversing it back. It will trigger the route parameter selector to emit the same selected product key value again.
Finally, we display the SnackBar notification to our users. The item now shows the balance of 0.0 ETH and the status changed to Canceled.

4. Remove Contract
After a contract has been canceled, the seller also has the option to completely remove it from the product collection.
When the user clicks on the trash icon, we‘ll dispatch the removePurchaseContract
action to the store, providing the product key as the payload. Then, it follows the standard NgRx Redux pattern. We have an effect that is listening to this action.
It will invoke the removePurchaseContract()
method on the FleaMarketContractService
service and call the smart contract method removeContractByKey()
using ethers.js
to initiate the transaction on the blockchain.
We’ll wait for the successful transaction execution and dispatch the removePurchaseContractSuccess
action to the store.
Going back to the store, we are using the NgRx entity adapter to remove the corresponding PurchaseWidgetMode
object from the entity state.
We then pass the action to another effect to navigate to the top-level feature route /p2p-bazaar.
Here is how it looks:

5. Buyer Makes Purchase
Let’s play as a buyer and switch to another MetaMask account that uses the buyer’s wallet.
According to the logic of the smart contract, to be able to purchase the item the buyer needs to deposit a double of the amount of ETH the item costs. The buyer then hits the Purchase button.

Here again, we use the powers of the NgRx Store. We have a dedicated side effect that manages the purchase logic.
Upon closing the dialog that confirms our intention to purchase the item, we invoke the service method confirmPurchase()
.
We send the transaction to deposit ethers by calling the buyerConfirmPurchase()
method on the contract using the ethers.js
library. Once we receive the transaction receipt from the blockchain, we return the control-flow logic back to the effect and dispatch the confirmBuySuccess
action to refresh the contract’s information on the page.

If everything goes as planned, we should see two important changes.
The first change is the contract now shows a balance of ETH equals 4x the value of the product price. It is the 2x from the seller and 2x from the buyer.
The second one is the status has been changed to Locked. At this point, the smart contract no longer allows the buyer nor the seller to pull back from the deal.
6. Confirming Delivery
This is the final step in our application.
After a buyer has staked the right amount of ETH into the contract, the seller is obligated to deliver the purchased item. Upon receiving the item, the buyer confirms the delivery by clicking on the Confirm Delivery button.

This is what happens when the buyer clicks the confirmation button. First, we dispatch the confirmDelivery
action to the store. Then, we use the confirmDelivery$
effect.
From the side effect, we handle the call to the service method.
Here we execute the buyerConfirmReceived()
method on the smart contract and wait for the contract to emit the transaction receipt. Finally, we dispatch the confirmDeliverySuccess
action to refresh the contract’s properties on the component template.
As we would expect, the contract item now shows the status Inactive and does not hold any balance ETH. According to the business logic built into the smart contract, all collateral funds staked in the contract have to be refunded back to the seller and buyer.
Conclusion
🐯 Thanks for reading this article. I really enjoyed my experience in building this DApp.
References
- ethers.js — Version 4.0 Release, by RicMoo
- Smart Contract Explained by Demonstration, by Jackson Ng
- Creating Purchase Contract with Ethers.js Using Angular NgRx v8, by Alex Yevseyevich
- Managing IPFS Image Uploads With Angular NgRx v8, by Alex Yevseyevich
- Angular NgRx Material Starter, by Tomas Trajan
- DApp development with ethers.js, by Sameep Singhania
- Angular + NgRx: Refactor Module, by Brian Love
- How Our Escrow Smart Contract Works, by LocalEthereum’s blog
- Ethereum Marketplace Step-by-Step Tutorial, by Dapp University
Icons made by prettycons from www.flaticon.com is licensed by CC 3.0 BY