DustRepository

@Singleton
class DustRepository @Inject constructor(dustDao: DustDao, dustStateDataStore: DataStore<Preferences>, balanceCalculator: DustBalanceCalculator, indexerClient: IndexerClient)

Repository for dust wallet operations.

Responsibilities:

  • Manage DustLocalState lifecycle (FFI bridge to Rust midnight-ledger)

  • Sync dust tokens from DustLocalState to database (cache layer)

  • Calculate current dust balance (time-based generation)

  • Provide available dust tokens for fee payment

  • Handle token state transitions (AVAILABLE → PENDING → SPENT)

  • Persist DustLocalState to encrypted storage

Architecture:

DustRepository
├─ DustLocalState (FFI) - Source of truth, in-memory Rust state
├─ DustDao (Database) - Cache layer for fast queries
└─ DataStore (Persistence) - Serialized state backup

Usage:

@Inject lateinit var dustRepository: DustRepository

// Initialize dust wallet
dustRepository.initializeIfNeeded(address)

// Get current balance
val balance = dustRepository.getCurrentBalance(address)

// Get tokens for fee payment
val tokens = dustRepository.getAvailableTokens(address)

// Observe balance live
dustRepository.observeBalance(address).collect { balance ->
updateUI(balance)
}

Thread Safety: All methods are thread-safe (suspend functions use coroutine context). DustLocalState instances are NOT thread-safe - use one per coroutine scope.

Constructors

Link copied to clipboard
@Inject
constructor(dustDao: DustDao, dustStateDataStore: DataStore<Preferences>, balanceCalculator: DustBalanceCalculator, indexerClient: IndexerClient)

Types

Link copied to clipboard
object Companion
Link copied to clipboard
class DustCheckpoint(val state: DustLocalState, val lastEventId: Long)

A dust checkpoint = the serialized DustLocalState plus the resume cursor (lastEventId) it was built up to. The two MUST describe the same chain point: the commitment-tree frontier carried inside the state has to equal the commitment index the event after lastEventId will try to insert at, or delta replay fails with NonLinearInsertion. state is owned by the caller and must be closed.

Functions

Link copied to clipboard

Clear the live state (for force-resync). Caller should close it.

Link copied to clipboard
suspend fun deleteState(address: String)

Delete serialized DustLocalState for an address.

Link copied to clipboard

Get all available dust tokens for an address.

Link copied to clipboard

Get available dust tokens sorted by value (smallest first).

Link copied to clipboard
suspend fun getCurrentBalance(address: String): BigInteger

Get current dust balance for an address.

Link copied to clipboard
suspend fun getLastAppliedEventId(address: String): Long?

Get last applied dust event ID for delta sync resume. Null if never synced.

Link copied to clipboard

Get the live in-memory state from the last sync (no deserialization).

Link copied to clipboard
suspend fun getTokenCount(address: String): Int

Get dust token count for an address.

Link copied to clipboard
suspend fun hasCachedState(address: String): Boolean

Check if cached dust state exists for an address.

Link copied to clipboard
suspend fun hasCheckpoint(address: String): Boolean

True only when BOTH halves of a checkpoint are present — cheaper than loadCheckpoint (no deserialize) for a presence test. A half-written checkpoint reads as absent, forcing a clean genesis sync.

Link copied to clipboard
suspend fun initializeIfNeeded(address: String): Boolean

Initialize dust wallet for an address if not already initialized.

Link copied to clipboard

Load the checkpoint (state + cursor) as ONE consistent snapshot. Reads both keys from a single DataStore emission, so the returned pair can never be torn across a concurrent write. Returns null if EITHER half is missing or the state fails to deserialize — callers then fall back to genesis rather than resuming on a half-checkpoint. The returned DustCheckpoint.state is owned by the caller and must be closed.

Link copied to clipboard
suspend fun loadState(address: String): DustLocalState?

Deserializes DustLocalState for use in fee payment.

Link copied to clipboard
suspend fun markTokensAsAvailable(nullifiers: List<String>)

Mark dust tokens as available (unlock after transaction failure).

Link copied to clipboard
suspend fun markTokensAsPending(nullifiers: List<String>)

Mark dust tokens as pending (lock for transaction).

Link copied to clipboard
suspend fun markTokensAsSpent(nullifiers: List<String>)

Mark dust tokens as spent (transaction confirmed).

Link copied to clipboard
fun observeBalance(address: String): Flow<BigInteger>

Observe dust balance for an address (live updates).

Link copied to clipboard
suspend fun saveCheckpoint(address: String, state: DustLocalState, lastEventId: Long)

Persist state and its resume cursor lastEventId as ONE atomic unit.

Link copied to clipboard
suspend fun saveLastAppliedEventId(address: String, eventId: Long)

Save last applied dust event ID (for delta sync resume).

Link copied to clipboard
suspend fun saveState(address: String, state: DustLocalState)

Saves updated DustLocalState after creating spends.

Link copied to clipboard
suspend fun syncFromBlockchain(address: String, dustSeed: ByteArray, maxBlocks: Int = 100, onProgress: suspend (eventsProcessed: Int, totalEvents: Int) -> Unit? = null): Boolean

Sync dust from blockchain (query events and replay into local state).

Link copied to clipboard
suspend fun syncTokensToCache(address: String)

Sync dust tokens from DustLocalState to database cache.