# How Nautilus Wallet Handles Wallet Synchronization with the Ergo Blockchain

> Discover how Nautilus Wallet synchronizes with the Ergo blockchain. Learn about its periodic sync routine, UTXO and asset data querying, and IndexedDB persistence for seamless wallet management.

- Repository: [Nautilus Team/nautilus-wallet](https://github.com/nautls/nautilus-wallet)
- Tags: internals
- Published: 2026-03-07

---

**Nautilus Wallet synchronizes with the Ergo blockchain by running a periodic sync routine that derives addresses, queries Ergo-GraphQL for UTXO and asset data, detects changes, and persists deltas to IndexedDB, triggered automatically on new block detection or manually via the wallet store.**

The `nautls/nautilus-wallet` repository implements a robust, reactive pipeline to keep local wallet state consistent with the live Ergo network. Understanding this wallet synchronization mechanism is essential for developers building on Ergo or contributing to the Nautilus codebase.

## The Sync Architecture Overview

At the heart of the synchronization logic lies [`src/stores/walletStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/walletStore.ts), a Pinia store that orchestrates a four-stage pipeline. The process ensures that address derivation, blockchain queries, change detection, and persistence happen atomically and efficiently.

The sync routine respects a `MIN_SYNC_INTERVAL` constant (defined in [`src/constants/intervals.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/constants/intervals.ts), default approximately 30 seconds) to prevent excessive network calls:

```typescript
if (Date.now() - privateState.lastSynced < MIN_SYNC_INTERVAL) return setSyncing(false);

```

## Stage 1: Address Derivation

Before querying the blockchain, the wallet must ensure it has derived enough addresses to detect all UTXOs. The `sync()` method calls `getOrDerive()` to obtain a batch of addresses using the HD key pool.

- **Source**: [`src/stores/walletStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/walletStore.ts) (via `getOrDerive`)
- **HD Logic**: [`src/chains/ergo/hdKey.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/chains/ergo/hdKey.ts)

The derivation process uses `CHUNK_DERIVE_LENGTH` to determine how many addresses to generate at once. If the local cache lacks sufficient addresses, `HdKey.deriveAddresses()` creates the missing ones deterministically from the master key stored in the per-wallet `hdKeyPool`.

## Stage 2: Blockchain Data Retrieval via Ergo-GraphQL

