DEV Community

Cover image for Build a Decentralized Freelancer Marketplace with React, Solidity, and CometChat
Gospel Darlington
Gospel Darlington

Posted on

Build a Decentralized Freelancer Marketplace with React, Solidity, and CometChat

What you will be building, see the live demo at sepolia test net and the git repo.

Chatting with client

Bid placements

Accepting Applicants

Paying out worker

Learn how to build a decentralized freelance marketplace like Upwork using React, Solidity, and CometChat.

In this step-by-step tutorial, we'll teach you:

  • How to build React interfaces for job listing
  • How to create solidity smart contracts for job managment
  • How to integrate CometChat for one-on-one real-time communication
  • How to make the marketplace decentralized and annoymous

Whether you're a seasoned developer or new to Web3, this tutorial will give you the skills to create your own freelance marketplace. Let's get started!

Prerequisites

You will need the following tools installed to build along with me:

  • Node.js
  • Yarn
  • MetaMask
  • React
  • Solidity
  • CometChat SDK
  • Tailwind CSS

I recommend watching the videos below to learn how to set up your MetaMask for this project.

Installing Dependencies

Clone the starter kit and open it in VS Code using the command below:

git clone https://github.com/Daltonic/tailwind_ethers_starter_kit dappworks
cd dappworks
Enter fullscreen mode Exit fullscreen mode

Next, update the package.json with the snippet below.

Please run the command yarn install in your terminal to install the dependencies for this project.

Configuring CometChat SDK

To configure the CometChat SDK, please follow the steps provided below. Once completed, make sure to save the generated keys as environment variables for future use.

STEP 1:
Head to CometChat Dashboard and create an account.

Register a new CometChat account if you do not have one

STEP 2:
Log in to the CometChat dashboard, only after registering.

Log in to the CometChat Dashboard with your created account

STEP 3:
From the dashboard, add a new app called Play-To-Earn.

Create a new CometChat app - Step 1

Create a new CometChat app - Step 2

STEP 4:
Select the app you just created from the list.

Select your created app

From the Quick Start copy the APP_ID, REGION, and AUTH_KEY, to your .env file. See the image and code snippet.

Copy the the APP_ID, REGION, and AUTH_KEY

Replace the REACT_COMET_CHAT placeholder keys with their appropriate values.

REACT_APP_COMETCHAT_APP_ID=****************
REACT_APP_COMETCHAT_AUTH_KEY=******************************
REACT_APP_COMETCHAT_REGION=**
Enter fullscreen mode Exit fullscreen mode

The .env file should be created at the root of your project.

Configuring the Hardhat script

Navigate to the root directory of the project and open the "hardhat.config.js" file. Replace the existing content of the file with the provided settings.

This code configures Hardhat for your project. It includes importing necessary plugins, setting up networks (with localhost as the default), specifying the Solidity compiler version, defining paths for contracts and artifacts, and setting a timeout for Mocha tests.

The Smart Contract File

The following steps will guide you through the process of creating the smart contract file for this project:

  1. Create a new folder named contracts inside the src folder.
  2. Create a new file named Dappworks.sol inside the contracts folder.
  3. Copy the provided codes below and paste it into their respective files and save.

By following these steps, you will have successfully set up the necessary directory structure and created the Dappworks.sol file, which will serve as the foundation for implementing the logic of the smart contract.

