Skip to main content

What it reports

When a client calls GET /api/2/wallet/positions?wallet=…&_backfillSwapsAndPositions=true, the platform runs a two-phase pipeline:
  1. Slot extraction (wallet-backfillingqa-blocks-processor) — fetches the wallet’s historical transactions from the active provider (Moralis / Dune / Solscan), splits the slots into batches, and ingests transfers + swaps into public.swaps.
  2. Position rebuild (update-wallet-positions) — once slot extraction is done, the job acquires a per-wallet lock, wipes existing rows in wallet_positions / wallet_positions_recipients, and re-aggregates swaps into positions.
This endpoint surfaces the state of both phases in a single response, plus a unified progressPercent and etaSeconds so callers can drive a progress bar or notify the user when the wallet is ready.

state values

StateMeaning
not_startedNo backfill has ever been requested for this wallet.
processingPhase 1 in progress (fetching transactions and ingesting slots).
rebuildingPhase 2 in progress (re-aggregating swaps into positions).
completedBoth phases finished — wallet_positions is up to date.
stuckPhase 1 started (transferStatus="1") but the provider call died before publishing any batches. A retry of the backfill trigger clears this.

Progress & ETA semantics

  • progressPercent is weighted: phase 1 spans 0 → 70%, phase 2 spans 70 → 100%. It caps at 99.9 until phase 2 observably completes, and only then flips to 100.
  • etaSeconds during phase 1 is a linear extrapolation (elapsed / done) × remaining. During phase 2 it is swapCount × globalAvgMsPerSwap − elapsedRebuild, where the global average is an EWMA (alpha 0.1) updated after every successful rebuild.
  • Both fields are null before the first batch lands (no data yet to extrapolate from).

Example

{
  "wallet": "A92oggkrdzUTd4NGpd2jDYR1rpeeo5LUHayGgjwPLnd4",
  "state": "rebuilding",
  "progressPercent": 82.4,
  "etaSeconds": 6,
  "estimatedCompletionAt": "2026-04-20T13:31:35.000Z",
  "transferStatus": "2",
  "swapStatus": "2",
  "phase1": {
    "expectedBatches": 58,
    "doneBatches": 58,
    "seenBatches": 58,
    "remainingBatches": 0,
    "progressPercent": 100,
    "startedAt": "2026-04-20T13:30:56.193Z",
    "elapsedSeconds": 47,
    "etaSeconds": null
  },
  "phase2": {
    "swapCount": 12430,
    "startedAt": "2026-04-20T13:31:23.101Z",
    "completedAt": null,
    "elapsedSeconds": 6,
    "predictedDurationSeconds": 18,
    "progressPercent": 33.3,
    "etaSeconds": 12,
    "avgMsPerSwap": 1.462
  },
  "rebuildPublished": true,
  "positionRebuildLocked": true,
  "backfillVersion": "1"
}

Bulk variant

GET /internal/wallet-backfill/status?wallets=w1,w2,w3 returns { statuses: [...] } with one entry per wallet, in the same order. Useful for dashboards polling many wallets at once.
  • Trigger a backfill: GET /api/2/wallet/positions?wallet=…&_backfillSwapsAndPositions=true
  • Tune phase 1 retention: BACKFILL_VERSION env var (bump to invalidate all cached states)