Introduction
The first e-commerce platform was created in 1982 by the Boston Computer Exchange (BCE). It wasn't until 13 years later that now-household names like eBay and Amazon came into the picture. When the idea of e-commerce was first introduced, it was met with a lot of skepticism. I understand—I’d be skeptical too if I were asked to trust an invisible company with not only my money but to deliver perfectly fitting shoes. The success we see in e-commerce today is largely due to the tireless efforts of companies that prioritized building trust among their users.
As the web continues to evolve, e-commerce solutions have also had to evolve with it. In a few years, we might see more companies move from using Web 2.0 to adopting Web 3.0 or even Web 5.0. At its core, Web 3.0's selling point is the ability to create an era of trustless transactions where smart contracts are the guarantors of trust. This begs the question, well actually two questions:
Can transactions in a Web 3.0 e-commerce platform truly remain decentralized?
Could a successful Web 3.0 e-commerce store drive higher adoption of Web 3.0 technologies and enhance user trust?
Let’s unpack these questions.
Can transactions in a Web 3.0 e-commerce platform truly remain decentralized?
In theory, a Web 3.0 e-commerce platform could maintain a certain degree of decentralization by automating many aspects of the transactions.
A decentralized storage solution can be used for product information and data. The best option to ensure the safety of user data is through the use of Web 5.0. To learn more about Web 5.0, you can read this article.
Basically, it can be challenging to build a completely decentralized e-commerce platform, but it is not impossible. Some level of centralization might be necessary for reasons like dispute resolution, product quality control, and shipping logistics.
Could a successful Web 3.0 e-commerce store drive higher adoption of Web 3.0 technologies and enhance user trust?
I have no doubt that a successful Web 3.0 e-commerce store will contribute to wider adoption. Here's how:
- An e-commerce platform will provide a everyday use case for cryptocurrency technologies.
- If a Web 3.0 platform can provide seamless transactions, it would help address skepticism surrounding Web 3.0 and make users more comfortable with making payments through it.
- The inherent transparency of blockchain allows users to verify their transactions, so dispute resolution will be much easier.
Knowing all these, it is clear that building more fast and seamless Web 3.0 ecommerce platforms is just what we need for a wider adoption of blockchain technologies.
In this article, you will be provided with the practical knowledge to start building your own Web 3.0 e-commerce application using Solidity, Hardhat, and React.js. Let's get started.
Prerequisites
Before jumping into this tutorial, make sure you have the following:
- A basic understanding of Solidity and JavaScript.
- Node.js and npm installed on your system.
- MetaMask installed in your browser.
- A code editor or IDE like VSCode
Setting Up the Development Environment
In your terminal or file directory create a folder called Ethcommerce, and open it in your code editor.
mkdir Ethcommerce
cd Ethcommerce
code .
Setting up a React project
In your project folder, enter the following commands to install React globally and create a new React project inside the root directory:
npm install --global create-react-app
npx create-react-app@latest ./
Setting up Hardhat
Hardhat is an environment that developers use to test, compile, deploy, and debug dApps. Hardhat provides a local Ethereum network designed for development. You can easily deploy your contracts, run tests, and debug Solidity code without dealing with live environments.
In the root of your project, install Hardhat and related dev dependencies:
npm install --save-dev hardhat @nomicfoundation/hardhat-chai-matchers
Then, initialize a new hardhat project. The command below will create starter files for your project:
npx hardhat init
After you run the command above, a menu option list will appear to configure your preferences. Since you already have a .gitignore file, choose “no”, when prompted to create one. For the other options, accept the default settings.
Smart contract development
In the root of your project, navigate to the contracts directory and create a solidity file named Ethcommerce.sol
. Then, add the following code to create a new smart contract.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
contract Ethcommerce {}
Inside your contract, declare an address variable to represent the owner of the project. In the constructor, assign the value of msg.sender
to this owner variable.
address public owner;
constructor() {
owner = msg.sender;
}
Create a struct called Item
in your contract. This struct will define all the properties for each individual store product.
struct Item {
uint256 id;
string name;
string category;
string image;
uint256 cost;
uint256 rating;
uint256 stock;
}
Then create a modifier called onlyOwner
. This modifier will ensure that only the owner of the smart contract can list an item in the store.
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
Listing Products
In a traditional e-commerce application, the seller or admin has permission to list products in the store. The list
function in this application serves precisely that purpose.
Before you create the list
function, you need to create a mapping and an event. The items
mapping will store the products in the smart contract. The List
event is emitted once a product has been successfully listed.
mapping(uint256 => Item) public items;
event List(string name, uint256 cost, uint256 quantity);
function list(
uint256 _id,
string memory _name,
string memory _category,
string memory _image,
uint256 _cost,
uint256 _rating,
uint256 _stock
) public onlyOwner {
// Create Item
Item memory item = Item(
_id,
_name,
_category,
_image,
_cost,
_rating,
_stock
);
// Add Item to mapping
items[_id] = item;
// Emit event
emit List(_name, _cost, _stock);
}
The function above takes in the properties of the struct Item, and these properties are used to create a new product item, which is added to the mapping of items
.
Adding tests
Writing tests is an important step when developing smart contract code. Even a small error in the logic can result in your wallet being drained. Since a smart contract becomes immutable once deployed, developers need to write comprehensive tests that cover all aspects of the logic.
Hardhat allows us to use Mocha and Chai to write tests in Javascript. To get started, navigate to the tests folder and create a new file called ethcommerce.js.
Add the code below to your test file:
const { expect } = require("chai")
const {ethers}=require("hardhat")
require("@nomicfoundation/hardhat-chai-matchers")
const tokens = (n) => {
return ethers.parseUnits(n.toString(), 'ether')
}
// Global constants for listing an item...
const ID = 1
const NAME = "Shoes"
const CATEGORY = "Clothing"
const IMAGE = "https://ipfs.io/ipfs/QmTYEboq8raiBs7GTUg2yLXB3PMz6HuBNgNfSZBx5Msztg/shoes.jpg"
const COST = tokens(1)
const RATING = 4
const STOCK = 5
The tokens
function in the code above uses the parseUnits
method to convert the ether value passed into the function to wei (the smallest unit in Ethereum).
Add the code below to create a new test suite called Ethcommerce
.
describe("Ethcommerce", () => {
let ethcommerce
let deployer, buyer
beforeEach(async () => {
// Setup accounts
[deployer, buyer] = await ethers.getSigners()
// Deploy contract
const Ethcommerce= await ethers.getContractFactory("Ethcommerce")
ethcommerce = await Ethcommerce.deploy()
})
//rest of the test
})
The Ethcommerce
test suite declares three variables: ethcommerce
(representing an instance of the smart contract), and deployer
and buyer
(representing Ethereum account signers).
The beforeEach
function runs before each test in the suite. So, before each test, a new instance of the Ethcommerce
contract is created and then deployed.
The code below creates tests to check the contract's deployment behaviour. The first test expects that the owner’s address matches the deployer’s address, which is the first address in the list returned from ethers.getSigners()
.
The next tests check the listing behaviour. It ensures that each item property is correctly assigned and that the right event is emitted at the end of the listing.
describe("Deployment", () => {
it("Sets the owner", async () => {
expect(await ethcommerce.owner()).to.equal(deployer.address)
})
})
describe("Listing", () => {
let transaction
beforeEach(async () => {
// List a item
transaction = await ethcommerce.connect(deployer).list(ID, NAME, CATEGORY, IMAGE, COST, RATING, STOCK)
await transaction.wait()
})
it("Returns item attributes", async () => {
const item = await ethcommerce.items(ID)
expect(item.id).to.equal(ID)
expect(item.name).to.equal(NAME)
expect(item.category).to.equal(CATEGORY)
expect(item.image).to.equal(IMAGE)
expect(item.cost).to.equal(COST)
expect(item.rating).to.equal(RATING)
expect(item.stock).to.equal(STOCK)
})
it("Emits List event", () => {
expect(transaction).to.emit(ethcommerce, "List")
})
})
Buying Products
In this section, you will create a function in your contract that allows users to buy products listed in the contract. Let’s get into it.
Add the code below to create three variables:
-
Order
: A struct for orders -
orderCount
: A mapping that keeps track of all orders -
orders
: Another mapping to store all the orders.
The mapping, orders
is a nested mapping. It uses the address of the user placing an order as the key, and the corresponding value is a mapping of all the orders made by that user.
mapping(address => mapping(uint256 => Order)) public orders;
mapping(address => uint256) public orderCount;
struct Order {
uint256 time;
Item item;
}
Add a Buy
event in your contract. This event will be emitted every time a user buys a product.
event Buy(address buyer, uint256 orderId, uint256 itemId);
Add the buy
function just after the list function:
function buy(uint256 _id) public payable {
// Fetch item
Item memory item = items[_id];
// Require enough ether to buy item
require(msg.value >= item.cost);
// Require item is in stock
require(item.stock > 0);
// Create order
Order memory order = Order(block.timestamp, item);
// Add order for user
orderCount[msg.sender]++; // <-- Order ID
orders[msg.sender][orderCount[msg.sender]] = order;
// Subtract stock
items[_id].stock = item.stock - 1;
// Emit event
emit Buy(msg.sender, orderCount[msg.sender], item.id);
}
The buy
function takes in the _id
of the product. This _id
is used to fetch the product from the mapping of items
.
If the msg.value
sent along with the buy request is greater or equal to the cost of the item and the item is still in stock, a new order will be created with the timestamp and the item.
The orderCount
, item’s stock (item.stock
) and items
mapping variables will then be updated with the appropriate values.
Finally, a Buy
event will be emitted to indicate a successful purchase.
Writing tests for the buy
function
The test suite for the buy
function will be checking 4 scenarios:
- Order Count Update: This test case checks whether the buyer’s order count increases by 1 after making a purchase.
- Order Details: It checks that the order details (such as timestamp and item name) are correctly stored in the contract after the purchase.
- Contract Balance Update: This test case confirms that the contract’s balance (in Ether) is updated by the cost of the purchased item.
- Event Emission: Finally, the last test case checks whether the contract emits a ‘Buy’ event during the purchase.
Go to your test file and add the code below:
describe('Buying', () => {
let transaction;
beforeEach(async () => {
// List a item
transaction = await ethcommerce
.connect(deployer)
.list(ID, NAME, CATEGORY, IMAGE, COST, RATING, STOCK);
await transaction.wait();
// Buy a item
transaction = await ethcommerce.connect(buyer).buy(ID, { value: COST });
await transaction.wait();
});
it("Updates buyer's order count", async () => {
const result = await ethcommerce.orderCount(buyer.address);
expect(result).to.equal(1);
});
it('Adds the order', async () => {
const order = await ethcommerce.orders(buyer.address, 1);
expect(order.time).to.be.greaterThan(0);
expect(order.item.name).to.equal(NAME);
});
it('Updates the contract balance', async () => {
const result = await ethers.provider.getBalance(ethcommerce.getAddress());
expect(result).to.equal(COST);
});
it('Emits Buy event', () => {
expect(transaction).to.emit(ethcommerce, 'Buy');
});
});
Withdrawing
Navigate back to Ethcommerce.sol and add a withdraw
function. This function sends the balance of the contract to the owner.
function withdraw() public onlyOwner {
(bool success, ) = owner.call{value: address(this).balance}("");
require(success);
}
Writing tests for withdrawing
Copy the code below into your test files to check for withdrawal behaviour.
These tests ensure that only the owner of the contract can withdraw and that the contract balance and owner balance are correctly updated when a withdrawal is triggered.
describe("Withdrawing", () => {
let balanceBefore
beforeEach(async () => {
// List a item
let transaction = await ethcommerce.connect(deployer).list(ID, NAME, CATEGORY, IMAGE, COST, RATING, STOCK)
await transaction.wait()
// Buy a item
transaction = await ethcommerce.connect(buyer).buy(ID, { value: COST })
await transaction.wait()
// Get Deployer balance before
balanceBefore = await ethers.provider.getBalance(deployer.address)
// Withdraw
transaction = await ethcommerce.connect(deployer).withdraw()
await transaction.wait()
})
it('Updates the owner balance', async () => {
const balanceAfter = await ethers.provider.getBalance(deployer.address)
expect(balanceAfter).to.be.greaterThan(balanceBefore)
})
it('Updates the contract balance', async () => {
const result = await ethers.provider.getBalance(ethcommerce.getAddress())
expect(result).to.equal(0)
})
})
Enter the command below in your terminal to run your tests:
npx hardhat test
To start the hardhat node, copy the command below:
npx hardhat node
Deploying the contract
Usually, in an e-commerce application, the listing of products is usually done in a dashboard. However, for the purpose of this tutorial, you will be seeding the list of product items from a JSON file.
You can copy the JSON file from my GitHub repository.
In the src directory, create an items.json file and paste the copied content into this file.
Hardhat allows users to create a script file containing deployment instructions. For this project you will create a deploy.js script file. In this file, the products listed in items.json will be seeded into the contract after deployment.
In the root of your project, create a scripts folder, then create a file called deploy.js. In this file, add the following code:
const hre = require("hardhat")
const { items } = require("../src/items.json")
const tokens = (n) => {
return ethers.parseUnits(n.toString(), 'ether')
}
async function main() {
// Setup accounts
const [deployer] = await ethers.getSigners()
// Deploy Ethcommerce
const Ethcommerce = await hre.ethers.getContractFactory("Ethcommerce")
const ethcommerce = await Ethcommerce.deploy()
await ethcommerce.waitForDeployment()
const address= await ethcommerce.getAddress()
console.log(`Deployed Ethcommerce Contract at: ${address}\n`)
// Listing items...
for (let i = 0; i < items.length; i++) {
const transaction = await ethcommerce.connect(deployer).list(
items[i].id,
items[i].name,
items[i].category,
items[i].image,
tokens(items[i].price),
items[i].rating,
items[i].stock,
)
await transaction.wait()
console.log(`Listed item ${items[i].id}: ${items[i].name}`)
}
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
The script above begins by deploying Ethcommerce
. Once the contract has been successfully deployed, we proceed to loop through the items from the JSON file. Using the list function, each product item is seeded into the contract.
Run the script with the command:
npx hardhat run ./scripts/deploy.js --network localhost
Make sure your hardhat node is running in another terminal when you run this command.
This will initiate the deployment process for your contract on the local network.
If you check your console, you should see a message that prints out your contract address.
Proceed to navigate to your src folder and create a config.json file. Add the following to the json file:
{
"31337": {
"ethcommerce": {
"address": "*********************”
}
}
}
Replace the address's placeholder text (****) with the contract address printed in your console.
Connecting to the front-end
Since this tutorial is focused on smart contracts, I won’t be dwelling much on the styling. You can find the stylesheet for index.css on Github.
Adding the abi (Application Binary Interface) file
When we create a smart contract using languages like Solidity, it compiles into bytecode, which is a low-level, machine-readable format. It’s hard for web applications or even other smart contracts to interact with the bytecode. The ABI serves as a bridge that enables communication between off-chain applications and on-chain smart contracts.
To add the ABI file to your front-end, navigate to artifacts/contracts/Ethcommerce.sol/Ethcommerce.json. This path is where the ABI is stored in your project. Inside the ABI file, locate the array with the key abi
and copy it.
In the src directory, create another directory called abis. Inside the abis directory, create an Ethcommerce.json file. Paste the abi
array into your newly created JSON file.
Adding Hardhat to Metamask
If you are working with hardhat for the first time, you need to add it as a network to your metamask in order to use the test accounts provided out of the box.
To get started, open your Metamask browser extension and click on Settings. In the list of settings, click on Networks, then click on the Add Network Button at the bottom of the popup to navigate to the networks page. Scroll to the bottom of the page and click on the Add Network Manually text to reveal a form. In the input field for Network name, type "Hardhat".
To get the RPC URL, run this command in your terminal:
npx hardhat node
This command should print out hardhat’s RPC URL, 10 test account addresses and their private keys.
Enter the RPC URL in the provided input field, then use "31337" as the chain ID and finally, add "ETH" as the currency symbol.
Adding Hardhat's test accounts
- Click on your account address at the top of Metamask’s extension pop- up.
- Next, click on the + Add Account or hardware wallet button.
- Select Import Account from the list of options that comes up.
- Return to your terminal and copy the private key of the first account from the list of test accounts.
- Paste the private key into the provided input field.
- Click the import button.
Your new account should now appear in your wallet, funded with a certain amount of ETH.
Repeat the process for 2 to 3 accounts. This gives you the option to switch between accounts and test your application.
Loading products from the smart contract
You’ll be using the abi file, Ethcommerce.json and the config.json file to load the product list into the state in React.
Copy the code below into your App.js file:
import { useEffect, useState } from 'react';
import { ethers } from 'ethers';
// ABIs
import Ethcommerce from './abis/Ethcommerce.json';
// Config
import config from './config.json';
function App() {
const [provider, setProvider] = useState(null);
const [ethcommerce, setEthcommerce] = useState(null);
const [electronics, setElectronics] = useState(null);
const [clothing, setClothing] = useState(null);
const [toys, setToys] = useState(null);
const [account, setAccount] = useState(null);
const loadBlockchainData = async () => {
const provider = new ethers.BrowserProvider(window.ethereum);
setProvider(provider);
const network = await provider.getNetwork();
const loadedEthcommerce = new ethers.Contract(
config[network.chainId].ethcommerce.address,
Ethcommerce,
provider
);
console.log(await loadedEthcommerce.items(1));
setEthcommerce(loadedEthcommerce);
const items = [];
for (var i = 0; i < 9; i++) {
const item = await ethcommerce.items(i + 1);
items.push(item);
}
console.log(items, loadedEthcommerce, 'hi');
const electronics = items.filter((item) => item.category === 'electronics');
const clothing = items.filter((item) => item.category === 'clothing');
const toys = items.filter((item) => item.category === 'toys');
setElectronics(electronics);
setClothing(clothing);
setToys(toys);
};
useEffect(() => {
loadBlockchainData();
}, []);
return (
<div>
<h2>Ethcommerce Best Sellers</h2>
</div>
);
}
export default App;
The loadBlockchainData
function initializes an Ethereum provider using the BrowserProvider
method. It then loads the Ethcommerce
smart contract using the contract address in the config.json file and the abi file. The function goes on to fetch item data, and filter them into 3 categories.
Connecting Metamask to the front-end
In this section, you will create the Navigation
component. This component will include a button that enables users to connect their wallet to the application. Let's get into it.
In the src directory, create a folder and name it components. Inside the components folder, create a Navigation.js file.
Copy the code below into the file:
import { ethers } from 'ethers';
const Navigation = ({ account, setAccount }) => {
const connectHandler = async () => {
try {
const accounts = await window.ethereum.request({
method: 'eth_requestAccounts'
});
const account = ethers.getAddress(accounts[0]);
setAccount(account);
} catch (error) {
if (error.code === 4001) {
console.error('User denied connection.');
}
}
};
return (
<nav>
<div className='nav__brand'>
<h1>Ethcommerce</h1>
</div>
<input type='text' className='nav__search' />
{account ? (
<button type='button' className='nav__connect'>
{account.slice(0, 6) + '...' + account.slice(38, 42)}
</button>
) : (
<button type='button' className='nav__connect' onClick={connectHandler}>
Connect
</button>
)}
<ul className='nav__links'>
<li>
<a href='#Clothing & Jewelry'>Clothing & Jewelry</a>
</li>
<li>
<a href='#Electronics & Gadgets'>Electronics & Gadgets</a>
</li>
<li>
<a href='#Toys & Gaming'>Toys & Gaming</a>
</li>
</ul>
</nav>
);
};
export default Navigation;
The connectHandler
function will attempt to connect to the user’s Ethereum wallet by requesting account access.
If successful, it retrieves the first account from the returned accounts array, converts it to an Ethereum address using ethers.getAddress(accounts[0])
, and sets it as the account state variable.
If an account has already been created, a button showing part of the user's address will be displayed. If not, the connect button is displayed.
Add the Navigation component to App.js. Add the code below between the div wrappers:
<Navigation account={account} setAccount={setAccount} />
<h2>Dappazon Best Sellers</h2>
Then import the Navigation component at the top of your code:
// Components
import Navigation from './components/Navigation'
Check your page in the browser, and it should look like the image below:
Listing products
In your components directory, create a Section.js file and add the code below into the file:
import { ethers } from 'ethers'
const Section = ({ title, items, togglePop }) => {
return (
<div className='cards__section'>
<h3 id={title}>{title}</h3>
<hr />
<div className='cards'>
{items.map((item, index) => (
<div className='card' key={index} onClick={() =>
togglePop(item)}>
<div className='card__image'>
<img src={item.image} alt="Item" />
</div>
<div className='card__info'>
<h4>{item.name}</h4> <p>{ether.formatUnits(item.cost.toString(), 'ether')} ETH</p>
</div>
</div>
))}
</div>
</div>
);
}
export default Section;
This file will display the list of products for each product category. Copy the code below into App.js:
import Section from './components/Section'
This should go right after the h2
tag.
{electronics && clothing && toys && (
<>
<Section title={"Clothing & Jewelry"} items={clothing} togglePop={togglePop} />
<Section title={"Electronics & Gadgets"} items={electronics} togglePop={togglePop} />
<Section title={"Toys & Gaming"} items={toys} togglePop={togglePop} />
</>
)}
The code above checks to see if all 3 categories have been successfully fetched from the smart contract, before displaying the sections.
Your page should look like this:
Create a toggleProp function to toggle the visibility of a specific product when clicked. So, when a product card is clicked, the product object is saved into the item state and then displayed as a modal on the page.
const [toggle, setToggle] = useState(false)
const [item, setItem] = useState({})
const togglePop = (item) => {
setItem(item)
toggle ? setToggle(false) : setToggle(true)
}
Creating products modal
In your components folder, create a Product.js file. Copy the code below into the file:
import { useEffect, useState } from 'react'
import { ethers } from 'ethers'
import close from '../assets/close.svg'
const Product = ({ item, provider, account, ethcommerce, togglePop }) => {
const [order, setOrder] = useState(null)
const [hasBought, setHasBought] = useState(false)
const fetchDetails = async () => {
const events = await ethcommerce.queryFilter("Buy")
const orders = events.filter(
(event) => event.args.buyer === account && event.args.itemId.toString() === item.id.toString()
)
if (orders.length === 0) return
const order = await ethcommerce.orders(account, orders[0].args.orderId)
setOrder(order)
}
return (
<div className="product">
</div >
);
}
export default Product;
This component takes in a few props including item
, which is an object that has the product details.
In the fetchDetails
function, the item.id
is used to check if the user has bought the product before, so the order details can be saved in the state.
Using useEffect
, fetchDetails
is immediately called after the component mounts.
useEffect(() => {
fetchDetails()
}, [hasBought])
close.svg is currently not in the project. Go to github to download the file, and then add it to src/assets.
Next, you’re going to create a buyHandler
function. This function will access the smart contract and pass the product’s id and cost to the smart contract's buy
function.
const buyHandler = async () => {
const signer = await provider.getSigner()
// Buy item...
let transaction = await ethcommerce.connect(signer).buy(item.id, { value: item.cost })
await transaction.wait()
setHasBought(true)
}
To display the product details add the code below between the div wrapper:
<div className='product__details'>
<div className='product__image'>
<img src={item.image} alt='Product' />
</div>
<div className='product__overview'>
<h1>{item.name}</h1>
<hr />
<p>{item.address}</p>
<h2>{ethers.formatUnits(item.cost.toString(), 'ether')} ETH</h2>
<hr />
<h2>Overview</h2>
<p>
{item.description}
Lorem ipsum dolor sit amet consectetur adipisicing elit. Minima rem,
iusto, consectetur inventore quod soluta quos qui assumenda aperiam,
eveniet doloribus commodi error modi eaque! Iure repudiandae
temporibus ex? Optio!
</p>
</div>
</div>
Just underneath the product__overview
div, add the code to display the order details and the button to trigger the buyHandler
.
<div className="product__order">
<h1>{ethers.formatUnits(item.cost.toString(), 'ether')} ETH</h1>
<p>
FREE delivery <br />
<strong>
{new Date(Date.now() + 345600000).toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' })}
</strong>
</p>
{item.stock > 0 ? (
<p>In Stock.</p>
) : (
<p>Out of Stock.</p>
)}
<button className='product__buy' onClick={buyHandler}>
Buy Now
</button>
<p><small>Ships from</small> Ethcommerce</p>
<p><small>Sold by</small> Ethcommerce</p>
{order && (
<div className='product__bought'>
Item bought on <br />
<strong>
{new Date(Number(order.time.toString() + '000')).toLocaleDateString(
undefined,
{
weekday: 'long',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
})}
</strong>
</div>
)}
</div>
<button onClick={togglePop} className="product__close">
<img src={close} alt="Close" />
</button>
Navigate to App.js and add Product
to the jsx.
import Product from './components/Product'
The toggle state determines the visibility of the Product modal, so when a product card is clicked, the toggle is set to true.
{toggle && (
<Product item={item} provider={provider} account={account} ethcommerce={ethcommerce} togglePop={togglePop} />
)}
And with that, we have come to the end of this tutorial.
Conclusion
Whew! That was quite a ride. In this tutorial, we explored the true meaning of decentralization for a Web3 e-commerce application and emphasized the importance of building a seamless and fast platform to promote global Web3 adoption. We walked through using Hardhat and React.js to create an application with basic e-commerce functionality. You now have an overview of what it takes to build e-commerce solutions using Web3 technology. However, there's still more ground to cover, including adding email notifications, chat support, and logistics to this application. Feel free to experiment and incorporate these features into the platform. Have fun!
Top comments (1)
Thanks! Bookmarked👌