1st August 2025
5 min read
Freelance work is increasingly global, but traditional platforms still face issues like delayed payments, high fees, and trust concerns. A blockchain-based freelance system, built using blockchain development services, can eliminate middlemen, ensure fair payments through smart contracts, and offer transparent dispute resolution.
A decentralized freelance payment system on Ethereum (or any EVM-compatible chain) featuring:
Freelance Escrow - Handles escrow, job creation, approval, payment withdrawal, disputes, and ratings.
This contract manages the core features of the system, such as creating freelance jobs, approving work, withdrawing payments, rating both parties, and handling disputes. Here's the contract code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract FreelanceEscrow {
struct Job {
address client;
address freelancer;
uint256 amount;
bool isApproved;
bool isWithdrawn;
bool disputeOpened;
address[] voters;
uint8 votesForFreelancer;
uint8 freelancerRating;
uint8 clientRating;
bool freelancerRated;
bool clientRated;
mapping(address => bool) hasVoted;
}
Job[] public jobs;
event JobCreated(uint256 indexed jobId, address client, address freelancer, uint256 amount);
event JobApproved(uint256 indexed jobId);
event PaymentWithdrawn(uint256 indexed jobId);
event FreelancerRated(uint256 indexed jobId, uint8 rating);
event ClientRated(uint256 indexed jobId, uint8 rating);
event DisputeOpened(uint256 indexed jobId);
event DisputeVoted(uint256 indexed jobId, bool supportFreelancer);
// Public method for creating freelance jobs
function createFreelanceJob(address _freelancer) external payable {
require(msg.value > 0, 'Escrow must have funds');
uint256 jobId = jobs.length;
Job storage newJob = jobs.push();
newJob.client = msg.sender;
newJob.freelancer = _freelancer;
newJob.amount = msg.value;
newJob.isApproved = false;
newJob.isWithdrawn = false;
newJob.disputeOpened = false;
newJob.voters = new address[](0) ;
newJob.votesForFreelancer = 0;
newJob.freelancerRating = 0;
newJob.clientRating = 0;
newJob.freelancerRated = false;
newJob.clientRated = false;
emit JobCreated(jobId, msg.sender, _freelancer, msg.value);
}
// Approve work function (public)
function approveWork(uint256 jobId) external {
Job storage job = jobs[jobId];
require(msg.sender == job.client, 'Only client can approve');
require(!job.isApproved, 'Already approved');
job.isApproved = true;
emit JobApproved(jobId);
}
// Withdraw payment function (public)
function withdrawPayment(uint256 jobId) external {
Job storage job = jobs[jobId];
require(msg.sender == job.freelancer, 'Only freelancer');
require(!job.isWithdrawn, 'Already withdrawn');
require(
job.isApproved || (job.disputeOpened && job.votesForFreelancer > job.voters.length / 2),
'Not eligible for withdrawal'
);
job.isWithdrawn = true;
payable(job.freelancer).transfer(job.amount);
emit PaymentWithdrawn(jobId);
}
// Rate freelancer (public)
function rateFreelancer(uint256 jobId, uint8 _rating) external {
Job storage job = jobs[jobId];
require(msg.sender == job.client, 'Only client');
require(!job.freelancerRated, 'Already rated');
require(_rating <= 5, 'Rating out of range');
job.freelancerRating = _rating;
job.freelancerRated = true;
emit FreelancerRated(jobId, _rating);
}
// Rate client (public)
function rateClient(uint256 jobId, uint8 _rating) external {
Job storage job = jobs[jobId];
require(msg.sender == job.freelancer, 'Only freelancer');
require(!job.clientRated, 'Already rated');
require(_rating <= 5, 'Rating out of range');
job.clientRating = _rating;
job.clientRated = true;
emit ClientRated(jobId, _rating);
}
// Open dispute (public)
function openDispute(uint256 jobId, address[] calldata _voters) external {
Job storage job = jobs[jobId];
require(msg.sender == job.client || msg.sender == job.freelancer, 'Unauthorized');
require(!job.disputeOpened, 'Already open');
require(_voters.length > 0, 'Voters required');
job.disputeOpened = true;
job.voters = _voters;
emit DisputeOpened(jobId);
}
// Vote in dispute (public)
function voteDispute(uint256 jobId, bool supportFreelancer) external {
Job storage job = jobs[jobId];
require(job.disputeOpened, 'No dispute active');
require(!job.hasVoted[msg.sender], 'Already voted');
bool validVoter = false;
for (uint i = 0; i < job.voters.length; i++) {
if (job.voters[i] == msg.sender) {
validVoter = true;
break;
}
}
require(validVoter, 'Not an assigned voter');
job.hasVoted[msg.sender] = true;
if (supportFreelancer) {
job.votesForFreelancer++;
}
emit DisputeVoted(jobId, supportFreelancer);
}
// Get job details
function getJob(uint256 jobId) external view returns (
address client,
address freelancer,
uint256 amount,
bool isApproved,
bool isWithdrawn,
bool disputeOpened,
uint8 votesForFreelancer,
uint8 freelancerRating,
uint8 clientRating,
bool freelancerRated,
bool clientRated
) {
Job storage job = jobs[jobId];
return (
job.client,
job.freelancer,
job.amount,
job.isApproved,
job.isWithdrawn,
job.disputeOpened,
job.votesForFreelancer,
job.freelancerRating,
job.clientRating,
job.freelancerRated,
job.clientRated
);
}
}Job Tracking: All jobs created are indexed and can be referenced in the factory contract for easy tracking.
This blockchain-based freelancer payment system empowers both freelancers and clients to interact directly, ensuring trustless, fair, and transparent transactions. With no platform fees, instant payouts, decentralized dispute resolution, and an on-chain reputation system, this solution is poised to redefine global freelance work, promoting fairness and reducing reliance on centralized platforms.
Ready to build your own decentralized freelance platform?
Get in touch with our blockchain experts to develop a secure, smart contract-powered payment system tailored to your needs. Contact us today.