The DappWorks smart contract is designed to facilitate a decentralized job marketplace, where users can create job listings, place bids on jobs, and manage the entire job lifecycle. Let's explore the key components and functionalities of this contract.

  1. Contract Inheritance and Libraries:

    • The contract inherits from the Ownable and ReentrancyGuard contracts, which provide access control and protection against reentrancy attacks.
    • It imports several libraries from the OpenZeppelin framework, including Counters for managing counters, SafeMath for secure mathematical operations, and Ownable and ReentrancyGuard for access control and reentrancy protection.
  2. State Variables:

    • The contract includes several state variables to manage job listings, freelancers, and bids. These include counters, structs, and mappings to store relevant data.
  3. Structs:

    • The contract defines several structs, including JobStruct, FreelancerStruct, and BidStruct, to structure and store information about jobs, freelancers, and bids.
  4. Modifiers:

    • The contract defines a modifier called onlyJobOwner(uint id) that restricts certain functions to the owner of a specific job listing.
  5. Functions:

    • The contract includes various functions for managing job listings and bids, including:
      • addJobListing: Allows users to create a new job listing by providing a title, description, and tags. Users must also attach ether as a prize for the job.
      • deleteJob: Allows the owner of a job listing to delete it and receive the prize.
      • updateJob: Allows the owner of a job listing to update its details.
      • bidForJob: Allows users to place bids on job listings.
      • acceptBid: Allows the owner of a job listing to accept a specific bid from a freelancer, assigning the job to them.
      • dispute: Allows the owner of a job listing to initiate a dispute.
      • revoke: Allows the contract owner to revoke an assignment during a dispute.
      • resolved: Allows the contract owner to mark a dispute as resolved.
      • payout: Allows the owner of a job listing to pay the assigned freelancer, deducting a platform fee.
      • Various getter functions to retrieve information about jobs, bidders, freelancers, and more.
  6. Internal Functions:

    • The contract includes internal functions for handling time and making payments.
  7. Events:

    • The contract emits events to log various actions and state changes.
  8. Constructor:

    • The contract does not have a constructor, indicating that it can be deployed as is.

Overall, this smart contract is designed to facilitate the creation, management, and completion of job listings on the Ethereum blockchain. Users can create job listings, place bids, and assign jobs to freelancers. It also includes features for dispute resolution and a platform fee for job payouts.

🚀 Blockchain has real use cases, Join Dapp Mentors Academy for $8.44/month to learn all you can.

Dapp Mentors Academy

Subscribe to Dapp Mentors Academy today and get exclusive access to over 40 hours of web3 content, including courses on NFT minting, blockchain development, and more!

The Test Script

The DappWorks test script is thoughtfully crafted to thoroughly evaluate and confirm the functionalities and behaviors of the DappWorks smart contract. Here's an organized breakdown of the primary tests and functions encompassed within the script:

  1. Setup and Initialization:

    • The script imports required libraries and defines a utility function **toWei** to convert ether amounts to wei.
    • It defines a test suite using Mocha's **describe** function and sets up initial variables and contract instances in the **beforeEach** hook.
  2. Job Creation Tests:

    • The script includes several test cases to verify the creation and management of job listings.
    • **should confirm fetching job listings**: Checks if a job listing can be retrieved and confirms that there is one job listing.
    • **should confirm fetching a single job listing**: Retrieves a specific job listing and verifies its properties.
    • **should confirm updating of job**: Tests updating the details of a job listing and checks if the changes are reflected correctly.
    • **should confirm job deletion**: Adds and then deletes a job listing, verifying that the job listing count becomes zero.
    • **should confirm bidding for job**: Tests placing a bid on a job listing and confirms that there is one bidder.
    • **should confirm accepting job bid**: Places a bid, accepts the bid, and confirms the assignment of the job to a freelancer.
    • **should confirm disputing a job**: Initiates a dispute for a job listing and verifies that the job is marked as disputed.
    • **should confirm revoking a disputed job**: Places a bid, accepts the bid, disputes the job, and then revokes it. Checks if the job is listed again and the freelancer's assignment is revoked.
    • **should confirm resolving a disputed job**: Initiates a dispute and then resolves it, ensuring that the job is no longer marked as disputed.
    • **should confirm payout of a job**: Places a bid, accepts the bid, and performs a payout, confirming that the job is marked as paid out.

Each test case uses Chai's **expect** assertion to verify the expected outcomes of various contract interactions. The test script thoroughly tests the functionality of the DappWorks smart contract, covering job creation, bidding, assignment, dispute handling, payout, and more.

By running **yarn hardhat test** on the terminal will test out all the essential function of this smart contract.

The Deployment Scripts

The DappWorks deployment script is responsible for deploying the DappWorks smart contract to the Ethereum network using the Hardhat development environment. Here's an overview of the script:

