In the burgeoning world of Web3, decentralized applications (dapps) have emerged as a cornerstone, providing secure, transparent, and trustless services. Among these, decentralized marketplaces stand out, allowing users to buy and sell items directly on the blockchain, effectively eliminating intermediaries. According to linea.mirror.xyz, this guide delves into building a simple marketplace dapp on Linea, a Layer 2 zkEVM technology that offers scalability and cost efficiency while maintaining Ethereum's security.
Understanding the Marketplace Dapp Architecture
The architecture of the marketplace dapp incorporates smart contracts, frontend, and blockchain integration. The smart contract will handle features like listing items, buying items, and transferring ownership. The frontend will serve as the user interface enabling buyers and sellers to interact with the smart contract. The interaction with the Linea blockchain will utilize the Metamask SDK along with Wagmi and Infura RPC providers.
Setting Up the Environment
First, initialize a monorepo to manage the project:
mkdir web3-marketplace-linea
cd web3-marketplace-linea
pnpm init
Create a pnpm-workspace.yaml
file in the root:
packages:
- 'packages/*'
The workspace structure will include:
packages
├── site # Frontend built with Next.js, Tailwind CSS, and Shadcn UI
└── blockchain # Smart contracts using Hardhat
Next, initialize a Hardhat project in the blockchain directory:
cd blockchain
npx hardhat init
Choose the TypeScript project option when prompted.
Writing the Smart Contract
The smart contract will manage the marketplace's items, their ownership, and transactions:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Marketplace {
struct Item {
uint id;
string name;
uint price;
address payable seller;
address owner;
bool isSold;
}
uint public itemCount = 0;
mapping(uint => Item) public items;
mapping(address => uint[]) public ownedItems;
function listItem(string memory _name, uint _price) public {
require(_price > 0, "Price must be greater than zero");
itemCount++;
items[itemCount] = Item(itemCount, _name, _price, payable(msg.sender), msg.sender, false);
ownedItems[msg.sender].push(itemCount);
}
function purchaseItem(uint _id) public payable {
Item storage item = items[_id];
require(_id > 0 && _id <= itemCount, "Item does not exist");
require(msg.value == item.price, "Incorrect price");
require(!item.isSold, "Item already sold");
require(msg.sender != item.seller, "Seller cannot buy their own item");
item.isSold = true;
item.seller.transfer(msg.value);
_transferOwnership(_id, item.seller, msg.sender);
}
function _transferOwnership(uint _id, address _from, address _to) internal {
Item storage item = items[_id];
item.owner = _to;
uint[] storage fromItems = ownedItems[_from];
for (uint i = 0; i < fromItems.length; i++) {
if (fromItems[i] == _id) {
fromItems[i] = fromItems[fromItems.length - 1];
fromItems.pop();
break;
}
}
ownedItems[_to].push(_id);
}
function transferItem(uint _id, address _to) public {
Item storage item = items[_id];
require(_id > 0 && _id <= itemCount, "Item does not exist");
require(msg.sender == item.owner, "You do not own this item");
_transferOwnership(_id, msg.sender, _to);
}
function getItemsByOwner(address _owner) public view returns (uint[] memory) {
return ownedItems[_owner];
}
}
This contract facilitates a decentralized marketplace where users can list items, purchase them, and transfer ownership, with all transactions recorded on the blockchain.
Deploying the Smart Contract
Create a deployment module in the ignition
folder:
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";
const MarketplaceModule = buildModule("MarketplaceModule", (m) => {
const marketplace = m.contract("Marketplace");
return { marketplace };
});
export default MarketplaceModule;
Update the .env
file with your Infura API key and account private key:
INFURA_API_KEY=your_infura_api_key_here
ACCOUNT_PRIVATE_KEY=your_account_private_key_here
Deploy the contract to the Linea testnet:
npx hardhat ignition deploy ignition/modules/Marketplace.ts --network linea-testnet
Frontend Development with Next.js and Shadcn UI
Initialize the Next.js project in the site
directory:
mkdir site
cd site
npx create-next-app@latest .
Select TypeScript, ESLint, Tailwind CSS, and App Router options when prompted. Install Shadcn UI CLI:
npx shadcn-ui@latest init
Create a wagmi.config.ts
file for blockchain connectivity:
import { http, createConfig } from "wagmi";
import { lineaSepolia } from "wagmi/chains";
import { metaMask } from "wagmi/connectors";
export const config = createConfig({
chains: [lineaSepolia],
connectors: [metaMask()],
transports: {
[lineaSepolia.id]: http(),
},
});
Create a constants.ts
file with the contract address and ABI:
export const CONTRACT_ADDRESS = "your_contract_address_here";
export const ABI = [/* ABI array here */];
Develop the frontend components and hook them up to the smart contract using Wagmi and Metamask SDK. Detailed instructions and code examples can be found in the full guide on linea.mirror.xyz.
Run the development server:
npm run dev
Your Next.js application with Shadcn UI should now be running at http://localhost:3000.
In this guide, a simple decentralized marketplace dapp was built on Linea, leveraging zkEVM technology for scalability and cost efficiency. The process covered everything from setting up the environment to writing and deploying smart contracts, and integrating the frontend with Next.js and Shadcn UI. This foundational knowledge opens doors for further enhancements, such as integrating additional features or optimizing the user experience.
Image source: Shutterstock