├── Readme.md ├── blockchain ├── .gitignore ├── README.md ├── contracts │ └── Ecommerce.sol ├── hardhat.config.js ├── package.json ├── scripts │ └── deploy.js ├── test │ └── Ecommerce.test.js └── yarn.lock └── frontend ├── .eslintrc.json ├── .gitignore ├── README.md ├── components ├── 404-page │ └── ErrorPage.jsx ├── admin │ ├── Admin.jsx │ ├── AdminDashboard.jsx │ └── TabComp.jsx ├── categories │ └── Categories.jsx ├── navbar │ └── Navbar.jsx ├── product-details │ └── ProductDetail.jsx └── wallet │ └── WalletConnect.jsx ├── configs └── tsConfig.json ├── constants ├── Ecommerce.json ├── index.js └── urls.js ├── context └── MainContext.js ├── data ├── carouselData.js ├── dropdownData.js ├── index.js └── navData.js ├── layouts └── MainLayout.jsx ├── next.config.js ├── package.json ├── pages ├── 404.jsx ├── [productid].js ├── _app.js ├── admin.js ├── api │ └── hello.js ├── index.js └── my-orders.js ├── postcss.config.js ├── public ├── favicon.ico ├── images │ ├── 404-image.webp │ ├── categories │ │ ├── all_img.webp │ │ ├── camera_img.webp │ │ ├── clothes_img.webp │ │ ├── laptops_img.webp │ │ ├── mobile_img.webp │ │ ├── television_img.webp │ │ └── toys_img.webp │ ├── connect-wallet.png │ ├── ipad_image.webp │ ├── login-img.webp │ └── logo.jpg └── vercel.svg ├── styles └── globals.css ├── subcomponents ├── card │ └── Card1.jsx ├── logo │ └── Logo.jsx ├── modal │ └── Modal.jsx └── particles-ts │ └── TsParticles.jsx ├── tailwind.config.js ├── utils └── DateConverter.js └── yarn.lock /Readme.md: -------------------------------------------------------------------------------- 1 | # FullStack Ecommerce DApp (Decentralized Application) 2 | Complete Full Stack Decentralization Ecommerce Website using **Solidity** language for Smart Contract, **hardhat** for Dev Enviroment, and Next.js framework For Frontend. 3 | 4 | 5 | ### See Deployed Version -> https://ecommerce-dapp-ten.vercel.app/ 6 | 7 |   8 |   9 |   10 |   11 | 12 | 13 | Screenshot-391 14 | 15 | Screenshot-394 16 | 17 | 18 | 19 | ## Functionalities :- 20 | - **For Users** 21 | - **See All Products** 22 | - **Filter Products** 23 | - **See perticular productn** 24 | - **Buy The Product** 25 | - **See the past history of products** 26 | 27 |   28 |   29 | 30 | - **For Admin** 31 | - **Add the Product** 32 | - **Upload image, Metadata to IPFS** 33 | - **See the Contract balance.** 34 | - **Withdraw Balance of contract.** 35 | 36 | 37 | ## How to Setup in your local enviroment :- 38 | 39 | ### Frontend 40 | 1. cd frontend 41 | 2. yarn 42 | 3. Setup Env 43 | 3. yarn run dev 44 | 45 | 46 | ### Blockchain 47 | 1. cd blockchain 48 | 2. yarn install 49 | 3. setup env for polygon test network or you can use localhost. 50 | 4. yarn hardhat run scripts/deploy.js --network polygon 51 | 52 | 53 | 54 | ## Technologies/Frameworks Used :- 55 | 56 | ### Frontend 57 | 1. Next.js (Using Vite). 58 | 2. Tailwind CSS and Material-Tailwind (For styling). 59 | 3. **ethers.js** for Web3 integration. 60 | 4. IPFS for storing images. 61 | 5. Alchemy mumbai Testnet (Polygon) 62 | 63 | ## Blockchain 64 | 1. **Solidity** (To develop Smart Contract) 65 | 2. Javascript (For deploying scripts) 66 | 3. **Chai** (For testing Smart Contract) 67 | 4. Polygon (Test network) 68 | 5. Hardhat 69 | 70 | 71 | 72 | Screenshot-396 73 | Screenshot-395 74 | Screenshot-393 75 | Screenshot-392 76 | -------------------------------------------------------------------------------- /blockchain/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | typechain-types 7 | 8 | #Hardhat files 9 | cache 10 | artifacts 11 | 12 | -------------------------------------------------------------------------------- /blockchain/README.md: -------------------------------------------------------------------------------- 1 | # Sample Hardhat Project 2 | 3 | This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract. 4 | 5 | Try running some of the following tasks: 6 | 7 | ```shell 8 | npx hardhat help 9 | npx hardhat test 10 | GAS_REPORT=true npx hardhat test 11 | npx hardhat node 12 | npx hardhat run scripts/deploy.js 13 | ``` 14 | -------------------------------------------------------------------------------- /blockchain/contracts/Ecommerce.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | error Ecommerce__YouAreNoteOwner(); 5 | error Ecommerce__PriceNotMet(uint256 _price); 6 | 7 | contract Ecommerce { 8 | /* State Variables */ 9 | uint256 private productId; 10 | address private owner; 11 | 12 | constructor() { 13 | owner = msg.sender; 14 | } 15 | 16 | /* structs */ 17 | struct ProductStruct { 18 | uint256 price; 19 | uint32 quantity; 20 | string metadata; 21 | } 22 | /* Events */ 23 | 24 | // After Adding the product 25 | event productAdded( 26 | string indexed _category, 27 | uint256 indexed _timestamp, 28 | uint256 indexed _productId, 29 | uint256 _price, 30 | string _metadata 31 | ); 32 | 33 | // After buying the product 34 | event productBought( 35 | address indexed buyer, 36 | uint256 indexed timestamp, 37 | uint256 _productId, 38 | uint256 price, 39 | string metadata 40 | ); 41 | 42 | /* Modifiers */ 43 | modifier onlyOwner() { 44 | if (msg.sender != owner) { 45 | revert Ecommerce__YouAreNoteOwner(); 46 | } 47 | _; 48 | } 49 | 50 | /* Mappings */ 51 | mapping(uint256 => ProductStruct) s_AllProducts; 52 | 53 | /* Logics */ 54 | 55 | function addProduct( 56 | uint256 _price, 57 | uint32 _quantity, 58 | string memory _metadata, 59 | string memory _category 60 | ) external onlyOwner { 61 | productId++; 62 | s_AllProducts[productId] = ProductStruct(_price, _quantity, _metadata); 63 | emit productAdded( 64 | _category, 65 | block.timestamp, 66 | productId, 67 | _price, 68 | _metadata 69 | ); 70 | } 71 | 72 | function getProduct(uint256 _productId) 73 | external 74 | view 75 | returns (ProductStruct memory) 76 | { 77 | return s_AllProducts[_productId]; 78 | } 79 | 80 | // Get the total contract balance 81 | function getContractBalance() external view returns (uint256) { 82 | return address(this).balance; 83 | } 84 | 85 | function updateQuantity(uint32 _quantity, uint256 _productId) external { 86 | s_AllProducts[_productId].quantity = _quantity; 87 | } 88 | 89 | function buyProduct(uint256 _productId) external payable { 90 | if (msg.value != s_AllProducts[_productId].price) { 91 | revert Ecommerce__PriceNotMet(msg.value); 92 | } 93 | string memory metadata = s_AllProducts[_productId].metadata; 94 | 95 | emit productBought( 96 | msg.sender, 97 | block.timestamp, 98 | _productId, 99 | msg.value, 100 | metadata 101 | ); 102 | } 103 | 104 | // Withdraw the contract balance. 105 | function withdraw(address _to) external payable onlyOwner { 106 | payable(_to).transfer(address(this).balance); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /blockchain/hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | require('dotenv').config(); 3 | 4 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 5 | const accounts = await hre.ethers.getSigners(); 6 | 7 | for (const account of accounts) { 8 | console.log(account.address); 9 | } 10 | }) 11 | 12 | const PRIVATE_KEY = process.env.POLYGON_PRIVATE_KEY 13 | const RPC_URL = process.env.POLYGON_RPC_URL 14 | 15 | module.exports = { 16 | solidity: "0.8.10", 17 | defaultNetwork: "polygon", 18 | networks: { 19 | hardhat: {}, 20 | polygon: { 21 | url: RPC_URL, 22 | accounts: [PRIVATE_KEY] 23 | } 24 | } 25 | }; -------------------------------------------------------------------------------- /blockchain/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchain", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "@nomicfoundation/hardhat-toolbox": "^1.0.2", 14 | "hardhat": "^2.11.0" 15 | }, 16 | "dependencies": { 17 | "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", 18 | "@nomicfoundation/hardhat-network-helpers": "^1.0.0", 19 | "@nomiclabs/hardhat-ethers": "^2.0.0", 20 | "@nomiclabs/hardhat-etherscan": "^3.0.0", 21 | "@nomiclabs/hardhat-waffle": "^2.0.3", 22 | "@typechain/ethers-v5": "^10.1.0", 23 | "@typechain/hardhat": "^6.1.2", 24 | "@types/mocha": "^9.1.0", 25 | "chai": "^4.2.0", 26 | "dotenv": "^16.0.1", 27 | "ethers": "^5.4.7", 28 | "hardhat-gas-reporter": "^1.0.8", 29 | "solidity-coverage": "^0.7.21", 30 | "ts-node": ">=8.0.0", 31 | "typechain": "^8.1.0", 32 | "typescript": ">=4.5.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /blockchain/scripts/deploy.js: -------------------------------------------------------------------------------- 1 | const hre = require('hardhat'); 2 | 3 | async function main() { 4 | 5 | const Ecommerce = await hre.ethers.getContractFactory("Ecommerce") 6 | const ecommerce = await Ecommerce.deploy(); 7 | 8 | await ecommerce.deployed(); 9 | 10 | console.log("contract deployed to:", ecommerce.address); 11 | } 12 | 13 | main() 14 | .then(() => process.exit(0)) 15 | .catch((error) => { 16 | console.log(error); 17 | process.exit(1); 18 | }); -------------------------------------------------------------------------------- /blockchain/test/Ecommerce.test.js: -------------------------------------------------------------------------------- 1 | const { 2 | time, 3 | loadFixture, 4 | } = require("@nomicfoundation/hardhat-network-helpers"); 5 | const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs"); 6 | const { expect } = require("chai"); 7 | 8 | describe("Ecommerce", function () { 9 | // We define a fixture to reuse the same setup in every test. 10 | // We use loadFixture to run this setup once, snapshot that state, 11 | // and reset Hardhat Network to that snapshot in every test. 12 | async function deployOneYearLockFixture() { 13 | // Contracts are deployed using the first signer/account by default 14 | const [owner, otherAccount] = await ethers.getSigners(); 15 | 16 | const Ecommerce = await ethers.getContractFactory("Ecommerce"); 17 | const ecommerce = await Ecommerce.deploy(); 18 | 19 | return { ecommerce, owner, otherAccount }; 20 | } 21 | 22 | describe("Deployment", function () { 23 | it("Should set the right owner", async function () { 24 | const { ecommerce, owner } = await loadFixture(deployOneYearLockFixture); 25 | expect(ecommerce.addProduct()).not.to.be.reverted; 26 | 27 | expect(await ecommerce.addProduct()).to.not.equals(owner.address); 28 | }); 29 | 30 | it("Should receive and store the funds to lock", async function () { 31 | const { lock, lockedAmount } = await loadFixture( 32 | deployOneYearLockFixture 33 | ); 34 | 35 | expect(await ethers.provider.getBalance(lock.address)).to.equal( 36 | lockedAmount 37 | ); 38 | }); 39 | 40 | it("Should fail if the unlockTime is not in the future", async function () { 41 | // We don't use the fixture here because we want a different deployment 42 | const latestTime = await time.latest(); 43 | const Lock = await ethers.getContractFactory("Lock"); 44 | await expect(Lock.deploy(latestTime, { value: 1 })).to.be.revertedWith( 45 | "Unlock time should be in the future" 46 | ); 47 | }); 48 | }); 49 | 50 | describe("Withdrawals", function () { 51 | describe("Validations", function () { 52 | it("Should revert with the right error if called too soon", async function () { 53 | const { lock } = await loadFixture(deployOneYearLockFixture); 54 | 55 | await expect(lock.withdraw()).to.be.revertedWith( 56 | "You can't withdraw yet" 57 | ); 58 | }); 59 | 60 | it("Should revert with the right error if called from another account", async function () { 61 | const { lock, unlockTime, otherAccount } = await loadFixture( 62 | deployOneYearLockFixture 63 | ); 64 | 65 | // We can increase the time in Hardhat Network 66 | await time.increaseTo(unlockTime); 67 | 68 | await expect(lock.connect(otherAccount).withdraw()).to.be.revertedWith( 69 | "You aren't the owner" 70 | ); 71 | }); 72 | 73 | it("Shouldn't fail if the unlockTime has arrived and the owner calls it", async function () { 74 | const { lock, unlockTime } = await loadFixture( 75 | deployOneYearLockFixture 76 | ); 77 | 78 | await time.increaseTo(unlockTime); 79 | 80 | await expect(lock.withdraw()).not.to.be.reverted; 81 | }); 82 | }); 83 | 84 | describe("Events", function () { 85 | it("Should emit an event on withdrawals", async function () { 86 | const { lock, unlockTime, lockedAmount } = await loadFixture( 87 | deployOneYearLockFixture 88 | ); 89 | 90 | await time.increaseTo(unlockTime); 91 | 92 | await expect(lock.withdraw()) 93 | .to.emit(lock, "Withdrawal") 94 | .withArgs(lockedAmount, anyValue); // We accept any value as `when` arg 95 | }); 96 | }); 97 | 98 | describe("Transfers", function () { 99 | it("Should transfer the funds to the owner", async function () { 100 | const { lock, unlockTime, lockedAmount, owner } = await loadFixture( 101 | deployOneYearLockFixture 102 | ); 103 | 104 | await time.increaseTo(unlockTime); 105 | 106 | await expect(lock.withdraw()).to.changeEtherBalances( 107 | [owner, lock], 108 | [lockedAmount, -lockedAmount] 109 | ); 110 | }); 111 | }); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | .env 9 | 10 | # testing 11 | /coverage 12 | 13 | # next.js 14 | /.next/ 15 | /out/ 16 | 17 | # production 18 | /build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | .pnpm-debug.log* 29 | 30 | # local env files 31 | .env*.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | next-env.d.ts 39 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /frontend/components/404-page/ErrorPage.jsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import React from 'react' 3 | import Logo from '../../subcomponents/logo/Logo' 4 | 5 | export default function ErrorPage({title, description, image, alt}) { 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 | 13 |
14 |
15 |

