# Hedera 0x NFT Marketplace An example web app that demonstrates how to interact with 0x protocol on the Hedera network using [Metamask](https://metamask.io/). All NFT on marketplace to be well displayed should be compilante with [HIP-412](https://hips.hedera.com/hip/hip-412). On marketplace sellers can sell NFTS on ``BUY NOW`` or ``DUTCH AUCTION`` orders. To purchase buyers can buy order straight-forward from seller or either make an offer. Marketplace (mostly) supports multi currencies using ``HBAR`` and ``Custom Fungible Token``. ![UI_welcome-screen](src/assets/images/readme/marketplace_welcome-screen.png) #### Built With - [Hedera Hashgraph](https://www.hedera.com/) - The enterprise-grade public network - [Hedera Mirror Node](https://docs.hedera.com/guides/core-concepts/mirror-nodes/) - provides a way to store and cost-effectively query historical data from the public ledger - [Hedera Hashgraph JavaScript SDK](https://github.com/hashgraph/hedera-sdk-js) - The easiest way to use Hedera in JavaScript - [0x protocol](https://www.0x.org/) - Is an open protocol that enables the peer-to-peer exchange of assets on the Ethereum blockchain. - [Node.JS](https://nodejs.org) - Node.js is an open-source, cross-platform, back-end JavaScript runtime environment - [React.JS](https://reactjs.org/) - A fast, unopinionated web framework for node.js - [TypeScript](https://www.typescriptlang.org/) - Strongly typed programming language that builds on JavaScript - [SASS](https://sass-lang.com/) - The most mature, stable, and powerful professional-grade CSS extension language in the world. - [Tailwind CSS](https://tailwindcss.com/) - A utility-first CSS framework. - [BabelJS](https://babeljs.io/) - A real-time client-to-server framework for node.js - [WebPack](https://webpack.js.org/) - Static module bundler for modern JavaScript applications ## Created by ### Product Manager - [Ashe Oro](https://github.com/Ashe-Oro) ### Front-end team - [Norbert Kulus](https://github.com/norbert-kulus-arianelabs) - [Patryk Matyjasiak](https://github.com/PatMat-Ariane) ### Back-end team - [Bartosz Solka](https://github.com/BartoszSolkaArianelabs) - [Piotr Swierzy](https://github.com/se7enarianelabs) ## Prerequisites This demo assumes that you are familiar with [Node.JS](https://nodejs.org) based applications and using [React.JS](https://reactjs.org/) UI framework. Also, you need to be familiar with using [Hedera Mirror Node](https://docs.hedera.com/guides/core-concepts/mirror-nodes/), [Hedera Hashgraph JavaScript SDK](https://github.com/hashgraph/hedera-sdk-js). Before first connection via [Metamask](https://metamask.io/) to marketplace, user has to connect with ECDSA Hedera account to HASHGRAPH.IO jRPC. ## Getting Started You can clone this repository by running the following command: ``` git clone https://github.com/hashgraph/hedera-nft-marketplace ``` Copy the `.env.sample` file and rename the copy to `.env` Then populate the newly renamed `.env` file with your data: ``` #### HEDERA CONNECTION DATA HEDERA_NETWORK= HEDERA_MIRROR_NODE_API_VERSION= CHAIN_ID= #### ORDERBOOK CONNECTION DATA API_URL= #### TOKENS DATA CONTRACT_0x= ERC20_FT= ERC20_HBAR= AUSDC_CONTRACT_ID= ### IPFS DATA IPFS_GATEWAYS= ## HEDERA RPC DATA HEDERA_RPC_URL= HEDERA_NATIVE_CURRENCY_NAME= HEDERA_NATIVE_CURRENCY_SYMBOL= HEDERA_NATIVE_CURRENCY_DECIMALS= HEDERA_CHAIN_NAME= TOKEN_PROXY_CONTRACT_ID= MARKETPLACE_FOOTER_LINK_URL= MARKETPLACE_FOOTER_LINK_NAME= ``` - The `HEDERA_NETWORK` holds Hedera Network type (testnet, mainnet) - The `HEDERA_MIRROR_NODE_API_VERSION` is a variable for storing the mirror node API version - The `CHAIN_ID` is a variable for storing jRPC chain ID - The `API_URL` is your backend orderbook API url - The `CONTRACT_0x` storing 0x contract ID for executing operations - The `AUSDC_CONTRACT_ID` storing AUSDC contract ID for executing operations - The `ERC20_FT` is for storing the ``Custom Fungible Token`` address in solidity format - The `ERC20_HBAR` is for storing the HBAR address in solidity format - The `IPFS_GATEWAYS` stores array of ipfs gateways. The ``{cid}`` prase in each string will be replaced with real NFT metadata CID. - The `HEDERA_RPC_URL` stores url address to Hedera RPC. - The `HEDERA_NATIVE_CURRENCY_NAME` holds currency name showed in MetaMask. - The `HEDERA_NATIVE_CURRENCY_SYMBOL` holds currency symbol showed in Metamask. - The `HEDERA_NATIVE_CURRENCY_DECIMALS` is for defining the decimals for native currency token. For HBAR it is 18. - The `TOKEN_PROXY_CONTRACT_ID` storing Hedera based ID for proxy contract - The `MARKETPLACE_FOOTER_LINK_URL` is for the external link in footer. - The `MARKETPLACE_FOOTER_LINK_NAME` is for the external link name in footer. After downloading and setting up our environment, we'll install our packages via [npm](https://docs.npmjs.com/about-npm/). ``` npm install ``` If installing the dependencies was successful, now try to run the app ``` npm run start ``` ## Application design #### Page Components (*src/pages*) 1. **Analytics** Page Component : On this page user can take a look on collections which has/had active orders. Analytics page shows ``total volume``, ``% change``, ``floor price`` and ``total sales`` for each collections. 2. **Auctions** Page Component: The active ``Dutch auction`` orders are listed here. 3. **Collection** Page Component: This page lists all NFTs from selected collections. NFTs which have active sell order have the prices displayed. Collection analytics are included here. 3. **Homepage** Page Component: The active ``Buy now`` orders are listed here. 3. **My NFT collection** Page Component: This Component displays owned NFTs of the connected user. 3. **NFT Details** Page Component: NFT details page render basic NFT details, and orders related to this NFT. #### Services (*src/services*) ##### IPFS Both methods are using random IPFS key from ``.env`` variable for each request. 1. ``fetchData(cid: string)``: fetch data from IPFS by given CID 2. ``fetchImage(cid: string)``: fetch image from IPFS by given CID #### ZeroX 1. ``makeOrder(tokenId: string, serialNumber: number | string, erc20Token: string, price: number, expire: number, type: TradeDirection)``: makes ``ERC721Order`` by given data. 2. ``makeBuyOrder(tokenId: string, serialNumber: number, price: number, erc20Token: string, expire: number)``: prepares ``Buy Order`` transaction by given data. 3. ``makeSellOrder(tokenId: string, serialNumber: number | string, price: number, erc20TokenAddress: string, expire: number)``: prepares transaction for sell firstly settled ``Buy Order`` by given data 4. ``makeDutchAuctionOrder(erc20Token: string, tokenId: string, serialNumber: number, startAt: number, expiresAt: number, startingPrice: number, discountPeriod: number, discountRate: number)``: prepares ``Dutch Auction`` order transaction by given data. 5. ``convertOrderBookAuctionToZeroXDutchAuction(dutchAuction: NFTDutchAuction)``: converts ``Dutch Auction`` transaction to ``Orderbook`` supported format. #### Metamask 1. ``checkForConnectedAccounts())``: check for connected accounts 2. ``connect()``: connect via Metamask 3. ``sign(hash: string)``: hash string via connected Metamask account 4. ``signERC7210(order: ERC721Order)``: prepares ``Dutch Auction`` order transaction by given data. Return ``signature``. 5. ``buyERC721(order: ERC721Order, signature: Signature)``: execute ``buyERC721`` contract method via Metamask. 6. ``sellERC721(order: ERC721Order, signature: Signature)``: execute ``sellERC721`` contract method via Metamask. 7. ``setApprovalToken(token: string, approved: boolean = true)``: execute ``setApprovalToken`` contract method via Metamask to approve all NFTs by given collection ID(``token``). 8. ``setApproval(value: number)``: execute ``setApproval`` contract method via Metamask. After successfully executing the ammount(``value``) of ``Custom Fungible Token`` approve ``0x contract`` for transfer tokens. 9. ``cancelOrder(nonce: string | number)``: execute ``cancelOrder`` contract method via Metamask by given nonce. 10. ``cancelAuction(auctionNonce: string | number)``: execute ``cancelAuction`` contract method via Metamask by given auctionNonce. 11. ``associateToken(tokenId: string)``: execute ``associateToken`` contract method via Metamask. Return true when association went throught or false if occurs error. 12. ``getNFTDutchAuctionHash(nftDutchAuction: ZeroXDutchAuction)``: execute ``getNFTDutchAuctionHash`` contract method via Metamask. Return hash to be signed with Metamask when preparing sell ``Dutch Auction`` order transaction. 13. ``freeMintAUSDCFromFaucet()``: execute our contract method via Metamask to mint free testnet USDC. 14. ``addTokenIntoWallet(tokenAddress: string, tokenSymbol: string, tokenImage?: string, tokenDecimals?: number)``: this method allow the user to add token to Metamask with specified token params. #### Analytics 1. ``fetchCollectionAnalytics(params: { range: AnalyticsRange, orderBy: AnalyticsOrderBy, sortDirection?: 'ASC' | 'DESC' })``: fetch analytic and sort via parameters. Returns ``Pageable<CollectionAnalytics>``. #### OrderBook 1. ``fetchOrders(params: { [key: string]: unknown })``: check for connected accounts 2. ``fetchAuctions(params: { [key: string]: unknown })``: connect via Metamask 3. ``createOrder(orderData: PostOrderData)``: hash string via connected Metamask account 4. ``fetchOrderById(orderId: string | number)``: prepares ``Dutch Auction`` order transaction by given data. Return ``signature``. 5. ``fetchCollectionAnalytics(collectionId: string, range: AnalyticsRange = 'HALF_YEAR')``: execute ``buyERC721`` contract method via Metamask. 6. ``fetchOffers(tokenId: string, serialNumber: number)``: execute ``sellERC721`` contract method via Metamask. 7. ``fetchListings(tokenId: string, serialNumber: number)``: execute ``setApprovalToken`` contract method via Metamask to approve all NFTs by given collection ID(``token``). 8. ``fetchPriceHistory(tokenId: string, serialNumber: number)``: execute ``setApproval`` contract method via Metamask. After successfully executing the ammount(``value``) of ``Custom Fungible Token`` approve ``0x contract`` for transfer tokens. 9. ``fetchCollections(params: {search?: string, page?: number, size?: number, sort?: CollectionSortQuery[]})``: execute ``cancelOrder`` contract method via Metamask by given nonce. 10. ``fetchCollectionNFTs(params: {[key: string]: unknown, tokenId: string})``: execute ``cancelAuction`` contract method via Metamask by given auctionNonce. 11. ``createDutchAuction(nftDutchAuction: NFTDutchAuction, signature: Signature)``: execute ``associateToken`` contract method via Metamask. Return true when association went throught or false if occurs error. 12. ``convertZeroXDutchAuctionToOrderBookAuction(nftDutchAuction: ZeroXDutchAuction)``: execute ``getNFTDutchAuctionHash`` contract method via Metamask. Return hash to be signed with Metamask when preparing sell ``Dutch Auction`` order transaction. #### MirrorNode 1. ``async fetchAccountInfo(accountId: string)``: fetch account info for Hedera account. [Mirror Node API - Accounts](https://docs.hedera.com/guides/docs/mirror-node-api/rest-api#api-v1-accounts-idoraliasorevmaddress) 2. ``async fetchTokenInfo(tokenId: string)``: fetch Hedera token info. [Mirror Node API - Token info](https://docs.hedera.com/guides/docs/mirror-node-api/rest-api#token-info) 3. ``async fetchCollectionNFTs(tokenId: string | TokenId, options: { fetchMetadata?: boolean, nextLink?: string | null, countOfNfts?: number, userAccountId?: string}))``: fetch all collection NFTs. Can sort via ``userAccountId``. 4. ``async fetchNFTInfo(tokenId: string | TokenId, serialNumber: number)``: fetch NFT data via given details. 5. ``async fetchNFTMetadata(cid: string)``: fetch metadata by given CID using ``IPFS`` service. 6. ``async fetchUserNFTs(accountId: string)``: fetch all NFTs for given ``accountId``. 7. ``async checkTokenAssociationStatus(tokenId: string, accountId: string)``: check association status of given ``tokenId`` by given ``accountId``. Return ``boolean``. #### Connection Hooks (*src/utils/hooks*) ##### useMetamask Inside this hook the logic for connecting client to Metamask is handled. Hook lisning for account changes. If exist, fetch ``Hedera account info`` associated with EVM wallet address and returns it with EVM wallet address, connection status, and connection method. ```js const { address, isConnected, connect, hederaAccountInfo, } = useMetamask(); ``` #### URL (*src/routes/base.tsx*) The application has: - ``/`` which ties to **HomePage** Component - ``/auctions`` which ties to **Auctions** Component - ``/analytics`` which ties to **Analytics** Component - ``/collection/{collectionId}`` which ties to **Collection** Component - ``/collection/{collectionId}/{serialNumber}`` which ties to **NFT Details** Component #### HTTP calls **axios** library is used to make HTTP Calls. https://axios-http.com/ #### Hedera calls - **@hashgraph/sdk**: to generate Hedera transactions. https://github.com/hashgraph/hedera-sdk-js #### 0x/Metamask calls - **web3** - interact with a local or remote ethereum node using HTTP, IPC or WebSocket. [Read more](https://web3js.readthedocs.io/en/v1.8.2/). - **ethers** - interacting with the Ethereum Blockchain and its ecosystem. [Read more](https://docs.ethers.org/v5/) ## UI Resources - **ReactJS** : Refer to https://reactjs.org/ to understand the concepts of ReactJS - **classnames**: https://github.com/JedWatson/classnames#readme - **lodash**: https://lodash.com/ - **formik**: Reactive forms with https://formik.org/ - **yup**: Easy form validation https://github.com/jquense/yup - **daisyui** : https://daisyui.com/ - **react-toastify**: https://github.com/fkhadra/react-toastify#readme - **react-icons**: https://react-icons.github.io/react-icons/ ## Disclaimer This is just a simple demo application. Please use responsibly. [File an issue](/issues) if you see problems. ## Contributing Contributions are welcome. Please see the [contributing guide](https://github.com/hashgraph/.github/blob/main/CONTRIBUTING.md) to see how you can get involved. ## Code of Conduct This project is governed by the [Contributor Covenant Code of Conduct](https://github.com/hashgraph/.github/blob/main/CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to [oss@hedera.com](mailto:oss@hedera.com). ## LICENSE [Apache 2.0](LICENSE)