FreeLancing Platform (solidity)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
/**
* @title FreelancePlatform
* @dev Smart contract for a decentralized freelance platform with escrow functionality
*/
contract FreelancePlatform {
// State variables
address public owner;
uint256 public platformFee; // Platform fee in basis points (1% = 100)
uint256 public jobCount;
uint256 public proposalCount;
uint256 public contractCount;
// Enums
enum JobStatus { Open, InProgress, Completed, Cancelled }
enum ContractStatus { Created, Started, Completed, Cancelled, Disputed }
enum DisputeStatus { None, InProgress, ResolvedForClient, ResolvedForFreelancer }
// Structs
struct Profile {
string name;
string description;
string[] skills;
bool isRegistered;
bool isClient;
bool isFreelancer;
uint256 clientRating; // Average rating as client (out of 100)
uint256 clientRatingCount;
uint256 freelancerRating; // Average rating as freelancer (out of 100)
uint256 freelancerRatingCount;
uint256[] completedJobs;
}
struct Job {
uint256 id;
address client;
string title;
string description;
string[] requiredSkills;
uint256 budget;
uint256 deadline; // Unix timestamp
JobStatus status;
uint256[] proposalIds;
uint256 selectedProposal;
uint256 contractId;
bool isFixed; // Fixed price vs hourly
uint256 createdAt;
}
struct Proposal {
uint256 id;
uint256 jobId;
address freelancer;
uint256 price; // Total price for fixed jobs OR hourly rate for hourly jobs
string description;
uint256 estimatedTime; // In days for fixed jobs OR hours for hourly jobs
uint256 createdAt;
bool selected;
}
struct WorkContract {
uint256 id;
uint256 jobId;
uint256 proposalId;
address client;
address freelancer;
uint256 amount;
uint256 deposit; // Client's deposit
ContractStatus status;
uint256 startDate;
uint256 endDate;
uint256 createdAt;
uint256 completedAt;
DisputeStatus disputeStatus;
string disputeReason;
uint256 milestones; // Number of milestones
uint256 completedMilestones; // Number of completed milestones
mapping(uint256 => Milestone) milestoneDetails;
}
struct Milestone {
string description;
uint256 amount;
bool completed;
bool paid;
}
// Mappings
mapping(address => Profile) public profiles;
mapping(uint256 => Job) public jobs;
mapping(uint256 => Proposal) public proposals;
mapping(uint256 => WorkContract) public contracts;
mapping(address => uint256[]) public clientJobs; // Jobs posted by a client
mapping(address => uint256[]) public freelancerProposals; // Proposals submitted by a freelancer
mapping(address => uint256[]) public freelancerContracts; // Contracts a freelancer is involved in
mapping(address => uint256[]) public clientContracts; // Contracts a client is involved in
// Events
event ProfileCreated(address indexed user, bool isClient, bool isFreelancer);
event JobPosted(uint256 indexed jobId, address indexed client, string title, uint256 budget);
event ProposalSubmitted(uint256 indexed proposalId, uint256 indexed jobId, address indexed freelancer);
event ProposalSelected(uint256 indexed jobId, uint256 indexed proposalId, address indexed freelancer);
event ContractCreated(uint256 indexed contractId, uint256 indexed jobId, address client, address freelancer);
event MilestoneAdded(uint256 indexed contractId, uint256 milestoneIndex, string description, uint256 amount);
event MilestoneCompleted(uint256 indexed contractId, uint256 milestoneIndex);
event MilestonePaid(uint256 indexed contractId, uint256 milestoneIndex, uint256 amount);
event ContractStarted(uint256 indexed contractId);
event ContractCompleted(uint256 indexed contractId);
event ContractCancelled(uint256 indexed contractId);
event DisputeRaised(uint256 indexed contractId, string reason);
event DisputeResolved(uint256 indexed contractId, DisputeStatus resolution);
event FundsDeposited(uint256 indexed contractId, address indexed sender, uint256 amount);
event FundsWithdrawn(uint256 indexed contractId, address indexed recipient, uint256 amount);
event RatingSubmitted(address indexed rated, address indexed rater, uint256 rating, bool isClientRating);
// Modifiers
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
modifier onlyRegistered() {
require(profiles[msg.sender].isRegistered, "User not registered");
_;
}
modifier onlyClient() {
require(profiles[msg.sender].isClient, "Only clients can call this function");
_;
}
modifier onlyFreelancer() {
require(profiles[msg.sender].isFreelancer, "Only freelancers can call this function");
_;
}
modifier onlyJobClient(uint256 _jobId) {
require(jobs[_jobId].client == msg.sender, "Only job client can call this function");
_;
}
modifier onlyContractParticipant(uint256 _contractId) {
require(
contracts[_contractId].client == msg.sender || contracts[_contractId].freelancer == msg.sender,
"Only contract participants can call this function"
);
_;
}
// Constructor
constructor() {
owner = msg.sender;
platformFee = 100; // Set as basis points (100 = 1%)
jobCount = 0;
proposalCount = 0;
contractCount = 0;
}
// Function to update platform fee (only owner)
function updatePlatformFee(uint256 _newFee) external onlyOwner {
require(_newFee <= 1000, "Fee cannot exceed 10%"); // 1000 basis points = 10%
platformFee = _newFee;
}
// Function to register a new user
function registerProfile(
string memory _name,
string memory _description,
string[] memory _skills,
bool _isClient,
bool _isFreelancer
) external {
require(!profiles[msg.sender].isRegistered, "Profile already registered");
require(_isClient || _isFreelancer, "Must register as client or freelancer");
Profile storage newProfile = profiles[msg.sender];
newProfile.name = _name;
newProfile.description = _description;
newProfile.skills = _skills;
newProfile.isRegistered = true;
newProfile.isClient = _isClient;
newProfile.isFreelancer = _isFreelancer;
newProfile.clientRating = 0;
newProfile.clientRatingCount = 0;
newProfile.freelancerRating = 0;
newProfile.freelancerRatingCount = 0;
emit ProfileCreated(msg.sender, _isClient, _isFreelancer);
}
// Function to update a user's profile
function updateProfile(
string memory _name,
string memory _description,
string[] memory _skills,
bool _isClient,
bool _isFreelancer
) external onlyRegistered {
require(_isClient || _isFreelancer, "Must be client or freelancer");
Profile storage profile = profiles[msg.sender];
profile.name = _name;
profile.description = _description;
profile.skills = _skills;
profile.isClient = _isClient;
profile.isFreelancer = _isFreelancer;
}
// Function to post a new job (only clients)
function postJob(
string memory _title,
string memory _description,
string[] memory _requiredSkills,
uint256 _budget,
uint256 _deadline,
bool _isFixed
) external onlyRegistered onlyClient {
require(bytes(_title).length > 0, "Title cannot be empty");
require(_budget > 0, "Budget must be greater than 0");
require(_deadline > block.timestamp, "Deadline must be in the future");
uint256 newJobId = jobCount++;
Job storage newJob = jobs[newJobId];
newJob.id = newJobId;
newJob.client = msg.sender;
newJob.title = _title;
newJob.description = _description;
newJob.requiredSkills = _requiredSkills;
newJob.budget = _budget;
newJob.deadline = _deadline;
newJob.status = JobStatus.Open;
newJob.isFixed = _isFixed;
newJob.createdAt = block.timestamp;
clientJobs[msg.sender].push(newJobId);
emit JobPosted(newJobId, msg.sender, _title, _budget);
}
// Function to submit a proposal for a job (only freelancers)
function submitProposal(
uint256 _jobId,
uint256 _price,
string memory _description,
uint256 _estimatedTime
) external onlyRegistered onlyFreelancer {
Job storage job = jobs[_jobId];
require(job.status == JobStatus.Open, "Job is not open for proposals");
require(job.deadline > block.timestamp, "Job deadline has passed");
require(_price > 0, "Price must be greater than 0");
require(bytes(_description).length > 0, "Description cannot be empty");
require(_estimatedTime > 0, "Estimated time must be greater than 0");
require(job.client != msg.sender, "Cannot submit proposal to own job");
uint256 newProposalId = proposalCount++;
Proposal storage newProposal = proposals[newProposalId];
newProposal.id = newProposalId;
newProposal.jobId = _jobId;
newProposal.freelancer = msg.sender;
newProposal.price = _price;
newProposal.description = _description;
newProposal.estimatedTime = _estimatedTime;
newProposal.createdAt = block.timestamp;
newProposal.selected = false;
job.proposalIds.push(newProposalId);
freelancerProposals[msg.sender].push(newProposalId);
emit ProposalSubmitted(newProposalId, _jobId, msg.sender);
}
// Function to select a proposal (only job client)
function selectProposal(uint256 _jobId, uint256 _proposalId) external onlyRegistered onlyJobClient(_jobId) {
Job storage job = jobs[_jobId];
Proposal storage proposal = proposals[_proposalId];
require(job.status == JobStatus.Open, "Job is not open");
require(proposal.jobId == _jobId, "Proposal not for this job");
require(!proposal.selected, "Proposal already selected");
job.selectedProposal = _proposalId;
proposal.selected = true;
// Create a contract
uint256 newContractId = contractCount++;
WorkContract storage newContract = contracts[newContractId];
newContract.id = newContractId;
newContract.jobId = _jobId;
newContract.proposalId = _proposalId;
newContract.client = job.client;
newContract.freelancer = proposal.freelancer;
newContract.amount = proposal.price;
newContract.status = ContractStatus.Created;
newContract.createdAt = block.timestamp;
newContract.disputeStatus = DisputeStatus.None;
job.contractId = newContractId;
job.status = JobStatus.InProgress;
clientContracts[job.client].push(newContractId);
freelancerContracts[proposal.freelancer].push(newContractId);
emit ProposalSelected(_jobId, _proposalId, proposal.freelancer);
emit ContractCreated(newContractId, _jobId, job.client, proposal.freelancer);
}
// Function to add milestones to a contract (only client)
function addMilestones(
uint256 _contractId,
string[] memory _descriptions,
uint256[] memory _amounts
) external onlyRegistered {
WorkContract storage workContract = contracts[_contractId];
require(workContract.client == msg.sender, "Only client can add milestones");
require(workContract.status == ContractStatus.Created, "Contract already started");
require(_descriptions.length == _amounts.length, "Arrays must have same length");
require(_descriptions.length > 0, "Must add at least one milestone");
uint256 totalAmount = 0;
for (uint256 i = 0; i < _amounts.length; i++) {
totalAmount += _amounts[i];
}
require(totalAmount == workContract.amount, "Total milestone amounts must equal contract amount");
// Add milestones
for (uint256 i = 0; i < _descriptions.length; i++) {
workContract.milestoneDetails[i] = Milestone({
description: _descriptions[i],
amount: _amounts[i],
completed: false,
paid: false
});
emit MilestoneAdded(_contractId, i, _descriptions[i], _amounts[i]);
}
workContract.milestones = _descriptions.length;
}
// Function for client to deposit funds to start the contract
function depositFunds(uint256 _contractId) external payable onlyRegistered {
WorkContract storage workContract = contracts[_contractId];
require(workContract.client == msg.sender, "Only client can deposit funds");
require(workContract.status == ContractStatus.Created, "Contract not in created state");
require(msg.value == workContract.amount, "Deposit must equal contract amount");
workContract.deposit = msg.value;
workContract.status = ContractStatus.Started;
workContract.startDate = block.timestamp;
emit FundsDeposited(_contractId, msg.sender, msg.value);
emit ContractStarted(_contractId);
}
// Function for freelancer to mark a milestone as completed
function completeMilestone(uint256 _contractId, uint256 _milestoneIndex) external onlyRegistered {
WorkContract storage workContract = contracts[_contractId];
require(workContract.freelancer == msg.sender, "Only freelancer can complete milestone");
require(workContract.status == ContractStatus.Started, "Contract not in started state");
require(_milestoneIndex < workContract.milestones, "Invalid milestone index");
require(!workContract.milestoneDetails[_milestoneIndex].completed, "Milestone already completed");
workContract.milestoneDetails[_milestoneIndex].completed = true;
workContract.completedMilestones++;
emit MilestoneCompleted(_contractId, _milestoneIndex);
}
// Function for client to release payment for a milestone
function releaseMilestonePayment(uint256 _contractId, uint256 _milestoneIndex) external onlyRegistered {
WorkContract storage workContract = contracts[_contractId];
require(workContract.client == msg.sender, "Only client can release payment");
require(workContract.status == ContractStatus.Started, "Contract not in started state");
require(_milestoneIndex < workContract.milestones, "Invalid milestone index");
require(workContract.milestoneDetails[_milestoneIndex].completed, "Milestone not completed");
require(!workContract.milestoneDetails[_milestoneIndex].paid, "Milestone already paid");
Milestone storage milestone = workContract.milestoneDetails[_milestoneIndex];
uint256 platformFeeAmount = (milestone.amount * platformFee) / 10000; // Calculate platform fee
uint256 paymentAmount = milestone.amount - platformFeeAmount; // Calculate freelancer payment
milestone.paid = true;
// Transfer payment to freelancer
(bool success, ) = workContract.freelancer.call{value: paymentAmount}("");
require(success, "Transfer to freelancer failed");
// Transfer platform fee to owner
(bool platformSuccess, ) = owner.call{value: platformFeeAmount}("");
require(platformSuccess, "Transfer of platform fee failed");
emit MilestonePaid(_contractId, _milestoneIndex, milestone.amount);
// Check if all milestones are completed and paid
bool allCompleted = true;
for (uint256 i = 0; i < workContract.milestones; i++) {
if (!workContract.milestoneDetails[i].paid) {
allCompleted = false;
break;
}
}
// If all milestones are completed and paid, complete the contract
if (allCompleted) {
workContract.status = ContractStatus.Completed;
workContract.completedAt = block.timestamp;
workContract.endDate = block.timestamp;
// Update job status
jobs[workContract.jobId].status = JobStatus.Completed;
// Add job to completed jobs for both client and freelancer
profiles[workContract.client].completedJobs.push(workContract.jobId);
profiles[workContract.freelancer].completedJobs.push(workContract.jobId);
emit ContractCompleted(_contractId);
}
}
// Function to cancel a contract (only client before work starts)
function cancelContract(uint256 _contractId) external onlyRegistered {
WorkContract storage workContract = contracts[_contractId];
require(
workContract.client == msg.sender || workContract.freelancer == msg.sender,
"Only contract participants can cancel"
);
require(workContract.status == ContractStatus.Created || workContract.status == ContractStatus.Started,
"Cannot cancel contract in current state");
// If contract has started (funds deposited), only refund if no milestones are paid
if (workContract.status == ContractStatus.Started) {
bool anyMilestonePaid = false;
for (uint256 i = 0; i < workContract.milestones; i++) {
if (workContract.milestoneDetails[i].paid) {
anyMilestonePaid = true;
break;
}
}
require(!anyMilestonePaid, "Cannot cancel after milestones are paid");
// Refund deposit to client
(bool success, ) = workContract.client.call{value: workContract.deposit}("");
require(success, "Refund to client failed");
}
workContract.status = ContractStatus.Cancelled;
jobs[workContract.jobId].status = JobStatus.Cancelled;
emit ContractCancelled(_contractId);
}
// Function to raise a dispute (only contract participants)
function raiseDispute(uint256 _contractId, string memory _reason) external onlyRegistered onlyContractParticipant(_contractId) {
WorkContract storage workContract = contracts[_contractId];
require(workContract.status == ContractStatus.Started, "Contract not in started state");
require(workContract.disputeStatus == DisputeStatus.None, "Dispute already raised");
workContract.disputeStatus = DisputeStatus.InProgress;
workContract.disputeReason = _reason;
emit DisputeRaised(_contractId, _reason);
}
// Function to resolve a dispute (only owner - platform admin)
function resolveDispute(
uint256 _contractId,
DisputeStatus _resolution,
uint256[] memory _clientRefundMilestones,
uint256[] memory _freelancerPaymentMilestones
) external onlyOwner {
WorkContract storage workContract = contracts[_contractId];
require(workContract.disputeStatus == DisputeStatus.InProgress, "No active dispute");
require(
_resolution == DisputeStatus.ResolvedForClient || _resolution == DisputeStatus.ResolvedForFreelancer,
"Invalid resolution status"
);
workContract.disputeStatus = _resolution;
// Process refunds to client
for (uint256 i = 0; i < _clientRefundMilestones.length; i++) {
uint256 milestoneIndex = _clientRefundMilestones[i];
require(milestoneIndex < workContract.milestones, "Invalid milestone index");
Milestone storage milestone = workContract.milestoneDetails[milestoneIndex];
if (!milestone.paid) {
uint256 refundAmount = milestone.amount;
// Mark as paid to prevent double payments
milestone.paid = true;
// Transfer refund to client
(bool success, ) = workContract.client.call{value: refundAmount}("");
require(success, "Refund to client failed");
}
}
// Process payments to freelancer
for (uint256 i = 0; i < _freelancerPaymentMilestones.length; i++) {
uint256 milestoneIndex = _freelancerPaymentMilestones[i];
require(milestoneIndex < workContract.milestones, "Invalid milestone index");
Milestone storage milestone = workContract.milestoneDetails[milestoneIndex];
if (!milestone.paid && milestone.completed) {
uint256 platformFeeAmount = (milestone.amount * platformFee) / 10000;
uint256 paymentAmount = milestone.amount - platformFeeAmount;
// Mark as paid to prevent double payments
milestone.paid = true;
// Transfer payment to freelancer
(bool success, ) = workContract.freelancer.call{value: paymentAmount}("");
require(success, "Payment to freelancer failed");
// Transfer platform fee to owner
(bool platformSuccess, ) = owner.call{value: platformFeeAmount}("");
require(platformSuccess, "Transfer of platform fee failed");
}
}
// Update contract status
workContract.status = ContractStatus.Completed;
workContract.completedAt = block.timestamp;
workContract.endDate = block.timestamp;
// Update job status
jobs[workContract.jobId].status = JobStatus.Completed;
emit DisputeResolved(_contractId, _resolution);
emit ContractCompleted(_contractId);
}
// Function to rate a user after contract completion
function rateUser(address _user, uint256 _rating, bool _isRatingClient) external onlyRegistered {
require(_rating >= 0 && _rating <= 100, "Rating must be between 0 and 100");
require(_user != msg.sender, "Cannot rate yourself");
require(profiles[_user].isRegistered, "User not registered");
// Verify that rater and rated have worked together
bool hasWorkedTogether = false;
if (_isRatingClient) {
// Freelancer rating client
require(profiles[_user].isClient, "Rated user is not a client");
require(profiles[msg.sender].isFreelancer, "Only freelancers can rate clients");
for (uint256 i = 0; i < freelancerContracts[msg.sender].length; i++) {
uint256 contractId = freelancerContracts[msg.sender][i];
if (contracts[contractId].client == _user &&
contracts[contractId].status == ContractStatus.Completed) {
hasWorkedTogether = true;
break;
}
}
} else {
// Client rating freelancer
require(profiles[_user].isFreelancer, "Rated user is not a freelancer");
require(profiles[msg.sender].isClient, "Only clients can rate freelancers");
for (uint256 i = 0; i < clientContracts[msg.sender].length; i++) {
uint256 contractId = clientContracts[msg.sender][i];
if (contracts[contractId].freelancer == _user &&
contracts[contractId].status == ContractStatus.Completed) {
hasWorkedTogether = true;
break;
}
}
}
require(hasWorkedTogether, "You have not worked with this user");
// Update ratings
if (_isRatingClient) {
// Update client rating
Profile storage profile = profiles[_user];
uint256 totalRating = profile.clientRating * profile.clientRatingCount;
profile.clientRatingCount++;
profile.clientRating = (totalRating + _rating) / profile.clientRatingCount;
} else {
// Update freelancer rating
Profile storage profile = profiles[_user];
uint256 totalRating = profile.freelancerRating * profile.freelancerRatingCount;
profile.freelancerRatingCount++;
profile.freelancerRating = (totalRating + _rating) / profile.freelancerRatingCount;
}
emit RatingSubmitted(_user, msg.sender, _rating, _isRatingClient);
}
// View functions
function getProfileData(address _user) external view returns (
string memory name,
string memory description,
bool isClient,
bool isFreelancer,
uint256 clientRating,
uint256 clientRatingCount,
uint256 freelancerRating,
uint256 freelancerRatingCount,
uint256 completedJobCount
) {
Profile storage profile = profiles[_user];
return (
profile.name,
profile.description,
profile.isClient,
profile.isFreelancer,
profile.clientRating,
profile.clientRatingCount,
profile.freelancerRating,
profile.freelancerRatingCount,
profile.completedJobs.length
);
}
function getProfileSkills(address _user) external view returns (string[] memory) {
return profiles[_user].skills;
}
function getCompletedJobs(address _user) external view returns (uint256[] memory) {
return profiles[_user].completedJobs;
}
function getJobProposals(uint256 _jobId) external view returns (uint256[] memory) {
return jobs[_jobId].proposalIds;
}
function getClientJobs(address _client) external view returns (uint256[] memory) {
return clientJobs[_client];
}
function getFreelancerProposals(address _freelancer) external view returns (uint256[] memory) {
return freelancerProposals[_freelancer];
}
function getContractDetails(uint256 _contractId) external view returns (
uint256 id,
uint256 jobId,
uint256 proposalId,
address client,
address freelancer,
uint256 amount,
uint256 deposit,
ContractStatus status,
uint256 startDate,
uint256 endDate,
uint256 completedMilestones,
uint256 totalMilestones,
DisputeStatus disputeStatus
) {
WorkContract storage workContract = contracts[_contractId];
return (
workContract.id,
workContract.jobId,
workContract.proposalId,
workContract.client,
workContract.freelancer,
workContract.amount,
workContract.deposit,
workContract.status,
workContract.startDate,
workContract.endDate,
workContract.completedMilestones,
workContract.milestones,
workContract.disputeStatus
);
}
// Function to get job details
function getJobDetails(uint256 _jobId) external view returns (
uint256 id,
address client,
string memory title,
string memory description,
string[] memory requiredSkills,
uint256 budget,
uint256 deadline,
JobStatus status,
uint256[] memory proposalIds,
uint256 selectedProposal,
uint256 contractId,
bool isFixed,
uint256 createdAt
) {
Job storage job = jobs[_jobId];
return (
job.id,
job.client,
job.title,
job.description,
job.requiredSkills,
job.budget,
job.deadline,
job.status,
job.proposalIds,
job.selectedProposal,
job.contractId,
job.isFixed,
job.createdAt
);
}
function getMilestoneDetails(uint256 _contractId, uint256 _milestoneIndex) external view returns (
string memory description,
uint256 amount,
bool completed,
bool paid
) {
require(_milestoneIndex < contracts[_contractId].milestones, "Invalid milestone index");
Milestone storage milestone = contracts[_contractId].milestoneDetails[_milestoneIndex];
return (
milestone.description,
milestone.amount,
milestone.completed,
milestone.paid
);
}
// Emergency withdrawal function for platform owner
function emergencyWithdraw() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No funds to withdraw");
(bool success, ) = owner.call{value: balance}("");
require(success, "Withdrawal failed");
}
// Function to get contract between specific client and freelancer for a job
function getContractByJob(uint256 _jobId) external view returns (uint256) {
return jobs[_jobId].contractId;
}
// Fallback and receive functions
receive() external payable {}
fallback() external payable {}
}
The contract implements a decentralized freelance platform with escrow functionality, allowing clients to post jobs, freelancers to submit proposals, and secure payments through milestones.
License and Pragma
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;
- SPDX-License-Identifier: Specifies the contract is unlicensed
- Pragma: Sets the Solidity compiler version to 0.8.19 or higher
Contract Structure
State Variables
address public owner;
uint256 public platformFee; // Platform fee in basis points (1% = 100)
uint256 public jobCount;
uint256 public proposalCount;
uint256 public contractCount;
- owner: Address of the contract deployer (platform admin)
- platformFee: Percentage fee taken by the platform (in basis points)
- jobCount, proposalCount, contractCount: Counters for tracking IDs
Enums
enum JobStatus { Open, InProgress, Completed, Cancelled }
enum ContractStatus { Created, Started, Completed, Cancelled, Disputed }
enum DisputeStatus { None, InProgress, ResolvedForClient, ResolvedForFreelancer }
- Define status types for jobs, contracts, and disputes
Structs
Profile
struct Profile {
string name;
string description;
string[] skills;
bool isRegistered;
bool isClient;
bool isFreelancer;
uint256 clientRating; // Average rating as client (out of 100)
uint256 clientRatingCount;
uint256 freelancerRating; // Average rating as freelancer (out of 100)
uint256 freelancerRatingCount;
uint256[] completedJobs;
}
- Stores user profile information including ratings and completed jobs
Job
struct Job {
uint256 id;
address client;
string title;
string description;
string[] requiredSkills;
uint256 budget;
uint256 deadline; // Unix timestamp
JobStatus status;
uint256[] proposalIds;
uint256 selectedProposal;
uint256 contractId;
bool isFixed; // Fixed price vs hourly
uint256 createdAt;
}
- Contains job details posted by clients
Proposal
struct Proposal {
uint256 id;
uint256 jobId;
address freelancer;
uint256 price; // Total price for fixed jobs OR hourly rate for hourly jobs
string description;
uint256 estimatedTime; // In days for fixed jobs OR hours for hourly jobs
uint256 createdAt;
bool selected;
}
- Freelancer’s bid for a job
WorkContract
struct WorkContract {
uint256 id;
uint256 jobId;
uint256 proposalId;
address client;
address freelancer;
uint256 amount;
uint256 deposit; // Client's deposit
ContractStatus status;
uint256 startDate;
uint256 endDate;
uint256 createdAt;
uint256 completedAt;
DisputeStatus disputeStatus;
string disputeReason;
uint256 milestones; // Number of milestones
uint256 completedMilestones; // Number of completed milestones
mapping(uint256 => Milestone) milestoneDetails;
}
- Contract between client and freelancer with payment details
Milestone
struct Milestone {
string description;
uint256 amount;
bool completed;
bool paid;
}
- Payment milestones within a contract
Mappings
mapping(address => Profile) public profiles;
mapping(uint256 => Job) public jobs;
mapping(uint256 => Proposal) public proposals;
mapping(uint256 => WorkContract) public contracts;
mapping(address => uint256[]) public clientJobs;
mapping(address => uint256[]) public freelancerProposals;
mapping(address => uint256[]) public freelancerContracts;
mapping(address => uint256[]) public clientContracts;
- Store relationships between addresses and their jobs/proposals/contracts
Events
Numerous events emitted for important contract actions like:
- Profile creation
- Job posting
- Proposal submission/selection
- Contract creation/completion
- Milestone actions
- Dispute handling
- Fund transfers
- Ratings
Modifiers
onlyOwner()
onlyRegistered()
onlyClient()
onlyFreelancer()
onlyJobClient(uint256 _jobId)
onlyContractParticipant(uint256 _contractId)
- Access control modifiers to restrict function calls
Core Functions
1. Profile Management
registerProfile(): Creates a new user profileupdateProfile(): Updates existing profile information
2. Job Management
postJob(): Allows clients to create new job postingssubmitProposal(): Lets freelancers bid on jobsselectProposal(): Client selects a freelancer’s proposal
3. Contract Management
addMilestones(): Client defines payment milestonesdepositFunds(): Client deposits funds to start contractcompleteMilestone(): Freelancer marks milestone as completereleaseMilestonePayment(): Client releases payment for completed milestone
4. Dispute Resolution
raiseDispute(): Either party can raise a disputeresolveDispute(): Platform owner resolves disputes and distributes funds
5. Rating System
rateUser(): Allows participants to rate each other after job completion
6. View Functions
Various functions to retrieve contract, job, profile, and milestone details
7. Utility Functions
updatePlatformFee(): Owner can adjust platform feeemergencyWithdraw(): Owner can withdraw funds in emergency
Workflow
- Users register as clients or freelancers
- Clients post jobs with requirements and budget
- Freelancers submit proposals for jobs
- Client selects a proposal, creating a contract
- Client adds payment milestones and deposits funds
- Freelancer completes milestones which client approves and pays
- After all milestones are paid, contract completes
- Parties can rate each other
Security Features
- Role-based access control (modifiers)
- Funds held in escrow until milestones are completed
- Dispute resolution mechanism
- Input validation on all functions
- Platform fee deducted from payments
- Emergency withdrawal for owner
This contract provides a complete decentralized solution for freelance work with secure payment handling and dispute resolution.