Key Components and Functionality

  • Imports the ethers and fs dependencies.
  • Defines an asynchronous main() function to deploy the contract.
  • Gets the contract factory for DappWorks.
  • Deploys the contract and stores the contract instance in the contract variable.
  • Waits for the deployment to complete.
  • Extracts the contract address and stores it in the address variable.
  • Writes the address to a JSON file in the src/abis/ directory.
  • Handles any errors that may occur during execution.
  • Invokes the main() function.
  • Logs the deployed contract address to the console if the deployment is successful.

The DappWorks deployment script simplifies the deployment of the DappWorks smart contract and generates a JSON file, **contractAddress.json**, containing the deployed contract's address. This file can be used for further integration and interaction with the deployed contract within the project.

To utilize this script, create a folder named "scripts" in the root directory of your project if it doesn't already exist. Inside the "scripts" folder, create a JavaScript file named **deploy.js**, and then copy and paste the provided deployment script into this file.

Next, run the **yarn hardhat run scripts/deploy.js** to deploy the smart contract into the network on a terminal.

Activities of Deployment on the Terminal

If you require additional assistance with setting up Hardhat or deploying your Fullstack DApp, I recommend watching this informative video that provides guidance and instructions.

Developing the Frontend

To start developing the frontend of our application, we will create a new folder called components inside the src directory. This folder will hold all the components needed for our project.

For each of the components listed below, you will need to create a corresponding file inside the src/components folder and paste its codes inside it.

Header Component

Header Component

The DappWorks header component is a central part of the user interface. It features the DappWorks logo, navigation links for easy access to various app sections, and a wallet connection button. The mobile-friendly design includes a toggle button for a compact menu. This component enhances user navigation and wallet integration.

Job Listing Card Component

Job listing card Freelancer view

Job listing card Owner view

The JobListingCard component renders a card displaying information about a job listing, including the title, price, tags, and description. Users can interact with the card to place bids or manage job management tasks. The component handles user interactions and provides visual feedback through toast notifications. It also checks the user's account status to determine the appropriate actions to display on the card.

Watch my new YouTube tutorial to learn how to integrate RainbowKit's multi-wallet connector into your dApp to improve the quality and user experience.

CreateJob Component

Create Job Component

The CreateJob component enables users to create job listings through a user-friendly modal interface. Users input job details, including title, prize, skills, and description. The component supports up to five featured skills, provides modal controls, and offers transaction feedback via toast notifications. It seamlessly integrates with global state management for a smooth user experience.

DeleteJob Component

Delete Job component

The DeleteJob component is responsible for displaying a confirmation modal when users attempt to delete a job listing. It provides clear visual cues, including a trash icon, a confirmation message, and warning about irreversible actions. Users can either cancel or proceed with the deletion, with toast notifications providing feedback on the outcome of the transaction.

UpdateJob Component

Update Job Component

The UpdateJob component is responsible for displaying a modal that allows users to update an existing job listing. It retrieves the job details, pre-fills the form fields, and provides an interface for users to make changes. Upon submission, it triggers the update process on the blockchain and displays toast notifications to indicate the outcome of the transaction.

JobListingOwnerActions Component

Owner Actions

Completed State

This component renders a set of actions that an owner of a job listing can perform. It displays information about the job listing, including the title, price, tags, and description. Depending on the status of the job listing, it allows the owner to perform actions such as updating, deleting, viewing bidders, paying, and chatting with a freelancer. The component also handles different visual representations based on the state of the job listing, such as showing a "Completed" status when the job has been paid out.

JobBid Component

Job Bid Component

Job Bid Component

This component displays information about a job listing for potential bidders. It renders the job title, price (in Ethereum), tags, and job description. Additionally, it provides a "Chat with freelancer" button that allows users to initiate a chat with the owner of the job listing. It serves as a preview of the job for individuals interested in bidding on it.

ApplicantsCard Component

Bidding Applicants

This component display information about applicants or bidders for a job listing. It shows the bidder's truncated Ethereum account address, along with buttons for initiating a chat with the bidder and accepting their bid. The "Chat" button links to a chat page with the bidder, and the "Accept" button allows the job owner to accept the bidder's offer. It provides a user-friendly interface for managing job applications.

Payout Components

The payout component

