DeWork is a decentralized freelance marketplace built on the Solana blockchain. It connects clients who need work done with workers who offer skills, replacing the role of a centralized intermediary with a transparent, programmable smart contract. All job lifecycle logic — posting, bidding, escrow, milestones, disputes, and payments — is enforced entirely on-chain.
Traditional freelance platforms act as trusted middlemen: they hold funds, arbitrate conflicts, and take a significant fee. DeWork replaces this with an Anchor-based Solana program. Funds are locked in a program-controlled escrow account when a proposal is accepted. They are only released to the worker when the client explicitly approves, or to the client if a dispute is resolved in their favor. No party — including the platform admin — can move funds outside of the defined instruction logic.
The repository is organized into two workspaces:
onchain/— the Anchor/Rust smart contract program deployed to Solanaoffchain-client/— a Rust client usinganchor-clientto interact with the deployed program, demonstrating the full job lifecycle
The program is identified by:
Program ID: FDp3JtCcWcsLBreHRdJC7dfuWtHVMhCNKfTApAbbt5EM
It is structured around eight instruction modules, each responsible for a distinct domain:
| Module | Responsibility |
|---|---|
global_flow |
Initialize the platform's global state (fee, treasury, admin) |
user_flow |
Register users as Client or Worker, manage worker profiles |
company_flow |
Register and verify company entities; create and mint utility tokens |
job_flow |
Post, update, cancel, and close job listings |
proposal_flow |
Submit, update, withdraw proposals; accept a proposal and lock escrow |
escrow_flow |
Release milestone payments to worker; refund client |
dispute_flow |
Raise a dispute with evidence, assign an arbitrator, resolve with split or winner |
admin_flow |
Manage arbitrator list; update platform fee (in basis points) and treasury address |
rating_flow |
Submit a numeric review and comment after job completion |
A Rust binary (tokio-based async) that demonstrates end-to-end interaction with the on-chain program using anchor-client. It walks through the full happy path: airdrop → global state init → user and worker registration → worker profile creation → job posting with milestones → proposal submission → account data fetch. This serves as both integration test and usage reference.
The program stores the following Program Derived Accounts (PDAs):
GlobalState— platform-wide config: admin, USDC mint, treasury address, platform fee in basis points, and job/user countersUserAccount— per-wallet record with role (ClientorWorker), registration timestamp, and active statusWorkerProfile— skill title, skill list, description, rate per job, and an active flag; seeded by[b"profile", worker_pubkey, index]CompanyProfile— company name, metadata URI, and verification statusJob— title, description, required skills, budget, deadline, status (Open,InProgress,Completed,Cancelled,Disputed), and a list ofMilestonestructsMilestone— per-milestone amount, description, and a booleanis_releasedflagProposal— proposed amount, pitch, worker reference, and statusEscrow— locked USDC amount tied to a specific job and workerDispute— evidence URI, assigned arbitrator, and resolution statusReview— numeric score and comment, linked to a jobArbitratorList— admin-managed list of approved arbitrator public keysTokenDiscount— optional struct on a job to apply a fee discount based on a utility token mint
A registered Client calls post_job_main with a title, description, required skills, USDC budget, Unix deadline, and an optional list of milestones. Each milestone carries an amount and description. An optional TokenDiscount can be attached to reduce the platform fee for clients holding a specific utility token.
A registered Worker with an active profile calls submit_proposal_main with a proposed USDC amount and a pitch. The client reviews and calls accept_proposal_and_initiate_escrow_main, which atomically marks the proposal as accepted and transfers the agreed USDC amount from the client's token account into a program-controlled escrow PDA.
The client calls release_payment_main. If milestones are defined, a milestone_index is provided and that specific milestone's amount is released. If no milestones exist, the full escrow amount is transferred to the worker's USDC token account, minus the platform fee which goes to the treasury.
Either party can call raise_dispute_main with an IPFS or Arweave URI pointing to off-chain evidence. An admin then calls assign_arbitrator_main to assign a trusted arbitrator from the on-chain list. The arbitrator resolves the dispute by calling resolve_dispute_main with either a clear winner or a percentage split (client_percentage out of 100), after which the escrow is distributed accordingly.
Companies can register with a name and metadata URI and become verified by an admin. Verified companies can create their own SPL utility token and mint supply to holders. Jobs can reference this token mint via TokenDiscount to offer reduced platform fees to clients who hold the token.
- Rust (stable)
- Solana CLI (
>= 1.18) - Anchor CLI (
>= 0.30) - Yarn
- Node.js (
>= 18)
- Rust (same toolchain)
- A running Solana local validator (
solana-test-validator)
git clone https://github.com/anubhav-auth/DeWork.git
cd DeWorkcd onchain
yarn install
anchor build
anchor deployThis deploys the program to localnet using the wallet at ~/.config/solana/id.json. The program ID is FDp3JtCcWcsLBreHRdJC7dfuWtHVMhCNKfTApAbbt5EM.
anchor testThis spins up a local validator, deploys the program, and runs the TypeScript test suite under onchain/tests/.
In a separate terminal, start the local validator if it is not already running:
solana-test-validatorThen, from the repository root:
cd offchain-client
cargo runThe client will generate keypairs, request airdrops, and execute the full job lifecycle — printing transaction signatures to stdout at each step.
DeWork/
├── onchain/ # Anchor workspace
│ ├── Anchor.toml
│ ├── programs/
│ │ └── onchain/
│ │ └── src/
│ │ ├── lib.rs # Program entrypoint, all instruction dispatchers
│ │ ├── instructions/ # Instruction modules by domain
│ │ │ ├── admin_flow/
│ │ │ ├── company_flow/
│ │ │ ├── dispute_flow/
│ │ │ ├── escrow_flow/
│ │ │ ├── global_flow/
│ │ │ ├── job_flow/
│ │ │ ├── proposal_flow/
│ │ │ ├── rating_flow/
│ │ │ └── user_flow/
│ │ └── states/ # Account struct definitions
│ │ ├── global_state.rs
│ │ ├── user_account.rs
│ │ ├── worker_profile.rs
│ │ ├── company_profile.rs
│ │ ├── job.rs
│ │ ├── milestones.rs
│ │ ├── proposal.rs
│ │ ├── escrow.rs
│ │ ├── disputes.rs
│ │ ├── review.rs
│ │ ├── arbitrator_list.rs
│ │ ├── token_discount.rs
│ │ ├── error_codes.rs
│ │ └── enums/
│ └── tests/
└── offchain-client/ # Rust client binary
└── src/
└── main.rs
The platform charges a configurable fee expressed in basis points (bps). The default used in the off-chain client demo is 200 bps (2%). The admin can update this at any time via update_platform_fee_main and redirect the treasury via update_treasury_address_main. Fees are collected into a USDC token account (the platform treasury) at the time of payment release.
This project is licensed under the MIT License. See LICENSE for details.