16 | {title} 17 |

18 |

{description}

19 |

Use the Go Back button below.

20 |
21 |
22 | 23 | 26 | 27 |
28 |
29 | 30 |
31 | {alt} 32 |
33 |
34 |
35 |
36 |
37 |
38 | ) 39 | } 40 | -------------------------------------------------------------------------------- /frontend/components/admin/Admin.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { 3 | CONTRACT_ADDRESS, 4 | IPFS_URL, 5 | PROJECT_ID, 6 | PROJECT_SECRET, 7 | } from "../../constants"; 8 | import EcomABI from "../../constants/Ecommerce.json"; 9 | import { categories } from "../../data"; 10 | import { create as ipfsHttpClient } from "ipfs-http-client"; 11 | import { useRouter } from "next/router"; 12 | import { MdAddTask } from "react-icons/md"; 13 | import { 14 | Input, 15 | Textarea, 16 | Select, 17 | Option, 18 | Button, 19 | IconButton, 20 | Typography, 21 | } from "@material-tailwind/react"; 22 | // import TsParticles from "../../subcomponents/particles-ts/TsParticles"; 23 | import { ToastContainer, toast } from "react-toastify"; 24 | import "react-toastify/dist/ReactToastify.css"; 25 | import { ethers } from "ethers"; 26 | import { MainContext } from "../../context/MainContext"; 27 | 28 | export default function Admin() { 29 | const {requestContract} = useContext(MainContext); 30 | /* IPFS Authentication ------------------------------------------------------------------ */ 31 | 32 | const auth = 33 | "Basic " + 34 | Buffer.from(PROJECT_ID + ":" + PROJECT_SECRET).toString("base64"); 35 | 36 | // IPFS Client -- 37 | const client = ipfsHttpClient({ 38 | host: "ipfs.infura.io", 39 | port: 5001, 40 | protocol: "https", 41 | headers: { 42 | authorization: auth, 43 | }, 44 | }); 45 | 46 | /* Use States ------------------------------------------------------------------ */ 47 | const [isLoading, setIsLoading] = useState(false); 48 | const [formData, setFormData] = useState({ 49 | title: "", 50 | description: "", 51 | price: "", 52 | qunatity: "", 53 | }); 54 | const [category, setCategory] = useState(""); 55 | const [file, setFile] = useState(""); 56 | 57 | // on change of any input. 58 | const handleInputChange = (e) => { 59 | setFormData({ ...formData, [e.target.name]: e.target.value }); 60 | }; 61 | 62 | // When image is changed. 63 | const onChangeFiles = async (e) => { 64 | const fileData = e.target.files[0]; 65 | try { 66 | const add = await client.add(fileData, { 67 | progress: (prog) => console.log("Image is uploaded : ", prog), 68 | }); 69 | const url = `${add.path}`; 70 | toast.success("Product Image Uploaded!"); 71 | setFile(url); 72 | } catch (error) { 73 | console.log("Error...", error); 74 | toast.error("Error While uploading Image, Try Again"); 75 | } 76 | }; 77 | 78 | const handleClick = async () => { 79 | const { title, description, price, qunatity } = formData; 80 | if (!title || !description || !file || !price || !qunatity || !category) { 81 | toast.error("Some Feilds Are Missing"); 82 | return; 83 | } 84 | 85 | try { 86 | setIsLoading(true); 87 | const metadata = { 88 | title, 89 | description, 90 | imageURL: file, 91 | }; 92 | const stringifyData = JSON.stringify(metadata) 93 | 94 | const add = await client.add(stringifyData); 95 | const url = `${add.path}`; 96 | toast.success("Product Data Uploaded!"); 97 | 98 | const amountInWEI = ethers.utils.parseEther(price); 99 | 100 | const contract = await requestContract() 101 | 102 | const productData = await contract.addProduct( 103 | amountInWEI, 104 | qunatity, 105 | url, 106 | category 107 | ); 108 | toast.promise(productData.wait(), { 109 | pending: "Wait...", 110 | success: "Product Successfully Added👌", 111 | error: "Some Error Occured. 🤯", 112 | }); 113 | await productData.wait(); 114 | 115 | if (productData.to) { 116 | setFormData({ title: "", description: "", price: "", qunatity: "" }); 117 | setFile(""); 118 | setCategory(""); 119 | setIsLoading(false); 120 | 121 | } 122 | } catch (error) { 123 | console.log("error... ", error); 124 | toast.error("Error occured. Please try Again."); 125 | setIsLoading(false); 126 | } 127 | }; 128 | 129 | return ( 130 |
131 | Add Product 132 | {/* */} 133 | 134 | 135 |
136 | 144 |