The Payout component is used to facilitate the payout process for a job listing. It displays a modal dialog with an option to initiate a payment. Users can either proceed with the payout or cancel it. Upon initiating the payout, it communicates with the blockchain using the payout function and displays toast notifications to inform the user about the transaction status. This component provides a user-friendly interface for handling payouts for completed jobs.

Want to learn how to build an Answer-To-Earn DApp with Next.js, TypeScript, Tailwind CSS, and Solidity? Watch this video now!

This video is a great resource for anyone who wants to learn how to build decentralized applications and earn ethers.

Now that we have covered all the components in this application, it is time to start coupling the various pages together. Let's start with the homepage.

To begin developing the pages of our application, we will create a new folder called pages inside the src directory. This folder will hold all the pages needed for our project.

For each of the pages listed below, you will need to create a corresponding file inside the src/pages folder, just as you did before with the components.

Home Page

The Home Page

The Home page combines other components, including Header, Hero, and CreateJob, to create the user interface for the home page. The Header typically contains navigation links and branding, the Hero section might display introductory content or visuals, and the CreateJob section allows users to create new job listings. Overall, this component assembles these parts to create the complete homepage of the application.

MyProjects Page

My Projects Page

The MyProjects page is where users can manage their own job listings and take various actions on them. It includes components such as Header for navigation, JobListingOwnerActions to display and manage job listings, UpdateJob for updating job details, DeleteJob for deleting job listings, and Payout for initiating payments. Users can view their posted jobs, edit or delete them, and initiate payouts to freelancers. If no jobs are posted, it displays a message indicating that no jobs are available.

MyJobs Page

My Jobs Page

The MyJobs component is where users can view the tasks or jobs that have been assigned to them. It includes a Header for navigation and a list of JobBid components that display information about each assigned task. If there are assigned tasks, it shows them with their details; otherwise, it displays a message indicating that there are no assigned tasks for the user.

MyBids Page

My Bids Page

The MyBids page is where users can view the jobs they have applied for by placing bids. It includes a Header for navigation and a list of JobBid components that display information about each job the user has bid on. If there are jobs the user has bid on, it shows them with their details; otherwise, it displays a message indicating that the user hasn't bid on any jobs yet.

ViewBidders Page

Bidders Page

The ViewBidders page is where users can view the list of applicants for a specific job listing. It utilizes the useParams hook to extract the job ID from the URL, fetches the list of bidders and job details from the blockchain, and displays them. Depending on whether there are applicants or if the position is filled or vacant, it displays relevant information such as applicants' cards or status messages. It also includes a Header component for navigation.

Authenticate Page

Authentication Page

The Authenticate page is for user authentication for chatting within the application. It allows users to either log in or sign up for chat functionality using the CometChat service. Upon successful authentication, it displays a success message and navigates the user to the chat messages page. It also includes a Header component for navigation and provides feedback to the user through toast notifications.

RecentConversations Page

Recent Conversation Page

The "RecentConversations" page in the DappWorks app displays a list of recent chat conversations. It fetches and displays these conversations, allowing users to click on a conversation to navigate to the chat interface with that user. Each conversation displays the user's name and a unique Identicon for easy identification. If there are no recent chats, a message notifies the user. This page provides a convenient way to access and manage recent chat interactions.

Chats Page

The Chat Page

The Chats page is responsible for managing and displaying chat messages between users. It retrieves and displays messages based on the selected user, allows users to send messages, and automatically updates the chat in real-time. Users can see their own messages on the right side and messages from the other user on the left side, with profile icons and message content. The component also includes a Header for navigation and maintains a scrolling chat container for easy viewing of messages.

The Blockchain Service

This script is a JavaScript module that interacts with a blockchain smart contract using the Ethereum blockchain. It provides functions for various actions related to the blockchain and stores or retrieves data from the blockchain. Note that you will have to create a file called blockchain.jsx within the src >> services folder and paste the code below inside.

