Skip to content

feat: wire PXE and walletDB to SQLite-OPFS (opt-in)#17

Open
mverzilli wants to merge 3 commits intomainfrom
martin/sqlite-opfs-wiring
Open

feat: wire PXE and walletDB to SQLite-OPFS (opt-in)#17
mverzilli wants to merge 3 commits intomainfrom
martin/sqlite-opfs-wiring

Conversation

@mverzilli
Copy link
Copy Markdown
Contributor

Summary

Uses the new @aztec/kv-store/sqlite-opfs backend for both PXE state and the EmbeddedWallet's walletDB, replacing the default IndexedDB stores. The new backend persists to the browser's Origin Private File System via sqlite-wasm's opfs-sahpool VFS.

Gregoswap is the reference consumer for exercising the sqlite-opfs backend on real testnet traffic before we upstream a broader switch.

Changes

src/services/walletService.ts

Constructs two AztecSQLiteOPFSStore instances — one for PXE, one for walletDB — and passes them via the new pxe.store / walletDb.store hooks on EmbeddedWallet.create. Each store uses a distinct OPFS pool directory (SAH Pool acquires an exclusive lock per directory, so two stores in one tab can't share one) and a rollup-address-scoped DB name so switching networks doesn't cross-contaminate state.

In dev mode, registers window.__aztecStores with ad-hoc query + exportDb() helpers (see src/utils/sqliteInspector.ts) for browser-console inspection.

src/utils/sqliteInspector.ts (new)

Small dev-only helper: exposes downloadPxe() / downloadWallet() (grabs a real .sqlite image you can open in DB Browser for SQLite / sqlite3 CLI) and summary() (container → row-count overview). Tree-shaken out of production builds via import.meta.env.DEV.

package.json

Adds @aztec/kv-store as a direct dep — it was only reaching gregoswap transitively, and the yarn link: resolutions used by local-aztec:enable don't propagate workspace deps, so making it explicit keeps the dep tree stable across installs.

scripts/toggle-local-aztec.js

  • Adds @aztec/wallets to PACKAGE_MAPPINGS (was missing — my edits to the wallets package weren't reaching gregoswap until this landed).
  • Extends VITE_FS_ALLOW_PATHS with the kv-store worker path and the @sqlite.org/sqlite-wasm node_modules path so Vite's dev server can serve them.

vite.config.ts

Four dev-setup additions, all gated/scoped so they're no-ops without local-aztec:

  1. resolve.alias for transitive workspace deps that yarn link: doesn't surface (@aztec/bb.js, @aztec/noir-acvm_js, @aztec/noir-noirc_abi, @sqlite.org/sqlite-wasm). The paths are read dynamically from .local-aztec-path (the file the toggle script writes), so no user-specific absolute paths get committed.
  2. wasmContentTypePlugin — forces Content-Type: application/wasm on .wasm responses. Without this, WebAssembly.compileStreaming() (used by sqlite-wasm's Emscripten init) rejects with an incorrect-MIME error.
  3. optimizeDeps.exclude: ['@sqlite.org/sqlite-wasm'] — Vite's prebundle extracts the JS but doesn't copy the adjacent sqlite3.wasm asset, so the generated fetch URL 404s. Excluding keeps the JS at its real location where the .wasm sits beside it. (Per the sqlite-wasm docs' recommendation.)
  4. nodePolyfillsFix returns absolute paths — needed so import { Buffer } from 'buffer' resolves correctly from files linked outside gregoswap's workspace root.
  5. server.host: true + allowedHosts for ngrok-free / cloudflare-tunnel domains, so you can test from an iPhone against a remote dev server.

Scope and safety

  • Opt-in: aztec-packages itself still defaults to IndexedDB (browser) / LMDB (node) for PXE and walletDB. Gregoswap is the only consumer that currently opts into sqlite-opfs.
  • Testnet-ready: the wallet flow has been exercised on iOS Safari against testnet via cloudflared tunnel — both .aztec-kv-pxe-<rollup>/ and .aztec-kv-wallet-<rollup>/ directories populate in OPFS as expected.
  • Reversible: revert this one commit and gregoswap falls back to IndexedDB immediately. No schema migrations needed.

Test plan

  • yarn dev + pick testnet in the network selector → wallet creation succeeds.
  • DevTools → Application → Storage → Origin Private File System shows .aztec-kv-pxe-* and .aztec-kv-wallet-* subdirectories.
  • Swap flow works end-to-end (drip → swap) on testnet.
  • window.__aztecStores.summary() in DevTools console returns container/row-count summaries for both stores.
  • Reloading the page preserves wallet identity (OPFS persistence works).

Related

End-to-end persistence of both PXE state and the wallet's own account DB
on the OPFS-backed sqlite-opfs kv-store backend (new in aztec-packages).
The rollup address scopes DB names so switching networks doesn't
cross-contaminate; each store uses a distinct OPFS SAH-Pool directory
because the pool acquires an exclusive lock per directory.

- src/services/walletService.ts: construct two AztecSQLiteOPFSStore instances
  and pass them via { pxe: { store }, walletDb: { store } }. Dev-mode
  registers console-accessible inspectors.
- src/utils/sqliteInspector.ts: dev-only helper exposing window.__aztecStores
  with downloadPxe()/downloadWallet() (exports a real .sqlite image) and
  summary() for a quick container+row-count overview.
- package.json: add @aztec/kv-store as a direct dep — it's imported from
  source now, so yarn needs to keep it in the tree.

Infra updates to support linked aztec-packages:
- scripts/toggle-local-aztec.js: @aztec/wallets added to PACKAGE_MAPPINGS
  (was missing); VITE_FS_ALLOW_PATHS extended with kv-store and
  @sqlite.org/sqlite-wasm paths so Vite can serve the worker + wasm.
- vite.config.ts:
  * resolve.alias now dynamic — reads .local-aztec-path for the
    aztec-packages root, so no user-specific paths get committed.
  * wasmContentTypePlugin forces Content-Type: application/wasm on
    .wasm responses (sqlite-wasm's streaming compile needs this).
  * optimizeDeps.exclude adds @sqlite.org/sqlite-wasm — Vite's prebundle
    breaks colocated .wasm asset resolution.
  * nodePolyfillsFix returns absolute paths so Buffer imports from
    files linked outside the workspace root resolve correctly.
Enables testing from devices on the same network (iPhone via mkcert-trusted
HTTPS) or through a public tunnel (ngrok, cloudflared). The allowedHosts
list covers rotating free-tier ngrok subdomains and ephemeral trycloudflare
tunnels — tighten or replace with a specific domain if you want to
restrict further.

Usage:
  yarn dev              # LAN access at https://<mac-IP>:5173
  ngrok http 5173       # public HTTPS tunnel, URL printed by ngrok
Reviewer rightly asked why this one entry points at node_modules
when the others point at workspace source trees. The short answer is
that sqlite-wasm is a real npm package (not a workspace), and it's a
transitive dep of @aztec/kv-store that yarn link: doesn't surface
into the consumer. Declaring it as a direct dep of gregoswap would
not help, because Vite resolves worker imports from the worker file's
location (in aztec-packages), not from the consumer's root. Comment
expanded inline so the next reviewer doesn't have to re-derive this.
Comment on lines +46 to +72
// Both PXE state and the wallet's own DB go on SQLite-OPFS. Each store needs a
// distinct OPFS pool directory because SAH Pool acquires an exclusive lock on
// its directory — one shared directory would collide in a single tab. The
// rollup address scopes the DB names so switching networks doesn't
// cross-contaminate.
const l1Contracts = await node.getL1ContractAddresses();
const rollup = l1Contracts.rollupAddress.toString();
const pxeStore = await AztecSQLiteOPFSStore.open(
createLogger('pxe:data:sqlite-opfs'),
`pxe_data_${rollup}`,
false,
`.aztec-kv-pxe-${rollup}`,
);
const walletStore = await AztecSQLiteOPFSStore.open(
createLogger('wallet:data:sqlite-opfs'),
`wallet_data_${rollup}`,
false,
`.aztec-kv-wallet-${rollup}`,
);
const wallet = await EmbeddedWallet.create(node, {
pxe: { proverEnabled: true, store: pxeStore },
walletDb: { store: walletStore },
});
if (import.meta.env.DEV) {
// Expose dev-only inspectors at `window.__aztecStores`. See sqliteInspector.ts.
registerSqliteInspectors({ pxe: pxeStore, wallet: walletStore });
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: this should maybe be reified in embedded-wallet, but while the grego-mono-repo is in the works, this shows what's needed

walletDb: { store: walletStore },
});
if (import.meta.env.DEV) {
// Expose dev-only inspectors at `window.__aztecStores`. See sqliteInspector.ts.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a devtool convenience

@@ -0,0 +1,73 @@
/**
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is just an ad-hoc tool which helps observe what's going on, considering unlinke IndexedDB, we don't get any out of the box storage inspection support from the browser

Comment thread vite.config.ts
// Accept Host headers from tunnel providers without needing per-URL config.
// Wildcards cover rotating ngrok-free subdomains; trycloudflare.com covers
// ephemeral Cloudflare tunnels. Tighten if you want to restrict further.
allowedHosts: ['.ngrok-free.app', '.ngrok.app', '.trycloudflare.com'],
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used to test on iPhone via tunnels, will probably remove from final PR

@mverzilli mverzilli requested a review from Thunkar April 20, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant