Workers
Location:apps/backend/src/workers/
The backend runs four BullMQ workers that consume jobs produced by the indexer. All workers share the same ioredis connection (src/config/redis.ts) and are registered by importing workers/index.ts at server startup.
Worker Summary
| Worker | Queue | Trigger | DB Operation |
|---|---|---|---|
trade.worker.ts | TradeProcessingQueue | TradeExecuted on-chain event | prisma.trade.create() |
snapshot.worker.ts | SnapshotCalculationQueue | After any trade | prisma.vault.update() — ROI + drawdown |
vault.worker.ts | VaultDeployedQueue | VaultDeployed on-chain event | prisma.user.upsert() + prisma.vault.create() |
follower.worker.ts | FollowerEventQueue | FollowerSubscribed / FollowerUnsubscribed | prisma.followerSubscription.upsert() |
TradeWorker
Queue:TradeProcessingQueueConcurrency: 5
Job Payload (TradeJobPayload)
Processing Logic
- Resolves
vault.idfromvaultAddressviaprisma.vault.findUnique({ where: { contractAddress } }) - Throws if vault is not in DB — lets BullMQ retry (indexer may have a race condition with
VaultDeployedQueue) - Calls
prisma.trade.create({ data: { txHash, vaultId, ... } }) - Logs success
SnapshotWorker
Queue:SnapshotCalculationQueue
Job Payload
Processing Logic
Triggered after every trade. Recalculatesroi and drawdown for the vault and updates prisma.vault.update().
VaultWorker
Queue:VaultDeployedQueue
Job Payload (VaultDeployedPayload)
Processing Logic
prisma.user.upsert({ where: { walletAddress: leader } })— creates the leader as aUserif they don’t exist yetprisma.vault.create({ data: { contractAddress, name, baseAsset, leaderId, status: 'ACTIVE', tvl: '0', roi: 0, drawdown: 0 } })- Logs success
FollowerWorker
Queue:FollowerEventQueue
Job Payload (FollowerPayload)
Processing Logic
DEPOSIT:prisma.user.upsert({ where: { walletAddress: followerAddress } })- If
FollowerSubscriptionexists for(userId, vaultId): addsamounttodepositedAmountvia BigInt arithmetic - If not: creates new
FollowerSubscription
- Finds existing
FollowerSubscription - Subtracts
amountfromdepositedAmount - If
remainingBalance <= 0: deletes the subscription record - Otherwise: updates
depositedAmountwith the remaining balance
Error Handling
All workers throw on failure, causing BullMQ to mark the job as
failed and retry with exponential backoff. Failed jobs persist in Redis and can be inspected via Bull Board or any BullMQ-compatible dashboard.failed and retry according to its default backoff policy.