We will learn how to build smart contracts by making an auction on which users can place bids, and deploy the smart contracts on Avalanche. We will then be able to interact with them using ReactJS and Drizzle.
We are going to generate ReactJS boilerplate code using
create-react-app, which we will modify for our auction bidding frontend. React is useful for this task due to its efficiency and user-friendly blockchain interaction. For the smart contracts which provide the on-chain functionality, Solidity code will be deployed to the Avalanche blockchain using Truffle.
- Basic knowledge of ReactJS.
- Basic knowledge of Avalanche's architecture and smart contracts.
- Basic familiarity with the React context API.
- You will also need an account on DataHub.
- NodeJS >= 10.16 and npm >= 5.6 installed.
- Truffle, which can be installed globally with
npm install -g truffle.
- The Metamask wallet software must be added to your browser, which should only be obtained from the official Metamask website: https://metamask.io. Do not download Metamask from an unofficial source!
Before starting work on the project, we need to set up a working directory. Follow the steps below to create a directory for the project. Open up a terminal and navigate to the directory where you would like to store this application - typically the user home directory.
Create a new subdirectory with the command
mkdir <directory_name>. Change the current working directory to this newly created directory using
cd <directory_name>. For instance: if we name it
We can create a new React app using
npx(npm's package runner). The typical use is to download the files needed to run an npm package. Using
npxto execute the package binaries for
create-react-appwill generate a new React app scaffold in the specified directory.
Now change to the directory "client" using
cd client, then install the required dependencies using the command:
Open the file
srcdirectory and replace the existing code with the following:
Next, open the
App.jsfile present inside the
srcdirectory and replace the existing code with the following:
When this is done, the React project setup is complete. We are now ready to set up Truffle.
Truffle can be used to create the boilerplate code needed for the project. Run the following command in the project root directory:
Now the basis of the project is set up. Solidity code will be stored in the
migrationsfolder. By default, the
/build/contractsfolder contains information about the compiled and deployed contract (like the Application Binary Interface) in JSON format. These meta-files are commonly referred to as
truffle-config.jsis the configuration file created by the
truffle initcommand. This file contains the information telling Truffle how to deploy contracts, which network to deploy on, and more. We should keep the default file for reference and we can create a copy of this file using the command:
Now that we have a copy of the default configuration, we can update
truffle-config.jswith the information needed to deploy the smart contract on the Fuji testnet. This will help us in connecting to the DataHub Avalanche node, however we will require an Avalanche API key (from DataHub) along with an Avalanche wallet mnemonic used for deploying the contract on the network. Replace the existing contents of
truffle-config.jswith the following:
Here we are setting the
gaspriceto appropriate values for the Avalanche C-Chain. You might notice that
contract_build_directoryis being used to change the default location of
artifactsfrom the project root directory to the
srcfolder. This is because React is unable to access the files present outside the
For the deployment of the smart contracts we need to take care of two things:
- A node connected to the Avalanche network and an account with some AVAX.
- An Avalanche API key is required to access the DataHub Avalanche node through RPC (Remote Procedure Call). Visit the Avalanche Services Dashboard on DataHub to get an Avalanche specific API key.
Next, we need to create a new Avalanche wallet to make transactions on the network and to deposit our funds. To create an Avalanche wallet, go to https://wallet.avax.network and save all the necessary information -- the wallet address and mnemonic seed phrase.
Create a new file named
.envin the project root folder. Copy your Avalanche API key from DataHub and the Avalanche wallet's mnemonic into the
.envfile as shown below. If you have any difficulty in setting up the
.envfile then please refer to the Figment Learn guide on dotenv and .env.
Now the project setup is completed! Run the command
npm startin the project root folder to start the development server. This will allow us to build the React interface with immediate visual feedback.
Next we will need to create our main Solidity smart contract file named
contractssubdirectory and add the following:
Auctionis a Solidity contract which enables us to view the auction details and correspondingly its minimum price. We will be accessing the deployed Auction contracts using their deployed address and ABI. Each time a new auction is created, the Solidity code will be deployed to the blockchain.
This code declares public variables for storing user information, their bids, auctions and auction analytics using Solidity mappings. Have a look at the struct definitions used in these variables: They contain the data types and fields which make up a User, Bid or AuctionAnalytics.
This function takes the public address as its argument and returns an integer array with 2 elements - isRegistered at index 0 and userId at index 1. If 0th index is 1 then the user exists and vice-versa. The 1st index represents the userId of the user. This function iterates over the mapping users to check if the required public address exists.
We have created a mapping for storing analytics like the latest bid, highest bid, and lowest bid for each auction. This mapping will map auctionId to AuctionAnalytic struct. When a new auction is created, we initialize its corresponding entry in the AuctionAnalytics map.
The auction analytics need to be updated every time there is a new bid. This function is called whenever a bid is created. It takes an auctionId and the latest bid amount as its two arguments, and updates the analytics corresponding to the auction.
The remaining functions are self explanatory, but have been well commented for readers to understand.
Create the file
Migration.solinside of the
contractsdirectory and add the following:
Migration.solsmart contract manages the deployment of other contracts that we want to migrate to Avalanche.
Next, create a new file in the
2_deploy_contracts.js, and add the following block of code. This handles deploying the
Auctionsmart contract to the blockchain.
If we have altered the code within our Solidity source files or made new ones (like
Auction.sol), we need to run
truffle compilein the terminal, from inside the project root directory.
The expected output would look similar:
The compiled smart contracts are written as JSON files in the
/src/build/contractsdirectory. These are the stored ABI and other necessary metadata - the artifacts.
During the deployment of the smart contract to the C-Chain, there is a required deployment cost. This can be seen inside
truffle-config.js. The HDWallet Provider will help us in deploying on Fuji C-Chain and the deployment cost will be supplied by the account whose mnemonic has been stored in the
.envfile. Therefore, we need to fund the account before we are able to deploy.
We need funds in our C-Chain address, as smart contracts on Avalanche are deployed on the C-Chain (Contract-Chain). This address can easily be found on the Avalanche Wallet dashboard. Avalanche network has 3 chains: X-Chain, P-Chain, and C-Chain. The address of all these chains can be found by switching tabs at the bottom of the division, where there is a QR code. So, switch to C-Chain and copy the address. Now fund your account using the Fuji testnet faucet by pasting your C-Chain address in the input field. Refer to the image below, to identify the address section.
Everything is in place to run the Truffle migrations and now we can easily deploy the
Auctioncontract with the command:
For rapid development and testing, we can also deploy our contracts on a local network by using Ganache (Truffle's local blockchain simulation) with the command:
On successful execution of either of the above commands, we should expect to see similar output:
If you have not created an account on the C-Chain, you'll see this error:
If you have not funded the account, you'll see this error:
The information and ABI of the deployed contract are present in the
Our blockchain code, which will act as the backend for this application, is deployed on the blockchain and now we can make the client-side for interacting with the contracts. We will be using Trufflesuite's Drizzle library for connecting our web app with the blockchain. Drizzle makes the integration process very easy and scalable. It also provides a mechanism to cache a particular contract-call, so that we can get real-time updates of any changes to the data on the blockchain.
We will be using React's context API to facilitate this integration. The context API makes the use of variables which are declared in the parent component very easy to access in the child components.
It is based upon the Provider and Consumer concept. The Provider component contains necessary logic and variables that need to be passed along. Then this Provider component is wrapped around the components which want to access its variables. Every child component can access these variables. But in order to access them, we use a Consumer API. This API will return the variables from the Provider component (only when called from its child components). Look at the code below to understand what is happening.
drizzleContext.jsfile, DrizzleProvider is the provider component and useDrizzleContext is the consumer function. Look at the return statements of these functions. One is returning the Context Provider (provider), and the other is returning the values of the Context itself (consumer).
Create a file
drizzle-auction/client/src/directory and paste the following code:
drizzleOptionsconstant contains the configuration like contracts we want to deploy, our custom web3 provider, smart contract events, etc. Here we are just instantiating the
Inside the file
srcdirectory, add the following code:
@drizzle/storemodule will help in instantiating drizzle according to our drizzleOptions. The following line is responsible for this action:
Then we wrap the
Appcomponent inside the
DrizzleProviderso that we can use extracted variables (see drizzleContext.js) inside
App. We pass the
drizzleobject to the provider component because it will be required to extract other information from it.
Create a file
drizzle-auction/client/src/directory with the following code:
drizzleas its argument and extracts other information like whether drizzle contracts are initialized or not, web3 info, account info, deployed contract's instance, etc. We need to subscribe to the drizzle's store for this information because the data is not fetched at once, and since we do not know when will we get the data, we subscribe to the store (where the data resides). Once the drizzle is initialized with our contract data, we unsubscribe from the store, so that it will not re-render infinitely!
This variable holds the state of the store. Cached calls are those contract calls for which we want real-time data from the blockchain. Whenever there is some change in our data on the blockchain, it gets notified in the drizzle state variable of the store.
drizzle.contractsis an object containing instances of all the deployed contracts which are added to drizzle (in drizzleOptions or manually). We are simply storing all the methods of this contract instance so that whenever we want to call functions or public identifiers from this contract, we can simply use
drizzle-auction/client/src/directory and add the following code:
drizzleVariables.initializedwould ensure that,
Loading...state is visible until Drizzle is ready for interaction.
Create a file
drizzle-auction/client/src/directory and add the following code. This component deals with the entry-point of our application, where all the data like
AuctionDetailsetc. get generated.
In order to keep our data fresh from the blockchain, Drizzle uses the caching mechanism. On our behalf, Drizzle keeps track of every change on the blockchain. If there is any transaction involving our smart contracts, then it will notify our dApp.
We need to define the calls which we want to monitor. Caching a particular method will provide cache keys (hash) to us. Each cached method is associated with a particular unique hash. Using this key, we can get live data from the blockchain. The component will re-render anytime there is some new value associated with this call.
For example, in the above code, we used the following cache keys
Suppose we want to cache the
isRegisteredmethod. This can be done using:
Once a method is cached, the Drizzle
storecreates a key-value pair representing hash-key and the real-time data associated with this call. In the above program, this data is accessed using the
In this component, we made a simple object of cached call variables named
cacheCall, which implements the above code snippet. The cached version of
isRegisteredcan be accessed as
Create a file
drizzle-auction/client/src/directory, adding the following code. This component deals with the management of the auction like creating a new bid, displaying the real-time auction analytics, etc. All the data is passed by its parent component i.e.
Auction.jswhich manages the cache keys and calls.
Create a file
drizzle-auction/client/src/directory and add the following code. This component deals with creation of new Auctions, by submitting transactions on the network.
Now go to the project root directory, i.e.
avalanche-voting, and run the command
npm start. The React development server will start automatically. Visit http://localhost:3000 in a web browser to interact with the dApp frontend.
Don't forget to set up Metamask with the Fuji testnet and also fund the account with Fuji C-Chain test tokens to be able to vote. In the Metamask extension, add a custom RPC endpoint by clicking the network dropdown in the centre of the extension. Fill in the details as shown in the table below.
|Network Name||Avalanche Fuji|
|New RPC URL||https://avalanche--fuji--rpc.datahub.figment.io/apikey/<YOUR_DATAHUB_API_KEY>/ext/bc/C/rpc|
|Block Explorer URL|
We have successfully built a dApp through which we can organize auctions, bid in them and declare results, with both frontend and smart contracts. We have used the Drizzle library from Trufflesuite for integrating our frontend with the blockchain and to keep our data updated in real-time.
Our dApp currently has very minimalistic designs. We can use Consensys' Rimble UI library for adding modals for each transaction, add links to drip Avalanche's test tokens etc. which can help users to navigate through our dApp.