├── .eslintrc.json
├── .gitignore
├── .husky
└── pre-commit
├── .prettierignore
├── .prettierrc
├── README.md
├── components
├── Footer.js
├── FundWallet.js
├── Header.js
├── MyNFTContainer.js
└── index.js
├── constants
├── mock-artist.json
└── mock-nft.json
├── context
└── bundlrContext.js
├── contracts
└── NFTMarketplace.sol
├── hardhat.config.js
├── next.config.js
├── package.json
├── pages
├── _app.js
├── api
│ └── hello.js
├── createnft.js
├── dashboard.js
├── index.js
├── nft-details.js
├── profile.js
└── sellnft.js
├── pnpm-lock.yaml
├── postcss.config.js
├── public
├── favicon.ico
├── images
│ ├── main1.png
│ ├── main2.png
│ ├── main3.png
│ ├── main4.png
│ ├── mock.png
│ ├── mockcreator.jpg
│ ├── nft2.png
│ ├── nft3.png
│ ├── placeholder1.png
│ ├── placeholder2.png
│ ├── placeholder3.png
│ ├── placeholder4.png
│ └── wallet.png
├── logo.png
└── vercel.svg
├── scripts
└── deploy.js
├── styles
├── Home.module.css
└── globals.css
├── tailwind.config.js
├── test
└── nftmarketplace-test.js
└── utils
└── truncAddress.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.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 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | node_modules
35 | .env
36 | coverage
37 | coverage.json
38 | typechain
39 | typechain-types
40 |
41 | #Hardhat files
42 | cache
43 | artifacts
44 |
45 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Directories
2 | node_modules
3 | .next
4 | build
5 | dist
6 | out
7 | coverage
8 |
9 | # Files
10 | *.min.js
11 | *.lock
12 | package-lock.json
13 | yarn.lock
14 | pnpm-lock.yaml
15 |
16 | # Config files
17 | .env*
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": true,
3 | "tabWidth": 2,
4 | "printWidth": 100,
5 | "singleQuote": true,
6 | "trailingComma": "es5",
7 | "bracketSpacing": true,
8 | "jsxBracketSameLine": false
9 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DesiNFT - Create & Sell Your Own NFT!
2 |
3 |
4 |
5 |
6 |
7 | ### Tech Stack
8 |
9 |   
10 |
11 | ### Screenshots
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ### Installing
25 |
26 | First, we need to clone the repository and change the working directory. Then, we must set up environment variables for the project. Make sure to add `.env` to `.gitignore`.
27 |
28 | ```bash
29 | git clone https://github.com/ayush-that/HackOn-Blocks-2024.git
30 | cd HackOn-Blocks-2024
31 | ```
32 |
33 | Use the node version manager [(nvm)](https://www.freecodecamp.org/news/node-version-manager-nvm-install-guide/) and set up the project to use Node v16 for seamless installation. Now install the dependencies.
34 |
35 | ```node
36 | nvm install 16
37 | nvm use 16
38 | npm i
39 | ```
40 |
41 | In the `.env`, add the following environment variables. Get the `PRIVATE_KEY` from your MetaMask account. For Polygon zkEVM (ETH):
42 |
43 | ```env
44 | PRIVATE_KEY="METAMASK_PRIVATE_KEY"
45 | NEXT_PUBLIC_CONTRACT_ADDRESS="CONTRACT_ADDRESS"
46 | URL=https://rpc.cardona.zkevm-rpc.com
47 | NEXT_PUBLIC_RPC_URL=https://rpc.cardona.zkevm-rpc.com
48 | ```
49 |
50 | To get the value of `NEXT_PUBLIC_CONTRACT_ADDRESS`, In the root directory of the project run the following commands. This will compile and deploy the smart contract.
51 |
52 | ```node
53 | npx hardhat compile
54 | npx hardhat run scripts/deploy.js --network polygonZkEvmTestnet
55 | ```
56 |
57 | Now, run `npm run dev` or `yarn dev` to start the live deployment server. You can use Vercel to deploy it too. See the [Live](https://desinft.vercel.app/) site here.
58 |
--------------------------------------------------------------------------------
/components/Footer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Footer = () => {
4 | return (
5 |
6 |
7 |
8 |
{new Date().getFullYear()} All Right Reserved
9 | Designed and Developed By{' '}
10 |
11 | {' '}
12 | Ayush{' '}
13 |
14 |
Deployed on the Polygon Cardona zkEVM 💜
{' '}
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | export default Footer;
22 |
--------------------------------------------------------------------------------
/components/FundWallet.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { useBundler } from '../context/bundlrContext';
3 |
4 | const FundWallet = () => {
5 | const { fundWallet, balance } = useBundler();
6 | const [value, setValue] = React.useState('0.005');
7 |
8 | return (
9 |
10 |
You Current Balance is : {balance || 0} $BNDLR
11 | setValue(e.target.value)}
15 | />
16 | fundWallet(+value)}
21 | >
22 | Add Funds 💱
23 |
24 |
25 | );
26 | };
27 |
28 | export default FundWallet;
29 |
--------------------------------------------------------------------------------
/components/Header.js:
--------------------------------------------------------------------------------
1 | import { CloseSquare, HambergerMenu } from 'iconsax-react';
2 | import Link from 'next/link';
3 | import { useRouter } from 'next/router';
4 | import React, { useEffect, useState } from 'react';
5 | import { truncateEthAddress } from '../utils/truncAddress';
6 |
7 | const Header = () => {
8 | const [isOpen, setIsOpen] = useState(false);
9 | const router = useRouter();
10 | const currentRoute = router.pathname;
11 |
12 | const [hasScrolled, setHasScrolled] = useState(false);
13 |
14 | const [addr, setAddr] = useState('');
15 |
16 | const changeNavbar = () => {
17 | if (window.scrollY >= 20) {
18 | setHasScrolled(true);
19 | } else {
20 | setHasScrolled(false);
21 | }
22 | };
23 |
24 | useEffect(() => {
25 | document.addEventListener('scroll', changeNavbar);
26 | });
27 |
28 | useEffect(() => {
29 | const addr = localStorage.getItem('walletAddress');
30 | setAddr(addr);
31 | }, []);
32 |
33 | const toggle = () => setIsOpen(!isOpen);
34 |
35 | return (
36 | <>
37 |
44 |
51 |
52 |
53 | DesiNFT
54 |
55 |
56 |
97 |
98 |
99 | {truncateEthAddress(addr)}
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
115 |
116 |
122 |
123 |
124 |
125 | DesiNFT
126 |
127 |
128 |
169 |
170 |
171 | {truncateEthAddress(addr)}
172 |
173 |
174 |
175 | >
176 | );
177 | };
178 |
179 | export default Header;
180 |
--------------------------------------------------------------------------------
/components/MyNFTContainer.js:
--------------------------------------------------------------------------------
1 | import { useRouter } from 'next/router';
2 | import React from 'react';
3 | import { truncateEthAddress } from '../utils/truncAddress';
4 |
5 | // Test mode settings
6 | const TEST_MODE = true;
7 | const DEFAULT_TEST_IMAGE = '/logo.png'; // Use your logo as placeholder for test images
8 |
9 | const mainURL = `https://arweave.net/`;
10 |
11 | const MyNFTContainer = ({ nft }) => {
12 | const router = useRouter();
13 |
14 | return (
15 |
16 |
{
19 | router.push({
20 | pathname: '/nft-details',
21 | query: nft,
22 | });
23 | }}
24 | >
25 |
26 |
31 |
32 |
33 | {
36 | router.push({
37 | pathname: '/nft-details',
38 | query: nft,
39 | });
40 | }}
41 | >
42 | View Details
43 |
44 |
45 |
46 |
47 |
48 |
{nft.name}
49 |
50 |
51 |
Owner
52 |
53 | {truncateEthAddress(nft.owner)}
54 |
55 |
56 |
57 |
Price
58 |
{nft.price} ETH
59 |
60 |
61 |
62 |
63 |
64 | );
65 | };
66 |
67 | export default MyNFTContainer;
68 |
--------------------------------------------------------------------------------
/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Header } from './Header';
2 | export { default as Footer } from './Footer';
3 | export { default as FundWallet } from './FundWallet';
4 | export { default as MyNFTContainer } from './MyNFTContainer';
5 |
--------------------------------------------------------------------------------
/constants/mock-artist.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "name": "Khamaj - The night Raga",
5 | "bgImage": "/images/placeholder1.png",
6 | "image": "/images/main1.png",
7 | "price": "37.7K"
8 | },
9 | {
10 | "id": 2,
11 | "name": "Ecotherapy",
12 | "bgImage": "/images/placeholder2.png",
13 | "image": "/images/main2.png",
14 | "price": "87.1K"
15 | },
16 | {
17 | "id": 3,
18 | "name": "Yuzen Sarees",
19 | "bgImage": "/images/placeholder3.png",
20 | "image": "/images/main3.png",
21 | "price": "79.4K "
22 | },
23 | {
24 | "id": 4,
25 | "name": "Toy Faces",
26 | "bgImage": "/images/placeholder4.png",
27 | "image": "/images/main4.png",
28 | "price": "13.9K"
29 | }
30 | ]
31 |
--------------------------------------------------------------------------------
/constants/mock-nft.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "image": "images/wallet.png",
5 | "title": "Set up your wallet",
6 | "description": "Choose a wallet of your choice. We recommend Metamask Wallet."
7 | },
8 | {
9 | "id": 2,
10 | "image": "images/nft2.png",
11 | "title": "Add your NFTs",
12 | "description": "Once you’ve set up your wallet of choice,Start Creating your Your NFTs"
13 | },
14 | {
15 | "id": 3,
16 | "image": "images/nft3.png",
17 | "title": "List them for sale",
18 | "description": "List Out all the Top hot notch NFTs available for sale"
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/context/bundlrContext.js:
--------------------------------------------------------------------------------
1 | import { WebBundlr } from '@bundlr-network/client';
2 | import BigNumber from 'bignumber.js';
3 | import { providers, utils } from 'ethers';
4 | import React, { createContext, useContext, useEffect, useState } from 'react';
5 | import { toast } from 'react-toastify';
6 |
7 | const TEST_MODE = true;
8 | const BundlrContext = createContext({
9 | initialiseBundlr: async () => {},
10 | fundWallet: (_) => {},
11 | balance: '',
12 | uploadFile: async (_file) => {},
13 | uploadURI: async (_file) => {},
14 | bundlrInstance: null,
15 | });
16 |
17 | const BundlrContextProvider = ({ children }) => {
18 | const [bundlrInstance, setBundlrInstance] = useState();
19 | const [balance, setBalance] = useState(TEST_MODE ? '1.0' : '');
20 |
21 | useEffect(() => {
22 | if (bundlrInstance) {
23 | fetchBalance();
24 | }
25 | }, [bundlrInstance]);
26 |
27 | const initialiseBundlr = async () => {
28 | if (TEST_MODE) {
29 | setBundlrInstance({ test: true });
30 | setBalance('1.0');
31 | return;
32 | }
33 |
34 | const { ethereum } = window;
35 | const provider = new providers.Web3Provider(ethereum);
36 | await provider._ready();
37 | const bundlr = new WebBundlr('https://devnet.bundlr.network', 'matic', provider, {
38 | providerUrl: process.env.NEXT_PUBLIC_RPC_URL,
39 | });
40 | await bundlr.ready();
41 | setBundlrInstance(bundlr);
42 | };
43 |
44 | async function fundWallet(amount) {
45 | if (TEST_MODE) {
46 | toast.success('Test mode: Funds added successfully');
47 | setBalance('1.0');
48 | return;
49 | }
50 |
51 | console.log(amount);
52 | try {
53 | if (bundlrInstance) {
54 | console.log(bundlrInstance);
55 | if (!amount) return;
56 | const amountParsed = parseInput(amount);
57 | console.log(amountParsed);
58 | if (amountParsed) {
59 | toast.info('Adding funds please wait', { progress: 1 });
60 | console.log('Adding...');
61 | let response = await bundlrInstance.fund(amountParsed);
62 | console.log('Wallet funded: ', response);
63 | toast.success('Funds added', { progress: 1 });
64 | }
65 | fetchBalance();
66 | }
67 | } catch (error) {
68 | console.log('error', error);
69 | toast.error(error.message || 'Something went wrong!');
70 | }
71 | }
72 |
73 | function parseInput(input) {
74 | if (TEST_MODE) return new BigNumber(input);
75 |
76 | const conv = new BigNumber(input).multipliedBy(bundlrInstance?.currencyConfig.base[1]);
77 | if (conv.isLessThan(1)) {
78 | console.log('error: value too small');
79 | toast.error('Error: value too small');
80 | return;
81 | } else {
82 | return conv;
83 | }
84 | }
85 |
86 | async function fetchBalance() {
87 | if (TEST_MODE) {
88 | setBalance('1.0');
89 | return;
90 | }
91 |
92 | if (bundlrInstance) {
93 | const bal = await bundlrInstance.getLoadedBalance();
94 | console.log('bal: ', utils.formatEther(bal.toString()));
95 | setBalance(utils.formatEther(bal.toString()));
96 | }
97 | }
98 |
99 | async function uploadFile(file) {
100 | if (TEST_MODE) {
101 | const mockTxId = `test-image-${Date.now()}`;
102 | return { data: { id: mockTxId } };
103 | }
104 |
105 | try {
106 | let tx = await bundlrInstance.uploader.upload(file, [
107 | { name: 'Content-Type', value: 'image/png' },
108 | ]);
109 | return tx;
110 | } catch (error) {
111 | toast.error(error.message || 'Something went wrong!');
112 | return null;
113 | }
114 | }
115 |
116 | async function uploadURI(file) {
117 | if (TEST_MODE) {
118 | const mockTxId = `test-uri-${Date.now()}`;
119 | return { data: { id: mockTxId } };
120 | }
121 |
122 | try {
123 | console.log(file);
124 | let tx = await bundlrInstance.uploader.upload(file, [
125 | { name: 'Content-Type', value: 'application/json' },
126 | ]);
127 | return tx;
128 | } catch (error) {
129 | toast.error(error.message || 'Something went wrong!');
130 | return null;
131 | }
132 | }
133 |
134 | return (
135 |
145 | {children}
146 |
147 | );
148 | };
149 |
150 | export default BundlrContextProvider;
151 |
152 | export const useBundler = () => {
153 | return useContext(BundlrContext);
154 | };
155 |
--------------------------------------------------------------------------------
/contracts/NFTMarketplace.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 |
3 | pragma solidity ^0.8.9;
4 |
5 | import "@openzeppelin/contracts/utils/Counters.sol";
6 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
7 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
8 |
9 | import "hardhat/console.sol";
10 |
11 | contract NFTMarketplace is ERC721URIStorage {
12 | using Counters for Counters.Counter;
13 | Counters.Counter private _tokenIds;
14 | Counters.Counter private _itemsSold;
15 |
16 | uint256 listingPrice = 0.025 ether;
17 | address payable owner;
18 |
19 | mapping(uint256 => MarketItem) private idToMarketItem;
20 |
21 | struct MarketItem {
22 | uint256 tokenId;
23 | address payable seller;
24 | address payable owner;
25 | uint256 price;
26 | bool sold;
27 | }
28 |
29 | event MarketItemCreated(
30 | uint256 indexed tokenId,
31 | address seller,
32 | address owner,
33 | uint256 price,
34 | bool sold
35 | );
36 |
37 | constructor() ERC721("Crypto Tokens", "CT") {
38 | owner = payable(msg.sender);
39 | }
40 |
41 | // Update the listing price of the Contract
42 | function updateListingPrice(uint _listingPrice) public payable {
43 | require(
44 | owner == msg.sender,
45 | "Only marketplace owner can update listing price."
46 | );
47 | listingPrice = _listingPrice;
48 | }
49 |
50 | // Returns the listing price of the contract
51 | function getListingPrice() public view returns (uint256) {
52 | return listingPrice;
53 | }
54 |
55 | // Mint the Token and list it in the marketplace
56 | function createToken(string memory tokenURI, uint256 price)
57 | public
58 | payable
59 | returns (uint)
60 | {
61 | _tokenIds.increment();
62 | uint256 newTokenId = _tokenIds.current();
63 |
64 | _mint(msg.sender, newTokenId);
65 | _setTokenURI(newTokenId, tokenURI);
66 | createMarketItem(newTokenId, price);
67 | return newTokenId;
68 | }
69 |
70 | // create market item
71 | function createMarketItem(uint256 tokenId, uint256 price) private {
72 | require(price > 0, "Price must be at least 1 wei");
73 | require(
74 | msg.value == listingPrice,
75 | "Price must be equal to listing price"
76 | );
77 |
78 | idToMarketItem[tokenId] = MarketItem(
79 | tokenId,
80 | payable(msg.sender),
81 | payable(address(this)),
82 | price,
83 | false
84 | );
85 |
86 | _transfer(msg.sender, address(this), tokenId);
87 | emit MarketItemCreated(
88 | tokenId,
89 | msg.sender,
90 | address(this),
91 | price,
92 | false
93 | );
94 | }
95 |
96 | // allow someone to resell
97 | function resellToken(uint256 tokenId, uint256 price) public payable {
98 | require(
99 | idToMarketItem[tokenId].owner == msg.sender,
100 | "Only item owner can perform this operation"
101 | );
102 | require(
103 | msg.value == listingPrice,
104 | "Price must be equal to listing price"
105 | );
106 | idToMarketItem[tokenId].sold = false;
107 | idToMarketItem[tokenId].price = price;
108 | idToMarketItem[tokenId].seller = payable(msg.sender);
109 | idToMarketItem[tokenId].owner = payable(address(this));
110 | _itemsSold.decrement();
111 |
112 | _transfer(msg.sender, address(this), tokenId);
113 | }
114 |
115 | // creating market sale
116 | function createMarketSale(uint256 tokenId) public payable {
117 | uint price = idToMarketItem[tokenId].price;
118 | address seller = idToMarketItem[tokenId].seller;
119 | require(
120 | msg.value == price,
121 | "Please submit the asking price in order to complete the purchase"
122 | );
123 | idToMarketItem[tokenId].owner = payable(msg.sender);
124 | idToMarketItem[tokenId].sold = true;
125 | idToMarketItem[tokenId].seller = payable(address(0));
126 | _itemsSold.increment();
127 | _transfer(address(this), msg.sender, tokenId);
128 | (bool ownersent, ) = payable(owner).call{value: listingPrice}("");
129 | require(ownersent, "Failed to send");
130 | (bool sellersent, ) = payable(seller).call{value: msg.value}("");
131 | require(sellersent, "Failed to send");
132 | }
133 |
134 | // Returns all unsold market items
135 | function fetchMarketItems() public view returns (MarketItem[] memory) {
136 | uint itemCount = _tokenIds.current();
137 | uint unsoldItemCount = _tokenIds.current() - _itemsSold.current();
138 | uint currentIndex = 0;
139 | // creating an empty array and provinding the size of the array that is unsold items as unsoldItemCount
140 | MarketItem[] memory items = new MarketItem[](unsoldItemCount);
141 | for (uint i = 0; i < itemCount; i++) {
142 | if (idToMarketItem[i + 1].owner == address(this)) {
143 | uint currentId = i + 1;
144 | MarketItem storage currentItem = idToMarketItem[currentId];
145 | items[currentIndex] = currentItem;
146 | currentIndex += 1;
147 | }
148 | }
149 | return items;
150 | }
151 |
152 | // Returns only items that a user has purchased
153 | function fetchMyNFTs() public view returns (MarketItem[] memory) {
154 | uint totalItemCount = _tokenIds.current();
155 | uint itemCount = 0;
156 | uint currentIndex = 0;
157 |
158 | for (uint i = 0; i < totalItemCount; i++) {
159 | if (idToMarketItem[i + 1].owner == msg.sender) {
160 | itemCount += 1;
161 | }
162 | }
163 | // creating an empty array and provinding the size of the array that is my item count as itemCount
164 | MarketItem[] memory items = new MarketItem[](itemCount);
165 | for (uint i = 0; i < totalItemCount; i++) {
166 | if (idToMarketItem[i + 1].owner == msg.sender) {
167 | uint currentId = i + 1;
168 | MarketItem storage currentItem = idToMarketItem[currentId];
169 | items[currentIndex] = currentItem;
170 | currentIndex += 1;
171 | }
172 | }
173 | return items;
174 | }
175 |
176 | // Returns only items a user has listed
177 | function fetchItemsListed() public view returns (MarketItem[] memory) {
178 | uint totalItemCount = _tokenIds.current();
179 | uint itemCount = 0;
180 | uint currentIndex = 0;
181 |
182 | for (uint i = 0; i < totalItemCount; i++) {
183 | if (idToMarketItem[i + 1].seller == msg.sender) {
184 | itemCount += 1;
185 | }
186 | }
187 | // creating an empty array and provinding the size of the array that is total items as itemCount
188 | MarketItem[] memory items = new MarketItem[](itemCount);
189 | for (uint i = 0; i < totalItemCount; i++) {
190 | if (idToMarketItem[i + 1].seller == msg.sender) {
191 | uint currentId = i + 1;
192 | MarketItem storage currentItem = idToMarketItem[currentId];
193 | items[currentIndex] = currentItem;
194 | currentIndex += 1;
195 | }
196 | }
197 | return items;
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/hardhat.config.js:
--------------------------------------------------------------------------------
1 | require('@nomicfoundation/hardhat-toolbox');
2 | require('dotenv').config();
3 |
4 | /** @type import('hardhat/config').HardhatUserConfig */
5 | module.exports = {
6 | solidity: '0.8.16',
7 | networks: {
8 | zkEVM: {
9 | url: 'https://rpc.cardona.zkevm-rpc.com',
10 | accounts: [process.env.PRIVATE_KEY],
11 | },
12 | },
13 | };
14 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | swcMinify: true,
5 | };
6 |
7 | module.exports = nextConfig;
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "08-nftmarketplace-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
11 | "format:check": "prettier --check \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
12 | "prepare": "husky"
13 | },
14 | "dependencies": {
15 | "@bundlr-network/client": "^0.7.8",
16 | "@openzeppelin/contracts": "^4.7.3",
17 | "axios": "^1.0.0",
18 | "bignumber.js": "^9.1.0",
19 | "dotenv": "^16.0.3",
20 | "ethers": "^5.7.1",
21 | "iconsax-react": "^0.0.8",
22 | "next": "12.3.1",
23 | "react": "18.2.0",
24 | "react-dom": "18.2.0",
25 | "react-toastify": "^9.0.8"
26 | },
27 | "devDependencies": {
28 | "@nomicfoundation/hardhat-toolbox": "2.0.0",
29 | "autoprefixer": "^10.4.12",
30 | "eslint": "8.24.0",
31 | "eslint-config-next": "12.3.1",
32 | "hardhat": "2.11.1",
33 | "husky": "^9.1.7",
34 | "lint-staged": "^15.5.2",
35 | "postcss": "^8.4.17",
36 | "prettier": "^3.5.3",
37 | "pretty-quick": "^4.1.1",
38 | "tailwindcss": "^3.1.8"
39 | },
40 | "lint-staged": {
41 | "**/*.{js,jsx,ts,tsx,json,css,md}": [
42 | "prettier --write"
43 | ]
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css';
2 | import { ToastContainer } from 'react-toastify';
3 | import 'react-toastify/dist/ReactToastify.css';
4 | import BundlrContextProvider from '../context/bundlrContext';
5 |
6 | function MyApp({ Component, pageProps }) {
7 | return (
8 |
9 |
10 |
11 |
23 |
24 |
25 | );
26 | }
27 |
28 | export default MyApp;
29 |
--------------------------------------------------------------------------------
/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default function handler(req, res) {
4 | res.status(200).json({ name: 'John Doe' });
5 | }
6 |
--------------------------------------------------------------------------------
/pages/createnft.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import { useRouter } from 'next/router';
3 | import React, { useRef, useState } from 'react';
4 | import { Footer, FundWallet, Header } from '../components';
5 | import { useBundler } from '../context/bundlrContext';
6 | import ContractABI from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json';
7 | import { toast } from 'react-toastify';
8 | import { ethers } from 'ethers';
9 |
10 | const TEST_MODE = true;
11 | const mainURL = `https://arweave.net/`;
12 |
13 | const Create = () => {
14 | const { initialiseBundlr, bundlrInstance, balance, uploadFile, uploadURI } = useBundler();
15 |
16 | const [nftDetails, setNftDetails] = useState({
17 | name: '',
18 | description: '',
19 | price: '',
20 | image: '',
21 | });
22 |
23 | const [file, setFile] = useState('');
24 |
25 | const [loading, setLoading] = useState(false);
26 |
27 | const router = useRouter();
28 |
29 | const dataRef = useRef();
30 |
31 | function triggerOnChange() {
32 | dataRef.current.click();
33 | }
34 |
35 | async function handleFileChange(e) {
36 | const uploadedFile = e.target.files[0];
37 | if (!uploadedFile) return;
38 | setNftDetails({ ...nftDetails, image: uploadedFile });
39 | let reader = new FileReader();
40 | reader.onload = function () {
41 | if (reader.result) {
42 | setFile(Buffer.from(reader.result));
43 | }
44 | };
45 | reader.readAsArrayBuffer(uploadedFile);
46 | }
47 |
48 | const getContract = async () => {
49 | const provider = new ethers.providers.Web3Provider(window.ethereum);
50 |
51 | const signer = provider.getSigner();
52 |
53 | let contract = new ethers.Contract(
54 | process.env.NEXT_PUBLIC_CONTRACT_ADDRESS,
55 | ContractABI.abi,
56 | signer
57 | );
58 | return contract;
59 | };
60 |
61 | const handleUpload = async () => {
62 | const { name, description, price, image } = nftDetails;
63 | if (name === '') {
64 | toast.error('Please provide name for NFT');
65 | } else if (description === '') {
66 | toast.error('Please provide description for NFT');
67 | } else if (price === '') {
68 | toast.error('Please provide Price');
69 | } else if (image === '') {
70 | toast.error('Please Select Image');
71 | } else {
72 | setLoading(true);
73 | const url = await uploadFile(file);
74 | uploadToArweave(url.data.id);
75 | }
76 | };
77 |
78 | const uploadToArweave = async (url) => {
79 | const { name, description } = nftDetails;
80 |
81 | const data = JSON.stringify({
82 | name,
83 | description,
84 | image: url,
85 | });
86 |
87 | const tokenURI = await uploadURI(data);
88 |
89 | mintNFT(tokenURI.data.id);
90 | };
91 |
92 | const mintNFT = async (tokenURI) => {
93 | try {
94 | if (TEST_MODE) {
95 | // In test mode, skip actual contract interaction
96 | setLoading(false);
97 | setNftDetails({
98 | name: '',
99 | description: '',
100 | price: '',
101 | image: '',
102 | });
103 | setFile('');
104 | toast.success('Test Mode: NFT Minted Successfully');
105 | router.push('/dashboard');
106 | return;
107 | }
108 |
109 | const contract = await getContract();
110 |
111 | const price = ethers.utils.parseUnits(nftDetails.price, 'ether');
112 |
113 | let listingPrice = await contract.getListingPrice();
114 | listingPrice = listingPrice.toString();
115 |
116 | let transaction = await contract.createToken(tokenURI, price, {
117 | value: listingPrice,
118 | });
119 | await transaction.wait();
120 |
121 | setLoading(false);
122 |
123 | setNftDetails({
124 | name: '',
125 | description: '',
126 | price: '',
127 | image: '',
128 | });
129 |
130 | setFile('');
131 |
132 | toast.success('Minted Successfully');
133 |
134 | router.push('/dashboard');
135 | } catch (error) {
136 | console.error(error);
137 | if (TEST_MODE) {
138 | setLoading(false);
139 | setNftDetails({
140 | name: '',
141 | description: '',
142 | price: '',
143 | image: '',
144 | });
145 | setFile('');
146 | toast.success('Test Mode: NFT Minted Successfully');
147 | router.push('/dashboard');
148 | } else {
149 | toast.error('Something went wrong', error);
150 | setLoading(false);
151 | }
152 | }
153 | };
154 |
155 | if (!bundlrInstance) {
156 | return (
157 |
158 |
Let's initialise Bundlr now 💱
159 |
165 | Initialise Bundlr 💸
166 |
167 |
168 | );
169 | }
170 |
171 | if (!balance || (Number(balance) <= 0 && !balance) || Number(balance) <= 0.0005) {
172 | return (
173 |
174 |
175 | Oops! Before Publishing NFT Please Add Some Funds.🪙
176 |
177 |
178 |
179 | );
180 | }
181 |
182 | return (
183 |
184 |
185 |
Create NFT || DesiNFT
186 |
187 |
188 |
189 |
190 |
191 |
Create NFT
192 |
193 |
194 |
195 |
199 |
206 | {nftDetails.image ? (
207 |
208 |
214 |
215 | ) : (
216 |
217 |
Please Select Here to See Your File Preview
218 |
219 | )}
220 |
221 |
222 |
223 |
224 | Name
225 | setNftDetails({ ...nftDetails, name: e.target.value })}
231 | />
232 |
233 |
234 |
235 | Description
236 |
244 |
245 |
246 | Price
247 | setNftDetails({ ...nftDetails, price: e.target.value })}
254 | />
255 |
256 |
262 | {loading ? 'Please Wait...' : 'Create'}
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 | );
271 | };
272 |
273 | export default Create;
274 |
--------------------------------------------------------------------------------
/pages/dashboard.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import React, { useEffect, useState } from 'react';
3 | import { Footer, Header } from '../components';
4 | import ContractABI from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json';
5 | import { toast } from 'react-toastify';
6 | import axios from 'axios';
7 | import { ethers } from 'ethers';
8 | import { truncateEthAddress } from '../utils/truncAddress';
9 | import { useRouter } from 'next/router';
10 |
11 | const TEST_MODE = true;
12 | const DEFAULT_TEST_IMAGE = '/logo.png';
13 | const mainURL = `https://arweave.net/`;
14 |
15 | const Dashboard = () => {
16 | const [nfts, setNts] = useState([]);
17 | const [loading, setLoading] = useState(false);
18 |
19 | const router = useRouter();
20 |
21 | const getContract = async () => {
22 | const provider = new ethers.providers.Web3Provider(window.ethereum);
23 |
24 | let contract = new ethers.Contract(
25 | process.env.NEXT_PUBLIC_CONTRACT_ADDRESS,
26 | ContractABI.abi,
27 | provider
28 | );
29 | return contract;
30 | };
31 |
32 | const getNfts = async () => {
33 | try {
34 | if (TEST_MODE) {
35 | const mockNfts = [
36 | {
37 | price: '0.1',
38 | tokenId: 1,
39 | seller: '0x796946405d715e384CA5D87a73E79C44aC8acbB7',
40 | owner: '0x0000000000000000000000000000000000000000',
41 | image: 'test-image-1',
42 | name: 'Test NFT 1',
43 | description: 'This is a test NFT in test mode',
44 | tokenURI: 'test-uri-1',
45 | },
46 | {
47 | price: '0.2',
48 | tokenId: 2,
49 | seller: '0x796946405d715e384CA5D87a73E79C44aC8acbB7',
50 | owner: '0x0000000000000000000000000000000000000000',
51 | image: 'test-image-2',
52 | name: 'Test NFT 2',
53 | description: 'Another test NFT in test mode',
54 | tokenURI: 'test-uri-2',
55 | },
56 | ];
57 | setNts(mockNfts);
58 | setLoading(true);
59 | return;
60 | }
61 |
62 | const contract = await getContract();
63 |
64 | const data = await contract.fetchMarketItems();
65 |
66 | const items = await Promise.all(
67 | data?.map(async (i) => {
68 | const tokenURI = await contract.tokenURI(i.tokenId);
69 | const meta = await axios.get(mainURL + tokenURI);
70 | let price = ethers.utils.formatUnits(i.price.toString(), 'ether');
71 |
72 | let item = {
73 | price,
74 | tokenId: i.tokenId.toNumber(),
75 | seller: i.seller,
76 | owner: i.owner,
77 | image: meta.data.image,
78 | name: meta.data.name,
79 | description: meta.data.description,
80 | tokenURI,
81 | };
82 | return item;
83 | })
84 | );
85 | setNts(items);
86 | setLoading(true);
87 | } catch (error) {
88 | console.error(error);
89 | toast.error('Something went wrong');
90 | if (!TEST_MODE) {
91 | const mockNfts = [
92 | {
93 | price: '0.1',
94 | tokenId: 1,
95 | seller: '0x796946405d715e384CA5D87a73E79C44aC8acbB7',
96 | owner: '0x0000000000000000000000000000000000000000',
97 | image: 'test-image-1',
98 | name: 'Test NFT 1',
99 | description: 'This is a test NFT in fallback mode',
100 | tokenURI: 'test-uri-1',
101 | },
102 | ];
103 | setNts(mockNfts);
104 | setLoading(true);
105 | }
106 | }
107 | };
108 |
109 | useEffect(() => {
110 | getNfts();
111 | }, []);
112 |
113 | if (!loading)
114 | return (
115 |
116 |
117 |
Loading...
118 |
119 | );
120 |
121 | return (
122 |
123 |
124 |
Dashboard || DesiNFT
125 |
126 |
127 |
128 |
129 | {!nfts.length ? (
130 |
131 |
No NFTs in Marketplace
132 |
133 | ) : (
134 |
135 |
Hot NFTs
136 |
137 | {nfts?.map((nft, i) => (
138 |
139 |
{
142 | router.push({
143 | pathname: '/nft-details',
144 | query: nft,
145 | });
146 | }}
147 | >
148 |
149 |
156 |
157 |
158 | {
161 | router.push({
162 | pathname: '/nft-details',
163 | query: nft,
164 | });
165 | }}
166 | >
167 | View Details
168 |
169 |
170 |
171 |
172 |
173 |
{nft.name}
174 |
175 |
176 |
Creator
177 |
178 | {truncateEthAddress(nft.seller)}
179 |
180 |
181 |
182 |
Price
183 |
{nft.price} ETH
184 |
185 |
186 |
187 |
188 |
189 | ))}
190 |
191 |
192 | )}
193 |
194 |
195 | );
196 | };
197 |
198 | export default Dashboard;
199 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import Image from 'next/image';
3 | import data from '../constants/mock-nft.json';
4 | import mockartist from '../constants/mock-artist.json';
5 | import { useEffect, useState } from 'react';
6 | import { useRouter } from 'next/router';
7 | import { Footer, Header } from '../components';
8 |
9 | export default function Home() {
10 | const [isWalletConnected, setIsWalletConnected] = useState(false);
11 |
12 | const [addr, setAddr] = useState('');
13 |
14 | const router = useRouter();
15 |
16 | const connectWallet = async () => {
17 | try {
18 | const { ethereum } = window;
19 |
20 | if (!ethereum) {
21 | alert('Please Install MetaMask');
22 | return;
23 | }
24 | const accounts = await ethereum.request({
25 | method: 'eth_requestAccounts',
26 | });
27 | setIsWalletConnected(true);
28 | localStorage.setItem('walletAddress', accounts[0]);
29 | router.push('/dashboard');
30 | } catch (error) {
31 | console.error(error);
32 | }
33 | };
34 |
35 | useEffect(() => {
36 | const addr = localStorage.getItem('walletAddress');
37 | setAddr(addr);
38 | }, []);
39 |
40 | return (
41 |
42 |
43 |
DesiNFT
44 |
45 |
46 |
47 |
48 |
49 | {isWalletConnected || addr ?
: null}
50 |
51 |
52 | {/* HeroSection */}
53 |
54 |
55 |
56 | Discover New Era of Crypto Currencies
57 |
58 |
59 | DesiNFT is the premier marketplace for NFT, which are digital items you can truly own.
60 | Digital items have existed for a long time, but never like this.
61 |
62 | {addr ? (
63 |
68 | Create an NFT
69 |
70 | ) : (
71 |
76 | Connect Wallet
77 |
78 | )}
79 |
80 |
81 |
82 |
89 |
90 |
Krishna
91 |
92 |
93 |
98 |
99 |
Prakruti Thakor
100 |
0xCF9a...dbEc
101 |
102 |
103 |
104 |
Current Price
105 |
0.99 ETH
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 | Create and sell your NFTs
115 |
116 |
117 | {data.map((item) => (
118 |
122 |
123 |
124 |
125 |
{item.title}
126 |
{item.description}
127 |
128 | ))}
129 |
130 |
131 |
132 |
133 | Featured Artist
134 |
135 | {mockartist.map((data) => (
136 |
140 |
141 |
147 |
153 |
154 |
155 |
{data.name}
156 |
Price: {data.price}
157 |
158 |
159 | ))}
160 |
161 |
162 |
163 | {/* Community */}
164 |
165 |
166 |
Create Your Own NFT!
167 |
168 | We have a large scale group to support each other in this game, Join us to get the
169 | news as soon as possible and follow our latest announcements!
170 |
171 |
172 | Join Community Now
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 | );
181 | }
182 |
--------------------------------------------------------------------------------
/pages/nft-details.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import { useRouter } from 'next/router';
3 | import React, { useEffect, useState } from 'react';
4 | import { Footer, Header } from '../components';
5 | import { truncateEthAddress } from '../utils/truncAddress';
6 | import ContractABI from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json';
7 | import { toast } from 'react-toastify';
8 | import { ethers } from 'ethers';
9 |
10 | const TEST_MODE = true;
11 | const DEFAULT_TEST_IMAGE = '/logo.png';
12 |
13 | const mainURL = `https://arweave.net/`;
14 |
15 | const NFTDetails = () => {
16 | const [isLoading, setIsLoading] = useState(true);
17 |
18 | const router = useRouter();
19 |
20 | const [addr, setAddr] = useState('');
21 |
22 | const [nft, setNft] = useState({
23 | price: '',
24 | tokenId: '',
25 | seller: '',
26 | owner: '',
27 | image: '',
28 | description: '',
29 | tokenURI: '',
30 | });
31 |
32 | useEffect(() => {
33 | if (!router.isReady) return;
34 |
35 | setNft(router?.query);
36 |
37 | setIsLoading(false);
38 | }, [router.isReady]);
39 |
40 | useEffect(() => {
41 | const addr = localStorage.getItem('walletAddress');
42 | setAddr(addr);
43 | }, []);
44 |
45 | const getContract = async () => {
46 | const provider = new ethers.providers.Web3Provider(window.ethereum);
47 |
48 | const signer = provider.getSigner();
49 |
50 | let contract = new ethers.Contract(
51 | process.env.NEXT_PUBLIC_CONTRACT_ADDRESS,
52 | ContractABI.abi,
53 | signer
54 | );
55 | return contract;
56 | };
57 |
58 | const buyNft = async (n) => {
59 | try {
60 | if (TEST_MODE) {
61 | toast.success('Test mode: NFT purchased successfully!');
62 | await router.push('/dashboard');
63 | return;
64 | }
65 |
66 | const contract = await getContract();
67 | const price = ethers.utils.parseUnits(n.price.toString(), 'ether');
68 | let tx = await contract.createMarketSale(n.tokenId, { value: price });
69 | await tx.wait();
70 | toast.success(`Bought NFT🎉`);
71 | await router.push('/dashboard');
72 | } catch (error) {
73 | console.log(error);
74 | if (TEST_MODE) {
75 | toast.success('Test mode: NFT purchased successfully!');
76 | await router.push('/dashboard');
77 | } else {
78 | toast.error(`You Can't Buy This Look At the Price 😂 ${error}`);
79 | }
80 | }
81 | };
82 |
83 | const reSellNFT = async (nft) => {
84 | router.push(`sellnft?tokenId=${nft.tokenId}&tokenURI=${nft.tokenURI}`);
85 | };
86 |
87 | return (
88 |
89 |
90 |
{nft.name} || DesiNFT
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
105 |
106 |
107 |
108 |
109 | #{nft.tokenId} {nft.name}
110 |
111 |
description: {nft.description}
112 |
113 |
Price
114 |
{nft.price} ETH
115 |
116 |
117 |
Owner
118 |
119 | {truncateEthAddress(nft.owner)}
120 |
121 |
122 |
123 | {nft.seller && nft.seller.startsWith('0x0') ? null : (
124 |
125 |
Seller
126 |
127 | {truncateEthAddress(nft.seller)}
128 |
129 |
130 | )}
131 |
132 |
133 |
Blockchain
134 |
{TEST_MODE ? 'Test Mode' : 'Ethereum ⟠'}
135 |
136 |
137 |
{
140 | addr === nft.owner?.toLocaleLowerCase() ? reSellNFT(nft) : buyNft(nft);
141 | }}
142 | >
143 | {addr === nft.owner?.toLocaleLowerCase() ? 'Sell NFT' : 'Buy NFT'}
144 |
145 |
146 |
147 |
148 |
149 |
150 | );
151 | };
152 |
153 | export default NFTDetails;
154 |
--------------------------------------------------------------------------------
/pages/profile.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import { useRouter } from 'next/router';
3 | import React, { useEffect, useState } from 'react';
4 | import { toast } from 'react-toastify';
5 | import { Footer, Header, MyNFTContainer } from '../components';
6 | import ContractABI from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json';
7 | import { ethers } from 'ethers';
8 | import axios from 'axios';
9 |
10 | const TEST_MODE = true;
11 | const mainURL = `https://arweave.net/`;
12 |
13 | const Profile = () => {
14 | const [nfts, setNts] = useState([]);
15 | const [loading, setLoading] = useState(false);
16 | const router = useRouter();
17 |
18 | useEffect(() => {
19 | getNfts();
20 | }, []);
21 |
22 | const getContract = async () => {
23 | const provider = new ethers.providers.Web3Provider(window.ethereum);
24 |
25 | const signer = provider.getSigner();
26 |
27 | let contract = new ethers.Contract(
28 | process.env.NEXT_PUBLIC_CONTRACT_ADDRESS,
29 | ContractABI.abi,
30 | signer
31 | );
32 | return contract;
33 | };
34 |
35 | const getNfts = async () => {
36 | try {
37 | if (TEST_MODE) {
38 | // In test mode, create mock owned NFTs
39 | const mockNfts = [
40 | {
41 | price: '0.1',
42 | tokenId: 3,
43 | seller: '0x0000000000000000000000000000000000000000',
44 | owner:
45 | localStorage.getItem('walletAddress') || '0x796946405d715e384CA5D87a73E79C44aC8acbB7',
46 | image: 'test-image-3',
47 | name: 'My Test NFT 1',
48 | description: 'This is my test NFT in test mode',
49 | tokenURI: 'test-uri-3',
50 | },
51 | {
52 | price: '0.3',
53 | tokenId: 4,
54 | seller: '0x0000000000000000000000000000000000000000',
55 | owner:
56 | localStorage.getItem('walletAddress') || '0x796946405d715e384CA5D87a73E79C44aC8acbB7',
57 | image: 'test-image-4',
58 | name: 'My Test NFT 2',
59 | description: 'Another one of my test NFTs in test mode',
60 | tokenURI: 'test-uri-4',
61 | },
62 | ];
63 | setNts(mockNfts);
64 | setLoading(true);
65 | return;
66 | }
67 |
68 | const contract = await getContract();
69 |
70 | const data = await contract.fetchMyNFTs();
71 |
72 | const items = await Promise.all(
73 | data?.map(async (i) => {
74 | const tokenURI = await contract.tokenURI(i.tokenId);
75 |
76 | const meta = await axios.get(mainURL + tokenURI);
77 |
78 | let price = ethers.utils.formatUnits(i.price.toString(), 'ether');
79 |
80 | let item = {
81 | price,
82 | tokenId: i.tokenId.toNumber(),
83 | seller: i.seller,
84 | owner: i.owner,
85 | image: meta.data.image,
86 | name: meta.data.name,
87 | description: meta.data.description,
88 | tokenURI,
89 | };
90 | return item;
91 | })
92 | );
93 | setNts(items);
94 | setLoading(true);
95 | } catch (error) {
96 | console.error(error);
97 | if (TEST_MODE) {
98 | const mockNfts = [
99 | {
100 | price: '0.1',
101 | tokenId: 3,
102 | seller: '0x0000000000000000000000000000000000000000',
103 | owner:
104 | localStorage.getItem('walletAddress') || '0x796946405d715e384CA5D87a73E79C44aC8acbB7',
105 | image: 'test-image-3',
106 | name: 'My Test NFT 1',
107 | description: 'This is my test NFT in test mode',
108 | tokenURI: 'test-uri-3',
109 | },
110 | ];
111 | setNts(mockNfts);
112 | setLoading(true);
113 | } else {
114 | toast.error('Something went wrong', error);
115 | }
116 | }
117 | };
118 |
119 | return (
120 |
121 |
122 |
My Profile || DesiNFT
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
My NFTs
134 |
135 |
136 |
137 |
138 | {nfts?.map((nft, i) => (
139 |
140 | ))}
141 |
142 |
143 |
144 |
145 | );
146 | };
147 |
148 | export default Profile;
149 |
--------------------------------------------------------------------------------
/pages/sellnft.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import Head from 'next/head';
3 | import { useRouter } from 'next/router';
4 | import React, { useEffect, useState } from 'react';
5 | import { Footer, Header } from '../components';
6 | import ContractABI from '../artifacts/contracts/NFTMarketplace.sol/NFTMarketplace.json';
7 | import { toast } from 'react-toastify';
8 | import { ethers } from 'ethers';
9 |
10 | const TEST_MODE = true;
11 | const DEFAULT_TEST_IMAGE = '/logo.png';
12 |
13 | const mainURL = `https://arweave.net/`;
14 |
15 | const SellNft = () => {
16 | const [isLoading, setIsLoading] = useState(true);
17 | const [btnLoading, setBtnLoading] = useState(false);
18 |
19 | const [formData, setFormData] = useState({ price: '', image: '' });
20 |
21 | const router = useRouter();
22 |
23 | const { tokenId, tokenURI } = router.query;
24 |
25 | useEffect(() => {
26 | if (!router.isReady) return;
27 | fetchNFT();
28 | setIsLoading(false);
29 | }, [router.isReady]);
30 |
31 | const fetchNFT = async () => {
32 | if (TEST_MODE && tokenURI?.startsWith('test-')) {
33 | setFormData((state) => ({
34 | ...state,
35 | image: `test-image-${Date.now()}`,
36 | price: '0.1',
37 | }));
38 | return;
39 | }
40 |
41 | try {
42 | const { data } = await axios.get(mainURL + tokenURI);
43 | setFormData((state) => ({
44 | ...state,
45 | image: data.image,
46 | price: data.price,
47 | }));
48 | } catch (error) {
49 | console.error('Error fetching NFT:', error);
50 | if (TEST_MODE) {
51 | // In test mode, provide fallback data if fetch fails
52 | setFormData((state) => ({
53 | ...state,
54 | image: `test-image-${Date.now()}`,
55 | price: '0.1',
56 | }));
57 | }
58 | }
59 | };
60 |
61 | const getContract = async () => {
62 | const provider = new ethers.providers.Web3Provider(window.ethereum);
63 |
64 | const signer = provider.getSigner();
65 |
66 | let contract = new ethers.Contract(
67 | process.env.NEXT_PUBLIC_CONTRACT_ADDRESS,
68 | ContractABI.abi,
69 | signer
70 | );
71 | return contract;
72 | };
73 |
74 | const resellToken = async () => {
75 | try {
76 | if (!formData.price) {
77 | toast.error('Please Enter Your Price For Selling NFT!');
78 | return;
79 | }
80 |
81 | if (TEST_MODE) {
82 | setBtnLoading(true);
83 | setTimeout(() => {
84 | setBtnLoading(false);
85 | setFormData({
86 | price: '',
87 | image: '',
88 | });
89 | toast.success('Test Mode: NFT Resold Successfully');
90 | router.push('/dashboard');
91 | }, 1000);
92 | return;
93 | }
94 |
95 | setBtnLoading(true);
96 | const contract = await getContract();
97 | let listingPrice = await contract.getListingPrice();
98 | const price = ethers.utils.parseUnits(formData.price.toString(), 'ether');
99 | const txt = await contract.resellToken(tokenId, price, {
100 | value: listingPrice,
101 | });
102 | await txt.wait();
103 |
104 | setBtnLoading(false);
105 |
106 | setFormData({
107 | price: '',
108 | image: '',
109 | });
110 |
111 | toast.success('Resell Successfully');
112 |
113 | await router.push('/dashboard');
114 | } catch (error) {
115 | console.log(error);
116 | if (TEST_MODE) {
117 | // Even if there's an error in test mode, simulate success
118 | setBtnLoading(false);
119 | toast.success('Test Mode: NFT Resold Successfully');
120 | await router.push('/dashboard');
121 | } else {
122 | setBtnLoading(false);
123 | toast.error(`Something went wrong! ${error}`);
124 | }
125 | }
126 | };
127 |
128 | return (
129 |
130 |
131 |
Sell Token || DesiNFT
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
150 |
151 |
152 | Price
153 | setFormData({ ...formData, price: e.target.value })}
160 | />
161 |
162 |
167 | {btnLoading ? 'Re Selling NFT' : 'Sell NFT'}
168 |
169 | {TEST_MODE && (
170 |
171 | Test Mode Active: No actual blockchain transactions will occur
172 |
173 | )}
174 |
175 |
176 |
177 |
178 |
179 | );
180 | };
181 |
182 | export default SellNft;
183 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/favicon.ico
--------------------------------------------------------------------------------
/public/images/main1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/main1.png
--------------------------------------------------------------------------------
/public/images/main2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/main2.png
--------------------------------------------------------------------------------
/public/images/main3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/main3.png
--------------------------------------------------------------------------------
/public/images/main4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/main4.png
--------------------------------------------------------------------------------
/public/images/mock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/mock.png
--------------------------------------------------------------------------------
/public/images/mockcreator.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/mockcreator.jpg
--------------------------------------------------------------------------------
/public/images/nft2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/nft2.png
--------------------------------------------------------------------------------
/public/images/nft3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/nft3.png
--------------------------------------------------------------------------------
/public/images/placeholder1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/placeholder1.png
--------------------------------------------------------------------------------
/public/images/placeholder2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/placeholder2.png
--------------------------------------------------------------------------------
/public/images/placeholder3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/placeholder3.png
--------------------------------------------------------------------------------
/public/images/placeholder4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/placeholder4.png
--------------------------------------------------------------------------------
/public/images/wallet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/images/wallet.png
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/public/logo.png
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/scripts/deploy.js:
--------------------------------------------------------------------------------
1 | const { ethers } = require('hardhat');
2 |
3 | async function main() {
4 | const contractFactory = await ethers.getContractFactory('NFTMarketplace');
5 | const contractDeploy = await contractFactory.deploy();
6 |
7 | await contractDeploy.deployed();
8 |
9 | console.log(`Contract Deployed at: ${contractDeploy.address}`);
10 | }
11 |
12 | main()
13 | .then(() => process.exit(0))
14 | .catch((error) => {
15 | console.error(error);
16 | process.exit(1);
17 | });
18 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ayush-that/HackOn-Blocks-2024/684e64285be2c19290146eee8b293bebc22eaa5f/styles/Home.module.css
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700;800&display=swap');
6 |
7 | html,
8 | body {
9 | padding: 0;
10 | margin: 0;
11 | font-family:
12 | 'Poppins',
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | 'Segoe UI',
16 | Roboto,
17 | sans-serif;
18 | }
19 |
20 | a {
21 | color: inherit;
22 | text-decoration: none;
23 | }
24 |
25 | * {
26 | box-sizing: border-box;
27 | scroll-behavior: smooth;
28 | }
29 |
30 | @media (prefers-color-scheme: dark) {
31 | html {
32 | color-scheme: dark;
33 | }
34 | body {
35 | color: white;
36 | background: linear-gradient(138.18deg, #110000 -7.24%, #000000 48.68%, #000800 105.61%);
37 | }
38 | }
39 |
40 | @media (max-width: 768px) {
41 | .medium {
42 | grid-template-columns: 360px auto;
43 | }
44 | }
45 |
46 | .footer {
47 | grid-template-columns: 393px auto auto auto;
48 | }
49 |
50 | @layer components {
51 | .title {
52 | @apply text-white;
53 | }
54 |
55 | .desc {
56 | @apply text-[#ADB9C7];
57 | }
58 | }
59 |
60 | .nav_links {
61 | margin-block-start: 0;
62 | margin-block-end: 0;
63 | padding-inline-start: 0;
64 | }
65 |
66 | .tx {
67 | resize: none;
68 | overflow: hidden;
69 | }
70 |
71 | .tx::-webkit-scrollbar {
72 | display: none;
73 | }
74 |
75 | .profile {
76 | background: linear-gradient(138.18deg, #216cfd -7.24%, #002ecf 105.61%);
77 | }
78 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
4 | theme: {
5 | extend: {},
6 | fontFamily: {
7 | sans: ['Poppins', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'],
8 | body: ['Poppins', '-apple-system', 'BlinkMacSystemFont', 'Segoe UI', 'Roboto', 'sans-serif'],
9 | },
10 | screens: {
11 | '2xl': { max: '1535px' },
12 | xl: { max: '1279px' },
13 | lg: { max: '1023px' },
14 | md: { max: '768px' },
15 | sm: { max: '639px' },
16 | ssm: { max: '360px' },
17 | },
18 | },
19 | plugins: [],
20 | corePlugins: {
21 | preflight: false,
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/test/nftmarketplace-test.js:
--------------------------------------------------------------------------------
1 | const { ethers } = require('hardhat');
2 |
3 | describe('NFTMarketplace App', async function () {
4 | it('Should create Marketplace', async function () {
5 | const contractFactory = await ethers.getContractFactory('NFTMarketplace');
6 | const contractDeploy = await contractFactory.deploy();
7 |
8 | await contractDeploy.deployed();
9 |
10 | console.log(contractDeploy.address);
11 |
12 | let listingPrice = await contractDeploy.getListingPrice();
13 |
14 | listingPrice = await listingPrice.toString();
15 |
16 | const auctionPrice = ethers.utils.parseUnits('1', 'ether');
17 |
18 | await contractDeploy.createToken('mytoken1', auctionPrice, {
19 | value: listingPrice,
20 | });
21 |
22 | await contractDeploy.createToken('mytoken2', auctionPrice, {
23 | value: listingPrice,
24 | });
25 |
26 | const [_, buyerAddress] = await ethers.getSigners();
27 |
28 | await contractDeploy.connect(buyerAddress).createMarketSale(1, { value: auctionPrice });
29 |
30 | await contractDeploy
31 | .connect(buyerAddress)
32 | .resellToken(1, auctionPrice, { value: listingPrice });
33 |
34 | unsoldItems = await contractDeploy.fetchMarketItems();
35 | unsoldItems = await Promise.all(
36 | unsoldItems.map(async (i) => {
37 | const tokenUri = await contractDeploy.tokenURI(i.tokenId);
38 | let item = {
39 | price: i.price.toString(),
40 | tokenId: i.tokenId.toString(),
41 | seller: i.seller,
42 | owner: i.owner,
43 | tokenUri,
44 | };
45 | return item;
46 | })
47 | );
48 | console.log(`Unsold items: ${unsoldItems}`);
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/utils/truncAddress.js:
--------------------------------------------------------------------------------
1 | export const truncateRegex = /^(0x[a-zA-Z0-9]{4})[a-zA-Z0-9]+([a-zA-Z0-9]{4})$/;
2 |
3 | export const truncateEthAddress = (addr) => {
4 | const match = addr?.match(truncateRegex);
5 | if (!match) return addr;
6 | return `${match[1]}…${match[2]}`;
7 | };
8 |
--------------------------------------------------------------------------------