# How Wallet Settings and User Preferences Are Stored in Nautilus Wallet

> Discover how Nautilus Wallet stores your global preferences in browser extension storage and per-wallet settings in IndexedDB. Learn about `useWebExtStorage` and `walletsDbService`.

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

---

**Nautilus Wallet persists global user preferences in the browser's extension storage (`browser.storage.local`) via the `useWebExtStorage` composable, while per-wallet settings like address reuse policies are stored in IndexedDB through the `walletsDbService`, with both layers synchronized through Pinia stores.**

Nautilus Wallet is an open-source browser extension for managing Ergo blockchain assets. Understanding how wallet settings and user preferences stored in Nautilus Wallet persist across sessions requires examining its dual-layer architecture that separates application-wide configuration from wallet-specific behavior.

## Global User Preferences in Extension Storage

Global preferences—such as UI theme, language, and zero-confirmation settings—apply to the entire extension instance. These values live in the browser's extension storage area and are managed reactively through Pinia.

### The useWebExtStorage Composable

In [`src/stores/appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/appStore.ts), the global `settings` object is initialized using the `useWebExtStorage` composable:

```typescript
// src/stores/appStore.ts
const settings = useWebExtStorage<Settings>("settings", DEFAULT_SETTINGS, {
  mergeDefaults: true
});

```

This composable, defined in [`src/composables/useWebExtStorage.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/composables/useWebExtStorage.ts), wraps the WebExtension Storage API (`browser.storage.local`). It serializes values using VueUse `StorageSerializers` and ensures that reads and writes are reactive within the Vue ecosystem.

### Default Settings and Migration

Default values are defined in [`src/constants/settings.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/constants/settings.ts):

```typescript
// src/constants/settings.ts
export const DEFAULT_SETTINGS = {
  lastOpenedWalletId: 0,
  isKyaAccepted: false,
  conversionCurrency: "usd",
  devMode: !MAINNET,
  graphQLServer: DEFAULT_SERVER_URL,
  explorerUrl: MAINNET ? "https://sigmaspace.io/en" : "https://testnet.ergoplatform.com",
  ipfsGateway: "https://ipfs.io/ipfs/",
  hideBalances: false,
  blacklistedTokensLists: ["nsfw", "scam"],
  zeroConf: false,
  locale: "auto" as const,
  colorMode: "auto" as const,
  extension: { viewMode: "popup" as const },
  ledger: { transport: "webusb" as const }
};

```

The extension includes migration logic to move legacy data from `localStorage` to the new extension storage. During initialization in [`appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/appStore.ts), if `isKyaAccepted` is false but wallets exist, the store checks for old `localStorage` settings and migrates them before clearing the legacy storage.

### Reading and Updating Global Preferences

To read a global setting, access the reactive ref through the app store:

```typescript
import { useAppStore } from "@/stores/appStore";
import { computed } from "vue";

const appStore = useAppStore();
const isDarkMode = computed(() => appStore.settings.colorMode === "dark");

```

Updates are automatically persisted:

```typescript
// Enable zero-confirmation transactions
appStore.settings.zeroConf = true;
// Changes are immediately written to browser.storage.local via useWebExtStorage

```

## Per-Wallet Settings in IndexedDB

While global preferences apply to the entire extension, **per-wallet settings** control behavior specific to individual wallets, such as address reuse policies and change address indices. These are stored within the wallet's database record.

### The WalletSettings Type and Database Schema

The type definition in [`src/types/internal.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/types/internal.ts) defines the structure:

```typescript
// src/types/internal.ts
export type WalletSettings = {
  avoidAddressReuse: boolean;
  addressFilter: AddressFilter;   // "all" | "active" | "unused"
  defaultChangeIndex: number;
};

```

The database schema in [`src/types/database.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/types/database.ts) nests these settings inside the wallet record:

```typescript
// src/types/database.ts
export interface IDbWallet {
  id: number;
  name: string;
  type: WalletType;
  publicKey: string;
  chainCode: string;
  mnemonic?: string;
  settings: WalletSettings;   // Persisted per-wallet configuration
}

```

### Persistence Flow via walletsDbService

When a wallet is created in [`src/stores/appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/appStore.ts), default settings are embedded in the database record:

```typescript
settings: {
  avoidAddressReuse: false,
  addressFilter: "all",
  defaultChangeIndex: 0
}

```

The `walletsDbService` in [`src/database/walletsDbService.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/database/walletsDbService.ts) handles IndexedDB operations. When settings change, `appStore.updateWallet` triggers a partial update to the `settings` column of the wallet record.