Here's an overview of what this script does:

  1. It imports necessary dependencies, including contract ABI (Application Binary Interface), contract addresses, and Ethereum-related libraries.
  2. Defines utility functions like toWei and fromWei to convert between Ether and Wei.
  3. Defines a function getEthereumContract to establish a connection to the Ethereum blockchain and retrieve a contract instance.
  4. isWalletConnected checks if a user's Ethereum wallet (e.g., MetaMask) is connected and handles account changes and chain changes.
  5. Functions like connectWallet, addJobListing, updateJob, deleteJob, bidForJob, acceptBid, dispute, resolved, revoke, payout, bidStatus, getBidders, getFreelancers, getAcceptedFreelancer, getJobs, getMyJobs, getJob, getMyBidJobs, getMyGigs, and loadData are defined to interact with the smart contract for various actions such as creating jobs, bidding, accepting bids, and more.
  6. structuredJobs, structuredBidder, and structuredFreelancers functions format data retrieved from the blockchain into structured objects for easier management and display.

Overall, this script acts as an interface between the front-end application and the Ethereum blockchain, allowing users to perform actions and retrieve data related to jobs and bids on the blockchain. It integrates with a smart contract using the contract's ABI and address. See the script above.

Please ensure that you update the environment variables to look like this:

REACT_APP_COMETCHAT_APP_ID=****************
REACT_APP_COMETCHAT_AUTH_KEY=******************************
REACT_APP_COMETCHAT_REGION=**
REACT_APP_RPC_URL=http://127.0.0.1:8545
Enter fullscreen mode Exit fullscreen mode

The Chat Service

This script is a JavaScript module that provides functionality for integrating and interacting with the CometChat Pro service within a web application. CometChat Pro is a chat and messaging platform that allows developers to add real-time chat features to their applications. Like with the previous service you will have to create another file called chat.jsx within the src >> services folder and paste the code below inside.

