Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@aztec/constants": "v4.3.0-nightly.20260417",
"@aztec/entrypoints": "v4.3.0-nightly.20260417",
"@aztec/foundation": "v4.3.0-nightly.20260417",
"@aztec/kv-store": "v4.3.0-nightly.20260417",
"@aztec/noir-contracts.js": "v4.3.0-nightly.20260417",
"@aztec/protocol-contracts": "v4.3.0-nightly.20260417",
"@aztec/pxe": "v4.3.0-nightly.20260417",
Expand Down
19 changes: 18 additions & 1 deletion scripts/toggle-local-aztec.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,32 @@ const PACKAGE_MAPPINGS = {
"@aztec/test-wallet": "yarn-project/test-wallet",
"@aztec/validator-client": "yarn-project/validator-client",
"@aztec/wallet-sdk": "yarn-project/wallet-sdk",
"@aztec/wallets": "yarn-project/wallets",
"@aztec/world-state": "yarn-project/world-state",
};

// Paths within aztec-packages that need to be allowed in vite's fs.allow
// Paths within aztec-packages that need to be allowed in vite's fs.allow.
//
// Most entries point at built-output directories of workspace packages — those
// packages live in the source tree (yarn workspaces, not node_modules).
//
// The `yarn-project/node_modules/@sqlite.org/sqlite-wasm` entry is the odd one
// out: @sqlite.org/sqlite-wasm is a real npm package, not a workspace, and it's
// a transitive dep of @aztec/kv-store. Yarn's `link:` protocol doesn't install
// the linked package's transitive deps into the consumer, so we can't resolve
// it from gregoswap's own node_modules. Instead, we resolve it from aztec-
// packages's hoisted install (yarn-project/node_modules) and Vite serves from
// there. See the matching alias in vite.config.ts's loadLocalAztecAliases().
// Declaring the package as a direct dep of gregoswap would not help: Vite
// resolves the worker file's imports from the worker file's location (in
// aztec-packages), not from the consumer's root.
const VITE_FS_ALLOW_PATHS = [
"yarn-project/noir-protocol-circuits-types/artifacts",
"noir/packages/noirc_abi/web",
"noir/packages/acvm_js/web",
"barretenberg/ts/dest/browser",
"yarn-project/kv-store",
"yarn-project/node_modules/@sqlite.org/sqlite-wasm",
];