### Accessing Wallet-Specific Configuration

The `walletStore` in [`src/stores/walletStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/walletStore.ts) loads the wallet record and exposes a reactive `settings` ref:

```typescript
// Inside walletStore load()
settings.value = wlt.settings;

```

Components bind directly to these reactive properties. For example, to check address reuse settings:

```typescript
import { useWalletStore } from "@/stores/walletStore";
import { computed } from "vue";

const walletStore = useWalletStore();
const avoidReuse = computed(() => walletStore.settings.avoidAddressReuse);

```

Changes trigger automatic persistence through a deep watcher in [`walletStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/walletStore.ts):

```typescript
watch(
  [name, settings, () => privateState.lastSynced],
  async () => {
    if (privateState.loading) return;
    appStore.updateWallet(privateState.id, {
      name: name.value,
      lastSynced: privateState.lastSynced,
      settings: toRaw(settings.value)
    });
  },
  { deep: true }
);

```

## How the Storage Layers Interact

Nautilus Wallet maintains a strict architectural separation between global and wallet-specific configuration:

- **Global preferences** in `browser.storage.local` affect the entire extension instance. These include theme settings, language, and global flags like `zeroConf`. They are accessible via `appStore.settings`.

- **Per-wallet settings** in IndexedDB affect only the active wallet. These include address derivation policies and filters. They are accessible via `walletStore.settings`.

The UI components in [`src/views/settings/SettingsView.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/views/settings/SettingsView.vue) and [`src/views/wallet/WalletSettings.vue`](https://github.com/nautls/nautilus-wallet/blob/main/src/views/wallet/WalletSettings.vue) read from the appropriate Pinia store based on whether the setting is global or wallet-specific. Writes flow back through the same stores, ensuring persistence happens automatically without manual database calls.

## Summary

- **Global user preferences** are stored in `browser.storage.local` via the `useWebExtStorage` composable and accessed through `appStore.settings`.
- **Per-wallet settings** are embedded in the `IDbWallet` records in IndexedDB, managed by `walletsDbService`, and exposed via `walletStore.settings`.
- **Automatic persistence** occurs through Pinia's reactivity system: global settings write immediately to extension storage, while wallet settings trigger IndexedDB updates through deep watchers.
- **Migration support** exists in [`appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/appStore.ts) for upgrading legacy `localStorage` data to the modern extension storage API.

## Frequently Asked Questions

### Where does Nautilus Wallet store theme and language preferences?

Theme and language preferences are stored in the browser's extension storage (`browser.storage.local`) as part of the global settings object. The `useWebExtStorage` composable in [`src/composables/useWebExtStorage.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/composables/useWebExtStorage.ts) wraps this API, exposing a reactive `settings` ref in [`src/stores/appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/appStore.ts) that includes `colorMode` for themes and `locale` for language settings.

### How are per-wallet address reuse settings persisted?

Per-wallet address reuse settings are stored in IndexedDB as part of the `IDbWallet` record. The `WalletSettings` type in [`src/types/internal.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/types/internal.ts) defines the `avoidAddressReuse` boolean, which is nested inside the `IDbWallet` interface from [`src/types/database.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/types/database.ts). When modified via `walletStore.settings`, a deep watcher triggers `walletsDbService` to update the specific wallet record in IndexedDB.

### Does Nautilus Wallet migrate old localStorage data?

Yes. During initialization in [`src/stores/appStore.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/stores/appStore.ts), the extension checks if `isKyaAccepted` is false while wallets exist in the database. If legacy `localStorage` data is detected, the store parses the old settings, merges them with `DEFAULT_SETTINGS`, writes them to `browser.storage.local` via `useWebExtStorage`, and clears the legacy `localStorage` to complete the migration.

### Can I access wallet settings programmatically from a content script?

No. Wallet settings in IndexedDB and global settings in `browser.storage.local` are isolated to the extension's internal contexts. Content scripts run in the context of web pages and cannot directly access IndexedDB or extension storage without explicit permissions and messaging APIs. All programmatic access to settings should occur within the extension's background scripts or internal pages using the provided `appStore` and `walletStore` Pinia stores.