diff --git a/.github/workflows/build.yml b/.github/workflows/build-artifacts.yml similarity index 79% rename from .github/workflows/build.yml rename to .github/workflows/build-artifacts.yml index eab1538..bcc4b77 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build-artifacts.yml @@ -1,4 +1,4 @@ -name: Build +name: Build Artifacts on: workflow_call: @@ -22,50 +22,45 @@ jobs: chrome-extension-id: ${{ steps.get-chrome-id.outputs.extension-id }} steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 + - uses: actions/setup-node@v4 with: node-version: "22" - - name: Corepack enable - run: corepack enable + - run: corepack enable - - name: Install dependencies + # Extension has its own lockfile and package manager — install separately + - name: Install extension dependencies working-directory: ./extension run: yarn install --immutable - name: Build Chrome extension (development) if: ${{ !inputs.release-mode }} working-directory: ./extension - run: yarn run build + run: yarn build - name: Build Firefox extension (development) if: ${{ !inputs.release-mode }} working-directory: ./extension - run: yarn run build:firefox + run: yarn build:firefox - - name: Zip Chrome extension (release mode) + - name: Zip Chrome extension (release) if: ${{ inputs.release-mode }} working-directory: ./extension - run: yarn run zip + run: yarn zip - - name: Zip Firefox extension (release mode) + - name: Zip Firefox extension (release) if: ${{ inputs.release-mode }} working-directory: ./extension - run: yarn run zip:firefox + run: yarn zip:firefox - name: Get Chrome extension ID id: get-chrome-id working-directory: ./extension run: | - # Read the extension key from manifest.json and compute the ID - # Chrome extension ID is derived from the "key" field in manifest.json MANIFEST_PATH=".output/chrome-mv3/manifest.json" if [ -f "$MANIFEST_PATH" ]; then - # Extract the key and compute the extension ID KEY=$(jq -r '.key // empty' "$MANIFEST_PATH") if [ -n "$KEY" ]; then # Decode base64 key, hash with SHA256, take first 32 chars, map to a-p @@ -73,11 +68,9 @@ jobs: echo "extension-id=$EXTENSION_ID" >> $GITHUB_OUTPUT echo "Chrome extension ID: $EXTENSION_ID" else - echo "No key found in manifest, extension ID will be empty" echo "extension-id=" >> $GITHUB_OUTPUT fi else - echo "Manifest not found at $MANIFEST_PATH" echo "extension-id=" >> $GITHUB_OUTPUT fi @@ -114,7 +107,7 @@ jobs: retention-days: ${{ inputs.retention-days }} build-electron: - name: Build Electron App + name: Build Electron App (${{ matrix.os }}) runs-on: ${{ matrix.os }} needs: build-extension @@ -123,25 +116,22 @@ jobs: os: [ubuntu-latest, macos-latest] steps: - - name: Checkout code - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Setup Node.js - uses: actions/setup-node@v4 + - uses: actions/setup-node@v4 with: node-version: "22" - - name: Corepack enable - run: corepack enable + - run: corepack enable + # Install from root so the shared workspace package is available to the app - name: Install dependencies - working-directory: ./app run: yarn install --immutable - name: Build Electron app (development) if: ${{ !inputs.release-mode }} working-directory: ./app - run: yarn run package + run: yarn package env: NODE_ENV: production NODE_OPTIONS: --max-old-space-size=4096 @@ -151,7 +141,7 @@ jobs: - name: Build Electron app (release) if: ${{ inputs.release-mode }} working-directory: ./app - run: yarn run make + run: yarn make env: NODE_ENV: production NODE_OPTIONS: --max-old-space-size=4096 @@ -171,6 +161,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: electron-release-${{ matrix.os }} - path: | - app/out/make/**/* + path: app/out/make/**/* retention-days: ${{ inputs.retention-days }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 37388ae..2717513 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,66 @@ -name: CI Build +name: CI on: + push: + branches: + - main + - next pull_request: branches: - main - next - push: + pull_request_target: branches: - main - next + workflow_dispatch: + +permissions: + contents: read + pull-requests: write jobs: + lint: + name: Lint & Typecheck + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - uses: actions/setup-node@v4 + with: + node-version: "22" + + - run: corepack enable + + - name: Install dependencies + run: yarn install --immutable + + - name: Lint + run: yarn lint + + - name: Typecheck + run: yarn typecheck + build: - uses: ./.github/workflows/build.yml + name: Build Artifacts + needs: lint + uses: ./.github/workflows/build-artifacts.yml with: release-mode: false retention-days: 7 + + deploy-web: + name: Deploy Web Wallet + needs: lint + uses: ./.github/workflows/deploy-web.yml + with: + production: false + ref: ${{ github.event.pull_request.head.sha || github.sha }} + pr-number: ${{ github.event.pull_request.number || 0 }} + secrets: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} diff --git a/.github/workflows/deploy-web.yml b/.github/workflows/deploy-web.yml index a33c9bc..d30a2ea 100644 --- a/.github/workflows/deploy-web.yml +++ b/.github/workflows/deploy-web.yml @@ -1,38 +1,50 @@ -name: Deploy Web Wallet to Vercel +name: Deploy Web Wallet on: - push: - branches: - - main - pull_request: - branches: - - main - - next - workflow_dispatch: + workflow_call: + inputs: + production: + description: "Deploy to production (true) or preview (false)" + required: false + type: boolean + default: false + ref: + description: "Git ref to check out" + required: false + type: string + default: "" + pr-number: + description: "PR number to comment on (omit to skip comment)" + required: false + type: number + default: 0 + secrets: + VERCEL_TOKEN: + required: true + VERCEL_ORG_ID: + required: true + VERCEL_PROJECT_ID: + required: true env: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} -permissions: - contents: read - pull-requests: write - jobs: - deploy-web: + deploy: + name: ${{ inputs.production && 'Deploy (production)' || 'Deploy (preview)' }} runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.sha }} - - name: Set up Node.js - uses: actions/setup-node@v4 + - uses: actions/setup-node@v4 with: - node-version: '22' + node-version: "22" - - name: Enable Corepack - run: corepack enable + - run: corepack enable - name: Install dependencies run: yarn install --immutable @@ -40,15 +52,15 @@ jobs: - name: Install Vercel CLI run: npm install --global vercel@latest - - name: Pull Vercel Environment Information + - name: Pull Vercel environment working-directory: ./web run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }} - - name: Build and deploy to Vercel + - name: Build and deploy id: deploy working-directory: ./web run: | - if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == "refs/heads/main" ]; then + if [ "${{ inputs.production }}" == "true" ]; then vercel build --prod --token=${{ secrets.VERCEL_TOKEN }} DEPLOY_URL=$(vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }} --archive=tgz --yes) else @@ -57,14 +69,14 @@ jobs: fi echo "url=$DEPLOY_URL" >> $GITHUB_OUTPUT - - name: Comment deployment URL on PR - if: github.event_name == 'pull_request' + - name: Comment preview URL on PR + if: ${{ inputs.pr-number != 0 }} uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ - issue_number: context.issue.number, + issue_number: ${{ inputs.pr-number }}, owner: context.repo.owner, repo: context.repo.repo, - body: '🚀 Web wallet deployed to Vercel!\n\n**Preview URL:** ${{ steps.deploy.outputs.url }}' + body: '🚀 Web wallet deployed!\n\n**Preview URL:** ${{ steps.deploy.outputs.url }}' }) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c6abace..de79bf1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,32 @@ on: type: string jobs: + lint: + name: Lint & Typecheck + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: "22" + + - run: corepack enable + + - name: Install dependencies + run: yarn install --immutable + + - name: Lint + run: yarn lint + + - name: Typecheck + run: yarn typecheck + build: - uses: ./.github/workflows/build.yml + name: Build Artifacts + needs: lint + uses: ./.github/workflows/build-artifacts.yml with: release-mode: true retention-days: 30 @@ -28,9 +52,6 @@ jobs: with: path: release-artifacts - - name: Display structure of downloaded files - run: ls -R release-artifacts - - name: Create Release uses: softprops/action-gh-release@v2 with: @@ -38,7 +59,17 @@ jobs: name: Release ${{ inputs.version }} draft: true generate_release_notes: true - files: | - release-artifacts/**/* + files: release-artifacts/**/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + deploy-web: + name: Deploy Web Wallet + needs: build + uses: ./.github/workflows/deploy-web.yml + with: + production: true + secrets: + VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} diff --git a/.github/workflows/update-nightly.yml b/.github/workflows/update-nightly.yml deleted file mode 100644 index fd6ec81..0000000 --- a/.github/workflows/update-nightly.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Update to Latest Nightly - -on: - schedule: - # Run daily at 6 AM UTC (after nextnet deploys at 5 AM) - - cron: '0 6 * * *' - workflow_dispatch: - inputs: - version: - description: 'Nightly version (e.g., 4.0.0-nightly.20260206)' - required: false - type: string - rollup_version: - description: 'Rollup version for nextnet (e.g., 3863723750)' - required: false - type: string - -permissions: - contents: write - pull-requests: write - -jobs: - update-nightly: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/next' - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - ref: next - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '24' - - - name: Enable Corepack - run: corepack enable - - - name: Run update script - env: - CI: 1 - run: | - ARGS="" - - if [ -n "${{ inputs.version }}" ]; then - ARGS="$ARGS --version ${{ inputs.version }}" - fi - - if [ -n "${{ inputs.rollup_version }}" ]; then - ARGS="$ARGS --rollup-version ${{ inputs.rollup_version }}" - fi - - node scripts/update-to-nightly.js $ARGS - - - name: Get updated version - id: version - run: | - VERSION=$(node -e "const p = JSON.parse(require('fs').readFileSync('app/package.json','utf-8')); console.log(p.dependencies['@aztec/wallet-sdk'])") - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 - with: - commit-message: "chore: update to ${{ steps.version.outputs.version }}" - title: "chore: update to ${{ steps.version.outputs.version }}" - body: | - Automated update to Aztec nightly version ${{ steps.version.outputs.version }} - - **Changes:** - - Updated all @aztec/* dependencies in app/package.json - - Updated @aztec/wallet-sdk in extension/package.json - - Updated nextnet rollup version (auto-fetched from nextnet node) - - **Note:** This PR was created automatically by the update-nightly workflow. - branch: automated-nightly-update-${{ steps.version.outputs.version }} - delete-branch: true - base: next diff --git a/.gitignore b/.gitignore index 59565bc..c8f56ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .local-aztec-path node_modules .vercel -.yarn/install-state.gz \ No newline at end of file +.yarn/install-state.gz +.turbo \ No newline at end of file diff --git a/app/.eslintrc.json b/app/.eslintrc.json deleted file mode 100644 index 2d7aa60..0000000 --- a/app/.eslintrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "node": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/electron", - "plugin:import/typescript" - ], - "parser": "@typescript-eslint/parser" -} diff --git a/app/eslint.config.js b/app/eslint.config.js deleted file mode 100644 index 80a979d..0000000 --- a/app/eslint.config.js +++ /dev/null @@ -1,34 +0,0 @@ -import js from "@eslint/js"; -import globals from "globals"; -import reactHooks from "eslint-plugin-react-hooks"; -import reactRefresh from "eslint-plugin-react-refresh"; -import tseslint from "typescript-eslint"; -import eslintConfigPrettier from "eslint-config-prettier/flat"; - -export default tseslint.config( - { ignores: ["dist"] }, - { - extends: [ - js.configs.recommended, - ...tseslint.configs.recommended, - eslintConfigPrettier, - ], - files: ["**/*.{ts,tsx}"], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - }, - plugins: { - "react-hooks": reactHooks, - "react-refresh": reactRefresh, - }, - rules: { - ...reactHooks.configs.recommended.rules, - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], - '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }], - }, - } -); diff --git a/app/forge.config.ts b/app/forge.config.ts index d93b1d4..37ef2f4 100644 --- a/app/forge.config.ts +++ b/app/forge.config.ts @@ -18,10 +18,7 @@ type CopyClass = { type CustomWalker = CopyClass & { modules: Module[]; - walkDependenciesForModule: ( - moduleRoot: string, - depType: DepType, - ) => Promise; + walkDependenciesForModule: (moduleRoot: string, depType: DepType) => Promise; }; const externalDependencies = ["@aztec/kv-store", "@aztec/bb.js"]; @@ -55,32 +52,22 @@ const config: ForgeConfig = { console.log(`External dependencies: ${externalDependencies.join(", ")}`); for (const dep of externalDependencies) { - const walker = new Walker( - path.join(sourceNodeModulesPath, dep), - ) as unknown as CustomWalker; + const walker = new Walker(path.join(sourceNodeModulesPath, dep)) as unknown as CustomWalker; - await walker.walkDependenciesForModule( - path.join(sourceNodeModulesPath, dep), - DepType.PROD, - ); + await walker.walkDependenciesForModule(path.join(sourceNodeModulesPath, dep), DepType.PROD); walker.modules.forEach((treeDep) => { depsToCopy.add(treeDep.name); }); } - console.log( - `Total packages to copy (including transitive): ${depsToCopy.size}`, - ); + console.log(`Total packages to copy (including transitive): ${depsToCopy.size}`); await Promise.all( Array.from(depsToCopy.values()).map(async (packageName) => { // Use mapped source if available, otherwise use the original package name const sourcePackageName = dependencyMap[packageName] || packageName; - const sourcePath = path.join( - sourceNodeModulesPath, - sourcePackageName, - ); + const sourcePath = path.join(sourceNodeModulesPath, sourcePackageName); const destPath = path.join(destNodeModulesPath, packageName); // Check if source exists (handles hoisted/symlinked deps that may not resolve) @@ -103,12 +90,7 @@ const config: ForgeConfig = { }, }, rebuildConfig: {}, - makers: [ - new MakerSquirrel({}), - new MakerZIP({}, ["darwin"]), - new MakerRpm({}), - new MakerDeb({}), - ], + makers: [new MakerSquirrel({}), new MakerZIP({}, ["darwin"]), new MakerRpm({}), new MakerDeb({})], plugins: [ new AutoUnpackNativesPlugin({}), new VitePlugin({ diff --git a/app/package.json b/app/package.json index da231ff..07e3e73 100644 --- a/app/package.json +++ b/app/package.json @@ -6,14 +6,15 @@ "main": ".vite/build/main.js", "scripts": { "start": "yarn run prebuild && electron-forge start", - "prebuild": "node scripts/copyBB.js", + "prebuild": "node scripts/copyBB.cjs", "package": "yarn run prebuild && yarn run build:native-host && NODE_ENV=production electron-forge package", "package:test": "yarn run package && ./out/app-darwin-arm64/app.app/Contents/MacOS/app", "make": "yarn run prebuild && yarn run build:native-host && NODE_ENV=production electron-forge make", "publish": "electron-forge publish", - "lint": "eslint --ext .ts,.tsx .", - "build:native-host": "node scripts/buildNativeHost.js", - "build:native-host:all": "node scripts/buildNativeHost.js --all" + "lint": "yarn exec eslint .", + "typecheck": "yarn exec tsc --noEmit", + "build:native-host": "node scripts/buildNativeHost.cjs", + "build:native-host:all": "node scripts/buildNativeHost.cjs --all" }, "keywords": [], "author": { @@ -32,46 +33,39 @@ "@electron-forge/plugin-vite": "^7.9.0", "@electron/fuses": "^1.8.0", "@types/electron-squirrel-startup": "^1.0.2", - "@typescript-eslint/eslint-plugin": "^5.62.0", - "@typescript-eslint/parser": "^5.62.0", + "@types/react": "^18.3.1", + "@types/react-dom": "^18.3.1", "@vitejs/plugin-react-swc": "^3.5.0", "@yao-pkg/pkg": "^5.15.0", "electron": "38.1.0", - "eslint": "^9.17.0", - "eslint-config-prettier": "^10.1.3", - "eslint-plugin-import": "^2.32.0", - "eslint-plugin-react-hooks": "^5.2.0", - "eslint-plugin-react-refresh": "^0.4.20", "glob": "^11.0.2", "json-stringify-deterministic": "^1.0.12", "lodash.chunk": "^4.2.0", "lodash.isequal": "^4.5.0", "lodash.times": "^4.3.2", "msgpackr": "^1.11.0", - "prettier": "^3.6.2", "sha3": "^2.1.4", "typescript": "~5.6.2", - "typescript-eslint": "^8.32.0", "vite": "^5.4.20", "vite-plugin-node-polyfills": "^0.24.0", "vite-plugin-static-copy": "^3.1.2" }, "dependencies": { - "@aztec/accounts": "v4.2.0-aztecnr-rc.2", - "@aztec/aztec.js": "v4.2.0-aztecnr-rc.2", - "@aztec/bb.js": "v4.2.0-aztecnr-rc.2", - "@aztec/blob-lib": "v4.2.0-aztecnr-rc.2", - "@aztec/constants": "v4.2.0-aztecnr-rc.2", - "@aztec/entrypoints": "v4.2.0-aztecnr-rc.2", - "@aztec/ethereum": "v4.2.0-aztecnr-rc.2", - "@aztec/foundation": "v4.2.0-aztecnr-rc.2", - "@aztec/kv-store": "v4.2.0-aztecnr-rc.2", - "@aztec/noir-contracts.js": "v4.2.0-aztecnr-rc.2", - "@aztec/noir-noirc_abi": "v4.2.0-aztecnr-rc.2", - "@aztec/protocol-contracts": "v4.2.0-aztecnr-rc.2", - "@aztec/pxe": "v4.2.0-aztecnr-rc.2", - "@aztec/stdlib": "v4.2.0-aztecnr-rc.2", - "@aztec/wallet-sdk": "v4.2.0-aztecnr-rc.2", + "@aztec/accounts": "v4.2.0-nightly.20260412", + "@aztec/aztec.js": "v4.2.0-nightly.20260412", + "@aztec/bb.js": "v4.2.0-nightly.20260412", + "@aztec/blob-lib": "v4.2.0-nightly.20260412", + "@aztec/constants": "v4.2.0-nightly.20260412", + "@aztec/entrypoints": "v4.2.0-nightly.20260412", + "@aztec/ethereum": "v4.2.0-nightly.20260412", + "@aztec/foundation": "v4.2.0-nightly.20260412", + "@aztec/kv-store": "v4.2.0-nightly.20260412", + "@aztec/noir-contracts.js": "v4.2.0-nightly.20260412", + "@aztec/noir-noirc_abi": "v4.2.0-nightly.20260412", + "@aztec/protocol-contracts": "v4.2.0-nightly.20260412", + "@aztec/pxe": "v4.2.0-nightly.20260412", + "@aztec/stdlib": "v4.2.0-nightly.20260412", + "@aztec/wallet-sdk": "v4.2.0-nightly.20260412", "@demo-wallet/shared": "workspace:*", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", diff --git a/app/scripts/buildNativeHost.js b/app/scripts/buildNativeHost.cjs similarity index 94% rename from app/scripts/buildNativeHost.js rename to app/scripts/buildNativeHost.cjs index 1c4cea2..ccf2a74 100644 --- a/app/scripts/buildNativeHost.js +++ b/app/scripts/buildNativeHost.cjs @@ -53,7 +53,7 @@ function buildBundle() { { cwd: ROOT_DIR, stdio: "inherit", - } + }, ); console.log(`Bundle created: ${BUNDLE_PATH}`); @@ -76,13 +76,10 @@ function buildBinary(target) { console.log(`Building binary for ${target}...`); try { - execSync( - `npx pkg "${BUNDLE_PATH}" --target ${pkgTarget} --output "${outputPath}"`, - { - cwd: ROOT_DIR, - stdio: "inherit", - } - ); + execSync(`npx pkg "${BUNDLE_PATH}" --target ${pkgTarget} --output "${outputPath}"`, { + cwd: ROOT_DIR, + stdio: "inherit", + }); // Make executable on Unix if (!target.startsWith("win32")) { diff --git a/app/scripts/copyBB.js b/app/scripts/copyBB.cjs similarity index 90% rename from app/scripts/copyBB.js rename to app/scripts/copyBB.cjs index f14a697..373c973 100644 --- a/app/scripts/copyBB.js +++ b/app/scripts/copyBB.cjs @@ -36,9 +36,7 @@ function getPlatformArch() { } else if (arch === "arm64") { return "arm64-linux"; } - console.error( - `✗ Unsupported Linux architecture: ${arch}. Only x64 and ARM64 are supported.`, - ); + console.error(`✗ Unsupported Linux architecture: ${arch}. Only x64 and ARM64 are supported.`); process.exit(1); case "win32": console.error(`✗ Windows builds are not available for Barretenberg.`); @@ -48,9 +46,7 @@ function getPlatformArch() { process.exit(1); } - console.error( - `✗ Unsupported architecture: ${arch} for platform: ${platform}`, - ); + console.error(`✗ Unsupported architecture: ${arch} for platform: ${platform}`); process.exit(1); } @@ -63,14 +59,8 @@ async function main() { BB_FOLDER, "dest/node/barretenberg_wasm/barretenberg-threads.wasm.gz", ); - const BB_NAPI_SOURCE = path.join( - BB_FOLDER, - `build/${getPlatformArch()}/nodejs_module.node`, - ); - const BB_BINARY_SOURCE = path.join( - BB_FOLDER, - `build/${getPlatformArch()}/bb`, - ); + const BB_NAPI_SOURCE = path.join(BB_FOLDER, `build/${getPlatformArch()}/nodejs_module.node`); + const BB_BINARY_SOURCE = path.join(BB_FOLDER, `build/${getPlatformArch()}/bb`); // Destination directory - will be packaged with the app const RESOURCES_DIR = path.join(__dirname, ".."); diff --git a/app/src/ipc/preload.ts b/app/src/ipc/preload.ts index c6d2d55..260fe97 100644 --- a/app/src/ipc/preload.ts +++ b/app/src/ipc/preload.ts @@ -2,10 +2,7 @@ import type { Aliased } from "@aztec/aztec.js/wallet"; import type { AztecAddress } from "@aztec/aztec.js/addresses"; import { contextBridge, ipcRenderer } from "electron"; import type { TxHash, TxReceipt } from "@aztec/stdlib/tx"; -import type { - WalletInteraction, - WalletInteractionType, -} from "@demo-wallet/shared/core"; +import type { WalletInteraction, WalletInteractionType } from "@demo-wallet/shared/core"; contextBridge.exposeInMainWorld("walletAPI", { getTxReceipt(stringifiedArgs: string): Promise { @@ -26,11 +23,15 @@ contextBridge.exposeInMainWorld("walletAPI", { deployAccount(stringifiedArgs: string): Promise { return ipcRenderer.invoke("deployAccount", stringifiedArgs); }, - getInteractions( - stringifiedArgs: string - ): Promise[]> { + getInteractions(stringifiedArgs: string): Promise[]> { return ipcRenderer.invoke("getInteractions", stringifiedArgs); }, + deleteInteraction(stringifiedArgs: string): Promise { + return ipcRenderer.invoke("deleteInteraction", stringifiedArgs); + }, + clearInteractions(stringifiedArgs: string): Promise { + return ipcRenderer.invoke("clearInteractions", stringifiedArgs); + }, getExecutionTrace(stringifiedArgs: string): Promise { return ipcRenderer.invoke("getExecutionTrace", stringifiedArgs); }, @@ -80,13 +81,11 @@ contextBridge.exposeInMainWorld("walletAPI", { }, // Proof debug export saveProofDebugData( - base64Data: string + base64Data: string, ): Promise<{ success: boolean; canceled?: boolean; filePath?: string; error?: string }> { return ipcRenderer.invoke("saveProofDebugData", base64Data); }, onProofDebugExportRequest(callback) { - return ipcRenderer.on("proof-debug-export-request", (_event, eventData) => - callback(eventData) - ); + return ipcRenderer.on("proof-debug-export-request", (_event, eventData) => callback(eventData)); }, }); diff --git a/app/src/ipc/wallet-internal-proxy.ts b/app/src/ipc/wallet-internal-proxy.ts index e594336..369e689 100644 --- a/app/src/ipc/wallet-internal-proxy.ts +++ b/app/src/ipc/wallet-internal-proxy.ts @@ -1,7 +1,4 @@ -import { - promiseWithResolvers, - type PromiseWithResolvers, -} from "@aztec/foundation/promise"; +import { promiseWithResolvers, type PromiseWithResolvers } from "@aztec/foundation/promise"; import { schemaHasMethod } from "@aztec/foundation/schemas"; import type { MessagePortMain } from "electron/main"; import { @@ -13,7 +10,7 @@ import { } from "@demo-wallet/shared/core"; type FunctionsOf = { - [K in keyof T as T[K] extends Function ? K : never]: T[K]; + [K in keyof T as T[K] extends (...args: unknown[]) => unknown ? K : never]: T[K]; }; export class WalletInternalProxy { @@ -32,9 +29,7 @@ export class WalletInternalProxy { this.authRequestCallback = callback; } - public onProofDebugExportRequest( - callback: OnProofDebugExportRequestListener - ) { + public onProofDebugExportRequest(callback: OnProofDebugExportRequestListener) { this.proofDebugExportCallback = callback; } diff --git a/app/src/main.ts b/app/src/main.ts index 1862eb8..31bf2f7 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -8,10 +8,7 @@ import fs, { mkdirSync, writeFile } from "node:fs"; import os from "node:os"; import { createServer, type Socket, type Server } from "node:net"; import { WALLET_DATA_DIR, getSocketPath } from "./shared/paths"; -import { - checkSystemWideManifest, - installNativeMessagingManifests, -} from "./native-messaging"; +import { checkSystemWideManifest, installNativeMessagingManifests } from "./native-messaging"; // Setup logging to file for debugging mkdirSync(WALLET_DATA_DIR, { recursive: true }); @@ -52,36 +49,34 @@ if (app.isPackaged) { if (process.env.BB_WASM_PATH?.includes("__RESOURCES_PATH__")) { process.env.BB_WASM_PATH = process.env.BB_WASM_PATH.replace( "__RESOURCES_PATH__", - resourcesPath + resourcesPath, ); } if (process.env.BB_BINARY_PATH?.includes("__RESOURCES_PATH__")) { process.env.BB_BINARY_PATH = process.env.BB_BINARY_PATH.replace( "__RESOURCES_PATH__", - resourcesPath + resourcesPath, ); } if (process.env.BB_NAPI_PATH?.includes("__RESOURCES_PATH__")) { process.env.BB_NAPI_PATH = process.env.BB_NAPI_PATH.replace( "__RESOURCES_PATH__", - resourcesPath + resourcesPath, ); } if (process.env.NATIVE_HOST_PATH?.includes("__RESOURCES_PATH__")) { process.env.NATIVE_HOST_PATH = process.env.NATIVE_HOST_PATH.replace( "__RESOURCES_PATH__", - resourcesPath + resourcesPath, ); } // Verify binary exists and is executable try { const stats = fs.statSync(process.env.BB_BINARY_PATH!); - console.log( - `BB binary found: ${stats.size} bytes, mode: ${stats.mode.toString(8)}` - ); + console.log(`BB binary found: ${stats.size} bytes, mode: ${stats.mode.toString(8)}`); } catch (error: any) { console.error("BB binary check failed:", error.message); } @@ -272,9 +267,7 @@ const createWindow = () => { if (MAIN_WINDOW_VITE_DEV_SERVER_URL) { mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL); } else { - mainWindow.loadFile( - join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`) - ); + mainWindow.loadFile(join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`)); } // Open the DevTools. @@ -298,12 +291,9 @@ app.on("ready", async () => { installNativeMessagingManifests(nativeHostPath, chromeExtensionId); createWindow(); - const { port1: externalPort1, port2: externalPort2 } = - new MessageChannelMain(); - const { port1: internalPort1, port2: internalPort2 } = - new MessageChannelMain(); - const { port1: walletLogPort1, port2: walletLogPort2 } = - new MessageChannelMain(); + const { port1: externalPort1, port2: externalPort2 } = new MessageChannelMain(); + const { port1: internalPort1, port2: internalPort2 } = new MessageChannelMain(); + const { port1: walletLogPort1, port2: walletLogPort2 } = new MessageChannelMain(); // Create IPC server for native messaging host communication const ipcServer = createIpcServer(externalPort1); @@ -324,11 +314,7 @@ app.on("ready", async () => { env: filteredEnv, }); - wallet.postMessage({ type: "ports" }, [ - externalPort2, - internalPort1, - walletLogPort1, - ]); + wallet.postMessage({ type: "ports" }, [externalPort2, internalPort1, walletLogPort1]); wallet.on("exit", () => { console.error("wallet process died"); @@ -372,6 +358,8 @@ app.on("ready", async () => { "createAccount", "deployAccount", "getInteractions", + "deleteInteraction", + "clearInteractions", "getExecutionTrace", "resolveAuthorization", "listAuthorizedApps", diff --git a/app/src/native-host/index.ts b/app/src/native-host/index.ts index 93b90f2..06316af 100644 --- a/app/src/native-host/index.ts +++ b/app/src/native-host/index.ts @@ -45,10 +45,7 @@ async function main(): Promise { status: "connected", }); } catch (err) { - const errorMsg = - err instanceof Error - ? err.message - : "Failed to connect to Demo Wallet app"; + const errorMsg = err instanceof Error ? err.message : "Failed to connect to Demo Wallet app"; log(`Failed to connect: ${errorMsg}`); // Send error back to extension before exiting stdio.send({ diff --git a/app/src/native-host/ipc-client.ts b/app/src/native-host/ipc-client.ts index bab227f..ca27d71 100644 --- a/app/src/native-host/ipc-client.ts +++ b/app/src/native-host/ipc-client.ts @@ -84,8 +84,8 @@ export class IpcClient { } else { reject( new Error( - `Failed to connect to Electron app after ${this.maxReconnectAttempts} attempts` - ) + `Failed to connect to Electron app after ${this.maxReconnectAttempts} attempts`, + ), ); } } else { diff --git a/app/src/native-host/stdio.ts b/app/src/native-host/stdio.ts index a78e720..6efb4a8 100644 --- a/app/src/native-host/stdio.ts +++ b/app/src/native-host/stdio.ts @@ -76,7 +76,9 @@ export class StdioTransport { chunks.push(json.slice(i, i + effectiveChunkSize)); } - console.error(`Chunking message: ${json.length} bytes into ${chunks.length} chunks (id: ${chunkId})`); + console.error( + `Chunking message: ${json.length} bytes into ${chunks.length} chunks (id: ${chunkId})`, + ); // Send each chunk for (let i = 0; i < chunks.length; i++) { diff --git a/app/src/native-messaging.ts b/app/src/native-messaging.ts index 458b215..10d7075 100644 --- a/app/src/native-messaging.ts +++ b/app/src/native-messaging.ts @@ -8,6 +8,7 @@ */ import { app } from "electron"; +import { execSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import { join } from "node:path"; @@ -16,7 +17,6 @@ import { join } from "node:path"; export const NATIVE_HOST_NAME = "com.aztec.keychain"; const FIREFOX_EXTENSION_ID = "aztec-keychain@aztec.network"; - /** * Get the system-wide native messaging manifest path for Chrome. * This is the location Chrome checks when running with a custom --user-data-dir @@ -65,46 +65,46 @@ export function checkSystemWideManifest(nativeHostPath: string, chromeExtensionI allowed_origins: [`chrome-extension://${extensionId}/`], }, null, - 2 + 2, ); console.error(""); console.error( - "╔══════════════════════════════════════════════════════════════════════════════╗" + "╔══════════════════════════════════════════════════════════════════════════════╗", ); console.error( - "║ ║" + "║ ║", ); console.error( - "║ ⚠️ NATIVE MESSAGING MANIFEST NOT FOUND IN SYSTEM-WIDE LOCATION ⚠️ ║" + "║ ⚠️ NATIVE MESSAGING MANIFEST NOT FOUND IN SYSTEM-WIDE LOCATION ⚠️ ║", ); console.error( - "║ ║" + "║ ║", ); console.error( - "║ When using WXT dev mode, Chrome runs with a custom --user-data-dir and ║" + "║ When using WXT dev mode, Chrome runs with a custom --user-data-dir and ║", ); console.error( - "║ only checks the SYSTEM-WIDE location for native messaging hosts. ║" + "║ only checks the SYSTEM-WIDE location for native messaging hosts. ║", ); console.error( - "║ ║" + "║ ║", ); console.error( - "║ The manifest must be installed at: ║" + "║ The manifest must be installed at: ║", ); console.error(`║ ${manifestPath.padEnd(72)}║`); console.error( - "║ ║" + "║ ║", ); console.error( - "║ Run the command below to install it. ║" + "║ Run the command below to install it. ║", ); console.error( - "║ ║" + "║ ║", ); console.error( - "╚══════════════════════════════════════════════════════════════════════════════╝" + "╚══════════════════════════════════════════════════════════════════════════════╝", ); console.error(""); console.error("Copy and paste this command:"); @@ -130,34 +130,26 @@ function getManifestPaths(): { firefox: string[]; chrome: string[] } { switch (process.platform) { case "darwin": - paths.firefox.push( - join(home, "Library/Application Support/Mozilla/NativeMessagingHosts") - ); - paths.chrome.push( - join( - home, - "Library/Application Support/Google/Chrome/NativeMessagingHosts" - ) - ); + paths.firefox.push(join(home, "Library/Application Support/Mozilla/NativeMessagingHosts")); paths.chrome.push( - join(home, "Library/Application Support/Chromium/NativeMessagingHosts") + join(home, "Library/Application Support/Google/Chrome/NativeMessagingHosts"), ); + paths.chrome.push(join(home, "Library/Application Support/Chromium/NativeMessagingHosts")); break; case "linux": paths.firefox.push(join(home, ".mozilla/native-messaging-hosts")); - paths.chrome.push( - join(home, ".config/google-chrome/NativeMessagingHosts") - ); + paths.chrome.push(join(home, ".config/google-chrome/NativeMessagingHosts")); paths.chrome.push(join(home, ".config/chromium/NativeMessagingHosts")); break; - case "win32": + case "win32": { // Windows manifests go in AppData, registry points to them const appData = join(home, "AppData", "Local", "AztecKeychain"); paths.firefox.push(appData); paths.chrome.push(appData); break; + } } return paths; @@ -179,10 +171,7 @@ function createFirefoxManifest(nativeHostPath: string): object { /** * Create Chrome native messaging manifest. */ -function createChromeManifest( - nativeHostPath: string, - extensionId: string -): object { +function createChromeManifest(nativeHostPath: string, extensionId: string): object { return { name: NATIVE_HOST_NAME, description: "Demo Wallet Native Messaging Host", @@ -195,21 +184,13 @@ function createChromeManifest( /** * Install Windows registry keys for native messaging. */ -function installWindowsRegistryKeys(paths: { - firefox: string[]; - chrome: string[]; -}): void { - const { execSync } = require("child_process"); - +function installWindowsRegistryKeys(paths: { firefox: string[]; chrome: string[] }): void { // Firefox registry key - const firefoxManifestPath = join( - paths.firefox[0], - `${NATIVE_HOST_NAME}.json` - ); + const firefoxManifestPath = join(paths.firefox[0], `${NATIVE_HOST_NAME}.json`); try { execSync( `reg add "HKCU\\Software\\Mozilla\\NativeMessagingHosts\\${NATIVE_HOST_NAME}" /ve /t REG_SZ /d "${firefoxManifestPath}" /f`, - { stdio: "pipe" } + { stdio: "pipe" }, ); console.log("Installed Firefox registry key"); } catch (err: any) { @@ -218,14 +199,11 @@ function installWindowsRegistryKeys(paths: { // Chrome registry key if (process.env.CHROME_EXTENSION_ID) { - const chromeManifestPath = join( - paths.chrome[0], - `${NATIVE_HOST_NAME}.json` - ); + const chromeManifestPath = join(paths.chrome[0], `${NATIVE_HOST_NAME}.json`); try { execSync( `reg add "HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${NATIVE_HOST_NAME}" /ve /t REG_SZ /d "${chromeManifestPath}" /f`, - { stdio: "pipe" } + { stdio: "pipe" }, ); console.log("Installed Chrome registry key"); } catch (err: any) { @@ -238,7 +216,10 @@ function installWindowsRegistryKeys(paths: { * Install native messaging manifests for all supported browsers. * Called on app startup to ensure the extension can communicate with the app. */ -export function installNativeMessagingManifests(nativeHostPath: string, chromeExtensionId: string): void { +export function installNativeMessagingManifests( + nativeHostPath: string, + chromeExtensionId: string, +): void { // Verify native host binary exists if (!fs.existsSync(nativeHostPath)) { console.error(`Native host binary not found: ${nativeHostPath}`); @@ -262,19 +243,13 @@ export function installNativeMessagingManifests(nativeHostPath: string, chromeEx fs.writeFileSync(manifestPath, JSON.stringify(firefoxManifest, null, 2)); console.log(`Installed Firefox manifest: ${manifestPath}`); } catch (err: any) { - console.error( - `Failed to install Firefox manifest to ${dir}:`, - err.message - ); + console.error(`Failed to install Firefox manifest to ${dir}:`, err.message); } } // Install Chrome manifests (if extension ID is configured) if (chromeExtensionId) { - const chromeManifest = createChromeManifest( - nativeHostPath, - chromeExtensionId - ); + const chromeManifest = createChromeManifest(nativeHostPath, chromeExtensionId); for (const dir of paths.chrome) { try { if (!fs.existsSync(dir)) { @@ -284,19 +259,12 @@ export function installNativeMessagingManifests(nativeHostPath: string, chromeEx fs.writeFileSync(manifestPath, JSON.stringify(chromeManifest, null, 2)); console.log(`Installed Chrome manifest: ${manifestPath}`); } catch (err: any) { - console.error( - `Failed to install Chrome manifest to ${dir}:`, - err.message - ); + console.error(`Failed to install Chrome manifest to ${dir}:`, err.message); } } } else { - console.log( - "Chrome extension ID not configured, skipping Chrome manifest installation." - ); - console.log( - "In production, set CHROME_EXTENSION_ID env var when building." - ); + console.log("Chrome extension ID not configured, skipping Chrome manifest installation."); + console.log("In production, set CHROME_EXTENSION_ID env var when building."); } // Windows: Add registry keys diff --git a/app/src/ui/utils/wallet-api.ts b/app/src/ui/utils/wallet-api.ts index 170bab9..8316fac 100644 --- a/app/src/ui/utils/wallet-api.ts +++ b/app/src/ui/utils/wallet-api.ts @@ -9,8 +9,7 @@ export class WalletApi { private constructor(chainId: Fr, version: Fr) { const safeCallback = (callback: any) => (eventData: any) => { if (eventData.chainInfo) { - const { chainId: eventChainId, version: eventVersion } = - eventData.chainInfo; + const { chainId: eventChainId, version: eventVersion } = eventData.chainInfo; const currentChainId = chainId.toString(); const currentVersion = version.toString(); @@ -38,9 +37,7 @@ export class WalletApi { args.unshift(chainId, version); const safeArgs = jsonStringify(args); const result = await window.walletAPI[prop](safeArgs); - return InternalWalletInterfaceSchema[ - prop.toString() as keyof InternalWalletInterface - ] + return InternalWalletInterfaceSchema[prop.toString() as keyof InternalWalletInterface] .returnType() .parseAsync(result); }; @@ -50,9 +47,7 @@ export class WalletApi { }; } else if (prop.toString() === "onAuthorizationRequest") { return (callback: any) => { - return window.walletAPI.onAuthorizationRequest( - safeCallback(callback) - ); + return window.walletAPI.onAuthorizationRequest(safeCallback(callback)); }; } else if (prop.toString() === "onProofDebugExportRequest") { return (callback: any) => { @@ -67,7 +62,7 @@ export class WalletApi { throw new Error(`Invalid method ${prop.toString()}`); } }, - } + }, ) as unknown as InternalWalletInterface; } diff --git a/app/src/utils/logger.ts b/app/src/utils/logger.ts index 94229ff..a56f7cd 100644 --- a/app/src/utils/logger.ts +++ b/app/src/utils/logger.ts @@ -2,22 +2,10 @@ import { createLogger, type Logger } from "@aztec/aztec.js/log"; import type { MessagePortMain } from "electron"; import { jsonStringify } from "@aztec/foundation/json-rpc"; -const logLevel = [ - "silent", - "fatal", - "error", - "warn", - "info", - "verbose", - "debug", - "trace", -] as const; +const logLevel = ["silent", "fatal", "error", "warn", "info", "verbose", "debug", "trace"] as const; type LogLevel = (typeof logLevel)[number]; -export function createProxyLogger( - prefix: string, - logPort: MessagePortMain -): Logger { +export function createProxyLogger(prefix: string, logPort: MessagePortMain): Logger { return new Proxy(createLogger(prefix), { get: (target, prop) => { if (logLevel.includes(prop as (typeof logLevel)[number])) { diff --git a/app/src/workers/wallet-worker.ts b/app/src/workers/wallet-worker.ts index 76fef9f..56e68f4 100644 --- a/app/src/workers/wallet-worker.ts +++ b/app/src/workers/wallet-worker.ts @@ -13,10 +13,7 @@ import { getNetworkByChainId, } from "@demo-wallet/shared/core"; import { createProxyLogger } from "../utils/logger.ts"; -import type { - AuthorizationRequest, - AuthorizationResponse, -} from "@demo-wallet/shared/core"; +import type { AuthorizationRequest, AuthorizationResponse } from "@demo-wallet/shared/core"; import { createPXE, getPXEConfig, @@ -48,10 +45,7 @@ type SessionData = { db: any; pendingAuthorizations: Map; }>; - wallets: Map< - string, - Promise<{ external: ExternalWallet; internal: InternalWallet }> - >; + wallets: Map>; }; const RUNNING_SESSIONS = new Map(); @@ -62,10 +56,7 @@ async function init( internalPort: MessagePortMain, logPort: MessagePortMain, ) { - const network = getNetworkByChainId( - chainInfo.chainId.toNumber(), - chainInfo.version.toNumber(), - ); + const network = getNetworkByChainId(chainInfo.chainId.toNumber(), chainInfo.version.toNumber()); if (!network) { throw new Error( `Unknown network: chainId=${chainInfo.chainId.toNumber()}, version=${chainInfo.version.toNumber()}`, @@ -126,11 +117,7 @@ async function init( ); const db = WalletDB.init(walletDBStore, walletDBLogger); - const pxe = await createPXE( - node, - { ...getPXEConfig(), ...configOverrides }, - options, - ); + const pxe = await createPXE(node, { ...getPXEConfig(), ...configOverrides }, options); const pendingAuthorizations = new Map< string, @@ -156,14 +143,8 @@ async function init( // Now create wallet instances for this specific appId if they don't exist if (!walletExists) { const internalInit = async () => { - const externalWalletLogger = createProxyLogger( - `wallet:external:${appId}`, - logPort, - ); - const internalWalletLogger = createProxyLogger( - `wallet:internal:${appId}`, - logPort, - ); + const externalWalletLogger = createProxyLogger(`wallet:external:${appId}`, logPort); + const internalWalletLogger = createProxyLogger(`wallet:internal:${appId}`, logPort); // Create both wallet instances sharing the same db, pxe and authorization logic const externalWallet = new ExternalWallet( @@ -199,35 +180,29 @@ async function init( }); }); - wallet.addEventListener( - "authorization-request", - (event: CustomEvent) => { - internalPort.postMessage({ - origin: "wallet", - type: "authorization-request", - content: event.detail, - chainInfo: { - chainId: chainInfo.chainId.toString(), - version: chainInfo.version.toString(), - }, - }); - }, - ); + wallet.addEventListener("authorization-request", (event: CustomEvent) => { + internalPort.postMessage({ + origin: "wallet", + type: "authorization-request", + content: event.detail, + chainInfo: { + chainId: chainInfo.chainId.toString(), + version: chainInfo.version.toString(), + }, + }); + }); - wallet.addEventListener( - "proof-debug-export-request", - (event: CustomEvent) => { - internalPort.postMessage({ - origin: "wallet", - type: "proof-debug-export-request", - content: event.detail, - chainInfo: { - chainId: chainInfo.chainId.toString(), - version: chainInfo.version.toString(), - }, - }); - }, - ); + wallet.addEventListener("proof-debug-export-request", (event: CustomEvent) => { + internalPort.postMessage({ + origin: "wallet", + type: "proof-debug-export-request", + content: event.detail, + chainInfo: { + chainId: chainInfo.chainId.toString(), + version: chainInfo.version.toString(), + }, + }); + }); }; setupWalletEvents(externalWallet); @@ -256,14 +231,14 @@ const handleEvent = async ( if (!schemaHasMethod(schema, type)) { throw new Error(`Unknown method: ${type}`); } - const sanitizedArgs = await parseWithOptionals( - args, - schema[type].parameters(), - ); + const sanitizedArgs = await parseWithOptionals(args, schema[type].parameters()); let result; let error; try { - result = await wallet[type](...sanitizedArgs); + const method = (wallet as unknown as Record Promise>)[ + type + ].bind(wallet); + result = await method(...sanitizedArgs); } catch (err: any) { userLog.error(`Error handling ${type}: ${err.message}`); if (err.stack) { @@ -311,7 +286,7 @@ async function main() { let messageContent; try { messageContent = JSON.parse(content); - } catch (err) { + } catch { userLog.debug(`Unable to parse message ${content}`); return; } @@ -328,24 +303,10 @@ async function main() { logPort, ); // Use external wallet for external requests - handleEvent( - externalPort, - wallets.external, - WalletSchema, - type, - messageId, - args, - userLog, - ); + handleEvent(externalPort, wallets.external, WalletSchema, type, messageId, args, userLog); }); internalPort.on("message", async (event) => { - const { - type, - messageId, - args, - appId: originalAppId, - chainInfo, - } = event.data; + const { type, messageId, args, appId: originalAppId, chainInfo } = event.data; if (!messageId) { return; } @@ -376,9 +337,7 @@ async function main() { // Use internal wallet for internal requests, except when handling // resolveAuthorization (which was always originated by the external one) const wallet = - type === "resolveAuthorization" && appId !== "this" - ? wallets.external - : wallets.internal; + type === "resolveAuthorization" && appId !== "this" ? wallets.external : wallets.internal; handleEvent( internalPort, wallet, diff --git a/app/tsconfig.json b/app/tsconfig.json index 0775267..d196412 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -22,5 +22,5 @@ "allowArbitraryExtensions": true, "allowSyntheticDefaultImports": true }, - "include": ["src/**/*", "wallet-api.d.ts"] + "include": ["src/**/*", "wallet-api.d.ts", "forge.env.d.ts"] } diff --git a/app/vite.main.config.ts b/app/vite.main.config.ts index 4fe9b6b..5d67047 100644 --- a/app/vite.main.config.ts +++ b/app/vite.main.config.ts @@ -57,9 +57,7 @@ const BB_WASM_PATH = isDev ? resolve(__dirname, "./bb/barretenberg-threads.wasm.gz") : "__RESOURCES_PATH__/bb/barretenberg-threads.wasm.gz"; -const BB_BINARY_PATH = isDev - ? resolve(__dirname, "./bb/bb") - : "__RESOURCES_PATH__/bb/bb"; +const BB_BINARY_PATH = isDev ? resolve(__dirname, "./bb/bb") : "__RESOURCES_PATH__/bb/bb"; const BB_NAPI_PATH = isDev ? resolve(__dirname, "./bb/nodejs_module.node") @@ -67,14 +65,10 @@ const BB_NAPI_PATH = isDev const platform = process.platform; const arch = process.arch; -const nativeHostBinaryName = - platform === "win32" ? "native-host.exe" : "native-host"; +const nativeHostBinaryName = platform === "win32" ? "native-host.exe" : "native-host"; const NATIVE_HOST_PATH = isDev - ? resolve( - __dirname, - `./dist/native-host/${platform}-${arch}/${nativeHostBinaryName}`, - ) + ? resolve(__dirname, `./dist/native-host/${platform}-${arch}/${nativeHostBinaryName}`) : `__RESOURCES_PATH__/${platform}-${arch}/${nativeHostBinaryName}`; // https://vitejs.dev/config diff --git a/app/vite.renderer.config.ts b/app/vite.renderer.config.ts index 417d4d7..2d4855c 100644 --- a/app/vite.renderer.config.ts +++ b/app/vite.renderer.config.ts @@ -6,12 +6,9 @@ import { PolyfillOptions, nodePolyfills } from "vite-plugin-node-polyfills"; const nodePolyfillsFix = (options?: PolyfillOptions | undefined): Plugin => { return { ...nodePolyfills(options), - /* @ts-ignore */ + // @ts-expect-error - resolveId signature mismatch with vite-plugin-node-polyfills spread type resolveId(source: string) { - const m = - /^vite-plugin-node-polyfills\/shims\/(buffer|global|process)$/.exec( - source, - ); + const m = /^vite-plugin-node-polyfills\/shims\/(buffer|global|process)$/.exec(source); if (m) { return `./node_modules/vite-plugin-node-polyfills/shims/${m[1]}/dist/index.cjs`; } @@ -30,14 +27,8 @@ export default defineConfig({ // app bundle (applying the SWC/JSX transform) rather than as externals // served raw via /@fs/node_modules/. alias: { - "@demo-wallet/shared/ui": resolve( - import.meta.dirname, - "../shared/src/ui.ts", - ), - "@demo-wallet/shared/core": resolve( - import.meta.dirname, - "../shared/src/core.ts", - ), + "@demo-wallet/shared/ui": resolve(import.meta.dirname, "../shared/src/ui.ts"), + "@demo-wallet/shared/core": resolve(import.meta.dirname, "../shared/src/core.ts"), }, }, plugins: [ diff --git a/app/wallet-api.d.ts b/app/wallet-api.d.ts index cbeb84e..b94a78b 100644 --- a/app/wallet-api.d.ts +++ b/app/wallet-api.d.ts @@ -1,8 +1,4 @@ -import type { - InternalWalletInterface, - OnAuthorizationRequestListener, - OnWalletUpdateListener, -} from "./src/wallet-internal-proxy.ts"; +import type { InternalWalletInterface } from "./src/wallet-internal-proxy.ts"; declare global { interface Window { walletAPI: InternalWalletInterface; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..f43296f --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,57 @@ +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; +import eslintConfigPrettier from "eslint-config-prettier/flat"; + +export default tseslint.config( + // Ignore build outputs across all packages + { ignores: ["**/dist/**", "**/out/**", "**/node_modules/**", "**/.turbo/**", "**/.vite/**"] }, + + // Base: all TypeScript files + { + extends: [js.configs.recommended, ...tseslint.configs.recommended, eslintConfigPrettier], + files: ["**/*.{ts,tsx}"], + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" }, + ], + "@typescript-eslint/no-explicit-any": "warn", + }, + }, + + // React packages (app + web + shared UI) + { + files: ["app/src/**/*.{ts,tsx}", "web/src/**/*.{ts,tsx}", "shared/src/ui/**/*.{ts,tsx}"], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + "react-refresh/only-export-components": ["warn", { allowConstantExport: true }], + }, + }, + + // Shared package: Node-compatible, no browser globals + { + files: ["shared/src/**/*.{ts,tsx}"], + languageOptions: { + globals: globals.node, + }, + }, + + // Worker files: Node environment + { + files: ["app/src/workers/**/*.ts", "app/src/native-host/**/*.ts"], + languageOptions: { + globals: globals.node, + }, + }, +); diff --git a/extension/entrypoints/background.ts b/extension/entrypoints/background.ts index 4a6370d..d8ad349 100644 --- a/extension/entrypoints/background.ts +++ b/extension/entrypoints/background.ts @@ -1,8 +1,5 @@ import { BackgroundConnectionHandler } from "@aztec/wallet-sdk/extension/handlers"; -import { - WalletMessageType, - type WalletResponse, -} from "@aztec/wallet-sdk/types"; +import { WalletMessageType, type WalletResponse } from "@aztec/wallet-sdk/types"; import { ChunkReassembler } from "../utils/chunk_reassembler"; // Wallet configuration @@ -60,8 +57,7 @@ export default defineBackground(async () => { return []; } const result = await browser.storage.local.get(REMEMBERED_APPS_KEY); - const apps = - (result[REMEMBERED_APPS_KEY] as RememberedApp[] | undefined) ?? []; + const apps = (result[REMEMBERED_APPS_KEY] as RememberedApp[] | undefined) ?? []; // Filter out old format entries that don't have chainId/version return apps.filter((app) => app.chainId && app.version); } @@ -134,8 +130,7 @@ export default defineBackground(async () => { }, { sendToTab: (tabId, message) => browser.tabs.sendMessage(tabId, message), - addContentListener: (handler) => - browser.runtime.onMessage.addListener(handler), + addContentListener: (handler) => browser.runtime.onMessage.addListener(handler), }, { onPendingDiscovery: (discovery) => { @@ -148,16 +143,9 @@ export default defineBackground(async () => { const version = discovery.chainInfo.version.toString(); // Check if app is remembered for this specific network - if so, auto-approve - isAppRemembered( - discovery.appId, - discovery.origin, - chainId, - version, - ).then((remembered) => { + isAppRemembered(discovery.appId, discovery.origin, chainId, version).then((remembered) => { if (remembered) { - const success = sessionHandler.approveDiscovery( - discovery.requestId, - ); + const success = sessionHandler.approveDiscovery(discovery.requestId); if (success) { console.log( `Auto-approved discovery for remembered app: ${discovery.appId} @ ${discovery.origin} (chain: ${chainId})`, @@ -382,11 +370,7 @@ export default defineBackground(async () => { // Handle messages from popup (content script messages are handled by SDK) browser.runtime.onMessage.addListener((event: any, _sender, sendResponse) => { if (event.origin === "popup") { - return handlePopupMessage( - event.type as PopupMessageType, - event, - sendResponse, - ); + return handlePopupMessage(event.type as PopupMessageType, event, sendResponse); } }); @@ -412,9 +396,7 @@ export default defineBackground(async () => { const sessionId = pendingRequests.get(response.messageId); if (!sessionId) { - console.error( - `No pending request found for messageId ${response.messageId}`, - ); + console.error(`No pending request found for messageId ${response.messageId}`); return; } pendingRequests.delete(response.messageId); @@ -429,9 +411,7 @@ export default defineBackground(async () => { nativePort.onDisconnect.addListener(() => { const error = browser.runtime.lastError; if (error) { - console.error( - `Native host disconnected with error: ${error.message}`, - ); + console.error(`Native host disconnected with error: ${error.message}`); } else { console.log("Native host disconnected"); } diff --git a/extension/entrypoints/content.ts b/extension/entrypoints/content.ts index 93bc9cc..ebf7dbd 100644 --- a/extension/entrypoints/content.ts +++ b/extension/entrypoints/content.ts @@ -16,8 +16,7 @@ export default defineContentScript({ main() { const handler = new ContentScriptConnectionHandler({ sendToBackground: (message) => browser.runtime.sendMessage(message), - addBackgroundListener: (listener) => - browser.runtime.onMessage.addListener(listener), + addBackgroundListener: (listener) => browser.runtime.onMessage.addListener(listener), }); handler.start(); diff --git a/extension/entrypoints/popup/App.tsx b/extension/entrypoints/popup/App.tsx index 4abab10..f945b1a 100644 --- a/extension/entrypoints/popup/App.tsx +++ b/extension/entrypoints/popup/App.tsx @@ -75,18 +75,16 @@ function getHostname(origin: string): string { function EmojiGrid({ emojis }: { emojis: string }): JSX.Element { // Split emojis into array (handles multi-byte emoji correctly) const emojiArray = [...emojis]; - const rows = [ - emojiArray.slice(0, 3), - emojiArray.slice(3, 6), - emojiArray.slice(6, 9), - ]; + const rows = [emojiArray.slice(0, 3), emojiArray.slice(3, 6), emojiArray.slice(6, 9)]; return (
{rows.map((row, i) => (
{row.map((emoji, j) => ( - {emoji} + + {emoji} + ))}
))} @@ -207,7 +205,12 @@ function App() { await refreshData(); }; - const handleForgetApp = async (appId: string, appOrigin: string, chainId: string, version: string) => { + const handleForgetApp = async ( + appId: string, + appOrigin: string, + chainId: string, + version: string, + ) => { await browser.runtime.sendMessage({ origin: "popup", type: "forget-app", @@ -278,12 +281,7 @@ function App() {
- + +
{s.appId || hostname} @@ -444,10 +445,16 @@ function App() { via {hostname} )} - + Chain: {hexToDecimal(s.chainId)} - + Version: {hexToDecimal(s.version)} @@ -485,30 +492,43 @@ function App() { {rememberedApps.map((app) => { const hostname = getHostname(app.origin); const hasActiveSession = sessions.some( - (s) => s.appId === app.appId && s.origin === app.origin + (s) => s.appId === app.appId && s.origin === app.origin, ); return ( -
+
{app.appId} {hasActiveSession && ( - + + ● + )} via {hostname} - + Chain: {hexToDecimal(app.chainId)} - + Version: {hexToDecimal(app.version)}
- diff --git a/shared/src/ui/components/dialogs/EditAddressBookAuthorizationDialog.tsx b/shared/src/ui/components/dialogs/EditAddressBookAuthorizationDialog.tsx index 4629450..35eaf5c 100644 --- a/shared/src/ui/components/dialogs/EditAddressBookAuthorizationDialog.tsx +++ b/shared/src/ui/components/dialogs/EditAddressBookAuthorizationDialog.tsx @@ -57,16 +57,12 @@ export function EditAddressBookAuthorizationDialog({ const contactsWithSelection = contacts.map((contact) => { const isSelected = currentAddresses.has(contact.item.toString()); - const currentContact = currentContacts.find( - (cc) => cc.item === 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, + displayAlias: isSelected ? currentContact?.alias || contact.alias : contact.alias, selected: isSelected, }; }); @@ -81,16 +77,16 @@ export function EditAddressBookAuthorizationDialog({ const handleToggleContact = (address: string) => { setAllContacts((prev) => prev.map((contact) => - contact.address === address ? { ...contact, selected: !contact.selected } : 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 - ) + contact.address === address ? { ...contact, displayAlias: newAlias } : contact, + ), ); }; @@ -124,8 +120,7 @@ export function EditAddressBookAuthorizationDialog({ Edit Address Book Authorization for {appId} - Select which contacts this app can access and customize the aliases - shown to the app. + Select which contacts this app can access and customize the aliases shown to the app. {error && ( @@ -161,9 +156,7 @@ export function EditAddressBookAuthorizationDialog({ } label={ - - {contact.originalAlias} - + {contact.originalAlias} - handleAliasChange(contact.address, e.target.value) - } + onChange={(e) => handleAliasChange(contact.address, e.target.value)} sx={{ mt: 1, ml: 4, - '& .MuiInputBase-input': { - overflow: 'hidden', - textOverflow: 'ellipsis' - } + "& .MuiInputBase-input": { + overflow: "hidden", + textOverflow: "ellipsis", + }, }} helperText="The app will see this contact under this alias" /> @@ -209,11 +200,7 @@ export function EditAddressBookAuthorizationDialog({ - diff --git a/shared/src/ui/components/dialogs/ExecutionTraceDialog.tsx b/shared/src/ui/components/dialogs/ExecutionTraceDialog.tsx index 2942eb8..4fdcdcf 100644 --- a/shared/src/ui/components/dialogs/ExecutionTraceDialog.tsx +++ b/shared/src/ui/components/dialogs/ExecutionTraceDialog.tsx @@ -35,7 +35,9 @@ export function ExecutionTraceDialog({ if (!trace) return null; - const isFromZero = from === "NO_FROM" || (from && from.startsWith("0x") && AztecAddress.fromString(from).equals(AztecAddress.ZERO)); + const isFromZero = + from === "NO_FROM" || + (from && from.startsWith("0x") && AztecAddress.fromString(from).equals(AztecAddress.ZERO)); const hasEmbeddedFeePayer = !!embeddedPaymentMethodFeePayer; return ( @@ -59,18 +61,15 @@ export function ExecutionTraceDialog({ }} > Execution Trace - t.palette.grey[500] }} - > + t.palette.grey[500] }}> - + {isFromZero && ( - This transaction uses an external entrypoint and does not execute from any of your accounts. + This transaction uses an external entrypoint and does not execute from any of your + accounts. )} {hasEmbeddedFeePayer && ( diff --git a/shared/src/ui/components/dialogs/ProofDebugExportDialog.tsx b/shared/src/ui/components/dialogs/ProofDebugExportDialog.tsx index 78c3938..b097734 100644 --- a/shared/src/ui/components/dialogs/ProofDebugExportDialog.tsx +++ b/shared/src/ui/components/dialogs/ProofDebugExportDialog.tsx @@ -56,8 +56,8 @@ export function ProofDebugExportDialog({ - The proving process failed. You can export debug data to help the - development team investigate this issue. + The proving process failed. You can export debug data to help the development team + investigate this issue. @@ -65,8 +65,7 @@ export function ProofDebugExportDialog({ Privacy Warning - The exported file contains sensitive information{" "} - including: + The exported file contains sensitive information including:
  • @@ -75,34 +74,26 @@ export function ProofDebugExportDialog({
  • - - Circuit bytecode and verification keys - + Circuit bytecode and verification keys
  • - - Function names and execution flow - + Function names and execution flow
  • - Only share this file with trusted parties (e.g., the Aztec development - team) for debugging purposes. + Only share this file with trusted parties (e.g., the Aztec development team) for debugging + purposes. setAcknowledged(e.target.checked)} - /> + setAcknowledged(e.target.checked)} /> } label={ - I understand this file contains sensitive data and I consent to - exporting it + I understand this file contains sensitive data and I consent to exporting it } /> diff --git a/shared/src/ui/components/sections/accounts/components/AccountBox.tsx b/shared/src/ui/components/sections/accounts/components/AccountBox.tsx index 66e574a..5ceb630 100644 --- a/shared/src/ui/components/sections/accounts/components/AccountBox.tsx +++ b/shared/src/ui/components/sections/accounts/components/AccountBox.tsx @@ -96,11 +96,7 @@ export function AccountBox({ account, QRButton = false, onDeploy }: AccountBoxPr
    {!account.deployed && onDeploy && ( - onDeploy(account.item)} - color="primary" - > + onDeploy(account.item)} color="primary"> diff --git a/shared/src/ui/components/sections/accounts/index.tsx b/shared/src/ui/components/sections/accounts/index.tsx index ac08545..a4b52db 100644 --- a/shared/src/ui/components/sections/accounts/index.tsx +++ b/shared/src/ui/components/sections/accounts/index.tsx @@ -53,7 +53,7 @@ export function AccountsManager() { "ecdsasecp256r1", Fr.random(), Fr.random(), - randomBytes(32) + randomBytes(32), ); await loadAccounts(); } catch (err: any) { @@ -72,7 +72,17 @@ export function AccountsManager() { if (loading) { return ( - + Loading accounts... @@ -96,19 +106,10 @@ export function AccountsManager() { Create an account in the standalone wallet first. - + Open wallet - @@ -148,11 +149,7 @@ export function AccountsManager() { onClose={() => setError(null)} anchorOrigin={{ vertical: "bottom", horizontal: "center" }} > - setError(null)} - severity="error" - sx={{ width: "100%" }} - > + setError(null)} severity="error" sx={{ width: "100%" }}> {error} diff --git a/shared/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx b/shared/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx index f3cda11..a367fbe 100644 --- a/shared/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx +++ b/shared/src/ui/components/sections/authorized-apps/components/AppAuthorizationCard.tsx @@ -1,26 +1,22 @@ import { useContext, useEffect, useState, useRef, useCallback } from "react"; -import { - Card, - CardContent, - Typography, - Box, - IconButton, - Tooltip, - Alert, -} from "@mui/material"; -import { - Apps as AppsIcon, - Block as RevokeIcon, -} from "@mui/icons-material"; +import { Card, CardContent, Typography, Box, IconButton, Tooltip, Alert } from "@mui/material"; +import { Apps as AppsIcon, Block as RevokeIcon } from "@mui/icons-material"; import { WalletContext } from "../../../../renderer"; -import type { GrantedCapability, AppCapabilities, CAPABILITY_VERSION, Capability } from "@aztec/aztec.js/wallet"; -import type { AuthorizationItem, RequestCapabilitiesParams } from "../../../../../wallet/types/authorization"; +import type { + GrantedCapability, + AppCapabilities, + CAPABILITY_VERSION, + Capability, +} from "@aztec/aztec.js/wallet"; +import type { + AuthorizationItem, + RequestCapabilitiesParams, +} from "../../../../../wallet/types/authorization"; import { AuthorizeCapabilitiesContent } from "../../../authorization/AuthorizeCapabilitiesContent"; interface AppAuthorizationCardProps { appId: string; onRevoke: (appId: string) => Promise; - onUpdate: () => Promise; } /** @@ -55,16 +51,13 @@ function extractContractAddresses(caps: GrantedCapability[]): string[] { return [...addresses]; } -export function AppAuthorizationCard({ - appId, - onRevoke, - onUpdate, -}: AppAuthorizationCardProps) { +export function AppAuthorizationCard({ appId, onRevoke }: AppAuthorizationCardProps) { const { walletAPI } = useContext(WalletContext); const [loading, setLoading] = useState(true); const [revoking, setRevoking] = useState(false); const [saving, setSaving] = useState(false); - const [fakeRequest, setFakeRequest] = useState | null>(null); + const [fakeRequest, setFakeRequest] = + useState | null>(null); // Track granted capabilities for diffing in handleCapabilitiesChange, but don't // use as a useEffect dependency to avoid re-render loops. const grantedRef = useRef([]); @@ -87,9 +80,8 @@ export function AppAuthorizationCard({ // Resolve contract names from all requested capabilities const addresses = extractContractAddresses(result.requested); - const resolvedNames = addresses.length > 0 - ? await walletAPI.resolveContractNames(addresses) - : {}; + const resolvedNames = + addresses.length > 0 ? await walletAPI.resolveContractNames(addresses) : {}; // Build existingGrants from currently-granted capabilities const existingGrants: Record = {}; @@ -105,9 +97,9 @@ export function AppAuthorizationCard({ // Merge granted account data into the requested capabilities so the // UI initialises checkboxes from the *currently granted* accounts // rather than from the (cumulative) requested list. - const grantedAccountsCap = result.granted.find( - (c) => c.type === "accounts", - ) as GrantedCapability & { accounts?: unknown[] } | undefined; + const grantedAccountsCap = result.granted.find((c) => c.type === "accounts") as + | (GrantedCapability & { accounts?: unknown[] }) + | undefined; const mergedCapabilities = result.requested.map((cap) => { if (cap.type === "accounts" && grantedAccountsCap) { @@ -164,57 +156,56 @@ export function AppAuthorizationCard({ } }; - const handleCapabilitiesChange = useCallback((data: { - granted: GrantedCapability[]; - mode: "strict" | "permissive"; - duration: number; - }) => { - // Clear any pending save - if (saveTimeoutRef.current) { - clearTimeout(saveTimeoutRef.current); - } + const handleCapabilitiesChange = useCallback( + (data: { granted: GrantedCapability[]; mode: "strict" | "permissive"; duration: number }) => { + // Clear any pending save + if (saveTimeoutRef.current) { + clearTimeout(saveTimeoutRef.current); + } - // Debounce: save after 500ms of no changes - saveTimeoutRef.current = setTimeout(async () => { - try { - setSaving(true); + // Debounce: save after 500ms of no changes + saveTimeoutRef.current = setTimeout(async () => { + try { + setSaving(true); - // Build old and new key sets for diffing - const oldGrantedKeys = new Set(); - for (const cap of grantedRef.current) { - const keys = await walletAPI.capabilityToStorageKeys(cap); - for (const key of keys) oldGrantedKeys.add(key); - } + // Build old and new key sets for diffing + const oldGrantedKeys = new Set(); + for (const cap of grantedRef.current) { + const keys = await walletAPI.capabilityToStorageKeys(cap); + for (const key of keys) oldGrantedKeys.add(key); + } - const newGrantedKeys = new Set(); - for (const cap of data.granted) { - const keys = await walletAPI.capabilityToStorageKeys(cap); - for (const key of keys) newGrantedKeys.add(key); - } + const newGrantedKeys = new Set(); + for (const cap of data.granted) { + const keys = await walletAPI.capabilityToStorageKeys(cap); + for (const key of keys) newGrantedKeys.add(key); + } - // Delete individual keys that were removed (not entire capabilities) - for (const key of oldGrantedKeys) { - if (!newGrantedKeys.has(key)) { - await walletAPI.revokeAuthorization(`${appId}:${key}`); + // Delete individual keys that were removed (not entire capabilities) + for (const key of oldGrantedKeys) { + if (!newGrantedKeys.has(key)) { + await walletAPI.revokeAuthorization(`${appId}:${key}`); + } } - } - // Store all newly granted capabilities (additive upsert). - // This also overwrites capability data blobs (e.g. account lists). - if (data.granted.length > 0) { - await walletAPI.storeCapabilityGrants(appId, data.granted); - } + // Store all newly granted capabilities (additive upsert). + // This also overwrites capability data blobs (e.g. account lists). + if (data.granted.length > 0) { + await walletAPI.storeCapabilityGrants(appId, data.granted); + } - // Update ref (not state — no re-render needed) - grantedRef.current = data.granted; - } catch (err) { - console.error("[AppAuthorizationCard] Failed to save capabilities:", err); - alert(`Failed to save: ${err instanceof Error ? err.message : String(err)}`); - } finally { - setSaving(false); - } - }, 500); - }, [appId, walletAPI]); + // Update ref (not state — no re-render needed) + grantedRef.current = data.granted; + } catch (err) { + console.error("[AppAuthorizationCard] Failed to save capabilities:", err); + alert(`Failed to save: ${err instanceof Error ? err.message : String(err)}`); + } finally { + setSaving(false); + } + }, 500); + }, + [appId, walletAPI], + ); return ( @@ -256,9 +247,7 @@ export function AppAuthorizationCard({ showAppId={false} /> ) : ( - - No capabilities have been requested by this app yet - + No capabilities have been requested by this app yet )} diff --git a/shared/src/ui/components/sections/authorized-apps/index.tsx b/shared/src/ui/components/sections/authorized-apps/index.tsx index 73b8a57..9b9676b 100644 --- a/shared/src/ui/components/sections/authorized-apps/index.tsx +++ b/shared/src/ui/components/sections/authorized-apps/index.tsx @@ -40,11 +40,6 @@ export function AuthorizedApps() { } }; - const handleUpdate = async () => { - // Reload apps after updating - await loadApps(); - }; - if (loading) { return ( - No apps have been authorized yet. When you grant persistent - authorizations to external applications, they will appear here. + No apps have been authorized yet. When you grant persistent authorizations to external + applications, they will appear here. ); @@ -89,12 +84,7 @@ export function AuthorizedApps() { {apps.map((appId) => ( - + ))} diff --git a/shared/src/ui/components/sections/contacts/components/ContactBox.tsx b/shared/src/ui/components/sections/contacts/components/ContactBox.tsx index 1902d74..e82673e 100644 --- a/shared/src/ui/components/sections/contacts/components/ContactBox.tsx +++ b/shared/src/ui/components/sections/contacts/components/ContactBox.tsx @@ -1,12 +1,9 @@ -import Button from "@mui/material/Button"; import Typography from "@mui/material/Typography"; import Box from "@mui/material/Box"; import Card from "@mui/material/Card"; -import ArrowDropDown from "@mui/icons-material/ArrowDropDown"; import ContentCopyIcon from "@mui/icons-material/ContentCopy"; import CheckIcon from "@mui/icons-material/Check"; -import { addressToShortStr, keyToShortStr } from "../../../../utils/format"; import IconButton from "@mui/material/IconButton"; import { useState } from "react"; import QrCode from "@mui/icons-material/QrCode"; diff --git a/shared/src/ui/components/sections/contacts/index.tsx b/shared/src/ui/components/sections/contacts/index.tsx index fc539d0..d8f893e 100644 --- a/shared/src/ui/components/sections/contacts/index.tsx +++ b/shared/src/ui/components/sections/contacts/index.tsx @@ -73,12 +73,7 @@ export function ContactsManager() { setAddDialogOpen(true)} /> {/* Add Contact Dialog */} - setAddDialogOpen(false)} - maxWidth="sm" - fullWidth - > + setAddDialogOpen(false)} maxWidth="sm" fullWidth> Add New Contact diff --git a/shared/src/ui/components/sections/interactions/index.tsx b/shared/src/ui/components/sections/interactions/index.tsx index 70d67c7..99d0133 100644 --- a/shared/src/ui/components/sections/interactions/index.tsx +++ b/shared/src/ui/components/sections/interactions/index.tsx @@ -1,6 +1,7 @@ import { useContext, useEffect, useState, type MutableRefObject } from "react"; import { Box, + Button, Card, CardContent, Chip, @@ -19,7 +20,12 @@ import { IconButton, Tooltip, } from "@mui/material"; -import { CheckCircle, Error as ErrorIcon, ContentCopy as CopyIcon } from "@mui/icons-material"; +import { + CheckCircle, + Error as ErrorIcon, + ContentCopy as CopyIcon, + Close as CloseIcon, +} from "@mui/icons-material"; import type { WalletInteraction, WalletInteractionType, @@ -30,12 +36,20 @@ import type { ExecutionStats } from "../../shared/PhaseTimeline"; import { TxProgressTimeline } from "../../shared/TxProgressTimeline"; import { WalletContext } from "../../../renderer"; -const TX_TYPES: WalletInteractionType[] = ["sendTx", "simulateTx", "simulateUtility", "createAccount", "deployAccount"]; +const TX_TYPES: WalletInteractionType[] = [ + "sendTx", + "simulateTx", + "simulateUtility", + "createAccount", + "deployAccount", +]; interface InteractionsListProps { interactions: WalletInteraction[]; selectedTypes: WalletInteractionType[]; onTypeFilterChange: (types: WalletInteractionType[]) => void; + onDismiss: (id: string) => void; + onClearCompleted: () => void; phaseStartsRef: MutableRefObject>; } @@ -64,8 +78,7 @@ const shimmer = keyframes` `; const getStatusIcon = (status: string, complete: boolean) => { - if (status.includes("ERROR") || status.includes("FAIL")) - return ; + if (status.includes("ERROR") || status.includes("FAIL")) return ; if (complete) return ; return ; }; @@ -147,20 +160,21 @@ export function InteractionsList({ interactions, selectedTypes, onTypeFilterChange, + onDismiss, + onClearCompleted, phaseStartsRef, }: InteractionsListProps) { const { walletAPI } = useContext(WalletContext); - const [selectedTrace, setSelectedTrace] = - useState(null); + const [selectedTrace, setSelectedTrace] = useState(null); const [selectedStats, setSelectedStats] = useState(null); const [selectedFrom, setSelectedFrom] = useState(null); const [selectedFeePayer, setSelectedFeePayer] = useState(null); const [traceDialogOpen, setTraceDialogOpen] = useState(false); // Lazy-loaded stats for completed tx interactions (for the inline timeline) - const [completedTimings, setCompletedTimings] = useState< - Map - >(new Map()); + const [completedTimings, setCompletedTimings] = useState>( + new Map(), + ); // Evict completed timings for interactions that are running again (same id, re-run). useEffect(() => { @@ -179,37 +193,36 @@ export function InteractionsList({ // Guard: don't fire while any TX interaction is still active — concurrent PXE DB // reads (via decodeTransaction → decodingCache) can corrupt in-flight IDB transactions. useEffect(() => { - const hasActiveTx = interactions.some( - (i) => !i.complete && TX_TYPES.includes(i.type) - ); + const hasActiveTx = interactions.some((i) => !i.complete && TX_TYPES.includes(i.type)); if (hasActiveTx) return; const completedTxInteractions = interactions.filter( - (i) => i.complete && TX_TYPES.includes(i.type) && !completedTimings.has(i.id) + (i) => i.complete && TX_TYPES.includes(i.type) && !completedTimings.has(i.id), ); if (completedTxInteractions.length === 0) return; for (const interaction of completedTxInteractions) { - walletAPI.getExecutionTrace(interaction.id).then((result) => { - setCompletedTimings((prev) => { - const next = new Map(prev); - next.set(interaction.id, { stats: result?.stats as ExecutionStats | undefined }); - return next; - }); - }).catch(() => { - // Mark as attempted so we don't retry - setCompletedTimings((prev) => { - const next = new Map(prev); - next.set(interaction.id, {}); - return next; + walletAPI + .getExecutionTrace(interaction.id) + .then((result) => { + setCompletedTimings((prev) => { + const next = new Map(prev); + next.set(interaction.id, { stats: result?.stats as ExecutionStats | undefined }); + return next; + }); + }) + .catch(() => { + // Mark as attempted so we don't retry + setCompletedTimings((prev) => { + const next = new Map(prev); + next.set(interaction.id, {}); + return next; + }); }); - }); } }, [interactions]); - const handleInteractionClick = async ( - interaction: WalletInteraction - ) => { + const handleInteractionClick = async (interaction: WalletInteraction) => { // Only show trace for simulateTx, simulateUtility, and sendTx interactions if ( interaction.type === "simulateTx" || @@ -238,12 +251,9 @@ export function InteractionsList({ // Filter interactions based on selected types const filteredInteractions = - selectedTypes.length === 0 || - selectedTypes.length === allInteractionTypes.length + selectedTypes.length === 0 || selectedTypes.length === allInteractionTypes.length ? interactions - : interactions.filter((interaction) => - selectedTypes.includes(interaction.type) - ); + : interactions.filter((interaction) => selectedTypes.includes(interaction.type)); return ( @@ -304,9 +314,7 @@ export function InteractionsList({ }} onClick={() => handleInteractionClick(interaction)} > - + {isProvenTx(interaction.type, interaction.status) && ( @@ -352,6 +354,18 @@ export function InteractionsList({ sx={{ fontSize: "0.7rem", height: 20 }} /> )} + + { + e.stopPropagation(); + onDismiss(interaction.id); + }} + > + + + { e.stopPropagation(); - navigator.clipboard.writeText(extractTxHash(interaction.description)!); + navigator.clipboard.writeText( + extractTxHash(interaction.description)!, + ); }} > @@ -432,11 +448,9 @@ export function InteractionsList({ {/* Filter Controls at Bottom */} - - - - Filter by Type - + + + Filter by Type + ); } else { - return ( - - ); + return ; } } diff --git a/shared/src/ui/components/shared/ExecutionTraceDisplay.tsx b/shared/src/ui/components/shared/ExecutionTraceDisplay.tsx index ae7930a..652068a 100644 --- a/shared/src/ui/components/shared/ExecutionTraceDisplay.tsx +++ b/shared/src/ui/components/shared/ExecutionTraceDisplay.tsx @@ -3,10 +3,7 @@ import type { ReadableCallAuthorization } from "../../../wallet/decoding/call-au import { FunctionCallDisplay } from "./FunctionCallDisplay"; import { PrivateCallDisplay } from "./PrivateCallDisplay"; import { PublicCallDisplay } from "./PublicCallDisplay"; -import { - SimulationPhaseTimeline, - type ExecutionStats, -} from "./PhaseTimeline"; +import { SimulationPhaseTimeline, type ExecutionStats } from "./PhaseTimeline"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; @@ -58,7 +55,8 @@ function ExecutionStatsDisplay({ stats, compact }: ExecutionStatsDisplayProps) { } const totalRpcTime = stats.nodeRPCCalls.roundTrips.roundTripDurations.reduce( - (sum, d) => sum + d, 0, + (sum, d) => sum + d, + 0, ); const tripCount = stats.nodeRPCCalls.roundTrips.roundTripDurations.length; @@ -84,19 +82,12 @@ function ExecutionStatsDisplay({ stats, compact }: ExecutionStatsDisplayProps) { borderRadius: 1, }} > - + RPC Round Trips ({tripCount}) - + @@ -106,27 +97,18 @@ function ExecutionStatsDisplay({ stats, compact }: ExecutionStatsDisplayProps) { - {stats.nodeRPCCalls.roundTrips.roundTripDurations.map( - (duration, index) => { - const methods = - stats.nodeRPCCalls.roundTrips.roundTripMethods[index] || []; - return ( - - - #{index + 1} - - - {methods.join(", ")} - - - {formatTime(duration)} - - - ); - }, - )} + {stats.nodeRPCCalls.roundTrips.roundTripDurations.map((duration, index) => { + const methods = stats.nodeRPCCalls.roundTrips.roundTripMethods[index] || []; + return ( + + #{index + 1} + + {methods.join(", ")} + + {formatTime(duration)} + + ); + })}
    diff --git a/shared/src/ui/components/shared/FunctionCallDisplay.tsx b/shared/src/ui/components/shared/FunctionCallDisplay.tsx index 1324c27..9ef10fe 100644 --- a/shared/src/ui/components/shared/FunctionCallDisplay.tsx +++ b/shared/src/ui/components/shared/FunctionCallDisplay.tsx @@ -7,7 +7,7 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import Chip from "@mui/material/Chip"; import CallMadeIcon from "@mui/icons-material/CallMade"; import VpnKeyIcon from "@mui/icons-material/VpnKey"; -import type { ReactNode } from "react"; +import { useState, type ReactNode } from "react"; export interface FunctionCallDisplayProps { contractName: string; @@ -45,6 +45,7 @@ export function FunctionCallDisplay({ accordionBgColor = "rgba(0, 0, 0, 0.01)", compact = false, }: FunctionCallDisplayProps) { + const [expanded, setExpanded] = useState(false); const hasArgs = args.length > 0; const hasReturnValues = returnValues.length > 0; // In compact mode, reduce nesting indent to avoid consuming too much width @@ -60,10 +61,11 @@ export function FunctionCallDisplay({ pl: depth > 0 ? (compact ? 1 : 2) : 0, // minWidth:0 allows flex children to shrink below their content size (needed for text truncation) minWidth: 0, - overflow: "hidden", }} > setExpanded(isExpanded)} sx={{ bgcolor: accordionBgColor, boxShadow: compact ? 0 : 1, @@ -73,26 +75,34 @@ export function FunctionCallDisplay({ > } - sx={compact ? { - minHeight: "32px !important", - "& .MuiAccordionSummary-content": { my: "4px !important", width: "100%", minWidth: 0, overflow: "hidden" }, - px: 1, - } : undefined} + sx={ + compact + ? { + minHeight: "32px !important", + "& .MuiAccordionSummary-content": { + my: "4px !important", + width: "100%", + minWidth: 0, + overflow: "hidden", + }, + px: 1, + } + : { + "& .MuiAccordionSummary-content": { + minWidth: 0, + overflow: "hidden", + }, + } + } > {contractName}.{functionName}({hasArgs ? "..." : ""})
    - - {needsAuth && !compact && ( - } - label="Requires Authorization" - size="small" - color="warning" - variant="filled" - /> - )} - {needsAuth && compact && ( + {/* Chips and return value — grouped so they never shrink away */} + } - label="Auth" + label={typeLabel} size="small" - color="warning" - variant="filled" - sx={{ flexShrink: 0, height: 16, fontSize: "0.6rem" }} + color={typeChipColor} + variant="outlined" + sx={{ + height: compact ? 16 : undefined, + fontSize: compact ? "0.6rem" : undefined, + }} /> - )} - {isStaticCall && !compact && ( - - )} - {hasReturnValues && !compact && ( - - → {returnValues.map((rv) => rv.value).join(", ")} - - )} + {needsAuth && ( + } + label={compact ? "Auth" : "Requires Authorization"} + size="small" + color="warning" + variant="filled" + sx={compact ? { height: 16, fontSize: "0.6rem" } : undefined} + /> + )} + {isStaticCall && !compact && } + {hasReturnValues && !compact && !expanded && ( + + → {returnValues.map((rv) => rv.value).join(", ")} + + )} +
    - + {/* Arguments (if available) - Show first as most important */} {hasArgs && ( Arguments: @@ -181,6 +205,7 @@ export function FunctionCallDisplay({ gap: compact ? 0.5 : 1, columnGap: compact ? 1 : 2, alignItems: "start", + minWidth: 0, }} > {args.map((arg, i) => ( @@ -219,7 +244,11 @@ export function FunctionCallDisplay({ Return Values: @@ -236,6 +265,7 @@ export function FunctionCallDisplay({ gap: compact ? 0.5 : 1, columnGap: compact ? 1 : 2, alignItems: "start", + minWidth: 0, }} > {returnValues.map((rv, i) => ( @@ -271,12 +301,20 @@ export function FunctionCallDisplay({ {/* Call Details */} - + Contract: {contractName} @@ -296,12 +334,20 @@ export function FunctionCallDisplay({ {callerName && ( - + Caller: {callerName} diff --git a/shared/src/ui/components/shared/PhaseTimeline.tsx b/shared/src/ui/components/shared/PhaseTimeline.tsx index d245552..aab1feb 100644 --- a/shared/src/ui/components/shared/PhaseTimeline.tsx +++ b/shared/src/ui/components/shared/PhaseTimeline.tsx @@ -22,7 +22,7 @@ interface StatsTimings { sync: number; publicSimulation?: number; validation?: number; - proving?: number; // circuit proof time + proving?: number; // circuit proof time perFunction: FunctionTiming[]; unaccounted: number; total: number; @@ -146,17 +146,10 @@ export function extractPhasesFromStats( return phases; } - /** * Phase timeline tooltip content */ -function PhaseTooltipContent({ - phase, - percentage, -}: { - phase: PhaseTiming; - percentage: number; -}) { +function PhaseTooltipContent({ phase, percentage }: { phase: PhaseTiming; percentage: number }) { return ( @@ -166,9 +159,7 @@ function PhaseTooltipContent({ {formatDurationLong(phase.duration)} ({percentage.toFixed(1)}%) {phase.breakdown && phase.breakdown.length > 0 && ( - + {phase.breakdown.map((item, idx) => ( {item.label}: {formatDuration(item.duration)} @@ -282,10 +273,7 @@ export function PhaseTimeline({ {size === "detailed" && ( {phases.map((phase) => ( - + { const percentage = (phase.duration / totalDuration) * 100; const segmentWidth = `${percentage}%`; - const showLabel = - showSegmentLabels && - percentage > (100 * minSegmentWidthForLabel) / 300; + const showLabel = showSegmentLabels && percentage > (100 * minSegmentWidthForLabel) / 300; return ( - } + title={} arrow placement="top" > @@ -344,11 +328,8 @@ export function PhaseTimeline({ transition: "all 0.2s ease", cursor: "pointer", borderRight: - index < phases.length - 1 - ? "1px solid rgba(255,255,255,0.3)" - : undefined, - transform: - hoveredPhase === phase.name ? "scaleY(1.1)" : "scaleY(1)", + index < phases.length - 1 ? "1px solid rgba(255,255,255,0.3)" : undefined, + transform: hoveredPhase === phase.name ? "scaleY(1.1)" : "scaleY(1)", zIndex: hoveredPhase === phase.name ? 1 : 0, "&:hover": { filter: "brightness(1.1)", @@ -387,10 +368,7 @@ export function PhaseTimeline({ }} > {phases.map((phase) => ( - + extractPhasesFromStats(stats), [stats]); return ; } - diff --git a/shared/src/ui/components/shared/PrivateCallDisplay.tsx b/shared/src/ui/components/shared/PrivateCallDisplay.tsx index c634202..2677594 100644 --- a/shared/src/ui/components/shared/PrivateCallDisplay.tsx +++ b/shared/src/ui/components/shared/PrivateCallDisplay.tsx @@ -1,8 +1,5 @@ import Box from "@mui/material/Box"; -import type { - PrivateCallEvent, - ExecutionEvent, -} from "../../../wallet/decoding/tx-callstack-decoder"; +import type { PrivateCallEvent } from "../../../wallet/decoding/tx-callstack-decoder"; import type { ReadableCallAuthorization } from "../../../wallet/decoding/call-authorization-formatter"; import { FunctionCallDisplay } from "./FunctionCallDisplay"; import { ExecutionEventDisplay } from "./ExecutionEventDisplay"; @@ -10,14 +7,12 @@ import { ExecutionEventDisplay } from "./ExecutionEventDisplay"; // Helper to check if a call requires authorization function requiresAuthorization( call: PrivateCallEvent, - authorizations?: ReadableCallAuthorization[] + authorizations?: ReadableCallAuthorization[], ): boolean { if (!authorizations || authorizations.length === 0) return false; return authorizations.some( - (auth) => - auth.contract.address === call.contract.address && - auth.function === call.function + (auth) => auth.contract.address === call.contract.address && auth.function === call.function, ); } diff --git a/shared/src/ui/components/shared/TxProgressTimeline.tsx b/shared/src/ui/components/shared/TxProgressTimeline.tsx index d32616c..7131799 100644 --- a/shared/src/ui/components/shared/TxProgressTimeline.tsx +++ b/shared/src/ui/components/shared/TxProgressTimeline.tsx @@ -8,11 +8,7 @@ import type { WalletInteraction, WalletInteractionType, } from "../../../wallet/types/wallet-interaction"; -import { - PhaseTimeline, - extractPhasesFromStats, - type ExecutionStats, -} from "./PhaseTimeline"; +import { PhaseTimeline, extractPhasesFromStats, type ExecutionStats } from "./PhaseTimeline"; // ── Types ───────────────────────────────────────────────────────────────────── @@ -37,10 +33,10 @@ interface LivePhaseTiming { // The end of simulation is marked by "REQUESTING AUTHORIZATION" (prepare completes). // Auth wait time is intentionally excluded (not a phase). const SEND_TX_PHASE_DEFS: PhaseDef[] = [ - { statusKey: "SIMULATING", name: "Simulation", color: "#ff9800", nextStatusKey: "PROVING" }, - { statusKey: "PROVING", name: "Proving", color: "#f48fb1", nextStatusKey: "SENDING" }, - { statusKey: "SENDING", name: "Sending", color: "#2196f3", nextStatusKey: "SENT" }, - { statusKey: "SENT", name: "Mining", color: "#4caf50", nextStatusKey: null }, + { statusKey: "SIMULATING", name: "Simulation", color: "#ff9800", nextStatusKey: "PROVING" }, + { statusKey: "PROVING", name: "Proving", color: "#f48fb1", nextStatusKey: "SENDING" }, + { statusKey: "SENDING", name: "Sending", color: "#2196f3", nextStatusKey: "SENT" }, + { statusKey: "SENT", name: "Mining", color: "#4caf50", nextStatusKey: null }, ]; // simulateTx / simulateUtility — the interaction is created at simulation start (timestamp), @@ -78,7 +74,8 @@ const formatDurationLong = (ms: number): string => { }; function getPhaseDefs(type: WalletInteractionType): PhaseDef[] { - if (type === "sendTx" || type === "createAccount" || type === "deployAccount") return SEND_TX_PHASE_DEFS; + if (type === "sendTx" || type === "createAccount" || type === "deployAccount") + return SEND_TX_PHASE_DEFS; return SIMULATE_TX_PHASE_DEFS; } @@ -89,7 +86,13 @@ function getCurrentPhaseKey(status: string, type: WalletInteractionType): string if (status.includes("SIMULATING")) return "SIMULATING"; if (status.includes("PROVING")) return "PROVING"; if (status.includes("SENDING")) return "SENDING"; - if (status.includes("MINING") || status.includes("SENT") || status.includes("MINED") || status.includes("DEPLOYED")) return "SENT"; + if ( + status.includes("MINING") || + status.includes("SENT") || + status.includes("MINED") || + status.includes("DEPLOYED") + ) + return "SENT"; return null; // REQUESTING AUTHORIZATION — auth wait excluded from timeline } // simulateTx / simulateUtility: single SIMULATING phase, paused during auth wait @@ -121,7 +124,8 @@ function PhaseTimelineBar({ phases }: { phases: LivePhaseTiming[] }) { const totalDuration = completedDuration + liveDuration; const miningDuration = useMemo( - () => completedPhases.filter((p) => p.name === "Mining").reduce((sum, p) => sum + p.duration, 0), + () => + completedPhases.filter((p) => p.name === "Mining").reduce((sum, p) => sum + p.duration, 0), [completedPhases], ); @@ -140,12 +144,24 @@ function PhaseTimelineBar({ phases }: { phases: LivePhaseTiming[] }) { ) : ( @@ -256,7 +276,8 @@ function PhaseTimelineBar({ phases }: { phases: LivePhaseTiming[] }) { }} /> - {phase.name}{phase.isLive ? " ●" : ""} + {phase.name} + {phase.isLive ? " ●" : ""} ))} @@ -280,9 +301,7 @@ export function TxProgressTimeline({ const currentPhaseKey = getCurrentPhaseKey(interaction.status, interaction.type); // The ref key for when the current phase started - const currentPhaseStartKey = currentPhaseKey - ? `${interaction.id}:${currentPhaseKey}` - : null; + const currentPhaseStartKey = currentPhaseKey ? `${interaction.id}:${currentPhaseKey}` : null; useEffect(() => { if (interaction.complete || !currentPhaseStartKey) return; @@ -317,9 +336,10 @@ export function TxProgressTimeline({ // During auth wait (REQUESTING AUTHORIZATION), currentPhaseKey is null. // Show the locked simulation segment so the user can see how long simulation took. if (!currentPhaseKey) { - const simStart = phaseStartsRef.current.get(`${interaction.id}:SIMULATING`) - ?? phaseStartsRef.current.get(`${interaction.id}:START`) - ?? Date.now(); + const simStart = + phaseStartsRef.current.get(`${interaction.id}:SIMULATING`) ?? + phaseStartsRef.current.get(`${interaction.id}:START`) ?? + Date.now(); const simEnd = phaseStartsRef.current.get(`${interaction.id}:REQUESTING AUTHORIZATION`); if (!simEnd) return null; const simulationSegment: LivePhaseTiming = { @@ -343,12 +363,16 @@ export function TxProgressTimeline({ const startKey = `${interaction.id}:${phaseDefs[i].statusKey}`; // Simulation ends when REQUESTING AUTHORIZATION fires (not when PROVING starts), // so use that as the boundary for the simulation→proving gap. - const endStatusKey = phaseDefs[i].statusKey === "SIMULATING" - ? "REQUESTING AUTHORIZATION" - : phaseDefs[i + 1].statusKey; + const endStatusKey = + phaseDefs[i].statusKey === "SIMULATING" + ? "REQUESTING AUTHORIZATION" + : phaseDefs[i + 1].statusKey; const endKey = `${interaction.id}:${endStatusKey}`; - const start = phaseStartsRef.current.get(startKey) - ?? (phaseDefs[i].statusKey === "SIMULATING" ? phaseStartsRef.current.get(`${interaction.id}:START`) : undefined); + const start = + phaseStartsRef.current.get(startKey) ?? + (phaseDefs[i].statusKey === "SIMULATING" + ? phaseStartsRef.current.get(`${interaction.id}:START`) + : undefined); const end = phaseStartsRef.current.get(endKey); if (start && end) { completedSegments.push({ @@ -362,7 +386,12 @@ export function TxProgressTimeline({ const liveDef = phaseDefs[currentIdx]; const displayPhases: LivePhaseTiming[] = [ ...completedSegments, - { name: liveDef.name, duration: phaseElapsed > 0 ? phaseElapsed : 100, color: liveDef.color, isLive: true }, + { + name: liveDef.name, + duration: phaseElapsed > 0 ? phaseElapsed : 100, + color: liveDef.color, + isLive: true, + }, ]; return ( diff --git a/shared/src/ui/contexts/NetworkContext.tsx b/shared/src/ui/contexts/NetworkContext.tsx index 100136b..91a94d5 100644 --- a/shared/src/ui/contexts/NetworkContext.tsx +++ b/shared/src/ui/contexts/NetworkContext.tsx @@ -1,10 +1,4 @@ -import { - createContext, - useContext, - useState, - useEffect, - type ReactNode, -} from "react"; +import { createContext, useContext, useState, type ReactNode } from "react"; import { NETWORKS, DEFAULT_NETWORK, @@ -65,9 +59,7 @@ export function NetworkProvider({ children }: NetworkProviderProps) { isNetworkSwitching, }; - return ( - {children} - ); + return {children}; } export function useNetwork(): NetworkContextType { diff --git a/shared/src/ui/renderer.tsx b/shared/src/ui/renderer.tsx index fac12d2..312ba2b 100644 --- a/shared/src/ui/renderer.tsx +++ b/shared/src/ui/renderer.tsx @@ -1,11 +1,6 @@ import { StrictMode, createContext, useMemo } from "react"; import "./index.css"; -import { - createTheme, - CssBaseline, - type ThemeOptions, - ThemeProvider, -} from "@mui/material"; +import { createTheme, CssBaseline, type ThemeOptions, ThemeProvider } from "@mui/material"; import { colors } from "./styles.ts"; import { App } from "./App.tsx"; import type { InternalWalletInterface } from "../ipc/wallet-internal-interface.ts"; diff --git a/shared/src/wallet/core/base-native-wallet.ts b/shared/src/wallet/core/base-native-wallet.ts deleted file mode 100644 index 2e5cfe9..0000000 --- a/shared/src/wallet/core/base-native-wallet.ts +++ /dev/null @@ -1,360 +0,0 @@ -import { - type Account, - type ChainInfo, - type NoFrom, - NO_FROM, -} from "@aztec/aztec.js/account"; -import { AztecAddress } from "@aztec/aztec.js/addresses"; -import { AccountManager, type Aliased } from "@aztec/aztec.js/wallet"; -import { Fq, Fr } from "@aztec/aztec.js/fields"; -import { getContractInstanceFromInstantiationParams } from "@aztec/aztec.js/contracts"; -import { type AztecNode } from "@aztec/aztec.js/node"; -import { type Logger } from "@aztec/aztec.js/log"; -import { DecodingCache } from "../decoding/decoding-cache"; -import { InteractionManager } from "../managers/interaction-manager"; -import { AuthorizationManager } from "../managers/authorization-manager"; -import type { PXE } from "@aztec/pxe/client/lazy"; -import type { AccountType, WalletDB } from "../database/wallet-db"; -import type { PromiseWithResolvers } from "@aztec/foundation/promise"; -import type { - AuthorizationRequest, - AuthorizationResponse, -} from "../types/authorization"; -import { - EcdsaKAccountContract, - EcdsaRAccountContract, -} from "@aztec/accounts/ecdsa"; -import { SchnorrAccountContract } from "@aztec/accounts/schnorr"; -import { - createStubAccount, - StubAccountContractArtifact, -} from "@aztec/accounts/stub"; -import type { ContractInstanceWithAddress } from "@aztec/stdlib/contract"; -import type { ContractArtifact } from "@aztec/stdlib/abi"; -import { BaseWallet } from "@aztec/wallet-sdk/base-wallet"; -import { Gas, GasSettings } from "@aztec/stdlib/gas"; -import type { FieldsOf } from "@aztec/foundation/types"; -import { - GAS_ESTIMATION_DA_GAS_LIMIT, - GAS_ESTIMATION_L2_GAS_LIMIT, - GAS_ESTIMATION_TEARDOWN_DA_GAS_LIMIT, - GAS_ESTIMATION_TEARDOWN_L2_GAS_LIMIT, -} from "@aztec/constants"; - -/** - * Base class for native wallet implementations (external and internal). - * Provides common functionality for both trusted and untrusted wallet contexts. - * - * This class handles: - * - Event emission (EventTarget implementation for wallet updates) - * - Interaction tracking and storage - * - Account management and creation - * - Fee calculation and payment method setup - * - Contract name resolution via shared decoding cache - * - * Subclasses must implement authorization logic appropriate to their trust level: - * - ExternalWallet: Requires user authorization for all operations - * - InternalWallet: Auto-approves all operations (trusted GUI) - */ -export abstract class BaseNativeWallet - extends BaseWallet - implements EventTarget -{ - protected decodingCache: DecodingCache; - protected interactionManager: InteractionManager; - protected authorizationManager: AuthorizationManager; - - constructor( - pxe: PXE, - node: AztecNode, - protected db: WalletDB, - protected pendingAuthorizations: Map< - string, - { - promise: PromiseWithResolvers; - request: AuthorizationRequest; - } - >, - protected appId: string, - protected chainInfo: ChainInfo, - protected override log: Logger, - ) { - super(pxe, node); - // Create a single decoding cache instance shared across all wallet operations - // This cache stores contract names, artifacts, and aliases to avoid repeated PXE lookups - this.decodingCache = new DecodingCache(pxe, db); - - // Create manager instances for operations to use - this.interactionManager = new InteractionManager(db); - this.authorizationManager = new AuthorizationManager( - appId, - db, - pendingAuthorizations, - this.interactionManager, // Use interactionManager as the event emitter - ); - } - - /** - * Creates an AccountManager for a given account type and signing key. - * - * This method: - * 1. Instantiates the appropriate account contract (Schnorr, ECDSA K-256, ECDSA R-1) - * 2. Creates an AccountManager with the contract - * 3. Registers the account contract with PXE for simulation/execution - * - * @param type - Account type (schnorr, ecdsasecp256k1, ecdsasecp256r1) - * @param secret - Account secret key - * @param salt - Deployment salt - * @param signingKey - Signing key for the account - * @returns AccountManager for this account - */ - async getAccountManager( - type: AccountType, - secret: Fr, - salt: Fr, - signingKey: Buffer, - ): Promise { - let contract; - switch (type) { - case "schnorr": { - contract = new SchnorrAccountContract(Fq.fromBuffer(signingKey)); - break; - } - case "ecdsasecp256k1": { - contract = new EcdsaKAccountContract(signingKey); - break; - } - case "ecdsasecp256r1": { - contract = new EcdsaRAccountContract(signingKey); - break; - } - default: { - throw new Error(`Unknown account type ${type}`); - } - } - - const accountManager = await AccountManager.create( - this, - secret, - contract, - salt, - ); - - const instance = accountManager.getInstance(); - const existingInstance = await this.pxe.getContractInstance( - instance.address, - ); - if (!existingInstance) { - const existingArtifact = await this.pxe.getContractArtifact( - instance.currentContractClassId, - ); - await this.registerContract( - instance, - !existingArtifact - ? await accountManager.getAccountContract().getContractArtifact() - : undefined, - accountManager.getSecretKey(), - ); - } - - return accountManager; - } - - /** - * Internal implementation for retrieving an account by address. - * - * This method handles the actual account retrieval logic: - * - For ZERO address: Returns a signerless account - * - For other addresses: Retrieves account data from DB and creates AccountManager - * - * Subclasses should wrap this with authorization checks as needed. - * - * @param address - The account address to retrieve - * @returns Account instance for the given address - */ - protected async getAccountFromAddressInternal( - address: AztecAddress, - ): Promise { - const { secretKey, salt, signingKey, type } = - await this.db.retrieveAccount(address); - const accountManager = await this.getAccountManager( - type, - secretKey, - salt, - signingKey, - ); - const account = await accountManager.getAccount(); - - if (!account) { - throw new Error(`Account not found in wallet for address: ${address}`); - } - - return account; - } - - /** - * Creates a "fake" stub account for simulation purposes. - * - * Used during transaction simulation to bypass actual signature generation - * while maintaining the correct account address and contract interface. - * The stub account allows simulating transactions without access to the real private key. - * - * @param address - The real account address to create a stub for - * @returns Stub account, instance, and artifact for simulation - */ - protected async getFakeAccountDataFor(address: AztecAddress) { - const originalAccount = await this.getAccountFromAddress(address); - const originalAddress = originalAccount.getCompleteAddress(); - const contractInstance = await this.pxe.getContractInstance( - originalAddress.address, - ); - if (!contractInstance) { - throw new Error( - `No contract instance found for address: ${originalAddress.address}`, - ); - } - const account = createStubAccount(originalAddress); - const instance = await getContractInstanceFromInstantiationParams( - StubAccountContractArtifact, - { - salt: Fr.random(), - }, - ); - return { - account, - instance, - artifact: StubAccountContractArtifact, - }; - } - - /** - * Builds simulation overrides for all known accounts in the wallet. - * This ensures kernelless simulation works for any call that touches - * account contracts, including sponsored calls with NO_FROM. - */ - protected async buildAccountOverrides(): Promise< - Record< - string, - { instance: ContractInstanceWithAddress; artifact: ContractArtifact } - > - > { - const accounts = await this.db.listAccounts(); - const contracts: Record< - string, - { instance: ContractInstanceWithAddress; artifact: ContractArtifact } - > = {}; - - for (const account of accounts) { - try { - const instance = await getContractInstanceFromInstantiationParams( - StubAccountContractArtifact, - { salt: Fr.random() }, - ); - contracts[account.item.toString()] = { - instance, - artifact: StubAccountContractArtifact, - }; - } catch { - // Skip accounts that can't be resolved - } - } - - return contracts; - } - - override getChainInfo(): Promise { - return Promise.resolve(this.chainInfo); - } - - /** - * Internal method to retrieve all senders from the address book. - * This method does not require authorization and returns all stored senders. - * It also ensures that all stored senders are registered with the PXE. - * - * @returns All stored senders with their aliases - */ - protected async getAddressBookInternal(): Promise[]> { - const senders = await this.pxe.getSenders(); - const storedSenders = await this.db.listSenders(); - - // Register any stored senders that aren't in PXE - for (const storedSender of storedSenders) { - if ( - senders.findIndex((sender) => sender.equals(storedSender.item)) === -1 - ) { - await this.pxe.registerSender(storedSender.item); - } - } - - return storedSenders; - } - - // ============================================================================ - // EventTarget Implementation - // ============================================================================ - // Delegates to InteractionManager which implements EventTarget. - // Allows external code to listen for wallet events (interactions, auth requests). - - dispatchEvent(event: Event): boolean { - return this.interactionManager.dispatchEvent(event); - } - - addEventListener( - type: string, - callback: EventListenerOrEventListenerObject, - options?: boolean | AddEventListenerOptions, - ): void { - return this.interactionManager.addEventListener(type, callback, options); - } - - removeEventListener( - type: string, - callback: EventListenerOrEventListenerObject, - options?: boolean | EventListenerOptions, - ): void { - return this.interactionManager.removeEventListener(type, callback, options); - } - - /** - * Resolves a pending authorization request with a user response. - * - * Called by the UI when the user approves/denies an authorization dialog. - * Completes the promise that the wallet is waiting on, allowing the operation to proceed or fail. - * - * @param response - Authorization response from user interaction - */ - resolveAuthorization(response: AuthorizationResponse) { - this.authorizationManager.resolveAuthorization(response); - } - - /** - * Overrides the base wallet's completeFeeOptionsForEstimation to respect - * user-provided gasLimits and teardownGasLimits during simulation. - * - * The installed @aztec/wallet-sdk version always replaces gas limits with - * high estimation constants, causing _ensure_max_fee assertions in FPC - * teardown to fail when the user has provided tight teardown gas limits. - */ - protected override async completeFeeOptionsForEstimation( - from: AztecAddress | NoFrom, - feePayer?: AztecAddress, - gasSettings?: Partial>, - ) { - const defaultFeeOptions = await this.completeFeeOptions(from, feePayer, gasSettings); - const { maxFeesPerGas, maxPriorityFeesPerGas } = defaultFeeOptions.gasSettings; - const gasSettingsForEstimation = new GasSettings( - gasSettings?.gasLimits - ? Gas.from(gasSettings.gasLimits) - : new Gas(GAS_ESTIMATION_DA_GAS_LIMIT, GAS_ESTIMATION_L2_GAS_LIMIT), - gasSettings?.teardownGasLimits - ? Gas.from(gasSettings.teardownGasLimits) - : new Gas(GAS_ESTIMATION_TEARDOWN_DA_GAS_LIMIT, GAS_ESTIMATION_TEARDOWN_L2_GAS_LIMIT), - maxFeesPerGas, - maxPriorityFeesPerGas, - ); - return { - ...defaultFeeOptions, - gasSettings: gasSettingsForEstimation, - }; - } -} diff --git a/shared/src/wallet/core/demo-wallet.ts b/shared/src/wallet/core/demo-wallet.ts new file mode 100644 index 0000000..18c2091 --- /dev/null +++ b/shared/src/wallet/core/demo-wallet.ts @@ -0,0 +1,272 @@ +import { type Account, type ChainInfo, NO_FROM } from "@aztec/aztec.js/account"; +import { AztecAddress } from "@aztec/aztec.js/addresses"; +import { + AccountManager, + TxSimulationResultWithAppOffset, + type Aliased, +} from "@aztec/aztec.js/wallet"; +import { Fq, Fr } from "@aztec/aztec.js/fields"; +import { type AztecNode } from "@aztec/aztec.js/node"; +import { type Logger } from "@aztec/aztec.js/log"; +import { DecodingCache } from "../decoding/decoding-cache"; +import { InteractionManager } from "../managers/interaction-manager"; +import { AuthorizationManager } from "../managers/authorization-manager"; +import type { PXE } from "@aztec/pxe/client/lazy"; +import type { AccountType, WalletDB } from "../database/wallet-db"; +import type { PromiseWithResolvers } from "@aztec/foundation/promise"; +import type { AuthorizationRequest, AuthorizationResponse } from "../types/authorization"; +import { EcdsaKAccountContract, EcdsaRAccountContract } from "@aztec/accounts/ecdsa"; +import { SchnorrAccountContract } from "@aztec/accounts/schnorr"; +import { + createStubSchnorrAccount, + StubSchnorrAccountContractArtifact, +} from "@aztec/accounts/stub/schnorr"; +import { + createStubEcdsaAccount, + StubEcdsaAccountContractArtifact, +} from "@aztec/accounts/stub/ecdsa"; +import type { ContractArtifact } from "@aztec/stdlib/abi"; +import { + type ContractOverrides, + ExecutionPayload, + SimulationOverrides, + mergeExecutionPayloads, +} from "@aztec/stdlib/tx"; +import { getContractInstanceFromInstantiationParams } from "@aztec/stdlib/contract"; +import { BaseWallet, type SimulateViaEntrypointOptions } from "@aztec/wallet-sdk/base-wallet"; +import { DefaultEntrypoint } from "@aztec/entrypoints/default"; +import { type DefaultAccountEntrypointOptions } from "@aztec/entrypoints/account"; + +/** + * Base class for native wallet implementations (external and internal). + * Provides common functionality for both trusted and untrusted wallet contexts. + * + * Mirrors EmbeddedWallet's approach: simulateViaEntrypoint and buildAccountOverrides + * live here so operations can call back into the wallet without duplicating logic. + * + * Subclasses must implement authorization logic appropriate to their trust level: + * - ExternalWallet: Requires user authorization for all operations + * - InternalWallet: Auto-approves all operations (trusted GUI) + */ +export abstract class DemoWallet extends BaseWallet implements EventTarget { + protected decodingCache: DecodingCache; + protected interactionManager: InteractionManager; + protected authorizationManager: AuthorizationManager; + + constructor( + pxe: PXE, + node: AztecNode, + protected db: WalletDB, + protected pendingAuthorizations: Map< + string, + { + promise: PromiseWithResolvers; + request: AuthorizationRequest; + } + >, + protected appId: string, + protected chainInfo: ChainInfo, + protected override log: Logger, + ) { + super(pxe, node); + this.decodingCache = new DecodingCache(pxe, db); + this.interactionManager = new InteractionManager(db); + this.authorizationManager = new AuthorizationManager( + appId, + db, + pendingAuthorizations, + this.interactionManager, + ); + } + + /** + * Creates an AccountManager for a given account type and signing key. + */ + async getAccountManager( + type: AccountType, + secret: Fr, + salt: Fr, + signingKey: Buffer, + ): Promise { + let contract; + switch (type) { + case "schnorr": + contract = new SchnorrAccountContract(Fq.fromBuffer(signingKey)); + break; + case "ecdsasecp256k1": + contract = new EcdsaKAccountContract(signingKey); + break; + case "ecdsasecp256r1": + contract = new EcdsaRAccountContract(signingKey); + break; + default: + throw new Error(`Unknown account type ${type}`); + } + + const accountManager = await AccountManager.create(this, secret, contract, salt); + + const instance = accountManager.getInstance(); + const existingInstance = await this.pxe.getContractInstance(instance.address); + if (!existingInstance) { + const existingArtifact = await this.pxe.getContractArtifact(instance.currentContractClassId); + await this.registerContract( + instance, + !existingArtifact + ? await accountManager.getAccountContract().getContractArtifact() + : undefined, + accountManager.getSecretKey(), + ); + } + + return accountManager; + } + + protected async getAccountFromAddressInternal(address: AztecAddress): Promise { + const { secretKey, salt, signingKey, type } = await this.db.retrieveAccount(address); + const accountManager = await this.getAccountManager(type, secretKey, salt, signingKey); + const account = await accountManager.getAccount(); + if (!account) { + throw new Error(`Account not found in wallet for address: ${address}`); + } + return account; + } + + /** + * Builds contract overrides for all accounts whose addresses appear in `scopes`, + * replacing their account contracts with type-appropriate stub implementations. + * Mirrors EmbeddedWallet.buildAccountOverrides. + */ + protected async buildAccountOverrides(scopes: AztecAddress[]): Promise { + const accounts = await this.db.listAccounts(); + const contracts: ContractOverrides = {}; + + const filtered = accounts.filter((acc) => scopes.some((addr) => addr.equals(acc.item))); + + for (const account of filtered) { + try { + const { type } = await this.db.retrieveAccount(account.item); + const isEcdsa = type === "ecdsasecp256k1" || type === "ecdsasecp256r1"; + const artifact: ContractArtifact = isEcdsa + ? StubEcdsaAccountContractArtifact + : StubSchnorrAccountContractArtifact; + const stubConstructorArgs = + type === "schnorr" ? [Fr.ZERO, Fr.ZERO] : [Buffer.alloc(32), Buffer.alloc(32)]; + const instance = await getContractInstanceFromInstantiationParams(artifact, { + salt: Fr.random(), + constructorArgs: stubConstructorArgs, + }); + contracts[account.item.toString()] = { instance, artifact }; + } catch { + // Skip accounts that can't be resolved + } + } + + return contracts; + } + + /** + * Simulates via a stub account entrypoint, bypassing real account authorization. + * Mirrors EmbeddedWallet.simulateViaEntrypoint — operations call this instead of + * housing their own copy of the logic. + */ + protected override async simulateViaEntrypoint( + executionPayload: ExecutionPayload, + opts: SimulateViaEntrypointOptions, + ): Promise { + const { from, feeOptions, additionalScopes, skipTxValidation, skipFeeEnforcement } = opts; + const scopes = this.scopesFrom(from, additionalScopes); + + const feeExecutionPayload = await feeOptions.walletFeePaymentMethod?.getExecutionPayload(); + const finalExecutionPayload = feeExecutionPayload + ? mergeExecutionPayloads([feeExecutionPayload, executionPayload]) + : executionPayload; + const chainInfo = await this.getChainInfo(); + + const accountOverrides = await this.buildAccountOverrides(scopes); + const overrides = new SimulationOverrides(accountOverrides); + + let txRequest; + if (from === NO_FROM) { + const entrypoint = new DefaultEntrypoint(); + txRequest = await entrypoint.createTxExecutionRequest( + finalExecutionPayload, + feeOptions.gasSettings, + chainInfo, + ); + } else { + const { type } = await this.db.retrieveAccount(from); + const originalAccount = await this.getAccountFromAddress(from); + const completeAddress = originalAccount.getCompleteAddress(); + const isEcdsa = type === "ecdsasecp256k1" || type === "ecdsasecp256r1"; + const stubAccount = isEcdsa + ? createStubEcdsaAccount(completeAddress) + : createStubSchnorrAccount(completeAddress); + const executionOptions: DefaultAccountEntrypointOptions = { + txNonce: Fr.random(), + cancellable: this.cancellableTransactions, + feePaymentMethodOptions: feeOptions.accountFeePaymentMethodOptions!, + }; + txRequest = await stubAccount.createTxExecutionRequest( + finalExecutionPayload, + feeOptions.gasSettings, + chainInfo, + executionOptions, + ); + } + + const result = await this.pxe.simulateTx(txRequest, { + simulatePublic: true, + skipFeeEnforcement, + skipTxValidation, + overrides, + scopes, + }); + const appCallOffset = await this.computeAppCallOffset(from, feeOptions); + return TxSimulationResultWithAppOffset.fromResultAndOffset(result, appCallOffset); + } + + override getChainInfo(): Promise { + return Promise.resolve(this.chainInfo); + } + + protected async getAddressBookInternal(): Promise[]> { + const senders = await this.pxe.getSenders(); + const storedSenders = await this.db.listSenders(); + + for (const storedSender of storedSenders) { + if (senders.findIndex((sender) => sender.equals(storedSender.item)) === -1) { + await this.pxe.registerSender(storedSender.item); + } + } + + return storedSenders; + } + + // ============================================================================ + // EventTarget — delegates to InteractionManager + // ============================================================================ + + dispatchEvent(event: Event): boolean { + return this.interactionManager.dispatchEvent(event); + } + + addEventListener( + type: string, + callback: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions, + ): void { + return this.interactionManager.addEventListener(type, callback, options); + } + + removeEventListener( + type: string, + callback: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions, + ): void { + return this.interactionManager.removeEventListener(type, callback, options); + } + + resolveAuthorization(response: AuthorizationResponse) { + this.authorizationManager.resolveAuthorization(response); + } +} diff --git a/shared/src/wallet/core/external-wallet.ts b/shared/src/wallet/core/external-wallet.ts index efb1687..7d44f2b 100644 --- a/shared/src/wallet/core/external-wallet.ts +++ b/shared/src/wallet/core/external-wallet.ts @@ -9,10 +9,7 @@ import { type PrivateEvent, type PrivateEventFilter, } from "@aztec/aztec.js/wallet"; -import { - type IntentInnerHash, - type CallIntent, -} from "@aztec/aztec.js/authorization"; +import { type IntentInnerHash, type CallIntent } from "@aztec/aztec.js/authorization"; import type { EventMetadataDefinition } from "@aztec/stdlib/abi"; import type { @@ -28,12 +25,12 @@ import type { ContractInstanceWithAddress } from "@aztec/stdlib/contract"; import { Fr } from "@aztec/foundation/curves/bn254"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; import { - type TxSimulationResult, type UtilityExecutionResult, ExecutionPayload, TxHash, type TxReceipt, } from "@aztec/stdlib/tx"; +import { TxSimulationResultWithAppOffset } from "@aztec/aztec.js/wallet"; import type { PXE } from "@aztec/pxe/client/lazy"; import { WalletDB } from "../database/wallet-db"; import { type PromiseWithResolvers } from "@aztec/foundation/promise"; @@ -42,7 +39,7 @@ import { type AuthorizationResponse, type AuthorizationItem, } from "../types/authorization"; -import { BaseNativeWallet } from "./base-native-wallet"; +import { DemoWallet } from "./demo-wallet"; import { ExternalOperation } from "../operations/base-operation"; import { RegisterContractOperation } from "../operations/register-contract-operation"; import { RegisterSenderOperation } from "../operations/register-sender-operation"; @@ -56,12 +53,9 @@ import { GetPrivateEventsOperation } from "../operations/get-private-events-oper import { GetContractMetadataOperation } from "../operations/get-contract-metadata-operation"; import { GetContractClassMetadataOperation } from "../operations/get-contract-class-metadata-operation"; import { RequestCapabilitiesOperation } from "../operations/request-capabilities-operation"; -import type { - InteractionWaitOptions, - SendReturn, -} from "@aztec/aztec.js/contracts"; +import type { InteractionWaitOptions, SendReturn } from "@aztec/aztec.js/contracts"; -export class ExternalWallet extends BaseNativeWallet { +export class ExternalWallet extends DemoWallet { constructor( pxe: PXE, node: AztecNode, @@ -130,13 +124,9 @@ export class ExternalWallet extends BaseNativeWallet { this.decodingCache, this.interactionManager, this.authorizationManager, - this.completeFeeOptionsForEstimation.bind(this), this.completeFeeOptions.bind(this), - this.getFakeAccountDataFor.bind(this), - this.buildAccountOverrides.bind(this), + this.simulateViaEntrypoint.bind(this), this.getChainInfo.bind(this), - this.scopesFrom.bind(this), - this.cancellableTransactions, this.log, ); } @@ -156,7 +146,7 @@ export class ExternalWallet extends BaseNativeWallet { this.interactionManager, this.authorizationManager, simulateTxOp, - this.createAuthWitForSendTx.bind(this), + this.createAuthWitForOperation.bind(this), this.createTxExecutionRequestFromPayloadAndFee.bind(this), this.completeFeeOptions.bind(this), this.contextualizeError.bind(this), @@ -168,49 +158,26 @@ export class ExternalWallet extends BaseNativeWallet { * Factory method to create a fresh GetAccountsOperation instance. */ private createGetAccountsOperation(): GetAccountsOperation { - return new GetAccountsOperation( - this.db, - this.interactionManager, - this.authorizationManager, - ); + return new GetAccountsOperation(this.db, this.interactionManager, this.authorizationManager); } /** * Factory method to create a fresh GetAddressBookOperation instance. */ private createGetAddressBookOperation(): GetAddressBookOperation { - return new GetAddressBookOperation( - this.db, - this.interactionManager, - this.authorizationManager, - ); + return new GetAddressBookOperation(this.db, this.interactionManager, this.authorizationManager); } /** * Internal helper to create authwit without authorization. * Used by CreateAuthWitOperation after user has approved. */ - private async createAuthWitInternal( + private async createAuthWitForOperation( from: AztecAddress, messageHashOrIntent: IntentInnerHash | CallIntent, - chainInfo: ChainInfo, ): Promise { const account = await this.getAccountFromAddress(from); - return account.createAuthWit(messageHashOrIntent, chainInfo); - } - - /** - * Internal helper for SendTxOperation to create auth witnesses without external authorization. - * This is used when sendTx internally needs to create auth witnesses for call authorizations. - * These are implicit authorizations that don't require user approval since the user already - * approved the transaction itself. - */ - private async createAuthWitForSendTx( - from: AztecAddress, - auth: CallIntent, - ): Promise { - const account = await this.getAccountFromAddress(from); - return account.createAuthWit(auth, this.chainInfo); + return account.createAuthWit(messageHashOrIntent, this.chainInfo); } /** @@ -221,7 +188,7 @@ export class ExternalWallet extends BaseNativeWallet { this.decodingCache, this.interactionManager, this.authorizationManager, - this.createAuthWitInternal.bind(this), + this.createAuthWitForOperation.bind(this), this.chainInfo, ); } @@ -285,14 +252,9 @@ export class ExternalWallet extends BaseNativeWallet { * @returns Account instance for the given address * @throws Error if app doesn't have authorization for this account */ - protected async getAccountFromAddress( - address: AztecAddress, - ): Promise { + protected async getAccountFromAddress(address: AztecAddress): Promise { // Check if there's a persistent getAccounts authorization - const authData = await this.db.retrievePersistentAuthorization( - this.appId, - "getAccounts", - ); + const authData = await this.db.retrievePersistentAuthorization(this.appId, "getAccounts"); if (!authData || !authData.accounts) { throw new Error( @@ -301,9 +263,7 @@ export class ExternalWallet extends BaseNativeWallet { } // Check if the specific account is in the authorized list - const authorizedAddresses = authData.accounts.map((acc: any) => - acc.item.toString(), - ); + const authorizedAddresses = authData.accounts.map((acc: any) => acc.item.toString()); const requestedAddress = address.toString(); if (!authorizedAddresses.includes(requestedAddress)) { @@ -336,10 +296,7 @@ export class ExternalWallet extends BaseNativeWallet { return await op.executeStandalone(instance, artifact, secretKey); } - override async registerSender( - address: AztecAddress, - alias: string, - ): Promise { + override async registerSender(address: AztecAddress, alias: string): Promise { const op = this.createRegisterSenderOperation(); return await op.executeStandalone(address, alias); } @@ -371,16 +328,12 @@ export class ExternalWallet extends BaseNativeWallet { return (await op.executeStandalone(address)) as any; } - override async getContractClassMetadata( - id: Fr, - ): Promise { + override async getContractClassMetadata(id: Fr): Promise { const op = this.createGetContractClassMetadataOperation(); return await op.executeStandalone(id); } - override async requestCapabilities( - manifest: AppCapabilities, - ): Promise { + override async requestCapabilities(manifest: AppCapabilities): Promise { const op = this.createRequestCapabilitiesOperation(); return await op.executeStandalone(manifest); } @@ -403,7 +356,7 @@ export class ExternalWallet extends BaseNativeWallet { | TxReceipt | AztecAddress | UtilityExecutionResult - | TxSimulationResult; + | TxSimulationResultWithAppOffset; interface BatchItem { operation: ExternalOperation; @@ -440,11 +393,12 @@ export class ExternalWallet extends BaseNativeWallet { case "simulateTx": operation = this.createSimulateTxOperation(); break; - case "sendTx": + case "sendTx": { // Only create simulateTxOp when needed for sendTx operations const simulateTxOp = this.createSimulateTxOperation(); operation = this.createSendTxOperation(simulateTxOp); break; + } case "getAccounts": operation = this.createGetAccountsOperation(); break; @@ -515,9 +469,7 @@ export class ExternalWallet extends BaseNativeWallet { } try { - const interaction = await (item.operation as any).createInteraction( - ...item.args, - ); + const interaction = await (item.operation as any).createInteraction(...item.args); item.operation.setCurrentInteraction(interaction); } catch (error) { item.error = error; @@ -539,8 +491,7 @@ export class ExternalWallet extends BaseNativeWallet { item.executionData = result.executionData; item.persistence = result.persistence; } catch (error) { - const description = - error instanceof Error ? error.message : String(error); + const description = error instanceof Error ? error.message : String(error); await item.operation.emitProgress("ERROR", description, true); item.error = error; } @@ -595,17 +546,12 @@ export class ExternalWallet extends BaseNativeWallet { } try { - response = - await this.authorizationManager.requestAuthorization(authItems); + response = await this.authorizationManager.requestAuthorization(authItems); } catch (error) { // Authorization was denied - mark all pending items as ERROR for (const item of items) { if (item.earlyReturn === undefined && !item.error) { - await item.operation.emitProgress( - "ERROR", - "Authorization denied", - true, - ); + await item.operation.emitProgress("ERROR", "Authorization denied", true); } } throw error; @@ -640,11 +586,7 @@ export class ExternalWallet extends BaseNativeWallet { if (itemId && response) { const itemResponse = response.itemResponses[itemId]; if (!itemResponse || !itemResponse.approved) { - await item.operation.emitProgress( - "ERROR", - "Authorization denied", - true, - ); + await item.operation.emitProgress("ERROR", "Authorization denied", true); throw new Error(`Authorization denied for ${item.originalName}`); } } @@ -653,8 +595,7 @@ export class ExternalWallet extends BaseNativeWallet { try { result = await item.operation.execute(item.executionData!); } catch (error) { - const description = - error instanceof Error ? error.message : String(error); + const description = error instanceof Error ? error.message : String(error); await item.operation.emitProgress("ERROR", description, true); throw error; } @@ -672,9 +613,9 @@ export class ExternalWallet extends BaseNativeWallet { override async simulateTx( executionPayload: ExecutionPayload, opts: SimulateOptions, - ): Promise { + ): Promise { const op = this.createSimulateTxOperation(); - return await op.executeStandalone(executionPayload, opts); + return op.executeStandalone(executionPayload, opts); } /** diff --git a/shared/src/wallet/core/index.ts b/shared/src/wallet/core/index.ts index 85520b1..1df3caf 100644 --- a/shared/src/wallet/core/index.ts +++ b/shared/src/wallet/core/index.ts @@ -1,5 +1,5 @@ // Wallet core exports - main wallet implementations -export { BaseNativeWallet } from "./base-native-wallet"; +export { DemoWallet } from "./demo-wallet"; export { ExternalWallet } from "./external-wallet"; export { InternalWallet } from "./internal-wallet"; export type { InternalAccount } from "./internal-wallet"; diff --git a/shared/src/wallet/core/internal-wallet.ts b/shared/src/wallet/core/internal-wallet.ts index 5a22321..14258f7 100644 --- a/shared/src/wallet/core/internal-wallet.ts +++ b/shared/src/wallet/core/internal-wallet.ts @@ -5,6 +5,7 @@ import { type DeployAccountOptions, type SendOptions, type GrantedCapability, + TxSimulationResultWithAppOffset, } from "@aztec/aztec.js/wallet"; import { type Fr } from "@aztec/aztec.js/fields"; import type { AccountType } from "../database/wallet-db"; @@ -14,16 +15,11 @@ import { type WalletInteractionType, } from "../types/wallet-interaction"; -import { - collectOffchainEffects, - type ExecutionPayload, - TxSimulationResult, - TxStatus, -} from "@aztec/stdlib/tx"; +import { collectOffchainEffects, type ExecutionPayload, TxStatus } from "@aztec/stdlib/tx"; import type { DecodedExecutionTrace } from "../decoding/tx-callstack-decoder"; import { TxDecodingService } from "../decoding/tx-decoding-service"; -import { BaseNativeWallet } from "./base-native-wallet.ts"; +import { DemoWallet } from "./demo-wallet.ts"; import { NO_WAIT, toSendOptions, @@ -47,11 +43,9 @@ export type InternalAccount = Aliased & { * 2. Returns enriched data (e.g., account types) * 3. Provides additional internal-only methods */ -export class InternalWallet extends BaseNativeWallet { +export class InternalWallet extends DemoWallet { // Override getAccountFromAddress to skip authorization check - protected override async getAccountFromAddress( - address: AztecAddress, - ): Promise { + protected override async getAccountFromAddress(address: AztecAddress): Promise { // Internal wallet is trusted, skip authorization and use base implementation return this.getAccountFromAddressInternal(address); } @@ -70,10 +64,7 @@ export class InternalWallet extends BaseNativeWallet { ); } - override async registerSender( - address: AztecAddress, - alias: string, - ): Promise { + override async registerSender(address: AztecAddress, alias: string): Promise { // Store sender in database await this.db.storeSender(address, alias); // Register with PXE @@ -109,12 +100,7 @@ export class InternalWallet extends BaseNativeWallet { await this.interactionManager.storeAndEmit(interaction); try { - const accountManager = await this.getAccountManager( - type, - secret, - salt, - signingKey, - ); + const accountManager = await this.getAccountManager(type, secret, salt, signingKey); await this.db.storeAccount(accountManager.address, { type, secretKey: secret, @@ -152,14 +138,8 @@ export class InternalWallet extends BaseNativeWallet { await this.interactionManager.storeAndEmit(interaction); try { - const { secretKey, salt, signingKey, type } = - await this.db.retrieveAccount(address); - const accountManager = await this.getAccountManager( - type, - secretKey, - salt, - signingKey, - ); + const { secretKey, salt, signingKey, type } = await this.db.retrieveAccount(address); + const accountManager = await this.getAccountManager(type, secretKey, salt, signingKey); await this.interactionManager.storeAndEmit( interaction.update({ @@ -179,36 +159,30 @@ export class InternalWallet extends BaseNativeWallet { deployer: AztecAddress.ZERO, }); - const feeOptions = await this.completeFeeOptionsForEstimation( - opts.from, - executionPayload.feePayer, - opts.fee?.gasSettings, - ); + const feeOptions = await this.completeFeeOptions({ + from: opts.from, + feePayer: executionPayload.feePayer, + gasSettings: opts.fee?.gasSettings, + forEstimation: true, + }); - const simulationResult = await this.simulateViaEntrypoint( - executionPayload, - { - from: opts.from, - feeOptions, - scopes: this.scopesFrom(opts.from, opts.additionalScopes), - skipTxValidation: true, - }, - ); + const simulationResult = await this.simulateViaEntrypoint(executionPayload, { + from: opts.from, + feeOptions, + additionalScopes: [address], + skipTxValidation: true, + }); // Mark simulation complete so the live timeline can measure its duration await this.interactionManager.storeAndEmit( interaction.update({ status: "REQUESTING AUTHORIZATION" }), ); - const offchainEffects = collectOffchainEffects( - simulationResult.privateExecutionResult, - ); + const offchainEffects = collectOffchainEffects(simulationResult.privateExecutionResult); const authWitnesses = await Promise.all( offchainEffects.map(async (effect) => { try { - const authRequest = await CallAuthorizationRequest.fromFields( - effect.data, - ); + const authRequest = await CallAuthorizationRequest.fromFields(effect.data); return this.createAuthWit(authRequest.onBehalfOf, { consumer: effect.contractAddress, innerHash: authRequest.innerHash, @@ -232,12 +206,11 @@ export class InternalWallet extends BaseNativeWallet { maxFeesPerGas: feeOptions.gasSettings.maxFeesPerGas, maxPriorityFeesPerGas: feeOptions.gasSettings.maxPriorityFeesPerGas, gasLimits: opts.fee?.gasSettings?.gasLimits ?? estimated.gasLimits, - teardownGasLimits: - opts.fee?.gasSettings?.teardownGasLimits ?? - estimated.teardownGasLimits, + teardownGasLimits: opts.fee?.gasSettings?.teardownGasLimits ?? estimated.teardownGasLimits, }); const sendOptions = { ...(await toSendOptions(opts)), + additionalScopes: [address], fee: { ...opts.fee, gasSettings }, }; await this.sendTx(executionPayload, sendOptions, interaction); @@ -263,11 +236,11 @@ export class InternalWallet extends BaseNativeWallet { opts: SendOptions, interaction?: WalletInteraction, ): Promise> { - const fee = await this.completeFeeOptions( - opts.from, - executionPayload.feePayer, - opts.fee?.gasSettings, - ); + const fee = await this.completeFeeOptions({ + from: opts.from, + feePayer: executionPayload.feePayer, + gasSettings: opts.fee?.gasSettings, + }); const txRequest = await this.createTxExecutionRequestFromPayloadAndFee( executionPayload, opts.from, @@ -289,21 +262,18 @@ export class InternalWallet extends BaseNativeWallet { ); const provenTx = await this.pxe.proveTx( txRequest, - this.scopesFrom(opts.from), + this.scopesFrom(opts.from, opts.additionalScopes), ); const provingTime = Date.now() - provingStartTime; const offchainOutput = extractOffchainOutput( provenTx.getOffchainEffects(), - provenTx.publicInputs.constants.anchorBlockHeader.globalVariables - .timestamp, + provenTx.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp, ); const tx = await provenTx.toTx(); const txHash = tx.getTxHash(); if (await this.aztecNode.getTxEffect(txHash)) { - throw new Error( - `A settled tx with equal hash ${txHash.toString()} exists.`, - ); + throw new Error(`A settled tx with equal hash ${txHash.toString()} exists.`); } // Track sending time @@ -337,9 +307,7 @@ export class InternalWallet extends BaseNativeWallet { } // Otherwise, wait for the full receipt (default behavior on wait: undefined) - await this.interactionManager.storeAndEmit( - interaction.update({ status: "MINING" }), - ); + await this.interactionManager.storeAndEmit(interaction.update({ status: "MINING" })); const miningStartTime = Date.now(); const waitOpts = typeof opts.wait === "object" ? opts.wait : undefined; const receipt = await waitForTx(this.aztecNode, txHash, { @@ -349,9 +317,7 @@ export class InternalWallet extends BaseNativeWallet { const miningTime = Date.now() - miningStartTime; const timingSummary = `Prove: ${formatDuration(provingTime)} | Send: ${formatDuration(sendingTime)} | Mine: ${formatDuration(miningTime)}`; - await this.interactionManager.storeAndEmit( - interaction.update({ description: timingSummary }), - ); + await this.interactionManager.storeAndEmit(interaction.update({ description: timingSummary })); if (interaction) { const rawStats = provenTx.stats; await this.db.updateTxPayloadStats(interaction.id, { @@ -377,6 +343,14 @@ export class InternalWallet extends BaseNativeWallet { return this.db.listInteractions(); } + deleteInteraction(id: string) { + return this.db.deleteInteraction(id); + } + + clearInteractions() { + return this.db.clearInteractions(); + } + async getExecutionTrace(interactionId: string): Promise< | { trace?: DecodedExecutionTrace; @@ -409,15 +383,13 @@ export class InternalWallet extends BaseNativeWallet { }; } - // Use the shared decoding cache from BaseNativeWallet + // Use the shared decoding cache from DemoWallet const decodingService = new TxDecodingService(this.decodingCache, this.log); - const parsedSimulationResult = TxSimulationResult.schema.parse( + const parsedSimulationResult = TxSimulationResultWithAppOffset.schema.parse( data.simulationResult, ); - const { executionTrace } = await decodingService.decodeTransaction( - parsedSimulationResult, - ); + const { executionTrace } = await decodingService.decodeTransaction(parsedSimulationResult); // stats is already enriched at origin with simulation/sending/mining wall-clock times. // Fall back to simStats for simulate-only interactions. const stats = data.metadata?.stats ?? parsedSimulationResult.stats; @@ -426,8 +398,7 @@ export class InternalWallet extends BaseNativeWallet { trace: executionTrace, stats, from: data.metadata?.from, - embeddedPaymentMethodFeePayer: - data.metadata?.embeddedPaymentMethodFeePayer, + embeddedPaymentMethodFeePayer: data.metadata?.embeddedPaymentMethodFeePayer, }; } @@ -446,21 +417,15 @@ export class InternalWallet extends BaseNativeWallet { return { requested, granted }; } - async resolveContractNames( - addresses: string[], - ): Promise> { + async resolveContractNames(addresses: string[]): Promise> { const result: Record = {}; for (const addrStr of addresses) { - result[addrStr] = await this.decodingCache.getAddressAlias( - AztecAddress.fromString(addrStr), - ); + result[addrStr] = await this.decodingCache.getAddressAlias(AztecAddress.fromString(addrStr)); } return result; } - async capabilityToStorageKeys( - capability: GrantedCapability, - ): Promise { + async capabilityToStorageKeys(capability: GrantedCapability): Promise { return this.db.capabilityToStorageKeys(capability); } @@ -473,10 +438,7 @@ export class InternalWallet extends BaseNativeWallet { this.emitCapabilityChange(); } - async revokeCapability( - appId: string, - capability: GrantedCapability, - ): Promise { + async revokeCapability(appId: string, capability: GrantedCapability): Promise { await this.db.revokeCapability(appId, capability); this.emitCapabilityChange(); } diff --git a/shared/src/wallet/database/wallet-db-authorization.test.ts b/shared/src/wallet/database/wallet-db-authorization.test.ts index b7686bd..9e9c495 100644 --- a/shared/src/wallet/database/wallet-db-authorization.test.ts +++ b/shared/src/wallet/database/wallet-db-authorization.test.ts @@ -26,21 +26,41 @@ const addr2 = AztecAddress.fromBigInt(2n); describe("capabilityToStorageKeys", () => { it("converts each capability type to correct keys", () => { // accounts - const accountsCap = { type: "accounts", canGet: true, canCreateAuthWit: true, accounts: [] } as any; + const accountsCap = { + type: "accounts", + canGet: true, + canCreateAuthWit: true, + accounts: [], + } as any; expect(db.capabilityToStorageKeys(accountsCap)).toEqual(["getAccounts", "createAuthWit"]); // accounts without authwit - const accountsNoAuth = { type: "accounts", canGet: true, canCreateAuthWit: false, accounts: [] } as any; + const accountsNoAuth = { + type: "accounts", + canGet: true, + canCreateAuthWit: false, + accounts: [], + } as any; expect(db.capabilityToStorageKeys(accountsNoAuth)).toEqual(["getAccounts"]); // contracts with specific addresses - const contractsCap = { type: "contracts", contracts: [addr1], canRegister: true, canGetMetadata: true } as any; + const contractsCap = { + type: "contracts", + contracts: [addr1], + canRegister: true, + canGetMetadata: true, + } as any; const contractKeys = db.capabilityToStorageKeys(contractsCap); expect(contractKeys).toContain(`registerContract:${addr1.toString()}`); expect(contractKeys).toContain(`getContractMetadata:${addr1.toString()}`); // contracts with wildcard - const contractsWild = { type: "contracts", contracts: "*", canRegister: true, canGetMetadata: false } as any; + const contractsWild = { + type: "contracts", + contracts: "*", + canRegister: true, + canGetMetadata: false, + } as any; expect(db.capabilityToStorageKeys(contractsWild)).toEqual(["registerContract:*"]); // simulation with patterns @@ -54,11 +74,18 @@ describe("capabilityToStorageKeys", () => { expect(simKeys).toContain(`simulateUtility:${addr2.toString()}:balance_of`); // transaction - const txCap = { type: "transaction", scope: [{ contract: addr1, function: "transfer" }] } as any; + const txCap = { + type: "transaction", + scope: [{ contract: addr1, function: "transfer" }], + } as any; expect(db.capabilityToStorageKeys(txCap)).toEqual([`sendTx:${addr1.toString()}:transfer`]); // data - const dataCap = { type: "data", addressBook: true, privateEvents: { contracts: [addr1] } } as any; + const dataCap = { + type: "data", + addressBook: true, + privateEvents: { contracts: [addr1] }, + } as any; const dataKeys = db.capabilityToStorageKeys(dataCap); expect(dataKeys).toContain("getAddressBook"); expect(dataKeys).toContain(`getPrivateEvents:${addr1.toString()}`); @@ -66,14 +93,19 @@ describe("capabilityToStorageKeys", () => { // contractClasses const classId = Fr.fromString("0x1234"); const classCap = { type: "contractClasses", classes: [classId], canGetMetadata: true } as any; - expect(db.capabilityToStorageKeys(classCap)).toEqual([`getContractClassMetadata:${classId.toString()}`]); + expect(db.capabilityToStorageKeys(classCap)).toEqual([ + `getContractClassMetadata:${classId.toString()}`, + ]); }); }); describe("storeCapabilityGrants", () => { it("stores additively and appends to __requested__", async () => { // Ad-hoc grant - await db.storePersistentAuthorization(APP_ID, "getAccounts", { persistent: true, accounts: [] }); + await db.storePersistentAuthorization(APP_ID, "getAccounts", { + persistent: true, + accounts: [], + }); // Manifest-based grant (should NOT delete the ad-hoc one) const simCap = { type: "simulation", transactions: { scope: "*" } } as any; @@ -89,7 +121,12 @@ describe("storeCapabilityGrants", () => { }); it("tracks denied capabilities in __requested__ when requestedCapabilities provided", async () => { - const grantedCap = { type: "accounts", canGet: true, canCreateAuthWit: false, accounts: [] } as any; + const grantedCap = { + type: "accounts", + canGet: true, + canCreateAuthWit: false, + accounts: [], + } as any; const deniedCap = { type: "simulation", transactions: { scope: "*" } } as any; await db.storeCapabilityGrants(APP_ID, [grantedCap], [grantedCap, deniedCap]); @@ -147,7 +184,9 @@ describe("reconstructCapabilitiesFromKeys", () => { persistent: true, accounts: [{ alias: "My Account", item: addr1.toString() }], }); - await db.storePersistentAuthorization(APP_ID, `registerContract:${addr1.toString()}`, { persistent: true }); + await db.storePersistentAuthorization(APP_ID, `registerContract:${addr1.toString()}`, { + persistent: true, + }); const caps = await db.reconstructCapabilitiesFromKeys(APP_ID); const accountsCap = caps.find((c) => c.type === "accounts") as any; @@ -162,7 +201,10 @@ describe("reconstructCapabilitiesFromKeys", () => { it("getRequestedCapabilities reconstructs from __requested__ keys", async () => { await db.appendRequestedKeys(APP_ID, [ - "getAccounts", "createAuthWit", "simulateTx:*", `registerContract:${addr1.toString()}`, + "getAccounts", + "createAuthWit", + "simulateTx:*", + `registerContract:${addr1.toString()}`, ]); const caps = await db.getRequestedCapabilities(APP_ID); @@ -236,7 +278,12 @@ describe("capability removal round-trips", () => { // Re-store only what remains (register for both, metadata for addr2 only) const newCaps = [ - { type: "contracts", contracts: [addr1, addr2], canRegister: true, canGetMetadata: false } as any, + { + type: "contracts", + contracts: [addr1, addr2], + canRegister: true, + canGetMetadata: false, + } as any, { type: "contracts", contracts: [addr2], canRegister: false, canGetMetadata: true } as any, ]; await db.storeCapabilityGrants(APP_ID, newCaps); @@ -282,20 +329,18 @@ describe("capability removal round-trips", () => { it("reconstruct → toKeys round-trip is lossless for per-contract permissions", async () => { // Store mixed permissions: addr1 has register only, addr2 has both - await db.storePersistentAuthorization( - APP_ID, `registerContract:${addr1.toString()}`, { persistent: true }, - ); - await db.storePersistentAuthorization( - APP_ID, `registerContract:${addr2.toString()}`, { persistent: true }, - ); - await db.storePersistentAuthorization( - APP_ID, `getContractMetadata:${addr2.toString()}`, { persistent: true }, - ); + await db.storePersistentAuthorization(APP_ID, `registerContract:${addr1.toString()}`, { + persistent: true, + }); + await db.storePersistentAuthorization(APP_ID, `registerContract:${addr2.toString()}`, { + persistent: true, + }); + await db.storePersistentAuthorization(APP_ID, `getContractMetadata:${addr2.toString()}`, { + persistent: true, + }); const granted = await db.reconstructCapabilitiesFromKeys(APP_ID); - const roundTrippedKeys = new Set( - granted.flatMap((c) => db.capabilityToStorageKeys(c)), - ); + const roundTrippedKeys = new Set(granted.flatMap((c) => db.capabilityToStorageKeys(c))); expect(roundTrippedKeys.has(`registerContract:${addr1.toString()}`)).toBe(true); expect(roundTrippedKeys.has(`registerContract:${addr2.toString()}`)).toBe(true); @@ -307,10 +352,12 @@ describe("capability removal round-trips", () => { it("reconstruct → toKeys round-trip handles simulation removal", async () => { const simCap = { type: "simulation", - transactions: { scope: [ - { contract: addr1, function: "swap" }, - { contract: addr2, function: "transfer" }, - ] }, + transactions: { + scope: [ + { contract: addr1, function: "swap" }, + { contract: addr2, function: "transfer" }, + ], + }, } as any; await db.storeCapabilityGrants(APP_ID, [simCap]); diff --git a/shared/src/wallet/database/wallet-db.ts b/shared/src/wallet/database/wallet-db.ts index 25a95e7..90bafae 100644 --- a/shared/src/wallet/database/wallet-db.ts +++ b/shared/src/wallet/database/wallet-db.ts @@ -11,21 +11,13 @@ import { } from "@aztec/aztec.js/wallet"; import { type Logger } from "@aztec/foundation/log"; import { type AztecAsyncMap, type AztecAsyncKVStore } from "@aztec/kv-store"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import { jsonStringify } from "@aztec/foundation/json-rpc"; import { TxSimulationResult } from "@aztec/stdlib/tx"; -export const AccountTypes = [ - "schnorr", - "ecdsasecp256r1", - "ecdsasecp256k1", -] as const; +export const AccountTypes = ["schnorr", "ecdsasecp256r1", "ecdsasecp256k1"] as const; export type AccountType = (typeof AccountTypes)[number]; - /** Per-function timing from simulation/proving */ interface FunctionTiming { functionName: string; @@ -89,17 +81,12 @@ export class WalletDB { leafIndex: bigint, ) { let stackPointer = - ( - await this.bridgedFeeJuice.getAsync( - `${recipient.toString()}:stackPointer`, - ) - )?.readInt8() || 0; + (await this.bridgedFeeJuice.getAsync(`${recipient.toString()}:stackPointer`))?.readInt8() || + 0; stackPointer++; await this.bridgedFeeJuice.set( `${recipient.toString()}:${stackPointer}`, - Buffer.from( - `${amount.toString()}:${secret.toString()}:${leafIndex.toString()}`, - ), + Buffer.from(`${amount.toString()}:${secret.toString()}:${leafIndex.toString()}`), ); await this.bridgedFeeJuice.set( `${recipient.toString()}:stackPointer`, @@ -112,14 +99,9 @@ export class WalletDB { async popBridgedFeeJuice(recipient: AztecAddress) { let stackPointer = - ( - await this.bridgedFeeJuice.getAsync( - `${recipient.toString()}:stackPointer`, - ) - )?.readInt8() || 0; - const result = await this.bridgedFeeJuice.getAsync( - `${recipient.toString()}:${stackPointer}`, - ); + (await this.bridgedFeeJuice.getAsync(`${recipient.toString()}:stackPointer`))?.readInt8() || + 0; + const result = await this.bridgedFeeJuice.getAsync(`${recipient.toString()}:${stackPointer}`); if (!result) { throw new Error( `No stored fee juice available for recipient ${recipient.toString()}. Please provide claim amount and secret. Stack pointer ${stackPointer}`, @@ -157,10 +139,7 @@ export class WalletDB { }, ) { if (alias) { - await this.aliases.set( - `accounts:${alias}`, - Buffer.from(address.toString()), - ); + await this.aliases.set(`accounts:${alias}`, Buffer.from(address.toString())); } await this.accounts.set(`${address.toString()}:type`, Buffer.from(type)); await this.accounts.set(`${address.toString()}:sk`, secretKey.toBuffer()); @@ -170,10 +149,7 @@ export class WalletDB { "toBuffer" in signingKey ? signingKey.toBuffer() : signingKey, ); // New accounts are undeployed by default - await this.accounts.set( - `${address.toString()}:deployed`, - Buffer.from("false"), - ); + await this.accounts.set(`${address.toString()}:deployed`, Buffer.from("false")); this.logger.info( `Account stored in database with alias${alias ? `es last & ${alias}` : " last"}`, ); @@ -193,14 +169,9 @@ export class WalletDB { await this.accounts.set(`${address.toString()}:${metadataKey}`, metadata); } - async retrieveAccountMetadata( - aliasOrAddress: AztecAddress | string, - metadataKey: string, - ) { + async retrieveAccountMetadata(aliasOrAddress: AztecAddress | string, metadataKey: string) { const { address } = await this.retrieveAccount(aliasOrAddress); - const result = await this.accounts.getAsync( - `${address.toString()}:${metadataKey}`, - ); + const result = await this.accounts.getAsync(`${address.toString()}:${metadataKey}`); if (!result) { throw new Error( `Could not find metadata with key ${metadataKey} for account ${aliasOrAddress}`, @@ -210,49 +181,34 @@ export class WalletDB { } async retrieveAccount(address: AztecAddress | string) { - const secretKeyBuffer = await this.accounts.getAsync( - `${address.toString()}:sk`, - ); + const secretKeyBuffer = await this.accounts.getAsync(`${address.toString()}:sk`); if (!secretKeyBuffer) { throw new Error( `Could not find ${address}:sk. Account "${address.toString}" does not exist on this wallet.`, ); } const secretKey = Fr.fromBuffer(secretKeyBuffer); - const salt = Fr.fromBuffer( - await this.accounts.getAsync(`${address.toString()}:salt`)!, - ); - const type = ( - await this.accounts.getAsync(`${address.toString()}:type`)! - ).toString("utf8") as AccountType; - const signingKey = await this.accounts.getAsync( - `${address.toString()}:signingKey`, - )!; - const deployedBuf = await this.accounts.getAsync( - `${address.toString()}:deployed`, - ); + const salt = Fr.fromBuffer(await this.accounts.getAsync(`${address.toString()}:salt`)!); + const type = (await this.accounts.getAsync(`${address.toString()}:type`)!).toString( + "utf8", + ) as AccountType; + const signingKey = await this.accounts.getAsync(`${address.toString()}:signingKey`)!; + const deployedBuf = await this.accounts.getAsync(`${address.toString()}:deployed`); const deployed = deployedBuf?.toString("utf8") === "true"; return { address, secretKey, salt, type, signingKey, deployed }; } async markAccountDeployed(address: AztecAddress): Promise { - await this.accounts.set( - `${address.toString()}:deployed`, - Buffer.from("true"), - ); + await this.accounts.set(`${address.toString()}:deployed`, Buffer.from("true")); this.logger.info(`Account ${address.toString()} marked as deployed`); } async isAccountDeployed(address: AztecAddress): Promise { - const buf = await this.accounts.getAsync( - `${address.toString()}:deployed`, - ); + const buf = await this.accounts.getAsync(`${address.toString()}:deployed`); return buf?.toString("utf8") === "true"; } - async listAccounts(opts?: { - deployedOnly?: boolean; - }): Promise[]> { + async listAccounts(opts?: { deployedOnly?: boolean }): Promise[]> { // Collect all account aliases first to avoid interleaving reads across // IndexedDB stores (which kills the cursor's transaction). const allAccounts: Aliased[] = []; @@ -299,23 +255,17 @@ export class WalletDB { await this.aliases.delete(account?.alias); } - async storeInteraction( - interaction: WalletInteraction, - ) { + async storeInteraction(interaction: WalletInteraction) { await this.interactions.set(interaction.id, interaction.toBuffer()); } - async createOrUpdateInteraction( - interaction: WalletInteraction, - ) { + async createOrUpdateInteraction(interaction: WalletInteraction) { const { id, status, complete } = interaction; const maybeInteractionBuffer = await this.interactions.getAsync(id); if (!maybeInteractionBuffer) { await this.storeInteraction(interaction); } else { - const storedInteraction = WalletInteraction.fromBuffer( - maybeInteractionBuffer, - ); + const storedInteraction = WalletInteraction.fromBuffer(maybeInteractionBuffer); storedInteraction.status = status; storedInteraction.complete = complete; await this.storeInteraction(storedInteraction); @@ -331,15 +281,22 @@ export class WalletDB { return result.sort((a, b) => b.timestamp - a.timestamp); } + async deleteInteraction(id: string) { + await this.interactions.delete(id); + } + + async clearInteractions() { + for await (const [id] of this.interactions.entriesAsync()) { + await this.interactions.delete(id); + } + } + async storePersistentAuthorization(appId: string, key: string, data: any) { const fullKey = `${appId}:${key}`; await this.authorizations.set(fullKey, Buffer.from(jsonStringify(data))); } - async retrievePersistentAuthorization( - appId: string, - key: string, - ): Promise { + async retrievePersistentAuthorization(appId: string, key: string): Promise { const fullKey = `${appId}:${key}`; const result = await this.authorizations.getAsync(fullKey); if (!result) { @@ -393,9 +350,7 @@ export class WalletDB { // Track all requested capabilities (including denied ones) in __requested__ // so the Apps tab can display them for future re-granting const toTrack = requestedCapabilities ?? granted; - const requestedKeys = toTrack.flatMap((cap) => - this.capabilityToStorageKeys(cap), - ); + const requestedKeys = toTrack.flatMap((cap) => this.capabilityToStorageKeys(cap)); if (requestedKeys.length > 0) { await this.appendRequestedKeys(appId, requestedKeys); } @@ -405,10 +360,7 @@ export class WalletDB { * Revoke a specific capability by deleting its authorization keys. * Does NOT remove from __requested__ — the capability remains visible for re-granting. */ - async revokeCapability( - appId: string, - capability: GrantedCapability, - ): Promise { + async revokeCapability(appId: string, capability: GrantedCapability): Promise { const keys = this.capabilityToStorageKeys(capability); for (const key of keys) { const fullKey = `${appId}:${key}`; @@ -424,17 +376,11 @@ export class WalletDB { * This tracks everything the app has ever requested, regardless of approval. * The set only grows — keys are never removed from __requested__. */ - async appendRequestedKeys( - appId: string, - keys: string[], - ): Promise { + async appendRequestedKeys(appId: string, keys: string[]): Promise { const existing = await this.getRequestedKeys(appId); const merged = new Set([...existing, ...keys]); const fullKey = `${appId}:__requested__`; - await this.authorizations.set( - fullKey, - Buffer.from(JSON.stringify([...merged])), - ); + await this.authorizations.set(fullKey, Buffer.from(JSON.stringify([...merged]))); } /** @@ -452,9 +398,7 @@ export class WalletDB { * Reconstruct capabilities from the __requested__ keys. * Returns everything the app has ever requested (for the Apps tab display). */ - async getRequestedCapabilities( - appId: string, - ): Promise { + async getRequestedCapabilities(appId: string): Promise { const keys = await this.getRequestedKeys(appId); if (keys.length === 0) return []; return this.reconstructCapabilitiesFromKeyList(keys); @@ -467,9 +411,7 @@ export class WalletDB { * @param capability - The granted capability * @returns Object with capability-specific data fields */ - private extractCapabilityData( - capability: GrantedCapability, - ): Record { + private extractCapabilityData(capability: GrantedCapability): Record { switch (capability.type) { case "accounts": { const accountsCap = capability as GrantedAccountsCapability; @@ -535,9 +477,7 @@ export class WalletDB { const classes = contractClassesCap.classes === "*" ? ["*"] - : contractClassesCap.classes.map((classId: any) => - classId.toString(), - ); + : contractClassesCap.classes.map((classId: any) => classId.toString()); if (contractClassesCap.canGetMetadata) { keys.push(...classes.map((c) => `getContractClassMetadata:${c}`)); @@ -557,8 +497,7 @@ export class WalletDB { // Pattern-based scopes - only generate keys for simulateTx // (utilities use simulateUtility, not simulateTx) for (const pattern of simCap.transactions.scope) { - const contract = - pattern.contract === "*" ? "*" : pattern.contract.toString(); + const contract = pattern.contract === "*" ? "*" : pattern.contract.toString(); const func = pattern.function; keys.push(`simulateTx:${contract}:${func}`); } @@ -571,8 +510,7 @@ export class WalletDB { } else { // Pattern-based scopes - only generate keys for simulateUtility for (const pattern of simCap.utilities.scope) { - const contract = - pattern.contract === "*" ? "*" : pattern.contract.toString(); + const contract = pattern.contract === "*" ? "*" : pattern.contract.toString(); const func = pattern.function; keys.push(`simulateUtility:${contract}:${func}`); } @@ -589,8 +527,7 @@ export class WalletDB { } else { // Pattern-based scopes for (const pattern of txCap.scope) { - const contract = - pattern.contract === "*" ? "*" : pattern.contract.toString(); + const contract = pattern.contract === "*" ? "*" : pattern.contract.toString(); const func = pattern.function; keys.push(`sendTx:${contract}:${func}`); } @@ -629,7 +566,10 @@ export class WalletDB { const keysToDelete: string[] = []; const prefix = `${appId}:`; - for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + for await (const key of this.authorizations.keysAsync({ + start: prefix, + end: `${prefix}\uffff`, + })) { keysToDelete.push(key); } @@ -638,9 +578,7 @@ export class WalletDB { await this.authorizations.delete(key); } - this.logger.info( - `Revoked ${keysToDelete.length} persistent authorizations for ${appId}`, - ); + this.logger.info(`Revoked ${keysToDelete.length} persistent authorizations for ${appId}`); } /** @@ -650,13 +588,14 @@ export class WalletDB { * @param appId - Application ID to get capabilities for * @returns Array of granted capabilities */ - async reconstructCapabilitiesFromKeys( - appId: string, - ): Promise { + async reconstructCapabilitiesFromKeys(appId: string): Promise { // Collect all authorization keys for this app (excluding metadata) const prefix = `${appId}:`; const keys: string[] = []; - for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + for await (const key of this.authorizations.keysAsync({ + start: prefix, + end: `${prefix}\uffff`, + })) { const storageKey = key.substring(prefix.length); if (!storageKey.startsWith("__")) { keys.push(storageKey); @@ -674,10 +613,7 @@ export class WalletDB { * @param keys - Storage keys to reconstruct from * @param appId - Optional appId for fetching stored data (accounts, contacts). If omitted, data-dependent fields use defaults. */ - reconstructCapabilitiesFromKeyList( - keys: string[], - appId?: string, - ): Promise { + reconstructCapabilitiesFromKeyList(keys: string[], appId?: string): Promise { return this._reconstructCapabilities(keys, appId); } @@ -688,26 +624,16 @@ export class WalletDB { const capabilities: GrantedCapability[] = []; // Group keys by method to reconstruct capabilities - const accountKeys = keys.filter( - (k) => k === "getAccounts" || k === "createAuthWit", - ); + const accountKeys = keys.filter((k) => k === "getAccounts" || k === "createAuthWit"); const contractKeys = keys.filter( - (k) => - k.startsWith("registerContract:") || - k.startsWith("getContractMetadata:"), - ); - const contractClassKeys = keys.filter((k) => - k.startsWith("getContractClassMetadata:"), + (k) => k.startsWith("registerContract:") || k.startsWith("getContractMetadata:"), ); + const contractClassKeys = keys.filter((k) => k.startsWith("getContractClassMetadata:")); const simulateTxKeys = keys.filter((k) => k.startsWith("simulateTx:")); - const simulateUtilityKeys = keys.filter((k) => - k.startsWith("simulateUtility:"), - ); + const simulateUtilityKeys = keys.filter((k) => k.startsWith("simulateUtility:")); const sendTxKeys = keys.filter((k) => k.startsWith("sendTx:")); const addressBookKeys = keys.filter((k) => k === "getAddressBook"); - const privateEventsKeys = keys.filter((k) => - k.startsWith("getPrivateEvents:"), - ); + const privateEventsKeys = keys.filter((k) => k.startsWith("getPrivateEvents:")); // Reconstruct AccountsCapability if (accountKeys.length > 0) { @@ -717,16 +643,10 @@ export class WalletDB { // Fetch accounts from stored data if appId available let accounts: Array<{ alias: string; item: AztecAddress }> = []; if (canGet && appId) { - const data = await this.retrievePersistentAuthorization( - appId, - "getAccounts", - ); + const data = await this.retrievePersistentAuthorization(appId, "getAccounts"); accounts = (data?.accounts || []).map((acc: any) => ({ alias: acc.alias, - item: - typeof acc.item === "string" - ? AztecAddress.fromString(acc.item) - : acc.item, + item: typeof acc.item === "string" ? AztecAddress.fromString(acc.item) : acc.item, })); } @@ -744,9 +664,7 @@ export class WalletDB { // register doesn't get a phantom metadata key). if (contractKeys.length > 0) { const registerAddrs = new Set( - contractKeys - .filter((k) => k.startsWith("registerContract:")) - .map((k) => k.split(":")[1]), + contractKeys.filter((k) => k.startsWith("registerContract:")).map((k) => k.split(":")[1]), ); const metadataAddrs = new Set( contractKeys @@ -834,10 +752,7 @@ export class WalletDB { } else { const patterns = simulateTxKeys.map((k) => { const parts = k.split(":"); - const contract = - parts[1] === "*" - ? ("*" as const) - : AztecAddress.fromString(parts[1]); + const contract = parts[1] === "*" ? ("*" as const) : AztecAddress.fromString(parts[1]); const func = parts[2] || "*"; return { contract, function: func }; }); @@ -846,18 +761,13 @@ export class WalletDB { } if (simulateUtilityKeys.length > 0) { - const hasWildcard = simulateUtilityKeys.some( - (k) => k === "simulateUtility:*", - ); + const hasWildcard = simulateUtilityKeys.some((k) => k === "simulateUtility:*"); if (hasWildcard) { simCap.utilities = { scope: "*" as const }; } else { const patterns = simulateUtilityKeys.map((k) => { const parts = k.split(":"); - const contract = - parts[1] === "*" - ? ("*" as const) - : AztecAddress.fromString(parts[1]); + const contract = parts[1] === "*" ? ("*" as const) : AztecAddress.fromString(parts[1]); const func = parts[2] || "*"; return { contract, function: func }; }); @@ -879,10 +789,7 @@ export class WalletDB { } else { const patterns = sendTxKeys.map((k) => { const parts = k.split(":"); - const contract = - parts[1] === "*" - ? ("*" as const) - : AztecAddress.fromString(parts[1]); + const contract = parts[1] === "*" ? ("*" as const) : AztecAddress.fromString(parts[1]); const func = parts[2] || "*"; return { contract, function: func }; }); @@ -900,10 +807,7 @@ export class WalletDB { if (addressBookKeys.length > 0) { // Fetch contacts from stored data if appId available if (appId) { - const data = await this.retrievePersistentAuthorization( - appId, - "getAddressBook", - ); + const data = await this.retrievePersistentAuthorization(appId, "getAddressBook"); const contacts = (data?.contacts || []).map((contact: any) => ({ alias: contact.alias, item: @@ -921,9 +825,7 @@ export class WalletDB { const contractAddrs = privateEventsKeys.map((k) => k.split(":")[1]); const contracts = contractAddrs.includes("*") ? ("*" as const) - : contractAddrs - .filter((c) => c !== "*") - .map((c) => AztecAddress.fromString(c)); + : contractAddrs.filter((c) => c !== "*").map((c) => AztecAddress.fromString(c)); dataCap.privateEvents = { contracts }; } @@ -955,20 +857,14 @@ export class WalletDB { /** * Update the getAccounts authorization for an app */ - async updateAccountAuthorization( - appId: string, - accounts: Aliased[], - ) { + async updateAccountAuthorization(appId: string, accounts: Aliased[]) { await this.storePersistentAuthorization(appId, "getAccounts", { accounts }); } /** * Update the getAddressBook authorization for an app */ - async updateAddressBookAuthorization( - appId: string, - contacts: Aliased[], - ) { + async updateAddressBookAuthorization(appId: string, contacts: Aliased[]) { await this.storePersistentAuthorization(appId, "getAddressBook", { contacts, }); @@ -996,7 +892,10 @@ export class WalletDB { async revokeAppAuthorizations(appId: string) { const prefix = `${appId}:`; const keysToDelete: string[] = []; - for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + for await (const key of this.authorizations.keysAsync({ + start: prefix, + end: `${prefix}\uffff`, + })) { keysToDelete.push(key); } @@ -1023,9 +922,7 @@ export class WalletDB { metadata, }); await this.txPayloadData.set(payloadHash, data); - this.logger.info( - `Transaction simulation stored for payload hash ${payloadHash}`, - ); + this.logger.info(`Transaction simulation stored for payload hash ${payloadHash}`); } async getTxPayloadData(payloadHash: string): Promise< @@ -1051,10 +948,7 @@ export class WalletDB { * Works for txs that went through simulation (upserts the metadata.stats field) * and for txs that didn't simulate (e.g. createAccount — stores stats-only record). */ - async updateTxPayloadStats( - payloadHash: string, - stats: StoredStats, - ): Promise { + async updateTxPayloadStats(payloadHash: string, stats: StoredStats): Promise { const existing = await this.getTxPayloadData(payloadHash); const metadata = existing?.metadata ?? {}; metadata.stats = stats; @@ -1076,9 +970,7 @@ export class WalletDB { this.logger.info(`Utility trace stored for payload hash ${payloadHash}`); } - async getUtilityTrace( - payloadHash: string, - ): Promise<{ trace: any; stats?: any } | undefined> { + async getUtilityTrace(payloadHash: string): Promise<{ trace: any; stats?: any } | undefined> { const result = await this.txPayloadData.getAsync(payloadHash); if (!result) { return undefined; @@ -1097,7 +989,10 @@ export class WalletDB { async getAllAuthorizationKeys(appId: string): Promise { const prefix = `${appId}:`; const keys: string[] = []; - for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + for await (const key of this.authorizations.keysAsync({ + start: prefix, + end: `${prefix}\uffff`, + })) { keys.push(key.substring(prefix.length)); } return keys; @@ -1113,8 +1008,7 @@ export class WalletDB { ): Promise> { const results = new Map(); for (const key of storageKeys) { - const exists = - (await this.retrievePersistentAuthorization(appId, key)) !== undefined; + const exists = (await this.retrievePersistentAuthorization(appId, key)) !== undefined; results.set(key, exists); } return results; @@ -1131,10 +1025,7 @@ export class WalletDB { ): Promise { const key = `${appId}:__behavior__`; const expiresAt = Date.now() + duration; - await this.authorizations.set( - key, - Buffer.from(jsonStringify({ mode, expiresAt })), - ); + await this.authorizations.set(key, Buffer.from(jsonStringify({ mode, expiresAt }))); this.logger.info( `Authorization behavior stored for ${appId}: mode=${mode}, expiresAt=${new Date(expiresAt).toISOString()}`, ); @@ -1181,7 +1072,10 @@ export class WalletDB { const prefix = `${appId}:`; const entries: Record = {}; - for await (const [key, value] of this.authorizations.entriesAsync({ start: prefix, end: `${prefix}\uffff` })) { + for await (const [key, value] of this.authorizations.entriesAsync({ + start: prefix, + end: `${prefix}\uffff`, + })) { const storageKey = key.substring(prefix.length); try { entries[storageKey] = JSON.parse(value.toString()); @@ -1215,17 +1109,17 @@ export class WalletDB { // Collect existing keys for this app so we can delete stale ones const existingKeys = new Set(); - for await (const key of this.authorizations.keysAsync({ start: prefix, end: `${prefix}\uffff` })) { + for await (const key of this.authorizations.keysAsync({ + start: prefix, + end: `${prefix}\uffff`, + })) { existingKeys.add(key); } // Write all entries from the cookie for (const [storageKey, value] of Object.entries(entries)) { const fullKey = `${appId}:${storageKey}`; - await this.authorizations.set( - fullKey, - Buffer.from(jsonStringify(value)), - ); + await this.authorizations.set(fullKey, Buffer.from(jsonStringify(value))); existingKeys.delete(fullKey); // Mark as seen updated++; } diff --git a/shared/src/wallet/decoding/call-authorization-formatter.ts b/shared/src/wallet/decoding/call-authorization-formatter.ts index 3e21787..55ac5a6 100644 --- a/shared/src/wallet/decoding/call-authorization-formatter.ts +++ b/shared/src/wallet/decoding/call-authorization-formatter.ts @@ -1,12 +1,7 @@ import { decodeFromAbi } from "@aztec/aztec.js/abi"; -import { type Aliased } from "@aztec/aztec.js/wallet"; import { CallAuthorizationRequest } from "@aztec/aztec.js/authorization"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; -import { - FunctionCall, - getFunctionArtifact, - type AbiDecoded, -} from "@aztec/stdlib/abi"; +import { FunctionCall, getFunctionArtifact, type AbiDecoded } from "@aztec/stdlib/abi"; import type { OffchainEffect } from "@aztec/stdlib/tx"; import type { DecodingCache } from "./decoding-cache"; @@ -57,9 +52,7 @@ export class CallAuthorizationFormatter { } if (typeof value === "object") { - return JSON.stringify(value, (_, v) => - typeof v === "bigint" ? v.toString() : v - ); + return JSON.stringify(value, (_, v) => (typeof v === "bigint" ? v.toString() : v)); } return String(value); @@ -68,22 +61,16 @@ export class CallAuthorizationFormatter { async parseCallAuthorizationFromEffect(effect: OffchainEffect) { let callAuthorizationRequest: CallAuthorizationRequest | undefined; try { - callAuthorizationRequest = await CallAuthorizationRequest.fromFields( - effect.data - ); - const instance = await this.cache.getContractInstance( - effect.contractAddress - ); - const artifact = await this.cache.getContractArtifact( - instance.currentContractClassId - ); + callAuthorizationRequest = await CallAuthorizationRequest.fromFields(effect.data); + const instance = await this.cache.getContractInstance(effect.contractAddress); + const artifact = await this.cache.getContractArtifact(instance.currentContractClassId); const functionAbi = await getFunctionArtifact( artifact, - callAuthorizationRequest.functionSelector + callAuthorizationRequest.functionSelector, ); const callData = decodeFromAbi( functionAbi.parameters.map((param) => param.type), - callAuthorizationRequest.args + callAuthorizationRequest.args, ) as AbiDecoded[]; const parameters = functionAbi.parameters.map((param, i) => ({ name: param.name, @@ -102,10 +89,10 @@ export class CallAuthorizationFormatter { functionAbi.isStatic, false, callAuthorizationRequest.args, - functionAbi.returnTypes + functionAbi.returnTypes, ), }; - } catch (error) { + } catch { return undefined; } } @@ -135,11 +122,7 @@ export class CallAuthorizationFormatter { let formattedValue = this.formatAbiValue(param.value); // If the value looks like an address, try to get its alias - if ( - param.value && - typeof param.value === "object" && - "toString" in param.value - ) { + if (param.value && typeof param.value === "object" && "toString" in param.value) { const valueStr = param.value.toString(); if (valueStr.startsWith("0x") && valueStr.length === 66) { try { @@ -156,7 +139,7 @@ export class CallAuthorizationFormatter { name: param.name || "arg", value: formattedValue, }; - }) + }), ); return { @@ -187,12 +170,12 @@ export class CallAuthorizationFormatter { functionCall: FunctionCall; } | undefined - > + >, ): Promise { const formattedCalls = await Promise.all( callAuthorizations .filter(Boolean) - .map((auth) => this.formatCallAuthorizationForDisplay(auth!)) + .map((auth) => this.formatCallAuthorizationForDisplay(auth!)), ); return formattedCalls; diff --git a/shared/src/wallet/decoding/decoding-cache.ts b/shared/src/wallet/decoding/decoding-cache.ts index 145103c..5401f18 100644 --- a/shared/src/wallet/decoding/decoding-cache.ts +++ b/shared/src/wallet/decoding/decoding-cache.ts @@ -31,9 +31,7 @@ export class DecodingCache { /** * Get contract metadata (instance) for an address, with caching. */ - async getContractInstance( - address: AztecAddress, - ): Promise { + async getContractInstance(address: AztecAddress): Promise { const key = address.toString(); if (this.instanceCache.has(key)) { @@ -42,9 +40,7 @@ export class DecodingCache { const instance = await this.pxe.getContractInstance(address); if (!instance) { - throw new Error( - `Contract instance not found for address ${address.toString()}`, - ); + throw new Error(`Contract instance not found for address ${address.toString()}`); } this.instanceCache.set(key, instance); return instance; @@ -70,10 +66,7 @@ export class DecodingCache { * This allows artifacts from earlier operations in a batch to be available * for decoding in later operations, without persisting to PXE. */ - cacheArtifactForBatch( - contractClassId: any, - artifact: ContractArtifact, - ): void { + cacheArtifactForBatch(contractClassId: any, artifact: ContractArtifact): void { const key = contractClassId.toString(); this.artifactCache.set(key, artifact); } @@ -136,9 +129,7 @@ export class DecodingCache { // Try to get contract metadata for more info (PXE-only calls now, no wallet-db interleaving) try { const instance = await this.getContractInstance(address); - const artifact = await this.getContractArtifact( - instance.currentContractClassId, - ); + const artifact = await this.getContractArtifact(instance.currentContractClassId); if (artifact) { this.addressAliasCache.set(key, artifact.name); return artifact.name; @@ -167,24 +158,18 @@ export class DecodingCache { let contractName = artifact?.name; // Check if instanceData contains an artifact - if ( - !contractName && - typeof instance === "object" && - "artifact" in instance - ) { + if (!contractName && typeof instance === "object" && "artifact" in instance) { contractName = (instance as any).artifact?.name; } // If we still don't have a name, try the artifact cache using the instance's contract class ID if (!contractName && instance?.currentContractClassId) { try { - const cachedArtifact = await this.getContractArtifact( - instance.currentContractClassId, - ); + const cachedArtifact = await this.getContractArtifact(instance.currentContractClassId); if (cachedArtifact) { contractName = cachedArtifact.name; } - } catch (error) { + } catch { // Artifact not in cache or PXE, continue to next method } } @@ -198,7 +183,7 @@ export class DecodingCache { if (!alias.includes("...")) { contractName = alias; } - } catch (error) { + } catch { // Ignore errors - we'll fall back to "Unknown Contract" } } diff --git a/shared/src/wallet/decoding/tx-callstack-decoder.ts b/shared/src/wallet/decoding/tx-callstack-decoder.ts index 6b69436..9d8d92f 100644 --- a/shared/src/wallet/decoding/tx-callstack-decoder.ts +++ b/shared/src/wallet/decoding/tx-callstack-decoder.ts @@ -1,7 +1,4 @@ -import type { - TxSimulationResult, - PrivateCallExecutionResult, -} from "@aztec/stdlib/tx"; +import type { TxSimulationResult, PrivateCallExecutionResult } from "@aztec/stdlib/tx"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; import { getFunctionArtifact, @@ -88,7 +85,7 @@ export class TxCallStackDecoder { const addr = AztecAddress.fromString(valueStr); const alias = await this.cache.getAddressAlias(addr); formatted = `${alias} (${formatted.slice(0, 10)}...${formatted.slice(-8)})`; - } catch (error) { + } catch { // Not a valid address, use original formatted value } } @@ -158,9 +155,7 @@ export class TxCallStackDecoder { const startCounter = call.publicInputs.startSideEffectCounter.toNumber(); const endCounter = call.publicInputs.endSideEffectCounter.toNumber(); - const contractName = await this.cache.getAddressAlias( - callContext.contractAddress, - ); + const contractName = await this.cache.getAddressAlias(callContext.contractAddress); const callerName = await this.cache.getAddressAlias(callContext.msgSender); let functionName = `0x${callContext.functionSelector.toString().slice(2, 10)}`; @@ -168,41 +163,28 @@ export class TxCallStackDecoder { let returnValues: Array<{ name: string; value: string }> = []; try { - const instance = await this.cache.getContractInstance( - callContext.contractAddress, - ); - const artifact = await this.cache.getContractArtifact( - instance.currentContractClassId, - ); - const functionAbi = await getFunctionArtifact( - artifact, - callContext.functionSelector, - ); + const instance = await this.cache.getContractInstance(callContext.contractAddress); + const artifact = await this.cache.getContractArtifact(instance.currentContractClassId); + const functionAbi = await getFunctionArtifact(artifact, callContext.functionSelector); functionName = functionAbi.name; // Extract arguments from partialWitness if (functionAbi.parameters.length > 0 && call.partialWitness) { try { - const argsValues = this.extractArgsFromWitness( - call.partialWitness, - functionAbi, - ); + const argsValues = this.extractArgsFromWitness(call.partialWitness, functionAbi); // Reuse the generic argument decoding helper args = await this.decodeAndFormatArguments(functionAbi, argsValues); - } catch (error) { + } catch { // Silently fail - args will remain empty } } // Decode return values - reuse the generic return value decoding helper if (functionAbi.returnTypes.length > 0) { - returnValues = await this.decodeAndFormatReturnValues( - functionAbi, - call.returnValues, - ); + returnValues = await this.decodeAndFormatReturnValues(functionAbi, call.returnValues); } - } catch (error) { + } catch { // If we can't decode, use raw values with consistent naming const rvCount = call.returnValues.length; returnValues = await Promise.all( @@ -222,10 +204,9 @@ export class TxCallStackDecoder { })); // Combine with parent's public enqueues for proper ordering - const allPublicEnqueues = [ - ...parentPublicEnqueues, - ...thisCallPublicEnqueues, - ].sort((a, b) => a.counter - b.counter); + const allPublicEnqueues = [...parentPublicEnqueues, ...thisCallPublicEnqueues].sort( + (a, b) => a.counter - b.counter, + ); // Build nested events with interleaved public enqueues const nestedEvents: ExecutionEvent[] = []; @@ -237,8 +218,7 @@ export class TxCallStackDecoder { if (call.nestedExecutionResults && call.nestedExecutionResults.length > 0) { for (let i = 0; i < call.nestedExecutionResults.length; i++) { const nestedCall = call.nestedExecutionResults[i]; - const nestedStartCounter = - nestedCall.publicInputs.startSideEffectCounter.toNumber(); + const nestedStartCounter = nestedCall.publicInputs.startSideEffectCounter.toNumber(); // Add public enqueues that happened before this nested call starts const enqueuedBefore = allPublicEnqueues.filter( @@ -249,39 +229,24 @@ export class TxCallStackDecoder { ); for (const enq of enqueuedBefore) { - const event = await this.decodePublicCall( - enq.request, - depth + 1, - enq.counter, - ); + const event = await this.decodePublicCall(enq.request, depth + 1, enq.counter); nestedEvents.push(event); addedEnqueues.add(enq.counter); } // Recursively decode nested call - const nestedEvent = await this.decodePrivateCall( - nestedCall, - depth + 1, - allPublicEnqueues, - ); + const nestedEvent = await this.decodePrivateCall(nestedCall, depth + 1, allPublicEnqueues); nestedEvents.push(nestedEvent); } } // Add any remaining public enqueues after all nested calls const enqueuedAfter = allPublicEnqueues.filter( - (e) => - e.counter >= startCounter && - e.counter < endCounter && - !addedEnqueues.has(e.counter), + (e) => e.counter >= startCounter && e.counter < endCounter && !addedEnqueues.has(e.counter), ); for (const enq of enqueuedAfter) { - const event = await this.decodePublicCall( - enq.request, - depth + 1, - enq.counter, - ); + const event = await this.decodePublicCall(enq.request, depth + 1, enq.counter); nestedEvents.push(event); addedEnqueues.add(enq.counter); } @@ -311,9 +276,7 @@ export class TxCallStackDecoder { depth: number, counter: number, ): Promise { - const contractName = await this.cache.getAddressAlias( - request.contractAddress, - ); + const contractName = await this.cache.getAddressAlias(request.contractAddress); const callerName = await this.cache.getAddressAlias(request.msgSender); // Get calldata using the calldataHash @@ -328,25 +291,16 @@ export class TxCallStackDecoder { // Try to resolve function name and decode arguments from contract ABI try { - const instance = await this.cache.getContractInstance( - request.contractAddress, - ); - const artifact = await this.cache.getContractArtifact( - instance.currentContractClassId, - ); + const instance = await this.cache.getContractInstance(request.contractAddress); + const artifact = await this.cache.getContractArtifact(instance.currentContractClassId); const allAbis = await getAllFunctionAbis(artifact); const abisWithSelector = await Promise.all( allAbis.map(async (abi) => ({ ...abi, - selector: await FunctionSelector.fromNameAndParameters( - abi.name, - abi.parameters, - ), + selector: await FunctionSelector.fromNameAndParameters(abi.name, abi.parameters), })), ); - const functionAbi = abisWithSelector.find((abi) => - abi.selector.equals(functionSelector), - ); + const functionAbi = abisWithSelector.find((abi) => abi.selector.equals(functionSelector)); if (functionAbi) { functionName = functionAbi.name; @@ -388,12 +342,8 @@ export class TxCallStackDecoder { // Build calldata map from publicFunctionCalldata this.calldataMap.clear(); if (simulationResult.privateExecutionResult.publicFunctionCalldata) { - for (const hashedCalldata of simulationResult.privateExecutionResult - .publicFunctionCalldata) { - this.calldataMap.set( - hashedCalldata.hash.toString(), - hashedCalldata.values, - ); + for (const hashedCalldata of simulationResult.privateExecutionResult.publicFunctionCalldata) { + this.calldataMap.set(hashedCalldata.hash.toString(), hashedCalldata.values); } } @@ -403,7 +353,7 @@ export class TxCallStackDecoder { const privateExecution = await this.decodePrivateCall(entrypoint, 0, []); // Collect all public calls in execution order (by counter) - let allPublicCalls: PublicCallEvent[] = []; + const allPublicCalls: PublicCallEvent[] = []; const collectPublicCalls = (event: ExecutionEvent) => { if (event.type === "public-call") { @@ -419,14 +369,9 @@ export class TxCallStackDecoder { allPublicCalls.sort((a, b) => a.counter - b.counter); // Populate return values from publicOutput.publicReturnValues - const publicReturnValues = - simulationResult.publicOutput?.publicReturnValues ?? []; + const publicReturnValues = simulationResult.publicOutput?.publicReturnValues ?? []; - for ( - let i = 0; - i < allPublicCalls.length && i < publicReturnValues.length; - i++ - ) { + for (let i = 0; i < allPublicCalls.length && i < publicReturnValues.length; i++) { const returnValue = publicReturnValues[i]; const publicCall = allPublicCalls[i]; @@ -461,9 +406,7 @@ export class TxCallStackDecoder { try { const instance = await this.cache.getContractInstance(contractAddress); - const artifact = await this.cache.getContractArtifact( - instance.currentContractClassId, - ); + const artifact = await this.cache.getContractArtifact(instance.currentContractClassId); // Use getAllFunctionAbis to get all functions including non-dispatch public functions const allAbis = getAllFunctionAbis(artifact); @@ -570,21 +513,17 @@ export class TxCallStackDecoder { // Retrieve contract instance and artifact const instance = await this.cache.getContractInstance(contractAddress); - const artifact = await this.cache.getContractArtifact( - instance.currentContractClassId, - ); + const artifact = await this.cache.getContractArtifact(instance.currentContractClassId); // Find the function in the artifact - const functionAbi = artifact.functions.find( - (f) => f.name === functionName, - ); + const functionAbi = artifact.functions.find((f) => f.name === functionName); if (!functionAbi) { throw new Error(`Function ${functionName} not found in artifact`); } // Reuse the generic argument decoding helper (same logic as transaction decoding) return await this.decodeAndFormatArguments(functionAbi, args); - } catch (error) { + } catch { // If formatting fails, return raw representation of Fr[] values return args.map((arg, i) => ({ name: `arg_${i}`, @@ -609,14 +548,10 @@ export class TxCallStackDecoder { // Retrieve contract instance and artifact const instance = await this.cache.getContractInstance(contractAddress); - const artifact = await this.cache.getContractArtifact( - instance.currentContractClassId, - ); + const artifact = await this.cache.getContractArtifact(instance.currentContractClassId); // Find the function in the artifact - const functionAbi = artifact.functions.find( - (f) => f.name === functionName, - ); + const functionAbi = artifact.functions.find((f) => f.name === functionName); if (!functionAbi) { throw new Error(`Function ${functionName} not found in artifact`); } @@ -627,10 +562,7 @@ export class TxCallStackDecoder { } // Reuse the generic return value decoding helper (same logic as transaction decoding) - const formattedReturns = await this.decodeAndFormatReturnValues( - functionAbi, - result, - ); + 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 @@ -641,7 +573,7 @@ export class TxCallStackDecoder { } else { return `[${formattedReturns.map((r) => r.value).join(", ")}]`; } - } catch (error) { + } catch { // If formatting fails, return raw representation of Fr[] values return `[${result.map((fr) => fr.toString()).join(", ")}]`; } diff --git a/shared/src/wallet/decoding/tx-decoding-service.ts b/shared/src/wallet/decoding/tx-decoding-service.ts index a39f32e..4e9dc0b 100644 --- a/shared/src/wallet/decoding/tx-decoding-service.ts +++ b/shared/src/wallet/decoding/tx-decoding-service.ts @@ -1,18 +1,10 @@ -import type { - TxSimulationResult, - NestedProcessReturnValues, -} from "@aztec/stdlib/tx"; -import type { AztecAddress } from "@aztec/stdlib/aztec-address"; -import type { Fr } from "@aztec/foundation/curves/bn254"; +import type { TxSimulationResult } from "@aztec/stdlib/tx"; import type { DecodingCache } from "./decoding-cache"; import { CallAuthorizationFormatter, type ReadableCallAuthorization, } from "./call-authorization-formatter"; -import { - TxCallStackDecoder, - type DecodedExecutionTrace, -} from "./tx-callstack-decoder"; +import { TxCallStackDecoder, type DecodedExecutionTrace } from "./tx-callstack-decoder"; import { collectOffchainEffects } from "@aztec/stdlib/tx"; /** @@ -39,28 +31,22 @@ export class TxDecodingService { callAuthorizations: ReadableCallAuthorization[]; executionTrace: DecodedExecutionTrace; }> { - const offChainEffects = collectOffchainEffects( - simulationResult.privateExecutionResult, - ); + const offChainEffects = collectOffchainEffects(simulationResult.privateExecutionResult); // Parse call authorizations from offchain effects const callAuthorizations = await Promise.all( - offChainEffects.map((effect) => - this.formatter.parseCallAuthorizationFromEffect(effect), - ), + offChainEffects.map((effect) => this.formatter.parseCallAuthorizationFromEffect(effect)), ); const filteredCallAuthorizations = callAuthorizations.filter(Boolean); // Format for display - const readableCallAuthorizations = - await this.formatter.formatCallAuthorizationsForDisplay( - filteredCallAuthorizations, - ); + const readableCallAuthorizations = await this.formatter.formatCallAuthorizationsForDisplay( + filteredCallAuthorizations, + ); // Decode execution call stack - const executionTrace = - await this.decoder.decodeSimulationResult(simulationResult); + const executionTrace = await this.decoder.decodeSimulationResult(simulationResult); return { callAuthorizations: readableCallAuthorizations, diff --git a/shared/src/wallet/decoding/utils.ts b/shared/src/wallet/decoding/utils.ts index bf7ae8e..f455258 100644 --- a/shared/src/wallet/decoding/utils.ts +++ b/shared/src/wallet/decoding/utils.ts @@ -24,9 +24,7 @@ export function formatAbiValue(value: AbiDecoded): string { } // For plain objects or objects with useless toString, use JSON - return JSON.stringify(value, (_, v) => - typeof v === "bigint" ? v.toString() : v - ); + return JSON.stringify(value, (_, v) => (typeof v === "bigint" ? v.toString() : v)); } return String(value); diff --git a/shared/src/wallet/managers/authorization-manager.test.ts b/shared/src/wallet/managers/authorization-manager.test.ts index 4db99b8..f071c9d 100644 --- a/shared/src/wallet/managers/authorization-manager.test.ts +++ b/shared/src/wallet/managers/authorization-manager.test.ts @@ -4,10 +4,7 @@ import { createLogger } from "@aztec/foundation/log"; import { AztecAddress } from "@aztec/aztec.js/addresses"; import { AuthorizationManager } from "./authorization-manager"; import { WalletDB } from "../database/wallet-db"; -import type { - AuthorizationItem, - AuthorizationResponse, -} from "../types/authorization"; +import type { AuthorizationItem, AuthorizationResponse } from "../types/authorization"; const logger = createLogger("test:auth-manager"); const contractAddr = AztecAddress.fromBigInt(42n).toString(); @@ -26,12 +23,7 @@ beforeEach(async () => { db = WalletDB.init(store, logger); pendingAuthorizations = new Map(); eventEmitter = new EventTarget(); - manager = new AuthorizationManager( - APP_ID, - db, - pendingAuthorizations, - eventEmitter, - ); + manager = new AuthorizationManager(APP_ID, db, pendingAuthorizations, eventEmitter); }); afterEach(async () => { @@ -77,9 +69,18 @@ function makeItem(overrides: Partial & { id: string }): Autho describe("auto-approval with existing grants", () => { it("auto-approves exact key and tracks in __requested__", async () => { - await db.storePersistentAuthorization(APP_ID, "getAccounts", { persistent: true, accounts: [] }); + await db.storePersistentAuthorization(APP_ID, "getAccounts", { + persistent: true, + accounts: [], + }); - const items = [makeItem({ id: "item-1", method: "getAccounts", persistence: { storageKey: "getAccounts", persistData: null } })]; + const items = [ + makeItem({ + id: "item-1", + method: "getAccounts", + persistence: { storageKey: "getAccounts", persistData: null }, + }), + ]; const response = await manager.requestAuthorization(items); expect(response.approved).toBe(true); @@ -92,21 +93,33 @@ describe("auto-approval with existing grants", () => { await db.storePersistentAuthorization(APP_ID, "registerContract:*", { persistent: true }); const regKey = `registerContract:${contractAddr}`; let response = await manager.requestAuthorization([ - makeItem({ id: "item-1", method: "registerContract", persistence: { storageKey: regKey, persistData: null } }), + makeItem({ + id: "item-1", + method: "registerContract", + persistence: { storageKey: regKey, persistData: null }, + }), ]); expect(response.approved).toBe(true); // Two-level wildcard: simulateTx:contract:* matches simulateTx:contract:swap - await db.storePersistentAuthorization(APP_ID, `simulateTx:${contractAddr}:*`, { persistent: true }); + await db.storePersistentAuthorization(APP_ID, `simulateTx:${contractAddr}:*`, { + persistent: true, + }); response = await manager.requestAuthorization([ - makeItem({ id: "item-2", persistence: { storageKey: `simulateTx:${contractAddr}:swap`, persistData: null } }), + makeItem({ + id: "item-2", + persistence: { storageKey: `simulateTx:${contractAddr}:swap`, persistData: null }, + }), ]); expect(response.approved).toBe(true); // Full wildcard: simulateTx:* matches simulateTx:contract:function await db.storePersistentAuthorization(APP_ID, "simulateTx:*", { persistent: true }); response = await manager.requestAuthorization([ - makeItem({ id: "item-3", persistence: { storageKey: `simulateTx:${contractAddr2}:transfer`, persistData: null } }), + makeItem({ + id: "item-3", + persistence: { storageKey: `simulateTx:${contractAddr2}:transfer`, persistData: null }, + }), ]); expect(response.approved).toBe(true); @@ -126,7 +139,10 @@ describe("strict mode", () => { // Unauthorized operation → rejected await expect( manager.requestAuthorization([ - makeItem({ id: "item-1", persistence: { storageKey: `simulateTx:${contractAddr}:swap`, persistData: null } }), + makeItem({ + id: "item-1", + persistence: { storageKey: `simulateTx:${contractAddr}:swap`, persistData: null }, + }), ]), ).rejects.toThrow("strict mode"); @@ -152,7 +168,10 @@ describe("ad-hoc approval tracks in __requested__", () => { // Array of keys await manager.requestAuthorization([ - makeItem({ id: "item-2", persistence: { storageKey: [key2, "getAccounts"], persistData: null } }), + makeItem({ + id: "item-2", + persistence: { storageKey: [key2, "getAccounts"], persistData: null }, + }), ]); const requested = await db.getRequestedKeys(APP_ID); @@ -164,12 +183,19 @@ describe("ad-hoc approval tracks in __requested__", () => { describe("mixed auto-approved and needs-auth items", () => { it("auto-approves pre-granted, prompts for new, tracks all in __requested__", async () => { - await db.storePersistentAuthorization(APP_ID, "getAccounts", { persistent: true, accounts: [] }); + await db.storePersistentAuthorization(APP_ID, "getAccounts", { + persistent: true, + accounts: [], + }); autoApproveAll(); const simKey = `simulateTx:${contractAddr}:swap`; const items = [ - makeItem({ id: "item-1", method: "getAccounts", persistence: { storageKey: "getAccounts", persistData: null } }), + makeItem({ + id: "item-1", + method: "getAccounts", + persistence: { storageKey: "getAccounts", persistData: null }, + }), makeItem({ id: "item-2", persistence: { storageKey: simKey, persistData: null } }), ]; @@ -200,13 +226,27 @@ describe("URL appId ad-hoc flow", () => { // Three sequential ad-hoc requests await urlManager.requestAuthorization([ - makeItem({ id: "item-1", appId: URL_APP_ID, method: "getAccounts", persistence: { storageKey: "getAccounts", persistData: null } }), + makeItem({ + id: "item-1", + appId: URL_APP_ID, + method: "getAccounts", + persistence: { storageKey: "getAccounts", persistData: null }, + }), ]); await urlManager.requestAuthorization([ - makeItem({ id: "item-2", appId: URL_APP_ID, method: "registerContract", persistence: { storageKey: regKey, persistData: null } }), + makeItem({ + id: "item-2", + appId: URL_APP_ID, + method: "registerContract", + persistence: { storageKey: regKey, persistData: null }, + }), ]); await urlManager.requestAuthorization([ - makeItem({ id: "item-3", appId: URL_APP_ID, persistence: { storageKey: simKey, persistData: null } }), + makeItem({ + id: "item-3", + appId: URL_APP_ID, + persistence: { storageKey: simKey, persistData: null }, + }), ]); // Full URL in listAuthorizedApps, not "https" diff --git a/shared/src/wallet/managers/authorization-manager.ts b/shared/src/wallet/managers/authorization-manager.ts index 2454efa..ed0204d 100644 --- a/shared/src/wallet/managers/authorization-manager.ts +++ b/shared/src/wallet/managers/authorization-manager.ts @@ -5,10 +5,7 @@ import type { AuthorizationItemResponse, } from "../types/authorization"; import { AuthorizationRequestEvent } from "../types/authorization"; -import { - promiseWithResolvers, - type PromiseWithResolvers, -} from "@aztec/foundation/promise"; +import { promiseWithResolvers, type PromiseWithResolvers } from "@aztec/foundation/promise"; import type { WalletDB } from "../database/wallet-db"; /** @@ -41,9 +38,7 @@ export class AuthorizationManager { * @param items - Array of authorization items (with optional persistence config) * @returns Authorization response with approved items */ - async requestAuthorization( - items: AuthorizationItem[], - ): Promise { + async requestAuthorization(items: AuthorizationItem[]): Promise { // Check for existing persistent authorizations const itemsNeedingAuth: AuthorizationItem[] = []; const autoApprovedItems: Record = {}; @@ -61,10 +56,7 @@ export class AuthorizationManager { for (const key of keys) { // Try exact match first - let auth = await this.db.retrievePersistentAuthorization( - this.appId, - key, - ); + let auth = await this.db.retrievePersistentAuthorization(this.appId, key); // If no exact match, try wildcard patterns if (auth === undefined) { @@ -172,11 +164,7 @@ export class AuthorizationManager { // Store authorization for each key for (const key of keys) { - await this.db.storePersistentAuthorization( - this.appId, - key, - dataToStore, - ); + await this.db.storePersistentAuthorization(this.appId, key, dataToStore); } } } @@ -238,9 +226,7 @@ export class AuthorizationManager { * @param storageKey - Storage key to check for wildcard matches * @returns Existing authorization data if wildcard match found, undefined otherwise */ - private async checkWildcardAuthorization( - storageKey: string, - ): Promise { + private async checkWildcardAuthorization(storageKey: string): Promise { const parts = storageKey.split(":"); if (parts.length === 1) { // No pattern to match (simple method like "getAccounts") @@ -258,10 +244,7 @@ export class AuthorizationManager { if (remaining.length === 2) { // Try contract-specific wildcard: "method:contract:*" const contractWildcard = `${method}:${remaining[0]}:*`; - const auth = await this.db.retrievePersistentAuthorization( - this.appId, - contractWildcard, - ); + const auth = await this.db.retrievePersistentAuthorization(this.appId, contractWildcard); if (auth !== undefined) { return auth; } @@ -269,9 +252,6 @@ export class AuthorizationManager { // Try full wildcard: "method:*" const fullWildcard = `${method}:*`; - return await this.db.retrievePersistentAuthorization( - this.appId, - fullWildcard, - ); + return await this.db.retrievePersistentAuthorization(this.appId, fullWildcard); } } diff --git a/shared/src/wallet/managers/interaction-manager.ts b/shared/src/wallet/managers/interaction-manager.ts index 78c4954..7671a6b 100644 --- a/shared/src/wallet/managers/interaction-manager.ts +++ b/shared/src/wallet/managers/interaction-manager.ts @@ -26,11 +26,19 @@ export class InteractionManager implements EventTarget { } // EventTarget implementation - delegate to internal eventEmitter - addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void { + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject | null, + options?: boolean | AddEventListenerOptions, + ): void { this.eventEmitter.addEventListener(type, listener, options); } - removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions): void { + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject | null, + options?: boolean | EventListenerOptions, + ): void { this.eventEmitter.removeEventListener(type, listener, options); } diff --git a/shared/src/wallet/operations/base-operation.ts b/shared/src/wallet/operations/base-operation.ts index 46b50f4..aed55e1 100644 --- a/shared/src/wallet/operations/base-operation.ts +++ b/shared/src/wallet/operations/base-operation.ts @@ -1,9 +1,5 @@ -import type { - WalletInteraction, - WalletInteractionType, -} from "../types/wallet-interaction"; +import type { WalletInteraction, WalletInteractionType } from "../types/wallet-interaction"; import type { InteractionManager } from "../managers/interaction-manager"; -import type { AuthorizationManager } from "../managers/authorization-manager"; /** * Persistence configuration for authorization caching. @@ -16,11 +12,10 @@ export interface PersistenceConfig { /** * Result from the prepare phase of an operation. * - * @template TResult - The final result type of the operation (unused but kept for backwards compatibility) * @template TDisplayData - The display data type for the UI * @template TExecutionData - The execution data type for the execute phase */ -export interface PrepareResult { +export interface PrepareResult { /** * Data to display in the UI and authorization dialog. * Always complete and accurate - never fake/placeholder data. @@ -99,9 +94,7 @@ export abstract class ExternalOperation< * @param args - Raw operation arguments * @returns The created interaction */ - abstract createInteraction( - ...args: TArgs - ): Promise>; + abstract createInteraction(...args: TArgs): Promise>; /** * PHASE 2: PREPARE @@ -111,9 +104,7 @@ export abstract class ExternalOperation< * @param args - Arguments for the operation * @returns PrepareResult containing earlyReturn, displayData, executionData, and persistence config */ - abstract prepare( - ...args: TArgs - ): Promise>; + abstract prepare(...args: TArgs): Promise>; /** * PHASE 2B: REQUEST AUTHORIZATION (Standalone only) @@ -127,7 +118,7 @@ export abstract class ExternalOperation< */ abstract requestAuthorization( displayData: TDisplayData, - persistence?: PersistenceConfig + persistence?: PersistenceConfig, ): Promise; /** @@ -144,9 +135,7 @@ export abstract class ExternalOperation< * Set the current interaction context. * Called by orchestrators (standalone or batch) before execute(). */ - setCurrentInteraction( - interaction: WalletInteraction | undefined - ): void { + setCurrentInteraction(interaction: WalletInteraction | undefined): void { this.interaction = interaction; } @@ -167,11 +156,11 @@ export abstract class ExternalOperation< updates?: Partial<{ title: string; [key: string]: unknown; - }> + }>, ): Promise { if (this.interaction) { await this.interactionManager.storeAndEmit( - this.interaction.update({ status, description, complete, ...updates }) + this.interaction.update({ status, description, complete, ...updates }), ); } } @@ -200,18 +189,14 @@ export abstract class ExternalOperation< const prepared = await this.prepare(...args); // PHASE 3: REQUEST AUTHORIZATION (throws on error) - await this.requestAuthorization( - prepared.displayData, - prepared.persistence - ); + await this.requestAuthorization(prepared.displayData, prepared.persistence); // PHASE 4: EXECUTE (throws on error, should set SUCCESS state before returning) const result = await this.execute(prepared.executionData!); return result; } catch (error) { // Unified error handling for all phases - const description = - error instanceof Error ? error.message : String(error); + const description = error instanceof Error ? error.message : String(error); await this.emitProgress("ERROR", description, true); throw error; } finally { diff --git a/shared/src/wallet/operations/create-authwit-operation.ts b/shared/src/wallet/operations/create-authwit-operation.ts index f43bf7a..138549b 100644 --- a/shared/src/wallet/operations/create-authwit-operation.ts +++ b/shared/src/wallet/operations/create-authwit-operation.ts @@ -1,29 +1,16 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { AztecAddress } from "@aztec/stdlib/aztec-address"; import type { AuthWitness } from "@aztec/stdlib/auth-witness"; -import type { - IntentInnerHash, - CallIntent, -} from "@aztec/aztec.js/authorization"; +import type { IntentInnerHash, CallIntent } from "@aztec/aztec.js/authorization"; import type { ChainInfo } from "@aztec/aztec.js/account"; import { Fr } from "@aztec/foundation/curves/bn254"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; import type { DecodingCache } from "../decoding/decoding-cache"; // Arguments tuple for the operation -type CreateAuthWitArgs = [ - from: AztecAddress, - messageHashOrIntent: IntentInnerHash | CallIntent, -]; +type CreateAuthWitArgs = [from: AztecAddress, messageHashOrIntent: IntentInnerHash | CallIntent]; // Result type for the operation type CreateAuthWitResult = AuthWitness; @@ -92,7 +79,7 @@ export class CreateAuthWitOperation extends ExternalOperation< async createInteraction( from: AztecAddress, - messageHashOrIntent: IntentInnerHash | CallIntent, + _messageHashOrIntent: IntentInnerHash | CallIntent, ): Promise> { const interaction = WalletInteraction.from({ type: "createAuthWit", @@ -110,13 +97,7 @@ export class CreateAuthWitOperation extends ExternalOperation< async prepare( from: AztecAddress, messageHashOrIntent: IntentInnerHash | CallIntent, - ): Promise< - PrepareResult< - CreateAuthWitResult, - CreateAuthWitDisplayData, - CreateAuthWitExecutionData - > - > { + ): Promise> { const displayData: CreateAuthWitDisplayData = { from: from.toString(), type: "hash", @@ -133,12 +114,8 @@ export class CreateAuthWitOperation extends ExternalOperation< displayData.type = "call"; // Decode call information - const callerAlias = await this.decodingCache.getAddressAlias( - intent.caller, - ); - const contractName = await this.decodingCache.getAddressAlias( - intent.call.to, - ); + const callerAlias = await this.decodingCache.getAddressAlias(intent.caller); + const contractName = await this.decodingCache.getAddressAlias(intent.call.to); displayData.call = { caller: intent.caller.toString(), @@ -179,9 +156,7 @@ export class CreateAuthWitOperation extends ExternalOperation< ]); } - async execute( - executionData: CreateAuthWitExecutionData, - ): Promise { + async execute(executionData: CreateAuthWitExecutionData): Promise { const result = await this.createAuthWitInternal( executionData.from, executionData.messageHashOrIntent, diff --git a/shared/src/wallet/operations/get-accounts-operation.ts b/shared/src/wallet/operations/get-accounts-operation.ts index 37a53ca..b42f075 100644 --- a/shared/src/wallet/operations/get-accounts-operation.ts +++ b/shared/src/wallet/operations/get-accounts-operation.ts @@ -1,14 +1,7 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { Aliased } from "@aztec/aztec.js/wallet"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +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"; @@ -75,13 +68,7 @@ export class GetAccountsOperation extends ExternalOperation< return interaction; } - async prepare(): Promise< - PrepareResult< - GetAccountsResult, - GetAccountsDisplayData, - GetAccountsExecutionData - > - > { + async prepare(): Promise> { // Load all accounts from database const accounts = await this.db.listAccounts(); const aliasedAccounts: Aliased[] = accounts.map((acc) => ({ @@ -139,9 +126,7 @@ export class GetAccountsOperation extends ExternalOperation< } } - async execute( - _executionData: GetAccountsExecutionData, - ): Promise { + async execute(_executionData: GetAccountsExecutionData): Promise { await this.emitProgress("SUCCESS", undefined, true); // Return the accounts selected by the user during authorization diff --git a/shared/src/wallet/operations/get-address-book-operation.ts b/shared/src/wallet/operations/get-address-book-operation.ts index 28cc26e..4cc48ab 100644 --- a/shared/src/wallet/operations/get-address-book-operation.ts +++ b/shared/src/wallet/operations/get-address-book-operation.ts @@ -1,14 +1,7 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { Aliased } from "@aztec/aztec.js/wallet"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +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"; @@ -75,13 +68,7 @@ export class GetAddressBookOperation extends ExternalOperation< return interaction; } - async prepare(): Promise< - PrepareResult< - GetAddressBookResult, - GetAddressBookDisplayData, - GetAddressBookExecutionData - > - > { + async prepare(): Promise> { // Load all senders from database const senders = await this.db.listSenders(); const contacts: Aliased[] = senders.map((sender) => ({ @@ -134,9 +121,7 @@ export class GetAddressBookOperation extends ExternalOperation< })); } - async execute( - _executionData: GetAddressBookExecutionData, - ): Promise { + async execute(_executionData: GetAddressBookExecutionData): Promise { await this.emitProgress("SUCCESS", undefined, true); // Return the contacts selected by the user during authorization diff --git a/shared/src/wallet/operations/get-contract-class-metadata-operation.ts b/shared/src/wallet/operations/get-contract-class-metadata-operation.ts index 3ec25c9..71a68ba 100644 --- a/shared/src/wallet/operations/get-contract-class-metadata-operation.ts +++ b/shared/src/wallet/operations/get-contract-class-metadata-operation.ts @@ -1,14 +1,7 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { ContractClassMetadata } from "@aztec/aztec.js/wallet"; import { Fr } from "@aztec/foundation/curves/bn254"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; @@ -61,16 +54,12 @@ export class GetContractClassMetadataOperation extends ExternalOperation< this.interactionManager = interactionManager; } - async check( - _id: Fr, - ): Promise { + async check(_id: Fr): Promise { // No early return - always requires authorization return undefined; } - async createInteraction( - id: Fr, - ): Promise> { + async createInteraction(id: Fr): Promise> { const interaction = WalletInteraction.from({ type: "getContractClassMetadata", status: "PREPARING", @@ -87,11 +76,7 @@ export class GetContractClassMetadataOperation extends ExternalOperation< async prepare( id: Fr, ): Promise< - PrepareResult< - GetContractClassMetadataResult, - GetContractClassMetadataDisplayData, - GetContractClassMetadataExecutionData - > + PrepareResult > { // Query metadata const metadata = await this.getContractClassMetadata(id); diff --git a/shared/src/wallet/operations/get-contract-metadata-operation.ts b/shared/src/wallet/operations/get-contract-metadata-operation.ts index 6edd13f..76596bc 100644 --- a/shared/src/wallet/operations/get-contract-metadata-operation.ts +++ b/shared/src/wallet/operations/get-contract-metadata-operation.ts @@ -1,14 +1,7 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { AztecAddress } from "@aztec/stdlib/aztec-address"; import { type ContractMetadata, ContractInitializationStatus } from "@aztec/aztec.js/wallet"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; import type { DecodingCache } from "../decoding/decoding-cache"; @@ -64,9 +57,7 @@ export class GetContractMetadataOperation extends ExternalOperation< this.interactionManager = interactionManager; } - async check( - _address: AztecAddress, - ): Promise { + async check(_address: AztecAddress): Promise { // No early return - always requires authorization return undefined; } @@ -89,13 +80,7 @@ export class GetContractMetadataOperation extends ExternalOperation< async prepare( address: AztecAddress, - ): Promise< - PrepareResult< - GetContractMetadataResult, - GetContractMetadataDisplayData, - GetContractMetadataExecutionData - > - > { + ): Promise> { // Query metadata const metadata = await this.getContractMetadata(address); diff --git a/shared/src/wallet/operations/get-private-events-operation.ts b/shared/src/wallet/operations/get-private-events-operation.ts index 0e983c5..e9a44f8 100644 --- a/shared/src/wallet/operations/get-private-events-operation.ts +++ b/shared/src/wallet/operations/get-private-events-operation.ts @@ -1,16 +1,9 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { PrivateEvent, PrivateEventFilter } from "@aztec/aztec.js/wallet"; import type { EventMetadataDefinition } from "@aztec/stdlib/abi"; import { decodeFromAbi } from "@aztec/stdlib/abi"; import type { PXE } from "@aztec/pxe/client/lazy"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; import type { DecodingCache } from "../decoding/decoding-cache"; @@ -81,7 +74,7 @@ export class GetPrivateEventsOperation extends ExternalOperation< async createInteraction( eventMetadata: EventMetadataDefinition, - eventFilter: PrivateEventFilter, + _eventFilter: PrivateEventFilter, ): Promise> { const interaction = WalletInteraction.from({ type: "getPrivateEvents", @@ -99,19 +92,10 @@ export class GetPrivateEventsOperation extends ExternalOperation< async prepare( eventMetadata: EventMetadataDefinition, eventFilter: PrivateEventFilter, - ): Promise< - PrepareResult< - GetPrivateEventsResult, - GetPrivateEventsDisplayData, - GetPrivateEventsExecutionData - > - > { + ): Promise>> { // Query events to show count to user // New PXE API: getPrivateEvents(eventSelector, filter) → PackedPrivateEvent[] - const packedEvents = await this.pxe.getPrivateEvents( - eventMetadata.eventSelector, - eventFilter, - ); + const packedEvents = await this.pxe.getPrivateEvents(eventMetadata.eventSelector, eventFilter); // Decode each packed event into the typed PrivateEvent format const events: PrivateEvent[] = packedEvents.map((packed: any) => ({ event: decodeFromAbi([eventMetadata.abiType], packed.packedEvent) as T, @@ -124,9 +108,7 @@ export class GetPrivateEventsOperation extends ExternalOperation< let contractName: string | undefined; if (eventFilter.contractAddress) { - contractName = await this.decodingCache.getAddressAlias( - eventFilter.contractAddress, - ); + contractName = await this.decodingCache.getAddressAlias(eventFilter.contractAddress); } const displayData: GetPrivateEventsDisplayData = { diff --git a/shared/src/wallet/operations/register-contract-operation.ts b/shared/src/wallet/operations/register-contract-operation.ts index 3103907..b095a7d 100644 --- a/shared/src/wallet/operations/register-contract-operation.ts +++ b/shared/src/wallet/operations/register-contract-operation.ts @@ -1,21 +1,11 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; import type { ContractInstanceWithAddress } from "@aztec/stdlib/contract"; -import { - computePartialAddress, - getContractClassFromArtifact, -} from "@aztec/stdlib/contract"; +import { computePartialAddress, getContractClassFromArtifact } from "@aztec/stdlib/contract"; import type { ContractArtifact } from "@aztec/stdlib/abi"; import type { Fr } from "@aztec/foundation/curves/bn254"; import type { PXE } from "@aztec/pxe/client/lazy"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { DecodingCache } from "../decoding/decoding-cache"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; @@ -79,10 +69,7 @@ export class RegisterContractOperation extends ExternalOperation< // Cache artifact early for batch operations // Uses instance.currentContractClassId as key (no expensive computation) if (artifact && instance.currentContractClassId) { - this.decodingCache.cacheArtifactForBatch( - instance.currentContractClassId, - artifact, - ); + this.decodingCache.cacheArtifactForBatch(instance.currentContractClassId, artifact); } // Resolve contract address @@ -128,13 +115,7 @@ export class RegisterContractOperation extends ExternalOperation< instance: ContractInstanceWithAddress, artifact?: ContractArtifact, secretKey?: Fr, - ): Promise< - PrepareResult< - RegisterContractResult, - RegisterContractDisplayData, - RegisterContractExecutionData - > - > { + ): Promise> { // Resolve contract address const contractAddress = instance.address; @@ -182,21 +163,16 @@ export class RegisterContractOperation extends ExternalOperation< ]); } - async execute( - executionData: RegisterContractExecutionData, - ): Promise { - let { instance, artifact, secretKey } = executionData; - const existingInstance = await this.pxe.getContractInstance( - instance.address, - ); + async execute(executionData: RegisterContractExecutionData): Promise { + const { instance, secretKey } = executionData; + let { artifact } = executionData; + const existingInstance = await this.pxe.getContractInstance(instance.address); if (existingInstance) { // Instance already registered in the wallet if (artifact) { const thisContractClass = await getContractClassFromArtifact(artifact); - if ( - !thisContractClass.id.equals(existingInstance.currentContractClassId) - ) { + 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; @@ -221,10 +197,7 @@ export class RegisterContractOperation extends ExternalOperation< } if (secretKey) { - await this.pxe.registerAccount( - secretKey, - await computePartialAddress(instance), - ); + await this.pxe.registerAccount(secretKey, await computePartialAddress(instance)); } // Automatically grant persistent authorizations for metadata queries diff --git a/shared/src/wallet/operations/register-sender-operation.ts b/shared/src/wallet/operations/register-sender-operation.ts index aafe67c..3750bc4 100644 --- a/shared/src/wallet/operations/register-sender-operation.ts +++ b/shared/src/wallet/operations/register-sender-operation.ts @@ -1,14 +1,7 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { AztecAddress } from "@aztec/stdlib/aztec-address"; import type { PXE } from "@aztec/pxe/client/lazy"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +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"; @@ -51,23 +44,20 @@ export class RegisterSenderOperation extends ExternalOperation< private pxe: PXE, private db: WalletDB, interactionManager: InteractionManager, - private authorizationManager: AuthorizationManager + private authorizationManager: AuthorizationManager, ) { super(); this.interactionManager = interactionManager; } - async check( - _address: AztecAddress, - _alias: string - ): Promise { + async check(_address: AztecAddress, _alias: string): Promise { // No early return checks for this operation return undefined; } async createInteraction( address: AztecAddress, - alias: string + alias: string, ): Promise> { // Create interaction with simple title from args only const interaction = WalletInteraction.from({ @@ -85,14 +75,8 @@ export class RegisterSenderOperation extends ExternalOperation< async prepare( address: AztecAddress, - alias: string - ): Promise< - PrepareResult< - RegisterSenderResult, - RegisterSenderDisplayData, - RegisterSenderExecutionData - > - > { + alias: string, + ): Promise> { return { displayData: { address, alias }, executionData: { address, alias }, @@ -101,7 +85,7 @@ export class RegisterSenderOperation extends ExternalOperation< async requestAuthorization( displayData: RegisterSenderDisplayData, - _persistence?: PersistenceConfig + _persistence?: PersistenceConfig, ): Promise { // Update interaction with detailed title and status await this.emitProgress("REQUESTING AUTHORIZATION", undefined, false, { @@ -122,9 +106,7 @@ export class RegisterSenderOperation extends ExternalOperation< ]); } - async execute( - executionData: RegisterSenderExecutionData - ): Promise { + async execute(executionData: RegisterSenderExecutionData): Promise { // Store sender in database await this.db.storeSender(executionData.address, executionData.alias); diff --git a/shared/src/wallet/operations/request-capabilities-operation.ts b/shared/src/wallet/operations/request-capabilities-operation.ts index 97f7d08..d2ed932 100644 --- a/shared/src/wallet/operations/request-capabilities-operation.ts +++ b/shared/src/wallet/operations/request-capabilities-operation.ts @@ -1,18 +1,11 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import type { AppCapabilities, WalletCapabilities, GrantedCapability, CAPABILITY_VERSION, } from "@aztec/aztec.js/wallet"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { RequestCapabilitiesParams } from "../types/authorization"; import type { WalletDB } from "../database/wallet-db"; import type { InteractionManager } from "../managers/interaction-manager"; @@ -62,9 +55,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< this.interactionManager = interactionManager; } - async check( - manifest: AppCapabilities, - ): Promise { + async check(manifest: AppCapabilities): Promise { // Calculate all storage keys that would be needed for the requested capabilities const allKeys: string[] = []; const capabilityKeys = new Map(); // Map capability index to its keys @@ -106,7 +97,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< // Contract is registered in PXE, treat as granted even if no persistent auth continue; // Skip counting this as missing } - } catch (e) { + } catch { // Contract not found in PXE, count as missing } } @@ -135,12 +126,12 @@ export class RequestCapabilitiesOperation extends ExternalOperation< // For accounts capability, we need to extract the accounts list from stored data if (requestedCap.type === "accounts" && storedData?.accounts) { // Convert stored accounts (with string items) back to AztecAddress objects - const accounts = ( - storedData.accounts as Array<{ alias: string; item: string }> - ).map((acc) => ({ - alias: acc.alias, - item: AztecAddress.fromString(acc.item), - })); + const accounts = (storedData.accounts as Array<{ alias: string; item: string }>).map( + (acc) => ({ + alias: acc.alias, + item: AztecAddress.fromString(acc.item), + }), + ); granted.push({ type: "accounts", @@ -186,13 +177,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< async prepare( manifest: AppCapabilities, - ): Promise< - PrepareResult< - RequestCapabilitiesResult, - RequestCapabilitiesParams, - RequestCapabilitiesExecutionData - > - > { + ): Promise> { // Calculate which capabilities are new (not already granted) const newCapabilityIndices: number[] = []; // Track which storage keys already exist @@ -205,10 +190,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< let keyStatus: Map; // For simulation and transaction capabilities, check if ad-hoc authorizations cover them - if ( - capability.type === "simulation" || - capability.type === "transaction" - ) { + if (capability.type === "simulation" || capability.type === "transaction") { // Build pattern array for checking against stored function calls const patterns: Array<{ contract: string; @@ -221,8 +203,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< if (simCap.transactions?.scope !== "*") { for (const pattern of simCap.transactions?.scope || []) { patterns.push({ - contract: - pattern.contract === "*" ? "*" : pattern.contract.toString(), + contract: pattern.contract === "*" ? "*" : pattern.contract.toString(), function: pattern.function, method: "simulateTx", }); @@ -231,8 +212,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< if (simCap.utilities?.scope !== "*") { for (const pattern of simCap.utilities?.scope || []) { patterns.push({ - contract: - pattern.contract === "*" ? "*" : pattern.contract.toString(), + contract: pattern.contract === "*" ? "*" : pattern.contract.toString(), function: pattern.function, method: "simulateUtility", }); @@ -243,8 +223,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< if (txCap.scope !== "*") { for (const pattern of txCap.scope) { patterns.push({ - contract: - pattern.contract === "*" ? "*" : pattern.contract.toString(), + contract: pattern.contract === "*" ? "*" : pattern.contract.toString(), function: pattern.function, method: "sendTx", }); @@ -253,19 +232,14 @@ export class RequestCapabilitiesOperation extends ExternalOperation< } // Build storage keys from patterns: method:contract:function - const patternKeys = patterns.map( - (p) => `${p.method}:${p.contract}:${p.function}`, - ); + const patternKeys = patterns.map((p) => `${p.method}:${p.contract}:${p.function}`); keyStatus = await this.db.checkAuthorizationKeys( this.authorizationManager.appId, patternKeys, ); } else { // For other capabilities, use simple key existence check - keyStatus = await this.db.checkAuthorizationKeys( - this.authorizationManager.appId, - keys, - ); + keyStatus = await this.db.checkAuthorizationKeys(this.authorizationManager.appId, keys); // Special handling for registerContract: check if contracts are already registered in PXE if (capability.type === "contracts") { @@ -280,7 +254,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< // Contract is registered in PXE, treat as granted keyStatus.set(key, true); } - } catch (e) { + } catch { // Contract not found in PXE, keep as missing } } @@ -288,9 +262,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< } } - const hasMissingKeys = Array.from(keyStatus.values()).some( - (exists) => !exists, - ); + const hasMissingKeys = Array.from(keyStatus.values()).some((exists) => !exists); // Store all key statuses in existingGrants map for (const [key, exists] of keyStatus.entries()) { @@ -363,9 +335,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< // Check if app has been seen before (has any stored authorization records) // This is separate from existingGrants which includes PXE-registered contracts - const storedKeys = await this.db.getAllAuthorizationKeys( - this.authorizationManager.appId, - ); + const storedKeys = await this.db.getAllAuthorizationKeys(this.authorizationManager.appId); const isAppFirstTime = storedKeys.length === 0; // Display data shows the full manifest plus which capabilities are new @@ -407,9 +377,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< const authData = itemResponse?.data as any; if (!authData || !authData.granted) { - throw new Error( - "Authorization response missing granted capabilities data", - ); + throw new Error("Authorization response missing granted capabilities data"); } // Store the capabilities granted by the user @@ -418,11 +386,7 @@ export class RequestCapabilitiesOperation extends ExternalOperation< // Store the authorization behavior (mode and expiration) const mode = authData.mode || "permissive"; const duration = authData.duration || 86400000 * 30; - await this.db.storeAppAuthorizationBehavior( - this.authorizationManager.appId, - mode, - duration, - ); + await this.db.storeAppAuthorizationBehavior(this.authorizationManager.appId, mode, duration); } async execute( diff --git a/shared/src/wallet/operations/send-tx-operation.ts b/shared/src/wallet/operations/send-tx-operation.ts index 2c4fb7f..2b99a94 100644 --- a/shared/src/wallet/operations/send-tx-operation.ts +++ b/shared/src/wallet/operations/send-tx-operation.ts @@ -1,31 +1,16 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; -import { TxHash, TxStatus } from "@aztec/stdlib/tx"; +import { TxStatus } from "@aztec/stdlib/tx"; import type { PXE } from "@aztec/pxe/client/lazy"; -import type { - ExecutionPayload, - TxExecutionRequest, - TxProvingResult, -} from "@aztec/stdlib/tx"; +import type { ExecutionPayload, TxExecutionRequest, TxProvingResult } from "@aztec/stdlib/tx"; import { waitForTx, type AztecNode } from "@aztec/aztec.js/node"; -import { inspect } from "util"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { InteractionManager } from "../managers/interaction-manager"; import type { AuthorizationManager } from "../managers/authorization-manager"; import type { DecodingCache } from "../decoding/decoding-cache"; import type { ReadableCallAuthorization } from "../decoding/call-authorization-formatter"; import type { DecodedExecutionTrace } from "../decoding/tx-callstack-decoder"; -import { - hashExecutionPayload, - generateSimulationTitle, -} from "../utils/simulation-utils"; +import { hashExecutionPayload, generateSimulationTitle } from "../utils/simulation-utils"; import type { SendOptions } from "@aztec/aztec.js/wallet"; import { type NoFrom, NO_FROM } from "@aztec/aztec.js/account"; import { @@ -107,10 +92,7 @@ export class SendTxOperation< interactionManager: InteractionManager, private authorizationManager: AuthorizationManager, private simulateTxOp: SimulateTxOperation, - private createAuthWit: ( - from: AztecAddress, - auth: CallIntent, - ) => Promise, + private createAuthWit: (from: AztecAddress, auth: CallIntent) => Promise, private createTxExecutionRequestFromPayloadAndFee: ( exec: ExecutionPayload, from: AztecAddress | NoFrom, @@ -169,9 +151,7 @@ export class SendTxOperation< async prepare( executionPayload: ExecutionPayload, opts: SendOptions, - ): Promise< - PrepareResult, SendTxDisplayData, SendTxExecutionData> - > { + ): Promise>> { const payloadHash = hashExecutionPayload(executionPayload); // Use simulateTx operation's prepare method (will throw if simulation fails). @@ -185,8 +165,7 @@ export class SendTxOperation< }); // Decode simulation results - const { callAuthorizations, executionTrace } = - prepared.executionData!.decoded; + const { callAuthorizations, executionTrace } = prepared.executionData!.decoded; // Create auth witnesses for call authorizations if (callAuthorizations.length > 0) { @@ -214,8 +193,7 @@ export class SendTxOperation< maxFeesPerGas: feeOptions.gasSettings.maxFeesPerGas, maxPriorityFeesPerGas: feeOptions.gasSettings.maxPriorityFeesPerGas, gasLimits: opts.fee?.gasSettings?.gasLimits ?? estimated.gasLimits, - teardownGasLimits: - opts.fee?.gasSettings?.teardownGasLimits ?? estimated.teardownGasLimits, + teardownGasLimits: opts.fee?.gasSettings?.teardownGasLimits ?? estimated.teardownGasLimits, }); // Create transaction request with estimated gas settings @@ -274,27 +252,19 @@ export class SendTxOperation< title: displayData.title, from: displayData.from.toString(), stats: displayData.stats, - embeddedPaymentMethodFeePayer: - displayData.embeddedPaymentMethodFeePayer?.toString(), + embeddedPaymentMethodFeePayer: displayData.embeddedPaymentMethodFeePayer?.toString(), }, timestamp: Date.now(), }, ]); } - async execute( - executionData: SendTxExecutionData, - ): Promise> { - // Track phase timings - const provingStartTime = Date.now(); - + async execute(executionData: SendTxExecutionData): Promise> { // Report proving stage await this.emitProgress("PROVING"); const from = - executionData.from === NO_FROM - ? NO_FROM - : AztecAddress.fromString(executionData.from!); + executionData.from === NO_FROM ? NO_FROM : AztecAddress.fromString(executionData.from!); let provenTx: TxProvingResult; try { @@ -305,27 +275,20 @@ export class SendTxOperation< } catch (provingError: unknown) { // Proving failed - offer to export debug data const errorMessage = - provingError instanceof Error - ? provingError.message - : String(provingError); + provingError instanceof Error ? provingError.message : String(provingError); await this.emitProgress("PROVING FAILED", errorMessage); // Generate profile data for debugging try { - const profileResult = await this.pxe.profileTx( - executionData.txRequest, - { - profileMode: "execution-steps", - skipProofGeneration: true, - scopes: this.scopesFrom(from), - }, - ); + const profileResult = await this.pxe.profileTx(executionData.txRequest, { + profileMode: "execution-steps", + skipProofGeneration: true, + scopes: this.scopesFrom(from, executionData.additionalScopes), + }); // Serialize the execution steps to msgpack format - const serializedData = serializePrivateExecutionSteps( - profileResult.executionSteps, - ); + const serializedData = serializePrivateExecutionSteps(profileResult.executionSteps); // Emit event for UI to show the debug export dialog this.interactionManager.dispatchEvent( @@ -341,10 +304,7 @@ export class SendTxOperation< ); } catch (profileError) { // If profiling also fails, just log and continue with original error - console.error( - "Failed to generate profile for debug export:", - profileError, - ); + console.error("Failed to generate profile for debug export:", profileError); } // Re-throw the original proving error @@ -355,17 +315,14 @@ export class SendTxOperation< const rawStats = provenTx.stats; const offchainOutput = extractOffchainOutput( provenTx.getOffchainEffects(), - provenTx.publicInputs.constants.anchorBlockHeader.globalVariables - .timestamp, + provenTx.publicInputs.constants.anchorBlockHeader.globalVariables.timestamp, ); const tx = await provenTx.toTx(); const txHash = tx.getTxHash(); if (await this.aztecNode.getTxEffect(txHash)) { - throw new Error( - `A settled tx with equal hash ${txHash.toString()} exists.`, - ); + throw new Error(`A settled tx with equal hash ${txHash.toString()} exists.`); } // Report sending stage @@ -389,18 +346,14 @@ export class SendTxOperation< sending: sendingTime, }, }; - await this.db.updateTxPayloadStats( - executionData.payloadHash, - enrichedStats, - ); + await this.db.updateTxPayloadStats(executionData.payloadHash, enrichedStats); return { txHash, ...offchainOutput } as SendTxResult; } // Otherwise, wait for the full receipt (default behavior on wait: undefined) await this.emitProgress("MINING", `TxHash: ${txHash.toString()}`); const miningStartTime = Date.now(); - const waitOpts = - typeof executionData.wait === "object" ? executionData.wait : undefined; + const waitOpts = typeof executionData.wait === "object" ? executionData.wait : undefined; const receipt = await waitForTx(this.aztecNode, txHash, { ...waitOpts, waitForStatus: TxStatus.PROPOSED, @@ -418,10 +371,7 @@ export class SendTxOperation< mining: miningTime, }, }; - await this.db.updateTxPayloadStats( - executionData.payloadHash, - enrichedStats, - ); + await this.db.updateTxPayloadStats(executionData.payloadHash, enrichedStats); return { receipt, ...offchainOutput } as SendTxResult; } diff --git a/shared/src/wallet/operations/simulate-tx-operation.ts b/shared/src/wallet/operations/simulate-tx-operation.ts index e8e46c4..8dffdcf 100644 --- a/shared/src/wallet/operations/simulate-tx-operation.ts +++ b/shared/src/wallet/operations/simulate-tx-operation.ts @@ -1,51 +1,29 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; import { AztecAddress } from "@aztec/stdlib/aztec-address"; -import { - TxSimulationResult, - SimulationOverrides, - type TxExecutionRequest, - type ExecutionPayload, - mergeExecutionPayloads, - BlockHeader, -} from "@aztec/stdlib/tx"; +import { type ExecutionPayload, BlockHeader } from "@aztec/stdlib/tx"; import type { PXE } from "@aztec/pxe/client/lazy"; -import { Fr } from "@aztec/foundation/curves/bn254"; -import { type NoFrom, NO_FROM } from "@aztec/aztec.js/account"; -import { DefaultEntrypoint } from "@aztec/entrypoints/default"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +import { NO_FROM } from "@aztec/aztec.js/account"; +import { WalletInteraction, type WalletInteractionType } from "../types/wallet-interaction"; import type { WalletDB, StoredStats } 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 type { DefaultAccountEntrypointOptions } from "@aztec/entrypoints/account"; 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 { SimulateOptions } from "@aztec/aztec.js/wallet"; -import type { ContractInstanceWithAddress } from "@aztec/stdlib/contract"; -import type { ContractArtifact, FunctionCall } from "@aztec/stdlib/abi"; -import type { GasSettings } from "@aztec/stdlib/gas"; -import type { FieldsOf } from "@aztec/foundation/types"; +import { hashExecutionPayload, generateSimulationTitle } from "../utils/simulation-utils"; +import { TxSimulationResultWithAppOffset, type SimulateOptions } from "@aztec/aztec.js/wallet"; +import type { Logger } from "@aztec/aztec.js/log"; import { type FeeOptions, + type CompleteFeeOptionsConfig, + type SimulateViaEntrypointOptions, extractOptimizablePublicStaticCalls, simulateViaNode, buildMergedSimulationResult, } from "@aztec/wallet-sdk/base-wallet"; -import type { ChainInfo } from "@aztec/entrypoints/interfaces"; import type { AztecNode } from "@aztec/aztec.js/node"; -import type { Logger } from "@aztec/aztec.js/log"; +import type { ChainInfo } from "@aztec/entrypoints/interfaces"; // Readable transaction information with decoded data interface ReadableTxInformation { @@ -53,20 +31,6 @@ interface ReadableTxInformation { executionTrace: DecodedExecutionTrace; } -// Fake account data structure -interface FakeAccountData { - account: { - createTxExecutionRequest: ( - payload: ExecutionPayload, - gasSettings: unknown, - chainInfo: ChainInfo, - options: DefaultAccountEntrypointOptions, - ) => Promise; - }; - instance: ContractInstanceWithAddress; - artifact: ContractArtifact; -} - // Arguments tuple for the operation type SimulateTxArgs = [ executionPayload: ExecutionPayload, @@ -75,11 +39,11 @@ type SimulateTxArgs = [ ]; // Result type for the operation -type SimulateTxResult = TxSimulationResult; +type SimulateTxResult = TxSimulationResultWithAppOffset; // Execution data stored between prepare and execute phases interface SimulateTxExecutionData { - simulationResult: TxSimulationResult; + simulationResult: TxSimulationResultWithAppOffset; payloadHash: string; decoded?: ReadableTxInformation; } @@ -88,7 +52,7 @@ interface SimulateTxExecutionData { type SimulateTxDisplayData = { payloadHash: string; title: string; - from: AztecAddress | NoFrom; + from: AztecAddress | typeof NO_FROM; decoded: ReadableTxInformation; stats?: StoredStats; embeddedPaymentMethodFeePayer?: string; @@ -97,14 +61,9 @@ type SimulateTxDisplayData = { /** * SimulateTx operation implementation. * - * Handles transaction simulation with the following features: - * - Fee options processing (gas estimation, payment methods) - * - Fake account creation for simulation - * - Transaction execution request creation - * - Transaction decoding with call authorizations and execution traces - * - Persistent authorization based on payload hash - * - Storage of simulation results - * - Support for existing interactions (e.g., from sendTx flow) + * Delegates simulation to the wallet's own simulateViaEntrypoint (injected as a + * callback), keeping the operation free of account/entrypoint plumbing. + * Public static calls are optimised via the node path before the private path runs. */ export class SimulateTxOperation extends ExternalOperation< SimulateTxArgs, @@ -121,31 +80,12 @@ export class SimulateTxOperation extends ExternalOperation< private decodingCache: DecodingCache, interactionManager: InteractionManager, private authorizationManager: AuthorizationManager, - private completeFeeOptionsForEstimation: ( - from: AztecAddress | NoFrom, - feePayer: AztecAddress | undefined, - gasSettings?: Partial>, - ) => Promise, - private completeFeeOptions: ( - from: AztecAddress | NoFrom, - feePayer: AztecAddress | undefined, - gasSettings?: Partial>, - ) => Promise, - private getFakeAccountDataFor: ( - address: AztecAddress, - ) => Promise, - private buildAccountOverrides: () => Promise< - Record< - string, - { instance: ContractInstanceWithAddress; artifact: ContractArtifact } - > - >, + private completeFeeOptions: (config: CompleteFeeOptionsConfig) => Promise, + private simulateViaEntrypoint: ( + payload: ExecutionPayload, + opts: SimulateViaEntrypointOptions, + ) => Promise, private getChainInfo: () => Promise, - private scopesFrom: ( - from: AztecAddress | NoFrom, - additionalScopes?: AztecAddress[], - ) => AztecAddress[], - private cancellableTransactions: boolean, private log: Logger, ) { super(); @@ -156,21 +96,13 @@ export class SimulateTxOperation extends ExternalOperation< _executionPayload: ExecutionPayload, _opts: SimulateOptions, ): Promise { - // No early return checks for this operation return undefined; } async prepare( executionPayload: ExecutionPayload, opts: SimulateOptions, - ): Promise< - PrepareResult< - SimulateTxResult, - SimulateTxDisplayData, - SimulateTxExecutionData - > - > { - // Generate payload hash and detailed title + ): Promise> { const payloadHash = hashExecutionPayload(executionPayload); const title = await generateSimulationTitle( executionPayload, @@ -179,44 +111,28 @@ export class SimulateTxOperation extends ExternalOperation< executionPayload.feePayer, ); - // Process fee options - const feeOptions = opts.fee?.estimateGas - ? 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(); - const executionOptions: DefaultAccountEntrypointOptions = { - txNonce: Fr.random(), - cancellable: this.cancellableTransactions, - feePaymentMethodOptions: feeOptions.accountFeePaymentMethodOptions, - }; + const feeOptions = await this.completeFeeOptions({ + from: opts.from, + feePayer: executionPayload.feePayer, + gasSettings: opts.fee?.gasSettings, + forEstimation: true, + }); - // STEP 1: Separate calls into optimized (public static) and normal paths + // Split calls into the public-static fast path and the normal private path const { optimizableCalls, remainingCalls } = extractOptimizablePublicStaticCalls(executionPayload); - const chainInfo = await this.getChainInfo(); let blockHeader: BlockHeader; - // PXE might not be synced yet, so we pull the latest header from the node - // To keep things consistent, we'll always try with PXE first try { blockHeader = await this.pxe.getSyncedBlockHeader(); } catch { blockHeader = (await this.node.getBlockHeader())!; } - // STEP 2: Run both paths in parallel - const simulationOrigin = - opts.from === NO_FROM ? AztecAddress.ZERO : opts.from; + + const simulationOrigin = opts.from === NO_FROM ? AztecAddress.ZERO : opts.from; + const chainInfo = await this.getChainInfo(); const simulationStart = Date.now(); + const [optimizedResults, normalResult] = await Promise.all([ optimizableCalls.length > 0 ? simulateViaNode( @@ -227,32 +143,25 @@ export class SimulateTxOperation extends ExternalOperation< feeOptions.gasSettings, blockHeader, opts.skipFeeEnforcement ?? true, - (address) => this.decodingCache.getAddressAlias(address), + (address: AztecAddress) => this.decodingCache.getAddressAlias(address), ) : Promise.resolve([]), - remainingCalls.length > 0 ? this.simulateViaEntrypoint( - executionPayload, - remainingCalls, - feeExecutionPayload, - feeOptions.gasSettings, - chainInfo, - executionOptions, - opts.from, - opts.additionalScopes, + { ...executionPayload, calls: remainingCalls }, + { + from: opts.from, + feeOptions, + additionalScopes: opts.additionalScopes, + skipTxValidation: opts.skipTxValidation, + skipFeeEnforcement: opts.skipFeeEnforcement ?? true, + }, ) : Promise.resolve(null), ]); - // STEP 3: Build the final merged TxSimulationResult const wallTime = Date.now() - simulationStart; - const simulationResult = buildMergedSimulationResult( - optimizedResults, - normalResult?.result ?? null, - ); - // Build metadata stats: prefer the real stats from simulation, but for public-view-only - // simulations stats is null so inject a minimal object with the wall-clock total. + const simulationResult = buildMergedSimulationResult(optimizedResults, normalResult); const metadataStats: StoredStats = simulationResult.stats ? (simulationResult.stats as StoredStats) : { @@ -271,15 +180,11 @@ export class SimulateTxOperation extends ExternalOperation< stats: metadataStats, }); - // STEP 4: Decode the transaction (including optimized public calls if any) const decodingService = new TxDecodingService(this.decodingCache, this.log); const decoded = await decodingService.decodeTransaction(simulationResult); - // Create one storage key per function call for 1:1 mapping with capabilities const storageKeys = - executionPayload.calls?.map( - (call) => `simulateTx:${call.to.toString()}:${call.name}`, - ) || []; + executionPayload.calls?.map((call) => `simulateTx:${call.to.toString()}:${call.name}`) || []; return { displayData: { @@ -302,72 +207,10 @@ export class SimulateTxOperation extends ExternalOperation< }; } - /** - * Run the normal (non-optimized) simulation path for private/non-static calls. - */ - private async simulateViaEntrypoint( - originalPayload: ExecutionPayload, - calls: FunctionCall[], - feeExecutionPayload: ExecutionPayload | undefined, - gasSettings: GasSettings, - chainInfo: ChainInfo, - executionOptions: DefaultAccountEntrypointOptions, - from: AztecAddress | NoFrom, - additionalScopes?: AztecAddress[], - ): Promise<{ result: TxSimulationResult; txReq: TxExecutionRequest }> { - const normalPayload = feeExecutionPayload - ? mergeExecutionPayloads([ - feeExecutionPayload, - { ...originalPayload, calls }, - ]) - : { ...originalPayload, calls }; - - // Build overrides for all known accounts so kernelless simulation works - // for any call that touches account contracts (including sponsored calls) - const accountOverrides = await this.buildAccountOverrides(); - - let txReq: TxExecutionRequest; - if (from === NO_FROM) { - const entrypoint = new DefaultEntrypoint(); - txReq = await entrypoint.createTxExecutionRequest( - normalPayload, - gasSettings, - chainInfo, - ); - } else { - const { account, instance, artifact } = - await this.getFakeAccountDataFor(from); - // Ensure the from account's stub is in the overrides map - accountOverrides[from.toString()] = { instance, artifact }; - txReq = await account.createTxExecutionRequest( - normalPayload, - gasSettings, - chainInfo, - executionOptions, - ); - } - - const overrides = - Object.keys(accountOverrides).length > 0 - ? new SimulationOverrides(accountOverrides) - : undefined; - - const result = await this.pxe.simulateTx(txReq, { - scopes: this.scopesFrom(from, additionalScopes), - simulatePublic: true, - skipFeeEnforcement: true, - skipTxValidation: true, - overrides, - }); - - return { result, txReq }; - } - async createInteraction( executionPayload: ExecutionPayload, opts: SimulateOptions, ): Promise> { - // Create interaction with simple title from args only const payloadHash = hashExecutionPayload(executionPayload); const title = await generateSimulationTitle( executionPayload, @@ -386,7 +229,6 @@ export class SimulateTxOperation extends ExternalOperation< }); await this.interactionManager.storeAndEmit(interaction); - return interaction; } @@ -394,12 +236,10 @@ export class SimulateTxOperation extends ExternalOperation< displayData: SimulateTxDisplayData, persistence?: PersistenceConfig, ): Promise { - // Update interaction with detailed title and status await this.emitProgress("REQUESTING AUTHORIZATION", undefined, false, { title: displayData.title, }); - // Request authorization with optional persistent caching await this.authorizationManager.requestAuthorization([ { id: crypto.randomUUID(), @@ -412,8 +252,7 @@ export class SimulateTxOperation extends ExternalOperation< title: displayData.title, from: displayData.from.toString(), stats: displayData.stats, - embeddedPaymentMethodFeePayer: - displayData.embeddedPaymentMethodFeePayer, + embeddedPaymentMethodFeePayer: displayData.embeddedPaymentMethodFeePayer, }, timestamp: Date.now(), persistence, @@ -421,9 +260,7 @@ export class SimulateTxOperation extends ExternalOperation< ]); } - async execute( - executionData: SimulateTxExecutionData, - ): Promise { + async execute(executionData: SimulateTxExecutionData): Promise { await this.emitProgress("SUCCESS", undefined, true); return executionData.simulationResult; } diff --git a/shared/src/wallet/operations/simulate-utility-operation.ts b/shared/src/wallet/operations/simulate-utility-operation.ts index d6bd0e6..c10f576 100644 --- a/shared/src/wallet/operations/simulate-utility-operation.ts +++ b/shared/src/wallet/operations/simulate-utility-operation.ts @@ -1,20 +1,8 @@ -import { - ExternalOperation, - type PrepareResult, - type PersistenceConfig, -} from "./base-operation"; -import type { AztecAddress } from "@aztec/stdlib/aztec-address"; -import type { AuthWitness } from "@aztec/stdlib/auth-witness"; -import type { - UtilityExecutionResult, - SimulationStats, -} from "@aztec/stdlib/tx"; +import { ExternalOperation, type PrepareResult, type PersistenceConfig } from "./base-operation"; +import type { UtilityExecutionResult, SimulationStats } from "@aztec/stdlib/tx"; import type { PXE } from "@aztec/pxe/client/lazy"; import type { ExecuteUtilityOptions } from "@aztec/aztec.js/wallet"; -import { - WalletInteraction, - type WalletInteractionType, -} from "../types/wallet-interaction"; +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"; @@ -119,18 +107,15 @@ export class SimulateUtilityOperation extends ExternalOperation< async prepare( call: FunctionCall, opts: ExecuteUtilityOptions, - ): Promise< - PrepareResult< - SimulateUtilityResult, - SimulateUtilityDisplayData, - SimulateUtilityExecutionData - > - > { + ): Promise> { // Generate hash for deduplication const payloadHash = hashUtilityCall(call); // Simulate the utility function - const simulationResult = await this.pxe.executeUtility(call, { authwits: opts.authWitnesses, scopes: opts.scopes }); + const simulationResult = await this.pxe.executeUtility(call, { + authwits: opts.authWitnesses, + scopes: opts.scopes, + }); // Get contract name for better display const contractName = await this.decodingCache.getAddressAlias(call.to); @@ -141,11 +126,7 @@ export class SimulateUtilityOperation extends ExternalOperation< const decoder = new TxCallStackDecoder(this.decodingCache, this.log); // Format the input arguments (these come from FunctionCall.args which are already typed) - const decodedArgs = await decoder.formatUtilityArguments( - call.to, - call.name, - call.args, - ); + const decodedArgs = await decoder.formatUtilityArguments(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( @@ -214,9 +195,7 @@ export class SimulateUtilityOperation extends ExternalOperation< ]); } - async execute( - executionData: SimulateUtilityExecutionData, - ): Promise { + async execute(executionData: SimulateUtilityExecutionData): Promise { // Execution is just returning the simulation result // The actual simulation happened in prepare phase await this.emitProgress("SUCCESS", undefined, true); diff --git a/shared/src/wallet/types/wallet-interaction.ts b/shared/src/wallet/types/wallet-interaction.ts index 502db34..a30c325 100644 --- a/shared/src/wallet/types/wallet-interaction.ts +++ b/shared/src/wallet/types/wallet-interaction.ts @@ -60,7 +60,7 @@ export class WalletInteraction { public complete: boolean, public title: string, public description: string, - public timestamp: number + public timestamp: number, ) {} update({ @@ -98,7 +98,7 @@ export class WalletInteraction { complete, title ?? "", description ?? "", - timestamp ?? Date.now() + timestamp ?? Date.now(), ); } @@ -110,7 +110,7 @@ export class WalletInteraction { this.complete, this.title, this.description, - BigInt(this.timestamp) + BigInt(this.timestamp), ); } @@ -138,7 +138,7 @@ export class WalletInteraction { complete, title, description, - timestamp + timestamp, ); } } diff --git a/shared/src/wallet/utils/index.ts b/shared/src/wallet/utils/index.ts index 0c775e5..b3ec77d 100644 --- a/shared/src/wallet/utils/index.ts +++ b/shared/src/wallet/utils/index.ts @@ -1,3 +1,2 @@ // Wallet utility exports export * from "./simulation-utils"; -export { prepareForFeePayment } from "./sponsored-fpc"; diff --git a/shared/src/wallet/utils/simulation-utils.ts b/shared/src/wallet/utils/simulation-utils.ts index 6163175..d5dc938 100644 --- a/shared/src/wallet/utils/simulation-utils.ts +++ b/shared/src/wallet/utils/simulation-utils.ts @@ -26,8 +26,8 @@ export function hashExecutionPayload(payload: ExecutionPayload): string { call.hideMsgSender, call.isStatic, call.args.length, - ...call.args - ) + ...call.args, + ), ); } @@ -63,7 +63,7 @@ export function hashUtilityCall(call: FunctionCall): string { call.hideMsgSender, call.isStatic, call.args.length, - ...call.args + ...call.args, ); return sha256(buffer).toString("hex"); } @@ -77,7 +77,7 @@ export async function generateSimulationTitle( executionPayload: ExecutionPayload, cache: DecodingCache, fromAccount: AztecAddress | NoFrom, - embeddedPaymentMethodFeePayer?: AztecAddress + embeddedPaymentMethodFeePayer?: AztecAddress, ): Promise { // Filter out wallet-added calls: // 1. Account entrypoint call (call to the account contract itself) diff --git a/shared/src/wallet/utils/sponsored-fpc.ts b/shared/src/wallet/utils/sponsored-fpc.ts deleted file mode 100644 index ad81751..0000000 --- a/shared/src/wallet/utils/sponsored-fpc.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { getContractInstanceFromInstantiationParams } from "@aztec/aztec.js/contracts"; -import { SponsoredFeePaymentMethod } from "@aztec/aztec.js/fee"; -import { AztecAddress } from "@aztec/aztec.js/addresses"; -import { Fr } from "@aztec/aztec.js/fields"; -import { type Wallet } from "@aztec/aztec.js/wallet"; -import { SPONSORED_FPC_SALT } from "@aztec/constants"; -import { SponsoredFPCContract } from "@aztec/noir-contracts.js/SponsoredFPC"; - -export async function prepareForFeePayment( - wallet: Wallet, - sponsoredFPCAddress?: AztecAddress, - sponsoredFPCVersion?: string -): Promise { - try { - const instance = await getContractInstanceFromInstantiationParams( - SponsoredFPCContract.artifact, - { - salt: new Fr(SPONSORED_FPC_SALT), - } - ); - - if (sponsoredFPCAddress && !sponsoredFPCAddress.equals(instance.address)) { - throw new Error( - `SponsoredFPC at version ${sponsoredFPCVersion} does not match the expected address. Computed ${instance.address} but received ${sponsoredFPCAddress}` - ); - } - - await wallet.registerContract(instance, SponsoredFPCContract.artifact); - return new SponsoredFeePaymentMethod(instance.address); - } catch (error) { - console.error("Error preparing SponsoredFeePaymentMethod:", error); - throw error; - } -} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..881c932 --- /dev/null +++ b/turbo.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://turbo.build/schema.json", + "ui": "tui", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", "out/**"] + }, + "lint": { + "dependsOn": ["^build"] + }, + "test": { + "dependsOn": ["^build"], + "outputs": [] + }, + "typecheck": { + "dependsOn": ["^build"] + }, + "dev": { + "cache": false, + "persistent": true + } + } +} diff --git a/web/package.json b/web/package.json index 965d10a..deaa2c8 100644 --- a/web/package.json +++ b/web/package.json @@ -7,22 +7,23 @@ "dev": "NODE_OPTIONS=--max_http_header_size=128000 vite", "build": "tsc -b && vite build", "preview": "NODE_OPTIONS=--max_http_header_size=128000 vite preview", - "lint": "eslint .", + "lint": "yarn exec eslint .", + "typecheck": "yarn exec tsc --noEmit", "local-aztec:enable": "node scripts/toggle-local-aztec.js enable && corepack yarn install", "local-aztec:disable": "node scripts/toggle-local-aztec.js disable && corepack yarn install" }, "dependencies": { - "@aztec/accounts": "v4.2.0-aztecnr-rc.2", - "@aztec/aztec.js": "v4.2.0-aztecnr-rc.2", - "@aztec/constants": "v4.2.0-aztecnr-rc.2", - "@aztec/entrypoints": "v4.2.0-aztecnr-rc.2", - "@aztec/foundation": "v4.2.0-aztecnr-rc.2", - "@aztec/kv-store": "v4.2.0-aztecnr-rc.2", - "@aztec/noir-contracts.js": "v4.2.0-aztecnr-rc.2", - "@aztec/protocol-contracts": "v4.2.0-aztecnr-rc.2", - "@aztec/pxe": "v4.2.0-aztecnr-rc.2", - "@aztec/stdlib": "v4.2.0-aztecnr-rc.2", - "@aztec/wallet-sdk": "v4.2.0-aztecnr-rc.2", + "@aztec/accounts": "v4.2.0-nightly.20260412", + "@aztec/aztec.js": "v4.2.0-nightly.20260412", + "@aztec/constants": "v4.2.0-nightly.20260412", + "@aztec/entrypoints": "v4.2.0-nightly.20260412", + "@aztec/foundation": "v4.2.0-nightly.20260412", + "@aztec/kv-store": "v4.2.0-nightly.20260412", + "@aztec/noir-contracts.js": "v4.2.0-nightly.20260412", + "@aztec/protocol-contracts": "v4.2.0-nightly.20260412", + "@aztec/pxe": "v4.2.0-nightly.20260412", + "@aztec/stdlib": "v4.2.0-nightly.20260412", + "@aztec/wallet-sdk": "v4.2.0-nightly.20260412", "@demo-wallet/shared": "workspace:*", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", @@ -39,7 +40,6 @@ "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", "@vitejs/plugin-react-swc": "^3.5.0", - "eslint": "^9.17.0", "typescript": "~5.6.2", "vite": "^5.4.20", "vite-plugin-node-polyfills": "^0.24.0" diff --git a/web/src/ui/IframeShell.tsx b/web/src/ui/IframeShell.tsx index 7bb66e7..7d0202b 100644 --- a/web/src/ui/IframeShell.tsx +++ b/web/src/ui/IframeShell.tsx @@ -13,14 +13,7 @@ * bootstrapping accounts from the cookie. */ -import { - StrictMode, - useMemo, - useState, - useEffect, - useRef, - useCallback, -} from "react"; +import { StrictMode, useMemo, useState, useEffect, useRef, useCallback } from "react"; import { Box, Button, @@ -57,10 +50,7 @@ import { setCookiePassphrase, hasCookiePassphrase, } from "../wallet/wallet-service.ts"; -import { - hasAccountsCookie, - readAccountsCookie, -} from "../wallet/sync-cookies.ts"; +import { hasAccountsCookie, readAccountsCookie } from "../wallet/sync-cookies.ts"; import { EmojiVerification } from "./components/EmojiVerification.tsx"; import { PinDialog } from "./components/PinDialog.tsx"; import { Fr } from "@aztec/aztec.js/fields"; @@ -218,8 +208,8 @@ function StorageAccessGate({ ) : ( <> - Your browser requires you to visit the wallet site directly before - it can be used in an iframe. + Your browser requires you to visit the wallet site directly before it can be used in an + iframe. Open wallet in a new tab @@ -249,8 +239,7 @@ function NoCookieGate({ onRetry }: { onRetry: () => void }) { > Aztec Web Demo Wallet - No wallet accounts found. Create an account in the standalone wallet - first. + No wallet accounts found. Create an account in the standalone wallet first. Open wallet @@ -267,7 +256,9 @@ function NoCookieGate({ onRetry }: { onRetry: () => void }) { function IframeContent() { const { currentNetwork, switchNetwork } = useNetwork(); const switchNetworkRef = useRef(switchNetwork); - useEffect(() => { switchNetworkRef.current = switchNetwork; }, [switchNetwork]); + useEffect(() => { + switchNetworkRef.current = switchNetwork; + }, [switchNetwork]); // Gate states: // checking → needs-storage → needs-pin → ready @@ -286,10 +277,7 @@ function IframeContent() { // Wallet state (only used after gate === "ready") const [authQueue, setAuthQueue] = useState([]); const [verificationHash, setVerificationHash] = useState(null); - const clearVerificationHash = useCallback( - () => setVerificationHash(null), - [], - ); + const clearVerificationHash = useCallback(() => setVerificationHash(null), []); const clearVerificationHashRef = useRef(clearVerificationHash); useEffect(() => { @@ -310,9 +298,7 @@ function IframeContent() { // ─── PIN gate promise ─── // getWallet awaits this before bootstrapping accounts from cookie. // Resolved when the PIN is verified or passphrase is already set. - const pinGateRef = useRef<{ resolve: () => void; promise: Promise }>( - null!, - ); + const pinGateRef = useRef<{ resolve: () => void; promise: Promise }>(null!); if (!pinGateRef.current) { let resolve: () => void; const promise = new Promise((r) => { @@ -355,7 +341,7 @@ function IframeContent() { } else { setGate("no-cookie"); } - } catch (err) { + } catch (_err) { setGate("needs-visit"); } }, []); @@ -420,22 +406,13 @@ function IframeContent() { clearVerificationHashRef.current(); const rawChainId = (chainInfo as any).chainId; const rawVersion = (chainInfo as any).version; - const chainId = - rawChainId instanceof Fr - ? rawChainId - : Fr.fromString(String(rawChainId)); - const version = - rawVersion instanceof Fr - ? rawVersion - : Fr.fromString(String(rawVersion)); + const chainId = rawChainId instanceof Fr ? rawChainId : Fr.fromString(String(rawChainId)); + const version = rawVersion instanceof Fr ? rawVersion : Fr.fromString(String(rawVersion)); // Lock the iframe to the dApp's network on first wallet request if (!networkLocked) { networkLocked = true; - const dAppNetwork = getNetworkByChainId( - chainId.toNumber(), - version.toNumber(), - ); + const dAppNetwork = getNetworkByChainId(chainId.toNumber(), version.toNumber()); if (dAppNetwork) { switchNetworkRef.current(dAppNetwork.id); } diff --git a/web/src/ui/StandaloneShell.tsx b/web/src/ui/StandaloneShell.tsx index 17d6434..d6c62ec 100644 --- a/web/src/ui/StandaloneShell.tsx +++ b/web/src/ui/StandaloneShell.tsx @@ -43,13 +43,7 @@ export function StandaloneShell() { ); if (!pinReady) { - return ( - - ); + return ; } return ; diff --git a/web/src/ui/components/PinDialog.tsx b/web/src/ui/components/PinDialog.tsx index 122ccca..2b383bd 100644 --- a/web/src/ui/components/PinDialog.tsx +++ b/web/src/ui/components/PinDialog.tsx @@ -12,12 +12,7 @@ */ import { useState, useCallback } from "react"; -import { - Box, - Button, - TextField, - Typography, -} from "@mui/material"; +import { Box, Button, TextField, Typography } from "@mui/material"; export interface PinDialogProps { mode: "set" | "enter"; diff --git a/web/src/ui/utils/wallet-api.ts b/web/src/ui/utils/wallet-api.ts index b287c31..cc268c7 100644 --- a/web/src/ui/utils/wallet-api.ts +++ b/web/src/ui/utils/wallet-api.ts @@ -22,22 +22,25 @@ const proofDebugExportListeners = new Set(); export function emitWalletUpdate(detail: unknown) { const parsed = typeof detail === "string" ? JSON.parse(detail) : detail; - walletUpdateListeners.forEach(cb => cb(parsed)); + walletUpdateListeners.forEach((cb) => cb(parsed)); } function emitAuthorizationRequest(detail: unknown) { // detail arrives as a JSON string (from AuthorizationRequestEvent which calls jsonStringify) const parsed = typeof detail === "string" ? JSON.parse(detail) : detail; - authorizationRequestListeners.forEach(cb => cb(parsed)); + authorizationRequestListeners.forEach((cb) => cb(parsed)); } function emitProofDebugExportRequest(detail: unknown) { const parsed = typeof detail === "string" ? JSON.parse(detail) : detail; - proofDebugExportListeners.forEach(cb => cb(parsed)); + proofDebugExportListeners.forEach((cb) => cb(parsed)); } // Cache: chainId-version → InternalWallet -const walletCache = new Map>["internal"]>>(); +const walletCache = new Map< + string, + Promise>["internal"]> +>(); function getCacheKey(chainId: Fr, version: Fr): string { return `${chainId.toString()}-${version.toString()}`; @@ -49,15 +52,11 @@ async function getInternalWallet( ): Promise>["internal"]> { const key = getCacheKey(chainId, version); if (!walletCache.has(key)) { - const p = getOrCreateSession( - { chainId, version }, - "internal-ui", - (eventType, detail) => { - if (eventType === "wallet-update") emitWalletUpdate(detail); - else if (eventType === "authorization-request") emitAuthorizationRequest(detail); - else if (eventType === "proof-debug-export-request") emitProofDebugExportRequest(detail); - }, - ).then(({ internal }) => internal); + const p = getOrCreateSession({ chainId, version }, "internal-ui", (eventType, detail) => { + if (eventType === "wallet-update") emitWalletUpdate(detail); + else if (eventType === "authorization-request") emitAuthorizationRequest(detail); + else if (eventType === "proof-debug-export-request") emitProofDebugExportRequest(detail); + }).then(({ internal }) => internal); walletCache.set(key, p); } return walletCache.get(key)!; @@ -110,7 +109,7 @@ export class WalletApi { return async () => { throw new Error( "Account creation is not available in embedded mode. " + - "Please create accounts in the standalone wallet.", + "Please create accounts in the standalone wallet.", ); }; } diff --git a/web/src/wallet/sync-cookies.ts b/web/src/wallet/sync-cookies.ts index 5e5de81..0b3a138 100644 --- a/web/src/wallet/sync-cookies.ts +++ b/web/src/wallet/sync-cookies.ts @@ -92,12 +92,8 @@ export async function writeAccountsCookie( * Returns empty array if cookie is missing or decryption fails (wrong passphrase). * Throws on wrong passphrase so the caller can prompt again. */ -export async function readAccountsCookie( - passphrase: string, -): Promise { - const match = document.cookie - .split("; ") - .find((c) => c.startsWith(`${COOKIE_NAME}=`)); +export async function readAccountsCookie(passphrase: string): Promise { + const match = document.cookie.split("; ").find((c) => c.startsWith(`${COOKIE_NAME}=`)); if (!match) return []; @@ -114,9 +110,7 @@ export async function readAccountsCookie( * Check whether the accounts cookie exists (without decrypting). */ export function hasAccountsCookie(): boolean { - return document.cookie - .split("; ") - .some((c) => c.startsWith(`${COOKIE_NAME}=`)); + return document.cookie.split("; ").some((c) => c.startsWith(`${COOKIE_NAME}=`)); } /** @@ -238,9 +232,7 @@ export async function writeContactsCookies( * Read and decrypt contacts from all numbered cookies. * Throws on wrong passphrase. */ -export async function readContactsCookies( - passphrase: string, -): Promise { +export async function readContactsCookies(passphrase: string): Promise { const allCookies = document.cookie.split("; "); const chunks: Uint8Array[] = []; @@ -271,9 +263,7 @@ export async function readContactsCookies( * Check whether any contacts cookies exist (without decrypting). */ export function hasContactsCookies(): boolean { - return document.cookie - .split("; ") - .some((c) => c.startsWith(`${CONTACTS_COOKIE_PREFIX}0=`)); + return document.cookie.split("; ").some((c) => c.startsWith(`${CONTACTS_COOKIE_PREFIX}0=`)); } /** @@ -378,9 +368,7 @@ export async function readCapabilitiesCookies( * Check whether any capabilities cookies exist (without decrypting). */ export function hasCapabilitiesCookies(): boolean { - return document.cookie - .split("; ") - .some((c) => c.startsWith(`${CAPS_COOKIE_PREFIX}0=`)); + return document.cookie.split("; ").some((c) => c.startsWith(`${CAPS_COOKIE_PREFIX}0=`)); } /** diff --git a/web/src/wallet/wallet-service.ts b/web/src/wallet/wallet-service.ts index 151feca..599ff75 100644 --- a/web/src/wallet/wallet-service.ts +++ b/web/src/wallet/wallet-service.ts @@ -62,10 +62,7 @@ type SessionData = { }>; /** One-time account/contact bootstrap from cookies. Runs once per session. */ bootstrapDone: Promise | null; - wallets: Map< - string, - Promise<{ external: ExternalWallet; internal: InternalWallet }> - >; + wallets: Map>; }; const RUNNING_SESSIONS = new Map(); @@ -110,10 +107,7 @@ export async function getOrCreateSession( appId: string, onWalletEvent: (eventType: string, detail: unknown) => void, ): Promise<{ external: ExternalWallet; internal: InternalWallet }> { - const network = getNetworkByChainId( - chainInfo.chainId.toNumber(), - chainInfo.version.toNumber(), - ); + const network = getNetworkByChainId(chainInfo.chainId.toNumber(), chainInfo.version.toNumber()); if (!network) { throw new Error( `Unknown network: chainId=${chainInfo.chainId.toNumber()}, version=${chainInfo.version.toNumber()}`, @@ -133,9 +127,7 @@ export async function getOrCreateSession( if (!session) { const log = createLogger("wallet:session"); - log.info( - `[PXE-INIT] Creating NEW session with shared PXE instance for sessionId=${sessionId}`, - ); + log.info(`[PXE-INIT] Creating NEW session with shared PXE instance for sessionId=${sessionId}`); const pxeInit = (async () => { const l1Contracts = await node.getL1ContractAddresses(); @@ -175,11 +167,7 @@ export async function getOrCreateSession( ); const db = WalletDB.init(walletDBStore, walletDBLogger); - const pxe = await createPXE( - node, - { ...getPXEConfig(), ...configOverrides }, - options, - ); + const pxe = await createPXE(node, { ...getPXEConfig(), ...configOverrides }, options); const pendingAuthorizations = new Map< string, @@ -248,7 +236,9 @@ export async function getOrCreateSession( let type: string | undefined; try { type = JSON.parse(detail)?.type; - } catch { /* ignore parse errors */ } + } catch { + /* ignore parse errors */ + } if (!IS_IFRAME && (type === "createAccount" || type === "deployAccount")) { scheduleAccountSync(sharedResources.db); @@ -266,15 +256,9 @@ export async function getOrCreateSession( wallet.addEventListener("authorization-request", (event: Event) => { onWalletEvent("authorization-request", (event as CustomEvent).detail); }); - wallet.addEventListener( - "proof-debug-export-request", - (event: Event) => { - onWalletEvent( - "proof-debug-export-request", - (event as CustomEvent).detail, - ); - }, - ); + wallet.addEventListener("proof-debug-export-request", (event: Event) => { + onWalletEvent("proof-debug-export-request", (event as CustomEvent).detail); + }); }; wireEvents(externalWallet); @@ -332,9 +316,7 @@ export async function bootstrapAccountsFromCookie( const { db } = await getSharedResources(chainInfo); const existingAccounts = await db.listAccounts(); - const existingAddresses = new Set( - existingAccounts.map((a) => a.item.toString()), - ); + const existingAddresses = new Set(existingAccounts.map((a) => a.item.toString())); let imported = 0; for (const portable of portableAccounts) { @@ -352,9 +334,7 @@ export async function bootstrapAccountsFromCookie( alias: portable.alias, }); imported++; - log.info( - `Imported account ${portable.alias ?? portable.address} from cookie`, - ); + log.info(`Imported account ${portable.alias ?? portable.address} from cookie`); } // Sync deployed state from cookie @@ -364,9 +344,7 @@ export async function bootstrapAccountsFromCookie( // Register with PXE via the wallet's getAccountManager (idempotent). await wallet.getAccountManager(portable.type, secretKey, salt, signingKey); - log.info( - `Registered account ${portable.alias ?? portable.address} with PXE`, - ); + log.info(`Registered account ${portable.alias ?? portable.address} with PXE`); } log.info( @@ -381,10 +359,7 @@ export async function bootstrapAccountsFromCookie( * Bypasses InternalWallet.registerSender to avoid emitting interaction events * for each bootstrapped contact (which would clutter the interaction history). */ -async function bootstrapContactsFromCookie( - db: WalletDB, - pxe: PXE, -): Promise { +async function bootstrapContactsFromCookie(db: WalletDB, pxe: PXE): Promise { const log = createLogger("wallet:cookie"); if (!_cookiePassphrase) { @@ -440,9 +415,7 @@ async function syncAccountsToCookie(db: WalletDB): Promise { `Synced ${portableAccounts.length} account(s) to encrypted cookie`, ); } catch (e) { - createLogger("wallet:cookie").warn( - `Failed to sync accounts to cookie: ${e}`, - ); + createLogger("wallet:cookie").warn(`Failed to sync accounts to cookie: ${e}`); } } @@ -465,9 +438,7 @@ async function syncContactsToCookie(db: WalletDB): Promise { `Synced ${portableContacts.length} contact(s) to encrypted cookies`, ); } catch (e) { - createLogger("wallet:cookie").warn( - `Failed to sync contacts to cookie: ${e}`, - ); + createLogger("wallet:cookie").warn(`Failed to sync contacts to cookie: ${e}`); } } @@ -486,9 +457,7 @@ async function syncCapabilitiesToCookie(db: WalletDB): Promise { `Synced capabilities for ${apps.length} app(s) to encrypted cookies`, ); } catch (e) { - createLogger("wallet:cookie").warn( - `Failed to sync capabilities to cookie: ${e}`, - ); + createLogger("wallet:cookie").warn(`Failed to sync capabilities to cookie: ${e}`); } } diff --git a/web/vite.config.ts b/web/vite.config.ts index bbf9f72..492b7cc 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -14,12 +14,9 @@ const polyfillsPkgPath = resolve( const nodePolyfillsFix = (options?: PolyfillOptions): Plugin => { return { ...nodePolyfills(options), - /* @ts-ignore */ + // @ts-expect-error - resolveId signature mismatch with vite-plugin-node-polyfills spread type resolveId(source: string) { - const m = - /^vite-plugin-node-polyfills\/shims\/(buffer|global|process)$/.exec( - source, - ); + const m = /^vite-plugin-node-polyfills\/shims\/(buffer|global|process)$/.exec(source); if (m) { return resolve(polyfillsPkgPath, `shims/${m[1]}/dist/index.cjs`); } @@ -38,8 +35,8 @@ export default defineConfig({ "Cross-Origin-Resource-Policy": "cross-origin", }, fs: { - allow: [searchForWorkspaceRoot(import.meta.dirname)], - }, + allow: [searchForWorkspaceRoot(import.meta.dirname)], + }, }, optimizeDeps: { // These packages contain native WASM/binary assets - exclude from pre-bundling diff --git a/yarn.lock b/yarn.lock index b8d1646..3426b06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -645,66 +645,66 @@ __metadata: languageName: node linkType: hard -"@aztec/accounts@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/accounts@npm:4.2.0-aztecnr-rc.2" - dependencies: - "@aztec/aztec.js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/entrypoints": "npm:4.2.0-aztecnr-rc.2" - "@aztec/ethereum": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" +"@aztec/accounts@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/accounts@npm:4.2.0-nightly.20260412" + dependencies: + "@aztec/aztec.js": "npm:4.2.0-nightly.20260412" + "@aztec/entrypoints": "npm:4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" tslib: "npm:^2.4.0" - checksum: 10c0/746711e7194a8c215eab294b3d45c548878bde4c980d73b67e571f34932aba0cf9dc050ea5c48850d8689395191627c3f1a293135833bc1733eac1ef222e2929 + checksum: 10c0/4c309f8391993139d4a5648e9f700901a5dd11fdf69676c7bea772f085314699effd33f259706814c9cedb5cd4b5065dc1f3c1ce1534e8524230c00c0c70c2ed languageName: node linkType: hard -"@aztec/aztec.js@npm:4.2.0-aztecnr-rc.2, @aztec/aztec.js@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/aztec.js@npm:4.2.0-aztecnr-rc.2" +"@aztec/aztec.js@npm:4.2.0-nightly.20260412, @aztec/aztec.js@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/aztec.js@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/entrypoints": "npm:4.2.0-aztecnr-rc.2" - "@aztec/ethereum": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/l1-artifacts": "npm:4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/entrypoints": "npm:4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/l1-artifacts": "npm:4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" axios: "npm:^1.13.5" tslib: "npm:^2.4.0" viem: "npm:@aztec/viem@2.38.2" zod: "npm:^3.23.8" - checksum: 10c0/7d513436c427c45c399dcc843dba36211d26d1655a8679725edb64b5df9dcf7de48d9c537970c3e67086dfeecd3624befecdff4b8f8052a5958b359736bac31a + checksum: 10c0/34bdbe90a03e5af753c56b5b2d5c0875e925676e91b043f828decf0af1381b2fe95564ef75f740d0a094fff6b76481e88dedc63a06da3096f0df310113781b21 languageName: node linkType: hard -"@aztec/bb-prover@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/bb-prover@npm:4.2.0-aztecnr-rc.2" +"@aztec/bb-prover@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/bb-prover@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/bb.js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-noirc_abi": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-protocol-circuits-types": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-types": "npm:4.2.0-aztecnr-rc.2" - "@aztec/simulator": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" - "@aztec/telemetry-client": "npm:4.2.0-aztecnr-rc.2" - "@aztec/world-state": "npm:4.2.0-aztecnr-rc.2" + "@aztec/bb.js": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/noir-noirc_abi": "npm:4.2.0-nightly.20260412" + "@aztec/noir-protocol-circuits-types": "npm:4.2.0-nightly.20260412" + "@aztec/noir-types": "npm:4.2.0-nightly.20260412" + "@aztec/simulator": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/telemetry-client": "npm:4.2.0-nightly.20260412" + "@aztec/world-state": "npm:4.2.0-nightly.20260412" commander: "npm:^12.1.0" pako: "npm:^2.1.0" source-map-support: "npm:^0.5.21" tslib: "npm:^2.4.0" bin: bb-cli: dest/bb/index.js - checksum: 10c0/ace75174c8a69d07aa92f4bc0656abeb6e506693f32738298d850e0507e038e2d0c303c3f9183edda26d9a25d2e4aaf26ac3c78de15ec70d9b4ef9f2ee5af58a + checksum: 10c0/d9b2f13852acd8f15b4ce3a2d2a6147ce5f5ca7a9205ff2cb30f47895c6f726fe3690919206db8b02ace465804392432cee7a158112c4dab17bca5783bec4fc9 languageName: node linkType: hard -"@aztec/bb.js@npm:4.2.0-aztecnr-rc.2, @aztec/bb.js@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/bb.js@npm:4.2.0-aztecnr-rc.2" +"@aztec/bb.js@npm:4.2.0-nightly.20260412, @aztec/bb.js@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/bb.js@npm:4.2.0-nightly.20260412" dependencies: comlink: "npm:^4.4.1" commander: "npm:^12.1.0" @@ -714,65 +714,65 @@ __metadata: tslib: "npm:^2.4.0" bin: bb: dest/node/bin/index.js - checksum: 10c0/9b471b0d6564841eebed8cfc47a2dd743362317ad83f3e4d6bb173afc8f71f02246f562e9db1b7bace3dbb23bcdb051c22019e463d83217f49a4a9c69104362c + checksum: 10c0/3e8e9a3888e414e836d8e0421136089266be9a66717238719c44da21a32e9ef176e2bc2829b0f23aa6aa0e810acc4f92ebc3b892188dec7f3bb7afe1760f2b24 languageName: node linkType: hard -"@aztec/blob-lib@npm:4.2.0-aztecnr-rc.2, @aztec/blob-lib@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/blob-lib@npm:4.2.0-aztecnr-rc.2" +"@aztec/blob-lib@npm:4.2.0-nightly.20260412, @aztec/blob-lib@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/blob-lib@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" "@crate-crypto/node-eth-kzg": "npm:^0.10.0" tslib: "npm:^2.4.0" - checksum: 10c0/783add261c60a9dcaade735cd55544ac14afd846d2cc31769b91588447fb59506b3ec0c7872282d6b87a369f4cbc0a0f2ec3d8f6984d2d868738d1b284b388c3 + checksum: 10c0/fe8b19a8bb26970cae58876b705ee3101790ac10041fef2f2d6b9b9acef6ea5986316704bb187307c05bd78b59cc06147f49bf1042adead24142e6c4b631c543 languageName: node linkType: hard -"@aztec/builder@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/builder@npm:4.2.0-aztecnr-rc.2" +"@aztec/builder@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/builder@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" commander: "npm:^12.1.0" - checksum: 10c0/8f6e05da3f42af1cf0397f898b67cd12d6343dbed15537cd6c8e5953596d926e5b0d51cedff8c27837f9e633c409740c3cbd2b712b31ed3beb8a7e1453df5fdb + checksum: 10c0/5488207fde2ab772e360e0a8ace186917c684002da01d48fcaa03f28fd82c0ffb9e64d3d686f406d02486e0703138695d73483e4d3302fe6c8b9435ab47ab685 languageName: node linkType: hard -"@aztec/constants@npm:4.2.0-aztecnr-rc.2, @aztec/constants@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/constants@npm:4.2.0-aztecnr-rc.2" +"@aztec/constants@npm:4.2.0-nightly.20260412, @aztec/constants@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/constants@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" tslib: "npm:^2.4.0" - checksum: 10c0/05a44435b45c49a0daa3459649dd361a69b3414cfa5ce476f6b7df818c707d0461ed4dfb133f185485c0670c4b66cbd994d0645cd197592b667524c6210dc32e + checksum: 10c0/cfec60215306956d536ded2fb285ee65bed9a4062c61f76dc8cb6dc7b6efd2501091ef5df7f699f8b5a6df3493febaba781f5a8ad4cab2ca4547cca322511322 languageName: node linkType: hard -"@aztec/entrypoints@npm:4.2.0-aztecnr-rc.2, @aztec/entrypoints@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/entrypoints@npm:4.2.0-aztecnr-rc.2" +"@aztec/entrypoints@npm:4.2.0-nightly.20260412, @aztec/entrypoints@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/entrypoints@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/ee2ca23db937c8047aa654b7d97edb1dc1cb0efe8bebcf902d95f4a1ae08f55997cef2375752a0ee974f1c5549e91e47b96db46517b491b0c1d21c4ea664d5e0 + checksum: 10c0/12383a50331cfce063c2cd02477ff69f85bcbf9b7d81fa44e0eda6b73107819b1cd8d4bb3312926e4f6662c1f640f39c3ca5c5a07df20559a12ce23dcbbb03cf languageName: node linkType: hard -"@aztec/ethereum@npm:4.2.0-aztecnr-rc.2, @aztec/ethereum@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/ethereum@npm:4.2.0-aztecnr-rc.2" +"@aztec/ethereum@npm:4.2.0-nightly.20260412, @aztec/ethereum@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/ethereum@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/blob-lib": "npm:4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/l1-artifacts": "npm:4.2.0-aztecnr-rc.2" + "@aztec/blob-lib": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/l1-artifacts": "npm:4.2.0-nightly.20260412" "@viem/anvil": "npm:^0.0.10" dotenv: "npm:^16.0.3" lodash.chunk: "npm:^4.2.0" @@ -780,15 +780,15 @@ __metadata: tslib: "npm:^2.4.0" viem: "npm:@aztec/viem@2.38.2" zod: "npm:^3.23.8" - checksum: 10c0/bef1b0c5e5495816f144afdfece50b4b898be61a8128bb889776541fd4623b585256e9a399f97126ccbaed458adeac55d1770b82b79a2804887a97db6a0c6ad7 + checksum: 10c0/b7bdb1015b7a453259e5da94599c4792944c1339d14707cb0dc7811aac698dee0d8c56258cb8aad825c020bcd769935546087d29802e193e3a156b11aa7c793a languageName: node linkType: hard -"@aztec/foundation@npm:4.2.0-aztecnr-rc.2, @aztec/foundation@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/foundation@npm:4.2.0-aztecnr-rc.2" +"@aztec/foundation@npm:4.2.0-nightly.20260412, @aztec/foundation@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/foundation@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/bb.js": "npm:4.2.0-aztecnr-rc.2" + "@aztec/bb.js": "npm:4.2.0-nightly.20260412" "@koa/cors": "npm:^5.0.0" "@noble/curves": "npm:=1.7.0" "@noble/hashes": "npm:^1.6.1" @@ -810,169 +810,169 @@ __metadata: sha3: "npm:^2.1.4" undici: "npm:^5.28.5" zod: "npm:^3.23.8" - checksum: 10c0/28d44afd6f28c9f54f2d3567b46776344a09072be12d2b761dd81b128e5d6a092a77111b536aefe7a899e8f123b07074ee1be3f47b90e422e4502992f3746305 + checksum: 10c0/379f6a93893775d99b3c91793eb3df7c53d8ff6424de83ac1a5d89344c7366b0892929593f08cece3d2e56ab825727b5d9ba546788cb6ff0cc3a9d8c908034ed languageName: node linkType: hard -"@aztec/key-store@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/key-store@npm:4.2.0-aztecnr-rc.2" +"@aztec/key-store@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/key-store@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/kv-store": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/kv-store": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" tslib: "npm:^2.4.0" - checksum: 10c0/954fa6755b2dffe85d3e1f858cb89844782c240b0542d21fda5be0b8347cb1469b468409d7db23d6c0263022ca241727f96ef53d4e7e646d930417d71f2c0c11 + checksum: 10c0/84e34402e3d7490d2a0c2c005aca57834c1b1ecbb621bd91e3a4e398cafe04617da109124757daae651ab2720e86f1f258267f9987303a10f804b5d61b37513d languageName: node linkType: hard -"@aztec/kv-store@npm:4.2.0-aztecnr-rc.2, @aztec/kv-store@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/kv-store@npm:4.2.0-aztecnr-rc.2" +"@aztec/kv-store@npm:4.2.0-nightly.20260412, @aztec/kv-store@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/kv-store@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/ethereum": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/native": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/native": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" idb: "npm:^8.0.0" lmdb: "npm:^3.2.0" msgpackr: "npm:^1.11.2" ohash: "npm:^2.0.11" ordered-binary: "npm:^1.5.3" - checksum: 10c0/d4403dd175d0a689ed4291cd128ce341cac389b1aa6cb83c481b6694d6955b3f1967e5474b9e54ae5830707855238f63ca0b3397f3cea534da6f6a0ecac410eb + checksum: 10c0/3fdb700f0842a12c98fc32e7a54abc3405f4dac3a33a556002cc2fe9d969c8e0a326168cc611e76d137ac6f1779dcb97cb3bc27b60c3698ac6726393668c5ac9 languageName: node linkType: hard -"@aztec/l1-artifacts@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/l1-artifacts@npm:4.2.0-aztecnr-rc.2" +"@aztec/l1-artifacts@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/l1-artifacts@npm:4.2.0-nightly.20260412" dependencies: tslib: "npm:^2.4.0" - checksum: 10c0/7ee413bf4cf147d301d93b1c25ec8b7c764ab901bcc72a11103d77a9c2af5eceaa3427c7d8c0b2185dc3e04adfdbc55ee92cb63ddba3811cc1c91c065118f528 + checksum: 10c0/209dd1b1e9cf42da472a866415a2c4d8191dc0e5209eec406c253f4100b7c1d3aaa28e96cf5c7fab7aab1670dd8b98a7cd3ab3631fa62ef14f0a5394652faa1c languageName: node linkType: hard -"@aztec/merkle-tree@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/merkle-tree@npm:4.2.0-aztecnr-rc.2" +"@aztec/merkle-tree@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/merkle-tree@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/kv-store": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/kv-store": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" sha256: "npm:^0.2.0" tslib: "npm:^2.4.0" - checksum: 10c0/f3aaf95af249a4c4b457609cd8085c420ee0e1a5491124031c18733c8e7389a3e0b78fdc6e1a87aaa7d374205a56b50bae3a8c9136e128f1746d58735dc30c81 + checksum: 10c0/f8c6c0fae86589d216f38780d46caceb678f8f9a6e3ace3a43eff00f9840246b8712854d0c126233f2c5364983635bb382333e4a505f63759b4834206f3c9593 languageName: node linkType: hard -"@aztec/native@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/native@npm:4.2.0-aztecnr-rc.2" +"@aztec/native@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/native@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/bb.js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" + "@aztec/bb.js": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" msgpackr: "npm:^1.11.2" - checksum: 10c0/baf2d7aff564b3fe4fb713a180671c9cf10e1349f4c90a8bb1c14221c7224ea367c5493160171484cbb749d339a317d8bae0178e9d026c814a5b89e93559e0ea + checksum: 10c0/32ad54727e85a4fed81707022b98940e42b15a3858c36cadbbb6429f07b98ebf9c3fc0f81bb7d3b112536f97458597f7005c04aa79a8a26d147ff38a2b469e06 languageName: node linkType: hard -"@aztec/noir-acvm_js@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/noir-acvm_js@npm:4.2.0-aztecnr-rc.2" - checksum: 10c0/e3e4d23f128c9100ac0036cb3bfa20f0d3ebaa485badba3af991d1e2efb26a5b8d2df25d3c796a00bbc8459321f8c62af670620552d62591c6ed39d398653614 +"@aztec/noir-acvm_js@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/noir-acvm_js@npm:4.2.0-nightly.20260412" + checksum: 10c0/3449ba2ee1fb08936fb47f0156ad41c5fc20baf862ee97311da02dcd17530bbe960697b4d2062382d125fdd8b05354df9acd2d80feb5b80f8d2af2f3df76a38b languageName: node linkType: hard -"@aztec/noir-contracts.js@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/noir-contracts.js@npm:4.2.0-aztecnr-rc.2" +"@aztec/noir-contracts.js@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/noir-contracts.js@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/aztec.js": "npm:4.2.0-aztecnr-rc.2" + "@aztec/aztec.js": "npm:4.2.0-nightly.20260412" tslib: "npm:^2.4.0" - checksum: 10c0/6f83d784794ea830e585d4db0328169508832abe56d13ec0826171a58ef92332404200b8dbcd8e4126fd643072c78bf8d55af72d03aff8c15869624a2b9d9dd5 + checksum: 10c0/ed8e7fdf736232dd8a6b3abac0ee6d47c68acb32305df47e916f4537b4aa52d26aa6a8de7b25d676275077114e20a4a90f9ca294280582a0828d1a359b605c84 languageName: node linkType: hard -"@aztec/noir-noir_codegen@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/noir-noir_codegen@npm:4.2.0-aztecnr-rc.2" +"@aztec/noir-noir_codegen@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/noir-noir_codegen@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/noir-types": "npm:4.2.0-aztecnr-rc.2" + "@aztec/noir-types": "npm:4.2.0-nightly.20260412" glob: "npm:^13.0.0" ts-command-line-args: "npm:^2.5.1" bin: noir-codegen: lib/main.js - checksum: 10c0/94ae2466e75d7c7d78ea7fe36ecec23bde90489cdedbc56dc5b62d6b789b79e46f78748b8a59cc93ffa7215995ff184ec58fc43e0e96deadb9d241b52a0e6253 + checksum: 10c0/31b7aedfb8f8403660462ceff20ec31c3ac69b9723ea9dd52b0edb13c17ea657f9b9b2b9e64efe2009eaa9934c9bc5c711e4409a9ffe975d3a12a69209255d39 languageName: node linkType: hard -"@aztec/noir-noirc_abi@npm:4.2.0-aztecnr-rc.2, @aztec/noir-noirc_abi@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/noir-noirc_abi@npm:4.2.0-aztecnr-rc.2" +"@aztec/noir-noirc_abi@npm:4.2.0-nightly.20260412, @aztec/noir-noirc_abi@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/noir-noirc_abi@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/noir-types": "npm:4.2.0-aztecnr-rc.2" - checksum: 10c0/987fe64a5c93e433998dcf52c2cbc10889cd7fe6200fb8a8d5ef778d523d64a6af49eafb6a00ea38ab0b6620d9c5e4cc86b678744fd7a84e5ce43528bc53ceb2 + "@aztec/noir-types": "npm:4.2.0-nightly.20260412" + checksum: 10c0/22c0d8652e2bb0e1eea3da5942349c944c4b438b3384b28002afcd4e6c0852e1d57855ec2432a5bdbf51bcbef6514dbac2e1f3ecddfa2c7392f90769f49d1523 languageName: node linkType: hard -"@aztec/noir-protocol-circuits-types@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/noir-protocol-circuits-types@npm:4.2.0-aztecnr-rc.2" +"@aztec/noir-protocol-circuits-types@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/noir-protocol-circuits-types@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/blob-lib": "npm:4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-acvm_js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-noir_codegen": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-noirc_abi": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-types": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/blob-lib": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/noir-acvm_js": "npm:4.2.0-nightly.20260412" + "@aztec/noir-noir_codegen": "npm:4.2.0-nightly.20260412" + "@aztec/noir-noirc_abi": "npm:4.2.0-nightly.20260412" + "@aztec/noir-types": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" change-case: "npm:^5.4.4" tslib: "npm:^2.4.0" - checksum: 10c0/993526734bf3b982ede64d56c3f96a416a1431365e7f8ee35be451fe6f79c62509e7939d962a7586035eb43aa1bfd6c7b71ed687936188c7c466d46dd452b209 + checksum: 10c0/d9a21827f9c4cb57e8065db77bf745750dc80c89217e9e94eb116909be187bbd0aea0a30ce7bfcb34eb7f226e2a28902af62e94b5ed088982e27d023a53d644a languageName: node linkType: hard -"@aztec/noir-types@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/noir-types@npm:4.2.0-aztecnr-rc.2" - checksum: 10c0/bced65ec3ea31e2f6e3de903b16427d336941f639858a1b68d42b8983a330685351b3901ec700e2e3487cdbde3bb2db6446b15ecb2d6411f147db9a5a4fd830f +"@aztec/noir-types@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/noir-types@npm:4.2.0-nightly.20260412" + checksum: 10c0/7fdc591bc5c36840bb13874e450d5ff2e426fbfed7b2a958e0902e32dafccd48f141cd84240f0941efa31657839c7901b0a7ef764a620de4cc712ef4587ab1c3 languageName: node linkType: hard -"@aztec/protocol-contracts@npm:4.2.0-aztecnr-rc.2, @aztec/protocol-contracts@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/protocol-contracts@npm:4.2.0-aztecnr-rc.2" +"@aztec/protocol-contracts@npm:4.2.0-nightly.20260412, @aztec/protocol-contracts@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/protocol-contracts@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" lodash.chunk: "npm:^4.2.0" lodash.omit: "npm:^4.5.0" tslib: "npm:^2.4.0" - checksum: 10c0/935d1fc2ca61bf15ed2c272142d81ca5633942a133860dcd2c62b20a81630c0e8f56cda6a9c50ace10c71b216f4e5c53ad060e3e923fb9fdfbc10f4697ed8df7 - languageName: node - linkType: hard - -"@aztec/pxe@npm:4.2.0-aztecnr-rc.2, @aztec/pxe@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/pxe@npm:4.2.0-aztecnr-rc.2" - dependencies: - "@aztec/bb-prover": "npm:4.2.0-aztecnr-rc.2" - "@aztec/bb.js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/builder": "npm:4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/ethereum": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/key-store": "npm:4.2.0-aztecnr-rc.2" - "@aztec/kv-store": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-protocol-circuits-types": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-types": "npm:4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:4.2.0-aztecnr-rc.2" - "@aztec/simulator": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + checksum: 10c0/ebffaf6141e2f858aaab3c450a69362f0150baa155efe59872170a7da8f0fc0b26c2e2ce3108d196432f754c26ed2e6bce774310010e77d300520ea3da04e819 + languageName: node + linkType: hard + +"@aztec/pxe@npm:4.2.0-nightly.20260412, @aztec/pxe@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/pxe@npm:4.2.0-nightly.20260412" + dependencies: + "@aztec/bb-prover": "npm:4.2.0-nightly.20260412" + "@aztec/bb.js": "npm:4.2.0-nightly.20260412" + "@aztec/builder": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/key-store": "npm:4.2.0-nightly.20260412" + "@aztec/kv-store": "npm:4.2.0-nightly.20260412" + "@aztec/noir-protocol-circuits-types": "npm:4.2.0-nightly.20260412" + "@aztec/noir-types": "npm:4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:4.2.0-nightly.20260412" + "@aztec/simulator": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" koa: "npm:^2.16.1" koa-router: "npm:^13.1.1" lodash.omit: "npm:^4.5.0" @@ -981,45 +981,45 @@ __metadata: viem: "npm:@aztec/viem@2.38.2" bin: pxe: dest/bin/index.js - checksum: 10c0/bc697e7fc443a55e58e8d158fb90d627b3b64334831c2923ce97d69e1b13bc55e293910c7c6e1b19f6ff0fa9b16ef73129e9a2168624b8911f985ee883f2a2cc + checksum: 10c0/6096abce2e6c7a894a8bd1ef0dcd2a28e2b694bb9beb03e73255817d9274519eae2f3c9ee0152792bba6579f7271fd4f33fc7f589a14fbd8b3c25f51c1e7d47d languageName: node linkType: hard -"@aztec/simulator@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/simulator@npm:4.2.0-aztecnr-rc.2" +"@aztec/simulator@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/simulator@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/native": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-acvm_js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-noirc_abi": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-protocol-circuits-types": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-types": "npm:4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" - "@aztec/telemetry-client": "npm:4.2.0-aztecnr-rc.2" - "@aztec/world-state": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/native": "npm:4.2.0-nightly.20260412" + "@aztec/noir-acvm_js": "npm:4.2.0-nightly.20260412" + "@aztec/noir-noirc_abi": "npm:4.2.0-nightly.20260412" + "@aztec/noir-protocol-circuits-types": "npm:4.2.0-nightly.20260412" + "@aztec/noir-types": "npm:4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/telemetry-client": "npm:4.2.0-nightly.20260412" + "@aztec/world-state": "npm:4.2.0-nightly.20260412" lodash.clonedeep: "npm:^4.5.0" lodash.merge: "npm:^4.6.2" tslib: "npm:^2.4.0" - checksum: 10c0/b85956e4c34f3d2b1885b663012d7b61d1242ca4c1f907492cd80ca71789f1ed0437c2394413f3fafcdefbcdce2941781a4a43bf84648ed074f305b9695560ab + checksum: 10c0/6c09b23f76ea3327e23f43cccc51aff6fca52599f755180173bdd170a116e25ee28cc89bd3b88b6ba0920256b0de3ff205007d0df4b7624717c4c9f3f0dd2c0d languageName: node linkType: hard -"@aztec/stdlib@npm:4.2.0-aztecnr-rc.2, @aztec/stdlib@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/stdlib@npm:4.2.0-aztecnr-rc.2" +"@aztec/stdlib@npm:4.2.0-nightly.20260412, @aztec/stdlib@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/stdlib@npm:4.2.0-nightly.20260412" dependencies: "@aws-sdk/client-s3": "npm:^3.892.0" - "@aztec/bb.js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/blob-lib": "npm:4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/ethereum": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/l1-artifacts": "npm:4.2.0-aztecnr-rc.2" - "@aztec/noir-noirc_abi": "npm:4.2.0-aztecnr-rc.2" - "@aztec/validator-ha-signer": "npm:4.2.0-aztecnr-rc.2" + "@aztec/bb.js": "npm:4.2.0-nightly.20260412" + "@aztec/blob-lib": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/l1-artifacts": "npm:4.2.0-nightly.20260412" + "@aztec/noir-noirc_abi": "npm:4.2.0-nightly.20260412" + "@aztec/validator-ha-signer": "npm:4.2.0-nightly.20260412" "@google-cloud/storage": "npm:^7.15.0" axios: "npm:^1.13.5" json-stringify-deterministic: "npm:1.0.12" @@ -1032,16 +1032,16 @@ __metadata: tslib: "npm:^2.4.0" viem: "npm:@aztec/viem@2.38.2" zod: "npm:^3.23.8" - checksum: 10c0/e4c45edfda489b900e75d68c3b84e0d18ebc2b676b4e30b7406fa38f7cd2cda837a68c846677d68b9a7df736fe972ae99cdaae6f626cb5f97d44ce6e453b724d + checksum: 10c0/061dec0273812223b5631da4222f6d2c718717ee8b78846b1fb5cfe0b2fe9b6a9b642023fcfa7fa96477e28e3601d86abd387c71e09258c54c963a397ef5a2d0 languageName: node linkType: hard -"@aztec/telemetry-client@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/telemetry-client@npm:4.2.0-aztecnr-rc.2" +"@aztec/telemetry-client@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/telemetry-client@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" "@opentelemetry/api": "npm:^1.9.0" "@opentelemetry/api-logs": "npm:^0.55.0" "@opentelemetry/core": "npm:^1.28.0" @@ -1058,53 +1058,53 @@ __metadata: "@opentelemetry/semantic-conventions": "npm:^1.28.0" prom-client: "npm:^15.1.3" viem: "npm:@aztec/viem@2.38.2" - checksum: 10c0/1db458e6a34d38ca7db1b2967f632a472bbdbaab0ddfd56e2e7136cd5d535396179e27574e2e514d00b2a9dd4dd9e68f14974228df810db319b205b4bbb2bf51 + checksum: 10c0/5306a910f25b45902b5cf808a3e019344e7ad6892728958883066d05a62df895cda3805d92de988680b4f3bd7e28716b0970e08084ec21a3be10f361ba09516f languageName: node linkType: hard -"@aztec/validator-ha-signer@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/validator-ha-signer@npm:4.2.0-aztecnr-rc.2" +"@aztec/validator-ha-signer@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/validator-ha-signer@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/ethereum": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" + "@aztec/ethereum": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" node-pg-migrate: "npm:^8.0.4" pg: "npm:^8.11.3" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/5029ac2ba6be151a9c1445f5829a1c0d6360b6569a7d875da002303292ebe55f46141a5625a48b6b597da1c366a633a804768b2e5ac0a62f079c4a75b63348df + checksum: 10c0/0f829eafc382bcba3bd90404d4135c1b5b5aa5c9359a486e195d6271056d0bc03dfb5bde4df50c8ed1823d4155870555577c50b938cb550bc882e376f1d8a190 languageName: node linkType: hard -"@aztec/wallet-sdk@npm:v4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/wallet-sdk@npm:4.2.0-aztecnr-rc.2" +"@aztec/wallet-sdk@npm:v4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/wallet-sdk@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/aztec.js": "npm:4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/entrypoints": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/pxe": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" - checksum: 10c0/15f63c7fb24f03ecedb86818524d966fee3a719751a79cbd4091510ad7853f42fe15291e3b465bcebe0a87d6459eae1a6529e8ee2f361bca1d1fbb6b272d1505 + "@aztec/aztec.js": "npm:4.2.0-nightly.20260412" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/entrypoints": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/pxe": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + checksum: 10c0/2840fff04662d84e723f6c620e4d128686fd2894dc3aae9d19b54bb5d179e0ba427670763346c3c0eab9eca74e40b6bf9ada38efade7d3cb4194ebcab7b824fb languageName: node linkType: hard -"@aztec/world-state@npm:4.2.0-aztecnr-rc.2": - version: 4.2.0-aztecnr-rc.2 - resolution: "@aztec/world-state@npm:4.2.0-aztecnr-rc.2" +"@aztec/world-state@npm:4.2.0-nightly.20260412": + version: 4.2.0-nightly.20260412 + resolution: "@aztec/world-state@npm:4.2.0-nightly.20260412" dependencies: - "@aztec/constants": "npm:4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:4.2.0-aztecnr-rc.2" - "@aztec/kv-store": "npm:4.2.0-aztecnr-rc.2" - "@aztec/merkle-tree": "npm:4.2.0-aztecnr-rc.2" - "@aztec/native": "npm:4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:4.2.0-aztecnr-rc.2" - "@aztec/telemetry-client": "npm:4.2.0-aztecnr-rc.2" + "@aztec/constants": "npm:4.2.0-nightly.20260412" + "@aztec/foundation": "npm:4.2.0-nightly.20260412" + "@aztec/kv-store": "npm:4.2.0-nightly.20260412" + "@aztec/merkle-tree": "npm:4.2.0-nightly.20260412" + "@aztec/native": "npm:4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:4.2.0-nightly.20260412" + "@aztec/telemetry-client": "npm:4.2.0-nightly.20260412" tslib: "npm:^2.4.0" zod: "npm:^3.23.8" - checksum: 10c0/45435b414c4692267c082e2d41363150b7d8043804134d9c260e702ea0a346ad58d16bde55dcbdf30823f24a4f6135857dd7b4dac6612a2f093640262ba4b077 + checksum: 10c0/71a72b1a8618e899fa5282a944e0d8b965635d20a3699fb124db57c13550a54afceeca7de31f6bd89e8cd5d90507f0d006e3e10ccbd1d520b3adf0e7a89c8458 languageName: node linkType: hard @@ -1290,17 +1290,17 @@ __metadata: version: 0.0.0-use.local resolution: "@demo-wallet/shared@workspace:shared" dependencies: - "@aztec/accounts": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/aztec.js": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/entrypoints": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/kv-store": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/noir-contracts.js": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/pxe": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/wallet-sdk": "npm:v4.2.0-aztecnr-rc.2" + "@aztec/accounts": "npm:v4.2.0-nightly.20260412" + "@aztec/aztec.js": "npm:v4.2.0-nightly.20260412" + "@aztec/constants": "npm:v4.2.0-nightly.20260412" + "@aztec/entrypoints": "npm:v4.2.0-nightly.20260412" + "@aztec/foundation": "npm:v4.2.0-nightly.20260412" + "@aztec/kv-store": "npm:v4.2.0-nightly.20260412" + "@aztec/noir-contracts.js": "npm:v4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:v4.2.0-nightly.20260412" + "@aztec/pxe": "npm:v4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:v4.2.0-nightly.20260412" + "@aztec/wallet-sdk": "npm:v4.2.0-nightly.20260412" "@emotion/react": "npm:^11.14.0" "@emotion/styled": "npm:^11.14.0" "@fontsource/roboto": "npm:^5.1.1" @@ -2293,7 +2293,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": +"@eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.1": version: 4.9.1 resolution: "@eslint-community/eslint-utils@npm:4.9.1" dependencies: @@ -2304,7 +2304,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2, @eslint-community/regexpp@npm:^4.4.0": +"@eslint-community/regexpp@npm:^4.12.1, @eslint-community/regexpp@npm:^4.12.2": version: 4.12.2 resolution: "@eslint-community/regexpp@npm:4.12.2" checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d @@ -2364,6 +2364,13 @@ __metadata: languageName: node linkType: hard +"@eslint/js@npm:^9.17.0": + version: 9.39.4 + resolution: "@eslint/js@npm:9.39.4" + checksum: 10c0/5aa7dea2cbc5decf7f5e3b0c6f86a084ccee0f792d288ca8e839f8bc1b64e03e227068968e49b26096e6f71fd857ab6e42691d1b993826b9a3883f1bdd7a0e46 + languageName: node + linkType: hard + "@eslint/object-schema@npm:^2.1.7": version: 2.1.7 resolution: "@eslint/object-schema@npm:2.1.7" @@ -4662,6 +4669,48 @@ __metadata: languageName: node linkType: hard +"@turbo/darwin-64@npm:2.9.6": + version: 2.9.6 + resolution: "@turbo/darwin-64@npm:2.9.6" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@turbo/darwin-arm64@npm:2.9.6": + version: 2.9.6 + resolution: "@turbo/darwin-arm64@npm:2.9.6" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/linux-64@npm:2.9.6": + version: 2.9.6 + resolution: "@turbo/linux-64@npm:2.9.6" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@turbo/linux-arm64@npm:2.9.6": + version: 2.9.6 + resolution: "@turbo/linux-arm64@npm:2.9.6" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@turbo/windows-64@npm:2.9.6": + version: 2.9.6 + resolution: "@turbo/windows-64@npm:2.9.6" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@turbo/windows-arm64@npm:2.9.6": + version: 2.9.6 + resolution: "@turbo/windows-arm64@npm:2.9.6" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@types/cacheable-request@npm:^6.0.1": version: 6.0.3 resolution: "@types/cacheable-request@npm:6.0.3" @@ -4870,13 +4919,6 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12": - version: 7.7.1 - resolution: "@types/semver@npm:7.7.1" - checksum: 10c0/c938aef3bf79a73f0f3f6037c16e2e759ff40c54122ddf0b2583703393d8d3127130823facb880e694caa324eb6845628186aac1997ee8b31dc2d18fafe26268 - languageName: node - linkType: hard - "@types/tough-cookie@npm:*": version: 4.0.5 resolution: "@types/tough-cookie@npm:4.0.5" @@ -4920,30 +4962,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" - dependencies: - "@eslint-community/regexpp": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/type-utils": "npm:5.62.0" - "@typescript-eslint/utils": "npm:5.62.0" - debug: "npm:^4.3.4" - graphemer: "npm:^1.4.0" - ignore: "npm:^5.2.0" - natural-compare-lite: "npm:^1.4.0" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependencies: - "@typescript-eslint/parser": ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/3f40cb6bab5a2833c3544e4621b9fdacd8ea53420cadc1c63fac3b89cdf5c62be1e6b7bcf56976dede5db4c43830de298ced3db60b5494a3b961ca1b4bff9f2a - languageName: node - linkType: hard - "@typescript-eslint/parser@npm:8.56.1": version: 8.56.1 resolution: "@typescript-eslint/parser@npm:8.56.1" @@ -4960,23 +4978,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/parser@npm:5.62.0" - dependencies: - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - debug: "npm:^4.3.4" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/315194b3bf39beb9bd16c190956c46beec64b8371e18d6bb72002108b250983eb1e186a01d34b77eb4045f4941acbb243b16155fbb46881105f65e37dc9e24d4 - languageName: node - linkType: hard - "@typescript-eslint/project-service@npm:8.56.1": version: 8.56.1 resolution: "@typescript-eslint/project-service@npm:8.56.1" @@ -4990,16 +4991,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/scope-manager@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - checksum: 10c0/861253235576c1c5c1772d23cdce1418c2da2618a479a7de4f6114a12a7ca853011a1e530525d0931c355a8fd237b9cd828fac560f85f9623e24054fd024726f - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:8.56.1": version: 8.56.1 resolution: "@typescript-eslint/scope-manager@npm:8.56.1" @@ -5019,23 +5010,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/type-utils@npm:5.62.0" - dependencies: - "@typescript-eslint/typescript-estree": "npm:5.62.0" - "@typescript-eslint/utils": "npm:5.62.0" - debug: "npm:^4.3.4" - tsutils: "npm:^3.21.0" - peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/93112e34026069a48f0484b98caca1c89d9707842afe14e08e7390af51cdde87378df29d213d3bbd10a7cfe6f91b228031b56218515ce077bdb62ddea9d9f474 - languageName: node - linkType: hard - "@typescript-eslint/type-utils@npm:8.56.1": version: 8.56.1 resolution: "@typescript-eslint/type-utils@npm:8.56.1" @@ -5052,13 +5026,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/types@npm:5.62.0" - checksum: 10c0/7febd3a7f0701c0b927e094f02e82d8ee2cada2b186fcb938bc2b94ff6fbad88237afc304cbaf33e82797078bbbb1baf91475f6400912f8b64c89be79bfa4ddf - languageName: node - linkType: hard - "@typescript-eslint/types@npm:8.56.1, @typescript-eslint/types@npm:^8.56.1": version: 8.56.1 resolution: "@typescript-eslint/types@npm:8.56.1" @@ -5066,24 +5033,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/visitor-keys": "npm:5.62.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - semver: "npm:^7.3.7" - tsutils: "npm:^3.21.0" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/d7984a3e9d56897b2481940ec803cb8e7ead03df8d9cfd9797350be82ff765dfcf3cfec04e7355e1779e948da8f02bc5e11719d07a596eb1cb995c48a95e38cf - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:8.56.1": version: 8.56.1 resolution: "@typescript-eslint/typescript-estree@npm:8.56.1" @@ -5103,24 +5052,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/utils@npm:5.62.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.2.0" - "@types/json-schema": "npm:^7.0.9" - "@types/semver": "npm:^7.3.12" - "@typescript-eslint/scope-manager": "npm:5.62.0" - "@typescript-eslint/types": "npm:5.62.0" - "@typescript-eslint/typescript-estree": "npm:5.62.0" - eslint-scope: "npm:^5.1.1" - semver: "npm:^7.3.7" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 10c0/f09b7d9952e4a205eb1ced31d7684dd55cee40bf8c2d78e923aa8a255318d97279825733902742c09d8690f37a50243f4c4d383ab16bd7aefaf9c4b438f785e1 - languageName: node - linkType: hard - "@typescript-eslint/utils@npm:8.56.1": version: 8.56.1 resolution: "@typescript-eslint/utils@npm:8.56.1" @@ -5136,16 +5067,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" - dependencies: - "@typescript-eslint/types": "npm:5.62.0" - eslint-visitor-keys: "npm:^3.3.0" - checksum: 10c0/7c3b8e4148e9b94d9b7162a596a1260d7a3efc4e65199693b8025c71c4652b8042501c0bc9f57654c1e2943c26da98c0f77884a746c6ae81389fcb0b513d995d - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:8.56.1": version: 8.56.1 resolution: "@typescript-eslint/visitor-keys@npm:8.56.1" @@ -5741,21 +5662,21 @@ __metadata: version: 0.0.0-use.local resolution: "app@workspace:app" dependencies: - "@aztec/accounts": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/aztec.js": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/bb.js": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/blob-lib": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/entrypoints": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/ethereum": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/kv-store": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/noir-contracts.js": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/noir-noirc_abi": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/pxe": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/wallet-sdk": "npm:v4.2.0-aztecnr-rc.2" + "@aztec/accounts": "npm:v4.2.0-nightly.20260412" + "@aztec/aztec.js": "npm:v4.2.0-nightly.20260412" + "@aztec/bb.js": "npm:v4.2.0-nightly.20260412" + "@aztec/blob-lib": "npm:v4.2.0-nightly.20260412" + "@aztec/constants": "npm:v4.2.0-nightly.20260412" + "@aztec/entrypoints": "npm:v4.2.0-nightly.20260412" + "@aztec/ethereum": "npm:v4.2.0-nightly.20260412" + "@aztec/foundation": "npm:v4.2.0-nightly.20260412" + "@aztec/kv-store": "npm:v4.2.0-nightly.20260412" + "@aztec/noir-contracts.js": "npm:v4.2.0-nightly.20260412" + "@aztec/noir-noirc_abi": "npm:v4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:v4.2.0-nightly.20260412" + "@aztec/pxe": "npm:v4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:v4.2.0-nightly.20260412" + "@aztec/wallet-sdk": "npm:v4.2.0-nightly.20260412" "@demo-wallet/shared": "workspace:*" "@electron-forge/cli": "npm:^7.9.0" "@electron-forge/maker-deb": "npm:^7.9.0" @@ -5773,17 +5694,12 @@ __metadata: "@mui/lab": "npm:^6.0.0-beta.27" "@mui/material": "npm:^6.4.4" "@types/electron-squirrel-startup": "npm:^1.0.2" - "@typescript-eslint/eslint-plugin": "npm:^5.62.0" - "@typescript-eslint/parser": "npm:^5.62.0" + "@types/react": "npm:^18.3.1" + "@types/react-dom": "npm:^18.3.1" "@vitejs/plugin-react-swc": "npm:^3.5.0" "@yao-pkg/pkg": "npm:^5.15.0" electron: "npm:38.1.0" electron-squirrel-startup: "npm:^1.0.1" - eslint: "npm:^9.17.0" - eslint-config-prettier: "npm:^10.1.3" - eslint-plugin-import: "npm:^2.32.0" - eslint-plugin-react-hooks: "npm:^5.2.0" - eslint-plugin-react-refresh: "npm:^0.4.20" glob: "npm:^11.0.2" json-stringify-deterministic: "npm:^1.0.12" lodash.chunk: "npm:^4.2.0" @@ -5791,13 +5707,11 @@ __metadata: lodash.times: "npm:^4.3.2" msgpackr: "npm:^1.11.0" pino: "npm:9.5.0" - prettier: "npm:^3.6.2" react: "npm:^18.3.1" react-dom: "npm:^18.3.1" react-qr-code: "npm:^2.0.18" sha3: "npm:^2.1.4" typescript: "npm:~5.6.2" - typescript-eslint: "npm:^8.32.0" vite: "npm:^5.4.20" vite-plugin-node-polyfills: "npm:^0.24.0" vite-plugin-static-copy: "npm:^3.1.2" @@ -5852,13 +5766,6 @@ __metadata: languageName: node linkType: hard -"array-union@npm:^2.1.0": - version: 2.1.0 - resolution: "array-union@npm:2.1.0" - checksum: 10c0/429897e68110374f39b771ec47a7161fc6a8fc33e196857c0a396dc75df0b5f65e4d046674db764330b6bb66b39ef48dd7c53b6a2ee75cfb0681e0c1a7033962 - languageName: node - linkType: hard - "array.prototype.findlastindex@npm:^1.2.6": version: 1.2.6 resolution: "array.prototype.findlastindex@npm:1.2.6" @@ -7187,6 +7094,18 @@ __metadata: "demo-wallet@workspace:.": version: 0.0.0-use.local resolution: "demo-wallet@workspace:." + dependencies: + "@eslint/js": "npm:^9.17.0" + eslint: "npm:^9.17.0" + eslint-config-prettier: "npm:^10.1.3" + eslint-plugin-import: "npm:^2.32.0" + eslint-plugin-react-hooks: "npm:^5.2.0" + eslint-plugin-react-refresh: "npm:^0.4.20" + globals: "npm:^15.0.0" + prettier: "npm:^3.6.2" + turbo: "npm:^2.5.4" + typescript: "npm:~5.6.2" + typescript-eslint: "npm:^8.32.0" languageName: unknown linkType: soft @@ -7256,15 +7175,6 @@ __metadata: languageName: node linkType: hard -"dir-glob@npm:^3.0.1": - version: 3.0.1 - resolution: "dir-glob@npm:3.0.1" - dependencies: - path-type: "npm:^4.0.0" - checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c - languageName: node - linkType: hard - "doctrine@npm:^2.1.0": version: 2.1.0 resolution: "doctrine@npm:2.1.0" @@ -7949,7 +7859,7 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": +"eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" dependencies: @@ -7969,7 +7879,7 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.3": +"eslint-visitor-keys@npm:^3.4.3": version: 3.4.3 resolution: "eslint-visitor-keys@npm:3.4.3" checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 @@ -8253,7 +8163,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.7, fast-glob@npm:^3.2.9": +"fast-glob@npm:^3.2.7": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -8939,6 +8849,13 @@ __metadata: languageName: node linkType: hard +"globals@npm:^15.0.0": + version: 15.15.0 + resolution: "globals@npm:15.15.0" + checksum: 10c0/f9ae80996392ca71316495a39bec88ac43ae3525a438b5626cd9d5ce9d5500d0a98a266409605f8cd7241c7acf57c354a48111ea02a767ba4f374b806d6861fe + languageName: node + linkType: hard + "globalthis@npm:^1.0.1, globalthis@npm:^1.0.4": version: 1.0.4 resolution: "globalthis@npm:1.0.4" @@ -8949,20 +8866,6 @@ __metadata: languageName: node linkType: hard -"globby@npm:^11.1.0": - version: 11.1.0 - resolution: "globby@npm:11.1.0" - dependencies: - array-union: "npm:^2.1.0" - dir-glob: "npm:^3.0.1" - fast-glob: "npm:^3.2.9" - ignore: "npm:^5.2.0" - merge2: "npm:^1.4.1" - slash: "npm:^3.0.0" - checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189 - languageName: node - linkType: hard - "google-auth-library@npm:^9.6.3": version: 9.15.1 resolution: "google-auth-library@npm:9.15.1" @@ -9017,13 +8920,6 @@ __metadata: languageName: node linkType: hard -"graphemer@npm:^1.4.0": - version: 1.4.0 - resolution: "graphemer@npm:1.4.0" - checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 - languageName: node - linkType: hard - "gtoken@npm:^7.0.0": version: 7.1.0 resolution: "gtoken@npm:7.1.0" @@ -10544,7 +10440,7 @@ __metadata: languageName: node linkType: hard -"merge2@npm:^1.3.0, merge2@npm:^1.4.1": +"merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1" checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb @@ -10952,13 +10848,6 @@ __metadata: languageName: node linkType: hard -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: 10c0/f6cef26f5044515754802c0fc475d81426f3b90fe88c20fabe08771ce1f736ce46e0397c10acb569a4dd0acb84c7f1ee70676122f95d5bfdd747af3a6c6bbaa8 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -12882,7 +12771,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.1.1, semver@npm:^7.1.3, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.2, semver@npm:^7.7.3": +"semver@npm:^7.1.1, semver@npm:^7.1.3, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.7.3": version: 7.7.4 resolution: "semver@npm:7.7.4" bin: @@ -13111,13 +13000,6 @@ __metadata: languageName: node linkType: hard -"slash@npm:^3.0.0": - version: 3.0.0 - resolution: "slash@npm:3.0.0" - checksum: 10c0/e18488c6a42bdfd4ac5be85b2ced3ccd0224773baae6ad42cfbb9ec74fc07f9fa8396bd35ee638084ead7a2a0818eb5e7151111544d4731ce843019dab4be47b - languageName: node - linkType: hard - "slice-ansi@npm:^5.0.0": version: 5.0.0 resolution: "slice-ansi@npm:5.0.0" @@ -13907,13 +13789,6 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1": - version: 1.14.1 - resolution: "tslib@npm:1.14.1" - checksum: 10c0/69ae09c49eea644bc5ebe1bca4fa4cc2c82b7b3e02f43b84bd891504edf66dbc6b2ec0eef31a957042de2269139e4acff911e6d186a258fb14069cd7f6febce2 - languageName: node - linkType: hard - "tslib@npm:^2.4.0, tslib@npm:^2.6.2": version: 2.8.1 resolution: "tslib@npm:2.8.1" @@ -13928,17 +13803,6 @@ __metadata: languageName: node linkType: hard -"tsutils@npm:^3.21.0": - version: 3.21.0 - resolution: "tsutils@npm:3.21.0" - dependencies: - tslib: "npm:^1.8.1" - peerDependencies: - typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - checksum: 10c0/02f19e458ec78ead8fffbf711f834ad8ecd2cc6ade4ec0320790713dccc0a412b99e7fd907c4cda2a1dc602c75db6f12e0108e87a5afad4b2f9e90a24cabd5a2 - languageName: node - linkType: hard - "tty-browserify@npm:0.0.1": version: 0.0.1 resolution: "tty-browserify@npm:0.0.1" @@ -13955,6 +13819,35 @@ __metadata: languageName: node linkType: hard +"turbo@npm:^2.5.4": + version: 2.9.6 + resolution: "turbo@npm:2.9.6" + dependencies: + "@turbo/darwin-64": "npm:2.9.6" + "@turbo/darwin-arm64": "npm:2.9.6" + "@turbo/linux-64": "npm:2.9.6" + "@turbo/linux-arm64": "npm:2.9.6" + "@turbo/windows-64": "npm:2.9.6" + "@turbo/windows-arm64": "npm:2.9.6" + dependenciesMeta: + "@turbo/darwin-64": + optional: true + "@turbo/darwin-arm64": + optional: true + "@turbo/linux-64": + optional: true + "@turbo/linux-arm64": + optional: true + "@turbo/windows-64": + optional: true + "@turbo/windows-arm64": + optional: true + bin: + turbo: bin/turbo + checksum: 10c0/8f06bc9f4fa8caa446b72276ba0ab69dbcaf46ffbf536926f5d2759cc5d180f027a4065286ce03d6788306b5404d7a320d8354670cabd459c599fcdca83e4611 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -14560,17 +14453,17 @@ __metadata: version: 0.0.0-use.local resolution: "web-wallet@workspace:web" dependencies: - "@aztec/accounts": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/aztec.js": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/constants": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/entrypoints": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/foundation": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/kv-store": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/noir-contracts.js": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/protocol-contracts": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/pxe": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/stdlib": "npm:v4.2.0-aztecnr-rc.2" - "@aztec/wallet-sdk": "npm:v4.2.0-aztecnr-rc.2" + "@aztec/accounts": "npm:v4.2.0-nightly.20260412" + "@aztec/aztec.js": "npm:v4.2.0-nightly.20260412" + "@aztec/constants": "npm:v4.2.0-nightly.20260412" + "@aztec/entrypoints": "npm:v4.2.0-nightly.20260412" + "@aztec/foundation": "npm:v4.2.0-nightly.20260412" + "@aztec/kv-store": "npm:v4.2.0-nightly.20260412" + "@aztec/noir-contracts.js": "npm:v4.2.0-nightly.20260412" + "@aztec/protocol-contracts": "npm:v4.2.0-nightly.20260412" + "@aztec/pxe": "npm:v4.2.0-nightly.20260412" + "@aztec/stdlib": "npm:v4.2.0-nightly.20260412" + "@aztec/wallet-sdk": "npm:v4.2.0-nightly.20260412" "@demo-wallet/shared": "workspace:*" "@emotion/react": "npm:^11.14.0" "@emotion/styled": "npm:^11.14.0" @@ -14581,7 +14474,6 @@ __metadata: "@types/react": "npm:^18.3.1" "@types/react-dom": "npm:^18.3.1" "@vitejs/plugin-react-swc": "npm:^3.5.0" - eslint: "npm:^9.17.0" react: "npm:^18.3.1" react-dom: "npm:^18.3.1" react-qr-code: "npm:^2.0.18"