WorkProof is an AI-verified freelance escrow application built on GenLayer. Clients define acceptance criteria and escrow GEN in an intelligent contract. Freelancers submit a public work URL, GenLayer validators evaluate the work, and the contract automatically releases payment when the submission passes.
The application has no backend server or database. The browser reads contract state directly, and every write is approved by the connected wallet.
- On-chain GEN escrow for freelance jobs.
- Plain-English acceptance criteria stored with each job.
- Browser-wallet approval for every state-changing transaction.
- Public job board with status filters.
- Separate client and freelancer dashboard views.
- AI review of submitted public URLs.
- Validator consensus on the exact fetched evidence, pass/fail, and score range.
- Append-only on-chain evidence and prompt SHA-256 commitments.
- Strict review-result types and score bounds before settlement.
- Submission and payout deadline enforcement.
- Configurable revision limits from one to five attempts.
- Automatic 98% freelancer payout and 2% treasury fee.
- Client cancellation and refund before a job is accepted.
- Client escrow reclaim after an accepted job passes its deadline.
- GenLayer transaction appeals after revisions are exhausted.
flowchart LR
C[Client wallet] -->|Create job and escrow GEN| F[Next.js frontend]
R[Freelancer wallet] -->|Accept and submit| F
F -->|Signed transactions and reads| G[WorkProof contract]
G -->|Fetch submitted URL| W[Public work]
G --> V[GenLayer validators and LLMs]
V -->|Consensus result| G
G -->|98% on pass| R
G -->|2% platform fee| T[Treasury wallet]
G -->|Refund on cancel or expiry| C
- A client connects MetaMask and creates a globally unique job ID.
- The client supplies acceptance criteria, a future deadline, a revision limit, and a GEN payment.
- The contract stores the job and holds the attached GEN in escrow.
- A different wallet accepts the open job.
- The assigned freelancer submits a public HTTP or HTTPS URL and notes.
- Validators fetch the URL, hash the complete response, and place a bounded text slice in a versioned JSON review prompt.
- Validators must agree on the exact evidence commitments before comparing the strict pass/fail result and score.
- The contract rechecks the deadline and stores an append-only submission record before a passing review can release payment.
- A failing review consumes one revision and returns the job to
in_progress. When revisions reach zero, the job becomesdisputedand the submission transaction can be appealed through GenLayer.
| Rule | Value |
|---|---|
| Minimum passing score | 70/100 |
| Platform fee | 2% |
| Freelancer payment | 98% |
| Allowed revision limit | 1-5 |
| Validator score tolerance | 10 points |
| Maximum fetched response | 512,000 bytes |
| Maximum reviewed text | 12,000 characters |
| Submission page size | 50 records |
Validators must independently produce identical source, reviewed-content, criteria, notes, and prompt commitments. They must also agree on pass/fail. Scores normally need to be within 10 points; very low scores are compared as a shared under-30 outcome.
The review result must contain exactly pass, score, met_criteria,
unmet_criteria, and summary. pass must be a JSON boolean, score must be
an integer from 0 through 100, and pass must equal score >= 70. The contract
checks the deadline before evaluation and immediately before settlement.
If the submitted URL cannot be fetched, validators receive different content, the AI response is malformed, or the deadline gate fails, the transaction fails before a revision is consumed.
| Status | Meaning | Next actions |
|---|---|---|
open |
Payment is escrowed and no freelancer is assigned | Accept or client cancel |
in_progress |
A freelancer accepted the job | Submit work or client reclaim after deadline |
completed |
AI review passed | Freelancer and treasury are paid |
disputed |
All revisions were exhausted | Appeal the submission transaction |
cancelled |
Client cancelled before acceptance | Escrow returned to client |
expired |
Accepted job passed its deadline and was reclaimed | Escrow returned to client |
An open job whose deadline has passed is displayed as expired by the frontend.
Contract storage changes only through transactions, so the client can still
cancel that on-chain open job to recover escrow.
contract/workproof.py is the complete application
backend and settlement layer. It stores jobs and append-only submission records
as JSON strings in separate TreeMap[str, str] collections, tracks IDs in a
DynArray[str], and stores the platform treasury address supplied at
deployment.
Public write methods:
create_jobaccept_jobsubmit_workcancel_jobreclaim_expired_job
Public view methods:
get_jobget_submissionlist_submissionslist_jobslist_jobs_by_statusjob_count
The contract pins its py-genlayer dependency in the source header and uses
GenLayer non-deterministic web and LLM execution for work evaluation.
The Next.js 15 and React 19 frontend:
- Connects to MetaMask through an EIP-1193 provider.
- Verifies and offers to add/switch the configured GenLayer network.
- Reads jobs directly from the contract.
- Sends writes directly from the connected wallet.
- Waits for GenLayer
ACCEPTEDstatus and checks for execution errors. - Retries temporary read RPC failures.
- Displays the latest on-chain evidence and settlement commitments.
- Tracks the latest submission transaction in session storage for appeal instructions.
There is no API service, database migration, or server-side private key.
.
|-- contract/
| |-- workproof.py # GenLayer intelligent contract
| |-- deploy.mjs # multi-network deployment script
| |-- package.json
| `-- .env.example
|-- frontend/
| |-- src/app/
| | |-- page.tsx # landing page
| | |-- jobs/page.tsx # job board
| | |-- jobs/new/page.tsx # create and fund a job
| | |-- jobs/[id]/page.tsx
| | `-- dashboard/page.tsx
| |-- src/lib/genlayer.ts # contract reads, writes, and GEN helpers
| |-- src/lib/useWallet.ts # MetaMask and network hook
| `-- src/components/
|-- tests/
| |-- test_workproof.py
| |-- test_workproof_security.py
| |-- requirements.txt
| `-- pytest.ini
`-- docs/
|-- GENLAYER_BRADBURY_DEPLOY_HANDOFF.md
`-- SETTLEMENT_SECURITY.md
| Layer | Technology |
|---|---|
| Intelligent contract | Python, GenLayer |
| Frontend | Next.js 15, React 19, TypeScript |
| Styling | Tailwind CSS |
| Wallet and contract SDK | MetaMask, genlayer-js |
| Contract tests | Standard-library unittest, Pytest, genlayer-test |
| Hosting | Vercel or any Next.js-compatible platform |
- Git
- Node.js 20 and npm
- MetaMask or another compatible browser wallet
- Bradbury testnet GEN for deployment, escrow, and transaction fees
- Python and Docker only when running the integration test suite
git clone https://github.com/Jerry-Tekh/workproveApp.git
cd workproveAppInstall the deploy package:
cd contract
npm installCreate an ignored deployment environment file:
cp .env.example .env.localSet:
PRIVATE_KEY=0xYOUR_DEPLOYER_PRIVATE_KEY
TREASURY_ADDRESS=0xYOUR_TREASURY_WALLET_ADDRESSDeploy to Bradbury:
npm run deploy:testnetThe script waits for transaction acceptance, attempts to observe finalization, extracts the deployed address, and writes:
NEXT_PUBLIC_GENLAYER_NETWORK=testnet-bradbury
NEXT_PUBLIC_CONTRACT_ADDRESS=0xDEPLOYED_CONTRACT_ADDRESSto frontend/.env.local.
The PRIVATE_KEY is used only by the deployment script. Normal application
transactions are signed by each user in MetaMask.
cd ../frontend
npm install
npm run devOpen:
http://localhost:3000
If a contract is already deployed, skip step 2 and configure
frontend/.env.local manually.
| Variable | Required | Description |
|---|---|---|
PRIVATE_KEY |
Non-local deployments | Deployer wallet private key |
TREASURY_ADDRESS |
No | Receives the 2% fee; defaults to deployer |
Never commit this file, paste the key into frontend configuration, or expose it
through a NEXT_PUBLIC_* variable.
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_GENLAYER_NETWORK |
Yes | SDK network key |
NEXT_PUBLIC_CONTRACT_ADDRESS |
Yes | Deployed WorkProof address |
Supported network values:
testnet-bradburytestnet-asimovstudionetlocalnet
Both frontend values are public configuration and are embedded at build time. Restart the development server or redeploy after changing them.
The browser wallet hook currently configures these networks:
| Network key | Chain ID | RPC |
|---|---|---|
testnet-bradbury |
4221 | https://rpc-bradbury.genlayer.com |
testnet-asimov |
4221 | https://rpc-asimov.genlayer.com |
studionet |
61999 | https://studio.genlayer.com/api |
localnet |
61127 | http://localhost:4000/api |
Bradbury and Asimov share the chain ID in the current frontend configuration. When switching between them, verify the active RPC in the wallet.
The deploy script supports:
cd contract
npm run deploy:localnet
npm run deploy:studionet
npm run deploy:testnetThe equivalent direct command is:
node deploy.mjs testnet-bradburyFor GenLayer Studio, deploy contract/workproof.py and enter the treasury
wallet address in the treasury_address constructor field. It is the contract's
single constructor argument.
To verify a new deployment, call:
job_count()
A new contract should return 0.
The hardened evidence schema requires a new deployment. Existing GenLayer
contract storage is not changed by updating this repository, so deploy
contract/workproof.py again and update NEXT_PUBLIC_CONTRACT_ADDRESS before
submitting the revised project for review.
- Connect the wallet and switch to the configured network.
- Open
/jobs/new. - Enter a unique lowercase job ID.
- Write acceptance criteria of at least 20 characters.
- Set GEN payment, a future deadline, and revision limit.
- Approve the escrow transaction in MetaMask.
- Monitor the job from
/dashboard. - Cancel while still open, or reclaim after an accepted job misses its deadline.
- Connect a different wallet from the client.
- Browse
/jobsand accept an open job. - Complete the work and publish it at a validator-accessible URL.
- Submit the URL and implementation notes.
- Wait for validator consensus.
- Resubmit after feedback when revisions remain.
- If the job becomes disputed, appeal the submission transaction:
genlayer transactions appeal --tx 0xSUBMISSION_TRANSACTION_HASHRun the dependency-free security suite without installing anything:
python3 scripts/verify_no_install.pyThe script runs these checks individually:
python3 -m unittest tests/test_workproof_security.py -v
python3 -m py_compile contract/workproof.py tests/test_workproof_security.pyThis suite covers strict verdict and score types, prompt boundaries, immutable evidence commitments, mutable URL disagreement, append-only records, malformed model output, deadline gates, and payout provenance.
The GenLayer integration suite additionally covers deployment, escrow, payout, fees, revisions, disputes, cancellation, pagination, duplicate IDs, deadline reclaim, malformed URLs, unreachable submissions, and provenance view methods.
Run against a local GenLayer environment:
python -m pip install -r tests/requirements.txt
genlayer up
python -m pytest tests/test_workproof.py -vThe test environment requires Docker. Contract deployment and normal frontend development do not require the Python test packages.
Build the frontend:
cd frontend
npm run buildWorkProof only needs the frontend to be hosted after the contract is deployed.
- Import
Jerry-Tekh/workproveApp. - Set the root directory to
frontend. - Select the Next.js framework preset.
- Add
NEXT_PUBLIC_GENLAYER_NETWORK. - Add
NEXT_PUBLIC_CONTRACT_ADDRESS. - Deploy.
Do not add PRIVATE_KEY or TREASURY_ADDRESS to Vercel. The treasury address
is already stored in the deployed contract, and users sign their own
transactions.
Any platform that supports a standard Next.js build can host the frontend:
npm run build
npm run start- Keep deployment keys only in ignored local files or a dedicated secret manager.
- Use a dedicated deployment wallet rather than a primary personal wallet.
- Every client and freelancer write must be reviewed and approved in the connected wallet.
- Submitted work is untrusted public content. Acceptance criteria should be specific, measurable, and avoid relying on secrets or private resources.
- The work URL must be publicly reachable by validators and should not require authentication.
- Prefer content-addressed or version-pinned URLs. The on-chain SHA-256 commitments identify the exact reviewed bytes even if a normal URL changes later.
- GEN remains in the contract until completion, cancellation, or eligible deadline reclaim.
- Read the settlement security model before changing review or payout logic.
- The project is configured for GenLayer test networks, not mainnet.
- WorkProof does not verify private repositories, authenticated pages, or local development URLs.
- There is no off-chain search index. The dashboard scans contract jobs and currently stops after 1,000 records.
- Appeals are performed with GenLayer tooling rather than an in-app appeal UI.
- Job IDs are globally unique within one contract deployment.
- A later submission can receive a different decision if its committed evidence changes or becomes unavailable.
- The contract accepts arbitrary HTTP/HTTPS work URLs; a production security review should define stricter URL and network-access controls.