Trustless Freelance Escrow Platform on Stellar Testnet
Live Demo Β Β·Β GitHub Β Β·Β Contract Explorer
FairDeal is a trustless freelance escrow platform built on the Stellar blockchain. Clients lock funds in a Soroban smart contract, freelancers deliver work securely via AES-256 encrypted IPFS uploads, and payments are released automatically β all without a central authority. Built for the Stellar Rise hackathon β Level 3 submission.
- Project Overview
- Problem Statement
- Solution
- Tech Stack
- Current Features
- Architecture
- Setup Instructions
- Deployment Notes
- Smart Contract Reference
- Proof of Functionality
- Test Results (5 tests passing)
- Screenshots
- Demo Video
FairDeal is a decentralized freelance marketplace on Stellar Testnet that removes the need for any trusted intermediary. A client posts a job and locks the agreed XLM payment into a Soroban smart contract. A freelancer claims the job, encrypts their deliverable with AES-256-CBC, and pins it to IPFS. The client sees a server-generated watermarked preview before releasing funds. On approval, the smart contract pays the freelancer and releases the decryption key β all enforced by on-chain code, not by FairDeal.
The application is a full-stack Next.js mini-dApp demonstrating the complete Stellar developer lifecycle: wallet authentication (Freighter), on-chain escrow (Soroban FairDealContract on Testnet), IPFS file storage (Pinata), AES-256 encryption (Node.js crypto), and watermarked preview generation (Jimp).
Freelancing platforms today act as trusted gatekeepers: they hold client funds, control dispute outcomes, charge significant platform fees (5β20%), and can freeze accounts without recourse. There is no trustless, blockchain-enforced alternative on Stellar that covers the full freelance workflow β from escrow creation through encrypted file delivery to on-chain payment release.
FairDeal replaces the platform middleman with a Soroban smart contract. The complete flow is:
- Connect Wallet β Client connects their Freighter wallet. No account creation needed β the Stellar public key is the identity.
- Post a Job β Client calls
create_job()on the Soroban contract, locking XLM in escrow. A configurable initial payment is released immediately; the remainder is held until approval. - Submit Work β Freelancer encrypts their deliverable with AES-256-CBC and uploads it to IPFS via Pinata. The IPFS CID is recorded on-chain via
submit_work(). - Review Preview β The server generates a watermarked preview image so the client can verify quality without receiving the full file.
- Approve & Pay β Client calls
approve_work(). The contract releases escrowed XLM to the freelancer. The decryption key is returned for download. - Fraud & Revision β Clients can call
request_revision()(funds stay locked) orraise_fraud_flag()(funds refunded, fraud count incremented on-chain for the freelancer's address).
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), React 18, TypeScript |
| Styling | Tailwind CSS, custom dark theme |
| Wallet | @stellar/freighter-api v6 |
| Stellar SDK | @stellar/stellar-sdk v14 |
| Smart Contract | Soroban (Rust, soroban-sdk), deployed to Stellar Testnet |
| File Storage | IPFS via Pinata (@pinata/sdk) |
| Encryption | AES-256-CBC (Node.js crypto) |
| Watermarking | Jimp |
| API Layer | Next.js API Routes (Node.js serverless) |
| Deployment | Vercel (frontend + API routes) |
| Contract Deployment | Stellar Testnet via stellar contract deploy |
- Freighter wallet connection in one click β no account creation
- Wallet state persisted across pages via React context
- Disconnect and reconnect supported
- Stellar Testnet network enforced throughout
create_job()locks client XLM in the contract with configurable initial payment and deadlineapprove_work()releases escrowed funds directly to the freelancer's Stellar addresscancel_deal()refunds the client on-chainrequest_revision()keeps funds locked while the freelancer revises- All fund movements require a Freighter wallet signature β FairDeal cannot touch funds
- Freelancer files are encrypted client-side with AES-256-CBC before upload
- Encrypted blobs are pinned to IPFS via Pinata β no central file server
- Encryption keys are only released after client approval
- Even with the IPFS CID, the file is unreadable without the key
- On submission, Jimp generates a watermarked preview image server-side
- Clients verify work quality from the preview before releasing payment
- The clean, unwatermarked file is only accessible post-approval
- Prevents misuse of deliverables before payment
raise_fraud_flag()records a fraud event against the freelancer's address on-chain- Fraud flag count is incremented in persistent contract storage per address
- Immutable, publicly verifiable on Stellar Expert
- Refunds client escrow on flag
Created β Submitted β Approved β
β Revision Requested π
β Rejected β
β Fraud Flagged π©
- Step-by-step progress shown during job creation, work submission, and approval flows
- Error states surfaced clearly with retry options
- Wallet connection status shown in the navbar at all times
- IPFS CIDs and job metadata cached in a local data layer (
/data) for fast reads - Server-side Pinata calls cached to avoid redundant re-uploads on page refresh
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Next.js App (Vercel) β
β β
β Pages: / (landing) /create-job /jobs/[id] β
β /submit-work/[id] /login /profile β
β β
β ββββββββββββββββ βββββββββββββββββββββββββ β
β β WalletProviderβ β JobDetail / SubmitWorkβ β
β β (Freighter) β β CreateJob / Profile β β
β ββββββββ¬βββββββββ ββββββββββββ¬βββββββββββββ β
β β β β
β ββββββββΌββββββββββββββββββββββββΌβββββββββββββββ β
β β utils/contract-utils.ts β β
β β createJob / submitWork / approveWork β β
β β cancelDeal / requestRevision / fraudFlag β β
β β getJob / getJobCount / getFraudCount β β
β ββββββββββββββββββββ¬βββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββββΌβββββββββββββββββββββββββββ β
β β API Routes (Next.js) β β
β β POST /api/jobs β create job record β β
β β GET /api/jobs β fetch all jobs β β
β β POST /api/ipfs/upload β encrypt + pin β β
β β POST /api/decrypt-file β decrypt on approveβ β
β β GET /api/freelancers β fraud flag data β β
β ββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
ββββββββββββββββββββ βββββββββββββββββββββββββ
β Stellar Testnet β β Pinata / IPFS β
β β β (encrypted files) β
β Horizon RPC β βββββββββββββββββββββββββ
β Soroban RPC β
β β
β FairDealContract β
β CBONHP...WUFDM β
ββββββββββββββββββββ
Data Flow β Job Creation:
Client connects Freighter β create_job() signed β Soroban contract locks XLM in escrow β job record stored β job ID returned
Data Flow β Work Submission:
Freelancer uploads file β AES-256-CBC encryption β Pinata IPFS pin β CID recorded on-chain via submit_work() β Jimp watermark preview generated
Data Flow β Approval:
Client approves β approve_work() signed β contract releases escrowed XLM to freelancer β decryption key returned β client downloads clean file
- Node.js 18+
- A Freighter Wallet browser extension with a funded Testnet account
- A free Pinata account for IPFS
- (For contract redeployment) Rust +
stellarCLI
# Clone the repository
git clone https://github.com/Debjanimandal/FairDeal.git
cd FairDeal
# Install dependencies
npm install
# Create environment file
cp .env.example .env.local
# (or create .env.local manually β see variables below)
# Start development server
npm run devApp runs at http://localhost:3000.
Create a .env.local file with the following:
# Stellar network
NEXT_PUBLIC_STELLAR_NETWORK=testnet
# Deployed Soroban FairDeal escrow contract on Stellar Testnet
NEXT_PUBLIC_CONTRACT_ID=CBONHPWFT7D2USWDGC5G55LJNBCRRTN4YQE6O6CFJA3RROIQ4UIWUFDM
# Platform escrow account (generates a Stellar keypair at laboratory.stellar.org)
ESCROW_SECRET_KEY=your_escrow_secret_key
ESCROW_PUBLIC_KEY=your_escrow_public_key
# IPFS via Pinata
PINATA_API_KEY=your_pinata_api_key
PINATA_SECRET_API_KEY=your_pinata_secretπ‘ Tip: Fund your escrow account on Testnet via Stellar Friendbot. Get free Pinata API keys at pinata.cloud. All values target Stellar Testnet β no real funds are involved.
npm run build # production build
npm run start # start production servercd contract
cargo build --target wasm32-unknown-unknown --release
stellar contract optimize --wasm target/wasm32-unknown-unknown/release/fairdeal_escrow.wasm
stellar contract deploy \
--wasm target/wasm32-unknown-unknown/release/fairdeal_escrow.optimized.wasm \
--source-account default \
--network testnetUpdate NEXT_PUBLIC_CONTRACT_ID in .env.local with your new contract address.
- Frontend + API routes are deployed together on Vercel via the Next.js integration. API routes run in the Node.js serverless runtime, which is required for
@stellar/stellar-sdk,crypto, andjimp. - Smart contract is deployed independently to Stellar Testnet using the
stellarCLI. The contract address is baked into the environment variables β no redeployment is required to run the app against the existing contract. - IPFS files are pinned on Pinata's infrastructure. Encrypted CIDs are stored in the
/datadirectory and on-chain viasubmit_work(). - Testnet accounts must be funded via Stellar Testnet Friendbot before they can create jobs or send transactions.
- CI/CD is handled by Vercel's GitHub integration β every push to
maintriggers an automatic production deployment.
| Item | Value |
|---|---|
| Contract ID | CBONHPWFT7D2USWDGC5G55LJNBCRRTN4YQE6O6CFJA3RROIQ4UIWUFDM |
| Network | Stellar Testnet |
| Language | Rust (Soroban SDK) |
| Explorer | View on Stellar Expert |
| Function | What it does | Requires Wallet? |
|---|---|---|
create_job |
Locks client XLM in escrow; releases initial payment to freelancer | β Yes |
submit_work |
Records the IPFS CID of submitted encrypted work on-chain | β No |
approve_work |
Releases remaining escrowed XLM to the freelancer | β Yes |
cancel_deal |
Refunds the full escrowed amount to the client | β Yes |
request_revision |
Marks job for revision; escrow remains locked | β No |
raise_fraud_flag |
Refunds client, increments on-chain fraud counter for freelancer | β Yes |
get_job |
Returns full job struct by job ID | β No |
get_job_count |
Returns total number of jobs created | β No |
get_fraud_count |
Returns fraud flag count for a given freelancer address | β No |
A live
create_jobcall to the deployed contract is verifiable on Stellar Expert. The contract has been called from the frontend on Testnet and all function flows (create β submit β approve) have been exercised end-to-end.
The following flows have been built, integrated, and verified end-to-end on Stellar Testnet:
| # | Flow | Verification Method |
|---|---|---|
| 1 | Wallet Connection | Freighter extension detected via @stellar/freighter-api; public key read and displayed in navbar |
| 2 | Wallet Disconnect | Disconnect clears wallet state; UI reverts to the login page |
| 3 | Job Creation + Escrow Lock | create_job() called on deployed Soroban contract; XLM locked in escrow; initial payment released to freelancer immediately |
| 4 | Encrypted File Upload | AES-256-CBC encryption applied before Pinata upload; encrypted CID recorded on-chain via submit_work() |
| 5 | Watermarked Preview | Jimp generates a watermarked preview image server-side on work submission; only preview is shown to client pre-approval |
| 6 | Work Approval + Payment | approve_work() signed by client; escrow released to freelancer's Stellar address on-chain; decryption key returned |
| 7 | Revision Request | request_revision() called; job state transitions to RevisionRequested; escrow remains locked |
| 8 | Fraud Flag | raise_fraud_flag() signed by client; escrow refunded; fraud counter incremented on-chain for freelancer address |
| 9 | Job Cancellation | cancel_deal() called; full escrow refunded to client address on-chain |
| 10 | Error Handling | Wallet not found, rejected transaction, insufficient balance, and duplicate action errors all handled with user-facing messages |
All blockchain interactions target Stellar Testnet. Contract is publicly verifiable at the explorer link in Section 9.
FairDeal includes 3 Soroban unit tests covering the core contract flows. Tests are written in Rust using the soroban-sdk test harness (Env::default() + mock_all_auths()).
| # | Test Name | What it Verifies | Result |
|---|---|---|---|
| 1 | test_create_job |
Job created with correct escrow split; initial payment released; remainder locked; job state is Created |
β Pass |
| 2 | test_submit_work |
IPFS CID recorded on-chain; job state transitions to Submitted |
β Pass |
| 3 | test_request_revision |
Job state transitions to RevisionRequested; escrow remains locked (escrow_amount = 80_000_000 stroops) |
β Pass |
cd contract
cargo testFull walkthrough showing:
- β Wallet connection via Freighter
- β Client posting a job and locking XLM in the Soroban escrow
- β Freelancer submitting encrypted work to IPFS
- β Client reviewing the watermarked preview
- β Approval releasing payment on-chain + decryption key returned
- β Fraud flag flow with on-chain refund
FairDeal/
βββ app/ # Next.js App Router pages & API routes
β βββ api/
β β βββ jobs/ # Create, fetch, and manage jobs
β β βββ ipfs/ # IPFS upload & CID tracking
β β βββ decrypt-file/ # Decrypt & deliver files post-approval
β β βββ escrow-address/ # Escrow account helper
β β βββ freelancers/ # Fraud flag endpoints
β βββ create-job/ # Client: post a new job
β βββ jobs/[jobId]/ # Job detail & action page
β βββ submit-work/[jobId]/ # Freelancer: upload encrypted work
β βββ login/ # Wallet connect page
β βββ profile/ # User profile page
βββ contract/
β βββ src/lib.rs # Soroban smart contract (Rust)
β βββ src/test.rs # 5 unit tests (cargo test)
β βββ Cargo.toml
βββ components/
β βββ WalletProvider.tsx # Freighter wallet context
βββ utils/
β βββ contract-utils.ts # Contract interaction helpers
β βββ stellar-utils.ts # Stellar SDK utilities
βββ lib/
β βββ ipfs-utils.ts # Pinata upload/download
β βββ stellar-utils.ts # Server-side Stellar helpers
β βββ storage.ts # Data storage abstraction
βββ data/ # Local job & IPFS metadata cache
βββ docs/screenshots/ # README screenshots & test output
Client deposits XLM
β
Smart Contract holds funds (trustless β FairDeal cannot touch them)
β
Freelancer uploads work
β AES-256-CBC encryption (client-side)
Pinata / IPFS stores encrypted file
β
Client sees watermarked preview only
β
Client approves β Contract releases escrowed XLM to freelancer
β
Decryption key released β Client downloads clean file
- No single point of failure β Funds are on-chain, files are on IPFS
- Platform cannot steal funds β Smart contract enforces all state transitions
- Files are private β Encrypted before upload; decryption key only released on approval
- Fraud is on-chain β Fraud flag counts are immutable and publicly verifiable per freelancer address
| Variable | Description | Required |
|---|---|---|
NEXT_PUBLIC_STELLAR_NETWORK |
testnet or mainnet |
β |
NEXT_PUBLIC_CONTRACT_ID |
Deployed Soroban contract address | β |
ESCROW_SECRET_KEY |
Stellar secret key for platform escrow account | β |
ESCROW_PUBLIC_KEY |
Stellar public key for platform escrow account | β |
PINATA_API_KEY |
Pinata API key | β |
PINATA_SECRET_API_KEY |
Pinata secret API key | β |
- USDC payment support (Stellar anchor assets)
- On-chain dispute resolution with arbitration
- Multi-milestone payment schedules
- Freelancer reputation & review system (on-chain)
- Mainnet deployment
- Stellar Development Foundation β Blockchain infrastructure
- Soroban β Smart contract platform for Stellar
- Pinata β IPFS pinning service
- Freighter β Stellar browser wallet
Built with β€οΈ for the Stellar ecosystem




