What you will be building, see the and the . live demo at sepolia test net git repo 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. https://www.youtube.com/watch?v=qnudOwva0fM&embedable=true 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 Next, update the with the snippet below. package.json https://gist.github.com/covelitein/99839042684ca244c24a6a45736f9138?embedable=true Please run the command in your terminal to install the dependencies for this project. yarn install Configuring CometChat SDK To configure the , please follow the steps provided below. Once completed, make sure to save the generated keys as environment variables for future use. CometChat SDK Head to Dashboard and create an account. STEP 1: CometChat Log in to the dashboard, only after registering. STEP 2: CometChat From the dashboard, add a new app called STEP 3: DappWorks Select the app you just created from the list. STEP 4: From the Quick Start copy the , , and , to your file. See the image and code snippet. APP_ID REGION AUTH_KEY .env Replace the placeholder keys with their appropriate values. REACT_COMET_CHAT REACT_APP_COMETCHAT_APP_ID=**************** REACT_APP_COMETCHAT_AUTH_KEY=****************************** REACT_APP_COMETCHAT_REGION=** The file should be created at the root of your project. .env Configuring the Hardhat Script Navigate to the root directory of the project, and open the " " file. Replace the existing content of the file with the provided settings. hardhat.config.js https://gist.github.com/covelitein/e494add9d15cd497678fe7fe135ea57e?embedable=true 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: Create a new folder named inside the folder. contracts src Create a new file named inside the folder. Dappworks.sol contracts 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 file, which will serve as the foundation for implementing the logic of the smart contract. Dappworks.sol https://gist.github.com/Daltonic/9469656d751e3396e25d6e33e67578fe?embedable=true 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. Contract Inheritance and Libraries: The contract inherits from the and contracts, which provide access control and protection against reentrancy attacks. Ownable ReentrancyGuard It imports several libraries from the OpenZeppelin framework, including for managing counters, for secure mathematical operations, and and for access control and reentrancy protection. Counters SafeMath Ownable ReentrancyGuard 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. Structs: The contract defines several structs, including , , and , to structure and store information about jobs, freelancers, and bids. JobStruct FreelancerStruct BidStruct Modifiers: The contract defines a modifier called that restricts certain functions to the owner of a specific job listing. onlyJobOwner(uint id) Functions: The contract includes various functions for managing job listings and bids, including: : 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. addJobListing : Allows the owner of a job listing to delete it and receive the prize. deleteJob : Allows the owner of a job listing to update its details. updateJob : Allows users to place bids on job listings. bidForJob : Allows the owner of a job listing to accept a specific bid from a freelancer, assigning the job to them. acceptBid : Allows the owner of a job listing to initiate a dispute. dispute : Allows the contract owner to revoke an assignment during a dispute. revoke : Allows the contract owner to mark a dispute as resolved. resolved : Allows the owner of a job listing to pay the assigned freelancer, deducting a platform fee. payout Various getter functions to retrieve information about jobs, bidders, freelancers, and more. Internal Functions: The contract includes internal functions for handling time and making payments. Events: The contract emits events to log various actions and state changes. 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, to learn all you can. Join Dapp Mentors Academy for $8.44/month today and get exclusive access to over 40 hours of web3 content, including courses on NFT minting, blockchain development, and more! Subscribe to Dapp Mentors Academy 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: https://gist.github.com/Daltonic/cd239d36a2578a755cb44adbb1518b93?embedable=true Setup and Initialization: The script imports required libraries and defines a utility function to convert ether amounts to wei. **toWei** It defines a test suite using Mocha's function and sets up initial variables and contract instances in the hook. **describe** **beforeEach** Job Creation Tests: The script includes several test cases to verify the creation and management of job listings. : Checks if a job listing can be retrieved and confirms that there is one job listing. **should confirm fetching job listings** : Retrieves a specific job listing and verifies its properties. **should confirm fetching a single job listing** : Tests updating the details of a job listing, and checks if the changes are reflected correctly. **should confirm updating of job** : Adds and then deletes a job listing, verifying that the job listing count becomes zero. **should confirm job deletion** : Tests placing a bid on a job listing and confirms that there is one bidder. **should confirm bidding for job** : Places a bid, accepts the bid, and confirms the assignment of the job to a freelancer. **should confirm accepting job bid** : Initiates a dispute for a job listing and verifies that the job is marked as disputed. **should confirm disputing a 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 revoking a disputed job** : Initiates a dispute and then resolves it, ensuring that the job is no longer marked as disputed. **should confirm resolving a disputed job** : Places a bid, accepts the bid, and performs a payout, confirming that the job is marked as paid out. **should confirm payout of a job** Each test case uses Chai's assertion to verify the expected outcomes of various contract interactions. The test script thoroughly tests the functionality of the smart contract, covering job creation, bidding, assignment, dispute handling, payout, and more. **expect** DappWorks https://gist.github.com/covelitein/613b69aad4a16dedaec600d0cd445884?embedable=true By running on the terminal, it will test out all the essential functions of this smart contract. **yarn hardhat test** 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 and dependencies. ethers fs Defines an asynchronous function to deploy the contract. main() Gets the contract factory for . DappWorks Deploys the contract, and stores the contract instance in the variable. contract Waits for the deployment to complete. Extracts the contract address, and stores it in the variable. address Writes the address to a JSON file in the directory. src/abis/ Handles any errors that may occur during execution. Invokes the function. main() 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, , containing the deployed contract's address. This file can be used for further integration and interaction with the deployed contract within the project. contractAddress.json* 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 , and then copy and paste the provided deployment script into this file. deploy.js https://gist.github.com/covelitein/9cac21f0ffaa36027f9cf5def1b9e339?embedable=true Next, run the to deploy the smart contract into the network on a terminal. **yarn hardhat run scripts/deploy.js** 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. https://www.youtube.com/watch?v=6lIq8MCfaGE&embedable=true Developing the Frontend To start developing the frontend of our application, we will create a new folder called inside the directory. This folder will hold all the components needed for our project. components src For each of the components listed below, you will need to create a corresponding file inside the folder and paste its codes inside it. src/components 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. https://gist.github.com/covelitein/ebee1b48843427770a25b94d2e520c5c?embedable=true Job Listing Card Component The 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. JobListingCard 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. https://gist.github.com/Daltonic/a1a04c46b478951c20db99400f899a54?embedable=true https://www.youtube.com/watch?v=JxkC2QtZBkw&embedable=true 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 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. https://gist.github.com/covelitein/646dfb750dd1f800cb36f844b37f4e5f?embedable=true DeleteJob 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 a warning about irreversible actions. Users can either cancel or proceed with the deletion, with toast notifications providing feedback on the outcome of the transaction. https://gist.github.com/covelitein/6e594f2c52ee26214b416e090b873226?embedable=true UpdateJob 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. https://gist.github.com/covelitein/b815acbdaf030106e760ff60a4c8c9f2?embedable=true JobListingOwnerActions Component 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. https://gist.github.com/Daltonic/2b3a13c8896fab10c48d33348d9b3a1f?embedable=true JobBid 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. https://gist.github.com/covelitein/2ec985aff09f1b14f9d3e49237fed973?embedable=true ApplicantsCard Component This component displays 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. https://gist.github.com/covelitein/a4248324a52b32e032cb2427576a41c8?embedable=true Payout Components The 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. Payout Upon initiating the payout, it communicates with the blockchain using the 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. payout https://gist.github.com/covelitein/06fedb94859895417ff62b685e46736b?embedable=true Want to learn how to build an Answer-To-Earn DApp with Next.js, TypeScript, Tailwind CSS, and Solidity? Watch this video now! https://www.youtube.com/watch?v=Ubom39y5jX8&feature=youtu.be&embedable=true 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 of 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 inside the directory. This folder will hold all the pages needed for our project. pages src For each of the pages listed below, you will need to create a corresponding file inside the folder, just as you did before with the components. src/pages Home Page The page combines other components, including , , and , to create the user interface for the home page. The typically contains navigation links and branding, the section might display introductory content or visuals, and the section allows users to create new job listings. Home Header Hero CreateJob Header Hero CreateJob Overall, this component assembles these parts to create the complete homepage of the application. https://gist.github.com/covelitein/87f77c7ba0f8c93921e41b8e8b817b7c?embedable=true MyProjects Page The page is where users can manage their own job listings and take various actions on them. It includes components such as for navigation, to display and manage job listings, for updating job details, for deleting job listings, and for initiating payments. MyProjects Header JobListingOwnerActions UpdateJob DeleteJob Payout 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. https://gist.github.com/covelitein/e023e124fd0de692322838ca1430f166?embedable=true MyJobs Page The component is where users can view the tasks or jobs that have been assigned to them. It includes a for navigation and a list of components that display information about each assigned task. MyJobs Header JobBid 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. https://gist.github.com/Daltonic/09a94830ea974a6649cb3602a9b22862?embedable=true MyBids Page The page is where users can view the jobs they have applied for by placing bids. It includes a for navigation and a list of components that display information about each job the user has bid on. MyBids Header JobBid If there are jobs the user has bid on, it shows them their details; otherwise, it displays a message indicating that the user hasn't bid on any jobs yet. https://gist.github.com/Daltonic/cd5fd8d3600407e4af98b99d077e4cb8 ViewBidders Page The page is where users can view the list of applicants for a specific job listing. It utilizes the hook to extract the job ID from the URL, fetches the list of bidders and job details from the blockchain, and displays them. ViewBidders useParams 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 component for navigation. Header https://gist.github.com/covelitein/e86b934f0c739036f2624e6f8e126293?embedable=true Authenticate Page The 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. Authenticate It also includes a component for navigation and provides feedback to the user through toast notifications. Header https://gist.github.com/covelitein/5b323f31b35e8e4b186993daee2b1cec?embedable=true RecentConversations 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. https://gist.github.com/covelitein/579e94cf4a5bf489cad32201a807a7e3?embedable=true Chats Page The 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. Chats The component also includes a for navigation and maintains a scrolling chat container for easy viewing of messages. Header https://gist.github.com/covelitein/039caf1ec6fb79cf6928627710209472?embedable=true 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 within the folder, and paste the code below inside. blockchain.jsx src >> services https://gist.github.com/Daltonic/9f69b20f66d9b49d2c90debcf3d8aeda?embedable=true Here's an overview of what this script does: It imports necessary dependencies, including contract ABI (Application Binary Interface), contract addresses, and Ethereum-related libraries. Defines utility functions like and to convert between Ether and Wei. toWei fromWei Defines a function to establish a connection to the Ethereum blockchain and retrieve a contract instance. getEthereumContract checks if a user's Ethereum wallet (e.g., MetaMask) is connected and handles account changes and chain changes. isWalletConnected Functions like , , , , , , , , , , , , , , , , , , , and are defined to interact with the smart contract for various actions such as creating jobs, bidding, accepting bids, and more. connectWallet addJobListing updateJob deleteJob bidForJob acceptBid dispute resolved revoke payout bidStatus getBidders getFreelancers getAcceptedFreelancer getJobs getMyJobs getJob getMyBidJobs getMyGigs loadData , , and functions format data retrieved from the blockchain into structured objects for easier management and display. structuredJobs structuredBidder structuredFreelancers 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 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 within the folder, and paste the code below inside. chat.jsx src >> services https://gist.github.com/covelitein/76af89be61f44450498220da9bcb4572?embedable=true Here is a breakdown of how this script works: It imports the CometChat library ( ) and a function ( ) from a custom store. CometChat getGlobalState It defines constants ( ) for the CometChat application settings, including the App ID, region, and authentication key. These settings are typically stored in environment variables for security. CONSTANTS The 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. initCometChat The 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 it with an error in case of failure. loginWithCometChat The 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 it with an error in case of failure. signUpWithCometChat The function logs the current user out of CometChat. It returns a promise that resolves upon successful logout or rejects in case of an error. logOutWithCometChat The function checks if a user is currently logged in to CometChat and returns a promise that resolves to the logged-in user or rejects it with an error. isUserLoggedIn The function retrieves user information from CometChat based on the provided UID. It returns a promise that resolves to the user object or rejects it with an error. getUser The 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. getMessages The function sends a text message to a specified receiver (receiverID) through CometChat. It returns a promise that resolves to the sent message or rejects it with an error. sendMessage The 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. getConversations The 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 it with an error. listenForMessage 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 file, which serves as a state management library. store The Store File This script provides a central state management system for a React application using the library. It also includes utility functions for text truncation and date formatting. react-hooks-global-state You will have to create a file called within the folder, and paste the code below inside. index.jsx src >> store https://gist.github.com/covelitein/af525c04ae5c5610da6a8e9b48c6072d?embedable=true Here's an overview of what this script does: It imports the function from the library. createGlobalState react-hooks-global-state It uses the function to create a global state container with initial state values. createGlobalState This global state container is then deconstructed into three functions: , , and . setGlobalState useGlobalState getGlobalState : A function used to set or update global state values. It takes a key and a new value as arguments. setGlobalState : 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. useGlobalState : A function that retrieves the current value associated with a specified key from the global state. getGlobalState It defines a 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. truncate It defines a 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. formatDate It defines a 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. timestampToDate 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 file is the entry point for the application. It initializes the CometChat service, sets up dependencies, and renders the React application using the component within a . It creates a root element for rendering and sets up the necessary configurations for the application to start running. index.jsx App BrowserRouter To use this code, you will need to replace the code below inside of the and files in the folder of your project. index.jsx index.css src https://gist.github.com/covelitein/0fce1ceb8686eb85216ad9dc1b9ef4f9?embedable=true https://gist.github.com/covelitein/5b2802e7166befcb0ed5751cf4ef5c1d?embedable=true 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: and then **yarn hardhat run scripts/deploy.js** **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 do it. https://www.youtube.com/watch?v=mVxRzkvX_w0&embedable=true 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 and YouTube channel exploring our website for additional resources. Best wishes on your journey of innovation and discovery in the world of web3 and decentralized applications! About the Author I am a web3 developer and the founder of , 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. Dapp Mentors I run a where I share tutorials and tips on web3 development, and I regularly post articles online about the latest trends in the blockchain space. YouTube channel called Dapp Mentors Stay connected with us, and join our communities on Discord: Twitter: LinkedIn: GitHub: Website: Join Follow Connect Explore Visit