Architecture
System overview and data flow
System Overview
IronQ is composed of five subsystems that work together on-chain.
| Subsystem | Instructions | Role |
|---|---|---|
| Queue Management | init · update · pause | Create, configure, and pause job queues. One queue per authority with an associated SPL token vault. |
| Worker Operations | register · stake · deregister | Workers register against a queue, lock stake tokens, and deregister when idle. |
| Job Lifecycle | create → claim → submit → approve | Full lifecycle from creation to completion. |
| Dispute Path | dispute → resolve | Contested results are flagged and resolved by the queue authority. |
| Permissionless Cranks | reclaim_expired · close_job | Anyone can call — slash timed-out workers, reclaim rent from terminal jobs. |
SPL Token Vault (PDA) — Holds worker stakes and job rewards. Authority is the vault PDA itself — fully self-custodial, no admin key can drain funds.
Data Flow
-
Creator creates job — calls
create_job(reward). The reward amount is transferred via SPL Token into the queue's vault PDA. -
Worker claims job — calls
claim_job(). The job status is set to Claimed and a deadline is assigned based on the queue's timeout configuration. -
Worker submits result — calls
submit_result(hash). A new JobResult PDA is created storing the result data hash. Job status moves to Submitted. -
Creator approves result — calls
approve_result(). The vault transfers the reward to the worker's token account. Job status becomes Completed. -
Creator closes job — calls
close_job()to reclaim the rent from the terminal-state Job and Result accounts.
PDA Derivation
All accounts are Program Derived Addresses (PDAs), ensuring deterministic addresses without account registries:
| Account | Seeds | Description |
|---|---|---|
| Queue | ["queue", authority] | One queue per authority |
| Vault | ["vault", queue] | One vault per queue |
| Job | ["job", queue, job_id_le_bytes] | Unique per queue + monotonic ID |
| Worker | ["worker", queue, wallet] | One registration per worker per queue |
| Result | ["result", job] | One result per job |
job_id_le_bytes is the 8-byte little-endian representation of the u64 job ID.
Design Decisions
| Decision | Rationale |
|---|---|
| One queue per authority | Simplifies PDA derivation; create multiple with different wallets |
| Max 3 concurrent jobs per worker | Prevents overcommitment, bounds compute |
| Vault PDA as its own authority | Self-custodial — no admin key can drain funds |
data_hash instead of data | Solana accounts are expensive; store payloads on Arweave/IPFS |
| Separate Result account | Keeps Job account fixed-size; result can be closed independently |
| Basis points for rates | Integer math, no floating point, max precision of 0.01% |
Immutable reward_mint | Changing it would orphan vault funds. Create a new queue for a different token |
max_concurrent_jobs snapshot | Copied at registration time; prevents retroactive restriction of active workers |
Performance
Compute Units
All instructions fit within Solana's default 200,000 CU limit — no setComputeUnitLimit needed.
initialize_queue: ~24,000 CU (most expensive — PDA creation + vault init)claim_job: ~6,500 CU (lightest — single status update)- Most others: 10,000–25,000 CU range
- Token transfers via CPI add ~4,500 CU per SPL Token call
Throughput
Solana enforces single-writer semantics per account. A single queue processes ~400 state transitions per second. For higher throughput, deploy multiple queues across different authorities — each queue's PDAs are independent and execute in parallel.
Latency
| Operation | Web2 (Celery + Redis) | IronQ (Solana) |
|---|---|---|
| Job dispatch | < 1ms | ~400ms (1 slot) |
| Result confirmation | Instant | ~400ms + finality (~6–13s) |
| End-to-end cycle | < 10ms | ~2–15 seconds |
IronQ trades latency for trustlessness. Suitable for tasks taking minutes/hours where assignment latency is negligible.