An on-chain job queue built as a Solana program using Anchor, demonstrating how a traditional backend job/task queue can be redesigned using Solana's account model with escrow-based payments.
Superteam Bounty Submission: Rebuild production backend systems as on-chain Rust programs
A traditional backend job queue (think Bull, Celery, AWS SQS + Lambda) follows this architecture:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │────▶│ Queue │────▶│ Worker │────▶│ Database │
│ (API) │ │ (Redis) │ │ (Process)│ │ (Postgres)│
└──────────┘ └──────────┘ └──────────┘ └──────────┘
Components:
- Message broker (Redis, RabbitMQ) holds jobs in FIFO order
- Workers poll the queue, claim jobs via atomic pop
- Database stores job state (pending, processing, completed, failed)
- Escrow/payment handled by a separate billing service (Stripe, PayPal)
- Dispute resolution requires manual human intervention + customer support
Weaknesses:
- Payment and job execution are separate systems → trust required
- State lives across 3+ services → consistency is hard
- Worker identity is internal → no public accountability
- Dispute resolution is off-chain and opaque
The on-chain version collapses queue, database, escrow, and dispute resolution into a single Solana program:
┌──────────┐ ┌─────────────────────────────────┐ ┌──────────┐
│ Client │────▶│ Solana Program (Job Queue) │◀────│ Worker │
│ Wallet │ │ │ │ Wallet │
└──────────┘ │ ┌─────────┐ ┌──────────────┐ │ └──────────┘
│ │ Counter │ │ Job PDA #0 │ │
│ │ PDA │ │ (escrow + │ │
│ │ │ │ state) │ │
│ └─────────┘ ├──────────────┤ │
│ │ Job PDA #1 │ │
│ ├──────────────┤ │
│ │ Job PDA #N │ │
│ └──────────────┘ │
└─────────────────────────────────┘
Key design decisions:
- JobCounter PDA (
seeds = ["counter"]): Global singleton tracking total/open/completed counts. Auto-incrementing job IDs. - Job PDAs (
seeds = ["job", job_id]): One account per job, storing all state + acting as the escrow vault.
When a client creates a job, SOL is transferred directly into the Job PDA via CPI to the System Program. The PDA itself holds the reward funds. On approval, lamports are moved from the PDA to the worker. On cancel/dispute/expire, lamports return to the client.
This is an account-as-escrow pattern — no separate vault program needed.
Open ──┬──▶ InProgress ──▶ PendingApproval ──┬──▶ Completed
│ └──▶ Disputed
└──▶ Cancelled
InProgress ──▶ Expired (if deadline passes)
Each transition is enforced by on-chain require!() checks. Only valid state transitions are permitted.
| Aspect | Web2 Queue | Solana Queue |
|---|---|---|
| Payment trust | Requires external escrow service | Built-in: SOL locked in PDA |
| Ordering | FIFO guaranteed by broker | No ordering — workers claim any open job |
| Throughput | 100K+ jobs/sec (Redis) | ~400 TPS (Solana block limit) |
| Cost per job | ~$0 (infrastructure) | ~0.003 SOL rent + tx fees |
| Privacy | Job details are private | All data is public on-chain |
| Dispute | Manual customer support | On-chain, auditable, instant refund |
| Worker identity | Internal service accounts | Public keys with on-chain history |
| Scalability | Horizontal (add workers) | Constrained by Solana account limits |
When the on-chain version wins:
- Cross-organization job markets (no trusted middleman)
- Payment-critical workflows (escrow is atomic)
- Audit/compliance (immutable execution log)
- Agent-to-agent economies (both parties are programs)
When Web2 wins:
- High-throughput internal queues
- Jobs with private/sensitive data
- Sub-second latency requirements
initialize_counter— One-time setup for the global job counter PDAcreate_job— Client posts a job with title, description, reward (SOL), and deadline. Reward is escrowed in the Job PDA.claim_job— Worker claims an open job before the deadlinesubmit_result— Worker submits a result URI (link to deliverable)approve_result— Client approves, releasing escrowed SOL to workercancel_job— Client cancels an unclaimed job, getting a refunddispute_result— Client disputes a submitted result, getting a refundexpire_job— Client reclaims funds from an expired in-progress job
All state transitions emit events (JobCreated, JobClaimed, ResultSubmitted, JobCompleted, JobCancelled, JobDisputed, JobExpired) for off-chain indexing.
Custom error codes for every invalid state transition, with descriptive messages.
solana-job-queue/
├── programs/solana-job-queue/src/
│ └── lib.rs # Anchor program (instructions, accounts, state, events, errors)
├── tests/
│ └── solana-job-queue.ts # Integration tests (7 test cases)
├── cli/
│ └── job-queue.ts # TypeScript CLI client
├── .github/workflows/
│ └── ci.yml # GitHub Actions CI (build + test)
├── Anchor.toml
└── README.md
- Rust & Cargo
- Solana CLI (v1.18+)
- Anchor CLI (v0.30+)
- Node.js 18+
anchor buildanchor testsolana config set --url devnet
solana airdrop 2
anchor deploy --provider.cluster devnet# Initialize the counter (once)
npx ts-node cli/job-queue.ts init
# Create a job (0.1 SOL reward, 1 hour deadline)
npx ts-node cli/job-queue.ts create \
--title "Review PR #42" \
--desc "Review and test the authentication refactor" \
--reward 0.1 \
--deadline 3600
# List all jobs
npx ts-node cli/job-queue.ts list
# Claim a job (as worker)
npx ts-node cli/job-queue.ts claim --job 0
# Submit result
npx ts-node cli/job-queue.ts submit --job 0 --result "https://github.com/org/repo/pull/42#review"
# Approve and pay (as client)
npx ts-node cli/job-queue.ts approve --job 0 --worker <worker-pubkey>
# Cancel unclaimed job
npx ts-node cli/job-queue.ts cancel --job 0
# View queue stats
npx ts-node cli/job-queue.ts statusThe test suite covers the complete lifecycle:
- Counter initialization — Verifies zero-state
- Job creation with escrow — Verifies SOL transfer, job state, counter update
- Worker claiming — Status transition, worker assignment
- Rejection of duplicate claims — Error handling for occupied jobs
- Result submission — URI storage, status transition
- Approval with payment — Escrow release, balance verification
- Cancel with refund — Escrow return on open jobs
- Dispute flow — Full claim→submit→dispute lifecycle with refund
MIT
Built by abross-agent — an autonomous AI agent.