Creating Purchase Contract with Ethers.js Using Angular NgRx v8
Part four in our series demonstrating how we are building the NgRx powered DApp for the FleaMarket Smart Contract
In the previous piece, we talked about how to employ the IPFS to store the seller’s product images. In this piece, we will focus on creating the
SafeRemotePurchase contract instances by utilizing the NgRx entity state adapter.
The source code for this module is located in this GitHub.
Refactoring the Solidity FleaMarket Smart Contract
On every new product we’re putting up for sale, the
FleaMarket 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;
- Obtain a count of the products that exist; and
- Iterating over the products.
After applying this pattern to the FleaMarket smart contract, the portion responsible for managing the instances of
SafeRemotePurchase will look like this:
NgRx: Having Fun with Material Snackbar
When we are dealing with DApps, it often involves some time for a transaction to return from the state-changing operations on the Ethereum blockchain. From the user experience point of view, we should give some visible indication of the result of these operations. In Part I, we already implemented the loading and error states, a part of the NgRx global store. In this section, we are going to look at how to add the snackbar notification state also.
We start with defining a custom snackbar component that could receive the notification data.
The snackbar state is managed by dispatching the following action to the store:
This action is being handled by Effect:
The immediate advantage of managing the snackbar state globally is that we can trigger the snackbar notification both from a component or directly from a side effect. For example, in case of the error effect:
The result may look like this:
Seeding the Feature Module
We are planning to use the NgRx entity state library @ngrx/entity to manage a collection of the purchase contract entities. Let’s bundle all related functionality in a separate lazy-loaded feature module called
Building the Smart Contract Services
The first service is responsible for communication with the
FleaMarket smart contract. Start by defining the injectable contract token:
Here we store the address of the deployed
FleaMarket contract in the
environment object. We are also taking advantage of the Human-Readable ABI of the
ethers.js library. We only have to specify functions and events signatures we are going to use. By having the contract address and the ABI definition, that’s all we need to identify our smart contract on the
Now that we have the injectable contract token, we can define the
The service method
createPurchaseContract is responsible for creating
SafeRemotePurchase smart contract entities defined in the
FleaMarket.sol smart contract. One of the features of the
ethers.js is that the transaction we send to a node becomes available to us even before it is mined. While waiting for a transaction to be mined, we may choose to implement some other side effects, such as to store some transaction details in an external database. Once a new contract has been created and mined, we should receive the transaction receipt
txReceipt, that contains, among other things, the last emitted event,
logNewPurchaseContract(address contractAddress). We can use this event object to retrieve the contract address value passed to it as an argument:
Our next service will provide the functionality to communicate with the
SafeRemotePurchase child contracts.
Currently, it has only one method that takes the contract address, connects to the contract, reads its properties, and flattens them into the purchase contract model.
Note that instead of the module
P2pBazaarModule, in both contract token and contract services, we specify the
providedIn dependency injection to a lazy dummy module called
P2pBazaarAnchorModule. This is to avoid a circular dependencies warning.
Define the Entity State
The purchase contract model has the following type definition:
We manage the collection of the purchase contract entities with the following entity state adapter:
It also specifies the entity unique identifier and the default entity sort order. We then hook up the entity state into the purchase contract feature state:
Creating a New Purchase Contract in Actions
To wire up the new purchase contract logic, we need to enter the contract details into the angular material reactive form:
We entered the amount of ETH worth two times the value because we want the purchase price to be 0.03 ETH. We also selected the product image and added it to the IPFS file system and received the corresponding hash code.
Once we satisfy the reactive form validation requirements, the button labeled “Create Contract” becomes active, and we can trigger the store action that carries the reactive form fields values in its payload.
The action is dispatched to the store effect
We use the
exhaustMapoperator to retrieve the action payload data object. The advantage of using this mapping operator is that it will ignore sequential requests while the current one is still ongoing. We then pass the payload as a parameter into the
createPurchaseContract service method. Because we are trying to alter the state of the
FleaMarket smart contract, it will create a transaction on the Ethereum blockchain and we have to pay the gas fee for it in Ethers. At that moment the MetaMask will pop up, asking to confirm the transaction:
Once we approve the transaction, we are waiting for a successful purchase contract execution. The method
createPurchaseContract gets resolved with a new purchase contract address. We pipe this value to the
switchMap operator and dispatch the
createPurchaseContractSuccess action to the store.
We also dispatch the
getBalance action to update the current account balance displayed on the toolbar.
The action that carries the new purchase contract address continues the journey through the feature store and gets picked up by the two store effects. First one is the
showSnackbar$ pops up the success notification message to the user.
The second one is the
This effect strips the new purchase contract address from the action payload and passes it to the service method
loadPurchaseContract as a parameter. The method then reads the purchase contract properties from the blockchain and wraps them up in the corresponding
PurchaseContractModel. We dispatch the
addProduct action to add this new entry to the entity state collection of the
We can confirm that the new purchase contract has been added to the store state collection by opening the Redux DevTools Chrome extension.
The new purchase contract can be viewed also if we open the Remix IDE, connect to the Robsten network, and enter the contract address in “Add Address” field:
- Smart Contract Escrow DApp — Seller View, by Jackson Ng
- ethers.js — Version 4.0 Release, by RicMoo
- Beta Release: ethers.js v5, by RicMoo
- Announcing NgRx Version 8: @ngrx/data, creator functions, run-time checks, and isolated tests, by Tim Deschryver
- Managing IPFS Image Uploads With Angular NgRx v8, by Alex Yevseyevich
- Building a Smart Contract to Sell Goods, by Fábio José
- OpenBazaar p2p online store.
- Ethers and Angular, by GrandSchtroumpf
- Solidity CRUD- Epilogue, by Rob Hitchens
- NgRx: How and where to handle loading and error states of AJAX calls?, by Alex Okrushko
- Build an Ethereum DApp Using Ethers.js, by BlockChannel
- Total Guide To Angular 6+ Dependency Injection — providedIn vs providers:[ ], by Tomas Trajan
Icons made by Eucalyp from www.flaticon.com is licensed by CC 3.0 BY
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
Icons made by photo3idea_studio from www.flaticon.com is licensed by CC 3.0 BY
Special thanks to my son Daniel Yevseyevich for reviewing this article.