Here is a breakdown of how this script works:

  1. It imports the CometChat library (CometChat) and a function (getGlobalState) from a custom store.

  2. It defines constants (CONSTANTS) for the CometChat application settings, including the App ID, region, and authentication key. These settings are typically stored in environment variables for security.

  3. The initCometChat function initializes CometChat with the provided application settings. It subscribes to user presence updates and sets the region. Initialization is done asynchronously, and any errors encountered during initialization are caught and logged.

  4. The loginWithCometChat function performs user login with CometChat. It takes the user's unique identifier (UID) and authentication key as parameters, attempts to log in, and returns a promise that resolves to the user object upon successful login or rejects with an error in case of failure.

  5. The signUpWithCometChat function creates a new user on CometChat. It also takes the user's UID and authentication key as parameters, sets the user's name, and returns a promise that resolves to the newly created user upon success or rejects with an error in case of failure.

  6. The logOutWithCometChat function logs the current user out of CometChat. It returns a promise that resolves upon successful logout or rejects in case of an error.

  7. The isUserLoggedIn function checks if a user is currently logged in to CometChat and returns a promise that resolves to the logged-in user or rejects with an error.

  8. The getUser function retrieves user information from CometChat based on the provided UID. It returns a promise that resolves to the user object or rejects with an error.

  9. The getMessages function fetches messages for a specific user (UID) from CometChat. It sets a message limit and returns a promise that resolves to an array of text messages or rejects with an error.

  10. The sendMessage function sends a text message to a specified receiver (receiverID) through CometChat. It returns a promise that resolves to the sent message or rejects with an error.

  11. The getConversations function retrieves a list of conversations from CometChat. It sets a conversation limit and returns a promise that resolves to the list of conversations or rejects with an error.

  12. The listenForMessage function sets up a listener to receive incoming text messages in real-time. It takes a listenerID (typically a user's UID) and returns a promise that resolves to the received message or rejects with an error.

Overall, this script enables the integration of CometChat Pro features into a web application, including user management, message retrieval, sending messages, and real-time message listening. It abstracts the CometChat functionality into convenient functions for use within the application.

Excellent! Now, let's work on the store file, which serves as a state management library.

The Store File

This script provides a central state management system for a React application using the react-hooks-global-state library. It also includes utility functions for text truncation and date formatting. You will have to create a file called index.jsx within the src >> store folder and paste the code below inside.

Here's an overview of what this script does:

  1. It imports the createGlobalState function from the react-hooks-global-state library.

  2. It uses the createGlobalState function to create a global state container with initial state values. This global state container is then deconstructed into three functions: setGlobalState, useGlobalState, and getGlobalState.

    • setGlobalState: A function used to set or update global state values. It takes a key and a new value as arguments.
    • useGlobalState: A custom hook that allows components to access and subscribe to global state values. It returns the current value associated with a specified key and automatically updates the component when that value changes.
    • getGlobalState: A function that retrieves the current value associated with a specified key from the global state.
  3. It defines a truncate function that takes a text string, start and end character counts, and a maximum length as arguments. This function truncates the text and adds ellipses (...) in the middle if the text exceeds the specified maximum length. It's used for shortening text for display.

  4. It defines a formatDate function that takes a timestamp as an argument and returns a formatted date string in the format "Month Day, Year." This function is used for formatting timestamps into more human-readable date strings.

  5. It defines a timestampToDate function that takes a timestamp as an argument and returns a formatted date string in the format "Month Day, Year Hour:Minute." This function is used for formatting timestamps into more detailed date and time strings.

Overall, this script serves as a global state management solution for a React application, allowing different components to share and access state data easily. It also provides utility functions for common text and date formatting operations.

The Index files

The index.jsx file is the entry point for the application. It initializes the CometChat service, sets up dependencies, and renders the React application using the App component within a BrowserRouter. It creates a root element for rendering and sets up the necessary configurations for the application to start running.

To use this code, you will need to replace the code below inside of the index.jsx and index.css files in the src folder of your project.

Now you are officially done with the build, just execute the following commands to have the application running on the browser.

  • Terminal #1: **yarn hardhat node**
  • Terminal #2: **yarn hardhat run scripts/deploy.js** and then **yarn start**

Congratulations on building a Web3 Upwork clone with React, Solidity, and CometChat! 🚀
Your achievement is a testament to your prowess in combining these cutting-edge technologies to craft an innovative and engaging application. By harnessing React for the frontend, Solidity for your smart contracts, and integrating CometChat for real-time communication, you've showcased a versatile skill set in both blockchain development and interactive user experiences.

Your Web3 Upwork clone stands as a remarkable project, bringing together the power of blockchain, modern web development, and real-time communication to create a platform that empowers users and facilitates collaboration in a decentralized manner.

Keep up the great work, and continue exploring the endless possibilities of technology! Your journey in the world of Web3 development is bound to lead to even more exciting and impactful projects in the future.

Here is a link to the CometChat website where you can learn more about the SDK and how to get started.

For more web3 resources, check out this video that teaches how to create a decentralized app by building a web3 lottery dapp, I recommend that you it.

The video provides a hands-on tutorial on how to build a lottery dapp using NextJs, Tailwind CSS, and Solidity.

Conclusion

Congratulations on completing the journey of building a Web3 Upwork clone with React, Solidity, and CometChat. This comprehensive guide has empowered you to create a cutting-edge platform that combines the power of React for the frontend, Solidity for smart contracts, and CometChat for real-time communication. By undertaking this project, you've demonstrated your prowess in blockchain development and interactive user experiences.

This tutorial has unveiled the potential of web3 technology in transforming the gig economy, offering secure and transparent transactions while enhancing user engagement through dynamic real-time interactions. The smart contracts have been rigorously tested to ensure reliability, making your Upwork clone a robust and trustworthy platform for users.

As you conclude this project, you're not only equipped with technical skills but also with a vision of the future where decentralized applications redefine traditional industries. Your Upwork clone is a testament to the possibilities that emerge when innovative technologies converge.
To further enhance your knowledge and stay updated on the latest developments in blockchain and web3, consider subscribing to our YouTube channel and exploring our website for additional resources.

Best wishes on your journey of innovation and discovery in the world of web3 and decentralized applications!

About Author

I am a web3 developer and the founder of Dapp Mentors, a company that helps businesses and individuals build and launch decentralized applications. I have over 7 years of experience in the software industry, and I am passionate about using blockchain technology to create new and innovative applications. I run a YouTube channel called Dapp Mentors where I share tutorials and tips on web3 development, and I regularly post articles online about the latest trends in the blockchain space.

Stay connected with us, join communities on
Discord: Join
Twitter: Follow
LinkedIn: Connect
GitHub: Explore
Website: Visit

Top comments (0)