With addresses prepared, the wallet queries the Ergo network through GraphQL. The `graphQLService.getAddressesInfo` method (located in [`src/chains/ergo/services/graphQlService.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/chains/ergo/services/graphQlService.ts)) executes the `ADDRESS_INFO_QUERY` against a public Ergo-GraphQL endpoint (e.g., *explore.sigmaspace.io*).

The GraphQL response provides for each address:
- A `used` flag indicating if the address has transaction history
- A complete list of assets (including native ERG and tokens)

This approach minimizes data transfer by batching address queries and leveraging GraphQL’s selective field retrieval.

## Stage 3: Change Detection and Persistence

Raw blockchain data alone does not update the UI. The `getChanges()` function in [`walletStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/walletStore.ts) performs a diff between the newly fetched address/asset rows and the existing `privateState.addresses` and `privateState.assets`.

The change detection identifies:
- **Changed addresses**: Updated balance or usage status
- **Changed assets**: Modified token quantities
- **Disappeared assets**: Tokens spent or moved

Persistence happens via IndexedDB bulk operations:
- `addressesDbService.bulkPut` ([`src/database/addressesDbService.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/database/addressesDbService.ts))
- `assetsDbService.bulkPut` and `assetsDbService.bulkDelete` ([`src/database/assetsDbService.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/database/assetsDbService.ts))

After persistence, `assetsStore.loadMetadata()` fetches EIP-4 metadata (names, decimals, artwork) for any newly discovered token IDs. Finally, the Pinia store patches its reactive arrays using `patchAddresses` and `patchAssets`, updates `lastSynced`, and clears the `hasOldUtxos` flag.

## Stage 4: Health Monitoring and UTXO Age Checks

The synchronization includes a health check mechanism. The `checkOldUtxos()` function uses the same GraphQL service (`checkBoxesOlderThan`) to query for UTXOs older than `HEALTHY_BLOCKS_AGE`.

This check exposes a `health.hasOldUtxos` flag, warning users if their wallet contains spent or stale boxes that might indicate synchronization issues or the need for consolidation.

## Trigger Mechanisms: Automatic and Manual Sync

The wallet supports both reactive and imperative synchronization triggers.

**Automatic Trigger**:
A `watch` on `chain.height` in [`src/stores/chainStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/chainStore.ts) invokes `sync()` whenever a new block is detected:

```typescript
watch(() => chain.height, () => {
  if (appStore.loading || privateState.loading || !appStore.settings.lastOpenedWalletId) return;
  sync();
});

```

**Manual Trigger**:
The public `load(walletId, { syncInBackground })` action allows components to force synchronization:

```typescript
if (opt.syncInBackground) {
  sync();               // fire-and-forget
} else {
  await sync();         // wait for completion
}

```

## Code Examples for Developers

### Force a Sync from a Component

```typescript
import { useWalletStore } from '@/stores/walletStore';

const wallet = useWalletStore();
// Force immediate resynchronisation
await wallet.load(wallet.id, { syncInBackground: false });

```

### Observe the Syncing Flag for UI Spinners

```typescript
import { useWalletStore } from '@/stores/walletStore';
import { watch } from 'vue';

const wallet = useWalletStore();

watch(() => wallet.syncing, (now) => {
  console.log(`Sync is ${now ? 'running' : 'idle'}`);
});

```

### Read the Latest Block Height

```typescript
import { useChainStore } from '@/stores/chainStore';
const chain = useChainStore();

console.log('Current Ergo block height:', chain.height);

```

## Summary

- **Periodic Sync Routine**: The `sync()` function in [`src/stores/walletStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/walletStore.ts) orchestrates wallet synchronization with the Ergo blockchain, respecting a `MIN_SYNC_INTERVAL` to prevent excessive calls.
- **Four-Stage Pipeline**: The process derives HD addresses in chunks, queries Ergo-GraphQL for UTXO and asset data, detects changes against local IndexedDB state, and persists deltas while updating the UI reactively.
- **Automatic & Manual Triggers**: Synchronization fires automatically when `chain.height` changes (new block detection) or manually via `load()` with configurable background/foreground execution.
- **Health Monitoring**: The `checkOldUtxos()` function monitors for stale UTXOs using `HEALTHY_BLOCKS_AGE` to flag potential synchronization issues.

## Frequently Asked Questions

### How does Nautilus Wallet prevent excessive synchronization calls to the Ergo blockchain?

Nautilus implements a rate-limiting mechanism using the `MIN_SYNC_INTERVAL` constant defined in [`src/constants/intervals.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/constants/intervals.ts). The `sync()` function checks if the time elapsed since `privateState.lastSynced` is less than this interval (approximately 30 seconds) and exits early if the threshold hasn't been met, preventing network spam and reducing load on public GraphQL endpoints.

### What happens if the GraphQL endpoint returns new token IDs that the wallet hasn't seen before?

When the sync process detects new token IDs during the change detection phase, it triggers `assetsStore.loadMetadata()` to fetch EIP-4 compliant metadata for those tokens. This metadata includes token names, decimal places, and artwork URLs. The metadata is then cached and displayed in the UI, ensuring users see human-readable token information rather than raw token IDs.

### Can developers force a wallet synchronization to complete before executing a transaction?

Yes, developers can force synchronous wallet loading by calling `wallet.load(walletId, { syncInBackground: false })`. When `syncInBackground` is set to `false`, the method awaits the completion of the `sync()` function before resolving, ensuring that the wallet state is fully up-to-date with the Ergo blockchain before proceeding with transaction construction or balance checks.

### How does the wallet detect if it contains old or potentially spent UTXOs?

The wallet runs a health check via `checkOldUtxos()` in [`src/stores/walletStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/walletStore.ts), which queries the GraphQL service using `checkBoxesOlderThan` to find UTXOs exceeding `HEALTHY_BLOCKS_AGE`. If old boxes are found, the `health.hasOldUtxos` flag is set to true, alerting users to potential synchronization issues or the need to consolidate UTXOs to maintain wallet health.