diff --git a/CLAUDE.MD b/CLAUDE.MD new file mode 100644 index 0000000..909d22d --- /dev/null +++ b/CLAUDE.MD @@ -0,0 +1,376 @@ +# Claude Context: Demo Wallet + +## Project Overview + +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. + +## Architecture + +### Core Components + +1. **Electron App (Main Process)** + - Native desktop application + - Manages wallet instances via worker threads + - Handles IPC between UI and wallet logic + - Located in `/app` + +2. **Browser Extension** + - Provides interface between dApps and wallet + - Supports Chromium-based browsers (Chrome, Brave, Edge) and Firefox + - Located in `/extension` (separate repository) + - Communicates with wallet via WebSocket on port **8765** + +3. **Wallet Worker (`wallet-worker.ts`)** + - Manages PXE (Private Execution Environment) instances + - Handles wallet operations (simulate, send, register contracts) + - Maintains session state per network (chainId-version) + - Creates separate wallet instances per app for authorization isolation + +4. **WebSocket Worker (`ws-worker.ts`)** + - Runs WebSocket server on port 8765 + - Bridges extension ↔ wallet communication + - **Temporary**: Should use Native Messaging API instead + +### Key Architectural Decisions + +#### Single PXE Instance Per Session + +**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. + +**Structure** (`wallet-worker.ts`): +```typescript +type SessionData = { + sharedResources: Promise<{ + pxe: any; // Shared across all apps + node: any; // Shared across all apps + db: any; // Shared across all apps + pendingAuthorizations: Map; + }>; + wallets: Map>; +}; + +const RUNNING_SESSIONS = new Map(); +``` + +- **Session Level**: One PXE per network, indexed by `sessionId` +- **App Level**: Separate `ExternalWallet` and `InternalWallet` instances per `appId` +- **Why**: PXE's `NoteDataProvider` maintains JavaScript Maps that must stay in sync with LMDB + +#### External vs Internal Wallets + +- **ExternalWallet**: Handles requests from dApps (via extension) +- **InternalWallet**: Handles internal requests (UI, account management) +- Both share the same PXE, node, db, and authorization state + +#### Authorization Flow + +1. dApp sends request → Extension → WebSocket → Wallet Worker +2. Wallet simulates transaction and creates `AuthorizationRequest` +3. Authorization dialog shown to user in Electron UI +4. User approves/denies → Response sent back through chain +5. Authorized operations can be persisted (e.g., `simulateTx` with payload hash) + +## Database Structure + +### WalletDB (`wallet-db.ts`) + +Uses LMDB (via `@aztec/kv-store/lmdb-v2`) for persistent storage: + +- **accounts**: Account data indexed by address +- **aliases**: Human-readable names for addresses +- **interactions**: History of all wallet operations +- **authorizations**: Persistent authorization grants (keyed by `appId:method:item`) +- **txSimulations**: Cached simulation results with metadata + - Stores: `{ simulationResult, txRequest, metadata: { from, embeddedPaymentMethodFeePayer } }` +- **bridgedFeeJuice**: Fee payment token management + +### PXE Storage + +PXE maintains its own LMDB database at `~/keychain/pxe-${rollupAddress}`: +- Note synchronization data +- Contract artifacts and instances +- Encrypted notes per account + +## Common Issues & Fixes + +### 1. PXE Note Synchronization Error + +**Symptom**: `Cannot read properties of undefined (reading 'getValuesAsync')` after deleting authorizations or creating new accounts. + +**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. + +**Solution**: +1. Ensure only one PXE per session (fixed in `wallet-worker.ts`) +2. Defensive fix in PXE's `note_data_provider.js` for lazy Map initialization (see patch file) + +**Files**: +- `app/src/workers/wallet-worker.ts`: Single PXE per session +- `app/node_modules/@aztec/pxe/dest/storage/note_data_provider/note_data_provider.js`: Defensive fix +- `pxe-note-provider-map-sync-fix.patch`: Patch file for Aztec repo + +### 2. Batch Authorization Denial + +**Symptom**: Operations stuck in "requesting authorization" after user clicks "Deny All". + +**Root Cause**: Batch method in `external-wallet.ts` didn't catch authorization denial error. + +**Solution**: Wrap `requestAuthorization` in try-catch and mark items as ERROR on denial. + +**File**: `app/src/wallet/core/external-wallet.ts` + +### 3. Execution Trace Performance + +**Symptom**: Unreasonable delay when opening execution trace dialog. + +**Root Cause**: `getExecutionTrace()` was creating a new `DecodingCache` instance on every call. + +**Solution**: Use shared `this.decodingCache` from `BaseNativeWallet`. + +**File**: `app/src/wallet/core/internal-wallet.ts:196-229` + +### 4. Stats Not Showing After Authorization + +**Symptom**: Simulation stats visible during authorization but not when clicking stored interactions. + +**Root Cause**: `getExecutionTrace()` wasn't returning stats from stored simulations. + +**Solution**: Update return type and extract stats from both utility traces and tx simulations. + +**Files**: +- `app/src/wallet/core/internal-wallet.ts` +- `app/src/wallet/database/wallet-db.ts` + +### 5. Utility Trace Confusion + +**Symptom**: `getUtilityTrace()` returning data for tx simulations. + +**Root Cause**: Method didn't check if `utilityTrace` field actually existed in stored data. + +**Solution**: Add check `if (!parsed.utilityTrace) return undefined;` + +**File**: `app/src/wallet/database/wallet-db.ts` + +## Important Code Patterns + +### Fee Payer Information + +When a transaction includes `embeddedPaymentMethodFeePayer`, the app is providing the fee payment method: +- Extract from `opts.fee?.embeddedPaymentMethodFeePayer` or `executionPayload.feePayer` +- Store in metadata when saving simulations +- Display in authorization dialogs and execution traces with success alert + +### MulticallEntrypoint Detection + +When `from` is set to `AztecAddress.ZERO`, the request uses MulticallEntrypoint: +- Does not execute from any user account +- Display reassuring info alert in authorization dialogs + +### Simulation Title Generation + +Use `generateSimulationTitle()` from `simulation-utils.ts`: +- Includes contract names and function calls +- Handles fee payer information +- Provides readable titles for UI + +### Zod Schemas for IPC + +All data crossing IPC boundaries must have Zod schemas: +- `InternalWalletInterfaceSchema` in `wallet-internal-interface.ts` +- Serialization/deserialization handled automatically +- Update schemas when adding new fields to return types + +## UI Components + +### Authorization Dialogs + +- `AuthorizeSendTxContent.tsx`: Send transaction authorization +- `AuthorizeSimulateTxContent.tsx`: Simulation authorization +- Display fee payer and MulticallEntrypoint alerts + +### Execution Trace Display + +- `ExecutionTraceDisplay.tsx`: Shows decoded execution with timing stats +- `ExecutionTraceDialog.tsx`: Modal dialog for viewing stored interactions +- `SimulationStatsDisplay`: Collapsible accordion with function hierarchy and RPC calls + +### Features + +- Collapsible simulation timing stats (default collapsed) +- Function call hierarchy with tree visualization +- RPC calls table sorted by total time +- Call authorization display +- Automatic contract/function decoding via `DecodingCache` + +## Development Setup + +### Requirements + +- Node.js v22 +- yarn +- Running Aztec node (local or remote) + +### Running + +```bash +# Wallet +cd app +yarn install +yarn start + +# Extension +cd extension +yarn install +yarn dev # Launches browser with extension +``` + +### Port Configuration + +- **WebSocket**: Port 8765 (configurable in `ws-worker.ts`) +- **Node RPC**: Default port 8080 (configured in `networks.ts`) + +## Testing Scenarios + +### Creating and Revoking Authorizations + +**Critical Test**: This scenario previously triggered the PXE synchronization bug: + +1. Create account "ECDSAR1 0" +2. Complete app onboarding (simulations, transactions, contract registrations) +3. Revoke ALL authorizations for the app +4. Create new account "ECDSAR1 1" +5. Attempt app onboarding again + +**Expected**: Should work without errors +**Previously**: Failed with `getValuesAsync` undefined error + +### Batch Operations + +Test "Deny All" in batch authorization dialogs: +- Should mark all pending operations as ERROR +- Should not leave operations stuck in "requesting authorization" + +### Execution Trace Performance + +- Opening execution trace dialog should be instant +- No delays or multiple decoding cache instantiations + +## Code Quality Guidelines + +### Error Handling + +- Always wrap authorization requests in try-catch +- Contextualize errors with relevant data (use `contextualizeError`) +- Log meaningful messages at appropriate levels + +### Resource Management + +- Reuse shared resources (PXE, node client, caches) +- Never create duplicate PXE instances for the same session +- Clean up event listeners when destroying wallet instances + +### Logging + +- Use `createProxyLogger` for worker threads +- Include context in logger names (e.g., `wallet:external:${appId}`) +- Avoid excessive logging in production code +- Debug logs helped identify the PXE multi-instance issue + +### TypeScript + +- Use strict typing for operation display data +- Define explicit types for IPC message parameters +- Avoid `any` except for Zod schemas (with `@ts-ignore`) + +## Known Limitations + +1. **WebSocket Communication**: Should use Native Messaging API instead +2. **Port Management**: Port 8765 must be available +3. **Single Network**: Currently only supports one rollup version at a time +4. **Authorization Persistence**: Limited to `appId:method:item` format + +## Future Improvements + +1. **Native Messaging**: Replace WebSocket with browser's native extension-to-app communication +2. **Multi-Network**: Support multiple networks simultaneously +3. **Account Derivation**: BIP-44 HD wallet support +4. **Hardware Wallets**: Ledger/Trezor integration +5. **Fee Estimation**: Better gas estimation and fee payment methods +6. **Transaction History**: Enhanced filtering and search +7. **Contact Book**: Address aliases and management + +## File Reference + +### Critical Files + +- `app/src/workers/wallet-worker.ts`: Session and PXE management +- `app/src/wallet/core/external-wallet.ts`: dApp request handling +- `app/src/wallet/core/internal-wallet.ts`: Internal operations +- `app/src/wallet/database/wallet-db.ts`: Persistent storage +- `app/src/wallet/operations/*.ts`: Operation implementations +- `app/src/ipc/wallet-internal-interface.ts`: IPC type definitions + +### UI Files + +- `app/src/ui/components/authorization/*`: Authorization dialogs +- `app/src/ui/components/dialogs/*`: Modals and dialogs +- `app/src/ui/components/shared/*`: Reusable components +- `app/src/ui/components/sections/*`: Main UI sections + +### Configuration + +- `app/src/config/networks.ts`: Network definitions +- `app/package.json`: Dependencies and scripts +- `README.md`: Setup instructions + +## Debugging Tips + +### Check PXE Instance Count + +Look for log messages: +``` +[PXE-INIT] Creating NEW session with shared PXE instance for sessionId=... +[PXE-INIT] Reusing existing shared PXE instance for sessionId=... +``` + +Should only see one "Creating NEW" per network session. + +### Authorization Flow + +Check logs for: +``` +Received external message: ... +Received internal message: ... +``` + +External = from dApp, Internal = from wallet UI + +### Note Synchronization + +If errors occur, check: +- `~/keychain/aztec-keychain-debug.log` for full logs +- Whether multiple PXE instances are being created +- LMDB data directory: `~/keychain/pxe-${rollupAddress}` + +### Performance Issues + +- Check if `DecodingCache` is being reused (should be singleton per wallet) +- Monitor database query times +- Look for unnecessary re-renders in React components + +## Version Information + +- **Node.js**: v22 +- **Electron**: (check `app/package.json`) +- **Aztec SDK**: (check `app/package.json` for `@aztec/*` packages) +- **React**: (check `app/package.json`) + +## Related Repositories + +- **Extension**: `../extension` (sibling directory) +- **Aztec Packages**: Official Aztec monorepo (for PXE patches) + +## Patch Files + +- `pxe-note-provider-map-sync-fix.patch`: Fix for PXE note synchronization issue + - Apply to: `yarn-project/pxe/src/storage/note_data_provider/note_data_provider.ts` + - Command: `git apply /path/to/patch` or `git am /path/to/patch` diff --git a/WALLET_SDK_INTEGRATION_PLAN.md b/WALLET_SDK_INTEGRATION_PLAN.md new file mode 100644 index 0000000..6f78823 --- /dev/null +++ b/WALLET_SDK_INTEGRATION_PLAN.md @@ -0,0 +1,647 @@ +# Wallet SDK Integration Plan for Demo Wallet + +## Overview + +This document outlines the plan to integrate the demo-wallet with the `@aztec/wallet-sdk` extension provider protocol. The wallet extension will respond to discovery requests from dApps that include specific chain information, only responding if it supports that network based on the existing `app/src/config/networks.ts` whitelist. + +## Quick Implementation Summary + +**Files to Modify:** +1. `extension/entrypoints/content.ts` - Add discovery message handling +2. `extension/entrypoints/background.ts` - Add network support checking with caching +3. `app/src/workers/ws-worker.ts` - Forward network check messages (minimal changes) +4. `app/src/workers/wallet-worker.ts` - Add network support validation using `getNetworkByChainId` + +**Network Whitelist:** +- Uses existing `app/src/config/networks.ts` with `NETWORKS` array +- Current networks: Localhost (31337) and Devnet (11155111) +- Network validation via `getNetworkByChainId(chainId, version)` helper + +**Discovery Flow:** +1. dApp sends discovery message with chainInfo (chainId, version) +2. Content script asks background script to validate network +3. Background script queries wallet worker via WebSocket +4. Wallet worker checks chainInfo against networks whitelist +5. Extension responds ONLY if network is supported + +## Architecture: Request-Based Discovery + +The SDK uses a **request-based** approach: + +1. **dApp requests wallets FOR a specific chain/version** via `WalletManager.getAvailableWallets({ chainInfo })` +2. **SDK broadcasts discovery message WITH chainInfo** to all extensions +3. **Extensions respond ONLY if they support that specific chain/version** +4. **dApp receives only compatible wallets** + +This means: +- Extensions don't broadcast what they support +- Extensions receive chainInfo in discovery message +- Extensions check if they can handle that network before responding +- No need to store or configure supported networks - it's determined dynamically + +## Current Architecture + +### Message Flow +``` +dApp (WalletManager) + ↓ window.postMessage (with chainInfo) +Content Script (content.ts) + [Checks if wallet supports chainInfo] + ↓ window.postMessage (response) +dApp (receives compatible wallet) + +Later, when calling wallet methods: +dApp (ExtensionWallet) + ↓ window.postMessage (wallet method with chainInfo) +Content Script (content.ts) + ↓ browser.runtime.sendMessage +Background Script (background.ts) + ↓ WebSocket (port 8765) +WS Worker (ws-worker.ts) + ↓ MessagePort +Wallet Worker (wallet-worker.ts) + ↓ PXE Operations +``` + +### Key Architectural Constraints +- **Single PXE Per Session**: Each network session (chainId-version) has ONE shared PXE instance used by all apps +- **Chain Info in Messages**: Chain information is embedded in every wallet method call for session routing +- **Discovery is Validation**: Extension checks if it can support the requested chain during discovery + +## Message Protocols + +### 1. Discovery Protocol + +**Discovery Request** (from dApp via SDK): +```typescript +{ + type: 'aztec-wallet-discovery', + requestId: string, + chainInfo: { + chainId: Fr, // e.g., Fr(31337) for local devnet + version: Fr // e.g., Fr(1) for protocol version 1 + } +} +``` + +**Discovery Response** (from wallet extension - ONLY if supported): +```typescript +{ + type: 'aztec-wallet-discovery-response', + requestId: string, + extensionInfo: { + id: string, // Unique extension ID + name: string, // Display name + icon?: string, // Icon URL + version: string // Extension version + } +} +``` + +**Note**: Extensions should NOT respond if they don't support the requested chainInfo. + +### 2. Wallet Method Protocol (Existing) + +**Request**: +```typescript +{ + type: keyof FunctionsOf, // e.g., 'getAccounts', 'signTransaction' + args: unknown[], + messageId: string, + chainInfo: { + chainId: Fr, + version: Fr + }, + appId: string, + extensionId?: string +} +``` + +**Response**: +```typescript +{ + messageId: string, + result?: unknown, + error?: unknown, + extensionId: string +} +``` + +## Implementation Plan + +### Phase 1: Update Content Script for Discovery + +**File**: `extension/entrypoints/content.ts` + +The content script needs to: +1. Intercept discovery messages +2. Check if the wallet supports the requested chainInfo +3. Respond immediately if supported (no WebSocket roundtrip needed for discovery) +4. Forward wallet method calls to background script as before + +```typescript +export default defineContentScript({ + matches: ["*://*/*"], + main() { + // Handle discovery requests immediately + window.addEventListener("message", async (event) => { + if (event.source !== window) { + return; + } + + const { data } = event; + + // Handle discovery request + if (data.type === 'aztec-wallet-discovery') { + const { requestId, chainInfo } = data; + + // Check if wallet supports this chain/version + const isSupported = await browser.runtime.sendMessage({ + type: 'check_network_support', + chainInfo + }); + + if (isSupported) { + window.postMessage({ + type: 'aztec-wallet-discovery-response', + requestId, + extensionInfo: { + id: 'demo-wallet-extension', + name: 'Demo Wallet', + icon: browser.runtime.getURL('icon/128.png'), + version: browser.runtime.getManifest().version + } + }, '*'); + } + // If not supported, don't respond at all + return; + } + + // Handle wallet method calls (existing logic) + if (data.result || data.error) { + return; + } + + const { data: content } = event; + browser.runtime.sendMessage({ origin: "injected", content }); + }); + + // Existing listener for responses from background + browser.runtime.onMessage.addListener((event: any) => { + const { content, origin } = event; + if (origin !== "background") { + return; + } + window.postMessage(content, '*'); + }); + }, +}); +``` + +### Phase 2: Update Background Script for Network Check + +**File**: `extension/entrypoints/background.ts` + +The background script needs to: +1. Handle `check_network_support` messages from content script +2. Query the wallet worker via WebSocket to check if network is supported +3. Cache the result temporarily for performance + +```typescript +export default defineBackground(() => { + let webSocket: WebSocket | null = null; + let networkCheckCache = new Map>(); + + // Handle messages from content script + browser.runtime.onMessage.addListener(async (event: any, sender, sendResponse) => { + const { content, origin, type, chainInfo } = event; + + // Handle network support check from content script + if (type === 'check_network_support') { + const cacheKey = `${chainInfo.chainId.toString()}-${chainInfo.version.toString()}`; + + // Return cached result if available and recent + if (networkCheckCache.has(cacheKey)) { + return networkCheckCache.get(cacheKey); + } + + // Check with wallet worker + const checkPromise = new Promise((resolve) => { + if (!webSocket || webSocket.readyState !== WebSocket.OPEN) { + resolve(false); + return; + } + + const messageId = crypto.randomUUID(); + const timeout = setTimeout(() => { + resolve(false); + pendingNetworkChecks.delete(messageId); + }, 500); // 500ms timeout for network check + + pendingNetworkChecks.set(messageId, { resolve, timeout }); + + webSocket.send(JSON.stringify({ + type: '__check_network_support', + messageId, + chainInfo + })); + }); + + // Cache for 5 seconds + networkCheckCache.set(cacheKey, checkPromise); + setTimeout(() => networkCheckCache.delete(cacheKey), 5000); + + return checkPromise; + } + + // Handle wallet method calls + if (webSocket && origin === "injected") { + webSocket.send(content); + } + }); + + const pendingNetworkChecks = new Map void; timeout: NodeJS.Timeout }>(); + + function connect() { + return new Promise((resolve, reject) => { + webSocket = new WebSocket("ws://localhost:8765"); + + webSocket.onopen = () => { + console.log("websocket open"); + keepAlive(); + resolve(true); + }; + + webSocket.onmessage = async (event) => { + const data = JSON.parse(event.data); + + // Handle network support check response + if (data.type === '__network_support_response') { + const pending = pendingNetworkChecks.get(data.messageId); + if (pending) { + clearTimeout(pending.timeout); + pending.resolve(data.result === true); + pendingNetworkChecks.delete(data.messageId); + } + return; + } + + // Forward wallet method responses to content script + const [tab] = await browser.tabs.query({ + active: true, + currentWindow: true, + }); + if (!tab?.id) { + console.error("No active tab found"); + return; + } + browser.tabs.sendMessage(tab.id, { + origin: "background", + content: data, + }); + }; + + webSocket.onclose = (event) => { + console.log("websocket connection closed"); + webSocket = null; + networkCheckCache.clear(); + pendingNetworkChecks.forEach(({ timeout }) => clearTimeout(timeout)); + pendingNetworkChecks.clear(); + connect(); + }; + }); + } + + // ... rest of existing code (keepAlive, etc.) +}); +``` + +### Phase 3: Update WS Worker to Forward Network Checks + +**File**: `app/src/workers/ws-worker.ts` + +The WS worker simply forwards the network check message: + +```typescript +async function main() { + const app = express(); + app.use(cors()); + app.use(json()); + + const server = createServer(app); + const wss = new WebSocketServer({ server }); + + let externalPort: MessagePortMain; + + const handleWalletEvent = (event: any) => { + const { origin, content } = event.data; + if (origin !== "wallet") { + return; + } + wss.clients.forEach((client) => client.send(content)); + }; + + wss.on("connection", (ws) => { + ws.on("message", (data) => { + if (data.toString() === "keepalive") { + return; + } + + // Forward all messages (including network checks) to wallet worker + externalPort.postMessage({ + origin: "websocket", + content: data.toString("utf-8"), + }); + }); + }); + + await server.listen(8765); +} +``` + +### Phase 4: Update Wallet Worker to Handle Network Checks + +**File**: `app/src/workers/wallet-worker.ts` + +Add handler for the special `__check_network_support` message type using the existing networks configuration: + +```typescript +import { getNetworkByChainId } from '../config/networks.js'; + +externalPort.on("message", async (event) => { + const { origin, content } = event.data; + if (origin !== "websocket") { + return; + } + + let messageContent = JSON.parse(content); + const { type, messageId, args, appId, chainInfo } = messageContent; + + // Handle network support check + if (type === '__check_network_support') { + try { + // Parse the chain info + const parsedChainInfo = ChainInfoSchema.parse(chainInfo); + + // Check against the networks whitelist + const chainIdNum = parsedChainInfo.chainId.toNumber(); + const versionNum = parsedChainInfo.version.toNumber(); + + const network = getNetworkByChainId(chainIdNum, versionNum); + const isSupported = network !== undefined; + + externalPort.postMessage({ + origin: "wallet", + content: JSON.stringify({ + type: '__network_support_response', + messageId, + result: isSupported + }) + }); + } catch (error) { + // If we can't parse the chain info, we don't support it + externalPort.postMessage({ + origin: "wallet", + content: JSON.stringify({ + type: '__network_support_response', + messageId, + result: false + }) + }); + } + return; + } + + // Existing wallet method handling + const parsedChainInfo = ChainInfoSchema.parse(chainInfo); + const wallets = await init(parsedChainInfo, appId, internalPort, logPort); + + handleEvent(externalPort, wallets.external, WalletSchema, type, messageId, args, userLog); +}); +``` + +**Key Implementation Details**: +- Import `getNetworkByChainId` from existing networks configuration +- Convert `Fr` values to numbers using `.toNumber()` +- Use existing helper to check if network is in whitelist +- Return `true` only if network is found in `NETWORKS` array +- The existing `networks.ts` handles version auto-detection (version 0) + +## Testing Plan + +### 1. Discovery Testing + +**Test**: Extension responds only to whitelisted networks +```typescript +// In browser console on any page + +// Test with Localhost network (chainId: 31337, version: 0) +window.postMessage({ + type: 'aztec-wallet-discovery', + requestId: 'test-localhost', + chainInfo: { + chainId: { value: 31337n }, // Fr representation + version: { value: 0n } + } +}, '*'); + +// Listen for response +window.addEventListener('message', (event) => { + if (event.data.type === 'aztec-wallet-discovery-response') { + console.log('Discovery response:', event.data); + } +}); + +// Test with Devnet network (chainId: 11155111, version: 1667575857) +window.postMessage({ + type: 'aztec-wallet-discovery', + requestId: 'test-devnet', + chainInfo: { + chainId: { value: 11155111n }, + version: { value: 1667575857n } + } +}, '*'); + +// Test with unsupported network (should NOT respond) +window.postMessage({ + type: 'aztec-wallet-discovery', + requestId: 'test-unsupported', + chainInfo: { + chainId: { value: 99999n }, + version: { value: 1n } + } +}, '*'); +``` + +**Expected**: +- Should receive response for Localhost (31337) within 500ms +- Should receive response for Devnet (11155111) within 500ms +- Should NOT receive response for unsupported network (99999) + +### 2. SDK Integration Testing + +**Test**: WalletManager discovers extension for supported network +```typescript +// Using the Wallet SDK in playground +const manager = WalletManager.configure({ + extensions: { enabled: true } +}); + +const wallets = await manager.getAvailableWallets({ + chainInfo: { + chainId: new Fr(31337), + version: new Fr(1) + }, + timeout: 1000 +}); + +console.log('Discovered wallets:', wallets); + +// Test with unsupported network +const noWallets = await manager.getAvailableWallets({ + chainInfo: { + chainId: new Fr(99999), + version: new Fr(99) + }, + timeout: 1000 +}); + +console.log('Unsupported network wallets:', noWallets); +``` + +**Expected**: +- Should discover demo wallet for supported network +- Should NOT discover demo wallet for unsupported network + +### 3. Wallet Method Testing + +**Test**: Wallet methods work after discovery +```typescript +// Connect and test getAccounts +const wallet = await wallets[0].connect('test-app'); +const accounts = await wallet.getAccounts(); +console.log('Accounts:', accounts); + +// Test other wallet methods +const address = accounts[0]; +const balance = await wallet.getBalance(address); +console.log('Balance:', balance); +``` + +**Expected**: All wallet methods should work correctly with chainInfo baked in. + +### 4. Multi-Network Testing + +**Test**: Wallet correctly handles multiple network sessions +```typescript +// Create two wallet instances with different chain info +const manager = WalletManager.configure({ extensions: { enabled: true } }); + +const wallets1 = await manager.getAvailableWallets({ + chainInfo: { chainId: new Fr(31337), version: new Fr(1) }, + timeout: 1000 +}); + +const wallets2 = await manager.getAvailableWallets({ + chainInfo: { chainId: new Fr(12345), version: new Fr(2) }, + timeout: 1000 +}); + +const wallet1 = await wallets1[0].connect('app1'); +const wallet2 = await wallets2[0].connect('app2'); + +const accounts1 = await wallet1.getAccounts(); +const accounts2 = await wallet2.getAccounts(); + +console.log('Wallet 1 accounts:', accounts1); +console.log('Wallet 2 accounts:', accounts2); +``` + +**Expected**: Each wallet maintains its own session without LMDB conflicts. + +## Migration Notes + +### After Nightly Build + +1. **Copy Extension Implementation**: Copy the extension implementation from `demo-wallet/extension/` to `aztec-packages/` as a reference implementation. + +2. **Update Documentation**: Add the demo wallet extension as an example in the wallet-sdk documentation. + +3. **Clean Up Demo Wallet**: Remove the temporary implementation and switch to using the released wallet-sdk package. + +### Breaking Changes + +None. The integration is purely additive: +- Wallet interface unchanged +- Existing wallet methods continue to work +- Discovery is a new feature at the extension level + +### Dependencies + +The extension will need types from `@aztec/wallet-sdk`: +- Use type-only imports to avoid runtime dependencies +- Or include minimal type definitions in extension code + +```typescript +// Option 1: Type-only import (no runtime dependency) +import type { ExtensionDiscoveryMessage } from '@aztec/wallet-sdk/providers/extension'; + +// Option 2: Local type definitions +type ExtensionDiscoveryMessage = { + type: 'aztec-wallet-discovery'; + requestId: string; + chainInfo: { chainId: Fr; version: Fr }; +}; +``` + +## Implementation Strategy + +Since npm linking is difficult: + +1. **Implement in demo-wallet first** (this repo) +2. **Test thoroughly** with the playground +3. **Wait for nightly build** with the updated wallet-sdk +4. **Copy to aztec-packages** as reference implementation +5. **Clean up demo-wallet** after migration + +## Network Support Strategy + +We will use **Option 1: Explicit Whitelist** using the existing `app/src/config/networks.ts` configuration file. + +The existing networks configuration at `app/src/config/networks.ts` defines: +- `NETWORKS`: Array of `NetworkConfig` with `chainId` and `version` fields +- `getNetworkByChainId(chainId, version)`: Helper to check if a network is supported + +The extension will: +1. Import the networks configuration +2. Check incoming discovery requests against the whitelist +3. Only respond if the chainId/version pair is in `NETWORKS` + +**Benefits**: +- Clear, secure, explicit control over supported networks +- Centralized configuration already used by the wallet +- Easy to add/remove networks +- No risk of responding to unsupported networks + +## Security Considerations + +1. **Origin Validation**: Content script validates `event.source === window` +2. **Extension ID**: Background script includes extension ID in all responses +3. **Timeout Handling**: Network checks timeout after 500ms to prevent hanging +4. **Error Handling**: All errors are caught and logged without exposing sensitive information +5. **No Response on Failure**: If network is unsupported, extension doesn't respond (fails silently) + +## Open Questions + +1. Should the wallet worker attempt to validate network support, or always return true? +2. Should network check results be cached, and for how long? +3. Should there be a configuration file for supported networks, or should it be dynamic? + +**Recommendation**: Start with dynamic support (always return true), then add explicit network configuration if needed based on feedback. + +## References + +- Wallet SDK Extension Provider: `yarn-project/wallet-sdk/src/providers/extension/` +- Demo Wallet Architecture: `~/repos/demo-wallet/CLAUDE.md` +- Current Extension: `~/repos/demo-wallet/extension/entrypoints/` +- Wallet Worker: `~/repos/demo-wallet/app/src/workers/wallet-worker.ts` diff --git a/app/package.json b/app/package.json index f100be2..186d490 100644 --- a/app/package.json +++ b/app/package.json @@ -51,16 +51,16 @@ "vite-plugin-static-copy": "^3.1.2" }, "dependencies": { - "@aztec/accounts": "v3.0.0-devnet.2", - "@aztec/aztec.js": "v3.0.0-devnet.2", - "@aztec/bb.js": "v3.0.0-devnet.2", - "@aztec/constants": "v3.0.0-devnet.2", - "@aztec/entrypoints": "v3.0.0-devnet.2", - "@aztec/kv-store": "v3.0.0-devnet.2", - "@aztec/noir-contracts.js": "v3.0.0-devnet.2", - "@aztec/protocol-contracts": "v3.0.0-devnet.2", - "@aztec/foundation": "v3.0.0-devnet.2", - "@aztec/pxe": "v3.0.0-devnet.2", + "@aztec/accounts": "v3.0.0-nightly.20251120", + "@aztec/aztec.js": "v3.0.0-nightly.20251120", + "@aztec/bb.js": "v3.0.0-nightly.20251120", + "@aztec/constants": "v3.0.0-nightly.20251120", + "@aztec/entrypoints": "v3.0.0-nightly.20251120", + "@aztec/kv-store": "v3.0.0-nightly.20251120", + "@aztec/noir-contracts.js": "v3.0.0-nightly.20251120", + "@aztec/protocol-contracts": "v3.0.0-nightly.20251120", + "@aztec/foundation": "v3.0.0-nightly.20251120", + "@aztec/pxe": "v3.0.0-nightly.20251120", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@fontsource/roboto": "^5.1.1", diff --git a/app/src/ipc/preload.ts b/app/src/ipc/preload.ts index 35054d4..b6f4f07 100644 --- a/app/src/ipc/preload.ts +++ b/app/src/ipc/preload.ts @@ -41,6 +41,9 @@ contextBridge.exposeInMainWorld("walletAPI", { updateAccountAuthorization(stringifiedArgs: string): Promise { return ipcRenderer.invoke("updateAccountAuthorization", stringifiedArgs); }, + updateAddressBookAuthorization(stringifiedArgs: string): Promise { + return ipcRenderer.invoke("updateAddressBookAuthorization", stringifiedArgs); + }, revokeAuthorization(stringifiedArgs: string): Promise { return ipcRenderer.invoke("revokeAuthorization", stringifiedArgs); }, diff --git a/app/src/ipc/wallet-internal-interface.ts b/app/src/ipc/wallet-internal-interface.ts index 0ad88c2..4a7f893 100644 --- a/app/src/ipc/wallet-internal-interface.ts +++ b/app/src/ipc/wallet-internal-interface.ts @@ -1,6 +1,6 @@ import { Fr } from "@aztec/aztec.js/fields"; import { type Wallet, WalletSchema } from "@aztec/aztec.js/wallet"; -import { schemas } from "@aztec/stdlib/schemas"; +import { optional, schemas } from "@aztec/stdlib/schemas"; import { z } from "zod"; import { type ApiSchemaFor } from "@aztec/stdlib/schemas"; import { AccountTypes, type AccountType } from "../wallet/database/wallet-db"; @@ -88,9 +88,15 @@ export type InternalWalletInterface = Omit & { ): Promise; getAccounts(): Promise; // Override with enriched type getInteractions(): Promise[]>; - getExecutionTrace( - interactionId: string - ): Promise<{ trace?: DecodedExecutionTrace; stats?: any; from?: string; embeddedPaymentMethodFeePayer?: string } | undefined>; + getExecutionTrace(interactionId: string): Promise< + | { + trace?: DecodedExecutionTrace; + stats?: any; + from?: string; + embeddedPaymentMethodFeePayer?: string; + } + | undefined + >; resolveAuthorization(response: AuthorizationResponse): void; onWalletUpdate(callback: OnWalletUpdateListener): void; onAuthorizationRequest(callback: OnAuthorizationRequestListener): void; @@ -98,6 +104,7 @@ export type InternalWalletInterface = Omit & { listAuthorizedApps(): Promise; getAppAuthorizations(appId: string): Promise<{ accounts: { alias: string; item: string }[]; + contacts: { alias: string; item: string }[]; simulations: Array<{ type: "simulateTx" | "simulateUtility"; payloadHash: string; @@ -110,6 +117,10 @@ export type InternalWalletInterface = Omit & { appId: string, accounts: { alias: string; item: string }[] ): Promise; + updateAddressBookAuthorization( + appId: string, + contacts: { alias: string; item: string }[] + ): Promise; revokeAuthorization(key: string): Promise; revokeAppAuthorizations(appId: string): Promise; }; @@ -148,12 +159,16 @@ export const InternalWalletInterfaceSchema: ApiSchemaFor { "listAuthorizedApps", "getAppAuthorizations", "updateAccountAuthorization", + "updateAddressBookAuthorization", "revokeAuthorization", "revokeAppAuthorizations", ]; diff --git a/app/src/ui/components/authorization/AuthorizeAddressBookContent.tsx b/app/src/ui/components/authorization/AuthorizeAddressBookContent.tsx new file mode 100644 index 0000000..ce23797 --- /dev/null +++ b/app/src/ui/components/authorization/AuthorizeAddressBookContent.tsx @@ -0,0 +1,161 @@ +import { useContext, useEffect, useState } from "react"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import Checkbox from "@mui/material/Checkbox"; +import TextField from "@mui/material/TextField"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import { WalletContext } from "../../renderer"; +import type { Aliased } from "@aztec/aztec.js/wallet"; +import type { AztecAddress } from "@aztec/aztec.js/addresses"; +import type { AuthorizationItem } from "../../../wallet/types/authorization"; + +type SelectedContact = { + address: string; + alias: string; + originalAlias: string; + selected: boolean; +}; + +interface AuthorizeAddressBookContentProps { + request: AuthorizationItem; + onContactsChange?: (contacts: any[]) => void; + showAppId?: boolean; +} + +export function AuthorizeAddressBookContent({ + request, + onContactsChange, + showAppId = true, +}: AuthorizeAddressBookContentProps) { + const [contacts, setContacts] = useState([]); + const { walletAPI } = useContext(WalletContext); + + useEffect(() => { + const loadContacts = async () => { + const allContacts: Aliased[] = await walletAPI.getAddressBook(); + setContacts( + allContacts.map((contact) => ({ + address: contact.item.toString(), + alias: contact.alias, + originalAlias: contact.alias, + selected: false, + })) + ); + }; + loadContacts(); + }, []); + + // Notify parent when contacts change + useEffect(() => { + if (onContactsChange) { + const selectedContacts = contacts + .filter((contact) => contact.selected) + .map((contact) => ({ + item: contact.address, + alias: contact.alias, + })); + onContactsChange(selectedContacts); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [contacts]); // Only depend on contacts, not onContactsChange to avoid infinite loop + + const handleToggleContact = (index: number) => { + setContacts((prev) => + prev.map((contact, i) => + i === index ? { ...contact, selected: !contact.selected } : contact + ) + ); + }; + + const handleAliasChange = (index: number, newAlias: string) => { + setContacts((prev) => + prev.map((contact, i) => (i === index ? { ...contact, alias: newAlias } : contact)) + ); + }; + + return ( + <> + {showAppId && ( + <> + + App {request.appId} is requesting access to your + address book. + + + Select which contacts to share. You can also customize the aliases + that will be visible to the app. + + + )} + + + {contacts.map((contact, index) => ( + + handleToggleContact(index)} + /> + } + label="" + sx={{ m: 0 }} + /> + + + {contact.address} + + {contact.selected ? ( + handleAliasChange(index, e.target.value)} + label="Alias (visible to app)" + fullWidth + sx={{ mt: 1 }} + /> + ) : ( + + {contact.originalAlias} + + )} + + + ))} + + + {contacts.length === 0 && ( + + No contacts available. Please add contacts to your address book first. + + )} + + + This authorization will be remembered. You can revoke it later from the + Authorized Apps settings. + + + ); +} diff --git a/app/src/ui/components/dialogs/AuthorizationDialog.tsx b/app/src/ui/components/dialogs/AuthorizationDialog.tsx index a546c50..75b89f5 100644 --- a/app/src/ui/components/dialogs/AuthorizationDialog.tsx +++ b/app/src/ui/components/dialogs/AuthorizationDialog.tsx @@ -24,6 +24,7 @@ import { AuthorizeSimulateTxContent } from "../authorization/AuthorizeSimulateTx import { AuthorizeContractContent } from "../authorization/AuthorizeContractContent"; import { AuthorizeSenderContent } from "../authorization/AuthorizeSenderContent"; import { AuthorizeAccountsContent } from "../authorization/AuthorizeAccountsContent"; +import { AuthorizeAddressBookContent } from "../authorization/AuthorizeAddressBookContent"; import { WalletContext } from "../../renderer"; import { AztecAddress } from "@aztec/aztec.js/addresses"; @@ -54,6 +55,8 @@ function formatMethodName(method: string): string { return "Register Sender"; case "getAccounts": return "Get Accounts"; + case "getAddressBook": + return "Get Address Book"; default: return method; } @@ -144,6 +147,7 @@ export function AuthorizationDialog({ approved: true, persistent: item.method === "getAccounts" || + item.method === "getAddressBook" || item.method === "simulateTx" || item.method === "simulateUtility", }, @@ -161,6 +165,7 @@ export function AuthorizationDialog({ approved: true, persistent: item.method === "getAccounts" || + item.method === "getAddressBook" || item.method === "simulateTx" || item.method === "simulateUtility", }, @@ -444,6 +449,16 @@ export function AuthorizationDialog({ showAppId={false} /> )} + + {item.method === "getAddressBook" && ( + { + handleItemDataChange(item.id, { contacts }); + }} + showAppId={false} + /> + )} diff --git a/app/src/ui/components/dialogs/EditAccountAuthorizationDialog.tsx b/app/src/ui/components/dialogs/EditAccountAuthorizationDialog.tsx index aed24de..3e14aac 100644 --- a/app/src/ui/components/dialogs/EditAccountAuthorizationDialog.tsx +++ b/app/src/ui/components/dialogs/EditAccountAuthorizationDialog.tsx @@ -212,7 +212,7 @@ export function EditAccountAuthorizationDialog({ diff --git a/app/src/ui/components/dialogs/EditAddressBookAuthorizationDialog.tsx b/app/src/ui/components/dialogs/EditAddressBookAuthorizationDialog.tsx new file mode 100644 index 0000000..4629450 --- /dev/null +++ b/app/src/ui/components/dialogs/EditAddressBookAuthorizationDialog.tsx @@ -0,0 +1,222 @@ +import { useContext, useEffect, useState } from "react"; +import { + Dialog, + DialogTitle, + DialogContent, + DialogActions, + Button, + Box, + Typography, + Checkbox, + FormControlLabel, + TextField, + List, + ListItem, + Alert, +} from "@mui/material"; +import { WalletContext } from "../../renderer"; + +interface EditAddressBookAuthorizationDialogProps { + open: boolean; + appId: string; + currentContacts: Array<{ alias: string; item: string }>; + onClose: () => void; + onSave: () => Promise; +} + +interface ContactWithSelection { + address: string; + originalAlias: string; + displayAlias: string; + selected: boolean; +} + +export function EditAddressBookAuthorizationDialog({ + open, + appId, + currentContacts, + onClose, + onSave, +}: EditAddressBookAuthorizationDialogProps) { + const { walletAPI } = useContext(WalletContext); + const [allContacts, setAllContacts] = useState([]); + const [saving, setSaving] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (open) { + loadContacts(); + } + }, [open, currentContacts]); + + const loadContacts = async () => { + try { + setError(null); + const contacts = await walletAPI.getAddressBook(); + const currentAddresses = new Set(currentContacts.map((c) => c.item)); + + const contactsWithSelection = contacts.map((contact) => { + const isSelected = currentAddresses.has(contact.item.toString()); + const currentContact = currentContacts.find( + (cc) => cc.item === contact.item.toString() + ); + + return { + address: contact.item.toString(), + originalAlias: contact.alias, + displayAlias: isSelected + ? currentContact?.alias || contact.alias + : contact.alias, + selected: isSelected, + }; + }); + + setAllContacts(contactsWithSelection); + } catch (err) { + console.error("Failed to load address book:", err); + setError(err instanceof Error ? err.message : String(err)); + } + }; + + const handleToggleContact = (address: string) => { + setAllContacts((prev) => + prev.map((contact) => + contact.address === address ? { ...contact, selected: !contact.selected } : contact + ) + ); + }; + + const handleAliasChange = (address: string, newAlias: string) => { + setAllContacts((prev) => + prev.map((contact) => + contact.address === address ? { ...contact, displayAlias: newAlias } : contact + ) + ); + }; + + const handleSave = async () => { + try { + setSaving(true); + setError(null); + + const selectedContacts = allContacts + .filter((contact) => contact.selected) + .map((contact) => ({ + alias: contact.displayAlias, + item: contact.address, + })); + + await walletAPI.updateAddressBookAuthorization(appId, selectedContacts); + await onSave(); + onClose(); + } catch (err) { + console.error("Failed to update authorization:", err); + setError(err instanceof Error ? err.message : String(err)); + } finally { + setSaving(false); + } + }; + + const selectedCount = allContacts.filter((c) => c.selected).length; + + return ( + + Edit Address Book Authorization for {appId} + + + Select which contacts this app can access and customize the aliases + shown to the app. + + + {error && ( + + {error} + + )} + + + Selected Contacts: {selectedCount} + + + + {allContacts.map((contact) => ( + + handleToggleContact(contact.address)} + /> + } + label={ + + + {contact.originalAlias} + + + {contact.address.slice(0, 10)}... + {contact.address.slice(-8)} + + + } + /> + {contact.selected && ( + + handleAliasChange(contact.address, e.target.value) + } + sx={{ + mt: 1, + ml: 4, + '& .MuiInputBase-input': { + overflow: 'hidden', + textOverflow: 'ellipsis' + } + }} + helperText="The app will see this contact under this alias" + /> + )} + + ))} + + + {allContacts.length === 0 && ( + + No contacts available. Add contacts to your address book first. + + )} + + + + + + + ); +} diff --git a/app/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx b/app/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx index eec3f0f..0f8a37e 100644 --- a/app/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx +++ b/app/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx @@ -28,6 +28,7 @@ import { } from "@mui/icons-material"; import { WalletContext } from "../../../../renderer"; import { EditAccountAuthorizationDialog } from "../../../dialogs/EditAccountAuthorizationDialog"; +import { EditAddressBookAuthorizationDialog } from "../../../dialogs/EditAddressBookAuthorizationDialog"; import { ExecutionTraceDialog } from "../../../dialogs/ExecutionTraceDialog"; import { AztecAddress } from "@aztec/aztec.js/addresses"; @@ -39,6 +40,7 @@ interface AppAuthorizationCardProps { interface Authorizations { accounts: { alias: string; item: string }[]; + contacts: { alias: string; item: string }[]; simulations: Array<{ type: "simulateTx" | "simulateUtility"; payloadHash: string; @@ -56,15 +58,20 @@ export function AppAuthorizationCard({ const { walletAPI } = useContext(WalletContext); const [authorizations, setAuthorizations] = useState({ accounts: [], + contacts: [], simulations: [], otherMethods: [], }); const [loading, setLoading] = useState(true); const [editDialogOpen, setEditDialogOpen] = useState(false); + const [editContactsDialogOpen, setEditContactsDialogOpen] = useState(false); const [revoking, setRevoking] = useState(false); const [accountList, setAccountList] = useState< Array<{ alias: string; item: string }> >([]); + const [contactList, setContactList] = useState< + Array<{ alias: string; item: string }> + >([]); useEffect(() => { loadAuthorizations(); @@ -73,17 +80,23 @@ export function AppAuthorizationCard({ const loadAuthorizations = async () => { try { setLoading(true); - // Load both in parallel and wait for both to complete - const [auths, accounts] = await Promise.all([ + // Load all in parallel and wait for all to complete + const [auths, accounts, contacts] = await Promise.all([ walletAPI.getAppAuthorizations(appId), walletAPI.getAccounts(), + walletAPI.getAddressBook(), ]); setAuthorizations(auths); setAccountList(accounts); - console.log("[AppAuthorizationCard] Loaded both auths and accounts:", { - auths, - accounts, - }); + setContactList(contacts); + console.log( + "[AppAuthorizationCard] Loaded auths, accounts, and contacts:", + { + auths, + accounts, + contacts, + } + ); } catch (err) { console.error("Failed to load app authorizations:", err); } finally { @@ -137,7 +150,14 @@ export function AppAuthorizationCard({ await onUpdate(); }; + const handleEditContactsSave = async () => { + setEditContactsDialogOpen(false); + await loadAuthorizations(); + await onUpdate(); + }; + const accounts = authorizations.accounts || []; + const contacts = authorizations.contacts || []; const simulations = authorizations.simulations || []; const otherMethods = authorizations.otherMethods || []; @@ -186,15 +206,6 @@ export function AppAuthorizationCard({ {appId} - - setEditDialogOpen(true)} - disabled={loading} - > - - - }> - - - Authorized Accounts ({accounts.length}) - + + + + Authorized Accounts ({accounts.length}) + + + { + e.stopPropagation(); + setEditDialogOpen(true); + }} + sx={{ mr: 1 }} + > + + @@ -321,6 +351,138 @@ export function AppAuthorizationCard({ )} + + + }> + + + + + Authorized Contacts ({contacts.length}) + + + { + e.stopPropagation(); + setEditContactsDialogOpen(true); + }} + sx={{ mr: 1 }} + > + + + + + + {contacts.length === 0 ? ( + + No contacts shared with this app. Click the edit button to grant access. + + ) : ( + + {contacts.map( + (contact: { alias: string; item: string }) => { + const internalContact = contactList.find( + (c: { alias: string; item: AztecAddress }) => + c.item.equals( + AztecAddress.fromString(contact.item) + ) + ); + + return ( + + + + + {internalContact?.alias || + "Unknown Contact"} + + + + + {contact.item.slice(0, 10)}... + {contact.item.slice(-8)} + + + ); + } + )} + + )} + + + + {simulations.length > 0 && ( + setEditContactsDialogOpen(false)} + onSave={handleEditContactsSave} + /> + {executionTrace && ( > ): Promise { const maxFeesPerGas = - userFeeOptions?.gasSettings?.maxFeesPerGas ?? + gasSettings?.maxFeesPerGas ?? (await this.aztecNode.getCurrentBaseFees()).mul(1 + this.baseFeePadding); let walletFeePaymentMethod; let accountFeePaymentMethodOptions; // The transaction does not include a fee payment method, so we set a default - if (!userFeeOptions?.embeddedPaymentMethodFeePayer) { + if (!feePayer) { walletFeePaymentMethod = await prepareForFeePayment(this); accountFeePaymentMethodOptions = AccountFeePaymentMethodOptions.EXTERNAL; } else { // The transaction includes fee payment method, so we check if we are the fee payer for it // (this can only happen if the embedded payment method is FeeJuiceWithClaim) - accountFeePaymentMethodOptions = from.equals( - userFeeOptions.embeddedPaymentMethodFeePayer - ) + accountFeePaymentMethodOptions = from.equals(feePayer) ? AccountFeePaymentMethodOptions.FEE_JUICE_WITH_CLAIM : AccountFeePaymentMethodOptions.EXTERNAL; } - const gasSettings: GasSettings = GasSettings.default({ - ...userFeeOptions?.gasSettings, + const fullGasSettings: GasSettings = GasSettings.default({ + ...gasSettings, maxFeesPerGas, }); - this.log.debug(`Using L2 gas settings`, gasSettings); return { - gasSettings, + gasSettings: fullGasSettings, walletFeePaymentMethod, accountFeePaymentMethodOptions, }; diff --git a/app/src/wallet/core/external-wallet.ts b/app/src/wallet/core/external-wallet.ts index f86d6f2..3c94252 100644 --- a/app/src/wallet/core/external-wallet.ts +++ b/app/src/wallet/core/external-wallet.ts @@ -6,22 +6,18 @@ import { type BatchedMethod, type BatchableMethods, type BatchResults, - type ContractInstanceAndArtifact, } from "@aztec/aztec.js/wallet"; import { type AztecNode } from "@aztec/aztec.js/node"; import { type Logger } from "@aztec/aztec.js/log"; import type { AuthWitness } from "@aztec/stdlib/auth-witness"; -import { type ContractArtifact } from "@aztec/stdlib/abi"; -import type { - ContractInstanceWithAddress, - ContractInstantiationData, -} from "@aztec/stdlib/contract"; -import { ExecutionPayload } from "@aztec/entrypoints/payload"; +import { FunctionCall, type ContractArtifact } from "@aztec/stdlib/abi"; +import type { ContractInstanceWithAddress } from "@aztec/stdlib/contract"; import { Fr } from "@aztec/foundation/fields"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; import { type TxSimulationResult, type UtilitySimulationResult, + ExecutionPayload, TxHash, } from "@aztec/stdlib/tx"; import { type PXE } from "@aztec/pxe/server"; @@ -108,8 +104,8 @@ export class ExternalWallet extends BaseNativeWallet { this.decodingCache, this.interactionManager, this.authorizationManager, - this.getFeeOptionsForGasEstimation.bind(this), - this.getDefaultFeeOptions.bind(this), + this.completeFeeOptionsForEstimation.bind(this), + this.completeFeeOptions.bind(this), this.getFakeAccountDataFor.bind(this), this.cancellableTransactions, this.appId, @@ -133,7 +129,7 @@ export class ExternalWallet extends BaseNativeWallet { simulateTxOp, this.createAuthWit.bind(this), this.createTxExecutionRequestFromPayloadAndFee.bind(this), - this.getDefaultFeeOptions.bind(this), + this.completeFeeOptions.bind(this), this.contextualizeError.bind(this) ); } @@ -220,16 +216,12 @@ export class ExternalWallet extends BaseNativeWallet { * Uses the RegisterContractOperation for clean separation of concerns. */ override async registerContract( - instanceData: - | AztecAddress - | ContractInstanceWithAddress - | ContractInstantiationData - | ContractInstanceAndArtifact, + instance: ContractInstanceWithAddress, artifact?: ContractArtifact, secretKey?: Fr ): Promise { const op = this.createRegisterContractOperation(); - return await op.executeStandalone(instanceData, artifact, secretKey); + return await op.executeStandalone(instance, artifact, secretKey); } override async registerSender( @@ -287,7 +279,8 @@ export class ExternalWallet extends BaseNativeWallet { | ContractInstanceWithAddress | TxHash | AztecAddress - | UtilitySimulationResult; + | UtilitySimulationResult + | TxSimulationResult; interface BatchItem { operation: ExternalOperation; @@ -321,6 +314,9 @@ export class ExternalWallet extends BaseNativeWallet { case "simulateUtility": operation = this.createSimulateUtilityOperation(); break; + case "simulateTx": + operation = this.createSimulateTxOperation(); + break; case "sendTx": // Only create simulateTxOp when needed for sendTx operations const simulateTxOp = this.createSimulateTxOperation(); @@ -420,11 +416,23 @@ export class ExternalWallet extends BaseNativeWallet { } const itemId = Fr.random().toString(); + + // Flatten displayData for simulateTx to match standalone flow format + let params = item.displayData!; + if (item.originalName === "simulateTx" && (params as any).decoded) { + params = { + ...params, + callAuthorizations: (params as any).decoded.callAuthorizations, + executionTrace: (params as any).decoded.executionTrace, + }; + delete (params as any).decoded; // Remove nested structure + } + authItems.push({ id: itemId, appId: this.appId, method: item.originalName, - params: item.displayData!, + params, timestamp: Date.now(), persistence: item.persistence, }); @@ -530,13 +538,11 @@ export class ExternalWallet extends BaseNativeWallet { * Handles interaction tracking and user authorization. */ override async simulateUtility( - functionName: string, - args: unknown[], - to: AztecAddress, + call: FunctionCall, authwits?: AuthWitness[], - from?: AztecAddress + scopes?: AztecAddress[] ): Promise { const op = this.createSimulateUtilityOperation(); - return await op.executeStandalone(functionName, args, to, authwits, from); + return await op.executeStandalone(call, authwits, scopes); } } diff --git a/app/src/wallet/core/internal-wallet.ts b/app/src/wallet/core/internal-wallet.ts index 59ef312..ad4fc8e 100644 --- a/app/src/wallet/core/internal-wallet.ts +++ b/app/src/wallet/core/internal-wallet.ts @@ -11,9 +11,12 @@ import { WalletInteraction, type WalletInteractionType, } from "../types/wallet-interaction"; -import type { ExecutionPayload } from "@aztec/entrypoints/payload"; -import { TxHash, TxSimulationResult } from "@aztec/stdlib/tx"; +import { + type ExecutionPayload, + TxHash, + TxSimulationResult, +} from "@aztec/stdlib/tx"; import type { DecodedExecutionTrace } from "../decoding/tx-callstack-decoder"; import { TxDecodingService } from "../decoding/tx-decoding-service"; @@ -151,7 +154,11 @@ export class InternalWallet extends BaseNativeWallet { opts: SendOptions, interaction?: WalletInteraction ): Promise { - const fee = await this.getDefaultFeeOptions(opts.from, opts.fee); + const fee = await this.completeFeeOptions( + opts.from, + executionPayload.feePayer, + opts.fee?.gasSettings + ); const txRequest = await this.createTxExecutionRequestFromPayloadAndFee( executionPayload, opts.from, @@ -193,9 +200,15 @@ export class InternalWallet extends BaseNativeWallet { return this.db.listInteractions(); } - async getExecutionTrace( - interactionId: string - ): Promise<{ trace?: DecodedExecutionTrace; stats?: any; from?: string; embeddedPaymentMethodFeePayer?: string } | undefined> { + async getExecutionTrace(interactionId: string): Promise< + | { + trace?: DecodedExecutionTrace; + stats?: any; + from?: string; + embeddedPaymentMethodFeePayer?: string; + } + | undefined + > { // First check if it's a utility trace (simple trace) const utilityData = await this.db.getUtilityTrace(interactionId); if (utilityData) { @@ -224,7 +237,8 @@ export class InternalWallet extends BaseNativeWallet { trace: executionTrace, stats: parsedSimulationResult.stats, from: data.metadata?.from, - embeddedPaymentMethodFeePayer: data.metadata?.embeddedPaymentMethodFeePayer, + embeddedPaymentMethodFeePayer: + data.metadata?.embeddedPaymentMethodFeePayer, }; } @@ -235,6 +249,7 @@ export class InternalWallet extends BaseNativeWallet { async getAppAuthorizations(appId: string): Promise<{ accounts: { alias: string; item: string }[]; + contacts: { alias: string; item: string }[]; simulations: Array<{ type: "simulateTx" | "simulateUtility"; payloadHash: string; diff --git a/app/src/wallet/decoding/decoding-cache.ts b/app/src/wallet/decoding/decoding-cache.ts index 0acf05a..cf68a4b 100644 --- a/app/src/wallet/decoding/decoding-cache.ts +++ b/app/src/wallet/decoding/decoding-cache.ts @@ -2,12 +2,7 @@ import type { PXE } from "@aztec/pxe/server"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; import type { ContractArtifact } from "@aztec/stdlib/abi"; import type { WalletDB } from "../database/wallet-db"; -import type { - ContractInstanceWithAddress, - ContractInstantiationData, -} from "@aztec/stdlib/contract"; -import { getContractInstanceFromInstantiationParams } from "@aztec/aztec.js/contracts"; -import { type ContractInstanceAndArtifact } from "@aztec/aztec.js/wallet"; +import type { ContractInstanceWithAddress } from "@aztec/stdlib/contract"; interface ContractMetadata { contractInstance?: { @@ -110,44 +105,12 @@ export class DecodingCache { return shortAddress; } - /** - * Resolve contract address from various instanceData formats. - * Handles AztecAddress, ContractInstanceWithAddress, ContractInstantiationData, etc. - */ - async resolveContractAddress( - instanceData: - | AztecAddress - | ContractInstanceWithAddress - | ContractInstantiationData - | ContractInstanceAndArtifact, - artifact?: ContractArtifact - ): Promise { - if (instanceData instanceof AztecAddress) { - return instanceData; - } else if ("address" in instanceData) { - return instanceData.address; - } else if ("instance" in instanceData) { - return instanceData.instance.address; - } else { - // ContractInstantiationData - compute the address - const instance = await getContractInstanceFromInstantiationParams( - artifact!, - instanceData - ); - return instance.address; - } - } - /** * Resolve contract name from various sources. * Uses caching internally via getAddressAlias and getContractArtifact. */ async resolveContractName( - instanceData: - | AztecAddress - | ContractInstanceWithAddress - | ContractInstantiationData - | ContractInstanceAndArtifact, + instance: ContractInstanceWithAddress, artifact: ContractArtifact | undefined, address: AztecAddress ): Promise { @@ -157,10 +120,10 @@ export class DecodingCache { // Check if instanceData contains an artifact if ( !contractName && - typeof instanceData === "object" && - "artifact" in instanceData + typeof instance === "object" && + "artifact" in instance ) { - contractName = (instanceData as any).artifact?.name; + contractName = (instance as any).artifact?.name; } // If we still don't have a name, try to fetch using cached methods diff --git a/app/src/wallet/decoding/tx-callstack-decoder.ts b/app/src/wallet/decoding/tx-callstack-decoder.ts index 2117746..3280564 100644 --- a/app/src/wallet/decoding/tx-callstack-decoder.ts +++ b/app/src/wallet/decoding/tx-callstack-decoder.ts @@ -178,37 +178,18 @@ export class TxCallStackDecoder { functionAbi ); - const decoded = decodeFromAbi( - functionAbi.parameters.map((p) => p.type), - argsValues - ); - - // decodeFromAbi returns a single value if there's one param, or an array for multiple - const decodedArgs = Array.isArray(decoded) ? decoded : [decoded]; - - args = await Promise.all( - decodedArgs.map(async (value, i) => ({ - name: functionAbi.parameters[i]?.name || `arg_${i}`, - value: await this.formatAndResolveValue(value), - })) - ); + // Reuse the generic argument decoding helper + args = await this.decodeAndFormatArguments(functionAbi, argsValues); } catch (error) { // Silently fail - args will remain empty } } - // Decode return values + // Decode return values - reuse the generic return value decoding helper if (functionAbi.returnTypes.length > 0) { - const decodedReturns = decodeFromAbi( - functionAbi.returnTypes, + returnValues = await this.decodeAndFormatReturnValues( + functionAbi, call.returnValues - ) as AbiDecoded[]; - - returnValues = await Promise.all( - decodedReturns.map(async (value, i) => ({ - name: `return_${i}`, - value: await this.formatAndResolveValue(value), - })) ); } } @@ -366,22 +347,11 @@ export class TxCallStackDecoder { if (functionAbi.parameters.length > 0 && calldata.length > 1) { try { const argsData = calldata.slice(1); // Skip the selector - const decoded = decodeFromAbi( - functionAbi.parameters.map((p) => p.type), + // Reuse the generic argument decoding helper + args = await this.decodeAndFormatArguments( + functionAbi, argsData ); - - // decodeFromAbi returns a single value if there's one param, or an array for multiple - const decodedArgs = Array.isArray(decoded) - ? decoded - : [decoded]; - - args = await Promise.all( - decodedArgs.map(async (value, i) => ({ - name: functionAbi.parameters[i]?.name || `arg_${i}`, - value: await this.formatAndResolveValue(value), - })) - ); } catch (error) { // Silently fail - args will remain empty } @@ -457,22 +427,90 @@ export class TxCallStackDecoder { }; } + /** + * Generic helper to decode and format function arguments. + * Reused by both transaction decoding and utility function decoding. + * + * @param functionAbi - The function ABI containing parameter definitions + * @param args - Raw Fr[] arguments to decode + * @returns Array of formatted arguments with names and display values + */ + private async decodeAndFormatArguments( + functionAbi: FunctionAbi, + args: Fr[] + ): Promise> { + if (!functionAbi.parameters || functionAbi.parameters.length === 0) { + return []; + } + + // Decode the Fr[] args using the function's parameter types + const decoded = decodeFromAbi( + functionAbi.parameters.map((p) => p.type), + args + ); + + // decodeFromAbi returns a single value if there's one param, or an array for multiple + const decodedArgs = Array.isArray(decoded) ? decoded : [decoded]; + + // Format each decoded argument with address resolution + return await Promise.all( + decodedArgs.map(async (value, i) => ({ + name: functionAbi.parameters[i]?.name || `arg_${i}`, + value: await this.formatAndResolveValue(value), + })) + ); + } + + /** + * Generic helper to decode and format function return values. + * Reused by both transaction decoding and utility function decoding. + * + * @param functionAbi - The function ABI containing return type definitions + * @param returnValues - Raw Fr[] return values to decode + * @returns Array of formatted return values with names and display values + */ + private async decodeAndFormatReturnValues( + functionAbi: FunctionAbi, + returnValues: Fr[] + ): Promise> { + if (!functionAbi.returnTypes || functionAbi.returnTypes.length === 0) { + return []; + } + + // Decode the Fr[] return values using the function's return types + const decoded = decodeFromAbi( + functionAbi.returnTypes, + returnValues + ); + + // decodeFromAbi returns a single value if there's one return type, or an array for multiple + const decodedReturns = Array.isArray(decoded) ? decoded : [decoded]; + + return await Promise.all( + decodedReturns.map(async (value, i) => ({ + name: `return_${i}`, + value: await this.formatAndResolveValue(value), + })) + ); + } + /** * Format utility function arguments for display. - * Retrieves contract metadata and artifact, then formats args with address resolution. + * Takes raw Fr[] from FunctionCall.args, decodes using function's parameter types ABI, + * then formats with address resolution. + * + * This method reuses the generic decoding helpers that are also used by transaction decoding. */ async formatUtilityArguments( contractAddress: AztecAddress, functionName: string, - args: any[] + args: Fr[] ): Promise> { if (args.length === 0) { return []; } try { - console.log("[formatUtilityArguments] Raw args:", args); - // Retrieve contract metadata and artifact const metadata = await this.cache.getContractMetadata(contractAddress); if (!metadata.contractInstance) { @@ -491,48 +529,28 @@ export class TxCallStackDecoder { throw new Error(`Function ${functionName} not found in artifact`); } - console.log( - "[formatUtilityArguments] Function ABI parameters:", - functionAbi.parameters - ); - - // Args are already decoded values, just need to format and resolve addresses - const formatted = await Promise.all( - args.map(async (value, i) => { - const formattedValue = await this.formatAndResolveValue(value); - console.log( - `[formatUtilityArguments] Arg ${i}:`, - value, - "=>", - formattedValue - ); - return { - name: functionAbi.parameters[i]?.name || `arg_${i}`, - value: formattedValue, - }; - }) - ); - - console.log("[formatUtilityArguments] Formatted result:", formatted); - return formatted; + // Reuse the generic argument decoding helper (same logic as transaction decoding) + return await this.decodeAndFormatArguments(functionAbi, args); } catch (error) { - console.error("[formatUtilityArguments] Error formatting args:", error); - // If formatting fails, return raw args + // If formatting fails, return raw representation of Fr[] values return args.map((arg, i) => ({ name: `arg_${i}`, - value: JSON.stringify(arg), + value: arg.toString(), })); } } /** * Format utility function result for display with address resolution. - * Retrieves contract metadata and artifact, then formats result with address aliases. + * Takes raw Fr[] from UtilitySimulationResult, decodes using function's return type ABI, + * then formats with address aliases. + * + * This method reuses the generic decoding helpers that are also used by transaction decoding. */ async formatUtilityResult( contractAddress: AztecAddress, functionName: string, - result: any + result: Fr[] ): Promise { try { // Retrieve contract metadata and artifact @@ -553,11 +571,29 @@ export class TxCallStackDecoder { throw new Error(`Function ${functionName} not found in artifact`); } - // Format the result value with address resolution - return await this.formatAndResolveValue(result); + // If the function has no return type, return empty string + if (!functionAbi.returnTypes || functionAbi.returnTypes.length === 0) { + return "void"; + } + + // Reuse the generic return value decoding helper (same logic as transaction decoding) + const formattedReturns = await this.decodeAndFormatReturnValues( + functionAbi, + result + ); + + // For utility functions, we typically have a single return value + // If there are multiple, join them with commas + if (formattedReturns.length === 0) { + return "void"; + } else if (formattedReturns.length === 1) { + return formattedReturns[0].value; + } else { + return `[${formattedReturns.map((r) => r.value).join(", ")}]`; + } } catch (error) { - // If formatting fails, return raw JSON - return JSON.stringify(result, null, 2); + // If formatting fails, return raw representation of Fr[] values + return `[${result.map((fr) => fr.toString()).join(", ")}]`; } } } diff --git a/app/src/wallet/operations/register-contract-operation.ts b/app/src/wallet/operations/register-contract-operation.ts index 9245bf0..b726ec7 100644 --- a/app/src/wallet/operations/register-contract-operation.ts +++ b/app/src/wallet/operations/register-contract-operation.ts @@ -11,10 +11,10 @@ import type { import { getContractInstanceFromInstantiationParams, computePartialAddress, + getContractClassFromArtifact, } from "@aztec/stdlib/contract"; import type { ContractArtifact } from "@aztec/stdlib/abi"; import type { Fr } from "@aztec/foundation/fields"; -import type { ContractInstanceAndArtifact } from "@aztec/aztec.js/wallet"; import type { PXE } from "@aztec/pxe/server"; import { WalletInteraction, @@ -24,16 +24,9 @@ import type { DecodingCache } from "../decoding/decoding-cache"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; -// Type for the possible instance data inputs -type RegisterContractInstanceData = - | AztecAddress - | ContractInstanceWithAddress - | ContractInstantiationData - | ContractInstanceAndArtifact; - // Arguments tuple for the operation type RegisterContractArgs = [ - instanceData: RegisterContractInstanceData, + instance: ContractInstanceWithAddress, artifact?: ContractArtifact, secretKey?: Fr, ]; @@ -43,7 +36,7 @@ type RegisterContractResult = ContractInstanceWithAddress; // Execution data stored between prepare and execute phases interface RegisterContractExecutionData { - instanceData: RegisterContractInstanceData; + instance: ContractInstanceWithAddress; artifact?: ContractArtifact; secretKey?: Fr; } @@ -81,15 +74,12 @@ export class RegisterContractOperation extends ExternalOperation< } async check( - instanceData: RegisterContractInstanceData, + instance: ContractInstanceWithAddress, artifact?: ContractArtifact, _secretKey?: Fr ): Promise { // Resolve contract address - const contractAddress = await this.decodingCache.resolveContractAddress( - instanceData, - artifact - ); + const contractAddress = instance.address; // Check if already registered (early return case) const metadata = await this.pxe.getContractMetadata(contractAddress); @@ -101,18 +91,15 @@ export class RegisterContractOperation extends ExternalOperation< } async createInteraction( - instanceData: RegisterContractInstanceData, + instance: ContractInstanceWithAddress, artifact?: ContractArtifact, _secretKey?: Fr ): Promise> { // Create interaction with simple title from args only - const contractAddress = await this.decodingCache.resolveContractAddress( - instanceData, - artifact - ); + const contractAddress = instance.address; const contractName = await this.decodingCache.resolveContractName( - instanceData, + instance, artifact, contractAddress ); @@ -131,7 +118,7 @@ export class RegisterContractOperation extends ExternalOperation< } async prepare( - instanceData: RegisterContractInstanceData, + instance: ContractInstanceWithAddress, artifact?: ContractArtifact, secretKey?: Fr ): Promise< @@ -142,21 +129,17 @@ export class RegisterContractOperation extends ExternalOperation< > > { // Resolve contract address - const contractAddress = await this.decodingCache.resolveContractAddress( - instanceData, - artifact - ); - + const contractAddress = instance.address; // Resolve contract name for display const contractName = await this.decodingCache.resolveContractName( - instanceData, + instance, artifact, contractAddress ); return { displayData: { contractAddress, contractName }, - executionData: { instanceData, artifact, secretKey }, + executionData: { instance, artifact, secretKey }, }; } @@ -184,67 +167,41 @@ export class RegisterContractOperation extends ExternalOperation< async execute( executionData: RegisterContractExecutionData ): Promise { - const { instanceData, artifact, secretKey } = executionData; - - // Type guards - const isInstanceWithAddress = ( - data: RegisterContractInstanceData - ): data is ContractInstanceWithAddress => - typeof data === "object" && - data !== null && - "address" in data && - !("instance" in data); - const isContractInstantiationData = ( - data: RegisterContractInstanceData - ): data is ContractInstantiationData => - typeof data === "object" && data !== null && "salt" in data; - const isContractInstanceAndArtifact = ( - data: RegisterContractInstanceData - ): data is ContractInstanceAndArtifact => - typeof data === "object" && - data !== null && - "instance" in data && - "artifact" in data; - - let instance: ContractInstanceWithAddress; - - if (isContractInstanceAndArtifact(instanceData)) { - // Already has instance and artifact - instance = instanceData.instance; - await this.pxe.registerContract(instanceData); - } else if (isInstanceWithAddress(instanceData)) { - // Has instance with address - instance = instanceData; - await this.pxe.registerContract({ artifact, instance }); - } else if (isContractInstantiationData(instanceData)) { - // Need to create instance from instantiation data - if (!artifact) { - throw new Error( - `Contract artifact must be provided when registering a contract using instantiation data` - ); + let { instance, artifact, secretKey } = executionData; + const { contractInstance: existingInstance } = + await this.pxe.getContractMetadata(instance.address); + + if (existingInstance) { + // Instance already registered in the wallet + if (artifact) { + const thisContractClass = await getContractClassFromArtifact(artifact); + if ( + !thisContractClass.id.equals(existingInstance.currentContractClassId) + ) { + // wallet holds an outdated version of this contract + await this.pxe.updateContract(instance.address, artifact); + instance.currentContractClassId = thisContractClass.id; + } } - instance = await getContractInstanceFromInstantiationParams( - artifact, - instanceData - ); - await this.pxe.registerContract({ artifact, instance }); + // If no artifact provided, we just use the existing registration } else { - // instanceData is AztecAddress + // Instance not registered yet if (!artifact) { - throw new Error( - `Contract artifact must be provided when registering a contract from an address` - ); - } - instance = await this.pxe.getContractInstance(instanceData); - if (!instance) { - throw new Error( - `No contract instance found for address: ${instanceData}` + // Try to get the artifact from the wallet's contract class storage + const classMetadata = await this.pxe.getContractClassMetadata( + instance.currentContractClassId, + true ); + if (!classMetadata.artifact) { + throw new Error( + `Cannot register contract at ${instance.address.toString()}: artifact is required but not provided, and wallet does not have the artifact for contract class ${instance.currentContractClassId.toString()}` + ); + } + artifact = classMetadata.artifact; } await this.pxe.registerContract({ artifact, instance }); } - // Register secret key if provided if (secretKey) { await this.pxe.registerAccount( secretKey, diff --git a/app/src/wallet/operations/send-tx-operation.ts b/app/src/wallet/operations/send-tx-operation.ts index a3e0d2c..a263d35 100644 --- a/app/src/wallet/operations/send-tx-operation.ts +++ b/app/src/wallet/operations/send-tx-operation.ts @@ -4,34 +4,29 @@ import { type PersistenceConfig, } from "./base-operation"; import type { AztecAddress } from "@aztec/stdlib/aztec-address"; -import type { ExecutionPayload } from "@aztec/entrypoints/payload"; import { TxHash } from "@aztec/stdlib/tx"; import type { PXE } from "@aztec/pxe/server"; -import type { TxExecutionRequest } from "@aztec/stdlib/tx"; +import type { ExecutionPayload, TxExecutionRequest } from "@aztec/stdlib/tx"; import type { AztecNode } from "@aztec/aztec.js/node"; import { inspect } from "util"; import { WalletInteraction, type WalletInteractionType, } from "../types/wallet-interaction"; -import type { WalletDB } from "../database/wallet-db"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; import type { DecodingCache } from "../decoding/decoding-cache"; -import { TxDecodingService } from "../decoding/tx-decoding-service"; import type { ReadableCallAuthorization } from "../decoding/call-authorization-formatter"; import type { DecodedExecutionTrace } from "../decoding/tx-callstack-decoder"; import { hashExecutionPayload, generateSimulationTitle, } from "../utils/simulation-utils"; -import type { - SendOptions, - FeeOptions, - UserFeeOptions, -} from "@aztec/aztec.js/wallet"; +import type { SendOptions, FeeOptions } from "@aztec/aztec.js/wallet"; import type { SimulateTxOperation } from "./simulate-tx-operation"; import type { AuthWitness } from "@aztec/stdlib/auth-witness"; +import type { GasSettings } from "@aztec/stdlib/gas"; +import type { FieldsOf } from "@aztec/foundation/types"; // Arguments tuple for the operation type SendTxArgs = [executionPayload: ExecutionPayload, opts: SendOptions]; @@ -90,9 +85,10 @@ export class SendTxOperation extends ExternalOperation< from: AztecAddress, fee: FeeOptions ) => Promise, - private getDefaultFeeOptions: ( + private completeFeeOptions: ( from: AztecAddress, - fee: UserFeeOptions + feePayer: AztecAddress | undefined, + gasSettings?: Partial> ) => Promise, private contextualizeError: (err: unknown, context: string) => Error ) { @@ -118,7 +114,7 @@ export class SendTxOperation extends ExternalOperation< executionPayload, this.decodingCache, opts.from, - opts.fee?.embeddedPaymentMethodFeePayer + executionPayload.feePayer ); const interaction = WalletInteraction.from({ id: payloadHash, @@ -142,13 +138,15 @@ export class SendTxOperation extends ExternalOperation< PrepareResult > { const payloadHash = hashExecutionPayload(executionPayload); - const fee = await this.getDefaultFeeOptions(opts.from, opts.fee); + const fee = await this.completeFeeOptions( + opts.from, + executionPayload.feePayer, + opts.fee?.gasSettings + ); // Use simulateTx operation's prepare method (will throw if simulation fails) const prepared = await this.simulateTxOp.prepare(executionPayload, opts); - console.log("PREPARED"); - // Decode simulation results const { callAuthorizations, executionTrace } = prepared.executionData!.decoded; @@ -175,7 +173,7 @@ export class SendTxOperation extends ExternalOperation< executionPayload, this.decodingCache, opts.from, - opts.fee?.embeddedPaymentMethodFeePayer + executionPayload.feePayer ); return { @@ -186,7 +184,7 @@ export class SendTxOperation extends ExternalOperation< callAuthorizations, executionTrace, stats: prepared.displayData?.stats, - embeddedPaymentMethodFeePayer: opts.fee?.embeddedPaymentMethodFeePayer, + embeddedPaymentMethodFeePayer: executionPayload.feePayer, }, executionData: { txRequest, @@ -215,7 +213,8 @@ export class SendTxOperation extends ExternalOperation< title: displayData.title, from: displayData.from.toString(), stats: displayData.stats, - embeddedPaymentMethodFeePayer: displayData.embeddedPaymentMethodFeePayer?.toString(), + embeddedPaymentMethodFeePayer: + displayData.embeddedPaymentMethodFeePayer?.toString(), }, timestamp: Date.now(), }, diff --git a/app/src/wallet/operations/simulate-tx-operation.ts b/app/src/wallet/operations/simulate-tx-operation.ts index 5f115f0..cb67661 100644 --- a/app/src/wallet/operations/simulate-tx-operation.ts +++ b/app/src/wallet/operations/simulate-tx-operation.ts @@ -5,13 +5,11 @@ import { } from "./base-operation"; import type { AztecAddress } from "@aztec/stdlib/aztec-address"; import { - ExecutionPayload, + type TxSimulationResult, + type TxExecutionRequest, + type SimulationStats, + type ExecutionPayload, mergeExecutionPayloads, -} from "@aztec/entrypoints/payload"; -import type { - TxSimulationResult, - TxExecutionRequest, - SimulationStats, } from "@aztec/stdlib/tx"; import type { PXE } from "@aztec/pxe/server"; import { Fr } from "@aztec/foundation/fields"; @@ -35,6 +33,8 @@ import type { FeeOptions, SimulateOptions } from "@aztec/aztec.js/wallet"; import type { Logger } from "@aztec/aztec.js/log"; import type { ContractInstanceWithAddress } from "@aztec/stdlib/contract"; import type { ContractArtifact } from "@aztec/stdlib/abi"; +import type { GasSettings } from "@aztec/stdlib/gas"; +import type { FieldsOf } from "@aztec/foundation/types"; // Readable transaction information with decoded data interface ReadableTxInformation { @@ -80,7 +80,7 @@ type SimulateTxDisplayData = { from: AztecAddress; decoded: ReadableTxInformation; stats?: SimulationStats; - embeddedPaymentMethodFeePayer?: AztecAddress; + embeddedPaymentMethodFeePayer?: string; } & Record; /** @@ -109,13 +109,15 @@ export class SimulateTxOperation extends ExternalOperation< private decodingCache: DecodingCache, interactionManager: InteractionManager, private authorizationManager: AuthorizationManager, - private getFeeOptionsForGasEstimation: ( + private completeFeeOptionsForEstimation: ( from: AztecAddress, - fee: SimulateOptions["fee"] + feePayer: AztecAddress | undefined, + gasSettings?: Partial> ) => Promise, - private getDefaultFeeOptions: ( + private completeFeeOptions: ( from: AztecAddress, - fee: SimulateOptions["fee"] + feePayer: AztecAddress | undefined, + gasSettings?: Partial> ) => Promise, private getFakeAccountDataFor: ( address: AztecAddress @@ -152,13 +154,13 @@ export class SimulateTxOperation extends ExternalOperation< executionPayload, this.decodingCache, opts.from, - opts.fee?.embeddedPaymentMethodFeePayer + executionPayload.feePayer ); // Process fee options const feeOptions = opts.fee?.estimateGas - ? await this.getFeeOptionsForGasEstimation(opts.from, opts.fee) - : await this.getDefaultFeeOptions(opts.from, opts.fee); + ? await this.completeFeeOptionsForEstimation(opts.from, executionPayload.feePayer, opts.fee?.gasSettings) + : await this.completeFeeOptions(opts.from, executionPayload.feePayer, opts.fee?.gasSettings); const feeExecutionPayload = await feeOptions.walletFeePaymentMethod?.getExecutionPayload(); @@ -179,15 +181,12 @@ export class SimulateTxOperation extends ExternalOperation< artifact, } = await this.getFakeAccountDataFor(opts.from); - console.log("FAKE"); const txRequest = await fromAccount.createTxExecutionRequest( finalExecutionPayload, feeOptions.gasSettings, executionOptions ); - console.log("TXREQUEST"); - const contractOverrides = { [opts.from.toString()]: { instance, artifact }, }; @@ -203,7 +202,7 @@ export class SimulateTxOperation extends ExternalOperation< await this.db.storeTxSimulation(payloadHash, simulationResult, txRequest, { from: opts.from.toString(), - embeddedPaymentMethodFeePayer: opts.fee?.embeddedPaymentMethodFeePayer?.toString(), + embeddedPaymentMethodFeePayer: executionPayload.feePayer?.toString(), }); const decodingService = new TxDecodingService(this.decodingCache); @@ -216,7 +215,7 @@ export class SimulateTxOperation extends ExternalOperation< from: opts.from, decoded, stats: simulationResult.stats, - embeddedPaymentMethodFeePayer: opts.fee?.embeddedPaymentMethodFeePayer, + embeddedPaymentMethodFeePayer: executionPayload.feePayer?.toString(), }, executionData: { simulationResult, @@ -241,7 +240,7 @@ export class SimulateTxOperation extends ExternalOperation< executionPayload, this.decodingCache, opts.from, - opts.fee?.embeddedPaymentMethodFeePayer + executionPayload.feePayer ); const interaction = WalletInteraction.from({ id: payloadHash, @@ -280,7 +279,8 @@ export class SimulateTxOperation extends ExternalOperation< title: displayData.title, from: displayData.from.toString(), stats: displayData.stats, - embeddedPaymentMethodFeePayer: displayData.embeddedPaymentMethodFeePayer?.toString(), + embeddedPaymentMethodFeePayer: + displayData.embeddedPaymentMethodFeePayer, }, timestamp: Date.now(), persistence, diff --git a/app/src/wallet/operations/simulate-utility-operation.ts b/app/src/wallet/operations/simulate-utility-operation.ts index 7bd050f..95d89aa 100644 --- a/app/src/wallet/operations/simulate-utility-operation.ts +++ b/app/src/wallet/operations/simulate-utility-operation.ts @@ -20,6 +20,7 @@ import type { AuthorizationManager } from "../managers/authorization-manager"; import type { DecodingCache } from "../decoding/decoding-cache"; import { TxCallStackDecoder } from "../decoding/tx-callstack-decoder"; import { hashUtilityCall } from "../utils/simulation-utils"; +import type { FunctionCall } from "@aztec/aztec.js/abi"; // Utility execution trace with decoded arguments and formatted result interface UtilityExecutionTrace { @@ -33,11 +34,9 @@ interface UtilityExecutionTrace { // Arguments tuple for the operation type SimulateUtilityArgs = [ - functionName: string, - args: unknown[], - to: AztecAddress, + call: FunctionCall, authwits?: AuthWitness[], - from?: AztecAddress, + scopes?: AztecAddress[], ]; // Result type for the operation @@ -89,32 +88,28 @@ export class SimulateUtilityOperation extends ExternalOperation< } async check( - _functionName: string, - _args: unknown[], - _to: AztecAddress, + _call: FunctionCall, _authwits?: AuthWitness[], - _from?: AztecAddress + _scopes?: AztecAddress[] ): Promise { // No early return checks for this operation return undefined; } async createInteraction( - functionName: string, - args: unknown[], - to: AztecAddress, + call: FunctionCall, _authwits?: AuthWitness[], - from?: AztecAddress + _scopes?: AztecAddress[] ): Promise> { // Create interaction with simple title from args only - const payloadHash = hashUtilityCall(functionName, args, to, from); - const contractName = await this.decodingCache.getAddressAlias(to); - const title = `${contractName}.${functionName}`; + const payloadHash = hashUtilityCall(call); + const contractName = await this.decodingCache.getAddressAlias(call.to); + const title = `${contractName}.${call.name}`; const interaction = WalletInteraction.from({ id: payloadHash, type: "simulateUtility", title, - description: `Contract: ${to.toString()}`, + description: `Contract: ${call.to.toString()}`, complete: false, status: "SIMULATING", timestamp: Date.now(), @@ -126,11 +121,9 @@ export class SimulateUtilityOperation extends ExternalOperation< } async prepare( - functionName: string, - args: unknown[], - to: AztecAddress, + call: FunctionCall, authwits?: AuthWitness[], - from?: AztecAddress + scopes?: AztecAddress[] ): Promise< PrepareResult< SimulateUtilityResult, @@ -141,46 +134,54 @@ export class SimulateUtilityOperation extends ExternalOperation< // NO TRY-CATCH - let errors throw naturally! // Generate hash for deduplication - const payloadHash = hashUtilityCall(functionName, args, to, from); + const payloadHash = hashUtilityCall(call); // Simulate the utility function const simulationResult = await this.pxe.simulateUtility( - functionName, - args, - to, + call, authwits, - from + scopes ); // Get contract name for better display - const contractName = await this.decodingCache.getAddressAlias(to); + const contractName = await this.decodingCache.getAddressAlias(call.to); // Format arguments and result using the TxCallStackDecoder + // Note: UtilitySimulationResult.result is now Fr[] (raw field elements) + // We need to decode them using the function's return type ABI const decoder = new TxCallStackDecoder(this.decodingCache); + + // Format the input arguments (these come from FunctionCall.args which are already typed) const decodedArgs = await decoder.formatUtilityArguments( - to, - functionName, - args + call.to, + call.name, + call.args ); + + // Format the result (now an array of Fr that needs decoding based on return type) const formattedResult = await decoder.formatUtilityResult( - to, - functionName, + call.to, + call.name, simulationResult.result ); const executionTrace = { - functionName, + functionName: call.name, args: decodedArgs, - contractAddress: to.toString(), + contractAddress: call.to.toString(), contractName, result: formattedResult, isUtility: true as const, }; - const title = `${contractName}.${functionName}`; + const title = `${contractName}.${call.name}`; // Store the utility trace and stats for display - await this.db.storeUtilityTrace(payloadHash, executionTrace, simulationResult.stats); + await this.db.storeUtilityTrace( + payloadHash, + executionTrace, + simulationResult.stats + ); return { displayData: { diff --git a/app/src/wallet/utils/simulation-utils.ts b/app/src/wallet/utils/simulation-utils.ts index 8c9b134..ee7eacb 100644 --- a/app/src/wallet/utils/simulation-utils.ts +++ b/app/src/wallet/utils/simulation-utils.ts @@ -1,10 +1,9 @@ import { AztecAddress } from "@aztec/stdlib/aztec-address"; -import { ExecutionPayload } from "@aztec/entrypoints/payload"; import { sha256 } from "@aztec/foundation/crypto"; -import { jsonStringify } from "@aztec/foundation/json-rpc"; import { serializeToBuffer } from "@aztec/foundation/serialize"; -import { FunctionType } from "@aztec/stdlib/abi"; +import { FunctionCall, FunctionType } from "@aztec/stdlib/abi"; import type { DecodingCache } from "../decoding/decoding-cache"; +import type { ExecutionPayload } from "@aztec/stdlib/tx"; /** * Creates a deterministic hash of an execution payload for comparison. @@ -32,15 +31,20 @@ export function hashExecutionPayload(payload: ExecutionPayload): string { } // Serialize capsules - for (const capsule of payload.capsules) { + for (const capsule of payload.capsules || []) { buffers.push(capsule.toBuffer()); } - // Serialize extra hashed args - for (const hashedValue of payload.extraHashedArgs) { + // Serialize extra hashed args (optional parameter, may be undefined after deserialization) + for (const hashedValue of payload.extraHashedArgs || []) { buffers.push(hashedValue.toBuffer()); } + // Serialize feePayer if present + if (payload.feePayer) { + buffers.push(serializeToBuffer(payload.feePayer)); + } + const concatenated = Buffer.concat(buffers); const hash = sha256(concatenated); return hash.toString("hex"); @@ -50,21 +54,17 @@ export function hashExecutionPayload(payload: ExecutionPayload): string { * Creates a deterministic hash of a utility function call for comparison. * This is used to determine if a utility simulation authorization can be reused. */ -export function hashUtilityCall( - functionName: string, - args: any[], - to: AztecAddress, - from?: AztecAddress -): string { - const callData = { - functionName, - args, - to: to.toString(), - from: from?.toString(), - }; - - const serialized = jsonStringify(callData); - return sha256(Buffer.from(serialized)).toString("hex"); +export function hashUtilityCall(call: FunctionCall): string { + const buffer = serializeToBuffer( + call.to, + call.selector, + call.type, + call.hideMsgSender, + call.isStatic, + call.args.length, + ...call.args + ); + return sha256(buffer).toString("hex"); } /** diff --git a/app/src/workers/wallet-worker.ts b/app/src/workers/wallet-worker.ts index d53b579..1b0e60e 100644 --- a/app/src/workers/wallet-worker.ts +++ b/app/src/workers/wallet-worker.ts @@ -235,6 +235,9 @@ const handleEvent = async ( result = await wallet[type](...sanitizedArgs); } catch (err: any) { userLog.error(`Error handling ${type}: ${err.message}`); + if (err.stack) { + userLog.error(`Stack trace: ${err.stack}`); + } // Serialize error properly - Error objects don't stringify well error = err instanceof Error ? err.message : String(err); } diff --git a/app/yarn.lock b/app/yarn.lock index 35efaaf..f427195 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -574,59 +574,59 @@ resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz#92d792a7dda250dfcb902e13228f37a81be57c8f" integrity sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw== -"@aztec/accounts@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/accounts/-/accounts-3.0.0-devnet.2.tgz#b66bbed52795e6d871cc6544bf610a2327050bde" - integrity sha512-xmhQYvkIR3KMhpTJoTPCSrOwdOOEbNUF4mXkQh0JWfWRPCw8DWqJr0dJcECS+PlxABIFE/OcLdUq7gNXmhhxWg== - dependencies: - "@aztec/aztec.js" "3.0.0-devnet.2" - "@aztec/entrypoints" "3.0.0-devnet.2" - "@aztec/ethereum" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" +"@aztec/accounts@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/accounts/-/accounts-3.0.0-nightly.20251120.tgz#283a2f39c7b5f0f2c4eddac5a4d00b857fdbcda4" + integrity sha512-HiJqgknWP7JxAF1w0N+j64brFsEUaGGkoQiEuHCGlVEPsmY0PRfyZWFOaCQEdImjatEAHdeke4qhsxfGGCj9lw== + dependencies: + "@aztec/aztec.js" "3.0.0-nightly.20251120" + "@aztec/entrypoints" "3.0.0-nightly.20251120" + "@aztec/ethereum" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" tslib "^2.4.0" -"@aztec/aztec.js@3.0.0-devnet.2", "@aztec/aztec.js@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/aztec.js/-/aztec.js-3.0.0-devnet.2.tgz#ac9d048a2ab77b7e61342ac8bc2ebaf19160bccd" - integrity sha512-3suXrSKeZUq9rMWgyYLxOUDKwxcbcnX1PX82dBRqlsAagThwHi3NPD+9QiEO/7vU2G27oQzrb6YRmARnVqL6aQ== - dependencies: - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/entrypoints" "3.0.0-devnet.2" - "@aztec/ethereum" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/l1-artifacts" "3.0.0-devnet.2" - "@aztec/protocol-contracts" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" +"@aztec/aztec.js@3.0.0-nightly.20251120", "@aztec/aztec.js@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/aztec.js/-/aztec.js-3.0.0-nightly.20251120.tgz#e9646def380a360c2c98dddd67f4666ed077b755" + integrity sha512-2jEQdK6NtZtMradNFNbjeCDEadME3iEGWU2+cYkjzUahdUQUecpyXXo4HWsOOZwwREvOPUqNlgU3nYIGl+z0qw== + dependencies: + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/entrypoints" "3.0.0-nightly.20251120" + "@aztec/ethereum" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/l1-artifacts" "3.0.0-nightly.20251120" + "@aztec/protocol-contracts" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" axios "^1.12.0" tslib "^2.4.0" viem "npm:@spalladino/viem@2.38.2-eip7594.0" zod "^3.23.8" -"@aztec/bb-prover@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/bb-prover/-/bb-prover-3.0.0-devnet.2.tgz#764ac37d922043500cf00e127c013e54dedd609a" - integrity sha512-b/uNARAQcrOFH3WGbdQB5+zZyu3rA5uX61L2Au//GAu2pLPAq9BjJfHPhDXrNmV5q8vk0RiMX0jR+ITw73jfww== - dependencies: - "@aztec/bb.js" "3.0.0-devnet.2" - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/noir-noirc_abi" "3.0.0-devnet.2" - "@aztec/noir-protocol-circuits-types" "3.0.0-devnet.2" - "@aztec/noir-types" "3.0.0-devnet.2" - "@aztec/simulator" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" - "@aztec/telemetry-client" "3.0.0-devnet.2" - "@aztec/world-state" "3.0.0-devnet.2" +"@aztec/bb-prover@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/bb-prover/-/bb-prover-3.0.0-nightly.20251120.tgz#013915a526c925ee91fe7d21872bbec7364dd752" + integrity sha512-e+cv+cGtJMZPiuTvNzqJRXUGh4UUthz2Qs+oaPUe3rPugu3Hw50J2N9Igtf5m12s3hxGJNBUZvPP03cD1+9r2g== + dependencies: + "@aztec/bb.js" "3.0.0-nightly.20251120" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/noir-noirc_abi" "3.0.0-nightly.20251120" + "@aztec/noir-protocol-circuits-types" "3.0.0-nightly.20251120" + "@aztec/noir-types" "3.0.0-nightly.20251120" + "@aztec/simulator" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" + "@aztec/telemetry-client" "3.0.0-nightly.20251120" + "@aztec/world-state" "3.0.0-nightly.20251120" commander "^12.1.0" pako "^2.1.0" source-map-support "^0.5.21" tslib "^2.4.0" -"@aztec/bb.js@3.0.0-devnet.2", "@aztec/bb.js@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/bb.js/-/bb.js-3.0.0-devnet.2.tgz#ecdd91bcbb2de525433ea3a6ff8ad17026422bf3" - integrity sha512-KRU8YuFuZ68J2I6woQ+NJsRZcr3WG2n1KaRKcT83l9fjG8OtNmPH77/SXio/cPOXjXvdzTDvfiNNt54o1olfLg== +"@aztec/bb.js@3.0.0-nightly.20251120", "@aztec/bb.js@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/bb.js/-/bb.js-3.0.0-nightly.20251120.tgz#68b0520540a21f9e0862c7f4ca324ab2928c945b" + integrity sha512-v4J38s29XLtZ5QKe5EGCNd3wx+mYrPTDBH6waD0daF4Y1N1z8VNLdOT/CS5ApyLw1tJJW0ZFE8I8M2CkcIsyTA== dependencies: comlink "^4.4.1" commander "^12.1.0" @@ -636,52 +636,52 @@ pino "^9.5.0" tslib "^2.4.0" -"@aztec/blob-lib@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/blob-lib/-/blob-lib-3.0.0-devnet.2.tgz#9a3bae3b7dd9724aea9126c5d128f0bcfe6c6f56" - integrity sha512-cRyjcgljsQ8ChNxufo4AdAasgS7IFzAgZw2pdkOkRyP/nXh1TyUx1QtrmgDU2R5oam+7HtUDck28gI0u2O80SA== +"@aztec/blob-lib@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/blob-lib/-/blob-lib-3.0.0-nightly.20251120.tgz#3221a228681edd1da6ddede422d9456c60fc67be" + integrity sha512-LtFbM/bqmKE8pgc4v59uuxeOjeWI1xzu2szilv4wTiWwE63gk2LjyXEzzNoav2Nccjj2RLcWMtbPIzCzEPx3Dg== dependencies: - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" "@crate-crypto/node-eth-kzg" "^0.10.0" tslib "^2.4.0" -"@aztec/builder@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/builder/-/builder-3.0.0-devnet.2.tgz#3afa554f26a8ac96d4f162b4cb986c8bc028eac0" - integrity sha512-zSSG3nDsHAlyUI8Ct3rpWmEOjc7MGQo7MSb9znPLa8mxZoLrXA0lP6R0M1srwjdhNxgTMod+VceAXocvka+sDQ== +"@aztec/builder@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/builder/-/builder-3.0.0-nightly.20251120.tgz#f7ca05595ec7ebf4d3cd985f3cf791974ca4d6ed" + integrity sha512-YkvEHDBEhkKNXiNTUY1qtcs4cHu2c6wu3H4Gej/BgaGLmW0/GNtZJxaYVURbFvdd4C3AfhemsoyRjFOLZxZEPg== dependencies: - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" commander "^12.1.0" -"@aztec/constants@3.0.0-devnet.2", "@aztec/constants@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/constants/-/constants-3.0.0-devnet.2.tgz#4444eceb6071ee90411a2316c0e43da6b47a1470" - integrity sha512-uM9kD87JDPwsSjb9ty6CO65I14G3J44FB90Btyf61Cv1Vi21hcTjWDLlExLdvm2EXSPyry/c3IM8hV7xMV5ZGA== +"@aztec/constants@3.0.0-nightly.20251120", "@aztec/constants@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/constants/-/constants-3.0.0-nightly.20251120.tgz#564fccb1dc1b618bdd640bc923389f199c618332" + integrity sha512-BeEOQaeSGrtR0RetIFouvuljEP15q2Ra4iRqBHLHxbGzjqwSH/SXHs5SW9aNpvw6GFlkcdJCDAw+H7V+CUnyfQ== dependencies: tslib "^2.4.0" -"@aztec/entrypoints@3.0.0-devnet.2", "@aztec/entrypoints@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/entrypoints/-/entrypoints-3.0.0-devnet.2.tgz#04bcdb957a504d882f7c45f30c83bb552906fe06" - integrity sha512-7A/CbOj8ACKkKi2Fet5Ddl44Pfohp3j8RewFR4j8ZyT55EKJBabM3SdGcQ9aGA9TaNAoMoqmOqp1R3yRCVj5Vw== +"@aztec/entrypoints@3.0.0-nightly.20251120", "@aztec/entrypoints@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/entrypoints/-/entrypoints-3.0.0-nightly.20251120.tgz#5fc900bb9887a39a4eb953d020100f7267d38148" + integrity sha512-36kgC+GVI8inIkQx3yatiUi4YnVfud8OD0hoQaqFRa6MUzFvuIRblZLvnqw/Z+N+3rYb2jJobKLnytR4v4FtPQ== dependencies: - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/protocol-contracts" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/protocol-contracts" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" tslib "^2.4.0" -"@aztec/ethereum@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/ethereum/-/ethereum-3.0.0-devnet.2.tgz#16e5c5161214d1ef8d2476f4db123e66a6f48e4c" - integrity sha512-BkY6jt10LifQkATtaeyenXBSMbVAWcH95dPaQbeQMykMlmT4c2sNISn8O69IQDY8zAoET61+ivBbT7hP108FpQ== +"@aztec/ethereum@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/ethereum/-/ethereum-3.0.0-nightly.20251120.tgz#16c56196b5fac01f98c7d74ee98f6a7d600cf3f9" + integrity sha512-rnMcv5Qkr/i4zK1xlOLQ7f2r8JUq7Aw6XeW0Us4t97vNerNhBlTEs1t6vckN5hiEPMJAndWLCt0nMhzpHVcUZg== dependencies: - "@aztec/blob-lib" "3.0.0-devnet.2" - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/l1-artifacts" "3.0.0-devnet.2" + "@aztec/blob-lib" "3.0.0-nightly.20251120" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/l1-artifacts" "3.0.0-nightly.20251120" "@viem/anvil" "^0.0.10" dotenv "^16.0.3" lodash.chunk "^4.2.0" @@ -690,14 +690,16 @@ viem "npm:@spalladino/viem@2.38.2-eip7594.0" zod "^3.23.8" -"@aztec/foundation@3.0.0-devnet.2", "@aztec/foundation@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/foundation/-/foundation-3.0.0-devnet.2.tgz#b4a409c80c0bc31ae3e1270802f1b660db2d6433" - integrity sha512-EYtzdsr8R+tIBjQWSKA/Z91ElCPSUobdcP9RmXmnNi5ByOs+3gpRIbrIz1mBb+9dG+jq0iVNadq/2raUz+e4pQ== +"@aztec/foundation@3.0.0-nightly.20251120", "@aztec/foundation@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/foundation/-/foundation-3.0.0-nightly.20251120.tgz#28b1e922f80a0a75cdb703ba333e5d2b92b9f4c7" + integrity sha512-aS9ScBKZinYRaajnBiw1jaBdWdIxRKbnGyC3lJc5dBN6Kd3U6bmAGvHcngXMCanDuheBl947xweIRCIoy815zQ== dependencies: - "@aztec/bb.js" "3.0.0-devnet.2" + "@aztec/bb.js" "3.0.0-nightly.20251120" "@koa/cors" "^5.0.0" "@noble/curves" "=1.7.0" + "@noble/hashes" "^1.6.1" + "@scure/bip39" "^2.0.1" bn.js "^5.2.1" colorette "^2.0.20" detect-node "^2.1.0" @@ -716,138 +718,139 @@ undici "^5.28.5" zod "^3.23.8" -"@aztec/key-store@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/key-store/-/key-store-3.0.0-devnet.2.tgz#ca784739b46d765933508a55c204f578f33cdf8e" - integrity sha512-gCrhwzGx9/fXHRF+x/6r2BrmCMvz6QkLGeROjNu9Fr5xcLxrxIiPrqzShaHTgSBzYCBiZRbdd7VQB6GUR9e59w== +"@aztec/key-store@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/key-store/-/key-store-3.0.0-nightly.20251120.tgz#1cdd38855e7438f558c15679e635d3a7f4f471a9" + integrity sha512-X3VQWITL0nU3ilRyvGwa7h7BaTTvRscjp8OOFdxgZ1wsbkzOrDaz0hc2zlu8tj7yGBygBE1yIu4g4OCCFpKrnw== dependencies: - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/kv-store" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/kv-store" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" tslib "^2.4.0" -"@aztec/kv-store@3.0.0-devnet.2", "@aztec/kv-store@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/kv-store/-/kv-store-3.0.0-devnet.2.tgz#40b5883c30595f4f9b9c3ce11bc04f3d8fd36c6b" - integrity sha512-U2VvPiZVwNIXNWCNu2h+3kfSXH6MoMjWQb1dNlm6CWh7O3PBxnJ+96YUWm1oY1kmQA5YcbU31kjYa0s93HntZA== +"@aztec/kv-store@3.0.0-nightly.20251120", "@aztec/kv-store@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/kv-store/-/kv-store-3.0.0-nightly.20251120.tgz#bcdf50b3e07516fb2794cb95293195a3d6e0b8cc" + integrity sha512-GlBD/+qmHEZUbnVXcCm+VrMbZT1Slg0dOeI+fdSAtE+BKJ/LzFQuDyWN8jgJEiVDgDUr5f9Rzoy8F8Zi/kHeLw== dependencies: - "@aztec/ethereum" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/native" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" + "@aztec/ethereum" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/native" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" idb "^8.0.0" lmdb "^3.2.0" msgpackr "^1.11.2" ohash "^2.0.11" ordered-binary "^1.5.3" -"@aztec/l1-artifacts@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/l1-artifacts/-/l1-artifacts-3.0.0-devnet.2.tgz#0365bc7a25ee9dd1dafa455b18b7c1a45cc7e489" - integrity sha512-F/zs3JlV6tA09OgxvrLZkphG+f+GvLPRqgPGD0JxHqYpcyHhj6oF91sCoUNmja/L1WhY9PBblI5/Sz2yftEySw== +"@aztec/l1-artifacts@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/l1-artifacts/-/l1-artifacts-3.0.0-nightly.20251120.tgz#93c84de25875576cbac1ac1ac46111c6700c97ab" + integrity sha512-zYSSQIEtpovDmDQQhOfm1tZ01fRS9FhH91tjhueAmpBQUguj4vBEaaYGIyj7H2PHLqoLV+43I/lGJ6C3Xg9tvw== dependencies: tslib "^2.4.0" -"@aztec/merkle-tree@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/merkle-tree/-/merkle-tree-3.0.0-devnet.2.tgz#34d659573cbbec596eec6840e50ca5e4370f3597" - integrity sha512-E5A5ZRKz1k/R8IG37e6o3rFHysmeC1uBg+TxYKyi07P6TgFeBTb9J+F2fCCDyPHAQSWU5yQbEbGaexVwuqzkXw== +"@aztec/merkle-tree@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/merkle-tree/-/merkle-tree-3.0.0-nightly.20251120.tgz#cdc69eccba3f82ee050efc635d22d5848e3d426c" + integrity sha512-fDZY2O7gy4f6m0V1NR3BC3PxTridnOdKpWzwdled9tw9udcjRrodYeco0QtfrP/b2TjhAVxC1uxXo9HxVfuoGA== dependencies: - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/kv-store" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/kv-store" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" sha256 "^0.2.0" tslib "^2.4.0" -"@aztec/native@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/native/-/native-3.0.0-devnet.2.tgz#2088a29907137adb06234b87f42807e910bb1fb4" - integrity sha512-FF67c8s8Wgu6s8Qvfg3Aq2RvLDFZZvwyHz4+mwI5aXbI2//xo65jPxz3HozjaLWz/CRiYC9Dkg9faZ9wUiG+Iw== +"@aztec/native@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/native/-/native-3.0.0-nightly.20251120.tgz#2b2a7e19c67d9ca9aa14966fdbae5349bd340078" + integrity sha512-CiF/6H+S5NspAlh+cuf6hSS1L2yscYhuTkSppn6kiy7PsygaHTBaWb+DcG+2yoZH3RVF/OF79uBHM6sZKHRXpQ== dependencies: - "@aztec/foundation" "3.0.0-devnet.2" + "@aztec/bb.js" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" msgpackr "^1.11.2" -"@aztec/noir-acvm_js@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/noir-acvm_js/-/noir-acvm_js-3.0.0-devnet.2.tgz#88bca6f04282709af7102f4980ddc193b1191a21" - integrity sha512-CyKCYq32w0gWN14Vc7SwAAsjMqKmQD5G2ssy6MW0UNJ5mVXusUTdq90LfFZFIAh139tLcpxG2lOOd7jYhzNZ/A== +"@aztec/noir-acvm_js@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/noir-acvm_js/-/noir-acvm_js-3.0.0-nightly.20251120.tgz#8df836bac401cf47c40d726876a1eb89534977f9" + integrity sha512-WshIBOCDr7uU1HaO+Qe+wSPp/JA0BGajsoJDGs00hPmrZOuX4E1l7o39UQMrq8QbI1TVRNgKuxDXd8bYU1z2Bw== -"@aztec/noir-contracts.js@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/noir-contracts.js/-/noir-contracts.js-3.0.0-devnet.2.tgz#ce81bfe678428d4c1f113dccb149dc947bf64416" - integrity sha512-GDItJIjO5AGuWKbS8mAbgEI+PrT1ZWafvf7BY+OXKjLAdRZ8lzLx9ftug9/iic6S1v3wHKC1R79YJj30YxQpLw== +"@aztec/noir-contracts.js@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/noir-contracts.js/-/noir-contracts.js-3.0.0-nightly.20251120.tgz#0f0317c41eb7dc9bb939e22992dc5370e722ad37" + integrity sha512-L4pCP7j4kLtrRmApTYgQ4c5HOelEztRPdzok7ZXgIBZlyVHVDT2NDzX24Mx1KXRJiDYhcYwpZAg+zSTbyBJjTQ== dependencies: - "@aztec/aztec.js" "3.0.0-devnet.2" + "@aztec/aztec.js" "3.0.0-nightly.20251120" tslib "^2.4.0" -"@aztec/noir-noir_codegen@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/noir-noir_codegen/-/noir-noir_codegen-3.0.0-devnet.2.tgz#846f2a5fb12ec7df1f2a4543661d213ae76ff79f" - integrity sha512-4H4QGvqXpyk6pV7khu88D2WDDkXUn6SxVMA6686qvJCK11I17g49f1Kg6+94XKWTAQ+BWFJgm0Ff3k7S3gppkA== +"@aztec/noir-noir_codegen@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/noir-noir_codegen/-/noir-noir_codegen-3.0.0-nightly.20251120.tgz#0edd4274d62e53674b8c81aa3906d8107c56cca1" + integrity sha512-R3oQqb7cC78unQhzdGbO93OmhTnF1q7wEN5yCarVUEprjSYQbasASRUsw+YuiJRWyti7A3I7Gu+wjc89+jagYA== dependencies: - "@aztec/noir-types" "3.0.0-devnet.2" + "@aztec/noir-types" "3.0.0-nightly.20251120" glob "^11.0.3" ts-command-line-args "^2.5.1" -"@aztec/noir-noirc_abi@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/noir-noirc_abi/-/noir-noirc_abi-3.0.0-devnet.2.tgz#8d0be9b9b15572dbb59cf9c10af2f642eee59362" - integrity sha512-stPneUeYTdGBzzt21oPJv3+8/QpjiI/3rvmet3k1ZFvGZFyPq0GL87kTKOQdStE4shc8ypOEG00isGTGNtTRmg== - dependencies: - "@aztec/noir-types" "3.0.0-devnet.2" - -"@aztec/noir-protocol-circuits-types@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/noir-protocol-circuits-types/-/noir-protocol-circuits-types-3.0.0-devnet.2.tgz#92b969122ddf1aeb8bf819fc77ad29c9c2a05de9" - integrity sha512-jOHgAMoOH52QDeEW8lIH+Fy9+rOf90DPNSO65DHgVt+n/VZG7ln7FxQl3lxHA2cjMv8moKD/wtTusAy77MXQuw== - dependencies: - "@aztec/blob-lib" "3.0.0-devnet.2" - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/noir-acvm_js" "3.0.0-devnet.2" - "@aztec/noir-noir_codegen" "3.0.0-devnet.2" - "@aztec/noir-noirc_abi" "3.0.0-devnet.2" - "@aztec/noir-types" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" +"@aztec/noir-noirc_abi@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/noir-noirc_abi/-/noir-noirc_abi-3.0.0-nightly.20251120.tgz#95bc725b8ea12285c200dc47951850084c5b4075" + integrity sha512-7lixtxY8+QUFhz9Mfw2iaLXFH/oqQ0tK0RJYr7AY3N2CGdvu7Ftc6vFhGs7BHDkw7s4SN4rwyhyzwds2haZp4w== + dependencies: + "@aztec/noir-types" "3.0.0-nightly.20251120" + +"@aztec/noir-protocol-circuits-types@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/noir-protocol-circuits-types/-/noir-protocol-circuits-types-3.0.0-nightly.20251120.tgz#bd3d3db685d75334e2f8561f04e25471c8fe7b33" + integrity sha512-qrziPpdBJbRMCUaNROPWZLASm9nbJ2lWlw8aQiHov68t7tP0nkMOnMN2DHrG10HFn37EuoD5z+HL4qeREuG2Ew== + dependencies: + "@aztec/blob-lib" "3.0.0-nightly.20251120" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/noir-acvm_js" "3.0.0-nightly.20251120" + "@aztec/noir-noir_codegen" "3.0.0-nightly.20251120" + "@aztec/noir-noirc_abi" "3.0.0-nightly.20251120" + "@aztec/noir-types" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" change-case "^5.4.4" tslib "^2.4.0" -"@aztec/noir-types@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/noir-types/-/noir-types-3.0.0-devnet.2.tgz#fa5ea3fd29bd507e3d8fd38cadb51842790c92d5" - integrity sha512-XXOfS9flhEStIJOneZJJlIQjgegWvmfeqSpGzSPHBBvYUAWSZVbjfl+8AEJcamVo311EyqQWiwScJh+1xvM4lw== +"@aztec/noir-types@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/noir-types/-/noir-types-3.0.0-nightly.20251120.tgz#f430336e1da4f9a82a932677810f9e9f4bdf974a" + integrity sha512-5jxLb0GIlGBjcYee87XPpHA4LLwnFt9R7MaidtvUIeo3Oj9U3tRsD+EYqjl0Dc1lqvQ2502+Dazz+OlecgG4fQ== -"@aztec/protocol-contracts@3.0.0-devnet.2", "@aztec/protocol-contracts@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/protocol-contracts/-/protocol-contracts-3.0.0-devnet.2.tgz#0d3a329f2c8782b05af32b1b5a9b72f0098b1dc2" - integrity sha512-W4bcAv2c5FwxzA9JLm8aQx5J0aH7hEWGICK/lMGTr0AotuXsg3mzixg27JB2T4XHSmL7jVX589ci72FdBv4/nQ== +"@aztec/protocol-contracts@3.0.0-nightly.20251120", "@aztec/protocol-contracts@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/protocol-contracts/-/protocol-contracts-3.0.0-nightly.20251120.tgz#f280b02fca76ab3b4c4940ae924301cd61ea1404" + integrity sha512-HFIWrNprBLuMNUroblZdrPL0xL4BpDpndZyD54IS3trz0lzpMbV2ZmjZByvJR1kUgrZD4RojNjOueXmm2lPewA== dependencies: - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" lodash.chunk "^4.2.0" lodash.omit "^4.5.0" tslib "^2.4.0" -"@aztec/pxe@v3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/pxe/-/pxe-3.0.0-devnet.2.tgz#fe834deec85f30602cba85daf3a01aad58bbef7d" - integrity sha512-DmY5j3YxJhdVnd2Ri2bqcOY4s996xmPcRCoIvg2kJwobKzaWTCHub0fdmHb8fJOJyiw5JVQhRrFYw0bY6ftyEA== - dependencies: - "@aztec/bb-prover" "3.0.0-devnet.2" - "@aztec/bb.js" "3.0.0-devnet.2" - "@aztec/builder" "3.0.0-devnet.2" - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/ethereum" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/key-store" "3.0.0-devnet.2" - "@aztec/kv-store" "3.0.0-devnet.2" - "@aztec/noir-protocol-circuits-types" "3.0.0-devnet.2" - "@aztec/noir-types" "3.0.0-devnet.2" - "@aztec/protocol-contracts" "3.0.0-devnet.2" - "@aztec/simulator" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" +"@aztec/pxe@v3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/pxe/-/pxe-3.0.0-nightly.20251120.tgz#b9bba43532d9310b0490b98e418bffc40818ca2c" + integrity sha512-Xjov0FG+cV46PHp9IpGvogZySSVTT3KyMxPdyiTMml+Efg2U77vtcpN3HPk4FsRLipd7LgDMkJydxZpQVrqNEQ== + dependencies: + "@aztec/bb-prover" "3.0.0-nightly.20251120" + "@aztec/bb.js" "3.0.0-nightly.20251120" + "@aztec/builder" "3.0.0-nightly.20251120" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/ethereum" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/key-store" "3.0.0-nightly.20251120" + "@aztec/kv-store" "3.0.0-nightly.20251120" + "@aztec/noir-protocol-circuits-types" "3.0.0-nightly.20251120" + "@aztec/noir-types" "3.0.0-nightly.20251120" + "@aztec/protocol-contracts" "3.0.0-nightly.20251120" + "@aztec/simulator" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" koa "^2.16.1" koa-router "^13.1.1" lodash.omit "^4.5.0" @@ -855,39 +858,39 @@ tslib "^2.4.0" viem "npm:@spalladino/viem@2.38.2-eip7594.0" -"@aztec/simulator@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/simulator/-/simulator-3.0.0-devnet.2.tgz#7c8423218d4f3d851d27fcb2c703ab207e75f654" - integrity sha512-WjLeQRSLkuE3HqTDBO2j9rUGKjDTxv0pUOa3BSmF+WtatZsjX5YjcBmnJO+Oca6wXYy3Asmif3L2k42rAOqCuA== - dependencies: - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/native" "3.0.0-devnet.2" - "@aztec/noir-acvm_js" "3.0.0-devnet.2" - "@aztec/noir-noirc_abi" "3.0.0-devnet.2" - "@aztec/noir-protocol-circuits-types" "3.0.0-devnet.2" - "@aztec/noir-types" "3.0.0-devnet.2" - "@aztec/protocol-contracts" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" - "@aztec/telemetry-client" "3.0.0-devnet.2" - "@aztec/world-state" "3.0.0-devnet.2" +"@aztec/simulator@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/simulator/-/simulator-3.0.0-nightly.20251120.tgz#105763d36a3a67a4b9ae1c6e7812557638d2f717" + integrity sha512-TPCCsyEoqwNYNFSsJsERUZXpDT/IRR6kBEQVc6kTMGkPswrzBpsS1dw3ip64kSz/p3A+fjNCSPVZTEAi2oLr6w== + dependencies: + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/native" "3.0.0-nightly.20251120" + "@aztec/noir-acvm_js" "3.0.0-nightly.20251120" + "@aztec/noir-noirc_abi" "3.0.0-nightly.20251120" + "@aztec/noir-protocol-circuits-types" "3.0.0-nightly.20251120" + "@aztec/noir-types" "3.0.0-nightly.20251120" + "@aztec/protocol-contracts" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" + "@aztec/telemetry-client" "3.0.0-nightly.20251120" + "@aztec/world-state" "3.0.0-nightly.20251120" lodash.clonedeep "^4.5.0" lodash.merge "^4.6.2" tslib "^2.4.0" -"@aztec/stdlib@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/stdlib/-/stdlib-3.0.0-devnet.2.tgz#efe4a0874ab4eba3b5da0408c375da28fef8f989" - integrity sha512-YTepOqs+0aPK6pXj1LqPiYFJHcq2pyYDvUXaNaLG5FdxghdhepZQd1KjG4V7zm2cXIqASlgVyzsBJ2mTZKhaXQ== +"@aztec/stdlib@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/stdlib/-/stdlib-3.0.0-nightly.20251120.tgz#15db25ba12a232484b840790fbba046a86a24899" + integrity sha512-ttiubLVwCkP2yym33nnB9fCMhwAOmBRawnVvlP6glTnly2zQlMPZnVsuHFmDVAT2zpeCI5ltUBOOUhG6h7ZRPQ== dependencies: "@aws-sdk/client-s3" "^3.892.0" - "@aztec/bb.js" "3.0.0-devnet.2" - "@aztec/blob-lib" "3.0.0-devnet.2" - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/ethereum" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/l1-artifacts" "3.0.0-devnet.2" - "@aztec/noir-noirc_abi" "3.0.0-devnet.2" + "@aztec/bb.js" "3.0.0-nightly.20251120" + "@aztec/blob-lib" "3.0.0-nightly.20251120" + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/ethereum" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/l1-artifacts" "3.0.0-nightly.20251120" + "@aztec/noir-noirc_abi" "3.0.0-nightly.20251120" "@google-cloud/storage" "^7.15.0" axios "^1.12.0" json-stringify-deterministic "1.0.12" @@ -901,13 +904,13 @@ viem "npm:@spalladino/viem@2.38.2-eip7594.0" zod "^3.23.8" -"@aztec/telemetry-client@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/telemetry-client/-/telemetry-client-3.0.0-devnet.2.tgz#8fc9aa023e7692f0ef3d4297ad78434dff3897a2" - integrity sha512-SAIg2ANvVzUDWbP00+2lN35dBRdJQPMcQ8QZ2M+aQBMKvRbyClgVy9800UVGRplPiiirClkN57kkwW+TWX9rrQ== +"@aztec/telemetry-client@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/telemetry-client/-/telemetry-client-3.0.0-nightly.20251120.tgz#df2e9bd2e883b3345adffdf8f5fea39e626c3613" + integrity sha512-5KQ6ISUU5CI9Ih6b9kIbhrRD8yI7BvQ4V0H7CerHZ0BwsJbA88Pp7XRHN9pDjvGx9YLPAlBU55VQH9TkON6k5A== dependencies: - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" "@opentelemetry/api" "^1.9.0" "@opentelemetry/api-logs" "^0.55.0" "@opentelemetry/core" "^1.28.0" @@ -925,19 +928,19 @@ prom-client "^15.1.3" viem "npm:@spalladino/viem@2.38.2-eip7594.0" -"@aztec/world-state@3.0.0-devnet.2": - version "3.0.0-devnet.2" - resolved "https://registry.yarnpkg.com/@aztec/world-state/-/world-state-3.0.0-devnet.2.tgz#711d875c58ec71c23abaf3635c8e4e41b0788ed2" - integrity sha512-YGcKr49SYgDrcUBiXLfbuAog9vzvv5+u6VDBeowBRbfjOrk5oEsujD+DvwwICl+y/vDEFV+uAtJF+QsjP3fBfQ== - dependencies: - "@aztec/constants" "3.0.0-devnet.2" - "@aztec/foundation" "3.0.0-devnet.2" - "@aztec/kv-store" "3.0.0-devnet.2" - "@aztec/merkle-tree" "3.0.0-devnet.2" - "@aztec/native" "3.0.0-devnet.2" - "@aztec/protocol-contracts" "3.0.0-devnet.2" - "@aztec/stdlib" "3.0.0-devnet.2" - "@aztec/telemetry-client" "3.0.0-devnet.2" +"@aztec/world-state@3.0.0-nightly.20251120": + version "3.0.0-nightly.20251120" + resolved "https://registry.yarnpkg.com/@aztec/world-state/-/world-state-3.0.0-nightly.20251120.tgz#35906f68975e9d6f653fe9e9b370d0c70211aa9e" + integrity sha512-sp2O8td/Jtf2uFK4s9yyUGlEamLkV19HF8sMc8cUKTgGpF9ODw1LIvORr1+5TOeV7Sd0Cbd0lWasr5JhJfCXcQ== + dependencies: + "@aztec/constants" "3.0.0-nightly.20251120" + "@aztec/foundation" "3.0.0-nightly.20251120" + "@aztec/kv-store" "3.0.0-nightly.20251120" + "@aztec/merkle-tree" "3.0.0-nightly.20251120" + "@aztec/native" "3.0.0-nightly.20251120" + "@aztec/protocol-contracts" "3.0.0-nightly.20251120" + "@aztec/stdlib" "3.0.0-nightly.20251120" + "@aztec/telemetry-client" "3.0.0-nightly.20251120" tslib "^2.4.0" zod "^3.23.8" @@ -2300,11 +2303,16 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.6.0.tgz#d4bfb516ad6e7b5111c216a5cc7075f4cf19e6c5" integrity sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ== -"@noble/hashes@1.8.0", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": +"@noble/hashes@1.8.0", "@noble/hashes@^1.6.1", "@noble/hashes@^1.8.0", "@noble/hashes@~1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.8.0.tgz#cee43d801fcef9644b11b8194857695acd5f815a" integrity sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A== +"@noble/hashes@2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e" + integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2745,6 +2753,11 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== +"@scure/base@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-2.0.0.tgz#ba6371fddf92c2727e88ad6ab485db6e624f9a98" + integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== + "@scure/base@~1.2.5": version "1.2.6" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.2.6.tgz#ca917184b8231394dd8847509c67a0be522e59f6" @@ -2767,6 +2780,14 @@ "@noble/hashes" "~1.8.0" "@scure/base" "~1.2.5" +"@scure/bip39@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-2.0.1.tgz#47a6dc15e04faf200041239d46ae3bb7c3c96add" + integrity sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg== + dependencies: + "@noble/hashes" "2.0.1" + "@scure/base" "2.0.0" + "@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"