|
| 1 | +# Claude Context: Demo Wallet |
| 2 | + |
| 3 | +## Project Overview |
| 4 | + |
| 5 | +This is an Aztec wallet application built with Electron that allows dApps to interact with user accounts through a secure browser extension interface. The wallet manages private keys, handles transaction simulation/execution, and provides authorization flows for dApp requests. |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +### Core Components |
| 10 | + |
| 11 | +1. **Electron App (Main Process)** |
| 12 | + - Native desktop application |
| 13 | + - Manages wallet instances via worker threads |
| 14 | + - Handles IPC between UI and wallet logic |
| 15 | + - Located in `/app` |
| 16 | + |
| 17 | +2. **Browser Extension** |
| 18 | + - Provides interface between dApps and wallet |
| 19 | + - Supports Chromium-based browsers (Chrome, Brave, Edge) and Firefox |
| 20 | + - Located in `/extension` (separate repository) |
| 21 | + - Communicates with wallet via WebSocket on port **8765** |
| 22 | + |
| 23 | +3. **Wallet Worker (`wallet-worker.ts`)** |
| 24 | + - Manages PXE (Private Execution Environment) instances |
| 25 | + - Handles wallet operations (simulate, send, register contracts) |
| 26 | + - Maintains session state per network (chainId-version) |
| 27 | + - Creates separate wallet instances per app for authorization isolation |
| 28 | + |
| 29 | +4. **WebSocket Worker (`ws-worker.ts`)** |
| 30 | + - Runs WebSocket server on port 8765 |
| 31 | + - Bridges extension ↔ wallet communication |
| 32 | + - **Temporary**: Should use Native Messaging API instead |
| 33 | + |
| 34 | +### Key Architectural Decisions |
| 35 | + |
| 36 | +#### Single PXE Instance Per Session |
| 37 | + |
| 38 | +**Critical**: Each network session (identified by `chainId-version`) has **one shared PXE instance** used by all apps. Multiple PXE instances per session causes JavaScript Maps to get out of sync with LMDB, leading to `Cannot read properties of undefined (reading 'getValuesAsync')` errors. |
| 39 | + |
| 40 | +**Structure** (`wallet-worker.ts`): |
| 41 | +```typescript |
| 42 | +type SessionData = { |
| 43 | + sharedResources: Promise<{ |
| 44 | + pxe: any; // Shared across all apps |
| 45 | + node: any; // Shared across all apps |
| 46 | + db: any; // Shared across all apps |
| 47 | + pendingAuthorizations: Map<string, any>; |
| 48 | + }>; |
| 49 | + wallets: Map<string, Promise<{ external: ExternalWallet; internal: InternalWallet }>>; |
| 50 | +}; |
| 51 | + |
| 52 | +const RUNNING_SESSIONS = new Map<string, SessionData>(); |
| 53 | +``` |
| 54 | + |
| 55 | +- **Session Level**: One PXE per network, indexed by `sessionId` |
| 56 | +- **App Level**: Separate `ExternalWallet` and `InternalWallet` instances per `appId` |
| 57 | +- **Why**: PXE's `NoteDataProvider` maintains JavaScript Maps that must stay in sync with LMDB |
| 58 | + |
| 59 | +#### External vs Internal Wallets |
| 60 | + |
| 61 | +- **ExternalWallet**: Handles requests from dApps (via extension) |
| 62 | +- **InternalWallet**: Handles internal requests (UI, account management) |
| 63 | +- Both share the same PXE, node, db, and authorization state |
| 64 | + |
| 65 | +#### Authorization Flow |
| 66 | + |
| 67 | +1. dApp sends request → Extension → WebSocket → Wallet Worker |
| 68 | +2. Wallet simulates transaction and creates `AuthorizationRequest` |
| 69 | +3. Authorization dialog shown to user in Electron UI |
| 70 | +4. User approves/denies → Response sent back through chain |
| 71 | +5. Authorized operations can be persisted (e.g., `simulateTx` with payload hash) |
| 72 | + |
| 73 | +## Database Structure |
| 74 | + |
| 75 | +### WalletDB (`wallet-db.ts`) |
| 76 | + |
| 77 | +Uses LMDB (via `@aztec/kv-store/lmdb-v2`) for persistent storage: |
| 78 | + |
| 79 | +- **accounts**: Account data indexed by address |
| 80 | +- **aliases**: Human-readable names for addresses |
| 81 | +- **interactions**: History of all wallet operations |
| 82 | +- **authorizations**: Persistent authorization grants (keyed by `appId:method:item`) |
| 83 | +- **txSimulations**: Cached simulation results with metadata |
| 84 | + - Stores: `{ simulationResult, txRequest, metadata: { from, embeddedPaymentMethodFeePayer } }` |
| 85 | +- **bridgedFeeJuice**: Fee payment token management |
| 86 | + |
| 87 | +### PXE Storage |
| 88 | + |
| 89 | +PXE maintains its own LMDB database at `~/keychain/pxe-${rollupAddress}`: |
| 90 | +- Note synchronization data |
| 91 | +- Contract artifacts and instances |
| 92 | +- Encrypted notes per account |
| 93 | + |
| 94 | +## Common Issues & Fixes |
| 95 | + |
| 96 | +### 1. PXE Note Synchronization Error |
| 97 | + |
| 98 | +**Symptom**: `Cannot read properties of undefined (reading 'getValuesAsync')` after deleting authorizations or creating new accounts. |
| 99 | + |
| 100 | +**Root Cause**: Multiple PXE instances sharing the same LMDB database. When `addScope()` is called and the scope already exists in LMDB, it returns early without populating JavaScript Maps. |
| 101 | + |
| 102 | +**Solution**: |
| 103 | +1. Ensure only one PXE per session (fixed in `wallet-worker.ts`) |
| 104 | +2. Defensive fix in PXE's `note_data_provider.js` for lazy Map initialization (see patch file) |
| 105 | + |
| 106 | +**Files**: |
| 107 | +- `app/src/workers/wallet-worker.ts`: Single PXE per session |
| 108 | +- `app/node_modules/@aztec/pxe/dest/storage/note_data_provider/note_data_provider.js`: Defensive fix |
| 109 | +- `pxe-note-provider-map-sync-fix.patch`: Patch file for Aztec repo |
| 110 | + |
| 111 | +### 2. Batch Authorization Denial |
| 112 | + |
| 113 | +**Symptom**: Operations stuck in "requesting authorization" after user clicks "Deny All". |
| 114 | + |
| 115 | +**Root Cause**: Batch method in `external-wallet.ts` didn't catch authorization denial error. |
| 116 | + |
| 117 | +**Solution**: Wrap `requestAuthorization` in try-catch and mark items as ERROR on denial. |
| 118 | + |
| 119 | +**File**: `app/src/wallet/core/external-wallet.ts` |
| 120 | + |
| 121 | +### 3. Execution Trace Performance |
| 122 | + |
| 123 | +**Symptom**: Unreasonable delay when opening execution trace dialog. |
| 124 | + |
| 125 | +**Root Cause**: `getExecutionTrace()` was creating a new `DecodingCache` instance on every call. |
| 126 | + |
| 127 | +**Solution**: Use shared `this.decodingCache` from `BaseNativeWallet`. |
| 128 | + |
| 129 | +**File**: `app/src/wallet/core/internal-wallet.ts:196-229` |
| 130 | + |
| 131 | +### 4. Stats Not Showing After Authorization |
| 132 | + |
| 133 | +**Symptom**: Simulation stats visible during authorization but not when clicking stored interactions. |
| 134 | + |
| 135 | +**Root Cause**: `getExecutionTrace()` wasn't returning stats from stored simulations. |
| 136 | + |
| 137 | +**Solution**: Update return type and extract stats from both utility traces and tx simulations. |
| 138 | + |
| 139 | +**Files**: |
| 140 | +- `app/src/wallet/core/internal-wallet.ts` |
| 141 | +- `app/src/wallet/database/wallet-db.ts` |
| 142 | + |
| 143 | +### 5. Utility Trace Confusion |
| 144 | + |
| 145 | +**Symptom**: `getUtilityTrace()` returning data for tx simulations. |
| 146 | + |
| 147 | +**Root Cause**: Method didn't check if `utilityTrace` field actually existed in stored data. |
| 148 | + |
| 149 | +**Solution**: Add check `if (!parsed.utilityTrace) return undefined;` |
| 150 | + |
| 151 | +**File**: `app/src/wallet/database/wallet-db.ts` |
| 152 | + |
| 153 | +## Important Code Patterns |
| 154 | + |
| 155 | +### Fee Payer Information |
| 156 | + |
| 157 | +When a transaction includes `embeddedPaymentMethodFeePayer`, the app is providing the fee payment method: |
| 158 | +- Extract from `opts.fee?.embeddedPaymentMethodFeePayer` or `executionPayload.feePayer` |
| 159 | +- Store in metadata when saving simulations |
| 160 | +- Display in authorization dialogs and execution traces with success alert |
| 161 | + |
| 162 | +### MulticallEntrypoint Detection |
| 163 | + |
| 164 | +When `from` is set to `AztecAddress.ZERO`, the request uses MulticallEntrypoint: |
| 165 | +- Does not execute from any user account |
| 166 | +- Display reassuring info alert in authorization dialogs |
| 167 | + |
| 168 | +### Simulation Title Generation |
| 169 | + |
| 170 | +Use `generateSimulationTitle()` from `simulation-utils.ts`: |
| 171 | +- Includes contract names and function calls |
| 172 | +- Handles fee payer information |
| 173 | +- Provides readable titles for UI |
| 174 | + |
| 175 | +### Zod Schemas for IPC |
| 176 | + |
| 177 | +All data crossing IPC boundaries must have Zod schemas: |
| 178 | +- `InternalWalletInterfaceSchema` in `wallet-internal-interface.ts` |
| 179 | +- Serialization/deserialization handled automatically |
| 180 | +- Update schemas when adding new fields to return types |
| 181 | + |
| 182 | +## UI Components |
| 183 | + |
| 184 | +### Authorization Dialogs |
| 185 | + |
| 186 | +- `AuthorizeSendTxContent.tsx`: Send transaction authorization |
| 187 | +- `AuthorizeSimulateTxContent.tsx`: Simulation authorization |
| 188 | +- Display fee payer and MulticallEntrypoint alerts |
| 189 | + |
| 190 | +### Execution Trace Display |
| 191 | + |
| 192 | +- `ExecutionTraceDisplay.tsx`: Shows decoded execution with timing stats |
| 193 | +- `ExecutionTraceDialog.tsx`: Modal dialog for viewing stored interactions |
| 194 | +- `SimulationStatsDisplay`: Collapsible accordion with function hierarchy and RPC calls |
| 195 | + |
| 196 | +### Features |
| 197 | + |
| 198 | +- Collapsible simulation timing stats (default collapsed) |
| 199 | +- Function call hierarchy with tree visualization |
| 200 | +- RPC calls table sorted by total time |
| 201 | +- Call authorization display |
| 202 | +- Automatic contract/function decoding via `DecodingCache` |
| 203 | + |
| 204 | +## Development Setup |
| 205 | + |
| 206 | +### Requirements |
| 207 | + |
| 208 | +- Node.js v22 |
| 209 | +- yarn |
| 210 | +- Running Aztec node (local or remote) |
| 211 | + |
| 212 | +### Running |
| 213 | + |
| 214 | +```bash |
| 215 | +# Wallet |
| 216 | +cd app |
| 217 | +yarn install |
| 218 | +yarn start |
| 219 | + |
| 220 | +# Extension |
| 221 | +cd extension |
| 222 | +yarn install |
| 223 | +yarn dev # Launches browser with extension |
| 224 | +``` |
| 225 | + |
| 226 | +### Port Configuration |
| 227 | + |
| 228 | +- **WebSocket**: Port 8765 (configurable in `ws-worker.ts`) |
| 229 | +- **Node RPC**: Default port 8080 (configured in `networks.ts`) |
| 230 | + |
| 231 | +## Testing Scenarios |
| 232 | + |
| 233 | +### Creating and Revoking Authorizations |
| 234 | + |
| 235 | +**Critical Test**: This scenario previously triggered the PXE synchronization bug: |
| 236 | + |
| 237 | +1. Create account "ECDSAR1 0" |
| 238 | +2. Complete app onboarding (simulations, transactions, contract registrations) |
| 239 | +3. Revoke ALL authorizations for the app |
| 240 | +4. Create new account "ECDSAR1 1" |
| 241 | +5. Attempt app onboarding again |
| 242 | + |
| 243 | +**Expected**: Should work without errors |
| 244 | +**Previously**: Failed with `getValuesAsync` undefined error |
| 245 | + |
| 246 | +### Batch Operations |
| 247 | + |
| 248 | +Test "Deny All" in batch authorization dialogs: |
| 249 | +- Should mark all pending operations as ERROR |
| 250 | +- Should not leave operations stuck in "requesting authorization" |
| 251 | + |
| 252 | +### Execution Trace Performance |
| 253 | + |
| 254 | +- Opening execution trace dialog should be instant |
| 255 | +- No delays or multiple decoding cache instantiations |
| 256 | + |
| 257 | +## Code Quality Guidelines |
| 258 | + |
| 259 | +### Error Handling |
| 260 | + |
| 261 | +- Always wrap authorization requests in try-catch |
| 262 | +- Contextualize errors with relevant data (use `contextualizeError`) |
| 263 | +- Log meaningful messages at appropriate levels |
| 264 | + |
| 265 | +### Resource Management |
| 266 | + |
| 267 | +- Reuse shared resources (PXE, node client, caches) |
| 268 | +- Never create duplicate PXE instances for the same session |
| 269 | +- Clean up event listeners when destroying wallet instances |
| 270 | + |
| 271 | +### Logging |
| 272 | + |
| 273 | +- Use `createProxyLogger` for worker threads |
| 274 | +- Include context in logger names (e.g., `wallet:external:${appId}`) |
| 275 | +- Avoid excessive logging in production code |
| 276 | +- Debug logs helped identify the PXE multi-instance issue |
| 277 | + |
| 278 | +### TypeScript |
| 279 | + |
| 280 | +- Use strict typing for operation display data |
| 281 | +- Define explicit types for IPC message parameters |
| 282 | +- Avoid `any` except for Zod schemas (with `@ts-ignore`) |
| 283 | + |
| 284 | +## Known Limitations |
| 285 | + |
| 286 | +1. **WebSocket Communication**: Should use Native Messaging API instead |
| 287 | +2. **Port Management**: Port 8765 must be available |
| 288 | +3. **Single Network**: Currently only supports one rollup version at a time |
| 289 | +4. **Authorization Persistence**: Limited to `appId:method:item` format |
| 290 | + |
| 291 | +## Future Improvements |
| 292 | + |
| 293 | +1. **Native Messaging**: Replace WebSocket with browser's native extension-to-app communication |
| 294 | +2. **Multi-Network**: Support multiple networks simultaneously |
| 295 | +3. **Account Derivation**: BIP-44 HD wallet support |
| 296 | +4. **Hardware Wallets**: Ledger/Trezor integration |
| 297 | +5. **Fee Estimation**: Better gas estimation and fee payment methods |
| 298 | +6. **Transaction History**: Enhanced filtering and search |
| 299 | +7. **Contact Book**: Address aliases and management |
| 300 | + |
| 301 | +## File Reference |
| 302 | + |
| 303 | +### Critical Files |
| 304 | + |
| 305 | +- `app/src/workers/wallet-worker.ts`: Session and PXE management |
| 306 | +- `app/src/wallet/core/external-wallet.ts`: dApp request handling |
| 307 | +- `app/src/wallet/core/internal-wallet.ts`: Internal operations |
| 308 | +- `app/src/wallet/database/wallet-db.ts`: Persistent storage |
| 309 | +- `app/src/wallet/operations/*.ts`: Operation implementations |
| 310 | +- `app/src/ipc/wallet-internal-interface.ts`: IPC type definitions |
| 311 | + |
| 312 | +### UI Files |
| 313 | + |
| 314 | +- `app/src/ui/components/authorization/*`: Authorization dialogs |
| 315 | +- `app/src/ui/components/dialogs/*`: Modals and dialogs |
| 316 | +- `app/src/ui/components/shared/*`: Reusable components |
| 317 | +- `app/src/ui/components/sections/*`: Main UI sections |
| 318 | + |
| 319 | +### Configuration |
| 320 | + |
| 321 | +- `app/src/config/networks.ts`: Network definitions |
| 322 | +- `app/package.json`: Dependencies and scripts |
| 323 | +- `README.md`: Setup instructions |
| 324 | + |
| 325 | +## Debugging Tips |
| 326 | + |
| 327 | +### Check PXE Instance Count |
| 328 | + |
| 329 | +Look for log messages: |
| 330 | +``` |
| 331 | +[PXE-INIT] Creating NEW session with shared PXE instance for sessionId=... |
| 332 | +[PXE-INIT] Reusing existing shared PXE instance for sessionId=... |
| 333 | +``` |
| 334 | + |
| 335 | +Should only see one "Creating NEW" per network session. |
| 336 | + |
| 337 | +### Authorization Flow |
| 338 | + |
| 339 | +Check logs for: |
| 340 | +``` |
| 341 | +Received external message: ... |
| 342 | +Received internal message: ... |
| 343 | +``` |
| 344 | + |
| 345 | +External = from dApp, Internal = from wallet UI |
| 346 | + |
| 347 | +### Note Synchronization |
| 348 | + |
| 349 | +If errors occur, check: |
| 350 | +- `~/keychain/aztec-keychain-debug.log` for full logs |
| 351 | +- Whether multiple PXE instances are being created |
| 352 | +- LMDB data directory: `~/keychain/pxe-${rollupAddress}` |
| 353 | + |
| 354 | +### Performance Issues |
| 355 | + |
| 356 | +- Check if `DecodingCache` is being reused (should be singleton per wallet) |
| 357 | +- Monitor database query times |
| 358 | +- Look for unnecessary re-renders in React components |
| 359 | + |
| 360 | +## Version Information |
| 361 | + |
| 362 | +- **Node.js**: v22 |
| 363 | +- **Electron**: (check `app/package.json`) |
| 364 | +- **Aztec SDK**: (check `app/package.json` for `@aztec/*` packages) |
| 365 | +- **React**: (check `app/package.json`) |
| 366 | + |
| 367 | +## Related Repositories |
| 368 | + |
| 369 | +- **Extension**: `../extension` (sibling directory) |
| 370 | +- **Aztec Packages**: Official Aztec monorepo (for PXE patches) |
| 371 | + |
| 372 | +## Patch Files |
| 373 | + |
| 374 | +- `pxe-note-provider-map-sync-fix.patch`: Fix for PXE note synchronization issue |
| 375 | + - Apply to: `yarn-project/pxe/src/storage/note_data_provider/note_data_provider.ts` |
| 376 | + - Command: `git apply /path/to/patch` or `git am /path/to/patch` |
0 commit comments