function savePath(aztecPath) {
Expand Down
31 changes: 30 additions & 1 deletion src/services/walletService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { createAztecNodeClient, type AztecNode } from '@aztec/aztec.js/node';
import type { Wallet } from '@aztec/aztec.js/wallet';
import type { ChainInfo } from '@aztec/aztec.js/account';
import { Fr } from '@aztec/aztec.js/fields';
import { createLogger } from '@aztec/foundation/log';
import { AztecSQLiteOPFSStore } from '@aztec/kv-store/sqlite-opfs';
import { registerSqliteInspectors } from '../utils/sqliteInspector';
import {
WalletManager,
type WalletProvider,
Expand Down Expand Up @@ -40,7 +43,33 @@ export function createNodeClient(nodeUrl: string): AztecNode {
export async function createEmbeddedWallet(
node: AztecNode,
): Promise<{ wallet: EmbeddedWallet; address: AztecAddress }> {
const wallet = await EmbeddedWallet.create(node, { pxeConfig: { proverEnabled: true } });
// 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.

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

registerSqliteInspectors({ pxe: pxeStore, wallet: walletStore });
}
Comment on lines +46 to +72

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

let accountManager = await wallet.loadStoredAccount();
if (!accountManager) {
accountManager = await wallet.createInitializerlessAccount();
Expand Down
73 changes: 73 additions & 0 deletions src/utils/sqliteInspector.ts
Original file line number Diff line number Diff line change
@@ -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

* Dev-only inspectors for SQLite-OPFS stores.
*
* Exposed on `window.__aztecStores` by walletService.ts in development mode so the
* DB contents can be examined from the browser DevTools console without
* copy-pasting recipes. These helpers are a no-op in production builds.
*/

import type { AztecAsyncKVStore } from '@aztec/kv-store';

/** Minimal subset of AztecSQLiteOPFSStore the inspectors need. */
interface InspectableStore extends AztecAsyncKVStore {
allAsync(sql: string, bind?: unknown[]): Promise<unknown[][]>;
exportDb(): Promise<Uint8Array>;
}

function downloadBytes(bytes: Uint8Array, filename: string): void {
const blob = new Blob([bytes], { type: 'application/x-sqlite3' });
const url = URL.createObjectURL(blob);
try {
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
} finally {
// Revoke after the click handler has started the download, give the browser a beat.
setTimeout(() => URL.revokeObjectURL(url), 10_000);
}
}

/**
* Summary row: container name and row count. Useful for a quick overview of what
* each store holds right now.
*/
async function summarize(store: InspectableStore): Promise<Array<{ container: string; rows: number }>> {
const rows = await store.allAsync(
'SELECT container, count(*) AS n FROM data GROUP BY container ORDER BY n DESC',
);
return rows.map(r => ({ container: String(r[0]), rows: Number(r[1]) }));
}

/** Stores exposed for inspection, plus their bound helpers. */
export type SqliteInspectors = {
pxe: InspectableStore;
wallet: InspectableStore;
/** Downloads the PXE store as `pxe.sqlite`. */
downloadPxe(): Promise<void>;
/** Downloads the walletDB store as `wallet.sqlite`. */
downloadWallet(): Promise<void>;
/** Prints container/row-count summaries for both stores (console-friendly). */
summary(): Promise<{ pxe: Array<{ container: string; rows: number }>; wallet: Array<{ container: string; rows: number }> }>;
};

/**
* Registers the inspectors on `window.__aztecStores`. Safe to call in SSR/non-dev
* contexts — it bails out cleanly.
*/
export function registerSqliteInspectors(stores: { pxe: InspectableStore; wallet: InspectableStore }): void {
if (typeof window === 'undefined') {
return;
}
const inspectors: SqliteInspectors = {
pxe: stores.pxe,
wallet: stores.wallet,
downloadPxe: async () => downloadBytes(await stores.pxe.exportDb(), 'pxe.sqlite'),
downloadWallet: async () => downloadBytes(await stores.wallet.exportDb(), 'wallet.sqlite'),
summary: async () => ({
pxe: await summarize(stores.pxe),
wallet: await summarize(stores.wallet),
}),
};
(window as unknown as { __aztecStores: SqliteInspectors }).__aztecStores = inspectors;
}
67 changes: 65 additions & 2 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,58 @@ const nodePolyfillsFix = (options?: PolyfillOptions | undefined): Plugin => {
resolveId(source: string) {
const m = /^vite-plugin-node-polyfills\/shims\/(buffer|global|process)$/.exec(source);
if (m) {
return `./node_modules/vite-plugin-node-polyfills/shims/${m[1]}/dist/index.cjs`;
return path.resolve(
process.cwd(),
`node_modules/vite-plugin-node-polyfills/shims/${m[1]}/dist/index.cjs`,
);
}
},
};
};

/**
* Loads resolve aliases for transitive aztec-packages workspace deps that yarn `link:`
* doesn't surface to gregoswap's node_modules. Reads the aztec-packages root from
* `.local-aztec-path` (written by `scripts/toggle-local-aztec.js enable`). Returns `{}`
* when the file doesn't exist (local-aztec disabled), leaving npm resolutions active.
*/
function loadLocalAztecAliases(): Record<string, string> {
try {
const root = fs.readFileSync(path.resolve(process.cwd(), '.local-aztec-path'), 'utf-8').trim();
if (!root) {
return {};
}
return {
'@aztec/bb.js': `${root}/barretenberg/ts/dest/browser/index.js`,
'@aztec/noir-acvm_js': `${root}/noir/packages/acvm_js/web/acvm_js.js`,
'@aztec/noir-noirc_abi': `${root}/noir/packages/noirc_abi/web/noirc_abi_wasm.js`,
'@sqlite.org/sqlite-wasm': `${root}/yarn-project/node_modules/@sqlite.org/sqlite-wasm/index.mjs`,
};
} catch {
// No .local-aztec-path file — we're using npm packages, no aliases needed.
return {};
}
}

/**
* Force `Content-Type: application/wasm` on `.wasm` files served by Vite's dev server.
* Without this, `WebAssembly.compileStreaming()` (used by sqlite-wasm and others)
* rejects the response with "Incorrect response MIME type. Expected 'application/wasm'".
* Vite's dev middleware doesn't set this header by default for files served from
* aliased / @fs paths outside node_modules.
*/
const wasmContentTypePlugin = (): Plugin => ({
name: 'wasm-content-type',
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url?.includes('.wasm')) {
res.setHeader('Content-Type', 'application/wasm');
}
next();
});
},
});

/**
* Lightweight chunk size validator plugin
* Checks chunk sizes after build completes and fails if limits are exceeded
Expand Down Expand Up @@ -105,12 +151,24 @@ export default defineConfig(({ command, mode }) => {
const isDev = command === 'serve';
const esTarget = isDev ? 'es2016' : 'esnext';

const localAztecAliases = loadLocalAztecAliases();

return {
base: './',
logLevel: process.env.CI ? 'error' : undefined,
esbuild: { target: esTarget },
build: { target: esTarget },
resolve: {
alias: localAztecAliases,
},
server: {
// Bind on 0.0.0.0 so a tunnel (ngrok, cloudflared) or same-network device
// (e.g. iPhone with mkcert-trusted HTTPS) can reach the dev server.
host: true,
// 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

// Headers needed for bb WASM to work in multithreaded mode
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
Expand All @@ -121,7 +179,11 @@ export default defineConfig(({ command, mode }) => {
},
},
optimizeDeps: {
exclude: ['@aztec/noir-acvm_js', '@aztec/noir-noirc_abi', '@aztec/bb.js'],
// @sqlite.org/sqlite-wasm must be excluded: Vite's prebundle extracts the JS
// into .vite/deps/ but doesn't copy the adjacent sqlite3.wasm binary, so the
// generated fetch URL 404s. Excluding keeps the JS at its real location where
// the .wasm sits next to it.
exclude: ['@aztec/noir-acvm_js', '@aztec/noir-noirc_abi', '@aztec/bb.js', '@sqlite.org/sqlite-wasm'],
include: ['@gregojuice/embedded-wallet/ui'],
esbuildOptions: { target: esTarget },
},
Expand All @@ -132,6 +194,7 @@ export default defineConfig(({ command, mode }) => {
...(isDev ? { devTarget: 'es2016' as const } : {}),
}),
nodePolyfillsFix({ include: ['buffer', 'path'] }),
wasmContentTypePlugin(),
chunkSizeValidator([
{
pattern: /assets\/index-.*\.js$/,
Expand Down
Loading